diff options
16 files changed, 1014 insertions, 499 deletions
diff --git a/dotnet/Qpid.Client.Tests/BrokerDetails/BrokerDetailsTest.cs b/dotnet/Qpid.Client.Tests/BrokerDetails/BrokerDetailsTest.cs new file mode 100644 index 0000000000..8bc615bd20 --- /dev/null +++ b/dotnet/Qpid.Client.Tests/BrokerDetails/BrokerDetailsTest.cs @@ -0,0 +1,65 @@ +/*
+ *
+ * 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.
+ *
+ */
+using System;
+using System.Net;
+using NUnit.Framework;
+using Qpid.Client.Qms;
+
+namespace Qpid.Client.Tests.BrokerDetails
+{
+ [TestFixture]
+ public class BrokerDetailsTest
+ {
+
+ [Test]
+ public void ValidateBrokerInfoEqualsMethod()
+ {
+ AmqBrokerInfo broker = new AmqBrokerInfo("amqp", "localhost", 5672, true);
+ AmqBrokerInfo broker1 = new AmqBrokerInfo("Amqp", "localhost", 5672, true);
+
+ Assert.IsTrue(broker.Equals(broker1),"The two AmqBrokerInfo objects are not equals");
+ Console.WriteLine(string.Format("The object broker: {0} and broker1: {1} are equals", broker, broker1));
+ }
+
+ [Test]
+ public void ValidateBrokerInfoWithDifferentSSL()
+ {
+ AmqBrokerInfo broker = new AmqBrokerInfo("amqp", "localhost", 5672, true);
+ AmqBrokerInfo broker1 = new AmqBrokerInfo("amqp", "localhost", 5672, false);
+
+ Assert.IsFalse(broker.Equals(broker1), "The two AmqBrokerInfo objects are equals");
+ Console.WriteLine(string.Format("The object broker: {0} and broker1: {1} are not equals", broker, broker1));
+ }
+
+ [Test]
+ public void ValidateBrokerInfoFromToString()
+ {
+ String url = "tcp://localhost:5672?timeout='200',immediatedelivery='true'";
+
+ AmqBrokerInfo broker = new AmqBrokerInfo(url);
+ AmqBrokerInfo broker1 = new AmqBrokerInfo(broker.ToString());
+
+ Assert.AreEqual(broker.GetOption("timeout"), broker1.GetOption("timeout"));
+ Assert.AreEqual(broker.GetOption("immediatedelivery"), broker1.GetOption("immediatedelivery"));
+ }
+
+ }
+}
diff --git a/dotnet/Qpid.Client.Tests/Channel/ChannelMessageCreationTests.cs b/dotnet/Qpid.Client.Tests/Channel/ChannelMessageCreationTests.cs new file mode 100644 index 0000000000..40ba1dd25a --- /dev/null +++ b/dotnet/Qpid.Client.Tests/Channel/ChannelMessageCreationTests.cs @@ -0,0 +1,78 @@ +/*
+ *
+ * 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.
+ *
+ */
+
+using System;
+
+using log4net;
+using NUnit.Framework;
+using Qpid.Client;
+using Qpid.Client.Message;
+using Qpid.Messaging;
+
+namespace Qpid.Client.Tests.Channel
+{
+ /// <summary>
+ /// Test that channels can create messages correctly
+ /// </summary>
+ [TestFixture]
+ public class ChannelMessageCreationTests
+ {
+ [Test]
+ public void CanCreateTextMessage()
+ {
+ IChannel channel = AmqChannel.CreateDisconnectedChannel();
+ ITextMessage msg = channel.CreateTextMessage();
+ Assert.IsNotNull(msg);
+ }
+ [Test]
+ public void CanCreateTextMessageWithContent()
+ {
+ IChannel channel = AmqChannel.CreateDisconnectedChannel();
+ const string CONTENT = "1234567890";
+ ITextMessage msg = channel.CreateTextMessage(CONTENT);
+ Assert.IsNotNull(msg);
+ Assert.AreEqual(CONTENT, msg.Text);
+ }
+ [Test]
+ public void CanCreateBytesMessage()
+ {
+ IChannel channel = AmqChannel.CreateDisconnectedChannel();
+ IBytesMessage msg = channel.CreateBytesMessage();
+ Assert.IsNotNull(msg);
+ }
+ [Test]
+ public void CanCreateMessage()
+ {
+ IChannel channel = AmqChannel.CreateDisconnectedChannel();
+ IMessage msg = channel.CreateMessage();
+ Assert.IsNotNull(msg);
+ }
+ [Test]
+ public void CanCreateMessageFromMimeType()
+ {
+ IChannel channel = AmqChannel.CreateDisconnectedChannel();
+ IMessage msg = channel.CreateMessage("text/xml");
+ Assert.IsNotNull(msg);
+ Assert.IsInstanceOfType(typeof(ITextMessage), msg);
+ }
+ }
+} // namespace Qpid.Client.Tests.Channel
+
diff --git a/dotnet/Qpid.Client.Tests/Messages/MessageFactoryRegistryTests.cs b/dotnet/Qpid.Client.Tests/Messages/MessageFactoryRegistryTests.cs new file mode 100644 index 0000000000..421d0d4e02 --- /dev/null +++ b/dotnet/Qpid.Client.Tests/Messages/MessageFactoryRegistryTests.cs @@ -0,0 +1,113 @@ +/*
+ *
+ * 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.
+ *
+ */
+
+using System;
+
+using log4net;
+using NUnit.Framework;
+using Qpid.Messaging;
+using Qpid.Client.Message;
+
+namespace Qpid.Client.Tests.Messages
+{
+ /// <summary>
+ /// Ensure a factory creates messages correctly
+ /// </summary>
+ [TestFixture]
+ public class MessageFactoryRegistryTests
+ {
+ const string TEXT_PLAIN = "text/plain";
+ const string TEXT_XML = "text/xml";
+ const string OCTET_STREAM = "application/octet-stream";
+
+ /// <summary>
+ /// Check default registry can create text/plain messages
+ /// </summary>
+ [Test]
+ public void CanCreateTextPlain()
+ {
+ MessageFactoryRegistry registry =
+ MessageFactoryRegistry.NewDefaultRegistry();
+
+ IMessage message = registry.CreateMessage(TEXT_PLAIN);
+ Assert.IsNotNull(message);
+ Assert.AreEqual(TEXT_PLAIN, message.ContentType);
+ Assert.IsInstanceOfType(typeof(QpidTextMessage), message);
+ }
+ /// <summary>
+ /// Check default registry can create text/xml messages
+ /// </summary>
+ [Test]
+ public void CanCreateTextXml()
+ {
+ MessageFactoryRegistry registry =
+ MessageFactoryRegistry.NewDefaultRegistry();
+
+ IMessage message = registry.CreateMessage(TEXT_XML);
+ Assert.IsNotNull(message);
+ Assert.AreEqual(TEXT_XML, message.ContentType);
+ Assert.IsInstanceOfType(typeof(QpidTextMessage), message);
+ }
+ /// <summary>
+ /// Check default registry can create application/octet-stream messages
+ /// </summary>
+ [Test]
+ public void CanCreateBinary()
+ {
+ MessageFactoryRegistry registry =
+ MessageFactoryRegistry.NewDefaultRegistry();
+
+ IMessage message = registry.CreateMessage(OCTET_STREAM);
+ Assert.IsNotNull(message);
+ Assert.AreEqual(OCTET_STREAM, message.ContentType);
+ Assert.IsInstanceOfType(typeof(QpidBytesMessage), message);
+ }
+ /// <summary>
+ /// Check default registry can create messages for unknown types
+ /// </summary>
+ [Test]
+ public void CanCreateUnknownType()
+ {
+ MessageFactoryRegistry registry =
+ MessageFactoryRegistry.NewDefaultRegistry();
+
+ const string OTHER = "application/unknown";
+ IMessage message = registry.CreateMessage(OTHER);
+ Assert.IsNotNull(message);
+ Assert.AreEqual(OTHER, message.ContentType);
+ Assert.IsInstanceOfType(typeof(QpidBytesMessage), message);
+ }
+ /// <summary>
+ /// Check that text messages default to UTF-8 encoding
+ /// </summary>
+ [Test]
+ public void TextMessagesDefaultToUTF8Encoding()
+ {
+ MessageFactoryRegistry registry =
+ MessageFactoryRegistry.NewDefaultRegistry();
+
+ IMessage message = registry.CreateMessage(TEXT_PLAIN);
+ Assert.AreEqual("utf-8", message.ContentEncoding.ToLower());
+ }
+
+ }
+} // namespace Qpid.Client.Tests.Messages
+
diff --git a/dotnet/Qpid.Client.Tests/Qpid.Client.Tests.csproj b/dotnet/Qpid.Client.Tests/Qpid.Client.Tests.csproj index 49145022b7..1a930b178b 100644 --- a/dotnet/Qpid.Client.Tests/Qpid.Client.Tests.csproj +++ b/dotnet/Qpid.Client.Tests/Qpid.Client.Tests.csproj @@ -46,6 +46,8 @@ <ItemGroup>
<Compile Include="bio\BlockingIo.cs" />
<Compile Include="BrokerDetails\BrokerDetailsTest.cs" />
+ <Compile Include="Channel\ChannelMessageCreationTests.cs" />
+ <Compile Include="Messages\MessageFactoryRegistryTests.cs" />
<Compile Include="connection\ConnectionTest.cs" />
<Compile Include="connection\SslConnectionTest.cs" />
<Compile Include="failover\FailoverTest.cs" />
diff --git a/dotnet/Qpid.Client/Client/AmqChannel.cs b/dotnet/Qpid.Client/Client/AmqChannel.cs index 3471ac3640..0d0c48303a 100644 --- a/dotnet/Qpid.Client/Client/AmqChannel.cs +++ b/dotnet/Qpid.Client/Client/AmqChannel.cs @@ -156,103 +156,72 @@ namespace Qpid.Client } } - internal AmqChannel(AMQConnection con, ushort channelId, bool transacted, AcknowledgeMode acknowledgeMode, int defaultPrefetch) : - this(con, channelId, transacted, acknowledgeMode, MessageFactoryRegistry.NewDefaultRegistry(), defaultPrefetch) - { - } - /// <summary> /// Initializes a new instance of the <see cref="AmqChannel"/> class. /// </summary> - /// <param name="con">The con.</param> + /// <param name="con">The connection.</param> /// <param name="channelId">The channel id.</param> /// <param name="transacted">if set to <c>true</c> [transacted].</param> /// <param name="acknowledgeMode">The acknowledge mode.</param> - /// <param name="messageFactoryRegistry">The message factory registry.</param> - internal AmqChannel(AMQConnection con, ushort channelId, bool transacted, AcknowledgeMode acknowledgeMode, - MessageFactoryRegistry messageFactoryRegistry, int defaultPrefetch) + /// <param name="defaultPrefetch">Default prefetch value</param> + internal AmqChannel(AMQConnection con, ushort channelId, bool transacted, AcknowledgeMode acknowledgeMode, int defaultPrefetch) + : this() + { + _sessionNumber = Interlocked.Increment(ref _nextSessionNumber); + _connection = con; + _transacted = transacted; + if ( transacted ) + { + _acknowledgeMode = AcknowledgeMode.SessionTransacted; + } else + { + _acknowledgeMode = acknowledgeMode; + } + _channelId = channelId; + } + + private AmqChannel() { - _sessionNumber = Interlocked.Increment(ref _nextSessionNumber); - _connection = con; - _transacted = transacted; - if (transacted) - { - _acknowledgeMode = AcknowledgeMode.SessionTransacted; - } - else - { - _acknowledgeMode = acknowledgeMode; - } - _channelId = channelId; - _messageFactoryRegistry = messageFactoryRegistry; + _messageFactoryRegistry = MessageFactoryRegistry.NewDefaultRegistry(); + } + + /// <summary> + /// Create a disconnected channel that will fault + /// for most things, but is useful for testing + /// </summary> + /// <returns>A new disconnected channel</returns> + public static IChannel CreateDisconnectedChannel() + { + return new AmqChannel(); } + public IBytesMessage CreateBytesMessage() { - lock (_connection.FailoverMutex) - { - CheckNotClosed(); - try - { - return (IBytesMessage)_messageFactoryRegistry.CreateMessage("application/octet-stream"); - } - catch (AMQException e) - { - throw new QpidException("Unable to create message: " + e); - } - } + return (IBytesMessage)_messageFactoryRegistry.CreateMessage("application/octet-stream"); } public IMessage CreateMessage() { - lock (_connection.FailoverMutex) - { - CheckNotClosed(); - try - { - // TODO: this is supposed to create a message consisting only of message headers - return (IBytesMessage)_messageFactoryRegistry.CreateMessage("application/octet-stream"); - } - catch (AMQException e) - { - throw new QpidException("Unable to create message: " + e); - } - } + // TODO: this is supposed to create a message consisting only of message headers + return (IBytesMessage)_messageFactoryRegistry.CreateMessage("application/octet-stream"); + } + + public IMessage CreateMessage(string mimeType) + { + return _messageFactoryRegistry.CreateMessage(mimeType); } public ITextMessage CreateTextMessage() { - lock (_connection.FailoverMutex) - { - CheckNotClosed(); - - try - { - return (ITextMessage)_messageFactoryRegistry.CreateMessage("text/plain"); - } - catch (AMQException e) - { - throw new QpidException("Unable to create message: " + e); - } - } + return CreateTextMessage(String.Empty); } public ITextMessage CreateTextMessage(string text) { - lock (_connection.FailoverMutex) - { - CheckNotClosed(); - try - { - ITextMessage msg = (ITextMessage)_messageFactoryRegistry.CreateMessage("text/plain"); - msg.Text = text; - return msg; - } - catch (AMQException e) - { - throw new QpidException("Unable to create message: " + e); - } - } + ITextMessage msg = (ITextMessage)_messageFactoryRegistry.CreateMessage("text/plain"); + msg.Text = text; + return msg; } public bool Transacted @@ -538,11 +507,6 @@ namespace Qpid.Client } } - public IFieldTable CreateFieldTable() - { - return new FieldTable(); - } - public void Unsubscribe(String name) { throw new NotImplementedException(); // FIXME diff --git a/dotnet/Qpid.Client/Client/Message/AMQMessageFactory.cs b/dotnet/Qpid.Client/Client/Message/AMQMessageFactory.cs index 75c4edd67d..61b308696c 100644 --- a/dotnet/Qpid.Client/Client/Message/AMQMessageFactory.cs +++ b/dotnet/Qpid.Client/Client/Message/AMQMessageFactory.cs @@ -27,7 +27,7 @@ namespace Qpid.Client.Message { public abstract class AbstractQmsMessageFactory : IMessageFactory { - public abstract AbstractQmsMessage CreateMessage(); + public abstract AbstractQmsMessage CreateMessage(string mimeType); private static readonly ILog _logger = LogManager.GetLogger(typeof (AbstractQmsMessageFactory)); diff --git a/dotnet/Qpid.Client/Client/Message/AbstractQmsMessage.cs b/dotnet/Qpid.Client/Client/Message/AbstractQmsMessage.cs index 1d2b2db3ca..8e90e852dd 100644 --- a/dotnet/Qpid.Client/Client/Message/AbstractQmsMessage.cs +++ b/dotnet/Qpid.Client/Client/Message/AbstractQmsMessage.cs @@ -72,24 +72,29 @@ namespace Qpid.Client.Message #endregion + #region Properties + // + // Properties + // + + /// <summary> + /// The application message identifier + /// </summary> public string MessageId { - get - { + get { if (ContentHeaderProperties.MessageId == null) { ContentHeaderProperties.MessageId = "ID:" + DeliveryTag; } return ContentHeaderProperties.MessageId; } - set - { - ContentHeaderProperties.MessageId = value; - } - - - } + set { ContentHeaderProperties.MessageId = value; } + } + /// <summary> + /// The message timestamp + /// </summary> public long Timestamp { get @@ -103,36 +108,22 @@ namespace Qpid.Client.Message } } - protected void CheckReadable() - { - if (!_readableMessage) - { - throw new MessageNotReadableException("You need to call reset() to make the message readable"); - } - } - + /// <summary> + /// The <see cref="CorrelationId"/> as a byte array. + /// </summary> public byte[] CorrelationIdAsBytes { - get - { - return Encoding.Default.GetBytes(ContentHeaderProperties.CorrelationId); - } - set - { - ContentHeaderProperties.CorrelationId = Encoding.Default.GetString(value); - } + get { return Encoding.Default.GetBytes(ContentHeaderProperties.CorrelationId); } + set { ContentHeaderProperties.CorrelationId = Encoding.Default.GetString(value); } } + /// <summary> + /// The application correlation identifier + /// </summary> public string CorrelationId { - get - { - return ContentHeaderProperties.CorrelationId; - } - set - { - ContentHeaderProperties.ContentType = value; - } + get { return ContentHeaderProperties.CorrelationId; } + set { ContentHeaderProperties.CorrelationId = value; } } struct Dest @@ -147,6 +138,9 @@ namespace Qpid.Client.Message } } + /// <summary> + /// Exchange name of the reply-to address + /// </summary> public string ReplyToExchangeName { get @@ -162,6 +156,9 @@ namespace Qpid.Client.Message } } + /// <summary> + /// Routing key of the reply-to address + /// </summary> public string ReplyToRoutingKey { get @@ -177,50 +174,11 @@ namespace Qpid.Client.Message } } - /// <summary> - /// Decodes the replyto field if one is set. - /// - /// Splits a replyto field containing an exchange name followed by a ':', followed by a routing key into the exchange name and - /// routing key seperately. The exchange name may be empty in which case the empty string is returned. If the exchange name is - /// empty the replyto field is expected to being with ':'. - /// - /// Anyhting other than a two part replyto field sperated with a ':' will result in an exception. - /// </summary> - /// - /// <returns>A destination initialized to the replyto location if a replyto field was set, or an empty destination otherwise.</returns> - private Dest ReadReplyToHeader() - { - string replyToEncoding = ContentHeaderProperties.ReplyTo; - - if (replyToEncoding == null) - { - return new Dest(); - } - else - { - // Split the replyto field on a ':' - string[] split = replyToEncoding.Split(':'); - // Ensure that the replyto field argument only consisted of two parts. - if (split.Length != 2) - { - throw new QpidException("Illegal value in ReplyTo property: " + replyToEncoding); - } - - // Extract the exchange name and routing key from the split replyto field. - string exchangeName = split[0]; - string routingKey = split[1]; - - return new Dest(exchangeName, routingKey); - } - } - - private void WriteReplyToHeader(Dest dest) - { - string encodedDestination = string.Format("{0}:{1}", dest.ExchangeName, dest.RoutingKey); - ContentHeaderProperties.ReplyTo = encodedDestination; - } + /// <summary> + /// Non-persistent (1) or persistent (2) + /// </summary> public DeliveryMode DeliveryMode { get @@ -242,100 +200,94 @@ namespace Qpid.Client.Message } } + /// <summary> + /// True, if this is a redelivered message + /// </summary> public bool Redelivered { - get - { - return _redelivered; - } - set - { - _redelivered = value; - } - } + get { return _redelivered; } + set { _redelivered = value; } + } + /// <summary> + /// The message type name + /// </summary> public string Type { - get - { - return MimeType; - } - set - { - //MimeType = value; - } + get { return ContentHeaderProperties.Type; } + set { ContentHeaderProperties.Type = value; } } - + + /// <summary> + /// Message expiration specification + /// </summary> public long Expiration { - get - { - return ContentHeaderProperties.Expiration; - } - set - { - ContentHeaderProperties.Expiration = value; - } + get { return ContentHeaderProperties.Expiration; } + set { ContentHeaderProperties.Expiration = value; } } - public int Priority + /// <summary> + /// The message priority, 0 to 9 + /// </summary> + public byte Priority { - get - { - return ContentHeaderProperties.Priority; - } - set - { - ContentHeaderProperties.Priority = (byte) value; - } + get { return ContentHeaderProperties.Priority; } + set { ContentHeaderProperties.Priority = (byte) value; } } - // FIXME: implement + /// <summary> + /// The MIME Content Type + /// </summary> public string ContentType { - get { throw new NotImplementedException(); } - set { throw new NotImplementedException(); } + get { return ContentHeaderProperties.ContentType; } + set { ContentHeaderProperties.ContentType = value; } } - // FIXME: implement + /// <summary> + /// The MIME Content Encoding + /// </summary> public string ContentEncoding { - get { throw new NotImplementedException(); } - set { throw new NotImplementedException(); } - } - - public void Acknowledge() - { - // the JMS 1.1 spec says in section 3.6 that calls to acknowledge are ignored when client acknowledge - // is not specified. In our case, we only set the session field where client acknowledge mode is specified. - if (_channel != null) - { - // we set multiple to true here since acknowledgement implies acknowledge of all previous messages - // received on the session - _channel.AcknowledgeMessage((ulong)DeliveryTag, true); - } - + get { return ContentHeaderProperties.Encoding; } + set { ContentHeaderProperties.Encoding = value; } } + /// <summary> + /// Headers of this message + /// </summary> public IHeaders Headers { get { return _headers; } } - public abstract void ClearBodyImpl(); + /// <summary> + /// The creating user id + /// </summary> + public string UserId + { + get { return ContentHeaderProperties.UserId; } + set { ContentHeaderProperties.UserId = value; } + } - public void ClearBody() + /// <summary> + /// The creating application id + /// </summary> + public string AppId { - ClearBodyImpl(); - _readableMessage = false; + get { return ContentHeaderProperties.AppId; } + set { ContentHeaderProperties.AppId = value; } } /// <summary> - /// Get a String representation of the body of the message. Used in the - /// toString() method which outputs this before message properties. + /// Intra-cluster routing identifier /// </summary> - /// <exception cref="QpidException"></exception> - public abstract string ToBodyString(); + public string ClusterId + { + get { return ContentHeaderProperties.ClusterId; } + set { ContentHeaderProperties.ClusterId = value; } + } /// <summary> /// Return the raw byte array that is used to populate the frame when sending @@ -367,12 +319,37 @@ namespace Qpid.Client.Message _data = value; } } + #endregion // Properties + - public abstract string MimeType + public void Acknowledge() { - get; + // the JMS 1.1 spec says in section 3.6 that calls to acknowledge are ignored when client acknowledge + // is not specified. In our case, we only set the session field where client acknowledge mode is specified. + if (_channel != null) + { + // we set multiple to true here since acknowledgement implies acknowledge of all previous messages + // received on the session + _channel.AcknowledgeMessage((ulong)DeliveryTag, true); + } + } + public abstract void ClearBodyImpl(); + + public void ClearBody() + { + ClearBodyImpl(); + _readableMessage = false; + } + + /// <summary> + /// Get a String representation of the body of the message. Used in the + /// toString() method which outputs this before message properties. + /// </summary> + /// <exception cref="QpidException"></exception> + public abstract string ToBodyString(); + public override string ToString() { try @@ -403,18 +380,6 @@ namespace Qpid.Client.Message } } - public IFieldTable UnderlyingMessagePropertiesMap - { - get - { - return ContentHeaderProperties.Headers; - } - set - { - ContentHeaderProperties.Headers = (FieldTable)value; - } - } - public FieldTable PopulateHeadersFromMessageProperties() { if (ContentHeaderProperties.Headers == null) @@ -466,5 +431,56 @@ namespace Qpid.Client.Message { get { return !_readableMessage; } } + + protected void CheckReadable() + { + if ( !_readableMessage ) + { + throw new MessageNotReadableException("You need to call reset() to make the message readable"); + } + } + + /// <summary> + /// Decodes the replyto field if one is set. + /// + /// Splits a replyto field containing an exchange name followed by a ':', followed by a routing key into the exchange name and + /// routing key seperately. The exchange name may be empty in which case the empty string is returned. If the exchange name is + /// empty the replyto field is expected to being with ':'. + /// + /// Anyhting other than a two part replyto field sperated with a ':' will result in an exception. + /// </summary> + /// + /// <returns>A destination initialized to the replyto location if a replyto field was set, or an empty destination otherwise.</returns> + private Dest ReadReplyToHeader() + { + string replyToEncoding = ContentHeaderProperties.ReplyTo; + + if ( replyToEncoding == null ) + { + return new Dest(); + } else + { + // Split the replyto field on a ':' + string[] split = replyToEncoding.Split(':'); + + // Ensure that the replyto field argument only consisted of two parts. + if ( split.Length != 2 ) + { + throw new QpidException("Illegal value in ReplyTo property: " + replyToEncoding); + } + + // Extract the exchange name and routing key from the split replyto field. + string exchangeName = split[0]; + string routingKey = split[1]; + + return new Dest(exchangeName, routingKey); + } + } + + private void WriteReplyToHeader(Dest dest) + { + string encodedDestination = string.Format("{0}:{1}", dest.ExchangeName, dest.RoutingKey); + ContentHeaderProperties.ReplyTo = encodedDestination; + } } } diff --git a/dotnet/Qpid.Client/Client/Message/IMessageFactory.cs b/dotnet/Qpid.Client/Client/Message/IMessageFactory.cs index 4a109b128e..cffc585067 100644 --- a/dotnet/Qpid.Client/Client/Message/IMessageFactory.cs +++ b/dotnet/Qpid.Client/Client/Message/IMessageFactory.cs @@ -28,11 +28,12 @@ namespace Qpid.Client.Message /// <summary> /// Create a message /// </summary> - /// <param name="messageNbr"></param> - /// <param name="redelivered"></param> - /// <param name="contentHeader"></param> - /// <param name="bodies"></param> - /// <returns></returns> + /// <param name="deliverTag">Delivery Tag</param> + /// <param name="messageNbr">Message Sequence Number</param> + /// <param name="redelivered">True if this is a redelivered message</param> + /// <param name="contentHeader">Content headers</param> + /// <param name="bodies">Message bodies</param> + /// <returns>The new message</returns> /// <exception cref="QpidMessagingException">if the message cannot be created</exception> AbstractQmsMessage CreateMessage(long deliverTag, bool redelivered, ContentHeaderBody contentHeader, @@ -41,9 +42,10 @@ namespace Qpid.Client.Message /// <summary> /// Creates the message. /// </summary> - /// <returns></returns> + /// <param name="mimeType">Mime type to associate the new message with</param> + /// <returns>The new message</returns> /// <exception cref="QpidMessagingException">if the message cannot be created</exception> - AbstractQmsMessage CreateMessage(); + AbstractQmsMessage CreateMessage(string mimeType); } } diff --git a/dotnet/Qpid.Client/Client/Message/MessageFactoryRegistry.cs b/dotnet/Qpid.Client/Client/Message/MessageFactoryRegistry.cs index 95257cef8a..f854a541fc 100644 --- a/dotnet/Qpid.Client/Client/Message/MessageFactoryRegistry.cs +++ b/dotnet/Qpid.Client/Client/Message/MessageFactoryRegistry.cs @@ -25,93 +25,104 @@ using Qpid.Messaging; namespace Qpid.Client.Message { - public class MessageFactoryRegistry - { - private readonly Hashtable _mimeToFactoryMap = new Hashtable(); + public class MessageFactoryRegistry + { + private readonly Hashtable _mimeToFactoryMap = new Hashtable(); + private IMessageFactory _defaultFactory; - public void RegisterFactory(string mimeType, IMessageFactory mf) - { - if (mf == null) - { - throw new ArgumentNullException("Message factory"); - } - if (mimeType == null) - { - throw new ArgumentNullException("mf"); - } - _mimeToFactoryMap[mimeType] = mf; - } + /// <summary> + /// Default factory to use for unknown message types + /// </summary> + public IMessageFactory DefaultFactory + { + get { return _defaultFactory; } + set { _defaultFactory = value; } + } - public void DeregisterFactory(string mimeType) - { - _mimeToFactoryMap.Remove(mimeType); - } + /// <summary> + /// Register a new message factory for a MIME type + /// </summary> + /// <param name="mimeType">Mime type to register</param> + /// <param name="mf"></param> + public void RegisterFactory(string mimeType, IMessageFactory mf) + { + if ( mf == null ) + throw new ArgumentNullException("mf"); + if ( mimeType == null || mimeType.Length == 0 ) + throw new ArgumentNullException("mimeType"); - /// <summary> - /// Create a message. This looks up the MIME type from the content header and instantiates the appropriate - /// concrete message type. - /// </summary> - /// <param name="messageNbr">the AMQ message id</param> - /// <param name="redelivered">true if redelivered</param> - /// <param name="contentHeader">the content header that was received</param> - /// <param name="bodies">a list of ContentBody instances</param> - /// <returns>the message.</returns> - /// <exception cref="AMQException"/> - /// <exception cref="QpidException"/> - public AbstractQmsMessage CreateMessage(long messageNbr, bool redelivered, - ContentHeaderBody contentHeader, - IList bodies) - { - BasicContentHeaderProperties properties = (BasicContentHeaderProperties) contentHeader.Properties; + _mimeToFactoryMap[mimeType] = mf; + } - if (properties.ContentType == null) - { - properties.ContentType = ""; - } + /// <summary> + /// Remove a message factory + /// </summary> + /// <param name="mimeType">MIME type to unregister</param> + public void DeregisterFactory(string mimeType) + { + _mimeToFactoryMap.Remove(mimeType); + } - IMessageFactory mf = (IMessageFactory) _mimeToFactoryMap[properties.ContentType]; - if (mf == null) - { - throw new AMQException("Unsupport MIME type of " + properties.ContentType); - } - else - { - return mf.CreateMessage(messageNbr, redelivered, contentHeader, bodies); - } - } + /// <summary> + /// Create a message. This looks up the MIME type from the content header and instantiates the appropriate + /// concrete message type. + /// </summary> + /// <param name="messageNbr">the AMQ message id</param> + /// <param name="redelivered">true if redelivered</param> + /// <param name="contentHeader">the content header that was received</param> + /// <param name="bodies">a list of ContentBody instances</param> + /// <returns>the message.</returns> + /// <exception cref="AMQException"/> + /// <exception cref="QpidException"/> + public AbstractQmsMessage CreateMessage(long messageNbr, bool redelivered, + ContentHeaderBody contentHeader, + IList bodies) + { + BasicContentHeaderProperties properties = (BasicContentHeaderProperties)contentHeader.Properties; - public AbstractQmsMessage CreateMessage(string mimeType) - { - if (mimeType == null) - { - throw new ArgumentNullException("Mime type must not be null"); - } - IMessageFactory mf = (IMessageFactory) _mimeToFactoryMap[mimeType]; - if (mf == null) - { - throw new AMQException("Unsupport MIME type of " + mimeType); - } - else - { - return mf.CreateMessage(); - } - } + if ( properties.ContentType == null ) + { + properties.ContentType = ""; + } - /// <summary> - /// Construct a new registry with the default message factories registered - /// </summary> - /// <returns>a message factory registry</returns> - public static MessageFactoryRegistry NewDefaultRegistry() - { - MessageFactoryRegistry mf = new MessageFactoryRegistry(); - mf.RegisterFactory("text/plain", new QpidTextMessageFactory()); - mf.RegisterFactory("text/xml", new QpidTextMessageFactory()); - mf.RegisterFactory("application/octet-stream", new QpidBytesMessageFactory()); - // TODO: use bytes message for default message factory - // MJA - just added this bit back in... - mf.RegisterFactory("", new QpidBytesMessageFactory()); - return mf; - } - } + IMessageFactory mf = GetFactory(properties.ContentType); + return mf.CreateMessage(messageNbr, redelivered, contentHeader, bodies); + } + + /// <summary> + /// Create a new message of the specified type + /// </summary> + /// <param name="mimeType">The Mime type</param> + /// <returns>The new message</returns> + public AbstractQmsMessage CreateMessage(string mimeType) + { + if ( mimeType == null || mimeType.Length == 0 ) + throw new ArgumentNullException("mimeType"); + + IMessageFactory mf = GetFactory(mimeType); + return mf.CreateMessage(mimeType); + } + + /// <summary> + /// Construct a new registry with the default message factories registered + /// </summary> + /// <returns>a message factory registry</returns> + public static MessageFactoryRegistry NewDefaultRegistry() + { + MessageFactoryRegistry mf = new MessageFactoryRegistry(); + mf.RegisterFactory("text/plain", new QpidTextMessageFactory()); + mf.RegisterFactory("text/xml", new QpidTextMessageFactory()); + mf.RegisterFactory("application/octet-stream", new QpidBytesMessageFactory()); + + mf.DefaultFactory = new QpidBytesMessageFactory(); + return mf; + } + + private IMessageFactory GetFactory(string mimeType) + { + IMessageFactory mf = (IMessageFactory)_mimeToFactoryMap[mimeType]; + return mf != null ? mf : _defaultFactory; + } + } } diff --git a/dotnet/Qpid.Client/Client/Message/QpidBytesMessage.cs b/dotnet/Qpid.Client/Client/Message/QpidBytesMessage.cs index 32e47d852a..cb504d1378 100644 --- a/dotnet/Qpid.Client/Client/Message/QpidBytesMessage.cs +++ b/dotnet/Qpid.Client/Client/Message/QpidBytesMessage.cs @@ -43,8 +43,6 @@ namespace Qpid.Client.Message public class QpidBytesMessage : AbstractQmsMessage, IBytesMessage { - private const string MIME_TYPE = "application/octet-stream"; - private const int DEFAULT_BUFFER_INITIAL_SIZE = 1024; public QpidBytesMessage() : this(null) @@ -59,7 +57,6 @@ namespace Qpid.Client.Message QpidBytesMessage(ByteBuffer data) : base(data) { // superclass constructor has instantiated a content header at this point - ContentHeaderProperties.ContentType = MIME_TYPE; if (data == null) { _data = ByteBuffer.Allocate(DEFAULT_BUFFER_INITIAL_SIZE); @@ -71,7 +68,6 @@ namespace Qpid.Client.Message // TODO: this casting is ugly. Need to review whole ContentHeaderBody idea : base(messageNbr, (BasicContentHeaderProperties)contentHeader.Properties, data) { - ContentHeaderProperties.ContentType = MIME_TYPE; } public override void ClearBodyImpl() @@ -116,14 +112,6 @@ namespace Qpid.Client.Message } } - public override string MimeType - { - get - { - return MIME_TYPE; - } - } - public long BodyLength { get diff --git a/dotnet/Qpid.Client/Client/Message/QpidBytesMessageFactory.cs b/dotnet/Qpid.Client/Client/Message/QpidBytesMessageFactory.cs index de4c6675c7..e96c38cbac 100644 --- a/dotnet/Qpid.Client/Client/Message/QpidBytesMessageFactory.cs +++ b/dotnet/Qpid.Client/Client/Message/QpidBytesMessageFactory.cs @@ -62,9 +62,11 @@ namespace Qpid.Client.Message return new QpidBytesMessage(deliveryTag, contentHeader, data); } - public override AbstractQmsMessage CreateMessage() + public override AbstractQmsMessage CreateMessage(string mimeType) { - return new QpidBytesMessage(); + QpidBytesMessage msg = new QpidBytesMessage(); + msg.ContentType = mimeType; + return msg; } } diff --git a/dotnet/Qpid.Client/Client/Message/QpidTextMessage.cs b/dotnet/Qpid.Client/Client/Message/QpidTextMessage.cs index cff42f1df5..ae8bdb2074 100644 --- a/dotnet/Qpid.Client/Client/Message/QpidTextMessage.cs +++ b/dotnet/Qpid.Client/Client/Message/QpidTextMessage.cs @@ -28,25 +28,22 @@ namespace Qpid.Client.Message { public class QpidTextMessage : AbstractQmsMessage, ITextMessage { - private const string MIME_TYPE = "text/plain"; - private string _decodedValue = null; + private static Encoding DEFAULT_ENCODING = Encoding.UTF8; internal QpidTextMessage() : this(null, null) { + ContentEncoding = DEFAULT_ENCODING.BodyName; } - QpidTextMessage(ByteBuffer data, String encoding) : base(data) + internal QpidTextMessage(ByteBuffer data, String encoding) : base(data) { - ContentHeaderProperties.ContentType = MIME_TYPE; - ContentHeaderProperties.Encoding = encoding; + ContentEncoding = encoding; } internal QpidTextMessage(long deliveryTag, BasicContentHeaderProperties contentHeader, ByteBuffer data) :base(deliveryTag, contentHeader, data) { - contentHeader.ContentType = MIME_TYPE; - _data = data; // FIXME: Unnecessary - done in base class ctor. } public override void ClearBodyImpl() @@ -64,14 +61,6 @@ namespace Qpid.Client.Message return Text; } - public override string MimeType - { - get - { - return MIME_TYPE; - } - } - public string Text { get @@ -100,7 +89,7 @@ namespace Qpid.Client.Message } else { - _decodedValue = Encoding.Default.GetString(bytes); + _decodedValue = DEFAULT_ENCODING.GetString(bytes); } return _decodedValue; } diff --git a/dotnet/Qpid.Client/Client/Message/QpidTextMessageFactory.cs b/dotnet/Qpid.Client/Client/Message/QpidTextMessageFactory.cs index cc4f6dafe1..4730fa56ad 100644 --- a/dotnet/Qpid.Client/Client/Message/QpidTextMessageFactory.cs +++ b/dotnet/Qpid.Client/Client/Message/QpidTextMessageFactory.cs @@ -25,9 +25,11 @@ namespace Qpid.Client.Message { public class QpidTextMessageFactory : AbstractQmsMessageFactory { - public override AbstractQmsMessage CreateMessage() + public override AbstractQmsMessage CreateMessage(string mimeType) { - return new QpidTextMessage(); + QpidTextMessage msg = new QpidTextMessage(); + msg.ContentType = mimeType; + return msg; } protected override AbstractQmsMessage CreateMessage(long deliveryTag, ByteBuffer data, ContentHeaderBody contentHeader) diff --git a/dotnet/Qpid.Common/Framing/BasicContentHeaderProperties.cs b/dotnet/Qpid.Common/Framing/BasicContentHeaderProperties.cs index 75d67fdfb8..0c06a01eb4 100644 --- a/dotnet/Qpid.Common/Framing/BasicContentHeaderProperties.cs +++ b/dotnet/Qpid.Common/Framing/BasicContentHeaderProperties.cs @@ -25,144 +25,266 @@ using Qpid.Messaging; namespace Qpid.Framing { - public class BasicContentHeaderProperties : IContentHeaderProperties - { - private static readonly ILog _log = LogManager.GetLogger(typeof(BasicContentHeaderProperties)); + public class BasicContentHeaderProperties : IContentHeaderProperties + { + private static readonly ILog _log = LogManager.GetLogger(typeof(BasicContentHeaderProperties)); - public string ContentType; + private string _contentType; + private string _encoding; + private FieldTable _headers; + private byte _deliveryMode; + private byte _priority; + private string _correlationId; + private long _expiration; + private string _replyTo; + private string _messageId; + private ulong _timestamp; + private string _type; + private string _userId; + private string _appId; + private string _clusterId; - public string Encoding; - public FieldTable Headers; + #region Properties + // + // Properties + // - public byte DeliveryMode; + /// <summary> + /// The MIME Content Type + /// </summary> + public string ContentType + { + get { return _contentType; } + set { _contentType = value; } + } - public byte Priority; + /// <summary> + /// The MIME Content Encoding + /// </summary> + public string Encoding + { + get { return _encoding; } + set { _encoding = value; } + } - public string CorrelationId; + /// <summary> + /// Message headers + /// </summary> + public FieldTable Headers + { + get { return _headers; } + set { _headers = value; } + } - public long Expiration; + /// <summary> + /// Non-persistent (1) or persistent (2) + /// </summary> + public byte DeliveryMode + { + get { return _deliveryMode; } + set { _deliveryMode = value; } + } - public string ReplyTo; + /// <summary> + /// The message priority, 0 to 9 + /// </summary> + public byte Priority + { + get { return _priority; } + set { _priority = value; } + } - public string MessageId; + /// <summary> + /// The application correlation identifier + /// </summary> + public string CorrelationId + { + get { return _correlationId; } + set { _correlationId = value; } + } - public ulong Timestamp; + /// <summary> + /// Message expiration specification + /// </summary> + // TODO: Should be string according to spec + public long Expiration + { + get { return _expiration; } + set { _expiration = value; } + } - public string Type; + /// <summary> + /// The destination to reply to + /// </summary> + public string ReplyTo + { + get { return _replyTo; } + set { _replyTo = value; } + } - public string UserId; + /// <summary> + /// The application message identifier + /// </summary> + public string MessageId + { + get { return _messageId; } + set { _messageId = value; } + } - public string AppId; + /// <summary> + /// The message timestamp + /// </summary> + public ulong Timestamp + { + get { return _timestamp; } + set { _timestamp = value; } + } - public string ClusterId; - - public BasicContentHeaderProperties() - { - } + /// <summary> + /// The message type name + /// </summary> + public string Type + { + get { return _type; } + set { _type = value; } + } - public uint PropertyListSize - { - get - { - return (uint)(EncodingUtils.EncodedShortStringLength(ContentType) + - EncodingUtils.EncodedShortStringLength(Encoding) + - EncodingUtils.EncodedFieldTableLength(Headers) + - 1 + 1 + - EncodingUtils.EncodedShortStringLength(CorrelationId) + - EncodingUtils.EncodedShortStringLength(ReplyTo) + - EncodingUtils.EncodedShortStringLength(String.Format("{0:D}", Expiration)) + - EncodingUtils.EncodedShortStringLength(MessageId) + - 8 + - EncodingUtils.EncodedShortStringLength(Type) + - EncodingUtils.EncodedShortStringLength(UserId) + - EncodingUtils.EncodedShortStringLength(AppId) + - EncodingUtils.EncodedShortStringLength(ClusterId)); - - } - } + /// <summary> + /// The creating user id + /// </summary> + public string UserId + { + get { return _userId; } + set { _userId = value; } + } - public ushort PropertyFlags - { - get - { - int value = 0; - - // for now we just blast in all properties - for (int i = 0; i < 14; i++) - { - value += (1 << (15-i)); - } - return (ushort) value; - } - } - - public void WritePropertyListPayload(ByteBuffer buffer) - { - EncodingUtils.WriteShortStringBytes(buffer, ContentType); - EncodingUtils.WriteShortStringBytes(buffer, Encoding); - EncodingUtils.WriteFieldTableBytes(buffer, Headers); - buffer.Put(DeliveryMode); - buffer.Put(Priority); - EncodingUtils.WriteShortStringBytes(buffer, CorrelationId); - EncodingUtils.WriteShortStringBytes(buffer, ReplyTo); - EncodingUtils.WriteShortStringBytes(buffer, String.Format("{0:D}", Expiration)); - EncodingUtils.WriteShortStringBytes(buffer, MessageId); - buffer.Put(Timestamp); - EncodingUtils.WriteShortStringBytes(buffer, Type); - EncodingUtils.WriteShortStringBytes(buffer, UserId); - EncodingUtils.WriteShortStringBytes(buffer, AppId); - EncodingUtils.WriteShortStringBytes(buffer, ClusterId); - } - - public void PopulatePropertiesFromBuffer(ByteBuffer buffer, ushort propertyFlags) - { - _log.Debug("Property flags: " + propertyFlags); - if ((propertyFlags & (1 << 15)) > 0) - ContentType = EncodingUtils.ReadShortString(buffer); - if ((propertyFlags & (1 << 14)) > 0) - Encoding = EncodingUtils.ReadShortString(buffer); - if ((propertyFlags & (1 << 13)) > 0) - Headers = EncodingUtils.ReadFieldTable(buffer); - if ((propertyFlags & (1 << 12)) > 0) - DeliveryMode = buffer.GetByte(); - if ((propertyFlags & (1 << 11)) > 0) - Priority = buffer.GetByte(); - if ((propertyFlags & (1 << 10)) > 0) - CorrelationId = EncodingUtils.ReadShortString(buffer); - if ((propertyFlags & (1 << 9)) > 0) - ReplyTo = EncodingUtils.ReadShortString(buffer); - if ((propertyFlags & (1 << 8)) > 0) - Expiration = EncodingUtils.ReadLongAsShortString(buffer); - if ((propertyFlags & (1 << 7)) > 0) - MessageId = EncodingUtils.ReadShortString(buffer); - if ((propertyFlags & (1 << 6)) > 0) - Timestamp = buffer.GetUInt64(); - if ((propertyFlags & (1 << 5)) > 0) - Type = EncodingUtils.ReadShortString(buffer); - if ((propertyFlags & (1 << 4)) > 0) - UserId = EncodingUtils.ReadShortString(buffer); - if ((propertyFlags & (1 << 3)) > 0) - AppId = EncodingUtils.ReadShortString(buffer); - if ((propertyFlags & (1 << 2)) > 0) - ClusterId = EncodingUtils.ReadShortString(buffer); - } - - public void SetDeliveryMode(DeliveryMode deliveryMode) - { - if (deliveryMode == Messaging.DeliveryMode.NonPersistent) - { - DeliveryMode = 1; - } - else + /// <summary> + /// The creating application id + /// </summary> + public string AppId + { + get { return _appId; } + set { _appId = value; } + } + + /// <summary> + /// Intra-cluster routing identifier + /// </summary> + public string ClusterId + { + get { return _clusterId; } + set { _clusterId = value; } + } + + #endregion // Properties + + + public BasicContentHeaderProperties() + { + } + + public uint PropertyListSize + { + get + { + return (uint)(EncodingUtils.EncodedShortStringLength(ContentType) + + EncodingUtils.EncodedShortStringLength(Encoding) + + EncodingUtils.EncodedFieldTableLength(Headers) + + 1 + 1 + + EncodingUtils.EncodedShortStringLength(CorrelationId) + + EncodingUtils.EncodedShortStringLength(ReplyTo) + + EncodingUtils.EncodedShortStringLength(String.Format("{0:D}", Expiration)) + + EncodingUtils.EncodedShortStringLength(MessageId) + + 8 + + EncodingUtils.EncodedShortStringLength(Type) + + EncodingUtils.EncodedShortStringLength(UserId) + + EncodingUtils.EncodedShortStringLength(AppId) + + EncodingUtils.EncodedShortStringLength(ClusterId)); + + } + } + + public ushort PropertyFlags + { + get + { + int value = 0; + + // for now we just blast in all properties + for ( int i = 0; i < 14; i++ ) { - DeliveryMode = 2; + value += (1 << (15 - i)); } - } + return (ushort)value; + } + } + + public void WritePropertyListPayload(ByteBuffer buffer) + { + EncodingUtils.WriteShortStringBytes(buffer, ContentType); + EncodingUtils.WriteShortStringBytes(buffer, Encoding); + EncodingUtils.WriteFieldTableBytes(buffer, Headers); + buffer.Put(DeliveryMode); + buffer.Put(Priority); + EncodingUtils.WriteShortStringBytes(buffer, CorrelationId); + EncodingUtils.WriteShortStringBytes(buffer, ReplyTo); + EncodingUtils.WriteShortStringBytes(buffer, String.Format("{0:D}", Expiration)); + EncodingUtils.WriteShortStringBytes(buffer, MessageId); + buffer.Put(Timestamp); + EncodingUtils.WriteShortStringBytes(buffer, Type); + EncodingUtils.WriteShortStringBytes(buffer, UserId); + EncodingUtils.WriteShortStringBytes(buffer, AppId); + EncodingUtils.WriteShortStringBytes(buffer, ClusterId); + } + + public void PopulatePropertiesFromBuffer(ByteBuffer buffer, ushort propertyFlags) + { + _log.Debug("Property flags: " + propertyFlags); + if ( (propertyFlags & (1 << 15)) > 0 ) + ContentType = EncodingUtils.ReadShortString(buffer); + if ( (propertyFlags & (1 << 14)) > 0 ) + Encoding = EncodingUtils.ReadShortString(buffer); + if ( (propertyFlags & (1 << 13)) > 0 ) + Headers = EncodingUtils.ReadFieldTable(buffer); + if ( (propertyFlags & (1 << 12)) > 0 ) + DeliveryMode = buffer.GetByte(); + if ( (propertyFlags & (1 << 11)) > 0 ) + Priority = buffer.GetByte(); + if ( (propertyFlags & (1 << 10)) > 0 ) + CorrelationId = EncodingUtils.ReadShortString(buffer); + if ( (propertyFlags & (1 << 9)) > 0 ) + ReplyTo = EncodingUtils.ReadShortString(buffer); + if ( (propertyFlags & (1 << 8)) > 0 ) + Expiration = EncodingUtils.ReadLongAsShortString(buffer); + if ( (propertyFlags & (1 << 7)) > 0 ) + MessageId = EncodingUtils.ReadShortString(buffer); + if ( (propertyFlags & (1 << 6)) > 0 ) + Timestamp = buffer.GetUInt64(); + if ( (propertyFlags & (1 << 5)) > 0 ) + Type = EncodingUtils.ReadShortString(buffer); + if ( (propertyFlags & (1 << 4)) > 0 ) + UserId = EncodingUtils.ReadShortString(buffer); + if ( (propertyFlags & (1 << 3)) > 0 ) + AppId = EncodingUtils.ReadShortString(buffer); + if ( (propertyFlags & (1 << 2)) > 0 ) + ClusterId = EncodingUtils.ReadShortString(buffer); + } + + public void SetDeliveryMode(DeliveryMode deliveryMode) + { + if ( deliveryMode == Messaging.DeliveryMode.NonPersistent ) + { + DeliveryMode = 1; + } else + { + DeliveryMode = 2; + } + } - public override string ToString() - { - return "Properties: " + ContentType + " " + Encoding + " " + Timestamp + " " + Type; - } - } + public override string ToString() + { + return "Properties: " + ContentType + " " + Encoding + " " + Timestamp + " " + Type; + } + } } diff --git a/dotnet/Qpid.Messaging/IChannel.cs b/dotnet/Qpid.Messaging/IChannel.cs index 7fceb1a532..37daccf730 100644 --- a/dotnet/Qpid.Messaging/IChannel.cs +++ b/dotnet/Qpid.Messaging/IChannel.cs @@ -23,42 +23,124 @@ using System; namespace Qpid.Messaging { public delegate void MessageReceivedDelegate(IMessage msg); - + + /// <summary> + /// Interface used to manipulate an AMQP channel. + /// </summary> + /// <remarks> + /// You can create a channel by using the CreateChannel() method + /// of the connection object. + /// </remarks> public interface IChannel : IDisposable { + /// <summary> + /// Acknowledge mode for messages received + /// </summary> AcknowledgeMode AcknowledgeMode { get; } + /// <summary> + /// True if the channel should use transactions + /// </summary> bool Transacted { get; } /// <summary> /// Prefetch value to be used as the default for consumers created on this channel. /// </summary> - int DefaultPrefetch - { - get; - set; - } - + int DefaultPrefetch { get; set; } + + /// <summary> + /// Declare a new exchange + /// </summary> + /// <param name="exchangeName">Name of the exchange</param> + /// <param name="exchangeClass">Class of the exchange, from <see cref="ExchangeClassConstants"/></param> void DeclareExchange(string exchangeName, string exchangeClass); + /// <summary> + /// Declare a new exchange using the default exchange class + /// </summary> + /// <param name="exchangeName">Name of the exchange</param> void DeleteExchange(string exchangeName); + /// <summary> + /// Declare a new queue with the specified set of arguments + /// </summary> + /// <param name="queueName">Name of the queue</param> + /// <param name="isDurable">True if the queue should be durable</param> + /// <param name="isExclusive">True if the queue should be exclusive to this channel</param> + /// <param name="isAutoDelete">True if the queue should be deleted when the channel closes</param> void DeclareQueue(string queueName, bool isDurable, bool isExclusive, bool isAutoDelete); + /// <summary> + /// Deletes a queue (todo: fix) + /// </summary> void DeleteQueue(); - + /// <summary> + /// Generate a new Unique name to use for a queue + /// </summary> + /// <returns>A unique name to this channel</returns> string GenerateUniqueName(); - IFieldTable CreateFieldTable(); - + + /// <summary> + /// Bind a queue to the specified exchange + /// </summary> + /// <param name="queueName">Name of queue to bind</param> + /// <param name="exchangeName">Name of exchange to bind to</param> + /// <param name="routingKey">Routing key</param> void Bind(string queueName, string exchangeName, string routingKey); + /// <summary> + /// Bind a queue to the specified exchange + /// </summary> + /// <param name="queueName">Name of queue to bind</param> + /// <param name="exchangeName">Name of exchange to bind to</param> + /// <param name="routingKey">Routing key</param> + /// <param name="args">Table of arguments for the binding. Used to bind with a Headers Exchange</param> void Bind(string queueName, string exchangeName, string routingKey, IFieldTable args); + /// <summary> + /// Create a new empty message with no body + /// </summary> + /// <returns>The new message</returns> IMessage CreateMessage(); + /// <summary> + /// Create a new message of the specified MIME type + /// </summary> + /// <param name="mimeType">The mime type to create</param> + /// <returns>The new message</returns> + IMessage CreateMessage(string mimeType); + /// <summary> + /// Creates a new message for bytes (application/octet-stream) + /// </summary> + /// <returns>The new message</returns> IBytesMessage CreateBytesMessage(); + /// <summary> + /// Creates a new text message (text/plain) with empty content + /// </summary> + /// <returns>The new message</returns> ITextMessage CreateTextMessage(); + /// <summary> + /// Creates a new text message (text/plain) with a body + /// </summary> + /// <param name="initialValue">Initial body of the message</param> + /// <returns>The new message</returns> ITextMessage CreateTextMessage(string initialValue); #region Consuming - + + /// <summary> + /// Creates a new Consumer using the builder pattern + /// </summary> + /// <param name="queueName">Name of queue to receive messages from</param> + /// <returns>The builder object</returns> MessageConsumerBuilder CreateConsumerBuilder(string queueName); + /// <summary> + /// Creates a new consumer + /// </summary> + /// <param name="queueName">Name of queue to receive messages from</param> + /// <param name="prefetchLow">Low prefetch value</param> + /// <param name="prefetchHigh">High prefetch value</param> + /// <param name="noLocal">If true, messages sent on this channel will not be received by this consumer</param> + /// <param name="exclusive">If true, the consumer opens the queue in exclusive mode</param> + /// <param name="durable">If true, create a durable subscription</param> + /// <param name="subscriptionName">Subscription name</param> + /// <returns>The new consumer</returns> IMessageConsumer CreateConsumer(string queueName, int prefetchLow, int prefetchHigh, @@ -66,15 +148,35 @@ namespace Qpid.Messaging bool exclusive, bool durable, string subscriptionName); - + + /// <summary> + /// Unsubscribe from a queue + /// </summary> + /// <param name="subscriptionName">Subscription name</param> void Unsubscribe(string subscriptionName); #endregion #region Publishing + /// <summary> + /// Create a new message publisher using the builder pattern + /// </summary> + /// <returns>The builder object</returns> MessagePublisherBuilder CreatePublisherBuilder(); - + + /// <summary> + /// Create a new message publisher + /// </summary> + /// <param name="exchangeName">Name of exchange to publish to</param> + /// <param name="routingKey">Routing key</param> + /// <param name="deliveryMode">Default delivery mode</param> + /// <param name="timeToLive">Default TTL time of messages</param> + /// <param name="immediate">If true, sent immediately</param> + /// <param name="mandatory">If true, the broker will return an error + /// (as a connection exception) if the message cannot be delivered</param> + /// <param name="priority">Default message priority</param> + /// <returns>The new message publisher</returns> IMessagePublisher CreatePublisher(string exchangeName, string routingKey, DeliveryMode deliveryMode, @@ -86,9 +188,18 @@ namespace Qpid.Messaging #endregion #region Transactions - + + /// <summary> + /// Recover after transaction failure + /// </summary> void Recover(); + /// <summary> + /// Commit the transaction + /// </summary> void Commit(); + /// <summary> + /// Rollback the transaction + /// </summary> void Rollback(); #endregion diff --git a/dotnet/Qpid.Messaging/IMessage.cs b/dotnet/Qpid.Messaging/IMessage.cs index 60febc942a..d63662f7e3 100644 --- a/dotnet/Qpid.Messaging/IMessage.cs +++ b/dotnet/Qpid.Messaging/IMessage.cs @@ -22,25 +22,75 @@ namespace Qpid.Messaging { public interface IMessage { - string ContentType { get; set;} - string ContentEncoding { get; set; } - string CorrelationId { get; set; } - byte[] CorrelationIdAsBytes { get; set; } - DeliveryMode DeliveryMode { get; set; } - long Expiration { get; set; } - string MessageId { get; set; } - int Priority { get; set; } + /// <summary> + /// The MIME Content Type + /// </summary> + string ContentType { get; set;} + /// <summary> + /// The MIME Content Encoding + /// </summary> + string ContentEncoding { get; set; } + /// <summary> + /// The application correlation identifier + /// </summary> + string CorrelationId { get; set; } + /// <summary> + /// The application correlation identifier, as an array of bytes + /// </summary> + byte[] CorrelationIdAsBytes { get; set; } + /// <summary> + /// Non-persistent (1) or persistent (2) + /// </summary> + DeliveryMode DeliveryMode { get; set; } + /// <summary> + /// Message expiration specification + /// </summary> + long Expiration { get; set; } + /// <summary> + /// The application message identifier + /// </summary> + string MessageId { get; set; } + /// <summary> + /// The message priority, 0 to 9 + /// </summary> + byte Priority { get; set; } + /// <summary> + /// True if the message has been redelivered + /// </summary> bool Redelivered { get; set; } + /// <summary> + /// Exchange name of the reply-to address + /// </summary> string ReplyToExchangeName { get; set; } + /// <summary> + /// Routing key of the reply-to address + /// </summary> string ReplyToRoutingKey { get; set; } + /// <summary> + /// The message timestamp + /// </summary> long Timestamp { get; set; } + /// <summary> + /// The message type name + /// </summary> string Type { get; set; } + /// <summary> + /// Message headers + /// </summary> IHeaders Headers { get; } - - // XXX: UserId? - // XXX: AppId? - // XXX: ClusterId? - + /// <summary> + /// The creating user id + /// </summary> + string UserId { get; set; } + /// <summary> + /// The creating application id + /// </summary> + string AppId { get; set; } + /// <summary> + /// Intra-cluster routing identifier + /// </summary> + string ClusterId { get; set; } + void Acknowledge(); void ClearBody(); } |