1 package com.atlassian.plugin.manager;
2
3 import com.atlassian.plugin.ModuleCompleteKey;
4 import com.atlassian.plugin.ModuleDescriptor;
5 import com.atlassian.plugin.ModuleDescriptorFactory;
6 import com.atlassian.plugin.Plugin;
7 import com.atlassian.plugin.PluginAccessor;
8 import com.atlassian.plugin.PluginArtifact;
9 import com.atlassian.plugin.PluginController;
10 import com.atlassian.plugin.PluginException;
11 import com.atlassian.plugin.PluginInstaller;
12 import com.atlassian.plugin.PluginParseException;
13 import com.atlassian.plugin.PluginRestartState;
14 import com.atlassian.plugin.PluginState;
15 import com.atlassian.plugin.PluginSystemLifecycle;
16 import com.atlassian.plugin.StateAware;
17 import com.atlassian.plugin.classloader.PluginsClassLoader;
18 import com.atlassian.plugin.descriptors.UnloadableModuleDescriptor;
19 import com.atlassian.plugin.descriptors.UnloadableModuleDescriptorFactory;
20 import com.atlassian.plugin.event.PluginEventListener;
21 import com.atlassian.plugin.event.PluginEventManager;
22 import com.atlassian.plugin.event.events.PluginDisabledEvent;
23 import com.atlassian.plugin.event.events.PluginEnabledEvent;
24 import com.atlassian.plugin.event.events.PluginFrameworkShutdownEvent;
25 import com.atlassian.plugin.event.events.PluginFrameworkStartedEvent;
26 import com.atlassian.plugin.event.events.PluginFrameworkStartingEvent;
27 import com.atlassian.plugin.event.events.PluginModuleDisabledEvent;
28 import com.atlassian.plugin.event.events.PluginModuleEnabledEvent;
29 import com.atlassian.plugin.event.events.PluginRefreshedEvent;
30 import com.atlassian.plugin.event.events.PluginUpgradedEvent;
31 import com.atlassian.plugin.impl.UnloadablePlugin;
32 import com.atlassian.plugin.impl.UnloadablePluginFactory;
33 import com.atlassian.plugin.loaders.DynamicPluginLoader;
34 import com.atlassian.plugin.loaders.PluginLoader;
35 import com.atlassian.plugin.parsers.DescriptorParserFactory;
36 import com.atlassian.plugin.predicate.EnabledModulePredicate;
37 import com.atlassian.plugin.predicate.EnabledPluginPredicate;
38 import com.atlassian.plugin.predicate.ModuleDescriptorOfClassPredicate;
39 import com.atlassian.plugin.predicate.ModuleDescriptorOfTypePredicate;
40 import com.atlassian.plugin.predicate.ModuleDescriptorPredicate;
41 import com.atlassian.plugin.predicate.ModuleOfClassPredicate;
42 import com.atlassian.plugin.predicate.PluginPredicate;
43 import com.atlassian.plugin.util.PluginUtils;
44 import com.atlassian.plugin.util.WaitUntil;
45 import com.atlassian.plugin.util.collect.CollectionUtil;
46 import static com.atlassian.plugin.util.collect.CollectionUtil.filter;
47 import static com.atlassian.plugin.util.collect.CollectionUtil.toList;
48 import static com.atlassian.plugin.util.collect.CollectionUtil.transform;
49 import com.atlassian.plugin.util.collect.Function;
50 import com.atlassian.plugin.util.collect.Predicate;
51 import org.apache.commons.collections.CollectionUtils;
52 import org.apache.commons.logging.Log;
53 import org.apache.commons.logging.LogFactory;
54 import org.apache.commons.lang.time.StopWatch;
55
56 import java.io.InputStream;
57 import java.util.ArrayList;
58 import java.util.Collection;
59 import java.util.Collections;
60 import java.util.HashMap;
61 import java.util.HashSet;
62 import java.util.LinkedList;
63 import java.util.List;
64 import java.util.Map;
65 import java.util.Set;
66 import java.util.TreeSet;
67 import java.util.concurrent.ConcurrentHashMap;
68
69
70
71
72
73
74
75
76
77
78
79
80 public class DefaultPluginManager implements PluginController, PluginAccessor, PluginSystemLifecycle
81 {
82 private static final Log log = LogFactory.getLog(DefaultPluginManager.class);
83
84 private final List<PluginLoader> pluginLoaders;
85 private final PluginPersistentStateStore store;
86 private final ModuleDescriptorFactory moduleDescriptorFactory;
87 private final PluginsClassLoader classLoader;
88 private final Map<String, Plugin> plugins = new ConcurrentHashMap<String, Plugin>();
89 private final PluginEventManager pluginEventManager;
90 private final PluginEnabler pluginEnabler = new PluginEnabler(this, this);
91
92 private final StateTracker tracker = new StateTracker();
93
94
95
96
97 private PluginInstaller pluginInstaller;
98
99
100
101
102 private final Map<Plugin, PluginLoader> pluginToPluginLoader = new HashMap<Plugin, PluginLoader>();
103
104 public DefaultPluginManager(final PluginPersistentStateStore store, final List<PluginLoader> pluginLoaders, final ModuleDescriptorFactory moduleDescriptorFactory, final PluginEventManager pluginEventManager)
105 {
106 if (store == null)
107 {
108 throw new IllegalArgumentException("PluginPersistentStateStore must not be null.");
109 }
110 if (pluginLoaders == null)
111 {
112 throw new IllegalArgumentException("Plugin Loaders list must not be null.");
113 }
114 if (moduleDescriptorFactory == null)
115 {
116 throw new IllegalArgumentException("ModuleDescriptorFactory must not be null.");
117 }
118 if (pluginEventManager == null)
119 {
120 throw new IllegalArgumentException("PluginEventManager must not be null.");
121 }
122 this.pluginLoaders = pluginLoaders;
123 this.store = store;
124 this.moduleDescriptorFactory = moduleDescriptorFactory;
125 this.pluginEventManager = pluginEventManager;
126 this.pluginEventManager.register(this);
127 classLoader = new PluginsClassLoader(this);
128 }
129
130
131
132
133
134
135
136 public void init() throws PluginParseException
137 {
138 tracker.setState(StateTracker.State.STARTING);
139 StopWatch stopWatch = new StopWatch();
140 stopWatch.start();
141 log.info("Initialising the plugin system");
142 pluginEventManager.broadcast(new PluginFrameworkStartingEvent(this, this));
143 for (final PluginLoader loader : pluginLoaders)
144 {
145 if (loader == null)
146 {
147 continue;
148 }
149
150 final Collection<Plugin> possiblePluginsToLoad = loader.loadAllPlugins(moduleDescriptorFactory);
151 final Collection<Plugin> pluginsToLoad = new ArrayList<Plugin>();
152 for (final Plugin plugin : possiblePluginsToLoad)
153 {
154 if (getState().getPluginRestartState(plugin.getKey()) == PluginRestartState.REMOVE)
155 {
156 loader.removePlugin(plugin);
157
158
159 removeStateFromStore(getStore(), plugin);
160 }
161 else
162 {
163 pluginsToLoad.add(plugin);
164 }
165 }
166 addPlugins(loader, pluginsToLoad);
167 }
168
169 final DefaultPluginPersistentState currentState = new DefaultPluginPersistentState(getStore().load());
170 currentState.clearPluginRestartState();
171 getStore().save(currentState);
172
173 pluginEventManager.broadcast(new PluginFrameworkStartedEvent(this, this));
174 stopWatch.stop();
175 log.info("Plugin system started in " + stopWatch);
176 tracker.setState(StateTracker.State.STARTED);
177 }
178
179
180
181
182
183
184 public void shutdown()
185 {
186 tracker.setState(StateTracker.State.SHUTTING_DOWN);
187 log.info("Shutting down the plugin system");
188 pluginEventManager.broadcast(new PluginFrameworkShutdownEvent(this, this));
189 tracker.setState(StateTracker.State.SHUTDOWN);
190 plugins.clear();
191 pluginEventManager.unregister(this);
192 }
193
194 @PluginEventListener
195 public void onPluginRefresh(final PluginRefreshedEvent event)
196 {
197 final Plugin plugin = event.getPlugin();
198
199
200 final List<ModuleDescriptor<?>> moduleDescriptors = new ArrayList<ModuleDescriptor<?>>(plugin.getModuleDescriptors());
201 Collections.reverse(moduleDescriptors);
202
203 for (final ModuleDescriptor<?> module : moduleDescriptors)
204 {
205
206
207
208 if (isPluginModuleEnabled(module.getCompleteKey()))
209 {
210 publishModuleDisabledEvents(module);
211 }
212 }
213
214
215 classLoader.notifyPluginOrModuleEnabled();
216 enablePluginModules(plugin);
217 pluginEventManager.broadcast(new PluginEnabledEvent(plugin));
218 }
219
220
221
222
223
224
225
226 public void setPluginInstaller(final PluginInstaller pluginInstaller)
227 {
228 this.pluginInstaller = pluginInstaller;
229 }
230
231 protected final PluginPersistentStateStore getStore()
232 {
233 return store;
234 }
235
236 public String installPlugin(final PluginArtifact pluginArtifact) throws PluginParseException
237 {
238 final String key = validatePlugin(pluginArtifact);
239 pluginInstaller.installPlugin(key, pluginArtifact);
240 scanForNewPlugins();
241 return key;
242 }
243
244
245
246
247
248
249
250
251
252
253 String validatePlugin(final PluginArtifact pluginArtifact) throws PluginParseException
254 {
255 boolean foundADynamicPluginLoader = false;
256 for (final PluginLoader loader : pluginLoaders)
257 {
258 if (loader instanceof DynamicPluginLoader)
259 {
260 foundADynamicPluginLoader = true;
261 final String key = ((DynamicPluginLoader) loader).canLoad(pluginArtifact);
262 if (key != null)
263 {
264 return key;
265 }
266 }
267 }
268
269 if (!foundADynamicPluginLoader)
270 {
271 throw new IllegalStateException("Should be at least one DynamicPluginLoader in the plugin loader list");
272 }
273 throw new PluginParseException("Jar " + pluginArtifact.getName() + " is not a valid plugin");
274 }
275
276 public int scanForNewPlugins() throws PluginParseException
277 {
278 int numberFound = 0;
279
280 for (final PluginLoader loader : pluginLoaders)
281 {
282 if (loader != null)
283 {
284 if (loader.supportsAddition())
285 {
286 final List<Plugin> pluginsToAdd = new ArrayList<Plugin>();
287 for (Plugin plugin : loader.addFoundPlugins(moduleDescriptorFactory))
288 {
289 final Plugin oldPlugin = plugins.get(plugin.getKey());
290
291
292 if (!(plugin instanceof UnloadablePlugin))
293 {
294 if (PluginUtils.doesPluginRequireRestart(plugin))
295 {
296 if (oldPlugin == null)
297 {
298 final DefaultPluginPersistentState currentState = new DefaultPluginPersistentState(getStore().load());
299 currentState.setPluginRestartState(plugin.getKey(), PluginRestartState.INSTALL);
300 getStore().save(currentState);
301
302 final UnloadablePlugin unloadablePlugin = new UnloadablePlugin("Plugin requires a restart of the application");
303 unloadablePlugin.setKey(plugin.getKey());
304 plugin = unloadablePlugin;
305 }
306 else
307 {
308 final DefaultPluginPersistentState currentState = new DefaultPluginPersistentState(getStore().load());
309 currentState.setPluginRestartState(plugin.getKey(), PluginRestartState.UPGRADE);
310 getStore().save(currentState);
311 continue;
312 }
313 }
314
315
316 else if ((oldPlugin != null) && PluginUtils.doesPluginRequireRestart(oldPlugin))
317 {
318 final DefaultPluginPersistentState currentState = new DefaultPluginPersistentState(getStore().load());
319 currentState.setPluginRestartState(plugin.getKey(), PluginRestartState.UPGRADE);
320 getStore().save(currentState);
321 continue;
322 }
323 pluginsToAdd.add(plugin);
324 }
325
326 }
327 addPlugins(loader, pluginsToAdd);
328 numberFound = pluginsToAdd.size();
329 }
330 }
331 }
332
333 return numberFound;
334 }
335
336
337
338
339
340
341 public void uninstall(final Plugin plugin) throws PluginException
342 {
343 if (PluginUtils.doesPluginRequireRestart(plugin))
344 {
345 ensurePluginAndLoaderSupportsUninstall(plugin);
346 final DefaultPluginPersistentState currentState = new DefaultPluginPersistentState(getStore().load());
347 currentState.setPluginRestartState(plugin.getKey(), PluginRestartState.REMOVE);
348 getStore().save(currentState);
349 }
350 else
351 {
352 unloadPlugin(plugin);
353
354
355 removeStateFromStore(getStore(), plugin);
356 }
357 }
358
359 protected void removeStateFromStore(final PluginPersistentStateStore stateStore, final Plugin plugin)
360 {
361 final DefaultPluginPersistentState currentState = new DefaultPluginPersistentState(stateStore.load());
362 currentState.removeState(plugin.getKey());
363 for (final ModuleDescriptor<?> moduleDescriptor : plugin.getModuleDescriptors())
364 {
365 currentState.removeState(moduleDescriptor.getCompleteKey());
366 }
367 stateStore.save(currentState);
368 }
369
370
371
372
373
374
375
376
377 protected void unloadPlugin(final Plugin plugin) throws PluginException
378 {
379 final PluginLoader loader = ensurePluginAndLoaderSupportsUninstall(plugin);
380
381 if (isPluginEnabled(plugin.getKey()))
382 {
383 notifyPluginDisabled(plugin);
384 }
385
386 notifyUninstallPlugin(plugin);
387 if (loader != null)
388 {
389 removePluginFromLoader(plugin);
390 }
391
392 plugins.remove(plugin.getKey());
393 }
394
395 private PluginLoader ensurePluginAndLoaderSupportsUninstall(final Plugin plugin)
396 {
397 if (!plugin.isUninstallable())
398 {
399 throw new PluginException("Plugin is not uninstallable: " + plugin.getKey());
400 }
401
402 final PluginLoader loader = pluginToPluginLoader.get(plugin);
403
404 if ((loader != null) && !loader.supportsRemoval())
405 {
406 throw new PluginException("Not uninstalling plugin - loader doesn't allow removal. Plugin: " + plugin.getKey());
407 }
408 return loader;
409 }
410
411 private void removePluginFromLoader(final Plugin plugin) throws PluginException
412 {
413 if (plugin.isDeleteable())
414 {
415 final PluginLoader pluginLoader = pluginToPluginLoader.get(plugin);
416 pluginLoader.removePlugin(plugin);
417 }
418
419 pluginToPluginLoader.remove(plugin);
420 }
421
422 protected void notifyUninstallPlugin(final Plugin plugin)
423 {
424 classLoader.notifyUninstallPlugin(plugin);
425
426 for (final ModuleDescriptor<?> descriptor : plugin.getModuleDescriptors())
427 {
428 descriptor.destroy(plugin);
429 }
430 }
431
432 protected PluginPersistentState getState()
433 {
434 return getStore().load();
435 }
436
437
438
439
440 @Deprecated
441 protected void addPlugin(final PluginLoader loader, final Plugin plugin) throws PluginParseException
442 {
443 addPlugins(loader, Collections.singletonList(plugin));
444 }
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460 protected void addPlugins(final PluginLoader loader, final Collection<Plugin> pluginsToInstall) throws PluginParseException
461 {
462 final Set<Plugin> pluginsToEnable = new HashSet<Plugin>();
463
464
465 for (final Plugin plugin : new TreeSet<Plugin>(pluginsToInstall))
466 {
467 boolean pluginUpgraded = false;
468
469 if (plugins.containsKey(plugin.getKey()))
470 {
471 final Plugin existingPlugin = plugins.get(plugin.getKey());
472 if (plugin.compareTo(existingPlugin) >= 0)
473 {
474 try
475 {
476 updatePlugin(existingPlugin, plugin);
477 pluginUpgraded = true;
478 }
479 catch (final PluginException e)
480 {
481 throw new PluginParseException(
482 "Duplicate plugin found (installed version is the same or older) and could not be unloaded: '" + plugin.getKey() + "'", e);
483 }
484 }
485 else
486 {
487
488 if (log.isDebugEnabled())
489 {
490 log.debug("Duplicate plugin found (installed version is newer): '" + plugin.getKey() + "'");
491 }
492
493 continue;
494 }
495 }
496
497 plugin.install();
498 if (getState().isEnabled(plugin))
499 {
500 pluginsToEnable.add(plugin);
501 }
502
503 if (pluginUpgraded)
504 {
505 pluginEventManager.broadcast(new PluginUpgradedEvent(plugin));
506 }
507 plugins.put(plugin.getKey(), plugin);
508 pluginToPluginLoader.put(plugin, loader);
509 }
510
511
512 pluginEnabler.enable(pluginsToEnable);
513
514
515 for (final Plugin plugin : pluginsToInstall)
516 {
517 if (plugin.getPluginState() == PluginState.ENABLED)
518 {
519
520 enablePluginModules(plugin);
521 pluginEventManager.broadcast(new PluginEnabledEvent(plugin));
522 }
523 }
524 }
525
526
527
528
529
530
531
532
533
534 protected void updatePlugin(final Plugin oldPlugin, final Plugin newPlugin) throws PluginException
535 {
536 if (!oldPlugin.getKey().equals(newPlugin.getKey()))
537 {
538 throw new IllegalArgumentException("New plugin must have the same key as the old plugin");
539 }
540
541 if (log.isInfoEnabled())
542 {
543 log.info("Updating plugin '" + oldPlugin + "' to '" + newPlugin + "'");
544 }
545
546
547
548 final Map<String, Boolean> oldPluginState = new HashMap<String, Boolean>(getState().getPluginStateMap(oldPlugin));
549
550 if (log.isDebugEnabled())
551 {
552 log.debug("Uninstalling old plugin: " + oldPlugin);
553 }
554 uninstall(oldPlugin);
555 if (log.isDebugEnabled())
556 {
557 log.debug("Plugin uninstalled '" + oldPlugin + "', preserving old state");
558 }
559
560
561 final Set<String> newModuleKeys = new HashSet<String>();
562 newModuleKeys.add(newPlugin.getKey());
563
564 for (final ModuleDescriptor<?> moduleDescriptor : newPlugin.getModuleDescriptors())
565 {
566 newModuleKeys.add(moduleDescriptor.getCompleteKey());
567 }
568
569
570 CollectionUtils.filter(oldPluginState.keySet(), new org.apache.commons.collections.Predicate()
571 {
572 public boolean evaluate(final Object o)
573 {
574 return newModuleKeys.contains(o);
575 }
576 });
577
578
579 final DefaultPluginPersistentState currentState = new DefaultPluginPersistentState(getStore().load());
580 currentState.addState(oldPluginState);
581 getStore().save(currentState);
582 }
583
584 public Collection<Plugin> getPlugins()
585 {
586 return plugins.values();
587 }
588
589
590
591
592
593 public Collection<Plugin> getPlugins(final PluginPredicate pluginPredicate)
594 {
595 return toList(filter(getPlugins(), new Predicate<Plugin>()
596 {
597 public boolean evaluate(final Plugin plugin)
598 {
599 return pluginPredicate.matches(plugin);
600 }
601 }));
602 }
603
604
605
606
607 public Collection<Plugin> getEnabledPlugins()
608 {
609 return getPlugins(new EnabledPluginPredicate(this));
610 }
611
612
613
614
615
616 public <M> Collection<M> getModules(final ModuleDescriptorPredicate<M> moduleDescriptorPredicate)
617 {
618 final Collection<ModuleDescriptor<M>> moduleDescriptors = getModuleDescriptors(moduleDescriptorPredicate);
619 return getModules(moduleDescriptors);
620 }
621
622
623
624
625
626 public <M> Collection<ModuleDescriptor<M>> getModuleDescriptors(final ModuleDescriptorPredicate<M> moduleDescriptorPredicate)
627 {
628 final List<ModuleDescriptor<M>> moduleDescriptors = getModuleDescriptorsList(getPlugins());
629 return toList(filter(moduleDescriptors, new Predicate<ModuleDescriptor<M>>()
630 {
631 public boolean evaluate(final ModuleDescriptor<M> input)
632 {
633 return moduleDescriptorPredicate.matches(input);
634 }
635 }));
636 }
637
638
639
640
641
642
643
644
645
646
647 private <M> List<ModuleDescriptor<M>> getModuleDescriptorsList(final Collection<Plugin> plugins)
648 {
649
650 final List<ModuleDescriptor<M>> moduleDescriptors = new LinkedList<ModuleDescriptor<M>>();
651 for (final Plugin plugin : plugins)
652 {
653 final Collection<ModuleDescriptor<?>> descriptors = plugin.getModuleDescriptors();
654 for (final ModuleDescriptor<?> moduleDescriptor : descriptors)
655 {
656 @SuppressWarnings("unchecked")
657 final ModuleDescriptor<M> typedDescriptor = (ModuleDescriptor<M>) moduleDescriptor;
658 moduleDescriptors.add(typedDescriptor);
659 }
660 }
661 return moduleDescriptors;
662 }
663
664
665
666
667
668
669
670
671
672 private <M> List<M> getModules(final Iterable<ModuleDescriptor<M>> moduleDescriptors)
673 {
674 final Set<String> pluginsToDisable = new HashSet<String>();
675 final List<M> modules = transform(moduleDescriptors, new Function<ModuleDescriptor<M>, M>()
676 {
677 public M get(final ModuleDescriptor<M> input)
678 {
679 M result = null;
680 try
681 {
682 result = input.getModule();
683 }
684 catch (final RuntimeException ex)
685 {
686 log.error("Exception when retrieving plugin module " + input.getKey() + ", will disable plugin " + input.getPlugin().getKey(), ex);
687 pluginsToDisable.add(input.getPlugin().getKey());
688 }
689 return result;
690 }
691 });
692
693 for (final String badPluginKey : pluginsToDisable)
694 {
695 disablePlugin(badPluginKey);
696 }
697 return modules;
698 }
699
700 public Plugin getPlugin(final String key)
701 {
702 return plugins.get(key);
703 }
704
705 public Plugin getEnabledPlugin(final String pluginKey)
706 {
707 if (!isPluginEnabled(pluginKey))
708 {
709 return null;
710 }
711 return getPlugin(pluginKey);
712 }
713
714 public ModuleDescriptor<?> getPluginModule(final String completeKey)
715 {
716 final ModuleCompleteKey key = new ModuleCompleteKey(completeKey);
717 final Plugin plugin = getPlugin(key.getPluginKey());
718
719 if (plugin == null)
720 {
721 return null;
722 }
723 return plugin.getModuleDescriptor(key.getModuleKey());
724 }
725
726 public ModuleDescriptor<?> getEnabledPluginModule(final String completeKey)
727 {
728 final ModuleCompleteKey key = new ModuleCompleteKey(completeKey);
729
730
731 if (!isPluginModuleEnabled(completeKey))
732 {
733 return null;
734 }
735
736 return getEnabledPlugin(key.getPluginKey()).getModuleDescriptor(key.getModuleKey());
737 }
738
739
740
741
742 public <M> List<M> getEnabledModulesByClass(final Class<M> moduleClass)
743 {
744 return getModules(getEnabledModuleDescriptorsByModuleClass(moduleClass));
745 }
746
747
748
749
750
751 @Deprecated
752 public <M> List<M> getEnabledModulesByClassAndDescriptor(final Class<ModuleDescriptor<M>>[] descriptorClasses, final Class<M> moduleClass)
753 {
754 final Iterable<ModuleDescriptor<M>> moduleDescriptors = filterModuleDescriptors(getEnabledModuleDescriptorsByModuleClass(moduleClass),
755 new ModuleDescriptorOfClassPredicate<M>(descriptorClasses));
756
757 return getModules(moduleDescriptors);
758 }
759
760
761
762
763
764 @Deprecated
765 public <M> List<M> getEnabledModulesByClassAndDescriptor(final Class<ModuleDescriptor<M>> descriptorClass, final Class<M> moduleClass)
766 {
767 final Iterable<ModuleDescriptor<M>> moduleDescriptors = getEnabledModuleDescriptorsByModuleClass(moduleClass);
768 return getModules(filterModuleDescriptors(moduleDescriptors, new ModuleDescriptorOfClassPredicate<M>(descriptorClass)));
769 }
770
771
772
773
774
775
776
777 private <M> Collection<ModuleDescriptor<M>> getEnabledModuleDescriptorsByModuleClass(final Class<M> moduleClass)
778 {
779 Iterable<ModuleDescriptor<M>> moduleDescriptors = getModuleDescriptorsList(getEnabledPlugins());
780 moduleDescriptors = filterModuleDescriptors(moduleDescriptors, new ModuleOfClassPredicate<M>(moduleClass));
781 moduleDescriptors = filterModuleDescriptors(moduleDescriptors, new EnabledModulePredicate<M>(this));
782
783 return toList(moduleDescriptors);
784 }
785
786 public <D extends ModuleDescriptor<?>> List<D> getEnabledModuleDescriptorsByClass(final Class<D> descriptorClazz)
787 {
788 return getEnabledModuleDescriptorsByClass(descriptorClazz, false);
789 }
790
791
792
793
794
795
796
797
798 public <D extends ModuleDescriptor<?>> List<D> getEnabledModuleDescriptorsByClass(final Class<D> descriptorClazz, final boolean verbose)
799 {
800 final List<D> result = new LinkedList<D>();
801 for (final Plugin plugin : plugins.values())
802 {
803
804 if (!isPluginEnabled(plugin.getKey()))
805 {
806 if (verbose && log.isInfoEnabled())
807 {
808 log.info("Plugin [" + plugin.getKey() + "] is disabled.");
809 }
810 continue;
811 }
812
813 for (final ModuleDescriptor<?> module : plugin.getModuleDescriptors())
814 {
815 if (descriptorClazz.isInstance(module) && isPluginModuleEnabled(module.getCompleteKey()))
816 {
817 @SuppressWarnings("unchecked")
818 final D moduleDescriptor = (D) module;
819 result.add(moduleDescriptor);
820 }
821 else
822 {
823 if (verbose && log.isInfoEnabled())
824 {
825 log.info("Module [" + module.getCompleteKey() + "] is disabled.");
826 }
827 }
828 }
829 }
830
831 return result;
832 }
833
834
835
836
837
838 @Deprecated
839 public <M> List<ModuleDescriptor<M>> getEnabledModuleDescriptorsByType(final String type) throws PluginParseException, IllegalArgumentException
840 {
841 Iterable<ModuleDescriptor<M>> moduleDescriptors = getModuleDescriptorsList(getEnabledPlugins());
842 moduleDescriptors = filterModuleDescriptors(moduleDescriptors, new ModuleDescriptorOfTypePredicate<M>(moduleDescriptorFactory, type));
843 moduleDescriptors = filterModuleDescriptors(moduleDescriptors, new EnabledModulePredicate<M>(this));
844 return toList(moduleDescriptors);
845 }
846
847
848
849
850
851
852
853 private static <M> Iterable<ModuleDescriptor<M>> filterModuleDescriptors(final Iterable<ModuleDescriptor<M>> moduleDescriptors, final ModuleDescriptorPredicate<M> moduleDescriptorPredicate)
854 {
855 return CollectionUtil.filter(moduleDescriptors, new Predicate<ModuleDescriptor<M>>()
856 {
857 public boolean evaluate(final ModuleDescriptor<M> input)
858 {
859 return moduleDescriptorPredicate.matches(input);
860 }
861 });
862 }
863
864 public void enablePlugin(final String key)
865 {
866 if (key == null)
867 {
868 throw new IllegalArgumentException("You must specify a plugin key to disable.");
869 }
870
871 if (!plugins.containsKey(key))
872 {
873 if (log.isInfoEnabled())
874 {
875 log.info("No plugin was found for key '" + key + "'. Not enabling.");
876 }
877
878 return;
879 }
880
881 final Plugin plugin = plugins.get(key);
882
883 if (!plugin.getPluginInformation().satisfiesMinJavaVersion())
884 {
885 log.error("Minimum Java version of '" + plugin.getPluginInformation().getMinJavaVersion() + "' was not satisfied for module '" + key + "'. Not enabling.");
886 return;
887 }
888
889 pluginEnabler.enableRecursively(plugin);
890
891 if (plugin.getPluginState().equals(PluginState.ENABLED))
892 {
893 enablePluginState(plugin, getStore());
894 notifyPluginEnabled(plugin);
895 }
896 }
897
898 protected void enablePluginState(final Plugin plugin, final PluginPersistentStateStore stateStore)
899 {
900 final DefaultPluginPersistentState currentState = new DefaultPluginPersistentState(stateStore.load());
901 currentState.setEnabled(plugin, true);
902 stateStore.save(currentState);
903 }
904
905
906
907
908
909
910
911
912 protected void notifyPluginEnabled(final Plugin plugin)
913 {
914 plugin.enable();
915 classLoader.notifyPluginOrModuleEnabled();
916 enablePluginModules(plugin);
917 pluginEventManager.broadcast(new PluginEnabledEvent(plugin));
918 }
919
920
921
922
923
924
925 private void enablePluginModules(final Plugin plugin)
926 {
927 for (final ModuleDescriptor<?> descriptor : plugin.getModuleDescriptors())
928 {
929 if (!isPluginModuleEnabled(descriptor.getCompleteKey()))
930 {
931 if (log.isDebugEnabled())
932 {
933 log.debug("Plugin module is disabled, so not enabling ModuleDescriptor '" + descriptor.getName() + "'.");
934 }
935 continue;
936 }
937
938 try
939 {
940 notifyModuleEnabled(descriptor);
941 }
942 catch (final Throwable exception)
943 {
944 log.error("There was an error loading the descriptor '" + descriptor.getName() + "' of plugin '" + plugin.getKey() + "'. Disabling.",
945 exception);
946 replacePluginWithUnloadablePlugin(plugin, descriptor, exception);
947 }
948 }
949 classLoader.notifyPluginOrModuleEnabled();
950 }
951
952 public void disablePlugin(final String key)
953 {
954 if (key == null)
955 {
956 throw new IllegalArgumentException("You must specify a plugin key to disable.");
957 }
958
959 if (!plugins.containsKey(key))
960 {
961 if (log.isInfoEnabled())
962 {
963 log.info("No plugin was found for key '" + key + "'. Not disabling.");
964 }
965
966 return;
967 }
968
969 final Plugin plugin = plugins.get(key);
970
971 notifyPluginDisabled(plugin);
972 disablePluginState(plugin, getStore());
973 }
974
975 protected void disablePluginState(final Plugin plugin, final PluginPersistentStateStore stateStore)
976 {
977 final DefaultPluginPersistentState currentState = new DefaultPluginPersistentState(stateStore.load());
978 currentState.setEnabled(plugin, false);
979 stateStore.save(currentState);
980 }
981
982 protected void notifyPluginDisabled(final Plugin plugin)
983 {
984 final List<ModuleDescriptor<?>> moduleDescriptors = new ArrayList<ModuleDescriptor<?>>(plugin.getModuleDescriptors());
985 Collections.reverse(moduleDescriptors);
986
987 for (final ModuleDescriptor<?> module : moduleDescriptors)
988 {
989
990
991
992 if (isPluginModuleEnabled(module.getCompleteKey()))
993 {
994 publishModuleDisabledEvents(module);
995 }
996 }
997
998
999 plugin.disable();
1000 pluginEventManager.broadcast(new PluginDisabledEvent(plugin));
1001 }
1002
1003 public void disablePluginModule(final String completeKey)
1004 {
1005 if (completeKey == null)
1006 {
1007 throw new IllegalArgumentException("You must specify a plugin module key to disable.");
1008 }
1009
1010 final ModuleDescriptor<?> module = getPluginModule(completeKey);
1011
1012 if (module == null)
1013 {
1014 if (log.isInfoEnabled())
1015 {
1016 log.info("Returned module for key '" + completeKey + "' was null. Not disabling.");
1017 }
1018
1019 return;
1020 }
1021 disablePluginModuleState(module, getStore());
1022 notifyModuleDisabled(module);
1023 }
1024
1025 protected void disablePluginModuleState(final ModuleDescriptor<?> module, final PluginPersistentStateStore stateStore)
1026 {
1027 final DefaultPluginPersistentState currentState = new DefaultPluginPersistentState(stateStore.load());
1028 currentState.setEnabled(module, false);
1029 stateStore.save(currentState);
1030 }
1031
1032 protected void notifyModuleDisabled(final ModuleDescriptor<?> module)
1033 {
1034 publishModuleDisabledEvents(module);
1035 }
1036
1037 private void publishModuleDisabledEvents(final ModuleDescriptor<?> module)
1038 {
1039 if (log.isDebugEnabled())
1040 {
1041 log.debug("Disabling " + module.getKey());
1042 }
1043
1044 if (module instanceof StateAware)
1045 {
1046 ((StateAware) module).disabled();
1047 }
1048
1049 pluginEventManager.broadcast(new PluginModuleDisabledEvent(module));
1050 }
1051
1052 public void enablePluginModule(final String completeKey)
1053 {
1054 if (completeKey == null)
1055 {
1056 throw new IllegalArgumentException("You must specify a plugin module key to disable.");
1057 }
1058
1059 final ModuleDescriptor<?> module = getPluginModule(completeKey);
1060
1061 if (module == null)
1062 {
1063 if (log.isInfoEnabled())
1064 {
1065 log.info("Returned module for key '" + completeKey + "' was null. Not enabling.");
1066 }
1067
1068 return;
1069 }
1070
1071 if (!module.satisfiesMinJavaVersion())
1072 {
1073 log.error("Minimum Java version of '" + module.getMinJavaVersion() + "' was not satisfied for module '" + completeKey + "'. Not enabling.");
1074 return;
1075 }
1076 enablePluginModuleState(module, getStore());
1077 notifyModuleEnabled(module);
1078 }
1079
1080 protected void enablePluginModuleState(final ModuleDescriptor<?> module, final PluginPersistentStateStore stateStore)
1081 {
1082 final DefaultPluginPersistentState currentState = new DefaultPluginPersistentState(stateStore.load());
1083 currentState.setEnabled(module, true);
1084 stateStore.save(currentState);
1085 }
1086
1087 protected void notifyModuleEnabled(final ModuleDescriptor<?> module)
1088 {
1089 if (log.isDebugEnabled())
1090 {
1091 log.debug("Enabling " + module.getKey());
1092 }
1093 classLoader.notifyPluginOrModuleEnabled();
1094 if (module instanceof StateAware)
1095 {
1096 ((StateAware) module).enabled();
1097 }
1098 pluginEventManager.broadcast(new PluginModuleEnabledEvent(module));
1099 }
1100
1101 public boolean isPluginModuleEnabled(final String completeKey)
1102 {
1103
1104 if (completeKey == null)
1105 {
1106 return false;
1107 }
1108 final ModuleCompleteKey key = new ModuleCompleteKey(completeKey);
1109
1110 final ModuleDescriptor<?> pluginModule = getPluginModule(completeKey);
1111 return isPluginEnabled(key.getPluginKey()) && (pluginModule != null) && getState().isEnabled(pluginModule);
1112 }
1113
1114
1115
1116
1117
1118
1119
1120 public boolean isPluginEnabled(final String key)
1121 {
1122 final Plugin plugin = plugins.get(key);
1123
1124 return (plugin != null) && getState().isEnabled(plugin) && (plugin.getPluginState() == PluginState.ENABLED);
1125 }
1126
1127 public InputStream getDynamicResourceAsStream(final String name)
1128 {
1129 return getClassLoader().getResourceAsStream(name);
1130 }
1131
1132 public Class<?> getDynamicPluginClass(final String className) throws ClassNotFoundException
1133 {
1134 return getClassLoader().loadClass(className);
1135 }
1136
1137 public PluginsClassLoader getClassLoader()
1138 {
1139 return classLoader;
1140 }
1141
1142 public InputStream getPluginResourceAsStream(final String pluginKey, final String resourcePath)
1143 {
1144 final Plugin plugin = getEnabledPlugin(pluginKey);
1145 if (plugin == null)
1146 {
1147 log.error("Attempted to retreive resource " + resourcePath + " for non-existent or inactive plugin " + pluginKey);
1148 return null;
1149 }
1150
1151 return plugin.getResourceAsStream(resourcePath);
1152 }
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162 private UnloadablePlugin replacePluginWithUnloadablePlugin(final Plugin plugin, final ModuleDescriptor<?> descriptor, final Throwable throwable)
1163 {
1164 final UnloadableModuleDescriptor unloadableDescriptor = UnloadableModuleDescriptorFactory.createUnloadableModuleDescriptor(plugin,
1165 descriptor, throwable);
1166 final UnloadablePlugin unloadablePlugin = UnloadablePluginFactory.createUnloadablePlugin(plugin, unloadableDescriptor);
1167
1168 unloadablePlugin.setUninstallable(plugin.isUninstallable());
1169 unloadablePlugin.setDeletable(plugin.isDeleteable());
1170 plugins.put(plugin.getKey(), unloadablePlugin);
1171
1172
1173 disablePluginState(plugin, getStore());
1174 return unloadablePlugin;
1175 }
1176
1177 public boolean isSystemPlugin(final String key)
1178 {
1179 final Plugin plugin = getPlugin(key);
1180 return (plugin != null) && plugin.isSystemPlugin();
1181 }
1182
1183 public PluginRestartState getPluginRestartState(final String key)
1184 {
1185 return getState().getPluginRestartState(key);
1186 }
1187
1188
1189
1190
1191 @Deprecated
1192 public void setDescriptorParserFactory(final DescriptorParserFactory descriptorParserFactory)
1193 {}
1194
1195 private static class PluginFinishedEnablingCondition implements WaitUntil.WaitCondition
1196 {
1197 private final Plugin plugin;
1198
1199 public PluginFinishedEnablingCondition(final Plugin plugin)
1200 {
1201 this.plugin = plugin;
1202 }
1203
1204 public boolean isFinished()
1205 {
1206 return plugin.getPluginState() != PluginState.ENABLING;
1207 }
1208
1209 public String getWaitMessage()
1210 {
1211 return "Waiting until plugin " + plugin + " is enabled";
1212 }
1213 }
1214 }