pace-lni-client.5.10.source-code.LNISmokeTest Maven / Gradle / Ivy
/**
* The contents of this file are subject to the license and copyright
* detailed in the LICENSE and NOTICE files at the root of the source
* tree and available online at
*
* http://www.dspace.org/license/
*/
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.ProtocolException;
import java.net.URL;
import java.util.List;
import java.util.ListIterator;
import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.HelpFormatter;
import org.apache.commons.cli.Option;
import org.apache.commons.cli.OptionGroup;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.ParseException;
import org.apache.commons.cli.PosixParser;
import org.apache.commons.codec.binary.Base64;
import org.apache.log4j.Logger;
import org.dspace.app.dav.client.LNIClientUtils;
import org.dspace.app.dav.client.LNISoapServlet;
import org.dspace.app.dav.client.LNISoapServletServiceLocator;
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jdom.Namespace;
import org.jdom.input.JDOMParseException;
import org.jdom.input.SAXBuilder;
import org.jdom.output.Format;
import org.jdom.output.XMLOutputter;
/**
* Very simple test program for DSpace Lightweight Network Interface (LNI).
*
* This will test and demonstrate the LNI's SOAP API and some WebDAV operations.
* It was written as a simple "smoke" test for the LNI to exercise basic
* features in a simple way, and to serve as a coding example.
*
* Example: (SOAP endpoint is http://mydspace.edu/dspace-lni/DSpaceLNI )
*
* /dspace/bin/dsrun LNISmokeTest \ -e
* http://user:[email protected]/dspace-lni/DSpaceLNI \ -f 123.45/67
*
* @author Larry Stone
* @version $Revision$
*/
public class LNISmokeTest
{
private static final Logger log = Logger.getLogger(LNISmokeTest.class);
/**
* The Constant NS_DAV.
* namespace for DAV XML objects
*/
private static final Namespace NS_DAV = Namespace.getNamespace("DAV:");
/**
* The Constant NS_DSPACE.
* DSpace's XML namespace
*/
/** The output pretty. */
private static XMLOutputter outputPretty = new XMLOutputter(Format
.getPrettyFormat());
// XML expressions for propfind calls (below)
/** The Constant allProp. */
private static final String allProp = " ";
/** The Constant nameProp. */
private static final String nameProp = " ";
/** The Constant someProp. */
private static final String someProp = ""
+ " "
+ " "
+ " ";
/** The Constant specificPropPrefix. */
private static final String specificPropPrefix = ""
+ " <";
/** The Constant specificPropSuffix. */
private static final String specificPropSuffix = "/> ";
/**
* usage. prints usage info to System.out & dies.
*
* @param options the options
* @param status the status
* @param msg the msg
*/
private static void usage(Options options, int status, String msg)
{
HelpFormatter hf = new HelpFormatter();
if (msg != null)
{
System.out.println(msg + "\n");
}
hf.printHelp("LNISmokeTest\n" + " -e SOAP-endpoint-URL\n"
+ " [ -s collection-handle -P package -i path ] |\n"
+ " [ -d item-handle -P package -o path ] |\n"
+ " [ -f handle [ -N propertyName ] ] |\n"
+ " [ -r handle [ -N propertyName ] ] |\n"
+ " [ -n handle ] |\n"
+ " [ -p handle -N propertyName -V newvalue ] |\n"
+ " [ -d item-handle -C collection-handle ]\n", options,
false);
System.exit(status);
}
/**
* Execute command line. See Usage string for options and arguments.
*
* @param argv the argv
*
* @throws Exception the exception
*/
public static void main(String[] argv) throws Exception
{
Options options = new Options();
OptionGroup func = new OptionGroup();
func.addOption(new Option("c", "copy", true,
"copy - to -C
"));
func.addOption(new Option("s", "submit", true,
"submit -P -i "));
func.addOption(new Option("d", "disseminate", true,
"disseminate - -P
-o "));
func.addOption(new Option("f", "propfind", true,
"propfind of all properties or -N "));
func.addOption(new Option("r", "rpropfind", true,
"recursive propfind, only collections"));
func.addOption(new Option("n", "names", true,
"list all property names on resource"));
func.addOption(new Option("p", "proppatch", true,
"set property: -N -V "));
func.setRequired(true);
options.addOptionGroup(func);
options.addOption("h", "help", false, "show help message");
options
.addOption("e", "endpoint", true,
"SOAP endpoint URL (REQUIRED)");
options.addOption("P", "packager", true,
"Packager to use to import/export a package.");
options.addOption("C", "collection", true,
"Target collection of -c copy");
options
.addOption("o", "output", true,
"file to create for new package");
options.addOption("i", "input", true,
"file containing package to submit");
options.addOption("N", "name", true, "name of property to query/set");
options.addOption("V", "value", true,
"new value for property being set");
try
{
CommandLine line = (new PosixParser()).parse(options, argv);
if (line.hasOption("h"))
{
usage(options, 0, null);
}
// get SOAP client connection, using the endpoint URL
String endpoint = line.getOptionValue("e");
if (endpoint == null)
{
usage(options, 2, "Missing the required -e endpoint argument");
}
LNISoapServletServiceLocator loc = new LNISoapServletServiceLocator();
LNISoapServlet lni = loc.getDSpaceLNI(new URL(endpoint));
// propfind - with optional single-property Name
if (line.hasOption("f"))
{
String pfXml = (line.hasOption("N")) ? specificPropPrefix
+ line.getOptionValue("N") + specificPropSuffix
: allProp;
doPropfind(lni, line.getOptionValue("f"), pfXml, 0, null);
}
// recursive propfind limited to collection, community objects
else if (line.hasOption("r"))
{
doPropfind(lni, line.getOptionValue("r"), someProp, -1,
"collection,community");
}
else if (line.hasOption("n"))
{
doPropfind(lni, line.getOptionValue("n"), nameProp, 0, null);
}
else if (line.hasOption("p"))
{
if (line.hasOption("N") && line.hasOption("V"))
{
doProppatch(lni, line.getOptionValue("p"), line
.getOptionValue("N"), line.getOptionValue("V"));
}
else
{
usage(options, 13,
"Missing required args: -N -V n");
}
}
// submit a package
else if (line.hasOption("s"))
{
if (line.hasOption("P") && line.hasOption("i"))
{
doPut(lni, line.getOptionValue("s"), line
.getOptionValue("P"), line.getOptionValue("i"),
endpoint);
}
else
{
usage(options, 13,
"Missing required args after -s: -P -i ");
}
}
// Disseminate (GET) item as package
else if (line.hasOption("d"))
{
if (line.hasOption("P") && line.hasOption("o"))
{
doGet(lni, line.getOptionValue("d"), line
.getOptionValue("P"), line.getOptionValue("o"),
endpoint);
}
else
{
usage(options, 13,
"Missing required args after -d: -P -o ");
}
}
// copy from src to dst
else if (line.hasOption("c"))
{
if (line.hasOption("C"))
{
doCopy(lni, line.getOptionValue("c"), line
.getOptionValue("C"));
}
else
{
usage(options, 13,
"Missing required args after -c: -C \n");
}
}
else
{
usage(options, 14, "Missing command option.\n");
}
}
catch (ParseException pe)
{
usage(options, 1, "Error in arguments: " + pe.toString());
}
catch (java.rmi.RemoteException de)
{
System.out.println("ERROR, got RemoteException, message="
+ de.getMessage());
de.printStackTrace();
die(1, " Exception class=" + de.getClass().getName());
}
}
/**
* Die.
*
* @param exit the exit
* @param msg the msg
*/
private static void die(int exit, String msg)
{
System.err.println(msg);
System.exit(exit);
}
// Like LNI lookup(), but dies on error so we don't have to check result.
// Also interprets the special format "handle bitstream" (with space
// separating the words) as the ``handle'' of a bitstream -- a combination
// of Item handle and bitstream sequence-ID.
// On success it returns a DAV resource URI, relative to the
// root of the DAV resource hierarchy, e.g. "/dso_12345.678$123"
/**
* Do lookup.
*
* @param lni the lni
* @param handle the handle
* @param bitstream the bitstream
*
* @return the string
*
* @throws RemoteException the remote exception
*/
private static String doLookup(LNISoapServlet lni, String handle,
String bitstream) throws java.rmi.RemoteException
{
// hack: if "handle" starts with '/' and there is no bitstream
// assume it is URI passed in and just return that.
if (handle.startsWith("/") && bitstream == null)
{
return handle;
}
// hack: parse "handle bitstream" syntax of handle.
if (handle.indexOf(' ') >= 0 && bitstream == null)
{
String h[] = handle.split("\\s+");
handle = h[0];
bitstream = h[1];
}
String uri = lni.lookup(handle, bitstream);
if (uri == null)
{
die(2, "ERROR, got null from lookup(\"" + handle + "\")");
}
System.out.println("DEBUG: lookup returns: \"" + uri + "\"");
return uri;
}
/**
* Do propfind.
*
* @param lni the lni
* @param handle the handle
* @param pf the pf
* @param depth the depth
* @param types the types
*
* @throws RemoteException the remote exception
* @throws IOException Signals that an I/O exception has occurred.
*/
private static void doPropfind(LNISoapServlet lni, String handle,
String pf, int depth, String types)
throws java.rmi.RemoteException, java.io.IOException
{
String uri = doLookup(lni, handle, null);
String result = lni.propfind(uri, pf, depth, types);
try
{
SAXBuilder builder = new SAXBuilder();
Document msDoc = builder.build(new java.io.StringReader(result));
Element ms = msDoc.getRootElement();
ListIterator ri = ms.getChildren("response", NS_DAV).listIterator();
while (ri.hasNext())
{
Element resp = (Element) ri.next();
String href = resp.getChildText("href", NS_DAV);
System.out.println("Resource = " + href);
ListIterator pi = resp.getChildren("propstat", NS_DAV)
.listIterator();
while (pi.hasNext())
{
Element ps = (Element) pi.next();
String status = ps.getChildText("status", NS_DAV);
if (status.indexOf("200") >= 0)
{
System.out
.println(" === PROPERTIES Successfully returned:");
}
else
{
System.out.println(" === PROPERTIES with Status="
+ status);
}
// print properties and values
Element prop = ps.getChild("prop", NS_DAV);
ListIterator ppi = prop.getChildren().listIterator();
while (ppi.hasNext())
{
Element e = (Element) ppi.next();
String value = e.getTextTrim();
if (value.equals(""))
{
List kids = e.getChildren();
if (kids.size() > 0)
{
value = outputPretty.outputString(kids);
}
if (value.indexOf('\n') >= 0)
{
value = "\n" + value;
}
}
else
{
value = "\"" + value + "\"";
}
String equals = value.equals("") ? "" : " = ";
System.out.println(" " + e.getQualifiedName()
+ equals + value);
}
}
}
}
catch (JDOMParseException je)
{
je.printStackTrace();
die(3, "ERROR: " + je.toString());
}
catch (JDOMException je)
{
je.printStackTrace();
die(4, "ERROR: " + je.toString());
}
}
/**
* Do proppatch.
*
* @param lni the lni
* @param handle the handle
* @param prop the prop
* @param val the val
*
* @throws RemoteException the remote exception
*/
private static void doProppatch(LNISoapServlet lni, String handle,
String prop, String val) throws java.rmi.RemoteException
{
String uri = doLookup(lni, handle, null);
String action = (val.length() > 0) ? "set" : "remove";
String pupdate = ""
+ "<"
+ action
+ "><"
+ prop
+ ">"
+ val
+ ""
+ prop
+ "> " + action + "> ";
System.err.println("DEBUG: sending: " + pupdate);
String result = lni.proppatch(uri, pupdate);
System.err.println("RESULT: " + result);
}
// "copy" src item into dst collection, both are handles.
/**
* Do copy.
*
* @param lni the lni
* @param src the src
* @param dst the dst
*
* @throws RemoteException the remote exception
*/
private static void doCopy(LNISoapServlet lni, String src, String dst)
throws java.rmi.RemoteException
{
String srcUri = doLookup(lni, src, null);
String dstUri = doLookup(lni, dst, null);
int status = lni.copy(srcUri, dstUri, -1, false, true);
System.err.println("Copy status = " + String.valueOf(status));
}
/**
* Implement WebDAV PUT http request.
*
* This might be simpler with a real HTTP client library, but
* java.net.HttpURLConnection is part of the standard SDK and it
* demonstrates the concepts.
*
* @param lni the lni
* @param collHandle the coll handle
* @param packager the packager
* @param source the source
* @param endpoint the endpoint
*
* @throws RemoteException the remote exception
* @throws ProtocolException the protocol exception
* @throws IOException Signals that an I/O exception has occurred.
* @throws FileNotFoundException the file not found exception
*/
private static void doPut(LNISoapServlet lni, String collHandle,
String packager, String source, String endpoint)
throws java.rmi.RemoteException, ProtocolException, IOException,
FileNotFoundException
{
// assemble URL from chopped endpoint-URL and relative URI
String collURI = doLookup(lni, collHandle, null);
URL url = LNIClientUtils.makeDAVURL(endpoint, collURI, packager);
System.err.println("DEBUG: PUT file=" + source + " to URL="
+ url.toString());
// connect with PUT method, then copy file over.
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("PUT");
conn.setDoOutput(true);
fixBasicAuth(url, conn);
conn.connect();
InputStream in = null;
OutputStream out = null;
try {
in = new FileInputStream(source);
out = conn.getOutputStream();
copyStream(in, out);
} finally {
if (in != null)
{
try
{
in.close();
}
catch (IOException e)
{
log.error("Unable to close input stream", e);
}
}
if (out!= null)
{
try
{
out.close();
}
catch (IOException e)
{
log.error("Unable to close output stream", e);
}
}
}
int status = conn.getResponseCode();
if (status < 200 || status >= 300)
{
die(status, "HTTP error, status=" + String.valueOf(status)
+ ", message=" + conn.getResponseMessage());
}
// diagnostics, and get resulting new item's location if avail.
System.err.println("DEBUG: sent " + source);
System.err.println("RESULT: Status="
+ String.valueOf(conn.getResponseCode()) + " "
+ conn.getResponseMessage());
String loc = conn.getHeaderField("Location");
System.err.println("RESULT: Location="
+ ((loc == null) ? "NULL!" : loc));
}
/**
* Get an item with WebDAV GET http request.
*
* @param lni the lni
* @param itemHandle the item handle
* @param packager the packager
* @param output the output
* @param endpoint the endpoint
*
* @throws RemoteException the remote exception
* @throws ProtocolException the protocol exception
* @throws IOException Signals that an I/O exception has occurred.
* @throws FileNotFoundException the file not found exception
*/
private static void doGet(LNISoapServlet lni, String itemHandle,
String packager, String output, String endpoint)
throws java.rmi.RemoteException, ProtocolException, IOException,
FileNotFoundException
{
// assemble URL from chopped endpoint-URL and relative URI
String itemURI = doLookup(lni, itemHandle, null);
URL url = LNIClientUtils.makeDAVURL(endpoint, itemURI, packager);
System.err.println("DEBUG: GET from URL: " + url.toString());
// connect with GET method, then copy file over.
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("GET");
conn.setDoInput(true);
fixBasicAuth(url, conn);
conn.connect();
int status = conn.getResponseCode();
if (status < 200 || status >= 300)
{
die(status, "HTTP error, status=" + String.valueOf(status)
+ ", message=" + conn.getResponseMessage());
}
InputStream in = conn.getInputStream();
OutputStream out = new FileOutputStream(output);
copyStream(in, out);
in.close();
out.close();
System.err.println("DEBUG: Created local file " + output);
System.err.println("RESULT: Status="
+ String.valueOf(conn.getResponseCode()) + " "
+ conn.getResponseMessage());
}
//
/**
* Fix basic auth.
*
* Set up HTTP basic authentication based on user/password in URL.
* The HttpURLConnection class should do this itself!
*
* @param url the url
* @param conn the conn
*/
private static void fixBasicAuth(URL url, HttpURLConnection conn)
{
String userinfo = url.getUserInfo();
if (userinfo != null)
{
String cui = new String(Base64.encodeBase64(userinfo.getBytes()));
conn.addRequestProperty("Authorization", "Basic " + cui);
System.err.println("DEBUG: Sending Basic auth=" + cui);
}
}
/**
* Copy stream. copy from one stream to another
*
* @param input the input
* @param output the output
*
* @throws IOException Signals that an I/O exception has occurred.
*/
private static void copyStream(final InputStream input,
final OutputStream output) throws IOException
{
final int BUFFER_SIZE = 1024 * 4;
final byte[] buffer = new byte[BUFFER_SIZE];
while (true)
{
final int count = input.read(buffer, 0, BUFFER_SIZE);
if (-1 == count)
{
break;
}
output.write(buffer, 0, count);
}
}
}