/* * 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.abstract_; import icy.file.FileUtil; import icy.gui.frame.IcyFrame; import icy.gui.viewer.Viewer; import icy.image.IcyBufferedImage; import icy.image.ImageUtil; import icy.main.Icy; import icy.network.NetworkUtil; import icy.plugin.PluginDescriptor; import icy.plugin.PluginLauncher; import icy.plugin.PluginLoader; import icy.plugin.interface_.PluginBundled; import icy.plugin.interface_.PluginThreaded; import icy.preferences.PluginsPreferences; import icy.preferences.XMLPreferences; import icy.resource.ResourceUtil; import icy.sequence.Sequence; import icy.system.IcyExceptionHandler; import icy.system.SystemUtil; import icy.system.audit.Audit; import icy.util.ClassUtil; import java.awt.image.BufferedImage; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.net.URL; import java.util.ArrayList; import java.util.Enumeration; import java.util.List; import javax.swing.ImageIcon; /** * Base class for Plugin, provide some helper methods.<br> * By default the constructor of a Plugin class is called in the EDT (Event Dispatch Thread).<br> * If the plugin implements the {@link PluginThreaded} there is no more guarantee that is the case. * * @author Fabrice de Chaumont & Stephane */ public abstract class Plugin { public static Plugin getPlugin(List<Plugin> list, String className) { for (Plugin plugin : list) if (plugin.getClass().getName().equals(className)) return plugin; return null; } private PluginDescriptor descriptor; /** * Default Plugin constructor.<br> * The {@link PluginLauncher} is normally responsible of Plugin class instantiation. */ public Plugin() { super(); // get descriptor from loader descriptor = PluginLoader.getPlugin(getClass().getName()); if (descriptor == null) { // descriptor not found (don't check for anonymous plugin class) ? if (!getClass().isAnonymousClass()) { System.out.println("Warning : Plugin '" + getClass().getName() + "' started but not found in PluginLoader !"); System.out.println("Local XML plugin description file is probably incorrect."); } // create dummy descriptor descriptor = new PluginDescriptor(this.getClass()); descriptor.setName(getClass().getSimpleName()); } // audit Audit.pluginInstancied(this); } @Override protected void finalize() throws Throwable { // unregister plugin (weak reference so we can do it here) Icy.getMainInterface().unRegisterPlugin(this); super.finalize(); } /** * @return the descriptor */ public PluginDescriptor getDescriptor() { return descriptor; } /** * @return the plugin name (from its descriptor) */ public String getName() { return descriptor.getName(); } /** * @return <code>true</code> if this is a bundled plugin (see {@link PluginBundled}). */ public boolean isBundled() { return this instanceof PluginBundled; } /** * @return the class name of the plugin owner.<br> * If this Plugin is not bundled (see {@link PluginBundled}) then it just returns the * current class name otherwise it will returns the plugin owner class name. */ public String getOwnerClassName() { if (isBundled()) return ((PluginBundled) this).getMainPluginClassName(); return getClass().getName(); } /** * @return the folder where the plugin is installed (or should be installed). */ public String getInstallFolder() { return ClassUtil.getPathFromQualifiedName(ClassUtil.getPackageName(getClass().getName())); } public Viewer getActiveViewer() { return Icy.getMainInterface().getActiveViewer(); } public Sequence getActiveSequence() { return Icy.getMainInterface().getActiveSequence(); } public IcyBufferedImage getActiveImage() { return Icy.getMainInterface().getActiveImage(); } /** * @deprecated Use {@link #getActiveViewer()} instead */ @Deprecated public Viewer getFocusedViewer() { return getActiveViewer(); } /** * @deprecated Use {@link #getActiveSequence()} instead */ @Deprecated public Sequence getFocusedSequence() { return getActiveSequence(); } /** * @deprecated Use {@link #getActiveImage()} instead */ @Deprecated public IcyBufferedImage getFocusedImage() { return getActiveImage(); } public void addIcyFrame(final IcyFrame frame) { frame.addToDesktopPane(); } public void addSequence(final Sequence sequence) { Icy.getMainInterface().addSequence(sequence); } public void removeSequence(final Sequence sequence) { sequence.close(); } public ArrayList<Sequence> getSequences() { return Icy.getMainInterface().getSequences(); } /** * Return the resource URL from given resource name.<br> * Ex: <code>getResource("plugins/author/resources/def.xml");</code> * * @param name * resource name */ public URL getResource(String name) { return getClass().getClassLoader().getResource(name); } /** * Return resources corresponding to given resource name.<br> * Ex: <code>getResources("plugins/author/resources/def.xml");</code> * * @param name * resource name * @throws IOException */ public Enumeration<URL> getResources(String name) throws IOException { return getClass().getClassLoader().getResources(name); } /** * Return the resource as data stream from given resource name.<br> * Ex: <code>getResourceAsStream("plugins/author/resources/def.xml");</code> * * @param name * resource name */ public InputStream getResourceAsStream(String name) { return getClass().getClassLoader().getResourceAsStream(name); } /** * Return the image resource from given resource name * Ex: <code>getResourceAsStream("plugins/author/resources/image.png");</code> * * @param resourceName * resource name */ public BufferedImage getImageResource(String resourceName) { return ImageUtil.load(getResourceAsStream(resourceName)); } /** * Return the icon resource from given resource name * Ex: <code>getResourceAsStream("plugins/author/resources/icon.png");</code> * * @param resourceName * resource name */ public ImageIcon getIconResource(String resourceName) { return ResourceUtil.getImageIcon(getImageResource(resourceName)); } /** * Retrieve the preferences root for this plugin.<br> */ public XMLPreferences getPreferencesRoot() { return PluginsPreferences.root(this); } /** * Retrieve the plugin preferences node for specified name.<br> * i.e : getPreferences("window") will return node * "plugins.[authorPackage].[pluginClass].window" */ public XMLPreferences getPreferences(String name) { return getPreferencesRoot().node(name); } /** * Returns the base resource path for plugin native libraries.<br/> * Depending the Operating System it can returns these values: * <ul> * <li>lib/unix32</li> * <li>lib/unix64</li> * <li>lib/mac32</li> * <li>lib/mac64</li> * <li>lib/win32</li> * <li>lib/win64</li> * </ul> */ protected String getResourceLibraryPath() { return "lib" + FileUtil.separator + SystemUtil.getOSArchIdString(); } /** * Load a packed native library from the JAR file.<br/> * Native libraries should be packaged with the following directory & file structure: * * <pre> * /lib/unix32 * libxxx.so * /lib/unix64 * libxxx.so * /lib/mac32 * libxxx.dylib * /lib/mac64 * libxxx.dylib * /lib/win32 * xxx.dll * /lib/win64 * xxx.dll * /plugins/myname/mypackage * MyPlugin.class * .... * </pre> * * Here "xxx" is the name of the native library.<br/> * Current approach is to unpack the native library into a temporary file and load from there. * * @param libName * @return true if the library was correctly loaded. * @see SystemUtil#loadLibrary(String) */ public boolean loadLibrary(String libName) { try { // get mapped library name String mappedlibName = System.mapLibraryName(libName); // get base resource path for native library final String basePath = getResourceLibraryPath() + FileUtil.separator; // search for library in resource URL libUrl = getResource(basePath + mappedlibName); // not found ? if (libUrl == null) { // jnilib extension may not work, try with "dylib" extension instead if (mappedlibName.endsWith(".jnilib")) { mappedlibName = mappedlibName.substring(0, mappedlibName.length() - 7) + ".dylib"; libUrl = getResource(basePath + mappedlibName); } // do the contrary in case we have an old "jnilib" file and system use "dylib" by default else if (mappedlibName.endsWith(".dylib")) { mappedlibName = mappedlibName.substring(0, mappedlibName.length() - 6) + ".jnilib"; libUrl = getResource(basePath + mappedlibName); } } // resource not found --> error if (libUrl == null) throw new IOException("Couldn't find resource " + basePath + mappedlibName); // extract resource final File extractedFile = extractResource(SystemUtil.getTempLibraryDirectory() + FileUtil.separator + mappedlibName, libUrl); // and load it System.load(extractedFile.getPath()); return true; } catch (IOException e) { System.err.println("Error while loading packed library " + libName + ": " + e); } return false; } /** * Extract a resource to the specified path * * @param outputPath * the file to extract the resource to * @param resource * the resource URL * @return the extracted file * @throws IOException */ protected File extractResource(String outputPath, URL resource) throws IOException { // open resource stream final InputStream in = resource.openStream(); // load resource final byte data[] = NetworkUtil.download(in); // create output file final File result = new File(outputPath); // file already exist ?? if (result.exists()) { // same size --> assume it's the same if (result.length() == data.length) return result; if (!FileUtil.delete(result, false)) throw new IOException("Cannot overwrite " + result + " file !"); } // save resource to file FileUtil.save(result, data, true); return result; } /** * Report an error log for this plugin (reported to Icy web site which report then to the * author of the plugin). * * @see IcyExceptionHandler#report(PluginDescriptor, String) */ public void report(String errorLog) { IcyExceptionHandler.report(descriptor, errorLog); } @Override public String toString() { return getDescriptor().getName(); } }