001: /*****************************************************************************
002: * Java Plug-in Framework (JPF)
003: * Copyright (C) 2004-2007 Dmitry Olshansky
004: *
005: * This library is free software; you can redistribute it and/or
006: * modify it under the terms of the GNU Lesser General Public
007: * License as published by the Free Software Foundation; either
008: * version 2.1 of the License, or (at your option) any later version.
009: *
010: * This library is distributed in the hope that it will be useful,
011: * but WITHOUT ANY WARRANTY; without even the implied warranty of
012: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
013: * Lesser General Public License for more details.
014: *
015: * You should have received a copy of the GNU Lesser General Public
016: * License along with this library; if not, write to the Free Software
017: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
018: *****************************************************************************/package org.java.plugin.standard;
019:
020: import java.net.URL;
021: import java.util.ArrayList;
022: import java.util.Collections;
023: import java.util.HashMap;
024: import java.util.HashSet;
025: import java.util.LinkedList;
026: import java.util.List;
027: import java.util.Map;
028: import java.util.Set;
029:
030: import org.apache.commons.logging.Log;
031: import org.apache.commons.logging.LogFactory;
032: import org.java.plugin.JpfException;
033: import org.java.plugin.PathResolver;
034: import org.java.plugin.Plugin;
035: import org.java.plugin.PluginClassLoader;
036: import org.java.plugin.PluginLifecycleException;
037: import org.java.plugin.PluginManager;
038: import org.java.plugin.registry.Identity;
039: import org.java.plugin.registry.PluginDescriptor;
040: import org.java.plugin.registry.PluginFragment;
041: import org.java.plugin.registry.PluginPrerequisite;
042: import org.java.plugin.registry.PluginRegistry;
043: import org.java.plugin.registry.PluginRegistry.RegistryChangeData;
044: import org.java.plugin.registry.PluginRegistry.RegistryChangeListener;
045:
046: /**
047: * Standard implementation of plug-in manager.
048: *
049: * @version $Id: StandardPluginManager.java,v 1.8 2007/04/07 12:41:01 ddimon Exp $
050: */
051: public final class StandardPluginManager extends PluginManager {
052: Log log = LogFactory.getLog(getClass());
053: private final PathResolver pathResolver;
054: private final PluginRegistry registry;
055: private final PluginLifecycleHandler lifecycleHandler;
056: private final Map<String, Plugin> activePlugins = new HashMap<String, Plugin>();
057: private final Set<String> activatingPlugins = new HashSet<String>();
058: private final Set<String> badPlugins = new HashSet<String>();
059: private final List<String> activationLog = new LinkedList<String>();
060: private final Map<String, PluginClassLoader> classLoaders = new HashMap<String, PluginClassLoader>();
061: private final Set<String> disabledPlugins = new HashSet<String>();
062: private final List<EventListener> listeners = Collections
063: .synchronizedList(new LinkedList<EventListener>());
064: private RegistryChangeListener registryChangeListener;
065: private Map<String, URL> notRegisteredPluginLocations = new HashMap<String, URL>();
066:
067: /**
068: * Creates instance of plug-in manager for given registry, path resolver and
069: * life cycle handler.
070: *
071: * @param aRegistry
072: * some implementation of plug-in registry interface
073: * @param aPathResolver
074: * some implementation of path resolver interface
075: * @param aLifecycleHandler
076: * an implementation of plug-in life cycle handler
077: *
078: * @see StandardObjectFactory
079: */
080: protected StandardPluginManager(final PluginRegistry aRegistry,
081: final PathResolver aPathResolver,
082: final PluginLifecycleHandler aLifecycleHandler) {
083: registry = aRegistry;
084: pathResolver = aPathResolver;
085: lifecycleHandler = aLifecycleHandler;
086: lifecycleHandler.init(this );
087: registryChangeListener = new RegistryChangeListener() {
088: public void registryChanged(final RegistryChangeData data) {
089: registryChangeHandler(data);
090: }
091: };
092: registry.registerListener(registryChangeListener);
093: }
094:
095: /**
096: * @see org.java.plugin.PluginManager#getRegistry()
097: */
098: @Override
099: public PluginRegistry getRegistry() {
100: return registry;
101: }
102:
103: /**
104: * @see org.java.plugin.PluginManager#getPathResolver()
105: */
106: @Override
107: public PathResolver getPathResolver() {
108: return pathResolver;
109: }
110:
111: /**
112: * Method to handle plug-in registry change events.
113: *
114: * @param data
115: * registry change data holder
116: */
117: synchronized void registryChangeHandler(
118: final RegistryChangeData data) {
119: badPlugins.clear();
120: for (String id : data.removedPlugins()) {
121: deactivatePlugin(id);
122: pathResolver.unregisterContext(id);
123: }
124: URL location;
125: for (PluginDescriptor idt : registry.getPluginDescriptors()) {
126: location = notRegisteredPluginLocations.remove(idt
127: .getLocation().toExternalForm());
128: if (location != null) {
129: pathResolver.registerContext(idt, location);
130: }
131: }
132: for (PluginFragment idt : registry.getPluginFragments()) {
133: location = notRegisteredPluginLocations.remove(idt
134: .getLocation().toExternalForm());
135: if (location != null) {
136: pathResolver.registerContext(idt, location);
137: }
138: }
139: for (String id : data.modifiedPlugins()) {
140: if (activePlugins.containsKey(id)) {
141: deactivatePlugin(id);
142: try {
143: activatePlugin(id);
144: } catch (Exception e) {
145: log
146: .error(
147: "failed activating modified plug-in " + id, e); //$NON-NLS-1$
148: }
149: } else {
150: PluginClassLoader clsLoader = classLoaders.get(id);
151: if (clsLoader != null) {
152: notifyClassLoader(clsLoader);
153: }
154: }
155: }
156: }
157:
158: /**
159: * Registers plug-ins and their locations with this plug-in manager. You
160: * should use this method to register new plug-ins to make them available
161: * for activation with this manager instance (compare this to
162: * {@link PluginRegistry#register(URL[])} method that just makes plug-in's
163: * meta-data available for reading and doesn't "know" where are things
164: * actually located).
165: *
166: * @param locations
167: * plug-in locations data
168: * @return map where keys are manifest URL's and values are registered
169: * plug-ins or plug-in fragments, URL's for unprocessed manifests
170: * are not included
171: * @throws JpfException
172: * if given plug-ins can't be registered or published (optional
173: * behavior)
174: */
175: @Override
176: public Map<String, Identity> publishPlugins(
177: final PluginLocation[] locations) throws JpfException {
178: LinkedList<URL> manifests = new LinkedList<URL>();
179: for (PluginLocation location : locations) {
180: URL manifest = location.getManifestLocation();
181: manifests.add(manifest);
182: notRegisteredPluginLocations.put(manifest.toExternalForm(),
183: location.getContextLocation());
184: }
185: return registry.register(manifests.toArray(new URL[manifests
186: .size()]));
187: }
188:
189: /**
190: * Looks for plug-in with given ID and activates it if it is not activated
191: * yet. Note that this method will never return <code>null</code>.
192: *
193: * @param id
194: * plug-in ID
195: * @return found plug-in
196: * @throws PluginLifecycleException
197: * if plug-in can't be found or activated
198: */
199: @Override
200: public Plugin getPlugin(final String id)
201: throws PluginLifecycleException {
202: Plugin result = activePlugins.get(id);
203: if (result != null) {
204: return result;
205: }
206: if (badPlugins.contains(id)) {
207: throw new IllegalArgumentException(
208: "plug-in " + id //$NON-NLS-1$
209: + " disabled internally as it wasn't properly initialized"); //$NON-NLS-1$
210: }
211: if (disabledPlugins.contains(id)) {
212: throw new IllegalArgumentException("plug-in " + id //$NON-NLS-1$
213: + " disabled externally"); //$NON-NLS-1$
214: }
215: PluginDescriptor descr = registry.getPluginDescriptor(id);
216: if (descr == null) {
217: throw new IllegalArgumentException(
218: "unknown plug-in ID - " + id); //$NON-NLS-1$
219: }
220: return activatePlugin(descr);
221: }
222:
223: /**
224: * Activates plug-in with given ID if it is not activated yet.
225: *
226: * @param id
227: * plug-in ID
228: * @throws PluginLifecycleException
229: * if plug-in can't be found or activated
230: */
231: @Override
232: public void activatePlugin(final String id)
233: throws PluginLifecycleException {
234: if (activePlugins.containsKey(id)) {
235: return;
236: }
237: if (badPlugins.contains(id)) {
238: throw new IllegalArgumentException(
239: "plug-in " + id //$NON-NLS-1$
240: + " disabled internally as it wasn't properly initialized"); //$NON-NLS-1$
241: }
242: if (disabledPlugins.contains(id)) {
243: throw new IllegalArgumentException("plug-in " + id //$NON-NLS-1$
244: + " disabled externally"); //$NON-NLS-1$
245: }
246: PluginDescriptor descr = registry.getPluginDescriptor(id);
247: if (descr == null) {
248: throw new IllegalArgumentException(
249: "unknown plug-in ID - " + id); //$NON-NLS-1$
250: }
251: activatePlugin(descr);
252: }
253:
254: /**
255: * Looks for plug-in, given object belongs to.
256: *
257: * @param obj
258: * any object that maybe belongs to some plug-in
259: * @return plug-in or <code>null</code> if given object doesn't belong to
260: * any plug-in (possibly it is part of "host" application) and thus
261: * doesn't managed by the Framework directly or indirectly
262: */
263: @Override
264: public Plugin getPluginFor(final Object obj) {
265: if (obj == null) {
266: return null;
267: }
268: ClassLoader clsLoader;
269: if (obj instanceof Class) {
270: clsLoader = ((Class) obj).getClassLoader();
271: } else if (obj instanceof ClassLoader) {
272: clsLoader = (ClassLoader) obj;
273: } else {
274: clsLoader = obj.getClass().getClassLoader();
275: }
276: if (!(clsLoader instanceof PluginClassLoader)) {
277: return null;
278: }
279: PluginDescriptor descr = ((PluginClassLoader) clsLoader)
280: .getPluginDescriptor();
281: Plugin result = activePlugins.get(descr.getId());
282: if (result != null) {
283: return result;
284: }
285: throw new IllegalStateException("can't get plug-in " + descr); //$NON-NLS-1$
286: }
287:
288: /**
289: * @param descr
290: * plug-in descriptor
291: * @return <code>true</code> if plug-in with given descriptor is activated
292: */
293: @Override
294: public boolean isPluginActivated(final PluginDescriptor descr) {
295: return activePlugins.containsKey(descr.getId());
296: }
297:
298: /**
299: * @param descr
300: * plug-in descriptor
301: * @return <code>true</code> if plug-in disabled as it's activation fails
302: */
303: @Override
304: public boolean isBadPlugin(final PluginDescriptor descr) {
305: return badPlugins.contains(descr.getId());
306: }
307:
308: /**
309: * @param descr
310: * plug-in descriptor
311: * @return <code>true</code> if plug-in is currently activating
312: */
313: @Override
314: public boolean isPluginActivating(final PluginDescriptor descr) {
315: return activatingPlugins.contains(descr.getId());
316: }
317:
318: /**
319: * Returns instance of plug-in's class loader and not tries to activate
320: * plug-in. Use this method if you need to get access to plug-in resources
321: * and don't want to cause plug-in activation.
322: *
323: * @param descr
324: * plug-in descriptor
325: * @return class loader instance for plug-in with given descriptor
326: */
327: @Override
328: public PluginClassLoader getPluginClassLoader(
329: final PluginDescriptor descr) {
330: if (badPlugins.contains(descr.getId())) {
331: throw new IllegalArgumentException(
332: "plug-in " + descr.getId() //$NON-NLS-1$
333: + " disabled internally as it wasn't properly initialized"); //$NON-NLS-1$
334: }
335: if (disabledPlugins.contains(descr.getId())) {
336: throw new IllegalArgumentException(
337: "plug-in " + descr.getId() //$NON-NLS-1$
338: + " disabled externally"); //$NON-NLS-1$
339: }
340: PluginClassLoader result = classLoaders.get(descr.getId());
341: if (result != null) {
342: return result;
343: }
344: synchronized (this ) {
345: result = classLoaders.get(descr.getId());
346: if (result != null) {
347: return result;
348: }
349: result = lifecycleHandler.createPluginClassLoader(descr);
350: classLoaders.put(descr.getId(), result);
351: }
352: return result;
353: }
354:
355: /**
356: * Shuts down the framework. <br>
357: * Calling this method will deactivate all active plug-ins in order, reverse
358: * to the order they was activated. It also releases all resources allocated
359: * by this manager (class loaders, plug-in descriptors etc.). All disabled
360: * plug-ins will be marked as "enabled", all registered event listeners will
361: * be unregistered.
362: */
363: @Override
364: public synchronized void shutdown() {
365: log.debug("shutting down..."); //$NON-NLS-1$
366: dump();
367: registry.unregisterListener(registryChangeListener);
368: final List<String> reversedLog = new ArrayList<String>(
369: activationLog);
370: Collections.reverse(reversedLog);
371: for (String id : reversedLog) {
372: PluginDescriptor descr = registry.getPluginDescriptor(id);
373: if (descr == null) {
374: log.warn("can't find descriptor for plug-in " + id //$NON-NLS-1$
375: + " to deactivate plug-in", new Exception( //$NON-NLS-1$
376: "fake exception to view stack trace")); //$NON-NLS-1$
377: continue;
378: }
379: deactivatePlugin(descr);
380: }
381: dump();
382: classLoaders.clear();
383: disabledPlugins.clear();
384: listeners.clear();
385: lifecycleHandler.dispose();
386: log.info("shutdown done"); //$NON-NLS-1$
387: }
388:
389: private synchronized Plugin activatePlugin(
390: final PluginDescriptor descr)
391: throws PluginLifecycleException {
392: Plugin result = activePlugins.get(descr.getId());
393: if (result != null) {
394: return result;
395: }
396: if (badPlugins.contains(descr.getId())) {
397: throw new IllegalArgumentException(
398: "plug-in " + descr.getId() //$NON-NLS-1$
399: + " disabled as it wasn't properly initialized"); //$NON-NLS-1$
400: }
401: if (activatingPlugins.contains(descr.getId())) {
402: throw new PluginLifecycleException(
403: StandardObjectFactory.PACKAGE_NAME,
404: "pluginActivating", descr.getId()); //$NON-NLS-1$
405: }
406: activatingPlugins.add(descr.getId());
407: try {
408: try {
409: checkPrerequisites(descr);
410: String pluginClassName = descr.getPluginClassName();
411: if ((pluginClassName == null)
412: || (pluginClassName.trim().length() == 0)) {
413: result = new EmptyPlugin();
414: } else {
415: result = lifecycleHandler
416: .createPluginInstance(descr);
417: }
418: initPlugin(result, descr);
419: lifecycleHandler.beforePluginStart(result);
420: startPlugin(result);
421: } catch (PluginLifecycleException ple) {
422: badPlugins.add(descr.getId());
423: classLoaders.remove(descr.getId());
424: throw ple;
425: } catch (Exception e) {
426: badPlugins.add(descr.getId());
427: classLoaders.remove(descr.getId());
428: throw new PluginLifecycleException(
429: StandardObjectFactory.PACKAGE_NAME,
430: "pluginStartFailed", descr.getUniqueId(), e); //$NON-NLS-1$
431: }
432: activePlugins.put(descr.getId(), result);
433: activationLog.add(descr.getId());
434: log.info("plug-in started - " + descr.getUniqueId() //$NON-NLS-1$
435: + " (active/total: " + activePlugins.size() //$NON-NLS-1$
436: + " of " //$NON-NLS-1$
437: + registry.getPluginDescriptors().size() + ")"); //$NON-NLS-1$
438: fireEvent(result, true);
439: return result;
440: } finally {
441: activatingPlugins.remove(descr.getId());
442: }
443: }
444:
445: private void checkPrerequisites(final PluginDescriptor descr)
446: throws PluginLifecycleException {
447: for (PluginPrerequisite pre : descr.getPrerequisites()) {
448: if (activatingPlugins.contains(pre.getPluginId())) {
449: log
450: .warn(
451: "dependencies loop detected during " //$NON-NLS-1$
452: + "activation of plug-in " + descr, new Exception( //$NON-NLS-1$
453: "fake exception to view stack trace")); //$NON-NLS-1$
454: continue;
455: }
456: if (badPlugins.contains(pre.getPluginId())) {
457: if (pre.isOptional()) {
458: continue;
459: }
460: throw new PluginLifecycleException(
461: StandardObjectFactory.PACKAGE_NAME,
462: "pluginPrerequisiteBad", //$NON-NLS-1$
463: new Object[] { descr.getId(), pre.getPluginId() });
464: }
465: if (disabledPlugins.contains(pre.getPluginId())) {
466: if (pre.isOptional()) {
467: continue;
468: }
469: throw new PluginLifecycleException(
470: StandardObjectFactory.PACKAGE_NAME,
471: "pluginPrerequisiteDisabled", //$NON-NLS-1$
472: new Object[] { descr.getId(), pre.getPluginId() });
473: }
474: if (!pre.matches()) {
475: if (pre.isOptional()) {
476: continue;
477: }
478: throw new PluginLifecycleException(
479: StandardObjectFactory.PACKAGE_NAME,
480: "pluginPrerequisiteNotMatches", //$NON-NLS-1$
481: new Object[] { descr.getId(), pre.getPluginId() });
482: }
483: try {
484: activatePlugin(registry.getPluginDescriptor(pre
485: .getPluginId()));
486: } catch (PluginLifecycleException ple) {
487: if (pre.isOptional()) {
488: log.warn("failed activating optional plug-in from" //$NON-NLS-1$
489: + " prerequisite " + pre, ple); //$NON-NLS-1$
490: continue;
491: }
492: throw ple;
493: }
494: }
495: }
496:
497: /**
498: * Deactivates plug-in with given ID if it has been successfully activated
499: * before. Note that this method will effectively deactivate all plug-ins
500: * that depend on the given plug-in.
501: *
502: * @param id
503: * plug-in ID
504: */
505: @Override
506: public void deactivatePlugin(final String id) {
507: if (!activePlugins.containsKey(id)) {
508: return;
509: }
510: PluginDescriptor descr = registry.getPluginDescriptor(id);
511: if (descr == null) {
512: throw new IllegalArgumentException(
513: "unknown plug-in ID - " + id); //$NON-NLS-1$
514: }
515: // Collect depending plug-ins
516: final Map<String, PluginDescriptor> dependingPluginsMap = new HashMap<String, PluginDescriptor>();
517: for (PluginDescriptor dependingPlugin : registry
518: .getDependingPlugins(descr)) {
519: dependingPluginsMap.put(dependingPlugin.getId(),
520: dependingPlugin);
521: }
522: // Prepare list of plug-ins to be deactivated in correct order
523: final List<PluginDescriptor> tobeDeactivated = new LinkedList<PluginDescriptor>();
524: final List<String> reversedLog = new ArrayList<String>(
525: activationLog);
526: Collections.reverse(reversedLog);
527: for (String pluginId : reversedLog) {
528: if (pluginId.equals(descr.getId())) {
529: tobeDeactivated.add(descr);
530: } else if (dependingPluginsMap.containsKey(pluginId)) {
531: tobeDeactivated.add(dependingPluginsMap.get(pluginId));
532: }
533: }
534: // Deactivate plug-ins
535: for (PluginDescriptor descriptor : tobeDeactivated) {
536: deactivatePlugin(descriptor);
537: }
538: dump();
539: }
540:
541: private synchronized void deactivatePlugin(
542: final PluginDescriptor descr) {
543: Plugin plugin = activePlugins.remove(descr.getId());
544: if (plugin != null) {
545: try {
546: if (plugin.isActive()) {
547: fireEvent(plugin, false);
548: stopPlugin(plugin);
549: lifecycleHandler.afterPluginStop(plugin);
550: log.info("plug-in stopped - " + descr.getUniqueId() //$NON-NLS-1$
551: + " (active/total: " + activePlugins.size() //$NON-NLS-1$
552: + " of " //$NON-NLS-1$
553: + registry.getPluginDescriptors().size()
554: + ")"); //$NON-NLS-1$
555: } else {
556: log
557: .warn(
558: "plug-in " + descr.getUniqueId() //$NON-NLS-1$
559: + " is not active although present in active " //$NON-NLS-1$
560: + "plug-ins list", new Exception( //$NON-NLS-1$
561: "fake exception to view stack trace")); //$NON-NLS-1$
562: }
563: } catch (Exception e) {
564: log.error("error while stopping plug-in " //$NON-NLS-1$
565: + descr.getUniqueId(), e);
566: }
567: }
568: PluginClassLoader clsLoader = classLoaders
569: .remove(descr.getId());
570: if (clsLoader != null) {
571: disposeClassLoader(clsLoader);
572: }
573: badPlugins.remove(descr.getId());
574: activationLog.remove(descr.getId());
575: }
576:
577: private void dump() {
578: if (!log.isDebugEnabled()) {
579: return;
580: }
581: StringBuilder buf = new StringBuilder(
582: "PLUGIN MANAGER DUMP:\r\n"); //$NON-NLS-1$
583: buf.append("-------------- DUMP BEGIN -----------------\r\n"); //$NON-NLS-1$
584: buf.append("\tActive plug-ins: " + activePlugins.size()) //$NON-NLS-1$
585: .append("\r\n"); //$NON-NLS-1$
586: for (Plugin plugin : activePlugins.values()) {
587: buf.append("\t\t") //$NON-NLS-1$
588: .append(plugin).append("\r\n"); //$NON-NLS-1$
589: }
590: buf.append("\tActivating plug-ins: " //$NON-NLS-1$
591: + activatingPlugins.size()).append("\r\n"); //$NON-NLS-1$
592: for (String s : activatingPlugins) {
593: buf.append("\t\t") //$NON-NLS-1$
594: .append(s).append("\r\n"); //$NON-NLS-1$
595: }
596: buf.append("\tPlug-ins with instantiated class loaders: " //$NON-NLS-1$
597: + classLoaders.size()).append("\r\n"); //$NON-NLS-1$
598: for (String s : classLoaders.keySet()) {
599: buf.append("\t\t") //$NON-NLS-1$
600: .append(s).append("\r\n"); //$NON-NLS-1$
601: }
602: buf.append("\tDisabled plug-ins: " + disabledPlugins.size()) //$NON-NLS-1$
603: .append("\r\n"); //$NON-NLS-1$
604: for (String s : disabledPlugins) {
605: buf.append("\t\t") //$NON-NLS-1$
606: .append(s).append("\r\n"); //$NON-NLS-1$
607: }
608: buf.append("\tBad plug-ins: " + badPlugins.size()) //$NON-NLS-1$
609: .append("\r\n"); //$NON-NLS-1$
610: for (String s : badPlugins) {
611: buf.append("\t\t") //$NON-NLS-1$
612: .append(s).append("\r\n"); //$NON-NLS-1$
613: }
614: buf.append("\tActivation log: " + activationLog.size()) //$NON-NLS-1$
615: .append("\r\n"); //$NON-NLS-1$
616: for (String s : activationLog) {
617: buf.append("\t\t") //$NON-NLS-1$
618: .append(s).append("\r\n"); //$NON-NLS-1$
619: }
620: buf.append("Memory TOTAL/FREE/MAX: ") //$NON-NLS-1$
621: .append(Runtime.getRuntime().totalMemory()).append("/") //$NON-NLS-1$
622: .append(Runtime.getRuntime().freeMemory()).append("/") //$NON-NLS-1$
623: .append(Runtime.getRuntime().maxMemory())
624: .append("\r\n"); //$NON-NLS-1$
625: buf.append("-------------- DUMP END -----------------"); //$NON-NLS-1$
626: log.debug(buf.toString());
627: }
628:
629: /**
630: * Disables plug-in (with dependencies) in this manager instance. Disabled
631: * plug-in can't be activated although it may be valid and successfully
632: * registered with plug-in registry. Before disabling, plug-in will be
633: * deactivated if it was successfully activated. <br>
634: * Be careful with this method as it can effectively disable large set of
635: * inter-depending plug-ins and your application may become unstable or even
636: * disabled as whole.
637: *
638: * @param descr
639: * descriptor of plug-in to be disabled
640: * @return descriptors of plug-ins that was actually disabled
641: */
642: @Override
643: public PluginDescriptor[] disablePlugin(final PluginDescriptor descr) {
644: final List<PluginDescriptor> result = new LinkedList<PluginDescriptor>();
645: if (!disabledPlugins.contains(descr.getId())) {
646: deactivatePlugin(descr);
647: fireEvent(descr, false);
648: disabledPlugins.add(descr.getId());
649: result.add(descr);
650: }
651:
652: for (PluginDescriptor dependedPlugin : registry
653: .getDependingPlugins(descr)) {
654: if (!disabledPlugins.contains(dependedPlugin.getId())) {
655: deactivatePlugin(dependedPlugin);
656: fireEvent(dependedPlugin, false);
657: disabledPlugins.add(dependedPlugin.getId());
658: result.add(dependedPlugin);
659: }
660: }
661: return result.toArray(new PluginDescriptor[result.size()]);
662: }
663:
664: /**
665: * Enables plug-in (or plug-ins) in this manager instance.
666: *
667: * @param descr
668: * descriptor of plug-in to be enabled
669: * @param includeDependings
670: * if <code>true</code>, depending plug-ins will be also
671: * enabled
672: * @return descriptors of plug-ins that was actually enabled
673: * @see #disablePlugin(PluginDescriptor)
674: */
675: @Override
676: public PluginDescriptor[] enablePlugin(
677: final PluginDescriptor descr,
678: final boolean includeDependings) {
679: final List<PluginDescriptor> result = new LinkedList<PluginDescriptor>();
680: if (disabledPlugins.contains(descr.getId())) {
681: disabledPlugins.remove(descr.getId());
682: fireEvent(descr, true);
683: result.add(descr);
684: }
685: if (includeDependings) {
686: for (PluginDescriptor dependedPlugin : registry
687: .getDependingPlugins(descr)) {
688: if (disabledPlugins.contains(dependedPlugin.getId())) {
689: disabledPlugins.remove(dependedPlugin.getId());
690: fireEvent(dependedPlugin, true);
691: result.add(dependedPlugin);
692: }
693: }
694: }
695: return result.toArray(new PluginDescriptor[result.size()]);
696: }
697:
698: /**
699: * @param descr
700: * plug-in descriptor
701: * @return <code>true</code> if given plug-in is disabled in this manager
702: */
703: @Override
704: public boolean isPluginEnabled(final PluginDescriptor descr) {
705: return !disabledPlugins.contains(descr.getId());
706: }
707:
708: /**
709: * Registers plug-in manager event listener. If given listener has been
710: * registered before, this method will throw an
711: * {@link IllegalArgumentException}.
712: *
713: * @param listener
714: * new manager event listener
715: */
716: @Override
717: public void registerListener(final EventListener listener) {
718: if (listeners.contains(listener)) {
719: throw new IllegalArgumentException("listener " + listener //$NON-NLS-1$
720: + " already registered"); //$NON-NLS-1$
721: }
722: listeners.add(listener);
723: }
724:
725: /**
726: * Unregisters manager event listener. If given listener hasn't been
727: * registered before, this method will throw an
728: * {@link IllegalArgumentException}.
729: *
730: * @param listener
731: * registered listener
732: */
733: @Override
734: public void unregisterListener(final EventListener listener) {
735: if (!listeners.remove(listener)) {
736: log.warn("unknown listener " + listener); //$NON-NLS-1$
737: }
738: }
739:
740: private void fireEvent(final Object data, final boolean on) {
741: if (listeners.isEmpty()) {
742: return;
743: }
744: // make local copy
745: EventListener[] arr = listeners
746: .toArray(new EventListener[listeners.size()]);
747: // propagate event basing on given data type and on/off flag
748: // NB: revise this logic if EventListener members are changed
749: if (data instanceof PluginDescriptor) {
750: PluginDescriptor descr = (PluginDescriptor) data;
751: if (on) {
752: if (log.isDebugEnabled()) {
753: log
754: .debug("propagating \"pluginEnabled\" event for " //$NON-NLS-1$
755: + descr);
756: }
757: for (EventListener element : arr) {
758: element.pluginEnabled(descr);
759: }
760: } else {
761: if (log.isDebugEnabled()) {
762: log
763: .debug("propagating \"pluginDisabled\" event for " //$NON-NLS-1$
764: + descr);
765: }
766: for (EventListener element : arr) {
767: element.pluginDisabled(descr);
768: }
769: }
770: } else {
771: Plugin plugin = (Plugin) data;
772: if (on) {
773: if (log.isDebugEnabled()) {
774: log
775: .debug("propagating \"pluginActivated\" event for " //$NON-NLS-1$
776: + plugin);
777: }
778: for (EventListener element : arr) {
779: element.pluginActivated(plugin);
780: }
781: } else {
782: if (log.isDebugEnabled()) {
783: log
784: .debug("propagating \"pluginDeactivated\" event for " //$NON-NLS-1$
785: + plugin);
786: }
787: for (EventListener element : arr) {
788: element.pluginDeactivated(plugin);
789: }
790: }
791: }
792: }
793:
794: static final class EmptyPlugin extends Plugin {
795: /**
796: * @see org.java.plugin.Plugin#doStart()
797: */
798: @Override
799: protected void doStart() throws Exception {
800: // no-op
801: }
802:
803: /**
804: * @see org.java.plugin.Plugin#doStop()
805: */
806: @Override
807: protected void doStop() throws Exception {
808: // no-op
809: }
810: }
811: }
|