/* The contents of this file are subject to the license and copyright terms
* detailed in the license directory at the root of the source tree (also
* available online at http://fedora-commons.org/license/).
*/
package org.fcrepo.client;
import java.awt.Dimension;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import org.apache.http.HttpHeaders;
import org.apache.http.HttpResponse;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.params.ClientPNames;
import org.apache.http.conn.scheme.PlainSocketFactory;
import org.apache.http.conn.scheme.Scheme;
import org.apache.http.conn.ssl.SSLSocketFactory;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.impl.conn.PoolingClientConnectionManager;
import org.apache.http.params.CoreConnectionPNames;
import org.apache.http.util.EntityUtils;
import org.fcrepo.common.http.PreemptiveAuth;
import org.fcrepo.server.utilities.StreamUtility;
/**
* A client for performing HTTP GET requests on a Fedora server (with
* authentication) or any other server (without authentication). Each kind of
* request can either request an InputStream or request that the Downloader
* write the content directly to a provided OutputStream.
*
* @author Chris Wilper
*/
public class Downloader {
private final PoolingClientConnectionManager m_cManager =
new PoolingClientConnectionManager();
private final String m_fedoraUrlStart;
private final AuthScope m_authScope;
private final UsernamePasswordCredentials m_creds;
/**
* Construct a downloader for a certain repository as a certain user.
*/
public Downloader(String host,
int port,
String context,
String user,
String pass)
throws IOException {
m_fedoraUrlStart =
Administrator.getProtocol() + "://" + host + ":" + port + "/"
+ context + "/" + "get/";
m_authScope =
new AuthScope(host, AuthScope.ANY_PORT, AuthScope.ANY_REALM);
m_creds = new UsernamePasswordCredentials(user, pass);
if (Administrator.getProtocol().equalsIgnoreCase("https")) {
m_cManager.getSchemeRegistry().register(
new Scheme("https", port, SSLSocketFactory.getSocketFactory()));
} else {
m_cManager.getSchemeRegistry().register(
new Scheme(Administrator.getProtocol(), port, PlainSocketFactory.getSocketFactory()));
}
}
public void getDatastreamContent(String pid,
String dsID,
String asOfDateTime,
OutputStream out) throws IOException {
InputStream in = getDatastreamContent(pid, dsID, asOfDateTime);
StreamUtility.pipeStream(in, out, 4096);
}
public InputStream getDatastreamContent(String pid,
String dsID,
String asOfDateTime)
throws IOException {
StringBuffer buf = new StringBuffer();
buf.append(m_fedoraUrlStart);
buf.append(pid);
buf.append('/');
buf.append(dsID);
if (asOfDateTime != null) {
buf.append('/');
buf.append(asOfDateTime);
}
return get(buf.toString());
}
public void getDatastreamDissemination(String pid,
String dsId,
String asOfDateTime,
OutputStream out) throws IOException {
InputStream in = getDatastreamDissemination(pid, dsId, asOfDateTime);
StreamUtility.pipeStream(in, out, 4096);
}
public InputStream getDatastreamDissemination(String pid,
String dsId,
String asOfDateTime)
throws IOException {
StringBuffer buf = new StringBuffer();
buf.append(m_fedoraUrlStart);
buf.append(pid);
buf.append('/');
buf.append(dsId);
if (asOfDateTime != null) {
buf.append('/');
buf.append(asOfDateTime);
}
return get(buf.toString());
}
/**
* Get data via HTTP and write it to an OutputStream, following redirects,
* and supplying credentials if the host is the Fedora server.
*/
public void get(String url, OutputStream out) throws IOException {
InputStream in = get(url);
StreamUtility.pipeStream(in, out, 4096);
}
/**
* Get data via HTTP as an InputStream, following redirects, and supplying
* credentials if the host is the Fedora server.
*/
public InputStream get(String url) throws IOException {
HttpGet get = null;
boolean ok = false;
try {
// don't bother with challenges
DefaultHttpClient client = new PreemptiveAuth(m_cManager);
client.getParams().setIntParameter(CoreConnectionPNames.CONNECTION_TIMEOUT, 20000);
client.getCredentialsProvider().setCredentials(m_authScope, m_creds);
int redirectCount = 0; // how many redirects did we follow
int resultCode = 300; // not really, but enter the loop that way
Dimension d = null;
HttpResponse response = null;
while (resultCode > 299 && resultCode < 400 && redirectCount < 25) {
get = new HttpGet(url);
client.getParams().setBooleanParameter(ClientPNames.HANDLE_REDIRECTS, true);
if (Administrator.INSTANCE != null) {
d = Administrator.PROGRESS.getSize();
// if they're using Administrator, tell them we're downloading...
Administrator.PROGRESS.setString("Downloading " + url
+ " . . .");
Administrator.PROGRESS.setValue(100);
Administrator.PROGRESS.paintImmediately(0, 0, (int) d
.getWidth() - 1, (int) d.getHeight() - 1);
}
response = client.execute(get);
resultCode = response.getStatusLine().getStatusCode();
if (resultCode > 299 && resultCode < 400) {
redirectCount++;
url = response.getFirstHeader(HttpHeaders.LOCATION).getValue();
}
}
if (resultCode != 200) {
System.err.println(EntityUtils.toString(response.getEntity()));
throw new IOException("Server returned error: " + resultCode
+ " " + response.getStatusLine().getReasonPhrase());
}
ok = true;
if (Administrator.INSTANCE != null) {
// cache it to a file
File tempFile =
File.createTempFile("fedora-client-download-", null);
tempFile.deleteOnExit();
final InputStream in = response.getEntity().getContent();
final OutputStream out= new FileOutputStream(tempFile);
// do the actual download in a safe thread
SwingWorker<String> worker = new SwingWorker<String>() {
@Override
public String construct() {
try {
StreamUtility.pipeStream(in,
out,
8192);
} catch (Exception e) {
thrownException = e;
}
return "";
}
};
worker.start();
// The following code will run in the (safe)
// Swing event dispatcher thread.
int ms = 200;
while (!worker.done) {
try {
Administrator.PROGRESS.setValue(ms);
Administrator.PROGRESS.paintImmediately(0, 0, (int) d
.getWidth() - 1, (int) d.getHeight() - 1);
Thread.sleep(100);
ms = ms + 100;
if (ms >= 2000) {
ms = 200;
}
} catch (InterruptedException ie) {
}
}
if (worker.thrownException != null) {
throw worker.thrownException;
}
Administrator.PROGRESS.setValue(2000);
Administrator.PROGRESS.paintImmediately(0, 0, (int) d
.getWidth() - 1, (int) d.getHeight() - 1);
try {
Thread.sleep(100);
} catch (InterruptedException ie) {
}
in.close();
return new FileInputStream(tempFile);
}
return response.getEntity().getContent();
} catch (Exception e) {
throw new IOException(e.getMessage());
} finally {
if (get != null && !ok) {
get.releaseConnection();
}
if (Administrator.INSTANCE != null) {
Administrator.PROGRESS.setValue(0);
Administrator.PROGRESS.setString("");
}
}
}
/**
* Test this class.
*/
public static void main(String[] args) {
try {
if (args.length == 8 || args.length == 9) {
String host = args[0];
int port = Integer.parseInt(args[1]);
String user = args[2];
String password = args[3];
String pid = args[4];
String dsid = args[5];
File outfile = new File(args[6]);
String asOfDateTime = args.length == 8 ? args[7] : null;
String context = args.length == 9 ? args[8] : null;
FileOutputStream outStream = new FileOutputStream(outfile);
Downloader downloader =
new Downloader(host, port, context, user, password);
downloader.getDatastreamContent(pid,
dsid,
asOfDateTime,
outStream);
} else {
System.err
.println("Usage: Downloader host port user password pid dsid outfile [MMDDYYTHH:MM:SS] [context]");
}
} catch (Exception e) {
e.printStackTrace();
System.err.println("ERROR: " + e.getMessage());
}
}
}