mirror of https://github.com/apache/activemq.git
added support for explicit client acknowledgement of messages or for auto-acknowledge
git-svn-id: https://svn.apache.org/repos/asf/incubator/activemq/trunk@380656 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
f0300f1251
commit
15dc02a88d
|
@ -22,6 +22,8 @@ using OpenWire.Client.Core;
|
||||||
|
|
||||||
namespace OpenWire.Client.Commands
|
namespace OpenWire.Client.Commands
|
||||||
{
|
{
|
||||||
|
public delegate void AcknowledgeHandler(ActiveMQMessage message);
|
||||||
|
|
||||||
public class ActiveMQMessage : Message, IMessage, MarshallAware
|
public class ActiveMQMessage : Message, IMessage, MarshallAware
|
||||||
{
|
{
|
||||||
public const byte ID_ActiveMQMessage = 23;
|
public const byte ID_ActiveMQMessage = 23;
|
||||||
|
@ -30,7 +32,7 @@ namespace OpenWire.Client.Commands
|
||||||
|
|
||||||
private PrimitiveMap properties;
|
private PrimitiveMap properties;
|
||||||
|
|
||||||
|
public event AcknowledgeHandler Acknowledger;
|
||||||
|
|
||||||
public static ActiveMQMessage Transform(IMessage message)
|
public static ActiveMQMessage Transform(IMessage message)
|
||||||
{
|
{
|
||||||
|
@ -46,6 +48,15 @@ namespace OpenWire.Client.Commands
|
||||||
return ID_ActiveMQMessage;
|
return ID_ActiveMQMessage;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void Acknowledge()
|
||||||
|
{
|
||||||
|
if (Acknowledger == null){
|
||||||
|
throw new OpenWireException("No Acknowledger has been associated with this message: " + this);}
|
||||||
|
else {
|
||||||
|
Acknowledger(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// Properties
|
// Properties
|
||||||
|
|
||||||
|
|
|
@ -21,7 +21,7 @@ namespace OpenWire.Client
|
||||||
private bool transacted;
|
private bool transacted;
|
||||||
private bool connected;
|
private bool connected;
|
||||||
private bool closed;
|
private bool closed;
|
||||||
private AcknowledgementMode acknowledgementMode;
|
private AcknowledgementMode acknowledgementMode = AcknowledgementMode.AutoAcknowledge;
|
||||||
private long sessionCounter;
|
private long sessionCounter;
|
||||||
private long temporaryDestinationCounter;
|
private long temporaryDestinationCounter;
|
||||||
private IDictionary consumers = new Hashtable(); // TODO threadsafe
|
private IDictionary consumers = new Hashtable(); // TODO threadsafe
|
||||||
|
@ -35,20 +35,20 @@ namespace OpenWire.Client
|
||||||
this.transport.Start();
|
this.transport.Start();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Starts message delivery for this connection.
|
/// Starts message delivery for this connection.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void Start()
|
public void Start()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Stop message delivery for this connection.
|
/// Stop message delivery for this connection.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void Stop()
|
public void Stop()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates a new session to work on this connection
|
/// Creates a new session to work on this connection
|
||||||
|
@ -66,7 +66,7 @@ namespace OpenWire.Client
|
||||||
CheckConnected();
|
CheckConnected();
|
||||||
SessionInfo info = CreateSessionInfo(transacted, acknowledgementMode);
|
SessionInfo info = CreateSessionInfo(transacted, acknowledgementMode);
|
||||||
SyncRequest(info);
|
SyncRequest(info);
|
||||||
Session session = new Session(this, info);
|
Session session = new Session(this, info, acknowledgementMode);
|
||||||
sessions.Add(session);
|
sessions.Add(session);
|
||||||
return session;
|
return session;
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,6 +25,12 @@ namespace OpenWire.Client
|
||||||
public interface IMessage
|
public interface IMessage
|
||||||
{
|
{
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// If using client acknowledgement mode on the session then this method will acknowledge that the
|
||||||
|
/// message has been processed correctly.
|
||||||
|
/// </summary>
|
||||||
|
void Acknowledge();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Provides access to the message properties (headers)
|
/// Provides access to the message properties (headers)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
@ -27,12 +27,12 @@ namespace OpenWire.Client
|
||||||
public interface IMessageConsumer : IDisposable
|
public interface IMessageConsumer : IDisposable
|
||||||
{
|
{
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Waits until a message is available and returns it
|
/// Waits until a message is available and returns it
|
||||||
/// </summary>
|
/// </summary>
|
||||||
IMessage Receive();
|
IMessage Receive();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// If a message is available within the timeout duration it is returned otherwise this method returns null
|
/// If a message is available within the timeout duration it is returned otherwise this method returns null
|
||||||
/// </summary>
|
/// </summary>
|
||||||
IMessage Receive(long timeout);
|
IMessage Receive(long timeout);
|
||||||
|
@ -46,6 +46,5 @@ namespace OpenWire.Client
|
||||||
/// An asynchronous listener which can be used to consume messages asynchronously
|
/// An asynchronous listener which can be used to consume messages asynchronously
|
||||||
/// </summary>
|
/// </summary>
|
||||||
event MessageHandler Listener;
|
event MessageHandler Listener;
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,9 +26,6 @@ namespace OpenWire.Client
|
||||||
public interface ISession : IDisposable
|
public interface ISession : IDisposable
|
||||||
{
|
{
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates a producer of messages
|
/// Creates a producer of messages
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -50,9 +47,9 @@ namespace OpenWire.Client
|
||||||
IMessageConsumer CreateConsumer(IDestination destination, string selector);
|
IMessageConsumer CreateConsumer(IDestination destination, string selector);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates a named durable consumer of messages on a given destination with a selector
|
/// Creates a named durable consumer of messages on a given destination with a selector
|
||||||
/// </summary>
|
/// </summary>
|
||||||
IMessageConsumer CreateDurableConsumer(ITopic destination, string name, string selector, bool noLocal);
|
IMessageConsumer CreateDurableConsumer(ITopic destination, string name, string selector, bool noLocal);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Returns the queue for the given name
|
/// Returns the queue for the given name
|
||||||
|
|
|
@ -22,6 +22,13 @@ using OpenWire.Client.Core;
|
||||||
|
|
||||||
namespace OpenWire.Client
|
namespace OpenWire.Client
|
||||||
{
|
{
|
||||||
|
public enum AckType {
|
||||||
|
DeliveredAck = 0, // Message delivered but not consumed
|
||||||
|
ConsumedAck = 1, // Message consumed, discard
|
||||||
|
PoisonAck = 2 // Message could not be processed due to poison pill but discard anyway
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// An object capable of receiving messages from some destination
|
/// An object capable of receiving messages from some destination
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -30,15 +37,17 @@ namespace OpenWire.Client
|
||||||
|
|
||||||
private Session session;
|
private Session session;
|
||||||
private ConsumerInfo info;
|
private ConsumerInfo info;
|
||||||
|
private AcknowledgementMode acknowledgementMode;
|
||||||
private bool closed;
|
private bool closed;
|
||||||
private Dispatcher dispatcher = new Dispatcher();
|
private Dispatcher dispatcher = new Dispatcher();
|
||||||
|
|
||||||
public event MessageHandler Listener;
|
public event MessageHandler Listener;
|
||||||
|
|
||||||
public MessageConsumer(Session session, ConsumerInfo info)
|
public MessageConsumer(Session session, ConsumerInfo info, AcknowledgementMode acknowledgementMode)
|
||||||
{
|
{
|
||||||
this.session = session;
|
this.session = session;
|
||||||
this.info = info;
|
this.info = info;
|
||||||
|
this.acknowledgementMode = acknowledgementMode;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -53,21 +62,23 @@ namespace OpenWire.Client
|
||||||
public IMessage Receive()
|
public IMessage Receive()
|
||||||
{
|
{
|
||||||
CheckClosed();
|
CheckClosed();
|
||||||
return dispatcher.Dequeue();
|
return AutoAcknowledge(dispatcher.Dequeue());
|
||||||
}
|
}
|
||||||
|
|
||||||
public IMessage Receive(long timeout)
|
public IMessage Receive(long timeout)
|
||||||
{
|
{
|
||||||
CheckClosed();
|
CheckClosed();
|
||||||
return dispatcher.Dequeue(timeout);
|
return AutoAcknowledge(dispatcher.Dequeue(timeout));
|
||||||
}
|
}
|
||||||
|
|
||||||
public IMessage ReceiveNoWait()
|
public IMessage ReceiveNoWait()
|
||||||
{
|
{
|
||||||
CheckClosed();
|
CheckClosed();
|
||||||
return dispatcher.DequeueNoWait();
|
return AutoAcknowledge(dispatcher.DequeueNoWait());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
session.DisposeOf(info.ConsumerId);
|
session.DisposeOf(info.ConsumerId);
|
||||||
|
@ -81,5 +92,53 @@ namespace OpenWire.Client
|
||||||
throw new ConnectionClosedException();
|
throw new ConnectionClosedException();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected IMessage AutoAcknowledge(IMessage message)
|
||||||
|
{
|
||||||
|
if (message is ActiveMQMessage)
|
||||||
|
{
|
||||||
|
ActiveMQMessage activeMessage = (ActiveMQMessage) message;
|
||||||
|
|
||||||
|
// lets register the handler for client acknowledgment
|
||||||
|
activeMessage.Acknowledger += new AcknowledgeHandler(DoClientAcknowledge);
|
||||||
|
|
||||||
|
if (acknowledgementMode != AcknowledgementMode.ClientAcknowledge)
|
||||||
|
{
|
||||||
|
DoAcknowledge(activeMessage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void DoClientAcknowledge(Message message)
|
||||||
|
{
|
||||||
|
if (acknowledgementMode == AcknowledgementMode.ClientAcknowledge)
|
||||||
|
{
|
||||||
|
DoAcknowledge(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void DoAcknowledge(Message message)
|
||||||
|
{
|
||||||
|
MessageAck ack = CreateMessageAck(message);
|
||||||
|
//Console.WriteLine("Sending Ack: " + ack);
|
||||||
|
session.Connection.SyncRequest(ack);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
protected virtual MessageAck CreateMessageAck(Message message)
|
||||||
|
{
|
||||||
|
MessageAck ack = new MessageAck();
|
||||||
|
ack.AckType = (int) AckType.ConsumedAck;
|
||||||
|
ack.ConsumerId = info.ConsumerId;
|
||||||
|
ack.Destination = message.Destination;
|
||||||
|
ack.FirstMessageId = message.MessageId;
|
||||||
|
ack.LastMessageId = message.MessageId;
|
||||||
|
ack.MessageCount = 1;
|
||||||
|
ack.TransactionId = message.TransactionId;
|
||||||
|
return ack;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,16 +26,17 @@ namespace OpenWire.Client
|
||||||
public class Session : ISession
|
public class Session : ISession
|
||||||
{
|
{
|
||||||
private Connection connection;
|
private Connection connection;
|
||||||
private AcknowledgementMode acknowledgementMode = AcknowledgementMode.AutoAcknowledge;
|
|
||||||
private SessionInfo info;
|
private SessionInfo info;
|
||||||
|
private AcknowledgementMode acknowledgementMode;
|
||||||
private long consumerCounter;
|
private long consumerCounter;
|
||||||
private long producerCounter;
|
private long producerCounter;
|
||||||
private int prefetchSize = 1000;
|
private int prefetchSize = 1000;
|
||||||
|
|
||||||
public Session(Connection connection, SessionInfo info)
|
public Session(Connection connection, SessionInfo info, AcknowledgementMode acknowledgementMode)
|
||||||
{
|
{
|
||||||
this.connection = connection;
|
this.connection = connection;
|
||||||
this.info = info;
|
this.info = info;
|
||||||
|
this.acknowledgementMode = acknowledgementMode;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
|
@ -55,14 +56,6 @@ namespace OpenWire.Client
|
||||||
return new MessageProducer(this, command);
|
return new MessageProducer(this, command);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Acknowledge(Message message)
|
|
||||||
{
|
|
||||||
if (acknowledgementMode == AcknowledgementMode.ClientAcknowledge)
|
|
||||||
{
|
|
||||||
MessageAck ack = CreateMessageAck(message);
|
|
||||||
connection.SyncRequest(ack);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public IMessageConsumer CreateConsumer(IDestination destination)
|
public IMessageConsumer CreateConsumer(IDestination destination)
|
||||||
|
@ -77,7 +70,7 @@ namespace OpenWire.Client
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
MessageConsumer consumer = new MessageConsumer(this, command);
|
MessageConsumer consumer = new MessageConsumer(this, command, acknowledgementMode);
|
||||||
// lets register the consumer first in case we start dispatching messages immediately
|
// lets register the consumer first in case we start dispatching messages immediately
|
||||||
connection.AddConsumer(consumerId, consumer);
|
connection.AddConsumer(consumerId, consumer);
|
||||||
|
|
||||||
|
@ -91,28 +84,28 @@ namespace OpenWire.Client
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public IMessageConsumer CreateDurableConsumer(ITopic destination, string name, string selector, bool noLocal)
|
public IMessageConsumer CreateDurableConsumer(ITopic destination, string name, string selector, bool noLocal)
|
||||||
{
|
{
|
||||||
ConsumerInfo command = CreateConsumerInfo(destination, selector);
|
ConsumerInfo command = CreateConsumerInfo(destination, selector);
|
||||||
ConsumerId consumerId = command.ConsumerId;
|
ConsumerId consumerId = command.ConsumerId;
|
||||||
command.SubcriptionName = name;
|
command.SubcriptionName = name;
|
||||||
command.NoLocal = noLocal;
|
command.NoLocal = noLocal;
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
MessageConsumer consumer = new MessageConsumer(this, command);
|
MessageConsumer consumer = new MessageConsumer(this, command, acknowledgementMode);
|
||||||
// lets register the consumer first in case we start dispatching messages immediately
|
// lets register the consumer first in case we start dispatching messages immediately
|
||||||
connection.AddConsumer(consumerId, consumer);
|
connection.AddConsumer(consumerId, consumer);
|
||||||
|
|
||||||
connection.SyncRequest(command);
|
connection.SyncRequest(command);
|
||||||
return consumer;
|
return consumer;
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
connection.RemoveConsumer(consumerId);
|
connection.RemoveConsumer(consumerId);
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public IQueue GetQueue(string name)
|
public IQueue GetQueue(string name)
|
||||||
{
|
{
|
||||||
|
@ -176,7 +169,12 @@ namespace OpenWire.Client
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Properties
|
||||||
|
public Connection Connection {
|
||||||
|
get {
|
||||||
|
return connection;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Implementation methods
|
// Implementation methods
|
||||||
public void DoSend(IDestination destination, IMessage message)
|
public void DoSend(IDestination destination, IMessage message)
|
||||||
|
@ -236,13 +234,6 @@ namespace OpenWire.Client
|
||||||
return answer;
|
return answer;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected virtual MessageAck CreateMessageAck(Message message)
|
|
||||||
{
|
|
||||||
MessageAck ack = new MessageAck();
|
|
||||||
// TODO complete packet
|
|
||||||
return ack;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Configures the message command
|
/// Configures the message command
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
@ -105,14 +105,14 @@ namespace OpenWire.Client
|
||||||
Assert.AreEqual(custom4, message.Properties["custom4"], "custom4");
|
Assert.AreEqual(custom4, message.Properties["custom4"], "custom4");
|
||||||
// TODO
|
// TODO
|
||||||
//Assert.AreEqual(custom5, message.Properties["custom5"], "custom5");
|
//Assert.AreEqual(custom5, message.Properties["custom5"], "custom5");
|
||||||
Assert.AreEqual(custom4, message.Properties["custom6"], "custom6");
|
Assert.AreEqual(custom6, message.Properties["custom6"], "custom6");
|
||||||
|
|
||||||
Assert.AreEqual(custom1, message.Properties.GetBool("custom1"), "custom1");
|
Assert.AreEqual(custom1, message.Properties.GetBool("custom1"), "custom1");
|
||||||
Assert.AreEqual(custom2, message.Properties.GetByte("custom2"), "custom2");
|
Assert.AreEqual(custom2, message.Properties.GetByte("custom2"), "custom2");
|
||||||
Assert.AreEqual(custom3, message.Properties.GetShort("custom3"), "custom3");
|
Assert.AreEqual(custom3, message.Properties.GetShort("custom3"), "custom3");
|
||||||
Assert.AreEqual(custom4, message.Properties.GetInt("custom4"), "custom4");
|
Assert.AreEqual(custom4, message.Properties.GetInt("custom4"), "custom4");
|
||||||
//Assert.AreEqual(custom5, message.Properties.GetLong("custom5"), "custom5");
|
//Assert.AreEqual(custom5, message.Properties.GetLong("custom5"), "custom5");
|
||||||
Assert.AreEqual(custom4, message.Properties.GetChar("custom6"), "custom6");
|
Assert.AreEqual(custom6, message.Properties.GetChar("custom6"), "custom6");
|
||||||
|
|
||||||
// lets now look at some standard JMS headers
|
// lets now look at some standard JMS headers
|
||||||
Console.WriteLine("JMSExpiration: " + message.JMSExpiration);
|
Console.WriteLine("JMSExpiration: " + message.JMSExpiration);
|
||||||
|
|
Loading…
Reference in New Issue