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

com.alee.managers.plugin.PluginManager Maven / Gradle / Ivy

The newest version!
/*
 * This file is part of WebLookAndFeel library.
 *
 * WebLookAndFeel library is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * WebLookAndFeel 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with WebLookAndFeel library.  If not, see .
 */

package com.alee.managers.plugin;

import com.alee.global.GlobalConstants;
import com.alee.log.Log;
import com.alee.managers.plugin.data.*;
import com.alee.utils.*;
import com.alee.utils.compare.Filter;

import javax.imageio.ImageIO;
import javax.swing.*;
import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.*;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;

/**
 * Base class for any plugin manager you might want to create.
 *
 * @author Mikle Garin
 * @see How to use PluginManager
 * @see com.alee.managers.plugin.Plugin
 */

public abstract class PluginManager
{
    /**
     * todo 1. Allow direct plugin loading from specified file/url (including loading from within another jar)
     * todo 2. Allow to disable logging from this manager
     * todo 3. Make plugin operations thread-safe
     * todo 4. PluginManager -> Plugin -> dependencies on other plugins
     * todo 5. Translate/replace error messages? or not
     */

    /**
     * Plugins listeners.
     */
    protected List> listeners = new ArrayList> ( 1 );

    /**
     * Plugin checks lock object.
     */
    protected final Object checkLock = new Object ();

    /**
     * Detected plugins list.
     * All plugins with available descriptions will get into this list.
     */
    protected List> detectedPlugins;

    /**
     * Recently detected plugins list.
     * Contains plugins detected while last plugins check.
     */
    protected List> recentlyDetected;

    /**
     * Special filter to filter out unwanted plugins before their initialization.
     * It is up to developer to specify this filter and its conditions.
     */
    protected Filter> pluginFilter = null;

    /**
     * Whether should allow loading multiply plugins with the same ID or not.
     * In case this is set to false only the newest version of the same plugin will be loaded if more than one provided.
     */
    protected boolean allowSimilarPlugins = false;

    /**
     * Loaded and running plugins list.
     * This might be less than list of detected plugins in the end due to lots of different reasons.
     * Only those plugins which are actually loaded successfully are getting added here.
     */
    protected List availablePlugins;

    /**
     * Map of plugins cached by their IDs.
     */
    protected Map availablePluginsById = new HashMap ();

    /**
     * Map of plugins cached by their classes.
     */
    protected Map, T> availablePluginsByClass = new HashMap, T> ();

    /**
     * Recently initialized plugins list.
     * Contains plugins initialized while last plugins check.
     */
    protected List recentlyInitialized;

    /**
     * Plugins directory path.
     * It is either absolute path or relative to working directory path.
     */
    protected String pluginsDirectoryPath;

    /**
     * Whether plugins directory subfolders should be checked recursively or not.
     */
    protected boolean checkRecursively;

    /**
     * Plugin directory files filter.
     * By defauly "*.jar" and "*.plugin" files are accepted.
     */
    protected FileFilter fileFilter;

    /**
     * Whether should create new class loader for each loaded plugin or not.
     * Be aware that you might experience various issues with separate class loaders.
     */
    protected boolean createNewClassLoader = false;

    /**
     * Constructs new plugin manager.
     */
    public PluginManager ()
    {
        this ( null );
    }

    /**
     * Constructs new plugin manager.
     *
     * @param pluginsDirectoryPath plugins directory path
     */
    public PluginManager ( final String pluginsDirectoryPath )
    {
        this ( pluginsDirectoryPath, true );
    }

    /**
     * Constructs new plugin manager.
     *
     * @param pluginsDirectoryPath plugins directory path
     * @param checkRecursively     whether plugins directory subfolders should be checked recursively or not
     */
    public PluginManager ( final String pluginsDirectoryPath, final boolean checkRecursively )
    {
        super ();

        // User settings
        this.pluginsDirectoryPath = pluginsDirectoryPath;
        this.checkRecursively = checkRecursively;

        // Default file filter
        this.fileFilter = new FileFilter ()
        {
            @Override
            public boolean accept ( final File file )
            {
                final String name = file.getName ().toLowerCase ();
                return name.endsWith ( ".jar" ) || name.endsWith ( ".plugin" );
            }
        };

        // Runtime variables
        detectedPlugins = new ArrayList> ();
        availablePlugins = new ArrayList ( detectedPlugins.size () );
    }

