Clover Coverage Report
Coverage timestamp: Fri May 24 2013 13:47:27 UTC
../../../../img/srcFileCovDistChart6.png 80% of files have more coverage
105   370   42   11.67
42   219   0.4   4.5
9     4.67  
2    
 
  UrlUtility       Line # 45 105 42 51.9% 0.5192308
  UrlUtility.LazyHolderTimeoutMethods       Line # 334 0 0 - -1.0
 
  (109)
 
1    /* This file is part of the project "Hilbert II" - http://www.qedeq.org
2    *
3    * Copyright 2000-2013, Michael Meyling <mime@qedeq.org>.
4    *
5    * "Hilbert II" is free software; you can redistribute
6    * it and/or modify it under the terms of the GNU General Public
7    * License as published by the Free Software Foundation; either
8    * version 2 of the License, or (at your option) any later version.
9    *
10    * This program is distributed in the hope that it will be useful,
11    * but WITHOUT ANY WARRANTY; without even the implied warranty of
12    * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13    * GNU General Public License for more details.
14    */
15   
16    package org.qedeq.base.io;
17   
18    import java.io.File;
19    import java.io.FileNotFoundException;
20    import java.io.FileOutputStream;
21    import java.io.IOException;
22    import java.io.InputStream;
23    import java.io.UnsupportedEncodingException;
24    import java.lang.reflect.InvocationTargetException;
25    import java.net.HttpURLConnection;
26    import java.net.MalformedURLException;
27    import java.net.URL;
28    import java.net.URLConnection;
29    import java.net.URLDecoder;
30   
31    import org.apache.commons.httpclient.DefaultHttpMethodRetryHandler;
32    import org.apache.commons.httpclient.HttpClient;
33    import org.apache.commons.httpclient.HttpStatus;
34    import org.apache.commons.httpclient.methods.GetMethod;
35    import org.apache.commons.httpclient.params.HttpMethodParams;
36    import org.qedeq.base.trace.Trace;
37    import org.qedeq.base.utility.YodaUtility;
38   
39   
40    /**
41    * A collection of useful static methods for URL s.
42    *
43    * @author Michael Meyling
44    */
 
