/** * * 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.DataInputStream; import java.io.DataOutputStream; import java.io.EOFException; import java.io.IOException; import java.lang.reflect.InvocationTargetException; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.Socket; import java.net.URI; import java.net.URISyntaxException; import java.util.concurrent.atomic.AtomicBoolean; import javax.jms.JMSException; import javax.net.SocketFactory; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.codehaus.stomp.ProtocolException; import org.codehaus.stomp.Stomp; import org.codehaus.stomp.StompFrame; import org.codehaus.stomp.StompMarshaller; import org.codehaus.stomp.jms.ProtocolConverter; /** * @version $Revision: 65 $ */ public class TcpTransport implements Runnable { private static final Log log = LogFactory.getLog(TcpTransport.class); private StompMarshaller marshaller = new StompMarshaller(); private ProtocolConverter inputHandler; private final URI remoteLocation; private final URI localLocation; private int connectionTimeout = 30000; private Socket socket; private DataOutputStream dataOut; private DataInputStream dataIn; private SocketFactory socketFactory; private Thread runner; private AtomicBoolean started = new AtomicBoolean(false); private AtomicBoolean stopped = new AtomicBoolean(false); /** * Initialize from a server Socket */ public TcpTransport(Socket socket) throws IOException { this.socket = socket; this.remoteLocation = null; this.localLocation = null; } /** * A one way asynchronous send * * @throws IOException */ public void onStompFrame(StompFrame command) throws IOException { if (!started.get() || stopped.get()) { throw new ProtocolException("The transport is not running."); } marshaller.marshal(command, dataOut); dataOut.flush(); log.debug("Flushed message to server side: remote - " + socket.getPort() + " local - " + socket.getLocalPort()); } /** * @return pretty print of 'this' */ public String toString() { return "tcp://" + socket.getInetAddress() + ":" + socket.getPort(); } /** * reads packets from a Socket */ public void run() { log.trace("StompConnect TCP consumer thread starting"); while (!stopped.get()) { try { StompFrame frame = marshaller.unmarshal(dataIn); log.debug("Sending stomp frame"); try { inputHandler.onStompFrame(frame); } catch (IOException e) { if (frame.getAction().equals(Stomp.Responses.ERROR)) { log.warn("Could not send frame to client: " + new String(frame.getContent())); } throw e; } } catch (Throwable e) { // no need to log EOF exceptions if (e instanceof EOFException) { // Happens when the remote side disconnects log.debug("Caught an EOFException: " + e.getMessage(), e); } else { log.fatal("Caught an exception: " + e.getMessage(), e); } try { stop(); } catch (Exception e2) { log.warn("Caught while closing: " + e2 + ". Now Closed", e2); } } } } public void setProtocolConverter(ProtocolConverter protocolConverter) { this.inputHandler = protocolConverter; } public void start() throws IOException, URISyntaxException, IllegalArgumentException, IllegalAccessException, InvocationTargetException { if (started.compareAndSet(false, true)) { connect(); runner = new Thread(this, "StompConnect Transport: " + toString()); runner.setDaemon(true); runner.start(); } } public void stop() throws InterruptedException, IOException, JMSException, URISyntaxException { if (stopped.compareAndSet(false, true)) { try { if (log.isDebugEnabled()) { log.debug("Stopping transport " + this); } if (inputHandler != null) { inputHandler.close(); } socket.close(); } finally { stopped.set(true); started.set(false); } } } protected void connect() throws IOException, IllegalArgumentException, IllegalAccessException, InvocationTargetException, URISyntaxException { if (socket == null && socketFactory == null) { throw new IllegalStateException("Cannot connect if the socket or socketFactory have not been set"); } InetSocketAddress localAddress = null; InetSocketAddress remoteAddress = null; if (localLocation != null) { localAddress = new InetSocketAddress(InetAddress.getByName(localLocation.getHost()), localLocation.getPort()); } if (remoteLocation != null) { String host = remoteLocation.getHost(); remoteAddress = new InetSocketAddress(host, remoteLocation.getPort()); } if (socket != null) { if (localAddress != null) { socket.bind(localAddress); } // If it's a server accepted socket.. we don't need to connect it // to a remote address. if (remoteAddress != null) { if (connectionTimeout >= 0) { socket.connect(remoteAddress, connectionTimeout); } else { socket.connect(remoteAddress); } } } else { // For SSL sockets.. you can't create an unconnected socket :( // This means the timout option are not supported either. if (localAddress != null) { socket = socketFactory.createSocket(remoteAddress.getAddress(), remoteAddress.getPort(), localAddress.getAddress(), localAddress.getPort()); } else { socket = socketFactory.createSocket(remoteAddress.getAddress(), remoteAddress.getPort()); } } // TcpBufferedInputStream buffIn = new TcpBufferedInputStream(socket.getInputStream(), ioBufferSize); this.dataIn = new DataInputStream(socket.getInputStream());// new DataInputStream(buffIn); // TcpBufferedOutputStream buffOut = new TcpBufferedOutputStream(socket.getOutputStream(), ioBufferSize); this.dataOut = new DataOutputStream(socket.getOutputStream());// new DataOutputStream(buffOut); } }