    /**
     * Returns name of the plugin descriptor file.
     * This file should contain serialized PluginInformation.
     *
     * @return name of the plugin descriptor file
     */
    protected String getPluginDescriptorFile ()
    {
        return "plugin.xml";
    }

    /**
     * Returns name of the plugin logo file.
     * Logo should be placed near the plugin descriptor file.
     *
     * @return name of the plugin logo file
     */
    protected String getPluginLogoFile ()
    {
        return "logo.png";
    }

    /**
     * Returns accepted by this manager plugin type.
     * In case {@code null} is returned this manager accepts any plugin type.
     *
     * @return accepted by this manager plugin type
     */
    protected String getAcceptedPluginType ()
    {
        return null;
    }

    /**
     * Registers programmatically loaded plugin within this PluginManager.
     * This call will add the specified plugin into available plugins list.
     * It will also create a custom DetectedPlugin data based on provided information.
     *
     * @param plugin plugin to register
     */
    public void registerPlugin ( final T plugin )
    {
        registerPlugin ( plugin, plugin.getPluginInformation (), plugin.getPluginLogo () );
    }

    /**
     * Registers programmatically loaded plugin within this PluginManager.
     * This call will add the specified plugin into available plugins list.
     * It will also create a custom DetectedPlugin data based on provided information.
     *
     * @param plugin      plugin to register
     * @param information about this plugin
     * @param logo        plugin logo
     */
    public void registerPlugin ( final T plugin, final PluginInformation information, final ImageIcon logo )
    {
        final String prefix = "[" + information + "] ";
        Log.info ( this, prefix + "Initializing pre-loaded plugin..." );

        // Creating base detected plugin information
        final DetectedPlugin detectedPlugin = new DetectedPlugin ( null, null, information, logo );
        detectedPlugin.setStatus ( PluginStatus.loaded );
        detectedPlugin.setPlugin ( plugin );
        plugin.setPluginManager ( PluginManager.this );
        plugin.setDetectedPlugin ( detectedPlugin );

        // Saving plugin
        detectedPlugins.add ( detectedPlugin );
        availablePlugins.add ( plugin );
        availablePluginsById.put ( plugin.getId (), plugin );
        availablePluginsByClass.put ( plugin.getClass (), plugin );

        Log.info ( this, prefix + "Pre-loaded plugin initialized" );

        // Informing
        firePluginsInitialized ( Arrays.asList ( plugin ) );
    }

    /**
     * Performs plugins search within the specified plugins directory.
     * This call might be performed as many times as you like.
     * It will simply ignore plugins detected before and will process newly found plugins appropriately.
     */
    public void checkPlugins ()
    {
        checkPlugins ( pluginsDirectoryPath, checkRecursively );
    }

    /**
     * Performs plugins search within the specified plugins directory.
     * This call might be performed as many times as you like.
     * It will simply ignore plugins detected before and will process newly found plugins appropriately.
     *
     * @param checkRecursively whether plugins directory subfolders should be checked recursively or not
     */
    public void checkPlugins ( final boolean checkRecursively )
    {
        checkPlugins ( pluginsDirectoryPath, checkRecursively );
    }

    /**
     * Performs plugins search within the specified plugins directory.
     * This call might be performed as many times as you like.
     * It will simply ignore plugins detected before and will process newly found plugins appropriately.
     *
     * @param pluginsDirectoryPath plugins directory path
     */
    public void checkPlugins ( final String pluginsDirectoryPath )
    {
        checkPlugins ( pluginsDirectoryPath, checkRecursively );
    }

    /**
     * Performs plugins search within the specified plugins directory.
     * This call might be performed as many times as you like.
     * It will simply ignore plugins detected before and will process newly found plugins appropriately.
     *
     * @param pluginsDirectoryPath plugins directory path
     * @param checkRecursively     whether plugins directory subfolders should be checked recursively or not
     */
    public void checkPlugins ( final String pluginsDirectoryPath, final boolean checkRecursively )
    {
        synchronized ( checkLock )
        {
            // Ignore check if check path is not specified
            if ( pluginsDirectoryPath == null )
            {
                return;
            }

            // Informing about plugins check start
            firePluginsCheckStarted ( pluginsDirectoryPath, checkRecursively );

            // Collecting plugins information
            if ( collectPluginsInformation ( pluginsDirectoryPath, checkRecursively ) )
            {
                // Informing about newly detected plugins
                firePluginsDetected ( recentlyDetected );

                Log.info ( this, "Initializing plugins..." );

                // Initializing plugins
                initializePlugins ();

                // Sorting plugins according to their initialization strategies
                applyInitializationStrategy ();

                // Properly sorting recently initialized plugins
                Collections.sort ( recentlyInitialized, new Comparator ()
                {
                    @Override
                    public int compare ( final T o1, final T o2 )
                    {
                        final Integer i1 = availablePlugins.indexOf ( o1 );
                        final Integer i2 = availablePlugins.indexOf ( o2 );
                        return i1.compareTo ( i2 );
                    }
                } );

                // Informing about new plugins initialization
                firePluginsInitialized ( recentlyInitialized );

                Log.info ( this, "Plugins initialization finished" );
            }

            // Informing about plugins check end
            firePluginsCheckEnded ( pluginsDirectoryPath, checkRecursively );
        }
    }

    /**
     * Collects information about available plugins.
     *
     * @return true if operation succeeded, false otherwise
     */
    protected boolean collectPluginsInformation ( final String pluginsDirectoryPath, final boolean checkRecursively )
    {
        if ( pluginsDirectoryPath != null )
        {
            Log.info ( this, "Collecting plugins information..." );
            recentlyDetected = new ArrayList> ();
            return collectPluginsInformationImpl ( new File ( pluginsDirectoryPath ), checkRecursively );
        }
        else
        {
            Log.error ( this, "Plugins directory is not yet specified" );
            return false;
        }
    }

    /**
     * Collects information about available plugins.
     *
     * @param dir plugins directory
     * @return true if operation succeeded, false otherwise
     */
    protected boolean collectPluginsInformationImpl ( final File dir, final boolean checkRecursively )
    {
        // Checking all files
        final File[] files = dir.listFiles ( fileFilter );
        if ( files != null )
        {
            for ( final File file : files )
            {
                collectPluginInformation ( file );
            }
        }

        // Checking sub-directories recursively
        if ( checkRecursively )
        {
            final File[] subfolders = dir.listFiles ( GlobalConstants.DIRECTORIES_FILTER );
            if ( subfolders != null )
            {
                for ( final File subfolder : subfolders )
                {
                    collectPluginsInformationImpl ( subfolder, checkRecursively );
                }
            }
        }

        return true;
    }

    /**
     * Tries to collect plugin information from the specified file.
     * This call will simply be ignored if this is not a plugin file or if something goes wrong.
     *
     * @param file plugin file to process
     */
    protected void collectPluginInformation ( final File file )
    {
        try
        {
            final String pluginDescriptor = getPluginDescriptorFile ();
            final String pluginLogo = getPluginLogoFile ();
            final ZipFile zipFile = new ZipFile ( file );
            final Enumeration entries = zipFile.entries ();
            while ( entries.hasMoreElements () )
            {
                final ZipEntry entry = ( ZipEntry ) entries.nextElement ();
                if ( entry.getName ().endsWith ( pluginDescriptor ) )
                {
                    // Reading plugin information
                    final InputStream inputStream = zipFile.getInputStream ( entry );
                    final PluginInformation info = XmlUtils.fromXML ( inputStream );
                    inputStream.close ();

                    // Reading plugin icon
                    final ZipEntry logoEntry = new ZipEntry ( ZipUtils.getZipEntryFileLocation ( entry ) + pluginLogo );
                    final InputStream logoInputStream = zipFile.getInputStream ( logoEntry );
                    final ImageIcon logo;
                    if ( logoInputStream != null )
                    {
                        logo = new ImageIcon ( ImageIO.read ( logoInputStream ) );
                        logoInputStream.close ();
                    }
                    else
                    {
                        logo = null;
                    }

                    // Checking whether we have already detected this plugin or not
                    if ( !wasDetected ( file.getParent (), file.getName () ) )
                    {
                        final DetectedPlugin plugin = new DetectedPlugin ( file.getParent (), file.getName (), info, logo );
                        detectedPlugins.add ( plugin );
                        recentlyDetected.add ( plugin );
                        Log.info ( this, "Plugin detected: " + info );
                    }

                    break;
                }
            }
            zipFile.close ();
        }
        catch ( final IOException e )
        {
            Log.error ( this, e );
        }
    }

    /**
     * Returns whether this plugin file was already detected before or not.
     *
     * @param pluginFolder plugin directory
     * @param pluginFile   plugin file
     * @return true if this plugin file was already detected before, false otherwise
     */
    protected boolean wasDetected ( final String pluginFolder, final String pluginFile )
    {
        for ( final DetectedPlugin plugin : detectedPlugins )
        {
            if ( plugin.getPluginFile () != null && plugin.getPluginFolder ().equals ( pluginFolder ) &&
                    plugin.getPluginFile ().equals ( pluginFile ) )
            {
                return true;
            }
        }
        return false;
    }

    /**
     * Initializes detected earlier plugins.
     */
    protected void initializePlugins ()
    {
        // Map to store plugin libraries
        final Map> pluginLibraries =
                new HashMap> ();

        // List to collect newly initialized plugins
        // This is required to properly inform about newly loaded plugins later
        recentlyInitialized = new ArrayList ();

        // Initializing detected plugins
        final String acceptedPluginType = getAcceptedPluginType ();
        for ( final DetectedPlugin dp : detectedPlugins )
        {
            // Skip plugins we have already tried to initialize
            if ( dp.getStatus () != PluginStatus.detected )
            {
                continue;
            }

            final File pluginFile = new File ( dp.getPluginFolder (), dp.getPluginFile () );
            final PluginInformation info = dp.getInformation ();
            final String prefix = "[" + FileUtils.getRelativePath ( pluginFile, new File ( pluginsDirectoryPath ) ) + "] [" + info + "] ";
            try
            {
                // Checking plugin type as we don't want (for example) to load server plugins on client side
                if ( acceptedPluginType != null && ( info.getType () == null || !info.getType ().equals ( acceptedPluginType ) ) )
                {
                    Log.warn ( this, prefix + "Plugin of type \"" + info.getType () + "\" cannot be loaded, " +
                            "required plugin type is \"" + acceptedPluginType + "\"" );
                    dp.setStatus ( PluginStatus.failed );
                    dp.setFailureCause ( "Wrong type" );
                    dp.setExceptionMessage ( "Detected plugin type: " + info.getType () + "\", " +
                            "required plugin type: \"" + acceptedPluginType + "\"" );
                    continue;
                }

                // Checking that this is latest plugin version of all available
                // Usually there shouldn't be different versions of the same plugin but everyone make mistakes
                if ( isDeprecatedVersion ( dp ) )
                {
                    Log.warn ( this, prefix + "This plugin is deprecated, newer version loaded instead" );
                    dp.setStatus ( PluginStatus.failed );
                    dp.setFailureCause ( "Deprecated" );
                    dp.setExceptionMessage ( "This plugin is deprecated, newer version loaded instead" );
                    continue;
                }

                // Checking that this plugin version is not yet loaded
                // This might occur in case the same plugin appears more than once in different files
                if ( isSameVersionAlreadyLoaded ( dp, detectedPlugins ) )
                {
                    Log.warn ( this, prefix + "Plugin is duplicate, it will be loaded from another file" );
                    dp.setStatus ( PluginStatus.failed );
                    dp.setFailureCause ( "Duplicate" );
                    dp.setExceptionMessage ( "This plugin is duplicate, it will be loaded from another file" );
                    continue;
                }

                // Checking that plugin filter accepts this plugin
                if ( getPluginFilter () != null && !getPluginFilter ().accept ( dp ) )
                {
                    Log.info ( this, prefix + "Plugin was not accepted by plugin filter" );
                    dp.setStatus ( PluginStatus.failed );
                    dp.setFailureCause ( "Filtered" );
                    dp.setExceptionMessage ( "Plugin was not accepted by plugin filter" );
                    continue;
                }

                // Now loading the plugin
                Log.info ( this, prefix + "Initializing plugin..." );
                dp.setStatus ( PluginStatus.loading );

                // Collecting plugin and its libraries JAR paths
                final List jarPaths = new ArrayList ( 1 + info.getLibrariesCount () );
                jarPaths.add ( pluginFile.toURI ().toURL () );
                if ( info.getLibraries () != null )
                {
                    for ( final PluginLibrary library : info.getLibraries () )
                    {
                        final File file = new File ( dp.getPluginFolder (), library.getFile () );
                        if ( file.exists () )
                        {
                            // Adding library URI to path
                            jarPaths.add ( file.toURI ().toURL () );

                            // Saving library information for futher checks
                            Map libraries = pluginLibraries.get ( library.getId () );
                            if ( libraries == null )
                            {
                                libraries = new HashMap ( 1 );
                                pluginLibraries.put ( library.getId (), libraries );
                            }
                            libraries.put ( library, info );
                        }
                        else
                        {
                            Log.warn ( this, prefix + "Unable to locate library: " + library.getFile () );
                        }
                    }
                }

                try
                {
                    // Choosing class loader
                    final ClassLoader cl = getClass ().getClassLoader ();
                    final ClassLoader classLoader;
                    if ( createNewClassLoader || !( cl instanceof URLClassLoader ) )
                    {
                        // Create new class loader
                        classLoader = URLClassLoader.newInstance ( jarPaths.toArray ( new URL[ jarPaths.size () ] ), cl );
                    }
                    else
                    {
                        // Use current class loader
                        classLoader = cl;
                        for ( final URL url : jarPaths )
                        {
                            ReflectUtils.callMethodSafely ( classLoader, "addURL", url );
                        }
                    }

                    // Loading plugin
                    final Class pluginClass = classLoader.loadClass ( info.getMainClass () );
                    final T plugin = ReflectUtils.createInstance ( pluginClass );
                    plugin.setPluginManager ( PluginManager.this );
                    plugin.setDetectedPlugin ( dp );

                    // Saving initialized plugin
                    availablePlugins.add ( plugin );
                    availablePluginsById.put ( plugin.getId (), plugin );
                    availablePluginsByClass.put ( plugin.getClass (), plugin );
                    recentlyInitialized.add ( plugin );

                    Log.info ( this, prefix + "Plugin initialized" );
                    dp.setStatus ( PluginStatus.loaded );
                    dp.setPlugin ( plugin );
                }
                catch ( final Throwable e )
                {
                    Log.error ( this, prefix + "Unable to initialize plugin", e );
                    dp.setStatus ( PluginStatus.failed );
                    dp.setFailureCause ( "Internal exception" );
                    dp.setException ( e );
                }
            }
            catch ( final Throwable e )
            {
                Log.error ( this, prefix + "Unable to initialize plugin data", e );
                dp.setStatus ( PluginStatus.failed );
                dp.setFailureCause ( "Data exception" );
                dp.setException ( e );
            }
        }

        // Checking for same/similar libraries used within plugins
        boolean warned = false;
        for ( final Map.Entry> libraries : pluginLibraries.entrySet () )
        {
            final Map sameLibraries = libraries.getValue ();
            if ( sameLibraries.size () > 1 )
            {
                final String title = sameLibraries.keySet ().iterator ().next ().getTitle ();
                final StringBuilder sb = new StringBuilder ( "Library [ " ).append ( title ).append ( " ] was found in plugins: " );
                for ( final Map.Entry library : sameLibraries.entrySet () )
                {
                    final PluginInformation plugin = library.getValue ();
                    final String libraryVersion = library.getKey ().getVersion ();
                    sb.append ( "[ " ).append ( plugin.toString () ).append ( ", version " ).append ( libraryVersion ).append ( " ] " );
                }
                Log.warn ( this, sb.toString () );
                warned = true;
            }
        }
        if ( warned )
        {
            Log.warn ( this, "Make sure the same library usafe within different plugins was actually your intent" );
        }
    }

    /**
     * Returns whether the list of detected plugins contain a newer version of the specified plugin or not.
     *
     * @param plugin plugin to compare with other detected plugins
     * @return true if the list of detected plugins contain a newer version of the specified plugin, false otherwise
     */
    public boolean isDeprecatedVersion ( final DetectedPlugin plugin )
    {
        return isDeprecatedVersion ( plugin, detectedPlugins );
    }

    /**
     * Returns whether the list of detected plugins contain a newer version of the specified plugin or not.
     *
     * @param plugin          plugin to compare with other detected plugins
     * @param detectedPlugins list of detected plugins
     * @return true if the list of detected plugins contain a newer version of the specified plugin, false otherwise
     */
    public boolean isDeprecatedVersion ( final DetectedPlugin plugin, final List> detectedPlugins )
    {
        final PluginInformation pluginInfo = plugin.getInformation ();
        for ( final DetectedPlugin detectedPlugin : detectedPlugins )
        {
            if ( detectedPlugin != plugin )
            {
                final PluginInformation detectedPluginInfo = detectedPlugin.getInformation ();
                if ( detectedPluginInfo.getId ().equals ( pluginInfo.getId () ) &&
                        detectedPluginInfo.getVersion () != null && pluginInfo.getVersion () != null &&
                        detectedPluginInfo.getVersion ().isNewerThan ( pluginInfo.getVersion () ) )
                {
                    return true;
                }
            }
        }
        return false;
    }

    /**
     * Returns whether the list of detected plugins contain the same version of the specified plugin or not.
     *
     * @param plugin          plugin to compare with other detected plugins
     * @param detectedPlugins list of detected plugins
     * @return true if the list of detected plugins contain the same version of the specified plugin, false otherwise
     */
    protected boolean isSameVersionAlreadyLoaded ( final DetectedPlugin plugin, final List> detectedPlugins )
    {
        final PluginInformation pluginInfo = plugin.getInformation ();
        for ( final DetectedPlugin detectedPlugin : detectedPlugins )
        {
            if ( detectedPlugin != plugin )
            {
                final PluginInformation detectedPluginInfo = detectedPlugin.getInformation ();
                if ( detectedPluginInfo.getId ().equals ( pluginInfo.getId () ) &&
                        ( detectedPluginInfo.getVersion () == null && pluginInfo.getVersion () == null ||
                                detectedPluginInfo.getVersion ().isSame ( pluginInfo.getVersion () ) ) &&
                        detectedPlugin.getStatus () == PluginStatus.loaded )
                {
                    return true;
                }
            }
        }
        return false;
    }

    /**
     * Sorting plugins according to their initialization strategies.
     */
    protected void applyInitializationStrategy ()
    {
        // Skip if no available plugins
        if ( availablePlugins.size () == 0 )
        {
            return;
        }

        // Splitting plugins by initial groups
        final List beforeAll = new ArrayList ( availablePlugins.size () );
        final List middle = new ArrayList ( availablePlugins.size () );
        final List afterAll = new ArrayList ( availablePlugins.size () );
        for ( final T plugin : availablePlugins )
        {
            final InitializationStrategy strategy = plugin.getInitializationStrategy ();
            if ( strategy.getId ().equals ( InitializationStrategy.ALL_ID ) )
            {
                switch ( strategy.getType () )
                {
                    case before:
                    {
                        beforeAll.add ( plugin );
                        break;
                    }
                    case any:
                    {
                        middle.add ( plugin );
                        break;
                    }
                    case after:
                    {
                        afterAll.add ( plugin );
                        break;
                    }
                }
            }
            else
            {
                middle.add ( plugin );
            }
        }

        // todo Sort only recently initialized plugins
        // todo Already initialized plugins should not be sorted again, that is useless and might break initial initialization order
        if ( middle.size () == 0 )
        {
            // Combining all plugins into single list
            availablePlugins.clear ();
            availablePlugins.addAll ( beforeAll );
            availablePlugins.addAll ( afterAll );
        }
        else
        {
            // Sorting middle plugins properly
            final List sortedMiddle = new ArrayList ( middle );
            for ( final T plugin : middle )
            {
                final InitializationStrategy strategy = plugin.getInitializationStrategy ();
                final String id = strategy.getId ();
                if ( !plugin.getId ().equals ( id ) )
                {
                    final int oldIndex = sortedMiddle.indexOf ( plugin );
                    for ( int index = 0; index < sortedMiddle.size (); index++ )
                    {
                        if ( sortedMiddle.get ( index ).getId ().equals ( id ) )
                        {
                            switch ( strategy.getType () )
                            {
                                case before:
                                {
                                    sortedMiddle.remove ( oldIndex );
                                    if ( oldIndex < index )
                                    {
                                        sortedMiddle.add ( index - 1, plugin );
                                    }
                                    else
                                    {
                                        sortedMiddle.add ( index, plugin );
                                    }
                                    break;
                                }
                                case after:
                                {
                                    sortedMiddle.remove ( oldIndex );
                                    if ( oldIndex < index )
                                    {
                                        sortedMiddle.add ( index, plugin );
                                    }
                                    else
                                    {
                                        sortedMiddle.add ( index + 1, plugin );
                                    }
                                    break;
                                }
                            }
                            break;
                        }
                    }
                }
            }

            // Combining all plugins into single list
            availablePlugins.clear ();
            availablePlugins.addAll ( beforeAll );
            availablePlugins.addAll ( sortedMiddle );
            availablePlugins.addAll ( afterAll );
        }
    }

    /**
     * Returns list of detected plugins.
     *
     * @return list of detected plugins
     */
    public List> getDetectedPlugins ()
    {
        return detectedPlugins;
    }

    /**
     * Returns list of available loaded plugins.
     *
     * @return list of available loaded plugins
     */
    public List getAvailablePlugins ()
    {
        return availablePlugins;
    }

    /**
     * Returns available plugin instance by its ID.
     *
     * @param pluginId plugin ID
     * @return available plugin instance by its ID
     */
    public 

P getPlugin ( final String pluginId ) { return ( P ) availablePluginsById.get ( pluginId ); } /** * Returns available plugin instance by its class. * * @param pluginClass plugin class * @return available plugin instance by its class */ public

P getPlugin ( final Class

pluginClass ) { return ( P ) availablePluginsByClass.get ( pluginClass ); } /** * Returns amount of detected plugins. * * @return amount of detected loaded plugins */ public int getDetectedPluginsAmount () { return getDetectedPlugins ().size (); } /** * Returns amount of successfully loaded plugins. * * @return amount of successfully loaded plugins */ public int getLoadedPluginsAmount () { return getAvailablePlugins ().size (); } /** * Returns amount of plugins which have failed to load. * There might be a lot of reasons why they failed to load - exception, broken JAR, missing libraries etc. * Simply check the log or retrieve failure cause from DetectedPlugin to understand what happened. * * @return amount of plugins which have failed to load */ public int getFailedPluginsAmount () { return getDetectedPlugins ().size () - getAvailablePlugins ().size (); } /** * Returns plugins directory path. * * @return plugins directory path */ public String getPluginsDirectoryPath () { return pluginsDirectoryPath; } /** * Sets plugins directory path. * * @param path new plugins directory path */ public void setPluginsDirectoryPath ( final String path ) { this.pluginsDirectoryPath = path; } /** * Returns whether plugins directory subfolders should be checked recursively or not. * * @return true if plugins directory subfolders should be checked recursively, false otherwise */ public boolean isCheckRecursively () { return checkRecursively; } /** * Sets whether plugins directory subfolders should be checked recursively or not. * * @param checkRecursively whether plugins directory subfolders should be checked recursively or not */ public void setCheckRecursively ( final boolean checkRecursively ) { this.checkRecursively = checkRecursively; } /** * Returns plugins directory file filter. * * @return plugins directory file filter */ public FileFilter getFileFilter () { return fileFilter; } /** * Sets plugins directory file filter. * Note that setting this filter will not have any effect on plugins which are already initialized. * * @param filter plugins directory file filter */ public void setFileFilter ( final FileFilter filter ) { this.fileFilter = filter; } /** * Returns whether should create new class loader for each loaded plugin or not. * * @return true if should create new class loader for each loaded plugin, false otherwise */ public boolean isCreateNewClassLoader () { return createNewClassLoader; } /** * Sets whether should create new class loader for each loaded plugin or not. * * @param createNewClassLoader whether should create new class loader for each loaded plugin or not */ public void setCreateNewClassLoader ( final boolean createNewClassLoader ) { this.createNewClassLoader = createNewClassLoader; } /** * Returns special filter that filters out unwanted plugins before their initialization. * * @return special filter that filters out unwanted plugins before their initialization */ public Filter> getPluginFilter () { return pluginFilter; } /** * Sets special filter that filters out unwanted plugins before their initialization. * * @param pluginFilter special filter that filters out unwanted plugins before their initialization */ public void setPluginFilter ( final Filter> pluginFilter ) { this.pluginFilter = pluginFilter; } /** * Returns whether should allow loading multiply plugins with the same ID or not. * * @return true if should allow loading multiply plugins with the same ID, false otherwise */ public boolean isAllowSimilarPlugins () { return allowSimilarPlugins; } /** * Sets whether should allow loading multiply plugins with the same ID or not. * * @param allow whether should allow loading multiply plugins with the same ID or not */ public void setAllowSimilarPlugins ( final boolean allow ) { this.allowSimilarPlugins = allow; } /** * Adds plugins listener. * * @param listener new plugins listener */ public void addPluginsListener ( final PluginsListener listener ) { listeners.add ( listener ); } /** * Removes plugins listener. * * @param listener plugins listener to remove */ public void removePluginsListener ( final PluginsListener listener ) { listeners.remove ( listener ); } /** * Informs about plugins check operation start. * * @param directory checked plugins directory path * @param recursive whether plugins directory subfolders are checked recursively or not */ public void firePluginsCheckStarted ( final String directory, final boolean recursive ) { for ( final PluginsListener listener : CollectionUtils.copy ( listeners ) ) { listener.pluginsCheckStarted ( directory, recursive ); } } /** * Informs about plugins check operation end. * * @param directory checked plugins directory path * @param recursive whether plugins directory subfolders are checked recursively or not */ public void firePluginsCheckEnded ( final String directory, final boolean recursive ) { for ( final PluginsListener listener : CollectionUtils.copy ( listeners ) ) { listener.pluginsCheckEnded ( directory, recursive ); } } /** * Informs about newly detected plugins. * * @param plugins newly detected plugins list */ public void firePluginsDetected ( final List> plugins ) { for ( final PluginsListener listener : CollectionUtils.copy ( listeners ) ) { listener.pluginsDetected ( CollectionUtils.copy ( plugins ) ); } } /** * Informs about newly initialized plugins. * * @param plugins newly initialized plugins list */ public void firePluginsInitialized ( final List plugins ) { for ( final PluginsListener listener : CollectionUtils.copy ( listeners ) ) { listener.pluginsInitialized ( CollectionUtils.copy ( plugins ) ); } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy