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.nio;
018
019import java.io.DataInputStream;
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.openwire.OpenWireFormat;
033import org.apache.activemq.transport.Transport;
034import org.apache.activemq.transport.tcp.TcpTransport;
035import org.apache.activemq.util.IOExceptionSupport;
036import org.apache.activemq.util.ServiceStopper;
037import org.apache.activemq.wireformat.WireFormat;
038
039/**
040 * An implementation of the {@link Transport} interface using raw tcp/ip
041 *
042 *
043 */
044public class NIOTransport extends TcpTransport {
045
046    // private static final Logger log = LoggerFactory.getLogger(NIOTransport.class);
047    protected SocketChannel channel;
048    protected SelectorSelection selection;
049    protected ByteBuffer inputBuffer;
050    protected ByteBuffer currentBuffer;
051    protected int nextFrameSize;
052
053    public NIOTransport(WireFormat wireFormat, SocketFactory socketFactory, URI remoteLocation, URI localLocation) throws UnknownHostException, IOException {
054        super(wireFormat, socketFactory, remoteLocation, localLocation);
055    }
056
057    public NIOTransport(WireFormat wireFormat, Socket socket) throws IOException {
058        super(wireFormat, socket);
059    }
060
061    @Override
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            @Override
069            public void onSelect(SelectorSelection selection) {
070                serviceRead();
071            }
072
073            @Override
074            public void onError(SelectorSelection selection, Throwable error) {
075                if (error instanceof IOException) {
076                    onException((IOException)error);
077                } else {
078                    onException(IOExceptionSupport.create(error));
079                }
080            }
081        });
082
083        // Send the data via the channel
084        // inputBuffer = ByteBuffer.allocateDirect(8*1024);
085        inputBuffer = ByteBuffer.allocateDirect(getIoBufferSize());
086        currentBuffer = inputBuffer;
087        nextFrameSize = -1;
088        currentBuffer.limit(4);
089        NIOOutputStream outPutStream = new NIOOutputStream(channel, getIoBufferSize());
090        this.dataOut = new DataOutputStream(outPutStream);
091        this.buffOut = outPutStream;
092    }
093
094    protected void serviceRead() {
095        try {
096            while (true) {
097
098                int readSize = channel.read(currentBuffer);
099                if (readSize == -1) {
100                    onException(new EOFException());
101                    selection.close();
102                    break;
103                }
104                if (readSize == 0) {
105                    break;
106                }
107
108                this.receiveCounter += readSize;
109                if (currentBuffer.hasRemaining()) {
110                    continue;
111                }
112
113                // Are we trying to figure out the size of the next frame?
114                if (nextFrameSize == -1) {
115                    assert inputBuffer == currentBuffer;
116
117                    // If the frame is too big to fit in our direct byte buffer,
118                    // Then allocate a non direct byte buffer of the right size
119                    // for it.
120                    inputBuffer.flip();
121                    nextFrameSize = inputBuffer.getInt() + 4;
122
123                    if (wireFormat instanceof OpenWireFormat) {
124                        long maxFrameSize = ((OpenWireFormat)wireFormat).getMaxFrameSize();
125                        if (nextFrameSize > maxFrameSize) {
126                            throw new IOException("Frame size of " + (nextFrameSize / (1024 * 1024)) + " MB larger than max allowed " + (maxFrameSize / (1024 * 1024)) + " MB");
127                        }
128                    }
129
130                    if (nextFrameSize > inputBuffer.capacity()) {
131                        currentBuffer = ByteBuffer.allocateDirect(nextFrameSize);
132                        currentBuffer.putInt(nextFrameSize);
133                    } else {
134                        inputBuffer.limit(nextFrameSize);
135                    }
136
137                } else {
138                    currentBuffer.flip();
139
140                    Object command = wireFormat.unmarshal(new DataInputStream(new NIOInputStream(currentBuffer)));
141                    doConsume(command);
142
143                    nextFrameSize = -1;
144                    inputBuffer.clear();
145                    inputBuffer.limit(4);
146                    currentBuffer = inputBuffer;
147                }
148
149            }
150
151        } catch (IOException e) {
152            onException(e);
153        } catch (Throwable e) {
154            onException(IOExceptionSupport.create(e));
155        }
156    }
157
158    @Override
159    protected void doStart() throws Exception {
160        connect();
161        selection.setInterestOps(SelectionKey.OP_READ);
162        selection.enable();
163    }
164
165    @Override
166    protected void doStop(ServiceStopper stopper) throws Exception {
167        if (selection != null) {
168            selection.close();
169            selection = null;
170        }
171        super.doStop(stopper);
172    }
173}