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