All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.openbp.jaspira.plugin.PluginMgr Maven / Gradle / Ivy

The newest version!
/*
 *   Licensed 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.
 */
package org.openbp.jaspira.plugin;

import java.io.File;
import java.io.FilenameFilter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.StringTokenizer;

import org.openbp.cockpit.CockpitConstants;
import org.openbp.common.ExceptionUtil;
import org.openbp.common.ReflectUtil;
import org.openbp.common.classloader.XClassLoader;
import org.openbp.common.classloader.XClassLoaderConfiguration;
import org.openbp.common.io.xml.XMLDriver;
import org.openbp.common.io.xml.XMLDriverException;
import org.openbp.common.logger.LogUtil;
import org.openbp.common.resource.ResourceMgr;
import org.openbp.common.resource.ResourceMgrException;
import org.openbp.common.setting.SettingUtil;
import org.openbp.common.util.iterator.EmptyIterator;
import org.openbp.jaspira.gui.plugin.ApplicationBase;
import org.openbp.jaspira.gui.plugin.VisiblePlugin;
import org.springframework.core.io.FileSystemResource;
import org.springframework.core.io.Resource;

/**
 * The PluginMgr is the central instance for managing classes and instances of plugins.
 * It has methods for loading plugin-classr files, creating new instances
 * of a given plugin and retrieving instances of already created plugins.
 *
 * @author Stephan Moritz
 */
public final class PluginMgr
{
	/** Plugin entry status: The class file has not yet been loaded. */
	public static final int PLUGIN_NOT_LOADED = 0;

	/** Plugin entry status: The class file has been loaded, but there a no instances. */
	public static final int PLUGIN_CLASS_LOADED = 1;

	/** Plugin entry status: The plugin has instances. */
	public static final int PLUGIN_HAS_INSTANCES = 2;

	/** Plugin entry status: An exception occurred during loading of class or instantiation. */
	public static final int PLUGIN_LOAD_FAILED = -1;

	/** Singleton instance */
	private static PluginMgr singletonInstance = new PluginMgr();

	/** Table of all available plugins as class name - {@link PluginMgr.PluginEntry} pairs */
	private Map pluginClasses;

	/** Classloader used to load the plugins. */
	private transient ClassLoader classloader;

	/**
	 * Constructor.
	 * We do not want this to be called directly (singleton pattern).
	 */
	private PluginMgr()
	{
		pluginClasses = new HashMap();
	}

	/**
	 * Returns the singleton instance of the plugin manager.
	 */
	public static synchronized PluginMgr getInstance()
	{
		if (singletonInstance == null)
		{
			singletonInstance = new PluginMgr();
		}

		return singletonInstance;
	}

	//////////////////////////////////////////////////
	// @@ Public methods
	//////////////////////////////////////////////////

	/**
	 * Returns the (already existing instance) of a plugin with the given
	 * unique ID or null if there is no such instance.
	 */
	public Plugin getPlugin(String uniqueId)
	{
		String classname = uniqueId.substring(0, uniqueId.indexOf(Plugin.ID_DELIMETER));

		PluginEntry entry = (PluginEntry) pluginClasses.get(classname);

		if (entry != null)
		{
			return entry.getPluginInstance(uniqueId);
		}

		return null;
	}

	/**
	 * Creates a new instance of the given plugin.
	 *
	 * @param classname Plugin class
	 * @param parent Parent for the new plugin in the plugin tree
	 * @return The new plugin or null if it could not be instantiated
	 */
	public Plugin createInstance(String classname, Plugin parent)
	{
		PluginEntry entry = (PluginEntry) pluginClasses.get(classname);
		if (entry == null)
		{
			entry = new PluginEntry(classname);
			pluginClasses.put(classname, entry);
		}

		return entry.createInstance(parent);
	}

	/**
	 * Creates a new instance of the given plugin.
	 *
	 * @param profile Plugin profile
	 * @param pluginClassLoader Separate class loader for the plugin or null
	 * @param parent Parent for the new plugin in the plugin tree
	 * @return The new plugin or null if it could not be instantiated
	 */
	public Plugin createInstance(PluginProfile profile, ClassLoader pluginClassLoader, Plugin parent)
	{
		PluginEntry entry = (PluginEntry) pluginClasses.get(profile.getClassName());
		if (entry == null)
		{
			entry = new PluginEntry(profile);
			entry.setPluginClassLoader(pluginClassLoader);
			pluginClasses.put(profile.getClassName(), entry);
		}

		return entry.createInstance(parent);
	}

