You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@activemq.apache.org by ta...@apache.org on 2017/03/08 13:45:14 UTC

[08/13] activemq-nms-xms git commit: Initial check-in of new Apache.NMS.XMS provider implementation. Big thanks to Stéphane Ramet for the implementation! Fixes [AMQNET-185]. (See https://issues.apache.org/jira/browse/AMQNET-185)

http://git-wip-us.apache.org/repos/asf/activemq-nms-xms/blob/653d676d/src/main/csharp/StreamMessage.cs
----------------------------------------------------------------------
diff --git a/src/main/csharp/StreamMessage.cs b/src/main/csharp/StreamMessage.cs
new file mode 100644
index 0000000..b25579b
--- /dev/null
+++ b/src/main/csharp/StreamMessage.cs
@@ -0,0 +1,480 @@
+/*
+ * 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 Apache.NMS;
+using Apache.NMS.Util;
+using Apache.NMS.XMS.Util;
+using IBM.XMS;
+
+namespace Apache.NMS.XMS
+{
+    /// <summary>
+    /// A StreamMessage object is used to send a stream of primitive types in the 
+    /// .NET programming language. It is filled and read sequentially. It inherits 
+    /// from the Message interface and adds a stream message body.
+    /// </summary>
+	class StreamMessage : Apache.NMS.XMS.Message, Apache.NMS.IStreamMessage
+	{
+		#region Constructors and access to internal stream message
+
+		/// <summary>
+		/// Internal IBM XMS stream message.
+		/// </summary>
+		public IBM.XMS.IStreamMessage xmsStreamMessage
+		{
+			get { return (IBM.XMS.IStreamMessage)this.xmsMessage; }
+			set { this.xmsMessage = value; }
+		}
+
+		/// <summary>
+		/// Constructs a <c>StreamMessage</c> object.
+		/// </summary>
+		/// <param name="message">XMS stream message.</param>
+		public StreamMessage(IBM.XMS.IStreamMessage message)
+			: base(message)
+		{
+		}
+
+		#endregion
+
+		#region IStreamMessage Members
+
+		#region Reset method
+
+		/// <summary>
+		/// Resets the contents of the stream message body.
+		/// </summary>
+		public void Reset()
+		{
+			try
+			{
+				this.xmsStreamMessage.Reset();
+			}
+			catch(Exception ex)
+			{
+				ExceptionUtil.WrapAndThrowNMSException(ex);
+			}
+		}
+
+		#endregion
+
+		#region Read methods
+
+		/// <summary>
+		/// Reads a boolean from the stream message.
+		/// </summary>
+		/// <returns>A <see cref="System.Boolean"/></returns>
+		public bool ReadBoolean()
+		{
+			try
+			{
+				return this.xmsStreamMessage.ReadBoolean();
+			}
+			catch(Exception ex)
+			{
+				ExceptionUtil.WrapAndThrowNMSException(ex);
+				return false;
+			}
+		}
+
+		/// <summary>
+		/// Reads a byte from the stream message.
+		/// </summary>
+		/// <returns>A <see cref="System.Byte"/></returns>
+		public byte ReadByte()
+		{
+			try
+			{
+				return this.xmsStreamMessage.ReadByte();
+			}
+			catch(Exception ex)
+			{
+				ExceptionUtil.WrapAndThrowNMSException(ex);
+				return 0;
+			}
+		}
+
+		/// <summary>
+		/// Reads a byte array from the stream message.
+		/// </summary>
+		/// <param name="value">A <see cref="System.Byte"/> array</param>
+		/// <returns>The total number of bytes read into the buffer, or -1 if
+		/// there is no more data because the end of the byte field has been
+		/// reached</returns>
+		public int ReadBytes(byte[] value)
+		{
+			try
+			{
+				return this.xmsStreamMessage.ReadBytes(value);
+			}
+			catch(Exception ex)
+			{
+				ExceptionUtil.WrapAndThrowNMSException(ex);
+				return 0;
+			}
+		}
+
+		/// <summary>
+		/// Reads a character from the stream message.
+		/// </summary>
+		/// <returns>A <see cref="System.Char"/></returns>
+		public char ReadChar()
+		{
+			try
+			{
+				return this.xmsStreamMessage.ReadChar();
+			}
+			catch(Exception ex)
+			{
+				ExceptionUtil.WrapAndThrowNMSException(ex);
+				return (char) 0;
+			}
+		}
+
+		/// <summary>
+		/// Reads a 16 bits (short) integer number from the stream message.
+		/// </summary>
+		/// <returns>A <see cref="System.Int16"/></returns>
+		public short ReadInt16()
+		{
+			try
+			{
+				return this.xmsStreamMessage.ReadShort();
+			}
+			catch(Exception ex)
+			{
+				ExceptionUtil.WrapAndThrowNMSException(ex);
+				return 0;
+			}
+		}
+
+		/// <summary>
+		/// Reads a 32 bits (int) integer number from the stream message.
+		/// </summary>
+		/// <returns>A <see cref="System.Int32"/></returns>
+		public int ReadInt32()
+		{
+			try
+			{
+				return this.xmsStreamMessage.ReadInt();
+			}
+			catch(Exception ex)
+			{
+				ExceptionUtil.WrapAndThrowNMSException(ex);
+				return 0;
+			}
+		}
+
+		/// <summary>
+		/// Reads a 64 bits (long) integer number from the stream message.
+		/// </summary>
+		/// <returns>A <see cref="System.Int64"/></returns>
+		public long ReadInt64()
+		{
+			try
+			{
+				return this.xmsStreamMessage.ReadLong();
+			}
+			catch(Exception ex)
+			{
+				ExceptionUtil.WrapAndThrowNMSException(ex);
+				return 0;
+			}
+		}
+
+		/// <summary>
+		/// Reads a single precision floating point number from the stream
+		/// message.
+		/// </summary>
+		/// <returns>A <see cref="System.Single"/></returns>
+		public float ReadSingle()
+		{
+			try
+			{
+				return this.xmsStreamMessage.ReadFloat();
+			}
+			catch(Exception ex)
+			{
+				ExceptionUtil.WrapAndThrowNMSException(ex);
+				return 0;
+			}
+		}
+
+		/// <summary>
+		/// Reads a double precision floating point number from the stream
+		/// message.
+		/// </summary>
+		/// <returns>A <see cref="System.Double"/></returns>
+		public double ReadDouble()
+		{
+			try
+			{
+				return this.xmsStreamMessage.ReadDouble();
+			}
+			catch(Exception ex)
+			{
+				ExceptionUtil.WrapAndThrowNMSException(ex);
+				return 0;
+			}
+		}
+
+		/// <summary>
+		/// Reads a character string from the stream message.
+		/// </summary>
+		/// <returns>A <see cref="System.String"/></returns>
+		public string ReadString()
+		{
+			try
+			{
+				return this.xmsStreamMessage.ReadString();
+			}
+			catch(Exception ex)
+			{
+				ExceptionUtil.WrapAndThrowNMSException(ex);
+				return null;
+			}
+		}
+
+		/// <summary>
+		/// Reads an object from the stream message.
+		/// </summary>
+		/// <returns>A <see cref="System.Object"/></returns>
+		public object ReadObject()
+		{
+			try
+			{
+				return this.xmsStreamMessage.ReadObject();
+			}
+			catch(Exception ex)
+			{
+				ExceptionUtil.WrapAndThrowNMSException(ex);
+				return 0;
+			}
+		}
+
+		#endregion
+
+		#region Write methods
+
+		/// <summary>
+		/// Writes a boolean to the stream message.
+		/// </summary>
+		/// <param name="value">A <see cref="System.Boolean"/></param>
+		public void WriteBoolean(bool value)
+		{
+			try
+			{
+				this.xmsStreamMessage.WriteBoolean(value);
+			}
+			catch(Exception ex)
+			{
+				ExceptionUtil.WrapAndThrowNMSException(ex);
+			}
+		}
+
+		/// <summary>
+		/// Writes a byte to the stream message.
+		/// </summary>
+		/// <param name="value">A <see cref="System.Byte"/></param>
+		public void WriteByte(byte value)
+		{
+			try
+			{
+				this.xmsStreamMessage.WriteByte(value);
+			}
+			catch(Exception ex)
+			{
+				ExceptionUtil.WrapAndThrowNMSException(ex);
+			}
+		}
+
+		/// <summary>
+		/// Writes a byte array to the stream message.
+		/// </summary>
+		/// <param name="value">A <see cref="System.Byte"/> array</param>
+		public void WriteBytes(byte[] value)
+		{
+			try
+			{
+				this.xmsStreamMessage.WriteBytes(value);
+			}
+			catch(Exception ex)
+			{
+				ExceptionUtil.WrapAndThrowNMSException(ex);
+			}
+		}
+
+		/// <summary>
+		/// Writes a portion of a byte array as a byte array field to the
+		/// stream message.
+		/// </summary>
+		/// <param name="value">A <see cref="System.Byte"/> array</param>
+		/// <param name="offset">A <see cref="System.Int32"/> value that
+		/// indicates the point in the buffer to begin writing to the stream
+		/// message.</param>
+		/// <param name="length">A <see cref="System.Int32"/> value that
+		/// indicates how many bytes in the buffer to write to the stream
+		/// message.</param>
+		public void WriteBytes(byte[] value, int offset, int length)
+		{
+			try
+			{
+				this.xmsStreamMessage.WriteBytes(value, offset, length);
+			}
+			catch(Exception ex)
+			{
+				ExceptionUtil.WrapAndThrowNMSException(ex);
+			}
+		}
+
+		/// <summary>
+		/// Writes a character to the stream message.
+		/// </summary>
+		/// <param name="value">A <see cref="System.Char"/></param>
+		public void WriteChar(char value)
+		{
+			try
+			{
+				this.xmsStreamMessage.WriteChar(value);
+			}
+			catch(Exception ex)
+			{
+				ExceptionUtil.WrapAndThrowNMSException(ex);
+			}
+		}
+
+		/// <summary>
+		/// Writes a 16 bts (short) integer to the stream message.
+		/// </summary>
+		/// <param name="value">A <see cref="System.Int16"/></param>
+		public void WriteInt16(short value)
+		{
+			try
+			{
+				this.xmsStreamMessage.WriteShort(value);
+			}
+			catch(Exception ex)
+			{
+				ExceptionUtil.WrapAndThrowNMSException(ex);
+			}
+		}
+
+		/// <summary>
+		/// Writes a 32 bts (int) integer to the stream message.
+		/// </summary>
+		/// <param name="value">A <see cref="System.Int32"/></param>
+		public void WriteInt32(int value)
+		{
+			try
+			{
+				this.xmsStreamMessage.WriteInt(value);
+			}
+			catch(Exception ex)
+			{
+				ExceptionUtil.WrapAndThrowNMSException(ex);
+			}
+		}
+
+		/// <summary>
+		/// Writes a 64 bts (long) integer to the stream message.
+		/// </summary>
+		/// <param name="value">A <see cref="System.Int64"/></param>
+		public void WriteInt64(long value)
+		{
+			try
+			{
+				this.xmsStreamMessage.WriteLong(value);
+			}
+			catch(Exception ex)
+			{
+				ExceptionUtil.WrapAndThrowNMSException(ex);
+			}
+		}
+
+		/// <summary>
+		/// Writes a single precision floating point number to the stream
+		/// message.
+		/// </summary>
+		/// <param name="value">A <see cref="System.Single"/></param>
+		public void WriteSingle(float value)
+		{
+			try
+			{
+				this.xmsStreamMessage.WriteFloat(value);
+			}
+			catch(Exception ex)
+			{
+				ExceptionUtil.WrapAndThrowNMSException(ex);
+			}
+		}
+
+		/// <summary>
+		/// Writes a double precision floating point number to the stream
+		/// message.
+		/// </summary>
+		/// <param name="value">A <see cref="System.Double"/></param>
+		public void WriteDouble(double value)
+		{
+			try
+			{
+				this.xmsStreamMessage.WriteDouble(value);
+			}
+			catch(Exception ex)
+			{
+				ExceptionUtil.WrapAndThrowNMSException(ex);
+			}
+		}
+
+		/// <summary>
+		/// Writes a character string to the stream
+		/// message.
+		/// </summary>
+		/// <param name="value">A <see cref="System.String"/></param>
+		public void WriteString(string value)
+		{
+			try
+			{
+				this.xmsStreamMessage.WriteString(value);
+			}
+			catch(Exception ex)
+			{
+				ExceptionUtil.WrapAndThrowNMSException(ex);
+			}
+		}
+
+		/// <summary>
+		/// Writes an object to the stream
+		/// message.
+		/// </summary>
+		/// <param name="value">A <see cref="System.Object"/></param>
+		public void WriteObject(object value)
+		{
+			try
+			{
+				this.xmsStreamMessage.WriteObject(value);
+			}
+			catch(Exception ex)
+			{
+				ExceptionUtil.WrapAndThrowNMSException(ex);
+			}
+		}
+
+		#endregion
+
+		#endregion
+	}
+}

http://git-wip-us.apache.org/repos/asf/activemq-nms-xms/blob/653d676d/src/main/csharp/TemporaryQueue.cs
----------------------------------------------------------------------
diff --git a/src/main/csharp/TemporaryQueue.cs b/src/main/csharp/TemporaryQueue.cs
new file mode 100644
index 0000000..4d896c5
--- /dev/null
+++ b/src/main/csharp/TemporaryQueue.cs
@@ -0,0 +1,62 @@
+/*
+ * 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.
+ */
+
+namespace Apache.NMS.XMS
+{
+	class TemporaryQueue : Apache.NMS.XMS.Queue, Apache.NMS.ITemporaryQueue
+	{
+		#region Constructors and destructors
+
+		/// <summary>
+		/// Constructs a <c>TemporaryQueue</c> object.
+		/// </summary>
+		/// <param name="temporaryQueue">IBM XMS queue</param>
+		public TemporaryQueue(IBM.XMS.IDestination temporaryQueue)
+			: base(temporaryQueue, true)
+		{
+		}
+
+		#endregion
+
+		#region ITemporaryQueue Members
+
+		/// <summary>
+		/// Deletes the temporary queue.
+		/// </summary>
+		public void Delete()
+		{
+			// IBM.XMS does not provide a method for deleting a destination.
+			// Should we throw an exception or ignore the request ?
+			//this.xmsDestination.Delete();
+		}
+
+		#endregion
+
+		#region ToString
+
+		/// <summary>
+		/// Returns a string representation of this instance.
+		/// </summary>
+		/// <returns>String representation of this instance</returns>
+		public override System.String ToString()
+		{
+			return "temp-queue://" + QueueName;
+		}
+
+		#endregion
+	}
+}

http://git-wip-us.apache.org/repos/asf/activemq-nms-xms/blob/653d676d/src/main/csharp/TemporaryTopic.cs
----------------------------------------------------------------------
diff --git a/src/main/csharp/TemporaryTopic.cs b/src/main/csharp/TemporaryTopic.cs
new file mode 100644
index 0000000..374b528
--- /dev/null
+++ b/src/main/csharp/TemporaryTopic.cs
@@ -0,0 +1,62 @@
+/*
+ * 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.
+ */
+
+namespace Apache.NMS.XMS
+{
+	class TemporaryTopic : Apache.NMS.XMS.Topic, Apache.NMS.ITemporaryTopic
+	{
+		#region Constructors and destructors
+
+		/// <summary>
+		/// Constructs a <c>TemporaryTopic</c> object.
+		/// </summary>
+		/// <param name="temporaryTopic">IBM XMS queue</param>
+		public TemporaryTopic(IBM.XMS.IDestination temporaryTopic)
+			: base(temporaryTopic, true)
+		{
+		}
+
+		#endregion
+
+		#region ITemporaryTopic Members
+
+		/// <summary>
+		/// Deletes the temporary topic.
+		/// </summary>
+		public void Delete()
+		{
+			// IBM.XMS does not provide a method for deleting a destination.
+			// Should we throw an exception or ignore the request ?
+			//this.xmsDestination.Delete();
+		}
+
+		#endregion
+
+		#region ToString
+
+		/// <summary>
+		/// Returns a string representation of this instance.
+		/// </summary>
+		/// <returns>string representation of this instance</returns>
+		public override System.String ToString()
+		{
+			return "temp-topic://" + TopicName;
+		}
+
+		#endregion
+	}
+}

http://git-wip-us.apache.org/repos/asf/activemq-nms-xms/blob/653d676d/src/main/csharp/TextMessage.cs
----------------------------------------------------------------------
diff --git a/src/main/csharp/TextMessage.cs b/src/main/csharp/TextMessage.cs
new file mode 100644
index 0000000..d98cd03
--- /dev/null
+++ b/src/main/csharp/TextMessage.cs
@@ -0,0 +1,86 @@
+/*
+ * 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 Apache.NMS;
+using Apache.NMS.Util;
+using Apache.NMS.XMS.Util;
+using IBM.XMS;
+
+namespace Apache.NMS.XMS
+{
+	/// <summary>
+	/// Represents a text based message.
+	/// </summary>
+	class TextMessage : Apache.NMS.XMS.Message, Apache.NMS.ITextMessage
+	{
+		#region Constructors and access to internal stream message
+
+		/// <summary>
+		/// Internal IBM XMS text message.
+		/// </summary>
+		public IBM.XMS.ITextMessage xmsTextMessage
+		{
+			get { return (IBM.XMS.ITextMessage)this.xmsMessage; }
+			set { this.xmsMessage = value; }
+		}
+
+		/// <summary>
+		/// Constructs a <c>TextMessage</c> object.
+		/// </summary>
+		/// <param name="message">XMS text message.</param>
+		public TextMessage(IBM.XMS.ITextMessage message)
+			: base(message)
+		{
+		}
+
+		#endregion
+
+		#region ITextMessage Members
+
+		/// <summary>
+		/// The text contents of the message body.
+		/// </summary>
+		public string Text
+		{
+			get
+			{
+				try
+				{
+					return this.xmsTextMessage.Text;
+				}
+				catch(Exception ex)
+				{
+					ExceptionUtil.WrapAndThrowNMSException(ex);
+					return null;
+				}
+			}
+			set
+			{
+				try
+				{
+					this.xmsTextMessage.Text = value;
+				}
+				catch(Exception ex)
+				{
+					ExceptionUtil.WrapAndThrowNMSException(ex);
+				}
+			}
+		}
+
+		#endregion
+	}
+}

http://git-wip-us.apache.org/repos/asf/activemq-nms-xms/blob/653d676d/src/main/csharp/Topic.cs
----------------------------------------------------------------------
diff --git a/src/main/csharp/Topic.cs b/src/main/csharp/Topic.cs
new file mode 100644
index 0000000..c4cc4c2
--- /dev/null
+++ b/src/main/csharp/Topic.cs
@@ -0,0 +1,72 @@
+/*
+ * 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 Apache.NMS;
+using Apache.NMS.Util;
+using Apache.NMS.XMS.Util;
+using IBM.XMS;
+
+namespace Apache.NMS.XMS
+{
+	public class Topic : Apache.NMS.XMS.Destination, Apache.NMS.ITopic
+	{
+		#region Constructors and destructors
+
+		/// <summary>
+		/// Constructs a <c>Topic</c> object.
+		/// </summary>
+		/// <param name="topic">IBM XMS topic</param>
+		public Topic(IBM.XMS.IDestination topic)
+			: base(topic)
+		{
+		}
+
+		/// <summary>
+		/// Constructs a <c>Topic</c> object.
+		/// </summary>
+		/// <param name="topic">IBM XMS topic</param>
+		/// <param name="isTemporary">Whether the topic is temporary</param>
+		public Topic(IBM.XMS.IDestination topic, bool isTemporary)
+			: base(topic, isTemporary)
+		{
+		}
+
+		#endregion
+
+        #region ITopic Members
+
+		public string TopicName
+		{
+			get { return this.xmsDestination.Name; }
+		}
+
+		#endregion
+
+		#region ToString
+
+		/// <summary>
+		/// Returns a String representation of this instance.
+		/// </summary>
+		/// <returns>String representation of this instance</returns>
+		public override System.String ToString()
+		{
+			return "topic://" + TopicName;
+		}
+
+		#endregion
+	}
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/activemq-nms-xms/blob/653d676d/src/main/csharp/Util/Dispatcher.cs
----------------------------------------------------------------------
diff --git a/src/main/csharp/Util/Dispatcher.cs b/src/main/csharp/Util/Dispatcher.cs
new file mode 100644
index 0000000..d036cb3
--- /dev/null
+++ b/src/main/csharp/Util/Dispatcher.cs
@@ -0,0 +1,173 @@
+/*
+ * 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.Collections;
+using System.Threading;
+
+namespace Apache.NMS.XMS.Util
+{
+	/// <summary>
+	/// Handles the multi-threaded dispatching between the transport and the consumers
+	/// </summary>
+	public class Dispatcher
+    {
+		System.Collections.Queue queue = new System.Collections.Queue();
+		readonly Object semaphore = new Object();
+		readonly ArrayList messagesToRedeliver = new ArrayList();
+        
+        // TODO can't use EventWaitHandle on MONO 1.0
+        AutoResetEvent messageReceivedEventHandle = new AutoResetEvent(false);
+        bool m_bAsyncDelivery = false;
+        bool m_bClosed = false;
+
+		public void SetAsyncDelivery(AutoResetEvent eventHandle)
+		{
+			lock (semaphore)
+			{
+				messageReceivedEventHandle = eventHandle;
+				m_bAsyncDelivery = true;
+				if (queue.Count > 0)
+				{
+					messageReceivedEventHandle.Set();
+				}
+			}
+		}
+
+        /// <summary>
+        /// Whem we start a transaction we must redeliver any rolled back messages
+        /// </summary>
+        public void RedeliverRolledBackMessages()
+		{
+            lock (semaphore)
+            {
+				System.Collections.Queue replacement = new System.Collections.Queue(queue.Count + messagesToRedeliver.Count);
+                foreach (Apache.NMS.IMessage element in messagesToRedeliver)
+                {
+                    replacement.Enqueue(element);
+                }
+                messagesToRedeliver.Clear();
+                
+                while (queue.Count > 0)
+                {
+					Apache.NMS.IMessage element = (Apache.NMS.IMessage) queue.Dequeue();
+                    replacement.Enqueue(element);
+                }
+
+				queue = replacement;
+                if (queue.Count > 0)
+                {
+                	messageReceivedEventHandle.Set();
+                }
+            }
+        }
+        
+        /// <summary>
+        /// Redeliver the given message, putting it at the head of the queue
+        /// </summary>
+		public void Redeliver(Apache.NMS.IMessage message)
+        {
+            lock (semaphore)
+			{
+				messagesToRedeliver.Add(message);
+            }
+        }
+        
+        /// <summary>
+        /// Method Enqueue
+        /// </summary>
+		public void Enqueue(Apache.NMS.IMessage message)
+        {
+            lock (semaphore)
+            {
+                queue.Enqueue(message);
+                messageReceivedEventHandle.Set();
+            }
+        }
+        
+        /// <summary>
+        /// Method DequeueNoWait
+        /// </summary>
+		public Apache.NMS.IMessage DequeueNoWait()
+        {
+			Apache.NMS.XMS.Message rc = null;
+            lock (semaphore)
+            {
+                if (!m_bClosed && queue.Count > 0)
+                {
+					rc = (Apache.NMS.XMS.Message) queue.Dequeue();
+					if(null != rc)
+					{
+						rc.ReadOnlyBody = true;
+						rc.ReadOnlyProperties = true;
+					}
+                } 
+            }
+            return rc;
+        }
+
+        /// <summary>
+        /// Method Dequeue
+        /// </summary>
+		public Apache.NMS.IMessage Dequeue(TimeSpan timeout)
+        {
+			Apache.NMS.IMessage rc;
+			bool bClosed = false;
+			lock (semaphore)
+			{
+				bClosed = m_bClosed;
+				rc = DequeueNoWait();
+			}
+
+            while (!bClosed && rc == null)
+            {
+                if( !messageReceivedEventHandle.WaitOne(timeout, false))
+                {
+                    break;
+                }
+
+				lock (semaphore)
+				{
+					rc = DequeueNoWait();
+					bClosed = m_bClosed;
+				}
+            }
+            return rc;
+        }
+        
+        /// <summary>
+        /// Method Dequeue
+        /// </summary>
+		public Apache.NMS.IMessage Dequeue()
+        {
+			TimeSpan indefiniteWait = TimeSpan.FromMilliseconds(Timeout.Infinite);
+			return Dequeue(indefiniteWait);
+        }
+
+		public void Close()
+		{
+			lock (semaphore)
+			{
+				m_bClosed = true;
+				queue.Clear();
+				if(m_bAsyncDelivery)
+				{
+					messageReceivedEventHandle.Set();
+				}
+			}
+		}
+	}
+}

http://git-wip-us.apache.org/repos/asf/activemq-nms-xms/blob/653d676d/src/main/csharp/Util/ExceptionUtil.cs
----------------------------------------------------------------------
diff --git a/src/main/csharp/Util/ExceptionUtil.cs b/src/main/csharp/Util/ExceptionUtil.cs
new file mode 100644
index 0000000..14ee5d1
--- /dev/null
+++ b/src/main/csharp/Util/ExceptionUtil.cs
@@ -0,0 +1,147 @@
+\ufeff/*
+ * 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.Text;
+using System.Collections.Generic;
+using IBM.XMS;
+
+namespace Apache.NMS.XMS.Util
+{
+	class ExceptionUtil
+	{
+		/// <summary>
+		/// Wrap the provider specific exception inside an NMS exception to
+		/// more tightly integrate the provider extensions into the NMS API.
+		/// </summary>
+		/// <param name="ex">Original exception.</param>
+		public static void WrapAndThrowNMSException(Exception ex)
+		{
+			if(ex is Apache.NMS.NMSException)
+			{
+				// Already derived from NMSException
+				throw ex;
+			}
+
+			if(ex is IBM.XMS.IllegalStateException)
+			{
+				IBM.XMS.IllegalStateException xmsEx =
+					(IBM.XMS.IllegalStateException)ex;
+				throw new Apache.NMS.IllegalStateException(
+					xmsEx.Message, xmsEx.ErrorCode, xmsEx);
+			}
+
+			if(ex is IBM.XMS.InvalidClientIDException)
+			{
+				IBM.XMS.InvalidClientIDException xmsEx =
+					(IBM.XMS.InvalidClientIDException)ex;
+				throw new Apache.NMS.InvalidClientIDException(
+					xmsEx.Message, xmsEx.ErrorCode, xmsEx);
+			}
+
+			if(ex is IBM.XMS.InvalidDestinationException)
+			{
+				IBM.XMS.InvalidDestinationException xmsEx =
+					(IBM.XMS.InvalidDestinationException)ex;
+				throw new Apache.NMS.InvalidDestinationException(
+					xmsEx.Message, xmsEx.ErrorCode, xmsEx);
+			}
+
+			if(ex is IBM.XMS.InvalidSelectorException)
+			{
+				IBM.XMS.InvalidSelectorException xmsEx =
+					(IBM.XMS.InvalidSelectorException)ex;
+				throw new Apache.NMS.InvalidSelectorException(
+					xmsEx.Message, xmsEx.ErrorCode, xmsEx);
+			}
+
+			if(ex is IBM.XMS.MessageEOFException)
+			{
+				IBM.XMS.MessageEOFException xmsEx =
+					(IBM.XMS.MessageEOFException)ex;
+				throw new Apache.NMS.MessageEOFException(
+					xmsEx.Message, xmsEx.ErrorCode, xmsEx);
+			}
+
+			if(ex is IBM.XMS.MessageFormatException)
+			{
+				IBM.XMS.MessageFormatException xmsEx =
+					(IBM.XMS.MessageFormatException)ex;
+				throw new Apache.NMS.MessageFormatException(
+					xmsEx.Message, xmsEx.ErrorCode, xmsEx);
+			}
+
+			if(ex is IBM.XMS.MessageNotReadableException)
+			{
+				IBM.XMS.MessageNotReadableException xmsEx =
+					(IBM.XMS.MessageNotReadableException)ex;
+				throw new Apache.NMS.MessageNotReadableException(
+					xmsEx.Message, xmsEx.ErrorCode, xmsEx);
+			}
+
+			if(ex is IBM.XMS.MessageNotWriteableException)
+			{
+				IBM.XMS.MessageNotWriteableException xmsEx =
+					(IBM.XMS.MessageNotWriteableException)ex;
+				throw new Apache.NMS.MessageNotWriteableException(
+					xmsEx.Message, xmsEx.ErrorCode, xmsEx);
+			}
+
+			if(ex is IBM.XMS.ResourceAllocationException)
+			{
+				IBM.XMS.ResourceAllocationException xmsEx =
+					(IBM.XMS.ResourceAllocationException)ex;
+				throw new Apache.NMS.ResourceAllocationException(
+					xmsEx.Message, xmsEx.ErrorCode, xmsEx);
+			}
+
+			if(ex is IBM.XMS.SecurityException)
+			{
+				IBM.XMS.SecurityException xmsEx =
+					(IBM.XMS.SecurityException)ex;
+				throw new Apache.NMS.NMSSecurityException(
+					xmsEx.Message, xmsEx.ErrorCode, xmsEx);
+			}
+
+			if(ex is IBM.XMS.TransactionInProgressException)
+			{
+				IBM.XMS.TransactionInProgressException xmsEx =
+					(IBM.XMS.TransactionInProgressException)ex;
+				throw new Apache.NMS.TransactionInProgressException(
+					xmsEx.Message, xmsEx.ErrorCode, xmsEx);
+			}
+
+			if(ex is IBM.XMS.TransactionRolledBackException)
+			{
+				IBM.XMS.TransactionRolledBackException xmsEx =
+					(IBM.XMS.TransactionRolledBackException)ex;
+				throw new Apache.NMS.TransactionRolledBackException(
+					xmsEx.Message, xmsEx.ErrorCode, xmsEx);
+			}
+
+			if(ex is IBM.XMS.XMSException)
+			{
+				IBM.XMS.XMSException xmsEx =
+					(IBM.XMS.XMSException)ex;
+				throw new Apache.NMS.NMSException(
+					xmsEx.Message, xmsEx.ErrorCode, xmsEx);
+			}
+
+			// Not an EMS exception that should be wrapped.
+			throw ex;
+		}
+	}
+}

http://git-wip-us.apache.org/repos/asf/activemq-nms-xms/blob/653d676d/src/main/csharp/Util/IntrospectionSupport.cs
----------------------------------------------------------------------
diff --git a/src/main/csharp/Util/IntrospectionSupport.cs b/src/main/csharp/Util/IntrospectionSupport.cs
new file mode 100644
index 0000000..175135f
--- /dev/null
+++ b/src/main/csharp/Util/IntrospectionSupport.cs
@@ -0,0 +1,434 @@
+\ufeff/*
+ * 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.Reflection;
+using System.Globalization;
+using System.Collections.Generic;
+using System.Collections.Specialized;
+using Apache.NMS;
+using Apache.NMS.Util;
+
+namespace Apache.NMS.XMS.Util
+{
+	/// <summary>
+	/// Utility class used to provide convenience methods that apply named
+	/// property settings to objects.
+	/// </summary>
+	public class IntrospectionSupport
+	{
+        #region Manage maps of member names and URI aliases
+
+		private static Dictionary<Type, StringDictionary> nameMaps =
+			new Dictionary<Type, StringDictionary>();
+		private static readonly object nameMapsLock = new object();
+
+		/// <summary>
+		/// Gets the member names map for the specified type.
+		/// </summary>
+		/// <param name="type">Type whose names map is requested.</param>
+		/// <returns>Names map for the specified type.</returns>
+        /// <remarks>
+		/// The map is created and registered if it is not found in the
+		/// <c>nameMaps</c> registry.
+        /// </remarks>
+		public static StringDictionary GetNameMap(Type type)
+		{
+			StringDictionary nameMap;
+			lock(IntrospectionSupport.nameMapsLock)
+			{
+				if(!IntrospectionSupport.nameMaps.TryGetValue(
+					type, out nameMap))
+				{
+					nameMap = CreateNameMap(type);
+					IntrospectionSupport.nameMaps.Add(type, nameMap);
+				}
+			}
+			return nameMap;
+		}
+
+		/// <summary>
+		/// Creates a dictionary of public property and attribute names,
+        /// indexed by themselves plus all URI attribute keys associated
+		/// to them.
+		/// </summary>
+		/// <param name="type">Type whose names map is requested.</param>
+		/// <returns>Names map for the specified type.</returns>
+		/// <remarks>
+		/// Applied to this property:
+		/// <code>
+		///   [UriAttribute("My.Test", "MyTest")]
+		///   public string Test
+		///   { get { return(_test); }
+		///     set { _test = value; }
+		///   }
+		/// </code>
+		/// the method returns a dictionary containing
+		/// ("test" -> "Test"), ("my.test" -> "Test"), ("mytest" -> "Test").
+		/// Note that <c>StringDictionary</c> converts keys to lowercase but
+		/// keeps values untouched.
+		/// </remarks>
+		public static StringDictionary CreateNameMap(Type type)
+		{
+			StringDictionary nameMap = new StringDictionary();
+			BindingFlags flags = BindingFlags.FlattenHierarchy
+							| BindingFlags.Public
+							| BindingFlags.Instance;
+
+			// Process public instance self or inherited property
+			foreach(PropertyInfo propertyInfo in type.GetProperties(flags))
+		    {
+				AddToNameMap(nameMap, propertyInfo);
+			}
+
+			// Process public instance self or inherited fields
+			foreach(FieldInfo fieldInfo in type.GetFields(flags))
+		    {
+				AddToNameMap(nameMap, fieldInfo);
+			}
+
+			return(nameMap);
+		}
+
+		/// <summary>
+		/// Adds a property or field name and URI attribute keys to the
+		/// specified name map.
+		/// </summary>
+		/// <param name="nameMap">Name map.</param>
+		/// <param name="memberInfo">Member information for the property
+		/// or field.</param>
+		private static void AddToNameMap(StringDictionary nameMap,
+			MemberInfo memberInfo)
+		{
+			// Add member name mapped to itself
+			nameMap.Add(memberInfo.Name, memberInfo.Name);
+
+			// For each UriAttribute custom attribute
+			foreach(Attribute attr in memberInfo.GetCustomAttributes(
+				typeof(UriAttributeAttribute), true))
+			{
+				// For each URI attribute key
+				foreach(string key in
+					((UriAttributeAttribute)attr).AttributeKeys)
+				{
+					// Index property name by URI attribute key
+					if(!nameMap.ContainsKey(key))
+					{
+						nameMap.Add(key, memberInfo.Name);
+					}
+				}
+			}
+
+			return;
+		}
+
+        #endregion
+
+		#region Set properties
+
+		/// <summary>
+		/// Sets the public properties of a target object using a string map.
+		/// This method uses .Net reflection to identify public properties of
+		/// the target object matching the keys from the passed map.
+		/// </summary>
+		/// <param name="target">Object whose properties will be set.</param>
+		/// <param name="valueMap">Map of key/value pairs.</param>
+		public static void SetProperties(object target,
+			StringDictionary valueMap)
+		{
+			SetProperties(target, valueMap, GetNameMap(target.GetType()));
+		}
+
+        /// <summary>
+        /// Sets the public properties of a target object using a string map.
+        /// This method uses .Net reflection to access public properties of
+        /// the target object matching the keys from the passed map.
+        /// </summary>
+        /// <param name="target">The object whose properties will be set.</param>
+        /// <param name="valueMap">Map of key/value pairs.</param>
+        /// <param name="nameMap">Map of key/property name pairs.</param>
+        public static void SetProperties(object target,
+            StringDictionary valueMap,
+			StringDictionary nameMap)
+        {
+			Tracer.DebugFormat("SetProperties called with target: {0}",
+				target.GetType().Name);
+
+			// Application of specified values is recursive. If a key does not
+			// correspond to a member of the current target object, it is
+			// supposed to refer to a sub-member of such a member. Since member
+			// keys can contain dot characters, an attempt is made to find the
+			// "longest" key corresponding to a member of the current object
+			// (this identifies the "sub-target"), and extract the remaining
+			// key characters as a sub-key to sub-members.
+			// The following dictionary indexes keys to "sub-targets", and
+			// "sub-key"/value pairs to assign to "sub-targets".
+			Dictionary<string, StringDictionary> subTargetMap = null;
+
+			foreach(string key in valueMap.Keys)
+			{
+				if(nameMap.ContainsKey(key))
+				{
+					// Key refers to a member of the current target
+					string memberName = nameMap[key];
+					MemberInfo member = FindMemberInfo(target, memberName);
+					if(member == null)
+					{
+						// Should not happen if the nameMap was indeed created
+						// for the current target object...
+						throw new NMSException(string.Format(
+							"No such property or field: {0} on class: {1}",
+							memberName, target.GetType().Name));
+					}
+
+					// Set value
+					try
+					{
+						if(member.MemberType == MemberTypes.Property)
+						{
+							PropertyInfo property = (PropertyInfo)member;
+							object value = ConvertValue(valueMap[key],
+								property.PropertyType);
+							property.SetValue(target, value, null);
+						}
+						else
+						{
+							FieldInfo field = (FieldInfo)member;
+							object value = ConvertValue(valueMap[key],
+								field.FieldType);
+							field.SetValue(target, value);
+						}
+					}
+					catch(Exception ex)
+					{
+						throw NMSExceptionSupport.Create(
+							"Error while attempting to apply option.", ex);
+					}
+                }
+				else
+				{
+					// Key does NOT refers to a member of the current target
+					// Extract maximal member key + subkeys
+					string memberKey = key;
+					int dotPos = memberKey.LastIndexOf('.');
+					bool memberFound = false;
+					while(!memberFound && dotPos > 0)
+					{
+						memberKey = memberKey.Substring(0, dotPos);
+						if(nameMap.ContainsKey(memberKey))
+						{
+							memberKey = nameMap[memberKey];
+							memberFound = true;
+						}
+						else
+						{
+							dotPos = memberKey.LastIndexOf('.');
+						}
+					}
+
+					if(!memberFound)
+					{
+						throw new NMSException(string.Format(
+							"Unknown property or field: {0} on class: {1}",
+							key, target.GetType().Name));
+					}
+
+					// Register memberKey, subKey and value for further processing
+					string subKey = key.Substring(dotPos + 1);
+					StringDictionary subValueMap;
+
+					if(subTargetMap == null)
+					{
+						subTargetMap = new Dictionary<string, StringDictionary>();
+					}
+
+					if(!subTargetMap.TryGetValue(memberKey, out subValueMap))
+					{
+						subValueMap = new StringDictionary();
+						subTargetMap.Add(memberKey, subValueMap);
+					}
+
+					// In theory, we can't have the same subkey twice, since
+					// they were unique subkeys from another dictionary.
+					// Therefore, no need to check for subValueMap.ContainsKey.
+					subValueMap.Add(subKey, valueMap[key]);
+				}
+            }
+
+			// Now process any compound assignments.
+			if(subTargetMap != null)
+			{
+				foreach(string subTargetKey in subTargetMap.Keys)
+				{
+					MemberInfo member = FindMemberInfo(target, subTargetKey);
+					object subTarget = GetUnderlyingObject(member, target);
+					SetProperties(subTarget, subTargetMap[subTargetKey]);
+				}
+			}
+        }
+
+		/// <summary>
+		/// Converts the specified string value to the type of the target
+		/// member.
+		/// </summary>
+		private static object ConvertValue(string inputString, Type targetType)
+		{
+			// If the target member is an enumeration, get the enumeration
+			// value or combined (or-ed) values
+			object value;
+			if(targetType.IsEnum)
+			{
+				if(inputString.Contains("+"))
+				{
+					string[] inputValues = inputString.Split('+');
+
+					FieldInfo fieldInfo = targetType.GetField(inputValues[0],
+						BindingFlags.Public
+						| BindingFlags.Static
+						| BindingFlags.IgnoreCase);
+					if(fieldInfo == null)
+					{
+						throw new NMSException(string.Format(
+							"Invalid {0} value \"{1}\"", targetType.Name,
+							inputValues[0]));
+					}
+					dynamic val = fieldInfo.GetValue(null);
+
+					for(int v = 1; v < inputValues.Length; v++)
+					{
+						fieldInfo = targetType.GetField(inputValues[v],
+							BindingFlags.Public
+							| BindingFlags.Static
+							| BindingFlags.IgnoreCase);
+						if(fieldInfo == null)
+						{
+							throw new NMSException(string.Format(
+								"Invalid {0} value \"{1}\"", targetType.Name,
+								inputValues[v]));
+						}
+						val = (dynamic)val | (dynamic)fieldInfo.GetValue(null);
+					}
+
+					value = Convert.ChangeType(val, targetType);
+				}
+				else
+				{
+					FieldInfo fieldInfo = targetType.GetField(inputString,
+                                 BindingFlags.Public
+                               | BindingFlags.Static
+                               | BindingFlags.IgnoreCase);
+					if(fieldInfo == null)
+					{
+						throw new NMSException(string.Format(
+							"Invalid {0} value \"{1}\"", targetType.Name,
+							inputString));
+					}
+					value = fieldInfo.GetValue(null);
+				}
+			}
+			else
+			{
+				// Not an enumeration
+				value = Convert.ChangeType(inputString,
+					targetType, CultureInfo.InvariantCulture);
+			}
+			return value;
+		}
+
+		#endregion
+
+		#region Get member information and objects
+
+		/// <summary>
+		/// Gets member information for a property or field of the target
+		/// object.
+		///	</summary>
+		/// <param name="target">Target object.</param>
+		/// <param name="name">Property or field name.</param>
+		/// <returns>Retrieved member information.</returns>
+        private static MemberInfo FindMemberInfo(object target, string name)
+        {
+            BindingFlags flags = BindingFlags.FlattenHierarchy
+                               | BindingFlags.Public
+                               | BindingFlags.Instance
+                               | BindingFlags.IgnoreCase;
+
+            Type type = target.GetType();
+
+            MemberInfo member = type.GetProperty(name, flags);
+
+            if(member == null)
+            {
+                member = type.GetField(name, flags);
+            }
+
+            return member;
+        }
+
+		/// <summary>
+		/// Gets object assigned to the specified property or field member of
+		/// the target object.
+		///	</summary>
+		/// <param name="member">Member information.</param>
+		/// <param name="target">Target object.</param>
+		/// <returns>Retrieved object.</returns>
+		private static object GetUnderlyingObject(
+			MemberInfo member, object target)
+		{
+			object result = null;
+
+			if(member.MemberType == MemberTypes.Field)
+			{
+				FieldInfo field = member as FieldInfo;
+
+				if(field.FieldType.IsPrimitive)
+				{
+					throw new NMSException(string.Format(
+						"The field given is a primitive type: {0}",
+						member.Name));
+				}
+
+				result = field.GetValue(target);
+			}
+			else
+			{
+				PropertyInfo property = member as PropertyInfo;
+				MethodInfo getter = property.GetGetMethod();
+
+				if(getter == null)
+				{
+					throw new NMSException(string.Format(
+						"Cannot access member: {0}",
+						member.Name));
+				}
+
+				result = getter.Invoke(target, null);
+			}
+
+			if(result == null)
+			{
+				throw new NMSException(string.Format(
+					"Could not retrieve the value of member {0}.",
+					member.Name));
+			}
+
+			return result;
+		}
+
+		#endregion
+    }
+}

http://git-wip-us.apache.org/repos/asf/activemq-nms-xms/blob/653d676d/src/main/csharp/Util/UriAttributeAttribute.cs
----------------------------------------------------------------------
diff --git a/src/main/csharp/Util/UriAttributeAttribute.cs b/src/main/csharp/Util/UriAttributeAttribute.cs
new file mode 100644
index 0000000..f7278d8
--- /dev/null
+++ b/src/main/csharp/Util/UriAttributeAttribute.cs
@@ -0,0 +1,51 @@
+\ufeff/*
+ * 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;
+
+namespace Apache.NMS.XMS.Util
+{
+    /// <summary>
+    /// Attribute for mapping a URI attribute key to an object's property.
+    /// </summary>
+    public class UriAttributeAttribute : System.Attribute
+    {
+        private readonly string[] attributeKeys;
+
+        /// <summary>
+        /// Constructs an <c>UriAttributeAttribute</c> specifying a list
+        /// of attribute keys.
+        /// </summary>
+        /// <param name="keys">URI attribute keys.</param>
+        public UriAttributeAttribute(params string[] keys)
+        {
+            this.attributeKeys = new string[keys.Length];
+            for(int k = 0; k < keys.Length; k++)
+            {
+                this.attributeKeys[k] = keys[k];
+            }
+        }
+
+        /// <summary>
+        /// URI attribute keys.
+        /// </summary>
+        public string[] AttributeKeys
+        {
+            get { return this.attributeKeys; }
+        }
+    }
+}