1 package com.atlassian.plugin.manager;
2
3 import com.atlassian.annotations.ExperimentalApi;
4 import com.atlassian.plugin.ModuleCompleteKey;
5 import com.atlassian.plugin.ModuleDescriptor;
6 import com.atlassian.plugin.ModuleDescriptorFactory;
7 import com.atlassian.plugin.Plugin;
8 import com.atlassian.plugin.PluginAccessor;
9 import com.atlassian.plugin.PluginArtifact;
10 import com.atlassian.plugin.PluginController;
11 import com.atlassian.plugin.PluginException;
12 import com.atlassian.plugin.PluginInstaller;
13 import com.atlassian.plugin.PluginParseException;
14 import com.atlassian.plugin.PluginRestartState;
15 import com.atlassian.plugin.PluginState;
16 import com.atlassian.plugin.PluginSystemLifecycle;
17 import com.atlassian.plugin.RevertablePluginInstaller;
18 import com.atlassian.plugin.StateAware;
19 import com.atlassian.plugin.classloader.PluginsClassLoader;
20 import com.atlassian.plugin.descriptors.CannotDisable;
21 import com.atlassian.plugin.descriptors.UnloadableModuleDescriptor;
22 import com.atlassian.plugin.descriptors.UnloadableModuleDescriptorFactory;
23 import com.atlassian.plugin.event.NotificationException;
24 import com.atlassian.plugin.event.PluginEventListener;
25 import com.atlassian.plugin.event.PluginEventManager;
26 import com.atlassian.plugin.event.events.BeforePluginDisabledEvent;
27 import com.atlassian.plugin.event.events.BeforePluginModuleDisabledEvent;
28 import com.atlassian.plugin.event.events.PluginContainerUnavailableEvent;
29 import com.atlassian.plugin.event.events.PluginDisabledEvent;
30 import com.atlassian.plugin.event.events.PluginEnabledEvent;
31 import com.atlassian.plugin.event.events.PluginFrameworkDelayedEvent;
32 import com.atlassian.plugin.event.events.PluginFrameworkResumingEvent;
33 import com.atlassian.plugin.event.events.PluginFrameworkShutdownEvent;
34 import com.atlassian.plugin.event.events.PluginFrameworkStartedEvent;
35 import com.atlassian.plugin.event.events.PluginFrameworkStartingEvent;
36 import com.atlassian.plugin.event.events.PluginFrameworkWarmRestartedEvent;
37 import com.atlassian.plugin.event.events.PluginFrameworkWarmRestartingEvent;
38 import com.atlassian.plugin.event.events.PluginModuleAvailableEvent;
39 import com.atlassian.plugin.event.events.PluginModuleDisabledEvent;
40 import com.atlassian.plugin.event.events.PluginModuleEnabledEvent;
41 import com.atlassian.plugin.event.events.PluginModuleUnavailableEvent;
42 import com.atlassian.plugin.event.events.PluginRefreshedEvent;
43 import com.atlassian.plugin.event.events.PluginUninstalledEvent;
44 import com.atlassian.plugin.event.events.PluginUpgradedEvent;
45 import com.atlassian.plugin.exception.NoOpPluginExceptionInterception;
46 import com.atlassian.plugin.exception.PluginExceptionInterception;
47 import com.atlassian.plugin.impl.UnloadablePlugin;
48 import com.atlassian.plugin.impl.UnloadablePluginFactory;
49 import com.atlassian.plugin.loaders.DiscardablePluginLoader;
50 import com.atlassian.plugin.loaders.DynamicPluginLoader;
51 import com.atlassian.plugin.loaders.PermissionCheckingPluginLoader;
52 import com.atlassian.plugin.loaders.PluginLoader;
53 import com.atlassian.plugin.manager.PluginPersistentState.Builder;
54 import com.atlassian.plugin.metadata.ClasspathFilePluginMetadata;
55 import com.atlassian.plugin.metadata.DefaultRequiredPluginValidator;
56 import com.atlassian.plugin.metadata.RequiredPluginValidator;
57 import com.atlassian.plugin.parsers.DescriptorParserFactory;
58 import com.atlassian.plugin.predicate.EnabledModulePredicate;
59 import com.atlassian.plugin.predicate.EnabledPluginPredicate;
60 import com.atlassian.plugin.predicate.ModuleDescriptorOfClassPredicate;
61 import com.atlassian.plugin.predicate.ModuleDescriptorOfTypePredicate;
62 import com.atlassian.plugin.predicate.ModuleDescriptorPredicate;
63 import com.atlassian.plugin.predicate.ModuleOfClassPredicate;
64 import com.atlassian.plugin.predicate.PluginPredicate;
65 import com.atlassian.plugin.util.PluginUtils;
66 import com.atlassian.util.concurrent.CopyOnWriteMap;
67 import com.google.common.annotations.VisibleForTesting;
68 import com.google.common.base.Function;
69 import com.google.common.base.Predicate;
70 import com.google.common.base.Predicates;
71 import com.google.common.collect.ArrayListMultimap;
72 import com.google.common.collect.Iterables;
73 import com.google.common.collect.Lists;
74 import com.google.common.collect.Multimap;
75 import com.google.common.collect.Ordering;
76 import com.google.common.collect.Sets;
77 import com.google.common.collect.TreeMultimap;
78 import org.apache.commons.lang.time.StopWatch;
79 import org.slf4j.Logger;
80 import org.slf4j.LoggerFactory;
81
82 import java.io.InputStream;
83 import java.util.ArrayDeque;
84 import java.util.ArrayList;
85 import java.util.Collection;
86 import java.util.Collections;
87 import java.util.HashMap;
88 import java.util.HashSet;
89 import java.util.LinkedHashMap;
90 import java.util.LinkedList;
91 import java.util.List;
92 import java.util.Map;
93 import java.util.Set;
94 import java.util.TreeSet;
95 import javax.annotation.Nullable;
96
97 import static com.atlassian.plugin.util.Assertions.notNull;
98 import static com.atlassian.plugin.util.collect.Transforms.toPluginKeys;
99 import static com.google.common.base.Preconditions.checkState;
100 import static com.google.common.collect.ImmutableList.copyOf;
101 import static com.google.common.collect.Iterables.concat;
102 import static com.google.common.collect.Iterables.filter;
103 import static com.google.common.collect.Iterables.transform;
104 import static com.google.common.collect.Maps.filterKeys;
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122 public class DefaultPluginManager implements PluginController, PluginAccessor, PluginSystemLifecycle
123 {
124 private static final Logger log = LoggerFactory.getLogger(DefaultPluginManager.class);
125
126 private final List<DiscardablePluginLoader> pluginLoaders;
127 private final PluginPersistentStateStore store;
128 private final ModuleDescriptorFactory moduleDescriptorFactory;
129 private final PluginEventManager pluginEventManager;
130
131 private final Map<String, Plugin> plugins;
132 private final PluginsClassLoader classLoader;
133 private final PluginEnabler pluginEnabler;
134 private final StateTracker tracker;
135
136 private final boolean verifyRequiredPlugins;
137
138
139
140
141
142
143 private final PluginPredicate delayLoadOf;
144
145
146
147
148
149 private RevertablePluginInstaller pluginInstaller;
150
151
152
153
154 private final Map<Plugin, PluginLoader> installedPluginsToPluginLoader;
155
156
157
158
159
160
161
162
163
164 private final Map<Plugin, DiscardablePluginLoader> candidatePluginsToPluginLoader;
165
166
167
168
169 private final List<Plugin> delayedPlugins;
170
171 private static PluginExceptionInterception defaultPluginExceptionInterception()
172 {
173 return NoOpPluginExceptionInterception.NOOP_INTERCEPTION;
174 }
175
176 private static boolean defaultVerifyRequiredPlugins()
177 {
178 return false;
179 }
180
181 private static PluginPredicate defaultDelayLoadOf()
182 {
183 return new PluginPredicate()
184 {
185 public boolean matches(Plugin plugin)
186 {
187 return false;
188 }
189 };
190 }
191
192 public DefaultPluginManager(
193 final PluginPersistentStateStore store,
194 final List<PluginLoader> pluginLoaders,
195 final ModuleDescriptorFactory moduleDescriptorFactory,
196 final PluginEventManager pluginEventManager)
197 {
198 this(store, pluginLoaders, moduleDescriptorFactory, pluginEventManager,
199 defaultPluginExceptionInterception(), defaultVerifyRequiredPlugins(), defaultDelayLoadOf());
200 }
201
202 public DefaultPluginManager(
203 final PluginPersistentStateStore store,
204 final List<PluginLoader> pluginLoaders,
205 final ModuleDescriptorFactory moduleDescriptorFactory,
206 final PluginEventManager pluginEventManager,
207 final PluginExceptionInterception pluginExceptionInterception)
208 {
209 this(store, pluginLoaders, moduleDescriptorFactory, pluginEventManager,
210 pluginExceptionInterception, defaultVerifyRequiredPlugins(), defaultDelayLoadOf());
211 }
212
213 public DefaultPluginManager(
214 final PluginPersistentStateStore store,
215 final List<PluginLoader> pluginLoaders,
216 final ModuleDescriptorFactory moduleDescriptorFactory,
217 final PluginEventManager pluginEventManager,
218 boolean verifyRequiredPlugins)
219 {
220 this(store, pluginLoaders, moduleDescriptorFactory, pluginEventManager,
221 defaultPluginExceptionInterception(), verifyRequiredPlugins, defaultDelayLoadOf());
222 }
223
224 @ExperimentalApi
225 public DefaultPluginManager(
226 final PluginPersistentStateStore store,
227 final List<PluginLoader> pluginLoaders,
228 final ModuleDescriptorFactory moduleDescriptorFactory,
229 final PluginEventManager pluginEventManager,
230 final PluginPredicate delayLoadOf)
231 {
232 this(store, pluginLoaders, moduleDescriptorFactory, pluginEventManager,
233 defaultPluginExceptionInterception(), defaultVerifyRequiredPlugins(), delayLoadOf);
234 }
235
236 @ExperimentalApi
237 public DefaultPluginManager(
238 final PluginPersistentStateStore store,
239 final List<PluginLoader> pluginLoaders,
240 final ModuleDescriptorFactory moduleDescriptorFactory,
241 final PluginEventManager pluginEventManager,
242 final PluginExceptionInterception pluginExceptionInterception,
243 final PluginPredicate delayLoadOf)
244 {
245 this(store, pluginLoaders, moduleDescriptorFactory, pluginEventManager,
246 pluginExceptionInterception, defaultVerifyRequiredPlugins(), delayLoadOf);
247 }
248
249 public DefaultPluginManager(
250 final PluginPersistentStateStore store,
251 final List<PluginLoader> pluginLoaders,
252 final ModuleDescriptorFactory moduleDescriptorFactory,
253 final PluginEventManager pluginEventManager,
254 final PluginExceptionInterception pluginExceptionInterception,
255 boolean verifyRequiredPlugins)
256 {
257 this(store, pluginLoaders, moduleDescriptorFactory, pluginEventManager,
258 pluginExceptionInterception, verifyRequiredPlugins, defaultDelayLoadOf());
259 }
260
261 @ExperimentalApi
262 public DefaultPluginManager(
263 final PluginPersistentStateStore store,
264 final List<PluginLoader> pluginLoaders,
265 final ModuleDescriptorFactory moduleDescriptorFactory,
266 final PluginEventManager pluginEventManager,
267 final PluginExceptionInterception pluginExceptionInterception,
268 boolean verifyRequiredPlugins,
269 PluginPredicate delayLoadOf)
270 {
271 this.pluginLoaders = toPermissionCheckingPluginLoaders(notNull("Plugin Loaders list must not be null.", pluginLoaders));
272 this.store = notNull("PluginPersistentStateStore must not be null.", store);
273 this.moduleDescriptorFactory = notNull("ModuleDescriptorFactory must not be null.", moduleDescriptorFactory);
274 this.pluginEventManager = notNull("PluginEventManager must not be null.", pluginEventManager);
275
276 this.pluginEnabler = new PluginEnabler(this, this, notNull("PluginExceptionInterception must not be null.", pluginExceptionInterception));
277 this.verifyRequiredPlugins = verifyRequiredPlugins;
278 this.delayLoadOf = delayLoadOf;
279
280 this.plugins = CopyOnWriteMap.<String, Plugin> builder().stableViews().newHashMap();
281 this.tracker = new StateTracker();
282 this.pluginInstaller = new NoOpRevertablePluginInstaller(new UnsupportedPluginInstaller());
283 this.installedPluginsToPluginLoader = new HashMap<Plugin, PluginLoader>();
284 this.candidatePluginsToPluginLoader = new HashMap<Plugin, DiscardablePluginLoader>();
285 this.delayedPlugins = new ArrayList<Plugin>();
286
287 this.pluginEventManager.register(this);
288 this.classLoader = new PluginsClassLoader(null, this, pluginEventManager);
289 }
290
291 private List<DiscardablePluginLoader> toPermissionCheckingPluginLoaders(List<PluginLoader> fromIterable)
292 {
293 return Lists.newArrayList(Iterables.transform(fromIterable, new Function<PluginLoader, DiscardablePluginLoader>()
294 {
295 @Override
296 public PermissionCheckingPluginLoader apply(PluginLoader pl)
297 {
298 return new PermissionCheckingPluginLoader(pl);
299 }
300 }));
301 }
302
303 public void init() throws PluginParseException, NotificationException
304 {
305 earlyStartup();
306 lateStartup();
307 }
308
309 @ExperimentalApi
310 public void earlyStartup() throws PluginParseException, NotificationException
311 {
312 final StopWatch stopWatch = new StopWatch();
313 stopWatch.start();
314 log.info("Plugin system earlyStartup begun");
315
316 tracker.setState(StateTracker.State.STARTING);
317 pluginEventManager.broadcast(new PluginFrameworkStartingEvent(this, this));
318 pluginInstaller.clearBackups();
319 final Multimap<String, Plugin> candidatePluginKeyToVersionedPlugins = TreeMultimap.create();
320 for (final DiscardablePluginLoader loader : pluginLoaders)
321 {
322 if (loader == null)
323 {
324 continue;
325 }
326
327 final Iterable<Plugin> possiblePluginsToLoad = loader.loadAllPlugins(moduleDescriptorFactory);
328
329 if (log.isDebugEnabled())
330 {
331 log.debug("Found {} plugins to possibly load: {}", Iterables.size(possiblePluginsToLoad), toPluginKeys(possiblePluginsToLoad));
332 }
333
334 for (final Plugin plugin : possiblePluginsToLoad)
335 {
336 if (getState().getPluginRestartState(plugin.getKey()) == PluginRestartState.REMOVE)
337 {
338 if (log.isInfoEnabled())
339 {
340 log.info("Plugin " + plugin.getKey() + " was marked to be removed on restart. Removing now.");
341 }
342 loader.removePlugin(plugin);
343
344
345 removeStateFromStore(getStore(), plugin);
346 }
347 else
348 {
349
350
351
352
353 candidatePluginsToPluginLoader.put(plugin, loader);
354 candidatePluginKeyToVersionedPlugins.put(plugin.getKey(), plugin);
355 }
356 }
357 }
358 final List<Plugin> pluginsToInstall = new ArrayList<Plugin>();
359 for (final Collection<Plugin> plugins : candidatePluginKeyToVersionedPlugins.asMap().values())
360 {
361 final Plugin plugin = Ordering.natural().max(plugins);
362 if (plugins.size() > 1)
363 {
364 log.debug("Plugin {} contained multiple versions. installing version {}.", plugin.getKey(), plugin.getPluginInformation().getVersion());
365 }
366 pluginsToInstall.add(plugin);
367
368 }
369
370
371
372
373
374
375 final List<Plugin> immediatePlugins = new ArrayList<Plugin>();
376 for (final Plugin plugin : pluginsToInstall)
377 {
378 if (delayLoadOf.matches(plugin))
379 {
380 delayedPlugins.add(plugin);
381 }
382 else
383 {
384 immediatePlugins.add(plugin);
385 }
386 }
387
388
389 addPlugins(null, immediatePlugins);
390
391
392
393
394 for (final Plugin plugin : immediatePlugins)
395 {
396 candidatePluginsToPluginLoader.remove(plugin);
397 }
398
399
400
401
402
403 final Map<Plugin, DiscardablePluginLoader> delayedPluginsLoaders = new HashMap<Plugin, DiscardablePluginLoader>();
404 for (final Plugin plugin : delayedPlugins)
405 {
406 final DiscardablePluginLoader loader = candidatePluginsToPluginLoader.remove(plugin);
407 delayedPluginsLoaders.put(plugin, loader);
408 }
409
410
411
412 for (final Map.Entry<Plugin, DiscardablePluginLoader> entry : candidatePluginsToPluginLoader.entrySet())
413 {
414 final Plugin plugin = entry.getKey();
415 final DiscardablePluginLoader loader = entry.getValue();
416 loader.discardPlugin(plugin);
417 }
418
419 candidatePluginsToPluginLoader.clear();
420 candidatePluginsToPluginLoader.putAll(delayedPluginsLoaders);
421 getStore().save(getBuilder().clearPluginRestartState().toState());
422 tracker.setState(StateTracker.State.DELAYED);
423
424 stopWatch.stop();
425 log.info("Plugin system earlyStartup finished in " + stopWatch);
426
427 pluginEventManager.broadcast(new PluginFrameworkDelayedEvent(this, this));
428 }
429
430 @ExperimentalApi
431 public void lateStartup() throws PluginParseException, NotificationException
432 {
433 final StopWatch stopWatch = new StopWatch();
434 stopWatch.start();
435 log.info("Plugin system lateStartup begun");
436
437 tracker.setState(StateTracker.State.RESUMING);
438 pluginEventManager.broadcast(new PluginFrameworkResumingEvent(this, this));
439
440 addPlugins(null, delayedPlugins);
441 delayedPlugins.clear();
442 candidatePluginsToPluginLoader.clear();
443
444 stopWatch.stop();
445 log.info("Plugin system lateStartup finished in " + stopWatch);
446
447 tracker.setState(StateTracker.State.STARTED);
448 if (verifyRequiredPlugins)
449 {
450 validateRequiredPlugins();
451 }
452 pluginEventManager.broadcast(new PluginFrameworkStartedEvent(this, this));
453 }
454
455
456 private void validateRequiredPlugins() throws PluginException
457 {
458 RequiredPluginValidator validator = new DefaultRequiredPluginValidator(this, new ClasspathFilePluginMetadata());
459 Collection<String> errors = validator.validate();
460 if (errors.size() > 0)
461 {
462 log.error("Unable to validate required plugins or modules - plugin system shutting down");
463 log.error("Failures:");
464 for (String error : errors)
465 {
466 log.error("\t{}", error);
467 }
468 shutdown();
469 throw new PluginException("Unable to validate required plugins or modules");
470 }
471 }
472
473
474
475
476
477
478
479
480 public void shutdown()
481 {
482 tracker.setState(StateTracker.State.SHUTTING_DOWN);
483 log.info("Shutting down the plugin system");
484 try
485 {
486 pluginEventManager.broadcast(new PluginFrameworkShutdownEvent(this, this));
487 }
488 catch (final NotificationException ex)
489 {
490 log.error("At least one error occured while broadcasting the PluginFrameworkShutdownEvent. We will continue to shutdown the Plugin Manager anyway.");
491 }
492 plugins.clear();
493 pluginEventManager.unregister(this);
494 tracker.setState(StateTracker.State.SHUTDOWN);
495 }
496
497 public final void warmRestart()
498 {
499 tracker.setState(StateTracker.State.WARM_RESTARTING);
500 log.info("Initiating a warm restart of the plugin system");
501 pluginEventManager.broadcast(new PluginFrameworkWarmRestartingEvent(this, this));
502
503
504 final List<Plugin> restartedPlugins = new ArrayList<Plugin>();
505 final List<PluginLoader> loaders = new ArrayList<PluginLoader>(pluginLoaders);
506 Collections.reverse(loaders);
507 for (final PluginLoader loader : pluginLoaders)
508 {
509 for (final Map.Entry<Plugin, PluginLoader> entry : installedPluginsToPluginLoader.entrySet())
510 {
511 if (entry.getValue() == loader)
512 {
513 final Plugin plugin = entry.getKey();
514 if (isPluginEnabled(plugin.getKey()))
515 {
516 disablePluginModules(plugin);
517 restartedPlugins.add(plugin);
518 }
519 }
520 }
521 }
522
523
524 Collections.reverse(restartedPlugins);
525 for (final Plugin plugin : restartedPlugins)
526 {
527 enableConfiguredPluginModules(plugin);
528 }
529
530 pluginEventManager.broadcast(new PluginFrameworkWarmRestartedEvent(this, this));
531 tracker.setState(StateTracker.State.STARTED);
532 }
533
534 @PluginEventListener
535 public void onPluginModuleAvailable(final PluginModuleAvailableEvent event)
536 {
537 enableConfiguredPluginModule(event.getModule().getPlugin(), event.getModule(), new HashSet<ModuleDescriptor<?>>());
538 }
539
540 @PluginEventListener
541 public void onPluginModuleUnavailable(final PluginModuleUnavailableEvent event)
542 {
543 notifyModuleDisabled(event.getModule());
544 }
545
546 @PluginEventListener
547 public void onPluginContainerUnavailable(final PluginContainerUnavailableEvent event)
548 {
549 disablePluginWithoutPersisting(event.getPluginKey());
550 }
551
552 @PluginEventListener
553 public void onPluginRefresh(final PluginRefreshedEvent event)
554 {
555 final Plugin plugin = event.getPlugin();
556
557 disablePluginModules(plugin);
558
559
560 if (enableConfiguredPluginModules(plugin))
561 {
562 pluginEventManager.broadcast(new PluginEnabledEvent(plugin));
563 }
564 }
565
566
567
568
569
570
571
572 public void setPluginInstaller(final PluginInstaller pluginInstaller)
573 {
574 if (pluginInstaller instanceof RevertablePluginInstaller)
575 {
576 this.pluginInstaller = (RevertablePluginInstaller) pluginInstaller;
577 }
578 else
579 {
580 this.pluginInstaller = new NoOpRevertablePluginInstaller(pluginInstaller);
581 }
582 }
583
584 protected final PluginPersistentStateStore getStore()
585 {
586 return store;
587 }
588
589 public String installPlugin(final PluginArtifact pluginArtifact) throws PluginParseException
590 {
591 final Set<String> keys = installPlugins(pluginArtifact);
592 if ((keys != null) && (keys.size() == 1))
593 {
594 return keys.iterator().next();
595 }
596 else
597 {
598
599 throw new PluginParseException("Could not install plugin");
600 }
601 }
602
603 public Set<String> installPlugins(final PluginArtifact... pluginArtifacts) throws PluginParseException
604 {
605 final Map<String, PluginArtifact> validatedArtifacts = new LinkedHashMap<String, PluginArtifact>();
606 try
607 {
608 for (final PluginArtifact pluginArtifact : pluginArtifacts)
609 {
610 validatedArtifacts.put(validatePlugin(pluginArtifact), pluginArtifact);
611 }
612 }
613 catch (final PluginParseException ex)
614 {
615 throw new PluginParseException("All plugins could not be validated", ex);
616 }
617
618 for (final Map.Entry<String, PluginArtifact> entry : validatedArtifacts.entrySet())
619 {
620 pluginInstaller.installPlugin(entry.getKey(), entry.getValue());
621 }
622
623 scanForNewPlugins();
624 return validatedArtifacts.keySet();
625 }
626
627
628
629
630
631
632
633
634
635
636 String validatePlugin(final PluginArtifact pluginArtifact) throws PluginParseException
637 {
638 boolean foundADynamicPluginLoader = false;
639 for (final PluginLoader loader : pluginLoaders)
640 {
641 if (loader.isDynamicPluginLoader())
642 {
643 foundADynamicPluginLoader = true;
644 final String key = ((DynamicPluginLoader) loader).canLoad(pluginArtifact);
645 if (key != null)
646 {
647 return key;
648 }
649 }
650 }
651
652 if (!foundADynamicPluginLoader)
653 {
654 throw new IllegalStateException("Should be at least one DynamicPluginLoader in the plugin loader list");
655 }
656 throw new PluginParseException("Jar " + pluginArtifact.getName() + " is not a valid plugin!");
657 }
658
659 public int scanForNewPlugins() throws PluginParseException
660 {
661 checkState(StateTracker.State.STARTED == tracker.get(), "Cannot scanForNewPlugins in state %s", tracker.get());
662
663 int numberFound = 0;
664
665 for (final PluginLoader loader : pluginLoaders)
666 {
667 if (loader != null)
668 {
669 if (loader.supportsAddition())
670 {
671 final List<Plugin> pluginsToAdd = new ArrayList<Plugin>();
672 for (Plugin plugin : loader.loadFoundPlugins(moduleDescriptorFactory))
673 {
674 final Plugin oldPlugin = plugins.get(plugin.getKey());
675
676
677
678 if (!(plugin instanceof UnloadablePlugin))
679 {
680 if (PluginUtils.doesPluginRequireRestart(plugin))
681 {
682 if (oldPlugin == null)
683 {
684 markPluginInstallThatRequiresRestart(plugin);
685
686 final UnloadablePlugin unloadablePlugin = UnloadablePluginFactory.createUnloadablePlugin(plugin);
687 unloadablePlugin.setErrorText("Plugin requires a restart of the application due " + "to the following modules: " + PluginUtils.getPluginModulesThatRequireRestart(plugin));
688 plugin = unloadablePlugin;
689 }
690 else
691 {
692
693
694 if (!PluginRestartState.INSTALL.equals(getPluginRestartState(plugin.getKey())))
695 {
696 markPluginUpgradeThatRequiresRestart(plugin);
697 }
698 continue;
699 }
700 }
701
702
703 else if (oldPlugin != null && PluginUtils.doesPluginRequireRestart(oldPlugin))
704 {
705
706
707
708
709 if (PluginRestartState.INSTALL.equals(getPluginRestartState(oldPlugin.getKey())))
710 {
711 revertRestartRequiredChange(oldPlugin.getKey());
712 }
713 else
714 {
715 markPluginUpgradeThatRequiresRestart(plugin);
716 continue;
717 }
718 }
719 pluginsToAdd.add(plugin);
720 }
721 }
722 addPlugins(loader, pluginsToAdd);
723 numberFound = pluginsToAdd.size();
724 }
725 }
726 }
727 return numberFound;
728 }
729
730 private void markPluginInstallThatRequiresRestart(final Plugin plugin)
731 {
732 if (log.isInfoEnabled())
733 {
734 log.info("Installed plugin '" + plugin.getKey() + "' requires a restart due to the following modules: " + PluginUtils.getPluginModulesThatRequireRestart(plugin));
735 }
736 updateRequiresRestartState(plugin.getKey(), PluginRestartState.INSTALL);
737 }
738
739 private void markPluginUpgradeThatRequiresRestart(final Plugin plugin)
740 {
741 if (log.isInfoEnabled())
742 {
743 log.info("Upgraded plugin '" + plugin.getKey() + "' requires a restart due to the following modules: " + PluginUtils.getPluginModulesThatRequireRestart(plugin));
744 }
745 updateRequiresRestartState(plugin.getKey(), PluginRestartState.UPGRADE);
746 }
747
748 private void markPluginUninstallThatRequiresRestart(final Plugin plugin)
749 {
750 if (log.isInfoEnabled())
751 {
752 log.info("Uninstalled plugin '" + plugin.getKey() + "' requires a restart due to the following modules: " + PluginUtils.getPluginModulesThatRequireRestart(plugin));
753 }
754 updateRequiresRestartState(plugin.getKey(), PluginRestartState.REMOVE);
755 }
756
757 private void updateRequiresRestartState(final String pluginKey, final PluginRestartState pluginRestartState)
758 {
759 getStore().save(getBuilder().setPluginRestartState(pluginKey, pluginRestartState).toState());
760 onUpdateRequiresRestartState(pluginKey, pluginRestartState);
761 }
762
763 protected void onUpdateRequiresRestartState(final String pluginKey, final PluginRestartState pluginRestartState)
764 {
765
766 }
767
768
769
770
771
772
773
774 public void uninstall(final Plugin plugin) throws PluginException
775 {
776 if (PluginUtils.doesPluginRequireRestart(plugin))
777 {
778 ensurePluginAndLoaderSupportsUninstall(plugin);
779 markPluginUninstallThatRequiresRestart(plugin);
780 }
781 else
782 {
783
784 disableDependentPlugins(plugin);
785
786 uninstallNoEvent(plugin);
787
788 broadcastIgnoreError(new PluginUninstalledEvent(plugin));
789 }
790 }
791
792
793
794
795
796
797
798 protected void uninstallNoEvent(final Plugin plugin)
799 {
800 unloadPlugin(plugin);
801
802
803 removeStateFromStore(getStore(), plugin);
804 }
805
806
807
808
809
810 public void revertRestartRequiredChange(final String pluginKey) throws PluginException
811 {
812 notNull("pluginKey", pluginKey);
813 final PluginRestartState restartState = getState().getPluginRestartState(pluginKey);
814 if (restartState == PluginRestartState.UPGRADE)
815 {
816 pluginInstaller.revertInstalledPlugin(pluginKey);
817 }
818 else if (restartState == PluginRestartState.INSTALL)
819 {
820 pluginInstaller.revertInstalledPlugin(pluginKey);
821 plugins.remove(pluginKey);
822 }
823 updateRequiresRestartState(pluginKey, PluginRestartState.NONE);
824 }
825
826 protected void removeStateFromStore(final PluginPersistentStateStore stateStore, final Plugin plugin)
827 {
828 final PluginPersistentState.Builder builder = PluginPersistentState.Builder.create(stateStore.load()).removeState(plugin.getKey());
829 for (final ModuleDescriptor<?> moduleDescriptor : plugin.getModuleDescriptors())
830 {
831 builder.removeState(moduleDescriptor.getCompleteKey());
832 }
833 stateStore.save(builder.toState());
834 }
835
836
837
838
839
840
841
842
843 protected void unloadPlugin(final Plugin plugin) throws PluginException
844 {
845 final PluginLoader loader = ensurePluginAndLoaderSupportsUninstall(plugin);
846
847 if (isPluginEnabled(plugin.getKey()))
848 {
849 notifyPluginDisabled(plugin);
850 }
851
852 notifyUninstallPlugin(plugin);
853 if (loader != null)
854 {
855 removePluginFromLoader(plugin);
856 }
857
858 plugins.remove(plugin.getKey());
859 }
860
861 private PluginLoader ensurePluginAndLoaderSupportsUninstall(final Plugin plugin)
862 {
863 if (!plugin.isUninstallable())
864 {
865 throw new PluginException("Plugin is not uninstallable: " + plugin.getKey());
866 }
867
868 final PluginLoader loader = installedPluginsToPluginLoader.get(plugin);
869
870 if ((loader != null) && !loader.supportsRemoval())
871 {
872 throw new PluginException("Not uninstalling plugin - loader doesn't allow removal. Plugin: " + plugin.getKey());
873 }
874 return loader;
875 }
876
877 private void removePluginFromLoader(final Plugin plugin) throws PluginException
878 {
879 if (plugin.isDeleteable())
880 {
881 final PluginLoader pluginLoader = installedPluginsToPluginLoader.get(plugin);
882 pluginLoader.removePlugin(plugin);
883 }
884
885 installedPluginsToPluginLoader.remove(plugin);
886 }
887
888 protected void notifyUninstallPlugin(final Plugin plugin)
889 {
890 classLoader.notifyUninstallPlugin(plugin);
891
892 for (final ModuleDescriptor<?> descriptor : plugin.getModuleDescriptors())
893 {
894 descriptor.destroy();
895 }
896 }
897
898 protected PluginPersistentState getState()
899 {
900 return getStore().load();
901 }
902
903
904
905
906
907 @Deprecated
908 protected void addPlugin(final PluginLoader loader, final Plugin plugin) throws PluginParseException
909 {
910 addPlugins(loader, Collections.singletonList(plugin));
911 }
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931 protected void addPlugins(@Nullable final PluginLoader loader, final Collection<Plugin> pluginsToInstall) throws PluginParseException
932 {
933 final List<Plugin> pluginsToEnable = new ArrayList<Plugin>();
934
935
936 for (final Plugin plugin : new TreeSet<Plugin>(pluginsToInstall))
937 {
938 boolean pluginUpgraded = false;
939
940 final Plugin existingPlugin = plugins.get(plugin.getKey());
941 if (existingPlugin != null)
942 {
943 if (plugin.compareTo(existingPlugin) >= 0)
944 {
945 try
946 {
947
948
949 pluginsToEnable.addAll(disableDependentPlugins(plugin));
950 updatePlugin(existingPlugin, plugin);
951 pluginsToEnable.remove(existingPlugin);
952 pluginUpgraded = true;
953 }
954 catch (final PluginException e)
955 {
956 throw new PluginParseException(
957 "Duplicate plugin found (installed version is the same or older) and could not be unloaded: '" + plugin.getKey() + "'", e);
958 }
959 }
960 else
961 {
962
963 log.debug("Duplicate plugin found (installed version is newer): '{}'", plugin.getKey());
964
965 if (null == loader)
966 {
967
968
969 candidatePluginsToPluginLoader.get(plugin).discardPlugin(plugin);
970 }
971 else if (loader instanceof DiscardablePluginLoader)
972 {
973
974
975
976 ((DiscardablePluginLoader) loader).discardPlugin(plugin);
977 }
978 else
979 {
980
981
982
983
984
985
986 log.debug("Ignoring discardPlugin({}, version {}) as delegate is not a DiscardablePluginLoader",
987 plugin.getKey(), plugin.getPluginInformation().getVersion());
988 }
989
990
991 continue;
992 }
993 }
994
995 plugin.install();
996 final boolean isPluginEnabled = getState().isEnabled(plugin);
997 if (isPluginEnabled)
998 {
999 log.debug("Plugin '{}' is to be enabled.", plugin.getKey());
1000 pluginsToEnable.add(plugin);
1001 }
1002 else
1003 {
1004 if (plugin.isSystemPlugin())
1005 {
1006 log.warn("System Plugin '{}' is disabled.", plugin.getKey());
1007 }
1008 else
1009 {
1010 log.debug("Plugin '{}' is disabled.", plugin.getKey());
1011 }
1012 }
1013 if (pluginUpgraded)
1014 {
1015 pluginEventManager.broadcast(new PluginUpgradedEvent(plugin));
1016 }
1017 plugins.put(plugin.getKey(), plugin);
1018 if (loader == null)
1019 {
1020 installedPluginsToPluginLoader.put(plugin, candidatePluginsToPluginLoader.get(plugin));
1021 }
1022 else
1023 {
1024 installedPluginsToPluginLoader.put(plugin, loader);
1025 }
1026 }
1027
1028 if (log.isDebugEnabled())
1029 {
1030 log.debug("Found {} plugins to enable: {}", Iterables.size(pluginsToEnable), toPluginKeys(pluginsToEnable));
1031 }
1032 pluginEnabler.enable(pluginsToEnable);
1033
1034
1035 for (final Plugin plugin : pluginsToEnable)
1036 {
1037 if (plugin.getPluginState() == PluginState.ENABLED)
1038 {
1039
1040 if (enableConfiguredPluginModules(plugin))
1041 {
1042 pluginEventManager.broadcast(new PluginEnabledEvent(plugin));
1043 }
1044 }
1045 }
1046 }
1047
1048
1049
1050
1051
1052
1053
1054
1055 private Set<Plugin> disableDependentPlugins(final Plugin plugin)
1056 {
1057 final Set<Plugin> dependentPlugins = getDependentPlugins(plugin, getEnabledPlugins());
1058
1059 if (log.isInfoEnabled())
1060 {
1061 final Iterable<String> dependentPluginKeys = transform(dependentPlugins, new Function<Plugin, String>()
1062 {
1063 @Override
1064 public String apply(Plugin p)
1065 {
1066 return p.getKey();
1067 }
1068 });
1069 log.info("Found dependent enabled plugins for uninstalled plugin '" + plugin.getKey() + "': " + dependentPluginKeys + ". Disabling...");
1070 }
1071 for (final Plugin depPlugin : dependentPlugins)
1072 {
1073 disablePluginWithoutPersisting(depPlugin.getKey());
1074 }
1075 return dependentPlugins;
1076 }
1077
1078
1079
1080
1081
1082
1083
1084
1085 @VisibleForTesting
1086 Set<Plugin> getDependentPlugins(final Plugin plugin, final Iterable<Plugin> pluginList)
1087 {
1088 final Multimap<String, Plugin> dependencies = ArrayListMultimap.create();
1089 for (Plugin p : pluginList)
1090 {
1091 for (String key : p.getRequiredPlugins())
1092 {
1093 dependencies.put(key, p);
1094 }
1095 }
1096
1097 final Set<Plugin> dependentPlugins = Sets.newHashSet();
1098
1099 final ArrayDeque<String> queue = new ArrayDeque<String>();
1100 queue.add(plugin.getKey());
1101 final Set<String> visited = Sets.newHashSet(plugin.getKey());
1102
1103 while (!queue.isEmpty())
1104 {
1105 final String pluginKey = queue.removeFirst();
1106 for (Plugin dependentPlugin : dependencies.get(pluginKey))
1107 {
1108 final String dependentPluginKey = dependentPlugin.getKey();
1109 if (visited.add(dependentPluginKey))
1110 {
1111 dependentPlugins.add(dependentPlugin);
1112 queue.addLast(dependentPluginKey);
1113 }
1114 }
1115 }
1116 return dependentPlugins;
1117 }
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127 protected void updatePlugin(final Plugin oldPlugin, final Plugin newPlugin) throws PluginException
1128 {
1129 if (!oldPlugin.getKey().equals(newPlugin.getKey()))
1130 {
1131 throw new IllegalArgumentException("New plugin must have the same key as the old plugin");
1132 }
1133
1134 if (log.isInfoEnabled())
1135 {
1136 log.info("Updating plugin '" + oldPlugin + "' to '" + newPlugin + "'");
1137 }
1138
1139
1140
1141
1142 final Map<String, Boolean> oldPluginState = new HashMap<String, Boolean>(getState().getPluginStateMap(oldPlugin));
1143
1144 if (log.isDebugEnabled())
1145 {
1146 log.debug("Uninstalling old plugin: " + oldPlugin);
1147 }
1148 uninstallNoEvent(oldPlugin);
1149 if (log.isDebugEnabled())
1150 {
1151 log.debug("Plugin uninstalled '" + oldPlugin + "', preserving old state");
1152 }
1153
1154
1155 final Set<String> newModuleKeys = new HashSet<String>();
1156 newModuleKeys.add(newPlugin.getKey());
1157 for (final ModuleDescriptor<?> moduleDescriptor : newPlugin.getModuleDescriptors())
1158 {
1159 newModuleKeys.add(moduleDescriptor.getCompleteKey());
1160 }
1161
1162
1163
1164
1165 final Predicate<String> filter = new Predicate<String>()
1166 {
1167 public boolean apply(final String o)
1168 {
1169 return newModuleKeys.contains(o);
1170 }
1171 };
1172
1173 getStore().save(getBuilder().addState(filterKeys(oldPluginState, filter)).toState());
1174 }
1175
1176 public Collection<Plugin> getPlugins()
1177 {
1178 return plugins.values();
1179 }
1180
1181
1182
1183
1184
1185 public Collection<Plugin> getPlugins(final PluginPredicate pluginPredicate)
1186 {
1187 return copyOf(filter(getPlugins(), new Predicate<Plugin>()
1188 {
1189 public boolean apply(final Plugin plugin)
1190 {
1191 return pluginPredicate.matches(plugin);
1192 }
1193 }));
1194 }
1195
1196
1197
1198
1199 public Collection<Plugin> getEnabledPlugins()
1200 {
1201 return getPlugins(new EnabledPluginPredicate(this));
1202 }
1203
1204
1205
1206
1207
1208 public <M> Collection<M> getModules(final ModuleDescriptorPredicate<M> moduleDescriptorPredicate)
1209 {
1210 return copyOf(getModules(getModuleDescriptors(getPlugins(), moduleDescriptorPredicate)));
1211 }
1212
1213
1214
1215
1216
1217 public <M> Collection<ModuleDescriptor<M>> getModuleDescriptors(final ModuleDescriptorPredicate<M> moduleDescriptorPredicate)
1218 {
1219 return copyOf(getModuleDescriptors(getPlugins(), moduleDescriptorPredicate));
1220 }
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233 private <M> Iterable<ModuleDescriptor<M>> getModuleDescriptors(final Collection<Plugin> plugins, final ModuleDescriptorPredicate<M> predicate)
1234 {
1235
1236
1237 final Function<ModuleDescriptor<?>, ModuleDescriptor<M>> coercer = new Function<ModuleDescriptor<?>, ModuleDescriptor<M>>()
1238 {
1239 public ModuleDescriptor<M> apply(final ModuleDescriptor<?> input)
1240 {
1241 @SuppressWarnings("unchecked")
1242 final ModuleDescriptor<M> result = (ModuleDescriptor<M>) input;
1243 return result;
1244 }
1245 };
1246
1247
1248 final Predicate<ModuleDescriptor<M>> adapter = new Predicate<ModuleDescriptor<M>>()
1249 {
1250 public boolean apply(final ModuleDescriptor<M> input)
1251 {
1252 return predicate.matches(input);
1253 }
1254 };
1255
1256
1257 final Function<Plugin, Iterable<ModuleDescriptor<M>>> descriptorExtractor = new Function<Plugin, Iterable<ModuleDescriptor<M>>>()
1258 {
1259 public Iterable<ModuleDescriptor<M>> apply(final Plugin plugin)
1260 {
1261 return filter(transform(plugin.getModuleDescriptors(), coercer), adapter);
1262 }
1263 };
1264
1265
1266 return concat(transform(plugins, descriptorExtractor));
1267 }
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279 private <M> Iterable<M> getModules(final Iterable<ModuleDescriptor<M>> moduleDescriptors)
1280 {
1281 return filter(transform(moduleDescriptors, new Function<ModuleDescriptor<M>, M>()
1282 {
1283 private final Set<String> disabled = new HashSet<String>();
1284 public M apply(final ModuleDescriptor<M> input)
1285 {
1286 try
1287 {
1288 return input.getModule();
1289 }
1290 catch (final RuntimeException ex)
1291 {
1292 final String key = input.getPlugin().getKey();
1293 log.error("Exception when retrieving plugin module {}", input.getKey(), ex);
1294 if (disabled.contains(key))
1295 {
1296 return null;
1297 }
1298
1299 log.error("Disabling plugin {}", key);
1300 disabled.add(key);
1301 disablePlugin(key);
1302 return null;
1303 }
1304 }
1305 }), Predicates.notNull());
1306 }
1307
1308 public Plugin getPlugin(final String key)
1309 {
1310 return plugins.get(notNull("The plugin key must be specified", key));
1311 }
1312
1313 public Plugin getEnabledPlugin(final String pluginKey)
1314 {
1315 if (!isPluginEnabled(pluginKey))
1316 {
1317 return null;
1318 }
1319 return getPlugin(pluginKey);
1320 }
1321
1322 public ModuleDescriptor<?> getPluginModule(final String completeKey)
1323 {
1324 return getPluginModule(new ModuleCompleteKey(completeKey));
1325 }
1326
1327 private ModuleDescriptor<?> getPluginModule(final ModuleCompleteKey key)
1328 {
1329 final Plugin plugin = getPlugin(key.getPluginKey());
1330 if (plugin == null)
1331 {
1332 return null;
1333 }
1334 return plugin.getModuleDescriptor(key.getModuleKey());
1335 }
1336
1337 public ModuleDescriptor<?> getEnabledPluginModule(final String completeKey)
1338 {
1339 final ModuleCompleteKey key = new ModuleCompleteKey(completeKey);
1340
1341
1342 if (!isPluginModuleEnabled(key))
1343 {
1344 return null;
1345 }
1346
1347 return getEnabledPlugin(key.getPluginKey()).getModuleDescriptor(key.getModuleKey());
1348 }
1349
1350
1351
1352
1353 public <M> List<M> getEnabledModulesByClass(final Class<M> moduleClass)
1354 {
1355 return copyOf(getModules(getEnabledModuleDescriptorsByModuleClass(moduleClass)));
1356 }
1357
1358
1359
1360
1361
1362
1363
1364 @Deprecated
1365 public <M> List<M> getEnabledModulesByClassAndDescriptor(final Class<ModuleDescriptor<M>>[] descriptorClasses, final Class<M> moduleClass)
1366 {
1367 final Iterable<ModuleDescriptor<M>> moduleDescriptors = filterDescriptors(getEnabledModuleDescriptorsByModuleClass(moduleClass), new ModuleDescriptorOfClassPredicate<M>(descriptorClasses));
1368
1369 return copyOf(getModules(moduleDescriptors));
1370 }
1371
1372
1373
1374
1375
1376
1377
1378 @Deprecated
1379 public <M> List<M> getEnabledModulesByClassAndDescriptor(final Class<ModuleDescriptor<M>> descriptorClass, final Class<M> moduleClass)
1380 {
1381 final Iterable<ModuleDescriptor<M>> moduleDescriptors = getEnabledModuleDescriptorsByModuleClass(moduleClass);
1382 return copyOf(getModules(filterDescriptors(moduleDescriptors, new ModuleDescriptorOfClassPredicate<M>(
1383 descriptorClass))));
1384 }
1385
1386
1387
1388
1389
1390
1391
1392
1393 private <M> Collection<ModuleDescriptor<M>> getEnabledModuleDescriptorsByModuleClass(final Class<M> moduleClass)
1394 {
1395 final ModuleOfClassPredicate<M> ofType = new ModuleOfClassPredicate<M>(moduleClass);
1396 final EnabledModulePredicate<M> enabled = new EnabledModulePredicate<M>(this);
1397 return copyOf(getModuleDescriptors(getEnabledPlugins(), new ModuleDescriptorPredicate<M>()
1398 {
1399 public boolean matches(final ModuleDescriptor<? extends M> moduleDescriptor)
1400 {
1401 return ofType.matches(moduleDescriptor) && enabled.matches(moduleDescriptor);
1402 }
1403 }));
1404 }
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414 public <D extends ModuleDescriptor<?>> List<D> getEnabledModuleDescriptorsByClass(final Class<D> descriptorClazz)
1415 {
1416 final List<D> result = new LinkedList<D>();
1417 for (final Plugin plugin : plugins.values())
1418 {
1419
1420 if (!isPluginEnabled(plugin.getKey()))
1421 {
1422 if (log.isDebugEnabled())
1423 {
1424 log.debug("Plugin [" + plugin.getKey() + "] is disabled.");
1425 }
1426 continue;
1427 }
1428
1429 for (final ModuleDescriptor<?> module : plugin.getModuleDescriptors())
1430 {
1431 if (descriptorClazz.isInstance(module))
1432 {
1433 if (isPluginModuleEnabled(module.getCompleteKey()))
1434 {
1435 result.add(descriptorClazz.cast(module));
1436 }
1437 else if (log.isDebugEnabled())
1438 {
1439 log.debug("Module [" + module.getCompleteKey() + "] is disabled.");
1440 }
1441 }
1442 }
1443 }
1444
1445 return result;
1446 }
1447
1448 public <D extends ModuleDescriptor<?>> List<D> getEnabledModuleDescriptorsByClass(final Class<D> descriptorClazz, final boolean verbose)
1449 {
1450 return getEnabledModuleDescriptorsByClass(descriptorClazz);
1451 }
1452
1453
1454
1455
1456
1457
1458
1459 @Deprecated
1460 public <M> List<ModuleDescriptor<M>> getEnabledModuleDescriptorsByType(final String type) throws PluginParseException, IllegalArgumentException
1461 {
1462 final ModuleDescriptorOfTypePredicate<M> ofType = new ModuleDescriptorOfTypePredicate<M>(moduleDescriptorFactory, type);
1463 final EnabledModulePredicate<M> enabled = new EnabledModulePredicate<M>(this);
1464 return copyOf(getModuleDescriptors(getEnabledPlugins(), new ModuleDescriptorPredicate<M>()
1465 {
1466 public boolean matches(final ModuleDescriptor<? extends M> moduleDescriptor)
1467 {
1468 return ofType.matches(moduleDescriptor) && enabled.matches(moduleDescriptor);
1469 }
1470 }));
1471 }
1472
1473
1474
1475
1476
1477
1478
1479 private static <M> Iterable<ModuleDescriptor<M>> filterDescriptors(final Iterable<ModuleDescriptor<M>> descriptors, final ModuleDescriptorPredicate<M> predicate)
1480 {
1481 return filter(descriptors, new Predicate<ModuleDescriptor<M>>()
1482 {
1483 public boolean apply(final ModuleDescriptor<M> input)
1484 {
1485 return predicate.matches(input);
1486 }
1487 });
1488 }
1489
1490
1491
1492
1493
1494
1495
1496
1497 public void enablePlugins(final String... keys)
1498 {
1499 final Collection<Plugin> pluginsToEnable = new ArrayList<Plugin>(keys.length);
1500
1501 for (final String key : keys)
1502 {
1503 if (key == null)
1504 {
1505 throw new IllegalArgumentException("Keys passed to enablePlugins must be non-null");
1506 }
1507
1508 final Plugin plugin = plugins.get(key);
1509 if (plugin == null)
1510 {
1511 if (log.isInfoEnabled())
1512 {
1513 log.info("No plugin was found for key '" + key + "'. Not enabling.");
1514 }
1515 continue;
1516 }
1517
1518 if (!plugin.getPluginInformation().satisfiesMinJavaVersion())
1519 {
1520 log.error("Minimum Java version of '" + plugin.getPluginInformation().getMinJavaVersion() + "' was not satisfied for module '" + key + "'. Not enabling.");
1521 continue;
1522 }
1523
1524
1525 if(plugin.getPluginState() != PluginState.ENABLED)
1526 {
1527 pluginsToEnable.add(plugin);
1528 }
1529 }
1530 final Collection<Plugin> enabledPlugins = pluginEnabler.enableAllRecursively(pluginsToEnable);
1531
1532 for (final Plugin plugin : enabledPlugins)
1533 {
1534 enablePluginState(plugin, getStore());
1535 notifyPluginEnabled(plugin);
1536 }
1537 }
1538
1539
1540
1541
1542 @Deprecated
1543 public void enablePlugin(final String key)
1544 {
1545 enablePlugins(key);
1546 }
1547
1548 protected void enablePluginState(final Plugin plugin, final PluginPersistentStateStore stateStore)
1549 {
1550 stateStore.save(getBuilder().setEnabled(plugin, true).toState());
1551 }
1552
1553
1554
1555
1556
1557
1558
1559
1560 protected void notifyPluginEnabled(final Plugin plugin)
1561 {
1562 plugin.enable();
1563 if (enableConfiguredPluginModules(plugin))
1564 {
1565 pluginEventManager.broadcast(new PluginEnabledEvent(plugin));
1566 }
1567 }
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579 private boolean enableConfiguredPluginModules(final Plugin plugin)
1580 {
1581 boolean success = true;
1582 final Set<ModuleDescriptor<?>> enabledDescriptors = new HashSet<ModuleDescriptor<?>>();
1583 for (final ModuleDescriptor<?> descriptor : plugin.getModuleDescriptors())
1584 {
1585 if (!enableConfiguredPluginModule(plugin, descriptor, enabledDescriptors))
1586 {
1587 success = false;
1588 break;
1589 }
1590 }
1591 return success;
1592 }
1593
1594 private boolean enableConfiguredPluginModule(final Plugin plugin, final ModuleDescriptor<?> descriptor, final Set<ModuleDescriptor<?>> enabledDescriptors)
1595 {
1596 boolean success = true;
1597 try
1598 {
1599
1600 if (pluginEnabler.isPluginBeingEnabled(plugin))
1601 {
1602 log.debug("The plugin is currently being enabled, so we won't bother trying to enable the '" + descriptor.getKey() + " module");
1603 return success;
1604 }
1605
1606
1607
1608 if (!isPluginModuleEnabled(descriptor.getCompleteKey()))
1609 {
1610 if (log.isDebugEnabled())
1611 {
1612 String name = descriptor.getName() == null ? descriptor.getKey() : descriptor.getName();
1613 log.debug("Plugin module '" + name + "' is explicitly disabled (or so by default), so not re-enabling.");
1614 }
1615 return success;
1616 }
1617
1618 notifyModuleEnabled(descriptor);
1619 enabledDescriptors.add(descriptor);
1620 }
1621 catch (final Throwable exception)
1622 {
1623
1624 log.error("There was an error loading the descriptor '" + descriptor.getName() + "' of plugin '" + plugin.getKey() + "'. Disabling.", exception);
1625
1626
1627 for (final ModuleDescriptor<?> desc : enabledDescriptors)
1628 {
1629 try
1630 {
1631 notifyModuleDisabled(desc);
1632 }
1633 catch (Exception ex2)
1634 {
1635 log.error("Could not notify previously enabled descriptor {} of module disabled in plugin {}",
1636 desc.getName(), plugin.getKey());
1637 log.info("Stacktrace : ", ex2);
1638 }
1639 }
1640
1641
1642 replacePluginWithUnloadablePlugin(plugin, descriptor, exception);
1643 success = false;
1644 }
1645
1646 return success;
1647 }
1648
1649 public void disablePlugin(final String key)
1650 {
1651 disablePluginInternal(key, true);
1652 }
1653
1654 public void disablePluginWithoutPersisting(final String key)
1655 {
1656 disablePluginInternal(key, false);
1657 }
1658
1659 protected void disablePluginInternal(final String key, final boolean persistDisabledState)
1660 {
1661 if (key == null)
1662 {
1663 throw new IllegalArgumentException("You must specify a plugin key to disable.");
1664 }
1665
1666 final Plugin plugin = plugins.get(key);
1667 if (plugin == null)
1668 {
1669 if (log.isInfoEnabled())
1670 {
1671 log.info("No plugin was found for key '" + key + "'. Not disabling.");
1672 }
1673 return;
1674 }
1675
1676
1677 if(plugin.getPluginState() != PluginState.DISABLED)
1678 {
1679 notifyPluginDisabled(plugin);
1680 if (persistDisabledState)
1681 {
1682 disablePluginState(plugin, getStore());
1683 }
1684 }
1685 }
1686
1687 protected void disablePluginState(final Plugin plugin, final PluginPersistentStateStore stateStore)
1688 {
1689 stateStore.save(getBuilder().setEnabled(plugin, false).toState());
1690 }
1691
1692 protected void notifyPluginDisabled(final Plugin plugin)
1693 {
1694 if (log.isInfoEnabled())
1695 {
1696 log.info("Disabling " + plugin.getKey());
1697 }
1698 broadcastIgnoreError(new BeforePluginDisabledEvent(plugin));
1699 disablePluginModules(plugin);
1700
1701
1702 plugin.disable();
1703 broadcastIgnoreError(new PluginDisabledEvent(plugin));
1704 }
1705
1706 private void disablePluginModules(final Plugin plugin)
1707 {
1708 final List<ModuleDescriptor<?>> moduleDescriptors = new ArrayList<ModuleDescriptor<?>>(plugin.getModuleDescriptors());
1709 Collections.reverse(moduleDescriptors);
1710
1711 for (final ModuleDescriptor<?> module : moduleDescriptors)
1712 {
1713
1714
1715
1716
1717
1718 disablePluginModuleNoPersist(module);
1719 }
1720 }
1721
1722 private void disablePluginModuleNoPersist(final ModuleDescriptor<?> module)
1723 {
1724 if (isPluginModuleEnabled(module.getCompleteKey()))
1725 {
1726 publishModuleDisabledEvents(module, false);
1727 }
1728 }
1729
1730 public void disablePluginModule(final String completeKey)
1731 {
1732 if (completeKey == null)
1733 {
1734 throw new IllegalArgumentException("You must specify a plugin module key to disable.");
1735 }
1736
1737 final ModuleDescriptor<?> module = getPluginModule(completeKey);
1738
1739 if (module == null)
1740 {
1741 if (log.isInfoEnabled())
1742 {
1743 log.info("Returned module for key '" + completeKey + "' was null. Not disabling.");
1744 }
1745 return;
1746 }
1747 if (module.getClass().isAnnotationPresent(CannotDisable.class))
1748 {
1749 if (log.isInfoEnabled())
1750 {
1751 log.info("Plugin module " + completeKey + " cannot be disabled; it is annotated with" + CannotDisable.class.getName());
1752 }
1753 return;
1754 }
1755 disablePluginModuleState(module, getStore());
1756 notifyModuleDisabled(module);
1757 }
1758
1759 protected void disablePluginModuleState(final ModuleDescriptor<?> module, final PluginPersistentStateStore stateStore)
1760 {
1761 stateStore.save(getBuilder().setEnabled(module, false).toState());
1762 }
1763
1764 protected void notifyModuleDisabled(final ModuleDescriptor<?> module)
1765 {
1766 publishModuleDisabledEvents(module, true);
1767 }
1768
1769 private void publishModuleDisabledEvents(final ModuleDescriptor<?> module, final boolean persistent)
1770 {
1771 if (log.isDebugEnabled())
1772 {
1773 log.debug("Disabling " + module.getKey());
1774 }
1775 broadcastIgnoreError(new BeforePluginModuleDisabledEvent(module, persistent));
1776
1777 if (module instanceof StateAware)
1778 {
1779 ((StateAware) module).disabled();
1780 }
1781
1782 broadcastIgnoreError(new PluginModuleDisabledEvent(module, persistent));
1783 }
1784
1785 public void enablePluginModule(final String completeKey)
1786 {
1787 if (completeKey == null)
1788 {
1789 throw new IllegalArgumentException("You must specify a plugin module key to disable.");
1790 }
1791
1792 final ModuleDescriptor<?> module = getPluginModule(completeKey);
1793
1794 if (module == null)
1795 {
1796 if (log.isInfoEnabled())
1797 {
1798 log.info("Returned module for key '" + completeKey + "' was null. Not enabling.");
1799 }
1800
1801 return;
1802 }
1803
1804 if (!module.satisfiesMinJavaVersion())
1805 {
1806 log.error("Minimum Java version of '" + module.getMinJavaVersion() + "' was not satisfied for module '" + completeKey + "'. Not enabling.");
1807 return;
1808 }
1809 enablePluginModuleState(module, getStore());
1810 notifyModuleEnabled(module);
1811 }
1812
1813 protected void enablePluginModuleState(final ModuleDescriptor<?> module, final PluginPersistentStateStore stateStore)
1814 {
1815 stateStore.save(getBuilder().setEnabled(module, true).toState());
1816 }
1817
1818 protected void notifyModuleEnabled(final ModuleDescriptor<?> module)
1819 {
1820 if (log.isDebugEnabled())
1821 {
1822 log.debug("Enabling " + module.getKey());
1823 }
1824 if (module instanceof StateAware)
1825 {
1826 ((StateAware) module).enabled();
1827 }
1828 pluginEventManager.broadcast(new PluginModuleEnabledEvent(module));
1829 }
1830
1831 public boolean isPluginModuleEnabled(final String completeKey)
1832 {
1833
1834 return (completeKey != null) && isPluginModuleEnabled(new ModuleCompleteKey(completeKey));
1835 }
1836
1837 private boolean isPluginModuleEnabled(final ModuleCompleteKey key)
1838 {
1839 if (!isPluginEnabled(key.getPluginKey()))
1840 {
1841 return false;
1842 }
1843 final ModuleDescriptor<?> pluginModule = getPluginModule(key);
1844 return (pluginModule != null) && getState().isEnabled(pluginModule);
1845 }
1846
1847
1848
1849
1850
1851
1852
1853
1854 public boolean isPluginEnabled(final String key)
1855 {
1856 final Plugin plugin = plugins.get(notNull("The plugin key must be specified", key));
1857
1858 return plugin != null &&
1859 plugin.getPluginState() == PluginState.ENABLED &&
1860 getState().isEnabled(plugin) &&
1861 !pluginEnabler.isPluginBeingEnabled(plugin);
1862 }
1863
1864 public InputStream getDynamicResourceAsStream(final String name)
1865 {
1866 return getClassLoader().getResourceAsStream(name);
1867 }
1868
1869 public Class<?> getDynamicPluginClass(final String className) throws ClassNotFoundException
1870 {
1871 return getClassLoader().loadClass(className);
1872 }
1873
1874 public PluginsClassLoader getClassLoader()
1875 {
1876 return classLoader;
1877 }
1878
1879 public InputStream getPluginResourceAsStream(final String pluginKey, final String resourcePath)
1880 {
1881 final Plugin plugin = getEnabledPlugin(pluginKey);
1882 if (plugin == null)
1883 {
1884 log.error("Attempted to retreive resource " + resourcePath + " for non-existent or inactive plugin " + pluginKey);
1885 return null;
1886 }
1887
1888 return plugin.getResourceAsStream(resourcePath);
1889 }
1890
1891
1892
1893
1894
1895
1896
1897
1898
1899 private UnloadablePlugin replacePluginWithUnloadablePlugin(final Plugin plugin, final ModuleDescriptor<?> descriptor, final Throwable throwable)
1900 {
1901 final UnloadableModuleDescriptor unloadableDescriptor = UnloadableModuleDescriptorFactory.createUnloadableModuleDescriptor(plugin,
1902 descriptor, throwable);
1903 final UnloadablePlugin unloadablePlugin = UnloadablePluginFactory.createUnloadablePlugin(plugin, unloadableDescriptor);
1904
1905
1906
1907 unloadablePlugin.setErrorText(unloadableDescriptor.getErrorText());
1908 plugins.put(plugin.getKey(), unloadablePlugin);
1909
1910
1911
1912
1913
1914 return unloadablePlugin;
1915 }
1916
1917 public boolean isSystemPlugin(final String key)
1918 {
1919 final Plugin plugin = getPlugin(key);
1920 return (plugin != null) && plugin.isSystemPlugin();
1921 }
1922
1923 public PluginRestartState getPluginRestartState(final String key)
1924 {
1925 return getState().getPluginRestartState(key);
1926 }
1927
1928 private Builder getBuilder()
1929 {
1930 return PluginPersistentState.Builder.create(getStore().load());
1931 }
1932
1933 private void broadcastIgnoreError(Object event)
1934 {
1935 try
1936 {
1937 pluginEventManager.broadcast(event);
1938 }
1939 catch (NotificationException ex)
1940 {
1941 log.warn("Error broadcasting '{}': {}. Continuing anyway.", event.getClass().getName(), ex.getMessage());
1942 if (log.isDebugEnabled())
1943 {
1944 log.debug(ex.getMessage(), ex);
1945 }
1946 }
1947 }
1948
1949
1950
1951
1952 @Deprecated
1953 public void setDescriptorParserFactory(final DescriptorParserFactory descriptorParserFactory)
1954 {
1955 }
1956 }