/* * Copyright(c) 2005 Center for E-Commerce Infrastructure Development, The * University of Hong Kong (HKU). All Rights Reserved. * * This software is licensed under the GNU GENERAL PUBLIC LICENSE Version 2.0 [1] * * [1] http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt */ package hk.hku.cecid.piazza.commons.spa; import hk.hku.cecid.piazza.commons.Sys; import hk.hku.cecid.piazza.commons.io.FileSystem; import hk.hku.cecid.piazza.commons.io.IOHandler; import hk.hku.cecid.piazza.commons.util.Zip; import java.io.File; import java.io.FileOutputStream; import java.io.InputStream; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.Iterator; import java.util.Map; /** * A PluginRegistry is a registry which stores all the plugin configurations. It * has a fixed registry location and the plugin configurations are specified in * plugin descriptors. * * @see Plugin * * @author Hugo Y. K. Lam * */ public class PluginRegistry { private String pluginExtension = PluginFile.DEFAULT_FILE_EXT; private String pluginDescriptor = PluginDescriptor.DEFAULT_DESCRIPTOR_NAME; private Map plugins = new HashMap(); private boolean activated = false; private boolean hasErrors = false; private FileSystem registrySystem; /** * Creates a new instance of PluginRegistry. * * @param registry the registry location. * @throws PluginException if the registry is invalid. */ public PluginRegistry(String registry) throws PluginException { this(registry, null); } /** * Creates a new instance of PluginRegistry. * * @param registry the registry location. * @throws PluginException if the registry is invalid. */ public PluginRegistry(File registry) throws PluginException { this(registry, null); } /** * Creates a new instance of PluginRegistry. * * @param registry the registry location. * @param descriptor the plugin descriptor name. * @throws PluginException if the registry is invalid. */ public PluginRegistry(String registry, String descriptor) throws PluginException { this(registry == null ? null : new File(registry), descriptor); } /** * Creates a new instance of PluginRegistry. * * @param registry the registry location. * @param descriptor the plugin descriptor name. * @throws PluginException if the registry is invalid. */ public PluginRegistry(File registry, String descriptor) throws PluginException { if (registry != null && !registry.exists()) { registry.mkdirs(); } if (registry == null || !registry.exists() || !registry.isDirectory()) { throw new PluginException("Invalid plugin registry: " + registry); } if (descriptor != null && !"".equals(descriptor = descriptor.trim())) { pluginDescriptor = descriptor; } registrySystem = new FileSystem(registry); deploySpaFiles(registrySystem.getFiles(false, ".*\\."+pluginExtension)); createPlugins(registrySystem.getDirectories(false)); setClasspaths(plugins.values()); if (hasErrors) { Sys.main.log.info("Plugin registry (" + registrySystem + ") initialized with errors."); } else { Sys.main.log.info("Plugin registry (" + registrySystem + ") initialized successfully."); } } /** * Deploys the SPA files in this plugin registry. Any previously deployed * plugin folders will be overwritten. The SPA file will be deleted upon a * successful deployment. * * @param spaCollection the SPA files to be deployed. * @return the plugin folders of the deployed SPA files. */ private Collection deploySpaFiles(Collection spaCollection) { ArrayList spaDirs = new ArrayList(); Iterator spaFiles = spaCollection.iterator(); while (spaFiles.hasNext()) { File spaFile = (File)spaFiles.next(); try { Sys.main.log.debug("Deploying SPA file: " + spaFile); PluginFile pluginFile = new PluginFile(spaFile, pluginDescriptor); String pluginID = pluginFile.getDescriptor().getId(); File pluginDir = new File(registrySystem.getRoot(), pluginID); try { undeploy(pluginID); } catch (Exception e) { Sys.main.log.warn("Cannot undeploy plugin for re-deployment", e); } Zip.extract(spaFile, pluginDir); spaFile.delete(); spaDirs.add(pluginDir); Sys.main.log.info("SPA file (" + spaFile + ") deployed successfully"); } catch (Throwable e) { hasErrors = true; Sys.main.log.error("Error in deploying SPA file: " + spaFile, e); } } return spaDirs; } /** * Creates all the plugins in this plugin registry. * * @param pluginDirCollection the plugin folders for creating the plugins. * @return the created plugins. */ private Collection createPlugins(Collection pluginDirCollection) { ArrayList createdPlugins = new ArrayList(); Iterator pluginDirs = pluginDirCollection.iterator(); while (pluginDirs.hasNext()) { try { File pluginDir = (File)pluginDirs.next(); if (!new File(pluginDir, pluginDescriptor).exists()) { Sys.main.log.info("Cleaning invalid plugin directory: " + pluginDir); new FileSystem(pluginDir).remove(); continue; } Plugin plugin = new Plugin(this, pluginDir, pluginDescriptor); plugins.put(plugin.getId(), plugin); createdPlugins.add(plugin); Sys.main.log.info("Plugin '" + plugin.getId() + "' created successfully"); } catch (Throwable e) { hasErrors = true; Sys.main.log.error("Error in creating plugin", e); } } return createdPlugins; } /** * Sets imported classpaths. * * @param pluginCollection the plugins for setting the classpaths. */ private void setClasspaths(Collection pluginCollection) { Iterator allPlugins = pluginCollection.iterator(); while (allPlugins.hasNext()) { Plugin plugin = (Plugin) allPlugins.next(); Iterator imports = plugin.getImports().iterator(); while (imports.hasNext()) { Import imp = (Import) imports.next(); Plugin importedPlugin = imp.getImportedPlugin(); if (importedPlugin != null) { plugin.getClassLoader().importClassLoader( importedPlugin.getClassLoader()); } } } } /** * Deploys a plugin to the plugin registry. * * @param spa the SPA file input stream. * @return the plugin ID of the deployed plugin. * @throws PluginException if unable to deploy the plugin. */ public String deploy(InputStream spa) throws PluginException { try { File spaFile = File.createTempFile("plugin-", "."+pluginExtension); FileOutputStream fos = new FileOutputStream(spaFile); IOHandler.pipe(spa, fos); fos.close(); Collection spaFiles = new ArrayList(); spaFiles.add(spaFile); Collection spaDirs = deploySpaFiles(spaFiles); if (spaDirs.size() < 1) { throw new PluginException("Unable to deploy SPA file: "+spaFile); } String id = ((File)spaDirs.iterator().next()).getName(); Collection createdPlugins = createPlugins(spaDirs); if (createdPlugins.size() < 1) { throw new PluginException("Unable to create plugin: "+id); } setClasspaths(createdPlugins); return id; } catch (Exception e) { throw new PluginException("Unable to deploy plugin", e); } } /** * Undeploys a plugin from the plugin registry. * * @param pluginID the plugin ID to undeploy. * @throws PluginException if unable to undeploy the plugin. */ public void undeploy(String pluginID) throws PluginException { try { if (pluginID != null) { pluginID = pluginID.trim(); File pluginDir = new File(registrySystem.getRoot(), pluginID); if (pluginDir.exists()) { new FileSystem(pluginDir).purge(); Sys.main.log.info("Plugin '"+pluginID+"' undeployed successfully"); } } } catch (Exception e) { throw new PluginException("Unable to undeploy plugin: "+pluginID, e); } } /** * Activates the plugin registry if it is not yet activated. */ public synchronized void activate() { if (!activated) { hasErrors = hasErrors | activatePlugins() | bindExtensionPoints(); activated = true; } } /** * Deactivates the plugin registry if it is activated. */ public synchronized void deactivate() { if (activated) { deactivatePlugins(); activated = false; } } /** * Activate all plugins in this registry. * * @return true if there is any error occurred during activating plugins. */ private boolean activatePlugins() { boolean hasErrors = false; Iterator allPlugins = plugins.values().iterator(); while (allPlugins.hasNext()) { Plugin plugin = (Plugin) allPlugins.next(); try { plugin.activate(); } catch (Throwable e) { hasErrors = true; Sys.main.log.error("Error in activating plugin: " + plugin.getId(), e); } } return hasErrors; } /** * Deactivate all plugins in this registry. * * @return true if there is any error occurred during activating plugins. */ private boolean deactivatePlugins() { boolean hasErrors = false; Iterator allPlugins = plugins.values().iterator(); while (allPlugins.hasNext()) { Plugin plugin = (Plugin) allPlugins.next(); try { plugin.deactivate(); } catch (Throwable e) { hasErrors = true; Sys.main.log.error("Error in deactivating plugin: " + plugin.getId(), e); } } return hasErrors; } /** * Binds all extensions to their corresponding extension points. * * @return true if there is any error occurred during binding extensions. */ private boolean bindExtensionPoints() { boolean hasErrors = false; Iterator allPlugins = plugins.values().iterator(); while (allPlugins.hasNext()) { Plugin plugin = (Plugin) allPlugins.next(); Iterator extensionPoints = plugin.getExtensionPoints().iterator(); while (extensionPoints.hasNext()) { ExtensionPoint extensionPoint = (ExtensionPoint) extensionPoints .next(); try { Collection extensions = getAllExtensions(extensionPoint .getId()); extensionPoint.processExtensions(extensions); } catch (Throwable e) { hasErrors = true; Sys.main.log.error("Error in binding extension point: " + extensionPoint.getId(), e); } } } return hasErrors; } /** * Checks if this plugin registry has already been activated. * * @return true if this plugin registry has already been activated. */ public boolean isActivated() { return activated; } /** * Checks if there is any error occurred during the initialization or * activation of this plugin registry. * * @return true if there is any error occurred during the activation of this * plugin registry. */ public boolean hasErrors() { return hasErrors; } /** * Gets a plugin from this plugin registry. * * @param id the plugin ID. * @return the plugin corresponding to the specified plugin ID. */ public Plugin getPlugin(String id) { if (id == null) { return null; } else { return (Plugin) plugins.get(id); } } /** * Gets all plugins in this plugin registry. * * @return all plugins in this plugin registry. */ public Collection getPlugins() { return plugins.values(); } /** * Gets all extensions corresponding to the specified extension point. * * @param point the extension point. * @return all extensions corresponding to the specified extension point. */ public Collection getAllExtensions(String point) { ArrayList list = new ArrayList(); if (point != null) { Iterator plugins = getPlugins().iterator(); while (plugins.hasNext()) { Plugin plugin = (Plugin) plugins.next(); Collection extensions = plugin.getExtensions(point); list.addAll(extensions); } } return list; } /** * Gets the plugin registry location. * * @return the plugin registry location. */ public String getLocation() { return registrySystem.toString(); } /** * Returns a string representation of this plugin registry. * * @return a string representation of this plugin registry. * @see java.lang.Object#toString() */ public String toString() { return "Plugin registry: " + getLocation(); } }