/*
* JBoss, Home of Professional Open Source.
* Copyright 2010, Red Hat, Inc., and individual contributors
* as indicated by the @author tags. See the copyright.txt file in the
* distribution for a full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.jboss.as.process.protocol;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;
import java.util.concurrent.Executor;
import java.util.concurrent.ThreadFactory;
import javax.net.ServerSocketFactory;
import org.jboss.as.process.logging.ProcessLogger;
import org.jboss.as.process.protocol.Connection.ClosedCallback;
import org.wildfly.common.Assert;
/**
* @author <a href="mailto:david.lloyd@redhat.com">David M. Lloyd</a>
*/
public final class ProtocolServer {
private final ThreadFactory threadFactory;
private final ServerSocketFactory socketFactory;
private final ConnectionHandler connectionHandler;
private final InetSocketAddress bindAddress;
private final int backlog;
private final int readTimeout;
private final Executor readExecutor;
private volatile boolean stop;
private volatile Thread thread;
private volatile ServerSocket serverSocket;
private volatile InetSocketAddress boundAddress;
private final ClosedCallback callback;
public ProtocolServer(final Configuration configuration) throws IOException {
threadFactory = configuration.getThreadFactory();
socketFactory = configuration.getSocketFactory();
connectionHandler = configuration.getConnectionHandler();
bindAddress = configuration.getBindAddress();
backlog = configuration.getBacklog();
readTimeout = configuration.getReadTimeout();
readExecutor = configuration.getReadExecutor();
callback = configuration.getClosedCallback();
Assert.checkNotNullParam("bindAddress", bindAddress);
Assert.checkNotNullParam("connectionHandler", connectionHandler);
}
public void start() throws IOException {
stop = false;
final ServerSocket serverSocket = socketFactory.createServerSocket();
this.serverSocket = serverSocket;
thread = threadFactory.newThread(new Runnable() {
public void run() {
try {
while (! serverSocket.isClosed() && ! stop) {
try {
final Socket socket = serverSocket.accept();
boolean ok = false;
try {
socket.setSoTimeout(readTimeout);
ok = true;
} finally {
if (! ok) {
try {
socket.close();
} catch (IOException e) {
ProcessLogger.PROTOCOL_CLIENT_LOGGER.failedToCloseSocket(e);
}
}
}
safeHandleConnection(socket);
} catch (SocketException e) {
if (!stop) {
// we do not log if service is stopped, we assume the exception was caused by closing the
// ServerSocket
ProcessLogger.PROTOCOL_CLIENT_LOGGER.failedToAcceptConnection(e);
}
} catch (IOException e) {
ProcessLogger.PROTOCOL_CLIENT_LOGGER.failedToAcceptConnection(e);
}
}
} finally {
StreamUtils.safeClose(serverSocket);
}
}
});
if (thread == null) {
throw ProcessLogger.ROOT_LOGGER.failedToCreateServerThread();
}
thread.setName("Accept thread");
serverSocket.setReuseAddress(true);
serverSocket.bind(bindAddress, backlog);
boundAddress = (InetSocketAddress) serverSocket.getLocalSocketAddress();
thread.start();
}
public void stop() {
stop = true;
final Thread thread = this.thread;
boundAddress = null;
if (thread != null) {
// thread.interrupt may not actually interrupt socket.accept()
thread.interrupt();
}
StreamUtils.safeClose(serverSocket);
}
private void safeHandleConnection(final Socket socket) {
boolean ok = false;
try {
final ConnectionImpl connection = new ConnectionImpl(socket, MessageHandler.NULL, readExecutor, callback);
connection.setMessageHandler(connectionHandler.handleConnected(connection));
final Thread thread = threadFactory.newThread(connection.getReadTask());
if (thread == null) {
throw ProcessLogger.ROOT_LOGGER.threadCreationRefused();
}
thread.setName("Read thread for " + socket.getRemoteSocketAddress());
thread.start();
ok = true;
} catch (IOException e) {
ProcessLogger.PROTOCOL_CLIENT_LOGGER.failedToHandleIncomingConnection(e);
} finally {
if (! ok) {
try {
socket.close();
} catch (IOException e) {
ProcessLogger.PROTOCOL_CLIENT_LOGGER.failedToCloseSocket(e);
}
}
}
}
public InetSocketAddress getBoundAddress() {
return boundAddress;
}
public static final class Configuration {
private ThreadFactory threadFactory;
private ServerSocketFactory socketFactory;
private ConnectionHandler connectionHandler;
private MessageHandler messageHandler;
private InetSocketAddress bindAddress;
private int backlog;
private int readTimeout;
private Executor readExecutor;
private ClosedCallback closedCallback;
public ThreadFactory getThreadFactory() {
return threadFactory;
}
public void setThreadFactory(final ThreadFactory threadFactory) {
this.threadFactory = threadFactory;
}
public ServerSocketFactory getSocketFactory() {
return socketFactory;
}
public void setSocketFactory(final ServerSocketFactory socketFactory) {
this.socketFactory = socketFactory;
}
public ConnectionHandler getConnectionHandler() {
return connectionHandler;
}
public void setConnectionHandler(final ConnectionHandler connectionHandler) {
this.connectionHandler = connectionHandler;
}
public MessageHandler getMessageHandler() {
return messageHandler;
}
public void setMessageHandler(final MessageHandler messageHandler) {
this.messageHandler = messageHandler;
}
public InetSocketAddress getBindAddress() {
return bindAddress;
}
public void setBindAddress(final InetSocketAddress bindAddress) {
this.bindAddress = bindAddress;
}
public int getBacklog() {
return backlog;
}
public void setBacklog(final int backlog) {
this.backlog = backlog;
}
public int getReadTimeout() {
return readTimeout;
}
public void setReadTimeout(final int readTimeout) {
this.readTimeout = readTimeout;
}
public Executor getReadExecutor() {
return readExecutor;
}
public void setReadExecutor(final Executor readExecutor) {
this.readExecutor = readExecutor;
}
public ClosedCallback getClosedCallback() {
return closedCallback;
}
public void setCallback(ClosedCallback closedCallback) {
this.closedCallback = closedCallback;
}
}
}