Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(135)

Side by Side Diff: remoting/android/java/src/org/chromium/chromoting/HostListManager.java

Issue 1948793002: Implement HostListManager by Refactoring HostListLoader (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Make Response clearer Created 4 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2014 The Chromium Authors. All rights reserved. 1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 package org.chromium.chromoting; 5 package org.chromium.chromoting;
6 6
7 import android.os.Handler; 7 import android.os.Handler;
8 import android.os.HandlerThread; 8 import android.os.HandlerThread;
9 import android.os.Looper; 9 import android.os.Looper;
10 10
11 import org.chromium.base.Log; 11 import org.chromium.base.Log;
12 import org.json.JSONArray; 12 import org.json.JSONArray;
13 import org.json.JSONException; 13 import org.json.JSONException;
14 import org.json.JSONObject; 14 import org.json.JSONObject;
15 15
16 import java.io.IOException; 16 import java.io.IOException;
17 import java.io.OutputStream;
17 import java.net.HttpURLConnection; 18 import java.net.HttpURLConnection;
18 import java.net.MalformedURLException; 19 import java.net.MalformedURLException;
19 import java.net.URL; 20 import java.net.URL;
20 import java.util.ArrayList; 21 import java.util.ArrayList;
21 import java.util.Collections; 22 import java.util.Collections;
22 import java.util.Comparator; 23 import java.util.Comparator;
23 import java.util.Locale; 24 import java.util.Locale;
24 import java.util.Scanner; 25 import java.util.Scanner;
25 26
26 /** Helper for fetching the host list. */ 27 /** Helper for fetching and modifying the host list. */
27 public class HostListLoader { 28 public class HostListManager {
28 public enum Error { 29 public enum Error {
29 AUTH_FAILED, 30 AUTH_FAILED,
30 NETWORK_ERROR, 31 NETWORK_ERROR,
31 SERVICE_UNAVAILABLE, 32 SERVICE_UNAVAILABLE,
32 UNEXPECTED_RESPONSE, 33 UNEXPECTED_RESPONSE,
33 UNKNOWN, 34 UNKNOWN,
34 } 35 }
35 36
36 /** Callback for receiving the host list, or getting notified of an error. * / 37 /** Callback for receiving the host list, or getting notified of an error. * /
37 public interface Callback { 38 public interface Callback {
38 void onHostListReceived(HostInfo[] hosts); 39 void onHostListReceived(HostInfo[] response);
40 void onHostUpdated();
41 void onHostDeleted();
39 void onError(Error error); 42 void onError(Error error);
40 } 43 }
41 44
45 /**
46 * Represents a response from the directory server.
47 * |error| and |body| can be null and they can't be both non-null.
Lambros 2016/05/05 00:00:14 The way this is phrased, it sounds like |error| an
Yuwei 2016/05/05 00:40:59 Basically error==/!=null tells you whether the req
Yuwei 2016/05/06 00:31:58 I guess it looks clearer now...
48 **/
49 private static class Response {
50 public final Error error;
51 public final String body;
52 public Response(Error error, String body) {
53 this.error = error;
54 this.body = body;
55 }
56 }
57
42 private static final String TAG = "Chromoting"; 58 private static final String TAG = "Chromoting";
43 59
44 /** Path from which to download a user's host list JSON object. */ 60 /** Path from which to download a user's host list JSON object. */
45 private static final String HOST_LIST_PATH = 61 private static final String HOST_LIST_PATH =
46 "https://www.googleapis.com/chromoting/v1/@me/hosts"; 62 "https://www.googleapis.com/chromoting/v1/@me/hosts";
47 63
48 /** Callback handler to be used for network operations. */ 64 /** Callback handler to be used for network operations. */
49 private Handler mNetworkThread; 65 private Handler mNetworkThread;
50 66
51 /** Handler for main thread. */ 67 /** Handler for main thread. */
52 private Handler mMainThread; 68 private Handler mMainThread;
53 69
54 public HostListLoader() { 70 public HostListManager() {
55 // Thread responsible for downloading the host list. 71 // Thread responsible for downloading the host list.
56 72
57 mMainThread = new Handler(Looper.getMainLooper()); 73 mMainThread = new Handler(Looper.getMainLooper());
58 } 74 }
59 75
60 private void initNetworkThread() { 76 private void runOnNetworkThread(Runnable runnable) {
61 if (mNetworkThread == null) { 77 if (mNetworkThread == null) {
62 HandlerThread thread = new HandlerThread("network"); 78 HandlerThread thread = new HandlerThread("network");
63 thread.start(); 79 thread.start();
64 mNetworkThread = new Handler(thread.getLooper()); 80 mNetworkThread = new Handler(thread.getLooper());
65 } 81 }
82 mNetworkThread.post(runnable);
66 } 83 }
67 84
68 /** 85 /**
69 * Causes the host list to be fetched on a background thread. This should b e called on the 86 * Causes the host list to be fetched on a background thread. This should b e called on the
70 * main thread, and callbacks will also be invoked on the main thread. On s uccess, 87 * main thread, and callbacks will also be invoked on the main thread. On s uccess,
71 * callback.onHostListReceived() will be called, otherwise callback.onError () will be called 88 * callback.onHostListReceived() will be called, otherwise callback.onError () will be called
72 * with an error-code describing the failure. 89 * with an error-code describing the failure.
73 */ 90 */
74 public void retrieveHostList(String authToken, Callback callback) { 91 public void retrieveHostList(final String authToken, final Callback callback ) {
75 initNetworkThread(); 92 runOnNetworkThread(new Runnable() {
76 final String authTokenFinal = authToken;
77 final Callback callbackFinal = callback;
78 mNetworkThread.post(new Runnable() {
79 @Override 93 @Override
80 public void run() { 94 public void run() {
81 doRetrieveHostList(authTokenFinal, callbackFinal); 95 doRetrieveHostList(authToken, callback);
82 } 96 }
83 }); 97 });
84 } 98 }
85 99
86 private void doRetrieveHostList(String authToken, Callback callback) { 100 private void doRetrieveHostList(String authToken, Callback callback) {
87 HttpURLConnection link = null; 101 Response response = sendRequest(authToken, HOST_LIST_PATH, "GET", null, null);
88 String response = null; 102 if (response.error != null) {
89 try { 103 postError(callback, response.error);
90 link = (HttpURLConnection) new URL(HOST_LIST_PATH).openConnection();
91 link.setRequestProperty("Authorization", "OAuth " + authToken);
92
93 // Listen for the server to respond.
94 int status = link.getResponseCode();
95 switch (status) {
96 case HttpURLConnection.HTTP_OK: // 200
97 break;
98 case HttpURLConnection.HTTP_UNAUTHORIZED: // 401
99 postError(callback, Error.AUTH_FAILED);
100 return;
101 case HttpURLConnection.HTTP_BAD_GATEWAY: // 502
102 case HttpURLConnection.HTTP_UNAVAILABLE: // 503
103 postError(callback, Error.SERVICE_UNAVAILABLE);
104 return;
105 default:
106 postError(callback, Error.UNKNOWN);
107 return;
108 }
109
110 StringBuilder responseBuilder = new StringBuilder();
111 Scanner incoming = new Scanner(link.getInputStream());
112 while (incoming.hasNext()) {
113 responseBuilder.append(incoming.nextLine());
114 }
115 response = String.valueOf(responseBuilder);
116 incoming.close();
117 } catch (MalformedURLException ex) {
118 // This should never happen.
119 throw new RuntimeException("Unexpected error while fetching host lis t: ", ex);
120 } catch (IOException ex) {
121 postError(callback, Error.NETWORK_ERROR);
122 return; 104 return;
123 } finally {
124 if (link != null) {
125 link.disconnect();
126 }
127 } 105 }
128 106
129 // Parse directory response. 107 // Parse directory response.
130 ArrayList<HostInfo> hostList = new ArrayList<HostInfo>(); 108 ArrayList<HostInfo> hostList = new ArrayList<HostInfo>();
131 try { 109 try {
132 JSONObject data = new JSONObject(response).getJSONObject("data"); 110 JSONObject data = new JSONObject(response.body).getJSONObject("data" );
133 if (data.has("items")) { 111 if (data.has("items")) {
134 JSONArray hostsJson = data.getJSONArray("items"); 112 JSONArray hostsJson = data.getJSONArray("items");
135 113
136 int index = 0; 114 int index = 0;
137 while (!hostsJson.isNull(index)) { 115 while (!hostsJson.isNull(index)) {
138 JSONObject hostJson = hostsJson.getJSONObject(index); 116 JSONObject hostJson = hostsJson.getJSONObject(index);
139 // If a host is only recently registered, it may be missing some of the keys 117 // If a host is only recently registered, it may be missing some of the keys
140 // below. It should still be visible in the list, even thoug h a connection 118 // below. It should still be visible in the list, even thoug h a connection
141 // attempt will fail because of the missing keys. The failed attempt will 119 // attempt will fail because of the missing keys. The failed attempt will
142 // trigger reloading of the host-list, by which time the key s will hopefully be 120 // trigger reloading of the host-list, by which time the key s will hopefully be
(...skipping 15 matching lines...) Expand all
158 final Callback callbackFinal = callback; 136 final Callback callbackFinal = callback;
159 final HostInfo[] hosts = hostList.toArray(new HostInfo[hostList.size()]) ; 137 final HostInfo[] hosts = hostList.toArray(new HostInfo[hostList.size()]) ;
160 mMainThread.post(new Runnable() { 138 mMainThread.post(new Runnable() {
161 @Override 139 @Override
162 public void run() { 140 public void run() {
163 callbackFinal.onHostListReceived(hosts); 141 callbackFinal.onHostListReceived(hosts);
164 } 142 }
165 }); 143 });
166 } 144 }
167 145
146 /**
147 * Updates a host on the background thread. On success, callback.onHostUpdat ed() will be called,
148 * otherwise callback.onError() will be called with an error-code describing the failure.
149 */
150 public void putHost(final String authToken, final String hostId, final Strin g hostName,
151 final String publicKey, final Callback callback) {
152 runOnNetworkThread(new Runnable() {
153 @Override
154 public void run() {
155 doPutHost(authToken, hostId, hostName, publicKey, callback);
156 }
157 });
158 }
159
160 private void doPutHost(String authToken, String hostId, String hostName, Str ing publicKey,
161 final Callback callback) {
162 String requestJson;
163 try {
164 JSONObject data = new JSONObject();
165 data.put("hostId", hostId);
166 data.put("hostName", hostName);
167 data.put("publicKey", publicKey);
168 JSONObject request = new JSONObject();
169 request.put("data", data);
170 requestJson = request.toString();
171 } catch (JSONException ex) {
172 Log.e(TAG, "Error creating put host JSON string: %s", ex.getMessage( ));
173 postError(callback, Error.UNKNOWN);
174 return;
175 }
176 Response response = sendRequest(authToken, HOST_LIST_PATH + '/' + hostId , "PUT",
177 "application/json", requestJson);
178 if (response.error != null) {
179 postError(callback, response.error);
180 } else {
181 mMainThread.post(new Runnable() {
182 @Override
183 public void run() {
184 callback.onHostUpdated();
185 }
186 });
187 }
188 }
189
190 /**
191 * Deletes a host on the background thread. On success, callback.onHostUpdat ed() will be called,
192 * otherwise callback.onError() will be called with an error-code describing the failure.
193 */
194 public void deleteHost(final String authToken, final String hostId,
195 final Callback callback) {
196 runOnNetworkThread(new Runnable() {
197 @Override
198 public void run() {
199 doDeleteHost(authToken, hostId, callback);
200 }
201 });
202 }
203
204 private void doDeleteHost(String authToken, String hostId, final Callback ca llback) {
205 Response response = sendRequest(authToken, HOST_LIST_PATH + '/' + hostId , "DELETE",
206 null, null);
207 if (response.error != null) {
208 postError(callback, response.error);
209 } else {
210 mMainThread.post(new Runnable() {
211 @Override
212 public void run() {
213 callback.onHostDeleted();
214 }
215 });
216 }
217 }
218
168 /** Posts error to callback on main thread. */ 219 /** Posts error to callback on main thread. */
169 private void postError(Callback callback, Error error) { 220 private void postError(Callback callback, Error error) {
170 final Callback callbackFinal = callback; 221 final Callback callbackFinal = callback;
171 final Error errorFinal = error; 222 final Error errorFinal = error;
172 mMainThread.post(new Runnable() { 223 mMainThread.post(new Runnable() {
173 @Override 224 @Override
174 public void run() { 225 public void run() {
175 callbackFinal.onError(errorFinal); 226 callbackFinal.onError(errorFinal);
176 } 227 }
177 }); 228 });
178 } 229 }
179 230
180 private static void sortHosts(ArrayList<HostInfo> hosts) { 231 private static void sortHosts(ArrayList<HostInfo> hosts) {
181 Comparator<HostInfo> hostComparator = new Comparator<HostInfo>() { 232 Comparator<HostInfo> hostComparator = new Comparator<HostInfo>() {
182 @Override 233 @Override
183 public int compare(HostInfo a, HostInfo b) { 234 public int compare(HostInfo a, HostInfo b) {
184 if (a.isOnline != b.isOnline) { 235 if (a.isOnline != b.isOnline) {
185 return a.isOnline ? -1 : 1; 236 return a.isOnline ? -1 : 1;
186 } 237 }
187 String aName = a.name.toUpperCase(Locale.getDefault()); 238 String aName = a.name.toUpperCase(Locale.getDefault());
188 String bName = b.name.toUpperCase(Locale.getDefault()); 239 String bName = b.name.toUpperCase(Locale.getDefault());
189 return aName.compareTo(bName); 240 return aName.compareTo(bName);
190 } 241 }
191 }; 242 };
192 Collections.sort(hosts, hostComparator); 243 Collections.sort(hosts, hostComparator);
193 } 244 }
245
246 /**
247 * Sends request to the url and returns the response.
248 * @param authToken auth token
249 * @param url the URL to send the request
250 * @param method /GET/POST/PUT/DELETE/etc.
251 * @param requestContentType The content type of the request body. This can be null.
252 * @param requestBody This can be null.
253 * @return The response.
254 */
255 private static Response sendRequest(String authToken, String url, String met hod,
256 String requestContentType, String reques tBody) {
257 HttpURLConnection link = null;
258 Error error = null;
259 try {
260 link = (HttpURLConnection) new URL(url).openConnection();
261 link.setRequestMethod(method);
262 link.setRequestProperty("Authorization", "OAuth " + authToken);
263 if (requestContentType != null) {
264 link.setRequestProperty("Content-Type", requestContentType);
265 }
266 if (requestBody != null) {
267 byte[] requestBytes = requestBody.getBytes("UTF-8");
268 OutputStream outStream = link.getOutputStream();
269 outStream.write(requestBytes);
270 outStream.close();
271 }
272
273 // Listen for the server to respond.
274 int status = link.getResponseCode();
275 switch (status) {
276 case HttpURLConnection.HTTP_OK: // 200
277 break;
278 case HttpURLConnection.HTTP_UNAUTHORIZED: // 401
279 error = Error.AUTH_FAILED;
280 break;
281 case HttpURLConnection.HTTP_BAD_GATEWAY: // 502
282 case HttpURLConnection.HTTP_UNAVAILABLE: // 503
283 error = Error.SERVICE_UNAVAILABLE;
284 break;
285 default:
286 error = Error.UNKNOWN;
287 }
288
289 if (error != null) {
290 return new Response(error, null);
291 }
292
293 StringBuilder responseBuilder = new StringBuilder();
294 Scanner incoming = new Scanner(link.getInputStream());
295 while (incoming.hasNext()) {
296 responseBuilder.append(incoming.nextLine());
297 }
298 String response = String.valueOf(responseBuilder);
Lambros 2016/05/05 00:00:14 Just my own preference, but I would write this as
Yuwei 2016/05/05 00:40:58 That's right... BTW the code comes from HostListLo
Yuwei 2016/05/06 00:31:58 Done.
299 incoming.close();
300 return new Response(null, response);
301 } catch (MalformedURLException ex) {
302 // This should never happen.
303 throw new RuntimeException("Unexpected error while fetching host lis t: ", ex);
304 } catch (IOException ex) {
305 return new Response(Error.NETWORK_ERROR, null);
306 } finally {
307 if (link != null) {
308 link.disconnect();
309 }
310 }
311 }
194 } 312 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698