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    /**
062     * @param format
063     * @param socket
064     * @param initBuffer
065     * @throws IOException
066     */
067    public NIOTransport(WireFormat format, Socket socket, InitBuffer initBuffer) throws IOException {
068        super(format, socket, initBuffer);
069    }
070
071    @Override
072    protected void initializeStreams() throws IOException {
073        channel = socket.getChannel();
074        channel.configureBlocking(false);
075
076        // listen for events telling us when the socket is readable.
077        selection = SelectorManager.getInstance().register(channel, new SelectorManager.Listener() {
078            @Override
079            public void onSelect(SelectorSelection selection) {
080                serviceRead();
081            }
082
083            @Override
084            public void onError(SelectorSelection selection, Throwable error) {
085                if (error instanceof IOException) {
086                    onException((IOException)error);
087                } else {
088                    onException(IOExceptionSupport.create(error));
089                }
090            }
091        });
092
093        // Send the data via the channel
094        // inputBuffer = ByteBuffer.allocateDirect(8*1024);
095        inputBuffer = ByteBuffer.allocateDirect(getIoBufferSize());
096        currentBuffer = inputBuffer;
097        nextFrameSize = -1;
098        currentBuffer.limit(4);
099        NIOOutputStream outPutStream = new NIOOutputStream(channel, getIoBufferSize());
100        this.dataOut = new DataOutputStream(outPutStream);
101        this.buffOut = outPutStream;
102    }
103
104    protected int readFromBuffer() throws IOException {
105        return channel.read(currentBuffer);
106    }
107
108    protected void serviceRead() {
109        try {
110            while (true) {
111                //If the transport was already stopped then break
112                if (this.isStopped()) {
113                    return;
114                }
115
116                int readSize = readFromBuffer();
117                if (readSize == -1) {
118                    onException(new EOFException());
119                    selection.close();
120                    break;
121                }
122                if (readSize == 0) {
123                    break;
124                }
125
126                this.receiveCounter += readSize;
127                if (currentBuffer.hasRemaining()) {
128                    continue;
129                }
130
131                // Are we trying to figure out the size of the next frame?
132                if (nextFrameSize == -1) {
133                    assert inputBuffer == currentBuffer;
134
135                    // If the frame is too big to fit in our direct byte buffer,
136                    // Then allocate a non direct byte buffer of the right size
137                    // for it.
138                    inputBuffer.flip();
139                    nextFrameSize = inputBuffer.getInt() + 4;
140
141                    if (wireFormat instanceof OpenWireFormat) {
142                        long maxFrameSize = ((OpenWireFormat)wireFormat).getMaxFrameSize();
143                        if (nextFrameSize > maxFrameSize) {
144                            throw new IOException("Frame size of " + (nextFrameSize / (1024 * 1024)) + " MB larger than max allowed " + (maxFrameSize / (1024 * 1024)) + " MB");
145                        }
146                    }
147
148                    if (nextFrameSize > inputBuffer.capacity()) {
149                        currentBuffer = ByteBuffer.allocateDirect(nextFrameSize);
150                        currentBuffer.putInt(nextFrameSize);
151                    } else {
152                        inputBuffer.limit(nextFrameSize);
153                    }
154
155                } else {
156                    currentBuffer.flip();
157
158                    Object command = wireFormat.unmarshal(new DataInputStream(new NIOInputStream(currentBuffer)));
159                    doConsume(command);
160
161                    nextFrameSize = -1;
162                    inputBuffer.clear();
163                    inputBuffer.limit(4);
164                    currentBuffer = inputBuffer;
165                }
166
167            }
168
169        } catch (IOException e) {
170            onException(e);
171        } catch (Throwable e) {
172            onException(IOExceptionSupport.create(e));
173        }
174    }
175
176    @Override
177    protected void doStart() throws Exception {
178        connect();
179        selection.setInterestOps(SelectionKey.OP_READ);
180        selection.enable();
181    }
182
183    @Override
184    protected void doStop(ServiceStopper stopper) throws Exception {
185        if (selection != null) {
186            selection.close();
187            selection = null;
188        }
189        super.doStop(stopper);
190    }
191}