Browse Source

Rewrote the updater system from scratch

Change-Id: Ia92ef9058621aa0a3cb67dc1fb30a13642580303
Depends-On: Iab5db6ba7738102fbe45e37670f6cfd3c705fd9c
Reviewed-on: http://gerrit.dmdirc.com/2381
Automatic-Compile: DMDirc Build Manager
Reviewed-by: Greg Holmes <greg@dmdirc.com>
tags/0.6.7rc1
Chris Smith 12 years ago
parent
commit
b0445f8470
39 changed files with 2356 additions and 720 deletions
  1. 2
    2
      src/com/dmdirc/Main.java
  2. 0
    239
      src/com/dmdirc/updater/Update.java
  3. 21
    355
      src/com/dmdirc/updater/UpdateChecker.java
  4. 66
    0
      src/com/dmdirc/updater/checking/BaseCheckResult.java
  5. 59
    0
      src/com/dmdirc/updater/checking/BaseDownloadableResult.java
  6. 48
    0
      src/com/dmdirc/updater/checking/CheckResultConsolidator.java
  7. 193
    0
      src/com/dmdirc/updater/checking/DMDircCheckStrategy.java
  8. 10
    7
      src/com/dmdirc/updater/checking/DownloadableUpdate.java
  9. 27
    38
      src/com/dmdirc/updater/checking/NaiveConsolidator.java
  10. 67
    0
      src/com/dmdirc/updater/checking/UpdateCheckResult.java
  11. 47
    0
      src/com/dmdirc/updater/checking/UpdateCheckStrategy.java
  12. 1
    2
      src/com/dmdirc/updater/components/ActionGroupComponent.java
  13. 2
    15
      src/com/dmdirc/updater/components/LauncherComponent.java
  14. 2
    9
      src/com/dmdirc/updater/components/PluginComponent.java
  15. 62
    0
      src/com/dmdirc/updater/installing/LegacyInstallationStrategy.java
  16. 49
    0
      src/com/dmdirc/updater/installing/TypeSensitiveInstallationStrategy.java
  17. 55
    0
      src/com/dmdirc/updater/installing/UpdateInstallationListener.java
  18. 64
    0
      src/com/dmdirc/updater/installing/UpdateInstallationStrategy.java
  19. 63
    0
      src/com/dmdirc/updater/manager/CachingUpdateManager.java
  20. 142
    0
      src/com/dmdirc/updater/manager/CachingUpdateManagerImpl.java
  21. 47
    0
      src/com/dmdirc/updater/manager/ConfigComponentPolicy.java
  22. 108
    0
      src/com/dmdirc/updater/manager/DMDircUpdateManager.java
  23. 49
    0
      src/com/dmdirc/updater/manager/InstallationTask.java
  24. 65
    0
      src/com/dmdirc/updater/manager/RetrievalTask.java
  25. 43
    0
      src/com/dmdirc/updater/manager/UpdateComponentPolicy.java
  26. 142
    0
      src/com/dmdirc/updater/manager/UpdateManager.java
  27. 358
    0
      src/com/dmdirc/updater/manager/UpdateManagerImpl.java
  28. 9
    7
      src/com/dmdirc/updater/manager/UpdateManagerListener.java
  29. 44
    0
      src/com/dmdirc/updater/manager/UpdateManagerStatus.java
  30. 66
    0
      src/com/dmdirc/updater/manager/UpdateStatus.java
  31. 45
    0
      src/com/dmdirc/updater/manager/UpdateStatusListener.java
  32. 46
    0
      src/com/dmdirc/updater/retrieving/BaseRetrievalResult.java
  33. 24
    28
      src/com/dmdirc/updater/retrieving/BaseSingleFileResult.java
  34. 117
    0
      src/com/dmdirc/updater/retrieving/DownloadRetrievalStrategy.java
  35. 9
    7
      src/com/dmdirc/updater/retrieving/SingleFileRetrievalResult.java
  36. 67
    0
      src/com/dmdirc/updater/retrieving/TypeSensitiveRetrievalStrategy.java
  37. 55
    0
      src/com/dmdirc/updater/retrieving/UpdateRetrievalListener.java
  38. 12
    11
      src/com/dmdirc/updater/retrieving/UpdateRetrievalResult.java
  39. 70
    0
      src/com/dmdirc/updater/retrieving/UpdateRetrievalStategy.java

+ 2
- 2
src/com/dmdirc/Main.java View File

@@ -110,6 +110,8 @@ public final class Main {
110 110
             handleInvalidConfigFile();
111 111
         }
112 112
 
113
+        UpdateChecker.init();
114
+
113 115
         MessageSinkManager.getManager().loadDefaultSinks();
114 116
 
115 117
         final PluginManager pm = PluginManager.getPluginManager();