	/**
	 * Create a new instance of the given class.
	 *
	 * @param cls Plugin class
	 * @param parent Parent for the new plugin in the plugin tree
	 * @return The new plugin or null if it could not be instantiated
	 */
	public Plugin createInstance(Class cls, Plugin parent)
	{
		return createInstance(cls.getName(), parent);
	}

	/**
	 * Creates a new instance via a supplied state object.
	 *
	 * @param state Plugin state for the new plugin
	 * @param parent Parent for the new plugin in the plugin tree
	 * @return The new plugin or null if it could not be instantiated
	 */
	public Plugin createInstance(PluginState state, Plugin parent)
	{
		String classname = state.getPluginClassName();

		PluginEntry entry = (PluginEntry) pluginClasses.get(classname);
		if (entry == null)
		{
			entry = new PluginEntry(classname);
			pluginClasses.put(classname, entry);
		}

		return entry.createInstance(state, parent);
	}

	/**
	 * Creates a new instance of the given plugin.
	 *
	 * @param classname Plugin class
	 * @param parent Parent for the new plugin in the plugin tree
	 * @return The new plugin or null if it could not be instantiated
	 */
	public VisiblePlugin createVisibleInstance(String classname, Plugin parent)
	{
		return (VisiblePlugin) createInstance(classname, parent);
	}

	/**
	 * Create a new instance of the given class.
	 *
	 * @param cls Plugin class
	 * @param parent Parent for the new plugin in the plugin tree
	 * @return The new plugin or null if it could not be instantiated
	 */
	public VisiblePlugin createVisibleInstance(Class cls, Plugin parent)
	{
		return (VisiblePlugin) createInstance(cls, parent);
	}

	/**
	 * Creates a new instance via a supplied state object.
	 *
	 * @param state Plugin state for the new plugin
	 * @param parent Parent for the new plugin in the plugin tree
	 * @return The new plugin or null if it could not be instantiated
	 */
	public VisiblePlugin createVisibleInstance(PluginState state, Plugin parent)
	{
		return (VisiblePlugin) createInstance(state, parent);
	}

	/**
	 * Creates a duplicate of the supplied plugin via its state object.
	 *
	 * @param source Plugin to copy
	 * @return The new plugin or null if it could not be instantiated
	 */
	public Plugin createCopy(Plugin source)
	{
		return createInstance(source.getPluginState(), source.getParentPlugin());
	}

	/**
	 * Registers an externally created Plugin with the manager.
	 * This should only be used in special cases.
	 *
	 * @param plugin Plugin to register
	 */
	public void registerPlugin(Plugin plugin)
	{
		String classname = plugin.getClass().getName();

		PluginEntry entry = (PluginEntry) pluginClasses.get(classname);
		if (entry == null)
		{
			entry = new PluginEntry(classname);
			pluginClasses.put(classname, entry);
		}

		entry.addInstance(plugin, null);
	}

