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 = new ArrayList<PluginUpgradeTask>();
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<Message>();
31  
32      private static final Comparator<PluginUpgradeTask> UPGRADE_TASK_COMPARATOR = new Comparator<PluginUpgradeTask>() {
33          public int compare(final PluginUpgradeTask t1, final PluginUpgradeTask t2) {
34              if (t1 == null) {
35                  return -1;
36              }
37              if (t2 == null) {
38                  return 1;
39              }
40              if (t1.getBuildNumber() > t2.getBuildNumber()) {
41                  return 1;
42              }
43  
44              return (t1.getBuildNumber() < t2.getBuildNumber()) ? -1 : 0;
45          }
46      };
47  
48      protected PluginUpgrader(final Plugin plugin, final PluginSettings pluginSettings, final List<PluginUpgradeTask> upgradeTasks) {
49          this.plugin = plugin;
50          this.pluginSettings = pluginSettings;
51          this.upgradeTasks = upgradeTasks;
52          Collections.sort(this.upgradeTasks, UPGRADE_TASK_COMPARATOR);
53      }
54  
55      protected List<Message> upgrade() {
56          if (needUpgrade()) {
57              doUpgrade();
58          }
59          return errors;
60      }
61  
62      protected void doUpgrade() {
63          try {
64              log.info("Upgrading plugin " + plugin.getKey());
65              for (final PluginUpgradeTask upgradeTask : upgradeTasks) {
66  
67                  if (upgradeTask.getBuildNumber() <= getDataBuildNumber()) {
68                      // Current buildnumber of data is higher or equal to this upgrade task. No need to run
69                      continue;
70                  }
71  
72                  final Collection<Message> messages = upgradeTask.doUpgrade();
73  
74                  if (messages == null || messages.isEmpty()) {
75                      upgradeTaskSucceeded(upgradeTask);
76                  } else {
77                      upgradeTaskFailed(upgradeTask, messages);
78                  }
79              }
80          } catch (final Throwable e) {
81              // stringify the stacktrace.
82              StringWriter sw = new StringWriter();
83              e.printStackTrace(new PrintWriter(sw));
84  
85              // use a message here intentionally instead of key. it won't get translated.
86              errors.add(new DefaultMessage("Unexpected exception caught during plugin upgrade: " + sw.toString()));
87              log.error("Upgrade failed: " + e.getMessage(), e);
88          } finally {
89              postUpgrade();
90          }
91      }
92  
93  
94      protected void upgradeTaskSucceeded(final PluginUpgradeTask upgradeTask) {
95          setDataBuildNumber(upgradeTask.getBuildNumber());
96          log.info("Upgraded plugin " + upgradeTask.getPluginKey() + " to version " + upgradeTask.getBuildNumber() + " - " + upgradeTask.getShortDescription());
97      }
98  
99      protected void upgradeTaskFailed(final PluginUpgradeTask upgradeTask, final Collection<Message> messages) {
100         errors.addAll(messages);
101         final StringBuilder msg = new StringBuilder();
102         msg.append("Plugin upgrade failed for ").append(upgradeTask.getPluginKey());
103         msg.append(" to version ").append(upgradeTask.getBuildNumber());
104         msg.append(" - ").append(upgradeTask.getShortDescription());
105         msg.append("\n");
106         for (final Message message : messages) {
107             msg.append("\t* ").append(message.getKey()).append(" ").append(Arrays.toString(message.getArguments()));
108         }
109 
110         log.warn(msg.toString());
111     }
112 
113     protected List<Message> getErrors() {
114         return errors;
115     }
116 
117     protected boolean needUpgrade() {
118         final PluginUpgradeTask lastUpgradeTask = this.upgradeTasks.get(this.upgradeTasks.size() - 1);
119         final int dataBuildNumber = getDataBuildNumber();
120         final int lastUpgradeTaskBuildNumber = lastUpgradeTask.getBuildNumber();
121         log.debug("Plugin: {}, current version: {}, highest upgrade task found: {}.", new Object[]{plugin.getKey(), dataBuildNumber, lastUpgradeTaskBuildNumber});
122         return lastUpgradeTaskBuildNumber > dataBuildNumber;
123     }
124 
125 
126     /**
127      * This is the build number of the current version that the user is running under.
128      */
129     protected int getDataBuildNumber() {
130         final String val = (String) pluginSettings.get(plugin.getKey() + BUILD);
131         if (val != null) {
132             return Integer.parseInt(val);
133         } else {
134             return 0;
135         }
136     }
137 
138     protected void setDataBuildNumber(final int buildNumber) {
139         pluginSettings.put(plugin.getKey() + BUILD, String.valueOf(buildNumber));
140     }
141 
142     protected void postUpgrade() {
143         log.info("Plugin " + plugin.getKey() + " upgrade completed. Current version is: " + getDataBuildNumber());
144     }
145 }