45    public final class UrlUtility {
46   
47    /** This class, for debugging purpose. */
48    private static final Class CLASS = UrlUtility.class;
49   
50    /**
51    * Constructor, should never be called.
52    */
 
53  0 toggle private UrlUtility() {
54    // don't call me
55    }
56   
57    /**
58    * Convert file in URL.
59    *
60    * @param file File.
61    * @return URL.
62    */
 
63  457 toggle public static URL toUrl(final File file) {
64  457 try {
65  457 return file.getAbsoluteFile().toURI().toURL();
66    } catch (MalformedURLException e) { // should only happen if there is a bug in the JDK
67  0 throw new RuntimeException(e);
68    }
69    }
70   
71    /**
72    * Convert URL path in file path.
73    *
74    * @param url Convert this URL path.
75    * @return File path.
76    */
 
77  59353 toggle public static File transformURLPathToFilePath(final URL url) {
78  59353 try {
79  59353 return new File(URLDecoder.decode(url.getFile(), "UTF-8"));
80    } catch (UnsupportedEncodingException e) {
81  0 throw new RuntimeException(e);
82    }
83    }
84   
85    /**
86    * Create relative address from <code>origin</code> to <code>next</code>.
87    * The resulting file path has "/" as directory name separator.
88    * If the resulting file path is the same as origin specifies, we return "".
89    * Otherwise the result will always have an "/" as last character.
90    *
91    * @param origin This is the original location. Must be a directory.
92    * @param next This should be the next location. Must also be a directory.
93    * @return Relative (or if necessary absolute) file path.
94    */
 
95  0 toggle public static final String createRelativePath(final File origin, final File next) {
96  0 if (origin.equals(next)) {
97  0 return "";
98    }
99  0 final Path org = new Path(origin.getPath().replace(File.separatorChar, '/'), "");
100  0 final Path ne = new Path(next.getPath().replace(File.separatorChar, '/'), "");
101  0 return org.createRelative(ne.toString()).toString();
102    }
103   
104    /**
105    * Simplify file URL by returning a file path.
106    *
107    * @param url URL to simplify.
108    * @return File path (if protocol is "file"). Otherwise just return <code>url</code>.
109    */
 
110  2 toggle public static String easyUrl(final String url) {
111  2 String result = url;
112  2 try {
113  2 final URL u = new URL(url);
114    // is this a file URL?
115  2 if (u.getProtocol().equalsIgnoreCase("file")) {
116  1 return transformURLPathToFilePath(u).getCanonicalPath();
117    }
118    } catch (RuntimeException e) {
119    // ignore
120    } catch (IOException e) {
121    // ignore
122    }
123  1 return result;
124    }
125   
126    /**
127    * Make local copy of an URL.
128    *
129    * @param url Save this URL.
130    * @param f Save into this file. An existing file is overwritten.
131    * @param proxyHost Use this proxy host.
132    * @param proxyPort Use this port at proxy host.
133    * @param nonProxyHosts This are hosts not to be proxied.
134    * @param connectTimeout Connection timeout.
135    * @param readTimeout Read timeout.
136    * @param listener Here completion events are fired.
137    * @throws IOException Saving failed.
138    */
 
139  7 toggle public static void saveUrlToFile(final String url, final File f, final String proxyHost,
140    final String proxyPort, final String nonProxyHosts, final int connectTimeout,
141    final int readTimeout, final LoadingListener listener)
142    throws IOException {
143  7 final String method = "saveUrlToFile()";
144  7 Trace.begin(CLASS, method);
145   
146    // if we are not web started and running under Java 1.4 we use apache commons
147    // httpclient library (so we can set timeouts)
148  7 if (!isSetConnectionTimeOutSupported()
149    && !IoUtility.isWebStarted()) {
150  0 saveQedeqFromWebToBufferApache(url, f, proxyHost, proxyPort, nonProxyHosts, connectTimeout,
151    readTimeout, listener);
152  0 Trace.end(CLASS, method);
153  0 return;
154    }
155   
156    // set proxy properties according to kernel configuration (if not webstarted)
157  7 if (!IoUtility.isWebStarted()) {
158  7 if (proxyHost != null) {
159  0 System.setProperty("http.proxyHost", proxyHost);
160    }
161  7 if (proxyPort != null) {
162  0 System.setProperty("http.proxyPort", proxyPort);
163    }
164  7 if (nonProxyHosts != null) {
165  0 System.setProperty("http.nonProxyHosts", nonProxyHosts);
166    }
167    }
168   
169  7 FileOutputStream out = null;
170  7 InputStream in = null;
171  7 try {
172  7 final URLConnection connection = new URL(url).openConnection();
173   
174  7 if (connection instanceof HttpURLConnection) {
175  7 final HttpURLConnection httpConnection = (HttpURLConnection) connection;
176    // if we are running at least under Java 1.5 the following code should be executed
177  7 if (isSetConnectionTimeOutSupported()) {
178  7 try {
179  7 YodaUtility.executeMethod(httpConnection, "setConnectTimeout",
180    new Class[] {Integer.TYPE}, new Object[] {new Integer(
181    connectTimeout)});
182    } catch (NoSuchMethodException e) {
183  0 Trace.fatal(CLASS, method,
184    "URLConnection.setConnectTimeout was previously found", e);
185    } catch (InvocationTargetException e) {
186  0 Trace.fatal(CLASS, method,
187    "URLConnection.setConnectTimeout throwed an error", e);
188    }
189    }
190    // if we are running at least under Java 1.5 the following code should be executed
191  7 if (isSetReadTimeoutSupported()) {
192  7 try {
193  7 YodaUtility.executeMethod(httpConnection, "setReadTimeout",
194    new Class[] {Integer.TYPE}, new Object[] {new Integer(
195    readTimeout)});
196    } catch (NoSuchMethodException e) {
197  0 Trace.fatal(CLASS, method,
198    "URLConnection.setReadTimeout was previously found", e);
199    } catch (InvocationTargetException e) {
200  0 Trace.fatal(CLASS, method,
201    "URLConnection.setReadTimeout throwed an error", e);
202    }
203    }
204  7 int responseCode = httpConnection.getResponseCode();
205  7 if (responseCode == 200) {
206  1 in = httpConnection.getInputStream();
207    } else {
208  6 in = httpConnection.getErrorStream();
209  6 final String errorText = IoUtility.loadStreamWithoutException(in, 1000);
210  6 throw new IOException("Response code from HTTP server was " + responseCode
211  6 + (errorText.length() > 0 ? "\nResponse text from HTTP server was:\n"
212    + errorText : ""));
213    }
214    } else {
215  0 Trace.paramInfo(CLASS, method, "connection.getClass", connection.getClass()
216    .toString());
217  0 in = connection.getInputStream();
218    }
219   
220  1 if (!url.equals(connection.getURL().toString())) {
221  0 throw new FileNotFoundException("\"" + url + "\" was substituted by "
222    + "\"" + connection.getURL() + "\" from server");
223    }
224  1 final double maximum = connection.getContentLength();
225  1 IoUtility.createNecessaryDirectories(f);
226  1 out = new FileOutputStream(f);
227  1 final byte[] buffer = new byte[4096];
228  1 int bytesRead; // bytes read during one buffer read
229  1 int position = 0; // current reading position within the whole document
230    // continue writing
231  ? while ((bytesRead = in.read(buffer)) != -1) {
232  1 position += bytesRead;
233  1 out.write(buffer, 0, bytesRead);
234  1 if (maximum > 0) {
235  1 double completeness = position / maximum;
236  1 if (completeness < 0) {
237  0 completeness = 0;
238    }
239  1 if (completeness > 100) {
240  0 completeness = 1;
241    }
242  1 listener.loadingCompletenessChanged(completeness);
243    }
244    }
245  1 listener.loadingCompletenessChanged(1);
246    } finally {
247  7 IoUtility.close(out);
248  7 out = null;
249  7 IoUtility.close(in);
250  7 in = null;
251  7 Trace.end(CLASS, method);
252    }
253    }
254   
255    /**
256    * Make local copy of a http accessable URL. This method uses apaches HttpClient,
257    * but it dosn't work under webstart with proxy configuration. If we don't use this
258    * method, the apache commons-httpclient library can be removed
259    *
260    * @param url Save this URL.
261    * @param f Save into this file. An existing file is overwritten.
262    * @param proxyHost Use this proxy host.
263    * @param proxyPort Use this port at proxy host.
264    * @param nonProxyHosts This are hosts not to be proxied.
265    * @param connectTimeout Connection timeout.
266    * @param readTimeout Read timeout.
267    * @param listener Here completion events are fired.
268    * @throws IOException Saving failed.
269    */
 
270  0 toggle private static void saveQedeqFromWebToBufferApache(final String url, final File f,
271    final String proxyHost, final String proxyPort,
272    final String nonProxyHosts, final int connectTimeout, final int readTimeout,
273    final LoadingListener listener)
274    throws IOException {
275  0 final String method = "saveQedeqFromWebToBufferApache()";
276  0 Trace.begin(CLASS, method);
277   
278    // Create an instance of HttpClient.
279  0 HttpClient client = new HttpClient();
280   
281    // set proxy properties according to kernel configuration (if not webstarted)
282  0 if (!IoUtility.isWebStarted() && proxyHost != null && proxyHost.length() > 0) {
283  0 final String pHost = proxyHost;
284  0 int pPort = 80;
285  0 if (proxyPort != null) {
286  0 try {
287  0 pPort = Integer.parseInt(proxyPort);
288    } catch (RuntimeException e) {
289  0 Trace.fatal(CLASS, method, "proxy port not numeric: "
290    + proxyPort, e);
291    }
292    }
293  0 if (pHost.length() > 0) {
294  0 client.getHostConfiguration().setProxy(pHost, pPort);
295    }
296    }
297   
298    // Create a method instance.
299  0 GetMethod httpMethod = new GetMethod(url);
300   
301  0 try {
302    // Provide custom retry handler is necessary
303  0 httpMethod.getParams().setParameter(HttpMethodParams.RETRY_HANDLER,
304    new DefaultHttpMethodRetryHandler(3, false));
305   
306  0 httpMethod.getParams().setSoTimeout(connectTimeout);
307    // Throws IOException on TimeOut.
308   
309  0 int statusCode = client.executeMethod(httpMethod);
310   
311  0 if (statusCode != HttpStatus.SC_OK) {
312  0 throw new FileNotFoundException("Problems loading: " + url + "\n"
313    + httpMethod.getStatusLine());
314    }
315   
316    // Read the response body.
317  0 byte[] responseBody = httpMethod.getResponseBody();
318  0 IoUtility.createNecessaryDirectories(f);
319  0 IoUtility.saveFileBinary(f, responseBody);
320  0 listener.loadingCompletenessChanged(1);
321    } finally {
322    // Release the connection.
323  0 httpMethod.releaseConnection();
324  0 Trace.end(CLASS, method);
325    }
326    }
327   
328   
329    /**
330    * This class ist just for solving the lazy loading problem thread save.
331    * see <a href="http://en.wikipedia.org/wiki/Initialization_on_demand_holder_idiom">
332    * Initialization_on_demand_holder_idiom</a>.
333    */
 
334    private static final class LazyHolderTimeoutMethods {
335   
336    /** Lazy initialized constant that knows about the existence of the method
337    * <code>URLConnection.setConnectTimeout</code>. This depends on the currently running
338    * JVM. */
339    private static final boolean IS_SET_CONNECTION_TIMEOUT_SUPPORTED = YodaUtility.existsMethod(
340    URLConnection.class, "setConnectTimeout",
341    new Class[] {Integer.TYPE});
342   
343    /** Lazy initialized constant that knows about the existence of the method
344    * <code>URLConnection.setReadTimeout</code>. This depends on the currently running
345    * JVM. */
346    private static final boolean IS_SET_READ_TIMEOUT_SUSPPORTED = YodaUtility.existsMethod(
347    URLConnection.class, "setReadTimeout",
348    new Class[] {Integer.TYPE});
349   
350    }
351   
352    /**
353    * Is setting of connection timeout supported in current environment?
354    *
355    * @return Setting connection timeout supported?
356    */
 
357  14 toggle public static boolean isSetConnectionTimeOutSupported() {
358  14 return LazyHolderTimeoutMethods.IS_SET_CONNECTION_TIMEOUT_SUPPORTED;
359    }
360   
361    /**
362    * Is setting of read timeout supported in current environment?
363    *
364    * @return Setting read timeout supported?
365    */
 
366  7 toggle public static boolean isSetReadTimeoutSupported() {
367  7 return LazyHolderTimeoutMethods.IS_SET_READ_TIMEOUT_SUSPPORTED;
368    }
369   
370    }