001/**
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.activemq.transport.stomp;
018
019import java.io.ByteArrayInputStream;
020import java.io.DataOutputStream;
021import java.io.EOFException;
022import java.io.IOException;
023import java.net.Socket;
024import java.net.URI;
025import java.net.UnknownHostException;
026import java.nio.ByteBuffer;
027import java.nio.channels.SelectionKey;
028import java.nio.channels.SocketChannel;
029
030import javax.net.SocketFactory;
031
032import org.apache.activemq.transport.Transport;
033import org.apache.activemq.transport.nio.NIOOutputStream;
034import org.apache.activemq.transport.nio.SelectorManager;
035import org.apache.activemq.transport.nio.SelectorSelection;
036import org.apache.activemq.transport.tcp.TcpTransport;
037import org.apache.activemq.util.IOExceptionSupport;
038import org.apache.activemq.util.ServiceStopper;
039import org.apache.activemq.wireformat.WireFormat;
040
041/**
042 * An implementation of the {@link Transport} interface for using Stomp over NIO
043 *
044 *
045 */
046public class StompNIOTransport extends TcpTransport {
047
048    private SocketChannel channel;
049    private SelectorSelection selection;
050
051    private ByteBuffer inputBuffer;
052    StompCodec codec;
053
054    public StompNIOTransport(WireFormat wireFormat, SocketFactory socketFactory, URI remoteLocation, URI localLocation) throws UnknownHostException, IOException {
055        super(wireFormat, socketFactory, remoteLocation, localLocation);
056    }
057
058    public StompNIOTransport(WireFormat wireFormat, Socket socket) throws IOException {
059        super(wireFormat, socket);
060    }
061
062    protected void initializeStreams() throws IOException {
063        channel = socket.getChannel();
064        channel.configureBlocking(false);
065
066        // listen for events telling us when the socket is readable.
067        selection = SelectorManager.getInstance().register(channel, new SelectorManager.Listener() {
068            public void onSelect(SelectorSelection selection) {
069                serviceRead();
070            }
071
072            public void onError(SelectorSelection selection, Throwable error) {
073                if (error instanceof IOException) {
074                    onException((IOException)error);
075                } else {
076                    onException(IOExceptionSupport.create(error));
077                }
078            }
079        });
080
081        inputBuffer = ByteBuffer.allocate(8 * 1024);
082        NIOOutputStream outPutStream = new NIOOutputStream(channel, 8 * 1024);
083        this.dataOut = new DataOutputStream(outPutStream);
084        this.buffOut = outPutStream;
085        codec = new StompCodec(this);
086    }
087
088    private void serviceRead() {
089        try {
090
091           while (true) {
092               // read channel
093               int readSize = channel.read(inputBuffer);
094               // channel is closed, cleanup
095               if (readSize == -1) {
096                   onException(new EOFException());
097                   selection.close();
098                   break;
099               }
100
101               // nothing more to read, break
102               if (readSize == 0) {
103                   break;
104               }
105
106               receiveCounter += readSize;
107
108               inputBuffer.flip();
109
110               ByteArrayInputStream input = new ByteArrayInputStream(inputBuffer.array());
111               codec.parse(input, readSize);
112
113               // clear the buffer
114               inputBuffer.clear();
115           }
116        } catch (IOException e) {
117            onException(e);
118        } catch (Throwable e) {
119            onException(IOExceptionSupport.create(e));
120        }
121    }
122
123    protected void doStart() throws Exception {
124        connect();
125        selection.setInterestOps(SelectionKey.OP_READ);
126        selection.enable();
127    }
128
129    protected void doStop(ServiceStopper stopper) throws Exception {
130        try {
131            if (selection != null) {
132                selection.close();
133            }
134        } finally {
135            super.doStop(stopper);
136        }
137    }
138}