1 19 package org.java.plugin.standard; 20 21 import java.net.URL ; 22 import java.util.ArrayList ; 23 import java.util.Collections ; 24 import java.util.HashMap ; 25 import java.util.HashSet ; 26 import java.util.Iterator ; 27 import java.util.LinkedList ; 28 import java.util.List ; 29 import java.util.Map ; 30 import java.util.Set ; 31 32 import org.apache.commons.logging.Log; 33 import org.apache.commons.logging.LogFactory; 34 import org.java.plugin.JpfException; 35 import org.java.plugin.PathResolver; 36 import org.java.plugin.Plugin; 37 import org.java.plugin.PluginClassLoader; 38 import org.java.plugin.PluginLifecycleException; 39 import org.java.plugin.PluginManager; 40 import org.java.plugin.registry.PluginDescriptor; 41 import org.java.plugin.registry.PluginFragment; 42 import org.java.plugin.registry.PluginPrerequisite; 43 import org.java.plugin.registry.PluginRegistry; 44 import org.java.plugin.registry.PluginRegistry.RegistryChangeData; 45 import org.java.plugin.registry.PluginRegistry.RegistryChangeListener; 46 47 51 public final class StandardPluginManager extends PluginManager { 52 Log log = LogFactory.getLog(getClass()); 53 54 private final PathResolver pathResolver; 55 private final PluginRegistry registry; 56 private final PluginLifecycleHandler lifecycleHandler; 57 private final Map activePlugins = new HashMap (); private final Set activatingPlugins = new HashSet (); private final Set badPlugins = new HashSet (); private final List activationLog = new LinkedList (); private final Map classLoaders = 62 new HashMap (); private final Set disabledPlugins = new HashSet (); private final List listeners = 65 Collections.synchronizedList(new LinkedList ()); private RegistryChangeListener registryChangeListener; 67 private Map notRegisteredPluginLocations = new HashMap (); 69 78 protected StandardPluginManager(final PluginRegistry aRegistry, 79 final PathResolver aPathResolver, 80 final PluginLifecycleHandler aLifecycleHandler) { 81 registry = aRegistry; 82 pathResolver = aPathResolver; 83 lifecycleHandler = aLifecycleHandler; 84 lifecycleHandler.init(this); 85 registryChangeListener = new RegistryChangeListener() { 86 public void registryChanged(final RegistryChangeData data) { 87 registryChangeHandler(data); 88 } 89 }; 90 registry.registerListener(registryChangeListener); 91 } 92 93 96 public PluginRegistry getRegistry() { 97 return registry; 98 } 99 100 103 public PathResolver getPathResolver() { 104 return pathResolver; 105 } 106 107 111 synchronized void registryChangeHandler( 112 final RegistryChangeData data) { 113 badPlugins.clear(); 114 for (Iterator it = data.removedPlugins().iterator(); it.hasNext();) { 115 String id = (String ) it.next(); 116 deactivatePlugin(id); 117 pathResolver.unregisterContext(id); 118 } 119 for (Iterator it = registry.getPluginDescriptors().iterator(); 120 it.hasNext();) { 121 PluginDescriptor idt = (PluginDescriptor) it.next(); 122 URL location = 123 (URL ) notRegisteredPluginLocations.remove(idt.getLocation()); 124 if (location != null) { 125 pathResolver.registerContext(idt, location); 126 } 127 } 128 for (Iterator it = registry.getPluginFragments().iterator(); 129 it.hasNext();) { 130 PluginFragment idt = (PluginFragment) it.next(); 131 URL location = 132 (URL ) notRegisteredPluginLocations.remove(idt.getLocation()); 133 if (location != null) { 134 pathResolver.registerContext(idt, location); 135 } 136 } 137 for (Iterator it = data.modifiedPlugins().iterator(); it.hasNext();) { 138 String id = (String ) it.next(); 139 if (activePlugins.containsKey(id)) { 140 deactivatePlugin(id); 141 try { 142 activatePlugin(id); 143 } catch (Exception e) { 144 log.error("failed activating modified plug-in " + id, e); } 146 } else { 147 PluginClassLoader clsLoader = 148 (PluginClassLoader) classLoaders.get(id); 149 if (clsLoader != null) { 150 notifyClassLoader(clsLoader); 151 } 152 } 153 } 154 } 155 156 170 public Map publishPlugins(final PluginLocation[] locations) 171 throws JpfException { 172 URL [] manifests = new URL [locations.length]; 173 for (int i = 0; i < manifests.length; i++) { 174 manifests[i] = locations[i].getManifestLocation(); 175 notRegisteredPluginLocations.put(manifests[i], 176 locations[i].getContextLocation()); 177 } 178 return registry.register(manifests); 179 } 180 181 188 public Plugin getPlugin(final String id) throws PluginLifecycleException { 189 Plugin result = (Plugin) activePlugins.get(id); 190 if (result != null) { 191 return result; 192 } 193 if (badPlugins.contains(id)) { 194 throw new IllegalArgumentException ("plug-in " + id + " disabled internally as it wasn't properly initialized"); } 197 if (disabledPlugins.contains(id)) { 198 throw new IllegalArgumentException ("plug-in " + id + " disabled externally"); } 201 PluginDescriptor descr = registry.getPluginDescriptor(id); 202 if (descr == null) { 203 throw new IllegalArgumentException ("unknown plug-in ID - " + id); } 205 return activatePlugin(descr); 206 } 207 208 213 public void activatePlugin(final String id) 214 throws PluginLifecycleException { 215 if (activePlugins.containsKey(id)) { 216 return; 217 } 218 if (badPlugins.contains(id)) { 219 throw new IllegalArgumentException ("plug-in " + id + " disabled internally as it wasn't properly initialized"); } 222 if (disabledPlugins.contains(id)) { 223 throw new IllegalArgumentException ("plug-in " + id + " disabled externally"); } 226 PluginDescriptor descr = registry.getPluginDescriptor(id); 227 if (descr == null) { 228 throw new IllegalArgumentException ("unknown plug-in ID - " + id); } 230 activatePlugin(descr); 231 } 232 233 241 public Plugin getPluginFor(final Object obj) { 242 if (obj == null) { 243 return null; 244 } 245 ClassLoader clsLoader; 246 if (obj instanceof Class ) { 247 clsLoader = ((Class ) obj).getClassLoader(); 248 } else if (obj instanceof ClassLoader ) { 249 clsLoader = (ClassLoader ) obj; 250 } else { 251 clsLoader = obj.getClass().getClassLoader(); 252 } 253 if (!(clsLoader instanceof PluginClassLoader)) { 254 return null; 255 } 256 PluginDescriptor descr = 257 ((PluginClassLoader) clsLoader).getPluginDescriptor(); 258 Plugin result = (Plugin) activePlugins.get(descr.getId()); 259 if (result != null) { 260 return result; 261 } 262 throw new IllegalStateException ("can't get plug-in " + descr); } 264 265 269 public boolean isPluginActivated(final PluginDescriptor descr) { 270 return activePlugins.containsKey(descr.getId()); 271 } 272 273 277 public boolean isBadPlugin(final PluginDescriptor descr) { 278 return badPlugins.contains(descr.getId()); 279 } 280 281 285 public boolean isPluginActivating(final PluginDescriptor descr) { 286 return activatingPlugins.contains(descr.getId()); 287 } 288 289 296 public PluginClassLoader getPluginClassLoader( 297 final PluginDescriptor descr) { 298 if (badPlugins.contains(descr.getId())) { 299 throw new IllegalArgumentException ("plug-in " + descr.getId() + " disabled internally as it wasn't properly initialized"); } 302 if (disabledPlugins.contains(descr.getId())) { 303 throw new IllegalArgumentException ("plug-in " + descr.getId() + " disabled externally"); } 306 PluginClassLoader result = 307 (PluginClassLoader) classLoaders.get(descr.getId()); 308 if (result != null) { 309 return result; 310 } 311 synchronized (this) { 312 result = (PluginClassLoader) classLoaders.get(descr.getId()); 313 if (result != null) { 314 return result; 315 } 316 result = lifecycleHandler.createPluginClassLoader(descr); 317 classLoaders.put(descr.getId(), result); 318 } 319 return result; 320 } 321 322 331 public synchronized void shutdown() { 332 log.debug("shutting down..."); dump(); 334 registry.unregisterListener(registryChangeListener); 335 List reversedLog = new ArrayList (activationLog); 336 Collections.reverse(reversedLog); 337 for (Iterator it = reversedLog.iterator(); it.hasNext();) { 338 String id = (String ) it.next(); 339 PluginDescriptor descr = registry.getPluginDescriptor(id); 340 if (descr == null) { 341 log.warn("can't find descriptor for plug-in " + id + " to deactivate plug-in", new Exception ( "fake exception to view stack trace")); continue; 345 } 346 deactivatePlugin(descr); 347 } 348 dump(); 349 classLoaders.clear(); 350 disabledPlugins.clear(); 351 listeners.clear(); 352 lifecycleHandler.dispose(); 353 log.info("shutdown done"); } 355 356 private synchronized Plugin activatePlugin(final PluginDescriptor descr) 357 throws PluginLifecycleException { 358 Plugin result = (Plugin) activePlugins.get(descr.getId()); 359 if (result != null) { 360 return result; 361 } 362 if (badPlugins.contains(descr.getId())) { 363 throw new IllegalArgumentException ("plug-in " + descr.getId() + " disabled as it wasn't properly initialized"); } 366 if (activatingPlugins.contains(descr.getId())) { 367 throw new PluginLifecycleException( 368 StandardObjectFactory.PACKAGE_NAME, 369 "pluginActivating", descr.getId()); } 371 activatingPlugins.add(descr.getId()); 372 try { 373 try { 374 checkPrerequisites(descr); 375 String pluginClassName = descr.getPluginClassName(); 376 if ((pluginClassName == null) 377 || (pluginClassName.trim().length() == 0)) { 378 result = new EmptyPlugin(); 379 } else { 380 result = lifecycleHandler.createPluginInstance(descr); 381 } 382 initPlugin(result, descr); 383 lifecycleHandler.beforePluginStart(result); 384 startPlugin(result); 385 } catch (PluginLifecycleException ple) { 386 badPlugins.add(descr.getId()); 387 classLoaders.remove(descr.getId()); 388 throw ple; 389 } catch (Exception e) { 390 badPlugins.add(descr.getId()); 391 classLoaders.remove(descr.getId()); 392 throw new PluginLifecycleException( 393 StandardObjectFactory.PACKAGE_NAME, 394 "pluginStartFailed", descr.getUniqueId(), e); } 396 activePlugins.put(descr.getId(), result); 397 activationLog.add(descr.getId()); 398 log.info("plug-in started - " + descr.getUniqueId()); fireEvent(result, true); 400 return result; 401 } finally { 402 activatingPlugins.remove(descr.getId()); 403 } 404 } 405 406 private void checkPrerequisites(final PluginDescriptor descr) 407 throws PluginLifecycleException { 408 for (Iterator it = descr.getPrerequisites().iterator(); it.hasNext();) { 409 PluginPrerequisite pre = (PluginPrerequisite) it.next(); 410 if (activatingPlugins.contains(pre.getPluginId())) { 411 log.warn("dependencies loop detected during " + "activation of plug-in " + descr, new Exception ( "fake exception to view stack trace")); continue; 415 } 416 if (badPlugins.contains(pre.getPluginId())) { 417 if (pre.isOptional()) { 418 continue; 419 } 420 throw new PluginLifecycleException( 421 StandardObjectFactory.PACKAGE_NAME, 422 "pluginPrerequisiteBad", new Object [] {descr.getId(), pre.getPluginId()}); 424 } 425 if (disabledPlugins.contains(pre.getPluginId())) { 426 if (pre.isOptional()) { 427 continue; 428 } 429 throw new PluginLifecycleException( 430 StandardObjectFactory.PACKAGE_NAME, 431 "pluginPrerequisiteDisabled", new Object [] {descr.getId(), pre.getPluginId()}); 433 } 434 if (!pre.matches()) { 435 if (pre.isOptional()) { 436 continue; 437 } 438 throw new PluginLifecycleException( 439 StandardObjectFactory.PACKAGE_NAME, 440 "pluginPrerequisiteNotMatches", new Object [] {descr.getId(), pre.getPluginId()}); 442 } 443 try { 444 activatePlugin(registry.getPluginDescriptor( 445 pre.getPluginId())); 446 } catch (PluginLifecycleException ple) { 447 if (pre.isOptional()) { 448 log.warn("failed activating optional plug-in from" + " prerequisite " + pre, ple); continue; 451 } 452 throw ple; 453 } 454 } 455 } 456 457 463 public void deactivatePlugin(final String id) { 464 if (!activePlugins.containsKey(id)) { 465 return; 466 } 467 PluginDescriptor descr = registry.getPluginDescriptor(id); 468 if (descr == null) { 469 throw new IllegalArgumentException ("unknown plug-in ID - " + id); } 471 Map dependingPluginsMap = new HashMap (); 473 for (Iterator it = registry.getDependingPlugins(descr).iterator(); 474 it.hasNext();) { 475 PluginDescriptor dependingPlugin = (PluginDescriptor) it.next(); 476 dependingPluginsMap.put(dependingPlugin.getId(), dependingPlugin); 477 } 478 List tobeDeactivated = new LinkedList (); 480 List reversedLog = new ArrayList (activationLog); 481 Collections.reverse(reversedLog); 482 for (Iterator it = reversedLog.iterator(); it.hasNext();) { 483 String pluginId = (String ) it.next(); 484 if (pluginId.equals(descr.getId())) { 485 tobeDeactivated.add(descr); 486 } else if (dependingPluginsMap.containsKey(pluginId)) { 487 tobeDeactivated.add(dependingPluginsMap.get(pluginId)); 488 } 489 } 490 for (Iterator it = tobeDeactivated.iterator(); it.hasNext();) { 492 deactivatePlugin((PluginDescriptor) it.next()); 493 } 494 dump(); 495 } 496 497 private synchronized void deactivatePlugin(final PluginDescriptor descr) { 498 Plugin plugin = (Plugin) activePlugins.remove(descr.getId()); 499 if (plugin != null) { 500 try { 501 if (plugin.isActive()) { 502 fireEvent(plugin, false); 503 stopPlugin(plugin); 504 lifecycleHandler.afterPluginStop(plugin); 505 log.info("plug-in stopped - " + descr.getUniqueId()); } else { 507 log.warn("plug-in " + descr.getUniqueId() + " is not active although present in active " + "plug-ins list", new Exception ( "fake exception to view stack trace")); } 512 } catch (Exception e) { 513 log.error("error while stopping plug-in " + descr.getUniqueId(), e); 515 } 516 } 517 PluginClassLoader clsLoader = 518 (PluginClassLoader) classLoaders.remove(descr.getId()); 519 if (clsLoader != null) { 520 disposeClassLoader(clsLoader); 521 } 522 badPlugins.remove(descr.getId()); 523 activationLog.remove(descr.getId()); 524 } 525 526 private void dump() { 527 if (!log.isDebugEnabled()) { 528 return; 529 } 530 StringBuffer buf = new StringBuffer ("PLUGIN MANAGER DUMP:\r\n"); buf.append("-------------- DUMP BEGIN -----------------\r\n"); buf.append("\tActive plug-ins: " + activePlugins.size()) .append("\r\n"); for (Iterator it = activePlugins.values().iterator(); it.hasNext();) { 535 buf.append("\t\t") .append(it.next()) 537 .append("\r\n"); } 539 buf.append("\tActivating plug-ins: " + activatingPlugins.size()).append("\r\n"); for (Iterator it = activatingPlugins.iterator(); it.hasNext();) { 542 buf.append("\t\t") .append(it.next()) 544 .append("\r\n"); } 546 buf.append("\tPlug-ins with instantiated class loaders: " + classLoaders.size()).append("\r\n"); for (Iterator it = classLoaders.keySet().iterator(); it.hasNext();) { 549 buf.append("\t\t") .append(it.next()) 551 .append("\r\n"); } 553 buf.append("\tDisabled plug-ins: " + disabledPlugins.size()) .append("\r\n"); for (Iterator it = disabledPlugins.iterator(); it.hasNext();) { 556 buf.append("\t\t") .append(it.next()) 558 .append("\r\n"); } 560 buf.append("\tBad plug-ins: " + badPlugins.size()) .append("\r\n"); for (Iterator it = badPlugins.iterator(); it.hasNext();) { 563 buf.append("\t\t") .append(it.next()) 565 .append("\r\n"); } 567 buf.append("\tActivation log: " + activationLog.size()) .append("\r\n"); for (Iterator it = activationLog.iterator(); it.hasNext();) { 570 buf.append("\t\t") .append(it.next()) 572 .append("\r\n"); } 574 buf.append("Memory TOTAL/FREE/MAX: ") .append(Runtime.getRuntime().totalMemory()) 576 .append("/") .append(Runtime.getRuntime().freeMemory()) 578 .append("/") .append(Runtime.getRuntime().maxMemory()) 580 .append("\r\n"); buf.append("-------------- DUMP END -----------------"); log.debug(buf.toString()); 583 } 584 585 597 public PluginDescriptor[] disablePlugin(final PluginDescriptor descr) { 598 List result = new LinkedList (); 599 if (!disabledPlugins.contains(descr.getId())) { 600 deactivatePlugin(descr); 601 fireEvent(descr, false); 602 disabledPlugins.add(descr.getId()); 603 result.add(descr); 604 } 605 for (Iterator it = registry.getDependingPlugins(descr).iterator(); 606 it.hasNext();) { 607 PluginDescriptor dependedPlugin = (PluginDescriptor) it.next(); 608 if (!disabledPlugins.contains(dependedPlugin.getId())) { 609 deactivatePlugin(dependedPlugin); 610 fireEvent(dependedPlugin, false); 611 disabledPlugins.add(dependedPlugin.getId()); 612 result.add(dependedPlugin); 613 } 614 } 615 return (PluginDescriptor[]) result.toArray( 616 new PluginDescriptor[result.size()]); 617 } 618 619 627 public PluginDescriptor[] enablePlugin(final PluginDescriptor descr, 628 final boolean includeDependings) { 629 List result = new LinkedList (); 630 if (disabledPlugins.contains(descr.getId())) { 631 disabledPlugins.remove(descr.getId()); 632 fireEvent(descr, true); 633 result.add(descr); 634 } 635 if (includeDependings) { 636 for (Iterator it = registry.getDependingPlugins(descr).iterator(); 637 it.hasNext();) { 638 PluginDescriptor dependedPlugin = (PluginDescriptor) it.next(); 639 if (disabledPlugins.contains(dependedPlugin.getId())) { 640 disabledPlugins.remove(dependedPlugin.getId()); 641 fireEvent(dependedPlugin, true); 642 result.add(dependedPlugin); 643 } 644 } 645 } 646 return (PluginDescriptor[]) result.toArray( 647 new PluginDescriptor[result.size()]); 648 } 649 650 654 public boolean isPluginEnabled(final PluginDescriptor descr) { 655 return !disabledPlugins.contains(descr.getId()); 656 } 657 658 664 public void registerListener(final EventListener listener) { 665 if (listeners.contains(listener)) { 666 throw new IllegalArgumentException ("listener " + listener + " already registered"); } 669 listeners.add(listener); 670 } 671 672 678 public void unregisterListener(final EventListener listener) { 679 if (!listeners.remove(listener)) { 680 log.warn("unknown listener " + listener); } 682 } 683 684 private void fireEvent(final Object data, final boolean on) { 685 if (listeners.isEmpty()) { 686 return; 687 } 688 EventListener[] arr = (EventListener[]) listeners.toArray( 690 new EventListener[listeners.size()]); 691 if (data instanceof PluginDescriptor) { 694 PluginDescriptor descr = (PluginDescriptor) data; 695 if (on) { 696 if (log.isDebugEnabled()) { 697 log.debug("propagating \"pluginEnabled\" event for " + descr); 699 } 700 for (int i = 0; i < arr.length; i++) { 701 arr[i].pluginEnabled(descr); 702 } 703 } else { 704 if (log.isDebugEnabled()) { 705 log.debug("propagating \"pluginDisabled\" event for " + descr); 707 } 708 for (int i = 0; i < arr.length; i++) { 709 arr[i].pluginDisabled(descr); 710 } 711 } 712 } else { 713 Plugin plugin = (Plugin) data; 714 if (on) { 715 if (log.isDebugEnabled()) { 716 log.debug("propagating \"pluginActivated\" event for " + plugin); 718 } 719 for (int i = 0; i < arr.length; i++) { 720 arr[i].pluginActivated(plugin); 721 } 722 } else { 723 if (log.isDebugEnabled()) { 724 log.debug("propagating \"pluginDeactivated\" event for " + plugin); 726 } 727 for (int i = 0; i < arr.length; i++) { 728 arr[i].pluginDeactivated(plugin); 729 } 730 } 731 } 732 } 733 734 static final class EmptyPlugin extends Plugin { 735 738 protected void doStart() throws Exception { 739 } 741 742 745 protected void doStop() throws Exception { 746 } 748 } 749 } 750 | Popular Tags |