EMMA Coverage Report (generated Fri Feb 14 08:28:31 UTC 2014)
[all classes][org.qedeq.base.io]

COVERAGE SUMMARY FOR SOURCE FILE [UrlUtility.java]

nameclass, %method, %block, %line, %
UrlUtility.java100% (2/2)67%  (8/12)52%  (281/543)49%  (59.2/121)

COVERAGE BREAKDOWN BY CLASS AND METHOD

nameclass, %method, %block, %line, %
     
class UrlUtility100% (1/1)70%  (7/10)50%  (252/505)49%  (57.4/117)
UrlUtility (): void 0%   (0/1)0%   (0/3)0%   (0/2)
createRelativePath (File, File): String 0%   (0/1)0%   (0/32)0%   (0/5)
saveQedeqFromWebToBufferApache (String, File, String, String, String, int, in... 0%   (0/1)0%   (0/113)0%   (0/26)
toUrl (File): URL 100% (1/1)45%  (5/11)33%  (1/3)
transformURLPathToFilePath (URL): File 100% (1/1)57%  (8/14)33%  (1/3)
saveUrlToFile (String, File, String, String, String, int, int, LoadingListene... 100% (1/1)70%  (207/296)70%  (47.2/67)
easyUrl (String): String 100% (1/1)86%  (19/22)67%  (5.3/8)
<static initializer> 100% (1/1)90%  (9/10)90%  (0.9/1)
isSetConnectionTimeOutSupported (): boolean 100% (1/1)100% (2/2)100% (1/1)
isSetReadTimeoutSupported (): boolean 100% (1/1)100% (2/2)100% (1/1)
     
class UrlUtility$LazyHolderTimeoutMethods100% (1/1)50%  (1/2)76%  (29/38)44%  (1.8/4)
UrlUtility$LazyHolderTimeoutMethods (): void 0%   (0/1)0%   (0/3)0%   (0/2)
<static initializer> 100% (1/1)83%  (29/35)88%  (1.8/2)

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 
16package org.qedeq.base.io;
17 
18import java.io.File;
19import java.io.FileNotFoundException;
20import java.io.FileOutputStream;
21import java.io.IOException;
22import java.io.InputStream;
23import java.io.UnsupportedEncodingException;
24import java.lang.reflect.InvocationTargetException;
25import java.net.HttpURLConnection;
26import java.net.MalformedURLException;
27import java.net.URL;
28import java.net.URLConnection;
29import java.net.URLDecoder;
30 
31import org.apache.commons.httpclient.DefaultHttpMethodRetryHandler;
32import org.apache.commons.httpclient.HttpClient;
33import org.apache.commons.httpclient.HttpStatus;
34import org.apache.commons.httpclient.methods.GetMethod;
35import org.apache.commons.httpclient.params.HttpMethodParams;
36import org.qedeq.base.trace.Trace;
37import org.qedeq.base.utility.YodaUtility;
38 
39 
40/**
41 * A collection of useful static methods for URL s.
42 *
43 * @author  Michael Meyling
44 */
45public 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    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    public static URL toUrl(final File file) {
64        try {
65            return file.getAbsoluteFile().toURI().toURL();
66        } catch (MalformedURLException e) { // should only happen if there is a bug in the JDK
67            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    public static File transformURLPathToFilePath(final URL url) {
78        try {
79            return new File(URLDecoder.decode(url.getFile(), "UTF-8"));
80        } catch (UnsupportedEncodingException e) {
81            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    public static final String createRelativePath(final File origin, final File next) {
96        if (origin.equals(next)) {
97            return "";
98        }
99        final Path org = new Path(origin.getPath().replace(File.separatorChar, '/'), "");
100        final Path ne = new Path(next.getPath().replace(File.separatorChar, '/'), "");
101        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    public static String easyUrl(final String url) {
111        String result = url;
112        try {
113            final URL u = new URL(url);
114            // is this a file URL?
115            if (u.getProtocol().equalsIgnoreCase("file")) {
116                return transformURLPathToFilePath(u).getCanonicalPath();
117            }
118        } catch (RuntimeException e) {
119            //  ignore
120        } catch (IOException e) {
121            //  ignore
122        }
123        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    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        final String method = "saveUrlToFile()";
144        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        if (!isSetConnectionTimeOutSupported()
149                && !IoUtility.isWebStarted()) {
150            saveQedeqFromWebToBufferApache(url, f, proxyHost, proxyPort, nonProxyHosts, connectTimeout,
151                readTimeout, listener);
152            Trace.end(CLASS, method);
153            return;
154        }
155 
156        // set proxy properties according to kernel configuration (if not webstarted)
157        if (!IoUtility.isWebStarted()) {
158            if (proxyHost != null) {
159                System.setProperty("http.proxyHost", proxyHost);
160            }
161            if (proxyPort != null) {
162                System.setProperty("http.proxyPort", proxyPort);
163            }
164            if (nonProxyHosts != null) {
165                System.setProperty("http.nonProxyHosts", nonProxyHosts);
166            }
167        }
168 
169        FileOutputStream out = null;
170        InputStream in = null;
171        try {
172            final URLConnection connection = new URL(url).openConnection();
173 
174            if (connection instanceof HttpURLConnection) {
175                final HttpURLConnection httpConnection = (HttpURLConnection) connection;
176                // if we are running at least under Java 1.5 the following code should be executed
177                if (isSetConnectionTimeOutSupported()) {
178                    try {
179                        YodaUtility.executeMethod(httpConnection, "setConnectTimeout",
180                            new Class[] {Integer.TYPE}, new Object[] {new Integer(
181                            connectTimeout)});
182                    } catch (NoSuchMethodException e) {
183                        Trace.fatal(CLASS, method,
184                            "URLConnection.setConnectTimeout was previously found", e);
185                    } catch (InvocationTargetException e) {
186                        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                if (isSetReadTimeoutSupported()) {
192                    try {
193                        YodaUtility.executeMethod(httpConnection, "setReadTimeout",
194                            new Class[] {Integer.TYPE}, new Object[] {new Integer(
195                            readTimeout)});
196                    } catch (NoSuchMethodException e) {
197                        Trace.fatal(CLASS, method,
198                            "URLConnection.setReadTimeout was previously found", e);
199                    } catch (InvocationTargetException e) {
200                        Trace.fatal(CLASS, method,
201                            "URLConnection.setReadTimeout throwed an error", e);
202                    }
203                }
204                int responseCode = httpConnection.getResponseCode();
205                if (responseCode == 200) {
206                    in = httpConnection.getInputStream();
207                } else {
208                    in = httpConnection.getErrorStream();
209                    final String errorText = IoUtility.loadStreamWithoutException(in, 1000);
210                    throw new IOException("Response code from HTTP server was " + responseCode
211                        + (errorText.length() > 0 ? "\nResponse  text from HTTP server was:\n"
212                        + errorText : ""));
213                }
214            } else {
215                Trace.paramInfo(CLASS, method, "connection.getClass", connection.getClass()
216                    .toString());
217                in = connection.getInputStream();
218            }
219 
220            if (!url.equals(connection.getURL().toString())) {
221                throw new FileNotFoundException("\"" + url + "\" was substituted by "
222                    + "\"" + connection.getURL() + "\" from server");
223            }
224            final double maximum = connection.getContentLength();
225            IoUtility.createNecessaryDirectories(f);
226            out = new FileOutputStream(f);
227            final byte[] buffer = new byte[4096];
228            int bytesRead; // bytes read during one buffer read
229            int position = 0; // current reading position within the whole document
230            // continue writing
231            while ((bytesRead = in.read(buffer)) != -1) {
232                position += bytesRead;
233                out.write(buffer, 0, bytesRead);
234                if (maximum > 0) {
235                    double completeness = position / maximum;
236                    if (completeness < 0) {
237                        completeness = 0;
238                    }
239                    if (completeness > 100) {
240                        completeness = 1;
241                    }
242                    listener.loadingCompletenessChanged(completeness);
243                }
244            }
245            listener.loadingCompletenessChanged(1);
246        } finally {
247            IoUtility.close(out);
248            out = null;
249            IoUtility.close(in);
250            in = null;
251            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    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        final String method = "saveQedeqFromWebToBufferApache()";
276        Trace.begin(CLASS, method);
277 
278        // Create an instance of HttpClient.
279        HttpClient client = new HttpClient();
280 
281        // set proxy properties according to kernel configuration (if not webstarted)
282        if (!IoUtility.isWebStarted() && proxyHost != null && proxyHost.length() > 0) {
283            final String pHost = proxyHost;
284            int pPort = 80;
285            if (proxyPort != null) {
286                try {
287                    pPort = Integer.parseInt(proxyPort);
288                } catch (RuntimeException e) {
289                    Trace.fatal(CLASS, method, "proxy port not numeric: "
290                        + proxyPort, e);
291                }
292            }
293            if (pHost.length() > 0) {
294                client.getHostConfiguration().setProxy(pHost, pPort);
295            }
296        }
297 
298        // Create a method instance.
299        GetMethod httpMethod = new GetMethod(url);
300 
301        try {
302            // Provide custom retry handler is necessary
303            httpMethod.getParams().setParameter(HttpMethodParams.RETRY_HANDLER,
304                    new DefaultHttpMethodRetryHandler(3, false));
305 
306            httpMethod.getParams().setSoTimeout(connectTimeout);
307            // Throws IOException on TimeOut.
308 
309            int statusCode = client.executeMethod(httpMethod);
310 
311            if (statusCode != HttpStatus.SC_OK) {
312                throw new FileNotFoundException("Problems loading: " + url + "\n"
313                    + httpMethod.getStatusLine());
314            }
315 
316            // Read the response body.
317            byte[] responseBody = httpMethod.getResponseBody();
318            IoUtility.saveFileBinary(f, responseBody);
319            listener.loadingCompletenessChanged(1);
320        } finally {
321            // Release the connection.
322            httpMethod.releaseConnection();
323            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        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    public static boolean isSetConnectionTimeOutSupported() {
364        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    public static boolean isSetReadTimeoutSupported() {
373        return LazyHolderTimeoutMethods.IS_SET_READ_TIMEOUT_SUSPPORTED;
374    }
375 
376}

[all classes][org.qedeq.base.io]
EMMA 2.1.5320 (stable) (C) Vladimir Roubtsov