2 * jSite - UpdateChecker.java - Copyright © 2008–2014 David Roden
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19 package de.todesbaum.jsite.application;
21 import java.io.IOException;
22 import java.io.InputStream;
23 import java.util.ArrayList;
24 import java.util.List;
25 import java.util.Properties;
26 import java.util.logging.Level;
27 import java.util.logging.Logger;
29 import net.pterodactylus.util.io.Closer;
30 import de.todesbaum.jsite.main.Version;
31 import de.todesbaum.util.freenet.fcp2.Client;
32 import de.todesbaum.util.freenet.fcp2.ClientGet;
33 import de.todesbaum.util.freenet.fcp2.Connection;
34 import de.todesbaum.util.freenet.fcp2.Message;
35 import de.todesbaum.util.freenet.fcp2.Persistence;
36 import de.todesbaum.util.freenet.fcp2.ReturnType;
37 import de.todesbaum.util.freenet.fcp2.Verbosity;
40 * Checks for newer versions of jSite.
42 * @author David ‘Bombe’ Roden <bombe@freenetproject.org>
44 public class UpdateChecker implements Runnable {
47 private static final Logger logger = Logger.getLogger(UpdateChecker.class.getName());
49 /** Counter for connection names. */
50 private static int counter = 0;
52 /** The edition for the update check URL. */
53 private static final int UPDATE_EDITION = 7;
55 /** The URL for update checks. */
56 private static final String UPDATE_KEY = "USK@1waTsw46L9-JEQ8yX1khjkfHcn--g0MlMsTlYHax9zQ,oYyxr5jyFnaTsVGDQWk9e3ddOWGKnqEASxAk08MHT2Y,AQACAAE";
58 /** Object used for synchronization. */
59 private final Object syncObject = new Object();
61 /** Update listeners. */
62 private final List<UpdateListener> updateListeners = new ArrayList<UpdateListener>();
64 /** Whether the main thread should stop. */
65 private boolean shouldStop = false;
67 /** Current last found edition of update key. */
68 private int lastUpdateEdition = UPDATE_EDITION;
70 /** Last found version. */
71 private Version lastVersion;
73 /** The freenet interface. */
74 private final Freenet7Interface freenetInterface;
77 * Creates a new update checker that uses the given frame as its parent and
78 * communications via the given freenet interface.
80 * @param freenetInterface
81 * The freenet interface
83 public UpdateChecker(Freenet7Interface freenetInterface, Version currentVersion) {
84 this.freenetInterface = freenetInterface;
85 this.lastVersion = currentVersion;
89 // EVENT LISTENER MANAGEMENT
93 * Adds an update listener to the list of registered listeners.
95 * @param updateListener
96 * The update listener to add
98 public void addUpdateListener(UpdateListener updateListener) {
99 updateListeners.add(updateListener);
103 * Removes the given listener from the list of registered listeners.
105 * @param updateListener
106 * The update listener to remove
108 public void removeUpdateListener(UpdateListener updateListener) {
109 updateListeners.remove(updateListener);
113 * Notifies all listeners that a version was found.
115 * @param foundVersion
116 * The version that was found
117 * @param versionTimestamp
118 * The timestamp of the version
120 protected void fireUpdateFound(Version foundVersion, long versionTimestamp) {
121 for (UpdateListener updateListener : updateListeners) {
122 updateListener.foundUpdateData(foundVersion, versionTimestamp);
131 * Returns the latest version that was found.
133 * @return The latest found version
135 public Version getLatestVersion() {
144 * Starts the update checker.
146 public void start() {
147 new Thread(this).start();
151 * Stops the update checker.
154 synchronized (syncObject) {
156 syncObject.notifyAll();
165 * Returns whether the update checker should stop.
167 * @return <code>true</code> if the update checker should stop,
168 * <code>false</code> otherwise
170 private boolean shouldStop() {
171 synchronized (syncObject) {
177 * Creates the URI of the update file for the given edition.
181 * @return The URI for the update file for the given edition
183 private static String constructUpdateKey(int edition) {
184 return UPDATE_KEY + "/jSite/" + edition + "/jSite.properties";
188 // INTERFACE Runnable
196 int currentEdition = lastUpdateEdition;
197 while (!shouldStop()) {
199 /* try to connect. */
202 Connection connection = freenetInterface.getConnection("jSite-" + ++counter + "-UpdateChecker");
204 connection.connect();
205 logger.log(Level.INFO, "Connected to " + freenetInterface.getNode() + ".");
206 client = new Client(connection);
208 } catch (IOException ioe1) {
209 logger.log(Level.INFO, "Could not connect to " + freenetInterface.getNode() + ".", ioe1);
211 if (!connection.isConnected()) {
213 Thread.sleep(60 * 1000);
214 } catch (InterruptedException ie1) {
215 /* ignore, we’re looping. */
220 boolean checkNow = false;
221 logger.log(Level.FINE, "Trying " + constructUpdateKey(currentEdition));
222 ClientGet clientGet = new ClientGet("get-update-key");
223 clientGet.setUri(constructUpdateKey(currentEdition));
224 clientGet.setPersistence(Persistence.CONNECTION);
225 clientGet.setReturnType(ReturnType.direct);
226 clientGet.setVerbosity(Verbosity.ALL);
228 client.execute(clientGet);
229 boolean stop = false;
231 Message message = client.readMessage();
232 logger.log(Level.FINEST, "Received message: " + message);
233 if (message == null) {
236 if ("GetFailed".equals(message.getName())) {
237 if ("27".equals(message.get("code"))) {
238 String editionString = message.get("redirecturi").split("/")[2];
239 int editionNumber = -1;
241 editionNumber = Integer.parseInt(editionString);
242 } catch (NumberFormatException nfe1) {
245 if (editionNumber != -1) {
246 logger.log(Level.INFO, "Found new edition " + editionNumber);
247 currentEdition = editionNumber;
248 lastUpdateEdition = editionNumber;
254 if ("AllData".equals(message.getName())) {
255 logger.log(Level.FINE, "Update data found.");
256 InputStream dataInputStream = null;
257 Properties properties = new Properties();
259 dataInputStream = message.getPayloadInputStream();
260 properties.load(dataInputStream);
262 Closer.close(dataInputStream);
265 String foundVersionString = properties.getProperty("jSite.Version");
266 if (foundVersionString != null) {
267 Version foundVersion = Version.parse(foundVersionString);
268 if (foundVersion != null) {
269 lastVersion = foundVersion;
270 String versionTimestampString = properties.getProperty("jSite.Date");
271 logger.log(Level.FINEST, "Version timestamp: " + versionTimestampString);
272 long versionTimestamp = -1;
274 versionTimestamp = Long.parseLong(versionTimestampString);
275 } catch (NumberFormatException nfe1) {
278 fireUpdateFound(foundVersion, versionTimestamp);
286 } catch (IOException e) {
287 logger.log(Level.INFO, "Got IOException: " + e.getMessage());
290 if (!checkNow && !shouldStop()) {
291 synchronized (syncObject) {
293 syncObject.wait(15 * 60 * 1000);
294 } catch (InterruptedException ie1) {