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