/**
* Distribution License:
* BibleDesktop is free software; you can redistribute it and/or modify it under
* the terms of the GNU Lesser General Public License, version 2.1 as published by
* the Free Software Foundation. This program is distributed in the hope
* that it will be useful, but WITHOUT ANY WARRANTY; without even the
* implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU Lesser General Public License for more details.
*
* The License is available on the internet at:
* http://www.gnu.org/copyleft/lgpl.html
* or by writing to:
* Free Software Foundation, Inc.
* 59 Temple Place - Suite 330
* Boston, MA 02111-1307, USA
*
* Copyright: 2005
* The copyright to this program is held by it's authors.
*
* ID: $Id: WebResource.java 2099 2011-03-07 17:13:00Z dmsmith $
*/
package org.crosswire.common.util;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ProxySelector;
import java.net.URI;
import java.util.Date;
import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.HttpHost;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.StatusLine;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpHead;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.conn.params.ConnRouteParams;
import org.apache.http.impl.client.AbstractHttpClient;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.impl.conn.ProxySelectorRoutePlanner;
import org.apache.http.params.HttpConnectionParams;
import org.apache.http.params.HttpParams;
import org.crosswire.common.progress.Progress;
import org.crosswire.jsword.JSMsg;
/**
* A WebResource is backed by an URL and potentially the proxy through which it
* need go. It can get basic information about the resource and it can get the
* resource. The requests are subject to a timeout, which can be set via the
* constructor or previously by a call to set the default timeout. The initial
* default timeout is 750 milliseconds.
*
*
* @see gnu.lgpl.License for license details.
* The copyright to this program is held by it's authors.
* @author DM Smith [dmsmith555 at yahoo dot com]
*/
public class WebResource {
/**
* Construct a WebResource for the given URL, while timing out if too much
* time has passed.
*
* @param theURI
* the Resource to get via HTTP
*/
public WebResource(URI theURI) {
this(theURI, null, null, timeout);
}
/**
* Construct a WebResource for the given URL, while timing out if too much
* time has passed.
*
* @param theURI
* the Resource to get via HTTP
* @param theTimeout
* the length of time in milliseconds to allow a connection to
* respond before timing out
*/
public WebResource(URI theURI, int theTimeout) {
this(theURI, null, null, theTimeout);
}
/**
* Construct a WebResource for the given URL, going through the optional
* proxy and default port, while timing out if too much time has passed.
*
* @param theURI
* the Resource to get via HTTP
* @param theProxyHost
* the proxy host or null
*/
public WebResource(URI theURI, String theProxyHost) {
this(theURI, theProxyHost, null, timeout);
}
/**
* Construct a WebResource for the given URL, going through the optional
* proxy and default port, while timing out if too much time has passed.
*
* @param theURI
* the Resource to get via HTTP
* @param theProxyHost
* the proxy host or null
* @param theTimeout
* the length of time in milliseconds to allow a connection to
* respond before timing out
*/
public WebResource(URI theURI, String theProxyHost, int theTimeout) {
this(theURI, theProxyHost, null, theTimeout);
}
/**
* Construct a WebResource for the given URL, going through the optional
* proxy and port, while timing out if too much time has passed.
*
* @param theURI
* the Resource to get via HTTP
* @param theProxyHost
* the proxy host or null
* @param theProxyPort
* the proxy port or null, where null means use the standard port
*/
public WebResource(URI theURI, String theProxyHost, Integer theProxyPort) {
this(theURI, theProxyHost, theProxyPort, timeout);
}
/**
* Construct a WebResource for the given URL, going through the optional
* proxy and port, while timing out if too much time has passed.
*
* @param theURI
* the Resource to get via HTTP
* @param theProxyHost
* the proxy host or null
* @param theProxyPort
* the proxy port or null, where null means use the standard port
* @param theTimeout
* the length of time in milliseconds to allow a connection to
* respond before timing out
*/
public WebResource(URI theURI, String theProxyHost, Integer theProxyPort, int theTimeout) {
uri = theURI;
client = new DefaultHttpClient();
HttpParams params = client.getParams();
// Allowable time between packets
HttpConnectionParams.setSoTimeout(params, theTimeout);
// Allowable time to get a connection
HttpConnectionParams.setConnectionTimeout(params, theTimeout);
// Configure proxy info if necessary and defined
if (theProxyHost != null && theProxyHost.length() > 0) {
// Configure the host and port
HttpHost proxy = new HttpHost(theProxyHost, theProxyPort == null ? -1 : theProxyPort.intValue());
ConnRouteParams.setDefaultProxy(params, proxy);
//MJD start move all proxy code inside proxy specific block
ProxySelectorRoutePlanner routePlanner = new ProxySelectorRoutePlanner(
client.getConnectionManager().getSchemeRegistry(),
ProxySelector.getDefault());
((AbstractHttpClient) client).setRoutePlanner(routePlanner);
}
}
/**
* When this WebResource is no longer needed it should be shutdown to return
* underlying resources back to the OS.
*/
public void shutdown() {
client.getConnectionManager().shutdown();
}
/**
* @return the timeout in milliseconds
*/
public static int getTimeout() {
return timeout;
}
/**
* @param timeout
* the timeout to set in milliseconds
*/
public static void setTimeout(int timeout) {
WebResource.timeout = timeout;
}
/**
* Determine the size of this WebResource.
*
* Note that the http client may read the entire file to determine this. *
* * @return the size of the file */ public int getSize() { HttpRequestBase method = new HttpHead(uri); HttpResponse response = null; try { // Execute the method. response = client.execute(method); StatusLine statusLine = response.getStatusLine(); if (statusLine.getStatusCode() == HttpStatus.SC_OK) { return getHeaderAsInt(response, "Content-Length"); } String reason = response.getStatusLine().getReasonPhrase(); // TRANSLATOR: Common error condition: {0} is a placeholder for the // URL of what could not be found. Reporter.informUser(this, JSMsg.gettext("Unable to find: {0}", reason + ':' + uri.getPath())); } catch (IOException e) { return 0; } return 0; } /** * Determine the last modified date of this WebResource. ** Note that the http client may read the entire file. *
* * @return the last mod date of the file */ public long getLastModified() { HttpRequestBase method = new HttpHead(uri); HttpResponse response = null; try { // Execute the method. response = client.execute(method); StatusLine statusLine = response.getStatusLine(); if (statusLine.getStatusCode() == HttpStatus.SC_OK) { return getHeaderAsDate(response, "Last-Modified"); } String reason = response.getStatusLine().getReasonPhrase(); // TRANSLATOR: Common error condition: {0} is a placeholder for the // URL of what could not be found. Reporter.informUser(this, JSMsg.gettext("Unable to find: {0}", reason + ':' + uri.getPath())); } catch (IOException e) { return new Date().getTime(); } return new Date().getTime(); } /** * Copy this WebResource to the destination and report progress. * * @param dest * the URI of the destination, typically a file:///. * @param meter * the job on which to report progress * @throws LucidException */ public void copy(URI dest, Progress meter) throws LucidException { InputStream in = null; OutputStream out = null; HttpRequestBase method = new HttpGet(uri); HttpResponse response = null; HttpEntity entity = null; try { // Execute the method. response = client.execute(method); // Initialize the meter, if present if (meter != null) { // Find out how big it is int size = getHeaderAsInt(response, "Content-Length"); // Sometimes the Content-Length is not given and we have to grab it via HEAD method if (size == 0) { size = getSize(); } meter.setTotalWork(size); } entity = response.getEntity(); if (entity != null) { in = entity.getContent(); // Download the index file out = NetUtil.getOutputStream(dest); byte[] buf = new byte[4096]; int count = in.read(buf); while (-1 != count) { if (meter != null) { meter.incrementWorkDone(count); } out.write(buf, 0, count); count = in.read(buf); } } else { String reason = response.getStatusLine().getReasonPhrase(); // TRANSLATOR: Common error condition: {0} is a placeholder for // the URL of what could not be found. Reporter.informUser(this, JSMsg.gettext("Unable to find: {0}", reason + ':' + uri.getPath())); } } catch (IOException e) { // TRANSLATOR: Common error condition: {0} is a placeholder for the // URL of what could not be found. throw new LucidException(JSMsg.gettext("Unable to find: {0}", uri.toString()), e); } finally { // Close the streams IOUtil.close(in); IOUtil.close(out); } } /** * Copy this WebResource to the destination. * * @param dest * @throws LucidException */ public void copy(URI dest) throws LucidException { copy(dest, null); } /** * Get the field as a long. * * @param response The response from the request * @param field the header field to check * @return the int value for the field */ private int getHeaderAsInt(HttpResponse response, String field) { Header header = response.getFirstHeader(field); String value = header.getValue(); try { return Integer.parseInt(value); } catch (NumberFormatException ex) { return 0; } } /** * Get the number of seconds since start of epoch for the field in the response headers as a Date. * * @param response The response from the request * @param field the header field to check * @return number of seconds since start of epoch */ @SuppressWarnings("deprecation") private long getHeaderAsDate(HttpResponse response, String field) { Header header = response.getFirstHeader(field); String value = header.getValue(); try { // This date cannot be readily parsed with DateFormatter return Date.parse(value); } catch (IllegalArgumentException ex) { return 0; } } /** * Define a 750 ms timeout to get a connection */ private static int timeout = 750; private URI uri; private HttpClient client; }