/* | |
* 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.Collections.Generic; | |
using System.IO; | |
using System.Reflection; | |
using System.Threading.Tasks; | |
using System.Xml; | |
using Apache.NMS.Util; | |
namespace Apache.NMS | |
{ | |
/// <summary> | |
/// Provider implementation mapping class. | |
/// </summary> | |
public class ProviderFactoryInfo | |
{ | |
public string assemblyFileName; | |
public string factoryClassName; | |
public ProviderFactoryInfo(string aFileName, string fClassName) | |
{ | |
assemblyFileName = aFileName; | |
factoryClassName = fClassName; | |
} | |
} | |
/// <summary> | |
/// Implementation of a factory for <see cref="IConnection" /> instances. | |
/// </summary> | |
public class NMSConnectionFactory : IConnectionFactory | |
{ | |
protected readonly IConnectionFactory factory; | |
protected static readonly Dictionary<string, ProviderFactoryInfo> schemaProviderFactoryMap; | |
/// <summary> | |
/// Static class constructor | |
/// </summary> | |
static NMSConnectionFactory() | |
{ | |
schemaProviderFactoryMap = new Dictionary<string, ProviderFactoryInfo>(); | |
schemaProviderFactoryMap["activemq"] = | |
new ProviderFactoryInfo("Apache.NMS.ActiveMQ", "Apache.NMS.ActiveMQ.ConnectionFactory"); | |
schemaProviderFactoryMap["activemqnettx"] = new ProviderFactoryInfo("Apache.NMS.ActiveMQ", | |
"Apache.NMS.ActiveMQ.NetTxConnectionFactory"); | |
schemaProviderFactoryMap["tcp"] = | |
new ProviderFactoryInfo("Apache.NMS.ActiveMQ", "Apache.NMS.ActiveMQ.ConnectionFactory"); | |
schemaProviderFactoryMap["ems"] = | |
new ProviderFactoryInfo("Apache.NMS.EMS", "Apache.NMS.EMS.ConnectionFactory"); | |
schemaProviderFactoryMap["mqtt"] = | |
new ProviderFactoryInfo("Apache.NMS.MQTT", "Apache.NMS.MQTT.ConnectionFactory"); | |
schemaProviderFactoryMap["msmq"] = | |
new ProviderFactoryInfo("Apache.NMS.MSMQ", "Apache.NMS.MSMQ.ConnectionFactory"); | |
schemaProviderFactoryMap["stomp"] = | |
new ProviderFactoryInfo("Apache.NMS.Stomp", "Apache.NMS.Stomp.ConnectionFactory"); | |
schemaProviderFactoryMap["xms"] = | |
new ProviderFactoryInfo("Apache.NMS.XMS", "Apache.NMS.XMS.ConnectionFactory"); | |
schemaProviderFactoryMap["zmq"] = | |
new ProviderFactoryInfo("Apache.NMS.ZMQ", "Apache.NMS.ZMQ.ConnectionFactory"); | |
schemaProviderFactoryMap["amqp"] = | |
new ProviderFactoryInfo("Apache.NMS.AMQP", "Apache.NMS.AMQP.ConnectionFactory"); | |
} | |
/// <summary> | |
/// The ConnectionFactory object must define a constructor that takes as a minimum a Uri object. | |
/// Any additional parameters are optional, but will typically include a Client ID string. | |
/// </summary> | |
/// <param name="providerURI">The URI for the ActiveMQ provider.</param> | |
/// <param name="constructorParams">Optional parameters to use when creating the ConnectionFactory.</param> | |
public NMSConnectionFactory(string providerURI, params object[] constructorParams) | |
: this(URISupport.CreateCompatibleUri(providerURI), constructorParams) | |
{ | |
} | |
/// <summary> | |
/// The ConnectionFactory object must define a constructor that takes as a minimum a Uri object. | |
/// Any additional parameters are optional, but will typically include a Client ID string. | |
/// </summary> | |
/// <param name="uriProvider">The URI for the ActiveMQ provider.</param> | |
/// <param name="constructorParams">Optional parameters to use when creating the ConnectionFactory.</param> | |
public NMSConnectionFactory(Uri uriProvider, params object[] constructorParams) | |
{ | |
this.factory = CreateConnectionFactory(uriProvider, constructorParams); | |
} | |
/// <summary> | |
/// Create a connection factory that can create connections for the given scheme in the URI. | |
/// </summary> | |
/// <param name="uriProvider">The URI for the ActiveMQ provider.</param> | |
/// <param name="constructorParams">Optional parameters to use when creating the ConnectionFactory.</param> | |
/// <returns>A <see cref="IConnectionFactory" /> implementation that will be used.</returns> | |
public static IConnectionFactory CreateConnectionFactory(Uri uriProvider, params object[] constructorParams) | |
{ | |
IConnectionFactory connectionFactory = null; | |
try | |
{ | |
Type factoryType = GetTypeForScheme(uriProvider.Scheme); | |
// If an implementation was found, try to instantiate it. | |
if (factoryType != null) | |
{ | |
#if NETCF | |
// Compact framework does not allow the activator ta pass parameters to a constructor. | |
connectionFactory = (IConnectionFactory) Activator.CreateInstance(factoryType); | |
connectionFactory.BrokerUri = uriProvider; | |
#else | |
object[] parameters = MakeParameterArray(uriProvider, constructorParams); | |
connectionFactory = (IConnectionFactory) Activator.CreateInstance(factoryType, parameters); | |
#endif | |
} | |
if (null == connectionFactory) | |
{ | |
throw new NMSConnectionException("No IConnectionFactory implementation found for connection URI: " + | |
uriProvider); | |
} | |
} | |
catch (NMSConnectionException) | |
{ | |
throw; | |
} | |
catch (Exception ex) | |
{ | |
throw new NMSConnectionException( | |
"Could not create the IConnectionFactory implementation: " + ex.Message, ex); | |
} | |
return connectionFactory; | |
} | |
/// <summary> | |
/// Finds the <see cref="System.Type" /> associated with the given scheme. | |
/// </summary> | |
/// <param name="scheme">The scheme (e.g. <c>tcp</c>, <c>activemq</c> or <c>stomp</c>).</param> | |
/// <returns>The <see cref="System.Type" /> of the ConnectionFactory that will be used | |
/// to create the connection for the specified <paramref name="scheme" />.</returns> | |
private static Type GetTypeForScheme(string scheme) | |
{ | |
string[] paths = GetConfigSearchPaths(); | |
string assemblyFileName; | |
string factoryClassName; | |
Type factoryType = null; | |
Tracer.DebugFormat("Locating provider for scheme: {0}", scheme); | |
if (LookupConnectionFactoryInfo(paths, scheme, out assemblyFileName, out factoryClassName)) | |
{ | |
Assembly assembly = null; | |
Tracer.DebugFormat("Attempting to load provider assembly: {0}", assemblyFileName); | |
try | |
{ | |
assembly = Assembly.Load(assemblyFileName); | |
if (null != assembly) | |
{ | |
Tracer.Debug("Succesfully loaded provider."); | |
} | |
} | |
catch (Exception ex) | |
{ | |
Tracer.ErrorFormat("Exception loading assembly failed: {0}", ex.Message); | |
assembly = null; | |
} | |
if (null == assembly) | |
{ | |
foreach (string path in paths) | |
{ | |
string fullpath = Path.Combine(path, assemblyFileName) + ".dll"; | |
Tracer.DebugFormat("Looking for: {0}", fullpath); | |
if (File.Exists(fullpath)) | |
{ | |
Tracer.Debug("\tAssembly found! Attempting to load..."); | |
try | |
{ | |
assembly = Assembly.LoadFrom(fullpath); | |
} | |
catch (Exception ex) | |
{ | |
Tracer.ErrorFormat("Exception loading assembly failed: {0}", ex.Message); | |
assembly = null; | |
} | |
if (null != assembly) | |
{ | |
Tracer.Debug("Successfully loaded provider."); | |
break; | |
} | |
Tracer.Debug("Failed to load provider. Continuing search..."); | |
} | |
} | |
} | |
if (null != assembly) | |
{ | |
#if NETCF | |
factoryType = assembly.GetType(factoryClassName, true); | |
#else | |
factoryType = assembly.GetType(factoryClassName, true, true); | |
#endif | |
if (null == factoryType) | |
{ | |
Tracer.Fatal("Failed to load class factory from provider."); | |
} | |
} | |
else | |
{ | |
Tracer.Fatal("Failed to load provider assembly."); | |
} | |
} | |
return factoryType; | |
} | |
/// <summary> | |
/// Lookup the connection factory assembly filename and class name. | |
/// Read an external configuration file that maps scheme to provider implementation. | |
/// Load XML config files named: nmsprovider-{scheme}.config | |
/// Following is a sample configuration file named nmsprovider-jms.config. Replace | |
/// the parenthesis with angle brackets for proper XML formatting. | |
/// | |
/// (?xml version="1.0" encoding="utf-8" ?) | |
/// (configuration) | |
/// (provider assembly="MyCompany.NMS.JMSProvider.dll" classFactory="MyCompany.NMS.JMSProvider.ConnectionFactory"/) | |
/// (/configuration) | |
/// | |
/// This configuration file would be loaded and parsed when a connection uri with a scheme of 'jms' | |
/// is used for the provider. In this example the connection string might look like: | |
/// jms://localhost:7222 | |
/// | |
/// </summary> | |
/// <param name="paths">Folder paths to look in.</param> | |
/// <param name="scheme">The scheme.</param> | |
/// <param name="assemblyFileName">Name of the assembly file.</param> | |
/// <param name="factoryClassName">Name of the factory class.</param> | |
/// <returns><c>true</c> if the configuration file for the specified <paramref name="scheme" /> could | |
/// be found; otherwise, <c>false</c>.</returns> | |
private static bool LookupConnectionFactoryInfo(string[] paths, string scheme, out string assemblyFileName, | |
out string factoryClassName) | |
{ | |
bool foundFactory = false; | |
string schemeLower = scheme.ToLower(); | |
ProviderFactoryInfo pfi; | |
// Look for a custom configuration to handle this scheme. | |
string configFileName = String.Format("nmsprovider-{0}.config", schemeLower); | |
assemblyFileName = String.Empty; | |
factoryClassName = String.Empty; | |
Tracer.DebugFormat("Attempting to locate provider configuration: {0}", configFileName); | |
foreach (string path in paths) | |
{ | |
string fullpath = Path.Combine(path, configFileName); | |
Tracer.DebugFormat("Looking for: {0}", fullpath); | |
try | |
{ | |
if (File.Exists(fullpath)) | |
{ | |
Tracer.DebugFormat("\tConfiguration file found in {0}", fullpath); | |
XmlDocument configDoc = new XmlDocument(); | |
configDoc.Load(fullpath); | |
XmlElement providerNode = (XmlElement) configDoc.SelectSingleNode("/configuration/provider"); | |
if (null != providerNode) | |
{ | |
assemblyFileName = providerNode.GetAttribute("assembly"); | |
factoryClassName = providerNode.GetAttribute("classFactory"); | |
if (!String.IsNullOrEmpty(assemblyFileName) && !String.IsNullOrEmpty(factoryClassName)) | |
{ | |
foundFactory = true; | |
Tracer.DebugFormat("Selected custom provider for {0}: {1}, {2}", schemeLower, | |
assemblyFileName, factoryClassName); | |
break; | |
} | |
} | |
} | |
} | |
catch (Exception ex) | |
{ | |
Tracer.DebugFormat("Exception while scanning {0}: {1}", fullpath, ex.Message); | |
} | |
} | |
if (!foundFactory) | |
{ | |
// Check for standard provider implementations. | |
if (schemaProviderFactoryMap.TryGetValue(schemeLower, out pfi)) | |
{ | |
assemblyFileName = pfi.assemblyFileName; | |
factoryClassName = pfi.factoryClassName; | |
foundFactory = true; | |
Tracer.DebugFormat("Selected standard provider for {0}: {1}, {2}", schemeLower, assemblyFileName, | |
factoryClassName); | |
} | |
} | |
return foundFactory; | |
} | |
/// <summary> | |
/// Get an array of search paths to look for config files. | |
/// </summary> | |
/// <returns> | |
/// A collection of search paths, including the current directory, the current AppDomain's | |
/// BaseDirectory and the current AppDomain's RelativeSearchPath. | |
/// </returns> | |
private static string[] GetConfigSearchPaths() | |
{ | |
ArrayList pathList = new ArrayList(); | |
// Check the current folder first. | |
pathList.Add(""); | |
#if !NETCF | |
try | |
{ | |
AppDomain currentDomain = AppDomain.CurrentDomain; | |
// Check the folder the assembly is located in. | |
Assembly executingAssembly = Assembly.GetExecutingAssembly(); | |
try | |
{ | |
pathList.Add(Path.GetDirectoryName(executingAssembly.Location)); | |
} | |
catch (Exception ex) | |
{ | |
Tracer.DebugFormat("Error parsing executing assembly location: {0} : {1}", | |
executingAssembly.Location, ex.Message); | |
} | |
if (null != currentDomain.BaseDirectory) | |
{ | |
pathList.Add(currentDomain.BaseDirectory); | |
} | |
if (null != currentDomain.RelativeSearchPath) | |
{ | |
pathList.Add(currentDomain.RelativeSearchPath); | |
} | |
} | |
catch (Exception ex) | |
{ | |
Tracer.DebugFormat("Error configuring search paths: {0}", ex.Message); | |
} | |
#endif | |
return (string[]) pathList.ToArray(typeof(string)); | |
} | |
/// <summary> | |
/// Converts a <c>params object[]</c> collection into a plain <c>object[]</c>s, to pass to the constructor. | |
/// </summary> | |
/// <param name="firstParam">The first parameter in the collection.</param> | |
/// <param name="varParams">The remaining parameters.</param> | |
/// <returns>An array of <see cref="Object" /> instances.</returns> | |
private static object[] MakeParameterArray(object firstParam, params object[] varParams) | |
{ | |
ArrayList paramList = new ArrayList(); | |
paramList.Add(firstParam); | |
foreach (object param in varParams) | |
{ | |
paramList.Add(param); | |
} | |
return paramList.ToArray(); | |
} | |
/// <summary> | |
/// Creates a new connection. | |
/// </summary> | |
/// <returns>An <see cref="IConnection" /> created by the requested ConnectionFactory.</returns> | |
public IConnection CreateConnection() | |
{ | |
return this.factory.CreateConnection(); | |
} | |
/// <summary> | |
/// Creates a new connection with the given <paramref name="userName" /> and <paramref name="password" /> credentials. | |
/// </summary> | |
/// <param name="userName">The username to use when establishing the connection.</param> | |
/// <param name="password">The password to use when establishing the connection.</param> | |
/// <returns>An <see cref="IConnection" /> created by the requested ConnectionFactory.</returns> | |
public IConnection CreateConnection(string userName, string password) | |
{ | |
return this.factory.CreateConnection(userName, password); | |
} | |
public Task<IConnection> CreateConnectionAsync() | |
{ | |
return this.factory.CreateConnectionAsync(); | |
} | |
public Task<IConnection> CreateConnectionAsync(string userName, string password) | |
{ | |
return this.factory.CreateConnectionAsync(userName, password); | |
} | |
public INMSContext CreateContext() | |
{ | |
return this.factory.CreateContext(); | |
} | |
public INMSContext CreateContext(AcknowledgementMode acknowledgementMode) | |
{ | |
return this.factory.CreateContext(acknowledgementMode); | |
} | |
public INMSContext CreateContext(string userName, string password) | |
{ | |
return this.factory.CreateContext(userName, password); | |
} | |
public INMSContext CreateContext(string userName, string password, AcknowledgementMode acknowledgementMode) | |
{ | |
return this.factory.CreateContext(userName, password, acknowledgementMode); | |
} | |
public Task<INMSContext> CreateContextAsync() | |
{ | |
return this.factory.CreateContextAsync(); | |
} | |
public Task<INMSContext> CreateContextAsync(AcknowledgementMode acknowledgementMode) | |
{ | |
return this.factory.CreateContextAsync(acknowledgementMode); | |
} | |
public Task<INMSContext> CreateContextAsync(string userName, string password) | |
{ | |
return this.factory.CreateContextAsync(userName, password); | |
} | |
public Task<INMSContext> CreateContextAsync(string userName, string password, AcknowledgementMode acknowledgementMode) | |
{ | |
return this.factory.CreateContextAsync(userName, password, acknowledgementMode); | |
} | |
/// <summary> | |
/// Get/or set the broker Uri. | |
/// </summary> | |
public Uri BrokerUri | |
{ | |
get { return ConnectionFactory.BrokerUri; } | |
set { ConnectionFactory.BrokerUri = value; } | |
} | |
/// <summary> | |
/// The actual IConnectionFactory implementation that is being used. This implementation | |
/// depends on the scheme of the URI used when constructed. | |
/// </summary> | |
public IConnectionFactory ConnectionFactory | |
{ | |
get { return factory; } | |
} | |
/// <summary> | |
/// Get/or Set the IRedeliveryPolicy instance using the IConnectionFactory implementation | |
/// that is being used. | |
/// </summary> | |
public IRedeliveryPolicy RedeliveryPolicy | |
{ | |
get { return this.factory.RedeliveryPolicy; } | |
set { this.factory.RedeliveryPolicy = value; } | |
} | |
/// <summary> | |
/// Get/or Set the ConsumerTransformerDelegate using the IConnectionFactory implementation | |
/// that is currently being used. | |
/// </summary> | |
public ConsumerTransformerDelegate ConsumerTransformer | |
{ | |
get { return this.factory.ConsumerTransformer; } | |
set { this.factory.ConsumerTransformer = value; } | |
} | |
/// <summary> | |
/// Get/or Set the ProducerTransformerDelegate using the IConnectionFactory implementation | |
/// that is currently being used. | |
/// </summary> | |
public ProducerTransformerDelegate ProducerTransformer | |
{ | |
get { return this.factory.ProducerTransformer; } | |
set { this.factory.ProducerTransformer = value; } | |
} | |
} | |
} |