OSDN Git Service

Initial commit
[worldopac/WorldOpac.git] / WorldOpac / src / de / geeksfactory / opacclient / apis / BaseApi.java
1 /**
2  * Copyright (C) 2013 by Raphael Michel under the MIT license:
3  * 
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:
10  * 
11  * The above copyright notice and this permission notice shall be included in 
12  * all copies or substantial portions of the Software.
13  * 
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.
21  */
22 package de.geeksfactory.opacclient.apis;
23
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;
35 import java.util.Map;
36
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;
55
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;
63
64 /**
65  * Abstract Base class for OpacApi implementations providing some helper methods
66  * for HTTP
67  */
68 public abstract class BaseApi implements OpacApi {
69
70         protected DefaultHttpClient http_client;
71
72         /**
73          * Initializes HTTP client
74          */
75         @Override
76         public void init(MetaDataSource metadata, Library library) {
77                 http_client = HTTPClient.getNewHttpClient(library);
78         }
79
80         /**
81          * Perform a HTTP GET request to a given URL
82          * 
83          * @param url
84          *            URL to fetch
85          * @param encoding
86          *            Expected encoding of the response body
87          * @param ignore_errors
88          *            If true, status codes above 400 do not raise an exception
89          * @param cookieStore
90          *            If set, the given cookieStore is used instead of the built-in
91          *            one.
92          * @return Answer content
93          * @throws NotReachableException
94          *             Thrown when server returns a HTTP status code greater or
95          *             equal than 400.
96          */
97         public String httpGet(String url, String encoding, boolean ignore_errors,
98                         CookieStore cookieStore) throws ClientProtocolException,
99                         IOException {
100
101                 HttpGet httpget = new HttpGet(cleanUrl(url));
102                 HttpResponse response;
103
104                 try {
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,
110                                                 cookieStore);
111
112                                 response = http_client.execute(httpget, localContext);
113                         } else {
114                                 response = http_client.execute(httpget);
115                         }
116                 } catch (ConnectTimeoutException e) {
117                         e.printStackTrace();
118                         throw new NotReachableException();
119                 } catch (UnknownHostException e) {
120                         e.printStackTrace();
121                         throw new NotReachableException();
122                 } catch (IllegalStateException e) {
123                         e.printStackTrace();
124                         throw new NotReachableException();
125                 } catch (NoHttpResponseException e) {
126                         e.printStackTrace();
127                         throw new NotReachableException();
128                 } catch (MalformedChunkCodingException e) {
129                         e.printStackTrace();
130                         throw new NotReachableException();
131                 } catch (javax.net.ssl.SSLPeerUnverifiedException e) {
132                         // TODO: Handly this well
133                         throw e;
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")) {
140                                 e.printStackTrace();
141                                 throw new NotReachableException();
142                         } else {
143                                 throw e;
144                         }
145                 } catch (InterruptedIOException e) {
146                         e.printStackTrace();
147                         throw new NotReachableException();
148                 } catch (IOException e) {
149                         if (e.getMessage() != null
150                                         && e.getMessage().contains("Request aborted")) {
151                                 e.printStackTrace();
152                                 throw new NotReachableException();
153                         } else {
154                                 throw e;
155                         }
156                 }
157
158                 if (!ignore_errors && response.getStatusLine().getStatusCode() >= 400) {
159                         throw new NotReachableException();
160                 }
161                 String html = convertStreamToString(response.getEntity().getContent(),
162                                 encoding);
163                 response.getEntity().consumeContent();
164                 return html;
165         }
166
167         public String httpGet(String url, String encoding, boolean ignore_errors)
168                         throws ClientProtocolException, IOException {
169                 return httpGet(url, encoding, ignore_errors, null);
170         }
171
172         public String httpGet(String url, String encoding)
173                         throws ClientProtocolException, IOException {
174                 return httpGet(url, encoding, false, null);
175         }
176
177         @Deprecated
178         public String httpGet(String url) throws ClientProtocolException,
179                         IOException {
180                 return httpGet(url, getDefaultEncoding(), false, null);
181         }
182
183         public static String cleanUrl(String myURL) {
184                 String[] parts = myURL.split("\\?");
185                 String url = parts[0];
186                 try {
187                         if (parts.length > 1) {
188                                 url += "?";
189                                 List<NameValuePair> params = new ArrayList<NameValuePair>();
190                                 String[] pairs = parts[1].split("&");
191                                 for (String pair : pairs) {
192                                         String[] kv = pair.split("=");
193                                         if (kv.length > 1)
194                                                 params.add(new BasicNameValuePair(URLDecoder.decode(
195                                                                 kv[0], "UTF-8"), URLDecoder.decode(kv[1],
196                                                                 "UTF-8")));
197                                         else
198                                                 params.add(new BasicNameValuePair(URLDecoder.decode(
199                                                                 kv[0], "UTF-8"), ""));
200                                 }
201                                 url += URLEncodedUtils.format(params, "UTF-8");
202                         }
203                         return url;
204                 } catch (UnsupportedEncodingException e) {
205                         e.printStackTrace();
206                         return myURL;
207                 }
208         }
209
210         public void downloadCover(CoverHolder item) {
211                 if (item.getCover() == null)
212                         return;
213                 HttpGet httpget = new HttpGet(cleanUrl(item.getCover()));
214                 HttpResponse response;
215
216                 try {
217                         response = http_client.execute(httpget);
218
219                         if (response.getStatusLine().getStatusCode() >= 400) {
220                                 return;
221                         }
222                         HttpEntity entity = response.getEntity();
223                         byte[] bytes = EntityUtils.toByteArray(entity);
224
225                         Bitmap bitmap = BitmapFactory.decodeByteArray(bytes, 0,
226                                         bytes.length);
227                         item.setCoverBitmap(bitmap);
228
229                 } catch (IOException e) {
230                         e.printStackTrace();
231                         return;
232                 }
233         }
234
235         /**
236          * Perform a HTTP POST request to a given URL
237          * 
238          * @param url
239          *            URL to fetch
240          * @param data
241          *            POST data to send
242          * @param encoding
243          *            Expected encoding of the response body
244          * @param ignore_errors
245          *            If true, status codes above 400 do not raise an exception
246          * @param cookieStore
247          *            If set, the given cookieStore is used instead of the built-in
248          *            one.
249          * @return Answer content
250          * @throws NotReachableException
251          *             Thrown when server returns a HTTP status code greater or
252          *             equal than 400.
253          */
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);
259
260                 HttpResponse response = null;
261                 try {
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,
267                                                 cookieStore);
268
269                                 response = http_client.execute(httppost, localContext);
270                         } else {
271                                 response = http_client.execute(httppost);
272                         }
273                 } catch (ConnectTimeoutException e) {
274                         e.printStackTrace();
275                         throw new NotReachableException();
276                 } catch (IllegalStateException e) {
277                         e.printStackTrace();
278                         throw new NotReachableException();
279                 } catch (UnknownHostException e) {
280                         e.printStackTrace();
281                         throw new NotReachableException();
282                 } catch (NoHttpResponseException e) {
283                         e.printStackTrace();
284                         throw new NotReachableException();
285                 } catch (MalformedChunkCodingException e) {
286                         e.printStackTrace();
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")) {
292                                 e.printStackTrace();
293                                 throw new NotReachableException();
294                         } else {
295                                 throw e;
296                         }
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
301                         throw e;
302                 } catch (InterruptedIOException e) {
303                         e.printStackTrace();
304                         throw new NotReachableException();
305                 } catch (IOException e) {
306                         if (e.getMessage() != null
307                                         && e.getMessage().contains("Request aborted")) {
308                                 e.printStackTrace();
309                                 throw new NotReachableException();
310                         } else {
311                                 throw e;
312                         }
313                 }
314
315                 if (!ignore_errors && response.getStatusLine().getStatusCode() >= 400) {
316                         throw new NotReachableException();
317                 }
318                 String html = convertStreamToString(response.getEntity().getContent(),
319                                 encoding);
320                 response.getEntity().consumeContent();
321                 return html;
322         }
323
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);
328         }
329
330         public String httpPost(String url, UrlEncodedFormEntity data,
331                         String encoding) throws ClientProtocolException, IOException {
332                 return httpPost(url, data, encoding, false, null);
333         }
334
335         @Deprecated
336         public String httpPost(String url, UrlEncodedFormEntity data)
337                         throws ClientProtocolException, IOException {
338                 return httpPost(url, data, getDefaultEncoding(), false, null);
339         }
340
341         /**
342          * Reads content from an InputStream into a string
343          * 
344          * @param is
345          *            InputStream to read from
346          * @return String content of the InputStream
347          */
348         protected static String convertStreamToString(InputStream is,
349                         String encoding) throws IOException {
350                 BufferedReader reader;
351                 try {
352                         reader = new BufferedReader(new InputStreamReader(is, encoding));
353                 } catch (UnsupportedEncodingException e1) {
354                         reader = new BufferedReader(new InputStreamReader(is));
355                 }
356                 StringBuilder sb = new StringBuilder();
357
358                 String line = null;
359                 try {
360                         while ((line = reader.readLine()) != null) {
361                                 sb.append((line + "\n"));
362                         }
363                 } finally {
364                         try {
365                                 is.close();
366                         } catch (IOException e) {
367                                 e.printStackTrace();
368                         }
369                 }
370                 return sb.toString();
371         }
372
373         protected static String convertStreamToString(InputStream is)
374                         throws IOException {
375                 return convertStreamToString(is, "ISO-8859-1");
376         }
377
378         protected String getDefaultEncoding() {
379                 return "ISO-8859-1";
380         }
381
382         /*
383          * Gets all values of all query parameters in an URL.
384          */
385         public static Map<String, List<String>> getQueryParams(String url) {
386                 try {
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");
394                                         String value = "";
395                                         if (pair.length > 1) {
396                                                 value = URLDecoder.decode(pair[1], "UTF-8");
397                                         }
398
399                                         List<String> values = params.get(key);
400                                         if (values == null) {
401                                                 values = new ArrayList<String>();
402                                                 params.put(key, values);
403                                         }
404                                         values.add(value);
405                                 }
406                         }
407
408                         return params;
409                 } catch (UnsupportedEncodingException ex) {
410                         throw new AssertionError(ex);
411                 }
412         }
413
414         /*
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
417          * method
418          */
419         public static Map<String, String> getQueryParamsFirst(String url) {
420                 try {
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");
428                                         String value = "";
429                                         if (pair.length > 1) {
430                                                 value = URLDecoder.decode(pair[1], "UTF-8");
431                                         }
432
433                                         String values = params.get(key);
434                                         if (values == null) {
435                                                 params.put(key, value);
436                                         }
437                                 }
438                         }
439
440                         return params;
441                 } catch (UnsupportedEncodingException ex) {
442                         throw new AssertionError(ex);
443                 }
444         }
445 }