Ticket #6396: reload-plugins-0.1.patch
File reload-plugins-0.1.patch, 15.5 KB (added by , 14 years ago) |
---|
-
new file src/org/openstreetmap/josm/actions/ReloadPluginsAction.java
diff --git a/src/org/openstreetmap/josm/actions/ReloadPluginsAction.java b/src/org/openstreetmap/josm/actions/ReloadPluginsAction.java new file mode 100644 index 0000000..b5cb91b
- + 1 // License: GPL. For details, see LICENSE file. 2 package org.openstreetmap.josm.actions; 3 4 import static org.openstreetmap.josm.tools.I18n.tr; 5 6 import java.awt.event.ActionEvent; 7 import java.awt.event.KeyEvent; 8 9 import org.openstreetmap.josm.plugins.PluginHandler; 10 import org.openstreetmap.josm.tools.Shortcut; 11 12 public class ReloadPluginsAction extends JosmAction { 13 14 public ReloadPluginsAction() { 15 super(tr("Reload plugins"), null, tr("Reload plugins"), Shortcut.registerShortcut("system:reload-plugins", 16 tr("Reload plugins"), KeyEvent.VK_F5, Shortcut.GROUP_DIRECT, Shortcut.SHIFT_DEFAULT), true); 17 } 18 19 @Override 20 public void actionPerformed(ActionEvent e) { 21 PluginHandler.reloadPlugins(); 22 } 23 } -
src/org/openstreetmap/josm/gui/MainMenu.java
diff --git a/src/org/openstreetmap/josm/gui/MainMenu.java b/src/org/openstreetmap/josm/gui/MainMenu.java index b17cb98..a86c584 100644
a b import org.openstreetmap.josm.actions.NewAction; 52 52 import org.openstreetmap.josm.actions.OpenFileAction; 53 53 import org.openstreetmap.josm.actions.OpenLocationAction; 54 54 import org.openstreetmap.josm.actions.OrthogonalizeAction; 55 import org.openstreetmap.josm.actions.ReloadPluginsAction; 55 56 import org.openstreetmap.josm.actions.OrthogonalizeAction.Undo; 56 57 import org.openstreetmap.josm.actions.PasteAction; 57 58 import org.openstreetmap.josm.actions.PasteTagsAction; … … public class MainMenu extends JMenuBar { 359 360 current.setAccelerator(Shortcut.registerShortcut("system:help", tr("Help"), KeyEvent.VK_F1, 360 361 Shortcut.GROUP_DIRECT).getKeyStroke()); 361 362 add(helpMenu, about); 363 add(helpMenu, new ReloadPluginsAction()); 362 364 363 365 new PresetsMenuEnabler(presetsMenu).refreshEnabled(); 364 366 } -
src/org/openstreetmap/josm/gui/MapFrame.java
diff --git a/src/org/openstreetmap/josm/gui/MapFrame.java b/src/org/openstreetmap/josm/gui/MapFrame.java index 96bddae..2969794 100644
a b public class MapFrame extends JPanel implements Destroyable, LayerChangeListener 290 290 throw new IllegalArgumentException("MapMode action must be subclass of MapMode"); 291 291 } 292 292 293 public void removeMapMode(IconToggleButton mode) { 294 toolBarActions.remove(mode); 295 toolGroup.remove(mode); 296 mapModes.remove(mode.getAction()); 297 } 298 293 299 /** 294 300 * Fires an property changed event "visible". 295 301 */ -
src/org/openstreetmap/josm/plugins/Plugin.java
diff --git a/src/org/openstreetmap/josm/plugins/Plugin.java b/src/org/openstreetmap/josm/plugins/Plugin.java index 0ef60ac..058da9c 100644
a b public abstract class Plugin { 72 72 this.info = info; 73 73 } 74 74 75 public void preReloadCleanup() { 76 System.out.println("no cleanup for this class"); 77 } 78 75 79 /** 76 80 * @return The directory for the plugin to store all kind of stuff. 77 81 */ -
src/org/openstreetmap/josm/plugins/PluginHandler.java
diff --git a/src/org/openstreetmap/josm/plugins/PluginHandler.java b/src/org/openstreetmap/josm/plugins/PluginHandler.java index db94edf..a97f4d4 100644
a b import java.awt.Window; 13 13 import java.awt.event.ActionEvent; 14 14 import java.io.File; 15 15 import java.io.FilenameFilter; 16 import java.lang.reflect.Method; 16 17 import java.net.URL; 17 18 import java.net.URLClassLoader; 18 19 import java.util.ArrayList; … … import org.openstreetmap.josm.gui.help.HelpUtil; 57 58 import org.openstreetmap.josm.gui.preferences.PreferenceSettingFactory; 58 59 import org.openstreetmap.josm.gui.progress.NullProgressMonitor; 59 60 import org.openstreetmap.josm.gui.progress.ProgressMonitor; 60 import org.openstreetmap.josm.io.remotecontrol.RemoteControl;61 61 import org.openstreetmap.josm.tools.CheckParameterUtil; 62 62 import org.openstreetmap.josm.tools.GBC; 63 63 import org.openstreetmap.josm.tools.ImageProvider; … … public class PluginHandler { 429 429 } 430 430 431 431 /** 432 * Creates a class loader for loading plugin code. 433 * 434 * @param plugins the collection of plugins which are going to be loaded with this 435 * class loader 436 * @return the class loader 432 * Helper to extract the jar paths of the plugins 437 433 */ 438 p ublic static ClassLoader createClassLoader(Collection<PluginInformation> plugins) {434 private static URL[] createJarURLs(Collection<PluginInformation> plugins) { 439 435 // iterate all plugins and collect all libraries of all plugins: 440 436 List<URL> allPluginLibraries = new LinkedList<URL>(); 441 437 File pluginDir = Main.pref.getPluginsDirectory(); … … public class PluginHandler { 451 447 452 448 // create a classloader for all plugins: 453 449 URL[] jarUrls = new URL[allPluginLibraries.size()]; 454 jarUrls = allPluginLibraries.toArray(jarUrls); 450 return allPluginLibraries.toArray(jarUrls); 451 } 452 /** 453 * Creates a class loader for loading plugin code. 454 * 455 * @param plugins the collection of plugins which are going to be loaded with this 456 * class loader 457 * @return the class loader 458 */ 459 public static ClassLoader createClassLoader(Collection<PluginInformation> plugins) { 460 URL[] jarUrls = createJarURLs(plugins); 455 461 URLClassLoader pluginClassLoader = new URLClassLoader(jarUrls, Main.class.getClassLoader()); 456 462 return pluginClassLoader; 457 463 } 458 464 465 private static String getPackagePartOfClassName(String name) { 466 for (int i = name.length() - 1; i >= 0; i--) { 467 if (name.charAt(i) == '.') 468 return name.substring(0, i); 469 } 470 return ""; 471 } 472 473 private static boolean isSubPackageOf(String sub, String parent) { 474 return sub.startsWith(parent); 475 } 476 477 /** 478 * Creates a class loader that will reload the supplied plugins 479 * 480 * The plugins should not define classes in packages above the package of the 'Plugin-Class' (defined in MANIFEST) 481 * ie. all classes must belong to a package matching: <plugin-class-package>.* 482 */ 483 public static ClassLoader createClassReloader(Collection<PluginInformation> plugins) { 484 final Set<String> packageList = new HashSet<String>(); 485 for (PluginInformation pi : plugins) { 486 packageList.add(getPackagePartOfClassName(pi.className)); 487 } 488 489 ClassLoader deceiving = new ClassLoader(Main.class.getClassLoader()) { 490 // Overriding loadClass/1 does not seem to work ... 491 // Are the 'fullClassName' guaranteed to be full? 492 @Override 493 protected Class<?> loadClass(String fullClassName, boolean resolve) throws ClassNotFoundException { 494 String packageOfClass = getPackagePartOfClassName(fullClassName); 495 boolean hide = false; 496 for(String pkg : packageList) { 497 if(isSubPackageOf(packageOfClass, pkg)) { 498 hide = true; 499 break; 500 } 501 } 502 if(hide) { 503 System.out.println(fullClassName); 504 return null; 505 } else { 506 // is it possible that super.loadClass/2 calls this.loadClass/2 again, causing infinite recursion? 507 return super.loadClass(fullClassName, resolve); 508 } 509 } 510 }; 511 URL[] jarUrls = createJarURLs(plugins); 512 return new URLClassLoader(jarUrls, deceiving); 513 } 514 515 // TODO: MANIFEST property instead? This is simpler for everyone though? 516 private static boolean supportReload(PluginProxy pluginProxy) { 517 try { 518 Method m = pluginProxy.plugin.getClass().getMethod("preReloadCleanup"); 519 return !m.getDeclaringClass().equals(Plugin.class); 520 } catch (NoSuchMethodException e) { 521 return false; 522 } 523 } 524 525 // FIXME: fucked if p1 depends on p2 and only p2 is reloadable 526 // Should be OK to let 'sources' keep the old class loader since the new class loader is prepended? 527 // Somewhat complicated if we should filter out the reloaded jars from the old loader... 528 // TODO: This does not check preconditions for the plugins, assuming they are fulfilled as the plugins are already loaded. This is of course not 100% correct. 529 // TODO: add progressmonitor support 530 // TODO: do we need to respect the loadearly/late flags? 531 // TODO: seems to be quick enough, but is it problematic to to in a worker thread? 532 /** 533 * Reloads all loaded plugins implementing 'preReloadCleanup' 534 */ 535 public static void reloadPlugins() { 536 // ProgressMonitor monitor = null; 537 // if (monitor == null) { 538 // monitor = NullProgressMonitor.INSTANCE; 539 // } 540 List<PluginInformation> plugins = new ArrayList<PluginInformation>(); 541 for (Iterator<PluginProxy> iter = pluginList.iterator(); iter.hasNext();) { 542 PluginProxy pp = iter.next(); 543 if (supportReload(pp)) { 544 pp.preReloadCleanup(); 545 plugins.add(pp.getPluginInformation()); 546 iter.remove(); 547 } 548 } 549 if (plugins.isEmpty()) 550 return; 551 // safest to not have a to-be-reloaded map mode active 552 Main.map.selectSelectTool(false); 553 // try { 554 555 // List<PluginInformation> plugins = PluginHandler.buildListOfPluginsToLoad(null,monitor.createSubTaskMonitor(1, false)); 556 557 // monitor.beginTask(tr("Loading plugins ...")); 558 // monitor.subTask(tr("Checking plugin preconditions...")); 559 // Collection<PluginInformation> toLoad = preproccessPluginList(null, plugins); 560 561 ClassLoader cl = createClassReloader(plugins); 562 563 loadPluginsNoCheck(null, plugins, null, cl); 564 for (PluginProxy plugin : pluginList) { 565 plugin.mapFrameInitialized(Main.map, Main.map); 566 } 567 // PluginHandler.notifyMapFrameChanged(Main.map, Main.map); 568 // } finally { 569 // monitor.finishTask(); 570 // } 571 } 572 459 573 /** 460 574 * Loads and instantiates the plugin described by <code>plugin</code> using 461 575 * the class loader <code>pluginClassLoader</code>. … … public class PluginHandler { 493 607 * @param plugins the list of plugins 494 608 * @param monitor the progress monitor. Defaults to {@see NullProgressMonitor#INSTANCE} if null. 495 609 */ 496 public static void loadPlugins(Window parent, Collection<PluginInformation> plugins, ProgressMonitor monitor) {610 public static void loadPlugins(Window parent, Collection<PluginInformation> plugins, ProgressMonitor monitor) { 497 611 if (monitor == null) { 498 612 monitor = NullProgressMonitor.INSTANCE; 499 613 } 500 614 try { 501 615 monitor.beginTask(tr("Loading plugins ...")); 502 616 monitor.subTask(tr("Checking plugin preconditions...")); 503 List<PluginInformation> toLoad = new LinkedList<PluginInformation>(); 504 for (PluginInformation pi: plugins) { 505 if (checkLoadPreconditions(parent, plugins, pi)) { 506 toLoad.add(pi); 507 } 508 } 509 // sort the plugins according to their "staging" equivalence class. The 510 // lower the value of "stage" the earlier the plugin should be loaded. 511 // 512 Collections.sort( 513 toLoad, 514 new Comparator<PluginInformation>() { 515 public int compare(PluginInformation o1, PluginInformation o2) { 516 if (o1.stage < o2.stage) return -1; 517 if (o1.stage == o2.stage) return 0; 518 return 1; 519 } 520 } 521 ); 617 Collection<PluginInformation> toLoad = preproccessPluginList(parent, plugins); 522 618 if (toLoad.isEmpty()) 523 619 return; 620 loadPluginsNoCheck(parent, toLoad, monitor, PluginHandler.createClassLoader(toLoad)); 621 } finally { 622 monitor.finishTask(); 623 } 624 } 524 625 525 ClassLoader pluginClassLoader = createClassLoader(toLoad); 526 sources.add(0, pluginClassLoader); 527 monitor.setTicksCount(toLoad.size()); 528 for (PluginInformation info : toLoad) { 626 public static void loadPluginsNoCheck(Window parent, Collection<PluginInformation> plugins, 627 ProgressMonitor monitor, ClassLoader classLoader) { 628 sources.add(0, classLoader); 629 if (monitor != null) 630 monitor.setTicksCount(plugins.size()); 631 for (PluginInformation info : plugins) { 632 if (monitor != null) 529 633 monitor.setExtraText(tr("Loading plugin ''{0}''...", info.name)); 530 loadPlugin(parent, info, pluginClassLoader); 634 else 635 System.out.println(tr("Loading plugin ''{0}''...", info.name)); 636 loadPlugin(parent, info, classLoader); 637 if (monitor != null) 531 638 monitor.worked(1); 639 } 640 } 641 642 private static Collection<PluginInformation> preproccessPluginList(Window parent, 643 Collection<PluginInformation> plugins) { 644 List<PluginInformation> toLoad = new LinkedList<PluginInformation>(); 645 for (PluginInformation pi : plugins) { 646 if (checkLoadPreconditions(parent, plugins, pi)) { 647 toLoad.add(pi); 532 648 } 533 } finally {534 monitor.finishTask();535 649 } 650 // sort the plugins according to their "staging" equivalence class. The 651 // lower the value of "stage" the earlier the plugin should be loaded. 652 // 653 Collections.sort(toLoad, new Comparator<PluginInformation>() { 654 public int compare(PluginInformation o1, PluginInformation o2) { 655 if (o1.stage < o2.stage) 656 return -1; 657 if (o1.stage == o2.stage) 658 return 0; 659 return 1; 660 } 661 }); 662 return toLoad; 536 663 } 537 664 538 665 /** -
src/org/openstreetmap/josm/plugins/PluginProxy.java
diff --git a/src/org/openstreetmap/josm/plugins/PluginProxy.java b/src/org/openstreetmap/josm/plugins/PluginProxy.java index 9d2e094..8388c79 100644
a b 1 1 // License: GPL. Copyright 2007 by Immanuel Scholz and others 2 2 package org.openstreetmap.josm.plugins; 3 3 4 import java.lang.reflect.Method; 4 5 import java.util.List; 5 6 6 7 import org.openstreetmap.josm.gui.MapFrame; … … public class PluginProxy extends Plugin { 33 34 } 34 35 } 35 36 37 @Override public void preReloadCleanup() { 38 try { 39 Method m = plugin.getClass().getMethod("preReloadCleanup"); 40 m.invoke(plugin); 41 } catch (NoSuchMethodException e) { 42 } catch (Exception e) { 43 BugReportExceptionHandler.handleException(new PluginException(this, getPluginInformation().name, e)); 44 } 45 } 46 36 47 @Override public PreferenceSetting getPreferenceSetting() { 37 48 try { 38 49 return (PreferenceSetting)plugin.getClass().getMethod("getPreferenceSetting").invoke(plugin);