/**
*
* Copyright 2004 Protique Ltd
*
* 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.activemq.transport.tcp;
import EDU.oswego.cs.dl.util.concurrent.BoundedBuffer;
import EDU.oswego.cs.dl.util.concurrent.PooledExecutor;
import EDU.oswego.cs.dl.util.concurrent.SynchronizedBoolean;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.activemq.io.WireFormat;
import org.activemq.transport.TransportServerChannelSupport;
import org.activemq.util.JMSExceptionHelper;
import javax.jms.JMSException;
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.net.UnknownHostException;
/**
* Binds to a well known port and listens for Sockets ...
*
* @version $Revision: 1.1.1.1 $
*/
public class TcpTransportServerChannel extends TransportServerChannelSupport implements Runnable {
private static final Log log = LogFactory.getLog(TcpTransportServerChannel.class);
protected static final int DEFAULT_BACKLOG = 500;
private WireFormat wireFormat;
private Thread serverSocketThread;
private ServerSocket serverSocket;
private SynchronizedBoolean closed;
private SynchronizedBoolean started;
private boolean useAsyncSend = false;
private int maxOutstandingMessages = 10;
private int backlog = DEFAULT_BACKLOG;
/**
* Default Constructor
*
* @param bindAddr
* @throws JMSException
*/
public TcpTransportServerChannel(WireFormat wireFormat, URI bindAddr) throws JMSException {
super(bindAddr);
this.wireFormat = wireFormat;
closed = new SynchronizedBoolean(false);
started = new SynchronizedBoolean(false);
try {
serverSocket = createServerSocket(bindAddr);
serverSocket.setSoTimeout(2000);
updatePhysicalUri(bindAddr);
}
catch (Exception se) {
System.out.println(se);
se.printStackTrace();
throw JMSExceptionHelper.newJMSException("Bind to " + bindAddr + " failed: " + se.getMessage(), se);
}
}
public TcpTransportServerChannel(WireFormat wireFormat, ServerSocket serverSocket) throws JMSException {
super(serverSocket.getInetAddress().toString());
this.wireFormat = wireFormat;
this.serverSocket = serverSocket;
closed = new SynchronizedBoolean(false);
started = new SynchronizedBoolean(false);
InetAddress address = serverSocket.getInetAddress();
try {
updatePhysicalUri(new URI("tcp", "", address.getHostName(), 0, "", "", ""));
}
catch (URISyntaxException e) {
throw JMSExceptionHelper.newJMSException("Failed to extract URI: : " + e.getMessage(), e);
}
}
public void start() throws JMSException {
super.start();
if (started.commit(false, true)) {
log.info("Listening for connections at: " + getUrl());
serverSocketThread = new Thread(this, toString());
serverSocketThread.setDaemon(true);
serverSocketThread.start();
}
}
public void stop() throws JMSException {
if (closed.commit(false, true)) {
super.stop();
try {
if (serverSocket != null) {
serverSocket.close();
serverSocketThread.join();
serverSocketThread = null;
}
}
catch (Throwable e) {
throw JMSExceptionHelper.newJMSException("Failed to stop: " + e, e);
}
}
}
/**
* @return pretty print of this
*/
public String toString() {
return "TcpTransportServerChannel@" + getUrl();
}
/**
* pull Sockets from the ServerSocket
*/
public void run() {
while (!closed.get()) {
Socket socket = null;
try {
socket = serverSocket.accept();
if (socket != null) {
if (closed.get()) {
socket.close();
}
else {
// have thread per channel for sending messages and a thread for receiving them
PooledExecutor executor = null;
if (useAsyncSend) {
executor = new PooledExecutor(new BoundedBuffer(maxOutstandingMessages), 1);
executor.waitWhenBlocked();
executor.setKeepAliveTime(1000);
}
TcpTransportChannel channel = createTransportChannel(socket, executor);
addClient(channel);
}
}
}
catch (SocketTimeoutException ste) {
//expect this to happen
}
catch (Throwable e) {
if (!closed.get()) {
log.warn("run()", e);
}
}
}
}
protected TcpTransportChannel createTransportChannel(Socket socket, PooledExecutor executor) throws JMSException {
TcpTransportChannel channel = new TcpTransportChannel(this,wireFormat.copy(), socket, executor);
return channel;
}
// Properties
//-------------------------------------------------------------------------
public boolean isUseAsyncSend() {
return useAsyncSend;
}
public void setUseAsyncSend(boolean useAsyncSend) {
this.useAsyncSend = useAsyncSend;
}
public int getMaxOutstandingMessages() {
return maxOutstandingMessages;
}
public void setMaxOutstandingMessages(int maxOutstandingMessages) {
this.maxOutstandingMessages = maxOutstandingMessages;
}
public int getBacklog() {
return backlog;
}
public void setBacklog(int backlog) {
this.backlog = backlog;
}
public WireFormat getWireFormat() {
return wireFormat;
}
public void setWireFormat(WireFormat wireFormat) {
this.wireFormat = wireFormat;
}
// Implementation methods
//-------------------------------------------------------------------------
/**
* In cases where we construct ourselves with a zero port we need to regenerate the URI with the real physical port
* so that people can connect to us via discovery
*/
protected void updatePhysicalUri(URI bindAddr) throws URISyntaxException {
URI newURI = new URI(bindAddr.getScheme(), bindAddr.getUserInfo(), resolveHostName(bindAddr.getHost()), serverSocket
.getLocalPort(), bindAddr.getPath(), bindAddr.getQuery(), bindAddr.getFragment());
setUrl(newURI.toString());
}
/**
* Factory method to create a new ServerSocket
*
* @throws UnknownHostException
* @throws IOException
*/
protected ServerSocket createServerSocket(URI bind) throws UnknownHostException, IOException {
ServerSocket answer = null;
String host = bind.getHost();
host = (host == null || host.length() == 0) ? "localhost" : host;
InetAddress addr = InetAddress.getByName(host);
if (host.trim().equals("localhost") || addr.equals(InetAddress.getLocalHost())) {
answer = new ServerSocket(bind.getPort(), backlog);
}
else {
answer = new ServerSocket(bind.getPort(), backlog, addr);
}
return answer;
}
}