Clover Coverage Report
Coverage timestamp: Thu Feb 13 2014 22:50:26 UTC
../../../../img/srcFileCovDistChart5.png 78% of files have more coverage
104   376   43   10.4
42   220   0.41   5
10     4.3  
2    
 
  UrlUtility       Line # 45 104 42 49% 0.4903226
  UrlUtility.LazyHolderTimeoutMethods       Line # 333 0 1 0% 0.0
 
  (7)
 
1    /* This file is part of the project "Hilbert II" - http://www.qedeq.org
2    *
3    * Copyright 2000-2014, 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  26 toggle public static URL toUrl(final File file) {
64  26 try {
65  26 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  3 toggle public static File transformURLPathToFilePath(final URL url) {
78  3 try {
79  3 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  1 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  1 final String method = "saveUrlToFile()";
144  1 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  1 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  1 if (!IoUtility.isWebStarted()) {
158  1 if (proxyHost != null) {
159  0 System.setProperty("http.proxyHost", proxyHost);
160    }
161  1 if (proxyPort != null) {
162  0 System.setProperty("http.proxyPort", proxyPort);
163    }
164  1 if (nonProxyHosts != null) {
165  0 System.setProperty("http.nonProxyHosts", nonProxyHosts);
166    }
167    }
168   
169  1 FileOutputStream out = null;
170  1 InputStream in = null;
171  1 try {
172  1 final URLConnection connection = new URL(url).openConnection();
173   
174  1 if (connection instanceof HttpURLConnection) {
175  1 final HttpURLConnection httpConnection = (HttpURLConnection) connection;
176    // if we are running at least under Java 1.5 the following code should be executed
177  1 if (isSetConnectionTimeOutSupported()) {
178  1 try {
179  1 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  1 if (isSetReadTimeoutSupported()) {
192  1 try {
193  1 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  1 int responseCode = httpConnection.getResponseCode();
205  1 if (responseCode == 200) {
206  1 in = httpConnection.getInputStream();
207    } else {
208  0 in = httpConnection.getErrorStream();
209  0 final String errorText = IoUtility.loadStreamWithoutException(in, 1000);
210  0 throw new IOException("Response code from HTTP server was " + responseCode
211  0 + (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  1 IoUtility.close(out);
248  1 out = null;
249  1 IoUtility.close(in);
250  1 in = null;
251  1 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.saveFileBinary(f, responseBody);
319  0 listener.loadingCompletenessChanged(1);
320    } finally {
321    // Release the connection.
322  0 httpMethod.releaseConnection();
323  0 Trace.end(CLASS, method);
324    }
325    }
326   
327   
328    /**
329    * This class ist just for solving the lazy loading problem thread save.
330    * see <a href="http://en.wikipedia.org/wiki/Initialization_on_demand_holder_idiom">
331    * Initialization_on_demand_holder_idiom</a>.
332    */
 
333    private static final class LazyHolderTimeoutMethods {
334   
335    /**
336    * Constructor, should never be called.
337    */
 
338  0 toggle private LazyHolderTimeoutMethods() {
339    // don't call me
340    }
341   
342    /** Lazy initialized constant that knows about the existence of the method
343    * <code>URLConnection.setConnectTimeout</code>. This depends on the currently running
344    * JVM. */
345    private static final boolean IS_SET_CONNECTION_TIMEOUT_SUPPORTED = YodaUtility.existsMethod(
346    URLConnection.class, "setConnectTimeout",
347    new Class[] {Integer.TYPE});
348   
349    /** Lazy initialized constant that knows about the existence of the method
350    * <code>URLConnection.setReadTimeout</code>. This depends on the currently running
351    * JVM. */
352    private static final boolean IS_SET_READ_TIMEOUT_SUSPPORTED = YodaUtility.existsMethod(
353    URLConnection.class, "setReadTimeout",
354    new Class[] {Integer.TYPE});
355   
356    }
357   
358    /**
359    * Is setting of connection timeout supported in current environment?
360    *
361    * @return Setting connection timeout supported?
362    */
 
363  2 toggle public static boolean isSetConnectionTimeOutSupported() {
364  2 return LazyHolderTimeoutMethods.IS_SET_CONNECTION_TIMEOUT_SUPPORTED;
365    }
366   
367    /**
368    * Is setting of read timeout supported in current environment?
369    *
370    * @return Setting read timeout supported?
371    */
 
372  1 toggle public static boolean isSetReadTimeoutSupported() {
373  1 return LazyHolderTimeoutMethods.IS_SET_READ_TIMEOUT_SUSPPORTED;
374    }
375   
376    }