View Javadoc

1   package com.atlassian.sal.core.upgrade;
2   
3   import java.util.ArrayList;
4   import java.util.HashMap;
5   import java.util.List;
6   import java.util.Map;
7   
8   import com.atlassian.plugin.Plugin;
9   import com.atlassian.plugin.PluginAccessor;
10  import com.atlassian.plugin.event.PluginEventListener;
11  import com.atlassian.plugin.event.PluginEventManager;
12  import com.atlassian.plugin.event.events.PluginEnabledEvent;
13  import com.atlassian.sal.api.lifecycle.LifecycleAware;
14  import com.atlassian.sal.api.message.Message;
15  import com.atlassian.sal.api.pluginsettings.PluginSettingsFactory;
16  import com.atlassian.sal.api.transaction.TransactionCallback;
17  import com.atlassian.sal.api.transaction.TransactionTemplate;
18  import com.atlassian.sal.api.upgrade.PluginUpgradeManager;
19  import com.atlassian.sal.api.upgrade.PluginUpgradeTask;
20  
21  import org.slf4j.Logger;
22  import org.slf4j.LoggerFactory;
23  import org.springframework.beans.factory.DisposableBean;
24  import org.springframework.beans.factory.InitializingBean;
25  
26  /**
27   * Processes plugin upgrade operations.
28   * <p/>
29   * Upgrades are triggered by the start lifecycle event, and plugin enabled.
30   */
31  public class DefaultPluginUpgradeManager implements PluginUpgradeManager, LifecycleAware, InitializingBean, DisposableBean
32  {
33      private static final Logger log = LoggerFactory.getLogger(DefaultPluginUpgradeManager.class);
34  
35      private volatile boolean started = false;
36  
37      private final List<PluginUpgradeTask> upgradeTasks;
38      private final TransactionTemplate transactionTemplate;
39      private final PluginAccessor pluginAccessor;
40      private final PluginSettingsFactory pluginSettingsFactory;
41      private final PluginEventManager pluginEventManager;
42  
43      public DefaultPluginUpgradeManager(final List<PluginUpgradeTask> upgradeTasks, final TransactionTemplate transactionTemplate,
44                                         final PluginAccessor pluginAccessor, final PluginSettingsFactory pluginSettingsFactory, final PluginEventManager pluginEventManager)
45      {
46          this.upgradeTasks = upgradeTasks;
47          this.transactionTemplate = transactionTemplate;
48          this.pluginAccessor = pluginAccessor;
49          this.pluginSettingsFactory = pluginSettingsFactory;
50          this.pluginEventManager = pluginEventManager;
51      }
52  
53      /**
54       * Notifies the plugin upgrade manager that a plugin update tasks has been registered.
55       * <p/>
56       * This method does nothing but logging at the moment. Is is now deprecated since it could result in circular
57       * dependency when trying to bind already exposed update tasks to plugin manager that is being created.
58       *
59       * @param task  the upgrade task that is being bound
60       * @param props the set of properties that the upgrade task was registered with
61       * @deprecated as of 2.0.16 no longer used we now use the eventing framework to run upgrade tasks for plugins that are 'enabled', see {@link #onPluginEnabled(com.atlassian.plugin.event.events.PluginEnabledEvent)}
62       */
63      @SuppressWarnings("unchecked")
64      @Deprecated
65      public void onBind(final PluginUpgradeTask task, final Map props)
66      {
67          // Doing lots here....
68          log.debug("onbind task = [" + task.getPluginKey() + ", " + task.getBuildNumber() + "] ");
69      }
70  
71      public void onStart()
72      {
73          log.debug("onStart");
74          final List<Message> messages = upgrade();
75  
76          // TODO 1: should do something useful with the messages
77          // TODO 2: we don't know what upgrade tasks these messages came from
78          if (messages != null)
79          {
80              for (final Message msg : messages)
81              {
82                  log.error("Upgrade error: " + msg);
83              }
84          }
85  
86          started = true;
87      }
88  
89      @PluginEventListener
90      public void onPluginEnabled(PluginEnabledEvent event)
91      {
92          // Check if the Application is fully started:
93          if (started)
94          {
95              // Run upgrades for this plugin that as been enabled AFTER the onStart event.
96              final List<Message> messages = upgradeInternal(event.getPlugin());
97              if (messages != null && messages.size() > 0)
98              {
99                  log.error("Error(s) encountered while upgrading plugin '" + event.getPlugin().getName() + "' on enable.");
100                 for (final Message msg : messages)
101                 {
102                     log.error("Upgrade error: " + msg);
103                 }
104             }
105         }
106         // If onStart() has not occurred yet then ignore event - we need to wait until the App is started properly.
107     }
108 
109     /**
110      * @return map of all upgrade tasks (stored by pluginKey)
111      */
112     protected Map<String, List<PluginUpgradeTask>> getUpgradeTasks()
113     {
114         final Map<String, List<PluginUpgradeTask>> pluginUpgrades = new HashMap<String, List<PluginUpgradeTask>>();
115 
116         // Find all implementations of PluginUpgradeTask
117         for (final PluginUpgradeTask upgradeTask : upgradeTasks)
118         {
119             List<PluginUpgradeTask> upgrades = pluginUpgrades.get(upgradeTask.getPluginKey());
120             if (upgrades == null)
121             {
122                 upgrades = new ArrayList<PluginUpgradeTask>();
123                 pluginUpgrades.put(upgradeTask.getPluginKey(), upgrades);
124             }
125             upgrades.add(upgradeTask);
126         }
127 
128         return pluginUpgrades;
129     }
130 
131 
132     @SuppressWarnings("unchecked")
133     public List<Message> upgrade()
134     {
135         return upgradeInternal();
136     }
137 
138     public List<Message> upgradeInternal()
139     {
140         log.info("Running plugin upgrade tasks...");
141 
142         // 1. get all upgrade tasks for all plugins
143         final Map<String, List<PluginUpgradeTask>> pluginUpgrades = getUpgradeTasks();
144 
145 
146         final ArrayList<Message> messages = new ArrayList<Message>();
147 
148         // 2. for each plugin, sort tasks by build number and execute them
149         for (final String pluginKey : pluginUpgrades.keySet())
150         {
151             final List<PluginUpgradeTask> upgrades = pluginUpgrades.get(pluginKey);
152 
153             final List<Message> upgradeMessages = upgradePlugin(pluginKey, upgrades);
154             if (upgradeMessages != null)
155             {
156                 messages.addAll(upgradeMessages);
157             }
158         }
159 
160         return messages;
161     }
162 
163     public List<Message> upgradeInternal(Plugin plugin)
164     {
165         final Map<String, List<PluginUpgradeTask>> pluginUpgrades = getUpgradeTasks();
166         final String pluginKey = plugin.getKey();
167         final List<PluginUpgradeTask> upgrades = pluginUpgrades.get(pluginKey);
168         if (upgrades == null)
169         {
170             // nothing to do
171             return null;
172         }
173         return upgradePlugin(pluginKey, upgrades);
174     }
175 
176     private List<Message> upgradePlugin(final String pluginKey, final List<PluginUpgradeTask> upgrades)
177     {
178         return transactionTemplate.execute(new TransactionCallback<List<Message>>()
179         {
180             @Override
181             public List<Message> doInTransaction()
182             {
183                 final Plugin plugin = pluginAccessor.getPlugin(pluginKey);
184                 if (plugin == null)
185                 {
186                     throw new IllegalArgumentException("Invalid plugin key: " + pluginKey);
187                 }
188 
189                 final PluginUpgrader pluginUpgrader = new PluginUpgrader(plugin, pluginSettingsFactory.createGlobalSettings(), upgrades);
190                 return pluginUpgrader.upgrade();
191             }
192         });
193     }
194 
195     public void afterPropertiesSet() throws Exception
196     {
197         pluginEventManager.register(this);
198     }
199 
200     public void destroy() throws Exception
201     {
202         pluginEventManager.unregister(this);
203     }
204 }