@@ -144,8 +146,6 @@ public final class Main {
144 146
         ActionManager.getActionManager().triggerEvent(
145 147
                 CoreActionType.CLIENT_OPENED, null);
146 148
 
147
-        UpdateChecker.init();
148
-
149 149
         clp.processArguments();
150 150
 
151 151
         GlobalWindow.init();

+ 0
- 239
src/com/dmdirc/updater/Update.java View File

@@ -1,239 +0,0 @@
1
-/*
2
- * Copyright (c) 2006-2012 DMDirc Developers
3
- *
4
- * Permission is hereby granted, free of charge, to any person obtaining a copy
5
- * of this software and associated documentation files (the "Software"), to deal
6
- * in the Software without restriction, including without limitation the rights
7
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
- * copies of the Software, and to permit persons to whom the Software is
9
- * furnished to do so, subject to the following conditions:
10
- *
11
- * The above copyright notice and this permission notice shall be included in
12
- * all copies or substantial portions of the Software.
13
- *
14
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
- * SOFTWARE.
21
- */
22
-
23
-package com.dmdirc.updater;
24
-
25
-import com.dmdirc.Main;
26
-import com.dmdirc.logger.ErrorLevel;
27
-import com.dmdirc.logger.Logger;
28
-import com.dmdirc.util.collections.WeakList;
29
-import com.dmdirc.util.io.DownloadListener;
30
-import com.dmdirc.util.io.Downloader;
31
-
32
-import java.io.IOException;
33
-import java.util.List;
34
-
35
-/**
36
- * Represents a single available update for some component.
37
- */
38
-public final class Update implements DownloadListener {
39
-
40
-    /** Update component. */
41
-    private final UpdateComponent component;
42
-    /** Remote version name. */
43
-    private final String versionName;
44
-    /** Update url. */
45
-    private final String url;
46
-    /** The progress of the current stage. */
47
-    private float progress;
48
-
49
-    /** A list of registered update listeners. */
50
-    private final List<UpdateListener> listeners
51
-            = new WeakList<UpdateListener>();
52
-
53
-    /** Our current status. */
54
-    private UpdateStatus status = UpdateStatus.PENDING;
55
-
56
-    /**
57
-     * Creates a new instance of Update, with details from the specified line.
58
-     *
59
-     * @param updateInfo An update information line from the update server
60
-     */
61
-    public Update(final String updateInfo) {
62
-        // outofdate client STABLE 20071007 0.5.1 file
63
-        final String[] parts = updateInfo.split(" ");
64
-
65
-        if (parts.length == 6) {
66
-            component = UpdateChecker.findComponent(parts[1]);
67
-            versionName = parts[4];
68
-            url = parts[5];
69
-        } else {
70
-            component = null;
71
-            versionName = null;
72
-            url = null;
73
-
74
-            Logger.appError(ErrorLevel.LOW,
75
-                    "Invalid update line received from server: ",
76
-                    new UnsupportedOperationException("line: " + updateInfo));
77
-        }
78
-    }
79
-
80
-    /**
81
-     * Retrieves a string representation of this update. The result from this
82
-     * method may be passed to the {@link Update} constructor to create a copy
83
-     * of the update.
84
-     *
85
-     * @since 0.6.5
86
-     * @return A string representation of this update
87
-     */
88
-    public String getStringRepresentation() {
89
-        return "outofdate " + component.getName()
90
-                + " CHANNEL versionNumber " + versionName + " " + url;
91
-    }
92
-
93
-    /**
94
-     * Retrieves the component that this update is for.
95
-     *
96
-     * @return The component of this update
97
-     */
98
-    public UpdateComponent getComponent() {
99
-        return component;
100
-    }
101
-
102
-    /**
103
-     * Returns the remote version of the component that's available.
104
-     *
105
-     * @return The remote version number
106
-     */
107
-    public String getRemoteVersion() {
108
-        return versionName;
109
-    }
110
-
111
-    /**
112
-     * Returns the URL where the new update may be downloaded.
113
-     *
114
-     * @return The URL of the update
115
-     */
116
-    public String getUrl() {
117
-        return url;
118
-    }
119
-
120
-    /**
121
-     * Retrieves the status of this update.
122
-     *
123
-     * @return This update's status
124
-     */
125
-    public UpdateStatus getStatus() {
126
-        return status;
127
-    }
128
-
129
-    /**
130
-     * Sets the status of this update, and notifies all listeners of the change.
131
-     *
132
-     * @param newStatus This update's new status
133
-     */
134
-    protected void setStatus(final UpdateStatus newStatus) {
135
-        status = newStatus;
136
-        progress = 0;
137
-
138
-        for (UpdateListener listener : listeners) {
139
-            listener.updateStatusChange(this, status);
140
-        }
141
-    }
142
-
143
-    /**
144
-     * Removes the specified update listener.
145
-     *
146
-     * @param o The update listener to remove
147
-     */
148
-    public void removeUpdateListener(final Object o) {
149
-        listeners.remove(o);
150
-    }
151
-
152
-    /**
153
-     * Adds the specified update listener.
154
-     *
155
-     * @param e The update listener to add
156
-     */
157
-    public void addUpdateListener(final UpdateListener e) {
158
-        listeners.add(e);
159
-    }
160
-
161
-    /**
162
-     * Makes this update download and install itself.
163
-     */
164
-    public void doUpdate() {
165
-        new Thread(new Runnable() {
166
-
167
-            /** {@inheritDoc} */
168
-            @Override
169
-            public void run() {
170
-                final String path = Main.getConfigDir() + "update.tmp."
171
-                        + Math.round(Math.random() * 1000);
172
-
173
-                setStatus(UpdateStatus.DOWNLOADING);
174
-
175
-                try {
176
-                    Downloader.downloadPage(getUrl(), path, Update.this);
177
-                } catch (IOException ex) {
178
-                    setStatus(UpdateStatus.ERROR);
179
-
180
-                    Logger.userError(ErrorLevel.MEDIUM,
181
-                            "Error when updating component "
182
-                            + component.getName(), ex);
183
-
184
-                    return;
185
-                }
186
-
187
-                setStatus(UpdateStatus.INSTALLING);
188
-
189
-                try {
190
-                    final boolean restart = getComponent().doInstall(path);
191
-
192
-                    if (restart) {
193
-                        setStatus(UpdateStatus.RESTART_NEEDED);
194
-                        UpdateChecker.removeComponent(getComponent().getName());
195
-                    } else {
196
-                        setStatus(UpdateStatus.INSTALLED);
197
-                    }
198
-                } catch (IOException ex) {
199
-                    setStatus(UpdateStatus.ERROR);
200
-                    Logger.userError(ErrorLevel.MEDIUM,
201
-                            "I/O error when updating component "
202
-                            + component.getName(), ex);
203
-                } catch (Exception ex) {
204
-                    setStatus(UpdateStatus.ERROR);
205
-                    Logger.appError(ErrorLevel.MEDIUM,
206
-                            "Error when updating component "
207
-                            + component.getName(), ex);
208
-                }
209
-            }
210
-
211
-        }, "Update thread").start();
212
-    }
213
-
214
-    /** {@inheritDoc} */
215
-    @Override
216
-    public void downloadProgress(final float percent) {
217
-        progress = percent;
218
-
219
-        for (UpdateListener listener : listeners) {
220
-            listener.updateProgressChange(this, percent);
221
-        }
222
-    }
223
-
224
-    /**
225
-     * Retrieves the current progress of the current state of this update.
226
-     *
227
-     * @return The percentage of the current stage that has been completed
228
-     */
229
-    public float getProgress() {
230
-        return progress;
231
-    }
232
-
233
-    /** {@inheritDoc} */
234
-    @Override
235
-    public void setIndeterminate(final boolean indeterminate) {
236
-        //TODO
237
-    }
238
-
239
-}

+ 21
- 355
src/com/dmdirc/updater/UpdateChecker.java View File

@@ -22,92 +22,40 @@
22 22
 
23 23
 package com.dmdirc.updater;
24 24
 
25
-import com.dmdirc.Precondition;
25
+import com.dmdirc.Main;
26 26
 import com.dmdirc.config.ConfigManager;
27 27
 import com.dmdirc.config.IdentityManager;
28 28
 import com.dmdirc.logger.ErrorLevel;
29 29
 import com.dmdirc.logger.Logger;
30
-import com.dmdirc.updater.components.ClientComponent;
31
-import com.dmdirc.updater.components.DefaultsComponent;
32
-import com.dmdirc.updater.components.ModeAliasesComponent;
33
-import com.dmdirc.util.collections.ListenerList;
34
-import com.dmdirc.util.io.Downloader;
30
+import com.dmdirc.updater.manager.CachingUpdateManager;
31
+import com.dmdirc.updater.manager.DMDircUpdateManager;
32
+import com.dmdirc.updater.manager.UpdateStatus;
35 33
 
36
-import java.io.IOException;
37
-import java.net.MalformedURLException;
38
-import java.util.ArrayList;
39 34
 import java.util.Date;
40
-import java.util.List;
41 35
 import java.util.Timer;
42 36
 import java.util.TimerTask;
43 37
 import java.util.concurrent.Semaphore;
44 38
 
39
+import lombok.Getter;
40
+
45 41
 /**
46 42
  * The update checker contacts the DMDirc website to check to see if there
47 43
  * are any updates available.
48 44
  */
49 45
 public final class UpdateChecker implements Runnable {
50 46
 
51
-    /** The possible states for the checker. */
52
-    public static enum STATE {
53
-        /** Nothing's happening. */
54
-        IDLE,
55
-        /** Currently checking for updates. */
56
-        CHECKING,
57
-        /** Currently updating. */
58
-        UPDATING,
59
-        /** New updates are available. */
60
-        UPDATES_AVAILABLE,
61
-        /** Updates installed but restart needed. */
62
-        RESTART_REQUIRED,
63
-    }
64
-
65 47
     /** The domain to use for updater settings. */
66 48
     private static final String DOMAIN = "updater";
67 49
 
68 50
     /** Semaphore used to prevent multiple invocations. */
69 51
     private static final Semaphore MUTEX = new Semaphore(1);
70 52
 
71
-    /** A list of components that we're to check. */
72
-    private static final List<UpdateComponent> COMPONENTS
73
-            = new ArrayList<UpdateComponent>();
74
-
75 53
     /** Our timer. */
76 54
     private static Timer timer = new Timer("Update Checker Timer");
77 55
 
78
-    /** The list of updates that are available. */
79
-    private static final List<Update> UPDATES = new ArrayList<Update>();
80
-
81
-    /** A list of our listeners. */
82
-    private static final ListenerList LISTENERS = new ListenerList();
83
-
84
-    /** Our current state. */
85
-    private static STATE status = STATE.IDLE;
86
-
87
-    /** A reference to the listener we use for update status changes. */
88
-    private static final UpdateListener LISTENER = new UpdateListener() {
89
-        @Override
90
-        public void updateStatusChange(final Update update, final UpdateStatus status) {
91
-            if (status == UpdateStatus.INSTALLED
92
-                || status == UpdateStatus.ERROR) {
93
-                removeUpdate(update);
94
-            } else if (status == UpdateStatus.RESTART_NEEDED && UpdateChecker.status
95
-                    == STATE.UPDATING) {
96
-                doNextUpdate();
97
-            }
98
-        }
99
-
100
-        @Override
101
-        public void updateProgressChange(final Update update, final float progress) {
102
-            // Don't care
103
-        }
104
-    };
105
-
106
-    static {
107
-        COMPONENTS.add(new ClientComponent());
108
-        COMPONENTS.add(new ModeAliasesComponent());
109
-        COMPONENTS.add(new DefaultsComponent());
110
-    }
56
+    /** The update manager to use. */
57
+    @Getter
58
+    private static CachingUpdateManager manager;
111 59
 
112 60
     /** {@inheritDoc} */
113 61
     @Override
@@ -120,8 +68,7 @@ public final class UpdateChecker implements Runnable {
120 68
 
121 69
         final ConfigManager config = IdentityManager.getIdentityManager().getGlobalConfiguration();
122 70
 
123
-        if (!config.getOptionBool(DOMAIN, "enable")
124
-                || status == STATE.UPDATING) {
71
+        if (!config.getOptionBool(DOMAIN, "enable")) {
125 72
             IdentityManager.getIdentityManager().getGlobalConfigIdentity().setOption(DOMAIN,
126 73
                     "lastcheck", String.valueOf((int) (new Date().getTime() / 1000)));
127 74
 
@@ -130,64 +77,7 @@ public final class UpdateChecker implements Runnable {
130 77
             return;
131 78
         }
132 79
 
133
-        setStatus(STATE.CHECKING);
134
-
135
-        // Remove any existing update that isn't waiting for a restart.
136
-        for (Update update : new ArrayList<Update>(UPDATES)) {
137
-            if (update.getStatus() != UpdateStatus.RESTART_NEEDED) {
138
-                UPDATES.remove(update);
139
-            }
140
-        }
141
-
142
-        final StringBuilder data = new StringBuilder();
143
-        final String updateChannel = config.getOption(DOMAIN, "channel");
144
-
145
-        // Build the data string to send to the server
146
-        for (UpdateComponent component : COMPONENTS) {
147
-            if (isEnabled(component)) {
148
-                data.append(component.getName());
149
-                data.append(',');
150
-                data.append(updateChannel);
151
-                data.append(',');
152
-                data.append(component.getVersion());
153
-                data.append(';');
154
-            }
155
-        }
156
-
157
-        // If we actually have components to check
158
-        if (data.length() > 0) {
159
-            try {
160
-                final List<String> response
161
-                    = Downloader.getPage("http://updates.dmdirc.com/", "data=" + data);
162
-
163
-                for (String line : response) {
164
-                    checkLine(line);
165
-                }
166
-            } catch (MalformedURLException ex) {
167
-                Logger.appError(ErrorLevel.LOW, "Error when checking for updates", ex);
168
-            } catch (IOException ex) {
169
-                Logger.userError(ErrorLevel.LOW,
170
-                        "I/O error when checking for updates: " + ex.getMessage());
171
-            }
172
-        }
173
-
174
-        if (UPDATES.isEmpty()) {
175
-            setStatus(STATE.IDLE);
176
-        } else {
177
-            boolean available = false;
178
-
179
-            // Check to see if the updates are outstanding or just waiting for
180
-            // a restart
181
-            for (Update update : UPDATES) {
182
-                if (update.getStatus() == UpdateStatus.PENDING) {
183
-                    available = true;
184
-                }
185
-            }
186
-
187
-            setStatus(available ? STATE.UPDATES_AVAILABLE : STATE.RESTART_REQUIRED);
188
-        }
189
-
190
-        updateCachedUpdates();
80
+        manager.checkForUpdates();
191 81
 
192 82
         MUTEX.release();
193 83
 
@@ -197,67 +87,17 @@ public final class UpdateChecker implements Runnable {
197 87
         UpdateChecker.initTimer();
198 88
 
199 89
         if (config.getOptionBool(DOMAIN, "autoupdate")) {
200
-            applyUpdates();
201
-        }
202
-    }
203
-
204
-    /**
205
-     * Updates the cache of pending updates in the configuration file.
206
-     *
207
-     * @since 0.6.5
208
-     */
209
-    private static void updateCachedUpdates() {
210
-       final List<String> stringCopies = new ArrayList<String>();
211
-
212
-        for (Update update : UPDATES) {
213
-            // Only include outstanding updates
214
-            if (update.getStatus() == UpdateStatus.PENDING
215
-                    || update.getStatus() == UpdateStatus.ERROR) {
216
-                stringCopies.add(update.getStringRepresentation());
90
+            for (UpdateComponent component : manager.getComponents()) {
91
+                if (manager.getStatus(component) == UpdateStatus.UPDATE_PENDING) {
92
+                    manager.install(component);
93
+                }
217 94
             }
218
-        }
219
-
220
-        IdentityManager.getIdentityManager().getGlobalConfigIdentity()
221
-                .setOption(DOMAIN, "updates", stringCopies);
222
-    }
223
-
224
-    /**
225
-     * Checks the specified line to determine the message from the update server.
226
-     *
227
-     * @param line The line to be checked
228
-     */
229
-    private static void checkLine(final String line) {
230
-        if (line.startsWith("outofdate")) {
231
-            doUpdateAvailable(line);
232
-        } else if (line.startsWith("error")) {
233
-            String errorMessage = "Error when checking for updates: " + line.substring(6);
234
-            final String[] bits = line.split(" ");
235
-            if (bits.length > 2) {
236
-                final UpdateComponent thisComponent = findComponent(bits[2]);
237
-
238
-                if (thisComponent instanceof FileComponent) {
239
-                    errorMessage = errorMessage + " (" + ((FileComponent) thisComponent)
240
-                            .getFileName() + ")";
95
+        } else if (config.getOptionBool(DOMAIN, "autodownload")) {
96
+            for (UpdateComponent component : manager.getComponents()) {
97
+                if (manager.getStatus(component) == UpdateStatus.UPDATE_PENDING) {
98
+                    manager.retrieve(component);
241 99
                 }
242 100
             }
243
-            Logger.userError(ErrorLevel.LOW, errorMessage);
244
-        } else if (!line.startsWith("uptodate")) {
245
-            Logger.userError(ErrorLevel.LOW, "Unknown update line received from server: "
246
-                    + line);
247
-        }
248
-    }
249
-
250
-    /**
251
-     * Informs the user that there's an update available.
252
-     *
253
-     * @param line The line that was received from the update server
254
-     */
255
-    private static void doUpdateAvailable(final String line) {
256
-        final Update update = new Update(line);
257
-
258
-        if (update.getUrl() != null) {
259
-            UPDATES.add(update);
260
-            update.addUpdateListener(LISTENER);
261 101
         }
262 102
     }
263 103
 
@@ -266,14 +106,8 @@ public final class UpdateChecker implements Runnable {
266 106
      * frequency specified in the config.
267 107
      */
268 108
     public static void init() {
269
-        for (String update : IdentityManager.getIdentityManager()
270
-                .getGlobalConfiguration().getOptionList(DOMAIN, "updates")) {
271
-            checkLine(update);
272
-        }
273
-
274
-        if (!UPDATES.isEmpty()) {
275
-            setStatus(STATE.UPDATES_AVAILABLE);
276
-        }
109
+        manager = new DMDircUpdateManager(IdentityManager.getIdentityManager()
110
+                .getGlobalConfiguration(), Main.getConfigDir(), 3);
277 111
 
278 112
         initTimer();
279 113
     }
@@ -320,172 +154,4 @@ public final class UpdateChecker implements Runnable {
320 154
         new Thread(new UpdateChecker(), "Update Checker thread").start();
321 155
     }
322 156
 
323
-    /**
324
-     * Registers an update component.
325
-     *
326
-     * @param component The component to be registered
327
-     */
328
-    public static void registerComponent(final UpdateComponent component) {
329
-        COMPONENTS.add(component);
330
-    }
331
-
332
-    /**
333
-     * Unregisters an update component with the specified name.
334
-     *
335
-     * @param name The name of the component to be removed
336
-     */
337
-    public static void removeComponent(final String name) {
338
-        UpdateComponent target = null;
339
-
340
-        for (UpdateComponent component : COMPONENTS) {
341
-            if (name.equals(component.getName())) {
342
-                target = component;
343
-            }
344
-        }
345
-
346
-        if (target != null) {
347
-            COMPONENTS.remove(target);
348
-        }
349
-    }
350
-
351
-    /**
352
-     * Finds and returns the component with the specified name.
353
-     *
354
-     * @param name The name of the component that we're looking for
355
-     * @return The corresponding UpdateComponent, or null if it's not found
356
-     */
357
-    @Precondition("The specified name is not null")
358
-    public static UpdateComponent findComponent(final String name) {
359
-        assert name != null;
360
-
361
-        for (UpdateComponent component : COMPONENTS) {
362
-            if (name.equals(component.getName())) {
363
-                return component;
364
-            }
365
-        }
366
-
367
-        return null;
368
-    }
369
-
370
-    /**
371
-     * Removes the specified update from the list. This should be called when
372
-     * the update has finished, has encountered an error, or the user does not
373
-     * want the update to be performed.
374
-     *
375
-     * @param update The update to be removed
376
-     */
377
-    public static void removeUpdate(final Update update) {
378
-        update.removeUpdateListener(LISTENER);
379
-        UPDATES.remove(update);
380
-
381
-        if (UPDATES.isEmpty()) {
382
-            setStatus(STATE.IDLE);
383
-            updateCachedUpdates();
384
-        } else if (status == STATE.UPDATING) {
385
-            doNextUpdate();
386
-        }
387
-    }
388
-
389
-    /**
390
-     * Downloads and installs all known updates.
391
-     */
392
-    public static void applyUpdates() {
393
-        if (!UPDATES.isEmpty()) {
394
-            setStatus(STATE.UPDATING);
395
-            doNextUpdate();
396
-        }
397
-    }
398
-
399
-    /**
400
-     * Finds and applies the next pending update, or sets the state to idle
401
-     * / restart needed if appropriate.
402
-     */
403
-    private static void doNextUpdate() {
404
-        boolean restart = false;
405
-
406
-        for (Update update : UPDATES) {
407
-            if (update.getStatus() == UpdateStatus.PENDING) {
408
-                update.doUpdate();
409
-                return;
410
-            } else if (update.getStatus() == UpdateStatus.RESTART_NEEDED) {
411
-                restart = true;
412
-            }
413
-        }
414
-
415
-        setStatus(restart ? STATE.RESTART_REQUIRED : STATE.IDLE);
416
-        updateCachedUpdates();
417
-    }
418
-
419
-    /**
420
-     * Retrieves a list of components registered with the checker.
421
-     *
422
-     * @return A list of registered components
423
-     */
424
-    public static List<UpdateComponent> getComponents() {
425
-        return COMPONENTS;
426
-    }
427
-
428
-    /**
429
-     * Retrives a list of available updates from the checker.
430
-     *
431
-     * @return A list of available updates
432
-     */
433
-    public static List<Update> getAvailableUpdates() {
434
-        return UPDATES;
435
-    }
436
-
437
-
438
-    /**
439
-     * Adds a new status listener to the update checker.
440
-     *
441
-     * @param listener The listener to be added
442
-     */
443
-    public static void addListener(final UpdateCheckerListener listener) {
444
-        LISTENERS.add(UpdateCheckerListener.class, listener);
445
-    }
446
-
447
-    /**
448
-     * Removes a status listener from the update checker.
449
-     *
450
-     * @param listener The listener to be removed
451
-     */
452
-    public static void removeListener(final UpdateCheckerListener listener) {
453
-        LISTENERS.remove(UpdateCheckerListener.class, listener);
454
-    }
455
-
456
-    /**
457
-     * Retrieves the current status of the update checker.
458
-     *
459
-     * @return The update checker's current status
460
-     */
461
-    public static STATE getStatus() {
462
-        return status;
463
-    }
464
-
465
-    /**
466
-     * Sets the status of the update checker to the specified new status.
467
-     *
468
-     * @param newStatus The new status of this checker
469
-     */
470
-    private static void setStatus(final STATE newStatus) {
471
-        status = newStatus;
472
-
473
-        for (UpdateCheckerListener myListener : LISTENERS.get(UpdateCheckerListener.class)) {
474
-            myListener.statusChanged(newStatus);
475
-        }
476
-    }
477
-
478
-    /**
479
-     * Checks is a specified component is enabled.
480
-     *
481
-     * @param component Update component to check state
482
-     * @return true iif the update component is enabled
483
-     */
484
-    public static boolean isEnabled(final UpdateComponent component) {
485
-        return !IdentityManager.getIdentityManager().getGlobalConfiguration()
486
-                .hasOptionBool(DOMAIN, "enable-" + component.getName())
487
-                || IdentityManager.getIdentityManager().getGlobalConfiguration()
488
-                .getOptionBool(DOMAIN, "enable-" + component.getName());
489
-    }
490
-
491 157
 }

+ 66
- 0
src/com/dmdirc/updater/checking/BaseCheckResult.java View File

@@ -0,0 +1,66 @@
1
+/*
2
+ * Copyright (c) 2006-2012 DMDirc Developers
3
+ *
4
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ * of this software and associated documentation files (the "Software"), to deal
6
+ * in the Software without restriction, including without limitation the rights
7
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ * copies of the Software, and to permit persons to whom the Software is
9
+ * furnished to do so, subject to the following conditions:
10
+ *
11
+ * The above copyright notice and this permission notice shall be included in
12
+ * all copies or substantial portions of the Software.
13
+ *
14
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ * SOFTWARE.
21
+ */
22
+
23
+package com.dmdirc.updater.checking;
24
+
25
+import com.dmdirc.updater.UpdateComponent;
26
+import com.dmdirc.updater.Version;
27
+
28
+import lombok.AllArgsConstructor;
29
+import lombok.Getter;
30
+import lombok.ToString;
31
+
32
+/**
33
+ * Simple implementation of {@link UpdateCheckResult}.
34
+ */
35
+@Getter
36
+@ToString
37
+@AllArgsConstructor
38
+@SuppressWarnings("PMD.UnusedPrivateField")
39
+public class BaseCheckResult implements UpdateCheckResult {
40
+
41
+    /** The component this result is for. */
42
+    private final UpdateComponent component;
43
+
44
+    /** Whether or not an update is available. */
45
+    private final boolean updateAvailable;
46
+
47
+    /** A user-friendly name of the updated version, if any. */
48
+    private final String updatedVersionName;
49
+
50
+    /** The version available to update to, if any. */
51
+    private final Version updatedVersion;
52
+
53
+    /**
54
+     * Creates a new {@link BaseCheckResult} which reports no update is
55
+     * available.
56
+     *
57
+     * @param component The component this result is for
58
+     */
59
+    public BaseCheckResult(final UpdateComponent component) {
60
+        this.component = component;
61
+        this.updateAvailable = false;
62
+        this.updatedVersion = null;
63
+        this.updatedVersionName = null;
64
+    }
65
+
66
+}

+ 59
- 0
src/com/dmdirc/updater/checking/BaseDownloadableResult.java View File

@@ -0,0 +1,59 @@
1
+/*
2
+ * Copyright (c) 2006-2012 DMDirc Developers
3
+ *
4
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ * of this software and associated documentation files (the "Software"), to deal
6
+ * in the Software without restriction, including without limitation the rights
7
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ * copies of the Software, and to permit persons to whom the Software is
9
+ * furnished to do so, subject to the following conditions:
10
+ *
11
+ * The above copyright notice and this permission notice shall be included in
12
+ * all copies or substantial portions of the Software.
13
+ *
14
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ * SOFTWARE.
21
+ */
22
+
23
+package com.dmdirc.updater.checking;
24
+
25
+import com.dmdirc.updater.UpdateComponent;
26
+import com.dmdirc.updater.Version;
27
+
28
+import java.net.URL;
29
+
30
+import lombok.Getter;
31
+import lombok.ToString;
32
+
33
+/**
34
+ * A simple implementation of a {@link DownloadableUpdate}.
35
+ */
36
+@ToString(callSuper=true)
37
+@SuppressWarnings("PMD.UnusedPrivateField")
38
+public class BaseDownloadableResult extends BaseCheckResult implements DownloadableUpdate {
39
+
40
+    /** The URL the update may be downloaded from. */
41
+    @Getter
42
+    private final URL url;
43
+
44
+    /**
45
+     * Creates a new instance of {@link BaseDownloadableResult}.
46
+     *
47
+     * @param component The component that this result is for
48
+     * @param url The URL the update may be downloaded from
49
+     * @param updatedVersionName The friendly name of the updated version
50
+     * @param updatedVersion The version of the file available at that URL
51
+     */
52
+    public BaseDownloadableResult(final UpdateComponent component, final URL url,
53
+            final String updatedVersionName, final Version updatedVersion) {
54
+        super(component, true, updatedVersionName, updatedVersion);
55
+
56
+        this.url = url;
57
+    }
58
+
59
+}

+ 48
- 0
src/com/dmdirc/updater/checking/CheckResultConsolidator.java View File

@@ -0,0 +1,48 @@
1
+/*
2
+ * Copyright (c) 2006-2012 DMDirc Developers
3
+ *
4
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ * of this software and associated documentation files (the "Software"), to deal
6
+ * in the Software without restriction, including without limitation the rights
7
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ * copies of the Software, and to permit persons to whom the Software is
9
+ * furnished to do so, subject to the following conditions:
10
+ *
11
+ * The above copyright notice and this permission notice shall be included in
12
+ * all copies or substantial portions of the Software.
13
+ *
14
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ * SOFTWARE.
21
+ */
22
+
23
+package com.dmdirc.updater.checking;
24
+
25
+import com.dmdirc.updater.UpdateComponent;
26
+
27
+import java.util.Collection;
28
+import java.util.Map;
29
+
30
+/**
31
+ *A {@link CheckResultConsolidator} defines a strategy for consolidating
32
+ * multiple sets of {@link UpdateCheckResult}s provided by different
33
+ * {@link UpdateCheckStrategy}s.
34
+ */
35
+public interface CheckResultConsolidator {
36
+
37
+    /**
38
+     * Consolidates the collection of results into a single mapping. The
39
+     * resulting map should contain one entry for each distinct component
40
+     * contained in any of the providing result sets, mapped to the best
41
+     * {@link UpdateCheckResult} according to this strategy.
42
+     *
43
+     * @param results The results to be consolidated
44
+     * @return A map of {@link UpdateComponent}s to their consolidated result
45
+     */
46
+    Map<UpdateComponent, UpdateCheckResult> consolidate(
47
+            Collection<Map<UpdateComponent, UpdateCheckResult>> results);
48
+}

+ 193
- 0
src/com/dmdirc/updater/checking/DMDircCheckStrategy.java View File

@@ -0,0 +1,193 @@
1
+/*
2
+ * Copyright (c) 2006-2012 DMDirc Developers
3
+ *
4
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ * of this software and associated documentation files (the "Software"), to deal
6
+ * in the Software without restriction, including without limitation the rights
7
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ * copies of the Software, and to permit persons to whom the Software is
9
+ * furnished to do so, subject to the following conditions:
10
+ *
11
+ * The above copyright notice and this permission notice shall be included in
12
+ * all copies or substantial portions of the Software.
13
+ *
14
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ * SOFTWARE.
21
+ */
22
+
23
+package com.dmdirc.updater.checking;
24
+
25
+import com.dmdirc.updater.UpdateChannel;
26
+import com.dmdirc.updater.UpdateComponent;
27
+import com.dmdirc.updater.Version;
28
+import com.dmdirc.util.io.Downloader;
29
+
30
+import java.io.IOException;
31
+import java.net.MalformedURLException;
32
+import java.net.URL;
33
+import java.util.Collection;
34
+import java.util.HashMap;
35
+import java.util.List;
36
+import java.util.Map;
37
+
38
+import lombok.AllArgsConstructor;
39
+import lombok.Setter;
40
+import lombok.extern.slf4j.Slf4j;
41
+
42
+/**
43
+ * A strategy which sends a request to the DMDirc update service for
44
+ * information.
45
+ */
46
+@Slf4j
47
+@AllArgsConstructor
48
+public class DMDircCheckStrategy implements UpdateCheckStrategy {
49
+
50
+    /** The URL to request to check for updates. */
51
+    private static final String UPDATE_URL = "http://updates.dmdirc.com/";
52
+
53
+    /** The update channel to check for updates on. */
54
+    @Setter
55
+    private UpdateChannel channel;
56
+
57
+    /** {@inheritDoc} */
58
+    @Override
59
+    public Map<UpdateComponent, UpdateCheckResult> checkForUpdates(final Collection<UpdateComponent> components) {
60
+        final Map<UpdateComponent, UpdateCheckResult> res
61
+                = new HashMap<UpdateComponent, UpdateCheckResult>();
62
+        final Map<String, UpdateComponent> names = getComponentsByName(components);
63
+
64
+        try {
65
+            final List<String> response = Downloader.getPage(UPDATE_URL, getPayload(components));
66
+            log.trace("Response from update server: {}", response);
67
+
68
+            for (String line : response) {
69
+                final UpdateComponent component = names.get(getComponent(line));
70
+
71
+                if (component == null) {
72
+                    log.warn("Unable to extract component from line: {}", line);
73
+                    continue;
74
+                }
75
+
76
+                final UpdateCheckResult result = parseResponse(component, line);
77
+
78
+                if (result != null) {
79
+                    res.put(component, result);
80
+                }
81
+            }
82
+        } catch (IOException ex) {
83
+            log.warn("I/O exception when checking for updates", ex);
84
+        }
85
+
86
+        return res;
87
+    }
88
+
89
+    /**
90
+     * Builds the data payload which will be sent to the update server.
91
+     * Specifically, iterates over each component and appends their name,
92
+     * the channel name, and the component's version number.
93
+     *
94
+     * @param components The components to be added to the payload
95
+     * @return A string which can be posted to the DMDirc update server
96
+     */
97
+    private String getPayload(final Collection<UpdateComponent> components) {
98
+        final StringBuilder data = new StringBuilder("data=");
99
+
100
+        for (UpdateComponent component : components) {
101
+            log.trace("Adding payload info for component {} (version {})",
102
+                    component.getName(), component.getVersion());
103
+
104
+            data.append(component.getName());
105
+            data.append(',');
106
+            data.append(channel.name());
107
+            data.append(',');
108
+            data.append(component.getVersion());
109
+            data.append(';');
110
+        }
111
+
112
+        log.debug("Constructed update payload: {}", data);
113
+
114
+        return data.toString();
115
+    }
116
+
117
+    /**
118
+     * Extracts the name of the component a given response line contains.
119
+     *
120
+     * @param line The line to be parsed
121
+     * @return The name of the component extracted from the given line
122
+     */
123
+    private String getComponent(final String line) {
124
+        final String[] parts = line.split(" ");
125
+
126
+        if ("outofdate".equals(parts[0])) {
127
+            return parts[1];
128
+        }
129
+
130
+        return parts[2];
131
+    }
132
+
133
+    /**
134
+     * Checks the specified line to determine the message from the update server.
135
+     *
136
+     * @param component The component the line refers to
137
+     * @param line The line to be checked
138
+     */
139
+    private UpdateCheckResult parseResponse(final UpdateComponent component,
140
+            final String line) {
141
+        final String[] parts = line.split(" ");
142
+
143
+        if ("outofdate".equals(parts[0])) {
144
+            return parseOutOfDateResponse(component, parts);
145
+        } else if ("uptodate".equals(parts[0])) {
146
+            return new BaseCheckResult(component);
147
+        } else if ("error".equals(parts[0])) {
148
+            log.warn("Error received from update server: {}", line);
149
+        } else {
150
+            log.error("Unknown update line received from server: {}", line);
151
+        }
152
+
153
+        return null;
154
+    }
155
+
156
+    /**
157
+     * Parses an "outofdate" response from the server. Extracts the URL,
158
+     * remote version and remote friendly version into a
159
+     * {@link BaseDownloadableResult}.
160
+     *
161
+     * @param parts The tokenised parts of the response line
162
+     * @return A corresponding {@link UpdateCheckResult} or null on failure
163
+     */
164
+    private UpdateCheckResult parseOutOfDateResponse(
165
+            final UpdateComponent component, final String[] parts) {
166
+        try {
167
+            return new BaseDownloadableResult(component, new URL(parts[5]),
168
+                    parts[4], new Version(parts[3]));
169
+        } catch (MalformedURLException ex) {
170
+            log.error("Unable to construct URL for update. Parts: {}", parts, ex);
171
+            return null;
172
+        }
173
+    }
174
+
175
+    /**
176
+     * Builds a mapping of components' names to their actual component
177
+     * objects.
178
+     *
179
+     * @param components A collection of components to be mapped
180
+     * @return A corresponding Map containing a single entry for each component,
181
+     * which the component's name as a key and the component itself as a value.
182
+     */
183
+    private Map<String, UpdateComponent> getComponentsByName(final Collection<UpdateComponent> components) {
184
+        final Map<String, UpdateComponent> res = new HashMap<String, UpdateComponent>();
185
+
186
+        for (UpdateComponent component : components) {
187
+            res.put(component.getName(), component);
188
+        }
189
+
190
+        return res;
191
+    }
192
+
193
+}

src/com/dmdirc/updater/OptionsComponent.java → src/com/dmdirc/updater/checking/DownloadableUpdate.java View File

@@ -20,18 +20,21 @@
20 20
  * SOFTWARE.
21 21
  */
22 22
 
23
-package com.dmdirc.updater;
23
+package com.dmdirc.updater.checking;
24
+
25
+import java.net.URL;
24 26
 
25 27
 /**
26
- * This interface is implemented by components that provide options for
27
- * upgrades.
28
+ * A specialised {@link UpdateCheckResult} that specifies where the update
29
+ * may be downloaded from.
28 30
  */
29
-public interface OptionsComponent {
31
+public interface DownloadableUpdate extends UpdateCheckResult {
30 32
 
31 33
     /**
32
-     * Retrieves the options for this component.
34
+     * Gets the URL that the update my be downloaded from.
33 35
      *
34
-     * @return Array containing the options for this component
36
+     * @return The update's download URL
35 37
      */
36
-    String[] getOptions();
38
+    URL getUrl();
39
+
37 40
 }

test/com/dmdirc/updater/UpdateTest.java → src/com/dmdirc/updater/checking/NaiveConsolidator.java View File

@@ -20,47 +20,36 @@
20 20
  * SOFTWARE.
21 21
  */
22 22
 
23
-package com.dmdirc.updater;
23
+package com.dmdirc.updater.checking;
24 24
 
25
-import com.dmdirc.config.IdentityManager;
26
-import com.dmdirc.updater.components.ClientComponent;
25
+import com.dmdirc.updater.UpdateComponent;
27 26
 
28
-import org.junit.Before;
29
-import org.junit.BeforeClass;
30
-import org.junit.Test;
27
+import java.util.Collection;
28
+import java.util.HashMap;
29
+import java.util.Map;
31 30
 
32
-import static org.junit.Assert.*;
33
-
34
-public class UpdateTest {
35
-
36
-    @BeforeClass
37
-    public static void setUpClass() throws Exception {
38
-        IdentityManager.getIdentityManager().initialise();
39
-    }
40
-
41
-    private static final String SUBJECT = "outofdate client channel date version url";
42
-
43
-    private Update update;
44
-
45
-    @Before
46
-    public void setUp() throws Exception {
47
-        UpdateChecker.init();
48
-        update = new Update(SUBJECT);
49
-    }
50
-
51
-    @Test
52
-    public void testGetComponent() {
53
-        assertTrue(update.getComponent() instanceof ClientComponent);
54
-    }
55
-
56
-    @Test
57
-    public void testGetRemoteVersion() {
58
-        assertEquals("version", update.getRemoteVersion());
59
-    }
60
-
61
-    @Test
62
-    public void testGetUrl() {
63
-        assertEquals("url", update.getUrl());
31
+/**
32
+ * A simple {@link CheckResultConsolidator} that returns the first check result
33
+ * for each component that has an update available.
34
+ */
35
+public class NaiveConsolidator implements CheckResultConsolidator {
36
+
37
+    /** {@inheritDoc} */
38
+    @Override
39
+    public Map<UpdateComponent, UpdateCheckResult> consolidate(
40
+            final Collection<Map<UpdateComponent, UpdateCheckResult>> results) {
41
+        final Map<UpdateComponent, UpdateCheckResult> res
42
+                = new HashMap<UpdateComponent, UpdateCheckResult>();
43
+
44
+        for (Map<UpdateComponent, UpdateCheckResult> set : results) {
45
+            for (Map.Entry<UpdateComponent, UpdateCheckResult> entry : set.entrySet()) {
46
+                if (!res.containsKey(entry.getKey()) || !res.get(entry.getKey()).isUpdateAvailable()) {
47
+                    res.put(entry.getKey(), entry.getValue());
48
+                }
49
+            }
50
+        }
51
+
52
+        return res;
64 53
     }
65 54
 
66 55
 }

+ 67
- 0
src/com/dmdirc/updater/checking/UpdateCheckResult.java View File

@@ -0,0 +1,67 @@
1
+/*
2
+ * Copyright (c) 2006-2012 DMDirc Developers
3
+ *
4
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ * of this software and associated documentation files (the "Software"), to deal
6
+ * in the Software without restriction, including without limitation the rights
7
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ * copies of the Software, and to permit persons to whom the Software is
9
+ * furnished to do so, subject to the following conditions:
10
+ *
11
+ * The above copyright notice and this permission notice shall be included in
12
+ * all copies or substantial portions of the Software.
13
+ *
14
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ * SOFTWARE.
21
+ */
22
+
23
+package com.dmdirc.updater.checking;
24
+
25
+import com.dmdirc.updater.UpdateComponent;
26
+import com.dmdirc.updater.Version;
27
+
28
+/**
29
+ * Defines the result of an update check. Specifically, provides whether or
30
+ * not the component in question is updatable, and to what version, and any
31
+ * additional information needed in order to effect an update.
32
+ */
33
+public interface UpdateCheckResult {
34
+
35
+    /**
36
+     * Retrieves the component that this result describes.
37
+     *
38
+     * @return The {@link UpdateComponent} that this result is for
39
+     */
40
+    UpdateComponent getComponent();
41
+
42
+    /**
43
+     * Indicates whether an update is available.
44
+     *
45
+     * @return True if an update was available, false otherwise
46
+     */
47
+    boolean isUpdateAvailable();
48
+
49
+    /**
50
+     * Returns the version that the component is updatable to. The behaviour
51
+     * of this method is not defined if {@link #isUpdateAvailable()} returns
52
+     * false.
53
+     *
54
+     * @return The version available for updating
55
+     */
56
+    Version getUpdatedVersion();
57
+
58
+    /**
59
+     * Returns a user-friendly version name of the version described by
60
+     * {@link #getUpdatedVersion()}. The behaviour of this method is not
61
+     * defined if {@link #isUpdateAvailable()} returns false.
62
+     *
63
+     * @return A user-friendly version name
64
+     */
65
+    String getUpdatedVersionName();
66
+
67
+}

+ 47
- 0
src/com/dmdirc/updater/checking/UpdateCheckStrategy.java View File

@@ -0,0 +1,47 @@
1
+/*
2
+ * Copyright (c) 2006-2012 DMDirc Developers
3
+ *
4
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ * of this software and associated documentation files (the "Software"), to deal
6
+ * in the Software without restriction, including without limitation the rights
7
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ * copies of the Software, and to permit persons to whom the Software is
9
+ * furnished to do so, subject to the following conditions:
10
+ *
11
+ * The above copyright notice and this permission notice shall be included in
12
+ * all copies or substantial portions of the Software.
13
+ *
14
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ * SOFTWARE.
21
+ */
22
+
23
+package com.dmdirc.updater.checking;
24
+
25
+import com.dmdirc.updater.UpdateComponent;
26
+
27
+import java.util.Collection;
28
+import java.util.Map;
29
+
30
+/**
31
+ * Defines a strategy for checking for updates for one or more
32
+ * {@link UpdateComponent}s.
33
+ */
34
+public interface UpdateCheckStrategy {
35
+
36
+    /**
37
+     * Checks the given collection of components to determine whether or
38
+     * not any updates are available.
39
+     *
40
+     * @param components The components to be checked for updates
41
+     * @return A map of each of the provided component to an
42
+     * {@link UpdateCheckResult} indicating the status of the component and
43
+     * any additional information concerning how it should be updated.
44
+     */
45
+    Map<UpdateComponent, UpdateCheckResult> checkForUpdates(Collection<UpdateComponent> components);
46
+
47
+}

+ 1
- 2
src/com/dmdirc/updater/components/ActionGroupComponent.java View File

@@ -47,8 +47,7 @@ public class ActionGroupComponent implements UpdateComponent {
47 47
         this.group = group;
48 48
 
49 49
         if (group.getComponent() != -1 && group.getVersion() != null) {
50
-            UpdateChecker.removeComponent(getName());
51
-            UpdateChecker.registerComponent(this);
50
+            UpdateChecker.getManager().addComponent(this);
52 51
         }
53 52
     }
54 53
 

+ 2
- 15
src/com/dmdirc/updater/components/LauncherComponent.java View File

@@ -22,7 +22,6 @@
22 22
 
23 23
 package com.dmdirc.updater.components;
24 24
 
25
-import com.dmdirc.updater.OptionsComponent;
26 25
 import com.dmdirc.updater.UpdateChecker;
27 26
 import com.dmdirc.updater.UpdateComponent;
28 27
 import com.dmdirc.updater.Version;
@@ -30,12 +29,11 @@ import com.dmdirc.util.resourcemanager.ZipResourceManager;
30 29
 
31 30
 import java.io.File;
32 31
 import java.io.IOException;
33
-import java.util.Arrays;
34 32
 
35 33
 /**
36 34
  * Component for updates of DMDirc's launcher.
37 35
  */
38
-public class LauncherComponent implements UpdateComponent, OptionsComponent {
36
+public class LauncherComponent implements UpdateComponent {
39 37
 
40 38
     /** The platform of our current launcher. */
41 39
     private static String platform = "";
@@ -43,9 +41,6 @@ public class LauncherComponent implements UpdateComponent, OptionsComponent {
43 41
     /** The version of our current launcher. */
44 42
     private static Version version = new Version();
45 43
 
46
-    /** The options of our current launcher. */
47
-    private static String[] options = new String[]{};
48
-
49 44
     /**
50 45
      * Parses the specified launcher information.
51 46
      *
@@ -64,10 +59,9 @@ public class LauncherComponent implements UpdateComponent, OptionsComponent {
64 59
             version = new Version(info.substring(hpos + 1));
65 60
         } else {
66 61
             version = new Version(info.substring(hpos + 1, cpos));
67
-            options = info.substring(cpos + 1).split(",");
68 62
         }
69 63
 
70
-        UpdateChecker.registerComponent(new LauncherComponent());
64
+        UpdateChecker.getManager().addComponent(new LauncherComponent());
71 65
     }
72 66
 
73 67
     /**
@@ -103,13 +97,6 @@ public class LauncherComponent implements UpdateComponent, OptionsComponent {
103 97
         return version;
104 98
     }
105 99
 
106
-    /** {@inheritDoc} */
107
-    @Override
108
-    public String[] getOptions() {
109
-        return Arrays.copyOf(options, options.length);
110
-    }
111
-
112
-
113 100
     /** {@inheritDoc} */
114 101
     @Override
115 102
     public boolean requiresRestart() {

+ 2
- 9
src/com/dmdirc/updater/components/PluginComponent.java View File

@@ -25,7 +25,6 @@ package com.dmdirc.updater.components;
25 25
 import com.dmdirc.config.ConfigManager;
26 26
 import com.dmdirc.config.IdentityManager;
27 27
 import com.dmdirc.plugins.PluginInfo;
28
-import com.dmdirc.updater.FileComponent;
29 28
 import com.dmdirc.updater.UpdateChecker;
30 29
 import com.dmdirc.updater.UpdateComponent;
31 30
 import com.dmdirc.updater.Version;
@@ -35,7 +34,7 @@ import java.io.File;
35 34
 /**
36 35
  * An update component for plugins.
37 36
  */
38
-public class PluginComponent implements UpdateComponent, FileComponent {
37
+public class PluginComponent implements UpdateComponent {
39 38
 
40 39
     /** The config to use. */
41 40
     private static final ConfigManager CONFIG = IdentityManager.getIdentityManager().getGlobalConfiguration();
@@ -54,8 +53,7 @@ public class PluginComponent implements UpdateComponent, FileComponent {
54 53
 
55 54
         if ((plugin.getMetaData().getUpdaterId() > 0 && plugin.getMetaData().getVersion().isValid())
56 55
                 || (CONFIG.hasOptionInt("plugin-addonid", plugin.getMetaData().getName()))) {
57
-            UpdateChecker.removeComponent(getName());
58
-            UpdateChecker.registerComponent(this);
56
+            UpdateChecker.getManager().addComponent(this);
59 57
         }
60 58
     }
61 59
 
@@ -138,9 +136,4 @@ public class PluginComponent implements UpdateComponent, FileComponent {
138 136
         return returnCode;
139 137
     }
140 138
 
141
-    @Override
142
-    public String getFileName() {
143
-        return plugin.getFilename();
144
-    }
145
-
146 139
 }

+ 62
- 0
src/com/dmdirc/updater/installing/LegacyInstallationStrategy.java View File

@@ -0,0 +1,62 @@
1
+/*
2
+ * Copyright (c) 2006-2012 DMDirc Developers
3
+ *
4
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ * of this software and associated documentation files (the "Software"), to deal
6
+ * in the Software without restriction, including without limitation the rights
7
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ * copies of the Software, and to permit persons to whom the Software is
9
+ * furnished to do so, subject to the following conditions:
10
+ *
11
+ * The above copyright notice and this permission notice shall be included in
12
+ * all copies or substantial portions of the Software.
13
+ *
14
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ * SOFTWARE.
21
+ */
22
+
23
+package com.dmdirc.updater.installing;
24
+
25
+import com.dmdirc.updater.UpdateComponent;
26
+import com.dmdirc.updater.retrieving.SingleFileRetrievalResult;
27
+
28
+import lombok.ListenerSupport;
29
+import lombok.extern.slf4j.Slf4j;
30
+
31
+/**
32
+ * An {@link UpdateInstallationStrategy} which uses the old
33
+ * {@link UpdateComponent#doInstall(java.lang.String)} methods to perform
34
+ * installation.
35
+ */
36
+@Slf4j
37
+@ListenerSupport(UpdateInstallationListener.class)
38
+public class LegacyInstallationStrategy extends TypeSensitiveInstallationStrategy<UpdateComponent, SingleFileRetrievalResult> {
39
+
40
+    /**
41
+     * Creates a new {@link LegacyInstallationStrategy}.
42
+     */
43
+    public LegacyInstallationStrategy() {
44
+        super(UpdateComponent.class, SingleFileRetrievalResult.class);
45
+    }
46
+
47
+    /** {@inheritDoc} */
48
+    @Override
49
+    protected void installImpl(final UpdateComponent component, final SingleFileRetrievalResult retrievalResult) {
50
+        log.info("Installing file from {} for component {} using legacy strategy",
51
+                retrievalResult.getFile(), component.getName());
52
+
53
+        try {
54
+            component.doInstall(retrievalResult.getFile().getAbsolutePath());
55
+            fireInstallCompleted(component);
56
+        } catch (Exception ex) {
57
+            log.warn("Error installing update for {}", component.getName(), ex);
58
+            fireInstallFailed(component);
59
+        }
60
+    }
61
+
62
+}

+ 49
- 0
src/com/dmdirc/updater/installing/TypeSensitiveInstallationStrategy.java View File

@@ -0,0 +1,49 @@
1
+
2
+package com.dmdirc.updater.installing;
3
+
4
+import com.dmdirc.updater.UpdateComponent;
5
+import com.dmdirc.updater.retrieving.UpdateRetrievalResult;
6
+
7
+import lombok.AllArgsConstructor;
8
+
9
+/**
10
+ * Base class for {@link UpdateInstallationStrategy} implementations that
11
+ * can only handle a specific sublcass of {@link UpdateComponent} or
12
+ * {@link UpdateRetrievalResult}.
13
+ *
14
+ * @param <S> The type of {@link UpdateComponent} that can be handled
15
+ * @param <T> The type of {@link UpdateRetrievalResult} that can be handled
16
+ */
17
+@AllArgsConstructor
18
+public abstract class TypeSensitiveInstallationStrategy<S extends UpdateComponent,T extends UpdateRetrievalResult>
19
+        implements UpdateInstallationStrategy {
20
+
21
+    /** The type of {@link UpdateComponent} that can be handled. */
22
+    private final Class<S> updateComponentClass;
23
+
24
+    /** The type of {@link UpdateRetrievalResult} that can be handled. */
25
+    private final Class<T> updateRetrievalClass;
26
+
27
+    /** {@inheritDoc} */
28
+    @Override
29
+    public boolean canHandle(final UpdateRetrievalResult retrievalResult) {
30
+        return updateComponentClass.isAssignableFrom(retrievalResult.getCheckResult().getComponent().getClass())
31
+                && updateRetrievalClass.isAssignableFrom(retrievalResult.getClass());
32
+    }
33
+
34
+    /** {@inheritDoc} */
35
+    @Override
36
+    @SuppressWarnings("unchecked")
37
+    public void install(final UpdateRetrievalResult retrievalResult) {
38
+        installImpl((S) retrievalResult.getCheckResult().getComponent(), (T) retrievalResult);
39
+    }
40
+
41
+    /**
42
+     * Installs the retrieved update for the given component.
43
+     *
44
+     * @param component The component to be updated
45
+     * @param retrievalResult The result of the retrieval operation
46
+     */
47
+    protected abstract void installImpl(S component, T retrievalResult);
48
+
49
+}

+ 55
- 0
src/com/dmdirc/updater/installing/UpdateInstallationListener.java View File

@@ -0,0 +1,55 @@
1
+/*
2
+ * Copyright (c) 2006-2012 DMDirc Developers
3
+ *
4
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ * of this software and associated documentation files (the "Software"), to deal
6
+ * in the Software without restriction, including without limitation the rights
7
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ * copies of the Software, and to permit persons to whom the Software is
9
+ * furnished to do so, subject to the following conditions:
10
+ *
11
+ * The above copyright notice and this permission notice shall be included in
12
+ * all copies or substantial portions of the Software.
13
+ *
14
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ * SOFTWARE.
21
+ */
22
+
23
+package com.dmdirc.updater.installing;
24
+
25
+import com.dmdirc.updater.UpdateComponent;
26
+
27
+/**
28
+ * Interface for objects interested in the progress of an installation attempt
29
+ * performed by an {@link UpdateInstallationStrategy}.
30
+ */
31
+public interface UpdateInstallationListener {
32
+
33
+    /**
34
+     * Called when the progress of an install has changed.
35
+     *
36
+     * @param component The component being installed
37
+     * @param progress The percentage progress completed (0-100)
38
+     */
39
+    void installProgressChanged(UpdateComponent component, double progress);
40
+
41
+    /**
42
+     * Called when an installation has failed.
43
+     *
44
+     * @param component The component that failed to install
45
+     */
46
+    void installFailed(UpdateComponent component);
47
+
48
+    /**
49
+     * Called when an installation has finished.
50
+     *
51
+     * @param component The component that was successfully installed
52
+     */
53
+    void installCompleted(UpdateComponent component);
54
+
55
+}

+ 64
- 0
src/com/dmdirc/updater/installing/UpdateInstallationStrategy.java View File

@@ -0,0 +1,64 @@
1
+/*
2
+ * Copyright (c) 2006-2012 DMDirc Developers
3
+ *
4
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ * of this software and associated documentation files (the "Software"), to deal
6
+ * in the Software without restriction, including without limitation the rights
7
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ * copies of the Software, and to permit persons to whom the Software is
9
+ * furnished to do so, subject to the following conditions:
10
+ *
11
+ * The above copyright notice and this permission notice shall be included in
12
+ * all copies or substantial portions of the Software.
13
+ *
14
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ * SOFTWARE.
21
+ */
22
+
23
+package com.dmdirc.updater.installing;
24
+
25
+import com.dmdirc.updater.retrieving.UpdateRetrievalResult;
26
+import com.dmdirc.updater.retrieving.UpdateRetrievalStategy;
27
+
28
+/**
29
+ * Describes a strategy for installing updates which have been retrieved
30
+ * by a {@link UpdateRetrievalStategy}.
31
+ */
32
+public interface UpdateInstallationStrategy {
33
+
34
+    /**
35
+     * Determines whether this strategy can handle the installation of
36
+     * the given retrieved update for the given component.
37
+     *
38
+     * @param retrievalResult The result of the retrieval operation
39
+     * @return True if this strategy can perform installation; false otherwise
40
+     */
41
+    boolean canHandle(UpdateRetrievalResult retrievalResult);
42
+
43
+    /**
44
+     * Installs the retrieved update for the given component.
45
+     *
46
+     * @param retrievalResult The result of the retrieval operation
47
+     */
48
+    void install(UpdateRetrievalResult retrievalResult);
49
+
50
+    /**
51
+     * Adds a new status listener to this strategy.
52
+     *
53
+     * @param listener The listener to be registered
54
+     */
55
+    void addUpdateInstallationListener(UpdateInstallationListener listener);
56
+
57
+    /**
58
+     * Removes the given listener from this strategy.
59
+     *
60
+     * @param listener The listener to be removed
61
+     */
62
+    void removeUpdateInstallationListener(UpdateInstallationListener listener);
63
+
64
+}

+ 63
- 0
src/com/dmdirc/updater/manager/CachingUpdateManager.java View File

@@ -0,0 +1,63 @@
1
+/*
2
+ * Copyright (c) 2006-2012 DMDirc Developers
3
+ *
4
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ * of this software and associated documentation files (the "Software"), to deal
6
+ * in the Software without restriction, including without limitation the rights
7
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ * copies of the Software, and to permit persons to whom the Software is
9
+ * furnished to do so, subject to the following conditions:
10
+ *
11
+ * The above copyright notice and this permission notice shall be included in
12
+ * all copies or substantial portions of the Software.
13
+ *
14
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ * SOFTWARE.
21
+ */
22
+
23
+package com.dmdirc.updater.manager;
24
+
25
+import com.dmdirc.updater.UpdateComponent;
26
+
27
+/**
28
+ * A specialised {@link UpdateManager} which caches the update status of each
29
+ * {@link UpdateComponent}.
30
+ */
31
+public interface CachingUpdateManager extends UpdateManager {
32
+
33
+    /**
34
+     * Gets the current status of the given component.
35
+     *
36
+     * @param component The component to get a status for
37
+     * @return The current status of the component
38
+     */
39
+    UpdateStatus getStatus(UpdateComponent component);
40
+
41
+    /**
42
+     * Gets the overall status of the manager.
43
+     *
44
+     * @return The current status of the manager
45
+     */
46
+    UpdateManagerStatus getManagerStatus();
47
+
48
+    /**
49
+     * Adds a new {@link UpdateManagerListener} to this manager, which will
50
+     * be informed when the manager's status changes.
51
+     *
52
+     * @param listener The listener to be added
53
+     */
54
+    void addUpdateManagerListener(UpdateManagerListener listener);
55
+
56
+    /**
57
+     * Removes the specified listener from this manager.
58
+     *
59
+     * @param listener The listener to be removed
60
+     */
61
+    void removeUpdateManagerListener(UpdateManagerListener listener);
62
+
63
+}

+ 142
- 0
src/com/dmdirc/updater/manager/CachingUpdateManagerImpl.java View File

@@ -0,0 +1,142 @@
1
+/*
2
+ * Copyright (c) 2006-2012 DMDirc Developers
3
+ *
4
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ * of this software and associated documentation files (the "Software"), to deal
6
+ * in the Software without restriction, including without limitation the rights
7
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ * copies of the Software, and to permit persons to whom the Software is
9
+ * furnished to do so, subject to the following conditions:
10
+ *
11
+ * The above copyright notice and this permission notice shall be included in
12
+ * all copies or substantial portions of the Software.
13
+ *
14
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ * SOFTWARE.
21
+ */
22
+
23
+package com.dmdirc.updater.manager;
24
+
25
+import com.dmdirc.updater.UpdateComponent;
26
+import com.dmdirc.updater.checking.CheckResultConsolidator;
27
+
28
+import java.util.HashMap;
29
+import java.util.Map;
30
+import java.util.concurrent.Executor;
31
+
32
+import lombok.Getter;
33
+import lombok.ListenerSupport;
34
+
35
+/**
36
+ * An extension of {@link UpdateManagerImpl} which implements status caching
37
+ * functionality.
38
+ */
39
+@ListenerSupport(UpdateManagerListener.class)
40
+public class CachingUpdateManagerImpl extends UpdateManagerImpl implements CachingUpdateManager {
41
+
42
+    /** Map of component to their most recent status. */
43
+    private final Map<UpdateComponent, UpdateStatus> cachedStatuses
44
+            = new HashMap<UpdateComponent, UpdateStatus>();
45
+
46
+    /** Our current status. */
47
+    @Getter
48
+    private UpdateManagerStatus managerStatus = UpdateManagerStatus.IDLE;
49
+
50
+    /**
51
+     * Creates a new instance of {@link CachingUpdateManagerImpl}.
52
+     *
53
+     * @param executor The executor to use to schedule tasks
54
+     * @param consolidator The consolidator to use to merge check results
55
+     * @param policy The policy to apply to update components
56
+     */
57
+    public CachingUpdateManagerImpl(final Executor executor,
58
+            final CheckResultConsolidator consolidator,
59
+            final UpdateComponentPolicy policy) {
60
+        super(executor, consolidator, policy);
61
+
62
+        addUpdateStatusListener(new Listener());
63
+    }
64
+
65
+    /** {@inheritDoc} */
66
+    @Override
67
+    public UpdateStatus getStatus(final UpdateComponent component) {
68
+        return cachedStatuses.get(component);
69
+    }
70
+
71
+    /** {@inheritDoc} */
72
+    @Override
73
+    public void addComponent(final UpdateComponent component) {
74
+        super.addComponent(component);
75
+        cachedStatuses.put(component, getPolicy().canCheck(component)
76
+                ? UpdateStatus.IDLE : UpdateStatus.CHECKING_NOT_PERMITTED);
77
+    }
78
+
79
+    /** {@inheritDoc} */
80
+    @Override
81
+    public void removeComponent(final UpdateComponent component) {
82
+        super.removeComponent(component);
83
+        cachedStatuses.remove(component);
84
+    }
85
+
86
+    /**
87
+     * Determines the current status of this manager, using the
88
+     * cached status of each update component. If the status has changed,
89
+     * fires the {@link UpdateManagerListener#updateManagerStatusChanged(com.dmdirc.updater.manager.UpdateManager, com.dmdirc.updater.manager.UpdateManagerStatus)}
90
+     * method on registered listeners.
91
+     */
92
+    private void checkStatus() {
93
+        UpdateManagerStatus newStatus = UpdateManagerStatus.IDLE;
94
+        UpdateManagerStatus componentStatus;
95
+
96
+        for (UpdateStatus cachedStatus : cachedStatuses.values()) {
97
+            switch (cachedStatus) {
98
+                case CHECKING:
99
+                case INSTALLING:
100
+                case RETRIEVING:
101
+                    componentStatus = UpdateManagerStatus.WORKING;
102
+                    break;
103
+                case UPDATE_PENDING:
104
+                case INSTALL_PENDING:
105
+                    componentStatus = UpdateManagerStatus.IDLE_UPDATE_AVAILABLE;
106
+                    break;
107
+                case RESTART_PENDING:
108
+                    componentStatus = UpdateManagerStatus.IDLE_RESTART_NEEDED;
109
+                    break;
110
+                case UPDATED:
111
+                case IDLE:
112
+                default:
113
+                    componentStatus = UpdateManagerStatus.IDLE;
114
+            }
115
+
116
+            if (componentStatus.compareTo(newStatus) < 0) {
117
+                newStatus = componentStatus;
118
+            }
119
+        }
120
+
121
+        if (managerStatus != newStatus) {
122
+            managerStatus = newStatus;
123
+            fireUpdateManagerStatusChanged(this, managerStatus);
124
+        }
125
+    }
126
+
127
+    /**
128
+     * Status listener which updates the {@link #cachedStatuses} map.
129
+     */
130
+    private class Listener implements UpdateStatusListener {
131
+
132
+        /** {@inheritDoc} */
133
+        @Override
134
+        public void updateStatusChanged(final UpdateComponent component,
135
+                final UpdateStatus status, final double progress) {
136
+            cachedStatuses.put(component, status);
137
+            checkStatus();
138
+        }
139
+
140
+    }
141
+
142
+}

+ 47
- 0
src/com/dmdirc/updater/manager/ConfigComponentPolicy.java View File

@@ -0,0 +1,47 @@
1
+/*
2
+ * Copyright (c) 2006-2012 DMDirc Developers
3
+ *
4
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ * of this software and associated documentation files (the "Software"), to deal
6
+ * in the Software without restriction, including without limitation the rights
7
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ * copies of the Software, and to permit persons to whom the Software is
9
+ * furnished to do so, subject to the following conditions:
10
+ *
11
+ * The above copyright notice and this permission notice shall be included in
12
+ * all copies or substantial portions of the Software.
13
+ *
14
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ * SOFTWARE.
21
+ */
22
+
23
+package com.dmdirc.updater.manager;
24
+
25
+import com.dmdirc.config.ConfigManager;
26
+import com.dmdirc.updater.UpdateComponent;
27
+
28
+import lombok.RequiredArgsConstructor;
29
+
30
+/**
31
+ * An {@link UpdateComponentPolicy} which checks if the component is enabled
32
+ * in DMDirc's configuration.
33
+ */
34
+@RequiredArgsConstructor
35
+public class ConfigComponentPolicy implements UpdateComponentPolicy {
36
+
37
+    /** The configuration manager to check. */
38
+    private final ConfigManager manager;
39
+
40
+    /** {@inheritDoc} */
41
+    @Override
42
+    public boolean canCheck(final UpdateComponent component) {
43
+        return !manager.hasOptionBool("updater", "enable-" + component.getName())
44
+                || manager.getOptionBool("updater", "enable-" + component.getName());
45
+    }
46
+
47
+}

+ 108
- 0
src/com/dmdirc/updater/manager/DMDircUpdateManager.java View File

@@ -0,0 +1,108 @@
1
+/*
2
+ * Copyright (c) 2006-2012 DMDirc Developers
3
+ *
4
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ * of this software and associated documentation files (the "Software"), to deal
6
+ * in the Software without restriction, including without limitation the rights
7
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ * copies of the Software, and to permit persons to whom the Software is
9
+ * furnished to do so, subject to the following conditions:
10
+ *
11
+ * The above copyright notice and this permission notice shall be included in
12
+ * all copies or substantial portions of the Software.
13
+ *
14
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ * SOFTWARE.
21
+ */
22
+
23
+package com.dmdirc.updater.manager;
24
+
25
+import com.dmdirc.config.ConfigBinding;
26
+import com.dmdirc.config.ConfigManager;
27
+import com.dmdirc.updater.UpdateChannel;
28
+import com.dmdirc.updater.checking.DMDircCheckStrategy;
29
+import com.dmdirc.updater.checking.NaiveConsolidator;
30
+import com.dmdirc.updater.components.ClientComponent;
31
+import com.dmdirc.updater.components.DefaultsComponent;
32
+import com.dmdirc.updater.components.ModeAliasesComponent;
33
+import com.dmdirc.updater.installing.LegacyInstallationStrategy;
34
+import com.dmdirc.updater.retrieving.DownloadRetrievalStrategy;
35
+
36
+import java.util.concurrent.LinkedBlockingQueue;
37
+import java.util.concurrent.ThreadFactory;
38
+import java.util.concurrent.ThreadPoolExecutor;
39
+import java.util.concurrent.TimeUnit;
40
+
41
+import lombok.extern.slf4j.Slf4j;
42
+
43
+/**
44
+ * Specialised update manager for DMDirc.
45
+ */
46
+@Slf4j
47
+public class DMDircUpdateManager extends CachingUpdateManagerImpl {
48
+
49
+    /** The default check strategy we use. */
50
+    private final DMDircCheckStrategy checkStrategy;
51
+
52
+    /**
53
+     * Creates a new instance of the update manager.
54
+     *
55
+     * @param configManager The config manager to use to retrieve channel info
56
+     * @param tempFolder Temporary folder to use for downloading updates
57
+     * @param threads The number of threads to use for updating
58
+     */
59
+    public DMDircUpdateManager(final ConfigManager configManager,
60
+            final String tempFolder, final int threads) {
61
+        super(new ThreadPoolExecutor(threads, threads, 1, TimeUnit.MINUTES,
62
+                new LinkedBlockingQueue<Runnable>(), new NamedThreadFactory()),
63
+                new NaiveConsolidator(),
64
+                new ConfigComponentPolicy(configManager));
65
+
66
+        addComponent(new ClientComponent());
67
+        addComponent(new ModeAliasesComponent());
68
+        addComponent(new DefaultsComponent());
69
+
70
+        checkStrategy = new DMDircCheckStrategy(UpdateChannel.STABLE);
71
+
72
+        configManager.getBinder().bind(this, DMDircUpdateManager.class);
73
+
74
+        addCheckStrategy(checkStrategy);
75
+        addRetrievalStrategy(new DownloadRetrievalStrategy(tempFolder));
76
+        addInstallationStrategy(new LegacyInstallationStrategy());
77
+    }
78
+
79
+    /**
80
+     * Sets the channel which will be used by the {@link DMDircCheckStrategy}.
81
+     *
82
+     * @param channel The new channel to use
83
+     */
84
+    @ConfigBinding(domain="updater", key="channel")
85
+    public void setChannel(final String channel) {
86
+        log.info("Changing channel to {}", channel);
87
+
88
+        try {
89
+            checkStrategy.setChannel(UpdateChannel.valueOf(channel.toUpperCase()));
90
+        } catch (IllegalArgumentException ex) {
91
+            log.warn("Unknown channel {}", channel, ex);
92
+        }
93
+    }
94
+
95
+    /**
96
+     * Thread factory which returns named threads.
97
+     */
98
+    private static class NamedThreadFactory implements ThreadFactory {
99
+
100
+        /** {@inheritDoc} */
101
+        @Override
102
+        public Thread newThread(final Runnable r) {
103
+            return new Thread(r, "Updater thread");
104
+        }
105
+
106
+    }
107
+
108
+}

+ 49
- 0
src/com/dmdirc/updater/manager/InstallationTask.java View File

@@ -0,0 +1,49 @@
1
+/*
2
+ * Copyright (c) 2006-2012 DMDirc Developers
3
+ *
4
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ * of this software and associated documentation files (the "Software"), to deal
6
+ * in the Software without restriction, including without limitation the rights
7
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ * copies of the Software, and to permit persons to whom the Software is
9
+ * furnished to do so, subject to the following conditions:
10
+ *
11
+ * The above copyright notice and this permission notice shall be included in
12
+ * all copies or substantial portions of the Software.
13
+ *
14
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ * SOFTWARE.
21
+ */
22
+
23
+package com.dmdirc.updater.manager;
24
+
25
+import com.dmdirc.updater.installing.UpdateInstallationStrategy;
26
+import com.dmdirc.updater.retrieving.UpdateRetrievalResult;
27
+
28
+import lombok.RequiredArgsConstructor;
29
+
30
+/**
31
+ * Task which installs an update using a given
32
+ * {@link UpdateInstallationStrategy}.
33
+ */
34
+@RequiredArgsConstructor
35
+public class InstallationTask implements Runnable {
36
+
37
+    /** The strategy to use to install the update. */
38
+    private final UpdateInstallationStrategy strategy;
39
+
40
+    /** The update which will be installed. */
41
+    private final UpdateRetrievalResult result;
42
+
43
+    /** {@inheritDoc} */
44
+    @Override
45
+    public void run() {
46
+        strategy.install(result);
47
+    }
48
+
49
+}

+ 65
- 0
src/com/dmdirc/updater/manager/RetrievalTask.java View File

@@ -0,0 +1,65 @@
1
+/*
2
+ * Copyright (c) 2006-2012 DMDirc Developers
3
+ *
4
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ * of this software and associated documentation files (the "Software"), to deal
6
+ * in the Software without restriction, including without limitation the rights
7
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ * copies of the Software, and to permit persons to whom the Software is
9
+ * furnished to do so, subject to the following conditions:
10
+ *
11
+ * The above copyright notice and this permission notice shall be included in
12
+ * all copies or substantial portions of the Software.
13
+ *
14
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ * SOFTWARE.
21
+ */
22
+
23
+package com.dmdirc.updater.manager;
24
+
25
+import com.dmdirc.updater.checking.UpdateCheckResult;
26
+import com.dmdirc.updater.retrieving.UpdateRetrievalResult;
27
+import com.dmdirc.updater.retrieving.UpdateRetrievalStategy;
28
+
29
+import lombok.RequiredArgsConstructor;
30
+
31
+/**
32
+ * Task which retrieves an available update using the given
33
+ * {@link UpdateRetrievalStategy} and passes the result back to the
34
+ * {@link UpdateManagerImpl}.
35
+ */
36
+@RequiredArgsConstructor
37
+public class RetrievalTask implements Runnable {
38
+
39
+    /** The update manager launching this task. */
40
+    private final UpdateManagerImpl manager;
41
+
42
+    /** The strategy to use to retrieve the update. */
43
+    private final UpdateRetrievalStategy strategy;
44
+
45
+    /** The update which will be downloaded. */
46
+    private final UpdateCheckResult result;
47
+
48
+    /** Whether to install afterwards or not. */
49
+    private final boolean install;
50
+
51
+    /** {@inheritDoc} */
52
+    @Override
53
+    public void run() {
54
+        final UpdateRetrievalResult retrievalResult = strategy.retrieve(result);
55
+
56
+        if (retrievalResult.isSuccessful()) {
57
+            manager.setRetrievalResult(retrievalResult);
58
+
59
+            if (install) {
60
+                manager.install(retrievalResult.getCheckResult().getComponent());
61
+            }
62
+        }
63
+    }
64
+
65
+}

+ 43
- 0
src/com/dmdirc/updater/manager/UpdateComponentPolicy.java View File

@@ -0,0 +1,43 @@
1
+/*
2
+ * Copyright (c) 2006-2012 DMDirc Developers
3
+ *
4
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ * of this software and associated documentation files (the "Software"), to deal
6
+ * in the Software without restriction, including without limitation the rights
7
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ * copies of the Software, and to permit persons to whom the Software is
9
+ * furnished to do so, subject to the following conditions:
10
+ *
11
+ * The above copyright notice and this permission notice shall be included in
12
+ * all copies or substantial portions of the Software.
13
+ *
14
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ * SOFTWARE.
21
+ */
22
+
23
+package com.dmdirc.updater.manager;
24
+
25
+import com.dmdirc.updater.UpdateComponent;
26
+
27
+/**
28
+ * Defines a policy to determine whether certain {@link UpdateComponent}s can
29
+ * be updated.
30
+ */
31
+public interface UpdateComponentPolicy {
32
+
33
+    /**
34
+     * Determines whether the manager can check for updates for the given
35
+     * component.
36
+     *
37
+     * @param component The component in question
38
+     * @return True if the manager is allowed to check this component, false
39
+     * otherwise
40
+     */
41
+    boolean canCheck(UpdateComponent component);
42
+
43
+}

+ 142
- 0
src/com/dmdirc/updater/manager/UpdateManager.java View File

@@ -0,0 +1,142 @@
1
+/*
2
+ * Copyright (c) 2006-2012 DMDirc Developers
3
+ *
4
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ * of this software and associated documentation files (the "Software"), to deal
6
+ * in the Software without restriction, including without limitation the rights
7
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ * copies of the Software, and to permit persons to whom the Software is
9
+ * furnished to do so, subject to the following conditions:
10
+ *
11
+ * The above copyright notice and this permission notice shall be included in
12
+ * all copies or substantial portions of the Software.
13
+ *
14
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ * SOFTWARE.
21
+ */
22
+
23
+package com.dmdirc.updater.manager;
24
+
25
+import com.dmdirc.updater.UpdateComponent;
26
+import com.dmdirc.updater.checking.UpdateCheckResult;
27
+import com.dmdirc.updater.checking.UpdateCheckStrategy;
28
+import com.dmdirc.updater.installing.UpdateInstallationStrategy;
29
+import com.dmdirc.updater.retrieving.UpdateRetrievalStategy;
30
+
31
+import java.util.Collection;
32
+
33
+/**
34
+ * Manages the update process for a collection of {@link UpdateComponent}s.
35
+ */
36
+public interface UpdateManager {
37
+
38
+    /**
39
+     * Adds a new check strategy to this manager. {@link UpdateCheckStrategy}s
40
+     * are responsible for checking to see if an updated version of a set of
41
+     * components is available. If multiple strategies are configured, their
42
+     * results are merged using this manager's {@link CheckResultConsolidator}.
43
+     *
44
+     * @param strategy The strategy to be added.
45
+     */
46
+    void addCheckStrategy(final UpdateCheckStrategy strategy);
47
+
48
+    /**
49
+     * Adds a new installation strategy to this manager.
50
+     * {@link UpdateInstallationStrategy}s are responsible for handling the
51
+     * installation of an update once it has been retrieved.
52
+     *
53
+     * @param strategy The strategy to be added.
54
+     */
55
+    void addInstallationStrategy(final UpdateInstallationStrategy strategy);
56
+
57
+    /**
58
+     * Adds a new retrieval strategy to this manager.
59
+     * {@link UpdateRetrievalStategy}s are responsible for retrieving the
60
+     * updated version of a component after an update has been identified
61
+     * by a {@link UpdateCheckStrategy}.
62
+     *
63
+     * @param strategy The strategy to be added.
64
+     */
65
+    void addRetrievalStrategy(final UpdateRetrievalStategy strategy);
66
+
67
+    /**
68
+     * Adds a new component to this manager. Components will be checked for
69
+     * updates the next time {@link #checkForUpdates()} is called. If a
70
+     * component with the same name already exists, it will be removed.
71
+     *
72
+     * @param component The component to be added
73
+     */
74
+    void addComponent(final UpdateComponent component);
75
+
76
+    /**
77
+     * Removes the specified component from this manager. If any operations
78
+     * are pending for the component they may complete after this method has
79
+     * been called.
80
+     *
81
+     * @param component The component to be removed
82
+     */
83
+    void removeComponent(final UpdateComponent component);
84
+
85
+    /**
86
+     * Retrieves the collection of all components known by this manager.
87
+     *
88
+     * @return The set of all known {@link UpdateComponent}s.
89
+     */
90
+    Collection<UpdateComponent> getComponents();
91
+
92
+    /**
93
+     * Checks for updates for all registered {@link UpdateComponent}s.
94
+     */
95
+    void checkForUpdates();
96
+
97
+    /**
98
+     * Installs any updates for the specified component. If an update for the
99
+     * component is available but has not yet been retrieved, this method
100
+     * should perform in the same way to a synchronous call to
101
+     * {@link #retrieve(com.dmdirc.updater.UpdateComponent)} followed by
102
+     * {@link #install(com.dmdirc.updater.UpdateComponent)}.
103
+     *
104
+     * @param component The component to be installed
105
+     */
106
+    void install(UpdateComponent component);
107
+
108
+    /**
109
+     * Retrieves any update associated with the given component. The update
110
+     * will not be installed until the
111
+     * {@link #install(com.dmdirc.updater.UpdateComponent)} method is called.
112
+     *
113
+     * @param component The component to retrieve updates for
114
+     */
115
+    void retrieve(UpdateComponent component);
116
+
117
+    /**
118
+     * Retrieves the last update check result for the given component.
119
+     *
120
+     * @param component The component to retrieve a check result for
121
+     * @return The component's most recent associated {@link UpdateCheckResult},
122
+     * or <code>null</code> if the component has not been checked or has since
123
+     * been updated.
124
+     */
125
+    UpdateCheckResult getCheckResult(UpdateComponent component);
126
+
127
+    /**
128
+     * Adds the given status listener to this manager. The listener will be
129
+     * called for any future status changes caused by this manager.
130
+     *
131
+     * @param listener The listener to be added
132
+     */
133
+    void addUpdateStatusListener(UpdateStatusListener listener);
134
+
135
+    /**
136
+     * Removes the given status listener from this manager.
137
+     *
138
+     * @param listener The listener to be removed
139
+     */
140
+    void removeUpdateStatusListener(UpdateStatusListener listener);
141
+
142
+}

+ 358
- 0
src/com/dmdirc/updater/manager/UpdateManagerImpl.java View File

@@ -0,0 +1,358 @@
1
+/*
2
+ * Copyright (c) 2006-2012 DMDirc Developers
3
+ *
4
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ * of this software and associated documentation files (the "Software"), to deal
6
+ * in the Software without restriction, including without limitation the rights
7
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ * copies of the Software, and to permit persons to whom the Software is
9
+ * furnished to do so, subject to the following conditions:
10
+ *
11
+ * The above copyright notice and this permission notice shall be included in
12
+ * all copies or substantial portions of the Software.
13
+ *
14
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ * SOFTWARE.
21
+ */
22
+
23
+package com.dmdirc.updater.manager;
24
+
25
+import com.dmdirc.updater.UpdateComponent;
26
+import com.dmdirc.updater.checking.CheckResultConsolidator;
27
+import com.dmdirc.updater.checking.UpdateCheckResult;
28
+import com.dmdirc.updater.checking.UpdateCheckStrategy;
29
+import com.dmdirc.updater.installing.UpdateInstallationListener;
30
+import com.dmdirc.updater.installing.UpdateInstallationStrategy;
31
+import com.dmdirc.updater.retrieving.UpdateRetrievalListener;
32
+import com.dmdirc.updater.retrieving.UpdateRetrievalResult;
33
+import com.dmdirc.updater.retrieving.UpdateRetrievalStategy;
34
+
35
+import java.util.ArrayList;
36
+import java.util.Collection;
37
+import java.util.Collections;
38
+import java.util.HashMap;
39
+import java.util.List;
40
+import java.util.Map;
41
+import java.util.concurrent.CopyOnWriteArrayList;
42
+import java.util.concurrent.Executor;
43
+
44
+import lombok.AccessLevel;
45
+import lombok.AllArgsConstructor;
46
+import lombok.Getter;
47
+import lombok.ListenerSupport;
48
+import lombok.extern.slf4j.Slf4j;
49
+
50
+/**
51
+ * Concrete implementation of {@link UpdateManager}.
52
+ */
53
+@Slf4j
54
+@AllArgsConstructor
55
+@ListenerSupport(UpdateStatusListener.class)
56
+public class UpdateManagerImpl implements UpdateManager {
57
+
58
+    /** Collection of known update checking strategies. */
59
+    private final List<UpdateCheckStrategy> checkers
60
+            = new CopyOnWriteArrayList<UpdateCheckStrategy>();
61
+
62
+    /** Collection of known update retrieval strategies. */
63
+    private final List<UpdateRetrievalStategy> retrievers
64
+            = new CopyOnWriteArrayList<UpdateRetrievalStategy>();
65
+
66
+    /** Collection of known update installation strategies. */
67
+    private final List<UpdateInstallationStrategy> installers
68
+            = new CopyOnWriteArrayList<UpdateInstallationStrategy>();
69
+
70
+    /** Map of known component names to their components. */
71
+    private final Map<String, UpdateComponent> components
72
+            = new HashMap<String, UpdateComponent>();
73
+
74
+    /** Listener used to proxy retrieval events. */
75
+    private final UpdateRetrievalListener retrievalListener
76
+            = new RetrievalListener();
77
+
78
+    /** Listener used to proxy setRetrievalResult events. */
79
+    private final UpdateInstallationListener installationListener
80
+            = new InstallListener();
81
+
82
+    /** Cache of update check results. */
83
+    private final Map<UpdateComponent, UpdateCheckResult> checkResults
84
+            = new HashMap<UpdateComponent, UpdateCheckResult>();
85
+
86
+    /** Cache of retrieval results. */
87
+    private final Map<UpdateComponent, UpdateRetrievalResult> retrievalResults
88
+            = new HashMap<UpdateComponent, UpdateRetrievalResult>();
89
+
90
+    /** Executor to use to schedule retrieval and installation jobs. */
91
+    private final Executor executor;
92
+
93
+    /** Consolidator to use to merge multiple check results. */
94
+    @Getter(AccessLevel.PROTECTED)
95
+    private final CheckResultConsolidator consolidator;
96
+
97
+    /** The policy to use to determine whether updates are enabled. */
98
+    @Getter(AccessLevel.PROTECTED)
99
+    private final UpdateComponentPolicy policy;
100
+
101
+    /** {@inheritDoc} */
102
+    @Override
103
+    public void addCheckStrategy(final UpdateCheckStrategy strategy) {
104
+        log.trace("Adding new check strategy: {}", strategy);
105
+        this.checkers.add(strategy);
106
+    }
107
+
108
+    /** {@inheritDoc} */
109
+    @Override
110
+    public void addRetrievalStrategy(final UpdateRetrievalStategy strategy) {
111
+        log.trace("Adding new retrieval strategy: {}", strategy);
112
+        strategy.addUpdateRetrievalListener(retrievalListener);
113
+        this.retrievers.add(strategy);
114
+    }
115
+
116
+    /** {@inheritDoc} */
117
+    @Override
118
+    public void addInstallationStrategy(final UpdateInstallationStrategy strategy) {
119
+        log.trace("Adding new installation strategy: {}", strategy);
120
+        strategy.addUpdateInstallationListener(installationListener);
121
+        this.installers.add(strategy);
122
+    }
123
+
124
+    /** {@inheritDoc} */
125
+    @Override
126
+    public void addComponent(final UpdateComponent component) {
127
+        log.trace("Adding new component: {}", component);
128
+        this.components.put(component.getName(), component);
129
+    }
130
+
131
+    /** {@inheritDoc} */
132
+    @Override
133
+    public void removeComponent(final UpdateComponent component) {
134
+        log.trace("Removing component: {}", component);
135
+        this.components.remove(component.getName());
136
+        this.checkResults.remove(component);
137
+        this.retrievalResults.remove(component);
138
+    }
139
+
140
+    /** {@inheritDoc} */
141
+    @Override
142
+    public Collection<UpdateComponent> getComponents() {
143
+        return Collections.unmodifiableCollection(this.components.values());
144
+    }
145
+
146
+    /** {@inheritDoc} */
147
+    @Override
148
+    public UpdateCheckResult getCheckResult(final UpdateComponent component) {
149
+        return checkResults.get(component);
150
+    }
151
+
152
+    /** {@inheritDoc} */
153
+    @Override
154
+    public void checkForUpdates() {
155
+        final Collection<Map<UpdateComponent, UpdateCheckResult>> results
156
+                = new ArrayList<Map<UpdateComponent, UpdateCheckResult>>();
157
+
158
+        log.info("Checking for updates for {} components using {} strategies",
159
+                components.size(), checkers.size());
160
+        log.trace("Components: {}", components);
161
+        log.trace("Strategies: {}", checkers);
162
+
163
+        final List<UpdateComponent> enabledComponents
164
+                = new ArrayList<UpdateComponent>(components.size());
165
+
166
+        for (UpdateComponent component : components.values()) {
167
+            if (policy.canCheck(component)) {
168
+                fireUpdateStatusChanged(component, UpdateStatus.CHECKING, 0);
169
+                enabledComponents.add(component);
170
+            } else {
171
+                log.debug("Checking for updates for {} denied by policy", component.getName());
172
+                fireUpdateStatusChanged(component, UpdateStatus.CHECKING_NOT_PERMITTED, 0);
173
+            }
174
+        }
175
+
176
+        for (UpdateCheckStrategy strategy : checkers) {
177
+            results.add(strategy.checkForUpdates(enabledComponents));
178
+        }
179
+
180
+        checkResults.putAll(consolidator.consolidate(results));
181
+
182
+        for (UpdateComponent component : enabledComponents) {
183
+            if (checkResults.containsKey(component) && checkResults.get(component).isUpdateAvailable()) {
184
+                log.trace("Update is available for {}", component);
185
+                fireUpdateStatusChanged(component, UpdateStatus.UPDATE_PENDING, 0);
186
+            } else {
187
+                fireUpdateStatusChanged(component, UpdateStatus.IDLE, 0);
188
+            }
189
+        }
190
+    }
191
+
192
+    /** {@inheritDoc} */
193
+    @Override
194
+    public void install(final UpdateComponent component) {
195
+        if (!retrievalResults.containsKey(component) || !retrievalResults.get(component).isSuccessful()) {
196
+            // Not downloaded yet - try retrieving first
197
+            retrieve(component, true);
198
+            return;
199
+        }
200
+
201
+        final UpdateRetrievalResult update = retrievalResults.get(component);
202
+        final UpdateInstallationStrategy strategy = getStrategy(update);
203
+
204
+        if (strategy == null) {
205
+            fireUpdateStatusChanged(component, UpdateStatus.IDLE, 0);
206
+        } else {
207
+            fireUpdateStatusChanged(component, UpdateStatus.INSTALL_PENDING, 0);
208
+            log.debug("Scheduling install for {}", update);
209
+            executor.execute(new InstallationTask(strategy, update));
210
+        }
211
+    }
212
+
213
+    /** {@inheritDoc} */
214
+    @Override
215
+    public void retrieve(final UpdateComponent component) {
216
+        retrieve(component, false);
217
+    }
218
+
219
+    /**
220
+     * Retrieves updates for the given component, optionally installing if
221
+     * retrieval is successful.
222
+     *
223
+     * @param component The component to be retrieved
224
+     * @param install True to install automatically, false to just retrieve
225
+     */
226
+    public void retrieve(final UpdateComponent component, final boolean install) {
227
+        if (!checkResults.containsKey(component) || !checkResults.get(component).isUpdateAvailable()) {
228
+            log.warn("Tried to retrieve component with no update: {}", component);
229
+            return;
230
+        }
231
+
232
+        final UpdateCheckResult update = checkResults.get(component);
233
+        final UpdateRetrievalStategy strategy = getStrategy(update);
234
+
235
+        if (strategy == null) {
236
+            fireUpdateStatusChanged(component, UpdateStatus.IDLE, 0);
237
+        } else {
238
+            fireUpdateStatusChanged(component, UpdateStatus.UPDATE_PENDING, 0);
239
+            log.debug("Scheduling retrieval for {}", update);
240
+            executor.execute(new RetrievalTask(this, strategy, update, install));
241
+        }
242
+    }
243
+
244
+    /**
245
+     * Sets the retrieval result for an {@link UpdateComponent}.
246
+     *
247
+     * @param result The result retrieved from the {@link UpdateRetrievalStategy}.
248
+     */
249
+    protected void setRetrievalResult(final UpdateRetrievalResult result) {
250
+        log.debug("Received retrieval result {}", result);
251
+        retrievalResults.put(result.getCheckResult().getComponent(), result);
252
+
253
+        if (result.isSuccessful()) {
254
+            checkResults.remove(result.getCheckResult().getComponent());
255
+        }
256
+    }
257
+
258
+    /**
259
+     * Gets an appropriate retrieval strategy for the given check result.
260
+     * Iterates over the set of known strategies and returns the first which
261
+     * claims to be able to handle the result.
262
+     *
263
+     * @param result The result to find a strategy for
264
+     * @return A relevant strategy, or <code>null</code> if none are available
265
+     */
266
+    protected UpdateRetrievalStategy getStrategy(final UpdateCheckResult result) {
267
+        log.debug("Trying to find retrieval strategy for {}", result);
268
+
269
+        for (UpdateRetrievalStategy strategy : retrievers) {
270
+            log.trace("Testing strategy {}", strategy);
271
+
272
+            if (strategy.canHandle(result)) {
273
+                log.debug("Found strategy {}", strategy);
274
+                return strategy;
275
+            }
276
+        }
277
+
278
+        log.warn("No strategy found to retrieve {}", result);
279
+        return null;
280
+    }
281
+
282
+    /**
283
+     * Gets an appropriate installation strategy for the given retrieval result.
284
+     * Iterates over the set of known strategies and returns the first which
285
+     * claims to be able to handle the result.
286
+     *
287
+     * @param result The result to find a strategy for
288
+     * @return A relevant strategy, or <code>null</code> if none are available
289
+     */
290
+    protected UpdateInstallationStrategy getStrategy(final UpdateRetrievalResult result) {
291
+        log.debug("Trying to find installation strategy for {}", result);
292
+
293
+        for (UpdateInstallationStrategy strategy : installers) {
294
+            log.trace("Testing strategy {}", strategy);
295
+
296
+            if (strategy.canHandle(result)) {
297
+                log.debug("Found strategy {}", strategy);
298
+                return strategy;
299
+            }
300
+        }
301
+
302
+        log.warn("No strategy found to install {}", result);
303
+        return null;
304
+    }
305
+
306
+    /**
307
+     * Listens for changes announced by {@link UpdateInstallationStrategy}s
308
+     * and proxies them on to the manager's own {@link UpdateStatusListener}s.
309
+     */
310
+    private class InstallListener implements UpdateInstallationListener {
311
+
312
+        /** {@inheritDoc} */
313
+        @Override
314
+        public void installCompleted(final UpdateComponent component) {
315
+            fireUpdateStatusChanged(component, component.requiresRestart()
316
+                    ? UpdateStatus.RESTART_PENDING : UpdateStatus.UPDATED, 0);
317
+        }
318
+
319
+        /** {@inheritDoc} */
320
+        @Override
321
+        public void installFailed(final UpdateComponent component) {
322
+            fireUpdateStatusChanged(component, UpdateStatus.IDLE, 0);
323
+        }
324
+
325
+        /** {@inheritDoc} */
326
+        @Override
327
+        public void installProgressChanged(final UpdateComponent component, final double progress) {
328
+            fireUpdateStatusChanged(component, UpdateStatus.INSTALLING, progress);
329
+        }
330
+
331
+    }
332
+
333
+    /**
334
+     * Listens for changes announced by {@link UpdateRetrievalStrategy}s
335
+     * and proxies them on to the manager's own {@link UpdateStatusListener}s.
336
+     */
337
+    private class RetrievalListener implements UpdateRetrievalListener {
338
+
339
+        /** {@inheritDoc} */
340
+        @Override
341
+        public void retrievalCompleted(final UpdateComponent component) {
342
+            fireUpdateStatusChanged(component, UpdateStatus.INSTALL_PENDING, 0);
343
+        }
344
+
345
+        /** {@inheritDoc} */
346
+        @Override
347
+        public void retrievalFailed(final UpdateComponent component) {
348
+            fireUpdateStatusChanged(component, UpdateStatus.IDLE, 0);
349
+        }
350
+
351
+        /** {@inheritDoc} */
352
+        @Override
353
+        public void retrievalProgressChanged(final UpdateComponent component, final double progress) {
354
+            fireUpdateStatusChanged(component, UpdateStatus.RETRIEVING, progress);
355
+        }
356
+
357
+    }
358
+}

src/com/dmdirc/updater/FileComponent.java → src/com/dmdirc/updater/manager/UpdateManagerListener.java View File

@@ -20,18 +20,20 @@
20 20
  * SOFTWARE.
21 21
  */
22 22
 
23
-package com.dmdirc.updater;
23
+package com.dmdirc.updater.manager;
24 24
 
25 25
 /**
26
- * This interface is implemented by components that are related to a specific
27
- * file.
26
+ * Interface for objects interested in the status of a
27
+ * {@link CachingUpdateManager}.
28 28
  */
29
-public interface FileComponent {
29
+public interface UpdateManagerListener {
30 30
 
31 31
     /**
32
-     * Retrieves the file name of this component.
32
+     * Called when the status of the {@link UpdateManager} changes.
33 33
      *
34
-     * @return This component's file name
34
+     * @param manager The manager whose status has changed
35
+     * @param status The new status of the manager
35 36
      */
36
-    String getFileName();
37
+    void updateManagerStatusChanged(UpdateManager manager, UpdateManagerStatus status);
38
+
37 39
 }

+ 44
- 0
src/com/dmdirc/updater/manager/UpdateManagerStatus.java View File

@@ -0,0 +1,44 @@
1
+/*
2
+ * Copyright (c) 2006-2012 DMDirc Developers
3
+ *
4
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ * of this software and associated documentation files (the "Software"), to deal
6
+ * in the Software without restriction, including without limitation the rights
7
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ * copies of the Software, and to permit persons to whom the Software is
9
+ * furnished to do so, subject to the following conditions:
10
+ *
11
+ * The above copyright notice and this permission notice shall be included in
12
+ * all copies or substantial portions of the Software.
13
+ *
14
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ * SOFTWARE.
21
+ */
22
+
23
+package com.dmdirc.updater.manager;
24
+
25
+/**
26
+ * Enumerates the possible states of a {@link CachingUpdateManager}. The
27
+ * statuses are ordered according to priority, with the first being the
28
+ * highest priority.
29
+ */
30
+public enum UpdateManagerStatus {
31
+
32
+    /** The manager is currently performing some task. */
33
+    WORKING,
34
+
35
+    /** The manager is {@link #IDLE} but there are updates available. */
36
+    IDLE_UPDATE_AVAILABLE,
37
+
38
+    /** The manager is {@link #IDLE} but a restart is needed. */
39
+    IDLE_RESTART_NEEDED,
40
+
41
+    /** The manager is idle and not performing any activity. */
42
+    IDLE,
43
+
44
+}

+ 66
- 0
src/com/dmdirc/updater/manager/UpdateStatus.java View File

@@ -0,0 +1,66 @@
1
+/*
2
+ * Copyright (c) 2006-2012 DMDirc Developers
3
+ *
4
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ * of this software and associated documentation files (the "Software"), to deal
6
+ * in the Software without restriction, including without limitation the rights
7
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ * copies of the Software, and to permit persons to whom the Software is
9
+ * furnished to do so, subject to the following conditions:
10
+ *
11
+ * The above copyright notice and this permission notice shall be included in
12
+ * all copies or substantial portions of the Software.
13
+ *
14
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ * SOFTWARE.
21
+ */
22
+
23
+package com.dmdirc.updater.manager;
24
+
25
+import lombok.Getter;
26
+import lombok.RequiredArgsConstructor;
27
+
28
+/**
29
+ * Describes the possible statuses of an {@link UpdateComponent}.
30
+ */
31
+@RequiredArgsConstructor
32
+@SuppressWarnings("PMD.UnusedPrivateField")
33
+public enum UpdateStatus {
34
+
35
+    /** There is no known update available, and the manager is not active checking. */
36
+    IDLE("Idle"),
37
+
38
+    /** The update manager is not permitted to check for updates for this component. */
39
+    CHECKING_NOT_PERMITTED("Not enabled"),
40
+
41
+    /** The manager is currently checking for updates for the component. */
42
+    CHECKING("Checking..."),
43
+
44
+    /** An update is available but it is not currently being retrieved. */
45
+    UPDATE_PENDING("Update available"),
46
+
47
+    /** An update is currently being retrieved. */
48
+    RETRIEVING("Retrieving..."),
49
+
50
+    /** An update has been retrieved but it is not currently being installed. */
51
+    INSTALL_PENDING("Retrieved"),
52
+
53
+    /** An update is currently being installed. */
54
+    INSTALLING("Installing..."),
55
+
56
+    /** An update has successfully been installed. */
57
+    UPDATED("Updated"),
58
+
59
+    /** An update has been installed but will not take effect until after a restart. */
60
+    RESTART_PENDING("Restart required");
61
+
62
+    /** Textual description of the status. */
63
+    @Getter
64
+    private final String description;
65
+
66
+}

+ 45
- 0
src/com/dmdirc/updater/manager/UpdateStatusListener.java View File

@@ -0,0 +1,45 @@
1
+/*
2
+ * Copyright (c) 2006-2012 DMDirc Developers
3
+ *
4
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ * of this software and associated documentation files (the "Software"), to deal
6
+ * in the Software without restriction, including without limitation the rights
7
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ * copies of the Software, and to permit persons to whom the Software is
9
+ * furnished to do so, subject to the following conditions:
10
+ *
11
+ * The above copyright notice and this permission notice shall be included in
12
+ * all copies or substantial portions of the Software.
13
+ *
14
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ * SOFTWARE.
21
+ */
22
+
23
+package com.dmdirc.updater.manager;
24
+
25
+import com.dmdirc.updater.UpdateComponent;
26
+
27
+/**
28
+ * Interface for objects interested in updates to the status of an
29
+ * {@link UpdateComponent}.
30
+ */
31
+public interface UpdateStatusListener {
32
+
33
+    /**
34
+     * Called when the status of an update is changed. Note that the
35
+     * <code>progress</code> parameter may not be used for all statuses, and
36
+     * will generally only be implemented for those that involve lengthy I/O
37
+     * operations such as {@link UpdateStatus#RETRIEVING}.
38
+     *
39
+     * @param component The component whose status has changed
40
+     * @param status The current (new) status of the component
41
+     * @param progress The percentage progress through the current state
42
+     */
43
+    void updateStatusChanged(UpdateComponent component, UpdateStatus status, double progress);
44
+
45
+}

+ 46
- 0
src/com/dmdirc/updater/retrieving/BaseRetrievalResult.java View File

@@ -0,0 +1,46 @@
1
+/*
2
+ * Copyright (c) 2006-2012 DMDirc Developers
3
+ *
4
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ * of this software and associated documentation files (the "Software"), to deal
6
+ * in the Software without restriction, including without limitation the rights
7
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ * copies of the Software, and to permit persons to whom the Software is
9
+ * furnished to do so, subject to the following conditions:
10
+ *
11
+ * The above copyright notice and this permission notice shall be included in
12
+ * all copies or substantial portions of the Software.
13
+ *
14
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ * SOFTWARE.
21
+ */
22
+
23
+package com.dmdirc.updater.retrieving;
24
+
25
+import com.dmdirc.updater.checking.UpdateCheckResult;
26
+
27
+import lombok.Getter;
28
+import lombok.RequiredArgsConstructor;
29
+import lombok.ToString;
30
+
31
+/**
32
+ * Simple implementation of a {@link UpdateRetrievalResult}.
33
+ */
34
+@Getter
35
+@ToString
36
+@RequiredArgsConstructor
37
+@SuppressWarnings("PMD.UnusedPrivateField")
38
+public class BaseRetrievalResult implements UpdateRetrievalResult {
39
+
40
+    /** The check result that triggered this retrieval. */
41
+    private final UpdateCheckResult checkResult;
42
+
43
+    /** Whether or not the retrieval was successful. */
44
+    private final boolean successful;
45
+
46
+}

src/com/dmdirc/updater/UpdateStatus.java → src/com/dmdirc/updater/retrieving/BaseSingleFileResult.java View File

@@ -20,41 +20,37 @@
20 20
  * SOFTWARE.
21 21
  */
22 22
 
23
-package com.dmdirc.updater;
23
+package com.dmdirc.updater.retrieving;
24
+
25
+import com.dmdirc.updater.checking.UpdateCheckResult;
26
+
27
+import java.io.File;
28
+
29
+import lombok.Getter;
30
+import lombok.ToString;
24 31
 
25 32
 /**
26
- * Represents the status of an update.
33
+ * Simple implementation of a {@link SingleFileRetrievalResult}.
27 34
  */
28
-public enum UpdateStatus {
29
-
30
-    /** Update pending. */
31
-    PENDING("Pending"),
32
-    /** Update downloading. */
33
-    DOWNLOADING("Downloading"),
34
-    /** Update installing. */
35
-    INSTALLING("Installing"),
36
-    /** Update installed. */
37
-    INSTALLED("Installed"),
38
-    /** Error updating. */
39
-    ERROR("Error"),
40
-    /** Update requires restart. */
41
-    RESTART_NEEDED("Restart needed");
42
-
43
-    /** Friendly name. */
44
-    private final String friendlyName;
35
+@Getter
36
+@ToString(callSuper=true)
37
+@SuppressWarnings("PMD.UnusedPrivateField")
38
+public class BaseSingleFileResult extends BaseRetrievalResult
39
+        implements SingleFileRetrievalResult {
40
+
41
+    /** The file where the update is located. */
42
+    private final File file;
45 43
 
46 44
     /**
47
-     * Instantiates a new update status.
45
+     * Creates a new instance of {@link BaseSingleFileResult}.
48 46
      *
49
-     * @param friendlyName Friendly name for the status
47
+     * @param checkResult The check result that triggered this retrieval
48
+     * @param file The file containing the update
50 49
      */
51
-    UpdateStatus(final String friendlyName) {
52
-        this.friendlyName = friendlyName;
53
-    }
50
+    public BaseSingleFileResult(final UpdateCheckResult checkResult, final File file) {
51
+        super(checkResult, true);
54 52
 
55
-    /** {@inheritDoc} */
56
-    @Override
57
-    public String toString() {
58
-        return friendlyName;
53
+        this.file = file;
59 54
     }
55
+
60 56
 }

+ 117
- 0
src/com/dmdirc/updater/retrieving/DownloadRetrievalStrategy.java View File

@@ -0,0 +1,117 @@
1
+/*
2
+ * Copyright (c) 2006-2012 DMDirc Developers
3
+ *
4
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ * of this software and associated documentation files (the "Software"), to deal
6
+ * in the Software without restriction, including without limitation the rights
7
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ * copies of the Software, and to permit persons to whom the Software is
9
+ * furnished to do so, subject to the following conditions:
10
+ *
11
+ * The above copyright notice and this permission notice shall be included in
12
+ * all copies or substantial portions of the Software.
13
+ *
14
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ * SOFTWARE.
21
+ */
22
+
23
+package com.dmdirc.updater.retrieving;
24
+
25
+import com.dmdirc.updater.UpdateComponent;
26
+import com.dmdirc.updater.checking.DownloadableUpdate;
27
+import com.dmdirc.util.io.DownloadListener;
28
+import com.dmdirc.util.io.Downloader;
29
+
30
+import java.io.File;
31
+import java.io.IOException;
32
+
33
+import lombok.AllArgsConstructor;
34
+import lombok.ListenerSupport;
35
+import lombok.extern.slf4j.Slf4j;
36
+
37
+/**
38
+ * An {@link UpdateRetrievalStategy} that downloads a file specified in a
39
+ * {@link DownloadableUpdate}.
40
+ */
41
+@Slf4j
42
+@ListenerSupport(UpdateRetrievalListener.class)
43
+public class DownloadRetrievalStrategy extends TypeSensitiveRetrievalStrategy<DownloadableUpdate> {
44
+
45
+    /** The directory to put temporary update files in. */
46
+    private final String directory;
47
+
48
+    /**
49
+     * Creates a new {@link DownloadRetrievalStrategy} which will place its
50
+     * temporary files in the given directory.
51
+     *
52
+     * @param directory The directory to use to download files to
53
+     */
54
+    public DownloadRetrievalStrategy(final String directory) {
55
+        super(DownloadableUpdate.class);
56
+
57
+        this.directory = directory;
58
+    }
59
+
60
+    /** {@inheritDoc} */
61
+    @Override
62
+    protected UpdateRetrievalResult retrieveImpl(final DownloadableUpdate checkResult) {
63
+        try {
64
+            final String file = getFileName();
65
+
66
+            fireRetrievalProgressChanged(checkResult.getComponent(), 0);
67
+
68
+            log.debug("Downloading file from {} to {}", checkResult.getUrl(), file);
69
+            Downloader.downloadPage(checkResult.getUrl().toString(), file,
70
+                    new DownloadProgressListener(checkResult.getComponent()));
71
+
72
+            fireRetrievalCompleted(checkResult.getComponent());
73
+
74
+            return new BaseSingleFileResult(checkResult, new File(file));
75
+        } catch (IOException ex) {
76
+            log.warn("I/O exception downloading update from {}", checkResult.getUrl(), ex);
77
+            fireRetrievalFailed(checkResult.getComponent());
78
+        }
79
+
80
+        return new BaseRetrievalResult(checkResult, false);
81
+    }
82
+
83
+    /**
84
+     * Creates a random local file name to download the remote file to.
85
+     *
86
+     * @return The full, local path to download the remote file to
87
+     */
88
+    private String getFileName() {
89
+        return directory + File.separator + "update."
90
+                    + Math.round(10000 * Math.random()) + ".tmp";
91
+    }
92
+
93
+    /**
94
+     * A {@link DownloadListener} which proxies progress updates on to
95
+     * this strategy's {@link UpdateRetrievalListener}s.
96
+     */
97
+    @AllArgsConstructor
98
+    private class DownloadProgressListener implements DownloadListener {
99
+
100
+        /** The component to fire updates for. */
101
+        private final UpdateComponent component;
102
+
103
+        /** {@inheritDoc} */
104
+        @Override
105
+        public void downloadProgress(final float percent) {
106
+            fireRetrievalProgressChanged(component, percent);
107
+        }
108
+
109
+        /** {@inheritDoc} */
110
+        @Override
111
+        public void setIndeterminate(final boolean indeterminate) {
112
+            // Do nothing
113
+        }
114
+
115
+    }
116
+
117
+}

src/com/dmdirc/updater/UpdateCheckerListener.java → src/com/dmdirc/updater/retrieving/SingleFileRetrievalResult.java View File

@@ -20,19 +20,21 @@
20 20
  * SOFTWARE.
21 21
  */
22 22
 
23
-package com.dmdirc.updater;
23
+package com.dmdirc.updater.retrieving;
24
+
25
+import java.io.File;
24 26
 
25 27
 /**
26
- * Update Checker Listeners receive notifications when the status of the update
27
- * checker changes.
28
+ * A specialised {@link UpdateRetrievalResult} which specifies a single file
29
+ * where the update is located.
28 30
  */
29
-public interface UpdateCheckerListener {
31
+public interface SingleFileRetrievalResult extends UpdateRetrievalResult {
30 32
 
31 33
     /**
32
-     * Called when the status of the update checker changes.
34
+     * Retrieves the location that the update is stored in.
33 35
      *
34
-     * @param newStatus The new status of the update checker
36
+     * @return The file containing the retrieved update
35 37
      */
36
-    void statusChanged(UpdateChecker.STATE newStatus);
38
+    File getFile();
37 39
 
38 40
 }

+ 67
- 0
src/com/dmdirc/updater/retrieving/TypeSensitiveRetrievalStrategy.java View File

@@ -0,0 +1,67 @@
1
+/*
2
+ * Copyright (c) 2006-2012 DMDirc Developers
3
+ *
4
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ * of this software and associated documentation files (the "Software"), to deal
6
+ * in the Software without restriction, including without limitation the rights
7
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ * copies of the Software, and to permit persons to whom the Software is
9
+ * furnished to do so, subject to the following conditions:
10
+ *
11
+ * The above copyright notice and this permission notice shall be included in
12
+ * all copies or substantial portions of the Software.
13
+ *
14
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ * SOFTWARE.
21
+ */
22
+
23
+package com.dmdirc.updater.retrieving;
24
+
25
+import com.dmdirc.updater.checking.UpdateCheckResult;
26
+
27
+import lombok.RequiredArgsConstructor;
28
+
29
+/**
30
+ * Base class for {@link UpdateRetrievalStategy} implementations that can
31
+ * handle a single subclass of {@link UpdateCheckResult}.
32
+ *
33
+ * @param <T> The type of result that this strategy can handle
34
+ */
35
+@RequiredArgsConstructor
36
+public abstract class TypeSensitiveRetrievalStrategy<T extends UpdateCheckResult>
37
+        implements UpdateRetrievalStategy {
38
+
39
+    /** The type of result that this strategy can handle. */
40
+    private final Class<T> clazz;
41
+
42
+    /** {@inheritDoc} */
43
+    @Override
44
+    public boolean canHandle(final UpdateCheckResult checkResult) {
45
+        return clazz.isAssignableFrom(checkResult.getClass());
46
+    }
47
+
48
+    /** {@inheritDoc} */
49
+    @Override
50
+    @SuppressWarnings("unchecked")
51
+    public UpdateRetrievalResult retrieve(final UpdateCheckResult checkResult) {
52
+        return retrieveImpl((T) checkResult);
53
+    }
54
+
55
+    /**
56
+     * Retrieves the update associated with the given {@link UpdateCheckResult}.
57
+     *
58
+     * @param checkResult The update check result describing the update
59
+     * requiring retrieval.
60
+     * @return An {@link UpdateRetrievalResult} which describes the result of
61
+     * the retrieval, providing information for an
62
+     * {@link UpdateInstallationStrategy} to use to install the update.
63
+     * @see #retrieve(com.dmdirc.updater.checking.UpdateCheckResult)
64
+     */
65
+    protected abstract UpdateRetrievalResult retrieveImpl(T checkResult);
66
+
67
+}

+ 55
- 0
src/com/dmdirc/updater/retrieving/UpdateRetrievalListener.java View File

@@ -0,0 +1,55 @@
1
+/*
2
+ * Copyright (c) 2006-2012 DMDirc Developers
3
+ *
4
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ * of this software and associated documentation files (the "Software"), to deal
6
+ * in the Software without restriction, including without limitation the rights
7
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ * copies of the Software, and to permit persons to whom the Software is
9
+ * furnished to do so, subject to the following conditions:
10
+ *
11
+ * The above copyright notice and this permission notice shall be included in
12
+ * all copies or substantial portions of the Software.
13
+ *
14
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ * SOFTWARE.
21
+ */
22
+
23
+package com.dmdirc.updater.retrieving;
24
+
25
+import com.dmdirc.updater.UpdateComponent;
26
+
27
+/**
28
+ * Interface for objects interested in the progress of a retrieval attempt
29
+ * performed by an {@link UpdateRetrievalStategy}.
30
+ */
31
+public interface UpdateRetrievalListener {
32
+
33
+    /**
34
+     * Called when the progress of a retrieval operation has changed.
35
+     *
36
+     * @param component The component being retrieved
37
+     * @param progress The percentage progress completed (0-100)
38
+     */
39
+    void retrievalProgressChanged(UpdateComponent component, double progress);
40
+
41
+    /**
42
+     * Called when a retrieval has failed.
43
+     *
44
+     * @param component The component that failed to install
45
+     */
46
+    void retrievalFailed(UpdateComponent component);
47
+
48
+    /**
49
+     * Called when a retrieval has finished.
50
+     *
51
+     * @param component The component that was successfully installed
52
+     */
53
+    void retrievalCompleted(UpdateComponent component);
54
+
55
+}

src/com/dmdirc/updater/UpdateListener.java → src/com/dmdirc/updater/retrieving/UpdateRetrievalResult.java View File

@@ -20,27 +20,28 @@
20 20
  * SOFTWARE.
21 21
  */
22 22
 
23
-package com.dmdirc.updater;
23
+package com.dmdirc.updater.retrieving;
24
+
25
+import com.dmdirc.updater.checking.UpdateCheckResult;
24 26
 
25 27
 /**
26
- * Defines the method that must be implemented by update listeners.
28
+ * Describes the result of a retrieval operation by an
29
+ * {@link UpdateRetrievalStategy}.
27 30
  */
28
-public interface UpdateListener {
31
+public interface UpdateRetrievalResult {
29 32
 
30 33
     /**
31
-     * Called when the state of the update has changed.
34
+     * Gets the check result which was used to perform the retrieval.
32 35
      *
33
-     * @param update The update in question
34
-     * @param status The new status of the update
36
+     * @return The check result used to perform the retrieval
35 37
      */
36
-    void updateStatusChange(Update update, UpdateStatus status);
38
+    UpdateCheckResult getCheckResult();
37 39
 
38 40
     /**
39
-     * Called when the progress of the current update state has changed.
41
+     * Determines whether the retrieval was successful or not.
40 42
      *
41
-     * @param update The update in question
42
-     * @param progress The progress (as a percentage) of the update
43
+     * @return True if the retrieval worked, false otherwise
43 44
      */
44
-    void updateProgressChange(Update update, float progress);
45
+    boolean isSuccessful();
45 46
 
46 47
 }

+ 70
- 0
src/com/dmdirc/updater/retrieving/UpdateRetrievalStategy.java View File

@@ -0,0 +1,70 @@
1
+/*
2
+ * Copyright (c) 2006-2012 DMDirc Developers
3
+ *
4
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ * of this software and associated documentation files (the "Software"), to deal
6
+ * in the Software without restriction, including without limitation the rights
7
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ * copies of the Software, and to permit persons to whom the Software is
9
+ * furnished to do so, subject to the following conditions:
10
+ *
11
+ * The above copyright notice and this permission notice shall be included in
12
+ * all copies or substantial portions of the Software.
13
+ *
14
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ * SOFTWARE.
21
+ */
22
+
23
+package com.dmdirc.updater.retrieving;
24
+
25
+import com.dmdirc.updater.checking.UpdateCheckResult;
26
+import com.dmdirc.updater.checking.UpdateCheckStrategy;
27
+import com.dmdirc.updater.installing.UpdateInstallationStrategy;
28
+
29
+/**
30
+ * Defines a strategy for retrieving updated files that have been identified by
31
+ * an {@link UpdateCheckStrategy}. Retrieval strategies will place updates in
32
+ * a temporary location on disk for use by an {@link UpdateInstallationStrategy}.
33
+ */
34
+public interface UpdateRetrievalStategy {
35
+
36
+    /**
37
+     * Determines whether this strategy can handle retrieving the given update.
38
+     *
39
+     * @param checkResult The update check result describing the update
40
+     * requiring retrieval.
41
+     * @return True if this strategy can handle the update; false otherwise
42
+     */
43
+    boolean canHandle(UpdateCheckResult checkResult);
44
+
45
+    /**
46
+     * Retrieves the update associated with the given {@link UpdateCheckResult}.
47
+     *
48
+     * @param checkResult The update check result describing the update
49
+     * requiring retrieval.
50
+     * @return An {@link UpdateRetrievalResult} which describes the result of
51
+     * the retrieval, providing information for an
52
+     * {@link UpdateInstallationStrategy} to use to install the update.
53
+     */
54
+    UpdateRetrievalResult retrieve(UpdateCheckResult checkResult);
55
+
56
+    /**
57
+     * Adds a new status listener to this strategy.
58
+     *
59
+     * @param listener The listener to be registered
60
+     */
61
+    void addUpdateRetrievalListener(UpdateRetrievalListener listener);
62
+
63
+    /**
64
+     * Removes the given listener from this strategy.
65
+     *
66
+     * @param listener The listener to be removed
67
+     */
68
+    void removeUpdateRetrievalListener(UpdateRetrievalListener listener);
69
+
70
+}

Loading…
Cancel
Save