Package org.jnode.plugin.model

Source Code of org.jnode.plugin.model.PluginRegistryModel

/*
* $Id$
*
* Copyright (C) 2003-2014 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.model;

import java.io.IOException;
import java.net.URL;
import java.nio.ByteBuffer;
import java.security.AccessController;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.jnode.plugin.Extension;
import org.jnode.plugin.ExtensionPoint;
import org.jnode.plugin.PluginDescriptor;
import org.jnode.plugin.PluginException;
import org.jnode.plugin.PluginLoader;
import org.jnode.plugin.PluginPrerequisite;
import org.jnode.plugin.PluginReference;
import org.jnode.plugin.PluginRegistry;
import org.jnode.plugin.PluginSecurityConstants;
import org.jnode.vm.isolate.VmIsolateLocal;
import org.jnode.vm.objects.BootableHashMap;
import org.jnode.vm.objects.VmSystemObject;
import org.xml.sax.EntityResolver;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;

/**
* @author epr
*/
public final class PluginRegistryModel extends VmSystemObject implements
    PluginRegistry {

    /**
     * A map of all descriptors (id, descriptor)
     */
    private final BootableHashMap<String, PluginDescriptor> descriptorMap;

    /**
     * A map off all extensionpoints (id, ep)
     */
    private final BootableHashMap<String, ExtensionPoint> extensionPoints;

    private transient VmIsolateLocal<PluginsClassLoader> classLoaderHolder;

    /**
     * Initialize this instance.
     *
     * @param pluginFiles
     */
    PluginRegistryModel(URL[] pluginFiles) throws PluginException {
        this.extensionPoints = new BootableHashMap<String, ExtensionPoint>();
        this.descriptorMap = new BootableHashMap<String, PluginDescriptor>();
        final List<PluginDescriptorModel> descriptors = loadDescriptors(pluginFiles);
        resolveDescriptors(descriptors);
    }

    /**
     * Gets the descriptor of the plugin with the given id.
     *
     * @param pluginId
     * @return The plugin descriptor found, or null if not found
     */
    public PluginDescriptor getPluginDescriptor(String pluginId) {
        return (PluginDescriptor) descriptorMap.get(pluginId);
    }

    /**
     * Gets the extension point with the given id.
     *
     * @param id
     * @return The extension point found, or null if not found
     */
    public ExtensionPoint getExtensionPoint(String id) {
        return (ExtensionPoint) extensionPoints.get(id);
    }

    /**
     * Returns an iterator to iterate over all PluginDescriptor's.
     *
     * @return Iterator&lt;PluginDescriptor&gt;
     */
    public Iterator<PluginDescriptor> iterator() {
        return descriptorMap.values().iterator();
    }

    /**
     * Load all plugin descriptors.
     *
     * @param pluginUrls
     */
    private List<PluginDescriptorModel> loadDescriptors(URL[] pluginUrls) throws PluginException {
        final int max = pluginUrls.length;
        final ArrayList<PluginDescriptorModel> descriptors = new ArrayList<PluginDescriptorModel>(max);

        for (int i = 0; i < max; i++) {
            //System.out.println(pluginUrls[i]);
            descriptors.add(loadPlugin(pluginUrls[i], false));
        }
        return descriptors;
    }

    /**
     * Resolve all plugin descriptors.
     */
    public void resolveDescriptors() throws PluginException {
        for (PluginDescriptor pluginDescriptor : descriptorMap.values()) {
            final PluginDescriptorModel descr = (PluginDescriptorModel) pluginDescriptor;
            descr.resolve(this);
        }
    }

    /**
     * Resolve the plugins corresponding to a collection of supplied descriptors.
     *
     * @param descriptors the collection of descriptors to be resolved.
     */
    public void resolveDescriptors(Collection<PluginDescriptorModel> descriptors)
        throws PluginException {
        // This method uses the brute-force approach of repeatedly checking the descriptors
        // in order until if finds one that will resolve.  This gives O(N**2) calls to
        // canResolve unless the descriptor collection is suitably ordered.
        while (!descriptors.isEmpty()) {
            boolean change = false;
            for (Iterator<PluginDescriptorModel> i = descriptors.iterator(); i.hasNext();) {
                final PluginDescriptorModel descr = i.next();
                if (canResolve(descr)) {
                    descr.resolve(this);
                    i.remove();
                    change = true;
                }
            }
            if (!change) {
                throw new PluginException("Failed to resolve all descriptors: " + descriptors);
            }
        }
    }

    /**
     * Check to see if a plugin is resolvable given the current (simulated) registry state.
     *
     * @param descr the descriptor for the plugin at issue.
     * @return <code>true</code> if the plugin is resolvable.
     */
    private final boolean canResolve(PluginDescriptor descr) {
        final PluginPrerequisite reqs[] = descr.getPrerequisites();
        final int length = reqs.length;
        for (int i = 0; i < length; i++) {
            // We cannot (yet) resolve a plugin if it imports an as-yet unresolved plugin.
            if (getPluginDescriptor(reqs[i].getPluginReference().getId()) == null) {
                return false;
            }
        }
        final Extension[] exts = descr.getExtensions();
        final ExtensionPoint[] extPoints = descr.getExtensionPoints();
        final int extsLength = exts.length;
        final int extPointsLength = extPoints.length;
        for (int i = 0; i < extsLength; i++) {
            // We cannot resolve a plugin has an extension whose extension point defined by an
            // as-yet unresolved plugin.  However, if an extension and its matching extension point
            // are defined in the same plugin, this will not prevent plugin resolution.
            if (getPluginDescriptor(exts[i].getExtensionPointPluginId()) == null) {
                boolean oneOfMine = false;
                String epId = exts[i].getExtensionPointUniqueIdentifier();
                for (int j = 0; j < extPointsLength; j++) {
                    if (epId.equals(extPoints[j].getUniqueIdentifier())) {
                        oneOfMine = true;
                        break;
                    }
                }
                if (!oneOfMine) {
                    return false;
                }
            }
        }
        return true;
    }

    /**
     * Register a plugin descriptor.
     *
     * @param descr
     */
    protected synchronized void registerPlugin(PluginDescriptorModel descr)
        throws PluginException {
        final String id = descr.getId();
        if (descriptorMap.containsKey(id)) {
            throw new PluginException("Duplicate plugin " + id);
        }
        descriptorMap.put(id, descr);
    }

    /**
     * Register a plugin descriptor.
     *
     * @param descr
     */
    protected synchronized void unregisterPlugin(PluginDescriptorModel descr)
        throws PluginException {
        final String id = descr.getId();
        descriptorMap.remove(id);
    }

    /**
     * Register a known extension point.
     *
     * @param ep
     */
    protected synchronized void registerExtensionPoint(ExtensionPoint ep)
        throws PluginException {
        final BootableHashMap<String, ExtensionPoint> epMap = this.extensionPoints;
        if (epMap.containsKey(ep.getUniqueIdentifier())) {
            throw new PluginException(
                "Duplicate extension point " + ep.getUniqueIdentifier());
        }
        epMap.put(ep.getUniqueIdentifier(), ep);
    }

    /**
     * Unregister a known extension point.
     *
     * @param ep
     */
    protected synchronized void unregisterExtensionPoint(ExtensionPoint ep)
        throws PluginException {
        final BootableHashMap<String, ExtensionPoint> epMap = this.extensionPoints;
        epMap.remove(ep.getUniqueIdentifier());
    }

    /**
     * Load a plugin from a given URL. This will not activate the plugin.
     *
     * @param pluginUrl
     * @return The descriptor of the loaded plugin.
     * @throws PluginException
     */
    public PluginDescriptorModel loadPlugin(final URL pluginUrl, boolean resolve)
        throws PluginException {
        final PluginRegistryModel registry = this;
        final PluginJar pluginJar;
        try {
            pluginJar = AccessController
                .doPrivileged(new PrivilegedExceptionAction<PluginJar>() {

                    public PluginJar run() throws PluginException, IOException {
                        return new PluginJar(registry, pluginUrl);
                    }
                });
        } catch (PrivilegedActionException pax) {
            final Throwable ex = pax.getException();
            if (ex instanceof PluginException) {
                throw (PluginException) ex;
            } else {
                throw new PluginException(ex);
            }
        }
        final PluginDescriptorModel descr = pluginJar.getDescriptorModel();
        if (resolve) {
            descr.resolve(this);
        }
        return descr;
    }

    /**
     * {@inheritDoc}
     */
    public PluginDescriptor loadPlugin(final PluginLoader loader, final PluginReference pluginReference,
                                       boolean resolve)
        throws PluginException {
        final SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            sm.checkPermission(PluginSecurityConstants.LOAD_PERM);
        }
        // Load the requested plugin
        final PluginDescriptorModel descr = loadPluginImpl(loader, pluginReference);

        if (resolve) {
            final HashMap<String, PluginDescriptorModel> descriptors = new HashMap<String, PluginDescriptorModel>();
            descriptors.put(descr.getId(), descr);
            // Load the dependent plugins
            loadDependencies(loader, descr, descriptors);

            // Resolve the loaded descriptors.
            resolveDescriptors(descriptors.values());
        }

        return descr;
    }

    private final void loadDependencies(PluginLoader loader, PluginDescriptorModel descr,
                                        Map<String, PluginDescriptorModel> descriptors) throws PluginException {
        // Prerequisites
        final PluginPrerequisite reqs[] = descr.getPrerequisites();
        final int reqLength = reqs.length;
        for (int i = 0; i < reqLength; i++) {
            final PluginPrerequisite req = reqs[i];
            loadDependency(loader, req.getPluginReference(), descriptors);
        }
        // Extensions
        final Extension[] exts = descr.getExtensions();
        final int extLength = exts.length;
        for (int i = 0; i < extLength; i++) {
            final Extension ext = exts[i];
            final String id = ext.getExtensionPointPluginId();
            loadDependency(loader, new PluginReference(id, descr.getVersion()), descriptors);
        }
    }

    private final void loadDependency(PluginLoader loader, PluginReference dependency,
                                      Map<String, PluginDescriptorModel> descriptors) throws PluginException {
        if (getPluginDescriptor(dependency.getId()) != null) {
            return;
        }
        if (descriptors.containsKey(dependency.getId())) {
            return;
        }
        final PluginDescriptorModel descr = loadPluginImpl(loader, dependency);
        descriptors.put(descr.getId(), descr);
        loadDependencies(loader, descr, descriptors);
    }

    /**
     * Load a plugin from a given loader but doesn't resolve its dependencies.
     *
     * @param loader
     * @param pluginReference
     * @return The descriptor of the loaded plugin.
     * @throws PluginException
     */
    private PluginDescriptorModel loadPluginImpl(final PluginLoader loader, final PluginReference pluginReference)
        throws PluginException {
        final PluginRegistryModel registry = this;
        final PluginJar pluginJar;
        try {
            pluginJar = AccessController
                .doPrivileged(new PrivilegedExceptionAction<PluginJar>() {

                    public PluginJar run() throws PluginException, IOException {
                        final ByteBuffer buf = loader.getPluginBuffer(pluginReference);
                        if (buf == null) {
                            throw new PluginException(
                                "Plugin " + pluginReference + " not found");
                        }
                        return new PluginJar(registry, buf, null);
                    }
                });
        } catch (PrivilegedActionException pax) {
            final Throwable ex = pax.getException();
            if (ex instanceof PluginException) {
                throw (PluginException) ex;
            } else {
                throw new PluginException(ex);
            }
        }
        return pluginJar.getDescriptorModel();
    }

    /**
     * Remove the plugin with the given id from this registry.
     *
     * @param pluginId
     * @throws PluginException
     */
    public synchronized List<PluginReference> unloadPlugin(String pluginId)
        throws PluginException {
        final SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            sm.checkPermission(PluginSecurityConstants.UNLOAD_PERM);
        }
        final List<PluginReference> unloadedIds = new ArrayList<PluginReference>();
        doUnloadPlugin(pluginId, unloadedIds);
        Collections.reverse(unloadedIds);
        return unloadedIds;
    }

    /**
     * Unload the given plugin and all plugins that depends on it.
     *
     * @param pluginId
     * @param unloadedIds
     * @throws PluginException
     */
    private final void doUnloadPlugin(String pluginId, List<PluginReference> unloadedIds) throws PluginException {
        final PluginDescriptorModel descr = (PluginDescriptorModel) getPluginDescriptor(pluginId);
        if (descr != null) {
            if (descr.isSystemPlugin()) {
                throw new PluginException(
                    "Cannot unload a system plugin: " + pluginId);
            }

            // Unload all plugins that depend on this plugin
            final ArrayList<PluginDescriptor> descriptors = new ArrayList<PluginDescriptor>(descriptorMap.values());
            for (PluginDescriptor dep : descriptors) {
                if (dep.depends(pluginId)) {
                    doUnloadPlugin(dep.getId(), unloadedIds);
                }
            }

            // Now remove it
            unloadedIds.add(descr.getPluginReference());
            descr.unresolve(this);
        }
    }

    /**
     * Gets the classloader that loads classes from all loaded plugins.
     *
     * @return ClassLoader
     */
    public ClassLoader getPluginsClassLoader() {
        if (classLoaderHolder == null) {
            classLoaderHolder = new VmIsolateLocal<PluginsClassLoader>();
        }
        if (classLoaderHolder.get() == null) {
            classLoaderHolder.set(new PluginsClassLoader(this));
        }
        return classLoaderHolder.get();
    }

    static class DTDResolver implements EntityResolver {

        /**
         * @see org.xml.sax.EntityResolver#resolveEntity(java.lang.String,
         *      java.lang.String)
         */
        public InputSource resolveEntity(String publicId, String systemId)
            throws SAXException, IOException {
            if ((systemId != null) && systemId.endsWith("jnode.dtd")) {
                return new InputSource(getClass().getResourceAsStream(
                    "/jnode.dtd"));
            } else {
                return null;
            }
        }

    }
}
TOP

Related Classes of org.jnode.plugin.model.PluginRegistryModel

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.