2 * Copyright (C) 2013 by Raphael Michel under the MIT license:
4 * Permission is hereby granted, free of charge, to any person obtaining a copy
5 * of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the Software
9 * is furnished to do so, subject to the following conditions:
11 * The above copyright notice and this permission notice shall be included in
12 * all copies or substantial portions of the Software.
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
17 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
18 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
19 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
20 * DEALINGS IN THE SOFTWARE.
22 package de.geeksfactory.opacclient.apis;
24 import java.io.BufferedReader;
25 import java.io.IOException;
26 import java.io.InputStream;
27 import java.io.InputStreamReader;
28 import java.io.InterruptedIOException;
29 import java.io.UnsupportedEncodingException;
30 import java.net.URLDecoder;
31 import java.net.UnknownHostException;
32 import java.util.ArrayList;
33 import java.util.HashMap;
34 import java.util.List;
37 import org.apache.http.HttpEntity;
38 import org.apache.http.HttpResponse;
39 import org.apache.http.MalformedChunkCodingException;
40 import org.apache.http.NameValuePair;
41 import org.apache.http.NoHttpResponseException;
42 import org.apache.http.client.ClientProtocolException;
43 import org.apache.http.client.CookieStore;
44 import org.apache.http.client.entity.UrlEncodedFormEntity;
45 import org.apache.http.client.methods.HttpGet;
46 import org.apache.http.client.methods.HttpPost;
47 import org.apache.http.client.protocol.ClientContext;
48 import org.apache.http.client.utils.URLEncodedUtils;
49 import org.apache.http.conn.ConnectTimeoutException;
50 import org.apache.http.impl.client.DefaultHttpClient;
51 import org.apache.http.message.BasicNameValuePair;
52 import org.apache.http.protocol.BasicHttpContext;
53 import org.apache.http.protocol.HttpContext;
54 import org.apache.http.util.EntityUtils;
56 import android.graphics.Bitmap;
57 import android.graphics.BitmapFactory;
58 import de.geeksfactory.opacclient.NotReachableException;
59 import de.geeksfactory.opacclient.networking.HTTPClient;
60 import de.geeksfactory.opacclient.objects.CoverHolder;
61 import de.geeksfactory.opacclient.objects.Library;
62 import de.geeksfactory.opacclient.storage.MetaDataSource;
65 * Abstract Base class for OpacApi implementations providing some helper methods
68 public abstract class BaseApi implements OpacApi {
70 protected DefaultHttpClient http_client;
73 * Initializes HTTP client
76 public void init(MetaDataSource metadata, Library library) {
77 http_client = HTTPClient.getNewHttpClient(library);
81 * Perform a HTTP GET request to a given URL
86 * Expected encoding of the response body
87 * @param ignore_errors
88 * If true, status codes above 400 do not raise an exception
90 * If set, the given cookieStore is used instead of the built-in
92 * @return Answer content
93 * @throws NotReachableException
94 * Thrown when server returns a HTTP status code greater or
97 public String httpGet(String url, String encoding, boolean ignore_errors,
98 CookieStore cookieStore) throws ClientProtocolException,
101 HttpGet httpget = new HttpGet(cleanUrl(url));
102 HttpResponse response;
105 if (cookieStore != null) {
106 // Create local HTTP context
107 HttpContext localContext = new BasicHttpContext();
108 // Bind custom cookie store to the local context
109 localContext.setAttribute(ClientContext.COOKIE_STORE,
112 response = http_client.execute(httpget, localContext);
114 response = http_client.execute(httpget);
116 } catch (ConnectTimeoutException e) {
118 throw new NotReachableException();
119 } catch (UnknownHostException e) {
121 throw new NotReachableException();
122 } catch (IllegalStateException e) {
124 throw new NotReachableException();
125 } catch (NoHttpResponseException e) {
127 throw new NotReachableException();
128 } catch (MalformedChunkCodingException e) {
130 throw new NotReachableException();
131 } catch (javax.net.ssl.SSLPeerUnverifiedException e) {
132 // TODO: Handly this well
134 } catch (javax.net.ssl.SSLException e) {
135 // TODO: Handly this well
136 // Can be "Not trusted server certificate" or can be a
137 // aborted/interrupted handshake/connection
138 if (e.getMessage().contains("timed out")
139 || e.getMessage().contains("reset by")) {
141 throw new NotReachableException();
145 } catch (InterruptedIOException e) {
147 throw new NotReachableException();
148 } catch (IOException e) {
149 if (e.getMessage() != null
150 && e.getMessage().contains("Request aborted")) {
152 throw new NotReachableException();
158 if (!ignore_errors && response.getStatusLine().getStatusCode() >= 400) {
159 throw new NotReachableException();
161 String html = convertStreamToString(response.getEntity().getContent(),
163 response.getEntity().consumeContent();
167 public String httpGet(String url, String encoding, boolean ignore_errors)
168 throws ClientProtocolException, IOException {
169 return httpGet(url, encoding, ignore_errors, null);
172 public String httpGet(String url, String encoding)
173 throws ClientProtocolException, IOException {
174 return httpGet(url, encoding, false, null);
178 public String httpGet(String url) throws ClientProtocolException,
180 return httpGet(url, getDefaultEncoding(), false, null);
183 public static String cleanUrl(String myURL) {
184 String[] parts = myURL.split("\\?");
185 String url = parts[0];
187 if (parts.length > 1) {
189 List<NameValuePair> params = new ArrayList<NameValuePair>();
190 String[] pairs = parts[1].split("&");
191 for (String pair : pairs) {
192 String[] kv = pair.split("=");
194 params.add(new BasicNameValuePair(URLDecoder.decode(
195 kv[0], "UTF-8"), URLDecoder.decode(kv[1],
198 params.add(new BasicNameValuePair(URLDecoder.decode(
199 kv[0], "UTF-8"), ""));
201 url += URLEncodedUtils.format(params, "UTF-8");
204 } catch (UnsupportedEncodingException e) {
210 public void downloadCover(CoverHolder item) {
211 if (item.getCover() == null)
213 HttpGet httpget = new HttpGet(cleanUrl(item.getCover()));
214 HttpResponse response;
217 response = http_client.execute(httpget);
219 if (response.getStatusLine().getStatusCode() >= 400) {
222 HttpEntity entity = response.getEntity();
223 byte[] bytes = EntityUtils.toByteArray(entity);
225 Bitmap bitmap = BitmapFactory.decodeByteArray(bytes, 0,
227 item.setCoverBitmap(bitmap);
229 } catch (IOException e) {
236 * Perform a HTTP POST request to a given URL
243 * Expected encoding of the response body
244 * @param ignore_errors
245 * If true, status codes above 400 do not raise an exception
247 * If set, the given cookieStore is used instead of the built-in
249 * @return Answer content
250 * @throws NotReachableException
251 * Thrown when server returns a HTTP status code greater or
254 public String httpPost(String url, UrlEncodedFormEntity data,
255 String encoding, boolean ignore_errors, CookieStore cookieStore)
256 throws ClientProtocolException, IOException {
257 HttpPost httppost = new HttpPost(cleanUrl(url));
258 httppost.setEntity(data);
260 HttpResponse response = null;
262 if (cookieStore != null) {
263 // Create local HTTP context
264 HttpContext localContext = new BasicHttpContext();
265 // Bind custom cookie store to the local context
266 localContext.setAttribute(ClientContext.COOKIE_STORE,
269 response = http_client.execute(httppost, localContext);
271 response = http_client.execute(httppost);
273 } catch (ConnectTimeoutException e) {
275 throw new NotReachableException();
276 } catch (IllegalStateException e) {
278 throw new NotReachableException();
279 } catch (UnknownHostException e) {
281 throw new NotReachableException();
282 } catch (NoHttpResponseException e) {
284 throw new NotReachableException();
285 } catch (MalformedChunkCodingException e) {
287 throw new NotReachableException();
288 } catch (javax.net.ssl.SSLPeerUnverifiedException e) {
289 // TODO: Handle this well
290 if (e.getMessage().contains("timed out")
291 || e.getMessage().contains("reset by")) {
293 throw new NotReachableException();
297 } catch (javax.net.ssl.SSLException e) {
298 // TODO: Handle this well
299 // Can be "Not trusted server certificate" or can be a
300 // aborted/interrupted handshake/connection
302 } catch (InterruptedIOException e) {
304 throw new NotReachableException();
305 } catch (IOException e) {
306 if (e.getMessage() != null
307 && e.getMessage().contains("Request aborted")) {
309 throw new NotReachableException();
315 if (!ignore_errors && response.getStatusLine().getStatusCode() >= 400) {
316 throw new NotReachableException();
318 String html = convertStreamToString(response.getEntity().getContent(),
320 response.getEntity().consumeContent();
324 public String httpPost(String url, UrlEncodedFormEntity data,
325 String encoding, boolean ignore_errors)
326 throws ClientProtocolException, IOException {
327 return httpPost(url, data, encoding, ignore_errors, null);
330 public String httpPost(String url, UrlEncodedFormEntity data,
331 String encoding) throws ClientProtocolException, IOException {
332 return httpPost(url, data, encoding, false, null);
336 public String httpPost(String url, UrlEncodedFormEntity data)
337 throws ClientProtocolException, IOException {
338 return httpPost(url, data, getDefaultEncoding(), false, null);
342 * Reads content from an InputStream into a string
345 * InputStream to read from
346 * @return String content of the InputStream
348 protected static String convertStreamToString(InputStream is,
349 String encoding) throws IOException {
350 BufferedReader reader;
352 reader = new BufferedReader(new InputStreamReader(is, encoding));
353 } catch (UnsupportedEncodingException e1) {
354 reader = new BufferedReader(new InputStreamReader(is));
356 StringBuilder sb = new StringBuilder();
360 while ((line = reader.readLine()) != null) {
361 sb.append((line + "\n"));
366 } catch (IOException e) {
370 return sb.toString();
373 protected static String convertStreamToString(InputStream is)
375 return convertStreamToString(is, "ISO-8859-1");
378 protected String getDefaultEncoding() {
383 * Gets all values of all query parameters in an URL.
385 public static Map<String, List<String>> getQueryParams(String url) {
387 Map<String, List<String>> params = new HashMap<String, List<String>>();
388 String[] urlParts = url.split("\\?");
389 if (urlParts.length > 1) {
390 String query = urlParts[1];
391 for (String param : query.split("&")) {
392 String[] pair = param.split("=");
393 String key = URLDecoder.decode(pair[0], "UTF-8");
395 if (pair.length > 1) {
396 value = URLDecoder.decode(pair[1], "UTF-8");
399 List<String> values = params.get(key);
400 if (values == null) {
401 values = new ArrayList<String>();
402 params.put(key, values);
409 } catch (UnsupportedEncodingException ex) {
410 throw new AssertionError(ex);
415 * Gets the value for every query parameter in the URL. If a parameter name
416 * occurs twice or more, only the first occurance is interpreted by this
419 public static Map<String, String> getQueryParamsFirst(String url) {
421 Map<String, String> params = new HashMap<String, String>();
422 String[] urlParts = url.split("\\?");
423 if (urlParts.length > 1) {
424 String query = urlParts[1];
425 for (String param : query.split("&")) {
426 String[] pair = param.split("=");
427 String key = URLDecoder.decode(pair[0], "UTF-8");
429 if (pair.length > 1) {
430 value = URLDecoder.decode(pair[1], "UTF-8");
433 String values = params.get(key);
434 if (values == null) {
435 params.put(key, value);
441 } catch (UnsupportedEncodingException ex) {
442 throw new AssertionError(ex);