View Javadoc
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   * Performs an upgrade of a plugin.  Originally copied from Confluence's AbstractUpgradeManager.
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                      // Current buildnumber of data is higher or equal to this upgrade task. No need to run
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              // stringify the stacktrace.
80              StringWriter sw = new StringWriter();
81              e.printStackTrace(new PrintWriter(sw));
82  
83              // use a message here intentionally instead of key. it won't get translated.
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      * This is the build number of the current version that the user is running under.
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 }