/* * $Id$ * * Copyright (C) 2003-2015 JNode.org * * 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. */ package org.jnode.plugin.manager; import gnu.java.security.action.GetPropertyAction; import java.security.AccessController; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import javax.naming.NamingException; import org.jnode.bootlog.BootLogInstance; import org.jnode.naming.InitialNaming; import org.jnode.permission.JNodePermission; import org.jnode.plugin.Plugin; import org.jnode.plugin.PluginDescriptor; import org.jnode.plugin.PluginException; import org.jnode.plugin.PluginLoaderManager; import org.jnode.plugin.PluginManager; import org.jnode.plugin.PluginPrerequisite; import org.jnode.plugin.PluginRegistry; import org.jnode.plugin.model.PluginRegistryModel; /** * @author epr * @author Matt Paine. */ public final class DefaultPluginManager extends PluginManager { /** * The registry of plugins */ private final PluginRegistry registry; /** * The loader manager */ private final DefaultPluginLoaderManager loaderMgr; private static final int START_TIMEOUT = 10000; private static final JNodePermission START_SYSTEM_PLUGINS_PERM = new JNodePermission("startSystemPlugins"); private static final JNodePermission STOP_PLUGINS_PERM = new JNodePermission("stopPlugins"); /** * Initialize a new instance. This will also bind this pluginmanager in the * initial namespace. * * @param registry */ public DefaultPluginManager(PluginRegistry registry) throws PluginException { this.loaderMgr = new DefaultPluginLoaderManager(); this.registry = registry; try { InitialNaming.bind(NAME, this); } catch (NamingException ex) { throw new PluginException("Cannot register name", ex); } } /** * Gets the plugin loader manager. */ public final PluginLoaderManager getLoaderManager() { return loaderMgr; } /** * Gets the plugin registry */ public PluginRegistry getRegistry() { return registry; } /** * Start all system plugins and plugins with the auto-start flag on. * * @throws PluginException */ public void startSystemPlugins(List descriptors) throws PluginException { SecurityManager sm = System.getSecurityManager(); if (sm != null) { sm.checkPermission(START_SYSTEM_PLUGINS_PERM); } // Resolve all plugins ((PluginRegistryModel) registry).resolveDescriptors(); ((PluginRegistryModel) registry).resolveDescriptors(descriptors); // Set the context classloader Thread.currentThread().setContextClassLoader( registry.getPluginsClassLoader()); // Start the plugins final String cmdLine = (String) AccessController.doPrivileged(new GetPropertyAction("jnode.cmdline", "")); final boolean debug = (cmdLine.indexOf("debug") > 0); final List<PluginDescriptor> descrList = createPluginDescriptorList(); // Order list by priority Collections.sort(descrList, new PriorityComparator()); // 2 loops, first start all system plugins, // then start all auto-start plugins for (int type = 0; type < 2; type++) { BootLogInstance.get().info("Starting " + ((type == 0) ? "system" : "auto-start") + " plugins"); for (PluginDescriptor descr : descrList) { try { final boolean start; if (type == 0) { start = descr.isSystemPlugin(); } else { start = (!descr.isSystemPlugin()) && descr.isAutoStart(); } if (start) { if (debug) { Thread.sleep(250); } startSinglePlugin(descr.getPlugin()); } } catch (Throwable ex) { BootLogInstance.get().error("Cannot start " + descr.getId(), ex); if (debug) { try { Thread.sleep(5000); } catch (InterruptedException ex1) { // Ignore } } } } } // Wait a while until all plugins have finished their startup process if (!isStartPluginsFinished()) { BootLogInstance.get().info("Waiting for plugins to finished their startprocess"); final long start = System.currentTimeMillis(); long now = start; int loop = 0; while (!isStartPluginsFinished() && (now - start < START_TIMEOUT)) { try { if (++loop == 10) { loop = 0; } Thread.sleep(100); } catch (InterruptedException ex) { // Ignore } now = System.currentTimeMillis(); } System.out.println(); if (now >= START_TIMEOUT) { // List all non-finished plugins listUnfinishedPlugins(); } } } /** * Stop all plugins that have been started */ public final void stopPlugins() { SecurityManager sm = System.getSecurityManager(); if (sm != null) { sm.checkPermission(STOP_PLUGINS_PERM); } try { final List<PluginDescriptor> descrList = createPluginDescriptorList(); Collections.reverse(descrList); for (PluginDescriptor descr : descrList) { //descr.getPlugin().stop(); try { stopPlugin(descr); } catch (PluginException ex) { //empty } } BootLogInstance.get().info("Stopped all plugins"); } catch (PluginException ex) { BootLogInstance.get().error("Cannot stop plugins", ex); } } /** * Stops a single plugin and all plugins that depend on it. * * @param d The descriptor to stop. * @throws PluginException if the plugin fails to stop. */ public final void stopPlugin(PluginDescriptor d) throws PluginException { final String id = d.getId(); //BootLogInstance.get().info("__Stopping " + id); for (PluginDescriptor descr : registry) { if (descr.depends(id)) { stopPlugin(descr); } } stopSinglePlugin(d.getPlugin()); } /** * Create a list on plugin descriptors in the right order for startPlugins. * * @return List<PluginDescriptor> */ private List<PluginDescriptor> createPluginDescriptorList() throws PluginException { // Get all descriptors into a hashmap (id, descriptor). final Map<String, PluginDescriptor> all = new HashMap<String, PluginDescriptor>(); final Set<String> systemSet = new HashSet<String>(); for (PluginDescriptor descr : registry) { all.put(descr.getId(), descr); if (descr.isSystemPlugin()) { systemSet.add(descr.getId()); } } // Remove those plugin where some prerequisites do not exist for (Iterator<PluginDescriptor> i = all.values().iterator(); i.hasNext();) { final PluginDescriptor descr = (PluginDescriptor) i.next(); if (!prerequisitesExist(descr, all)) { BootLogInstance.get().info("Skipping plugin " + descr.getId()); all.remove(descr.getId()); systemSet.remove(descr.getId()); i = all.values().iterator(); } } // Now create a sorted list final ArrayList<PluginDescriptor> list = new ArrayList<PluginDescriptor>(); final HashSet<String> nameSet = new HashSet<String>(); while (all.size() > 0) { int additions = 0; for (Iterator<PluginDescriptor> i = all.values().iterator(); i.hasNext();) { final PluginDescriptor descr = (PluginDescriptor) i.next(); if (canAdd(descr, nameSet, systemSet)) { list.add(descr); nameSet.add(descr.getId()); all.remove(descr.getId()); systemSet.remove(descr.getId()); additions++; i = all.values().iterator(); } } if (additions == 0) { throw new PluginException( "Cycle in plugin prerequisites remaining: " + all.keySet()); } } return list; } /** * Can the given descriptor be added to a startPlugin ordered list? * * @param descr * @param nameSet */ private boolean canAdd(PluginDescriptor descr, Set<String> nameSet, Set<String> systemSet) { //Syslog.debug("Testing " + descr.getId()); if (!descr.isSystemPlugin()) { if (!systemSet.isEmpty()) { return false; } } final PluginPrerequisite[] prereq = descr.getPrerequisites(); for (final PluginPrerequisite pr : prereq) { if (!nameSet.contains(pr.getPluginReference().getId())) { //Syslog.debug("Not in set: " + pr.getPluginId()); return false; } } return true; } /** * Do all prerequisite plugins exists? * * @param descr * @param all */ private boolean prerequisitesExist(PluginDescriptor descr, Map<String, PluginDescriptor> all) { final PluginPrerequisite[] prereq = descr.getPrerequisites(); for (final PluginPrerequisite pr : prereq) { if (!all.containsKey(pr.getPluginReference().getId())) { return false; } } return true; } /** * Is the isStartFinished property of all started plugins true. * * @return */ private boolean isStartPluginsFinished() { for (PluginDescriptor descr : registry) { try { final Plugin pi = descr.getPlugin(); if (pi.isActive()) { if (!pi.isStartFinished()) { return false; } } } catch (PluginException ex) { // Ignore } } return true; } /** * List of started but unfinished plugins. */ private void listUnfinishedPlugins() { for (PluginDescriptor descr : registry) { try { final Plugin pi = descr.getPlugin(); if (pi.isActive()) { if (!pi.isStartFinished()) { BootLogInstance.get().error("Plugin " + descr.getId() + " has not yet finished"); } } } catch (PluginException ex) { // Ignore } } } static class StartError { private final Throwable exception; private final String pluginId; /** * @param exception * @param pluginId */ public StartError(final Throwable exception, final String pluginId) { super(); this.exception = exception; this.pluginId = pluginId; } /** * @return Returns the exception. */ public final Throwable getException() { return this.exception; } /** * @return Returns the pluginId. */ public final String getPluginId() { return this.pluginId; } } private static class PriorityComparator implements Comparator<PluginDescriptor> { /** * @see java.util.Comparator#compare(java.lang.Object, java.lang.Object) */ public int compare(PluginDescriptor o1, PluginDescriptor o2) { int p1 = o1.getPriority(); int p2 = o2.getPriority(); if (p1 > p2) { return -1; } if (p1 == p2) { return 0; } return 1; } } }