Add calls to TIBCO API to turn on failover mode.
Add support for URL parameters:
   connection.ExceptionOnFTEvents
   connection.ExceptionOnFTSwitch
   connection.ConnAttemptCount
   connection.ConnAttemptDelay
   connection.ConnAttemptTimeout
   connection.ReconnAttemptCount
   connection.ReconnAttemptDelay
   connection.ReconnAttemptTimeout

Fixes [AMQNET-511]. (See https://issues.apache.org/jira/browse/AMQNET-511)

diff --git a/src/main/csharp/ConnectionFactory.cs b/src/main/csharp/ConnectionFactory.cs
index 7cd3c1d..ba5959c 100644
--- a/src/main/csharp/ConnectionFactory.cs
+++ b/src/main/csharp/ConnectionFactory.cs
@@ -17,7 +17,10 @@
 

 using System;

 using System.Collections;

+using System.Collections.Specialized;

+using Apache.NMS.EMS.Util;

 using Apache.NMS.Policies;

+using Apache.NMS.Util;

 

 namespace Apache.NMS.EMS

 {

@@ -30,6 +33,14 @@
 		private Uri brokerUri;

 		private string clientId;

 		private Hashtable properties;

+		private bool exceptionOnFTEvents = true;

+		private bool exceptionOnFTSwitch = true;

+		private int connAttemptCount = Int32.MaxValue;   // Infinite

+		private int connAttemptDelay = 30000;            // 30 seconds

+		private int connAttemptTimeout = 5000;           // 5 seconds

+		private int reconnAttemptCount = Int32.MaxValue; // Infinite

+		private int reconnAttemptDelay = 30000;          // 30 seconds

+		private int reconnAttemptTimeout = 5000;         // 5 seconds

 

 		private IRedeliveryPolicy redeliveryPolicy = new RedeliveryPolicy();

 

@@ -38,6 +49,7 @@
 			try

 			{

 				this.tibcoConnectionFactory = new TIBCO.EMS.ConnectionFactory();

+				ConfigureConnectionFactory();

 			}

 			catch(Exception ex)

 			{

@@ -77,10 +89,11 @@
 		{

 			try

 			{

-				this.tibcoConnectionFactory = new TIBCO.EMS.ConnectionFactory(serverUrl.AbsolutePath, clientId, properties);

-				this.brokerUri = serverUrl;

+				this.brokerUri = ParseUriProperties(serverUrl);

+				this.tibcoConnectionFactory = new TIBCO.EMS.ConnectionFactory(TrimParens(this.brokerUri.AbsolutePath), clientId, properties);

 				this.clientId = clientId;

 				this.properties = properties;

+				ConfigureConnectionFactory();

 			}

 			catch(Exception ex)

 			{

@@ -91,6 +104,22 @@
 			VerifyConnectionFactory();

 		}

 

+		private void ConfigureConnectionFactory()

+		{

+			TIBCO.EMS.Tibems.SetExceptionOnFTEvents(this.ExceptionOnFTEvents);

+			TIBCO.EMS.Tibems.SetExceptionOnFTSwitch(this.ExceptionOnFTSwitch);

+

+			// Set the initial connection retry settings.

+			this.tibcoConnectionFactory.SetConnAttemptCount(this.ConnAttemptCount);

+			this.tibcoConnectionFactory.SetConnAttemptDelay(this.ConnAttemptDelay);

+			this.tibcoConnectionFactory.SetConnAttemptTimeout(this.ConnAttemptTimeout);

+

+			// Set the failover reconnect retry settings

+			this.tibcoConnectionFactory.SetReconnAttemptCount(this.ReconnAttemptCount);

+			this.tibcoConnectionFactory.SetReconnAttemptDelay(this.ReconnAttemptDelay);

+			this.tibcoConnectionFactory.SetReconnAttemptTimeout(this.ReconnAttemptTimeout);

+		}

+

 		private void VerifyConnectionFactory()

 		{

 			if(null == this.tibcoConnectionFactory)

@@ -99,6 +128,58 @@
 			}

 		}

 

+		#region Connection Factory Properties (configure via URL parameters)

+

+		public bool ExceptionOnFTEvents

+		{

+			get { return this.exceptionOnFTEvents; }

+			set { this.exceptionOnFTEvents = value; }

+		}

+

+		public bool ExceptionOnFTSwitch

+		{

+			get { return this.exceptionOnFTSwitch; }

+			set { this.exceptionOnFTSwitch = value; }

+		}

+

+		public int ConnAttemptCount

+		{

+			get { return this.connAttemptCount; }

+			set { this.connAttemptCount = value; }

+		}

+

+		public int ConnAttemptDelay

+		{

+			get { return this.connAttemptDelay; }

+			set { this.connAttemptDelay = value; }

+		}

+

+		public int ConnAttemptTimeout

+		{

+			get { return this.connAttemptTimeout; }

+			set { this.connAttemptTimeout = value; }

+		}

+

+		public int ReconnAttemptCount

+		{

+			get { return this.reconnAttemptCount; }

+			set { this.reconnAttemptCount = value; }

+		}

+

+		public int ReconnAttemptDelay

+		{

+			get { return this.reconnAttemptDelay; }

+			set { this.reconnAttemptDelay = value; }

+		}

+

+		public int ReconnAttemptTimeout

+		{

+			get { return this.reconnAttemptTimeout; }

+			set { this.reconnAttemptTimeout = value; }

+		}

+

+		#endregion

+

 		#region IConnectionFactory Members

 

 		/// <summary>

@@ -162,33 +243,32 @@
 			{

 				try

 				{

-					if(null == this.brokerUri || !this.brokerUri.Equals(value))

+					// Create or Re-create the TIBCO connection factory.

+					this.brokerUri = ParseUriProperties(value);

+					if(null == this.brokerUri)

 					{

-						// Re-create the TIBCO connection factory.

-						this.brokerUri = value;

-						if(null == this.brokerUri)

+						this.tibcoConnectionFactory = new TIBCO.EMS.ConnectionFactory();

+					}

+					else

+					{

+						if(null == this.clientId)

 						{

-							this.tibcoConnectionFactory = new TIBCO.EMS.ConnectionFactory();

+							this.tibcoConnectionFactory = new TIBCO.EMS.ConnectionFactory(TrimParens(this.brokerUri.AbsolutePath));

 						}

 						else

 						{

-							if(null == this.clientId)

+							if(null == this.properties)

 							{

-								this.tibcoConnectionFactory = new TIBCO.EMS.ConnectionFactory(this.brokerUri.OriginalString);

+								this.tibcoConnectionFactory = new TIBCO.EMS.ConnectionFactory(TrimParens(this.brokerUri.AbsolutePath), this.clientId);

 							}

 							else

 							{

-								if(null == this.properties)

-								{

-									this.tibcoConnectionFactory = new TIBCO.EMS.ConnectionFactory(this.brokerUri.OriginalString, this.clientId);

-								}

-								else

-								{

-									this.tibcoConnectionFactory = new TIBCO.EMS.ConnectionFactory(this.brokerUri.OriginalString, this.clientId, this.properties);

-								}

+								this.tibcoConnectionFactory = new TIBCO.EMS.ConnectionFactory(TrimParens(this.brokerUri.AbsolutePath), this.clientId, this.properties);

 							}

 						}

 					}

+

+					ConfigureConnectionFactory();

 				}

 				catch(Exception ex)

 				{

@@ -197,6 +277,37 @@
 			}

 		}

 

+		private Uri ParseUriProperties(Uri rawUri)

+		{

+			Tracer.InfoFormat("BrokerUri set = {0}", rawUri.OriginalString);

+			Uri parsedUri = rawUri;

+

+			if(!String.IsNullOrEmpty(rawUri.Query) && !rawUri.OriginalString.EndsWith(")"))

+			{

+				parsedUri = new Uri(rawUri.OriginalString);

+				// Since the Uri class will return the end of a Query string found in a Composite

+				// URI we must ensure that we trim that off before we proceed.

+				string query = parsedUri.Query.Substring(parsedUri.Query.LastIndexOf(")") + 1);

+

+				StringDictionary properties = URISupport.ParseQuery(query);

+

+				StringDictionary connection = URISupport.ExtractProperties(properties, "connection.");

+				StringDictionary nms = URISupport.ExtractProperties(properties, "nms.");

+

+				IntrospectionSupport.SetProperties(this, connection, "connection.");

+				IntrospectionSupport.SetProperties(this, nms, "nms.");

+

+				parsedUri = URISupport.CreateRemainingUri(parsedUri, properties);

+			}

+

+			return parsedUri;

+		}

+

+		private string TrimParens(string stringWithParens)

+		{

+			return stringWithParens.TrimStart('(').TrimEnd(')');

+		}

+

 		/// <summary>

 		/// Get/or set the redelivery policy that new IConnection objects are

 		/// assigned upon creation.

diff --git a/src/main/csharp/IntrospectionSupport.cs b/src/main/csharp/IntrospectionSupport.cs
new file mode 100644
index 0000000..2077901
--- /dev/null
+++ b/src/main/csharp/IntrospectionSupport.cs
@@ -0,0 +1,182 @@
+/*

+ * 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.EMS.Util

+{

+    /// <summary>

+    /// Utility class used to provide conveince methods that apply named property

+    /// settings to objects.

+    /// </summary>

+    public class IntrospectionSupport

+    {

+        /// <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">The object whose properties will be set.</param>

+        /// <param name="map">Map of key/value pairs.</param>

+        public static void SetProperties(object target, StringDictionary map)

+        {

+            SetProperties(target, map, "");

+        }

+

+        /// <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">The object whose properties will be set.</param>

+        /// <param name="map">Map of key/value pairs.</param>

+        /// <param name="prefix">Key value prefix.  This is prepended to the property name

+        /// before searching for a matching key value.</param>

+        public static void SetProperties(object target, StringDictionary map, string prefix)

+        {

+            Tracer.DebugFormat("SetProperties called with target: {0}, and prefix: {1}",

+                               target.GetType().Name, prefix);

+

+            foreach(string key in map.Keys)

+            {

+                if(key.StartsWith(prefix, StringComparison.InvariantCultureIgnoreCase))

+                {

+                    string propertyName = key.Substring(prefix.Length);

+

+                    // Process all member assignments at this level before processing

+                    // any deeper member assignments.

+                    if(!propertyName.Contains("."))

+                    {

+                        MemberInfo member = FindPropertyInfo(target, propertyName);

+

+                        if(member == null)

+                        {

+                            throw new NMSException(string.Format("No such property or field: {0} on class: {1}", propertyName, target.GetType().Name));

+                        }

+

+                        try

+                        {

+                            if(member.MemberType == MemberTypes.Property)

+                            {

+                                PropertyInfo property = member as PropertyInfo;

+                                property.SetValue(target, Convert.ChangeType(map[key], property.PropertyType, CultureInfo.InvariantCulture), null);

+                            }

+                            else

+                            {

+                                FieldInfo field = member as FieldInfo;

+                                field.SetValue(target, Convert.ChangeType(map[key], field.FieldType, CultureInfo.InvariantCulture));

+                            }

+                        }

+                        catch(Exception ex)

+                        {

+                            throw NMSExceptionSupport.Create("Error while attempting to apply option.", ex);

+                        }

+                    }

+                }

+            }

+

+            IList<string> propertiesSet = new List<string>();

+

+            // Now process any compound assignments, ensuring that once we recurse into an

+            // object we don't do it again as there could be multiple compunds element assignments

+            // and they'd have already been processed recursively.

+            foreach(string key in map.Keys)

+            {

+                if(key.StartsWith(prefix, StringComparison.InvariantCultureIgnoreCase))

+                {

+                    string propertyName = key.Substring(prefix.Length);

+

+                    if(propertyName.Contains("."))

+                    {

+                        string newTargetName = propertyName.Substring(0, propertyName.IndexOf('.'));

+                        string newPrefix = prefix + newTargetName + ".";

+

+                        if(!propertiesSet.Contains(newPrefix))

+                        {

+                            MemberInfo member = FindPropertyInfo(target, newTargetName);

+                            object newTarget = GetUnderlyingObject(member, target);

+                            SetProperties(newTarget, map, newPrefix);

+                            propertiesSet.Add(newPrefix);

+                        }

+                    }

+                }

+            }

+        }

+

+        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("The field given is a priomitive type: " + member.Name);

+                }

+

+                result = field.GetValue(target);

+            }

+            else

+            {

+                PropertyInfo property = member as PropertyInfo;

+                MethodInfo getter = property.GetGetMethod();

+

+                if(getter == null)

+                {

+                    throw new NMSException("Cannot access member: " + 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;

+        }

+

+        private static MemberInfo FindPropertyInfo(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;

+        }

+

+    }

+}

diff --git a/vs2008-ems.csproj b/vs2008-ems.csproj
index c21fa3f..baebf54 100644
--- a/vs2008-ems.csproj
+++ b/vs2008-ems.csproj
@@ -74,6 +74,7 @@
     <Compile Include="src\main\csharp\ConnectionFactory.cs" />

     <Compile Include="src\main\csharp\ConnectionMetaData.cs" />

     <Compile Include="src\main\csharp\ExceptionUtil.cs" />

+    <Compile Include="src\main\csharp\IntrospectionSupport.cs" />

     <Compile Include="src\main\csharp\StreamMessage.cs" />

     <Compile Include="src\main\csharp\QueueBrowser.cs" />

     <Compile Include="src\main\csharp\Destination.cs" />