#region Disclaimer / License // Copyright (C) 2010, Jackie Ng // http://trac.osgeo.org/mapguide/wiki/maestro, jumpinjackie@gmail.com // // This library is free software; you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public // License as published by the Free Software Foundation; either // version 2.1 of the License, or (at your option) any later version. // // This library is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public // License along with this library; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA // #endregion using System; using System.Collections.Generic; using System.Text; using System.Xml; using System.Reflection; using System.Collections.Specialized; using System.Data.Common; namespace OSGeo.MapGuide.MaestroAPI { /// /// Represents an entry in the Connection Provider Registry /// public class ConnectionProviderEntry { /// /// Gets or sets the name. /// /// The name. public string Name { get; private set; } /// /// Gets or sets the description. /// /// The description. public string Description { get; private set; } /// /// Gets or sets a value indicating whether this instance is multi platform. /// /// /// true if this instance is multi platform; otherwise, false. /// public bool IsMultiPlatform { get; private set; } /// /// Initializes a new instance of the class. /// /// The name. /// The desc. /// if set to true [multi platform]. public ConnectionProviderEntry(string name, string desc, bool multiPlatform) { this.Name = name; this.Description = desc; this.IsMultiPlatform = multiPlatform; } } /// /// A method that creates instances from the given parameters /// /// The init params. /// public delegate IServerConnection ConnectionFactoryMethod(NameValueCollection initParams); /// /// /// The entry point of the Maestro API. The is used to create /// objects. is the root object of the Maestro API, and is where most of the functionality provided /// by this API is accessed from. /// /// /// The supports dynamic creation of objects given a provider name /// and a connection string, which specifies the initialization parameters of the connection. The connection providers are defined in an XML /// file called ConnectionProviders.xml which contains all the registered providers. Each provider has the following properties: /// /// /// The name of the provider /// The assembly containing the implementation /// The name of this implementation /// /// /// The implementation is expected to have a non-public constructor which takes a single parameter, /// a containing the initialization parameters parsed from the given connection /// string. /// /// /// /// This example shows how to create a http-based MapGuide Server connection to the server's mapagent interface. /// /// using OSGeo.MapGuide.MaestroAPI; /// /// ... /// /// IServerConnection conn = ConnectionProviderRegistry.CreateConnection("Maestro.Http", /// "Url", "http://localhost/mapguide/mapagent/mapagent.fcgi", /// "Username", "Administrator", /// "Password", "admin"); /// /// /// /// /// This example shows how to create a TCP/IP connection that wraps the official MapGuide API /// /// using OSGeo.MapGuide.MaestroAPI; /// /// ... /// /// IServerConnection conn = ConnectionProviderRegistry.CreateConnection("Maestro.LocalNative", /// "ConfigFile", "webconfig.ini", /// "Username", "Administrator", /// "Password", "admin"); /// /// public sealed class ConnectionProviderRegistry { const string PROVIDER_CONFIG = "ConnectionProviders.xml"; static Dictionary _ctors; static List _providers; static string _dllRoot; static ConnectionProviderRegistry() { _ctors = new Dictionary(); _providers = new List(); var dir = System.IO.Path.GetDirectoryName(new Uri(Assembly.GetExecutingAssembly().CodeBase).LocalPath); var path = System.IO.Path.Combine(dir, PROVIDER_CONFIG); _dllRoot = System.IO.Path.GetDirectoryName(path); XmlDocument doc = new XmlDocument(); doc.Load(path); XmlNodeList providers = doc.SelectNodes("//ConnectionProviderRegistry/ConnectionProvider"); foreach (XmlNode prov in providers) { string name = prov["Name"].InnerText.ToUpper(); string desc = prov["Description"].InnerText; string dll = prov["Assembly"].InnerText; string type = prov["Type"].InnerText; if (!System.IO.Path.IsPathRooted(dll)) dll = System.IO.Path.Combine(_dllRoot, dll); try { Assembly asm = Assembly.LoadFrom(dll); MaestroApiProviderAttribute[] attr = asm.GetCustomAttributes(typeof(MaestroApiProviderAttribute), true) as MaestroApiProviderAttribute[]; if (attr != null && attr.Length == 1) { name = attr[0].Name.ToUpper(); desc = attr[0].Description; var impl = attr[0].ImplType; _ctors[name] = new ConnectionFactoryMethod((initParams) => { BindingFlags flags = BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.CreateInstance; IServerConnection conn = (IServerConnection)impl.InvokeMember(null, flags, null, null, new object[] { initParams }); return conn; }); _providers.Add(new ConnectionProviderEntry(name, desc, attr[0].IsMultiPlatform)); } } catch { } } } internal static NameValueCollection ParseConnectionString(string connectionString) { var builder = new DbConnectionStringBuilder(); builder.ConnectionString = connectionString; NameValueCollection values = new NameValueCollection(); foreach (string key in builder.Keys) { values.Add(key, builder[key].ToString()); } return values; } /// /// Gets a list of registered provider names. The returned names are in upper-case. /// /// public static ConnectionProviderEntry[] GetProviders() { return _providers.ToArray(); } /// /// Registers a new connection provider /// /// The provider entry. /// The factory method. public static void RegisterProvider(ConnectionProviderEntry entry, ConnectionFactoryMethod method) { string name = entry.Name.ToUpper(); if (_ctors.ContainsKey(name)) throw new ArgumentException("Provider already registered: " + entry.Name); _ctors[name] = method; _providers.Add(entry); } /// /// Creates an initialized object given the provider name and connection string /// /// /// /// public static IServerConnection CreateConnection(string provider, string connectionString) { string name = provider.ToUpper(); if (!_ctors.ContainsKey(name)) throw new ArgumentException("Provider not registered: " + provider); ConnectionProviderEntry prv = FindProvider(provider); if (prv != null && !prv.IsMultiPlatform && Platform.IsRunningOnMono) throw new NotSupportedException("The specified provider is not usable in your operating system"); ConnectionFactoryMethod method = _ctors[name]; NameValueCollection initParams = ParseConnectionString(connectionString); return method(initParams); } /// /// Creates an initialized object given the provider name and the initalization parameters /// /// /// /// public static IServerConnection CreateConnection(string provider, NameValueCollection connInitParams) { string name = provider.ToUpper(); if (!_ctors.ContainsKey(name)) throw new ArgumentException("Provider not registered: " + provider); ConnectionProviderEntry prv = FindProvider(provider); if (prv != null && !prv.IsMultiPlatform && Platform.IsRunningOnMono) throw new NotSupportedException("The specified provider is not usable in your operating system"); var method = _ctors[name]; return method(connInitParams); } /// /// Creates an initialized object given the provider name and the initalization parameters. /// /// /// A variable list of initialization parameters. They must be specified in the form: [Param1], [Value1], [Param2], [Value2], etc /// public static IServerConnection CreateConnection(string provider, params string[] initParameters) { var initP = new NameValueCollection(); for (int i = 0; i < initParameters.Length; i += 2) { string name = null; string value = null; if (i < initParameters.Length - 1) name = initParameters[i]; if (i + 1 <= initParameters.Length - 1) value = initParameters[i + 1]; if (name != null) initP[name] = value ?? string.Empty; } return CreateConnection(provider, initP); } private static ConnectionProviderEntry FindProvider(string provider) { foreach (var prv in _providers) { if (prv.Name == provider) return prv; } return null; } } }