2 * Sone - UpdateChecker.java - Copyright © 2011 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 3 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, see <http://www.gnu.org/licenses/>.
18 package net.pterodactylus.sone.core;
20 import java.io.IOException;
21 import java.io.InputStream;
22 import java.io.InputStreamReader;
23 import java.net.MalformedURLException;
24 import java.util.Date;
25 import java.util.Properties;
26 import java.util.logging.Level;
27 import java.util.logging.Logger;
29 import net.pterodactylus.sone.main.SonePlugin;
30 import net.pterodactylus.util.collection.Pair;
31 import net.pterodactylus.util.io.Closer;
32 import net.pterodactylus.util.logging.Logging;
33 import net.pterodactylus.util.version.Version;
34 import freenet.client.FetchResult;
35 import freenet.keys.FreenetURI;
36 import freenet.support.api.Bucket;
39 * Watches the official Sone homepage for new releases.
41 * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
43 public class UpdateChecker {
46 private static final Logger logger = Logging.getLogger(UpdateChecker.class);
48 /** The key of the Sone homepage. */
49 private static final String SONE_HOMEPAGE = "USK@nwa8lHa271k2QvJ8aa0Ov7IHAV-DFOCFgmDt3X6BpCI,DuQSUZiI~agF8c-6tjsFFGuZ8eICrzWCILB60nT8KKo,AQACAAE/sone/";
51 /** The current latest known edition. */
52 private static final int LATEST_EDITION = 23;
54 /** The Freenet interface. */
55 private final FreenetInterface freenetInterface;
57 /** The update listener manager. */
58 private final UpdateListenerManager updateListenerManager = new UpdateListenerManager();
60 /** The current URI of the homepage. */
61 private FreenetURI currentUri;
63 /** The current latest known version. */
64 private Version currentLatestVersion = SonePlugin.VERSION;
66 /** The release date of the latest version. */
67 private long latestVersionDate;
70 * Creates a new update checker.
72 * @param freenetInterface
73 * The freenet interface to use
75 public UpdateChecker(FreenetInterface freenetInterface) {
76 this.freenetInterface = freenetInterface;
80 // EVENT LISTENER MANAGEMENT
84 * Adds the given listener to the list of registered listeners.
86 * @param updateListener
89 public void addUpdateListener(UpdateListener updateListener) {
90 updateListenerManager.addListener(updateListener);
94 * Removes the given listener from the list of registered listeners.
96 * @param updateListener
97 * The listener to remove
99 public void removeUpdateListener(UpdateListener updateListener) {
100 updateListenerManager.removeListener(updateListener);
108 * Returns whether a version that is later than the currently running
109 * version has been found.
111 * @return {@code true} if a new version was found
113 public boolean hasLatestVersion() {
114 return currentLatestVersion.compareTo(SonePlugin.VERSION) > 0;
118 * Returns the latest version. If no new latest version has been found, the
119 * current version is returned.
121 * @return The latest known version
123 public Version getLatestVersion() {
124 return currentLatestVersion;
128 * Returns the release time of the latest version. If no new latest version
129 * has been found, the returned value is undefined.
131 * @return The release time of the latest version, if a new version was
134 public long getLatestVersionDate() {
135 return latestVersionDate;
143 * Starts the update checker.
145 public void start() {
147 currentUri = new FreenetURI(SONE_HOMEPAGE + LATEST_EDITION);
148 } catch (MalformedURLException mue1) {
149 /* this can not really happen unless I screw up. */
150 logger.log(Level.SEVERE, "Sone Homepage URI invalid!", mue1);
152 freenetInterface.registerUsk(currentUri, new FreenetInterface.Callback() {
155 @SuppressWarnings("synthetic-access")
156 public void editionFound(FreenetURI uri, long edition, boolean newKnownGood, boolean newSlot) {
157 logger.log(Level.FINEST, "Found update for %s: %d, %s, %s", new Object[] { uri, edition, newKnownGood, newSlot });
158 if (newKnownGood || newSlot) {
159 Pair<FreenetURI, FetchResult> uriResult = freenetInterface.fetchUri(uri.setMetaString(new String[] { "sone.properties" }));
160 if (uriResult == null) {
161 logger.log(Level.WARNING, "Could not fetch properties of latest homepage: %s", uri);
164 Bucket resultBucket = uriResult.getRight().asBucket();
166 parseProperties(resultBucket.getInputStream());
167 } catch (IOException ioe1) {
168 logger.log(Level.WARNING, "Could not parse sone.properties of " + uri, ioe1);
178 * Stops the update checker.
181 freenetInterface.unregisterUsk(currentUri);
189 * Parses the properties of the latest version and fires events, if
192 * @see UpdateListener#updateFound(Version, long)
193 * @see UpdateListenerManager#fireUpdateFound(Version, long)
194 * @param propertiesInputStream
195 * The input stream to parse
196 * @throws IOException
197 * if an I/O error occured
199 private void parseProperties(InputStream propertiesInputStream) throws IOException {
200 Properties properties = new Properties();
201 InputStreamReader inputStreamReader = null;
203 inputStreamReader = new InputStreamReader(propertiesInputStream, "UTF-8");
204 properties.load(inputStreamReader);
206 Closer.close(inputStreamReader);
208 String versionString = properties.getProperty("CurrentVersion/Version");
209 String releaseTimeString = properties.getProperty("CurrentVersion/ReleaseTime");
210 if ((versionString == null) || (releaseTimeString == null)) {
211 logger.log(Level.INFO, "Invalid data parsed from properties.");
214 Version version = Version.parse(versionString);
215 long releaseTime = 0;
217 releaseTime = Long.parseLong(releaseTimeString);
218 } catch (NumberFormatException nfe1) {
221 if ((version == null) || (releaseTime == 0)) {
222 logger.log(Level.INFO, "Could not parse data from properties.");
225 if (version.compareTo(currentLatestVersion) > 0) {
226 currentLatestVersion = version;
227 latestVersionDate = releaseTime;
228 logger.log(Level.INFO, "Found new version: %s (%tc)", new Object[] { version, new Date(releaseTime) });
229 updateListenerManager.fireUpdateFound(version, releaseTime);