// **********************************************************************
//
// Copyright (c) 2003-2017 ZeroC, Inc. All rights reserved.
//
// This copy of Ice is licensed to you under the terms described in the
// ICE_LICENSE file included in this distribution.
//
// **********************************************************************
#if !SILVERLIGHT
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";
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 ex)
{
throw ex;
}
catch(System.Exception ex)
{
PluginInitializationException e = new PluginInitializationException(ex);
e.reason = "plugin `" + p.name + "' initialization failed";
throw e;
}
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)
{
Ice.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);
//
// 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 prefix = "Ice.Plugin.";
Properties properties = _communicator.getProperties();
Dictionary plugins = properties.getPropertiesForPrefix(prefix);
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";
bool hasKey = plugins.ContainsKey(key);
if(hasKey)
{
plugins.Remove("Ice.Plugin." + loadOrder[i]);
}
else
{
key = "Ice.Plugin." + loadOrder[i];
hasKey = plugins.ContainsKey(key);
}
if(hasKey)
{
string value = plugins[key];
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);
//
// Split the entire property value into arguments. An entry point containing spaces
// must be enclosed in quotes.
//
string[] args = null;
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);
string 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);
//
// Extract the assembly name and the class name.
//
string err = "unable to load plug-in `" + entryPoint + "': ";
int sepPos = entryPoint.IndexOf(':');
if(sepPos != -1 && IceInternal.AssemblyUtil.platform_ == IceInternal.AssemblyUtil.Platform.Windows)
{
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.
//
try
{
pluginAssembly = System.Reflection.Assembly.Load(assemblyName);
}
catch(System.IO.IOException ex)
{
try
{
pluginAssembly = System.Reflection.Assembly.LoadFrom(assemblyName);
}
catch(System.IO.IOException)
{
throw ex;
}
}
}
catch(System.Exception ex)
{
#if COMPACT
//
// IceSSL is not supported with the Compact Framework.
//
if(name == "IceSSL")
{
if(!_sslWarnOnce)
{
_communicator.getLogger().warning(
"IceSSL plug-in not loaded: IceSSL is not supported with the .NET Compact Framework");
_sslWarnOnce = true;
}
return;
}
#else
//
// IceSSL is not yet supported with Mono. We avoid throwing an exception in that case,
// so the same configuration can be used with Mono or Visual C#.
//
if(IceInternal.AssemblyUtil.runtime_ == IceInternal.AssemblyUtil.Runtime.Mono && name == "IceSSL")
{
if(!_sslWarnOnce)
{
_communicator.getLogger().warning(
"IceSSL plug-in not loaded: IceSSL is not supported with Mono");
_sslWarnOnce = true;
}
return;
}
#endif
PluginInitializationException e = new PluginInitializationException();
e.reason = err + "unable to load assembly: `" + assemblyName + "': " + ex.ToString();
throw e;
}
//
// Instantiate the class.
//
PluginFactory pluginFactory = null;
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(System.InvalidCastException ex)
{
PluginInitializationException e = new PluginInitializationException(ex);
e.reason = err + "InvalidCastException to Ice.PluginFactory";
throw e;
}
catch(System.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 ex;
}
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 bool _sslWarnOnce = false;
}
}
#endif