Treat an empty index file as no index file.
[jSite.git] / src / de / todesbaum / jsite / gui / UpdateChecker.java
1 /*
2  * jSite-remote - UpdateChecker.java -
3  * Copyright © 2008 David Roden
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18  */
19
20 package de.todesbaum.jsite.gui;
21
22 import java.io.IOException;
23 import java.io.InputStream;
24 import java.util.ArrayList;
25 import java.util.List;
26 import java.util.Properties;
27
28 import de.todesbaum.jsite.application.Freenet7Interface;
29 import de.todesbaum.jsite.main.Main;
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;
38 import de.todesbaum.util.io.Closer;
39
40 /**
41  * Checks for newer versions of jSite.
42  *
43  * @author David ‘Bombe’ Roden <bombe@freenetproject.org>
44  */
45 public class UpdateChecker implements Runnable {
46
47         /** Counter for connection names. */
48         private static int counter = 0;
49
50         /** The edition for the update check URL. */
51         private static final int UPDATE_EDITION = 0;
52
53         /** The URL for update checks. */
54         private static final String UPDATE_KEY = "USK@e3myoFyp5avg6WYN16ImHri6J7Nj8980Fm~aQe4EX1U,QvbWT0ImE0TwLODTl7EoJx2NBnwDxTbLTE6zkB-eGPs,AQACAAE";
55
56         /** Object used for synchronization. */
57         private final Object syncObject = new Object();
58
59         /** Update listeners. */
60         private final List<UpdateListener> updateListeners = new ArrayList<UpdateListener>();
61
62         /** Whether the main thread should stop. */
63         private boolean shouldStop = false;
64
65         /** Current last found edition of update key. */
66         private int lastUpdateEdition = UPDATE_EDITION;
67
68         /** Last found version. */
69         private Version lastVersion = Main.getVersion();
70
71         /** The freenet interface. */
72         private final Freenet7Interface freenetInterface;
73
74         /**
75          * Creates a new update checker that uses the given frame as its parent and
76          * communications via the given freenet interface.
77          *
78          * @param freenetInterface
79          *            The freenet interface
80          */
81         public UpdateChecker(Freenet7Interface freenetInterface) {
82                 this.freenetInterface = freenetInterface;
83         }
84
85         //
86         // EVENT LISTENER MANAGEMENT
87         //
88
89         /**
90          * Adds an update listener to the list of registered listeners.
91          *
92          * @param updateListener
93          *            The update listener to add
94          */
95         public void addUpdateListener(UpdateListener updateListener) {
96                 updateListeners.add(updateListener);
97         }
98
99         /**
100          * Removes the given listener from the list of registered listeners.
101          *
102          * @param updateListener
103          *            The update listener to remove
104          */
105         public void removeUpdateListener(UpdateListener updateListener) {
106                 updateListeners.remove(updateListener);
107         }
108
109         /**
110          * Notifies all listeners that a version was found.
111          *
112          * @param foundVersion
113          *            The version that was found
114          * @param versionTimestamp
115          *            The timestamp of the version
116          */
117         protected void fireUpdateFound(Version foundVersion, long versionTimestamp) {
118                 for (UpdateListener updateListener : updateListeners) {
119                         updateListener.foundUpdateData(foundVersion, versionTimestamp);
120                 }
121         }
122
123         //
124         // ACCESSORS
125         //
126
127         /**
128          * Returns the latest version that was found.
129          *
130          * @return The latest found version
131          */
132         public Version getLatestVersion() {
133                 return lastVersion;
134         }
135
136         //
137         // ACTIONS
138         //
139
140         /**
141          * Starts the update checker.
142          */
143         public void start() {
144                 new Thread(this).start();
145         }
146
147         /**
148          * Stops the update checker.
149          */
150         public void stop() {
151                 synchronized (syncObject) {
152                         shouldStop = true;
153                         syncObject.notifyAll();
154                 }
155         }
156
157         //
158         // PRIVATE METHODS
159         //
160
161         /**
162          * Returns whether the update checker should stop.
163          *
164          * @return <code>true</code> if the update checker should stop,
165          *         <code>false</code> otherwise
166          */
167         private boolean shouldStop() {
168                 synchronized (syncObject) {
169                         return shouldStop;
170                 }
171         }
172
173         /**
174          * Creates the URI of the update file for the given edition.
175          *
176          * @param edition
177          *            The edition number
178          * @return The URI for the update file for the given edition
179          */
180         private String constructUpdateKey(int edition) {
181                 return UPDATE_KEY + "/jSite/" + edition + "/jSite.properties";
182         }
183
184         //
185         // INTERFACE Runnable
186         //
187
188         /**
189          * {@inheritDoc}
190          */
191         public void run() {
192                 Connection connection = freenetInterface.getConnection("jSite-" + ++counter + "-UpdateChecker");
193                 try {
194                         connection.connect();
195                 } catch (IOException e1) {
196                         e1.printStackTrace();
197                 }
198                 Client client = new Client(connection);
199                 boolean checkNow = false;
200                 int currentEdition = lastUpdateEdition;
201                 while (!shouldStop()) {
202                         checkNow = false;
203                         System.out.println("Trying " + constructUpdateKey(currentEdition));
204                         ClientGet clientGet = new ClientGet("get-update-key");
205                         clientGet.setUri(constructUpdateKey(currentEdition));
206                         clientGet.setPersistence(Persistence.CONNECTION);
207                         clientGet.setReturnType(ReturnType.direct);
208                         clientGet.setVerbosity(Verbosity.ALL);
209                         try {
210                                 client.execute(clientGet);
211                                 boolean stop = false;
212                                 while (!stop) {
213                                         Message message = client.readMessage();
214                                         System.out.println(message);
215                                         if ("GetFailed".equals(message.getName())) {
216                                                 if ("27".equals(message.get("code"))) {
217                                                         String editionString = message.get("redirecturi").split("/")[2];
218                                                         int editionNumber = -1;
219                                                         try {
220                                                                 editionNumber = Integer.parseInt(editionString);
221                                                         } catch (NumberFormatException nfe1) {
222                                                                 /* ignore. */
223                                                         }
224                                                         if (editionNumber != -1) {
225                                                                 System.out.println("Found new edition " + editionNumber);
226                                                                 currentEdition = editionNumber;
227                                                                 lastUpdateEdition = editionNumber;
228                                                                 checkNow = true;
229                                                                 break;
230                                                         }
231                                                 }
232                                         }
233                                         if ("AllData".equals(message.getName())) {
234                                                 System.out.println("Update data found.");
235                                                 InputStream dataInputStream = null;
236                                                 Properties properties = new Properties();
237                                                 try {
238                                                         dataInputStream = message.getPayloadInputStream();
239                                                         properties.load(dataInputStream);
240                                                 } finally {
241                                                         Closer.close(dataInputStream);
242                                                 }
243
244                                                 String foundVersionString = properties.getProperty("jSite.Version");
245                                                 if (foundVersionString != null) {
246                                                         Version foundVersion = Version.parse(foundVersionString);
247                                                         if (foundVersion != null) {
248                                                                 lastVersion = foundVersion;
249                                                                 String versionTimestampString = properties.getProperty("jSite.Date");
250                                                                 System.out.println(versionTimestampString);
251                                                                 long versionTimestamp = -1;
252                                                                 try {
253                                                                         versionTimestamp = Long.parseLong(versionTimestampString);
254                                                                 } catch (NumberFormatException nfe1) {
255                                                                         /* ignore. */
256                                                                 }
257                                                                 fireUpdateFound(foundVersion, versionTimestamp);
258                                                                 stop = true;
259                                                                 checkNow = true;
260                                                                 ++currentEdition;
261                                                         }
262                                                 }
263                                         }
264                                 }
265                         } catch (IOException e) {
266                                 System.out.println("Got IOException: " + e.getMessage());
267                                 e.printStackTrace();
268                         }
269                         if (!checkNow && !shouldStop()) {
270                                 synchronized (syncObject) {
271                                         try {
272                                                 syncObject.wait(15 * 60 * 1000);
273                                         } catch (InterruptedException ie1) {
274                                                 /* ignore. */
275                                         }
276                                 }
277                         }
278                 }
279         }
280
281 }