/**
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.codehaus.stomp.tcp;
import java.io.IOException;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketTimeoutException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.atomic.AtomicBoolean;
import javax.jms.JMSException;
import javax.net.ServerSocketFactory;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.codehaus.stomp.jms.StompConnect;
/**
* @version $Revision: 52 $
*/
public class TcpTransportServer implements Runnable {
private static final Log log = LogFactory.getLog(TcpTransportServer.class);
private StompConnect stompHandlerFactory;
private ServerSocket serverSocket;
private int backlog = 5000;
private ServerSocketFactory serverSocketFactory;
private boolean daemon = true;
private Thread runner;
private URI connectURI;
private URI bindLocation;
private List<TcpTransport> connections = new CopyOnWriteArrayList<TcpTransport>();
private AtomicBoolean stopped = new AtomicBoolean(false);
public TcpTransportServer(StompConnect stompHandlerFactory, URI location, ServerSocketFactory serverSocketFactory)
throws IOException, URISyntaxException {
this.stompHandlerFactory = stompHandlerFactory;
this.connectURI = location;
this.bindLocation = location;
this.serverSocketFactory = serverSocketFactory;
}
/**
* @return pretty print of this
*/
public String toString() {
return bindLocation.toString();
}
/**
* pull Sockets from the ServerSocket
*/
public void run() {
while (!stopped.get()) {
Socket socket = null;
try {
socket = serverSocket.accept();
if (socket != null) {
if (stopped.get()) {
socket.close();
} else {
TcpTransport transport = new TcpTransport(socket);
stompHandlerFactory.assignProtocolConverter(transport);
transport.start();
connections.add(transport);
}
}
} catch (SocketTimeoutException ste) {
// expect this to happen
} catch (Exception e) {
if (!stopped.get()) {
log.error("Received accept error: " + e, e);
try {
stop();
} catch (Exception e1) {
log.error("Failed to shut down: " + e, e);
}
}
}
}
}
public void start() throws IOException {
URI bind = bindLocation;
String host = bind.getHost();
host = (host == null || host.length() == 0) ? "localhost" : host;
InetAddress addr = InetAddress.getByName(host);
try {
this.serverSocket = serverSocketFactory.createServerSocket(bind.getPort(), backlog, addr);
this.serverSocket.setSoTimeout(2000);
} catch (IOException e) {
throw new IOException("Failed to bind to server socket: " + bind + " due to: " + e, e);
}
try {
connectURI = new URI(bind.getScheme(), bind.getUserInfo(), bind.getHost(), serverSocket.getLocalPort(),
bind.getPath(), bind.getQuery(), bind.getFragment());
} catch (URISyntaxException e) {
throw new IOException(e.getMessage(), e);
}
log.info("Listening for connections at: " + connectURI);
runner = new Thread(this, "StompConnect Server Thread: " + toString());
runner.setDaemon(daemon);
runner.start();
}
public void stop() throws InterruptedException, IOException, JMSException, URISyntaxException {
stopped.set(true);
// lets stop accepting new connections first
if (serverSocket != null) {
serverSocket.close();
}
// now lets close all the connections
try {
for (TcpTransport connection : connections) {
connection.stop();
}
} finally {
connections.clear();
}
// lets join the server thread in case its blocked a little while
if (runner != null) {
log.debug("Attempting to join with runner");
runner.join();
log.debug("Joined with runner");
runner = null;
}
}
}