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:
James Strachan 2006-02-24 12:47:14 +00:00
parent f0300f1251
commit 15dc02a88d
8 changed files with 135 additions and 72 deletions

View File

@ -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

View File

@ -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;
} }

View File

@ -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>

View File

@ -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;
} }
} }

View File

@ -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

View File

@ -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;
}
} }
} }

View File

@ -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>

View File

@ -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);