View Javadoc

1   package com.atlassian.plugin.manager;
2   
3   import com.atlassian.plugin.Plugin;
4   import com.atlassian.plugin.PluginController;
5   import com.atlassian.plugin.PluginAccessor;
6   import com.atlassian.plugin.PluginState;
7   import com.atlassian.plugin.util.WaitUntil;
8   
9   import java.util.*;
10  
11  import org.apache.commons.logging.LogFactory;
12  import org.apache.commons.logging.Log;
13  
14  /**
15   * Helper class that handles the problem of enabling a set of plugins at once.  This functionality is used for both
16   * the initial plugin loading and manual plugin enabling.
17   *
18   * @since 2.2.0
19   */
20  class PluginEnabler
21  {
22      private final PluginAccessor pluginAccessor;
23      private final PluginController pluginController;
24      private Log log = LogFactory.getLog(PluginEnabler.class);
25  
26      public PluginEnabler(PluginAccessor pluginAccessor, PluginController pluginController)
27      {
28          this.pluginAccessor = pluginAccessor;
29          this.pluginController = pluginController;
30      }
31  
32      /**
33       * Determines, recursively, which disabled plugins this plugin depends upon, and enables all of them at once.
34       *
35       * @param plugin The requested plugin to enable
36       */
37      void enableRecursively(Plugin plugin)
38      {
39          Set<String> dependentKeys = new HashSet<String>();
40          scanDependencies(plugin, dependentKeys);
41  
42          List<Plugin> pluginsToEnable = new ArrayList<Plugin>();
43          for (String key : dependentKeys)
44          {
45              pluginsToEnable.add(pluginAccessor.getPlugin(key));
46          }
47          enable(pluginsToEnable);
48      }
49  
50      /**
51       * Enables a collection of plugins at once, waiting for 60 seconds.  If any plugins are still in the enabling state,
52       * the plugins are explicitly disabled.
53       *
54       * @param plugins The plugins to enable
55       */
56      void enable(Collection<Plugin> plugins)
57      {
58  
59          final Set<Plugin> pluginsInEnablingState = new HashSet<Plugin>();
60          for (final Plugin plugin : plugins)
61          {
62              try
63              {
64                  plugin.enable();
65                  if (plugin.getPluginState() == PluginState.ENABLING)
66                  {
67                      pluginsInEnablingState.add(plugin);
68                  }
69              }
70              catch (final RuntimeException ex)
71              {
72                  log.error("Unable to enable plugin " + plugin.getKey(), ex);
73              }
74          }
75  
76          if (!pluginsInEnablingState.isEmpty())
77          {
78              // Now try to enable plugins that weren't enabled before, probably due to dependency ordering issues
79              WaitUntil.invoke(new WaitUntil.WaitCondition()
80              {
81                  public boolean isFinished()
82                  {
83                      for (final Iterator<Plugin> i = pluginsInEnablingState.iterator(); i.hasNext();)
84                      {
85                          final Plugin plugin = i.next();
86                          if (plugin.getPluginState() != PluginState.ENABLING)
87                          {
88                              i.remove();
89                          }
90                      }
91                      return pluginsInEnablingState.isEmpty();
92                  }
93  
94                  public String getWaitMessage()
95                  {
96                      return "Plugins that have yet to enabled: " + pluginsInEnablingState;
97                  }
98              }, 60);
99  
100             // Disable any plugins that aren't enabled by now
101             if (!pluginsInEnablingState.isEmpty())
102             {
103                 final StringBuilder sb = new StringBuilder();
104                 for (final Plugin plugin : pluginsInEnablingState)
105                 {
106                     sb.append(plugin.getKey()).append(',');
107                     pluginController.disablePlugin(plugin.getKey());
108                 }
109                 sb.deleteCharAt(sb.length() - 1);
110                 log.error("Unable to start the following plugins: " + sb.toString());
111             }
112         }
113     }
114 
115     /**
116      * Scans, recursively, to build a set of plugin dependencies for the target plugin
117      *
118      * @param plugin The plugin to scan
119      * @param dependentKeys The set of keys collected so far
120      */
121     private void scanDependencies(Plugin plugin, Set<String> dependentKeys)
122     {
123         dependentKeys.add(plugin.getKey());
124         
125         // Ensure dependent plugins are enabled first
126         for (String dependencyKey : plugin.getRequiredPlugins())
127         {
128             if (!dependentKeys.contains(dependencyKey) && !pluginAccessor.isPluginEnabled(dependencyKey))
129             {
130                 scanDependencies(pluginAccessor.getPlugin(dependencyKey), dependentKeys);
131             }
132         }
133     }
134 }