Skip to content

Commit

Permalink
Merge pull request #187 from jamezp/LOGMGR-195
Browse files Browse the repository at this point in the history
[LOGMGR-195] Create a ClientSocketFactory SPI to allow consumers to c…
  • Loading branch information
jamezp authored Jul 3, 2018
2 parents 6a419aa + 56ab415 commit 6743142
Show file tree
Hide file tree
Showing 5 changed files with 291 additions and 45 deletions.
134 changes: 134 additions & 0 deletions src/main/java/org/jboss/logmanager/handlers/ClientSocketFactory.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
/*
* JBoss, Home of Professional Open Source.
*
* Copyright 2018 Red Hat, Inc., and individual contributors
* as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.jboss.logmanager.handlers;

import java.io.IOException;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.SocketAddress;
import java.net.SocketException;
import javax.net.SocketFactory;

/**
* A factory used to create writable sockets.
*
* @author <a href="mailto:jperkins@redhat.com">James R. Perkins</a>
*/
public interface ClientSocketFactory {

/**
* Creates a datagram socket for UDP communication.
*
* @return the newly created socket
*
* @throws SocketException if binding the socket fails
*/
DatagramSocket createDatagramSocket() throws SocketException;

/**
* Creates a TCP socket.
*
* @return the newly created socket
*
* @throws IOException if an error occurs creating the socket
*/
Socket createSocket() throws IOException;

/**
* Returns the address being used to create sockets.
*
* @return the address being used
*/
InetAddress getAddress();

/**
* Returns the port being used to create sockets.
*
* @return the port being used
*/
int getPort();

/**
* A convenience method to return the socket address.
* <p>
* The default implementation simply returns {@code new InetSocketAddress(getAddress(), getPort())}.
* </p>
*
* @return a socket address
*/
default SocketAddress getSocketAddress() {
return new InetSocketAddress(getAddress(), getPort());
}

/**
* Creates a new default implementation of the factory which uses {@link SocketFactory#getDefault()} for TCP
* sockets and {@code new DatagramSocket()} for UDP sockets.
*
* @param address the address to bind to
* @param port the port to bind to
*
* @return the client socket factory
*/
static ClientSocketFactory of(final InetAddress address, final int port) {
return of(SocketFactory.getDefault(), address, port);
}

/**
* Creates a new default implementation of the factory which uses the provided
* {@linkplain SocketFactory#createSocket(InetAddress, int) socket factory} to create TCP connections and
* {@code new DatagramSocket()} for UDP sockets.
*
* @param socketFactory the socket factory used for TCP connections, if {@code null} the
* {@linkplain SocketFactory#getDefault() default} socket factory will be used
* @param address the address to bind to
* @param port the port to bind to
*
* @return the client socket factory
*/
static ClientSocketFactory of(final SocketFactory socketFactory, final InetAddress address, final int port) {
if (address == null || port < 0) {
throw new IllegalArgumentException(String.format("The address cannot be null (%s) and the port must be a positive integer (%d)", address, port));
}
final SocketFactory factory = (socketFactory == null ? SocketFactory.getDefault() : socketFactory);
return new ClientSocketFactory() {
@Override
public DatagramSocket createDatagramSocket() throws SocketException {
return new DatagramSocket();
}

@Override
public Socket createSocket() throws IOException {
return factory.createSocket(address, port);
}

@Override
public InetAddress getAddress() {
return address;
}

@Override
public int getPort() {
return port;
}
};
}
}
112 changes: 92 additions & 20 deletions src/main/java/org/jboss/logmanager/handlers/SocketHandler.java
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ public enum Protocol {
private final Object outputLock = new Object();

// All the following fields are guarded by outputLock
private ClientSocketFactory clientSocketFactory;
private SocketFactory socketFactory;
private InetAddress address;
private int port;
Expand Down Expand Up @@ -143,6 +144,7 @@ public SocketHandler(final Protocol protocol, final InetAddress address, final i
* @param port the port to connect to
*
* @throws UnknownHostException if an error occurs resolving the hostname
* @see #SocketHandler(ClientSocketFactory, Protocol)
*/
public SocketHandler(final SocketFactory socketFactory, final Protocol protocol, final String hostname, final int port) throws UnknownHostException {
this(socketFactory, protocol, InetAddress.getByName(hostname), port);
Expand All @@ -157,14 +159,35 @@ public SocketHandler(final SocketFactory socketFactory, final Protocol protocol,
* @param protocol the protocol to connect with
* @param address the address to connect to
* @param port the port to connect to
*
* @see #SocketHandler(ClientSocketFactory, Protocol)
*/
public SocketHandler(final SocketFactory socketFactory, final Protocol protocol, final InetAddress address, final int port) {
this.socketFactory = socketFactory;
this.clientSocketFactory = null;
this.address = address;
this.port = port;
this.protocol = protocol;
this.protocol = (protocol == null ? Protocol.TCP : protocol);
initialize = true;
writer = null;
blockOnReconnect = false;
}

/**
* Creates a socket handler.
*
* @param clientSocketFactory the client socket factory used to create sockets
* @param protocol the protocol to connect with
*/
public SocketHandler(final ClientSocketFactory clientSocketFactory, final Protocol protocol) {
this.clientSocketFactory = clientSocketFactory;
if (clientSocketFactory != null) {
address = clientSocketFactory.getAddress();
port = clientSocketFactory.getPort();
}
this.protocol = (protocol == null ? Protocol.TCP : protocol);
initialize = true;
writer = null;
this.socketFactory = socketFactory;
blockOnReconnect = false;
}

Expand Down Expand Up @@ -229,19 +252,28 @@ public InetAddress getAddress() {

/**
* Sets the address to connect to.
* <p>
* Note that is resets the {@linkplain #setClientSocketFactory(ClientSocketFactory) client socket factory}.
* </p>
*
* @param address the address
*/
public void setAddress(final InetAddress address) {
checkAccess(this);
synchronized (outputLock) {
if (!this.address.equals(address)) {
initialize = true;
clientSocketFactory = null;
}
this.address = address;
initialize = true;
}
}

/**
* Sets the address to connect to by doing a lookup on the hostname.
* <p>
* Note that is resets the {@linkplain #setClientSocketFactory(ClientSocketFactory) client socket factory}.
* </p>
*
* @param hostname the host name used to resolve the address
*
Expand Down Expand Up @@ -277,6 +309,7 @@ public void setBlockOnReconnect(final boolean blockOnReconnect) {
checkAccess(this);
synchronized (outputLock) {
this.blockOnReconnect = blockOnReconnect;
initialize = true;
}
}

Expand All @@ -293,7 +326,7 @@ public Protocol getProtocol() {
* Sets the protocol to use. If the value is {@code null} the protocol will be set to
* {@linkplain Protocol#TCP TCP}.
* <p>
* Note that is resets the {@linkplain #setSocketFactory(SocketFactory) socket factory}.
* Note that is resets the {@linkplain #setSocketFactory(SocketFactory) socket factory} if it was previously set.
* </p>
*
* @param protocol the protocol to use
Expand All @@ -304,10 +337,11 @@ public void setProtocol(final Protocol protocol) {
if (protocol == null) {
this.protocol = Protocol.TCP;
}
// Reset the socket factory
socketFactory = null;
if (this.protocol != protocol) {
socketFactory = null;
initialize = true;
}
this.protocol = protocol;
initialize = true;
}
}

Expand All @@ -322,14 +356,20 @@ public int getPort() {

/**
* Sets the port to connect to.
* <p>
* Note that is resets the {@linkplain #setClientSocketFactory(ClientSocketFactory) client socket factory}.
* </p>
*
* @param port the port
*/
public void setPort(final int port) {
checkAccess(this);
synchronized (outputLock) {
if (this.port != port) {
initialize = true;
clientSocketFactory = null;
}
this.port = port;
initialize = true;
}
}

Expand All @@ -338,15 +378,33 @@ public void setPort(final int port) {
* connections.
* <p>
* Note that if the {@linkplain #setProtocol(Protocol) protocol} is set the socket factory will be set to
* {@code null} and reset.
* {@code null} and reset. Setting a value here also resets the
* {@linkplain #setClientSocketFactory(ClientSocketFactory) client socket factory}.
* </p>
*
* @param socketFactory the socket factory
*
* @see #setClientSocketFactory(ClientSocketFactory)
*/
public void setSocketFactory(final SocketFactory socketFactory) {
checkAccess(this);
synchronized (outputLock) {
this.socketFactory = socketFactory;
this.clientSocketFactory = null;
initialize = true;
}
}

/**
* Sets the client socket factory used to create sockets. If {@code null} the
* {@linkplain #setAddress(InetAddress) address} and {@linkplain #setPort(int) port} are required to be set.
*
* @param clientSocketFactory the client socket factory to use
*/
public void setClientSocketFactory(final ClientSocketFactory clientSocketFactory) {
checkAccess(this);
synchronized (outputLock) {
this.clientSocketFactory = clientSocketFactory;
initialize = true;
}
}
Expand Down Expand Up @@ -388,26 +446,40 @@ private void initialize() {
private OutputStream createOutputStream() {
if (address != null || port >= 0) {
try {
final ClientSocketFactory socketFactory = getClientSocketFactory();
if (protocol == Protocol.UDP) {
return new UdpOutputStream(address, port);
}
SocketFactory socketFactory = this.socketFactory;
if (socketFactory == null) {
if (protocol == Protocol.SSL_TCP) {
this.socketFactory = socketFactory = SSLSocketFactory.getDefault();
} else {
// Assume we want a TCP connection
this.socketFactory = socketFactory = SocketFactory.getDefault();
}
return new UdpOutputStream(socketFactory);
}
return new TcpOutputStream(socketFactory, address, port, blockOnReconnect);
return new TcpOutputStream(socketFactory, blockOnReconnect);
} catch (IOException e) {
reportError("Failed to create socket output stream", e, ErrorManager.OPEN_FAILURE);
}
}
return null;
}

private ClientSocketFactory getClientSocketFactory() {
synchronized (outputLock) {
if (clientSocketFactory != null) {
return clientSocketFactory;
}
if (address == null || port <= 0) {
throw new IllegalStateException("An address and port greater than 0 is required.");
}
final ClientSocketFactory clientSocketFactory;
if (socketFactory == null) {
if (protocol == Protocol.SSL_TCP) {
clientSocketFactory = ClientSocketFactory.of(SSLSocketFactory.getDefault(), address, port);
} else {
clientSocketFactory = ClientSocketFactory.of(address, port);
}
} else {
clientSocketFactory = ClientSocketFactory.of(socketFactory, address, port);
}
return clientSocketFactory;
}
}

private void writeHead(final Writer writer) {
try {
final Formatter formatter = getFormatter();
Expand Down
Loading

0 comments on commit 6743142

Please sign in to comment.