123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925 |
- /*
- * Copyright (c) 2006-2013 DMDirc Developers
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- */
-
- package com.dmdirc.plugins;
-
- import com.dmdirc.Main;
- import com.dmdirc.ServerManager;
- import com.dmdirc.actions.ActionManager;
- import com.dmdirc.actions.CoreActionType;
- import com.dmdirc.actions.wrappers.PerformWrapper;
- import com.dmdirc.commandparser.CommandManager;
- import com.dmdirc.config.Identity;
- import com.dmdirc.config.IdentityManager;
- import com.dmdirc.config.InvalidIdentityFileException;
- import com.dmdirc.config.prefs.PreferencesManager;
- import com.dmdirc.logger.ErrorLevel;
- import com.dmdirc.logger.Logger;
- import com.dmdirc.messages.MessageSinkManager;
- import com.dmdirc.ui.WindowManager;
- import com.dmdirc.ui.core.components.StatusBarManager;
- import com.dmdirc.util.SimpleInjector;
- import com.dmdirc.util.resourcemanager.ResourceManager;
- import com.dmdirc.util.validators.ValidationResponse;
-
- import java.io.File;
- import java.io.IOException;
- import java.io.InputStream;
- import java.util.ArrayList;
- import java.util.Collection;
- import java.util.Collections;
- import java.util.HashMap;
- import java.util.List;
- import java.util.Map;
- import java.util.Timer;
- import java.util.TimerTask;
- import java.util.TreeMap;
-
- import lombok.Getter;
- import lombok.extern.slf4j.Slf4j;
-
- /**
- * Stores plugin metadata and handles loading of plugin resources.
- */
- @Slf4j
- public class PluginInfo implements Comparable<PluginInfo>, ServiceProvider {
-
- /** The metadata for this plugin. */
- @Getter
- private final PluginMetaData metaData;
- /** Filename for this plugin (taken from URL). */
- @Getter
- private final String filename;
- /** The actual Plugin from this jar. */
- @Getter
- private Plugin plugin;
- /** The classloader used for this Plugin. */
- @Getter
- private PluginClassLoader pluginClassLoader;
- /** The resource manager used by this pluginInfo. */
- private ResourceManager resourceManager;
- /** Is this plugin only loaded temporarily? */
- private boolean tempLoaded;
- /** List of classes this plugin has. */
- private final List<String> myClasses = new ArrayList<>();
- /** Last Error Message. */
- @Getter
- private String lastError = "No Error";
- /** Are we trying to load? */
- private boolean isLoading;
- /** List of services we provide. */
- private final List<Service> provides = new ArrayList<>();
- /** List of children of this plugin. */
- private final List<PluginInfo> children = new ArrayList<>();
- /** Map of exports. */
- private final Map<String, ExportInfo> exports = new HashMap<>();
- /** List of identities. */
- private final List<Identity> identities = new ArrayList<>();
-
- /**
- * Create a new PluginInfo.
- *
- * @param metadata The plugin's metadata information
- * @throws PluginException if there is an error loading the Plugin
- * @since 0.6.6
- */
- public PluginInfo(final PluginMetaData metadata) throws PluginException {
- this.filename = new File(metadata.getPluginUrl().getPath()).getName();
- this.metaData = metadata;
-
- ResourceManager res;
-
- try {
- res = getResourceManager();
- } catch (IOException ioe) {
- lastError = "Error with resourcemanager: " + ioe.getMessage();
- throw new PluginException("Plugin " + filename + " failed to load. " + lastError, ioe);
- }
-
- updateClassList(res);
-
- if (!myClasses.contains(metadata.getMainClass())) {
- lastError = "main class file (" + metadata.getMainClass() + ") not found in jar.";
- throw new PluginException("Plugin " + filename + " failed to load. " + lastError);
- }
-
- updateProvides();
- getDefaults();
- }
-
- /**
- * Updates the list of known classes within this plugin from the specified
- * resource manager.
- */
- private void updateClassList(final ResourceManager res) {
- myClasses.clear();
-
- for (final String classfilename : res.getResourcesStartingWith("")) {
- final String classname = classfilename.replace('/', '.');
- if (classname.endsWith(".class")) {
- myClasses.add(classname.substring(0, classname.length() - 6));
- }
- }
- }
-
- /**
- * Retrieves the injector used to inject parameters into this
- * plugin's methods.
- *
- * @return The injector used for this plugin
- */
- public SimpleInjector getInjector() {
- final SimpleInjector injector;
- final SimpleInjector[] parents = new SimpleInjector[metaData.getParents().length];
- final Plugin[] plugins = new Plugin[parents.length];
-
- for (int i = 0; i < metaData.getParents().length; i++) {
- final PluginInfo parent = metaData.getManager()
- .getPluginInfoByName(metaData.getParents()[i]);
- parents[i] = parent.getInjector();
- plugins[i] = parent.getPlugin();
- }
-
- injector = new SimpleInjector(parents);
-
- for (Plugin parentPlugin : plugins) {
- injector.addParameter(parentPlugin);
- }
-
- // TODO: This should be switched to using a full DI framework.
- injector.addParameter(PluginManager.class, metaData.getManager());
- injector.addParameter(ActionManager.getActionManager());
- injector.addParameter(PluginInfo.class, this);
- injector.addParameter(Main.class, Main.mainInstance);
- injector.addParameter(PluginMetaData.class, metaData);
- injector.addParameter(IdentityManager.getIdentityManager());
- injector.addParameter(ServerManager.class, Main.mainInstance.getServerManager());
- injector.addParameter(CommandManager.getCommandManager());
- injector.addParameter(MessageSinkManager.class, MessageSinkManager.getManager());
- injector.addParameter(WindowManager.class, WindowManager.getWindowManager());
- injector.addParameter(StatusBarManager.getStatusBarManager());
- injector.addParameter(PreferencesManager.class, PreferencesManager.getPreferencesManager());
- injector.addParameter(PerformWrapper.class, PerformWrapper.getPerformWrapper());
-
- return injector;
- }
-
- /**
- * Get the licence for this plugin if it exists.
- *
- * @return An InputStream for the licence of this plugin, or null if no
- * licence found.
- * @throws IOException if there is an error with the ResourceManager.
- */
- public Map<String, InputStream> getLicenceStreams() throws IOException {
- final TreeMap<String, InputStream> licences = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
- licences.putAll(getResourceManager().getResourcesStartingWithAsInputStreams(
- "META-INF/licences/"));
- return licences;
- }
-
- /**
- * Get the defaults, formatters and icons for this plugin.
- */
- private void getDefaults() {
- final Identity defaults = IdentityManager.getIdentityManager().getGlobalAddonIdentity();
- final String domain = "plugin-" + metaData.getName();
-
- log.trace("{}: Using domain '{}'",
- new Object[]{metaData.getName(), domain});
-
- for (Map.Entry<String, String> entry : metaData.getDefaultSettings().entrySet()) {
- final String key = entry.getKey();
- final String value = entry.getValue();
-
- defaults.setOption(domain, key, value);
- }
-
- for (Map.Entry<String, String> entry : metaData.getFormatters().entrySet()) {
- final String key = entry.getKey();
- final String value = entry.getValue();
-
- defaults.setOption("formatter", key, value);
- }
-
- for (Map.Entry<String, String> entry : metaData.getIcons().entrySet()) {
- final String key = entry.getKey();
- final String value = entry.getValue();
-
- defaults.setOption("icon", key, value);
- }
- }
-
- /**
- * Try get the identities for this plugin.
- * This will unload any identities previously loaded by this plugin.
- */
- private void loadIdentities() {
- try {
- final Map<String, InputStream> identityStreams = getResourceManager().getResourcesStartingWithAsInputStreams("META-INF/identities/");
-
- unloadIdentities();
-
- for (Map.Entry<String, InputStream> entry : identityStreams.entrySet()) {
- final String name = entry.getKey();
- final InputStream stream = entry.getValue();
-
- if (name.endsWith("/") || stream == null) {
- // Don't try to load folders or null streams
- continue;
- }
-
- synchronized (identities) {
- try {
- final Identity thisIdentity = new Identity(stream, false);
- IdentityManager.getIdentityManager().registerIdentity(thisIdentity);
- identities.add(thisIdentity);
- } catch (final InvalidIdentityFileException ex) {
- Logger.userError(ErrorLevel.MEDIUM,
- "Error with identity file '" + name
- + "' in plugin '" + metaData.getName() + "'", ex);
- }
- }
- }
- } catch (final IOException ioe) {
- Logger.userError(ErrorLevel.MEDIUM, "Error finding identities in plugin '"
- + metaData.getName() + "'", ioe);
- }
- }
-
- /**
- * Unload any identities loaded by this plugin.
- */
- private void unloadIdentities() {
- synchronized (identities) {
- for (Identity identity : identities) {
- IdentityManager.getIdentityManager().unregisterIdentity(identity);
- }
-
- identities.clear();
- }
- }
-
- /**
- * Update provides list.
- */
- private void updateProvides() {
- // Remove us from any existing provides lists.
- synchronized (provides) {
- for (Service service : provides) {
- service.delProvider(this);
- }
- provides.clear();
- }
-
- // Get services provided by this plugin
- final Collection<String> providesList = metaData.getServices();
- if (providesList != null) {
- for (String item : providesList) {
- final String[] bits = item.split(" ");
- final String name = bits[0];
- final String type = bits.length > 1 ? bits[1] : "misc";
-
- if (!name.equalsIgnoreCase("any") && !type.equalsIgnoreCase("export")) {
- final Service service = metaData.getManager().getService(type, name, true);
- synchronized (provides) {
- service.addProvider(this);
- provides.add(service);
- }
- }
- }
- }
-
- updateExports();
- }
-
- /**
- * Called when the plugin is updated using the updater.
- * Reloads metaData and updates the list of files.
- */
- public void pluginUpdated() {
- try {
- // Force a new resourcemanager just incase.
- updateClassList(getResourceManager(true));
-
- updateMetaData();
- updateProvides();
- getDefaults();
- } catch (IOException ioe) {
- Logger.userError(ErrorLevel.MEDIUM, "There was an error updating "
- + metaData.getName(), ioe);
- }
- }
-
- /**
- * Try to reload the metaData from the plugin.config file.
- * If this fails, the old data will be used still.
- *
- * @return true if metaData was reloaded ok, else false.
- */
- private boolean updateMetaData() {
- metaData.load();
-
- return !metaData.hasErrors();
- }
-
- /**
- * Gets a resource manager for this plugin
- *
- * @return The resource manager for this plugin
- * @throws IOException if there is any problem getting a ResourceManager for this plugin
- */
- public ResourceManager getResourceManager() throws IOException {
- return getResourceManager(false);
- }
-
- /**
- * Get the resource manager for this plugin
- *
- * @return The resource manager for this plugin
- * @param forceNew Force a new resource manager rather than using the old one.
- * @throws IOException if there is any problem getting a ResourceManager for this plugin
- * @since 0.6
- */
- public synchronized ResourceManager getResourceManager(final boolean forceNew) throws IOException {
- if (resourceManager == null || forceNew) {
- resourceManager = ResourceManager.getResourceManager("jar://" + metaData.getPluginUrl().getPath());
-
- // Clear the resourcemanager in 10 seconds to stop us holding the file open
- new Timer(filename + "-resourcemanagerTimer").schedule(new TimerTask() {
-
- /** {@inheritDoc} */
- @Override
- public void run() {
- resourceManager = null;
- }
-
- }, 10000);
- }
-
- return resourceManager;
- }
-
- /** {@inheritDoc} */
- @Override
- public final boolean isActive() {
- return isLoaded();
- }
-
- /** {@inheritDoc} */
- @Override
- public void activateServices() {
- loadPlugin();
- }
-
- /** {@inheritDoc} */
- @Override
- public String getProviderName() {
- return "Plugin: " + metaData.getFriendlyName() + " ("
- + metaData.getName() + " / " + getFilename() + ")";
- }
-
- /** {@inheritDoc} */
- @Override
- public List<Service> getServices() {
- synchronized (provides) {
- return new ArrayList<>(provides);
- }
- }
-
- /**
- * Is this plugin loaded?
- *
- * @return True if the plugin is currently (non-temporarily) loaded, false
- * otherwise
- */
- public boolean isLoaded() {
- return plugin != null && !tempLoaded;
- }
-
- /**
- * Is this plugin temporarily loaded?
- *
- * @return True if this plugin is currently temporarily loaded, false
- * otherwise
- */
- public boolean isTempLoaded() {
- return plugin != null && tempLoaded;
- }
-
- /**
- * Try to Load the plugin files temporarily.
- */
- public void loadPluginTemp() {
- if (isLoaded() || isTempLoaded()) {
- // Already loaded, don't do anything
- return;
- }
-
- tempLoaded = true;
- loadPlugin();
- }
-
- /**
- * Load any required plugins or services.
- *
- * @return True if all requirements have been satisfied, false otherwise
- */
- protected boolean loadRequirements() {
- return loadRequiredPlugins() && loadRequiredServices();
- }
-
- /**
- * Attempts to load all services that are required by this plugin.
- *
- * @return True iff all required services were found and satisfied
- */
- protected boolean loadRequiredServices() {
- final ServiceManager manager = metaData.getManager();
-
- for (String serviceInfo : metaData.getRequiredServices()) {
- final String[] parts = serviceInfo.split(" ", 2);
-
- if ("any".equals(parts[0])) {
- Service best = null;
- boolean found = false;
-
- for (Service service : manager.getServicesByType(parts[1])) {
- if (service.isActive()) {
- found = true;
- break;
- }
-
- best = service;
- }
-
- if (!found && best != null) {
- found = best.activate();
- }
-
- if (!found) {
- return false;
- }
- } else {
- final Service service = manager.getService(parts[1], parts[0]);
-
- if (service == null || !service.activate()) {
- return false;
- }
- }
- }
-
- return true;
- }
-
- /**
- * Attempts to load all plugins that are required by this plugin.
- *
- * @return True if all required plugins were found and loaded
- */
- protected boolean loadRequiredPlugins() {
- final String required = metaData.getRequirements().get("plugins");
-
- if (required != null) {
- for (String pluginName : required.split(",")) {
- final String[] data = pluginName.split(":");
- if (!data[0].trim().isEmpty() && !loadRequiredPlugin(data[0])) {
- return false;
- }
- }
- }
-
- for (String parent : metaData.getParents()) {
- return loadRequiredPlugin(parent);
- }
-
- return true;
- }
-
- /**
- * Attempts to load the specified required plugin.
- *
- * @param name The name of the plugin to be loaded
- * @return True if the plugin was found and loaded, false otherwise
- */
- protected boolean loadRequiredPlugin(final String name) {
- log.info("Loading required plugin '{}' for plugin {}",
- new Object[] { name, metaData.getName() });
-
- final PluginInfo pi = metaData.getManager().getPluginInfoByName(name);
-
- if (pi == null) {
- return false;
- }
-
- if (tempLoaded) {
- pi.loadPluginTemp();
- } else {
- pi.loadPlugin();
- }
-
- return true;
- }
-
- /**
- * Load the plugin files.
- */
- public void loadPlugin() {
- if (isLoaded() || isLoading) {
- lastError = "Not Loading: (" + isLoaded() + "||" + isLoading + ")";
- return;
- }
-
- updateProvides();
-
- if (isTempLoaded()) {
- tempLoaded = false;
-
- if (!loadRequirements()) {
- tempLoaded = true;
- lastError = "Unable to satisfy dependencies for " + metaData.getName();
- return;
- }
-
- try {
- plugin.onLoad();
- } catch (LinkageError | Exception e) {
- lastError = "Error in onLoad for " + metaData.getName() + ":"
- + e.getMessage();
- Logger.userError(ErrorLevel.MEDIUM, lastError, e);
- unloadPlugin();
- }
- } else {
- isLoading = true;
-
- if (!loadRequirements()) {
- isLoading = false;
- lastError = "Unable to satisfy dependencies for " + metaData.getName();
- return;
- }
-
- loadIdentities();
-
- loadClass(metaData.getMainClass());
-
- if (isLoaded()) {
- ActionManager.getActionManager().triggerEvent(
- CoreActionType.PLUGIN_LOADED, null, this);
- }
-
- isLoading = false;
- }
- }
-
- /**
- * Add the given Plugin as a child of this plugin.
- *
- * @param child Child to add
- */
- public void addChild(final PluginInfo child) {
- children.add(child);
- }
-
- /**
- * Remove the given Plugin as a child of this plugin.
- *
- * @param child Child to remove
- */
- public void delChild(final PluginInfo child) {
- children.remove(child);
- }
-
- /**
- * Returns a list of this plugin's children.
- *
- * @return List of child plugins
- */
- public List<PluginInfo> getChildren() {
- return Collections.unmodifiableList(children);
- }
-
- /**
- * Checks if this plugin has any children.
- *
- * @return true iff this plugin has children
- */
- public boolean hasChildren() {
- return !children.isEmpty();
- }
-
- /**
- * Initialises this plugin's classloader.
- */
- private void initialiseClassLoader() {
- if (pluginClassLoader == null) {
- final PluginClassLoader[] loaders = new PluginClassLoader[metaData.getParents().length];
-
- for (int i = 0; i < metaData.getParents().length; i++) {
- final String parentName = metaData.getParents()[i];
- final PluginInfo parent = metaData.getManager()
- .getPluginInfoByName(parentName);
- parent.addChild(this);
- loaders[i] = parent.getPluginClassLoader();
-
- if (loaders[i] == null) {
- // Not loaded? Try again...
-
- parent.loadPlugin();
- loaders[i] = parent.getPluginClassLoader();
-
- if (loaders[i] == null) {
- lastError = "Unable to get classloader from required parent '" + parentName + "' for " + metaData.getName();
- return;
- }
- }
- }
-
- pluginClassLoader = new PluginClassLoader(this, metaData.getManager().getGlobalClassLoader(), loaders);
- }
- }
-
- /**
- * Load the given classname.
- *
- * @param classname Class to load
- */
- private void loadClass(final String classname) {
- try {
- initialiseClassLoader();
-
- // Don't reload a class if its already loaded.
- if (pluginClassLoader.isClassLoaded(classname, true)) {
- lastError = "Classloader says we are already loaded.";
- return;
- }
-
- final Class<?> clazz = pluginClassLoader.loadClass(classname);
- if (clazz == null) {
- lastError = "Class '" + classname + "' was not able to load.";
- return;
- }
-
- // Only try and construct the main class, anything else should be constructed
- // by the plugin itself.
- if (classname.equals(metaData.getMainClass())) {
- final Object temp = getInjector().createInstance(clazz);
-
- if (temp instanceof Plugin) {
- final ValidationResponse prerequisites = ((Plugin) temp).checkPrerequisites();
- if (prerequisites.isFailure()) {
- if (!tempLoaded) {
- lastError = "Prerequisites for plugin not met. ('"
- + filename + ":" + metaData.getMainClass()
- + "' -> '" + prerequisites.getFailureReason() + "') ";
- Logger.userError(ErrorLevel.LOW, lastError);
- }
- } else {
- final String domain = "plugin-" + metaData.getName();
- plugin = (Plugin) temp;
-
- log.debug("{}: Setting domain '{}'",
- new Object[]{metaData.getName(), domain});
-
- plugin.setDomain(domain);
- if (!tempLoaded) {
- try {
- plugin.onLoad();
- } catch (LinkageError | Exception e) {
- lastError = "Error in onLoad for "
- + metaData.getName() + ":" + e.getMessage();
- Logger.userError(ErrorLevel.MEDIUM, lastError, e);
- unloadPlugin();
- }
- }
- }
- }
- }
- } catch (ClassNotFoundException cnfe) {
- lastError = "Class not found ('" + filename + ":" + classname + ":"
- + classname.equals(metaData.getMainClass()) + "') - "
- + cnfe.getMessage();
- Logger.userError(ErrorLevel.LOW, lastError, cnfe);
- } catch (NoClassDefFoundError ncdf) {
- lastError = "Unable to instantiate plugin ('" + filename + ":"
- + classname + ":"
- + classname.equals(metaData.getMainClass())
- + "') - Unable to find class: " + ncdf.getMessage();
- Logger.userError(ErrorLevel.LOW, lastError, ncdf);
- } catch (VerifyError ve) {
- lastError = "Unable to instantiate plugin ('" + filename + ":"
- + classname + ":"
- + classname.equals(metaData.getMainClass())
- + "') - Incompatible: " + ve.getMessage();
- Logger.userError(ErrorLevel.LOW, lastError, ve);
- } catch (IllegalArgumentException ex) {
- lastError = "Unable to instantiate plugin ('" + filename + ":"
- + classname + ":"
- + classname.equals(metaData.getMainClass())
- + "') - Unable to construct: " + ex.getMessage();
- Logger.userError(ErrorLevel.LOW, lastError, ex);
- }
- }
-
- /**
- * Unload the plugin if possible.
- */
- public void unloadPlugin() {
- unloadPlugin(false);
- }
-
- /**
- * Can this plugin be unloaded?
- * Will return false if:
- * - The plugin is persistent (all its classes are loaded into the global class loader)
- * - The plugin isn't currently loaded
- * - The metadata key "unloadable" is set to false, no or 0
- *
- * @return true if plugin can be unloaded
- */
- public boolean isUnloadable() {
- return !isPersistent() && (isTempLoaded() || isLoaded())
- && metaData.isUnloadable();
- }
-
- /**
- * Unload the plugin if possible.
- *
- * @param parentUnloading is our parent already unloading? (if so, don't call delChild)
- */
- private void unloadPlugin(final boolean parentUnloading) {
- if (isUnloadable()) {
- if (!isTempLoaded()) {
- // Unload all children
- for (PluginInfo child : children) {
- child.unloadPlugin(true);
- }
-
- // Delete ourself as a child of our parent.
- if (!parentUnloading) {
- for (String parent : metaData.getParents()) {
- final PluginInfo pi = metaData.getManager().getPluginInfoByName(parent);
- if (pi != null) {
- pi.delChild(this);
- }
- }
- }
-
- // Now unload ourself
- try {
- plugin.onUnload();
- } catch (Exception | LinkageError e) {
- lastError = "Error in onUnload for " + metaData.getName()
- + ":" + e + " - " + e.getMessage();
- Logger.userError(ErrorLevel.MEDIUM, lastError, e);
- }
-
- ActionManager.getActionManager().triggerEvent(
- CoreActionType.PLUGIN_UNLOADED, null, this);
- synchronized (provides) {
- for (Service service : provides) {
- service.delProvider(this);
- }
- provides.clear();
- }
- }
- unloadIdentities();
- tempLoaded = false;
- plugin = null;
- pluginClassLoader = null;
- }
- }
-
- /**
- * Get the list of Classes
- *
- * @return Classes this plugin has
- */
- public List<String> getClassList() {
- return Collections.unmodifiableList(myClasses);
- }
-
- /**
- * Is this a persistent plugin?
- *
- * @return true if persistent, else false
- */
- public boolean isPersistent() {
- return metaData.getPersistentClasses().contains("*");
- }
-
- /**
- * Does this plugin contain any persistent classes?
- *
- * @return true if this plugin contains any persistent classes, else false
- */
- public boolean hasPersistent() {
- return !metaData.getPersistentClasses().isEmpty();
- }
-
- /**
- * Get a list of all persistent classes in this plugin
- *
- * @return List of all persistent classes in this plugin
- */
- public Collection<String> getPersistentClasses() {
- if (isPersistent()) {
- return getClassList();
- } else {
- return metaData.getPersistentClasses();
- }
- }
-
- /**
- * Is this a persistent class?
- *
- * @param classname class to check persistence of
- * @return true if file (or whole plugin) is persistent, else false
- */
- public boolean isPersistent(final String classname) {
- return isPersistent() || metaData.getPersistentClasses().contains(classname);
- }
-
- /**
- * String Representation of this plugin
- *
- * @return String Representation of this plugin
- */
- @Override
- public String toString() {
- return metaData.getFriendlyName() + " - " + filename;
- }
-
- /** {@inheritDoc} */
- @Override
- public int compareTo(final PluginInfo o) {
- return toString().compareTo(o.toString());
- }
-
- /**
- * Update exports list.
- */
- private void updateExports() {
- exports.clear();
-
- // Get exports provided by this plugin
- final Collection<String> exportsList = metaData.getExports();
- for (String item : exportsList) {
- final String[] bits = item.split(" ");
- if (bits.length > 2) {
- final String methodName = bits[0];
- final String methodClass = bits[2];
- final String serviceName = bits.length > 4 ? bits[4] : bits[0];
-
- // Add a provides for this
- final Service service = metaData.getManager().getService("export", serviceName, true);
- synchronized (provides) {
- service.addProvider(this);
- provides.add(service);
- }
- // Add is as an export
- exports.put(serviceName, new ExportInfo(methodName, methodClass, this));
- }
- }
- }
-
- /** {@inheritDoc} */
- @Override
- public ExportedService getExportedService(final String name) {
- if (exports.containsKey(name)) {
- return exports.get(name).getExportedService();
- } else {
- return null;
- }
- }
-
- /**
- * Does this plugin export the specified service?
- *
- * @param name Name of the service to check
- *
- * @return true iff the plugin exports the service
- */
- public boolean hasExportedService(final String name) {
- return exports.containsKey(name);
- }
-
- }
|