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;
7 import java.io.OutputStreamWriter;
9 import java.io.UnsupportedEncodingException;
10 import java.net.HttpURLConnection;
12 import java.net.URLEncoder;
13 import java.util.ArrayList;
15 import net.sf.openrocket.util.ComparablePair;
16 import net.sf.openrocket.util.Prefs;
18 public class Communication {
20 private static final String BUG_REPORT_URL =
21 "http://openrocket.sourceforge.net/actions/reportbug";
22 private static final String UPDATE_INFO_URL =
23 "http://openrocket.sourceforge.net/actions/updates";
25 private static final String VERSION_PARAM = "version";
28 private static final String BUG_REPORT_PARAM = "content";
29 private static final int BUG_REPORT_RESPONSE_CODE = HttpURLConnection.HTTP_ACCEPTED;
30 private static final int CONNECTION_TIMEOUT = 10000; // in milliseconds
32 private static final int UPDATE_INFO_UPDATE_AVAILABLE = HttpURLConnection.HTTP_OK;
33 private static final int UPDATE_INFO_NO_UPDATE_CODE = HttpURLConnection.HTTP_NO_CONTENT;
34 private static final String UPDATE_INFO_CONTENT_TYPE = "text/plain";
37 private static UpdateInfoFetcher fetcher = null;
41 * Send the provided report to the OpenRocket bug report URL. If the connection
42 * fails or the server does not respond with the correct response code, an
43 * exception is thrown.
45 * @param report the report to send.
46 * @throws IOException if an error occurs while connecting to the server or
47 * the server responds with a wrong response code.
49 public static void sendBugReport(String report) throws IOException {
50 URL url = new URL(BUG_REPORT_URL);
52 HttpURLConnection connection = (HttpURLConnection) url.openConnection();
54 connection.setConnectTimeout(CONNECTION_TIMEOUT);
55 connection.setInstanceFollowRedirects(true);
56 connection.setRequestMethod("POST");
57 connection.setUseCaches(false);
58 connection.setRequestProperty("X-OpenRocket-Version", encode(Prefs.getVersion()));
61 post = (VERSION_PARAM + "=" + encode(Prefs.getVersion())
62 + "&" + BUG_REPORT_PARAM + "=" + encode(report));
64 OutputStreamWriter wr = null;
66 // Send post information
67 connection.setDoOutput(true);
68 wr = new OutputStreamWriter(connection.getOutputStream(), "UTF-8");
72 if (connection.getResponseCode() != BUG_REPORT_RESPONSE_CODE) {
73 throw new IOException("Server responded with code " +
74 connection.getResponseCode() + ", expecting " + BUG_REPORT_RESPONSE_CODE);
79 connection.disconnect();
86 * Start an asynchronous task that will fetch information about the latest
87 * OpenRocket version. This will overwrite any previous fetching operation.
89 public static void startFetchUpdateInfo() {
90 fetcher = new UpdateInfoFetcher();
96 * Check whether the update info fetching is still in progress.
98 * @return <code>true</code> if the communication is still in progress.
100 public static boolean isFetchUpdateInfoRunning() {
101 if (fetcher == null) {
102 throw new IllegalStateException("startFetchUpdateInfo() has not been called");
104 return fetcher.isAlive();
109 * Retrieve the result of the background update info fetcher. This method returns
110 * the result of the previous call to {@link #startFetchUpdateInfo()}. It must be
111 * called before calling this method.
113 * This method will return <code>null</code> if the info fetcher is still running or
114 * if it encountered a problem in communicating with the server. The difference can
115 * be checked using {@link #isFetchUpdateInfoRunning()}.
117 * @return the update result, or <code>null</code> if the fetching is still in progress
118 * or an error occurred while communicating with the server.
119 * @throws IllegalStateException if {@link #startFetchUpdateInfo()} has not been called.
121 public static UpdateInfo getUpdateInfo() {
122 if (fetcher == null) {
123 throw new IllegalStateException("startFetchUpdateInfo() has not been called");
131 * Parse the data received from the server.
133 * @param r the Reader from which to read.
134 * @return an UpdateInfo construct, or <code>null</code> if the data was invalid.
135 * @throws IOException if an I/O exception occurs.
137 /* package-private */
138 static UpdateInfo parseUpdateInput(Reader r) throws IOException {
139 BufferedReader reader;
140 if (r instanceof BufferedReader) {
141 reader = (BufferedReader)r;
143 reader = new BufferedReader(r);
147 String version = null;
148 ArrayList<ComparablePair<Integer,String>> updates =
149 new ArrayList<ComparablePair<Integer,String>>();
151 String str = reader.readLine();
152 while (str != null) {
153 if (str.matches("^Version: *[0-9]+\\.[0-9]+\\.[0-9]+[a-zA-Z0-9.-]* *$")) {
154 version = str.substring(8).trim();
155 } else if (str.matches("^[0-9]+:\\p{Print}+$")) {
156 int index = str.indexOf(':');
157 int value = Integer.parseInt(str.substring(0, index));
158 String desc = str.substring(index+1).trim();
159 if (!desc.equals("")) {
160 updates.add(new ComparablePair<Integer,String>(value, desc));
163 // Ignore anything else
164 str = reader.readLine();
167 if (version != null) {
168 return new UpdateInfo(version, updates);
177 private static class UpdateInfoFetcher extends Thread {
179 private volatile UpdateInfo info = null;
185 } catch (IOException e) {
191 private void doConnection() throws IOException {
193 url = new URL(UPDATE_INFO_URL + "?" + VERSION_PARAM + "=" +
194 encode(Prefs.getVersion()));
196 HttpURLConnection connection = (HttpURLConnection) url.openConnection();
198 connection.setConnectTimeout(CONNECTION_TIMEOUT);
199 connection.setInstanceFollowRedirects(true);
200 connection.setRequestMethod("GET");
201 connection.setUseCaches(false);
202 connection.setRequestProperty("X-OpenRocket-Version", encode(Prefs.getVersion()));
203 connection.setRequestProperty("X-OpenRocket-ID", encode(Prefs.getUniqueID()));
204 connection.setRequestProperty("X-OpenRocket-OS", encode(
205 System.getProperty("os.name") + " " + System.getProperty("os.arch")));
206 connection.setRequestProperty("X-OpenRocket-Java", encode(
207 System.getProperty("java.vendor") + " " + System.getProperty("java.version")));
208 connection.setRequestProperty("X-OpenRocket-Country", encode(
209 System.getProperty("user.country")));
211 InputStream is = null;
213 connection.connect();
215 if (connection.getResponseCode() == UPDATE_INFO_NO_UPDATE_CODE) {
216 // No updates are available
217 info = new UpdateInfo();
221 if (connection.getResponseCode() != UPDATE_INFO_UPDATE_AVAILABLE) {
222 // Error communicating with server
226 if (!UPDATE_INFO_CONTENT_TYPE.equalsIgnoreCase(connection.getContentType())) {
227 // Unknown response type
231 // Update is available, parse input
232 is = connection.getInputStream();
233 String encoding = connection.getContentEncoding();
234 if (encoding == null)
236 BufferedReader reader = new BufferedReader(new InputStreamReader(is, encoding));
243 connection.disconnect();
252 private static String encode(String str) {
256 return URLEncoder.encode(str, "UTF-8");
257 } catch (UnsupportedEncodingException e) {
258 throw new RuntimeException("Unsupported encoding UTF-8", e);