// // Copyright (c) ZeroC, Inc. All rights reserved. // namespace Ice { using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; /// /// Applications implement this interface to provide a plug-in factory /// to the Ice run time. /// public interface PluginFactory { /// /// Called by the Ice run time to create a new plug-in. /// /// /// The communicator that is in the process of being initialized. /// The name of the plug-in. /// The arguments that are specified in the plug-ins configuration. /// The plug-in that was created by this method. Plugin create(Communicator communicator, string name, string[] args); } public sealed class PluginManagerI : PluginManager { private static string _kindOfObject = "plugin"; internal static void registerPluginFactory(string name, PluginFactory factory, bool loadOnInit) { if(!_factories.ContainsKey(name)) { _factories[name] = factory; if(loadOnInit) { _loadOnInitialization.Add(name); } } } public void initializePlugins() { if(_initialized) { InitializationException ex = new InitializationException(); ex.reason = "plug-ins already initialized"; throw ex; } // // Invoke initialize() on the plug-ins, in the order they were loaded. // ArrayList initializedPlugins = new ArrayList(); try { foreach(PluginInfo p in _plugins) { try { p.plugin.initialize(); } catch(PluginInitializationException) { throw; } catch(System.Exception ex) { throw new PluginInitializationException(String.Format("plugin `{0}' initialization failed", p.name), ex); } initializedPlugins.Add(p.plugin); } } catch(System.Exception) { // // Destroy the plug-ins that have been successfully initialized, in the // reverse order. // initializedPlugins.Reverse(); foreach(Plugin p in initializedPlugins) { try { p.destroy(); } catch(System.Exception) { // Ignore. } } throw; } _initialized = true; } public string[] getPlugins() { lock(this) { ArrayList names = new ArrayList(); foreach(PluginInfo p in _plugins) { names.Add(p.name); } return (string[])names.ToArray(typeof(string)); } } public Plugin getPlugin(string name) { lock(this) { if(_communicator == null) { throw new CommunicatorDestroyedException(); } Plugin p = findPlugin(name); if(p != null) { return p; } NotRegisteredException ex = new NotRegisteredException(); ex.id = name; ex.kindOfObject = _kindOfObject; throw ex; } } public void addPlugin(string name, Plugin plugin) { lock(this) { if(_communicator == null) { throw new CommunicatorDestroyedException(); } if(findPlugin(name) != null) { AlreadyRegisteredException ex = new AlreadyRegisteredException(); ex.id = name; ex.kindOfObject = _kindOfObject; throw ex; } PluginInfo info = new PluginInfo(); info.name = name; info.plugin = plugin; _plugins.Add(info); } } public void destroy() { lock(this) { if(_communicator != null) { if(_initialized) { ArrayList plugins = (ArrayList)_plugins.Clone(); plugins.Reverse(); foreach(PluginInfo p in plugins) { try { p.plugin.destroy(); } catch(System.Exception ex) { Util.getProcessLogger().warning("unexpected exception raised by plug-in `" + p.name + "' destruction:\n" + ex.ToString()); } } } _communicator = null; } } } public PluginManagerI(Communicator communicator) { _communicator = communicator; _plugins = new ArrayList(); _initialized = false; } public void loadPlugins(ref string[] cmdArgs) { Debug.Assert(_communicator != null); string prefix = "Ice.Plugin."; Properties properties = _communicator.getProperties(); Dictionary plugins = properties.getPropertiesForPrefix(prefix); // // First, load static plugin factories which were setup to load on // communicator initialization. If a matching plugin property is // set, we load the plugin with the plugin specification. The // entryPoint will be ignored but the rest of the plugin // specification might be used. // foreach(var name in _loadOnInitialization) { string key = "Ice.Plugin." + name + ".clr"; string r = null; plugins.TryGetValue(key, out r); if(r != null) { plugins.Remove("Ice.Plugin." + name); } else { key = "Ice.Plugin." + name; plugins.TryGetValue(key, out r); } if(r != null) { loadPlugin(name, r, ref cmdArgs); plugins.Remove(key); } else { loadPlugin(name, "", ref cmdArgs); } } // // Load and initialize the plug-ins defined in the property set // with the prefix "Ice.Plugin.". These properties should // have the following format: // // Ice.Plugin.name[.]=entry_point [args] // // The code below is different from the Java/C++ algorithm // because C# must support full assembly names such as: // // Ice.Plugin.Logger=logger, Version=0.0.0.0, Culture=neutral:LoginPluginFactory // // If the Ice.PluginLoadOrder property is defined, load the // specified plug-ins in the specified order, then load any // remaining plug-ins. // string[] loadOrder = properties.getPropertyAsList("Ice.PluginLoadOrder"); for(int i = 0; i < loadOrder.Length; ++i) { if(loadOrder[i].Length == 0) { continue; } if(findPlugin(loadOrder[i]) != null) { PluginInitializationException e = new PluginInitializationException(); e.reason = "plug-in `" + loadOrder[i] + "' already loaded"; throw e; } string key = "Ice.Plugin." + loadOrder[i] + ".clr"; string value = null; plugins.TryGetValue(key, out value); if(value != null) { plugins.Remove("Ice.Plugin." + loadOrder[i]); } else { key = "Ice.Plugin." + loadOrder[i]; plugins.TryGetValue(key, out value); } if(value != null) { loadPlugin(loadOrder[i], value, ref cmdArgs); plugins.Remove(key); } else { PluginInitializationException e = new PluginInitializationException(); e.reason = "plug-in `" + loadOrder[i] + "' not defined"; throw e; } } // // Load any remaining plug-ins that weren't specified in PluginLoadOrder. // while(plugins.Count > 0) { IEnumerator> p = plugins.GetEnumerator(); p.MoveNext(); string key = p.Current.Key; string val = p.Current.Value; string name = key.Substring(prefix.Length); int dotPos = name.LastIndexOf('.'); if(dotPos != -1) { string suffix = name.Substring(dotPos + 1); if(suffix.Equals("cpp") || suffix.Equals("java")) { // // Ignored // plugins.Remove(key); } else if(suffix.Equals("clr")) { name = name.Substring(0, dotPos); loadPlugin(name, val, ref cmdArgs); plugins.Remove(key); plugins.Remove("Ice.Plugin." + name); } else { // // Name is just a regular name that happens to contain a dot // dotPos = -1; } } if(dotPos == -1) { plugins.Remove(key); // // Is there a .clr entry? // string clrKey = "Ice.Plugin." + name + ".clr"; if(plugins.ContainsKey(clrKey)) { val = plugins[clrKey]; plugins.Remove(clrKey); } loadPlugin(name, val, ref cmdArgs); } } } private void loadPlugin(string name, string pluginSpec, ref string[] cmdArgs) { Debug.Assert(_communicator != null); string[] args = null; string entryPoint = null; if(pluginSpec.Length > 0) { // // Split the entire property value into arguments. An entry point containing spaces // must be enclosed in quotes. // try { args = IceUtilInternal.Options.split(pluginSpec); } catch(IceUtilInternal.Options.BadQuote ex) { PluginInitializationException e = new PluginInitializationException(); e.reason = "invalid arguments for plug-in `" + name + "':\n" + ex.Message; throw e; } Debug.Assert(args.Length > 0); entryPoint = args[0]; // // Shift the arguments. // string[] tmp = new string[args.Length - 1]; Array.Copy(args, 1, tmp, 0, args.Length - 1); args = tmp; // // Convert command-line options into properties. First // we convert the options from the plug-in // configuration, then we convert the options from the // application command-line. // Properties properties = _communicator.getProperties(); args = properties.parseCommandLineOptions(name, args); cmdArgs = properties.parseCommandLineOptions(name, cmdArgs); } string err = "unable to load plug-in `" + entryPoint + "': "; // // Always check the static plugin factory table first, it takes // precedence over the the entryPoint specified in the plugin // property value. // PluginFactory pluginFactory = null; if(!_factories.TryGetValue(name, out pluginFactory)) { // // Extract the assembly name and the class name. // int sepPos = entryPoint.IndexOf(':'); if(sepPos != -1) { const string driveLetters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; if(entryPoint.Length > 3 && sepPos == 1 && driveLetters.IndexOf(entryPoint[0]) != -1 && (entryPoint[2] == '\\' || entryPoint[2] == '/')) { sepPos = entryPoint.IndexOf(':', 3); } } if(sepPos == -1) { PluginInitializationException e = new PluginInitializationException(); e.reason = err + "invalid entry point format"; throw e; } System.Reflection.Assembly pluginAssembly = null; string assemblyName = entryPoint.Substring(0, sepPos); string className = entryPoint.Substring(sepPos + 1); try { // // First try to load the assembly using Assembly.Load, which will succeed // if a fully-qualified name is provided or if a partial name has been qualified // in configuration. If that fails, try Assembly.LoadFrom(), which will succeed // if a file name is configured or a partial name is configured and DEVPATH is used. // // We catch System.Exception as this can fail with System.ArgumentNullException // or System.IO.IOException depending of the .NET framework and platform. // try { pluginAssembly = System.Reflection.Assembly.Load(assemblyName); } catch(System.Exception ex) { try { pluginAssembly = System.Reflection.Assembly.LoadFrom(assemblyName); } catch(System.IO.IOException) { #pragma warning disable CA2200 // Rethrow to preserve stack details throw ex; #pragma warning restore CA2200 // Rethrow to preserve stack details } } } catch(System.Exception ex) { PluginInitializationException e = new PluginInitializationException(); e.reason = err + "unable to load assembly: `" + assemblyName + "': " + ex.ToString(); throw e; } // // Instantiate the class. // System.Type c = null; try { c = pluginAssembly.GetType(className, true); } catch(System.Exception ex) { PluginInitializationException e = new PluginInitializationException(ex); e.reason = err + "GetType failed for `" + className + "'"; throw e; } try { pluginFactory = (PluginFactory)IceInternal.AssemblyUtil.createInstance(c); if(pluginFactory == null) { PluginInitializationException e = new PluginInitializationException(); e.reason = err + "can't find constructor for `" + className + "'"; throw e; } } catch(InvalidCastException ex) { PluginInitializationException e = new PluginInitializationException(ex); e.reason = err + "InvalidCastException to Ice.PluginFactory"; throw e; } catch(UnauthorizedAccessException ex) { PluginInitializationException e = new PluginInitializationException(ex); e.reason = err + "UnauthorizedAccessException: " + ex.ToString(); throw e; } catch(System.Exception ex) { PluginInitializationException e = new PluginInitializationException(ex); e.reason = err + "System.Exception: " + ex.ToString(); throw e; } } Plugin plugin = null; try { plugin = pluginFactory.create(_communicator, name, args); } catch(PluginInitializationException ex) { ex.reason = err + ex.reason; throw; } catch(System.Exception ex) { PluginInitializationException e = new PluginInitializationException(ex); e.reason = err + "System.Exception in factory.create: " + ex.ToString(); throw e; } if(plugin == null) { PluginInitializationException ex = new PluginInitializationException(); ex.reason = err + "factory.create returned null plug-in"; throw ex; } PluginInfo info = new PluginInfo(); info.name = name; info.plugin = plugin; _plugins.Add(info); } private Plugin findPlugin(string name) { foreach(PluginInfo p in _plugins) { if(name.Equals(p.name)) { return p.plugin; } } return null; } internal class PluginInfo { internal string name; internal Plugin plugin; } private Communicator _communicator; private ArrayList _plugins; private bool _initialized; private static Dictionary _factories = new Dictionary(); private static List _loadOnInitialization = new List(); } }