PageRenderTime 38ms CodeModel.GetById 15ms RepoModel.GetById 0ms app.codeStats 0ms

/atlassian-plugins-core/src/main/java/com/atlassian/plugin/manager/DefaultPluginManager.java

https://bitbucket.org/mrdon/atlassian-plugins
Java | 1263 lines | 886 code | 122 blank | 255 comment | 117 complexity | 77babbe0960dcf480cfb1ce896779241 MD5 | raw file
  1. package com.atlassian.plugin.manager;
  2. import static com.atlassian.plugin.util.Assertions.notNull;
  3. import static com.atlassian.plugin.util.collect.CollectionUtil.toList;
  4. import static com.google.common.collect.Iterables.concat;
  5. import static com.google.common.collect.Iterables.filter;
  6. import static com.google.common.collect.Iterables.transform;
  7. import static com.google.common.collect.Maps.filterKeys;
  8. import com.atlassian.plugin.*;
  9. import com.atlassian.plugin.classloader.PluginsClassLoader;
  10. import com.atlassian.plugin.descriptors.CannotDisable;
  11. import com.atlassian.plugin.descriptors.UnloadableModuleDescriptor;
  12. import com.atlassian.plugin.descriptors.UnloadableModuleDescriptorFactory;
  13. import com.atlassian.plugin.event.NotificationException;
  14. import com.atlassian.plugin.event.PluginEventListener;
  15. import com.atlassian.plugin.event.PluginEventManager;
  16. import com.atlassian.plugin.event.events.PluginContainerUnavailableEvent;
  17. import com.atlassian.plugin.event.events.PluginDisabledEvent;
  18. import com.atlassian.plugin.event.events.PluginEnabledEvent;
  19. import com.atlassian.plugin.event.events.PluginFrameworkShutdownEvent;
  20. import com.atlassian.plugin.event.events.PluginFrameworkStartedEvent;
  21. import com.atlassian.plugin.event.events.PluginFrameworkStartingEvent;
  22. import com.atlassian.plugin.event.events.PluginFrameworkWarmRestartedEvent;
  23. import com.atlassian.plugin.event.events.PluginFrameworkWarmRestartingEvent;
  24. import com.atlassian.plugin.event.events.PluginModuleAvailableEvent;
  25. import com.atlassian.plugin.event.events.PluginModuleDisabledEvent;
  26. import com.atlassian.plugin.event.events.PluginModuleEnabledEvent;
  27. import com.atlassian.plugin.event.events.PluginModuleUnavailableEvent;
  28. import com.atlassian.plugin.event.events.PluginRefreshedEvent;
  29. import com.atlassian.plugin.event.events.PluginUninstalledEvent;
  30. import com.atlassian.plugin.event.events.PluginUpgradedEvent;
  31. import com.atlassian.plugin.impl.UnloadablePlugin;
  32. import com.atlassian.plugin.impl.UnloadablePluginFactory;
  33. import com.atlassian.plugin.loaders.DynamicPluginLoader;
  34. import com.atlassian.plugin.loaders.PluginLoader;
  35. import com.atlassian.plugin.manager.PluginPersistentState.Builder;
  36. import com.atlassian.plugin.metadata.ClasspathFilePluginMetadata;
  37. import com.atlassian.plugin.metadata.RequiredPluginValidator;
  38. import com.atlassian.plugin.parsers.DescriptorParserFactory;
  39. import com.atlassian.plugin.predicate.EnabledModulePredicate;
  40. import com.atlassian.plugin.predicate.EnabledPluginPredicate;
  41. import com.atlassian.plugin.predicate.ModuleDescriptorOfClassPredicate;
  42. import com.atlassian.plugin.predicate.ModuleDescriptorOfTypePredicate;
  43. import com.atlassian.plugin.predicate.ModuleDescriptorPredicate;
  44. import com.atlassian.plugin.predicate.ModuleOfClassPredicate;
  45. import com.atlassian.plugin.predicate.PluginPredicate;
  46. import com.atlassian.plugin.util.PluginUtils;
  47. import com.atlassian.util.concurrent.CopyOnWriteMap;
  48. import org.apache.commons.lang.time.StopWatch;
  49. import org.slf4j.Logger;
  50. import org.slf4j.LoggerFactory;
  51. import com.google.common.base.Function;
  52. import com.google.common.base.Predicate;
  53. import java.io.InputStream;
  54. import java.util.ArrayList;
  55. import java.util.Collection;
  56. import java.util.Collections;
  57. import java.util.HashMap;
  58. import java.util.HashSet;
  59. import java.util.LinkedHashMap;
  60. import java.util.LinkedList;
  61. import java.util.List;
  62. import java.util.Map;
  63. import java.util.Set;
  64. import java.util.TreeSet;
  65. /**
  66. * This implementation delegates the initiation and classloading of plugins to a
  67. * list of {@link com.atlassian.plugin.loaders.PluginLoader}s and records the
  68. * state of plugins in a
  69. * {@link com.atlassian.plugin.manager.PluginPersistentStateStore}.
  70. * <p/>
  71. * This class is responsible for enabling and disabling plugins and plugin
  72. * modules and reflecting these state changes in the PluginPersistentStateStore.
  73. * <p/>
  74. * An interesting quirk in the design is that
  75. * {@link #installPlugin(com.atlassian.plugin.PluginArtifact)} explicitly stores
  76. * the plugin via a {@link com.atlassian.plugin.PluginInstaller}, whereas
  77. * {@link #uninstall(Plugin)} relies on the underlying
  78. * {@link com.atlassian.plugin.loaders.PluginLoader} to remove the plugin if
  79. * necessary.
  80. */
  81. public class DefaultPluginManager implements PluginController, PluginAccessor, PluginSystemLifecycle
  82. {
  83. private static final Logger log = LoggerFactory.getLogger(DefaultPluginManager.class);
  84. private final List<PluginLoader> pluginLoaders;
  85. private final PluginPersistentStateStore store;
  86. private final ModuleDescriptorFactory moduleDescriptorFactory;
  87. private final PluginEventManager pluginEventManager;
  88. private final Map<String, Plugin> plugins = CopyOnWriteMap.<String, Plugin> builder().stableViews().newHashMap();
  89. private final PluginsClassLoader classLoader;
  90. private final PluginEnabler pluginEnabler = new PluginEnabler(this, this);
  91. private final StateTracker tracker = new StateTracker();
  92. private final boolean verifyRequiredPlugins;
  93. /**
  94. * Installer used for storing plugins. Used by
  95. * {@link #installPlugin(PluginArtifact)}.
  96. */
  97. private RevertablePluginInstaller pluginInstaller = new NoOpRevertablePluginInstaller(new UnsupportedPluginInstaller());
  98. /**
  99. * Stores {@link Plugin}s as a key and {@link PluginLoader} as a value.
  100. */
  101. private final Map<Plugin, PluginLoader> pluginToPluginLoader = new HashMap<Plugin, PluginLoader>();
  102. public DefaultPluginManager(final PluginPersistentStateStore store, final List<PluginLoader> pluginLoaders, final ModuleDescriptorFactory moduleDescriptorFactory, final PluginEventManager pluginEventManager)
  103. {
  104. this(store, pluginLoaders, moduleDescriptorFactory, pluginEventManager, false);
  105. }
  106. public DefaultPluginManager(final PluginPersistentStateStore store, final List<PluginLoader> pluginLoaders, final ModuleDescriptorFactory moduleDescriptorFactory, final PluginEventManager pluginEventManager, boolean verifyRequiredPlugins)
  107. {
  108. this.pluginLoaders = notNull("Plugin Loaders list must not be null.", pluginLoaders);
  109. this.store = notNull("PluginPersistentStateStore must not be null.", store);
  110. this.moduleDescriptorFactory = notNull("ModuleDescriptorFactory must not be null.", moduleDescriptorFactory);
  111. this.pluginEventManager = notNull("PluginEventManager must not be null.", pluginEventManager);
  112. this.pluginEventManager.register(this);
  113. this.verifyRequiredPlugins = verifyRequiredPlugins;
  114. classLoader = new PluginsClassLoader(null, this, pluginEventManager);
  115. }
  116. public void init() throws PluginParseException, NotificationException
  117. {
  118. tracker.setState(StateTracker.State.STARTING);
  119. final StopWatch stopWatch = new StopWatch();
  120. stopWatch.start();
  121. log.info("Initialising the plugin system");
  122. pluginEventManager.broadcast(new PluginFrameworkStartingEvent(this, this));
  123. pluginInstaller.clearBackups();
  124. for (final PluginLoader loader : pluginLoaders)
  125. {
  126. if (loader == null)
  127. {
  128. continue;
  129. }
  130. final Iterable<Plugin> possiblePluginsToLoad = loader.loadAllPlugins(moduleDescriptorFactory);
  131. final Collection<Plugin> pluginsToLoad = new ArrayList<Plugin>();
  132. for (final Plugin plugin : possiblePluginsToLoad)
  133. {
  134. if (getState().getPluginRestartState(plugin.getKey()) == PluginRestartState.REMOVE)
  135. {
  136. if (log.isInfoEnabled())
  137. {
  138. log.info("Plugin " + plugin.getKey() + " was marked to be removed on restart. Removing now.");
  139. }
  140. loader.removePlugin(plugin);
  141. // PLUG-13: Plugins should not save state across uninstalls.
  142. removeStateFromStore(getStore(), plugin);
  143. }
  144. else
  145. {
  146. pluginsToLoad.add(plugin);
  147. }
  148. }
  149. addPlugins(loader, pluginsToLoad);
  150. }
  151. getStore().save(getBuilder().clearPluginRestartState().toState());
  152. stopWatch.stop();
  153. log.info("Plugin system started in " + stopWatch);
  154. tracker.setState(StateTracker.State.STARTED);
  155. if (verifyRequiredPlugins)
  156. {
  157. validateRequiredPlugins();
  158. }
  159. pluginEventManager.broadcast(new PluginFrameworkStartedEvent(this, this));
  160. }
  161. private void validateRequiredPlugins() throws PluginException
  162. {
  163. RequiredPluginValidator validator = new RequiredPluginValidator(this, new ClasspathFilePluginMetadata());
  164. Collection<String> errors = validator.validate();
  165. if (errors.size() > 0)
  166. {
  167. log.error("Unable to validate required plugins or modules - plugin system shutting down");
  168. log.error("Failures:");
  169. for (String error : errors)
  170. {
  171. log.error("\t{}", error);
  172. }
  173. shutdown();
  174. throw new PluginException("Unable to validate required plugins or modules");
  175. }
  176. }
  177. /**
  178. * Fires the shutdown event
  179. *
  180. * @since 2.0.0
  181. * @throws IllegalStateException if already shutdown or already in the
  182. * process of shutting down.
  183. */
  184. public void shutdown()
  185. {
  186. tracker.setState(StateTracker.State.SHUTTING_DOWN);
  187. log.info("Shutting down the plugin system");
  188. try
  189. {
  190. pluginEventManager.broadcast(new PluginFrameworkShutdownEvent(this, this));
  191. }
  192. catch (final NotificationException ex)
  193. {
  194. log.error("At least one error occured while broadcasting the PluginFrameworkShutdownEvent. We will continue to shutdown the Plugin Manager anyway.");
  195. }
  196. plugins.clear();
  197. pluginEventManager.unregister(this);
  198. tracker.setState(StateTracker.State.SHUTDOWN);
  199. }
  200. public final void warmRestart()
  201. {
  202. tracker.setState(StateTracker.State.WARM_RESTARTING);
  203. log.info("Initiating a warm restart of the plugin system");
  204. pluginEventManager.broadcast(new PluginFrameworkWarmRestartingEvent(this, this));
  205. // Make sure we reload plugins in order
  206. final List<Plugin> restartedPlugins = new ArrayList<Plugin>();
  207. final List<PluginLoader> loaders = new ArrayList<PluginLoader>(pluginLoaders);
  208. Collections.reverse(loaders);
  209. for (final PluginLoader loader : pluginLoaders)
  210. {
  211. for (final Map.Entry<Plugin, PluginLoader> entry : pluginToPluginLoader.entrySet())
  212. {
  213. if (entry.getValue() == loader)
  214. {
  215. final Plugin plugin = entry.getKey();
  216. if (isPluginEnabled(plugin.getKey()))
  217. {
  218. disablePluginModules(plugin);
  219. restartedPlugins.add(plugin);
  220. }
  221. }
  222. }
  223. }
  224. // then enable them in reverse order
  225. Collections.reverse(restartedPlugins);
  226. for (final Plugin plugin : restartedPlugins)
  227. {
  228. enableConfiguredPluginModules(plugin);
  229. }
  230. pluginEventManager.broadcast(new PluginFrameworkWarmRestartedEvent(this, this));
  231. tracker.setState(StateTracker.State.STARTED);
  232. }
  233. @PluginEventListener
  234. public void onPluginModuleAvailable(final PluginModuleAvailableEvent event)
  235. {
  236. enableConfiguredPluginModule(event.getModule().getPlugin(), event.getModule(), new HashSet<ModuleDescriptor<?>>());
  237. }
  238. @PluginEventListener
  239. public void onPluginModuleUnavailable(final PluginModuleUnavailableEvent event)
  240. {
  241. notifyModuleDisabled(event.getModule());
  242. }
  243. @PluginEventListener
  244. public void onPluginContainerUnavailable(final PluginContainerUnavailableEvent event)
  245. {
  246. disablePluginWithoutPersisting(event.getPluginKey());
  247. }
  248. @PluginEventListener
  249. public void onPluginRefresh(final PluginRefreshedEvent event)
  250. {
  251. final Plugin plugin = event.getPlugin();
  252. disablePluginModules(plugin);
  253. // enable the plugin, shamefully copied from notifyPluginEnabled()
  254. if (enableConfiguredPluginModules(plugin))
  255. {
  256. pluginEventManager.broadcast(new PluginEnabledEvent(plugin));
  257. }
  258. }
  259. /**
  260. * Set the plugin installation strategy for this manager
  261. *
  262. * @param pluginInstaller the plugin installation strategy to use
  263. * @see PluginInstaller
  264. */
  265. public void setPluginInstaller(final PluginInstaller pluginInstaller)
  266. {
  267. if (pluginInstaller instanceof RevertablePluginInstaller)
  268. {
  269. this.pluginInstaller = (RevertablePluginInstaller) pluginInstaller;
  270. }
  271. else
  272. {
  273. this.pluginInstaller = new NoOpRevertablePluginInstaller(pluginInstaller);
  274. }
  275. }
  276. protected final PluginPersistentStateStore getStore()
  277. {
  278. return store;
  279. }
  280. public String installPlugin(final PluginArtifact pluginArtifact) throws PluginParseException
  281. {
  282. final Set<String> keys = installPlugins(pluginArtifact);
  283. if ((keys != null) && (keys.size() == 1))
  284. {
  285. return keys.iterator().next();
  286. }
  287. else
  288. {
  289. // should never happen
  290. throw new PluginParseException("Could not install plugin");
  291. }
  292. }
  293. public Set<String> installPlugins(final PluginArtifact... pluginArtifacts) throws PluginParseException
  294. {
  295. final Map<String, PluginArtifact> validatedArtifacts = new LinkedHashMap<String, PluginArtifact>();
  296. try
  297. {
  298. for (final PluginArtifact pluginArtifact : pluginArtifacts)
  299. {
  300. validatedArtifacts.put(validatePlugin(pluginArtifact), pluginArtifact);
  301. }
  302. }
  303. catch (final PluginParseException ex)
  304. {
  305. throw new PluginParseException("All plugins could not be validated", ex);
  306. }
  307. for (final Map.Entry<String, PluginArtifact> entry : validatedArtifacts.entrySet())
  308. {
  309. pluginInstaller.installPlugin(entry.getKey(), entry.getValue());
  310. }
  311. scanForNewPlugins();
  312. return validatedArtifacts.keySet();
  313. }
  314. /**
  315. * Validate a plugin jar. Looks through all plugin loaders for ones that can
  316. * load the plugin and extract the plugin key as proof.
  317. *
  318. * @param pluginArtifact the jar file representing the plugin
  319. * @return The plugin key
  320. * @throws PluginParseException if the plugin cannot be parsed
  321. * @throws NullPointerException if <code>pluginJar</code> is null.
  322. */
  323. String validatePlugin(final PluginArtifact pluginArtifact) throws PluginParseException
  324. {
  325. boolean foundADynamicPluginLoader = false;
  326. for (final PluginLoader loader : pluginLoaders)
  327. {
  328. if (loader instanceof DynamicPluginLoader)
  329. {
  330. foundADynamicPluginLoader = true;
  331. final String key = ((DynamicPluginLoader) loader).canLoad(pluginArtifact);
  332. if (key != null)
  333. {
  334. return key;
  335. }
  336. }
  337. }
  338. if (!foundADynamicPluginLoader)
  339. {
  340. throw new IllegalStateException("Should be at least one DynamicPluginLoader in the plugin loader list");
  341. }
  342. throw new PluginParseException("Jar " + pluginArtifact.getName() + " is not a valid plugin");
  343. }
  344. public int scanForNewPlugins() throws PluginParseException
  345. {
  346. int numberFound = 0;
  347. for (final PluginLoader loader : pluginLoaders)
  348. {
  349. if (loader != null)
  350. {
  351. if (loader.supportsAddition())
  352. {
  353. final List<Plugin> pluginsToAdd = new ArrayList<Plugin>();
  354. for (Plugin plugin : loader.addFoundPlugins(moduleDescriptorFactory))
  355. {
  356. final Plugin oldPlugin = plugins.get(plugin.getKey());
  357. // Only actually install the plugin if its module
  358. // descriptors support it. Otherwise, mark it as
  359. // unloadable.
  360. if (!(plugin instanceof UnloadablePlugin))
  361. {
  362. if (PluginUtils.doesPluginRequireRestart(plugin))
  363. {
  364. if (oldPlugin == null)
  365. {
  366. markPluginInstallThatRequiresRestart(plugin);
  367. final UnloadablePlugin unloadablePlugin = UnloadablePluginFactory.createUnloadablePlugin(plugin);
  368. unloadablePlugin.setErrorText("Plugin requires a restart of the application due " + "to the following modules: " + PluginUtils.getPluginModulesThatRequireRestart(plugin));
  369. plugin = unloadablePlugin;
  370. }
  371. else
  372. {
  373. // If a plugin has been installed but is waiting for restart then we do not want to
  374. // put the plugin into the update state, we want to keep it in the install state.
  375. if (!PluginRestartState.INSTALL.equals(getPluginRestartState(plugin.getKey())))
  376. {
  377. markPluginUpgradeThatRequiresRestart(plugin);
  378. }
  379. continue;
  380. }
  381. }
  382. // If the new plugin does not require restart we need to check what the restart state of
  383. // the old plugin was and act accordingly
  384. else if (oldPlugin != null && PluginUtils.doesPluginRequireRestart(oldPlugin))
  385. {
  386. // If you have installed the plugin that requires restart and before restart you have
  387. // reinstalled a version of that plugin that does not require restart then you should
  388. // just go ahead and install that plugin. This means reverting the previous install
  389. // and letting the plugin fall into the plugins to add list
  390. if (PluginRestartState.INSTALL.equals(getPluginRestartState(oldPlugin.getKey())))
  391. {
  392. revertRestartRequiredChange(oldPlugin.getKey());
  393. }
  394. else
  395. {
  396. markPluginUpgradeThatRequiresRestart(plugin);
  397. continue;
  398. }
  399. }
  400. pluginsToAdd.add(plugin);
  401. }
  402. }
  403. addPlugins(loader, pluginsToAdd);
  404. numberFound = pluginsToAdd.size();
  405. }
  406. }
  407. }
  408. return numberFound;
  409. }
  410. private void markPluginInstallThatRequiresRestart(final Plugin plugin)
  411. {
  412. if (log.isInfoEnabled())
  413. {
  414. log.info("Installed plugin '" + plugin.getKey() + "' requires a restart due to the following modules: " + PluginUtils.getPluginModulesThatRequireRestart(plugin));
  415. }
  416. updateRequiresRestartState(plugin.getKey(), PluginRestartState.INSTALL);
  417. }
  418. private void markPluginUpgradeThatRequiresRestart(final Plugin plugin)
  419. {
  420. if (log.isInfoEnabled())
  421. {
  422. log.info("Upgraded plugin '" + plugin.getKey() + "' requires a restart due to the following modules: " + PluginUtils.getPluginModulesThatRequireRestart(plugin));
  423. }
  424. updateRequiresRestartState(plugin.getKey(), PluginRestartState.UPGRADE);
  425. }
  426. private void markPluginUninstallThatRequiresRestart(final Plugin plugin)
  427. {
  428. if (log.isInfoEnabled())
  429. {
  430. log.info("Uninstalled plugin '" + plugin.getKey() + "' requires a restart due to the following modules: " + PluginUtils.getPluginModulesThatRequireRestart(plugin));
  431. }
  432. updateRequiresRestartState(plugin.getKey(), PluginRestartState.REMOVE);
  433. }
  434. private void updateRequiresRestartState(final String pluginKey, final PluginRestartState pluginRestartState)
  435. {
  436. getStore().save(getBuilder().setPluginRestartState(pluginKey, pluginRestartState).toState());
  437. onUpdateRequiresRestartState(pluginKey, pluginRestartState);
  438. }
  439. protected void onUpdateRequiresRestartState(final String pluginKey, final PluginRestartState pluginRestartState)
  440. {
  441. // nothing to do in this implementation
  442. }
  443. /**
  444. * Uninstalls the given plugin, emitting disabled and uninstalled events as it does so.
  445. * @param plugin the plugin to uninstall.
  446. * @throws PluginException If the plugin or loader doesn't support
  447. * uninstallation
  448. */
  449. public void uninstall(final Plugin plugin) throws PluginException
  450. {
  451. if (PluginUtils.doesPluginRequireRestart(plugin))
  452. {
  453. ensurePluginAndLoaderSupportsUninstall(plugin);
  454. markPluginUninstallThatRequiresRestart(plugin);
  455. }
  456. else
  457. {
  458. // Explicitly disable any plugins that require this plugin
  459. disableDependentPlugins(plugin);
  460. uninstallNoEvent(plugin);
  461. pluginEventManager.broadcast(new PluginUninstalledEvent(plugin));
  462. }
  463. }
  464. /**
  465. * Preforms an uninstallation without broadcasting the uninstallation event.
  466. *
  467. * @param plugin The plugin to uninstall
  468. * @since 2.5.0
  469. */
  470. protected void uninstallNoEvent(final Plugin plugin)
  471. {
  472. unloadPlugin(plugin);
  473. // PLUG-13: Plugins should not save state across uninstalls.
  474. removeStateFromStore(getStore(), plugin);
  475. }
  476. /**
  477. * @param pluginKey The plugin key to revert
  478. * @throws PluginException If the revert cannot be completed
  479. */
  480. public void revertRestartRequiredChange(final String pluginKey) throws PluginException
  481. {
  482. notNull("pluginKey", pluginKey);
  483. final PluginRestartState restartState = getState().getPluginRestartState(pluginKey);
  484. if (restartState == PluginRestartState.UPGRADE)
  485. {
  486. pluginInstaller.revertInstalledPlugin(pluginKey);
  487. }
  488. else if (restartState == PluginRestartState.INSTALL)
  489. {
  490. pluginInstaller.revertInstalledPlugin(pluginKey);
  491. plugins.remove(pluginKey);
  492. }
  493. updateRequiresRestartState(pluginKey, PluginRestartState.NONE);
  494. }
  495. protected void removeStateFromStore(final PluginPersistentStateStore stateStore, final Plugin plugin)
  496. {
  497. final PluginPersistentState.Builder builder = PluginPersistentState.Builder.create(stateStore.load()).removeState(plugin.getKey());
  498. for (final ModuleDescriptor<?> moduleDescriptor : plugin.getModuleDescriptors())
  499. {
  500. builder.removeState(moduleDescriptor.getCompleteKey());
  501. }
  502. stateStore.save(builder.toState());
  503. }
  504. /**
  505. * Unload a plugin. Called when plugins are added locally, or remotely in a
  506. * clustered application.
  507. *
  508. * @param plugin the plugin to remove
  509. * @throws PluginException if the plugin cannot be uninstalled
  510. */
  511. protected void unloadPlugin(final Plugin plugin) throws PluginException
  512. {
  513. final PluginLoader loader = ensurePluginAndLoaderSupportsUninstall(plugin);
  514. if (isPluginEnabled(plugin.getKey()))
  515. {
  516. notifyPluginDisabled(plugin);
  517. }
  518. notifyUninstallPlugin(plugin);
  519. if (loader != null)
  520. {
  521. removePluginFromLoader(plugin);
  522. }
  523. plugins.remove(plugin.getKey());
  524. }
  525. private PluginLoader ensurePluginAndLoaderSupportsUninstall(final Plugin plugin)
  526. {
  527. if (!plugin.isUninstallable())
  528. {
  529. throw new PluginException("Plugin is not uninstallable: " + plugin.getKey());
  530. }
  531. final PluginLoader loader = pluginToPluginLoader.get(plugin);
  532. if ((loader != null) && !loader.supportsRemoval())
  533. {
  534. throw new PluginException("Not uninstalling plugin - loader doesn't allow removal. Plugin: " + plugin.getKey());
  535. }
  536. return loader;
  537. }
  538. private void removePluginFromLoader(final Plugin plugin) throws PluginException
  539. {
  540. if (plugin.isDeleteable())
  541. {
  542. final PluginLoader pluginLoader = pluginToPluginLoader.get(plugin);
  543. pluginLoader.removePlugin(plugin);
  544. }
  545. pluginToPluginLoader.remove(plugin);
  546. }
  547. protected void notifyUninstallPlugin(final Plugin plugin)
  548. {
  549. classLoader.notifyUninstallPlugin(plugin);
  550. for (final ModuleDescriptor<?> descriptor : plugin.getModuleDescriptors())
  551. {
  552. descriptor.destroy(plugin);
  553. }
  554. }
  555. protected PluginPersistentState getState()
  556. {
  557. return getStore().load();
  558. }
  559. /**
  560. * @deprecated Since 2.0.2, use {@link
  561. * #addPlugins(PluginLoader,Collection<Plugin>...)} instead
  562. */
  563. @Deprecated
  564. protected void addPlugin(final PluginLoader loader, final Plugin plugin) throws PluginParseException
  565. {
  566. addPlugins(loader, Collections.singletonList(plugin));
  567. }
  568. /**
  569. * Update the local plugin state and enable state aware modules.
  570. * <p>
  571. * If there is an existing plugin with the same key, the version strings of
  572. * the existing plugin and the plugin provided to this method will be parsed
  573. * and compared. If the installed version is newer than the provided
  574. * version, it will not be changed. If the specified plugin's version is the
  575. * same or newer, the existing plugin state will be saved and the plugin
  576. * will be unloaded before the provided plugin is installed. If the existing
  577. * plugin cannot be unloaded a {@link PluginException} will be thrown.
  578. *
  579. * @param loader the loader used to load this plugin
  580. * @param pluginsToInstall the plugins to add
  581. * @throws PluginParseException if the plugin cannot be parsed
  582. * @since 2.0.2
  583. */
  584. protected void addPlugins(final PluginLoader loader, final Collection<Plugin> pluginsToInstall) throws PluginParseException
  585. {
  586. final List<Plugin> pluginsToEnable = new ArrayList<Plugin>();
  587. // Install plugins, looking for upgrades and duplicates
  588. for (final Plugin plugin : new TreeSet<Plugin>(pluginsToInstall))
  589. {
  590. boolean pluginUpgraded = false;
  591. // testing to make sure plugin keys are unique
  592. final Plugin existingPlugin = plugins.get(plugin.getKey());
  593. if (existingPlugin != null)
  594. {
  595. if (plugin.compareTo(existingPlugin) >= 0)
  596. {
  597. try
  598. {
  599. // Disable all plugins now before the upgrade, as an upgrade of a bundled plugin
  600. // may trigger a package refresh, bringing down dependent plugins
  601. pluginsToEnable.addAll(disableDependentPlugins(plugin));
  602. updatePlugin(existingPlugin, plugin);
  603. pluginsToEnable.remove(existingPlugin);
  604. pluginUpgraded = true;
  605. }
  606. catch (final PluginException e)
  607. {
  608. throw new PluginParseException(
  609. "Duplicate plugin found (installed version is the same or older) and could not be unloaded: '" + plugin.getKey() + "'", e);
  610. }
  611. }
  612. else
  613. {
  614. // If we find an older plugin, don't error, just ignore it.
  615. // PLUG-12.
  616. if (log.isDebugEnabled())
  617. {
  618. log.debug("Duplicate plugin found (installed version is newer): '" + plugin.getKey() + "'");
  619. }
  620. // and don't install the older plugin
  621. continue;
  622. }
  623. }
  624. plugin.install();
  625. final boolean isPluginEnabled = getState().isEnabled(plugin);
  626. if (isPluginEnabled)
  627. {
  628. pluginsToEnable.add(plugin);
  629. }
  630. if (plugin.isSystemPlugin() && !isPluginEnabled)
  631. {
  632. log.warn("System plugin is disabled: " + plugin.getKey());
  633. }
  634. if (pluginUpgraded)
  635. {
  636. pluginEventManager.broadcast(new PluginUpgradedEvent(plugin));
  637. }
  638. plugins.put(plugin.getKey(), plugin);
  639. pluginToPluginLoader.put(plugin, loader);
  640. }
  641. // enable all plugins, waiting a time period for them to enable
  642. pluginEnabler.enable(pluginsToEnable);
  643. // handle the plugins that were able to be successfully enabled
  644. for (final Plugin plugin : pluginsToEnable)
  645. {
  646. if (plugin.getPluginState() == PluginState.ENABLED)
  647. {
  648. // This method enables the plugin modules
  649. if (enableConfiguredPluginModules(plugin))
  650. {
  651. pluginEventManager.broadcast(new PluginEnabledEvent(plugin));
  652. }
  653. }
  654. }
  655. }
  656. /**
  657. * Disables all dependent plugins to prevent a dependent plugin trying to access, indirectly,
  658. * the felix global lock, which is held by the PackageAdmin while refreshing.
  659. * see http://studio.atlassian.com/browse/PLUG-582
  660. * @param plugin The plugin to disable
  661. * @return A set of plugins that were disabled
  662. */
  663. private Set<Plugin> disableDependentPlugins(final Plugin plugin)
  664. {
  665. final Set<Plugin> dependentPlugins = new HashSet<Plugin>();
  666. final Set<String> dependentPluginKeys = new HashSet<String>();
  667. for (final Plugin depPlugin : getEnabledPlugins())
  668. {
  669. if ((plugin != depPlugin) && depPlugin.getRequiredPlugins().contains(plugin.getKey()))
  670. {
  671. dependentPlugins.add(depPlugin);
  672. dependentPluginKeys.add(depPlugin.getKey());
  673. }
  674. }
  675. if (log.isInfoEnabled())
  676. {
  677. log.info("Found dependent enabled plugins for uninstalled plugin '" + plugin.getKey() + "': " + dependentPluginKeys + ". Disabling...");
  678. }
  679. for (final Plugin depPlugin : dependentPlugins)
  680. {
  681. disablePluginWithoutPersisting(depPlugin.getKey());
  682. }
  683. return dependentPlugins;
  684. }
  685. /**
  686. * Replace an already loaded plugin with another version. Relevant stored
  687. * configuration for the plugin will be preserved.
  688. *
  689. * @param oldPlugin Plugin to replace
  690. * @param newPlugin New plugin to install
  691. * @throws PluginException if the plugin cannot be updated
  692. */
  693. protected void updatePlugin(final Plugin oldPlugin, final Plugin newPlugin) throws PluginException
  694. {
  695. if (!oldPlugin.getKey().equals(newPlugin.getKey()))
  696. {
  697. throw new IllegalArgumentException("New plugin must have the same key as the old plugin");
  698. }
  699. if (log.isInfoEnabled())
  700. {
  701. log.info("Updating plugin '" + oldPlugin + "' to '" + newPlugin + "'");
  702. }
  703. // Preserve the old plugin configuration - uninstall changes it (as
  704. // disable is called on all modules) and then
  705. // removes it
  706. final Map<String, Boolean> oldPluginState = new HashMap<String, Boolean>(getState().getPluginStateMap(oldPlugin));
  707. if (log.isDebugEnabled())
  708. {
  709. log.debug("Uninstalling old plugin: " + oldPlugin);
  710. }
  711. uninstallNoEvent(oldPlugin);
  712. if (log.isDebugEnabled())
  713. {
  714. log.debug("Plugin uninstalled '" + oldPlugin + "', preserving old state");
  715. }
  716. // Build a set of module keys from the new plugin version
  717. final Set<String> newModuleKeys = new HashSet<String>();
  718. newModuleKeys.add(newPlugin.getKey());
  719. for (final ModuleDescriptor<?> moduleDescriptor : newPlugin.getModuleDescriptors())
  720. {
  721. newModuleKeys.add(moduleDescriptor.getCompleteKey());
  722. }
  723. // for removing any keys from the old plugin state that do not exist in
  724. // the
  725. // new version
  726. final Predicate<String> filter = new Predicate<String>()
  727. {
  728. public boolean apply(final String o)
  729. {
  730. return newModuleKeys.contains(o);
  731. }
  732. };
  733. getStore().save(getBuilder().addState(filterKeys(oldPluginState, filter)).toState());
  734. }
  735. public Collection<Plugin> getPlugins()
  736. {
  737. return plugins.values();
  738. }
  739. /**
  740. * @see PluginAccessor#getPlugins(com.atlassian.plugin.predicate.PluginPredicate)
  741. * @since 0.17
  742. */
  743. public Collection<Plugin> getPlugins(final PluginPredicate pluginPredicate)
  744. {
  745. return toList(filter(getPlugins(), new Predicate<Plugin>()
  746. {
  747. public boolean apply(final Plugin plugin)
  748. {
  749. return pluginPredicate.matches(plugin);
  750. }
  751. }));
  752. }
  753. /**
  754. * @see PluginAccessor#getEnabledPlugins()
  755. */
  756. public Collection<Plugin> getEnabledPlugins()
  757. {
  758. return getPlugins(new EnabledPluginPredicate(this));
  759. }
  760. /**
  761. * @see PluginAccessor#getModules(com.atlassian.plugin.predicate.ModuleDescriptorPredicate)
  762. * @since 0.17
  763. */
  764. public <M> Collection<M> getModules(final ModuleDescriptorPredicate<M> moduleDescriptorPredicate)
  765. {
  766. return toList(getModules(getModuleDescriptors(moduleDescriptorPredicate)));
  767. }
  768. /**
  769. * @see PluginAccessor#getModuleDescriptors(com.atlassian.plugin.predicate.ModuleDescriptorPredicate)
  770. * @since 0.17
  771. */
  772. public <M> Collection<ModuleDescriptor<M>> getModuleDescriptors(final ModuleDescriptorPredicate<M> moduleDescriptorPredicate)
  773. {
  774. return toList(getModuleDescriptors(getPlugins(), moduleDescriptorPredicate));
  775. }
  776. /**
  777. * Get the all the module descriptors from the given collection of plugins,
  778. * filtered by the predicate.
  779. * <p>
  780. * Be careful, your predicate must filter ModuleDescriptors that are not M,
  781. * this method does not guarantee that the descriptors are of the correct
  782. * type by itself.
  783. *
  784. * @param plugins a collection of {@link Plugin}s
  785. * @return a collection of {@link ModuleDescriptor descriptors}
  786. */
  787. private <M> Iterable<ModuleDescriptor<M>> getModuleDescriptors(final Collection<Plugin> plugins, final ModuleDescriptorPredicate<M> predicate)
  788. {
  789. // hack way to get typed descriptors from plugin and
  790. // keep generics happy
  791. final Function<ModuleDescriptor<?>, ModuleDescriptor<M>> coercer = new Function<ModuleDescriptor<?>, ModuleDescriptor<M>>()
  792. {
  793. public ModuleDescriptor<M> apply(final ModuleDescriptor<?> input)
  794. {
  795. @SuppressWarnings("unchecked")
  796. final ModuleDescriptor<M> result = (ModuleDescriptor<M>) input;
  797. return result;
  798. }
  799. };
  800. // google predicate adapter
  801. final Predicate<ModuleDescriptor<M>> adapter = new Predicate<ModuleDescriptor<M>>()
  802. {
  803. public boolean apply(final ModuleDescriptor<M> input)
  804. {
  805. return predicate.matches(input);
  806. }
  807. };
  808. // get the filtered module descriptors from a plugin
  809. final Function<Plugin, Iterable<ModuleDescriptor<M>>> descriptorExtractor = new Function<Plugin, Iterable<ModuleDescriptor<M>>>()
  810. {
  811. public Iterable<ModuleDescriptor<M>> apply(final Plugin plugin)
  812. {
  813. return filter(transform(plugin.getModuleDescriptors(), coercer), adapter);
  814. }
  815. };
  816. // concatenate all the descriptor iterables into one
  817. return concat(transform(plugins, descriptorExtractor));
  818. }
  819. /**
  820. * Get the modules of all the given descriptor. If any of the getModule()
  821. * calls fails, the error is recorded in the logs and the plugin is
  822. * disabled.
  823. *
  824. * @param moduleDescriptors the collection of module descriptors to get the
  825. * modules from.
  826. * @return a {@link Collection} modules that can be any type of object. This
  827. * collection will not contain any null value.
  828. */
  829. private <M> List<M> getModules(final Iterable<ModuleDescriptor<M>> moduleDescriptors)
  830. {
  831. final Set<String> pluginsToDisable = new HashSet<String>();
  832. try
  833. {
  834. return toList(transform(moduleDescriptors, new Function<ModuleDescriptor<M>, M>()
  835. {
  836. public M apply(final ModuleDescriptor<M> input)
  837. {
  838. M result = null;
  839. try
  840. {
  841. result = input.getModule();
  842. }
  843. catch (final RuntimeException ex)
  844. {
  845. log.error(
  846. "Exception when retrieving plugin module " + input.getKey() + ", will disable plugin " + input.getPlugin().getKey(), ex);
  847. pluginsToDisable.add(input.getPlugin().getKey());
  848. }
  849. return result;
  850. }
  851. }));
  852. }
  853. finally
  854. {
  855. for (final String badPluginKey : pluginsToDisable)
  856. {
  857. disablePlugin(badPluginKey);
  858. }
  859. }
  860. }
  861. public Plugin getPlugin(final String key)
  862. {
  863. return plugins.get(notNull("The plugin key must be specified", key));
  864. }
  865. public Plugin getEnabledPlugin(final String pluginKey)
  866. {
  867. if (!isPluginEnabled(pluginKey))
  868. {
  869. return null;
  870. }
  871. return getPlugin(pluginKey);
  872. }
  873. public ModuleDescriptor<?> getPluginModule(final String completeKey)
  874. {
  875. return getPluginModule(new ModuleCompleteKey(completeKey));
  876. }
  877. private ModuleDescriptor<?> getPluginModule(final ModuleCompleteKey key)
  878. {
  879. final Plugin plugin = getPlugin(key.getPluginKey());
  880. if (plugin == null)
  881. {
  882. return null;
  883. }
  884. return plugin.getModuleDescriptor(key.getModuleKey());
  885. }
  886. public ModuleDescriptor<?> getEnabledPluginModule(final String completeKey)
  887. {
  888. final ModuleCompleteKey key = new ModuleCompleteKey(completeKey);
  889. // If it's disabled, return null
  890. if (!isPluginModuleEnabled(key))
  891. {
  892. return null;
  893. }
  894. return getEnabledPlugin(key.getPluginKey()).getModuleDescriptor(key.getModuleKey());
  895. }
  896. /**
  897. * @see PluginAccessor#getEnabledModulesByClass(Class)
  898. */
  899. public <M> List<M> getEnabledModulesByClass(final Class<M> moduleClass)
  900. {
  901. return getModules(getEnabledModuleDescriptorsByModuleClass(moduleClass));
  902. }
  903. /**
  904. * @see PluginAccessor#getEnabledModulesByClassAndDescriptor(Class[], Class)
  905. * @deprecated since 0.17, use
  906. * {@link #getModules(com.atlassian.plugin.predicate.ModuleDescriptorPredicate)}
  907. * with an appropriate predicate instead.
  908. */
  909. @Deprecated
  910. public <M> List<M> getEnabledModulesByClassAndDescriptor(final Class<ModuleDescriptor<M>>[] descriptorClasses, final Class<M> moduleClass)
  911. {
  912. final Iterable<ModuleDescriptor<M>> moduleDescriptors = filterDescriptors(getEnabledModuleDescriptorsByModuleClass(moduleClass), new ModuleDescriptorOfClassPredicate<M>(descriptorClasses));
  913. return getModules(moduleDescriptors);
  914. }
  915. /**
  916. * @see PluginAccessor#getEnabledModulesByClassAndDescriptor(Class, Class)
  917. * @deprecated since 0.17, use
  918. * {@link #getModules(com.atlassian.plugin.predicate.ModuleDescriptorPredicate)}
  919. * with an appropriate predicate instead.
  920. */
  921. @Deprecated
  922. public <M> List<M> getEnabledModulesByClassAndDescriptor(final Class<ModuleDescriptor<M>> descriptorClass, final Class<M> moduleClass)
  923. {
  924. final Iterable<ModuleDescriptor<M>> moduleDescriptors = getEnabledModuleDescriptorsByModuleClass(moduleClass);
  925. return getModules(filterDescriptors(moduleDescriptors, new ModuleDescriptorOfClassPredicate<M>(descriptorClass)));
  926. }
  927. /**
  928. * Get all module descriptor that are enabled and for which the module is an
  929. * instance of the given class.
  930. *
  931. * @param moduleClass the class of the module within the module descriptor.
  932. * @return a collection of {@link ModuleDescriptor}s
  933. */
  934. private <M> Collection<ModuleDescriptor<M>> getEnabledModuleDescriptorsByModuleClass(final Class<M> moduleClass)
  935. {
  936. final ModuleOfClassPredicate<M> ofType = new ModuleOfClassPredicate<M>(moduleClass);
  937. final EnabledModulePredicate<M> enabled = new EnabledModulePredicate<M>(this);
  938. return toList(getModuleDescriptors(getEnabledPlugins(), new ModuleDescriptorPredicate<M>()
  939. {
  940. public boolean matches(final ModuleDescriptor<? extends M> moduleDescriptor)
  941. {
  942. return ofType.matches(moduleDescriptor) && enabled.matches(moduleDescriptor);
  943. }
  944. }));
  945. }
  946. /**
  947. * This method has been reverted to pre PLUG-40 to fix performance issues
  948. * that were encountered during load testing. This should be reverted to the
  949. * state it was in at 54639 when the fundamental issue leading to this
  950. * slowdown has been corrected (that is, slowness of PluginClassLoader).
  951. *
  952. * @see PluginAccessor#getEnabledModuleDescriptorsByClass(Class)
  953. */
  954. public <D extends ModuleDescriptor<?>> List<D> getEnabledModuleDescriptorsByClass(final Class<D> descriptorClazz)
  955. {
  956. final List<D> result = new LinkedList<D>();
  957. for (final Plugin plugin : plugins.values())
  958. {
  959. // Skip disabled plugins
  960. if (!isPluginEnabled(plugin.getKey()))
  961. {
  962. if (log.isDebugEnabled())
  963. {
  964. log.debug("Plugin [" + plugin.getKey() + "] is disabled.");
  965. }
  966. continue;
  967. }
  968. for (final ModuleDescriptor<?> module : plugin.getModuleDescriptors())
  969. {
  970. if (descriptorClazz.isInstance(module))
  971. {
  972. if (isPluginModuleEnabled(module.getCompleteKey()))
  973. {
  974. result.add(descriptorClazz.cast(module));
  975. }
  976. else if (log.isDebugEnabled())
  977. {
  978. log.debug("Module [" + module.getCompleteKey() + "] is disabled.");
  979. }
  980. }
  981. }
  982. }
  983. return result;
  984. }
  985. public <D extends ModuleDescriptor<?>> List<D> getEnabledModuleDescriptorsByClass(final Class<D> descriptorClazz, final boolean verbose)
  986. {
  987. return getEnabledModuleDescriptorsByClass(descriptorClazz);
  988. }
  989. /**
  990. * @see PluginAccessor#getEnabledModuleDescriptorsByType(String)
  991. * @deprecated since 0.17, use
  992. * {@link #getModuleDescriptors(com.atlassian.plugin.predicate.ModuleDescriptorPredicate)}
  993. * with an appropriate predicate instead.
  994. */
  995. @Deprecated
  996. public <M> List<ModuleDescriptor<M>> getEnabledModuleDescriptorsByType(final String type) throws PluginParseException, IllegalArgumentException
  997. {
  998. final ModuleDescriptorOfTypePredicate<M> ofType = new ModuleDescriptorOfTypePredicate<M>(moduleDescriptorFactory, type);
  999. final EnabledModulePredicate<M> enabled = new EnabledModulePredicate<M>(this);
  1000. return toList(getModuleDescriptors(getEnabledPlugins(), new ModuleDescriptorPredicate<M>()
  1001. {
  1002. public boolean matches(final ModuleDescriptor<? extends M> moduleDescriptor)
  1003. {
  1004. return ofType.matches(moduleDescriptor) && enabled.matches(moduleDescriptor);
  1005. }
  1006. }));
  1007. }
  1008. /**
  1009. * Filters out a collection of {@link ModuleDescriptor}s given a predicate.
  1010. *
  1011. * @param descriptors the collection of {@link ModuleDescriptor}s to filter.
  1012. * @param predicate the predicate to use for filtering.
  1013. */
  1014. private static <M> Iterable<ModuleDescriptor<M>> filterDescriptors(final Iterable<ModuleDescriptor<M>> descriptors, final ModuleDescriptorPredicate<M> predicate)
  1015. {
  1016. return filter(descriptors, new Predicate<ModuleDescriptor<M>>()
  1017. {
  1018. public boolean apply(final ModuleDescriptor<M> input)
  1019. {
  1020. return predicate.matches(input);
  1021. }
  1022. });
  1023. }
  1024. /**
  1025. * Enable a set of plugins by key. This will implicitly and recursively
  1026. * enable all dependent plugins.
  1027. *
  1028. * @param keys The plugin keys. Must not be null.
  1029. * @since 2.5.0
  1030. */
  1031. public void enablePlugins(final String... keys)
  1032. {
  1033. final Collection<Plugin> pluginsToEnable = new ArrayList<Plugin>(keys.length);
  1034. for (final String key : keys)
  1035. {
  1036. if (key == null)
  1037. {
  1038. throw new IllegalArgumentException("Keys passed to enablePlugins must be non-null");
  1039. }
  1040. final Plugin plugin = plugins.get(key);
  1041. if (plugin == null)
  1042. {
  1043. if (log.isInfoEnabled())
  1044. {
  1045. log.info("No plugin was found for key '" + key + "'. Not enabling.");
  1046. }
  1047. continue;
  1048. }
  1049. if (!plugin.getPluginInformation().satisfiesMinJavaVersion())
  1050. {
  1051. log.error("Minimum Java version of '" + plugin.getPluginInformation().getMinJavaVersion() + "' was not satisfied for module '" + key + "'. Not enabling.");
  1052. continue;
  1053. }
  1054. pluginsToEnable.add(plugin);
  1055. }
  1056. final Collection<Plugin> enabledPlugins = pluginEnabler.enableAllRecursively(pluginsToEnable);
  1057. for (final Plugin plugin : enabledPlugins)
  1058. {
  1059. enablePluginState(plugin, getStore());
  1060. notifyPluginEnabled(plugin);
  1061. }
  1062. }
  1063. /**
  1064. * @deprecated since 2.5.0, use {#link enablePlugins(String... keys)} instead
  1065. */
  1066. @Deprecated
  1067. public void enablePlugin(final String key)
  1068. {
  1069. enablePlugins(key);
  1070. }
  1071. protected void enablePluginState(final Plugin plugin, final PluginPersistentStateStore stateStore)
  1072. {
  1073. stateStore.save(getBuilder().setEnabled(plugin, true).toState());
  1074. }
  1075. /**
  1076. * Called on all clustered application nodes, rather than
  1077. * {@link #enablePlugin(String)} to just update the local state, state aware
  1078. * modules and loaders, but not affect the global plugin state.
  1079. *
  1080. * @param plugin the plugin being enabled
  1081. */
  1082. protected void notifyPluginEnabled(final Plugin plugin)
  1083. {
  1084. plugin.enable();
  1085. if (enableConfiguredPluginModules(plugin))
  1086. {
  1087. pluginEventManager.broadcast(new PluginEnabledEvent(plugin));
  1088. }
  1089. }
  1090. /**
  1091. * For each module in the plugin, call the module descriptor's enabled()
  1092. * method if the module is StateAware and enabled.
  1093. * <p>
  1094. * If any modules fail to enable then the plugin is replaced by an
  1095. * UnloadablePlugin, and this method will return {@code false}.
  1096. *
  1097. * @param plugin the plugin to enable
  1098. * @return true if the modules were all enabled correctly, false otherwise.
  1099. */
  1100. private boolean enableConfiguredPluginModules(final Plugin plugin)
  1101. {
  1102. boolean success = true;
  1103. final Set<ModuleDescriptor<?>> enabledDescriptors = new HashSet<ModuleDescriptor<?>>();
  1104. for (final ModuleDescriptor<?> descriptor : plugin.getModuleDescriptors())
  1105. {
  1106. if (!enableConfiguredPluginModule(plugin, descriptor, enabledDescriptors))
  1107. {
  1108. success = false;
  1109. break;
  1110. }
  1111. }
  1112. return success;
  1113. }
  1114. private boolean enableConfiguredPluginModule(final Plugin plugin, final ModuleDescriptor<?> descriptor, final Set<ModuleDescriptor<?>> enabledDescriptors)
  1115. {
  1116. boolean success = true;
  1117. // This can happen if the plugin available event is fired as part of the plugin initialization process
  1118. if (pluginEnabler.isPluginBeingEnabled(plugin))
  1119. {
  1120. log.debug("The plugin is currently being enabled, so we won't bother trying to enable the '" + descriptor.getKey() + " module");
  1121. return success;
  1122. }
  1123. // We only want to re-enable modules that weren't explicitly
  1124. // disabled by the user.
  1125. if (!isPluginModuleEnabled(descriptor.getCompleteKey()))
  1126. {
  1127. if (log.isDebugEnabled())
  1128. {
  1129. String name = descriptor.getName() == null ? descriptor.getKey() : descriptor.getName();
  1130. log.debug("Plugin module '" + name + "' is explicitly disabled (or so by default), so not re-enabling.");
  1131. }
  1132. return success;
  1133. }
  1134. try
  1135. {
  1136. notifyModuleEnabled(descriptor);
  1137. enabledDescriptors.add(descriptor);
  1138. }
  1139. catch (final Throwable exception)
  1140. {
  1141. // catch any errors and insert an UnloadablePlugi