1 package com.atlassian.sal.core.upgrade;
2
3 import com.atlassian.plugin.Plugin;
4 import com.atlassian.sal.api.message.Message;
5 import com.atlassian.sal.api.pluginsettings.PluginSettings;
6 import com.atlassian.sal.api.upgrade.PluginUpgradeTask;
7 import com.atlassian.sal.core.message.DefaultMessage;
8 import org.slf4j.Logger;
9 import org.slf4j.LoggerFactory;
10
11 import java.io.PrintWriter;
12 import java.io.StringWriter;
13 import java.util.ArrayList;
14 import java.util.Arrays;
15 import java.util.Collection;
16 import java.util.Collections;
17 import java.util.Comparator;
18 import java.util.List;
19
20
21
22
23 public class PluginUpgrader {
24
25 public static final String BUILD = ":build";
26 protected List<PluginUpgradeTask> upgradeTasks;
27 private static final Logger log = LoggerFactory.getLogger(PluginUpgrader.class);
28 protected Plugin plugin;
29 protected PluginSettings pluginSettings;
30 protected List<Message> errors = new ArrayList<>();
31
32 private static final Comparator<PluginUpgradeTask> UPGRADE_TASK_COMPARATOR = (t1, t2) -> {
33 if (t1 == null) {
34 return -1;
35 }
36 if (t2 == null) {
37 return 1;
38 }
39 if (t1.getBuildNumber() > t2.getBuildNumber()) {
40 return 1;
41 }
42
43 return (t1.getBuildNumber() < t2.getBuildNumber()) ? -1 : 0;
44 };
45
46 protected PluginUpgrader(final Plugin plugin, final PluginSettings pluginSettings, final List<PluginUpgradeTask> upgradeTasks) {
47 this.plugin = plugin;
48 this.pluginSettings = pluginSettings;
49 this.upgradeTasks = new ArrayList<>(upgradeTasks);
50 Collections.sort(this.upgradeTasks, UPGRADE_TASK_COMPARATOR);
51 }
52
53 protected List<Message> upgrade() {
54 if (needUpgrade()) {
55 doUpgrade();
56 }
57 return errors;
58 }
59
60 protected void doUpgrade() {
61 try {
62 log.info("Upgrading plugin " + plugin.getKey());
63 for (final PluginUpgradeTask upgradeTask : upgradeTasks) {
64
65 if (upgradeTask.getBuildNumber() <= getDataBuildNumber()) {
66
67 continue;
68 }
69
70 final Collection<Message> messages = upgradeTask.doUpgrade();
71
72 if (messages == null || messages.isEmpty()) {
73 upgradeTaskSucceeded(upgradeTask);
74 } else {
75 upgradeTaskFailed(upgradeTask, messages);
76 }
77 }
78 } catch (final Throwable e) {
79
80 StringWriter sw = new StringWriter();
81 e.printStackTrace(new PrintWriter(sw));
82
83
84 errors.add(new DefaultMessage("Unexpected exception caught during plugin upgrade: " + sw.toString()));
85 log.error("Upgrade failed: " + e.getMessage(), e);
86 } finally {
87 postUpgrade();
88 }
89 }
90
91
92 protected void upgradeTaskSucceeded(final PluginUpgradeTask upgradeTask) {
93 setDataBuildNumber(upgradeTask.getBuildNumber());
94 log.info("Upgraded plugin " + upgradeTask.getPluginKey() + " to version " + upgradeTask.getBuildNumber() + " - " + upgradeTask.getShortDescription());
95 }
96
97 protected void upgradeTaskFailed(final PluginUpgradeTask upgradeTask, final Collection<Message> messages) {
98 errors.addAll(messages);
99 final StringBuilder msg = new StringBuilder();
100 msg.append("Plugin upgrade failed for ").append(upgradeTask.getPluginKey());
101 msg.append(" to version ").append(upgradeTask.getBuildNumber());
102 msg.append(" - ").append(upgradeTask.getShortDescription());
103 msg.append("\n");
104 for (final Message message : messages) {
105 msg.append("\t* ").append(message.getKey()).append(" ").append(Arrays.toString(message.getArguments()));
106 }
107
108 log.warn(msg.toString());
109 }
110
111 protected List<Message> getErrors() {
112 return errors;
113 }
114
115 protected boolean needUpgrade() {
116 final PluginUpgradeTask lastUpgradeTask = this.upgradeTasks.get(this.upgradeTasks.size() - 1);
117 final int dataBuildNumber = getDataBuildNumber();
118 final int lastUpgradeTaskBuildNumber = lastUpgradeTask.getBuildNumber();
119 log.debug("Plugin: {}, current version: {}, highest upgrade task found: {}.",
120 plugin.getKey(), dataBuildNumber, lastUpgradeTaskBuildNumber);
121 return lastUpgradeTaskBuildNumber > dataBuildNumber;
122 }
123
124
125
126
127
128 protected int getDataBuildNumber() {
129 final String val = (String) pluginSettings.get(plugin.getKey() + BUILD);
130 if (val != null) {
131 return Integer.parseInt(val);
132 } else {
133 return 0;
134 }
135 }
136
137 protected void setDataBuildNumber(final int buildNumber) {
138 pluginSettings.put(plugin.getKey() + BUILD, String.valueOf(buildNumber));
139 }
140
141 protected void postUpgrade() {
142 log.info("Plugin {} upgrade completed. Current version is: {}", plugin.getKey(), getDataBuildNumber());
143 }
144 }