1 package net.sf.openrocket.communication;
3 import java.io.BufferedReader;
4 import java.io.IOException;
5 import java.io.InputStream;
6 import java.io.InputStreamReader;
8 import java.net.HttpURLConnection;
9 import java.util.ArrayList;
11 import net.sf.openrocket.util.ComparablePair;
12 import net.sf.openrocket.util.LimitedInputStream;
13 import net.sf.openrocket.util.Prefs;
15 public class UpdateInfoRetriever {
17 private UpdateInfoFetcher fetcher = null;
21 * Start an asynchronous task that will fetch information about the latest
22 * OpenRocket version. This will overwrite any previous fetching operation.
23 * This call will return immediately.
26 fetcher = new UpdateInfoFetcher();
27 fetcher.setDaemon(true);
33 * Check whether the update info fetching is still in progress.
35 * @return <code>true</code> if the communication is still in progress.
37 public boolean isRunning() {
38 if (fetcher == null) {
39 throw new IllegalStateException("startFetchUpdateInfo() has not been called");
41 return fetcher.isAlive();
46 * Retrieve the result of the background update info fetcher. This method returns
47 * the result of the previous call to {@link #start()}. It must be
48 * called before calling this method.
50 * This method will return <code>null</code> if the info fetcher is still running or
51 * if it encountered a problem in communicating with the server. The difference can
52 * be checked using {@link #isRunning()}.
54 * @return the update result, or <code>null</code> if the fetching is still in progress
55 * or an error occurred while communicating with the server.
56 * @throws IllegalStateException if {@link #start()} has not been called.
58 public UpdateInfo getUpdateInfo() {
59 if (fetcher == null) {
60 throw new IllegalStateException("start() has not been called");
68 * Parse the data received from the server.
70 * @param r the Reader from which to read.
71 * @return an UpdateInfo construct, or <code>null</code> if the data was invalid.
72 * @throws IOException if an I/O exception occurs.
75 static UpdateInfo parseUpdateInput(Reader r) throws IOException {
76 BufferedReader reader;
77 if (r instanceof BufferedReader) {
78 reader = (BufferedReader)r;
80 reader = new BufferedReader(r);
84 String version = null;
85 ArrayList<ComparablePair<Integer,String>> updates =
86 new ArrayList<ComparablePair<Integer,String>>();
88 String str = reader.readLine();
90 if (str.matches("^Version: *[0-9]+\\.[0-9]+\\.[0-9]+[a-zA-Z0-9.-]* *$")) {
91 version = str.substring(8).trim();
92 } else if (str.matches("^[0-9]+:\\p{Print}+$")) {
93 int index = str.indexOf(':');
94 int value = Integer.parseInt(str.substring(0, index));
95 String desc = str.substring(index+1).trim();
96 if (!desc.equals("")) {
97 updates.add(new ComparablePair<Integer,String>(value, desc));
100 // Ignore anything else
101 str = reader.readLine();
104 if (version != null) {
105 return new UpdateInfo(version, updates);
114 * An asynchronous task that fetches and parses the update info.
116 * @author Sampo Niskanen <sampo.niskanen@iki.fi>
118 private class UpdateInfoFetcher extends Thread {
120 private volatile UpdateInfo info = null;
126 } catch (IOException e) {
127 System.out.println("fetching update failed: " + e);
133 private void doConnection() throws IOException {
134 String url = Communicator.UPDATE_INFO_URL + "?" + Communicator.VERSION_PARAM + "="
135 + Communicator.encode(Prefs.getVersion());
137 HttpURLConnection connection = Communicator.connectionSource.getConnection(url);
139 connection.setConnectTimeout(Communicator.CONNECTION_TIMEOUT);
140 connection.setInstanceFollowRedirects(true);
141 connection.setRequestMethod("GET");
142 connection.setUseCaches(false);
143 connection.setDoInput(true);
144 connection.setRequestProperty("X-OpenRocket-Version",
145 Communicator.encode(Prefs.getVersion()));
146 connection.setRequestProperty("X-OpenRocket-ID",
147 Communicator.encode(Prefs.getUniqueID()));
148 connection.setRequestProperty("X-OpenRocket-OS",
149 Communicator.encode(System.getProperty("os.name") + " " +
150 System.getProperty("os.arch")));
151 connection.setRequestProperty("X-OpenRocket-Java",
152 Communicator.encode(System.getProperty("java.vendor") + " " +
153 System.getProperty("java.version")));
154 connection.setRequestProperty("X-OpenRocket-Country",
155 Communicator.encode(System.getProperty("user.country") + " " +
156 Communicator.encode(System.getProperty("user.timezone"))));
158 InputStream is = null;
160 connection.connect();
162 System.out.println("response code: " + connection.getResponseCode());
164 if (connection.getResponseCode() == Communicator.UPDATE_INFO_NO_UPDATE_CODE) {
165 // No updates are available
166 info = new UpdateInfo();
170 if (connection.getResponseCode() != Communicator.UPDATE_INFO_UPDATE_AVAILABLE) {
171 // Error communicating with server
175 if (!Communicator.UPDATE_INFO_CONTENT_TYPE.equalsIgnoreCase(
176 connection.getContentType())) {
177 // Unknown response type
182 // Update is available, parse input
183 is = connection.getInputStream();
184 is = new LimitedInputStream(is, Communicator.MAX_INPUT_BYTES);
185 String encoding = connection.getContentEncoding();
186 if (encoding == null || encoding.equals(""))
188 BufferedReader reader = new BufferedReader(new InputStreamReader(is, encoding));
190 String version = null;
191 ArrayList<ComparablePair<Integer, String>> updates =
192 new ArrayList<ComparablePair<Integer, String>>();
194 String line = reader.readLine();
195 while (line != null) {
197 if (line.matches("^Version:[a-zA-Z0-9._ -]{1,30}$")) {
198 version = line.substring(8).trim();
199 } else if (line.matches("^[0-9]{1,9}:\\P{Cntrl}{1,300}$")) {
200 String[] split = line.split(":", 2);
201 int n = Integer.parseInt(split[0]);
202 updates.add(new ComparablePair<Integer,String>(n, split[1].trim()));
204 // Ignore line otherwise
205 line = reader.readLine();
208 // Check version input
209 if (version == null || version.length() == 0 ||
210 version.equalsIgnoreCase(Prefs.getVersion())) {
216 info = new UpdateInfo(version, updates);
222 connection.disconnect();
223 } catch (Exception e) {