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 import java.util.concurrent.TimeUnit;
8
9 import com.atlassian.beehive.ClusterLock;
10 import com.atlassian.beehive.ClusterLockService;
11 import com.atlassian.plugin.Plugin;
12 import com.atlassian.plugin.PluginAccessor;
13 import com.atlassian.plugin.event.PluginEventListener;
14 import com.atlassian.plugin.event.PluginEventManager;
15 import com.atlassian.plugin.event.events.PluginEnabledEvent;
16 import com.atlassian.sal.api.lifecycle.LifecycleAware;
17 import com.atlassian.sal.api.message.Message;
18 import com.atlassian.sal.api.pluginsettings.PluginSettingsFactory;
19 import com.atlassian.sal.api.transaction.TransactionCallback;
20 import com.atlassian.sal.api.transaction.TransactionTemplate;
21 import com.atlassian.sal.api.upgrade.PluginUpgradeManager;
22 import com.atlassian.sal.api.upgrade.PluginUpgradeTask;
23
24 import com.atlassian.sal.core.message.DefaultMessage;
25 import com.google.common.collect.ImmutableList;
26 import org.slf4j.Logger;
27 import org.slf4j.LoggerFactory;
28 import org.springframework.beans.factory.DisposableBean;
29 import org.springframework.beans.factory.InitializingBean;
30
31
32
33
34
35
36 public class DefaultPluginUpgradeManager implements PluginUpgradeManager, LifecycleAware, InitializingBean, DisposableBean
37 {
38 private static final Logger log = LoggerFactory.getLogger(DefaultPluginUpgradeManager.class);
39
40 protected static final String LOCK_TIMEOUT_PROPERTY = "sal.upgrade.task.lock.timeout";
41 protected static final int LOCK_TIMEOUT_SECONDS = Integer.getInteger(LOCK_TIMEOUT_PROPERTY, 300000);
42
43 private volatile boolean started = false;
44
45 private final List<PluginUpgradeTask> upgradeTasks;
46 private final TransactionTemplate transactionTemplate;
47 private final PluginAccessor pluginAccessor;
48 private final PluginSettingsFactory pluginSettingsFactory;
49 private final PluginEventManager pluginEventManager;
50 private final ClusterLockService clusterLockService;
51
52 public DefaultPluginUpgradeManager(final List<PluginUpgradeTask> upgradeTasks, final TransactionTemplate transactionTemplate,
53 final PluginAccessor pluginAccessor, final PluginSettingsFactory pluginSettingsFactory,
54 final PluginEventManager pluginEventManager, final ClusterLockService clusterLockService)
55 {
56 this.upgradeTasks = upgradeTasks;
57 this.transactionTemplate = transactionTemplate;
58 this.pluginAccessor = pluginAccessor;
59 this.pluginSettingsFactory = pluginSettingsFactory;
60 this.pluginEventManager = pluginEventManager;
61 this.clusterLockService = clusterLockService;
62 }
63
64
65
66
67
68
69
70
71
72
73
74 @SuppressWarnings("unchecked")
75 @Deprecated
76 public void onBind(final PluginUpgradeTask task, final Map props)
77 {
78
79 log.debug("onbind task = [" + task.getPluginKey() + ", " + task.getBuildNumber() + "] ");
80 }
81
82 public void onStart()
83 {
84 log.debug("onStart");
85 final List<Message> messages = upgrade();
86
87
88
89 if (messages != null)
90 {
91 for (final Message msg : messages)
92 {
93 log.error("Upgrade error: " + msg);
94 }
95 }
96
97 started = true;
98 }
99
100 @PluginEventListener
101 public void onPluginEnabled(PluginEnabledEvent event)
102 {
103
104 if (started)
105 {
106
107 final List<Message> messages = upgradeInternal(event.getPlugin());
108 if (messages != null && messages.size() > 0)
109 {
110 log.error("Error(s) encountered while upgrading plugin '" + event.getPlugin().getName() + "' on enable.");
111 for (final Message msg : messages)
112 {
113 log.error("Upgrade error: " + msg);
114 }
115 }
116 }
117
118 }
119
120
121
122
123 protected Map<String, List<PluginUpgradeTask>> getUpgradeTasks()
124 {
125 final Map<String, List<PluginUpgradeTask>> pluginUpgrades = new HashMap<String, List<PluginUpgradeTask>>();
126
127
128 for (final PluginUpgradeTask upgradeTask : upgradeTasks)
129 {
130 List<PluginUpgradeTask> upgrades = pluginUpgrades.get(upgradeTask.getPluginKey());
131 if (upgrades == null)
132 {
133 upgrades = new ArrayList<PluginUpgradeTask>();
134 pluginUpgrades.put(upgradeTask.getPluginKey(), upgrades);
135 }
136 upgrades.add(upgradeTask);
137 }
138
139 return pluginUpgrades;
140 }
141
142
143 @SuppressWarnings("unchecked")
144 public List<Message> upgrade()
145 {
146 return upgradeInternal();
147 }
148
149 public List<Message> upgradeInternal()
150 {
151 log.info("Running plugin upgrade tasks...");
152
153
154 final Map<String, List<PluginUpgradeTask>> pluginUpgrades = getUpgradeTasks();
155
156
157 final ArrayList<Message> messages = new ArrayList<Message>();
158
159
160 for (final String pluginKey : pluginUpgrades.keySet())
161 {
162 final List<PluginUpgradeTask> upgrades = pluginUpgrades.get(pluginKey);
163
164 final List<Message> upgradeMessages = upgradePlugin(pluginKey, upgrades);
165 if (upgradeMessages != null)
166 {
167 messages.addAll(upgradeMessages);
168 }
169 }
170
171 return messages;
172 }
173
174 public List<Message> upgradeInternal(Plugin plugin)
175 {
176 final Map<String, List<PluginUpgradeTask>> pluginUpgrades = getUpgradeTasks();
177 final String pluginKey = plugin.getKey();
178 final List<PluginUpgradeTask> upgrades = pluginUpgrades.get(pluginKey);
179 if (upgrades == null)
180 {
181
182 return null;
183 }
184 return upgradePlugin(pluginKey, upgrades);
185 }
186
187 private List<Message> upgradePlugin(final String pluginKey, final List<PluginUpgradeTask> upgrades)
188 {
189 return transactionTemplate.execute(new TransactionCallback<List<Message>>()
190 {
191 @Override
192 public List<Message> doInTransaction()
193 {
194 final Plugin plugin = pluginAccessor.getPlugin(pluginKey);
195 if (plugin == null)
196 {
197 throw new IllegalArgumentException("Invalid plugin key: " + pluginKey);
198 }
199
200 final PluginUpgrader pluginUpgrader = new PluginUpgrader(plugin, pluginSettingsFactory.createGlobalSettings(), upgrades);
201
202 final String lockName = "sal.upgrade." + pluginKey;
203 final ClusterLock lock = clusterLockService.getLockForName(lockName);
204 try
205 {
206 if (!lock.tryLock(LOCK_TIMEOUT_SECONDS, TimeUnit.SECONDS))
207 {
208 final String timeoutMessage = "unable to acquire cluster lock named '" + lockName + "' after waiting " + LOCK_TIMEOUT_SECONDS + " seconds; note that this timeout may be adjusted via the system property '" + LOCK_TIMEOUT_PROPERTY + "'";
209 log.error(timeoutMessage);
210 return ImmutableList.<Message>of(new DefaultMessage(timeoutMessage));
211 }
212 }
213 catch (InterruptedException e)
214 {
215 final String interruptedMessage = "interrupted while trying to acquire cluster lock named '" + lockName + "' " + e.getMessage();
216 log.error(interruptedMessage);
217 return ImmutableList.<Message>of(new DefaultMessage(interruptedMessage));
218 }
219
220 try
221 {
222 return pluginUpgrader.upgrade();
223 }
224 finally
225 {
226 lock.unlock();
227 }
228 }
229 });
230 }
231
232 public void afterPropertiesSet() throws Exception
233 {
234 pluginEventManager.register(this);
235 }
236
237 public void destroy() throws Exception
238 {
239 pluginEventManager.unregister(this);
240 }
241 }