Merge branch 'release/5.0.0'
[cdmlib.git] / cdmlib-commons / src / main / java / eu / etaxonomy / cdm / common / UriUtils.java
1 /**
2 * Copyright (C) 2007 EDIT
3 * European Distributed Institute of Taxonomy
4 * http://www.e-taxonomy.eu
5 *
6 * The contents of this file are subject to the Mozilla Public License Version 1.1
7 * See LICENSE.TXT at the top of this package for the full license terms.
8 */
9
10 package eu.etaxonomy.cdm.common;
11
12 import java.io.File;
13 import java.io.FileInputStream;
14 import java.io.IOException;
15 import java.io.InputStream;
16 import java.net.InetAddress;
17 import java.net.MalformedURLException;
18 import java.net.Socket;
19 import java.net.URI;
20 import java.net.URISyntaxException;
21 import java.net.URL;
22 import java.net.UnknownHostException;
23 import java.security.KeyManagementException;
24 import java.security.NoSuchAlgorithmException;
25 import java.security.cert.X509Certificate;
26 import java.util.ArrayList;
27 import java.util.HashMap;
28 import java.util.List;
29 import java.util.Map;
30 import java.util.Map.Entry;
31
32 import javax.net.ssl.SSLContext;
33 import javax.net.ssl.TrustManager;
34 import javax.net.ssl.X509TrustManager;
35
36 import org.apache.http.Header;
37 import org.apache.http.HttpEntity;
38 import org.apache.http.HttpException;
39 import org.apache.http.HttpResponse;
40 import org.apache.http.HttpStatus;
41 import org.apache.http.NameValuePair;
42 import org.apache.http.StatusLine;
43 import org.apache.http.client.ClientProtocolException;
44 import org.apache.http.client.HttpClient;
45 import org.apache.http.client.config.RequestConfig;
46 import org.apache.http.client.methods.HttpGet;
47 import org.apache.http.client.methods.HttpPost;
48 import org.apache.http.client.methods.HttpUriRequest;
49 import org.apache.http.client.utils.URIBuilder;
50 import org.apache.http.client.utils.URLEncodedUtils;
51 import org.apache.http.conn.scheme.Scheme;
52 import org.apache.http.conn.ssl.SSLSocketFactory;
53 import org.apache.http.impl.client.CloseableHttpClient;
54 import org.apache.http.impl.client.DefaultHttpClient;
55 import org.apache.http.impl.client.HttpClients;
56 import org.apache.log4j.Logger;
57
58 /**
59 * @author n.hoffmann
60 * @since Sep 23, 2010
61 * @version 1.0
62 */
63 public class UriUtils {
64 private static final Logger logger = Logger.getLogger(UriUtils.class);
65
66 protected static final String URI_IS_NOT_ABSOLUTE = "URI is not absolute (protocol is missing)";
67
68
69 public enum HttpMethod{
70 GET,
71 POST
72 }
73
74 /**
75 * see {@link #getInputStream(URI, Map)}
76 *
77 * @param uri
78 * @return
79 * @throws IOException
80 * @throws HttpException
81 */
82 public static InputStream getInputStream(URI uri) throws IOException, HttpException{
83 return getInputStream(uri, null);
84 }
85
86 /**
87 * Retrieves an {@link InputStream input stream} of the resource located at the given uri.
88 *
89 * @param uri
90 * @return
91 * @throws IOException
92 * @throws HttpException
93 */
94 public static InputStream getInputStream(URI uri, Map<String, String> requestHeaders) throws IOException, HttpException{
95
96 if(requestHeaders == null){
97 requestHeaders = new HashMap<String, String>();
98 }
99
100 if (uri.getScheme().equals("http") || uri.getScheme().equals("https")){
101 HttpResponse response = UriUtils.getResponse(uri, requestHeaders);
102 if(UriUtils.isOk(response)){
103 InputStream stream = getContent(response);
104 return stream;
105 } else {
106 throw new HttpException("HTTP Reponse code is not = 200 (OK): " + UriUtils.getStatus(response));
107 }
108 }else if (uri.getScheme().equals("file")){
109 File file = new File(uri);
110 return new FileInputStream(file);
111 }else{
112 throw new RuntimeException("Protocol not handled yet: " + uri.getScheme());
113 }
114 }
115
116 /**
117 * Retrieves the size of the resource defined by the given uri in bytes
118 *
119 * @param uri the resource
120 * @param requestHeaders additional headers. May be <code>null</code>
121 * @return the size of the resource in bytes
122 *
123 * @throws ClientProtocolException
124 * @throws IOException
125 * @throws HttpException
126 */
127 public static long getResourceLength(URI uri, Map<String, String> requestHeaders) throws ClientProtocolException, IOException, HttpException{
128 if(requestHeaders == null){
129 requestHeaders = new HashMap<String, String>();
130 }
131
132 if(! uri.isAbsolute()){
133 throw new IOException(URI_IS_NOT_ABSOLUTE);
134 }else if ("http".equals(uri.getScheme()) || "https".equals(uri.getScheme())){
135 HttpResponse response = UriUtils.getResponse(uri, requestHeaders);
136 if(UriUtils.isOk(response)){
137 Header[] contentLengths = response.getHeaders("Content-Length");
138
139 if(contentLengths == null || contentLengths.length == 0){
140 throw new HttpException("Could not retrieve Content-Length");
141 }
142
143 if(contentLengths.length > 1){
144 throw new HttpException("Multiple Conten-Length headers sent");
145 }
146
147 Header contentLength = contentLengths[0];
148 String value = contentLength.getValue();
149
150 return Long.valueOf(value);
151
152 } else {
153 throw new HttpException("HTTP Reponse code is not = 200 (OK): " + UriUtils.getStatus(response));
154 }
155 }else if ("file".equals(uri.getScheme())){
156 File file = new File(uri);
157 return file.length();
158 }else{
159 throw new RuntimeException("Protocol not handled yet: " + uri.getScheme());
160 }
161 }
162
163 /**
164 * Checks if the given HTTP return status is OK
165 * @param response the {@link HttpResponse} to check
166 * @return <code>true</code> if response is OK, <code>false</code> otherwise
167 */
168 public static boolean isOk(HttpResponse response){
169 return response.getStatusLine().getStatusCode() == HttpStatus.SC_OK;
170 }
171
172 /**
173 * Retrieves the content of an {@link HttpResponse} as an {@link InputStream}
174 * @param response the HTTPResponse to retrieve the content from
175 * @return the content as InputStream
176 * @throws IOException
177 */
178 public static InputStream getContent(HttpResponse response) throws IOException{
179 return response.getEntity().getContent();
180 }
181
182 /**
183 * Gets the status of the given {@link HttpResponse} as a string
184 * @param response the response to get the status for
185 * @return status as a string
186 */
187 public static String getStatus(HttpResponse response){
188 StatusLine statusLine = response.getStatusLine();
189 return "(" + statusLine.getStatusCode() + ")" + statusLine.getReasonPhrase();
190 }
191
192 /**
193 * Sends a HTTP GET request to the defined URI and returns the {@link HttpResponse}.
194 * @param uri the URI of this HTTP request
195 * @param requestHeaders the parameters (name-value pairs) of the connection added to the header of the request
196 * @return the {@link HttpResponse} of the request
197 * @throws IOException
198 * @throws ClientProtocolException
199 */
200 public static HttpResponse getResponse(URI uri, Map<String, String> requestHeaders) throws ClientProtocolException, IOException{
201 return getResponseByType(uri, requestHeaders, HttpMethod.GET, null);
202 }
203
204 /**
205 * Sends a HTTP POST request to the defined URI and returns the {@link HttpResponse}.
206 * @param uri the URI of this HTTP request
207 * @param requestHeaders the parameters (name-value pairs) of the connection added to the header of the request
208 * @param entity the {@link HttpEntity} attached to a HTTP POST request
209 * @return the {@link HttpResponse} of the request
210 * @throws IOException
211 * @throws ClientProtocolException
212 */
213 public static HttpResponse getPostResponse(URI uri, Map<String, String> requestHeaders, HttpEntity entity) throws ClientProtocolException, IOException{
214 return getResponseByType(uri, requestHeaders, HttpMethod.POST, entity);
215 }
216
217 /**
218 * Sends a HTTP request of the given {@link HttpMethod} to the defined URI and returns the {@link HttpResponse}.
219 * @param uri the URI of this HTTP request
220 * @param requestHeaders the parameters (name-value pairs) of the connection added to the header of the request
221 * @param httpMethod defines if method is POST or GET
222 * @param entity the {@link HttpEntity} attached to a HTTP POST request
223 * @return the {@link HttpResponse} of the request
224 * @throws IOException
225 * @throws ClientProtocolException
226 */
227 public static HttpResponse getResponseByType(URI uri, Map<String, String> requestHeaders, HttpMethod httpMethod, HttpEntity entity) throws IOException, ClientProtocolException {
228 // Create an instance of HttpClient.
229 HttpClient client = new DefaultHttpClient();
230
231 try {
232 SSLContext sc = SSLContext.getInstance("SSL");
233 sc.init(null, getTrustingManager(), new java.security.SecureRandom());
234 SSLSocketFactory socketFactory = new SSLSocketFactory(sc);
235 Scheme sch = new Scheme("https", 443, socketFactory);
236 client.getConnectionManager().getSchemeRegistry().register(sch);
237 } catch (KeyManagementException e1) {
238 throw new RuntimeException("Registration of ssl support failed", e1);
239 } catch (NoSuchAlgorithmException e2) {
240 throw new RuntimeException("Registration of ssl support failed", e2);
241 }
242
243
244 HttpUriRequest method;
245 switch (httpMethod) {
246 case GET:
247 method = new HttpGet(uri);
248 break;
249 case POST:
250 HttpPost httpPost = new HttpPost(uri);
251 if(entity!=null){
252 httpPost.setEntity(entity);
253 }
254 method = httpPost;
255 break;
256 default:
257 method = new HttpPost(uri);
258 break;
259 }
260
261 // configure the connection
262 if(requestHeaders != null){
263 for(Entry<String, String> e : requestHeaders.entrySet()){
264 method.addHeader(e.getKey(), e.getValue());
265 }
266 }
267
268 //TODO method.setFollowRedirects(followRedirects);
269
270 logger.debug("sending "+httpMethod+" request: " + uri);
271
272 return client.execute(method);
273 }
274
275 /**
276 * Creates a {@link URI} based on the baseUrl and the given subPath, qParams and fragment
277 * @param subPath the sub path of the URI
278 * @param qparams the parameters added as GET parameters to the URI
279 * @param fragment the fragment of the URI
280 * @return a URI consisting of the baseURL, the subPath and qParams
281 * @throws URISyntaxException
282 */
283 public static URI createUri(URL baseUrl, String subPath, List<NameValuePair> qparams, String fragment) throws URISyntaxException {
284
285 String path = baseUrl.getPath();
286
287 if(subPath != null){
288 if(!path.endsWith("/")){
289 path += "/";
290 }
291 if(subPath.startsWith("/")){
292 subPath = subPath.substring(1);
293 }
294 path += subPath;
295 }
296
297 if(qparams == null){
298 qparams = new ArrayList<NameValuePair>(0);
299 }
300 String query = null;
301 if(! qparams.isEmpty()){
302 query = URLEncodedUtils.format(qparams, "UTF-8");
303 }
304
305 URIBuilder uriBuilder = new URIBuilder();
306 uriBuilder.setScheme(baseUrl.getProtocol());
307 uriBuilder.setHost(baseUrl.getHost());
308 uriBuilder.setPort(baseUrl.getPort());
309 uriBuilder.setPath(path);
310 uriBuilder.setQuery(query);
311 uriBuilder.setFragment(fragment);
312 return uriBuilder.build();
313 }
314
315 /**
316 * Tests internet connectivity by testing HEAD request for 4 known URL's.<BR>
317 * If non of them is available <code>false</code> is returned. Otherwise true.<BR>
318 * @param firstUriToTest if not <code>null</code> this URI is tested before testing the standard URLs.
319 * @return true if internetconnectivity is given.
320 */
321 public static boolean isInternetAvailable(URI firstUriToTest){
322 boolean result = false;
323 if (firstUriToTest != null && isServiceAvailable(firstUriToTest)){
324 return true;
325 }
326
327 URI uri = URI.create("http://www.cnn.com/");
328 if (isServiceAvailable(uri)){
329 return true;
330 }
331 uri = URI.create("http://www.bahn.de/");
332 if (isServiceAvailable(uri)){
333 return true;
334 }
335 uri = URI.create("http://www.google.com/");
336 if (isServiceAvailable(uri)){
337 return true;
338 }
339 uri = URI.create("http://www.facebook.com/");
340 if (isServiceAvailable(uri)){
341 return true;
342 }
343
344 return result;
345 }
346
347 /**
348 * Performs HEAD request for the given URI.<BR>
349 * If any exception occurs <code>false</code> is returned. Otherwise true. <BR>
350 * @param serviceUri the URI to test.
351 * @return true if service is available, false otherwise. Also a non-absolute URI will return false.
352 */
353 public static boolean isServiceAvailable(URI serviceUri){
354 return isServiceAvailable(serviceUri, null);
355 }
356
357 /**
358 * Performs HEAD request for the given URI.<BR>
359 * If any exception occurs <code>false</code> is returned. Otherwise true. <BR>
360 * @param serviceUri the URI to test.
361 * @param timeout the timeout of the request in milliseconds
362 * @return true if service is available, false otherwise. Also a non-absolute URI will return false.
363 */
364 public static boolean isServiceAvailable(URI serviceUri, Integer timeout){
365 boolean result = false;
366
367 if(serviceUri==null || serviceUri.getHost()==null || !serviceUri.isAbsolute()){
368 return false;
369 }
370
371 //Http
372 CloseableHttpClient httpclient = HttpClients.createDefault();
373 HttpGet httpget = new HttpGet(serviceUri);
374
375
376 if(timeout!=null){
377
378 RequestConfig requestConfig = RequestConfig.custom()
379 .setSocketTimeout(timeout)
380 .setConnectTimeout(timeout)
381 .setConnectionRequestTimeout(timeout)
382 .build();
383 httpget.setConfig(requestConfig);
384 }
385
386 try {
387 // Execute the request
388 HttpResponse response = httpclient.execute(httpget);
389 // Examine the response status
390 if (logger.isDebugEnabled()){
391 logger.debug(response.getStatusLine());
392 }
393 result = true;
394
395 } catch (UnknownHostException e1) {
396 logger.info("Unknown Host: " +e1.getMessage());
397 } catch (ClientProtocolException e2) {
398 logger.info("ClientProtocolException: " + e2.getMessage());
399 } catch (IOException e3) {
400 logger.info("IOException: " + e3.getMessage());
401 }
402
403 // When HttpClient instance is no longer needed,
404 // shut down the connection manager to ensure
405 // immediate deallocation of all system resources
406 //needed ?
407 // client.getConnectionManager().shutdown();
408
409 return result;
410 }
411
412 /**
413 * Tests reachability of a root server by trying to resolve a host name.
414 * @param hostNameToResolve the host name to resolve. If <code>null</code>
415 * a default host name is tested.
416 * @return
417 */
418 public static boolean isRootServerAvailable(String hostNameToResolve){
419 try {
420 if (hostNameToResolve == null){
421 hostNameToResolve = "cnn.com";
422 }
423 InetAddress inetHost = InetAddress.getByName(hostNameToResolve);
424 logger.debug("The hosts IP address is: " + inetHost.getHostAddress());
425 return true;
426 } catch(UnknownHostException ex) {
427 logger.info("Unrecognized host");
428 return false;
429 }
430 }
431
432 //from http://www.javabeginners.de/Netzwerk/File_zu_URL.php
433 public static URL fileToURL(File file){
434 URL url = null;
435 try {
436 // Sonderzeichen (z.B. Leerzeichen) bleiben erhalten
437 url = new URL("file://" + file.getPath());
438 // Sonderzeichen (z.B. Leerzeichen) werden codiert
439 url = file.toURI().toURL();
440 } catch (MalformedURLException e) {
441 e.printStackTrace();
442 }
443 return url;
444 }
445
446 //from http://blogs.sphinx.at/java/erzeugen-von-javaiofile-aus-javaneturl/
447 public static File urlToFile(URL url) {
448 URI uri;
449 try {
450 // this is the step that can fail, and so
451 // it should be this step that should be fixed
452 uri = url.toURI();
453 } catch (URISyntaxException e) {
454 // OK if we are here, then obviously the URL did
455 // not comply with RFC 2396. This can only
456 // happen if we have illegal unescaped characters.
457 // If we have one unescaped character, then
458 // the only automated fix we can apply, is to assume
459 // all characters are unescaped.
460 // If we want to construct a URI from unescaped
461 // characters, then we have to use the component
462 // constructors:
463 try {
464 uri = new URI(url.getProtocol(), url.getUserInfo(), url
465 .getHost(), url.getPort(), url.getPath(), url
466 .getQuery(), url.getRef());
467 } catch (URISyntaxException e1) {
468 throw new IllegalArgumentException("broken URL: " + url);
469 }
470 }
471 return new File(uri);
472 }
473
474 public static boolean checkServiceAvailable(String host, int port){
475 boolean success = true;
476 try {
477 (new Socket(host, port)).close();
478 } catch (UnknownHostException e) {
479 // unknown host
480 success = false;
481 } catch (IOException e) {
482 // io exception, service probably not running
483 success = false;
484 }
485 return success;
486 }
487
488 private static TrustManager[] getTrustingManager() {
489 TrustManager[] trustAllCerts = new TrustManager[] { new X509TrustManager() {
490 @Override
491 public java.security.cert.X509Certificate[] getAcceptedIssuers() {
492 return null;
493 }
494
495 @Override
496 public void checkClientTrusted(X509Certificate[] certs, String authType) {
497 // Do nothing
498 }
499
500 @Override
501 public void checkServerTrusted(X509Certificate[] certs, String authType) {
502 // Do nothing
503 }
504
505 } };
506 return trustAllCerts;
507 }
508 }