Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[LOGMGR-195] Create a ClientSocketFactory SPI to allow consumers to c… #187

Merged
merged 1 commit into from
Jul 3, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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