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 org.apache.log4j.Logger;
9   
10  import com.atlassian.plugin.Plugin;
11  import com.atlassian.plugin.PluginAccessor;
12  import com.atlassian.sal.api.lifecycle.LifecycleAware;
13  import com.atlassian.sal.api.message.Message;
14  import com.atlassian.sal.api.pluginsettings.PluginSettingsFactory;
15  import com.atlassian.sal.api.transaction.TransactionCallback;
16  import com.atlassian.sal.api.transaction.TransactionTemplate;
17  import com.atlassian.sal.api.upgrade.PluginUpgradeManager;
18  import com.atlassian.sal.api.upgrade.PluginUpgradeTask;
19  
20  /**
21   * Processes plugin upgrade operations.  Upgrades are triggered by the start lifecycle event.
22   *
23   * NOTE: Currently, this implementation only runs upgrade tasks when the plugins system is initialized or restarted.
24   * If your plugin is disabled and it provides an upgrade task and a user then enables your plugin, the upgrade task
25   * will not run until the plugins system or application is restarted.
26   */
27  public class DefaultPluginUpgradeManager implements PluginUpgradeManager, LifecycleAware
28  {
29  	private static final Logger log = Logger.getLogger(DefaultPluginUpgradeManager.class);
30  
31      private final List<PluginUpgradeTask> upgradeTasks;
32      private final TransactionTemplate transactionTemplate;
33      private final PluginAccessor pluginAccessor;
34      private final PluginSettingsFactory pluginSettingsFactory;
35  
36      public DefaultPluginUpgradeManager(final List<PluginUpgradeTask> upgradeTasks, final TransactionTemplate transactionTemplate,
37                                         final PluginAccessor pluginAccessor, final PluginSettingsFactory pluginSettingsFactory)
38      {
39          this.upgradeTasks = upgradeTasks;
40          this.transactionTemplate = transactionTemplate;
41          this.pluginAccessor = pluginAccessor;
42          this.pluginSettingsFactory = pluginSettingsFactory;
43      }
44  
45      /**
46       * Notifies the plugin upgrade manager that a plugin update tasks has been registered.
47       * 
48       * This method does nothing but logging at the moment. Is is now deprecated since it could result in circular
49       * dependency when trying to bind already exposed update tasks to plugin manager that is being created.
50       * 
51       * @deprecated
52       * @param task the upgrade task that is being bound
53       * @param props the set of properties that the upgrade task was registered with
54       */
55      @SuppressWarnings("unchecked")
56      @Deprecated
57      public void onBind(final PluginUpgradeTask task, final Map props)
58      {
59          // Doing lots here....
60          log.debug("onbind task = [" + task.getPluginKey() + ", " + task.getBuildNumber() + "] ");
61      }
62  
63      public void onStart()
64      {
65          log.debug("onStart");
66          final List<Message> messages = upgrade();
67  
68          // TODO 1: should do something useful with the messages
69          // TODO 2: we don't know what upgrade tasks these messages came from
70          if (messages != null)
71          {
72              for(final Message msg : messages)
73              {
74                  log.error("Upgrade error: "+msg);
75              }
76          }
77      }
78  
79      /**
80  	 * @return map of all upgrade tasks (stored by pluginKey)
81  	 */
82  	protected Map<String, List<PluginUpgradeTask>> getUpgradeTasks()
83  	{
84  		final Map<String, List<PluginUpgradeTask>> pluginUpgrades = new HashMap<String, List<PluginUpgradeTask>>();
85  
86  		// Find all implementations of PluginUpgradeTask
87      	for (final PluginUpgradeTask upgradeTask : upgradeTasks)
88  		{
89      		List<PluginUpgradeTask> upgrades = pluginUpgrades.get(upgradeTask.getPluginKey());
90      		if (upgrades==null)
91      		{
92      			upgrades=new ArrayList<PluginUpgradeTask>();
93      			pluginUpgrades.put(upgradeTask.getPluginKey(), upgrades);
94      		}
95      		upgrades.add(upgradeTask);
96  		}
97  
98      	return pluginUpgrades;
99  	}
100 
101 
102     @SuppressWarnings("unchecked")
103     public List<Message> upgrade()
104 	{
105 		//JRA-737: Need to ensure upgrades run in a transaction.  Just calling upgrade here may not provide this
106         //as no this may be executed outside of a 'normal' context where a transaction is available.
107         final List<Message> messages = (List<Message>) transactionTemplate.execute(new TransactionCallback()
108         {
109             public Object doInTransaction()
110             {
111                 return upgradeInternal();
112             }
113         });
114 		return messages;
115 	}
116 
117 	public List<Message> upgradeInternal()
118 	{
119         log.info("Running plugin upgrade tasks...");
120 
121 		// 1. get all upgrade tasks for all plugins
122 		final Map<String, List<PluginUpgradeTask>> pluginUpgrades = getUpgradeTasks();
123 
124 
125 		final ArrayList<Message> messages = new ArrayList<Message>();
126 
127 		// 2. for each plugin, sort tasks by build number and execute them
128 		for (final String pluginKey : pluginUpgrades.keySet())
129 		{
130 			final List<PluginUpgradeTask> upgrades = pluginUpgrades.get(pluginKey);
131 
132 			final Plugin plugin = pluginAccessor.getPlugin(pluginKey);
133 			if (plugin == null)
134 				throw new IllegalArgumentException("Invalid plugin key: " + pluginKey);
135 
136 			final PluginUpgrader pluginUpgrader = new PluginUpgrader(plugin, pluginSettingsFactory.createGlobalSettings(), upgrades);
137 			final List<Message> upgradeMessages = pluginUpgrader.upgrade();
138 			if (upgradeMessages != null)
139 			{
140 				messages.addAll(upgradeMessages);
141 			}
142 		}
143 
144 		return messages;
145 	}
146 
147 }