[DS-1005] SWORD v2 implementation for DSpace : Restructure project and use external SWORD dependency.

git-svn-id: http://scm.dspace.org/svn/repo/dspace/trunk@6678 9c30dcfa-912a-0410-8fc2-9e0234be79fd
This commit is contained in:
Mark Diggory
2011-09-10 16:13:42 +00:00
parent 0c528afbd3
commit 546426b659
88 changed files with 166 additions and 4960 deletions

View File

@@ -1,102 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<name>DSpace SWORD v2 :: API and Implementation</name>
<description>DSpace SWORD v2 Deposit Service Provider Web Application</description>
<groupId>org.dspace</groupId>
<artifactId>dspace-swordv2-api</artifactId>
<version>1.8.0-SNAPSHOT</version>
<packaging>jar</packaging>
<!--
A Parent POM that Maven inherits DSpace Default
POM atrributes from.
-->
<parent>
<groupId>org.dspace</groupId>
<artifactId>dspace-swordv2</artifactId>
<version>1.8.0-SNAPSHOT</version>
<relativePath>..</relativePath>
</parent>
<scm>
<connection>scm:svn:http://scm.dspace.org/svn/repo/dspace/trunk/dspace-swordv2/dspace-swordv2-api</connection>
<developerConnection>scm:svn:https://scm.dspace.org/svn/repo/dspace/trunk/dspace-swordv2/dspace-swordv2-api</developerConnection>
<url>http://scm.dspace.org/svn/repo/dspace/trunk/dspace-swordv2/dspace-swordv2-api</url>
</scm>
<dependencies>
<dependency>
<groupId>org.dspace</groupId>
<artifactId>dspace-api</artifactId>
<exclusions>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.dspace</groupId>
<artifactId>dspace-services-impl</artifactId>
</dependency>
<dependency>
<groupId>org.dspace</groupId>
<artifactId>dspace-api-lang</artifactId>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.4</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.15</version>
<exclusions>
<exclusion>
<groupId>javax.mail</groupId>
<artifactId>mail</artifactId>
</exclusion>
<exclusion>
<groupId>javax.jms</groupId>
<artifactId>jms</artifactId>
</exclusion>
<exclusion>
<groupId>com.sun.jdmk</groupId>
<artifactId>jmxtools</artifactId>
</exclusion>
<exclusion>
<groupId>com.sun.jmx</groupId>
<artifactId>jmxri</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.apache.abdera</groupId>
<artifactId>abdera-client</artifactId>
<version>1.1.1</version>
</dependency>
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.2.1</version>
</dependency>
<dependency>
<groupId>com.hp.hpl.jena</groupId>
<artifactId>jena</artifactId>
<version>2.6.4</version>
</dependency>
<dependency>
<groupId>xom</groupId>
<artifactId>xom</artifactId>
<version>1.1</version>
</dependency>
</dependencies>
</project>

View File

@@ -1,131 +0,0 @@
/**
* 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/
*/
package org.swordapp.server;
import org.apache.abdera.Abdera;
import org.apache.abdera.i18n.iri.IRI;
import org.apache.abdera.model.Category;
import org.apache.abdera.model.Element;
import org.apache.abdera.model.Entry;
import org.apache.abdera.model.Feed;
import javax.xml.namespace.QName;
import java.io.IOException;
import java.io.Writer;
import java.text.SimpleDateFormat;
import java.util.Date;
public class AtomStatement extends Statement
{
private String author;
private String feedUri;
private String title;
private String updated;
public AtomStatement(String feedUri, String author, String title, String updated)
{
this.contentType = "application/atom+xml;type=feed";
this.author = author != null ? author : "Unknown";
this.feedUri = feedUri;
this.title = title != null ? title : "Untitled";
this.updated = updated;
}
@Override
public void writeTo(Writer out)
throws IOException
{
Abdera abdera = new Abdera();
Feed feed = abdera.newFeed();
// id
// link@rel="self" -> point to id
// title
// updated
feed.setId(this.feedUri);
feed.addLink(this.feedUri, "self");
feed.setTitle(this.title);
feed.addAuthor(this.author);
if (this.updated != null)
{
feed.setUpdated(this.updated);
}
else
{
feed.setUpdated(new Date());
}
// create an entry for each Resource Part
for (ResourcePart resource : this.resources)
{
Entry entry = feed.addEntry();
// id
// summary
// title
// updated
entry.setContent(new IRI(resource.getUri()), resource.getMediaType());
entry.setId(resource.getUri());
entry.setTitle("Resource " + resource.getUri());
entry.setSummary("Resource Part");
entry.setUpdated(new Date());
}
// create an entry for each original deposit
for (OriginalDeposit deposit : this.originalDeposits)
{
Entry entry = feed.addEntry();
// id
// summary
// title
// updated
entry.setId(deposit.getUri());
entry.setTitle("Original Deposit " + deposit.getUri());
entry.setSummary("Original Deposit");
entry.setUpdated(new Date());
entry.setContent(new IRI(deposit.getUri()), deposit.getMediaType());
entry.addCategory(UriRegistry.SWORD_TERMS_NAMESPACE, UriRegistry.SWORD_ORIGINAL_DEPOSIT, "Original Deposit");
if (deposit.getDepositedOn() != null)
{
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
entry.addSimpleExtension(new QName(UriRegistry.SWORD_DEPOSITED_ON), sdf.format(deposit.getDepositedOn()));
}
if (deposit.getDepositedOnBehalfOf() != null)
{
entry.addSimpleExtension(new QName(UriRegistry.SWORD_DEPOSITED_ON_BEHALF_OF), deposit.getDepositedOnBehalfOf());
}
if (deposit.getDepositedBy() != null)
{
entry.addSimpleExtension(new QName(UriRegistry.SWORD_DEPOSITED_BY), deposit.getDepositedBy());
}
for (String packaging : deposit.getPackaging())
{
entry.addSimpleExtension(UriRegistry.SWORD_PACKAGING, packaging);
}
}
// now at the state as a categories
for (String state : this.states.keySet())
{
Category cat = feed.addCategory(UriRegistry.SWORD_STATE, state, "State");
if (this.states.get(state) != null)
{
cat.setText(this.states.get(state));
}
}
// now write the feed
feed.writeTo(out);
}
}

View File

@@ -1,37 +0,0 @@
/**
* 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/
*/
package org.swordapp.server;
public class AuthCredentials
{
private String username;
private String password;
private String onBehalfOf;
public AuthCredentials(String username, String password, String onBehalfOf)
{
this.username = username;
this.password = password;
this.onBehalfOf = onBehalfOf;
}
public String getUsername()
{
return username;
}
public String getPassword()
{
return password;
}
public String getOnBehalfOf()
{
return onBehalfOf;
}
}

View File

@@ -1,165 +0,0 @@
/**
* 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/
*/
package org.swordapp.server;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import org.apache.log4j.Logger;
/**
* Utility class that holds Checksum related methods.
*
* @author Neil Taylor, Stuart Lewis
*/
public class ChecksumUtils
{
/** Logger */
private static Logger log = Logger.getLogger(ChecksumUtils.class);
/**
* Generate an MD5 hash for the file that is specified in the
* filepath. The hash is returned as a String representation.
*
* @param filepath The path to the file to load.
* @return A string hash of the file.
* @throws NoSuchAlgorithmException If the MD5 algorithm is
* not supported by the installed virtual machine.
*
* @throws IOException If there is an error accessing the file.
*/
public static String generateMD5(String filepath)
throws NoSuchAlgorithmException, IOException
{
return generateMD5(new FileInputStream(filepath));
}
/**
* Generate an MD5 hash for the file that is specified in the
* filepath. The hash is returned as a String representation.
*
* @param md5Stream The InputStream to checksum.
* @return A string hash of the file.
* @throws NoSuchAlgorithmException If the MD5 algorithm is
* not supported by the installed virtual machine.
*
* @throws IOException If there is an error accessing the file.
*/
public static String generateMD5(InputStream md5Stream)
throws NoSuchAlgorithmException, IOException
{
String md5 = null;
try
{
MessageDigest md = MessageDigest.getInstance("MD5");
md.reset();
byte[] bytes = new byte[1024];
int count = 0;
while( (count = md5Stream.read(bytes)) != -1 )
{
md.update(bytes, 0, count);
}
byte[] md5Digest = md.digest();
StringBuffer buffer = new StringBuffer();
for( byte b : md5Digest )
{
// 0xFF is used to handle the issue of negative numbers in the bytes
String hex = Integer.toHexString(b & 0xFF);
if( hex.length() == 1 )
{
buffer.append("0");
}
buffer.append(hex);
}
md5 = buffer.toString();
}
catch(NoSuchAlgorithmException ex )
{
log.error("MD5 Algorithm Not found");
throw ex;
}
finally
{
if( md5Stream != null )
{
md5Stream.close();
}
}
return md5;
}
/**
* Generate an MD5 hash for the file that is specified in the
* filepath. The hash is returned as a String representation.
*
* @param bytes The byte array to checksum.
* @return A string hash of the file.
* @throws NoSuchAlgorithmException If the MD5 algorithm is
* not supported by the installed virtual machine.
*
* @throws IOException If there is an error accessing the file.
*/
public static String generateMD5(byte[] bytes)
throws NoSuchAlgorithmException, IOException
{
String md5 = null;
try
{
MessageDigest md = MessageDigest.getInstance("MD5");
md.reset();
md.update(bytes);
byte[] md5Digest = md.digest();
StringBuffer buffer = new StringBuffer();
for( byte b : md5Digest )
{
// 0xFF is used to handle the issue of negative numbers in the bytes
String hex = Integer.toHexString(b & 0xFF);
if( hex.length() == 1 )
{
buffer.append("0");
}
buffer.append(hex);
}
md5 = buffer.toString();
}
catch(NoSuchAlgorithmException ex )
{
log.error("MD5 Algorithm Not found");
throw ex; // rethrow
}
return md5;
}
/**
* Run a simple test to process the file.
*
* @param args The command line arguments.
* @throws NoSuchAlgorithmException If there was an error generating the MD5.
* @throws IOException If there is an error accessing the file.
*/
public static void main(String[] args)
throws NoSuchAlgorithmException, IOException
{
System.out.println(ChecksumUtils.generateMD5(args[0]));
}
}

View File

