Better management of shared resources between the background run thread
and the main start / stop thread.  Makes sure to cleanup all resources
before finally throwing on stop to prevent leaking and resources.
This commit is contained in:
Timothy Bish 2016-05-13 11:13:11 -04:00
parent d7b5a62bb0
commit ff99872263
1 changed files with 131 additions and 81 deletions

View File

@ -26,6 +26,7 @@ import java.net.SocketTimeoutException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.UnknownHostException;
import java.nio.channels.ClosedChannelException;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
@ -50,8 +51,6 @@ import org.apache.activemq.transport.Transport;
import org.apache.activemq.transport.TransportFactory;
import org.apache.activemq.transport.TransportServer;
import org.apache.activemq.transport.TransportServerThreadSupport;
import org.apache.activemq.transport.nio.SelectorManager;
import org.apache.activemq.transport.nio.SelectorSelection;
import org.apache.activemq.util.IOExceptionSupport;
import org.apache.activemq.util.InetAddressUtil;
import org.apache.activemq.util.IntrospectionSupport;
@ -69,8 +68,9 @@ import org.slf4j.LoggerFactory;
public class TcpTransportServer extends TransportServerThreadSupport implements ServiceListener {
private static final Logger LOG = LoggerFactory.getLogger(TcpTransportServer.class);
protected ServerSocket serverSocket;
protected Selector selector;
protected volatile ServerSocket serverSocket;
protected volatile Selector selector;
protected int backlog = 5000;
protected WireFormatFactory wireFormatFactory = new OpenWireFormatFactory();
protected final TcpTransportFactory transportFactory;
@ -113,14 +113,14 @@ public class TcpTransportServer extends TransportServerThreadSupport implements
*/
protected boolean startLogging = true;
protected final ServerSocketFactory serverSocketFactory;
protected BlockingQueue<Socket> socketQueue = new LinkedBlockingQueue<Socket>();
protected final BlockingQueue<Socket> socketQueue = new LinkedBlockingQueue<Socket>();
protected Thread socketHandlerThread;
/**
* The maximum number of sockets allowed for this server
*/
protected int maximumConnections = Integer.MAX_VALUE;
protected AtomicInteger currentTransportCount = new AtomicInteger();
protected final AtomicInteger currentTransportCount = new AtomicInteger();
public TcpTransportServer(TcpTransportFactory transportFactory, URI location, ServerSocketFactory serverSocketFactory) throws IOException,
URISyntaxException {
@ -137,8 +137,8 @@ public class TcpTransportServer extends TransportServerThreadSupport implements
InetAddress addr = InetAddress.getByName(host);
try {
this.serverSocket = serverSocketFactory.createServerSocket(bind.getPort(), backlog, addr);
configureServerSocket(this.serverSocket);
serverSocket = serverSocketFactory.createServerSocket(bind.getPort(), backlog, addr);
configureServerSocket(serverSocket);
} catch (IOException e) {
throw IOExceptionSupport.create("Failed to bind to server socket: " + bind + " due to: " + e, e);
}
@ -146,7 +146,6 @@ public class TcpTransportServer extends TransportServerThreadSupport implements
setConnectURI(new URI(bind.getScheme(), bind.getUserInfo(), resolveHostName(serverSocket, addr), serverSocket.getLocalPort(), bind.getPath(),
bind.getQuery(), bind.getFragment()));
} catch (URISyntaxException e) {
// it could be that the host name contains invalid characters such
// as _ on unix platforms so lets try use the IP address instead
try {
@ -302,87 +301,114 @@ public class TcpTransportServer extends TransportServerThreadSupport implements
*/
@Override
public void run() {
final ServerSocketChannel chan = serverSocket.getChannel();
if (chan != null) {
if (!isStopped() && !isStopping()) {
final ServerSocket serverSocket = this.serverSocket;
if (serverSocket == null) {
onAcceptError(new IOException("Server started without a valid ServerSocket"));
}
final ServerSocketChannel channel = serverSocket.getChannel();
if (channel != null) {
doRunWithServerSocketChannel(channel);
} else {
doRunWithServerSocket(serverSocket);
}
}
}
private void doRunWithServerSocketChannel(final ServerSocketChannel channel) {
try {
channel.configureBlocking(false);
final Selector selector = Selector.open();
try {
chan.configureBlocking(false);
selector = Selector.open();
chan.register(selector, SelectionKey.OP_ACCEPT);
while (!isStopped()) {
int count = selector.select(10);
channel.register(selector, SelectionKey.OP_ACCEPT);
} catch (ClosedChannelException ex) {
try {
selector.close();
} catch (IOException ignore) {}
if (count == 0) {
continue;
}
throw ex;
}
Set<SelectionKey> keys = selector.selectedKeys();
// Update object instance for later cleanup.
this.selector = selector;
for (Iterator<SelectionKey> i = keys.iterator(); i.hasNext(); ) {
final SelectionKey key = i.next();
if (key.isAcceptable()) {
try {
SocketChannel sc = chan.accept();
if (sc != null) {
if (isStopped() || getAcceptListener() == null) {
sc.close();
while (!isStopped()) {
int count = selector.select(10);
if (count == 0) {
continue;
}
Set<SelectionKey> keys = selector.selectedKeys();
for (Iterator<SelectionKey> i = keys.iterator(); i.hasNext(); ) {
final SelectionKey key = i.next();
if (key.isAcceptable()) {
try {
SocketChannel sc = channel.accept();
if (sc != null) {
if (isStopped() || getAcceptListener() == null) {
sc.close();
} else {
if (useQueueForAccept) {
socketQueue.put(sc.socket());
} else {
if (useQueueForAccept) {
socketQueue.put(sc.socket());
} else {
handleSocket(sc.socket());
}
handleSocket(sc.socket());
}
}
}
} catch (SocketTimeoutException ste) {
// expect this to happen
} catch (Exception e) {
e.printStackTrace();
if (!isStopping()) {
onAcceptError(e);
} else if (!isStopped()) {
LOG.warn("run()", e);
onAcceptError(e);
}
} catch (SocketTimeoutException ste) {
// expect this to happen
} catch (Exception e) {
e.printStackTrace();
if (!isStopping()) {
onAcceptError(e);
} else if (!isStopped()) {
LOG.warn("run()", e);
onAcceptError(e);
}
}
i.remove();
}
}
} catch (IOException ex) {
if (selector != null) {
try {
selector.close();
} catch (IOException ioe) {}
selector = null;
i.remove();
}
}
} else {
while (!isStopped()) {
Socket socket = null;
try {
socket = serverSocket.accept();
if (socket != null) {
if (isStopped() || getAcceptListener() == null) {
socket.close();
} catch (IOException ex) {
if (!isStopping()) {
onAcceptError(ex);
} else if (!isStopped()) {
LOG.warn("run()", ex);
onAcceptError(ex);
}
}
}
private void doRunWithServerSocket(final ServerSocket serverSocket) {
while (!isStopped()) {
Socket socket = null;
try {
socket = serverSocket.accept();
if (socket != null) {
if (isStopped() || getAcceptListener() == null) {
socket.close();
} else {
if (useQueueForAccept) {
socketQueue.put(socket);
} else {
if (useQueueForAccept) {
socketQueue.put(socket);
} else {
handleSocket(socket);
}
handleSocket(socket);
}
}
} catch (SocketTimeoutException ste) {
// expect this to happen
} catch (Exception e) {
if (!isStopping()) {
onAcceptError(e);
} else if (!isStopped()) {
LOG.warn("run()", e);
onAcceptError(e);
}
}
} catch (SocketTimeoutException ste) {
// expect this to happen
} catch (Exception e) {
if (!isStopping()) {
onAcceptError(e);
} else if (!isStopped()) {
LOG.warn("run()", e);
onAcceptError(e);
}
}
}
@ -472,19 +498,43 @@ public class TcpTransportServer extends TransportServerThreadSupport implements
@Override
protected void doStop(ServiceStopper stopper) throws Exception {
if (selector != null) {
selector.close();
selector = null;
Exception firstFailure = null;
try {
final Selector selector = this.selector;
if (selector != null) {
this.selector = null;
selector.close();
}
} catch (Exception error) {
}
if (serverSocket != null) {
serverSocket.close();
serverSocket = null;
try {
final ServerSocket serverSocket = this.serverSocket;
if (serverSocket != null) {
this.serverSocket = null;
serverSocket.close();
}
} catch (Exception error) {
firstFailure = error;
}
if (socketHandlerThread != null) {
socketHandlerThread.interrupt();
socketHandlerThread = null;
}
super.doStop(stopper);
try {
super.doStop(stopper);
} catch (Exception error) {
if (firstFailure != null) {
firstFailure = error;
}
}
if (firstFailure != null) {
throw firstFailure;
}
}
@Override