Skip to content

Commit

Permalink
[LOGMGR-195] Create a ClientSocketFactory SPI to allow consumers to c…
Browse files Browse the repository at this point in the history
…reate their own sockets for the SocketHandler and SyslogHandler.
  • Loading branch information
jamezp committed Jun 25, 2018
1 parent 6a419aa commit 0f62848
Show file tree
Hide file tree
Showing 5 changed files with 265 additions and 40 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:[email protected]">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;
}
};
}
}
88 changes: 70 additions & 18 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 @@ -159,12 +160,31 @@ public SocketHandler(final SocketFactory socketFactory, final Protocol protocol,
* @param port the port to connect to
*/
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 @@ -236,7 +256,7 @@ public void setAddress(final InetAddress address) {
checkAccess(this);
synchronized (outputLock) {
this.address = address;
initialize = true;
setInitialize();
}
}

Expand Down Expand Up @@ -277,6 +297,7 @@ public void setBlockOnReconnect(final boolean blockOnReconnect) {
checkAccess(this);
synchronized (outputLock) {
this.blockOnReconnect = blockOnReconnect;
setInitialize();
}
}

Expand Down Expand Up @@ -304,10 +325,8 @@ public void setProtocol(final Protocol protocol) {
if (protocol == null) {
this.protocol = Protocol.TCP;
}
// Reset the socket factory
socketFactory = null;
this.protocol = protocol;
initialize = true;
setInitialize();
}
}

Expand All @@ -329,7 +348,7 @@ public void setPort(final int port) {
checkAccess(this);
synchronized (outputLock) {
this.port = port;
initialize = true;
setInitialize();
}
}

Expand All @@ -347,10 +366,51 @@ public void setSocketFactory(final SocketFactory socketFactory) {
checkAccess(this);
synchronized (outputLock) {
this.socketFactory = socketFactory;
setInitialize();
}
}

/**
* 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;
}
}

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 setInitialize() {
initialize = true;
clientSocketFactory = null;
}

private void initialize() {
final Writer current = this.writer;
boolean okay = false;
Expand Down Expand Up @@ -388,19 +448,11 @@ 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);
}
Expand Down
40 changes: 33 additions & 7 deletions src/main/java/org/jboss/logmanager/handlers/SyslogHandler.java
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@
import java.util.logging.Formatter;
import java.util.logging.Level;
import java.util.regex.Pattern;
import javax.net.SocketFactory;
import javax.net.ssl.SSLSocketFactory;

import org.jboss.logmanager.ExtHandler;
import org.jboss.logmanager.ExtLogRecord;
Expand Down Expand Up @@ -324,6 +326,7 @@ public static enum SyslogType {
private boolean truncate;
private int maxLen;
private boolean blockOnReconnect;
private ClientSocketFactory clientSocketFactory;

/**
* The default class constructor.
Expand Down Expand Up @@ -667,6 +670,19 @@ public void setBlockOnReconnect(final boolean blockOnReconnect) {
}
}

/**
* Sets the client socket factory used to create sockets.
*
* @param clientSocketFactory the client socket factory to use
*/
public void setClientSocketFactory(final ClientSocketFactory clientSocketFactory) {
checkAccess(this);
synchronized (outputLock) {
this.clientSocketFactory = clientSocketFactory;
initializeConnection = true;
}
}

/**
* Checks whether or not characters below decimal 32, traditional US-ASCII control values expect {@code DEL}, are
* being escaped or not.
Expand Down Expand Up @@ -733,6 +749,7 @@ public void setPort(final int port) {
synchronized (outputLock) {
this.port = port;
initializeConnection = true;
clientSocketFactory = null;
}
}

Expand Down Expand Up @@ -943,6 +960,7 @@ public void setServerAddress(final InetAddress serverAddress) {
synchronized (outputLock) {
this.serverAddress = serverAddress;
initializeConnection = true;
clientSocketFactory = null;
}
}

Expand Down Expand Up @@ -993,6 +1011,7 @@ public void setProtocol(final Protocol type) {
synchronized (outputLock) {
this.protocol = type;
initializeConnection = true;
clientSocketFactory = null;
}
}

Expand Down Expand Up @@ -1084,14 +1103,11 @@ private void init() {
final OutputStream out;
// Check the sockets
try {
if (protocol == Protocol.TCP) {
out = new TcpOutputStream(serverAddress, port, blockOnReconnect);
} else if (protocol == Protocol.UDP) {
out = new UdpOutputStream(serverAddress, port);
} else if (protocol == Protocol.SSL_TCP) {
out = new SslTcpOutputStream(serverAddress, port, blockOnReconnect);
final ClientSocketFactory clientSocketFactory = getClientSocketFactory();
if (protocol == Protocol.UDP) {
out = new UdpOutputStream(clientSocketFactory);
} else {
throw new IllegalStateException("Invalid protocol: " + protocol);
out = new TcpOutputStream(clientSocketFactory, blockOnReconnect);
}
setOutputStream(out, false);
} catch (IOException e) {
Expand Down Expand Up @@ -1296,6 +1312,16 @@ protected byte[] createRFC3164Header(final ExtLogRecord record) throws IOExcepti
return buffer.toArray();
}

private ClientSocketFactory getClientSocketFactory() {
synchronized (outputLock) {
if (clientSocketFactory != null) {
return clientSocketFactory;
}
final SocketFactory socketFactory = (protocol == Protocol.SSL_TCP ? SSLSocketFactory.getDefault() : SocketFactory.getDefault());
return ClientSocketFactory.of(socketFactory, serverAddress, port);
}
}

private static String checkPrintableAscii(final String name, final String value) {
if (value != null && PRINTABLE_ASCII_PATTERN.matcher(value).find()) {
final String upper = Character.toUpperCase(name.charAt(0)) + name.substring(1);
Expand Down
Loading

0 comments on commit 0f62848

Please sign in to comment.