@@ -1,242 +0,0 @@
/**
* 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/
*/
package org.swordapp.server;
import org.apache.abdera.i18n.iri.IRI;
import org.apache.abdera.model.Element;
import org.apache.abdera.model.Entry;
import org.apache.abdera.model.Feed;
import org.apache.log4j.Logger;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.StringWriter;
import java.security.NoSuchAlgorithmException;
import java.text.SimpleDateFormat;
import java.util.Date;
public class CollectionAPI extends SwordAPIEndpoint
{
private static Logger log = Logger.getLogger(CollectionAPI.class);
protected CollectionListManager clm = null;
protected CollectionDepositManager cdm;
public CollectionAPI(CollectionListManager clm, CollectionDepositManager cdm, SwordConfiguration config)
{
super(config);
this.clm = clm;
this.cdm = cdm;
}
public void get(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException
{
// first find out if this is supported
if (this.clm == null)
{
resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
return;
}
// do the initial authentication
AuthCredentials auth = null;
try
{
auth = this.getAuthCredentials(req);
}
catch (SwordAuthException e)
{
if (e.isRetry())
{
String s = "Basic realm=\"SWORD2\"";
resp.setHeader("WWW-Authenticate", s);
resp.setStatus(401);
return;
}
else
{
resp.sendError(HttpServletResponse.SC_BAD_REQUEST, e.getMessage());
return;
}
}
try
{
Feed feed = this.clm.listCollectionContents(new IRI(this.getFullUrl(req)), auth, this.config);
// since the spec doesn't require the collection to be listable, this might
// give us back null
if (feed == null)
{
// method not allowed
resp.sendError(405, "This server does not support listing collection contents");
return;
}
// otherwise process and return
this.addGenerator(feed, this.config);
resp.setHeader("Content-Type", "application/atom+xml;type=feed");
feed.writeTo(resp.getWriter());
resp.getWriter().flush();
}
catch (SwordServerException e)
{
throw new ServletException(e);
}
catch (SwordAuthException e)
{
// authentication actually failed at the server end; not a SwordError, but
// need to throw a 403 Forbidden
resp.sendError(403);
}
catch (SwordError se)
{
this.swordError(req, resp, se);
}
}
public void post(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException
{
// do the initial authentication
AuthCredentials auth = null;
try
{
auth = this.getAuthCredentials(req);
}
catch (SwordAuthException e)
{
if (e.isRetry())
{
String s = "Basic realm=\"SWORD2\"";
resp.setHeader("WWW-Authenticate", s);
resp.setStatus(401);
return;
}
else
{
resp.sendError(HttpServletResponse.SC_BAD_REQUEST, e.getMessage());
return;
}
}
try
{
// the first thing to do is determine what the deposit type is:
String contentType = this.getContentType(req);
boolean isMultipart = contentType.startsWith("multipart/related");
boolean isEntryOnly = contentType.startsWith("application/atom+xml");
boolean isBinaryOnly = !isMultipart && !isEntryOnly;
// get the common HTTP headers before leaping into the deposit type specific processes
String slug = req.getHeader("Slug");
boolean inProgress = this.getInProgress(req);
Deposit deposit = new Deposit();
deposit.setInProgress(inProgress);
deposit.setSlug(slug);
DepositReceipt receipt = null;
// do the different kinds of deposit details extraction
if (isMultipart)
{
this.addDepositPropertiesFromMultipart(deposit, req);
}
else if (isEntryOnly)
{
this.addDepositPropertiesFromEntry(deposit, req);
}
else if (isBinaryOnly)
{
this.addDepositPropertiesFromBinary(deposit, req);
}
// now send the deposit to the implementation for processing
String colUri = this.getFullUrl(req);
receipt = this.cdm.createNew(colUri, deposit, auth, this.config);
this.addGenerator(receipt, this.config);
// prepare and return the response
IRI location = receipt.getLocation();
if (location == null)
{
throw new SwordServerException("No Location found in Deposit Receipt; unable to send valid response");
}
resp.setStatus(201); // Created
if (this.config.returnDepositReceipt() && !receipt.isEmpty())
{
resp.setHeader("Content-Type", "application/atom+xml;type=entry");
resp.setHeader("Location", location.toString());
// set the last modified header
// like: Last-Modified: Tue, 15 Nov 1994 12:45:26 GMT
SimpleDateFormat sdf = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss Z");
Date lastModified = receipt.getLastModified() != null ? receipt.getLastModified() : new Date();
resp.setHeader("Last-Modified", sdf.format(lastModified));
// to set the content-md5 header we need to write the output to
// a string and checksum it
StringWriter writer = new StringWriter();
Entry responseEntry = receipt.getAbderaEntry();
responseEntry.writeTo(writer);
// write the content-md5 header
String md5 = ChecksumUtils.generateMD5(writer.toString().getBytes());
resp.setHeader("Content-MD5", md5);
resp.getWriter().append(writer.toString());
resp.getWriter().flush();
}
else
{
resp.setHeader("Location", location.toString());
}
}
catch (SwordError se)
{
this.swordError(req, resp, se);
}
catch (SwordServerException e)
{
throw new ServletException(e);
}
catch (NoSuchAlgorithmException e)
{
throw new ServletException(e);
}
catch (SwordAuthException e)
{
// authentication actually failed at the server end; not a SwordError, but
// need to throw a 403 Forbidden
resp.sendError(403);
}
}
protected void addGenerator(DepositReceipt doc, SwordConfiguration config)
{
Element generator = this.getGenerator(this.config);
if (generator != null)
{
doc.getWrappedEntry().addExtension(generator);
}
}
protected void addGenerator(Feed doc, SwordConfiguration config)
{
Element generator = this.getGenerator(this.config);
if (generator != null)
{
doc.addExtension(generator);
}
}
}

View File

@@ -1,13 +0,0 @@
/**
* 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/
*/
package org.swordapp.server;
public interface CollectionDepositManager
{
DepositReceipt createNew(String collectionURI, Deposit deposit, AuthCredentials auth, SwordConfiguration config) throws SwordError, SwordServerException, SwordAuthException;
}

View File

@@ -1,16 +0,0 @@
/**
* 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/
*/
package org.swordapp.server;
import org.apache.abdera.i18n.iri.IRI;
import org.apache.abdera.model.Feed;
public interface CollectionListManager
{
Feed listCollectionContents(IRI collectionIRI, AuthCredentials auth, SwordConfiguration config) throws SwordServerException, SwordAuthException, SwordError;
}

View File

@@ -1,471 +0,0 @@
/**
* 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/
*/
package org.swordapp.server;
import org.apache.abdera.i18n.iri.IRI;
import org.apache.abdera.model.Element;
import org.apache.abdera.model.Entry;
import org.apache.log4j.Logger;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.StringWriter;
import java.security.NoSuchAlgorithmException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Map;
public class ContainerAPI extends SwordAPIEndpoint
{
private static Logger log = Logger.getLogger(ContainerAPI.class);
private ContainerManager cm;
private StatementManager sm;
public ContainerAPI(ContainerManager cm, StatementManager sm, SwordConfiguration config)
{
super(config);
this.cm = cm;
this.sm = sm;
}
public void get(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException
{
this.get(req, resp, true);
}
public void get(HttpServletRequest req, HttpServletResponse resp, boolean sendBody)
throws ServletException, IOException
{
// do the initial authentication
AuthCredentials auth = null;
try
{
auth = this.getAuthCredentials(req);
}
catch (SwordAuthException e)
{
if (e.isRetry())
{
String s = "Basic realm=\"SWORD2\"";
resp.setHeader("WWW-Authenticate", s);
resp.setStatus(401);
return;
}
else
{
resp.sendError(HttpServletResponse.SC_BAD_REQUEST, e.getMessage());
return;
}
}
try
{
// we allow content negotiation on this header
Map<String, String> accept = this.getAcceptHeaders(req);
String iri = this.getFullUrl(req);
// the content negotiation may be for the deposit receipt, OR for
// the Statement
if (this.cm.isStatementRequest(iri, accept, auth, this.config))
{
Statement statement = this.sm.getStatement(iri, accept, auth, this.config);
// set the content type
resp.setHeader("Content-Type", statement.getContentType());
// set the last modified header
// like: Last-Modified: Tue, 15 Nov 1994 12:45:26 GMT
SimpleDateFormat sdf = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss Z");
Date lastModified = statement.getLastModified() != null ? statement.getLastModified() : new Date();
resp.setHeader("Last-Modified", sdf.format(lastModified));
// to set the content-md5 header we need to write the output to
// a string and checksum it
StringWriter writer = new StringWriter();
statement.writeTo(writer);
// write the content-md5 header
String md5 = ChecksumUtils.generateMD5(writer.toString().getBytes());
resp.setHeader("Content-MD5", md5);
if (sendBody)
{
resp.getWriter().append(writer.toString());
resp.getWriter().flush();
}
}
else
{
DepositReceipt receipt = this.cm.getEntry(iri, accept, auth, this.config);
this.addGenerator(receipt, this.config);
IRI location = receipt.getLocation();
resp.setHeader("Content-Type", "application/atom+xml;type=entry");
resp.setHeader("Location", location.toString());
// set the last modified header
// like: Last-Modified: Tue, 15 Nov 1994 12:45:26 GMT
SimpleDateFormat sdf = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss Z");
Date lastModified = receipt.getLastModified() != null ? receipt.getLastModified() : new Date();
resp.setHeader("Last-Modified", sdf.format(lastModified));
// to set the content-md5 header we need to write the output to
// a string and checksum it
StringWriter writer = new StringWriter();
Entry responseEntry = receipt.getAbderaEntry();
responseEntry.writeTo(writer);
// write the content-md5 header
String md5 = ChecksumUtils.generateMD5(writer.toString().getBytes());
resp.setHeader("Content-MD5", md5);
if (sendBody)
{
resp.getWriter().append(writer.toString());
resp.getWriter().flush();
}
}
}
catch (SwordError se)
{
this.swordError(req, resp, se);
}
catch (SwordServerException e)
{
throw new ServletException(e);
}
catch (NoSuchAlgorithmException e)
{
throw new ServletException(e);
}
catch (SwordAuthException e)
{
// authentication actually failed at the server end; not a SwordError, but
// need to throw a 403 Forbidden
resp.sendError(403);
}
}
public void head(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException
{
this.get(req, resp, false);
}
public void put(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException
{
// do the initial authentication
AuthCredentials auth = null;
try
{
auth = this.getAuthCredentials(req);
}
catch (SwordAuthException e)
{
if (e.isRetry())
{
String s = "Basic realm=\"SWORD2\"";
resp.setHeader("WWW-Authenticate", s);
resp.setStatus(401);
return;
}
else
{
resp.sendError(HttpServletResponse.SC_BAD_REQUEST, e.getMessage());
return;
}
}
try
{
// the first thing to do is determine what the deposit type is:
String contentType = this.getContentType(req);
boolean isMultipart = contentType.startsWith("multipart/related");
boolean isEntryOnly = contentType.startsWith("application/atom+xml");
// get the In-Progress header
boolean inProgress = this.getInProgress(req);
Deposit deposit = new Deposit();
deposit.setInProgress(inProgress);
String iri = this.getFullUrl(req);
DepositReceipt receipt;
if (isMultipart)
{
this.addDepositPropertiesFromMultipart(deposit, req);
// defer to the implementation layer to update both the metadata and the media resource
receipt = this.cm.replaceMetadataAndMediaResource(iri, deposit, auth, this.config);
}
else if (isEntryOnly)
{
// check that we have the right content type
if (!(contentType.startsWith("application/atom+xml") || contentType.startsWith("application/atom+xml;type=entry")))
{
throw new SwordError(UriRegistry.ERROR_BAD_REQUEST, "Content-Type must be 'application/atom+xml' or 'application/atom+xml;type=entry'");
}
this.addDepositPropertiesFromEntry(deposit, req);
// now defer to the implementation layer
receipt = this.cm.replaceMetadata(iri, deposit, auth, this.config);
}
else
{
// some other sort of deposit which is not supported
throw new SwordError(UriRegistry.ERROR_BAD_REQUEST, "PUT to Edit-IRI MUST be a multipart request or an Atom Entry");
}
// prepare and return the response
IRI location = receipt.getLocation();
if (location == null)
{
throw new SwordServerException("No Location found in Deposit Receipt; unable to send valid response");
}
if (this.config.returnDepositReceipt() && !receipt.isEmpty())
{
this.addGenerator(receipt, this.config);
resp.setStatus(200);
resp.setHeader("Content-Type", "application/atom+xml;type=entry");
resp.setHeader("Location", location.toString());
// set the last modified header
// like: Last-Modified: Tue, 15 Nov 1994 12:45:26 GMT
SimpleDateFormat sdf = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss Z");
Date lastModified = receipt.getLastModified() != null ? receipt.getLastModified() : new Date();
resp.setHeader("Last-Modified", sdf.format(lastModified));
// to set the content-md5 header we need to write the output to
// a string and checksum it
StringWriter writer = new StringWriter();
Entry responseEntry = receipt.getAbderaEntry();
responseEntry.writeTo(writer);
// write the content-md5 header
String md5 = ChecksumUtils.generateMD5(writer.toString().getBytes());
resp.setHeader("Content-MD5", md5);
resp.getWriter().append(writer.toString());
resp.getWriter().flush();
}
else
{
resp.setStatus(204);
resp.setHeader("Location", location.toString());
}
}
catch (SwordError se)
{
this.swordError(req, resp, se);
}
catch (SwordServerException e)
{
throw new ServletException(e);
}
catch (NoSuchAlgorithmException e)
{
throw new ServletException(e);
}
catch (SwordAuthException e)
{
// authentication actually failed at the server end; not a SwordError, but
// need to throw a 403 Forbidden
resp.sendError(403);
}
}
public void post(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException
{
// do the initial authentication
AuthCredentials auth = null;
try
{
auth = this.getAuthCredentials(req);
}
catch (SwordAuthException e)
{
if (e.isRetry())
{
String s = "Basic realm=\"SWORD2\"";
resp.setHeader("WWW-Authenticate", s);
resp.setStatus(401);
return;
}
else
{
resp.sendError(HttpServletResponse.SC_BAD_REQUEST, e.getMessage());
return;
}
}
try
{
// the first thing to do is determine what the deposit type is:
String contentType = this.getContentType(req);
boolean isEntryOnly = contentType.startsWith("application/atom+xml");
// if neither of these content types are set, we may have an empty deposit
// which is just providing instructions to the server (i.e. In-Progress is complete)
// Content-Length is zero in this case
int contentLength = req.getContentLength();
boolean headersOnly = contentLength == 0;
// get the common HTTP headers before leaping into the deposit type specific processes
boolean inProgress = this.getInProgress(req);
String iri = this.getFullUrl(req);
Deposit deposit = new Deposit();
deposit.setInProgress(inProgress);
DepositReceipt receipt;
// do the different kinds of deposit details extraction, and then delegate to the implementation
// for handling
if (isEntryOnly)
{
this.addDepositPropertiesFromEntry(deposit, req);
receipt = this.cm.addMetadata(iri, deposit, auth, this.config);
}
else if (headersOnly)
{
receipt = this.cm.useHeaders(iri, deposit, auth, this.config);
}
else
{
// some other sort of deposit which is not supported (shouldn't ever get here)
throw new SwordError(UriRegistry.ERROR_BAD_REQUEST);
}
// prepare and return the response
IRI location = receipt.getLocation();
// NOTE that in this case, no Location header is required, so the editIRI MAY be null
if (this.config.returnDepositReceipt() && !receipt.isEmpty())
{
this.addGenerator(receipt, this.config);
resp.setHeader("Content-Type", "application/atom+xml;type=entry");
if (location != null)
{
resp.setHeader("Location", location.toString());
}
// set the last modified header
// like: Last-Modified: Tue, 15 Nov 1994 12:45:26 GMT
SimpleDateFormat sdf = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss Z");
Date lastModified = receipt.getLastModified() != null ? receipt.getLastModified() : new Date();
resp.setHeader("Last-Modified", sdf.format(lastModified));
// to set the content-md5 header we need to write the output to
// a string and checksum it
StringWriter writer = new StringWriter();
Entry responseEntry = receipt.getAbderaEntry();
responseEntry.writeTo(writer);
// write the content-md5 header
String md5 = ChecksumUtils.generateMD5(writer.toString().getBytes());
resp.setHeader("Content-MD5", md5);
resp.getWriter().append(writer.toString());
resp.getWriter().flush();
}
else
{
if (location != null)
{
resp.setHeader("Location", location.toString());
}
}
}
catch (SwordError se)
{
this.swordError(req, resp, se);
}
catch (SwordServerException e)
{
throw new ServletException(e);
}
catch (NoSuchAlgorithmException e)
{
throw new ServletException(e);
}
catch (SwordAuthException e)
{
// authentication actually failed at the server end; not a SwordError, but
// need to throw a 403 Forbidden
resp.sendError(403);
}
}
public void delete(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException
{
// do the initial authentication
AuthCredentials auth = null;
try
{
auth = this.getAuthCredentials(req);
}
catch (SwordAuthException e)
{
if (e.isRetry())
{
String s = "Basic realm=\"SWORD2\"";
resp.setHeader("WWW-Authenticate", s);
resp.setStatus(401);
return;
}
else
{
resp.sendError(HttpServletResponse.SC_BAD_REQUEST, e.getMessage());
return;
}
}
try
{
String uri = this.getFullUrl(req);
// send it to the implementation
this.cm.deleteContainer(uri, auth, this.config);
// Not expecting any response, so if no error just return a 204
resp.setStatus(204);
}
catch (SwordError se)
{
this.swordError(req, resp, se);
}
catch (SwordServerException e)
{
throw new ServletException(e);
}
catch (SwordAuthException e)
{
// authentication actually failed at the server end; not a SwordError, but
// need to throw a 403 Forbidden
resp.sendError(403);
}
}
protected void addGenerator(DepositReceipt doc, SwordConfiguration config)
{
Element generator = this.getGenerator(this.config);
if (generator != null)
{
doc.getWrappedEntry().addExtension(generator);
}
}
}

View File

@@ -1,40 +0,0 @@
/**
* 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/
*/
package org.swordapp.server;
import java.util.Map;
public interface ContainerManager
{
DepositReceipt getEntry(String editIRI, Map<String, String> accept, AuthCredentials auth, SwordConfiguration config)
throws SwordServerException, SwordError, SwordAuthException;
DepositReceipt replaceMetadata(String editIRI, Deposit deposit, AuthCredentials auth, SwordConfiguration config)
throws SwordError, SwordServerException, SwordAuthException;
DepositReceipt replaceMetadataAndMediaResource(String editIRI, Deposit deposit, AuthCredentials auth, SwordConfiguration config)
throws SwordError, SwordServerException, SwordAuthException;
DepositReceipt addMetadataAndResources(String editIRI, Deposit deposit, AuthCredentials auth, SwordConfiguration config)
throws SwordError, SwordServerException, SwordAuthException;
DepositReceipt addMetadata(String editIRI, Deposit deposit, AuthCredentials auth, SwordConfiguration config)
throws SwordError, SwordServerException, SwordAuthException;
DepositReceipt addResources(String editIRI, Deposit deposit, AuthCredentials auth, SwordConfiguration config)
throws SwordError, SwordServerException, SwordAuthException;
void deleteContainer(String editIRI, AuthCredentials auth, SwordConfiguration config)
throws SwordError, SwordServerException, SwordAuthException;
DepositReceipt useHeaders(String editIRI, Deposit deposit, AuthCredentials auth, SwordConfiguration config)
throws SwordError, SwordServerException, SwordAuthException;
boolean isStatementRequest(String editIRI, Map<String, String> accept, AuthCredentials auth, SwordConfiguration config)
throws SwordError, SwordServerException, SwordAuthException;
}

View File

@@ -1,185 +0,0 @@
/**
* 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/
*/
package org.swordapp.server;
import org.apache.abdera.model.Entry;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
public class Deposit
{
private SwordEntry entry = null;
private InputStream inputStream = null;
private String filename;
private String mimeType;
private String slug = null;
private String md5 = null;
private String packaging;
private boolean inProgress = false;
private boolean metadataRelevant = true;
private File file = null;
public Deposit() { }
public Deposit(Entry entry, InputStream inputStream, String filename, String mimeType, String slug, String md5,
String packaging, boolean inProgress)
{
this.entry = new SwordEntry(entry);
this.inputStream = inputStream;
this.filename = filename;
this.mimeType = mimeType;
this.slug = slug;
this.md5 = md5;
this.packaging = packaging;
this.inProgress = inProgress;
}
public boolean isEntryOnly()
{
return this.entry != null && this.inputStream == null && this.file == null;
}
public boolean isMultipart()
{
return this.entry != null && (this.inputStream != null || this.file != null);
}
public boolean isBinaryOnly()
{
return this.entry == null && (this.inputStream != null || this.file != null);
}
public File getFile()
{
return file;
}
public void setFile(File file)
{
this.file = file;
this.inputStream = null;
}
public SwordEntry getSwordEntry()
{
return entry;
}
public void setEntry(Entry entry)
{
this.entry = new SwordEntry(entry);
}
public InputStream getInputStream()
throws SwordServerException
{
try
{
if (inputStream == null && file == null)
{
return null;
}
else if (inputStream == null && file != null)
{
return new FileInputStream(this.file);
}
else if (inputStream != null)
{
return inputStream;
}
return null;
}
catch (FileNotFoundException e)
{
throw new SwordServerException(e);
}
catch (IOException e)
{
throw new SwordServerException(e);
}
}
public void setInputStream(InputStream inputStream)
{
this.inputStream = inputStream;
}
public String getFilename()
{
return filename;
}
public void setFilename(String filename)
{
this.filename = filename;
}
public String getMimeType()
{
return mimeType;
}
public void setMimeType(String mimeType)
{
this.mimeType = mimeType;
}
public String getSlug()
{
return slug;
}
public void setSlug(String slug)
{
this.slug = slug;
}
public String getMd5()
{
return md5;
}
public void setMd5(String md5)
{
this.md5 = md5;
}
public String getPackaging()
{
return packaging;
}
public void setPackaging(String packaging)
{
this.packaging = packaging;
}
public boolean isInProgress()
{
return inProgress;
}
public void setInProgress(boolean inProgress)
{
this.inProgress = inProgress;
}
public boolean isMetadataRelevant()
{
return metadataRelevant;
}
public void setMetadataRelevant(boolean metadataRelevant)
{
this.metadataRelevant = metadataRelevant;
}
}

View File

@@ -1,297 +0,0 @@
/**
* 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/
*/
package org.swordapp.server;
import org.apache.abdera.Abdera;
import org.apache.abdera.i18n.iri.IRI;
import org.apache.abdera.model.Element;
import org.apache.abdera.model.Entry;
import org.apache.abdera.model.ExtensibleElement;
import org.apache.abdera.model.Link;
import javax.xml.namespace.QName;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class DepositReceipt
{
private List<String> packagingFormats = new ArrayList<String>();
private IRI editIRI = null;
private IRI seIRI = null;
private IRI emIRI = null;
private IRI feedIRI = null;
private IRI location = null;
private Entry entry;
private Map<String, String> statements = new HashMap<String, String>();
private String treatment = null;
private String verboseDescription = null;
private String splashUri = null;
private String originalDepositUri = null;
private String originalDepositType = null;
private Map<String, String> derivedResources = new HashMap<String, String>();
private boolean empty = false;
private Date lastModified = null;
public DepositReceipt()
{
Abdera abdera = new Abdera();
this.entry = abdera.newEntry();
}
public Entry getWrappedEntry()
{
return this.entry;
}
public Entry getAbderaEntry()
{
Entry abderaEntry = (Entry) this.entry.clone();
// use the edit iri as the id
abderaEntry.setId(this.editIRI.toString());
// add the Edit IRI Link
if (this.editIRI != null)
{
abderaEntry.addLink(this.editIRI.toString(), "edit");
}
// add the Sword Edit IRI link
if (this.seIRI != null)
{
abderaEntry.addLink(this.seIRI.toString(), UriRegistry.REL_SWORD_EDIT);
}
// add the atom formatted feed
if (this.feedIRI != null)
{
Link fl = abderaEntry.addLink(this.feedIRI.toString(), "edit-media");
fl.setMimeType("application/atom+xml;type=feed");
}
// add the edit-media link
if (this.emIRI != null)
{
abderaEntry.addLink(this.emIRI.toString(), "edit-media");
}
// add the packaging formats
for (String pf : this.packagingFormats)
{
abderaEntry.addSimpleExtension(UriRegistry.SWORD_PACKAGING, pf);
}
// add the statement URIs
for (String statement : this.statements.keySet())
{
Link link = abderaEntry.addLink(statement, UriRegistry.REL_STATEMENT);
link.setMimeType(this.statements.get(statement));
}
if (this.treatment != null)
{
abderaEntry.addSimpleExtension(UriRegistry.SWORD_TREATMENT, this.treatment);
}
if (this.verboseDescription != null)
{
abderaEntry.addSimpleExtension(UriRegistry.SWORD_VERBOSE_DESCRIPTION, this.verboseDescription);
}
if (this.splashUri == null)
{
abderaEntry.addLink(this.splashUri, "alternate");
}
if (this.originalDepositUri != null)
{
Link link = abderaEntry.addLink(this.originalDepositUri, UriRegistry.REL_ORIGINAL_DEPOSIT);
if (this.originalDepositType != null)
{
link.setMimeType(this.originalDepositType);
}
}
for (String uri : this.derivedResources.keySet())
{
Link link = abderaEntry.addLink(uri, UriRegistry.REL_DERIVED_RESOURCE);
if (this.derivedResources.get(uri) != null)
{
link.setMimeType(this.derivedResources.get(uri));
}
}
return abderaEntry;
}
public Date getLastModified()
{
return lastModified;
}
public void setLastModified(Date lastModified)
{
this.lastModified = lastModified;
}
public boolean isEmpty()
{
return empty;
}
public void setEmpty(boolean empty)
{
this.empty = empty;
}
public void setMediaFeedIRI(IRI feedIRI)
{
this.feedIRI = feedIRI;
}
public void setEditMediaIRI(IRI emIRI)
{
this.emIRI = emIRI;
}
public void setEditIRI(IRI editIRI)
{
this.editIRI = editIRI;
// set the SE-IRI as the same if it has not already been set
if (this.seIRI == null)
{
this.seIRI = editIRI;
}
}
public IRI getLocation()
{
return this.location == null ? this.editIRI : this.location;
}
public void setLocation(IRI location)
{
this.location = location;
}
public IRI getEditIRI()
{
return this.editIRI;
}
public IRI getSwordEditIRI()
{
return this.seIRI;
}
public void setSwordEditIRI(IRI seIRI)
{
this.seIRI = seIRI;
// set the Edit-IRI the same if it has not already been set
if (this.editIRI == null)
{
this.editIRI = seIRI;
}
}
public void setContent(IRI href, String mediaType)
{
this.entry.setContent(href, mediaType);
}
public void addEditMediaIRI(IRI href)
{
this.entry.addLink(href.toString(), "edit-media");
}
public void addEditMediaIRI(IRI href, String mediaType)
{
Abdera abdera = new Abdera();
Link link = abdera.getFactory().newLink();
link.setHref(href.toString());
link.setRel("edit-media");
link.setMimeType(mediaType);
this.entry.addLink(link);
}
public void addEditMediaFeedIRI(IRI href)
{
this.addEditMediaIRI(href, "application/atom+xml;type=feed");
}
public void setPackaging(List<String> packagingFormats)
{
this.packagingFormats = packagingFormats;
}
public void addPackaging(String packagingFormat)
{
this.packagingFormats.add(packagingFormat);
}
public void setOREStatementURI(String statement)
{
this.setStatementURI("application/rdf+xml", statement);
}
public void setAtomStatementURI(String statement)
{
this.setStatementURI("application/atom+xml;type=feed", statement);
}
public void setStatementURI(String type, String statement)
{
this.statements.put(statement, type);
}
public Element addSimpleExtension(QName qname, String value)
{
return this.entry.addSimpleExtension(qname, value);
}
public Element addDublinCore(String element, String value)
{
return this.entry.addSimpleExtension(new QName(UriRegistry.DC_NAMESPACE, element), value);
}
public void setTreatment(String treatment)
{
this.treatment = treatment;
}
public void setVerboseDescription(String verboseDescription)
{
this.verboseDescription = verboseDescription;
}
public void setSplashUri(String splashUri)
{
this.splashUri = splashUri;
}
public void setOriginalDeposit(String originalDepositUri, String originalDepositType)
{
this.originalDepositUri = originalDepositUri;
this.originalDepositType = originalDepositType;
}
public void setDerivedResources(Map<String, String> derivedResources)
{
this.derivedResources = derivedResources;
}
public void addDerivedResource(String resourceUri, String resourceType)
{
this.derivedResources.put(resourceUri, resourceType);
}
}

View File

@@ -1,142 +0,0 @@
/**
* 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/
*/
package org.swordapp.server;
import nu.xom.Attribute;
import nu.xom.Document;
import nu.xom.Element;
import nu.xom.Serializer;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.io.Writer;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
public class ErrorDocument
{
private String errorUri;
private Map<String, Integer> errorCodes = new HashMap<String, Integer>();
private String summary = null;
private String verboseDescription = null;
private int status;
public ErrorDocument(String errorUri)
{
this(errorUri, -1, null, null);
}
public ErrorDocument(String errorUri, int status)
{
this(errorUri, status, null, null);
}
public ErrorDocument(String errorUri, String verboseDescription)
{
this(errorUri, -1, null, verboseDescription);
}
public ErrorDocument(String errorUri, int status, String verboseDescription)
{
this(errorUri, status, null, verboseDescription);
}
public ErrorDocument(String errorUri, int status, String summary, String verboseDescription)
{
// set up the error codes mapping
this.errorCodes.put(UriRegistry.ERROR_BAD_REQUEST, 400); // bad request
this.errorCodes.put(UriRegistry.ERROR_CHECKSUM_MISMATCH, 412); // precondition failed
this.errorCodes.put(UriRegistry.ERROR_CONTENT, 415); // unsupported media type
this.errorCodes.put(UriRegistry.ERROR_MEDIATION_NOT_ALLOWED, 412); // precondition failed
this.errorCodes.put(UriRegistry.ERROR_METHOD_NOT_ALLOWED, 405); // method not allowed
this.errorCodes.put(UriRegistry.ERROR_TARGET_OWNER_UNKNOWN, 403); // forbidden
this.errorCodes.put(UriRegistry.ERROR_MAX_UPLOAD_SIZE_EXCEEDED, 413); // forbidden
this.errorUri = errorUri;
this.summary = summary;
this.verboseDescription = verboseDescription;
this.status = status;
}
public int getStatus()
{
if (this.status > -1)
{
return this.status;
}
if (this.errorCodes.containsKey(errorUri))
{
return this.errorCodes.get(errorUri);
}
else
{
return 400; // bad request
}
}
public void writeTo(Writer out, SwordConfiguration config)
throws IOException, SwordServerException
{
// do the XML serialisation
Element error = new Element("sword:error", UriRegistry.SWORD_TERMS_NAMESPACE);
error.addAttribute(new Attribute("href", this.errorUri));
// write some boiler-plate text into the document
Element title = new Element("atom:title", UriRegistry.ATOM_NAMESPACE);
Element updates = new Element("atom:updated", UriRegistry.ATOM_NAMESPACE);
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
updates.appendChild(sdf.format(new Date()));
Element generator = new Element("atom:generator", UriRegistry.ATOM_NAMESPACE);
generator.addAttribute(new Attribute("uri", config.generator()));
generator.addAttribute(new Attribute("version", config.generatorVersion()));
if (config.administratorEmail() != null)
{
generator.appendChild(config.administratorEmail());
}
Element treatment = new Element("sword:treatment", UriRegistry.SWORD_TERMS_NAMESPACE);
treatment.appendChild("Processing failed");
error.appendChild(title);
error.appendChild(updates);
error.appendChild(generator);
error.appendChild(treatment);
// now add the operational bits
if (this.summary != null)
{
Element summary = new Element("atom:summary", UriRegistry.ATOM_NAMESPACE);
summary.appendChild(this.summary);
error.appendChild(summary);
}
if (this.verboseDescription != null)
{
Element vd = new Element("sword:verboseDescription", UriRegistry.SWORD_TERMS_NAMESPACE);
vd.appendChild(this.verboseDescription);
error.appendChild(vd);
}
try
{
// now get it written out
Document doc = new Document(error);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
Serializer serializer = new Serializer(baos, "ISO-8859-1");
serializer.write(doc);
out.write(baos.toString());
}
catch (UnsupportedEncodingException e)
{
throw new SwordServerException(e);
}
}
}

View File

@@ -1,94 +0,0 @@
/**
* 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/
*/
package org.swordapp.server;
import java.io.InputStream;
import java.util.Date;
public class MediaResource
{
private String packaging = UriRegistry.PACKAGE_SIMPLE_ZIP;
private String contentType = "application/octet-stream";
private InputStream inputStream = null;
private boolean unpackaged = false;
private String contentMD5;
private Date lastModified;
public MediaResource(InputStream in, String contentType, String packaging)
{
this(in, contentType, packaging, false);
}
public MediaResource(InputStream in, String contentType, String packaging, boolean unpackaged)
{
this.inputStream = in;
this.contentType = contentType;
this.packaging = packaging;
this.unpackaged = unpackaged;
}
public String getContentMD5()
{
return contentMD5;
}
public void setContentMD5(String contentMD5)
{
this.contentMD5 = contentMD5;
}
public Date getLastModified()
{
return lastModified;
}
public void setLastModified(Date lastModified)
{
this.lastModified = lastModified;
}
public boolean isUnpackaged()
{
return unpackaged;
}
public void setUnpackaged(boolean unpackaged)
{
this.unpackaged = unpackaged;
}
public String getPackaging()
{
return packaging;
}
public InputStream getInputStream()
{
return inputStream;
}
public void setInputStream(InputStream inputStream)
{
this.inputStream = inputStream;
}
public String getContentType()
{
return contentType;
}
public void setContentType(String contentType)
{
this.contentType = contentType;
}
public void setPackaging(String packaging)
{
this.packaging = packaging;
}
}

View File

@@ -1,346 +0,0 @@
/**
* 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/
*/
package org.swordapp.server;
import org.apache.abdera.i18n.iri.IRI;
import org.apache.abdera.model.Element;
import org.apache.abdera.model.Entry;
import org.apache.log4j.Logger;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.StringWriter;
import java.text.SimpleDateFormat;
import java.util.*;
public class MediaResourceAPI extends SwordAPIEndpoint
{
private static Logger log = Logger.getLogger(MediaResourceAPI.class);
protected MediaResourceManager mrm;
public MediaResourceAPI(MediaResourceManager mrm, SwordConfiguration config)
{
super(config);
this.mrm = mrm;
}
public void get(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException
{
this.get(req, resp, true);
}
public void get(HttpServletRequest req, HttpServletResponse resp, boolean sendBody)
throws ServletException, IOException
{
// do the initial authentication
AuthCredentials auth = null;
try
{
auth = this.getAuthCredentials(req);
}
catch (SwordAuthException e)
{
if (e.isRetry())
{
String s = "Basic realm=\"SWORD2\"";
resp.setHeader("WWW-Authenticate", s);
resp.setStatus(401);
return;
}
else
{
resp.sendError(HttpServletResponse.SC_BAD_REQUEST, e.getMessage());
return;
}
}
try
{
// get all of the Accept- headers out for content negotiation
Map<String, String> acceptHeaders = this.getAcceptHeaders(req);
// get the original request URI
String editMediaURI = this.getFullUrl(req);
// delegate to the implementation to get the resource representation
MediaResource resource = this.mrm.getMediaResourceRepresentation(editMediaURI, acceptHeaders, auth, this.config);
// now deliver the resource representation to the client
// if this is a packaged resource, then write the package header
if (!resource.isUnpackaged())
{
String packaging = resource.getPackaging();
if (packaging == null || "".equals(packaging))
{
packaging = UriRegistry.PACKAGE_SIMPLE_ZIP;
}
resp.setHeader("Packaging", packaging);
}
String contentType = resource.getContentType();
if (contentType == null || "".equals(contentType))
{
contentType = "application/octet-stream";
}
resp.setHeader("Content-Type", contentType);
// set the last modified header
// like: Last-Modified: Tue, 15 Nov 1994 12:45:26 GMT
SimpleDateFormat sdf = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss Z");
Date lastModified = resource.getLastModified() != null ? resource.getLastModified() : new Date();
resp.setHeader("Last-Modified", sdf.format(lastModified));
// to set the content-md5 header we need to write the output to
// a string and checksum it
String md5 = resource.getContentMD5();
resp.setHeader("Content-MD5", md5);
if (sendBody)
{
OutputStream out = resp.getOutputStream();
InputStream in = resource.getInputStream();
this.copyInputToOutput(in, out);
out.flush();
}
}
catch (SwordError se)
{
this.swordError(req, resp, se);
return;
}
catch (SwordServerException e)
{
throw new ServletException(e);
}
catch (SwordAuthException e)
{
// authentication actually failed at the server end; not a SwordError, but
// need to throw a 403 Forbidden
resp.sendError(403);
}
}
public void head(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException
{
this.get(req, resp, false);
}
public void put(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException
{
// do the initial authentication
AuthCredentials auth = null;
try
{
auth = this.getAuthCredentials(req);
}
catch (SwordAuthException e)
{
if (e.isRetry())
{
String s = "Basic realm=\"SWORD2\"";
resp.setHeader("WWW-Authenticate", s);
resp.setStatus(401);
return;
}
else
{
resp.sendError(HttpServletResponse.SC_BAD_REQUEST, e.getMessage());
return;
}
}
try
{
String editMediaIRI = this.getFullUrl(req);
Deposit deposit = new Deposit();
// add the properties from the binary deposit
this.addDepositPropertiesFromBinary(deposit, req);
// now fire the deposit object into the implementation
DepositReceipt receipt = this.mrm.replaceMediaResource(editMediaIRI, deposit, auth, this.config);
// no response is expected, if no errors get thrown we just return a success: 204 No Content
// and the appropriate location header
resp.setHeader("Location", receipt.getLocation().toString());
resp.setStatus(204);
}
catch (SwordError se)
{
this.swordError(req, resp, se);
}
catch (SwordServerException e)
{
throw new ServletException(e);
}
catch (SwordAuthException e)
{
// authentication actually failed at the server end; not a SwordError, but
// need to throw a 403 Forbidden
resp.sendError(403);
}
}
public void post(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException
{
// do the initial authentication
AuthCredentials auth = null;
try
{
auth = this.getAuthCredentials(req);
}
catch (SwordAuthException e)
{
if (e.isRetry())
{
String s = "Basic realm=\"SWORD2\"";
resp.setHeader("WWW-Authenticate", s);
resp.setStatus(401);
return;
}
else
{
resp.sendError(HttpServletResponse.SC_BAD_REQUEST, e.getMessage());
return;
}
}
try
{
// the first thing to do is determine what the deposit type is:
String contentType = this.getContentType(req);
boolean isMultipart = contentType.startsWith("multipart/related");
String uri = this.getFullUrl(req);
Deposit deposit = new Deposit();
if (isMultipart)
{
this.addDepositPropertiesFromMultipart(deposit, req);
}
else
{
this.addDepositPropertiesFromBinary(deposit, req);
}
// this method has a special header (Metadata-Relevant) which we need to pull out
boolean metadataRelevant = this.getMetadataRelevant(req);
deposit.setMetadataRelevant(metadataRelevant);
// now send the deposit to the implementation for processing
DepositReceipt receipt = this.mrm.addResource(uri, deposit, auth, this.config);
// prepare and return the response
IRI location = receipt.getLocation();
if (location == null)
{
throw new SwordServerException("No Edit-IRI found in Deposit Receipt; unable to send valid response");
}
resp.setStatus(201); // Created
if (this.config.returnDepositReceipt() && !receipt.isEmpty())
{
this.addGenerator(receipt, this.config);
resp.setHeader("Content-Type", "application/atom+xml;type=entry");
resp.setHeader("Location", location.toString());
Entry responseEntry = receipt.getAbderaEntry();
responseEntry.writeTo(resp.getWriter());
resp.getWriter().flush();
}
else
{
resp.setHeader("Location", location.toString());
}
}
catch (SwordError se)
{
this.swordError(req, resp, se);
return;
}
catch (SwordServerException e)
{
throw new ServletException(e);
}
catch (SwordAuthException e)
{
// authentication actually failed at the server end; not a SwordError, but
// need to throw a 403 Forbidden
resp.sendError(403);
}
}
public void delete(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException
{
// do the initial authentication
AuthCredentials auth = null;
try
{
auth = this.getAuthCredentials(req);
}
catch (SwordAuthException e)
{
if (e.isRetry())
{
String s = "Basic realm=\"SWORD2\"";
resp.setHeader("WWW-Authenticate", s);
resp.setStatus(401);
return;
}
else
{
resp.sendError(HttpServletResponse.SC_BAD_REQUEST, e.getMessage());
return;
}
}
try
{
String editMediaIRI = this.getFullUrl(req);
// delegate to the implementation
this.mrm.deleteMediaResource(editMediaIRI, auth, this.config);
// no response is expected, if no errors get thrown then we just return a success: 204 No Content
resp.setStatus(204);
}
catch (SwordError se)
{
this.swordError(req, resp, se);
}
catch (SwordServerException e)
{
throw new ServletException(e);
}
catch (SwordAuthException e)
{
// authentication actually failed at the server end; not a SwordError, but
// need to throw a 403 Forbidden
resp.sendError(403);
}
}
protected void addGenerator(DepositReceipt doc, SwordConfiguration config)
{
Element generator = this.getGenerator(this.config);
if (generator != null)
{
doc.getWrappedEntry().addExtension(generator);
}
}
}

View File

@@ -1,25 +0,0 @@
/**
* 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/
*/
package org.swordapp.server;
import java.util.Map;
public interface MediaResourceManager
{
MediaResource getMediaResourceRepresentation(String uri, Map<String, String> accept, AuthCredentials auth, SwordConfiguration config)
throws SwordError, SwordServerException, SwordAuthException;
DepositReceipt replaceMediaResource(String uri, Deposit deposit, AuthCredentials auth, SwordConfiguration config)
throws SwordError, SwordServerException, SwordAuthException;
void deleteMediaResource(String uri, AuthCredentials auth, SwordConfiguration config)
throws SwordError, SwordServerException, SwordAuthException;
DepositReceipt addResource(String uri, Deposit deposit, AuthCredentials auth, SwordConfiguration config)
throws SwordError, SwordServerException, SwordAuthException;
}

View File

@@ -1,106 +0,0 @@
/**
* 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/
*/
package org.swordapp.server;
import com.hp.hpl.jena.rdf.model.Model;
import com.hp.hpl.jena.rdf.model.ModelFactory;
import com.hp.hpl.jena.rdf.model.Property;
import com.hp.hpl.jena.rdf.model.Resource;
import com.hp.hpl.jena.vocabulary.RDF;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.Writer;
import java.text.SimpleDateFormat;
public class OREStatement extends Statement
{
private String remUri;
private String aggUri;
public OREStatement(String remUri, String aggUri)
{
this.remUri = remUri;
this.aggUri = aggUri;
this.contentType = "application/rdf+xml";
}
@Override
public void writeTo(Writer out)
throws IOException
{
// create the default model (in memory) to start with
Model model = ModelFactory.createDefaultModel();
// create the resource map in the model
Resource rem = model.createResource(this.remUri);
rem.addProperty(RDF.type, model.createResource(UriRegistry.ORE_NAMESPACE + "ResourceMap"));
// create the aggregation
Resource agg = model.createResource(this.aggUri);
agg.addProperty(RDF.type, model.createResource(UriRegistry.ORE_NAMESPACE + "Aggregation"));
// add the aggregation to the resource (and vice versa)
rem.addProperty(model.createProperty(UriRegistry.ORE_NAMESPACE + "describes"), agg);
agg.addProperty(model.createProperty(UriRegistry.ORE_NAMESPACE + "isDescribedBy"), rem);
// now go through and add all the ResourceParts as aggregated resources
for (ResourcePart rp : this.resources)
{
Resource part = model.createResource(rp.getUri());
part.addProperty(RDF.type, model.createResource(UriRegistry.ORE_NAMESPACE + "AggregatedResource"));
agg.addProperty(model.createProperty(UriRegistry.ORE_NAMESPACE + "aggregates"), part);
}
// now go through all the original deposits and add them as both aggregated
// resources and as originalDeposits (with all the trimmings)
for (OriginalDeposit od : this.originalDeposits)
{
Resource deposit = model.createResource(od.getUri());
deposit.addProperty(RDF.type, model.createResource(UriRegistry.ORE_NAMESPACE + "AggregatedResource"));
if (od.getDepositedBy() != null)
{
deposit.addLiteral(model.createProperty(UriRegistry.SWORD_DEPOSITED_BY), od.getDepositedBy());
}
if (od.getDepositedOnBehalfOf() != null)
{
deposit.addLiteral(model.createProperty(UriRegistry.SWORD_DEPOSITED_ON_BEHALF_OF), od.getDepositedOnBehalfOf());
}
if (od.getDepositedOn() != null)
{
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
deposit.addLiteral(model.createProperty(UriRegistry.SWORD_DEPOSITED_ON), sdf.format(od.getDepositedOn()));
}
for (String packaging : od.getPackaging())
{
deposit.addLiteral(model.createProperty(UriRegistry.SWORD_PACKAGING.toString()), packaging);
}
agg.addProperty(model.createProperty(UriRegistry.ORE_NAMESPACE + "aggregates"), deposit);
agg.addProperty(model.createProperty(UriRegistry.SWORD_ORIGINAL_DEPOSIT), deposit);
}
// now add the state information
for (String state : this.states.keySet())
{
Resource s = model.createResource(state);
if (this.states.get(state) != null)
{
s.addProperty(model.createProperty(UriRegistry.SWORD_STATE_DESCRIPTION), this.states.get(state));
}
agg.addProperty(model.createProperty(UriRegistry.SWORD_STATE), s);
}
// write the model directly to the output
model.write(out, "RDF/XML");
}
}

View File

@@ -1,79 +0,0 @@
/**
* 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/
*/
package org.swordapp.server;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
public class OriginalDeposit extends ResourcePart
{
private List<String> packaging;
private Date depositedOn;
private String depositedBy;
private String depositedOnBehalfOf;
public OriginalDeposit(String uri)
{
this(uri, new ArrayList<String>(), null, null, null);
}
public OriginalDeposit(String uri, List<String> packaging, Date depositedOn, String depositedBy, String depositedOnBehalfOf)
{
super(uri);
this.packaging = packaging;
this.depositedOn = depositedOn;
this.depositedBy = depositedBy;
this.depositedOnBehalfOf = depositedOnBehalfOf;
}
public String getUri()
{
return uri;
}
public List<String> getPackaging()
{
return packaging;
}
public void setPackaging(List<String> packaging)
{
this.packaging = packaging;
}
public Date getDepositedOn()
{
return depositedOn;
}
public void setDepositedOn(Date depositedOn)
{
this.depositedOn = depositedOn;
}
public String getDepositedBy()
{
return depositedBy;
}
public void setDepositedBy(String depositedBy)
{
this.depositedBy = depositedBy;
}
public String getDepositedOnBehalfOf()
{
return depositedOnBehalfOf;
}
public void setDepositedOnBehalfOf(String depositedOnBehalfOf)
{
this.depositedOnBehalfOf = depositedOnBehalfOf;
}
}

View File

@@ -1,48 +0,0 @@
/**
* 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/
*/
package org.swordapp.server;
import java.util.HashMap;
import java.util.Map;
public class ResourcePart
{
protected String uri;
protected String mediaType;
protected Map<String, String> properties = new HashMap<String, String>();
public ResourcePart(String uri)
{
this.uri = uri;
}
public String getUri()
{
return uri;
}
public String getMediaType()
{
return mediaType;
}
public void setMediaType(String mediaType)
{
this.mediaType = mediaType;
}
public void setProperties(Map<String, String> properties)
{
this.properties = properties;
}
public void addProperty(String predicate, String object)
{
this.properties.put(predicate, object);
}
}

View File

@@ -1,61 +0,0 @@
/**
* 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/
*/
package org.swordapp.server;
import org.apache.abdera.Abdera;
import org.apache.abdera.model.Service;
import org.apache.abdera.model.Workspace;
public class ServiceDocument
{
private String version = "2.0";
private int maxUploadSize = -1;
private Service service;
public ServiceDocument()
{
Abdera abdera = new Abdera();
this.service = abdera.newService();
}
public Service getWrappedService()
{
return service;
}
public Service getAbderaService()
{
// here is where we compress everything from SWORD into Abdera
// and the output has to be a full clone, not by reference
Service abderaService = (Service) this.service.clone();
abderaService.addSimpleExtension(UriRegistry.SWORD_VERSION, this.version);
if (maxUploadSize > -1)
{
abderaService.addSimpleExtension(UriRegistry.SWORD_MAX_UPLOAD_SIZE, Integer.toString(this.maxUploadSize));
}
return abderaService;
}
public void setVersion(String version)
{
this.version = version;
}
public void setMaxUploadSize(int maxUploadSize)
{
this.maxUploadSize = maxUploadSize;
}
public void addWorkspace(SwordWorkspace workspace)
{
// FIXME: or do we just keep a reference of these until we get a call to getAbderaService()?
Workspace abderaWorkspace = workspace.getAbderaWorkspace();
this.service.addWorkspace(abderaWorkspace);
}
}

View File

@@ -1,98 +0,0 @@
/**
* 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/
*/
package org.swordapp.server;
import org.apache.abdera.model.Element;
import org.apache.log4j.Logger;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class ServiceDocumentAPI extends SwordAPIEndpoint
{
private static Logger log = Logger.getLogger(ServiceDocumentAPI.class);
protected ServiceDocumentManager sdm;
public ServiceDocumentAPI(ServiceDocumentManager sdm, SwordConfiguration config)
{
super(config);
this.sdm = sdm;
}
public void get(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException
{
// do the initial authentication
AuthCredentials auth = null;
try
{
auth = this.getAuthCredentials(req);
}
catch (SwordAuthException e)
{
if (e.isRetry())
{
String s = "Basic realm=\"SWORD2\"";
resp.setHeader("WWW-Authenticate", s);
resp.setStatus(401);
return;
}
else
{
resp.sendError(HttpServletResponse.SC_BAD_REQUEST, e.getMessage());
return;
}
}
try
{
String sdUri = this.getFullUrl(req);
// delegate to the implementation to get the service document itself
ServiceDocument serviceDocument = this.sdm.getServiceDocument(sdUri, auth, this.config);
this.addGenerator(serviceDocument, this.config);
// set the content-type and write the service document to the output stream
resp.setHeader("Content-Type", "application/atomserv+xml");
serviceDocument.getAbderaService().writeTo(resp.getWriter());
}
catch (SwordError se)
{
// this is a SWORD level error, to be thrown to the client appropriately
this.swordError(req, resp, se);
}
catch (SwordServerException e)
{
// this is something else, to be raised as an internal server error
throw new ServletException(e);
}
catch (SwordAuthException e)
{
// authentication actually failed at the server end; not a SwordError, but
// need to throw a 403 Forbidden
resp.sendError(403);
}
finally
{
// flush the output stream
resp.getWriter().flush();
}
}
protected void addGenerator(ServiceDocument doc, SwordConfiguration config)
{
Element generator = this.getGenerator(this.config);
if (generator != null)
{
doc.getWrappedService().addExtension(generator);
}
}
}

View File

@@ -1,13 +0,0 @@
/**
* 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/
*/
package org.swordapp.server;
public interface ServiceDocumentManager
{
ServiceDocument getServiceDocument(String sdUri, AuthCredentials auth, SwordConfiguration config) throws SwordError, SwordServerException, SwordAuthException;
}

View File

@@ -1,76 +0,0 @@
/**
* 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/
*/
package org.swordapp.server;
import java.io.IOException;
import java.io.Writer;
import java.util.Date;
import java.util.List;
import java.util.Map;
public abstract class Statement
{
protected String contentType;
protected List<OriginalDeposit> originalDeposits;
protected Map<String, String> states;
protected List<ResourcePart> resources;
protected Date lastModified;
public abstract void writeTo(Writer out) throws IOException;
public String getContentType()
{
return contentType;
}
public void setOriginalDeposits(List<OriginalDeposit> originalDeposits)
{
this.originalDeposits = originalDeposits;
}
public void addOriginalDeposit(OriginalDeposit originalDeposit)
{
this.originalDeposits.add(originalDeposit);
}
public void setResources(List<ResourcePart> resources)
{
this.resources = resources;
}
public void addResource(ResourcePart resource)
{
this.resources.add(resource);
}
public void setStates(Map<String, String> states)
{
this.states = states;
}
public void setState(String state, String description)
{
this.states.clear();
this.states.put(state, description);
}
public void addState(String state, String description)
{
this.states.put(state, description);
}
public Date getLastModified()
{
return lastModified;
}
public void setLastModified(Date lastModified)
{
this.lastModified = lastModified;
}
}

View File

@@ -1,108 +0,0 @@
/**
* 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/
*/
package org.swordapp.server;
import org.apache.log4j.Logger;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.StringWriter;
import java.security.NoSuchAlgorithmException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Map;
public class StatementAPI extends SwordAPIEndpoint
{
private static Logger log = Logger.getLogger(CollectionAPI.class);
private StatementManager sm;
public StatementAPI(StatementManager sm, SwordConfiguration config)
{
super(config);
this.sm = sm;
}
public void get(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException
{
// do the initial authentication
AuthCredentials auth = null;
try
{
auth = this.getAuthCredentials(req);
}
catch (SwordAuthException e)
{
if (e.isRetry())
{
String s = "Basic realm=\"SWORD2\"";
resp.setHeader("WWW-Authenticate", s);
resp.setStatus(401);
return;
}
else
{
resp.sendError(HttpServletResponse.SC_BAD_REQUEST, e.getMessage());
return;
}
}
try
{
// there may be some content negotiation going on
Map<String, String> accept = this.getAcceptHeaders(req);
String uri = this.getFullUrl(req);
Statement statement = this.sm.getStatement(uri, accept, auth, this.config);
// set the content type
resp.setHeader("Content-Type", statement.getContentType());
// set the last modified header
// like: Last-Modified: Tue, 15 Nov 1994 12:45:26 GMT
SimpleDateFormat sdf = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss Z");
Date lastModified = statement.getLastModified() != null ? statement.getLastModified() : new Date();
resp.setHeader("Last-Modified", sdf.format(lastModified));
// to set the content-md5 header we need to write the output to
// a string and checksum it
StringWriter writer = new StringWriter();
statement.writeTo(writer);
// write the content-md5 header
String md5 = ChecksumUtils.generateMD5(writer.toString().getBytes());
resp.setHeader("Content-MD5", md5);
resp.getWriter().append(writer.toString());
resp.getWriter().flush();
}
catch (SwordServerException e)
{
throw new ServletException(e);
}
catch (SwordError se)
{
this.swordError(req, resp, se);
}
catch (NoSuchAlgorithmException e)
{
throw new ServletException(e);
}
catch (SwordAuthException e)
{
// authentication actually failed at the server end; not a SwordError, but
// need to throw a 403 Forbidden
resp.sendError(403);
}
}
}

View File

@@ -1,16 +0,0 @@
/**
* 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/
*/
package org.swordapp.server;
import java.util.Map;
public interface StatementManager
{
Statement getStatement(String iri, Map<String, String> accept, AuthCredentials auth, SwordConfiguration config)
throws SwordServerException, SwordError, SwordAuthException;
}

View File

@@ -1,547 +0,0 @@
/**
* 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/
*/
package org.swordapp.server;
import org.apache.abdera.Abdera;
import org.apache.abdera.model.Document;
import org.apache.abdera.model.Element;
import org.apache.abdera.model.Entry;
import org.apache.abdera.parser.Parser;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.fileupload.FileItemFactory;
import org.apache.commons.fileupload.FileUploadException;
import org.apache.commons.fileupload.disk.DiskFileItem;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import org.apache.log4j.Logger;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.security.NoSuchAlgorithmException;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
public class SwordAPIEndpoint
{
protected SwordConfiguration config;
private static Logger log = Logger.getLogger(SwordAPIEndpoint.class);
protected SwordAPIEndpoint(SwordConfiguration config)
{
this.config = config;
}
protected AuthCredentials getAuthCredentials(HttpServletRequest request)
throws SwordAuthException
{
// is there an On-Behalf-Of header?
String obo = request.getHeader("On-Behalf-Of");
// is authentication required
String authType = this.config.getAuthType();
boolean authRequired = "Basic".equals(authType);
// is the user authenticating?
String authHeader = request.getHeader("Authorization");
// are we meant to authenticate, but haven't been given anything?
if (authRequired && (authHeader == null || "".equals(authHeader)))
{
throw new SwordAuthException(true);
}
// by this stage we are either meant to authenticate and have been given credentials or
// we don't need to authenticate. Either way we just fill in the AuthCredentials
String[] userPass = this.decodeAuthHeader(authHeader);
if (userPass == null)
{
log.debug("No Authentication Credentials supplied");
return new AuthCredentials(null, null, obo);
}
AuthCredentials auth = new AuthCredentials(userPass[0], userPass[1], obo);
return auth;
}
protected String[] decodeAuthHeader(String encodedHeader)
throws SwordAuthException
{
// we have an authentication header, so parse it
String[] authBits = encodedHeader.split(" ");
// Auth header doesn't have 2 parts (Basic, [base 64 username/password])?
if (authBits.length != 2)
{
log.fatal("Malformed Authorization header");
throw new SwordAuthException("Malformed Authorization header");
}
// is this basic auth? if not, we don't support it
if (!"Basic".equalsIgnoreCase(authBits[0].trim()))
{
log.warn("Authentication method not supported: " + authBits[0]);
return null;
}
// get the username and password out of the base64 encoded Basic auth string
String unencodedCreds = new String(Base64.decodeBase64(authBits[1].trim().getBytes()));
String[] userPass = unencodedCreds.split(":", 2);
// did we get a username and password?
if (userPass.length != 2)
{
log.fatal("Malformed Authorization header; unable to determine username/password boundary");
throw new SwordAuthException("Malformed Authorization header; unable to determine username/password boundary");
}
return userPass;
}
protected String getFullUrl(HttpServletRequest req)
{
String url = req.getRequestURL().toString();
String q = req.getQueryString();
if (q != null && !"".equals(q))
{
url += "?" + q;
}
return url;
}
protected void storeAndCheckBinary(Deposit deposit, SwordConfiguration config)
throws SwordServerException, SwordError
{
// we require an input stream for this to work
if (deposit.getInputStream() == null)
{
throw new SwordServerException("Attempting to store and check deposit which has no input stream");
}
if (!config.storeAndCheckBinary())
{
return;
}
String tempDirectory = config.getTempDirectory();
if (tempDirectory == null)
{
throw new SwordServerException("Store and Check operation requested, but no tempDirectory specified in config");
}
String filename = tempDirectory + File.separator + "SWORD-" + UUID.randomUUID().toString();
try
{
InputStream inputstream = deposit.getInputStream();
OutputStream outputstream = new FileOutputStream(new File(filename));
try
{
byte[] buf = new byte[1024];
int len;
while ((len = inputstream.read(buf)) > 0)
{
outputstream.write(buf, 0, len);
}
}
finally
{
inputstream.close();
outputstream.close();
}
}
catch (IOException e)
{
throw new SwordServerException(e);
}
// Check the size is OK
File file = new File(filename);
long fLength = file.length() / 1024; // in kilobytes
if ((config.getMaxUploadSize() != -1) && (fLength > config.getMaxUploadSize()))
{
String msg = "The uploaded file exceeded the maximum file size this server will accept (the file is " +
fLength + "kB but the server will only accept files as large as " +
config.getMaxUploadSize() + "kB)";
throw new SwordError(UriRegistry.ERROR_MAX_UPLOAD_SIZE_EXCEEDED, msg);
}
try
{
// get the things we might want to compare
String receivedMD5 = ChecksumUtils.generateMD5(filename);
log.debug("Received filechecksum: " + receivedMD5);
String md5 = deposit.getMd5();
log.debug("Received file checksum header: " + md5);
if ((md5 != null) && (!md5.equals(receivedMD5)))
{
log.debug("Bad MD5 for file. Aborting with appropriate error message");
String msg = "The received MD5 checksum for the deposited file did not match the checksum sent by the deposit client";
throw new SwordError(UriRegistry.ERROR_CHECKSUM_MISMATCH, msg);
}
else
{
// Set the file to be deposited
deposit.setFile(file);
}
log.debug("Package temporarily stored as: " + filename);
}
catch (NoSuchAlgorithmException e)
{
throw new SwordServerException(e);
}
catch (IOException e)
{
throw new SwordServerException(e);
}
}
protected void addDepositPropertiesFromMultipart(Deposit deposit, HttpServletRequest req)
throws ServletException, IOException, SwordError
{
// Parse the request for files (using the fileupload commons library)
List<DiskFileItem> items = this.getPartsFromRequest(req);
for (DiskFileItem item : items)
{
// find out which part we are looking at
String contentDisposition = item.getHeaders().getHeader("Content-Disposition");
String name = this.getName(contentDisposition);
if ("atom".equals(name))
{
InputStream entryPart = item.getInputStream();
Abdera abdera = new Abdera();
Parser parser = abdera.getParser();
Document<Entry> entryDoc = parser.parse(entryPart);
Entry entry = entryDoc.getRoot();
deposit.setEntry(entry);
}
else if ("payload".equals(name))
{
String md5 = item.getHeaders().getHeader("Content-MD5");
String packaging = item.getHeaders().getHeader("Packaging");
String filename = this.getFilename(contentDisposition);
if (filename == null || "".equals(filename))
{
throw new SwordError(UriRegistry.ERROR_BAD_REQUEST, "Filename could not be extracted from Content-Disposition");
}
String ct = item.getContentType();
String mimeType = "application/octet-stream";
if (ct != null)
{
String[] bits = ct.split(";");
mimeType = bits[0].trim();
}
InputStream mediaPart = item.getInputStream();
deposit.setFilename(filename);
deposit.setInputStream(mediaPart);
deposit.setMimeType(mimeType);
deposit.setMd5(md5);
deposit.setPackaging(packaging);
}
}
try
{
this.storeAndCheckBinary(deposit, this.config);
}
catch (SwordServerException e)
{
throw new ServletException(e);
}
}
protected Element getGenerator(SwordConfiguration config)
{
String generatorUri = config.generator();
String generatorVersion = config.generatorVersion();
String adminEmail = config.administratorEmail();
if (generatorUri != null && !"".equals(generatorUri))
{
Abdera abdera = new Abdera();
Element generator = abdera.getFactory().newGenerator();
generator.setAttributeValue("uri", generatorUri);
if (generatorVersion != null)
{
generator.setAttributeValue("version", generatorVersion);
}
if (adminEmail != null && !"".equals(adminEmail))
{
generator.setText(adminEmail);
}
return generator;
}
return null;
}
protected void addDepositPropertiesFromEntry(Deposit deposit, HttpServletRequest req)
throws IOException
{
InputStream entryPart = req.getInputStream();
Abdera abdera = new Abdera();
Parser parser = abdera.getParser();
Document<Entry> entryDoc = parser.parse(entryPart);
Entry entry = entryDoc.getRoot();
deposit.setEntry(entry);
}
protected void addDepositPropertiesFromBinary(Deposit deposit, HttpServletRequest req)
throws ServletException, IOException, SwordError
{
String contentType = this.getContentType(req);
String contentDisposition = req.getHeader("Content-Disposition");
String md5 = req.getHeader("Content-MD5");
String packaging = req.getHeader("Packaging");
if (packaging == null || "".equals(packaging))
{
packaging = UriRegistry.PACKAGE_BINARY;
}
InputStream file = req.getInputStream();
// now let's interpret and deal with the headers that we have
String filename = this.getFilename(contentDisposition);
if (filename == null || "".equals(filename))
{
throw new SwordError(UriRegistry.ERROR_BAD_REQUEST, "Filename could not be extracted from Content-Disposition");
}
deposit.setFilename(filename);
deposit.setMd5(md5);
deposit.setPackaging(packaging);
deposit.setInputStream(file);
deposit.setMimeType(contentType);
try
{
this.storeAndCheckBinary(deposit, this.config);
}
catch (SwordServerException e)
{
throw new ServletException(e);
}
}
protected void swordError(HttpServletRequest req, HttpServletResponse resp, SwordError e)
throws IOException, ServletException
{
try
{
if (!this.config.returnErrorBody())
{
ErrorDocument doc = new ErrorDocument(e.getErrorUri(), e.getStatus());
resp.sendError(doc.getStatus());
return;
}
// treatment is either the default value in the ErrorDocument OR the error message if it exists
String treatment = e.getMessage();
// verbose description is the stack trace if allowed, otherwise null
String verbose = null;
if (this.config.returnStackTraceInError())
{
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
e.printStackTrace(pw);
verbose = sw.getBuffer().toString();
}
ErrorDocument doc;
if (treatment == null)
{
doc = new ErrorDocument(e.getErrorUri(), e.getStatus(), verbose);
}
else
{
doc = new ErrorDocument(e.getErrorUri(), e.getStatus(), treatment, verbose);
}
// now write the response
resp.setStatus(doc.getStatus());
resp.setHeader("Content-Type", "text/xml");
doc.writeTo(resp.getWriter(), this.config);
resp.getWriter().flush();
}
catch (SwordServerException sse)
{
throw new ServletException(sse);
}
}
protected String getFilename(String contentDisposition)
{
if (contentDisposition == null)
{
return null;
}
// content dispositon should be of the form
//
// Content-Disposition: attachment; filename="[filename]"
// but may have other features too, so we need to pick it apart
String token = "; filename=";
int fnArg = contentDisposition.indexOf(token);
String fromFilename = contentDisposition.substring(fnArg + token.length());
int nextSemiColon = fromFilename.indexOf(";");
if (nextSemiColon < 0)
{
// we already have the filename
return fromFilename;
}
String filename = fromFilename.substring(0, nextSemiColon);
if (filename.startsWith("\""))
{
filename = filename.substring(1);
}
if (filename.endsWith("\""))
{
filename = filename.substring(0, filename.length());
}
return filename;
}
protected String getName(String contentDisposition)
{
// FIXME: this is the same code as above, but with a different token; generalise
if (contentDisposition == null)
{
return null;
}
// content dispositon should be of the form
//
// Content-Disposition: attachment; filename="[filename]"
// but may have other features too, so we need to pick it apart
String token = "; name=";
int nameArg = contentDisposition.indexOf(token);
String fromName = contentDisposition.substring(nameArg + token.length());
int nextSemiColon = fromName.indexOf(";");
String name = fromName.substring(0, nextSemiColon);
if (name.startsWith("\""))
{
name = name.substring(1);
}
if (name.endsWith("\""))
{
name = name.substring(0, name.length());
}
return name;
}
protected List<DiskFileItem> getPartsFromRequest(HttpServletRequest request)
throws ServletException
{
try
{
// Create a factory for disk-based file items
FileItemFactory factory = new DiskFileItemFactory();
// Create a new file upload handler
ServletFileUpload upload = new ServletFileUpload(factory);
// Parse the request
List<DiskFileItem> items = upload.parseRequest(request);
return items;
}
catch (FileUploadException e)
{
throw new ServletException(e);
}
}
protected Map<String, String> getAcceptHeaders(HttpServletRequest req)
{
Map<String, String> acceptHeaders = new HashMap<String, String>();
Enumeration headers = req.getHeaderNames();
while (headers.hasMoreElements())
{
String header = (String) headers.nextElement();
if (header.toLowerCase().startsWith("accept"))
{
acceptHeaders.put(header, req.getHeader(header));
}
}
return acceptHeaders;
}
protected void copyInputToOutput(InputStream in, OutputStream out)
throws IOException
{
final int BUFFER_SIZE = 1024 * 4;
final byte[] buffer = new byte[BUFFER_SIZE];
while (true)
{
final int count = in.read(buffer, 0, BUFFER_SIZE);
if (-1 == count)
{
break;
}
// write out those same bytes
out.write(buffer, 0, count);
}
}
protected String getContentType(HttpServletRequest req)
{
String contentType = req.getHeader("Content-Type");
if (contentType == null)
{
contentType = "application/octet-stream";
}
return contentType;
}
protected boolean getInProgress(HttpServletRequest req)
throws SwordError
{
String iph = req.getHeader("In-Progress");
boolean inProgress = false; // default value
if (iph != null)
{
// first of all validate that the value is "true" or "false"
if (!"true".equals(iph.trim()) && !"false".equals(iph.trim()))
{
throw new SwordError(UriRegistry.ERROR_BAD_REQUEST, "The In-Progress header MUST be 'true' or 'false'");
}
inProgress = "true".equals(iph.trim());
}
return inProgress;
}
protected boolean getMetadataRelevant(HttpServletRequest req)
throws SwordError
{
String mdr = req.getHeader("Metadata-Relevant");
boolean metadataRelevant = false; // default value
if (mdr != null)
{
// first of all validate that the value is "true" or "false"
if (!"true".equals(mdr.trim()) && !"false".equals(mdr.trim()))
{
throw new SwordError(UriRegistry.ERROR_BAD_REQUEST, "The In-Progress header MUST be 'true' or 'false'");
}
metadataRelevant = "true".equals(mdr.trim());
}
return metadataRelevant;
}
}

View File

@@ -1,44 +0,0 @@
/**
* 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/
*/
package org.swordapp.server;
public class SwordAuthException extends Exception
{
private boolean retry = false;
public SwordAuthException()
{
super();
}
public SwordAuthException(boolean retry)
{
super();
this.retry = retry;
}
public SwordAuthException(String message)
{
super(message);
}
public SwordAuthException(String message, Throwable cause)
{
super(message, cause);
}
public SwordAuthException(Throwable cause)
{
super(cause);
}
public boolean isRetry()
{
return retry;
}
}

View File

@@ -1,345 +0,0 @@
/**
* 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/
*/
package org.swordapp.server;
import org.apache.abdera.Abdera;
import org.apache.abdera.i18n.iri.IRI;
import org.apache.abdera.model.Collection;
import org.apache.abdera.model.Element;
import javax.xml.namespace.QName;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class SwordCollection
{
private Collection collection;
private List<String> multipartAccept = new ArrayList<String>();
private Abdera abdera;
private String collectionPolicy = null;
private boolean mediation = false;
private String treatment = null;
private List<String> acceptPackaging = new ArrayList<String>();
private List<IRI> subServices = new ArrayList<IRI>();
private String dcAbstract = null;
public SwordCollection()
{
this.abdera = new Abdera();
this.collection = this.abdera.getFactory().newCollection();
}
public Collection getWrappedCollection()
{
return this.collection;
}
public Collection getAbderaCollection()
{
Collection abderaCollection = (Collection) this.collection.clone();
// FIXME: this is wrong; clients must be free to leave the accepts blank
// // ensure that there is an accept field set
// List<String> singlePartAccepts = this.getSinglepartAccept();
// if (singlePartAccepts.size() == 0)
// {
// abderaCollection.addAccepts("*/*");
// }
//
// // ensure that there is multipart accepts set
// List<String> multipartAccepts = this.getMultipartAccept();
// if (multipartAccepts.size() == 0 || this.multipartAccept.size() == 0)
// {
// this.multipartAccept.add("*/*");
// }
// add the multipart accepts as elements
for (String mpa : this.multipartAccept)
{
Element element = this.abdera.getFactory().newElement(UriRegistry.APP_ACCEPT);
element.setAttributeValue("alternate", "multipart/related");
element.setText(mpa);
abderaCollection.addExtension(element);
}
// add the collection Policy
if (this.collectionPolicy != null)
{
abderaCollection.addSimpleExtension(UriRegistry.SWORD_COLLECTION_POLICY, this.collectionPolicy);
}
// add the mediation
abderaCollection.addSimpleExtension(UriRegistry.SWORD_MEDIATION, (this.mediation ? "true" : "false"));
// add the treatment
if (this.treatment != null)
{
abderaCollection.addSimpleExtension(UriRegistry.SWORD_TREATMENT, this.treatment);
}
// add the acceptPackaging
for (String ap : this.acceptPackaging)
{
abderaCollection.addSimpleExtension(UriRegistry.SWORD_ACCEPT_PACKAGING, ap);
}
// add the sub service document IRI
for (IRI ss : this.subServices)
{
abderaCollection.addSimpleExtension(UriRegistry.SWORD_SERVICE, ss.toString());
}
// add the abstract
if (this.dcAbstract != null)
{
abderaCollection.addSimpleExtension(UriRegistry.DC_ABSTRACT, this.dcAbstract);
}
return abderaCollection;
}
public void setLocation(String href)
{
this.collection.setHref(href);
}
public void setAbstract(String dcAbstract)
{
this.dcAbstract = dcAbstract;
}
public List<IRI> getSubServices()
{
return subServices;
}
public void setSubServices(List<IRI> subServices)
{
this.subServices = subServices;
}
public void addSubService(IRI subService)
{
this.subServices.add(subService);
}
public void setCollectionPolicy(String collectionPolicy)
{
this.collectionPolicy = collectionPolicy;
}
public void setMediation(boolean mediation)
{
this.mediation = mediation;
}
public void setTreatment(String treatment)
{
this.treatment = treatment;
}
public void setAcceptPackaging(List<String> acceptPackaging)
{
this.acceptPackaging = acceptPackaging;
}
public void addAcceptPackaging(String acceptPackaging)
{
this.acceptPackaging.add(acceptPackaging);
}
public String getCollectionPolicy()
{
return collectionPolicy;
}
public boolean isMediation()
{
return mediation;
}
public String getTreatment()
{
return treatment;
}
public List<String> getAcceptPackaging()
{
return acceptPackaging;
}
public void setTitle(String title)
{
this.collection.setTitle(title);
}
public void setHref(String href)
{
this.collection.setHref(href);
}
public void setAccept(String... mediaRanges)
{
this.collection.setAccept(mediaRanges);
}
public void setAcceptsEntry()
{
this.collection.setAcceptsEntry();
}
public void setAcceptsNothing()
{
this.collection.setAcceptsNothing();
}
public void addAccepts(String mediaRange)
{
this.collection.addAccepts(mediaRange);
}
public void addAccepts(String... mediaRanges)
{
this.collection.addAccepts(mediaRanges);
}
public void addAcceptsEntry()
{
this.collection.addAcceptsEntry();
}
public void setMultipartAccept(String... mediaRanges)
{
List<String> mrs = Arrays.asList(mediaRanges);
this.multipartAccept.clear();
this.multipartAccept.addAll(mrs);
}
public void addMultipartAccepts(String mediaRange)
{
this.multipartAccept.add(mediaRange);
}
public void addMultipartAccepts(String... mediaRanges)
{
List<String> mrs = Arrays.asList(mediaRanges);
this.multipartAccept.addAll(mrs);
}
public List<String> getMultipartAccept()
{
List<String> accepts = new ArrayList<String>();
List<Element> elements = this.collection.getElements();
boolean noAccept = false;
for (Element e : elements)
{
String multipartRelated = e.getAttributeValue("alternate");
QName qn = e.getQName();
if (qn.getLocalPart().equals("accept") &&
qn.getNamespaceURI().equals(UriRegistry.APP_NAMESPACE) &&
"multipart-related".equals(multipartRelated))
{
String content = e.getText();
if (content == null || "".equals(content))
{
noAccept = true;
}
if (content != null && !"".equals(content) && !accepts.contains(content))
{
accepts.add(content);
}
}
}
// if there are no accept values, and noAccept has not been triggered, then we add the
// default accept type
if (accepts.size() == 0 && !noAccept)
{
accepts.add("application/atom+xml;type=entry");
}
// rationalise and return
return this.rationaliseAccepts(accepts);
}
public List<String> getSinglepartAccept()
{
List<String> accepts = new ArrayList<String>();
List<Element> elements = this.collection.getElements();
boolean noAccept = false;
for (Element e : elements)
{
String multipartRelated = e.getAttributeValue("alternate");
QName qn = e.getQName();
if (qn.getLocalPart().equals("accept") &&
qn.getNamespaceURI().equals(UriRegistry.APP_NAMESPACE) &&
!"multipart-related".equals(multipartRelated))
{
String content = e.getText();
if (content == null || "".equals(content))
{
noAccept = true;
}
if (content != null && !"".equals(content) && !accepts.contains(content))
{
accepts.add(content);
}
}
}
// if there are no accept values, and noAccept has not been triggered, then we add the
// default accept type
if (accepts.size() == 0 && !noAccept)
{
accepts.add("application/atom+xml;type=entry");
}
// rationalise and return
return this.rationaliseAccepts(accepts);
}
private List<String> rationaliseAccepts(List<String> accepts)
{
List<String> rational = new ArrayList<String>();
// first, if "*/*" is there, then we accept anything
if (accepts.contains("*/*"))
{
rational.add("*/*");
return rational;
}
// now look to see if we have <x>/* and if so eliminate the unnecessary accepts
List<String> wildcards = new ArrayList<String>();
for (String a : accepts)
{
if (a.contains("/*"))
{
String wild = a.substring(0, a.indexOf("/"));
wildcards.add(wild);
if (!rational.contains(a))
{
rational.add(a);
}
}
}
for (String a : accepts)
{
String type = a.substring(0, a.indexOf("/"));
if (!wildcards.contains(type))
{
rational.add(a);
}
}
// by the time we get here we will have only unique and correctly wildcarded accept fields
return rational;
}
}

View File

@@ -1,31 +0,0 @@
/**
* 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/
*/
package org.swordapp.server;
public interface SwordConfiguration
{
boolean returnDepositReceipt();
boolean returnStackTraceInError();
boolean returnErrorBody();
String generator();
String generatorVersion();
String administratorEmail();
String getAuthType();
boolean storeAndCheckBinary();
String getTempDirectory();
int getMaxUploadSize();
}

View File

@@ -1,61 +0,0 @@
/**
* 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/
*/
package org.swordapp.server;
public class SwordConfigurationDefault implements SwordConfiguration
{
public boolean returnDepositReceipt()
{
return true;
}
public boolean returnStackTraceInError()
{
return true;
}
public boolean returnErrorBody()
{
return true;
}
public String generator()
{
return "http://www.swordapp.org/";
}
public String generatorVersion()
{
return "2.0";
}
public String administratorEmail()
{
return null;
}
public String getAuthType()
{
return "None";
}
public boolean storeAndCheckBinary()
{
return false;
}
public String getTempDirectory()
{
return null;
}
public int getMaxUploadSize()
{
return -1;
}
}

View File

@@ -1,86 +0,0 @@
/**
* 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/
*/
package org.swordapp.server;
import org.apache.abdera.model.Element;
import org.apache.abdera.model.Entry;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class SwordEntry
{
public Entry entry;
public SwordEntry(Entry entry)
{
this.entry = entry;
}
public String getTitle()
{
return this.entry.getTitle();
}
public String getSummary()
{
return this.entry.getSummary();
}
public Entry getEntry()
{
return this.entry;
}
public String toString()
{
try
{
ByteArrayOutputStream baos = new ByteArrayOutputStream();
this.entry.writeTo(baos);
return baos.toString();
}
catch (IOException e)
{
throw new RuntimeException(e);
}
}
// put the code in for getting a dublin core record out
public Map<String, List<String>> getDublinCore()
{
Map<String, List<String>> dc = new HashMap<String, List<String>>();
List<Element> extensions = this.entry.getExtensions();
for (Element element : extensions)
{
if (UriRegistry.DC_NAMESPACE.equals(element.getQName().getNamespaceURI()))
{
// we have a dublin core extension
String field = element.getQName().getLocalPart();
String value = element.getText();
if (dc.containsKey(field))
{
dc.get(field).add(value);
}
else
{
ArrayList<String> values = new ArrayList<String>();
values.add(value);
dc.put(field, values);
}
}
}
return dc;
}
}

View File

@@ -1,84 +0,0 @@
/**
* 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/
*/
package org.swordapp.server;
import java.util.HashMap;
import java.util.Map;
public class SwordError extends Exception
{
private String errorUri;
private int status = -1;
public SwordError()
{
super();
}
public SwordError(String errorUri)
{
super(errorUri);
this.errorUri = errorUri;
}
public SwordError(String errorUri, int status)
{
super(errorUri);
this.errorUri = errorUri;
this.status = status;
}
public SwordError(String errorUri, String message)
{
super(message);
this.errorUri = errorUri;
}
public SwordError(String errorUri, int status, String message)
{
super(message);
this.errorUri = errorUri;
this.status = status;
}
public SwordError(String errorUri, Throwable cause)
{
super(errorUri, cause);
this.errorUri = errorUri;
}
public SwordError(String errorUri, int status, Throwable cause)
{
super(errorUri, cause);
this.errorUri = errorUri;
this.status = status;
}
public SwordError(String errorUri, String message, Throwable cause)
{
super(message, cause);
this.errorUri = errorUri;
}
public SwordError(String errorUri, int status, String message, Throwable cause)
{
super(message, cause);
this.errorUri = errorUri;
this.status = status;
}
public String getErrorUri()
{
return errorUri;
}
public int getStatus()
{
return status;
}
}

View File

@@ -1,31 +0,0 @@
/**
* 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/
*/
package org.swordapp.server;
public class SwordServerException extends Exception
{
public SwordServerException()
{
super();
}
public SwordServerException(String message)
{
super(message);
}
public SwordServerException(String message, Throwable cause)
{
super(message, cause);
}
public SwordServerException(Throwable cause)
{
super(cause);
}
}

View File

@@ -1,47 +0,0 @@
/**
* 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/
*/
package org.swordapp.server;
import org.apache.abdera.Abdera;
import org.apache.abdera.model.Collection;
import org.apache.abdera.model.Text;
import org.apache.abdera.model.Workspace;
public class SwordWorkspace
{
private Workspace workspace;
public SwordWorkspace()
{
Abdera abdera = new Abdera();
this.workspace = abdera.getFactory().newWorkspace();
}
public Workspace getWrappedWorkspace()
{
return workspace;
}
public Workspace getAbderaWorkspace()
{
// at the moment, this doesn't need to clone anything
return workspace;
}
public void addCollection(SwordCollection collection)
{
// FIXME: or should collections be managed internally until getAbderaWorkspace is called
Collection abderaCollection = collection.getAbderaCollection();
this.workspace.addCollection(abderaCollection);
}
public Text setTitle(String title)
{
return this.workspace.setTitle(title);
}
}

View File

@@ -1,60 +0,0 @@
/**
* 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/
*/
package org.swordapp.server;
import javax.xml.namespace.QName;
public class UriRegistry
{
// Namespaces
public static String SWORD_TERMS_NAMESPACE = "http://purl.org/net/sword/terms/";
public static String APP_NAMESPACE = "http://www.w3.org/2007/app";
public static String DC_NAMESPACE = "http://purl.org/dc/terms/";
public static String ORE_NAMESPACE = "http://www.openarchives.org/ore/terms/";
public static String ATOM_NAMESPACE = "http://www.w3.org/2005/Atom";
// QNames for Extension Elements
public static QName SWORD_VERSION = new QName(SWORD_TERMS_NAMESPACE, "version");
public static QName SWORD_MAX_UPLOAD_SIZE = new QName(SWORD_TERMS_NAMESPACE, "maxUploadSize");
public static QName SWORD_COLLECTION_POLICY = new QName(SWORD_TERMS_NAMESPACE, "collectionPolicy");
public static QName SWORD_MEDIATION = new QName(SWORD_TERMS_NAMESPACE, "mediation");
public static QName SWORD_TREATMENT = new QName(SWORD_TERMS_NAMESPACE, "treatment");
public static QName SWORD_ACCEPT_PACKAGING = new QName(SWORD_TERMS_NAMESPACE, "acceptPackaging");
public static QName SWORD_SERVICE = new QName(SWORD_TERMS_NAMESPACE, "service");
public static QName SWORD_PACKAGING = new QName(SWORD_TERMS_NAMESPACE, "packaging");
public static QName SWORD_VERBOSE_DESCRIPTION = new QName(SWORD_TERMS_NAMESPACE, "verboseDescription");
public static QName APP_ACCEPT = new QName(APP_NAMESPACE, "accept");
public static QName DC_ABSTRACT = new QName(DC_NAMESPACE, "abstract");
// URIs for the statement
public static String SWORD_DEPOSITED_BY = SWORD_TERMS_NAMESPACE + "depositedBy";
public static String SWORD_DEPOSITED_ON_BEHALF_OF = SWORD_TERMS_NAMESPACE + "depositedOnBehalfOf";
public static String SWORD_DEPOSITED_ON = SWORD_TERMS_NAMESPACE + "depositedOn";
public static String SWORD_ORIGINAL_DEPOSIT = SWORD_TERMS_NAMESPACE + "originalDeposit";
public static String SWORD_STATE_DESCRIPTION = SWORD_TERMS_NAMESPACE + "stateDescription";
public static String SWORD_STATE = SWORD_TERMS_NAMESPACE + "state";
// rel values
public static String REL_STATEMENT = "http://purl.org/net/sword/terms/statement";
public static String REL_SWORD_EDIT = "http://purl.org/net/sword/terms/add";
public static String REL_ORIGINAL_DEPOSIT = "http://purl.org/net/sword/terms/originalDeposit";
public static String REL_DERIVED_RESOURCE = "http://purl.org/net/sword/terms/derivedResource";
// Package Formats
public static String PACKAGE_SIMPLE_ZIP = "http://purl.org/net/sword/package/SimpleZip";
public static String PACKAGE_BINARY = "http://purl.org/net/sword/package/Binary";
// Error Codes
public static String ERROR_BAD_REQUEST = "http://purl.org/net/sword/error/ErrorBadRequest";
public static String ERROR_CONTENT = "http://purl.org/net/sword/error/ErrorContent";
public static String ERROR_CHECKSUM_MISMATCH = "http://purl.org/net/sword/error/ErrorChecksumMismatch";
public static String ERROR_TARGET_OWNER_UNKNOWN = "http://purl.org/net/sword/error/TargetOwnerUnknown";
public static String ERROR_MEDIATION_NOT_ALLOWED = "http://purl.org/net/sword/error/MediationNotAllowed";
public static String ERROR_METHOD_NOT_ALLOWED = "http://purl.org/net/sword/error/MethodNotAllowed";
public static String ERROR_MAX_UPLOAD_SIZE_EXCEEDED = "http://purl.org/net/sword/error/MaxUploadSizeExceeded";
}

View File

@@ -1,57 +0,0 @@
/**
* 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/
*/
package org.swordapp.server.servlets;
import org.apache.log4j.Logger;
import org.swordapp.server.CollectionAPI;
import org.swordapp.server.CollectionDepositManager;
import org.swordapp.server.CollectionListManager;
import org.swordapp.server.SwordConfiguration;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class CollectionServletDefault extends SwordServlet
{
private static Logger log = Logger.getLogger(CollectionServletDefault.class);
protected CollectionListManager clm = null;
protected CollectionDepositManager cdm;
protected CollectionAPI api;
public void init() throws ServletException
{
super.init();
// load the collection list manager implementation
Object possibleClm = this.loadImplClass("collection-list-impl", true); // allow null
this.clm = possibleClm == null ? null : (CollectionListManager) possibleClm;
// load the deposit manager implementation
this.cdm = (CollectionDepositManager) this.loadImplClass("collection-deposit-impl", false);
// load the API
this.api = new CollectionAPI(this.clm, this.cdm, this.config);
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException
{
this.api.get(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException
{
this.api.post(req, resp);
}
}

View File

@@ -1,78 +0,0 @@
/**
* 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/
*/
package org.swordapp.server.servlets;
import org.apache.log4j.Logger;
import org.swordapp.server.ContainerAPI;
import org.swordapp.server.ContainerManager;
import org.swordapp.server.StatementAPI;
import org.swordapp.server.StatementManager;
import org.swordapp.server.SwordConfiguration;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class ContainerServletDefault extends SwordServlet
{
private static Logger log = Logger.getLogger(ContainerServletDefault.class);
private ContainerManager cm;
private ContainerAPI api;
private StatementManager sm;
public void init() throws ServletException
{
super.init();
// load the container manager implementation
this.cm = (ContainerManager) this.loadImplClass("container-impl", false);
// load the container manager implementation
this.sm = (StatementManager) this.loadImplClass("statement-impl", false);
// initialise the underlying servlet processor
this.api = new ContainerAPI(this.cm, this.sm, this.config);
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException
{
this.api.get(req, resp);
}
@Override
protected void doHead(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException
{
this.api.head(req, resp);
}
@Override
protected void doPut(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException
{
this.api.put(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException
{
this.api.post(req, resp);
}
@Override
protected void doDelete(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException
{
this.api.delete(req, resp);
}
}

View File

@@ -1,72 +0,0 @@
/**
* 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/
*/
package org.swordapp.server.servlets;
import org.apache.log4j.Logger;
import org.swordapp.server.MediaResourceAPI;
import org.swordapp.server.MediaResourceManager;
import org.swordapp.server.SwordConfiguration;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class MediaResourceServletDefault extends SwordServlet
{
private static Logger log = Logger.getLogger(MediaResourceServletDefault.class);
protected MediaResourceManager mrm;
protected MediaResourceAPI api;
public void init() throws ServletException
{
super.init();
// load the Media Resource Manager
this.mrm = (MediaResourceManager) this.loadImplClass("media-resource-impl", false);
// load the api
this.api = new MediaResourceAPI(this.mrm, this.config);
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException
{
this.api.get(req, resp);
}
@Override
protected void doHead(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException
{
this.api.head(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException
{
this.api.post(req, resp);
}
@Override
protected void doPut(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException
{
this.api.put(req, resp);
}
@Override
protected void doDelete(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException
{
this.api.delete(req, resp);
}
}

View File

@@ -1,41 +0,0 @@
/**
* 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/
*/
package org.swordapp.server.servlets;
import org.swordapp.server.ServiceDocumentAPI;
import org.swordapp.server.ServiceDocumentManager;
import org.swordapp.server.SwordConfiguration;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class ServiceDocumentServletDefault extends SwordServlet
{
protected ServiceDocumentManager sdm;
protected ServiceDocumentAPI api;
public void init() throws ServletException
{
super.init();
// load the service document implementation
this.sdm = (ServiceDocumentManager) this.loadImplClass("service-document-impl", false);
// load the api
this.api = new ServiceDocumentAPI(this.sdm, this.config);
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException
{
this.api.get(req, resp);
}
}

View File

@@ -1,42 +0,0 @@
/**
* 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/
*/
package org.swordapp.server.servlets;
import org.swordapp.server.*;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import org.apache.log4j.Logger;
public class StatementServletDefault extends SwordServlet
{
private static Logger log = Logger.getLogger(StatementServletDefault.class);
private StatementManager sm;
private StatementAPI statementApi;
public void init() throws ServletException
{
super.init();
// load the container manager implementation
this.sm = (StatementManager) this.loadImplClass("statement-impl", false);
// initialise the underlying servlet processor
this.statementApi = new StatementAPI(this.sm, this.config);
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException
{
this.statementApi.get(req, resp);
}
}

View File

@@ -1,78 +0,0 @@
/**
* 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/
*/
package org.swordapp.server.servlets;
import org.apache.abdera.Abdera;
import org.apache.abdera.model.Document;
import org.apache.abdera.model.Entry;
import org.apache.abdera.parser.Parser;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.fileupload.FileItemFactory;
import org.apache.commons.fileupload.FileUploadException;
import org.apache.commons.fileupload.disk.DiskFileItem;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import org.apache.log4j.Logger;
import org.swordapp.server.*;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class SwordServlet extends HttpServlet
{
private static Logger log = Logger.getLogger(SwordServlet.class);
protected SwordConfiguration config;
public void init() throws ServletException
{
// load the configuration implementation
this.config = (SwordConfiguration) this.loadImplClass("config-impl", false);
}
protected Object loadImplClass(String paramName, boolean allowNull)
throws ServletException
{
String className = getServletContext().getInitParameter(paramName);
if (className == null)
{
if (allowNull)
{
return null;
}
else
{
log.fatal("'" + paramName + "' init parameter not set in Servlet context");
throw new ServletException("'" + paramName + "' init parameter not set in Servlet context");
}
}
else
{
try
{
Object obj = Class.forName(className).newInstance();
log.info("Using " + className + " as '" + paramName + "'");
return obj;
}
catch (Exception e)
{
log.fatal("Unable to instantiate class from '" + paramName + "': " + className);
throw new ServletException("Unable to instantiate class from '" + paramName + "': " + className, e);
}
}
}
}

View File

@@ -1,140 +0,0 @@
<?xml version="1.0" encoding="ISO-8859-1" ?>
<!--
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/
-->
<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd">
<web-app>
<display-name>SWORD 2.0 Server - Example web.xml</display-name>
<!-- Configuration Information -->
<context-param>
<param-name>service-document-impl</param-name>
<param-value>org.swordapp.server.ServiceDocumentManagerImpl</param-value>
<description>
The ServiceDocumentManager server implementation class name
</description>
</context-param>
<!-- this can be omitted if the server does not wish to support listing collection contents -->
<context-param>
<param-name>collection-list-impl</param-name>
<param-value>org.swordapp.server.CollectionListManagerImpl</param-value>
<description>
The CollectionListManager server implementation class name
</description>
</context-param>
<context-param>
<param-name>collection-deposit-impl</param-name>
<param-value>org.swordapp.server.CollectionDepositManagerImpl</param-value>
<description>
The CollectionDepositManager server implementation class name
</description>
</context-param>
<context-param>
<param-name>media-resource-impl</param-name>
<param-value>org.swordapp.server.MediaResourceManagerImpl</param-value>
<description>
The MediaResourceManager server implementation class name
</description>
</context-param>
<context-param>
<param-name>container-impl</param-name>
<param-value>org.swordapp.server.ContainerManagerImpl</param-value>
<description>
The ContainerManager server implementation class name
</description>
</context-param>
<context-param>
<param-name>statement-impl</param-name>
<param-value>org.swordapp.server.StatementManagerImpl</param-value>
<description>
The StatementManager server implementation class name
</description>
</context-param>
<!-- This option here is an actual implementation of the configuration class, which
contains some default values -->
<context-param>
<param-name>config-impl</param-name>
<param-value>org.swordapp.server.SwordConfigurationDefault</param-value>
<description>
The SwordConfiguration server implementation class name
</description>
</context-param>
<context-param>
<param-name>authentication-method</param-name>
<param-value>Basic</param-value>
<description>
The type of authentication used : [Basic|None]
</description>
</context-param>
<!-- Servlets -->
<servlet>
<servlet-name>servicedocument</servlet-name>
<servlet-class>org.swordapp.server.servlets.ServiceDocumentServletDefault</servlet-class>
</servlet>
<servlet>
<servlet-name>collection</servlet-name>
<servlet-class>org.swordapp.server.servlets.CollectionServletDefault</servlet-class>
</servlet>
<servlet>
<servlet-name>mediaresource</servlet-name>
<servlet-class>org.swordapp.server.servlets.MediaResourceServletDefault</servlet-class>
</servlet>
<servlet>
<servlet-name>container</servlet-name>
<servlet-class>org.swordapp.server.servlets.ContainerServletDefault</servlet-class>
</servlet>
<servlet>
<servlet-name>statement</servlet-name>
<servlet-class>org.swordapp.server.servlets.StatementServletDefault</servlet-class>
</servlet>
<!-- Servlet Mappings -->
<servlet-mapping>
<servlet-name>servicedocument</servlet-name>
<url-pattern>/servicedocument/*</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>collection</servlet-name>
<url-pattern>/collection/*</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>mediaresource</servlet-name>
<url-pattern>/edit-media/*</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>container</servlet-name>
<url-pattern>/edit/*</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>statement</servlet-name>
<url-pattern>/statement/*</url-pattern>
</servlet-mapping>
</web-app>

View File

@@ -1,164 +0,0 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.dspace</groupId>
<artifactId>dspace-swordv2-webapp</artifactId>
<packaging>war</packaging>
<name>DSpace SWORD v2 :: Web Application Resources</name>
<description>DSpace SWORD v2 Deposit Service Provider Web Application Resources</description>
<url>http://projects.dspace.org/dspace-sword-webapp</url>
<!--
A Parent POM that Maven inherits DSpace Default
POM atrributes from.
-->
<parent>
<groupId>org.dspace</groupId>
<artifactId>dspace-swordv2</artifactId>
<version>1.8.0-SNAPSHOT</version>
<relativePath>..</relativePath>
</parent>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<configuration>
<archiveClasses>true</archiveClasses>
<attachClasses>true</attachClasses>
<!-- In version 2.1-alpha-1, this was incorrectly named warSourceExcludes -->
<packagingExcludes>WEB-INF/lib/*.jar</packagingExcludes>
<warSourceExcludes>WEB-INF/lib/*.jar</warSourceExcludes>
<webResources>
<resource>
<filtering>true</filtering>
<directory>${basedir}/src/main/webapp</directory>
<includes>
<include>WEB-INF/web.xml</include>
</includes>
</resource>
</webResources>
</configuration>
<executions>
<execution>
<phase>prepare-package</phase>
</execution>
</executions>
</plugin>
</plugins>
</build>
<profiles>
<!--
when activated a dspace.config configuration
file location passed on the commandline
(-Ddspace.config=...) can be passed through
to be used as a filter source by projects for
tasks such as updating the ${dspace.dir} in
web.xml etc.
-->
<profile>
<activation>
<property>
<name>dspace.config</name>
</property>
</activation>
<build>
<filters>
<filter>${dspace.config}</filter>
</filters>
</build>
</profile>
<profile>
<id>oracle-support</id>
<activation>
<property>
<name>db.name</name>
<value>oracle</value>
</property>
</activation>
<dependencies>
<dependency>
<groupId>com.oracle</groupId>
<artifactId>ojdbc6</artifactId>
</dependency>
</dependencies>
</profile>
<profile>
<id>postgres-support</id>
<activation>
<property>
<name>!db.name</name>
</property>
</activation>
<dependencies>
<dependency>
<groupId>postgresql</groupId>
<artifactId>postgresql</artifactId>
</dependency>
</dependencies>
</profile>
</profiles>
<dependencies>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.3</version>
</dependency>
<dependency>
<groupId>org.dspace</groupId>
<artifactId>dspace-swordv2-api</artifactId>
<!--<version>1.8.0-SNAPSHOT</version>-->
</dependency>
<dependency>
<groupId>org.dspace</groupId>
<artifactId>dspace-api</artifactId>
</dependency>
<dependency>
<groupId>com.googlecode.foresite-toolkit</groupId>
<artifactId>foresite</artifactId>
<version>0.9</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.15</version>
<exclusions>
<exclusion>
<artifactId>jmxtools</artifactId>
<groupId>com.sun.jdmk</groupId>
</exclusion>
<exclusion>
<artifactId>jms</artifactId>
<groupId>javax.jms</groupId>
</exclusion>
<exclusion>
<artifactId>jmxri</artifactId>
<groupId>com.sun.jmx</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.apache.abdera</groupId>
<artifactId>abdera-client</artifactId>
<version>1.1.1</version>
</dependency>
</dependencies>
<!--
The Subversion repository location is used by Continuum to update against
when changes have occured, this spawns a new build cycle and releases snapshots
into the snapshot repository below.
-->
<scm>
<connection>scm:svn:http://scm.dspace.org/svn/repo/dspace/trunk/dspace-swordv2/dspace-swordv2-webapp</connection>
<developerConnection>scm:svn:https://scm.dspace.org/svn/repo/dspace/trunk/dspace-swordv2/dspace-swordv2-webapp</developerConnection>
<url>http://scm.dspace.org/svn/repo/dspace/trunk/dspace-swordv2/dspace-swordv2-webapp</url>
</scm>
</project>

View File

@@ -1,39 +1,170 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.dspace</groupId>
<artifactId>dspace-swordv2</artifactId>
<packaging>pom</packaging>
<name>DSpace SWORD v2</name>
<description>DSpace SWORD v2 Deposit Service Provider Web Application</description>
<url>http://swordapp.org/</url>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.dspace</groupId>
<artifactId>dspace-swordv2</artifactId>
<packaging>war</packaging>
<name>DSpace SWORD v2 :: Web Application Resources</name>
<description>DSpace SWORD v2 Deposit Service Provider Web Application</description>
<url>http://projects.dspace.org/dspace-sword-webapp</url>
<!--
A Parent POM that Maven inherits DSpace Default
POM atrributes from.
-->
<parent>
<groupId>org.dspace</groupId>
<artifactId>dspace-parent</artifactId>
<version>1.8.0-SNAPSHOT</version>
<relativePath>..</relativePath>
</parent>
<!--
A Parent POM that Maven inherits DSpace Default
POM atrributes from.
-->
<parent>
<groupId>org.dspace</groupId>
<artifactId>dspace-parent</artifactId>
<version>1.8.0-SNAPSHOT</version>
<relativePath>..</relativePath>
</parent>
<!--
The Subversion repository location is used by Continuum to update against
when changes have occurred, this spawns a new build cycle and releases snapshots
into the snapshot repository below.
-->
<scm>
<connection>scm:svn:http://scm.dspace.org/svn/repo/dspace/trunk/dspace-swordv2</connection>
<developerConnection>scm:svn:https://scm.dspace.org/svn/repo/dspace/trunk/dspace-swordv2</developerConnection>
<url>http://scm.dspace.org/svn/repo/dspace/trunk/dspace-swordv2</url>
</scm>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<configuration>
<archiveClasses>true</archiveClasses>
<attachClasses>true</attachClasses>
<!-- In version 2.1-alpha-1, this was incorrectly named warSourceExcludes -->
<packagingExcludes>WEB-INF/lib/*.jar</packagingExcludes>
<warSourceExcludes>WEB-INF/lib/*.jar</warSourceExcludes>
<webResources>
<resource>
<filtering>true</filtering>
<directory>${basedir}/src/main/webapp</directory>
<includes>
<include>WEB-INF/web.xml</include>
</includes>
</resource>
</webResources>
</configuration>
<executions>
<execution>
<phase>prepare-package</phase>
</execution>
</executions>
</plugin>
</plugins>
</build>
<!--
The Subversion repository location is used by Continuum to update against
when changes have occurred, this spawns a new build cycle and releases snapshots
into the snapshot repository below.
-->
<scm>
<connection>scm:svn:http://scm.dspace.org/svn/repo/dspace/trunk/dspace-swordv2</connection>
<developerConnection>scm:svn:https://scm.dspace.org/svn/repo/dspace/trunk/dspace-swordv2</developerConnection>
<url>http://scm.dspace.org/svn/repo/dspace/trunk/dspace-swordv2</url>
</scm>
<profiles>
<!--
when activated a dspace.config configuration
file location passed on the commandline
(-Ddspace.config=...) can be passed through
to be used as a filter source by projects for
tasks such as updating the ${dspace.dir} in
web.xml etc.
-->
<profile>
<activation>
<property>
<name>dspace.config</name>
</property>
</activation>
<build>
<filters>
<filter>${dspace.config}</filter>
</filters>
</build>
</profile>
<profile>
<id>oracle-support</id>
<activation>
<property>
<name>db.name</name>
<value>oracle</value>
</property>
</activation>
<dependencies>
<dependency>
<groupId>com.oracle</groupId>
<artifactId>ojdbc6</artifactId>
</dependency>
</dependencies>
</profile>
<profile>
<id>postgres-support</id>
<activation>
<property>
<name>!db.name</name>
</property>
</activation>
<dependencies>
<dependency>
<groupId>postgresql</groupId>
<artifactId>postgresql</artifactId>
</dependency>
</dependencies>
</profile>
</profiles>
<modules>
<module>dspace-swordv2-api</module>
<module>dspace-swordv2-webapp</module>
</modules>
<dependencies>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.3</version>
</dependency>
<dependency>
<groupId>org.swordapp</groupId>
<artifactId>server</artifactId>
<version>2.0</version>
<type>jar</type>
<classifier>classes</classifier>
</dependency>
<dependency>
<groupId>org.swordapp</groupId>
<artifactId>server</artifactId>
<version>2.0</version>
<type>war</type>
</dependency>
<dependency>
<groupId>org.dspace</groupId>
<artifactId>dspace-api</artifactId>
</dependency>
<dependency>
<groupId>com.googlecode.foresite-toolkit</groupId>
<artifactId>foresite</artifactId>
<version>0.9</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.15</version>
<exclusions>
<exclusion>
<artifactId>jmxtools</artifactId>
<groupId>com.sun.jdmk</groupId>
</exclusion>
<exclusion>
<artifactId>jms</artifactId>
<groupId>javax.jms</groupId>
</exclusion>
<exclusion>
<artifactId>jmxri</artifactId>
<groupId>com.sun.jmx</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.apache.abdera</groupId>
<artifactId>abdera-client</artifactId>
<version>1.1.1</version>
</dependency>
</dependencies>
</project>

View File

@@ -117,23 +117,17 @@
<!-- DSpace Implementation of SWORDv2 Provider -->
<dependency>
<groupId>org.dspace</groupId>
<artifactId>dspace-swordv2-webapp</artifactId>
<artifactId>dspace-swordv2</artifactId>
<type>jar</type>
<classifier>classes</classifier>
</dependency>
<dependency>
<groupId>org.dspace</groupId>
<artifactId>dspace-swordv2-webapp</artifactId>
<artifactId>dspace-swordv2</artifactId>
<type>war</type>
</dependency>
<!-- SWORD v2 API -->
<dependency>
<groupId>org.dspace</groupId>
<artifactId>dspace-swordv2-api</artifactId>
</dependency>
<dependency>
<groupId>org.dspace</groupId>
<artifactId>dspace-discovery-provider</artifactId>