	/**
	 * Removes the instance of a plugin with the given uniqueID.
	 *
	 * @param uniqueId Id of the plugin to remove
	 * @return
	 *		true	There are no instances of this plugin left.
* false There is at least one instance alive. */ public boolean removeInstance(String uniqueId) { return removeInstance(getPlugin(uniqueId)); } /** * Removes a plugin from its container, uninstalls it and deletes it. * * @param plugin Plugin to remove * @return * true There are no instances of this plugin left.
* false There is at least one instance alive. */ public boolean removeInstance(Plugin plugin) { if (plugin == null) { return true; } PluginEntry entry = (PluginEntry) pluginClasses.get(plugin.getClass().getName()); if (entry != null) { return entry.removeInstance(plugin); } return true; } /** * Gets all currently available instances of a given plugin (identified by its class name). * * @param classname Plugin class * @return An iterator of {@link Plugin} objects */ public Iterator getPluginInstances(String classname) { PluginEntry entry = (PluginEntry) pluginClasses.get(classname); return entry != null ? entry.getPluginInstances() : EmptyIterator.getInstance(); } /** * Gets the instance of a given plugin (identified by its class name). * The plugin is expected to be present only once in the system. * * @param classname Plugin class * @return The instance or null if no such plugin exists */ public Plugin getPluginInstance(String classname) { PluginEntry entry = (PluginEntry) pluginClasses.get(classname); if (entry != null) { Iterator it = entry.getPluginInstances(); if (it.hasNext()) { return (Plugin) it.next(); } } return null; } /** * Returns a list of all loaded plugins. * * @return A list of {@link Plugin} objects */ public List getPluginInstances() { List list = new ArrayList(); for (Iterator it = pluginClasses.values().iterator(); it.hasNext();) { PluginEntry entry = (PluginEntry) it.next(); for (Iterator it2 = entry.getPluginInstances(); it2.hasNext();) { list.add(it2.next()); } } return list; } /** * Returns the class loader used to load plugins. * Instantiated if not present yet. */ ClassLoader getClassLoader() { if (classloader == null) { classloader = this.getClass().getClassLoader(); } return classloader; } ////////////////////////////////////////////////// // @@ Custom plugin support ////////////////////////////////////////////////// /** * This method loads custom plugins that may be specified in a property file. * The property file entry (e. g. in the Cockpit.properties file) - if present - is expected to contain * a space- or comma-separated list of plugin class names to load. * Any error will be printed to the output, but ignored otherwise. * * \bNote\b: This function is preliminary until a full-fledged xml-based plugin loading mechanism * has been implemented. * * @param settingName Name of the property file entry (e. g. */ public void loadCustomPlugins(String settingName) { String strList = SettingUtil.getStringSetting(settingName, null); if (strList != null) { StringTokenizer st = new StringTokenizer(strList, " ,"); while (st.hasMoreTokens()) { String pluginClassName = st.nextToken(); if (pluginClassName.length() != 0) { try { createInstance(pluginClassName, ApplicationBase.getInstance()); } catch (Exception e) { // Log as warning LogUtil.error(getClass(), "Error loading custom plugin $0.", pluginClassName, e); ExceptionUtil.printTrace(e); } } } } } /** * This method loads custom plugins from the 'plugin' directory. * This directory contains a sub directory for each plugin. * Each sub directory contains a plugin profile ending with '.plugin.xml' and any jar files needed by the plugin. */ public void loadPluginsFromResource() { XMLDriver xmlDriver = XMLDriver.getInstance(); try { Class [] profileClasses = { PluginProfile.class, }; xmlDriver.loadMappings(profileClasses); } catch (XMLDriverException e) { ExceptionUtil.printTrace(e); return; } ResourceMgr resMgr = ResourceMgr.getDefaultInstance(); String resourcePattern = CockpitConstants.PLUGIN + "/*/*.plugin.xml"; Resource[] resources = null; try { resources = resMgr.findResources(resourcePattern); } catch (ResourceMgrException e) { return; } for (int i = 0; i < resources.length; i++) { PluginProfile profile = null; try { profile = (PluginProfile) xmlDriver.deserializeResource(PluginProfile.class, resources[i]); } catch (XMLDriverException e) { LogUtil.error(getClass(), "Error reading plugin profile $0.", resources[i].getDescription(), e); continue; } String pluginName = profile.getName(); if (pluginName == null) { pluginName = profile.getClassName(); } LogUtil.info(getClass(), "Loading plugin $0.", pluginName); try { ClassLoader pluginClassLoader = obtainPluginClassLoader(resources[i], pluginName); createInstance(profile, pluginClassLoader, ApplicationBase.getInstance()); } catch (Throwable t) { // Log as warning LogUtil.error(getClass(), "Error loading custom plugin $0.", resources[i].getDescription(), t); } } } private ClassLoader obtainPluginClassLoader(Resource r, String pluginName) { ClassLoader cl = getClassLoader(); if (r instanceof FileSystemResource) { XClassLoaderConfiguration config = new XClassLoaderConfiguration(); config.setName("Plugin '" + pluginName + "' class loader"); config.setParentClassLoader(cl); config.setLoggingEnabled(false); FilenameFilter fnf = new FilenameFilter() { public boolean accept(File dir, String name) { return name.endsWith(".jar"); } }; File pluginDir = null; try { pluginDir = r.getFile().getParentFile(); File [] jars = pluginDir.listFiles(fnf); for (int i = 0; i < jars.length; ++i) { config.addRepository(jars[i].getAbsolutePath()); LogUtil.info(getClass(), "Loading repository for plugin $0: $1.", pluginName, jars[i]); } cl = new XClassLoader(config); } catch (Exception e) { ExceptionUtil.printTrace(e); } } return cl; } ///////////////////////////////////////////////////////////////////////// // @@ Inner classes ///////////////////////////////////////////////////////////////////////// /** * A PluginEntry contains various information about a particular plugin type. */ protected class PluginEntry { /** Class name */ private String pluginClassName; /** Separate class loader for the plugin or null */ private ClassLoader pluginClassLoader; /** Plugin profile */ private PluginProfile profile; /** Class of this plugin. Can be null if the class is not yet loaded. */ protected transient Class pluginClass; /** Table containing the existing instances of this plugin as unique id (String) - {@link Plugin} pair. */ protected Map instances; /** Status of this plugin */ protected int status; /** * Creates a new plugin entry for a plugin with the given name. * * @param pluginClassName Plugin class name */ public PluginEntry(String pluginClassName) { this.pluginClassName = pluginClassName; status = PLUGIN_NOT_LOADED; } /** * Creates a new plugin entry for a plugin based on the given profile. * * @param profile Plugin profile */ public PluginEntry(PluginProfile profile) { this.profile = profile; this.pluginClassName = profile.getClassName(); status = PLUGIN_NOT_LOADED; } /** * Gets the class name. */ public String getPluginClassName() { return pluginClassName; } /** * Gets the separate class loader for the plugin or null. */ public ClassLoader getPluginClassLoader() { if (pluginClassLoader != null) { return pluginClassLoader; } return getClassLoader(); } /** * Sets the separate class loader for the plugin or null. */ public void setPluginClassLoader(ClassLoader pluginClassLoader) { this.pluginClassLoader = pluginClassLoader; } /** * Returns the class-object of the plugin. If it has not been * loaded yet, load it via the class loader of the pluginManager. */ private Class getPluginClass() { if (pluginClass == null && status != PLUGIN_LOAD_FAILED) { try { ClassLoader cl = getPluginClassLoader(); pluginClass = cl.loadClass(pluginClassName); status = PLUGIN_CLASS_LOADED; } catch (ClassNotFoundException e) { ExceptionUtil.printTrace(e); status = PLUGIN_LOAD_FAILED; } } return pluginClass; } /** * Returns the instance of the plugin with the given unique id * or null if none such present. */ public Plugin getPluginInstance(String uniqueId) { return (Plugin) instances.get(uniqueId); } /** * Returns an iterator over all instances of this plugin. */ public Iterator getPluginInstances() { return instances == null ? EmptyIterator.getInstance() : instances.values().iterator(); } ///////////////////////////////////////////////////////////////////////// // @@ Instantiation ///////////////////////////////////////////////////////////////////////// /** * Creates a new instance of a plugin with the given state data. * * @param state Plugin state for the new plugin * @param parent Parent for the new plugin in the plugin tree * @return The new plugin or null if it could not be instantiated */ public Plugin createInstance(PluginState state, Plugin parent) { Plugin instance = createInstance(parent); if (instance != null) { instance.setPluginState(state); } return instance; } /** * Creates a new instance of the plugin. * * @param parent Parent for the new plugin in the plugin tree * @return The new plugin or null if it could not be instantiated */ public Plugin createInstance(Plugin parent) { // We retrieve the class Class appClass = getPluginClass(); Plugin plugin = null; if (appClass != null) { try { // Create the plugin instance ClassLoader cl = getPluginClassLoader(); plugin = (Plugin) ReflectUtil.instantiate(appClass, cl, Plugin.class, "plugin"); if (!ConfigMgr.getInstance().evaluate(plugin.getCondition())) { // Plugin condition evaluation failed, don't create return null; } if (profile != null && (plugin instanceof AbstractPlugin)) { ((AbstractPlugin) plugin).initializeFromPluginProfile(profile); } plugin.initializePlugin(); status = PLUGIN_HAS_INSTANCES; addInstance(plugin, parent); } catch (Exception e) { e.printStackTrace(); status = PLUGIN_LOAD_FAILED; } } return plugin; } /** * Adds an existing instance of a plugin. * * @param plugin Plugin to add */ public void addInstance(Plugin plugin, Plugin parent) { if (instances == null) { instances = new HashMap(); } if (instances.isEmpty()) { // First instance loaded plugin.installFirstPlugin(); } plugin.installPlugin(); instances.put(plugin.getUniqueId(), plugin); plugin.setParentPlugin(parent); } /** * Removes the given plugin from the user interface structure as well as from the plugin manager itself. * Causes the Plugin to uninstall itself before closing. * * @param plugin Plugin to remove * @return * true There are no instances of this plugin left.
* false There is at least one instance alive. */ public boolean removeInstance(Plugin plugin) { // Check if this instance is still registered, don't uninstall twice if (instances.get(plugin.getUniqueId()) != null) { plugin.setParentPlugin(null); // Remove plugin from the ui if visible if (plugin instanceof VisiblePlugin) { VisiblePlugin vp = (VisiblePlugin) plugin; if (vp.getPluginHolder() != null) { vp.getPluginHolder().unlinkHolder(); } } plugin.uninstallPlugin(); instances.remove(plugin.getUniqueId()); if (instances.size() == 0) { plugin.uninstallLastPlugin(); status = PLUGIN_CLASS_LOADED; } } return instances.isEmpty(); } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy