/* * Copyright 2010-2015 Institut Pasteur. * * This file is part of Icy. * * Icy 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. * * Icy 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 Icy. If not, see <http://www.gnu.org/licenses/>. */ package icy.plugin; import icy.main.Icy; import icy.network.NetworkUtil; import icy.network.URLUtil; import icy.plugin.PluginDescriptor.PluginIdent; import icy.plugin.PluginDescriptor.PluginNameSorter; import icy.plugin.PluginDescriptor.PluginOnlineIdent; import icy.preferences.PluginPreferences; import icy.preferences.RepositoryPreferences; import icy.preferences.RepositoryPreferences.RepositoryInfo; import icy.system.IcyExceptionHandler; import icy.system.thread.SingleProcessor; import icy.system.thread.ThreadUtil; import icy.util.XMLUtil; import java.util.ArrayList; import java.util.Collections; import java.util.EventListener; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.swing.event.EventListenerList; import org.w3c.dom.Document; import org.w3c.dom.Node; /** * @author stephane */ public class PluginRepositoryLoader { public static interface PluginRepositoryLoaderListener extends EventListener { public void pluginRepositeryLoaderChanged(PluginDescriptor plugin); } private class Loader implements Runnable { public Loader() { super(); } @Override public void run() { final List<PluginDescriptor> newPlugins = new ArrayList<PluginDescriptor>(); try { final List<RepositoryInfo> repositories = RepositoryPreferences.getRepositeries(); // load online plugins from all active repositories for (RepositoryInfo repoInfo : repositories) { // reload requested --> stop current loading if (processor.hasWaitingTasks()) return; if (repoInfo.isEnabled()) { final List<PluginDescriptor> pluginsRepos = loadInternal(repoInfo); if (pluginsRepos == null) { failed = true; return; } newPlugins.addAll(pluginsRepos); } } // sort list on plugin class name Collections.sort(newPlugins, PluginNameSorter.instance); plugins = newPlugins; } catch (Exception e) { IcyExceptionHandler.showErrorMessage(e, true); failed = true; return; } // notify basic data has been loaded loaded = true; changed(null); } } private static final String ID_ROOT = "plugins"; private static final String ID_PLUGIN = "plugin"; // private static final String ID_PATH = "path"; /** * static class */ private static final PluginRepositoryLoader instance = new PluginRepositoryLoader(); /** * Online plugin list */ List<PluginDescriptor> plugins; /** * listeners */ private final EventListenerList listeners; /** * internals */ boolean loaded; boolean failed; private final Loader loader; final SingleProcessor processor; /** * static class */ private PluginRepositoryLoader() { super(); plugins = new ArrayList<PluginDescriptor>(); listeners = new EventListenerList(); loader = new Loader(); processor = new SingleProcessor(true, "Online Plugin Loader"); loaded = false; // initial loading load(); } /** * Return the plugins identifier list from a repository URL */ public static List<PluginOnlineIdent> getPluginIdents(RepositoryInfo repos) { String address = repos.getLocation(); final boolean networkAddr = URLUtil.isNetworkURL(address); final boolean betaAllowed = PluginPreferences.getAllowBeta(); if (networkAddr && repos.getSupportParam()) { // prepare parameters for plugin list request final Map<String, String> values = new HashMap<String, String>(); // add kernel information parameter values.put(NetworkUtil.ID_KERNELVERSION, Icy.version.toString()); // add beta allowed information parameter values.put(NetworkUtil.ID_BETAALLOWED, Boolean.toString(betaAllowed)); // concat to address address += "?" + NetworkUtil.getContentString(values); } // load the XML file final Document document = XMLUtil.loadDocument(address, repos.getAuthenticationInfo(), false); // error if (document == null) { if (networkAddr && !NetworkUtil.hasInternetAccess()) System.out.println("You are not connected to internet."); return null; } final List<PluginOnlineIdent> result = new ArrayList<PluginOnlineIdent>(); // get plugins node final Node pluginsNode = XMLUtil.getElement(document.getDocumentElement(), ID_ROOT); // plugins node found if (pluginsNode != null) { // ident nodes final List<Node> nodes = XMLUtil.getChildren(pluginsNode, ID_PLUGIN); for (Node node : nodes) { final PluginOnlineIdent ident = new PluginOnlineIdent(); ident.loadFromXML(node); // accept only if not empty if (!ident.isEmpty()) { // accept only if required kernel version is ok and beta accepted if (ident.getRequiredKernelVersion().isLowerOrEqual(Icy.version) && (betaAllowed || (!ident.getVersion().isBeta()))) { // check if we have several version of the same plugin final int ind = PluginIdent.getIndex(result, ident.getClassName()); // other version found ? if (ind != -1) { // replace old version if needed if (result.get(ind).isOlderOrEqual(ident)) result.set(ind, ident); } else result.add(ident); } } } } return result; } /** * Do loading process. */ private void load() { loaded = false; failed = false; processor.submit(loader); } /** * Reload all plugins from all active repositories (old list is cleared).<br> * Asynchronous process, use {@link #waitLoaded()} method to wait for basic data to be loaded. */ public static synchronized void reload() { instance.load(); } /** * Load the list of online plugins located at specified repository */ // public static void load(final RepositoryInfo repos, boolean asynch, final boolean // loadDescriptor, // final boolean loadImages) // { // instance.loadSingleRunner.setParameters(repos, loadDescriptor, loadImages); // // if (asynch) // ThreadUtil.bgRunSingle(instance.loadAllRunner); // else // instance.loadAllRunner.run(); // } /** * Load and return the list of online plugins located at specified repository */ static List<PluginDescriptor> loadInternal(RepositoryInfo repos) { // we start by loading only identifier part final List<PluginOnlineIdent> idents = getPluginIdents(repos); // error while retrieving identifiers ? if (idents == null) { System.out.println("Can't access repository '" + repos.getName() + "'"); return null; } final List<PluginDescriptor> result = new ArrayList<PluginDescriptor>(); for (PluginOnlineIdent ident : idents) { try { result.add(new PluginDescriptor(ident, repos)); } catch (Exception e) { System.out.println("PluginRepositoryLoader.load('" + repos.getLocation() + "') error :"); IcyExceptionHandler.showErrorMessage(e, false); } } return result; } /** * @return the pluginList */ public static ArrayList<PluginDescriptor> getPlugins() { synchronized (instance.plugins) { return new ArrayList<PluginDescriptor>(instance.plugins); } } public static PluginDescriptor getPlugin(String className) { synchronized (instance.plugins) { return PluginDescriptor.getPlugin(instance.plugins, className); } } public static List<PluginDescriptor> getPlugins(String className) { synchronized (instance.plugins) { return PluginDescriptor.getPlugins(instance.plugins, className); } } /** * Return the plugins list from the specified repository */ public static List<PluginDescriptor> getPlugins(RepositoryInfo repos) { final List<PluginDescriptor> result = new ArrayList<PluginDescriptor>(); synchronized (instance.plugins) { for (PluginDescriptor plugin : instance.plugins) if (plugin.getRepository().equals(repos)) result.add(plugin); } return result; } /** * @return true if loader is loading the basic informations */ public static boolean isLoading() { return instance.processor.isProcessing(); } /** * @return true if basic informations (class names, versions...) are loaded. */ public static boolean isLoaded() { return instance.failed || instance.loaded; } /** * Wait until basic informations are loaded. */ public static void waitLoaded() { while (!isLoaded()) ThreadUtil.sleep(10); } /** * @deprecated use {@link #isLoaded()} instead. */ @Deprecated public static boolean isBasicLoaded() { return isLoaded(); } /** * @deprecated descriptor loading is now done per descriptor when needed */ @Deprecated public static boolean isDescriptorsLoaded() { return true; } /** * @deprecated image loading is now done per descriptor when needed */ @Deprecated public static boolean isImagesLoaded() { return true; } /** * @deprecated use {@link #waitLoaded()} instead. */ @Deprecated public static void waitBasicLoaded() { waitLoaded(); } /** * @deprecated descriptor loading is now done per descriptor when needed */ @Deprecated public static void waitDescriptorsLoaded() { // do nothing } /** * Returns true if an error occurred during the plugin loading process. */ public static boolean failed() { return instance.failed; } /** * Plugin list has changed */ void changed(PluginDescriptor plugin) { fireEvent(plugin); } /** * Add a listener * * @param listener */ public static void addListener(PluginRepositoryLoaderListener listener) { synchronized (instance.listeners) { instance.listeners.add(PluginRepositoryLoaderListener.class, listener); } } /** * Remove a listener * * @param listener */ public static void removeListener(PluginRepositoryLoaderListener listener) { synchronized (instance.listeners) { instance.listeners.remove(PluginRepositoryLoaderListener.class, listener); } } /** * fire event * * @param plugin */ private void fireEvent(PluginDescriptor plugin) { for (PluginRepositoryLoaderListener listener : listeners.getListeners(PluginRepositoryLoaderListener.class)) listener.pluginRepositeryLoaderChanged(plugin); } }