package com.ubermq.jms.common.datagram.impl;
import com.ubermq.jms.common.datagram.*;
import com.ubermq.kernel.*;
import java.io.*;
import java.nio.*;
import org.apache.log4j.*;
/**
* The standard UberMQ datagram protocol handler, aka DatagramFactory. This
* class also implements message and control datagram creation as well as protocol
* handling.
*
* <PRE>
* UBERMQ DATAGRAM HEADER:
* ------
* SOH = 0xea 1 byte 1 in 255 chance this is a real datagram.
* ------
* size 4 bytes the size of the data in addition to header data.
* ------
* </PRE>
*
* followed by <code>size - 5</code> bytes of datagram specific data.
*
*/
public class DatagramFactory
implements IDatagramFactory,
IMessageDatagramFactory,
IControlDatagramFactory,
IAckDatagramFactory,
java.io.Serializable
{
private static final Logger log = Logger.getLogger(DatagramFactory.class);
public static final long serialVersionUID = 1L;
public final static byte UBERMQ_START_OF_HEADER = (byte)0xea;
public final static int UBERMQ_HEADER_LENGTH = 6;
public static final int UBERMQ_TYPE_POSITION = 5;
public static final int DGRAM_CONTROL = 1;
public static final int DGRAM_ACK = 2;
public static final int DGRAM_MSG = 3;
private static final DatagramFactory theInstance;
private static final DatagramFactoryHolder theHolder;
static {
theInstance = new DatagramFactory();
theHolder = new DatagramFactoryHolder(theInstance);
}
/**
* singleton pattern. this is a stateless object.
*/
DatagramFactory() {}
private Object readResolve() {return theInstance;}
/**
* Gets the instance of the UberMQ datagram factory.
* @return a datagram factory instance.
*/
public static DatagramFactory getInstance() {return theInstance;}
/**
* Gets a datagram factory holder representing the factory singleton.
* @return a factory holder
*/
public static DatagramFactoryHolder getHolder() {return theHolder;}
public int frame(ByteBuffer bb)
throws IOException
{
if (bb.remaining() < UBERMQ_HEADER_LENGTH)
return UBERMQ_HEADER_LENGTH;
int boundary = bb.position();
try {
if (bb.get() == UBERMQ_START_OF_HEADER) {
return UBERMQ_HEADER_LENGTH + bb.getInt();
} else {
bb.position(boundary);
log.debug(com.ubermq.util.Utility.displayBuffer(bb));
throw new IOException("packet header byte not detected");
}
} finally {
bb.position(boundary);
}
}
public IDatagram incoming(ByteBuffer bb)
throws IllegalArgumentException
{
// get the type byte.
bb.position(UBERMQ_TYPE_POSITION);
int datagramType = bb.get();
// move past all header data
bb.position(UBERMQ_HEADER_LENGTH);
// read the datagram.
try {
IDatagram d = createDatagramInstance(datagramType);
d.incoming(bb);
return d;
}
catch(IllegalArgumentException iae) {throw iae;}
catch(Exception io) {throw new IllegalArgumentException(io.toString());}
}
IDatagram createDatagramInstance(int type)
{
switch(type)
{
case DGRAM_ACK:
return new AckDatagram();
case DGRAM_CONTROL:
return new ControlDatagram();
case DGRAM_MSG:
return new MessageDatagram();
default:
return null;
}
}
public void outgoing(ByteBuffer bb, IDatagram d)
{
// output the UBER start byte,
// then a zero placeholder for the size,
// then the datagram type (this implementation only uses
// the low order byte)
bb.put(UBERMQ_START_OF_HEADER);
bb.putInt(0);
bb.put((byte)(0xFF & d.getDatagramType()));
// write the datagram out
d.outgoing(bb);
// update the length byte
int position = bb.position();
bb.position(1);
bb.putInt(position - UBERMQ_HEADER_LENGTH);
bb.position(position);
}
///// IControlDatagramFactory methods
/**
* Creates or resurrects a durable subscription, with the given name and
* topic specification.
*/
public IControlDatagram durableSubscribe(String durable, String topic)
{
return durableSubscribe(durable, topic, null);
}
/**
* Creates or resurrects a durable subscription, with the given name,
* topic specification and selector.
*/
public IControlDatagram durableSubscribe(String durable, String topic, String selector)
{
return new ControlDatagram(ControlDatagram.CONTROL_DURABLE_SUB,
new ControlDatagram.DurableSubscribeDatagramImpl(durable, topic, selector));
}
/**
* Indicates that the durable subscription is switching to disconnected mode.
*/
public IControlDatagram durableGoingAway(String durable)
{
return new ControlDatagram(ControlDatagram.CONTROL_DURABLE_GOING_AWAY,
new ControlDatagram.DurableGoingAwayDatagramImpl(durable));
}
/**
* Recovers a durable subscription, resending all unacknowledged messages.
*/
public IControlDatagram durableRecover(String durable)
{
return new ControlDatagram(ControlDatagram.CONTROL_DURABLE_RECOVER,
new ControlDatagram.DurableRecoverDatagramImpl(durable));
}
/**
* Permanently removes the named durable subscription.
*/
public IControlDatagram durableUnsubscribe(String durable)
{
return new ControlDatagram(ControlDatagram.CONTROL_DURABLE_UNSUB,
new ControlDatagram.DurableUnSubDatagramImpl(durable));
}
/**
* Subscribes to the given topic specification. The topic specification
* is not defined here; it is only meaningful to the recipient.
*/
public IControlDatagram subscribe(String topic)
{
return subscribe(topic, null);
}
/**
* Subscribes to the given topic specification and message selector.
* Both are interpreted by the peer.
*
*/
public IControlDatagram subscribe(String topic, String selector)
{
return new ControlDatagram(ControlDatagram.CONTROL_SUB,
new ControlDatagram.SubscribeDatagramImpl(topic, selector));
}
/**
* Unsubscribe from the topic specification given. The same specification
* should have been given in a prior subscribe() call.
*/
public IControlDatagram unsubscribe(String topic)
{
return new ControlDatagram(ControlDatagram.CONTROL_UNSUB,
new ControlDatagram.UnsubscribeDatagramImpl(topic));
}
/**
* Informs the peer that this connection should be considered as a
* clustering propagation connection, and any messages emerging from it
* should be interpreted as repeated.
*/
public IControlDatagram cluster()
{
return new ControlDatagram(ControlDatagram.CONTROL_CLUSTER,
new ControlDatagram.ClusterDatagramImpl());
}
/**
* Gives the peer the unique identifier of this clustering connection.
*/
public IControlDatagram clusterPeer(String peerId)
{
return new ControlDatagram(ControlDatagram.CONTROL_CLUSTER_PEER_ID,
new ControlDatagram.ClusterPeerDatagramImpl(peerId));
}
/**
* Asks the connection peer to begin sending messages.
*/
public IControlDatagram start()
{
return new ControlDatagram(ControlDatagram.CONTROL_START,
new ControlDatagram.StartDatagramImpl());
}
/**
* Asks the connection peer to stop sending messages.
*/
public IControlDatagram stop()
{
return new ControlDatagram(ControlDatagram.CONTROL_STOP,
new ControlDatagram.StopDatagramImpl());
}
/**
* The null operation. This can be used for connection keep alive.
*/
public IControlDatagram noop()
{
return new ControlDatagram(ControlDatagram.CONTROL_NOOP,
new ControlDatagram.NoopDatagramImpl());
}
/**
* Starts receiving messages from the named queue, with
* the specified message selector (or null for none).
* @param queue the name of the queue
* @param selector a message selector, or null to allow all messages.
* @return a control datagram
*/
public IControlDatagram queueStart(String queue, String selector)
{
return new ControlDatagram(ControlDatagram.CONTROL_QUEUE_START,
new ControlDatagram.QueueStartDatagramImpl(queue, selector));
}
/**
* Stops receiving messages from the named queue.
* @param queue the name of the queue
* @return a control datagram
*/
public IControlDatagram queueStop(String queue)
{
return new ControlDatagram(ControlDatagram.CONTROL_QUEUE_STOP,
new ControlDatagram.QueueStopDatagramImpl(queue));
}
/**
* Deletes a queue.
* @param queue the name of the queue.
*/
public IControlDatagram queueDelete(String queue)
{
return new ControlDatagram(ControlDatagram.CONTROL_QUEUE_DELETE,
new ControlDatagram.QueueDeleteDatagramImpl(queue));
}
/**
* Creates an acknowledgement datagram for a message. The acknowledgement
* may be positive or negative.
* @param id the message identifier
* @param nack true if the Ack is negative, false otherwise
*/
public IAckDatagram ack(MessageId id, boolean nack)
{
return new AckDatagram(id, nack);
}
/**
* Creates an acknowledgement datagram that is unrelated to a message.
* @param nack true if the Ack is negative, false otherwise
*/
public IAckDatagram ack(boolean nack)
{
return new AckDatagram(nack);
}
/////// MessageFactory
public IMessageDatagram createMessage()
{
return new MessageDatagram();
}
public IMessageDatagram createMessage(String topic)
{
return new MessageDatagram(topic);
}
/////// Ack Factory
/**
* Creates a negative acknowledgement (NACK) datagram in reference
* to the specified message identifier.
* @param id a message identifier
*/
public IAckDatagram nack(MessageId id)
{
return new AckDatagram(id, true);
}
/**
* Creates an acknowledgement datagram for the given message identifier.
* This creates a positive ack.
* @param id a message identifier
*/
public IAckDatagram ack(MessageId id)
{
return new AckDatagram(id, false);
}
/**
* Creates an acknowledgement datagram with no contextual information.
* This creates a positive ack.
*/
public IAckDatagram ack()
{
return new AckDatagram(false);
}
/**
* Creates a negative acknowledgement (NACK) datagram
* with no contextual information.
*/
public IAckDatagram nack()
{
return new AckDatagram(true);
}
}
|