org.apache.activemq.transport.nio.NIOTransport Maven / Gradle / Ivy
/**
* 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.apache.activemq.transport.nio;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.EOFException;
import java.io.IOException;
import java.net.Socket;
import java.net.URI;
import java.net.UnknownHostException;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.SocketChannel;
import javax.net.SocketFactory;
import org.apache.activemq.MaxFrameSizeExceededException;
import org.apache.activemq.openwire.OpenWireFormat;
import org.apache.activemq.transport.Transport;
import org.apache.activemq.transport.tcp.TcpTransport;
import org.apache.activemq.util.IOExceptionSupport;
import org.apache.activemq.util.ServiceStopper;
import org.apache.activemq.wireformat.WireFormat;
/**
* An implementation of the {@link Transport} interface using raw tcp/ip
*
*
*/
public class NIOTransport extends TcpTransport {
// private static final Logger log = LoggerFactory.getLogger(NIOTransport.class);
protected SocketChannel channel;
protected SelectorSelection selection;
protected ByteBuffer inputBuffer;
protected ByteBuffer currentBuffer;
protected int nextFrameSize;
public NIOTransport(WireFormat wireFormat, SocketFactory socketFactory, URI remoteLocation, URI localLocation) throws UnknownHostException, IOException {
super(wireFormat, socketFactory, remoteLocation, localLocation);
}
public NIOTransport(WireFormat wireFormat, Socket socket) throws IOException {
super(wireFormat, socket);
}
/**
* @param format
* @param socket
* @param initBuffer
* @throws IOException
*/
public NIOTransport(WireFormat format, Socket socket, InitBuffer initBuffer) throws IOException {
super(format, socket, initBuffer);
}
@Override
protected void initializeStreams() throws IOException {
channel = socket.getChannel();
channel.configureBlocking(false);
// listen for events telling us when the socket is readable.
selection = SelectorManager.getInstance().register(channel, new SelectorManager.Listener() {
@Override
public void onSelect(SelectorSelection selection) {
serviceRead();
}
@Override
public void onError(SelectorSelection selection, Throwable error) {
if (error instanceof IOException) {
onException((IOException)error);
} else {
onException(IOExceptionSupport.create(error));
}
}
});
// Send the data via the channel
// inputBuffer = ByteBuffer.allocateDirect(8*1024);
inputBuffer = ByteBuffer.allocateDirect(getIoBufferSize());
currentBuffer = inputBuffer;
nextFrameSize = -1;
currentBuffer.limit(4);
NIOOutputStream outPutStream = new NIOOutputStream(channel, getIoBufferSize());
this.dataOut = new DataOutputStream(outPutStream);
this.buffOut = outPutStream;
}
protected int readFromBuffer() throws IOException {
return channel.read(currentBuffer);
}
protected void serviceRead() {
try {
while (true) {
//If the transport was already stopped then break
if (this.isStopped()) {
return;
}
int readSize = readFromBuffer();
if (readSize == -1) {
onException(new EOFException());
selection.close();
break;
}
if (readSize == 0) {
break;
}
this.receiveCounter += readSize;
if (currentBuffer.hasRemaining()) {
continue;
}
// Are we trying to figure out the size of the next frame?
if (nextFrameSize == -1) {
assert inputBuffer == currentBuffer;
// If the frame is too big to fit in our direct byte buffer,
// Then allocate a non direct byte buffer of the right size
// for it.
inputBuffer.flip();
nextFrameSize = inputBuffer.getInt() + 4;
if (wireFormat instanceof OpenWireFormat) {
OpenWireFormat openWireFormat = (OpenWireFormat)wireFormat;
long maxFrameSize = openWireFormat.getMaxFrameSize();
if (openWireFormat.isMaxFrameSizeEnabled() && nextFrameSize > maxFrameSize) {
throw new MaxFrameSizeExceededException("Frame size of " + (nextFrameSize / (1024 * 1024)) + " MB larger than max allowed " + (maxFrameSize / (1024 * 1024)) + " MB");
}
}
if (nextFrameSize > inputBuffer.capacity()) {
currentBuffer = ByteBuffer.allocateDirect(nextFrameSize);
currentBuffer.putInt(nextFrameSize);
} else {
inputBuffer.limit(nextFrameSize);
}
} else {
currentBuffer.flip();
Object command = wireFormat.unmarshal(new DataInputStream(new NIOInputStream(currentBuffer)));
doConsume(command);
nextFrameSize = -1;
inputBuffer.clear();
inputBuffer.limit(4);
currentBuffer = inputBuffer;
}
}
} catch (IOException e) {
onException(e);
} catch (Throwable e) {
onException(IOExceptionSupport.create(e));
}
}
@Override
protected void doStart() throws Exception {
connect();
selection.setInterestOps(SelectionKey.OP_READ);
selection.enable();
}
@Override
protected void doStop(ServiceStopper stopper) throws Exception {
if (selection != null) {
selection.close();
selection = null;
}
super.doStop(stopper);
}
}