org.apache.activemq.transport.udp.CommandDatagramSocket Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of activemq-osgi Show documentation
Show all versions of activemq-osgi Show documentation
Puts together an ActiveMQ OSGi bundle
/**
* 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.udp;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketAddress;
import org.apache.activemq.command.Command;
import org.apache.activemq.command.Endpoint;
import org.apache.activemq.command.LastPartialCommand;
import org.apache.activemq.command.PartialCommand;
import org.apache.activemq.openwire.BooleanStream;
import org.apache.activemq.openwire.OpenWireFormat;
import org.apache.activemq.transport.reliable.ReplayBuffer;
import org.apache.activemq.util.ByteArrayInputStream;
import org.apache.activemq.util.ByteArrayOutputStream;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* A strategy for reading datagrams and de-fragmenting them together.
*
*
*/
public class CommandDatagramSocket extends CommandChannelSupport {
private static final Logger LOG = LoggerFactory.getLogger(CommandDatagramSocket.class);
private DatagramSocket channel;
private Object readLock = new Object();
private Object writeLock = new Object();
private volatile int receiveCounter;
public CommandDatagramSocket(UdpTransport transport, OpenWireFormat wireFormat, int datagramSize, SocketAddress targetAddress, DatagramHeaderMarshaller headerMarshaller,
DatagramSocket channel) {
super(transport, wireFormat, datagramSize, targetAddress, headerMarshaller);
this.channel = channel;
}
public void start() throws Exception {
}
public void stop() throws Exception {
}
public Command read() throws IOException {
Command answer = null;
Endpoint from = null;
synchronized (readLock) {
while (true) {
DatagramPacket datagram = createDatagramPacket();
channel.receive(datagram);
// TODO could use a DataInput implementation that talks direct
// to the byte[] to avoid object allocation
receiveCounter++;
DataInputStream dataIn = new DataInputStream(new ByteArrayInputStream(datagram.getData(), 0, datagram.getLength()));
from = headerMarshaller.createEndpoint(datagram, dataIn);
answer = (Command)wireFormat.unmarshal(dataIn);
break;
}
}
if (answer != null) {
answer.setFrom(from);
if (LOG.isDebugEnabled()) {
LOG.debug("Channel: " + name + " about to process: " + answer);
}
}
return answer;
}
public void write(Command command, SocketAddress address) throws IOException {
synchronized (writeLock) {
ByteArrayOutputStream writeBuffer = createByteArrayOutputStream();
DataOutputStream dataOut = new DataOutputStream(writeBuffer);
headerMarshaller.writeHeader(command, dataOut);
int offset = writeBuffer.size();
wireFormat.marshal(command, dataOut);
if (remaining(writeBuffer) >= 0) {
sendWriteBuffer(address, writeBuffer, command.getCommandId());
} else {
// lets split the command up into chunks
byte[] data = writeBuffer.toByteArray();
boolean lastFragment = false;
int length = data.length;
for (int fragment = 0; !lastFragment; fragment++) {
writeBuffer = createByteArrayOutputStream();
headerMarshaller.writeHeader(command, dataOut);
int chunkSize = remaining(writeBuffer);
// we need to remove the amount of overhead to write the
// partial command
// lets write the flags in there
BooleanStream bs = null;
if (wireFormat.isTightEncodingEnabled()) {
bs = new BooleanStream();
bs.writeBoolean(true); // the partial data byte[] is
// never null
}
// lets remove the header of the partial command
// which is the byte for the type and an int for the size of
// the byte[]
// data type + the command ID + size of the partial data
chunkSize -= 1 + 4 + 4;
// the boolean flags
if (bs != null) {
chunkSize -= bs.marshalledSize();
} else {
chunkSize -= 1;
}
if (!wireFormat.isSizePrefixDisabled()) {
// lets write the size of the command buffer
dataOut.writeInt(chunkSize);
chunkSize -= 4;
}
lastFragment = offset + chunkSize >= length;
if (chunkSize + offset > length) {
chunkSize = length - offset;
}
if (lastFragment) {
dataOut.write(LastPartialCommand.DATA_STRUCTURE_TYPE);
} else {
dataOut.write(PartialCommand.DATA_STRUCTURE_TYPE);
}
if (bs != null) {
bs.marshal(dataOut);
}
int commandId = command.getCommandId();
if (fragment > 0) {
commandId = sequenceGenerator.getNextSequenceId();
}
dataOut.writeInt(commandId);
if (bs == null) {
dataOut.write((byte)1);
}
// size of byte array
dataOut.writeInt(chunkSize);
// now the data
dataOut.write(data, offset, chunkSize);
offset += chunkSize;
sendWriteBuffer(address, writeBuffer, commandId);
}
}
}
}
public int getDatagramSize() {
return datagramSize;
}
public void setDatagramSize(int datagramSize) {
this.datagramSize = datagramSize;
}
// Implementation methods
// -------------------------------------------------------------------------
protected void sendWriteBuffer(SocketAddress address, ByteArrayOutputStream writeBuffer, int commandId) throws IOException {
byte[] data = writeBuffer.toByteArray();
sendWriteBuffer(commandId, address, data, false);
}
protected void sendWriteBuffer(int commandId, SocketAddress address, byte[] data, boolean redelivery) throws IOException {
// lets put the datagram into the replay buffer first to prevent timing
// issues
ReplayBuffer bufferCache = getReplayBuffer();
if (bufferCache != null && !redelivery) {
bufferCache.addBuffer(commandId, data);
}
if (LOG.isDebugEnabled()) {
String text = redelivery ? "REDELIVERING" : "sending";
LOG.debug("Channel: " + name + " " + text + " datagram: " + commandId + " to: " + address);
}
DatagramPacket packet = new DatagramPacket(data, 0, data.length, address);
channel.send(packet);
}
public void sendBuffer(int commandId, Object buffer) throws IOException {
if (buffer != null) {
byte[] data = (byte[])buffer;
sendWriteBuffer(commandId, replayAddress, data, true);
} else {
if (LOG.isWarnEnabled()) {
LOG.warn("Request for buffer: " + commandId + " is no longer present");
}
}
}
protected DatagramPacket createDatagramPacket() {
return new DatagramPacket(new byte[datagramSize], datagramSize);
}
protected int remaining(ByteArrayOutputStream buffer) {
return datagramSize - buffer.size();
}
protected ByteArrayOutputStream createByteArrayOutputStream() {
return new ByteArrayOutputStream(datagramSize);
}
public int getReceiveCounter() {
return receiveCounter;
}
}