Coverage Report - org.crosswire.common.util.WebResource
 
Classes in this File Line Coverage Branch Coverage Complexity
WebResource
0%
0/92
0%
0/22
2.467
 
 1  
 /**
 2  
  * Distribution License:
 3  
  * JSword is free software; you can redistribute it and/or modify it under
 4  
  * the terms of the GNU Lesser General Public License, version 2.1 or later
 5  
  * as published by the Free Software Foundation. This program is distributed
 6  
  * in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
 7  
  * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 8  
  * See the GNU Lesser General Public License for more details.
 9  
  *
 10  
  * The License is available on the internet at:
 11  
  *      http://www.gnu.org/copyleft/lgpl.html
 12  
  * or by writing to:
 13  
  *      Free Software Foundation, Inc.
 14  
  *      59 Temple Place - Suite 330
 15  
  *      Boston, MA 02111-1307, USA
 16  
  *
 17  
  * © CrossWire Bible Society, 2005 - 2016
 18  
  *
 19  
  */
 20  
 package org.crosswire.common.util;
 21  
 
 22  
 import java.io.IOException;
 23  
 import java.io.InputStream;
 24  
 import java.io.OutputStream;
 25  
 import java.net.URI;
 26  
 import java.util.Date;
 27  
 
 28  
 import org.apache.http.Header;
 29  
 import org.apache.http.HttpEntity;
 30  
 import org.apache.http.HttpHost;
 31  
 import org.apache.http.HttpResponse;
 32  
 import org.apache.http.HttpStatus;
 33  
 import org.apache.http.StatusLine;
 34  
 import org.apache.http.client.config.RequestConfig;
 35  
 import org.apache.http.client.methods.HttpGet;
 36  
 import org.apache.http.client.methods.HttpHead;
 37  
 import org.apache.http.client.methods.HttpRequestBase;
 38  
 import org.apache.http.client.utils.DateUtils;
 39  
 import org.apache.http.impl.client.CloseableHttpClient;
 40  
 import org.apache.http.impl.client.HttpClientBuilder;
 41  
 import org.crosswire.common.progress.Progress;
 42  
 import org.crosswire.jsword.JSMsg;
 43  
 
 44  
 /**
 45  
  * A WebResource is backed by an URL and potentially the proxy through which it
 46  
  * need go. It can get basic information about the resource and it can get the
 47  
  * resource. The requests are subject to a timeout, which can be set via the
 48  
  * constructor or previously by a call to set the default timeout. The initial
 49  
  * default timeout is 750 milliseconds.
 50  
  * 
 51  
  * 
 52  
  * @see gnu.lgpl.License The GNU Lesser General Public License for details.
 53  
  * @author DM Smith
 54  
  */
 55  
 public class WebResource {
 56  
     /**
 57  
      * Construct a WebResource for the given URL, while timing out if too much
 58  
      * time has passed.
 59  
      * 
 60  
      * @param theURI
 61  
      *            the Resource to get via HTTP
 62  
      */
 63  
     public WebResource(URI theURI) {
 64  0
         this(theURI, null, null, timeout);
 65  0
     }
 66  
 
 67  
     /**
 68  
      * Construct a WebResource for the given URL, while timing out if too much
 69  
      * time has passed.
 70  
      * 
 71  
      * @param theURI
 72  
      *            the Resource to get via HTTP
 73  
      * @param theTimeout
 74  
      *            the length of time in milliseconds to allow a connection to
 75  
      *            respond before timing out
 76  
      */
 77  
     public WebResource(URI theURI, int theTimeout) {
 78  0
         this(theURI, null, null, theTimeout);
 79  0
     }
 80  
 
 81  
     /**
 82  
      * Construct a WebResource for the given URL, going through the optional
 83  
      * proxy and default port, while timing out if too much time has passed.
 84  
      * 
 85  
      * @param theURI
 86  
      *            the Resource to get via HTTP
 87  
      * @param theProxyHost
 88  
      *            the proxy host or null
 89  
      */
 90  
     public WebResource(URI theURI, String theProxyHost) {
 91  0
         this(theURI, theProxyHost, null, timeout);
 92  0
     }
 93  
 
 94  
     /**
 95  
      * Construct a WebResource for the given URL, going through the optional
 96  
      * proxy and default port, while timing out if too much time has passed.
 97  
      * 
 98  
      * @param theURI
 99  
      *            the Resource to get via HTTP
 100  
      * @param theProxyHost
 101  
      *            the proxy host or null
 102  
      * @param theTimeout
 103  
      *            the length of time in milliseconds to allow a connection to
 104  
      *            respond before timing out
 105  
      */
 106  
     public WebResource(URI theURI, String theProxyHost, int theTimeout) {
 107  0
         this(theURI, theProxyHost, null, theTimeout);
 108  0
     }
 109  
 
 110  
     /**
 111  
      * Construct a WebResource for the given URL, going through the optional
 112  
      * proxy and port, while timing out if too much time has passed.
 113  
      * 
 114  
      * @param theURI
 115  
      *            the Resource to get via HTTP
 116  
      * @param theProxyHost
 117  
      *            the proxy host or null
 118  
      * @param theProxyPort
 119  
      *            the proxy port or null, where null means use the standard port
 120  
      */
 121  
     public WebResource(URI theURI, String theProxyHost, Integer theProxyPort) {
 122  0
         this(theURI, theProxyHost, theProxyPort, timeout);
 123  0
     }
 124  
 
 125  
     /**
 126  
      * Construct a WebResource for the given URL, going through the optional
 127  
      * proxy and port, while timing out if too much time has passed.
 128  
      * 
 129  
      * @param theURI
 130  
      *            the Resource to get via HTTP
 131  
      * @param theProxyHost
 132  
      *            the proxy host or null
 133  
      * @param theProxyPort
 134  
      *            the proxy port or null, where null means use the standard port
 135  
      * @param theTimeout
 136  
      *            the length of time in milliseconds to allow a connection to
 137  
      *            respond before timing out
 138  
      */
 139  0
     public WebResource(URI theURI, String theProxyHost, Integer theProxyPort, int theTimeout) {
 140  0
         uri = theURI;
 141  0
         HttpHost proxy = null;
 142  
 
 143  
         // Configure proxy info if necessary and defined
 144  0
         if (theProxyHost != null && theProxyHost.length() > 0) {
 145  0
             proxy = new HttpHost(theProxyHost, theProxyPort == null ? -1 : theProxyPort.intValue());
 146  
         }
 147  
 
 148  0
         final RequestConfig.Builder builder = RequestConfig.custom();
 149  0
         builder.setConnectTimeout(theTimeout).setConnectionRequestTimeout(theTimeout).setSocketTimeout(theTimeout).setProxy(proxy);
 150  0
         client = HttpClientBuilder.create().setDefaultRequestConfig(builder.build()).build();
 151  0
     }
 152  
 
 153  
     /**
 154  
      * When this WebResource is no longer needed it should be shutdown to return
 155  
      * underlying resources back to the OS.
 156  
      */
 157  
     public void shutdown() {
 158  0
         IOUtil.close(client);
 159  0
     }
 160  
 
 161  
     /**
 162  
      * @return the timeout in milliseconds
 163  
      */
 164  
     public static int getTimeout() {
 165  0
         return timeout;
 166  
     }
 167  
 
 168  
     /**
 169  
      * @param timeout
 170  
      *            the timeout to set in milliseconds
 171  
      */
 172  
     public static void setTimeout(int timeout) {
 173  0
         WebResource.timeout = timeout;
 174  0
     }
 175  
 
 176  
     /**
 177  
      * Determine the size of this WebResource.
 178  
      * <p>
 179  
      * Note that the http client may read the entire file to determine this.
 180  
      * </p>
 181  
      * 
 182  
      * @return the size of the file
 183  
      */
 184  
     public int getSize() {
 185  0
         HttpRequestBase method = new HttpHead(uri);
 186  0
         HttpResponse response = null;
 187  
         try {
 188  
             // Execute the method.
 189  0
             response = client.execute(method);
 190  0
             StatusLine statusLine = response.getStatusLine();
 191  0
             if (statusLine.getStatusCode() == HttpStatus.SC_OK) {
 192  0
                 return getHeaderAsInt(response, "Content-Length");
 193  
             }
 194  0
             String reason = response.getStatusLine().getReasonPhrase();
 195  
             // TRANSLATOR: Common error condition: {0} is a placeholder for the
 196  
             // URL of what could not be found.
 197  0
             Reporter.informUser(this, JSMsg.gettext("Unable to find: {0}", reason + ':' + uri.getPath()));
 198  0
         } catch (IOException e) {
 199  0
             return 0;
 200  0
         }
 201  0
         return 0;
 202  
     }
 203  
 
 204  
     /**
 205  
      * Determine the last modified date of this WebResource.
 206  
      * <p>
 207  
      * Note that the http client may read the entire file.
 208  
      * </p>
 209  
      * 
 210  
      * @return the last mod date of the file
 211  
      */
 212  
     public long getLastModified() {
 213  0
         HttpRequestBase method = new HttpHead(uri);
 214  0
         HttpResponse response = null;
 215  
         try {
 216  
             // Execute the method.
 217  0
             response = client.execute(method);
 218  0
             StatusLine statusLine = response.getStatusLine();
 219  0
             if (statusLine.getStatusCode() == HttpStatus.SC_OK) {
 220  0
                 return getHeaderAsDate(response, "Last-Modified");
 221  
             }
 222  0
             String reason = response.getStatusLine().getReasonPhrase();
 223  
             // TRANSLATOR: Common error condition: {0} is a placeholder for the
 224  
             // URL of what could not be found.
 225  0
             Reporter.informUser(this, JSMsg.gettext("Unable to find: {0}", reason + ':' + uri.getPath()));
 226  0
         } catch (IOException e) {
 227  0
             return new Date().getTime();
 228  0
         }
 229  0
         return new Date().getTime();
 230  
     }
 231  
 
 232  
     /**
 233  
      * Copy this WebResource to the destination and report progress.
 234  
      * 
 235  
      * @param dest
 236  
      *            the URI of the destination, typically a file:///.
 237  
      * @param meter
 238  
      *            the job on which to report progress
 239  
      * @throws LucidException when an error is encountered
 240  
      */
 241  
     public void copy(URI dest, Progress meter) throws LucidException  {
 242  0
         InputStream in = null;
 243  0
         OutputStream out = null;
 244  0
         HttpRequestBase method = new HttpGet(uri);
 245  0
         HttpResponse response = null;
 246  0
         HttpEntity entity = null;
 247  
         try {
 248  
             // Execute the method.
 249  0
             response = client.execute(method);
 250  
             // Initialize the meter, if present
 251  0
             if (meter != null) {
 252  
                 // Find out how big it is
 253  0
                 int size = getHeaderAsInt(response, "Content-Length");
 254  
                 // Sometimes the Content-Length is not given and we have to grab it via HEAD method
 255  0
                 if (size == 0) {
 256  0
                     size = getSize();
 257  
                 }
 258  0
                 meter.setTotalWork(size);
 259  
             }
 260  
 
 261  0
             entity = response.getEntity();
 262  0
             if (entity != null) {
 263  0
                 in = entity.getContent();
 264  
 
 265  
                 // Download the index file
 266  0
                 out = NetUtil.getOutputStream(dest);
 267  
 
 268  0
                 byte[] buf = new byte[4096];
 269  0
                 int count = in.read(buf);
 270  0
                 while (-1 != count) {
 271  0
                     if (meter != null) {
 272  0
                         meter.incrementWorkDone(count);
 273  
                     }
 274  0
                     out.write(buf, 0, count);
 275  0
                     count = in.read(buf);
 276  
                 }
 277  0
             } else {
 278  0
                 String reason = response.getStatusLine().getReasonPhrase();
 279  
                 // TRANSLATOR: Common error condition: {0} is a placeholder for
 280  
                 // the URL of what could not be found.
 281  0
                 Reporter.informUser(this, JSMsg.gettext("Unable to find: {0}", reason + ':' + uri.getPath()));
 282  
             }
 283  0
         } catch (IOException e) {
 284  
             // TRANSLATOR: Common error condition: {0} is a placeholder for the
 285  
             // URL of what could not be found.
 286  0
             throw new LucidException(JSMsg.gettext("Unable to find: {0}", uri.toString()), e);
 287  
         } finally {
 288  
             // Close the streams
 289  0
             IOUtil.close(in);
 290  0
             IOUtil.close(out);
 291  0
         }
 292  0
     }
 293  
 
 294  
     /**
 295  
      * Copy this WebResource to the destination.
 296  
      * 
 297  
      * @param dest the destination URI
 298  
      * @throws LucidException when an error is encountered
 299  
      */
 300  
     public void copy(URI dest) throws LucidException {
 301  0
         copy(dest, null);
 302  0
     }
 303  
 
 304  
     /**
 305  
      * Get the field as a long.
 306  
      * 
 307  
      * @param response The response from the request
 308  
      * @param field the header field to check
 309  
      * @return the int value for the field
 310  
      */
 311  
     private int getHeaderAsInt(HttpResponse response, String field) {
 312  0
         Header header = response.getFirstHeader(field);
 313  
         // If there is no matching header in the message null is returned.
 314  0
         if (header == null) {
 315  0
             return 0;
 316  
         }
 317  
 
 318  0
         String value = header.getValue();
 319  
         try {
 320  0
             return Integer.parseInt(value);
 321  0
         } catch (NumberFormatException ex) {
 322  0
             return 0;
 323  
         }
 324  
     }
 325  
 
 326  
     /**
 327  
      * Get the number of seconds since start of epoch for the field in the response headers as a Date.
 328  
      * 
 329  
      * @param response The response from the request
 330  
      * @param field the header field to check
 331  
      * @return number of seconds since start of epoch
 332  
      */
 333  
     private long getHeaderAsDate(HttpResponse response, String field) {
 334  0
         Header header = response.getFirstHeader(field);
 335  0
         String value = header.getValue();
 336  
         // This date cannot be readily parsed with DateFormatter
 337  0
         return DateUtils.parseDate(value).getTime();
 338  
     }
 339  
     /**
 340  
      * Define a 750 ms timeout to get a connection
 341  
      */
 342  0
     private static int timeout = 750;
 343  
 
 344  
     private URI uri;
 345  
     private CloseableHttpClient client;
 346  
 }