]> git.gag.com Git - debian/openrocket/blob - src/net/sf/openrocket/communication/UpdateInfoRetriever.java
enhanced motor handling, bug fixes, initial optimization code
[debian/openrocket] / src / net / sf / openrocket / communication / UpdateInfoRetriever.java
1 package net.sf.openrocket.communication;
2
3 import java.io.BufferedReader;
4 import java.io.IOException;
5 import java.io.InputStream;
6 import java.io.InputStreamReader;
7 import java.io.Reader;
8 import java.net.HttpURLConnection;
9 import java.util.ArrayList;
10
11 import net.sf.openrocket.logging.LogHelper;
12 import net.sf.openrocket.startup.Application;
13 import net.sf.openrocket.util.ComparablePair;
14 import net.sf.openrocket.util.LimitedInputStream;
15 import net.sf.openrocket.util.Prefs;
16
17 public class UpdateInfoRetriever {
18         private static final LogHelper log = Application.getLogger();
19         
20         private UpdateInfoFetcher fetcher = null;
21         
22         
23         /**
24          * Start an asynchronous task that will fetch information about the latest
25          * OpenRocket version.  This will overwrite any previous fetching operation.
26          * This call will return immediately.
27          */
28         public void start() {
29                 fetcher = new UpdateInfoFetcher();
30                 fetcher.setDaemon(true);
31                 fetcher.start();
32         }
33         
34         
35         /**
36          * Check whether the update info fetching is still in progress.
37          * 
38          * @return      <code>true</code> if the communication is still in progress.
39          */
40         public boolean isRunning() {
41                 if (fetcher == null) {
42                         throw new IllegalStateException("startFetchUpdateInfo() has not been called");
43                 }
44                 return fetcher.isAlive();
45         }
46         
47         
48         /**
49          * Retrieve the result of the background update info fetcher.  This method returns 
50          * the result of the previous call to {@link #start()}. It must be
51          * called before calling this method.
52          * <p>
53          * This method will return <code>null</code> if the info fetcher is still running or
54          * if it encountered a problem in communicating with the server.  The difference can
55          * be checked using {@link #isRunning()}.
56          * 
57          * @return      the update result, or <code>null</code> if the fetching is still in progress
58          *                      or an error occurred while communicating with the server.
59          * @throws      IllegalStateException   if {@link #start()} has not been called.
60          */
61         public UpdateInfo getUpdateInfo() {
62                 if (fetcher == null) {
63                         throw new IllegalStateException("start() has not been called");
64                 }
65                 return fetcher.info;
66         }
67         
68         
69
70         /**
71          * Parse the data received from the server.
72          * 
73          * @param r             the Reader from which to read.
74          * @return              an UpdateInfo construct, or <code>null</code> if the data was invalid.
75          * @throws IOException  if an I/O exception occurs.
76          */
77         /* package-private */
78         static UpdateInfo parseUpdateInput(Reader r) throws IOException {
79                 BufferedReader reader;
80                 if (r instanceof BufferedReader) {
81                         reader = (BufferedReader) r;
82                 } else {
83                         reader = new BufferedReader(r);
84                 }
85                 
86
87                 String version = null;
88                 ArrayList<ComparablePair<Integer, String>> updates =
89                                 new ArrayList<ComparablePair<Integer, String>>();
90                 
91                 String str = reader.readLine();
92                 while (str != null) {
93                         if (str.matches("^Version: *[0-9]+\\.[0-9]+\\.[0-9]+[a-zA-Z0-9.-]* *$")) {
94                                 version = str.substring(8).trim();
95                         } else if (str.matches("^[0-9]+:\\p{Print}+$")) {
96                                 int index = str.indexOf(':');
97                                 int value = Integer.parseInt(str.substring(0, index));
98                                 String desc = str.substring(index + 1).trim();
99                                 if (!desc.equals("")) {
100                                         updates.add(new ComparablePair<Integer, String>(value, desc));
101                                 }
102                         }
103                         // Ignore anything else
104                         str = reader.readLine();
105                 }
106                 
107                 if (version != null) {
108                         return new UpdateInfo(version, updates);
109                 } else {
110                         return null;
111                 }
112         }
113         
114         
115
116         /**
117          * An asynchronous task that fetches and parses the update info.
118          * 
119          * @author Sampo Niskanen <sampo.niskanen@iki.fi>
120          */
121         private class UpdateInfoFetcher extends Thread {
122                 
123                 private volatile UpdateInfo info = null;
124                 
125                 @Override
126                 public void run() {
127                         try {
128                                 doConnection();
129                         } catch (IOException e) {
130                                 log.info("Fetching update failed: " + e);
131                                 return;
132                         }
133                 }
134                 
135                 
136                 private void doConnection() throws IOException {
137                         String url = Communicator.UPDATE_INFO_URL + "?" + Communicator.VERSION_PARAM + "="
138                                         + Communicator.encode(Prefs.getVersion());
139                         
140                         HttpURLConnection connection = Communicator.connectionSource.getConnection(url);
141                         
142                         connection.setConnectTimeout(Communicator.CONNECTION_TIMEOUT);
143                         connection.setInstanceFollowRedirects(true);
144                         connection.setRequestMethod("GET");
145                         connection.setUseCaches(false);
146                         connection.setDoInput(true);
147                         connection.setRequestProperty("X-OpenRocket-Version",
148                                         Communicator.encode(Prefs.getVersion() + " " + Prefs.getBuildSource()));
149                         connection.setRequestProperty("X-OpenRocket-ID",
150                                         Communicator.encode(Prefs.getUniqueID()));
151                         connection.setRequestProperty("X-OpenRocket-OS",
152                                         Communicator.encode(System.getProperty("os.name") + " " +
153                                                         System.getProperty("os.arch")));
154                         connection.setRequestProperty("X-OpenRocket-Java",
155                                         Communicator.encode(System.getProperty("java.vendor") + " " +
156                                                         System.getProperty("java.version")));
157                         connection.setRequestProperty("X-OpenRocket-Country",
158                                         Communicator.encode(System.getProperty("user.country") + " " +
159                                                         System.getProperty("user.timezone")));
160                         connection.setRequestProperty("X-OpenRocket-CPUs", "" + Runtime.getRuntime().availableProcessors());
161                         
162                         InputStream is = null;
163                         try {
164                                 connection.connect();
165                                 
166                                 log.debug("Update response code: " + connection.getResponseCode());
167                                 
168                                 if (connection.getResponseCode() == Communicator.UPDATE_INFO_NO_UPDATE_CODE) {
169                                         // No updates are available
170                                         log.info("No updates available");
171                                         info = new UpdateInfo();
172                                         return;
173                                 }
174                                 
175                                 if (connection.getResponseCode() != Communicator.UPDATE_INFO_UPDATE_AVAILABLE) {
176                                         // Error communicating with server
177                                         log.warn("Unknown server response code: " + connection.getResponseCode());
178                                         return;
179                                 }
180                                 
181                                 String contentType = connection.getContentType();
182                                 if (contentType == null ||
183                                                 contentType.toLowerCase().indexOf(Communicator.UPDATE_INFO_CONTENT_TYPE) < 0) {
184                                         // Unknown response type
185                                         log.warn("Unknown Content-type received:" + contentType);
186                                         return;
187                                 }
188                                 
189                                 // Update is available, parse input
190                                 is = connection.getInputStream();
191                                 is = new LimitedInputStream(is, Communicator.MAX_INPUT_BYTES);
192                                 String encoding = connection.getContentEncoding();
193                                 if (encoding == null || encoding.equals(""))
194                                         encoding = "UTF-8";
195                                 BufferedReader reader = new BufferedReader(new InputStreamReader(is, encoding));
196                                 
197                                 String version = null;
198                                 ArrayList<ComparablePair<Integer, String>> updates =
199                                                 new ArrayList<ComparablePair<Integer, String>>();
200                                 
201                                 String line = reader.readLine();
202                                 while (line != null) {
203                                         
204                                         if (line.matches("^Version:[a-zA-Z0-9._ -]{1,30}$")) {
205                                                 version = line.substring(8).trim();
206                                         } else if (line.matches("^[0-9]{1,9}:\\P{Cntrl}{1,300}$")) {
207                                                 String[] split = line.split(":", 2);
208                                                 int n = Integer.parseInt(split[0]);
209                                                 updates.add(new ComparablePair<Integer, String>(n, split[1].trim()));
210                                         }
211                                         // Ignore line otherwise
212                                         line = reader.readLine();
213                                 }
214                                 
215                                 // Check version input
216                                 if (version == null || version.length() == 0 ||
217                                                 version.equalsIgnoreCase(Prefs.getVersion())) {
218                                         // Invalid response
219                                         log.warn("Invalid version received, ignoring.");
220                                         return;
221                                 }
222                                 
223
224                                 info = new UpdateInfo(version, updates);
225                                 log.info("Found update: " + info);
226                         } finally {
227                                 try {
228                                         if (is != null)
229                                                 is.close();
230                                         connection.disconnect();
231                                 } catch (Exception e) {
232                                         e.printStackTrace();
233                                 }
234                         }
235                 }
236         }
237 }