View Javadoc
1   package com.atlassian.plugin.manager;
2   
3   import com.atlassian.annotations.nonnull.ReturnValuesAreNonnullByDefault;
4   import com.atlassian.plugin.ModuleDescriptor;
5   import com.atlassian.plugin.Plugin;
6   import com.atlassian.plugin.metadata.PluginMetadataManager;
7   import com.atlassian.plugin.parsers.SafeModeCommandLineArguments;
8   import com.atlassian.plugin.parsers.SafeModeCommandLineArgumentsFactory;
9   import com.google.common.base.Supplier;
10  import org.slf4j.Logger;
11  import org.slf4j.LoggerFactory;
12  
13  import javax.annotation.ParametersAreNonnullByDefault;
14  import java.util.Comparator;
15  import java.util.Map;
16  import java.util.Optional;
17  
18  import static com.atlassian.plugin.manager.ApplicationDefinedPluginsProvider.NO_APPLICATION_PLUGINS;
19  import static com.atlassian.plugin.manager.PluginEnabledState.UNKNOWN_ENABLED_TIME;
20  import static com.google.common.base.Suppliers.memoize;
21  import static java.util.Collections.emptyList;
22  
23  /**
24   * Default implementation of {@link SafeModeManager}
25   * NB: The safe mode applies only in single node environment! The end application (e.g. JIRA) must implement
26   * {@link ClusterEnvironmentProvider} to trigger the safe mode manager logic
27   */
28  @ParametersAreNonnullByDefault
29  @ReturnValuesAreNonnullByDefault
30  public class DefaultSafeModeManager implements SafeModeManager {
31      private static final Logger LOGGER = LoggerFactory.getLogger(DefaultSafeModeManager.class);
32      private final SafeModeCommandLineArguments commandLineArguments;
33      private final PluginMetadataManager pluginMetadataManager;
34      private final ApplicationDefinedPluginsProvider appRelatedPluginsProvider;
35      private final ClusterEnvironmentProvider clusterEnvironmentProvider;
36      private final Supplier<Optional<String>> lastEnabledPluginKey = memoize(new Supplier<Optional<String>>() {
37          @Override
38          public Optional<String> get() {
39              PluginPersistentState state = pluginPersistentStateStore.load();
40              if(state.getStatesMap().isEmpty()){
41                  return Optional.empty();
42              }
43              Optional<Map.Entry<String, PluginEnabledState>> mostRecentlyEnabled =
44                      state
45                              .getStatesMap()
46                              .entrySet()
47                              .stream()
48                              .max(Comparator.comparingLong(entry -> entry.getValue().getTimestamp()));
49              if(mostRecentlyEnabled.isPresent() &&
50                      mostRecentlyEnabled.get().getValue().getTimestamp() == UNKNOWN_ENABLED_TIME){
51                  return Optional.empty();
52              }
53              return(mostRecentlyEnabled.map(Map.Entry::getKey));
54          }
55  
56      });
57  
58      private final boolean isInSafeMode;
59  
60      private final PluginPersistentStateStore pluginPersistentStateStore;
61  
62      public DefaultSafeModeManager(PluginMetadataManager pluginMetadataManager,
63                                    ClusterEnvironmentProvider clusterEnvironmentProvider,
64                                    SafeModeCommandLineArgumentsFactory safeModeCommandLineArgumentsFactory,
65                                    PluginPersistentStateStore pluginPersistentStateStore) {
66          this(pluginMetadataManager,
67                  NO_APPLICATION_PLUGINS, clusterEnvironmentProvider, safeModeCommandLineArgumentsFactory, pluginPersistentStateStore);
68      }
69  
70      DefaultSafeModeManager(final PluginMetadataManager pluginMetadataManager,
71                             final ApplicationDefinedPluginsProvider appRelatedPluginsProvider,
72                             final ClusterEnvironmentProvider clusterEnvironmentProvider,
73                             final SafeModeCommandLineArgumentsFactory safeModeCommandLineArgumentsFactory,
74                             final PluginPersistentStateStore pluginPersistentStateStore) {
75          this.pluginMetadataManager = pluginMetadataManager;
76          this.commandLineArguments = safeModeCommandLineArgumentsFactory.get();
77          this.appRelatedPluginsProvider = appRelatedPluginsProvider;
78          this.clusterEnvironmentProvider = clusterEnvironmentProvider;
79          this.pluginPersistentStateStore = pluginPersistentStateStore;
80          this.isInSafeMode = !clusterEnvironmentProvider.isInCluster() && (commandLineArguments.isSafeMode());
81          if (clusterEnvironmentProvider.isInCluster() && (commandLineArguments.isSafeMode() ||
82                  !commandLineArguments.getDisabledPlugins().orElse(emptyList()).isEmpty())) {
83              LOGGER.warn("Add-ons disable options from '{}' are being ignored due to start up in clustered mode!",
84                      commandLineArguments.getSafeModeArguments());
85          }
86      }
87  
88      @Override
89      public boolean pluginShouldBeStarted(final Plugin plugin, final Iterable<ModuleDescriptor> descriptors) {
90          return clusterEnvironmentProvider.isInCluster() ||
91                  (!isPluginDisabledByDisableLastEnabled(plugin)
92                          && !isPluginDisabledBySafeMode(plugin, descriptors)
93                          && !commandLineArguments.isDisabledByParam(plugin.getKey()));
94      }
95  
96      @Override
97      public boolean isInSafeMode() {
98          return isInSafeMode;
99      }
100 
101     private boolean isPluginDisabledByDisableLastEnabled(Plugin plugin){
102         return (commandLineArguments.shouldLastEnabledBeDisabled()
103                 && plugin.getKey().equals(lastEnabledPluginKey.get().orElse(null)));
104 
105     }
106 
107     private boolean isPluginDisabledBySafeMode(Plugin plugin, Iterable<ModuleDescriptor> descriptors){
108         return (commandLineArguments.isSafeMode() && !isSystemPlugin(plugin, descriptors));
109     }
110 
111     /**
112      * Checks if the plugin is a system one (or is present within the application descriptors plugins).
113      *
114      * @param plugin      plugin to be checked
115      * @param descriptors list of module descriptors to find the plugin references within
116      * @return true if the plugin is a system one or is present within one of the application module descriptors
117      */
118     @SuppressWarnings("unchecked")
119     private boolean isSystemPlugin(final Plugin plugin, Iterable<ModuleDescriptor> descriptors) {
120         return pluginMetadataManager.isSystemProvided(plugin) ||
121                 !pluginMetadataManager.isOptional(plugin) ||
122                 appRelatedPluginsProvider.getPluginKeys(descriptors).contains(plugin.getKey());
123     }
124 }