Author: jgomes Date: Fri Sep 25 23:18:10 2015 New Revision: 1705382 URL: http://svn.apache.org/viewvc?rev=1705382&view=rev Log: 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) Added: activemq/activemq-dotnet/Apache.NMS.EMS/trunk/src/main/csharp/IntrospectionSupport.cs Modified: activemq/activemq-dotnet/Apache.NMS.EMS/trunk/src/main/csharp/ConnectionFactory.cs activemq/activemq-dotnet/Apache.NMS.EMS/trunk/vs2008-ems.csproj Modified: activemq/activemq-dotnet/Apache.NMS.EMS/trunk/src/main/csharp/ConnectionFactory.cs URL: http://svn.apache.org/viewvc/activemq/activemq-dotnet/Apache.NMS.EMS/trunk/src/main/csharp/ConnectionFactory.cs?rev=1705382&r1=1705381&r2=1705382&view=diff ============================================================================== --- activemq/activemq-dotnet/Apache.NMS.EMS/trunk/src/main/csharp/ConnectionFactory.cs (original) +++ activemq/activemq-dotnet/Apache.NMS.EMS/trunk/src/main/csharp/ConnectionFactory.cs Fri Sep 25 23:18:10 2015 @@ -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 @@ namespace Apache.NMS.EMS 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 @@ namespace Apache.NMS.EMS try { this.tibcoConnectionFactory = new TIBCO.EMS.ConnectionFactory(); + ConfigureConnectionFactory(); } catch(Exception ex) { @@ -77,10 +89,11 @@ namespace Apache.NMS.EMS { 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 @@ namespace Apache.NMS.EMS 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 @@ namespace Apache.NMS.EMS } } + #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 @@ namespace Apache.NMS.EMS { 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 @@ namespace Apache.NMS.EMS } } + 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. Added: activemq/activemq-dotnet/Apache.NMS.EMS/trunk/src/main/csharp/IntrospectionSupport.cs URL: http://svn.apache.org/viewvc/activemq/activemq-dotnet/Apache.NMS.EMS/trunk/src/main/csharp/IntrospectionSupport.cs?rev=1705382&view=auto ============================================================================== --- activemq/activemq-dotnet/Apache.NMS.EMS/trunk/src/main/csharp/IntrospectionSupport.cs (added) +++ activemq/activemq-dotnet/Apache.NMS.EMS/trunk/src/main/csharp/IntrospectionSupport.cs Fri Sep 25 23:18:10 2015 @@ -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; + } + + } +} Modified: activemq/activemq-dotnet/Apache.NMS.EMS/trunk/vs2008-ems.csproj URL: http://svn.apache.org/viewvc/activemq/activemq-dotnet/Apache.NMS.EMS/trunk/vs2008-ems.csproj?rev=1705382&r1=1705381&r2=1705382&view=diff ============================================================================== --- activemq/activemq-dotnet/Apache.NMS.EMS/trunk/vs2008-ems.csproj (original) +++ activemq/activemq-dotnet/Apache.NMS.EMS/trunk/vs2008-ems.csproj Fri Sep 25 23:18:10 2015 @@ -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" />