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