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