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 HttpGet httpget = new HttpGet(url);
103 httpget.addHeader("User-Agent", "Mozilla/5.0 (Windows NT 6.0; rv:32.0) Gecko/20100101 Firefox/32.0");
104 HttpResponse response;
107 if (cookieStore != null) {
108 // Create local HTTP context
109 HttpContext localContext = new BasicHttpContext();
110 // Bind custom cookie store to the local context
111 localContext.setAttribute(ClientContext.COOKIE_STORE,
114 response = http_client.execute(httpget, localContext);
116 response = http_client.execute(httpget);
118 } catch (ConnectTimeoutException e) {
120 throw new NotReachableException();
121 } catch (UnknownHostException e) {
123 throw new NotReachableException();
124 } catch (IllegalStateException e) {
126 throw new NotReachableException();
127 } catch (NoHttpResponseException e) {
129 throw new NotReachableException();
130 } catch (MalformedChunkCodingException e) {
132 throw new NotReachableException();
133 } catch (javax.net.ssl.SSLPeerUnverifiedException e) {
134 // TODO: Handly this well
136 } catch (javax.net.ssl.SSLException e) {
137 // TODO: Handly this well
138 // Can be "Not trusted server certificate" or can be a
139 // aborted/interrupted handshake/connection
140 if (e.getMessage().contains("timed out")
141 || e.getMessage().contains("reset by")) {
143 throw new NotReachableException();
147 } catch (InterruptedIOException e) {
149 throw new NotReachableException();
150 } catch (IOException e) {
151 if (e.getMessage() != null
152 && e.getMessage().contains("Request aborted")) {
154 throw new NotReachableException();
160 if (!ignore_errors && response.getStatusLine().getStatusCode() >= 400) {
161 throw new NotReachableException();
163 String html = convertStreamToString(response.getEntity().getContent(),
165 response.getEntity().consumeContent();
169 public String httpGet(String url, String encoding, boolean ignore_errors)
170 throws ClientProtocolException, IOException {
171 return httpGet(url, encoding, ignore_errors, null);
174 public String httpGet(String url, String encoding)
175 throws ClientProtocolException, IOException {
176 return httpGet(url, encoding, false, null);
180 public String httpGet(String url) throws ClientProtocolException,
182 return httpGet(url, getDefaultEncoding(), false, null);
185 public static String cleanUrl(String myURL) {
186 String[] parts = myURL.split("\\?");
187 String url = parts[0];
189 if (parts.length > 1) {
191 List<NameValuePair> params = new ArrayList<NameValuePair>();
192 String[] pairs = parts[1].split("&");
193 for (String pair : pairs) {
194 String[] kv = pair.split("=");
196 params.add(new BasicNameValuePair(URLDecoder.decode(
197 kv[0], "UTF-8"), URLDecoder.decode(kv[1],
200 params.add(new BasicNameValuePair(URLDecoder.decode(
201 kv[0], "UTF-8"), ""));
203 url += URLEncodedUtils.format(params, "UTF-8");
206 } catch (UnsupportedEncodingException e) {
212 public void downloadCover(CoverHolder item) {
213 if (item.getCover() == null)
215 HttpGet httpget = new HttpGet(cleanUrl(item.getCover()));
216 HttpResponse response;
219 response = http_client.execute(httpget);
221 if (response.getStatusLine().getStatusCode() >= 400) {
224 HttpEntity entity = response.getEntity();
225 byte[] bytes = EntityUtils.toByteArray(entity);
227 Bitmap bitmap = BitmapFactory.decodeByteArray(bytes, 0,
229 item.setCoverBitmap(bitmap);
231 } catch (IOException e) {
238 * Perform a HTTP POST request to a given URL
245 * Expected encoding of the response body
246 * @param ignore_errors
247 * If true, status codes above 400 do not raise an exception
249 * If set, the given cookieStore is used instead of the built-in
251 * @return Answer content
252 * @throws NotReachableException
253 * Thrown when server returns a HTTP status code greater or
256 public String httpPost(String url, UrlEncodedFormEntity data,
257 String encoding, boolean ignore_errors, CookieStore cookieStore)
258 throws ClientProtocolException, IOException {
259 HttpPost httppost = new HttpPost(cleanUrl(url));
260 httppost.setEntity(data);
262 HttpResponse response = null;
264 if (cookieStore != null) {
265 // Create local HTTP context
266 HttpContext localContext = new BasicHttpContext();
267 // Bind custom cookie store to the local context
268 localContext.setAttribute(ClientContext.COOKIE_STORE,
271 response = http_client.execute(httppost, localContext);
273 response = http_client.execute(httppost);
275 } catch (ConnectTimeoutException e) {
277 throw new NotReachableException();
278 } catch (IllegalStateException e) {
280 throw new NotReachableException();
281 } catch (UnknownHostException e) {
283 throw new NotReachableException();
284 } catch (NoHttpResponseException e) {
286 throw new NotReachableException();
287 } catch (MalformedChunkCodingException e) {
289 throw new NotReachableException();
290 } catch (javax.net.ssl.SSLPeerUnverifiedException e) {
291 // TODO: Handle this well
292 if (e.getMessage().contains("timed out")
293 || e.getMessage().contains("reset by")) {
295 throw new NotReachableException();
299 } catch (javax.net.ssl.SSLException e) {
300 // TODO: Handle this well
301 // Can be "Not trusted server certificate" or can be a
302 // aborted/interrupted handshake/connection
304 } catch (InterruptedIOException e) {
306 throw new NotReachableException();
307 } catch (IOException e) {
308 if (e.getMessage() != null
309 && e.getMessage().contains("Request aborted")) {
311 throw new NotReachableException();
317 if (!ignore_errors && response.getStatusLine().getStatusCode() >= 400) {
318 throw new NotReachableException();
320 String html = convertStreamToString(response.getEntity().getContent(),
322 response.getEntity().consumeContent();
326 public String httpPost(String url, UrlEncodedFormEntity data,
327 String encoding, boolean ignore_errors)
328 throws ClientProtocolException, IOException {
329 return httpPost(url, data, encoding, ignore_errors, null);
332 public String httpPost(String url, UrlEncodedFormEntity data,
333 String encoding) throws ClientProtocolException, IOException {
334 return httpPost(url, data, encoding, false, null);
338 public String httpPost(String url, UrlEncodedFormEntity data)
339 throws ClientProtocolException, IOException {
340 return httpPost(url, data, getDefaultEncoding(), false, null);
344 * Reads content from an InputStream into a string
347 * InputStream to read from
348 * @return String content of the InputStream
350 protected static String convertStreamToString(InputStream is,
351 String encoding) throws IOException {
352 BufferedReader reader;
354 reader = new BufferedReader(new InputStreamReader(is, encoding));
355 } catch (UnsupportedEncodingException e1) {
356 reader = new BufferedReader(new InputStreamReader(is));
358 StringBuilder sb = new StringBuilder();
362 while ((line = reader.readLine()) != null) {
363 sb.append((line + "\n"));
368 } catch (IOException e) {
372 return sb.toString();
375 protected static String convertStreamToString(InputStream is)
377 return convertStreamToString(is, "ISO-8859-1");
380 protected String getDefaultEncoding() {
385 * Gets all values of all query parameters in an URL.
387 public static Map<String, List<String>> getQueryParams(String url) {
389 Map<String, List<String>> params = new HashMap<String, List<String>>();
390 String[] urlParts = url.split("\\?");
391 if (urlParts.length > 1) {
392 String query = urlParts[1];
393 for (String param : query.split("&")) {
394 String[] pair = param.split("=");
395 String key = URLDecoder.decode(pair[0], "UTF-8");
397 if (pair.length > 1) {
398 value = URLDecoder.decode(pair[1], "UTF-8");
401 List<String> values = params.get(key);
402 if (values == null) {
403 values = new ArrayList<String>();
404 params.put(key, values);
411 } catch (UnsupportedEncodingException ex) {
412 throw new AssertionError(ex);
417 * Gets the value for every query parameter in the URL. If a parameter name
418 * occurs twice or more, only the first occurance is interpreted by this
421 public static Map<String, String> getQueryParamsFirst(String url) {
423 Map<String, String> params = new HashMap<String, String>();
424 String[] urlParts = url.split("\\?");
425 if (urlParts.length > 1) {
426 String query = urlParts[1];
427 for (String param : query.split("&")) {
428 String[] pair = param.split("=");
429 String key = URLDecoder.decode(pair[0], "UTF-8");
431 if (pair.length > 1) {
432 value = URLDecoder.decode(pair[1], "UTF-8");
435 String values = params.get(key);
436 if (values == null) {
437 params.put(key, value);
443 } catch (UnsupportedEncodingException ex) {
444 throw new AssertionError(ex);