001/* 002 * Copyright 2010-2015 Institut Pasteur. 003 * 004 * This file is part of Icy. 005 * 006 * Icy is free software: you can redistribute it and/or modify 007 * it under the terms of the GNU General Public License as published by 008 * the Free Software Foundation, either version 3 of the License, or 009 * (at your option) any later version. 010 * 011 * Icy is distributed in the hope that it will be useful, 012 * but WITHOUT ANY WARRANTY; without even the implied warranty of 013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 014 * GNU General Public License for more details. 015 * 016 * You should have received a copy of the GNU General Public License 017 * along with Icy. If not, see <http://www.gnu.org/licenses/>. 018 */ 019package icy.plugin; 020 021import icy.file.FileUtil; 022import icy.file.Loader; 023import icy.gui.frame.progress.ProgressFrame; 024import icy.main.Icy; 025import icy.network.NetworkUtil; 026import icy.plugin.PluginDescriptor.PluginIdent; 027import icy.plugin.PluginDescriptor.PluginKernelNameSorter; 028import icy.plugin.abstract_.Plugin; 029import icy.plugin.classloader.JarClassLoader; 030import icy.plugin.interface_.PluginBundled; 031import icy.plugin.interface_.PluginDaemon; 032import icy.preferences.PluginPreferences; 033import icy.system.IcyExceptionHandler; 034import icy.system.thread.SingleProcessor; 035import icy.system.thread.ThreadUtil; 036import icy.util.ClassUtil; 037 038import java.io.IOException; 039import java.io.InputStream; 040import java.net.URL; 041import java.util.ArrayList; 042import java.util.Collections; 043import java.util.EventListener; 044import java.util.HashMap; 045import java.util.HashSet; 046import java.util.List; 047import java.util.Map; 048import java.util.Map.Entry; 049import java.util.Set; 050 051import javax.swing.event.EventListenerList; 052 053/** 054 * Plugin Loader class.<br> 055 * This class is used to load plugins from "plugins" package and "plugins" directory 056 * 057 * @author Stephane<br> 058 */ 059public class PluginLoader 060{ 061 public final static String PLUGIN_PACKAGE = "plugins"; 062 public final static String PLUGIN_KERNEL_PACKAGE = "plugins.kernel"; 063 public final static String PLUGIN_PATH = "plugins"; 064 065 // used to identify java version problem 066 public final static String NEWER_JAVA_REQUIRED = "Newer java version required"; 067 068 /** 069 * static class 070 */ 071 private static final PluginLoader instance = new PluginLoader(); 072 073 /** 074 * class loader 075 */ 076 private ClassLoader loader; 077 /** 078 * active daemons plugins 079 */ 080 private List<PluginDaemon> activeDaemons; 081 /** 082 * Loaded plugin list 083 */ 084 private List<PluginDescriptor> plugins; 085 086 /** 087 * listeners 088 */ 089 private final EventListenerList listeners; 090 091 /** 092 * JAR Class Loader disabled flag 093 */ 094 protected boolean JCLDisabled; 095 096 /** 097 * internals 098 */ 099 private final Runnable reloader; 100 final SingleProcessor processor; 101 102 private boolean initialized; 103 private boolean loading; 104 105 // private boolean logError; 106 107 /** 108 * static class 109 */ 110 private PluginLoader() 111 { 112 super(); 113 114 // default class loader 115 loader = new PluginClassLoader(); 116 // active daemons 117 activeDaemons = new ArrayList<PluginDaemon>(); 118 119 JCLDisabled = false; 120 initialized = false; 121 loading = false; 122 // needReload = false; 123 // logError = true; 124 125 plugins = new ArrayList<PluginDescriptor>(); 126 listeners = new EventListenerList(); 127 128 // reloader 129 reloader = new Runnable() 130 { 131 @Override 132 public void run() 133 { 134 reloadInternal(); 135 } 136 }; 137 138 processor = new SingleProcessor(true, "Local Plugin Loader"); 139 140 // don't load by default as we need Preferences to be ready first 141 }; 142 143 static void prepare() 144 { 145 if (!instance.initialized) 146 { 147 if (isLoading()) 148 waitWhileLoading(); 149 else 150 reload(); 151 } 152 } 153 154 /** 155 * Reload the list of installed plugins (asynchronous version). 156 */ 157 public static void reloadAsynch() 158 { 159 instance.processor.submit(instance.reloader); 160 } 161 162 /** 163 * Reload the list of installed plugins (wait for completion). 164 */ 165 public static void reload() 166 { 167 instance.processor.submit(instance.reloader); 168 // ensure we don't miss the reloading 169 ThreadUtil.sleep(500); 170 waitWhileLoading(); 171 } 172 173 /** 174 * @deprecated Use {@link #reload()} instead. 175 */ 176 @SuppressWarnings("unused") 177 @Deprecated 178 public static void reload(boolean forceNow) 179 { 180 reload(); 181 } 182 183 /** 184 * Stop and restart all daemons plugins. 185 */ 186 public static synchronized void resetDaemons() 187 { 188 // reset will be done later 189 if (isLoading()) 190 return; 191 192 stopDaemons(); 193 startDaemons(); 194 } 195 196 /** 197 * Reload the list of installed plugins (in "plugins" directory) 198 */ 199 void reloadInternal() 200 { 201 // needReload = false; 202 loading = true; 203 204 // stop daemon plugins 205 stopDaemons(); 206 207 // reset plugins and loader 208 final List<PluginDescriptor> newPlugins = new ArrayList<PluginDescriptor>(); 209 final ClassLoader newLoader; 210 211 // special case where JCL is disabled 212 if (JCLDisabled) 213 newLoader = PluginLoader.class.getClassLoader(); 214 else 215 { 216 newLoader = new PluginClassLoader(); 217 218 // reload plugins directory to search path 219 ((PluginClassLoader) newLoader).add(PLUGIN_PATH); 220 } 221 222 // no need to complete loading... 223 if (processor.hasWaitingTasks()) 224 return; 225 226 final Set<String> classes = new HashSet<String>(); 227 228 try 229 { 230 // search for plugins in "Plugins" package (needed when working from JAR archive) 231 ClassUtil.findClassNamesInPackage(PLUGIN_PACKAGE, true, classes); 232 // search for plugins in "Plugins" directory with default plugin package name 233 ClassUtil.findClassNamesInPath(PLUGIN_PATH, PLUGIN_PACKAGE, true, classes); 234 } 235 catch (IOException e) 236 { 237 System.err.println("Error loading plugins :"); 238 IcyExceptionHandler.showErrorMessage(e, true); 239 } 240 241 for (String className : classes) 242 { 243 // we only want to load classes from 'plugins' package 244 if (!className.startsWith(PLUGIN_PACKAGE)) 245 continue; 246 // filter incorrect named classes (Jython classes for instances) 247 if (className.contains("$")) 248 continue; 249 250 // no need to complete loading... 251 if (processor.hasWaitingTasks()) 252 return; 253 254 try 255 { 256 // try to load class and check we have a Plugin class at same time 257 final Class<? extends Plugin> pluginClass = newLoader.loadClass(className).asSubclass(Plugin.class); 258 // add to list 259 newPlugins.add(new PluginDescriptor(pluginClass)); 260 } 261 catch (NoClassDefFoundError e) 262 { 263 // fatal error 264 System.err.println("Class '" + className + "' cannot be loaded :"); 265 System.err.println( 266 "Required class '" + ClassUtil.getQualifiedNameFromPath(e.getMessage()) + "' not found."); 267 } 268 catch (OutOfMemoryError e) 269 { 270 // fatal error 271 IcyExceptionHandler.showErrorMessage(e, false); 272 System.err.println("Class '" + className + "' is discarded"); 273 } 274 catch (UnsupportedClassVersionError e) 275 { 276 // java version error (here we just notify in the console) 277 System.err.println(NEWER_JAVA_REQUIRED + " for class '" + className + "' (discarded)"); 278 } 279 catch (Error e) 280 { 281 // fatal error 282 IcyExceptionHandler.showErrorMessage(e, false); 283 System.err.println("Class '" + className + "' is discarded"); 284 } 285 catch (ClassCastException e) 286 { 287 // ignore ClassCastException (for classes which doesn't extend Plugin) 288 } 289 catch (ClassNotFoundException e) 290 { 291 // ignore ClassNotFoundException (for no public classes) 292 } 293 catch (Exception e) 294 { 295 // fatal error 296 IcyExceptionHandler.showErrorMessage(e, false); 297 System.err.println("Class '" + className + "' is discarded"); 298 } 299 } 300 301 // sort list 302 Collections.sort(newPlugins, PluginKernelNameSorter.instance); 303 304 // release loaded resources 305 if (loader instanceof JarClassLoader) 306 ((JarClassLoader) loader).unloadAll(); 307 308 loader = newLoader; 309 plugins = newPlugins; 310 311 loading = false; 312 313 // notify change 314 changed(); 315 } 316 317 /** 318 * Returns the list of daemon type plugins. 319 */ 320 public static ArrayList<PluginDescriptor> getDaemonPlugins() 321 { 322 final ArrayList<PluginDescriptor> result = new ArrayList<PluginDescriptor>(); 323 324 synchronized (instance.plugins) 325 { 326 for (PluginDescriptor pluginDescriptor : instance.plugins) 327 { 328 if (pluginDescriptor.isInstanceOf(PluginDaemon.class)) 329 { 330 // accept class ? 331 if (!pluginDescriptor.isAbstract() && !pluginDescriptor.isInterface()) 332 result.add(pluginDescriptor); 333 } 334 } 335 } 336 337 return result; 338 } 339 340 /** 341 * Returns the list of active daemon plugins. 342 */ 343 public static ArrayList<PluginDaemon> getActiveDaemons() 344 { 345 synchronized (instance.activeDaemons) 346 { 347 return new ArrayList<PluginDaemon>(instance.activeDaemons); 348 } 349 } 350 351 /** 352 * Start daemons plugins. 353 */ 354 static synchronized void startDaemons() 355 { 356 // at this point active daemons should be empty ! 357 if (!instance.activeDaemons.isEmpty()) 358 stopDaemons(); 359 360 final List<String> inactives = PluginPreferences.getInactiveDaemons(); 361 final List<PluginDaemon> newDaemons = new ArrayList<PluginDaemon>(); 362 363 for (PluginDescriptor pluginDesc : getDaemonPlugins()) 364 { 365 // not found in inactives ? 366 if (inactives.indexOf(pluginDesc.getClassName()) == -1) 367 { 368 try 369 { 370 final PluginDaemon plugin = (PluginDaemon) pluginDesc.getPluginClass().newInstance(); 371 final Thread thread = new Thread(plugin, pluginDesc.getName()); 372 373 thread.setName(pluginDesc.getName()); 374 // so icy can exit even with running daemon plugin 375 thread.setDaemon(true); 376 377 // init daemon 378 plugin.init(); 379 // start daemon 380 thread.start(); 381 // register daemon plugin (so we can stop it later) 382 Icy.getMainInterface().registerPlugin((Plugin) plugin); 383 384 // add daemon plugin to list 385 newDaemons.add(plugin); 386 } 387 catch (Throwable t) 388 { 389 IcyExceptionHandler.handleException(pluginDesc, t, true); 390 } 391 } 392 } 393 394 instance.activeDaemons = newDaemons; 395 } 396 397 /** 398 * Stop daemons plugins. 399 */ 400 public synchronized static void stopDaemons() 401 { 402 for (PluginDaemon daemonPlug : getActiveDaemons()) 403 { 404 try 405 { 406 // stop the daemon 407 daemonPlug.stop(); 408 } 409 catch (Throwable t) 410 { 411 IcyExceptionHandler.handleException(((Plugin) daemonPlug).getDescriptor(), t, true); 412 } 413 } 414 415 // no more active daemons 416 instance.activeDaemons = new ArrayList<PluginDaemon>(); 417 } 418 419 /** 420 * Return the loader 421 */ 422 public static ClassLoader getLoader() 423 { 424 return instance.loader; 425 } 426 427 /** 428 * Return all resources present in the Plugin class loader. 429 */ 430 public static Map<String, URL> getAllResources() 431 { 432 prepare(); 433 434 synchronized (instance.loader) 435 { 436 if (instance.loader instanceof JarClassLoader) 437 return ((JarClassLoader) instance.loader).getResources(); 438 } 439 440 return new HashMap<String, URL>(); 441 } 442 443 /** 444 * Return content of all loaded resources. 445 */ 446 public static Map<String, byte[]> getLoadedResources() 447 { 448 prepare(); 449 450 synchronized (instance.loader) 451 { 452 if (instance.loader instanceof JarClassLoader) 453 return ((JarClassLoader) instance.loader).getLoadedResources(); 454 } 455 456 return new HashMap<String, byte[]>(); 457 } 458 459 /** 460 * Return all loaded classes. 461 */ 462 @SuppressWarnings("rawtypes") 463 public static Map<String, Class<?>> getLoadedClasses() 464 { 465 prepare(); 466 467 synchronized (instance.loader) 468 { 469 if (instance.loader instanceof JarClassLoader) 470 { 471 final HashMap<String, Class<?>> result = new HashMap<String, Class<?>>(); 472 final Map<String, Class> classes = ((JarClassLoader) instance.loader).getLoadedClasses(); 473 474 for (Entry<String, Class> entry : classes.entrySet()) 475 result.put(entry.getKey(), entry.getValue()); 476 477 return result; 478 } 479 } 480 481 return new HashMap<String, Class<?>>(); 482 } 483 484 /** 485 * Return all classes. 486 * 487 * @deprecated Use {@link #getLoadedClasses()} instead as we load classes on demand. 488 */ 489 @Deprecated 490 public static Map<String, Class<?>> getAllClasses() 491 { 492 return getLoadedClasses(); 493 } 494 495 /** 496 * Return a resource as data stream from given resource name 497 * 498 * @param name 499 * resource name 500 */ 501 public static InputStream getResourceAsStream(String name) 502 { 503 prepare(); 504 505 synchronized (instance.loader) 506 { 507 return instance.loader.getResourceAsStream(name); 508 } 509 } 510 511 /** 512 * Return the list of loaded plugins. 513 */ 514 public static ArrayList<PluginDescriptor> getPlugins() 515 { 516 return getPlugins(true); 517 } 518 519 /** 520 * Return the list of loaded plugins. 521 * 522 * @param wantBundled 523 * specify if we also want plugin implementing the {@link PluginBundled} interface. 524 */ 525 public static ArrayList<PluginDescriptor> getPlugins(boolean wantBundled) 526 { 527 prepare(); 528 529 final ArrayList<PluginDescriptor> result = new ArrayList<PluginDescriptor>(); 530 531 // better to return a copy as we have async list loading 532 synchronized (instance.plugins) 533 { 534 for (PluginDescriptor plugin : instance.plugins) 535 { 536 if (wantBundled || (!plugin.isBundled())) 537 result.add(plugin); 538 } 539 } 540 541 return result; 542 } 543 544 /** 545 * Return the list of loaded plugins which derive from the specified class. 546 * 547 * @param clazz 548 * The class object defining the class we want plugin derive from. 549 */ 550 public static ArrayList<PluginDescriptor> getPlugins(Class<?> clazz) 551 { 552 return getPlugins(clazz, true, false, false); 553 } 554 555 /** 556 * Return the list of loaded plugins which derive from the specified class. 557 * 558 * @param clazz 559 * The class object defining the class we want plugin derive from. 560 * @param wantBundled 561 * specify if we also want plugin implementing the {@link PluginBundled} interface 562 * @param wantAbstract 563 * specify if we also want abstract classes 564 * @param wantInterface 565 * specify if we also want interfaces 566 */ 567 public static ArrayList<PluginDescriptor> getPlugins(Class<?> clazz, boolean wantBundled, boolean wantAbstract, 568 boolean wantInterface) 569 { 570 prepare(); 571 572 final ArrayList<PluginDescriptor> result = new ArrayList<PluginDescriptor>(); 573 574 if (clazz != null) 575 { 576 synchronized (instance.plugins) 577 { 578 for (PluginDescriptor pluginDescriptor : instance.plugins) 579 { 580 if (pluginDescriptor.isInstanceOf(clazz)) 581 { 582 // accept class ? 583 if ((wantAbstract || !pluginDescriptor.isAbstract()) 584 && (wantInterface || !pluginDescriptor.isInterface()) 585 && (wantBundled || !pluginDescriptor.isBundled())) 586 result.add(pluginDescriptor); 587 } 588 } 589 } 590 } 591 592 return result; 593 } 594 595 /** 596 * Return the list of "actionable" plugins (mean we can launch them from GUI). 597 * 598 * @param wantBundled 599 * specify if we also want plugin implementing the {@link PluginBundled} interface 600 */ 601 public static ArrayList<PluginDescriptor> getActionablePlugins(boolean wantBundled) 602 { 603 prepare(); 604 605 final ArrayList<PluginDescriptor> result = new ArrayList<PluginDescriptor>(); 606 607 synchronized (instance.plugins) 608 { 609 for (PluginDescriptor pluginDescriptor : instance.plugins) 610 { 611 if (pluginDescriptor.isActionable() && (wantBundled || !pluginDescriptor.isBundled())) 612 result.add(pluginDescriptor); 613 } 614 } 615 616 return result; 617 } 618 619 /** 620 * Return the list of "actionable" plugins (mean we can launch them from GUI).<br> 621 * By default plugin implementing the {@link PluginBundled} interface are also returned. 622 */ 623 public static ArrayList<PluginDescriptor> getActionablePlugins() 624 { 625 return getActionablePlugins(true); 626 } 627 628 /** 629 * @return the loading 630 */ 631 public static boolean isLoading() 632 { 633 return instance.processor.hasWaitingTasks() || instance.loading; 634 } 635 636 /** 637 * wait until loading completed 638 */ 639 public static void waitWhileLoading() 640 { 641 while (isLoading()) 642 ThreadUtil.sleep(100); 643 } 644 645 /** 646 * Returns <code>true</code> if the specified plugin exists in the {@link PluginLoader}. 647 * 648 * @param plugin 649 * the plugin we are looking for. 650 * @param acceptNewer 651 * allow newer version of the plugin 652 */ 653 public static boolean isLoaded(PluginDescriptor plugin, boolean acceptNewer) 654 { 655 return (getPlugin(plugin.getIdent(), acceptNewer) != null); 656 } 657 658 /** 659 * Returns <code>true</code> if the specified plugin class exists in the {@link PluginLoader}. 660 * 661 * @param className 662 * class name of the plugin we are looking for. 663 */ 664 public static boolean isLoaded(String className) 665 { 666 return (getPlugin(className) != null); 667 } 668 669 /** 670 * Returns the plugin corresponding to the specified plugin identity structure.<br> 671 * Returns <code>null</code> if the plugin does not exists in the {@link PluginLoader}. 672 * 673 * @param ident 674 * plugin identity 675 * @param acceptNewer 676 * allow newer version of the plugin 677 */ 678 public static PluginDescriptor getPlugin(PluginIdent ident, boolean acceptNewer) 679 { 680 prepare(); 681 682 synchronized (instance.plugins) 683 { 684 return PluginDescriptor.getPlugin(instance.plugins, ident, acceptNewer); 685 } 686 } 687 688 /** 689 * Returns the plugin corresponding to the specified plugin class name.<br> 690 * Returns <code>null</code> if the plugin does not exists in the {@link PluginLoader}. 691 * 692 * @param className 693 * class name of the plugin we are looking for. 694 */ 695 public static PluginDescriptor getPlugin(String className) 696 { 697 prepare(); 698 699 synchronized (instance.plugins) 700 { 701 return PluginDescriptor.getPlugin(instance.plugins, className); 702 } 703 } 704 705 /** 706 * Returns the plugin class corresponding to the specified plugin class name.<br> 707 * Returns <code>null</code> if the plugin does not exists in the {@link PluginLoader}. 708 * 709 * @param className 710 * class name of the plugin we are looking for. 711 */ 712 public static Class<? extends Plugin> getPluginClass(String className) 713 { 714 prepare(); 715 716 final PluginDescriptor descriptor = getPlugin(className); 717 718 if (descriptor != null) 719 return descriptor.getPluginClass(); 720 721 return null; 722 } 723 724 /** 725 * Try to load and returns the specified class from the {@link PluginLoader}.<br> 726 * This method is equivalent to call {@link #getLoader()} then call 727 * <code>loadClass(String)</code> method from it. 728 * 729 * @param className 730 * class name of the class we want to load. 731 */ 732 public static Class<?> loadClass(String className) throws ClassNotFoundException 733 { 734 prepare(); 735 736 synchronized (instance.loader) 737 { 738 // try to load class 739 return instance.loader.loadClass(className); 740 } 741 } 742 743 /** 744 * Verify the specified plugin is correctly installed.<br> 745 * Returns an empty string if the plugin is valid otherwise it returns the error message. 746 */ 747 public static String verifyPlugin(PluginDescriptor plugin) 748 { 749 synchronized (instance.loader) 750 { 751 try 752 { 753 // then try to load the plugin class as Plugin class 754 instance.loader.loadClass(plugin.getClassName()).asSubclass(Plugin.class); 755 } 756 catch (UnsupportedClassVersionError e) 757 { 758 return NEWER_JAVA_REQUIRED + "."; 759 } 760 catch (Error e) 761 { 762 return e.toString(); 763 } 764 catch (ClassCastException e) 765 { 766 return IcyExceptionHandler.getErrorMessage(e, false) 767 + "Your plugin class should extends 'icy.plugin.abstract_.Plugin' class."; 768 } 769 catch (ClassNotFoundException e) 770 { 771 return IcyExceptionHandler.getErrorMessage(e, false) 772 + "Verify you correctly set the class name in your plugin description."; 773 } 774 catch (Exception e) 775 { 776 return IcyExceptionHandler.getErrorMessage(e, false); 777 } 778 } 779 780 return ""; 781 } 782 783 /** 784 * Load all classes from specified path 785 */ 786 // private static ArrayList<String> loadAllClasses(String path) 787 // { 788 // // search for class names in that path 789 // final HashSet<String> classNames = ClassUtil.findClassNamesInPath(path, true); 790 // final ArrayList<String> result = new ArrayList<String>(); 791 // 792 // synchronized (loader) 793 // { 794 // for (String className : classNames) 795 // { 796 // try 797 // { 798 // // try to load class 799 // loader.loadClass(className); 800 // } 801 // catch (Error err) 802 // { 803 // // fatal error while loading class, store error String 804 // result.add("Fatal error while loading " + className + " :\n" + err.toString() + "\n"); 805 // } 806 // catch (ClassNotFoundException cnfe) 807 // { 808 // // ignore ClassNotFoundException (happen with private class) 809 // } 810 // catch (Exception exc) 811 // { 812 // result.add("Fatal error while loading " + className + " :\n" + exc.toString() + "\n"); 813 // } 814 // } 815 // } 816 // 817 // return result; 818 // } 819 820 public static boolean isJCLDisabled() 821 { 822 return instance.JCLDisabled; 823 } 824 825 public static void setJCLDisabled(boolean value) 826 { 827 instance.JCLDisabled = value; 828 } 829 830 /** 831 * @deprecated 832 */ 833 @Deprecated 834 public static boolean getLogError() 835 { 836 return false; 837 // return instance.logError; 838 } 839 840 /** 841 * @deprecated 842 */ 843 @Deprecated 844 public static void setLogError(boolean value) 845 { 846 // instance.logError = value; 847 } 848 849 /** 850 * Called when class loader 851 */ 852 protected void changed() 853 { 854 // check for missing or mis-installed plugins on first start 855 if (!initialized) 856 { 857 initialized = true; 858 checkPlugins(false); 859 } 860 861 // start daemon plugins 862 startDaemons(); 863 // notify listener we have changed 864 fireEvent(new PluginLoaderEvent()); 865 866 ThreadUtil.bgRun(new Runnable() 867 { 868 @Override 869 public void run() 870 { 871 // pre load the importers classes as they can be heavy 872 Loader.getSequenceFileImporters(); 873 Loader.getFileImporters(); 874 Loader.getImporters(); 875 } 876 }); 877 } 878 879 /** 880 * Check for missing plugins and install them if needed. 881 */ 882 public static void checkPlugins(boolean showProgress) 883 { 884 final List<PluginDescriptor> plugins = getPlugins(false); 885 final List<PluginDescriptor> required = new ArrayList<PluginDescriptor>(); 886 final List<PluginDescriptor> missings = new ArrayList<PluginDescriptor>(); 887 final List<PluginDescriptor> faulties = new ArrayList<PluginDescriptor>(); 888 889 if (NetworkUtil.hasInternetAccess()) 890 { 891 ProgressFrame pf; 892 893 if (showProgress) 894 { 895 pf = new ProgressFrame("Checking plugins..."); 896 pf.setLength(plugins.size()); 897 pf.setPosition(0); 898 } 899 else 900 pf = null; 901 902 PluginRepositoryLoader.waitLoaded(); 903 904 // get list of required and faulty plugins 905 for (PluginDescriptor plugin : plugins) 906 { 907 // get dependencies 908 if (!PluginInstaller.getDependencies(plugin, required, null, false)) 909 // error in dependencies --> try to reinstall the plugin 910 faulties.add(PluginRepositoryLoader.getPlugin(plugin.getClassName())); 911 912 if (pf != null) 913 pf.incPosition(); 914 } 915 916 if (pf != null) 917 pf.setLength(required.size()); 918 919 // check for missing plugins 920 for (PluginDescriptor plugin : required) 921 { 922 // dependency missing ? --> try to reinstall the plugin 923 if (!plugin.isInstalled()) 924 { 925 final PluginDescriptor toInstall = PluginRepositoryLoader.getPlugin(plugin.getClassName()); 926 if (toInstall != null) 927 missings.add(toInstall); 928 } 929 930 if (pf != null) 931 pf.incPosition(); 932 } 933 934 if ((faulties.size() > 0) || (missings.size() > 0)) 935 { 936 if (pf != null) 937 { 938 pf.setMessage("Installing missing plugins..."); 939 pf.setPosition(0); 940 pf.setLength(faulties.size() + missings.size()); 941 } 942 943 // remove faulty plugins 944 // for (PluginDescriptor plugin : faulties) 945 // PluginInstaller.desinstall(plugin, false, false); 946 // PluginInstaller.waitDesinstall(); 947 948 // install missing plugins 949 for (PluginDescriptor plugin : missings) 950 { 951 PluginInstaller.install(plugin, true); 952 if (pf != null) 953 pf.incPosition(); 954 } 955 // and reinstall faulty plugins 956 for (PluginDescriptor plugin : faulties) 957 { 958 PluginInstaller.install(plugin, true); 959 if (pf != null) 960 pf.incPosition(); 961 } 962 } 963 } 964 } 965 966 /** 967 * Add a listener 968 * 969 * @param listener 970 */ 971 public static void addListener(PluginLoaderListener listener) 972 { 973 synchronized (instance.listeners) 974 { 975 instance.listeners.add(PluginLoaderListener.class, listener); 976 } 977 } 978 979 /** 980 * Remove a listener 981 * 982 * @param listener 983 */ 984 public static void removeListener(PluginLoaderListener listener) 985 { 986 synchronized (instance.listeners) 987 { 988 instance.listeners.remove(PluginLoaderListener.class, listener); 989 } 990 } 991 992 /** 993 * fire event 994 */ 995 void fireEvent(PluginLoaderEvent e) 996 { 997 synchronized (listeners) 998 { 999 for (PluginLoaderListener listener : listeners.getListeners(PluginLoaderListener.class)) 1000 listener.pluginLoaderChanged(e); 1001 } 1002 } 1003 1004 public static class PluginClassLoader extends JarClassLoader 1005 { 1006 public PluginClassLoader() 1007 { 1008 super(); 1009 } 1010 1011 /** 1012 * Give access to this method 1013 */ 1014 public Class<?> getLoadedClass(String name) 1015 { 1016 return super.findLoadedClass(name); 1017 } 1018 1019 /** 1020 * Give access to this method 1021 */ 1022 public boolean isLoadedClass(String name) 1023 { 1024 return getLoadedClass(name) != null; 1025 } 1026 } 1027 1028 public static interface PluginLoaderListener extends EventListener 1029 { 1030 public void pluginLoaderChanged(PluginLoaderEvent e); 1031 } 1032 1033 public static class PluginLoaderEvent 1034 { 1035 public PluginLoaderEvent() 1036 { 1037 super(); 1038 } 1039 1040 @Override 1041 public boolean equals(Object obj) 1042 { 1043 if (obj instanceof PluginLoaderEvent) 1044 return true; 1045 1046 return super.equals(obj); 1047 } 1048 } 1049}