PageRenderTime 54ms CodeModel.GetById 22ms RepoModel.GetById 0ms app.codeStats 0ms

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

https://bitbucket.org/atlassian/atlassian-plugins
Java | 1027 lines | 760 code | 119 blank | 148 comment | 78 complexity | 69b7fa78b7c76182534faf7011d37740 MD5 | raw file
  1. package com.atlassian.plugin.manager;
  2. import com.atlassian.annotations.ExperimentalApi;
  3. import com.atlassian.annotations.Internal;
  4. import com.atlassian.instrumentation.operations.OpTimer;
  5. import com.atlassian.plugin.ModuleCompleteKey;
  6. import com.atlassian.plugin.ModuleDescriptor;
  7. import com.atlassian.plugin.ModuleDescriptorFactory;
  8. import com.atlassian.plugin.Plugin;
  9. import com.atlassian.plugin.PluginAccessor;
  10. import com.atlassian.plugin.PluginArtifact;
  11. import com.atlassian.plugin.PluginController;
  12. import com.atlassian.plugin.PluginDependencies;
  13. import com.atlassian.plugin.PluginException;
  14. import com.atlassian.plugin.PluginInformation;
  15. import com.atlassian.plugin.PluginInstaller;
  16. import com.atlassian.plugin.PluginInternal;
  17. import com.atlassian.plugin.PluginParseException;
  18. import com.atlassian.plugin.PluginRegistry;
  19. import com.atlassian.plugin.PluginRestartState;
  20. import com.atlassian.plugin.PluginState;
  21. import com.atlassian.plugin.RevertablePluginInstaller;
  22. import com.atlassian.plugin.SplitStartupPluginSystemLifecycle;
  23. import com.atlassian.plugin.StateAware;
  24. import com.atlassian.plugin.classloader.PluginsClassLoader;
  25. import com.atlassian.plugin.descriptors.CannotDisable;
  26. import com.atlassian.plugin.descriptors.UnloadableModuleDescriptor;
  27. import com.atlassian.plugin.descriptors.UnloadableModuleDescriptorFactory;
  28. import com.atlassian.plugin.event.NotificationException;
  29. import com.atlassian.plugin.event.PluginEventListener;
  30. import com.atlassian.plugin.event.PluginEventManager;
  31. import com.atlassian.plugin.event.events.PluginContainerUnavailableEvent;
  32. import com.atlassian.plugin.event.events.PluginDependentsChangedEvent;
  33. import com.atlassian.plugin.event.events.PluginDisabledEvent;
  34. import com.atlassian.plugin.event.events.PluginDisablingEvent;
  35. import com.atlassian.plugin.event.events.PluginEnabledEvent;
  36. import com.atlassian.plugin.event.events.PluginEnablingEvent;
  37. import com.atlassian.plugin.event.events.PluginFrameworkDelayedEvent;
  38. import com.atlassian.plugin.event.events.PluginFrameworkResumingEvent;
  39. import com.atlassian.plugin.event.events.PluginFrameworkShutdownEvent;
  40. import com.atlassian.plugin.event.events.PluginFrameworkShuttingDownEvent;
  41. import com.atlassian.plugin.event.events.PluginFrameworkStartedEvent;
  42. import com.atlassian.plugin.event.events.PluginFrameworkStartingEvent;
  43. import com.atlassian.plugin.event.events.PluginFrameworkWarmRestartedEvent;
  44. import com.atlassian.plugin.event.events.PluginFrameworkWarmRestartingEvent;
  45. import com.atlassian.plugin.event.events.PluginInstalledEvent;
  46. import com.atlassian.plugin.event.events.PluginInstallingEvent;
  47. import com.atlassian.plugin.event.events.PluginModuleAvailableEvent;
  48. import com.atlassian.plugin.event.events.PluginModuleDisabledEvent;
  49. import com.atlassian.plugin.event.events.PluginModuleDisablingEvent;
  50. import com.atlassian.plugin.event.events.PluginModuleEnabledEvent;
  51. import com.atlassian.plugin.event.events.PluginModuleEnablingEvent;
  52. import com.atlassian.plugin.event.events.PluginModuleUnavailableEvent;
  53. import com.atlassian.plugin.event.events.PluginRefreshedEvent;
  54. import com.atlassian.plugin.event.events.PluginUninstalledEvent;
  55. import com.atlassian.plugin.event.events.PluginUninstallingEvent;
  56. import com.atlassian.plugin.event.events.PluginUpgradedEvent;
  57. import com.atlassian.plugin.event.events.PluginUpgradingEvent;
  58. import com.atlassian.plugin.exception.NoOpPluginExceptionInterception;
  59. import com.atlassian.plugin.exception.PluginExceptionInterception;
  60. import com.atlassian.plugin.impl.UnloadablePlugin;
  61. import com.atlassian.plugin.impl.UnloadablePluginFactory;
  62. import com.atlassian.plugin.instrumentation.PluginSystemInstrumentation;
  63. import com.atlassian.plugin.instrumentation.Timer;
  64. import com.atlassian.plugin.loaders.DiscardablePluginLoader;
  65. import com.atlassian.plugin.loaders.DynamicPluginLoader;
  66. import com.atlassian.plugin.loaders.PermissionCheckingPluginLoader;
  67. import com.atlassian.plugin.loaders.PluginLoader;
  68. import com.atlassian.plugin.metadata.ClasspathFilePluginMetadata;
  69. import com.atlassian.plugin.metadata.DefaultRequiredPluginValidator;
  70. import com.atlassian.plugin.metadata.RequiredPluginValidator;
  71. import com.atlassian.plugin.predicate.EnabledModulePredicate;
  72. import com.atlassian.plugin.predicate.EnabledPluginPredicate;
  73. import com.atlassian.plugin.predicate.ModuleOfClassPredicate;
  74. import com.atlassian.plugin.scope.ScopeManager;
  75. import com.atlassian.plugin.util.PluginUtils;
  76. import com.atlassian.plugin.util.VersionStringComparator;
  77. import com.google.common.annotations.VisibleForTesting;
  78. import org.dom4j.Element;
  79. import org.slf4j.Logger;
  80. import org.slf4j.LoggerFactory;
  81. import javax.annotation.Nullable;
  82. import java.io.FileInputStream;
  83. import java.io.IOException;
  84. import java.io.InputStream;
  85. import java.util.ArrayList;
  86. import java.util.Collection;
  87. import java.util.Collections;
  88. import java.util.Comparator;
  89. import java.util.EnumSet;
  90. import java.util.HashMap;
  91. import java.util.HashSet;
  92. import java.util.LinkedHashMap;
  93. import java.util.List;
  94. import java.util.Map;
  95. import java.util.Objects;
  96. import java.util.Optional;
  97. import java.util.Properties;
  98. import java.util.Set;
  99. import java.util.TreeMap;
  100. import java.util.TreeSet;
  101. import java.util.concurrent.TimeUnit;
  102. import java.util.concurrent.atomic.AtomicInteger;
  103. import java.util.concurrent.atomic.AtomicReference;
  104. import java.util.function.Predicate;
  105. import java.util.stream.Stream;
  106. import static com.atlassian.plugin.PluginDependencies.Type.DYNAMIC;
  107. import static com.atlassian.plugin.PluginDependencies.Type.MANDATORY;
  108. import static com.atlassian.plugin.PluginDependencies.Type.OPTIONAL;
  109. import static com.atlassian.plugin.impl.AbstractPlugin.cleanVersionString;
  110. import static com.atlassian.plugin.manager.SafeModeManager.START_ALL_PLUGINS;
  111. import static com.atlassian.plugin.util.Assertions.notNull;
  112. import static com.google.common.base.Preconditions.checkState;
  113. import static com.google.common.collect.Maps.filterKeys;
  114. import static java.util.Arrays.asList;
  115. import static java.util.Collections.emptyList;
  116. import static java.util.Collections.max;
  117. import static java.util.Collections.reverse;
  118. import static java.util.Collections.singleton;
  119. import static java.util.Collections.singletonList;
  120. import static java.util.Collections.unmodifiableMap;
  121. import static java.util.Collections.unmodifiableSet;
  122. import static java.util.stream.Collectors.collectingAndThen;
  123. import static java.util.stream.Collectors.partitioningBy;
  124. import static java.util.stream.Collectors.toList;
  125. import static java.util.stream.Collectors.toSet;
  126. import static java.util.stream.StreamSupport.stream;
  127. /**
  128. * This implementation delegates the initiation and classloading of plugins to a
  129. * list of {@link com.atlassian.plugin.loaders.PluginLoader}s and records the
  130. * state of plugins in a
  131. * {@link com.atlassian.plugin.manager.PluginPersistentStateStore}.
  132. * <p>
  133. * This class is responsible for enabling and disabling plugins and plugin
  134. * modules and reflecting these state changes in the PluginPersistentStateStore.
  135. * <p>
  136. * An interesting quirk in the design is that
  137. * {@link #installPlugins(com.atlassian.plugin.PluginArtifact[])} explicitly stores
  138. * the plugin via a {@link com.atlassian.plugin.PluginInstaller}, whereas
  139. * {@link #uninstall(Plugin)} relies on the underlying
  140. * {@link com.atlassian.plugin.loaders.PluginLoader} to remove the plugin if
  141. * necessary.
  142. */
  143. public class DefaultPluginManager implements PluginController, PluginAccessor, SplitStartupPluginSystemLifecycle {
  144. private static final Logger log = LoggerFactory.getLogger(DefaultPluginManager.class);
  145. @Internal
  146. public static String getStartupOverrideFileProperty() {
  147. return DefaultPluginManager.class.getName() + ".startupOverrideFile";
  148. }
  149. @Internal
  150. public static String getLateStartupEnableRetryProperty() {
  151. return DefaultPluginManager.class.getName() + ".lateStartupEnableRetry";
  152. }
  153. @Internal
  154. public static String getMinimumPluginVersionsFileProperty() {
  155. return DefaultPluginManager.class.getName() + ".minimumPluginVersionsFile";
  156. }
  157. private final List<DiscardablePluginLoader> pluginLoaders;
  158. private final PluginPersistentStateModifier persistentStateModifier;
  159. private final ModuleDescriptorFactory moduleDescriptorFactory;
  160. private final PluginEventManager pluginEventManager;
  161. private final PluginRegistry.ReadWrite pluginRegistry;
  162. private final PluginsClassLoader classLoader;
  163. private final PluginEnabler pluginEnabler;
  164. private final StateTracker tracker;
  165. private final boolean verifyRequiredPlugins;
  166. /**
  167. * Predicate for identify plugins which are not loaded at initialization.
  168. * <p>
  169. * This can be used to support two-phase "tenant aware" startup.
  170. */
  171. private final Predicate<Plugin> delayLoadOf;
  172. /**
  173. * Installer used for storing plugins. Used by
  174. * {@link #installPlugins(PluginArtifact[])}.
  175. */
  176. private RevertablePluginInstaller pluginInstaller;
  177. /**
  178. * Stores {@link Plugin}s as a key and {@link PluginLoader} as a value.
  179. */
  180. private final Map<Plugin, PluginLoader> installedPluginsToPluginLoader;
  181. /**
  182. * A map to pass information from {@link #earlyStartup} to {@link #addPlugins} without an API change.
  183. * <p>
  184. * This map allows earlyStartup to specify a plugin-specific loader for the list of plugins provided to addPlugins. It is only
  185. * valid when addPlugins is called from earlyStartup or lateStartup. Products may override addPlugins (to add clustering
  186. * behaviour for example), so fixing the API here is a more involved change.
  187. */
  188. private final Map<Plugin, DiscardablePluginLoader> candidatePluginsToPluginLoader;
  189. /**
  190. * A list of plugins to be re-enabled when adding plugins.
  191. * <p>
  192. * As with {@link #candidatePluginsToPluginLoader}, this is passing data from {@link #earlyStartup} and {@link #lateStartup} to
  193. * {@link #addPlugins} without an API change.
  194. */
  195. private final Collection<Plugin> additionalPluginsToEnable;
  196. private final DefaultPluginManagerJmxBridge defaultPluginManagerJmxBridge;
  197. /**
  198. * The list of plugins whose load was delayed.
  199. */
  200. private final List<Plugin> delayedPlugins;
  201. /**
  202. * A map of plugins which need to be removed during lateStartup.
  203. * <p>
  204. * The plugins which need to be removed on restart are discovered during earlyStartup so we can avoid installing them
  205. * when not required, but cannot be removed until persistence is available during late startup.
  206. */
  207. private final Map<Plugin, DiscardablePluginLoader> delayedPluginRemovalsToLoader;
  208. private final SafeModuleExtractor safeModuleExtractor;
  209. private final SafeModeManager safeModeManager;
  210. private final PluginTransactionContext pluginTransactionContext;
  211. public DefaultPluginManager(
  212. final PluginPersistentStateStore store,
  213. final List<PluginLoader> pluginLoaders,
  214. final ModuleDescriptorFactory moduleDescriptorFactory,
  215. final PluginEventManager pluginEventManager) {
  216. this(newBuilder()
  217. .withStore(store)
  218. .withPluginLoaders(pluginLoaders)
  219. .withModuleDescriptorFactory(moduleDescriptorFactory)
  220. .withPluginEventManager(pluginEventManager));
  221. }
  222. public DefaultPluginManager(
  223. final PluginPersistentStateStore store,
  224. final List<PluginLoader> pluginLoaders,
  225. final ModuleDescriptorFactory moduleDescriptorFactory,
  226. final PluginEventManager pluginEventManager,
  227. final PluginExceptionInterception pluginExceptionInterception) {
  228. this(newBuilder()
  229. .withStore(store)
  230. .withPluginLoaders(pluginLoaders)
  231. .withModuleDescriptorFactory(moduleDescriptorFactory)
  232. .withPluginEventManager(pluginEventManager)
  233. .withPluginExceptionInterception(pluginExceptionInterception));
  234. }
  235. public DefaultPluginManager(
  236. final PluginPersistentStateStore store,
  237. final List<PluginLoader> pluginLoaders,
  238. final ModuleDescriptorFactory moduleDescriptorFactory,
  239. final PluginEventManager pluginEventManager,
  240. final boolean verifyRequiredPlugins) {
  241. this(newBuilder()
  242. .withStore(store)
  243. .withPluginLoaders(pluginLoaders)
  244. .withModuleDescriptorFactory(moduleDescriptorFactory)
  245. .withPluginEventManager(pluginEventManager)
  246. .withVerifyRequiredPlugins(verifyRequiredPlugins));
  247. }
  248. @ExperimentalApi
  249. public DefaultPluginManager(
  250. final PluginPersistentStateStore store,
  251. final List<PluginLoader> pluginLoaders,
  252. final ModuleDescriptorFactory moduleDescriptorFactory,
  253. final PluginEventManager pluginEventManager,
  254. final Predicate<Plugin> delayLoadOf) {
  255. this(newBuilder()
  256. .withStore(store)
  257. .withPluginLoaders(pluginLoaders)
  258. .withModuleDescriptorFactory(moduleDescriptorFactory)
  259. .withPluginEventManager(pluginEventManager)
  260. .withDelayLoadOf(delayLoadOf));
  261. }
  262. @ExperimentalApi
  263. public DefaultPluginManager(
  264. final PluginPersistentStateStore store,
  265. final List<PluginLoader> pluginLoaders,
  266. final ModuleDescriptorFactory moduleDescriptorFactory,
  267. final PluginEventManager pluginEventManager,
  268. final PluginExceptionInterception pluginExceptionInterception,
  269. final Predicate<Plugin> delayLoadOf) {
  270. this(newBuilder()
  271. .withStore(store)
  272. .withPluginLoaders(pluginLoaders)
  273. .withModuleDescriptorFactory(moduleDescriptorFactory)
  274. .withPluginEventManager(pluginEventManager)
  275. .withPluginExceptionInterception(pluginExceptionInterception)
  276. .withDelayLoadOf(delayLoadOf));
  277. }
  278. public DefaultPluginManager(
  279. final PluginPersistentStateStore store,
  280. final List<PluginLoader> pluginLoaders,
  281. final ModuleDescriptorFactory moduleDescriptorFactory,
  282. final PluginEventManager pluginEventManager,
  283. final PluginExceptionInterception pluginExceptionInterception,
  284. final boolean verifyRequiredPlugins) {
  285. this(newBuilder()
  286. .withStore(store)
  287. .withPluginLoaders(pluginLoaders)
  288. .withModuleDescriptorFactory(moduleDescriptorFactory)
  289. .withPluginEventManager(pluginEventManager)
  290. .withPluginExceptionInterception(pluginExceptionInterception)
  291. .withVerifyRequiredPlugins(verifyRequiredPlugins));
  292. }
  293. public DefaultPluginManager(
  294. final PluginPersistentStateStore store,
  295. final List<PluginLoader> pluginLoaders,
  296. final ModuleDescriptorFactory moduleDescriptorFactory,
  297. final PluginEventManager pluginEventManager,
  298. final PluginExceptionInterception pluginExceptionInterception,
  299. final boolean verifyRequiredPlugins,
  300. final Predicate<Plugin> delayLoadOf) {
  301. this(newBuilder()
  302. .withStore(store)
  303. .withPluginLoaders(pluginLoaders)
  304. .withModuleDescriptorFactory(moduleDescriptorFactory)
  305. .withPluginEventManager(pluginEventManager)
  306. .withPluginExceptionInterception(pluginExceptionInterception)
  307. .withVerifyRequiredPlugins(verifyRequiredPlugins)
  308. .withDelayLoadOf(delayLoadOf));
  309. }
  310. protected DefaultPluginManager(Builder<? extends Builder> builder) {
  311. this.safeModeManager = builder.safeModeManager;
  312. this.pluginLoaders = toPermissionCheckingPluginLoaders(notNull("Plugin Loaders list", builder.pluginLoaders));
  313. this.persistentStateModifier = new PluginPersistentStateModifier(notNull("PluginPersistentStateStore", builder.store));
  314. this.moduleDescriptorFactory = notNull("ModuleDescriptorFactory", builder.moduleDescriptorFactory);
  315. this.pluginEventManager = notNull("PluginEventManager", builder.pluginEventManager);
  316. this.pluginEnabler = new PluginEnabler(this, this, notNull("PluginExceptionInterception", builder.pluginExceptionInterception));
  317. this.verifyRequiredPlugins = builder.verifyRequiredPlugins;
  318. this.delayLoadOf = wrapDelayPredicateWithOverrides(builder.delayLoadOf);
  319. this.pluginRegistry = builder.pluginRegistry;
  320. this.classLoader = builder.pluginAccessor.map(pa -> PluginsClassLoader.class.cast(pa.getClassLoader()))
  321. .orElseGet(() -> new PluginsClassLoader(null, this, this.pluginEventManager));
  322. this.tracker = new StateTracker();
  323. this.pluginInstaller = new NoOpRevertablePluginInstaller(new UnsupportedPluginInstaller());
  324. this.installedPluginsToPluginLoader = new HashMap<>();
  325. this.candidatePluginsToPluginLoader = new HashMap<>();
  326. this.additionalPluginsToEnable = new ArrayList<>();
  327. this.delayedPlugins = new ArrayList<>();
  328. this.delayedPluginRemovalsToLoader = new HashMap<>();
  329. this.pluginEventManager.register(this);
  330. this.defaultPluginManagerJmxBridge = new DefaultPluginManagerJmxBridge(this);
  331. this.safeModuleExtractor = new SafeModuleExtractor(this);
  332. this.pluginTransactionContext = new PluginTransactionContext(this.pluginEventManager);
  333. }
  334. @SuppressWarnings("unchecked")
  335. public static class Builder<T extends Builder<?>> {
  336. private PluginPersistentStateStore store;
  337. private List<PluginLoader> pluginLoaders = new ArrayList<>();
  338. private ModuleDescriptorFactory moduleDescriptorFactory;
  339. private PluginEventManager pluginEventManager;
  340. private PluginExceptionInterception pluginExceptionInterception = NoOpPluginExceptionInterception.NOOP_INTERCEPTION;
  341. private boolean verifyRequiredPlugins = false;
  342. private Predicate<Plugin> delayLoadOf = p -> false;
  343. private PluginRegistry.ReadWrite pluginRegistry = new PluginRegistryImpl();
  344. private Optional<PluginAccessor> pluginAccessor = Optional.empty();
  345. private SafeModeManager safeModeManager = START_ALL_PLUGINS;
  346. public T withSafeModeManager(final SafeModeManager safeModeManager) {
  347. this.safeModeManager = safeModeManager;
  348. return (T) this;
  349. }
  350. public T withStore(final PluginPersistentStateStore store) {
  351. this.store = store;
  352. return (T) this;
  353. }
  354. public T withPluginLoaders(final List<PluginLoader> pluginLoaders) {
  355. this.pluginLoaders.addAll(pluginLoaders);
  356. return (T) this;
  357. }
  358. public T withPluginLoader(final PluginLoader pluginLoader) {
  359. this.pluginLoaders.add(pluginLoader);
  360. return (T) this;
  361. }
  362. public T withModuleDescriptorFactory(final ModuleDescriptorFactory moduleDescriptorFactory) {
  363. this.moduleDescriptorFactory = moduleDescriptorFactory;
  364. return (T) this;
  365. }
  366. public T withPluginEventManager(final PluginEventManager pluginEventManager) {
  367. this.pluginEventManager = pluginEventManager;
  368. return (T) this;
  369. }
  370. public T withPluginExceptionInterception(final PluginExceptionInterception pluginExceptionInterception) {
  371. this.pluginExceptionInterception = pluginExceptionInterception;
  372. return (T) this;
  373. }
  374. public T withVerifyRequiredPlugins(final boolean verifyRequiredPlugins) {
  375. this.verifyRequiredPlugins = verifyRequiredPlugins;
  376. return (T) this;
  377. }
  378. public T withDelayLoadOf(final Predicate<Plugin> delayLoadOf) {
  379. this.delayLoadOf = delayLoadOf;
  380. return (T) this;
  381. }
  382. public T withPluginRegistry(final PluginRegistry.ReadWrite pluginRegistry) {
  383. this.pluginRegistry = pluginRegistry;
  384. return (T) this;
  385. }
  386. public T withPluginAccessor(PluginAccessor pluginAccessor) {
  387. this.pluginAccessor = Optional.of(pluginAccessor);
  388. return (T) this;
  389. }
  390. /**
  391. * @deprecated in 5.0 for removal in 6.0 when {@link ScopeManager} will be removed.
  392. */
  393. @Deprecated
  394. public T withScopeManager(ScopeManager ignored) {
  395. return (T) this;
  396. }
  397. public DefaultPluginManager build() {
  398. return new DefaultPluginManager(this);
  399. }
  400. }
  401. public static Builder<? extends Builder<?>> newBuilder() {
  402. return new Builder<>();
  403. }
  404. private static Iterable<String> toPluginKeys(Iterable<Plugin> plugins) {
  405. return stream(plugins.spliterator(), false).map(Plugin::getKey).collect(toList());
  406. }
  407. private List<DiscardablePluginLoader> toPermissionCheckingPluginLoaders(final List<PluginLoader> fromIterable) {
  408. return fromIterable.stream().map(PermissionCheckingPluginLoader::new).collect(toList());
  409. }
  410. private Predicate<Plugin> wrapDelayPredicateWithOverrides(final Predicate<Plugin> pluginPredicate) {
  411. final Map<String, String> startupOverridesMap = parseFileNamedByPropertyAsMap(getStartupOverrideFileProperty());
  412. return new Predicate<Plugin>() {
  413. @Override
  414. public boolean test(final Plugin plugin) {
  415. final String pluginKey = plugin.getKey();
  416. // Check startup overrides for plugin startup declaration
  417. final String stringFromFile = startupOverridesMap.get(pluginKey);
  418. final Optional<Boolean> parsedFromFile = parseStartupToDelay(stringFromFile, pluginKey, "override file");
  419. if (parsedFromFile.isPresent()) {
  420. return parsedFromFile.get();
  421. }
  422. // Check PluginInformation for plugin startup declaration
  423. final PluginInformation pluginInformation = plugin.getPluginInformation();
  424. final String stringFromInformation = (null != pluginInformation) ? pluginInformation.getStartup() : null;
  425. final Optional<Boolean> parsedFromInformation = parseStartupToDelay(stringFromInformation, pluginKey, "PluginInformation");
  426. // If no plugin startup information, use product supplied predicate
  427. return parsedFromInformation.orElseGet(() -> pluginPredicate.test(plugin));
  428. }
  429. private Optional<Boolean> parseStartupToDelay(final String startup, final String pluginKey, final String source) {
  430. if (null != startup) {
  431. if ("early".equals(startup)) {
  432. return Optional.of(Boolean.FALSE);
  433. }
  434. if ("late".equals(startup)) {
  435. return Optional.of(Boolean.TRUE);
  436. }
  437. log.warn("Unknown startup '{}' for plugin '{}' from {}", startup, pluginKey, source);
  438. // and fall through
  439. }
  440. return Optional.empty();
  441. }
  442. };
  443. }
  444. private Map<String, String> parseFileNamedByPropertyAsMap(final String property) {
  445. final Properties properties = new Properties();
  446. final String fileName = System.getProperty(property);
  447. if (null != fileName) {
  448. try (FileInputStream inStream = new FileInputStream(fileName)) {
  449. properties.load(inStream);
  450. } catch (final IOException eio) {
  451. log.warn("Failed to load file named by property {}, that is '{}': {}",
  452. property, fileName, eio);
  453. }
  454. }
  455. return unmodifiableMap(propertiesToMap(properties));
  456. }
  457. private Map<String, String> propertiesToMap(final Properties properties) {
  458. final Map<String, String> propertiesMap = new HashMap<>();
  459. properties.forEach((key, value) -> propertiesMap.put((String) key, (String) value));
  460. return propertiesMap;
  461. }
  462. @Override
  463. public void init() {
  464. pluginTransactionContext.wrap(() -> {
  465. earlyStartup();
  466. lateStartup();
  467. });
  468. }
  469. @ExperimentalApi
  470. @Override
  471. public void earlyStartup() {
  472. pluginTransactionContext.wrap(() -> {
  473. try (Timer timer = PluginSystemInstrumentation.instance().pullSingleTimer("earlyStartup")) {
  474. log.info("Plugin system earlyStartup begun");
  475. tracker.setState(StateTracker.State.STARTING);
  476. defaultPluginManagerJmxBridge.register();
  477. broadcastIgnoreError(new PluginFrameworkStartingEvent(this, this));
  478. pluginInstaller.clearBackups();
  479. final PluginPersistentState pluginPersistentState = getState();
  480. final Map<String, List<Plugin>> candidatePluginKeyToVersionedPlugins = new TreeMap<>();
  481. for (final DiscardablePluginLoader loader : pluginLoaders) {
  482. if (loader == null) {
  483. continue;
  484. }
  485. final Iterable<Plugin> possiblePluginsToLoad = loader.loadAllPlugins(moduleDescriptorFactory);
  486. if (log.isDebugEnabled()) {
  487. log.debug("Found {} plugins to possibly load: {}",
  488. stream(possiblePluginsToLoad.spliterator(), false).count(), toPluginKeys(possiblePluginsToLoad));
  489. }
  490. for (final Plugin plugin : possiblePluginsToLoad) {
  491. if (pluginPersistentState.getPluginRestartState(plugin.getKey()) == PluginRestartState.REMOVE) {
  492. log.info("Plugin {} was marked to be removed on restart. Removing now.", plugin);
  493. // We need to remove the plugin and clear its state, but we don't want to do any persistence until
  494. // late startup. We may as well delay PluginLoader#removePlugin also, as it doesn't hurt,
  495. // and it makes fewer assumptions about how the product persists the plugins themselves.
  496. delayedPluginRemovalsToLoader.put(plugin, loader);
  497. } else {
  498. // We need the loaders for installed plugins so we can issue removePlugin() when
  499. // the plugin is unloaded. So anything we didn't remove above, we put into the
  500. // candidatePluginsToPluginLoader map. All of these need either removePlugin()
  501. // or discardPlugin() for resource management.
  502. candidatePluginsToPluginLoader.put(plugin, loader);
  503. List<Plugin> plugins = candidatePluginKeyToVersionedPlugins.computeIfAbsent(plugin.getKey(), key -> new ArrayList<>());
  504. plugins.add(plugin);
  505. }
  506. }
  507. }
  508. final List<Plugin> pluginsToInstall = new ArrayList<>();
  509. for (final List<Plugin> plugins : candidatePluginKeyToVersionedPlugins.values()) {
  510. final Plugin plugin = max(plugins, Comparator.naturalOrder());
  511. if (plugins.size() > 1) {
  512. log.debug("Plugin {} contained multiple versions. installing version {}.", plugin.getKey(), plugin.getPluginInformation().getVersion());
  513. }
  514. pluginsToInstall.add(plugin);
  515. }
  516. // Partition pluginsToInstall into immediatePlugins that we install now, and delayedPlugins
  517. // that we install when instructed by a call to lateStartup.
  518. // In later versions of Guava, ImmutableListMultimap.index is another way to slice this if
  519. // you convert the PluginPredicate to a function. Whether or this is cleaner is a bit moot
  520. // since we can't use it yet anyway.
  521. final List<Plugin> immediatePlugins = new ArrayList<>();
  522. for (final Plugin plugin : pluginsToInstall) {
  523. if (delayLoadOf.test(plugin)) {
  524. delayedPlugins.add(plugin);
  525. } else {
  526. immediatePlugins.add(plugin);
  527. }
  528. }
  529. // Install the non-delayed plugins
  530. addPlugins(null, immediatePlugins);
  531. // For each immediatePlugins, addPlugins has either called removePlugin()/discardPlugin()
  532. // for its loader, or is tracking it in installedPluginsToPluginLoader for a removePlugin()
  533. // when it is uninstalled (either via upgrade or shutdown).
  534. for (final Plugin plugin : immediatePlugins) {
  535. candidatePluginsToPluginLoader.remove(plugin);
  536. }
  537. if (Boolean.getBoolean(getLateStartupEnableRetryProperty())) {
  538. for (final Plugin plugin : immediatePlugins) {
  539. // For each plugin that didn't enable but should have, make a note so we can try them again later. It's a little
  540. // bit vexing that we are checking the persistent state here again just after addPlugins did it, but refactoring this
  541. // stuff is fraught with API danger, so this can wait. PLUG-1116 seems like a time this might be worth revisiting.
  542. if ((PluginState.ENABLED != plugin.getPluginState()) && pluginPersistentState.isEnabled(plugin)) {
  543. additionalPluginsToEnable.add(plugin);
  544. }
  545. }
  546. if (!additionalPluginsToEnable.isEmpty()) {
  547. // Let people know we're going to retry them, so there is information in the logs about this near the resolution errors
  548. log.warn("Failed to enable some ({}) early plugins, will fallback during lateStartup. Plugins: {}",
  549. additionalPluginsToEnable.size(), additionalPluginsToEnable);
  550. }
  551. }
  552. // We need to keep candidatePluginsToPluginLoader populated with the delayedPlugins so that
  553. // addPlugins can do the right thing when called by lateStartup. However, we want to
  554. // discardPlugin anything we don't need so that loaders can release resources. So move what
  555. // we need later from candidatePluginsToPluginLoader to delayedPluginsLoaders.
  556. final Map<Plugin, DiscardablePluginLoader> delayedPluginsLoaders = new HashMap<>();
  557. for (final Plugin plugin : delayedPlugins) {
  558. final DiscardablePluginLoader loader = candidatePluginsToPluginLoader.remove(plugin);
  559. delayedPluginsLoaders.put(plugin, loader);
  560. }
  561. // Now candidatePluginsToPluginLoader contains exactly Plugins returned by loadAllPlugins
  562. // for which we didn't removePlugin() above, didn't pass on addPlugins(), and won't handle
  563. // in loadDelayedPlugins. So loaders can release resources, we discardPlugin() these.
  564. for (final Map.Entry<Plugin, DiscardablePluginLoader> entry : candidatePluginsToPluginLoader.entrySet()) {
  565. final Plugin plugin = entry.getKey();
  566. final DiscardablePluginLoader loader = entry.getValue();
  567. loader.discardPlugin(plugin);
  568. }
  569. // Finally, make candidatePluginsToPluginLoader contain what loadDelayedPlugins needs.
  570. candidatePluginsToPluginLoader.clear();
  571. candidatePluginsToPluginLoader.putAll(delayedPluginsLoaders);
  572. tracker.setState(StateTracker.State.DELAYED);
  573. logTime(timer, "Plugin system earlyStartup ended");
  574. broadcastIgnoreError(new PluginFrameworkDelayedEvent(this, this));
  575. }
  576. });
  577. }
  578. @ExperimentalApi
  579. @Override
  580. public void lateStartup() {
  581. pluginTransactionContext.wrap(() -> {
  582. try (Timer timer = PluginSystemInstrumentation.instance().pullSingleTimer("lateStartup")) {
  583. log.info("Plugin system lateStartup begun");
  584. tracker.setState(StateTracker.State.RESUMING);
  585. broadcastIgnoreError(new PluginFrameworkResumingEvent(this, this));
  586. addPlugins(null, delayedPlugins);
  587. delayedPlugins.clear();
  588. candidatePluginsToPluginLoader.clear();
  589. persistentStateModifier.clearPluginRestartState();
  590. for (final Map.Entry<Plugin, DiscardablePluginLoader> entry : delayedPluginRemovalsToLoader.entrySet()) {
  591. final Plugin plugin = entry.getKey();
  592. final DiscardablePluginLoader loader = entry.getValue();
  593. // Remove the plugin from the loader, and discard saved state (see PLUG-13).
  594. loader.removePlugin(plugin);
  595. persistentStateModifier.removeState(plugin);
  596. }
  597. delayedPluginRemovalsToLoader.clear();
  598. logTime(timer, "Plugin system lateStartup ended");
  599. tracker.setState(StateTracker.State.STARTED);
  600. if (verifyRequiredPlugins) {
  601. validateRequiredPlugins();
  602. }
  603. broadcastIgnoreError(new PluginFrameworkStartedEvent(this, this));
  604. }
  605. });
  606. }
  607. private void validateRequiredPlugins() {
  608. final RequiredPluginValidator validator = new DefaultRequiredPluginValidator(this, new ClasspathFilePluginMetadata());
  609. final Collection<String> errors = validator.validate();
  610. if (errors.size() > 0) {
  611. log.error("Unable to validate required plugins or modules - plugin system shutting down");
  612. log.error("Failures:");
  613. for (final String error : errors) {
  614. log.error("\t{}", error);
  615. }
  616. shutdown();
  617. throw new PluginException("Unable to validate required plugins or modules");
  618. }
  619. }
  620. /**
  621. * @param timer the timer
  622. * @param message Message to log as info
  623. */
  624. private void logTime(Timer timer, String message) {
  625. Optional<OpTimer> opTimer = timer.getOpTimer();
  626. if (opTimer.isPresent()) {
  627. long elapsedSeconds = opTimer.get().snapshot().getElapsedTotalTime(TimeUnit.SECONDS);
  628. log.info("{} in {}s", message, elapsedSeconds);
  629. } else {
  630. log.info(message);
  631. }
  632. }
  633. /**
  634. * Fires the shutdown event
  635. *
  636. * @throws IllegalStateException if already shutdown or already in the
  637. * process of shutting down.
  638. * @since 2.0.0
  639. */
  640. @Override
  641. public void shutdown() {
  642. pluginTransactionContext.wrap(() -> {
  643. try (Timer ignored = PluginSystemInstrumentation.instance().pullSingleTimer("shutdown")) {
  644. tracker.setState(StateTracker.State.SHUTTING_DOWN);
  645. log.info("Preparing to shut down the plugin system");
  646. broadcastIgnoreError(new PluginFrameworkShuttingDownEvent(DefaultPluginManager.this, DefaultPluginManager.this));
  647. log.info("Shutting down the plugin system");
  648. broadcastIgnoreError(new PluginFrameworkShutdownEvent(DefaultPluginManager.this, DefaultPluginManager.this));
  649. pluginRegistry.clear();
  650. pluginEventManager.unregister(this);
  651. tracker.setState(StateTracker.State.SHUTDOWN);
  652. defaultPluginManagerJmxBridge.unregister();
  653. }
  654. });
  655. }
  656. @Override
  657. public final void warmRestart() {
  658. pluginTransactionContext.wrap(() -> {
  659. tracker.setState(StateTracker.State.WARM_RESTARTING);
  660. log.info("Initiating a warm restart of the plugin system");
  661. broadcastIgnoreError(new PluginFrameworkWarmRestartingEvent(DefaultPluginManager.this, DefaultPluginManager.this));
  662. // Make sure we reload plugins in order
  663. final List<Plugin> restartedPlugins = new ArrayList<>();
  664. final List<PluginLoader> loaders = new ArrayList<>(pluginLoaders);
  665. reverse(loaders);
  666. for (final PluginLoader loader : pluginLoaders) {
  667. for (final Map.Entry<Plugin, PluginLoader> entry : installedPluginsToPluginLoader.entrySet()) {
  668. if (entry.getValue() == loader) {
  669. final Plugin plugin = entry.getKey();
  670. if (isPluginEnabled(plugin.getKey())) {
  671. disablePluginModules(plugin);
  672. restartedPlugins.add(plugin);
  673. }
  674. }
  675. }
  676. }
  677. // then enable them in reverse order
  678. reverse(restartedPlugins);
  679. for (final Plugin plugin : restartedPlugins) {
  680. enableConfiguredPluginModules(plugin);
  681. }
  682. broadcastIgnoreError(new PluginFrameworkWarmRestartedEvent(DefaultPluginManager.this, DefaultPluginManager.this));
  683. tracker.setState(StateTracker.State.STARTED);
  684. });
  685. }
  686. @PluginEventListener
  687. public void onPluginModuleAvailable(final PluginModuleAvailableEvent event) {
  688. pluginTransactionContext.wrap(() -> enableConfiguredPluginModule(event.getModule().getPlugin(), event.getModule(), new HashSet<>()));
  689. }
  690. @PluginEventListener
  691. public void onPluginModuleUnavailable(final PluginModuleUnavailableEvent event) {
  692. pluginTransactionContext.wrap(() -> disablePluginModuleNoPersist(event.getModule()));
  693. }
  694. @PluginEventListener
  695. public void onPluginContainerUnavailable(final PluginContainerUnavailableEvent event) {
  696. pluginTransactionContext.wrap(() -> disablePluginWithoutPersisting(event.getPluginKey()));
  697. }
  698. @PluginEventListener
  699. public void onPluginRefresh(final PluginRefreshedEvent event) {
  700. pluginTransactionContext.wrap(() -> {
  701. final Plugin plugin = event.getPlugin();
  702. disablePluginModules(plugin);
  703. // It would be nice to fire this earlier, but doing it earlier than the disable of the plugin modules seems too early.
  704. // We should probably hook methods on NonValidatingOsgiBundleXmlApplicationContext (such as prepareRefresh ?) to
  705. // move this and the disable earlier if it makes sense.
  706. broadcastIgnoreError(new PluginEnablingEvent(plugin));
  707. if (enableConfiguredPluginModules(plugin)) {
  708. broadcastPluginEnabled(plugin);
  709. }
  710. });
  711. }
  712. /**
  713. * Set the plugin installation strategy for this manager
  714. *
  715. * @param pluginInstaller the plugin installation strategy to use
  716. * @see PluginInstaller
  717. */
  718. public void setPluginInstaller(final PluginInstaller pluginInstaller) {
  719. if (pluginInstaller instanceof RevertablePluginInstaller) {
  720. this.pluginInstaller = (RevertablePluginInstaller) pluginInstaller;
  721. } else {
  722. this.pluginInstaller = new NoOpRevertablePluginInstaller(pluginInstaller);
  723. }
  724. }
  725. @Override
  726. public Set<String> installPlugins(final PluginArtifact... pluginArtifacts) {
  727. final Map<String, PluginArtifact> validatedArtifacts = new LinkedHashMap<>();
  728. pluginTransactionContext.wrap(
  729. () -> {
  730. try {
  731. for (final PluginArtifact pluginArtifact : pluginArtifacts) {
  732. validatedArtifacts.put(validatePlugin(pluginArtifact), pluginArtifact);
  733. }
  734. } catch (final PluginParseException ex) {
  735. throw new PluginParseException("All plugins could not be validated", ex);
  736. }
  737. for (final Map.Entry<String, PluginArtifact> entry : validatedArtifacts.entrySet()) {
  738. pluginInstaller.installPlugin(entry.getKey(), entry.getValue());
  739. }
  740. scanForNewPlugins();
  741. }
  742. );
  743. return validatedArtifacts.keySet();
  744. }
  745. /**
  746. * Validate a plugin jar. Looks through all plugin loaders for ones that can
  747. * load the plugin and extract the plugin key as proof.
  748. *
  749. * @param pluginArtifact the jar file representing the plugin
  750. * @return The plugin key
  751. * @throws PluginParseException if the plugin cannot be parsed
  752. * @throws NullPointerException if <code>pluginJar</code> is null.
  753. */
  754. String validatePlugin(final PluginArtifact pluginArtifact) {
  755. boolean foundADynamicPluginLoader = false;
  756. for (final PluginLoader loader : pluginLoaders) {
  757. if (loader.isDynamicPluginLoader()) {
  758. foundADynamicPluginLoader = true;
  759. final String key = ((DynamicPluginLoader) loader).canLoad(pluginArtifact);
  760. if (key != null) {
  761. return key;
  762. }
  763. }
  764. }
  765. if (!foundADynamicPluginLoader) {
  766. throw new IllegalStateException("Should be at least one DynamicPluginLoader in the plugin loader list");
  767. }
  768. throw new PluginParseException("Jar " + pluginArtifact.getName() + " is not a valid plugin!");
  769. }
  770. @Override
  771. public int scanForNewPlugins() {
  772. final StateTracker.State state = tracker.get();
  773. checkState((StateTracker.State.RESUMING == state) || (StateTracker.State.STARTED == state),
  774. "Cannot scanForNewPlugins in state %s", state);
  775. final AtomicInteger numberFound = new AtomicInteger(0);
  776. pluginTransactionContext.wrap(() -> {
  777. for (final PluginLoader loader : pluginLoaders) {
  778. if (loader != null && loader.supportsAddition()) {
  779. final List<Plugin> pluginsToAdd = new ArrayList<>();
  780. for (Plugin plugin : loader.loadFoundPlugins(moduleDescriptorFactory)) {
  781. final Plugin oldPlugin = pluginRegistry.get(plugin.getKey());
  782. // Only actually install the plugin if its module
  783. // descriptors support it. Otherwise, mark it as
  784. // unloadable.
  785. if (!(plugin instanceof UnloadablePlugin)) {
  786. if (PluginUtils.doesPluginRequireRestart(plugin)) {
  787. if (oldPlugin == null) {
  788. markPluginInstallThatRequiresRestart(plugin);
  789. final UnloadablePlugin unloadablePlugin = UnloadablePluginFactory.createUnloadablePlugin(plugin);
  790. unloadablePlugin.setErrorText("Plugin requires a restart of the application due "
  791. + "to the following modules: " + PluginUtils.getPluginModulesThatRequireRestart(plugin));
  792. plugin = unloadablePlugin;
  793. } else {
  794. // If a plugin has been installed but is waiting for restart then we do not want to
  795. // put the plugin into the update state, we want to keep it in the install state.
  796. if (!PluginRestartState.INSTALL.equals(getPluginRestartState(plugin.getKey()))) {
  797. markPluginUpgradeThatRequiresRestart(plugin);
  798. }
  799. continue;
  800. }
  801. }
  802. // If the new plugin does not require restart we need to check what the restart state of
  803. // the old plugin was and act accordingly
  804. else if (oldPlugin != null && PluginUtils.doesPluginRequireRestart(oldPlugin)) {
  805. // If you have installed the plugin that requires restart and before restart you have
  806. // reinstalled a version of that plugin that does not require restart then you should
  807. // just go ahead and install that plugin. This means reverting the previous install
  808. // and letting the plugin fall into the plugins to add list
  809. if (PluginRestartState.INSTALL.equals(getPluginRestartState(oldPlugin.getKey()))) {
  810. revertRestartRequiredChange(oldPlugin.getKey());
  811. } else {
  812. markPluginUpgradeThatRequiresRestart(plugin);
  813. continue;
  814. }
  815. }
  816. pluginsToAdd.add(plugin);
  817. }
  818. }
  819. addPlugins(loader, pluginsToAdd);
  820. numberFound.addAndGet(pluginsToAdd.size());
  821. }
  822. }
  823. });
  824. return numberFound.get();
  825. }
  826. private void markPluginInstallThatRequiresRestart(final Plugin plugin) {
  827. log.info("Installed plugin '{}' requires a restart due to the following modules: {}", plugin, PluginUtils.getPluginModulesThatRequireRestart(plugin));
  828. updateRequiresRestartState(plugin.getKey(), PluginRestartState.INSTALL);
  829. }
  830. private void markPluginUpgradeThatRequiresRestart(final Plugin plugin) {
  831. log.info("Upgraded plugin '{}' requires a restart due to the following modules: {}", plugin, PluginUtils.getPluginModulesThatRequireRestart(plugin));
  832. updateRequiresRestartState(plugin.getKey(), PluginRestartState.UPGRADE);
  833. }
  834. private void markPluginUninstallThatRequiresRestart(final Plugin plugin) {
  835. log.info("Uninstalled plugin '{}' requires a restart due to the following modules: {}", plugin, PluginUtils.getPluginModulesThatRequireRestart(plugin));
  836. updateRequiresRestartState(plugin.getKey(), PluginRestartState.REMOVE);
  837. }
  838. private void updateRequiresRestartState(final String pluginKey, final PluginRestartState pluginRestartState) {
  839. persistentStateModifier.setPluginRestartState(pluginKey, pluginRestartState);
  840. onUpdateRequiresRestartState(pluginKey, pluginRestartState);
  841. }
  842. @SuppressWarnings("UnusedParameters")
  843. protected void onUpdateRequiresRestartState(final String pluginKey, final PluginRestartState pluginRestartState) {
  844. // nothing to do in this implementation
  845. }
  846. /**
  847. * Uninstalls the given plugin, emitting disabled and uninstalled events as it does so.
  848. *
  849. * @param plugin the plugin to uninstall.
  850. * @throws PluginException If the plugin or loader doesn't support uninstallation
  851. */
  852. @Override
  853. public void uninstall(final Plugin plugin) {
  854. pluginTransactionContext.wrap(() ->
  855. uninstallPlugins(singletonList(plugin))
  856. );
  857. }
  858. @Override
  859. public void uninstallPlugins(Collection<Plugin> plugins) {
  860. pluginTransactionContext.wrap(() -> {
  861. Map<Boolean, Set<Plugin>> requireRestart = plugins.stream()
  862. .collect(partitioningBy(PluginUtils::doesPluginRequireRestart, toSet()));
  863. // Plugins that require application restart will be uninstalled on the next application start
  864. // (see com.atlassian.plugin.descriptors.RequiresRestart).
  865. requireRestart.get(true)
  866. .forEach(plugin -> {
  867. ensurePluginAndLoaderSupportsUninstall(plugin);
  868. markPluginUninstallThatRequiresRestart(plugin);
  869. });
  870. Set<Plugin> pluginsToDisable = requireRestart.get(false);
  871. if (!pluginsToDisable.isEmpty()) {
  872. final DependentPlugins disabledPlugins = disablePluginsAndTheirDependencies(
  873. pluginsToDisable.stream().map(Plugin::getKey).collect(toList()),
  874. unmodifiableSet(new HashSet<>(asList(MANDATORY, OPTIONAL, DYNAMIC)))
  875. );
  876. disabledPlugins.getPluginsByTypes(singleton(MANDATORY), true).forEach(persistentStateModifier::disable);
  877. pluginsToDisable.forEach(p -> broadcastIgnoreError(new PluginUninstallingEvent(p)));
  878. pluginsToDisable.forEach(this::uninstallNoEvent);
  879. pluginsToDisable.forEach(p -> broadcastIgnoreError(new PluginUninstalledEvent(p)));
  880. reenableDependent(pluginsToDisable, disabledPlugins, PluginState.UNINSTALLED);
  881. }
  882. });
  883. }
  884. /**
  885. * Preforms an uninstallation without broadcasting the uninstallation event.
  886. *
  887. * @param plugin The plugin to uninstall
  888. * @since 2.5.0
  889. */
  890. protected void uninstallNoEvent(final Plugin plugin) {
  891. unloadPlugin(plugin);
  892. // PLUG-13: Plugins should not save state across uninstalls.
  893. persistentStateModifier.removeState(plugin);
  894. }
  895. /**
  896. * @param pluginKey The plugin key to revert
  897. * @throws PluginException If the revert cannot be completed
  898. */
  899. @Override
  900. public void revertRestartRequiredChange(final String pluginKey) {
  901. pluginTransactionContext.wrap(() -> {
  902. notNull("pluginKey", pluginKey);
  903. final PluginRestartState restartState = getState().getPluginRestartState(pluginKey);
  904. if (restartState == PluginRestartState.UPGRADE) {
  905. pluginInstaller.revertInstalledPlugin(pluginKey);
  906. } else if (restartState == PluginRestartState.INSTALL) {
  907. pluginInstaller.revertInstalledPlugin(pluginKey);
  908. pluginRegistry.remove(pluginK