diff --git a/dspace-sword/LICENCE.txt b/dspace-sword/LICENCE.txt new file mode 100644 index 0000000000..48281026a4 --- /dev/null +++ b/dspace-sword/LICENCE.txt @@ -0,0 +1,34 @@ +Copyright (c) 2007, Aberystwyth University + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + - Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + - Neither the name of the Centre for Advanced Software and + Intelligent Systems (CASIS) nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR +TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +SUCH DAMAGE. \ No newline at end of file diff --git a/dspace-sword/README.txt b/dspace-sword/README.txt new file mode 100644 index 0000000000..d8dc199524 --- /dev/null +++ b/dspace-sword/README.txt @@ -0,0 +1,64 @@ +DSpace SWORD README +=================== + +Author: Richard Jones +Last Updated: 07-02-2007 + +This document describes the DSpace implementation of the SWORD deposit +standard. This is an extension to the ATOM Publishing Protocol (APP), +which provides a framework to discover deposit targets, and to deposit packaged +content into remote repositories. + +For more information see: + +http://www.ukoln.ac.uk/repositories/digirep/index/SWORD + +Testing +------- + +Supplied along with the source code is a package which can be used for testing. +This consists of a mets.xml file, which is a METS document containing a Dublin +Core XML section of descriptive metadata which conforms to the SWAP standard. +There are additionally 3 example PDF files. + +These files are provided additionally inside a zip file which should form the +content of a deposit request (example.zip). + +These files are all available in the directory: [dspace-sword]/example + +Testing can be performed using the separately available SWORD Client + +Implementation Notes +-------------------- + +- DSpace has no equivalent concept for the SWORD term "treatment". This is + therefore always null in the Service Document + +- The logic of onBehalfOf is as follows: The list of collections which is + supplied during a request which is done onBehalfOf another user is the + intersection of the lists of collections that the authenticated user can + submit to and the list that the onBehalfOf user can submit to. + +- This implementation only supports the default PasswordAuthentication + mechanism for DSpace. Modifications are required to tie it in to alternative + authentication mechanisms. + +- When items are deposited and pass into the DSpace workflow system, they + cannot be assigned external identifiers immediately. Therefore the returned + id on "Accepted" items will be the front page of the repository on which the + deposit happened. Alternatives to this mechanism are being sought, but may + require core DSpace modifications. + +- If a request is made with an onBehalfOf user supplied, the authentication + process requires that the username/password pair successfully authenticate a + user, and that the onBehalfOf user simply exists in the user database. If + any of these conditions fail, authentication fails. + +- The DSpace package ingester does not permit for a copy of the orignal package + to be retained. To modify this requires changes to the core DSpace which + could be considered. In the mean time, it is not possible to return a link + to the original package in the element. + +- The ingest stylesheet used by default is available in the file: + [dspace-source]/config/crosswalks/sword-swap-ingest.xsl but does not cover + the complete SWAP profile yet. \ No newline at end of file diff --git a/dspace-sword/dspace-sword-api/pom.xml b/dspace-sword/dspace-sword-api/pom.xml new file mode 100644 index 0000000000..36a8412bab --- /dev/null +++ b/dspace-sword/dspace-sword-api/pom.xml @@ -0,0 +1,109 @@ + + + 4.0.0 + org.dspace + dspace-sword-api + jar + DSpace SWORD :: API and Implementation + DSpace SWORD Deposit Service Provider Web Application + http://www.ukoln.ac.uk/repositories/digirep/index/SWORD + + + + org.dspace + dspace-oai + 1.5-SNAPSHOT + + + + + maven.dspace.org/snapshot + DSpace Maven Snapshot Repository + http://maven.dspace.org/snapshot + + false + fail + + + true + fail + + + + + + + maven.dspace.org/snapshot + DSpace Maven Repository + http://maven.dspace.org/snapshot + + never + fail + + + always + fail + + + + + + + + scm:svn:http://dspace.svn.sourceforge.net/svnroot/dspace/branches/dspace-1_5_x/dspace-sword/dspace-sword-api + + + scm:svn:https://dspace.svn.sourceforge.net/svnroot/dspace/branches/dspace-1_5_x/dspace-sword/dspace-sword-api + + + http://dspace.svn.sourceforge.net/viewvc/dspace/branches/dspace-1_5_x/dspace-sword/dspace-sword-api + + + + + + + + + + org.dspace + dspace-api + 1.5-SNAPSHOT + + + + xom + xom + 1.1 + + + + jaxen + jaxen + 1.1 + + + + javax.servlet + servlet-api + 2.3 + provided + + + + + diff --git a/dspace-sword/dspace-sword-api/src/main/java/org/dspace/sword/CollectionLocation.java b/dspace-sword/dspace-sword-api/src/main/java/org/dspace/sword/CollectionLocation.java new file mode 100644 index 0000000000..7176b6e5df --- /dev/null +++ b/dspace-sword/dspace-sword-api/src/main/java/org/dspace/sword/CollectionLocation.java @@ -0,0 +1,155 @@ +/* CollectionLocation.java + * + * Copyright (c) 2007, Aberystwyth University + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the + * following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * - Neither the name of the Centre for Advanced Software and + * Intelligent Systems (CASIS) nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +package org.dspace.sword; + +import java.sql.SQLException; + +import org.apache.log4j.Logger; + +import org.dspace.content.Collection; +import org.dspace.content.DSpaceObject; +import org.dspace.core.ConfigurationManager; +import org.dspace.core.Context; +import org.dspace.handle.HandleManager; + +/** + * This class provides a single point of contact for + * resolving Collections from SWORD Deposit URLs and for + * generating SWORD Deposit URLs from Collections + * + * @author Richard Jones + * + */ +public class CollectionLocation +{ + /** Log4j logger */ + public static Logger log = Logger.getLogger(CollectionLocation.class); + + /** + * Obtain the deposit URL for the given collection. These URLs + * should not be considered persistent, but will remain consistent + * unless configuration changes are made to DSpace + * + * @param collection + * @return The Deposit URL + * @throws DSpaceSWORDException + */ + public String getLocation(Collection collection) + throws DSpaceSWORDException + { + return this.getBaseUrl() + "/" + collection.getHandle(); + } + + /** + * Obtain the collection which is represented by the given + * URL + * + * @param context the DSpace context + * @param location the URL to resolve to a collection + * @return The collection to which the url resolves + * @throws DSpaceSWORDException + */ + public Collection getCollection(Context context, String location) + throws DSpaceSWORDException + { + try + { + String baseUrl = this.getBaseUrl(); + if (baseUrl.length() == location.length()) + { + throw new DSpaceSWORDException("The deposit URL is incomplete"); + } + String handle = location.substring(baseUrl.length()); + if (handle.startsWith("/")) + { + handle = handle.substring(1); + } + if ("".equals(handle)) + { + throw new DSpaceSWORDException("The deposit URL is incomplete"); + } + + DSpaceObject dso = HandleManager.resolveToObject(context, handle); + + if (!(dso instanceof Collection)) + { + throw new DSpaceSWORDException("The deposit URL does not resolve to a valid collection"); + } + + return (Collection) dso; + } + catch (SQLException e) + { + log.error("Caught exception:", e); + throw new DSpaceSWORDException("There was a problem resolving the collection", e); + } + } + + /** + * Get the base deposit URL for the DSpace SWORD implementation. This + * is effectively the URL of the servlet which deals with deposit + * requests, and is used as the basis for the individual Collection + * URLs + * + * If the configuration sword.deposit.url is set, this will be returned, + * but if not, it will construct the url as follows: + * + * [dspace.url]/dspace-sword/deposit + * + * where dspace.url is also in the configuration file. + * + * @return the base URL for sword deposit + * @throws DSpaceSWORDException + */ + private String getBaseUrl() + throws DSpaceSWORDException + { + String depositUrl = ConfigurationManager.getProperty("sword.deposit.url"); + if (depositUrl == null || "".equals(depositUrl)) + { + String dspaceUrl = ConfigurationManager.getProperty("dspace.url"); + if (dspaceUrl == null || "".equals(dspaceUrl)) + { + throw new DSpaceSWORDException("Unable to construct deposit urls, due to missing/invalid config in sword.deposit.url and/or dspace.url"); + } + depositUrl = dspaceUrl + "/dspace-sword/deposit"; + } + return depositUrl; + } +} diff --git a/dspace-sword/dspace-sword-api/src/main/java/org/dspace/sword/DSpaceATOMEntry.java b/dspace-sword/dspace-sword-api/src/main/java/org/dspace/sword/DSpaceATOMEntry.java new file mode 100644 index 0000000000..a37e70018f --- /dev/null +++ b/dspace-sword/dspace-sword-api/src/main/java/org/dspace/sword/DSpaceATOMEntry.java @@ -0,0 +1,463 @@ +/* DSpaceATOMEntry.java + * + * Copyright (c) 2007, Aberystwyth University + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the + * following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * - Neither the name of the Centre for Advanced Software and + * Intelligent Systems (CASIS) nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +package org.dspace.sword; + +import java.sql.SQLException; +import java.text.ParseException; + +import org.dspace.content.Bitstream; +import org.dspace.content.Bundle; +import org.dspace.content.DCDate; +import org.dspace.content.DCValue; +import org.dspace.content.Item; +import org.dspace.core.ConfigurationManager; +import org.dspace.handle.HandleManager; + +import org.purl.sword.base.SWORDEntry; + +import org.w3.atom.Author; +import org.w3.atom.Content; +import org.w3.atom.ContentType; +import org.w3.atom.Contributor; +import org.w3.atom.Generator; +import org.w3.atom.InvalidMediaTypeException; +import org.w3.atom.Link; +import org.w3.atom.Rights; +import org.w3.atom.Source; +import org.w3.atom.Summary; +import org.w3.atom.Title; + +/** + * Class to represent a DSpace Item as an ATOM Entry. This + * handles the objects in a default way, but the intention is + * for you to be able to extend the class with your own + * representation if necessary. + * + * @author Richard Jones + * + */ +public class DSpaceATOMEntry +{ + /** the SWORD ATOM entry which this class effectively decorates */ + protected SWORDEntry entry; + + /** the item this ATOM entry represents */ + protected Item item; + + /** + * Construct the SWORDEntry object which represents the given + * item with the given handle. An argument as to whether this + * is a NoOp request is required because in that event the + * assigned identifier for the item will not be added to the + * SWORDEntry as it will be invalid. + * + * @param item the item to represent + * @param handle the handle for the item + * @param noOp whether this is a noOp request + * @return the SWORDEntry for the item + */ + public SWORDEntry getSWORDEntry(Item item, String handle, boolean noOp) + throws DSpaceSWORDException + { + entry = new SWORDEntry(); + this.item = item; + + // add the authors to the sword entry + this.addAuthors(); + + // add the category information to the sword entry + this.addCategories(); + + // add a content element to the sword entry + this.addContentElement(handle, noOp); + + // add contributors (authors plus any other bits) to the sword entry + this.addContributors(); + + // add the identifier for the item, if the id is going + // to be valid by the end of the request + if (!noOp) + { + this.addIdentifier(handle, noOp); + } + + // add any appropriate links + this.addLinks(handle); + + // add the publish date + this.addPublishDate(); + + // add the rights information + this.addRights(handle); + + // add the source infomation + this.addSource(); + + // add the summary of the item + this.addSummary(); + + // add the title of the item + this.addTitle(); + + // add the date on which the entry was last updated + this.addLastUpdatedDate(); + + // set the format namespace for the response + this.setFormatNamespace(); + + return entry; + } + + /** + * add the author names from the bibliographic metadata. Does + * not supply email addresses or URIs, both for privacy, and + * because the data is not so readily available in DSpace. + * + */ + protected void addAuthors() + { + DCValue[] dcv = item.getMetadata("dc.contributor.author"); + if (dcv != null) + { + for (int i = 0; i < dcv.length; i++) + { + Author author = new Author(); + author.setName(dcv[i].value); + entry.addAuthors(author); + } + } + } + + /** + * Add all the subject classifications from the bibliographic + * metadata. + * + */ + protected void addCategories() + { + DCValue[] dcv = item.getMetadata("dc.subject.*"); + if (dcv != null) + { + for (int i = 0; i < dcv.length; i++) + { + entry.addCategory(dcv[i].value); + } + } + } + + /** + * Set the content type that DSpace received. This is just + * "application/zip" in this default implementation. + * + */ + protected void addContentElement(String handle, boolean noOp) + { + try + { + if (!noOp) + { + if (item.getHandle() != null) + { + handle = item.getHandle(); + } + + if (handle != null && !"".equals(handle)) + { + Content content = new Content(); + // content.setType("application/zip"); + content.setType("text/html"); + content.setSource(HandleManager.getCanonicalForm(handle)); + entry.setContent(content); + } + } + } + catch (InvalidMediaTypeException e) + { + // do nothing; we'll live without the content type declaration! + } + } + + /** + * Add the list of contributors to the item. This will include + * the authors, and any other contributors that are supplied + * in the bibliographic metadata + * + */ + protected void addContributors() + { + DCValue[] dcv = item.getMetadata("dc.contributor.*"); + if (dcv != null) + { + for (int i = 0; i < dcv.length; i++) + { + Contributor cont = new Contributor(); + cont.setName(dcv[i].value); + entry.addContributor(cont); + } + } + } + + /** + * Add the identifier for the item. If the item object has + * a handle already assigned, this is used, otherwise, the + * passed handle is used. It is set in the form that + * they can be used to access the resource over http (i.e. + * a real URL). + * + * @param handle + */ + protected void addIdentifier(String handle, boolean noOp) + { + // it's possible that the item hasn't been assigned a handle yet + if (!noOp) + { + if (item.getHandle() != null) + { + handle = item.getHandle(); + } + + if (handle != null && !"".equals(handle)) + { + entry.setId(HandleManager.getCanonicalForm(handle)); + return; + } + } + + // if we get this far, then we just use the dspace url as the + // property + String cfg = ConfigurationManager.getProperty("dspace.url"); + entry.setId(cfg); + + // FIXME: later on we will maybe have a workflow page supplied + // by the sword interface? + } + + /** + * Add links associated with this item. The default implementation + * does not support item linking, so this method currently does + * nothing + * + */ + protected void addLinks(String handle) + throws DSpaceSWORDException + { + try + { + // if there is no handle, we can't generate links + if (handle == null) + { + return; + } + + String base = ConfigurationManager.getProperty("dspace.url"); + + // in the default set up we just pass urls to all of the + // inidivual files in the item + Bundle[] bundles = item.getBundles("ORIGINAL"); + for (int i = 0; i < bundles.length ; i++) + { + Bitstream[] bss = bundles[i].getBitstreams(); + for (int j = 0; j < bss.length; j++) + { + Link link = new Link(); + String url = base + "/bitstream/" + handle + "/" + bss[j].getSequenceID() + "/" + bss[j].getName(); + link.setHref(url); + entry.addLink(link); + } + } + } + catch (SQLException e) + { + throw new DSpaceSWORDException(e); + } + } + + /** + * Add the date of publication from the bibliographic metadata + * + */ + protected void addPublishDate() + { + try + { + DCValue[] dcv = item.getMetadata("dc.date.issued"); + if (dcv != null) + { + if (dcv.length == 1) + { + entry.setPublished(dcv[0].value); + } + } + } + catch (ParseException e) + { + // do nothing; we'll live without the publication date + } + } + + /** + * Add rights information. This attaches an href to the URL + * of the item's licence file + * + */ + protected void addRights(String handle) + { + try + { + // if there's no handle, we can't give a link + if (handle == null) + { + return; + } + + String base = ConfigurationManager.getProperty("dspace.url"); + + // if there's no base URL, we are stuck + if (base == null) + { + return; + } + + StringBuilder rightsString = new StringBuilder(); + Bundle[] bundles = item.getBundles("LICENSE"); + for (int i = 0; i < bundles.length; i++) + { + Bitstream[] bss = bundles[i].getBitstreams(); + for (int j = 0; j < bss.length; j++) + { + String url = base + "/bitstream/" + handle + "/" + bss[j].getSequenceID() + "/" + bss[j].getName(); + rightsString.append(url + " "); + } + } + + Rights rights = new Rights(); + rights.setContent(rightsString.toString()); + rights.setType(ContentType.TEXT); + entry.setRights(rights); + } + catch (SQLException e) + { + // do nothing + } + } + + /** + * Add the source of the bibliographic metadata. + * + */ + protected void addSource() + { + String base = ConfigurationManager.getProperty("dspace.url"); + String name = ConfigurationManager.getProperty("dspace.name"); + Source source = new Source(); + Generator gen = new Generator(); + gen.setUri(base); + gen.setContent(name); + source.setGenerator(gen); + entry.setSource(source); + } + + /** + * Add the summary/abstract from the bibliographic metadata + * + */ + protected void addSummary() + { + DCValue[] dcv = item.getMetadata("dc.description.abstract"); + if (dcv != null) + { + for (int i = 0; i < dcv.length; i++) + { + Summary summary = new Summary(); + summary.setContent(dcv[i].value); + summary.setType(ContentType.TEXT); + entry.setSummary(summary); + } + } + } + + /** + * Add the title from the bibliographic metadata + * + */ + protected void addTitle() + { + DCValue[] dcv = item.getMetadata("dc.title"); + if (dcv != null) + { + for (int i = 0; i < dcv.length; i++) + { + Title title = new Title(); + title.setContent(dcv[i].value); + title.setType(ContentType.TEXT); + entry.setTitle(title); + } + } + } + + /** + * Add the date that this item was last updated + * + */ + protected void addLastUpdatedDate() + { + try + { + String config = ConfigurationManager.getProperty("sword.updated.field"); + DCValue[] dcv = item.getMetadata(config); + if (dcv != null) + { + if (dcv.length == 1) + { + DCDate dcd = new DCDate(dcv[0].value); + entry.setUpdated(dcd.toString()); + } + } + } + catch (ParseException e) + { + // do nothing + } + } + + protected void setFormatNamespace() + { + entry.setFormatNamespace("http://www.log.gov/METS"); + } +} diff --git a/dspace-sword/dspace-sword-api/src/main/java/org/dspace/sword/DSpaceSWORDException.java b/dspace-sword/dspace-sword-api/src/main/java/org/dspace/sword/DSpaceSWORDException.java new file mode 100644 index 0000000000..2bd4269665 --- /dev/null +++ b/dspace-sword/dspace-sword-api/src/main/java/org/dspace/sword/DSpaceSWORDException.java @@ -0,0 +1,71 @@ +/* DSpaceSWORDException.java + * + * Copyright (c) 2007, Aberystwyth University + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the + * following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * - Neither the name of the Centre for Advanced Software and + * Intelligent Systems (CASIS) nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +package org.dspace.sword; + +/** + * This Exception class can be thrown by the internals of the + * DSpace SWORD implementation + * + * @author Richard Jones + * + */ +public class DSpaceSWORDException extends Exception +{ + + public DSpaceSWORDException() + { + super(); + } + + public DSpaceSWORDException(String arg0, Throwable arg1) + { + super(arg0, arg1); + } + + public DSpaceSWORDException(String arg0) + { + super(arg0); + } + + public DSpaceSWORDException(Throwable arg0) + { + super(arg0); + } + +} diff --git a/dspace-sword/dspace-sword-api/src/main/java/org/dspace/sword/DSpaceSWORDServer.java b/dspace-sword/dspace-sword-api/src/main/java/org/dspace/sword/DSpaceSWORDServer.java new file mode 100644 index 0000000000..91a9bafa7c --- /dev/null +++ b/dspace-sword/dspace-sword-api/src/main/java/org/dspace/sword/DSpaceSWORDServer.java @@ -0,0 +1,328 @@ +/* DSpaceSWORDServer.java + * + * Copyright (c) 2007, Aberystwyth University + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the + * following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * - Neither the name of the Centre for Advanced Software and + * Intelligent Systems (CASIS) nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +package org.dspace.sword; + +import org.apache.log4j.Logger; + +import org.dspace.core.Context; +import org.dspace.core.LogManager; +import org.dspace.eperson.EPerson; +import org.dspace.authorize.AuthorizeException; + +import org.purl.sword.server.SWORDServer; +import org.purl.sword.base.DepositResponse; +import org.purl.sword.base.SWORDAuthenticationException; +import org.purl.sword.base.SWORDException; +import org.purl.sword.base.ServiceDocument; +import org.purl.sword.base.ServiceDocumentRequest; +import org.purl.sword.base.Deposit; + +import java.sql.SQLException; + +/** + * An implementation of the SWORDServer interface to allow SWORD deposit + * operations on DSpace. See: + * + * http://www.ukoln.ac.uk/repositories/digirep/index/SWORD_APP_Profile_0.5 + * + * @author Richard Jones + */ +public class DSpaceSWORDServer implements SWORDServer +{ + /** Log4j logger */ + public static Logger log = Logger.getLogger(DSpaceSWORDServer.class); + + /** DSpace context */ + private Context context; + + // methods required by SWORDServer interface + //////////////////////////////////////////// + + /* (non-Javadoc) + * @see org.purl.sword.SWORDServer#doServiceDocument(org.purl.sword.base.ServiceDocumentRequest) + */ + public ServiceDocument doServiceDocument(ServiceDocumentRequest request) + throws SWORDAuthenticationException, SWORDException + { + if (log.isDebugEnabled()) + { + log.debug(LogManager.getHeader(context, "sword_do_service_document", "")); + } + + try + { + // first authenticate the request + // note: this will build our context for us + SWORDContext sc = this.authenticate(request); + + // log the request + log.info(LogManager.getHeader(context, "sword_service_document_request", "username=" + request.getUsername() + ",on_behalf_of=" + request.getOnBehalfOf())); + + // prep the service request, then get the service document out of it + SWORDService service = new SWORDService(); + service.setContext(context); + service.setSWORDContext(sc); + ServiceDocument doc = service.getServiceDocument(); + + return doc; + } + catch (DSpaceSWORDException e) + { + log.error("caught exception: ", e); + throw new SWORDException("The DSpace SWORD interface experienced an error", e); + } + finally + { + // this is a read operation only, so there's never any need to commit the context + if (context != null) + { + context.abort(); + } + } + } + + /* (non-Javadoc) + * @see org.purl.sword.SWORDServer#doSWORDDeposit(org.purl.sword.server.Deposit) + */ + public DepositResponse doDeposit(Deposit deposit) + throws SWORDAuthenticationException, SWORDException + { + try + { + if (log.isDebugEnabled()) + { + log.debug(LogManager.getHeader(context, "sword_do_deposit", "")); + } + + // first authenticate the request + // note: this will build our context for us + SWORDContext sc = this.authenticate(deposit); + + // log the request + log.info(LogManager.getHeader(context, "sword_deposit_request", "username=" + deposit.getUsername() + ",on_behalf_of=" + deposit.getOnBehalfOf())); + + // prep and execute the deposit + DepositManager dm = new DepositManager(); + dm.setContext(context); + dm.setDeposit(deposit); + dm.setSWORDContext(sc); + DepositResponse response = dm.deposit(); + + // if something hasn't killed it already (allowed), then complete the transaction + if (context != null && context.isValid()) + { + context.commit(); + } + + return response; + } + catch (DSpaceSWORDException e) + { + log.error("caught exception:", e); + throw new SWORDAuthenticationException("There was a problem depositing the item", e); + } + catch (SQLException e) + { + log.error("caught exception: ", e); + throw new SWORDException("There was a problem completing the transaction", e); + } + finally + { + // if, for some reason, we wind up here with a not null context + // then abort it (the above should commit it if everything works fine) + if (context != null && context.isValid()) + { + context.abort(); + } + } + } + + /** + * Construct the context object member variable of this class + * using the passed IP address as part of the loggable + * information + * + * @param ip the ip address of the incoming request + * @throws SWORDException + */ + private void constructContext(String ip) + throws SWORDException + { + try + { + this.context = new Context(); + } + catch (SQLException e) + { + log.error("caught exception: ", e); + throw new SWORDException("There was a problem with the database", e); + } + + // Set the session ID and IP address + this.context.setExtraLogInfo("session_id=0:ip_addr=" + ip); + } + + /** + * Authenticate the incoming service document request. Calls: + * + * authenticatate(username, password, onBehalfOf) + * + * @param request + * @return a SWORDContext object containing the relevant users + * @throws SWORDAuthenticationException + * @throws SWORDException + */ + private SWORDContext authenticate(ServiceDocumentRequest request) + throws SWORDAuthenticationException, SWORDException + { + this.constructContext(request.getIPAddress()); + return this.authenticate(request.getUsername(), request.getPassword(), request.getOnBehalfOf()); + } + + /** + * Authenticate the incoming deposit request. Calls: + * + * authenticate(username, password, onBehalfOf) + * + * @param deposit + * @return a SWORDContext object containing the relevant users + * @throws SWORDAuthenticationException + * @throws SWORDException + */ + private SWORDContext authenticate(Deposit deposit) + throws SWORDAuthenticationException, SWORDException + { + this.constructContext(deposit.getIPAddress()); + return this.authenticate(deposit.getUsername(), deposit.getPassword(), deposit.getOnBehalfOf()); + } + + /** + * Authenticate the given username/password pair, in conjunction with + * the onBehalfOf user. The rules are that the username/password pair + * must successfully authenticate the user, and the onBehalfOf user + * must exist in the user database. + * + * @param un + * @param pw + * @param obo + * @return a SWORD context holding the various user information + * @throws SWORDAuthenticationException + * @throws SWORDException + */ + private SWORDContext authenticate(String un, String pw, String obo) + throws SWORDAuthenticationException, SWORDException + { + // smooth out the OnBehalfOf request, so that empty strings are + // treated as null + if ("".equals(obo)) + { + obo = null; + } + + log.info(LogManager.getHeader(context, "sword_authenticate", "username=" + un + ",on_behalf_of=" + obo)); + try + { + // attempt to authenticate the primary user + SWORDContext sc = new SWORDContext(); + SWORDAuthentication auth = new SWORDAuthentication(); + EPerson ep = null; + boolean authenticated = false; + if (auth.authenticates(this.context, un, pw)) + { + // if authenticated, obtain the eperson object + ep = EPerson.findByEmail(context, un); + + if (ep != null) + { + authenticated = true; + sc.setAuthenticated(ep); + } + + // if there is an onBehalfOfuser, then find their eperson + // record, and if it exists set it. If not, then the + // authentication process fails + if (obo != null) + { + EPerson epObo= EPerson.findByEmail(this.context, obo); + if (epObo != null) + { + sc.setOnBehalfOf(epObo); + } + else + { + authenticated = false; + } + } + } + + // deal with the context or throw an authentication exception + if (ep != null && authenticated) + { + this.context.setCurrentUser(ep); + log.info(LogManager.getHeader(context, "sword_set_authenticated_user", "user_id=" + ep.getID())); + } + else + { + // decide what kind of error to throw + if (ep != null) + { + log.info(LogManager.getHeader(context, "sword_unable_to_set_user", "username=" + un)); + throw new SWORDAuthenticationException("Unable to authenticate the supplied used"); + } + else + { + log.info(LogManager.getHeader(context, "sword_unable_to_set_on_behalf_of", "username=" + un + ",on_behalf_of=" + obo)); + throw new SWORDAuthenticationException("Unable to authenticate the onBehalfOf account"); + } + } + + return sc; + } + catch (SQLException e) + { + log.error("caught exception: ", e); + throw new SWORDException("There was a problem accessing the repository user database", e); + } + catch (AuthorizeException e) + { + log.error("caught exception: ", e); + throw new SWORDAuthenticationException("There was a problem authenticating or authorising the user", e); + } + } +} diff --git a/dspace-sword/dspace-sword-api/src/main/java/org/dspace/sword/DepositManager.java b/dspace-sword/dspace-sword-api/src/main/java/org/dspace/sword/DepositManager.java new file mode 100644 index 0000000000..581fa0dc4e --- /dev/null +++ b/dspace-sword/dspace-sword-api/src/main/java/org/dspace/sword/DepositManager.java @@ -0,0 +1,277 @@ +/* DepositManager.java + * + * Copyright (c) 2007, Aberystwyth University + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the + * following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * - Neither the name of the Centre for Advanced Software and + * Intelligent Systems (CASIS) nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +package org.dspace.sword; + +import java.io.IOException; +import java.sql.SQLException; +import java.util.Date; + +import org.apache.log4j.Logger; + +import org.dspace.authorize.AuthorizeException; +import org.dspace.content.Collection; +import org.dspace.content.Item; +import org.dspace.core.Context; +import org.dspace.core.LogManager; + +import org.purl.sword.base.Deposit; +import org.purl.sword.base.DepositResponse; +import org.purl.sword.base.SWORDEntry; + +/** + * This class is responsible for initiating the process of + * deposit of SWORD Deposit objects into the DSpace repository + * + * @author Richard Jones + * + */ +public class DepositManager +{ + /** Log4j logger */ + public static Logger log = Logger.getLogger(DepositManager.class); + + /** DSpace context object */ + private Context context; + + /** SWORD Deposit request object */ + private Deposit deposit; + + /** SWORD Context object */ + private SWORDContext swordContext; + + /** + * @param context the context to set + */ + public void setContext(Context context) + { + this.context = context; + } + + /** + * @param deposit the deposit to set + */ + public void setDeposit(Deposit deposit) + { + this.deposit = deposit; + } + + /** + * @param sc the sword context to set + */ + public void setSWORDContext(SWORDContext sc) + { + this.swordContext = sc; + } + + /** + * Once this object is fully prepared, this method will execute + * the deposit process. The returned DepositRequest can be + * used then to assembel the SWORD response. + * + * @return the response to the deposit request + * @throws DSpaceSWORDException + */ + public DepositResponse deposit() + throws DSpaceSWORDException + { + // start the timer, and initialise the verboseness of the request + Date start = new Date(); + StringBuilder verbs = new StringBuilder(); + if (deposit.isVerbose()) + { + verbs.append(start.toString() + "; \n\n"); + verbs.append("Initialising verbose deposit; \n\n"); + } + + // FIXME: currently we don't verify, because this is done higher + // up the stack + // first we want to verify that the deposit is safe + // check the checksums and all that stuff + // This throws an exception if it can't verify the deposit + // this.verify(); + + // find out if the supplied SWORDContext can submit to the given + // collection + if (!this.canSubmit()) + { + // throw an exception if the deposit can't be made + String oboEmail = "none"; + if (swordContext.getOnBehalfOf() != null) + { + oboEmail = swordContext.getOnBehalfOf().getEmail(); + } + log.info(LogManager.getHeader(context, "deposit_failed_authorisation", "user=" + swordContext.getAuthenticated().getEmail() + ",on_behalf_of=" + oboEmail)); + throw new DSpaceSWORDException("Cannot submit to the given collection with this context"); + } + + // make a note of the authentication in the verbose string + if (deposit.isVerbose()) + { + verbs.append("Authenticated user " + swordContext.getAuthenticated().getEmail() + "; \n\n"); + if (swordContext.getOnBehalfOf() != null) + { + verbs.append("Depositing on behalf of: " + swordContext.getOnBehalfOf().getEmail() + "; \n\n"); + } + } + + // Obtain the relevant ingester from the factory + SWORDIngester si = SWORDIngesterFactory.getInstance(context, deposit); + + // do the deposit + DepositResult result = si.ingest(context, deposit); + + // now construct the deposit response. The response will be + // CREATED if the deposit is in the archive, or ACCEPTED if + // the deposit is in the workflow. We use a separate record + // for the handle because DSpace will not supply the Item with + // a record of the handle straight away. + String handle = result.getHandle(); + int state = Deposit.CREATED; + if (handle == null || "".equals(handle)) + { + state = Deposit.ACCEPTED; + } + DepositResponse response = new DepositResponse(state); + DSpaceATOMEntry dsatom = new DSpaceATOMEntry(); + SWORDEntry entry = dsatom.getSWORDEntry(result.getItem(), handle, deposit.isNoOp()); + + // if this was a no-op, we need to remove the files we just + // deposited, and abort the transaction + String nooplog = ""; + if (deposit.isNoOp()) + { + this.undoDeposit(result); + nooplog = "NoOp Requested: Removed all traces of submission; \n\n"; + } + + entry.setNoOp(deposit.isNoOp()); + + if (deposit.isVerbose()) + { + Date finish = new Date(); + long delta = finish.getTime() - start.getTime(); + String timer = "Total time for deposit processing: " + delta + " ms;"; + String verboseness = result.getVerboseDescription(); + if (verboseness != null && !"".equals(verboseness)) + { + entry.setVerboseDescription(verbs.toString() + result.getVerboseDescription() + nooplog + timer); + } + } + + response.setEntry(entry); + + return response; + } + + /** + * Can the users contained in this object's member SWORDContext + * make a successful submission to the selected collection + * + * @return true if yes, false if not + * @throws DSpaceSWORDException + */ + private boolean canSubmit() + throws DSpaceSWORDException + { + String loc = deposit.getLocation(); + CollectionLocation cl = new CollectionLocation(); + Collection collection = cl.getCollection(context, loc); + boolean submit = swordContext.canSubmitTo(context, collection); + return submit; + } + + /** + * @deprecated verification is currently done further up the stack + * @throws DSpaceSWORDException + */ + private void verify() + throws DSpaceSWORDException + { + // FIXME: please implement + // in reality, all this is done higher up the stack, so we don't + // need to worry! + } + + /** + * Remove all traces of the deposit from DSpace. The database changes + * are easy, as this method will call context.abort(), + * rolling back the transaction. In additon, though, files which have + * been placed on the disk are also removed. + * + * @param result the result of the deposit which is to be removed + * @throws DSpaceSWORDException + */ + private void undoDeposit(DepositResult result) + throws DSpaceSWORDException + { + try + { + // obtain the item's owning collection (there can be only one) + // and ask it to remove the item. Although we're going to abort + // the context, so that this nevers gets written to the db, + // it will get rid of the files on the disk + Item item = result.getItem(); + Collection collection = item.getOwningCollection(); + collection.removeItem(item); + + // abort the context, so no database changes are written + if (context != null && context.isValid()) + { + context.abort(); + } + } + catch (IOException e) + { + log.error("caught exception: ", e); + throw new DSpaceSWORDException(e); + } + catch (AuthorizeException e) + { + log.error("authentication problem; caught exception: ", e); + throw new DSpaceSWORDException(e); + } + catch (SQLException e) + { + log.error("caught exception: ", e); + throw new DSpaceSWORDException(e); + } + } + + +} diff --git a/dspace-sword/dspace-sword-api/src/main/java/org/dspace/sword/DepositResult.java b/dspace-sword/dspace-sword-api/src/main/java/org/dspace/sword/DepositResult.java new file mode 100644 index 0000000000..2607087e6b --- /dev/null +++ b/dspace-sword/dspace-sword-api/src/main/java/org/dspace/sword/DepositResult.java @@ -0,0 +1,109 @@ +/* DepositResult.java + * + * Copyright (c) 2007, Aberystwyth University + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the + * following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * - Neither the name of the Centre for Advanced Software and + * Intelligent Systems (CASIS) nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +package org.dspace.sword; + +import org.dspace.content.Item; + +/** + * The DSpace class for representing the results of a deposit + * request. This class can be used to hold all of the relevant + * components required to later build the SWORD response + * + * @author Richard Jones + * + */ +public class DepositResult +{ + /** the handle assigned to the item, if available */ + private String handle; + + /** the item created during deposit */ + private Item item; + + /** the verbose description string to be returned to the requester */ + private String verboseDescription; + + /** + * @return the item + */ + public Item getItem() + { + return item; + } + + /** + * @param item the item to set + */ + public void setItem(Item item) + { + this.item = item; + } + + /** + * @return the handle + */ + public String getHandle() + { + return handle; + } + + /** + * @param handle the item handle + */ + public void setHandle(String handle) + { + this.handle = handle; + } + + /** + * @return the verboseDescription + */ + public String getVerboseDescription() + { + return verboseDescription; + } + + /** + * @param verboseDescription the verboseDescription to set + */ + public void setVerboseDescription(String verboseDescription) + { + this.verboseDescription = verboseDescription; + } +} diff --git a/dspace-sword/dspace-sword-api/src/main/java/org/dspace/sword/LoadDSpaceConfig.java b/dspace-sword/dspace-sword-api/src/main/java/org/dspace/sword/LoadDSpaceConfig.java new file mode 100644 index 0000000000..17e7cc5e13 --- /dev/null +++ b/dspace-sword/dspace-sword-api/src/main/java/org/dspace/sword/LoadDSpaceConfig.java @@ -0,0 +1,63 @@ +/* + * LoadDSpaceConfig.java + * + * Copyright (c) 2002-2005, Hewlett-Packard Company and Massachusetts + * Institute of Technology. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the Hewlett-Packard Company nor the name of the + * Massachusetts Institute of Technology nor the names of their + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + */ +package org.dspace.sword; + +import javax.servlet.http.HttpServlet; + +import org.dspace.core.ConfigurationManager; + +/** + * Simple servlet to load in DSpace and log4j configurations. Should always be + * started up before other servlets (use ) + * + * This class has been duplicated into the DSpace SWORD module from its + * original home in the DSpace JSPUI, but authorship and copyright + * ownership are as dictated in this file. + * + * @author Robert Tansley + */ +public class LoadDSpaceConfig extends HttpServlet +{ + public void init() + { + // Get config parameter + String config = getServletContext().getInitParameter("dspace-config"); + + // Load in DSpace config + ConfigurationManager.loadConfig(config); + + } +} diff --git a/dspace-sword/dspace-sword-api/src/main/java/org/dspace/sword/SWORDAuthentication.java b/dspace-sword/dspace-sword-api/src/main/java/org/dspace/sword/SWORDAuthentication.java new file mode 100644 index 0000000000..13985d3b5c --- /dev/null +++ b/dspace-sword/dspace-sword-api/src/main/java/org/dspace/sword/SWORDAuthentication.java @@ -0,0 +1,72 @@ +/* SWORDAuthentication.java + * + * Copyright (c) 2007, Aberystwyth University + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the + * following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * - Neither the name of the Centre for Advanced Software and + * Intelligent Systems (CASIS) nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +package org.dspace.sword; + +import org.dspace.core.Context; +import org.dspace.authenticate.AuthenticationManager; +import org.dspace.authenticate.AuthenticationMethod; + +/** + * This class offers a thin wrapper for the default DSpace + * authentication module for the SWORD implementation + * + * @author Richard Jones + * + */ +public class SWORDAuthentication +{ + /** + * Does the given username and password authenticate for the + * given DSpace Context? + * + * @param context + * @param un + * @param pw + * @return true if yes, false if not + */ + public boolean authenticates(Context context, String un, String pw) + { + int auth = AuthenticationManager.authenticate(context, un, pw, null, null); + if (auth == AuthenticationMethod.SUCCESS) + { + return true; + } + return false; + } +} diff --git a/dspace-sword/dspace-sword-api/src/main/java/org/dspace/sword/SWORDContext.java b/dspace-sword/dspace-sword-api/src/main/java/org/dspace/sword/SWORDContext.java new file mode 100644 index 0000000000..568eafd984 --- /dev/null +++ b/dspace-sword/dspace-sword-api/src/main/java/org/dspace/sword/SWORDContext.java @@ -0,0 +1,328 @@ +/* SWORDContext.java + * + * Copyright (c) 2007, Aberystwyth University + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the + * following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * - Neither the name of the Centre for Advanced Software and + * Intelligent Systems (CASIS) nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +package org.dspace.sword; + +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; + +import org.dspace.content.Collection; +import org.dspace.core.Constants; +import org.dspace.core.Context; +import org.dspace.eperson.EPerson; +import org.dspace.eperson.Group; + +/** + * This class represents the users who are involved in the + * deposit or service document request process. This is the + * authenticated primary user and the potentially null + * onBehalfOf user. + * + * It can then answer various authorisation questions regarding + * those users. + * + * @author Richard Jones + * + */ +public class SWORDContext +{ + /** The primary authenticated user for the request */ + private EPerson authenticated = null; + + /** The onBehalfOf user for the request */ + private EPerson onBehalfOf = null; + + /** + * @return the authenticated user + */ + public EPerson getAuthenticated() + { + return authenticated; + } + + /** + * @param authenticated the eperson to set + */ + public void setAuthenticated(EPerson authenticated) + { + this.authenticated = authenticated; + } + + /** + * @return the onBehalfOf user + */ + public EPerson getOnBehalfOf() + { + return onBehalfOf; + } + + /** + * @param onBehalfOf the eperson to set + */ + public void setOnBehalfOf(EPerson onBehalfOf) + { + this.onBehalfOf = onBehalfOf; + } + + /** + * Is the authenticated user a DSpace administrator? This translates + * as asking the question of whether the given eperson is a member + * of the special DSpace group Administrator, with id 1 + * + * @param eperson + * @return true if administrator, false if not + * @throws SQLException + */ + public boolean isUserAdmin(Context context) + throws DSpaceSWORDException + { + try + { + if (this.authenticated != null) + { + Group admin = Group.find(context, 1); + return admin.isMember(this.authenticated); + } + return false; + } + catch (SQLException e) + { + throw new DSpaceSWORDException(e); + } + } + + /** + * Is the given onBehalfOf user DSpace administrator? This translates + * as asking the question of whether the given eperson is a member + * of the special DSpace group Administrator, with id 1 + * + * @param eperson + * @return true if administrator, false if not + * @throws SQLException + */ + public boolean isOnBehalfOfAdmin(Context context) + throws DSpaceSWORDException + { + try + { + if (this.onBehalfOf != null) + { + Group admin = Group.find(context, 1); + return admin.isMember(this.onBehalfOf); + } + return false; + } + catch (SQLException e) + { + throw new DSpaceSWORDException(e); + } + } + + /** + * Is the authenticated user a member of the given group + * or one of its sub groups? + * + * @param group + * @return + */ + public boolean isUserInGroup(Group group) + { + if (this.authenticated != null) + { + return isInGroup(group, this.authenticated); + } + return false; + } + + /** + * Is the onBehalfOf user a member of the given group or + * one of its sub groups + * + * @param group + * @return + */ + public boolean isOnBehalfOfInGroup(Group group) + { + if (this.onBehalfOf != null) + { + return isInGroup(group, this.onBehalfOf); + } + return false; + } + + /** + * Is the given eperson in the given group, or any of the groups + * that are also members of that group. This method recurses + * until it has exhausted the tree of groups or finds the given + * eperson + * + * @param group + * @param eperson + * @return true if in group, false if not + */ + public boolean isInGroup(Group group, EPerson eperson) + { + EPerson[] eps = group.getMembers(); + Group[] groups = group.getMemberGroups(); + + // is the user in the current group + for (int i = 0; i < eps.length; i++) + { + if (eperson.getID() == eps[i].getID()) + { + return true; + } + } + + // is the eperson in the sub-groups (recurse) + if (groups != null && groups.length > 0) + { + for (int j = 0; j < groups.length; j++) + { + if (isInGroup(groups[j], eperson)) + { + return true; + } + } + } + + // ok, we didn't find you + return false; + } + + /** + * Get an array of all the collections that the current SWORD + * context will allow deposit onto in the given DSpace context + * + * @param context + * @return the array of allowed collections + * @throws DSpaceSWORDException + */ + public Collection[] getAllowedCollections(Context context) + throws DSpaceSWORDException + { + try + { + // locate the collections to which the authenticated user has ADD rights + Collection[] cols = Collection.findAuthorized(context, null, Constants.ADD); + + // if there is no onBehalfOf user, just return the list + if (this.getOnBehalfOf() == null) + { + return cols; + } + + // if the onBehalfOf user is an administrator, return the list + if (this.isOnBehalfOfAdmin(context)) + { + return cols; + } + + // if we are here, then we have to filter the list of collections + List colList = new ArrayList(); + + for (int i = 0; i < cols.length; i++) + { + // we check each collection to see if the onBehalfOf user + // is permitted to deposit + + // urgh, this is so inefficient, but the authorisation API is + // a total hellish nightmare + Group subs = cols[i].getSubmitters(); + if (isOnBehalfOfInGroup(subs)) + { + colList.add(cols[i]); + } + } + + // now create the new array and return that + Collection[] newCols = new Collection[colList.size()]; + newCols = colList.toArray((Collection[]) newCols); + return newCols; + } + catch (SQLException e) + { + throw new DSpaceSWORDException(e); + } + } + + /** + * Can the current SWORD Context permit deposit into the given + * collection in the given DSpace Context + * + * @param context + * @param collection + * @return + * @throws DSpaceSWORDException + */ + public boolean canSubmitTo(Context context, Collection collection) + throws DSpaceSWORDException + { + Group subs = collection.getSubmitters(); + if (isUserAdmin(context)) + { + if (this.onBehalfOf != null) + { + if (isOnBehalfOfAdmin(context)) + { + return true; + } + return isOnBehalfOfInGroup(subs); + } + return true; + } + else + { + if (isUserInGroup(subs)) + { + if (this.onBehalfOf != null) + { + if (isOnBehalfOfAdmin(context)) + { + return true; + } + return isOnBehalfOfInGroup(subs); + } + return true; + } + return false; + } + + } +} diff --git a/dspace-sword/dspace-sword-api/src/main/java/org/dspace/sword/SWORDIngester.java b/dspace-sword/dspace-sword-api/src/main/java/org/dspace/sword/SWORDIngester.java new file mode 100644 index 0000000000..d0b66d97f9 --- /dev/null +++ b/dspace-sword/dspace-sword-api/src/main/java/org/dspace/sword/SWORDIngester.java @@ -0,0 +1,65 @@ +/* SWORDIngester.java + * + * Copyright (c) 2007, Aberystwyth University + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the + * following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * - Neither the name of the Centre for Advanced Software and + * Intelligent Systems (CASIS) nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +package org.dspace.sword; + +import org.dspace.core.Context; + +import org.purl.sword.base.Deposit; + +/** + * Interface behind which can be implemented ingest mechanisms + * for SWORD deposit requests. Instances of this class should + * be obtained via the SWORDIngesterFactory class. + * + * @author Richard Jones + * + */ +public interface SWORDIngester +{ + /** + * Ingest the package as described in the given Deposit object + * within the given DSpace Context + * + * @param context + * @param deposit + * @return the result of the deposit + * @throws DSpaceSWORDException + */ + DepositResult ingest(Context context, Deposit deposit) throws DSpaceSWORDException; +} diff --git a/dspace-sword/dspace-sword-api/src/main/java/org/dspace/sword/SWORDIngesterFactory.java b/dspace-sword/dspace-sword-api/src/main/java/org/dspace/sword/SWORDIngesterFactory.java new file mode 100644 index 0000000000..bb0107d0ad --- /dev/null +++ b/dspace-sword/dspace-sword-api/src/main/java/org/dspace/sword/SWORDIngesterFactory.java @@ -0,0 +1,74 @@ +/* SWORDIngesterFactory.java + * + * Copyright (c) 2007, Aberystwyth University + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the + * following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * - Neither the name of the Centre for Advanced Software and + * Intelligent Systems (CASIS) nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +package org.dspace.sword; + +import org.dspace.core.Context; + +import org.purl.sword.base.Deposit; + +/** + * Factory class which will mint objects conforming to the + * SWORDIngester interface. + * + * @author Richard Jones + * + */ +public class SWORDIngesterFactory +{ + /** + * Generate an object which conforms to the SWORDIngester interface. + * This Factory method may use the given DSpace context and the given + * SWORD Deposit request to decide on the most appropriate implementation + * of the interface to return. + * + * The current implementation is capable only of returning the single + * implementation SWORDMETSIngester. + * + * @param context + * @param deposit + * @return + * @throws DSpaceSWORDException + */ + public static SWORDIngester getInstance(Context context, Deposit deposit) + throws DSpaceSWORDException + { + // so there is only one implementation at the moment, and this is it + return new SWORDMETSIngester(); + } +} diff --git a/dspace-sword/dspace-sword-api/src/main/java/org/dspace/sword/SWORDMETSIngester.java b/dspace-sword/dspace-sword-api/src/main/java/org/dspace/sword/SWORDMETSIngester.java new file mode 100644 index 0000000000..dc70f56a4e --- /dev/null +++ b/dspace-sword/dspace-sword-api/src/main/java/org/dspace/sword/SWORDMETSIngester.java @@ -0,0 +1,273 @@ +/* SWORDMETSIngester.java + * + * Copyright (c) 2007, Aberystwyth University + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the + * following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * - Neither the name of the Centre for Advanced Software and + * Intelligent Systems (CASIS) nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +package org.dspace.sword; + +import java.io.InputStream; +import java.util.Date; +import java.util.StringTokenizer; + +import org.apache.log4j.Logger; + +import org.dspace.content.Collection; +import org.dspace.content.DCDate; +import org.dspace.content.DCValue; +import org.dspace.content.Item; +import org.dspace.content.WorkspaceItem; +import org.dspace.content.packager.PackageIngester; +import org.dspace.content.packager.PackageParameters; +import org.dspace.core.ConfigurationManager; +import org.dspace.core.Context; +import org.dspace.core.PluginManager; +import org.dspace.handle.HandleManager; +import org.dspace.workflow.WorkflowItem; +import org.dspace.workflow.WorkflowManager; + +import org.purl.sword.base.Deposit; + +public class SWORDMETSIngester implements SWORDIngester +{ + /** Log4j logger */ + public static Logger log = Logger.getLogger(SWORDMETSIngester.class); + + /** holder for the messages that may be delivered back to the user */ + private StringBuilder verboseDesc = new StringBuilder(); + + /** is this a verbose deposit request */ + private boolean verbose = false; + + /* (non-Javadoc) + * @see org.dspace.sword.SWORDIngester#ingest(org.dspace.core.Context, org.purl.sword.base.Deposit) + */ + public DepositResult ingest(Context context, Deposit deposit) + throws DSpaceSWORDException + { + try + { + // set the verbosity of the response + this.verbose = deposit.isVerbose(); + + // the DSpaceMETSIngester requires an input stream + InputStream is = deposit.getFile(); + + // get the target collection + String loc = deposit.getLocation(); + CollectionLocation cl = new CollectionLocation(); + Collection collection = cl.getCollection(context, loc); + message("Performing deposit using location: " + loc + "; "); + message("Location resolves to collection with handle: " + collection.getHandle() + + " and name: " + collection.getMetadata("name") + "; "); + + // load the plugin manager for the required configuration + String cfg = ConfigurationManager.getProperty("sword.mets-ingester.package-ingester"); + if (cfg == null || "".equals(cfg)) + { + cfg = "METS"; // default to METS + } + message("Using package manifest format: " + cfg); + + PackageIngester pi = (PackageIngester) PluginManager.getNamedPlugin(PackageIngester.class, cfg); + + // the licence is either in the zip or the mets manifest. Either way + // it's none of our business here + String licence = null; + + // We don't need to include any parameters + PackageParameters params = new PackageParameters(); + + // ingest the item + WorkspaceItem wsi = pi.ingest(context, collection, is, params, licence); + if (wsi == null) + { + message("Failed to ingest the package; throwing exception"); + throw new DSpaceSWORDException("Package Ingest failed"); + } + + // now we can inject the newly constructed item into the workflow + WorkflowItem wfi = WorkflowManager.startWithoutNotify(context, wsi); + + // pull the item out so that we can report on it + Item installedItem = wfi.getItem(); + + // update the item metadata to inclue the current time as + // the updated date + this.setUpdatedDate(installedItem); + + // DSpace ignores the slug value as suggested identifier, but + // it does store it in the metadata + this.setSlug(installedItem, deposit.getSlug()); + + // in order to write these changes, we need to bypass the + // authorisation briefly, because although the user may be + // able to add stuff to the repository, they may not have + // WRITE permissions on the archive. + boolean ignore = context.ignoreAuthorization(); + context.setIgnoreAuthorization(true); + installedItem.update(); + context.setIgnoreAuthorization(ignore); + + // for some reason, DSpace will not give you the handle automatically, + // so we have to look it up + String handle = HandleManager.findHandle(context, installedItem); + + message("Ingest successful; "); + message("Item created with internal identifier: " + installedItem.getID() + "; "); + if (handle != null) + { + message("Item created with external identifier: " + handle + "; "); + } + else + { + message("No external identifier available at this stage (item in workflow); "); + } + + DepositResult dr = new DepositResult(); + dr.setItem(installedItem); + dr.setHandle(handle); + dr.setVerboseDescription(verboseDesc.toString()); + + return dr; + } + catch (Exception e) + { + log.error("caught exception: ", e); + throw new DSpaceSWORDException(e); + } + } + + /** + * shortcut to registering a message with the verboseDescription + * member variable. This checks to see if the request is + * verbose, meaning we don't have to do it inline and break nice + * looking code up + * + * @param msg + */ + private void message(String msg) + { + if (this.verbose) + { + verboseDesc.append("\n\n"); + verboseDesc.append(msg); + } + } + + /** + * Add the current date to the item metadata. This looks up + * the field in which to store this metadata in the configuration + * sword.updated.field + * + * @param item + * @throws DSpaceSWORDException + */ + private void setUpdatedDate(Item item) + throws DSpaceSWORDException + { + String field = ConfigurationManager.getProperty("sword.updated.field"); + if (field == null || "".equals(field)) + { + throw new DSpaceSWORDException("No configuration, or configuration is invalid for: sword.updated.field"); + } + + DCValue dc = this.configToDC(field, null); + item.clearMetadata(dc.schema, dc.element, dc.qualifier, Item.ANY); + DCDate date = new DCDate(new Date()); + item.addMetadata(dc.schema, dc.element, dc.qualifier, null, date.toString()); + } + + /** + * Store the given slug value (which is used for suggested identifiers, + * and which DSpace ignores) in the item metadata. This looks up the + * field in which to store this metadata in the configuration + * sword.slug.field + * + * @param item + * @param slugVal + * @throws DSpaceSWORDException + */ + private void setSlug(Item item, String slugVal) + throws DSpaceSWORDException + { + // if there isn't a slug value, don't set it + if (slugVal == null) + { + return; + } + + String field = ConfigurationManager.getProperty("sword.slug.field"); + if (field == null || "".equals(field)) + { + throw new DSpaceSWORDException("No configuration, or configuration is invalid for: sword.slug.field"); + } + + DCValue dc = this.configToDC(field, null); + item.clearMetadata(dc.schema, dc.element, dc.qualifier, Item.ANY); + item.addMetadata(dc.schema, dc.element, dc.qualifier, null, slugVal); + } + + /** + * utility method to turn given metadata fields of the form + * schema.element.qualifier into DCValue objects which can be + * used to access metadata in items. + * + * The def parameter should be null, * or "" depending on how + * you intend to use the DCValue object + * + * @param config + * @param def + * @return + */ + private DCValue configToDC(String config, String def) + { + DCValue dcv = new DCValue(); + dcv.schema = def; + dcv.element= def; + dcv.qualifier = def; + + StringTokenizer stz = new StringTokenizer(config, "."); + dcv.schema = stz.nextToken(); + dcv.element = stz.nextToken(); + if (stz.hasMoreTokens()) + { + dcv.qualifier = stz.nextToken(); + } + + return dcv; + } + +} diff --git a/dspace-sword/dspace-sword-api/src/main/java/org/dspace/sword/SWORDService.java b/dspace-sword/dspace-sword-api/src/main/java/org/dspace/sword/SWORDService.java new file mode 100644 index 0000000000..2c474b77da --- /dev/null +++ b/dspace-sword/dspace-sword-api/src/main/java/org/dspace/sword/SWORDService.java @@ -0,0 +1,278 @@ +/* SWORDService.java + * + * Copyright (c) 2007, Aberystwyth University + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the + * following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * - Neither the name of the Centre for Advanced Software and + * Intelligent Systems (CASIS) nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +package org.dspace.sword; + +import java.sql.SQLException; + +import org.apache.log4j.Logger; + +import org.dspace.content.Collection; +import org.dspace.core.ConfigurationManager; +import org.dspace.core.Constants; +import org.dspace.core.Context; +import org.dspace.eperson.EPerson; +import org.dspace.eperson.Group; + +import org.purl.sword.base.Service; +import org.purl.sword.base.ServiceDocument; +import org.purl.sword.base.ServiceLevel; +import org.purl.sword.base.Workspace; + +/** + * Represents the SWORD service provided by DSpace. This class is + * responsible for generating the Service Document, which is the + * primary descriptor of the SWORD service + * + * @author Richard Jones + * + */ +public class SWORDService +{ + /** Log4j logging instance */ + public static Logger log = Logger.getLogger(SWORDService.class); + + /** The DSpace context under which to perform requests */ + private Context context = null; + + /** The SWORD context of the request */ + private SWORDContext swordContext = null; + + /** + * Set the DSpace context to use + * + * @param context + */ + public void setContext(Context context) + { + this.context = context; + } + + /** + * Set the SWORD Context to use + * + * @param sc + */ + public void setSWORDContext(SWORDContext sc) + { + this.swordContext = sc; + } + + /** + * Obtain the service document for the repository based on the + * DSpace context and the SWORD context which must be set for + * this object prior to calling this method. + * + * @return The service document based on the context of the request + * @throws DSpaceSWORDException + */ + public ServiceDocument getServiceDocument() + throws DSpaceSWORDException + { + // first check that the context and sword context have + // been set + if (context == null) + { + throw new DSpaceSWORDException("The Context is null; please set it before calling getServiceDocument"); + } + + if (swordContext == null) + { + throw new DSpaceSWORDException("The SWORD Context is null; please set it before calling getServiceDocument"); + } + + // DSpace will support the top level service option + ServiceLevel sl = ServiceLevel.ONE; + + // can we dry-run requests + boolean noOp = true; + + // can we be verbose in our actions + boolean verbose = true; + + // construct a new service document + Service service = new Service(sl, noOp, verbose); + + // set the title of the workspace as per the name of the DSpace installation + String ws = ConfigurationManager.getProperty("dspace.name"); + Workspace workspace = new Workspace(); + workspace.setTitle(ws); + + Collection[] cols = swordContext.getAllowedCollections(context); + for (int i = 0; i < cols.length; i++) + { + org.purl.sword.base.Collection scol = this.buildSwordCollection(cols[i]); + workspace.addCollection(scol); + } + + service.addWorkspace(workspace); + + ServiceDocument sd = new ServiceDocument(service); + return sd; + } + + /** + * Is the given eperson a DSpace administrator? This translates + * as asking the question of whether the given eperson a member + * of the special DSpace group Administrator, with id 1 + * + * @param eperson + * @return true if administrator, false if not + * @throws SQLException + */ + private boolean isAdmin(EPerson eperson) + throws SQLException + { + Group admin = Group.find(context, 1); + return admin.isMember(eperson); + } + + /** + * Is the given eperson in the given group, or any of the groups + * that are also members of that group. This method recurses + * until it has exhausted the tree of groups or finds the given + * eperson + * + * @param group + * @param eperson + * @return true if in group, false if not + */ + private boolean isInGroup(Group group, EPerson eperson) + { + EPerson[] eps = group.getMembers(); + Group[] groups = group.getMemberGroups(); + + // is the user in the current group + for (int i = 0; i < eps.length; i++) + { + if (eperson.getID() == eps[i].getID()) + { + return true; + } + } + + // is the eperson in the sub-groups (recurse) + if (groups != null && groups.length > 0) + { + for (int j = 0; j < groups.length; j++) + { + if (isInGroup(groups[j], eperson)) + { + return true; + } + } + } + + // ok, we didn't find you + return false; + } + + /** + * Build an instance of a SWORD collection from an instance of + * a DSpace collection. + * + * Note that DSpace collections do not have an analogue of the + * SWORD field "treatment", so this field is always blank. + * + * @param col + * @return the SWORD Collection object + * @throws DSpaceSWORDException + */ + private org.purl.sword.base.Collection buildSwordCollection(Collection col) + throws DSpaceSWORDException + { + org.purl.sword.base.Collection scol = new org.purl.sword.base.Collection(); + + // prepare the parameters to be put in the sword collection + CollectionLocation cl = new CollectionLocation(); + String location = cl.getLocation(col); + + // collection title is just its name + String title = col.getMetadata("name"); + + // the collection policy is the licence to which the collection adheres + String collectionPolicy = col.getLicense(); + + // FIXME: what is the treatment? Doesn't seem appropriate for DSpace + // String treatment = " "; + + // the format namespace is only METS in this implementation + String namespace = "http://www.loc.gov/METS"; + + // abstract is the short description of the collection + String dcAbstract = col.getMetadata("short_description"); + + // we just do support mediation + boolean mediation = true; + + // the list of mime types that we accept + // for the time being, we just take a zip, and we have to trust what's in it + String zip = "application/zip"; + + // load up the sword collection + scol.setLocation(location); + + // add the title if it exists + if (title != null && !"".equals(title)) + { + scol.setTitle(title); + } + + // add the collection policy if it exists + if (collectionPolicy != null && !"".equals(collectionPolicy)) + { + scol.setCollectionPolicy(collectionPolicy); + } + + // FIXME: leave the treatment out for the time being, + // as there is no analogue + // scol.setTreatment(treatment); + + // add the abstract if it exists + if (dcAbstract != null && !"".equals(dcAbstract)) + { + scol.setAbstract(dcAbstract); + } + + scol.setMediation(mediation); + scol.addAccepts(zip); + scol.setFormatNamespace(namespace); + + return scol; + } +} diff --git a/dspace-sword/dspace-sword-api/src/main/java/org/purl/sword/base/ChecksumUtils.java b/dspace-sword/dspace-sword-api/src/main/java/org/purl/sword/base/ChecksumUtils.java new file mode 100755 index 0000000000..6427a6988c --- /dev/null +++ b/dspace-sword/dspace-sword-api/src/main/java/org/purl/sword/base/ChecksumUtils.java @@ -0,0 +1,181 @@ +/** + * Copyright (c) 2007, Aberystwyth University + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the + * following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * - Neither the name of the Centre for Advanced Software and + * Intelligent Systems (CASIS) nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +package org.purl.sword.base; + +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; + +/** + * Utility class that holds Checksum related methods. + * + * @author Neil Taylor + */ +public class ChecksumUtils +{ + /** + * 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 ) + { + InfoLogger.getLogger().writeInfo("Error accessing string"); + throw ex; // rethrow + } + 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 ) + { + InfoLogger.getLogger().writeInfo("Error accessing string"); + throw ex; // rethrow + } + + return md5; + } + + public static void main(String[] args) throws NoSuchAlgorithmException, IOException { + System.out.println(ChecksumUtils.generateMD5(args[0])); + } +} diff --git a/dspace-sword/dspace-sword-api/src/main/java/org/purl/sword/base/Collection.java b/dspace-sword/dspace-sword-api/src/main/java/org/purl/sword/base/Collection.java new file mode 100755 index 0000000000..1d540321e8 --- /dev/null +++ b/dspace-sword/dspace-sword-api/src/main/java/org/purl/sword/base/Collection.java @@ -0,0 +1,495 @@ +/** + * Copyright (c) 2007, Aberystwyth University + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the + * following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * - Neither the name of the Centre for Advanced Software and + * Intelligent Systems (CASIS) nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +package org.purl.sword.base; + +/** + * Author : $Author: nst $ + * Date : $Date: 2007/09/21 15:18:54 $ + * Revision : $Revision: 1.4 $ + * Name : $Name: $ + */ + +import java.util.ArrayList; +import java.util.List; + +import org.purl.sword.base.Namespaces; +import org.w3.atom.ContentType; +import org.w3.atom.Title; + +import nu.xom.Attribute; +import nu.xom.Elements; +import nu.xom.Element; + +/** + * A representation of a SWORD Collection. + * + * http://www.ukoln.ac.uk/repositories/digirep/index/SWORD_APP_Profile_0.5 + * + * @author Stuart Lewis + * @author Neil Taylor + */ +public class Collection extends XmlElement implements SwordElementInterface +{ + /** + * The element name. + */ + public static final String ELEMENT_NAME = "collection"; + + /** + * Collection location, expressed as a URL. + */ + private String location; + + /** + * Holds the ATOM Title for the collection. + */ + private Title title; + + /** + * List of the APP:Accept elements. + */ + private List accepts; + + /** + * Holds the SWORD Collection policy. + */ + private String collectionPolicy; + + /** + * The SWORD mediation value. Indicates if mediation is allowed. + */ + private boolean mediation; + + /** + * Internal value to track if the mediation value has been + * set programmatically. + */ + private boolean mediationSet; + + /** + * The SWORD treatment value. + */ + private String treatment; + + /** + * The SWORD namespace. + */ + private String namespace; + + /** + * The DC Terms Abstract details. + */ + private String dcAbstract; + + /** + * Create a new instance. + */ + public Collection() + { + super(null); + accepts = new ArrayList(); + mediationSet = false; + } + + /** + * Create a new instance and set the initial location for the collection. + * + * @param location The initial location, expressed as a URL. + */ + public Collection(String location) + { + super(null); + this.location = location; + } + + /** + * Retrieve an array that holds all of the Accept details. + * + * @return An array of strings. Each string represents an + * individual accept element. The array will have a length + * of 0 if no accepts elements are stored in this collection. + */ + public String[] getAccepts() + { + String[] values = new String[this.accepts.size()]; + return (String[])accepts.toArray(values); + } + + /** + * Retrieve an array that holds all of the Accept details. + * + * @return An array of strings. Each string represents an + * individual accept element. The array will have a length + * of 0 if no accepts elements are stored in this collection. + */ + public List getAcceptsList() + { + return accepts; + } + + /** + * Add an accepts entry. + * + * @param accepts The accepts value. + */ + public void addAccepts(String accepts) { + this.accepts.add(accepts); + } + + /** + * Remove all of the accepts associated with this Collection. + */ + public void clearAccepts( ) + { + this.accepts.clear(); + } + + /** + * Get the collection policy. + * + * @return The SWORD collectionPolicy. + */ + public String getCollectionPolicy() { + return collectionPolicy; + } + + /** + * Set the collection policy. + * + * @param collectionPolicy The collection policy. + */ + public void setCollectionPolicy(String collectionPolicy) { + this.collectionPolicy = collectionPolicy; + } + + /** + * Get the location. + * + * @return TShe location + */ + public String getLocation() { + return location; + } + + /** + * Set the location. + * + * @param location The location. + */ + public void setLocation(String location) { + this.location = location; + } + + /** + * Get the mediation value. + * + * @return The mediation + */ + public boolean getMediation() { + return mediation; + } + + /** + * Set the mediation value. + * + * @param mediation The mediation value. + */ + public void setMediation(boolean mediation) { + this.mediation = mediation; + mediationSet = true; + } + + /** + * See getFormatNamespace. + * @return the namespace + * @deprecated Use getFormatNamespace() + */ + public String getNamespace() { + return getFormatNamespace(); + } + + /** + * Get the format namespace. + * + * @return The format namespace. + */ + public String getFormatNamespace() + { + return namespace; + } + + /** + * See setFormatNamespace. + * @param namespace the namespace to set + * @deprecated Use setFormatNamespace + */ + public void setNamespace(String namespace) { + setFormatNamespace(namespace); + } + + /** + * Set the format namespace. + * + * @param namespace The namespace. + */ + public void setFormatNamespace(String namespace) + { + this.namespace = namespace; + } + + /** + * Get the DC Term abstract. + * + * @return The abstract. + */ + public String getAbstract() + { + return dcAbstract; + } + + /** + * Set the abstract. + * + * @param abstractString The abstract. + */ + public void setAbstract(String abstractString) + { + this.dcAbstract = abstractString; + } + + /** + * Set the title. This will set the title type to ContentType.TEXT. + * + * @param title The title. + */ + public void setTitle( String title ) + { + if( this.title == null) + { + this.title = new Title(); + } + this.title.setContent(title); + this.title.setType(ContentType.TEXT); + } + + /** + * Get the title. + * + * @return The title, or null if no title has been set. + */ + public String getTitle( ) + { + if( title == null ) + { + return null; + } + return title.getContent(); + } + + /** + * Get the treatment value. + * + * @return The treatment. + */ + public String getTreatment() + { + return treatment; + } + + /** + * Set the treatment. + * + * @param treatment The treatment. + */ + public void setTreatment(String treatment) + { + this.treatment = treatment; + } + + /** + * Get a string representation of this object. This is + * equivalent to calling marshall().toString(). + */ + public String toString() + { + Element element = marshall(); + return element.toString(); + } + + /** + * Marshall the data in this object to an Element object. + * + * @return A XOM Element that holds the data for this Content element. + */ + public Element marshall( ) + { + // convert data into XOM elements and return the 'root', i.e. the one + // that represents the collection. + Element collection = new Element(ELEMENT_NAME, Namespaces.NS_APP); + Attribute href = new Attribute("href", location); + collection.addAttribute(href); + + //title = new Title(); + collection.appendChild(title.marshall()); + + Element acceptsElement = null; + for( String item : accepts ) + { + acceptsElement = new Element("accepts", Namespaces.NS_APP); + acceptsElement.appendChild(item); + collection.appendChild(acceptsElement); + } + + if( collectionPolicy != null ) + { + Element colPolicyElement = new Element("sword:collectionPolicy", Namespaces.NS_SWORD); + colPolicyElement.appendChild(collectionPolicy); + collection.appendChild(colPolicyElement); + } + + if( dcAbstract != null ) + { + Element dcAbstractElement = new Element("dcterms:abstract", Namespaces.NS_DC_TERMS); + dcAbstractElement.appendChild(dcAbstract); + collection.appendChild(dcAbstractElement); + } + + if( mediationSet ) + { + Element mediationElement = new Element("sword:mediation", Namespaces.NS_SWORD); + mediationElement.appendChild(Boolean.toString(mediation)); + collection.appendChild(mediationElement); + } + + // treatment + if( treatment != null ) + { + Element treatmentElement = new Element("sword:treatment", Namespaces.NS_SWORD); + treatmentElement.appendChild(treatment); + collection.appendChild(treatmentElement); + } + + // namespace + if( namespace != null ) + { + Element namespaceElement = new Element("sword:namespace", Namespaces.NS_SWORD); + namespaceElement.appendChild(namespace); + collection.appendChild(namespaceElement); + } + + return collection; + } + + /** + * Unmarshall the content element into the data in this object. + * + * @throws UnmarshallException If the element does not contain a + * content element or if there are problems + * accessing the data. + */ + public void unmarshall( Element collection ) + throws UnmarshallException + { + if( ! isInstanceOf(collection, "collection", Namespaces.NS_APP)) + { + throw new UnmarshallException( "Not an app:collection element" ); + } + + try + { + // retrieve the attributes + int count = collection.getAttributeCount(); + Attribute a = null; + for( int i = 0; i < count; i++ ) + { + a = collection.getAttribute(i); + if( "href".equals(a.getQualifiedName())) + { + location = a.getValue(); + } + } + + accepts.clear(); + + // retrieve all of the sub-elements + Elements elements = collection.getChildElements(); + Element element = null; + int length = elements.size(); + + for(int i = 0; i < length; i++ ) + { + element = elements.get(i); + // FIXME - atom assumes that it has been defined. not correct. + if( isInstanceOf(element, "title", Namespaces.NS_ATOM ) ) + { + title = new Title(); + title.unmarshall(element); + } + else if( isInstanceOf(element, "accepts", Namespaces.NS_APP )) + { + accepts.add(unmarshallString(element)); + } + else if( isInstanceOf(element, "collectionPolicy", Namespaces.NS_SWORD )) + { + collectionPolicy = unmarshallString(element); + } + else if( isInstanceOf(element, "abstract", Namespaces.NS_DC_TERMS )) + { + dcAbstract = unmarshallString(element); + } + else if( isInstanceOf(element, "mediation", Namespaces.NS_SWORD )) + { + setMediation(unmarshallBoolean(element)); + } + else if( isInstanceOf(element, "treatment", Namespaces.NS_SWORD )) + { + treatment = unmarshallString(element); + } + else if( isInstanceOf(element, "namespace", Namespaces.NS_SWORD )) + { + namespace = unmarshallString(element); + } + } + } + catch( Exception ex ) + { + InfoLogger.getLogger().writeError("Unable to parse an element in Collection: " + ex.getMessage()); + throw new UnmarshallException("Unable to parse an element in Collection", ex); + } + + } +} \ No newline at end of file diff --git a/dspace-sword/dspace-sword-api/src/main/java/org/purl/sword/base/Deposit.java b/dspace-sword/dspace-sword-api/src/main/java/org/purl/sword/base/Deposit.java new file mode 100755 index 0000000000..7338ee1a03 --- /dev/null +++ b/dspace-sword/dspace-sword-api/src/main/java/org/purl/sword/base/Deposit.java @@ -0,0 +1,317 @@ +/** + * Copyright (c) 2007, Aberystwyth University + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the + * following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * - Neither the name of the Centre for Advanced Software and + * Intelligent Systems (CASIS) nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +package org.purl.sword.base; + +import java.io.InputStream; + +import javax.servlet.http.HttpServletResponse; + +/** + * Represents a deposit. + * + * @author Stuart Lewis + * + */ +public class Deposit +{ + + /** The File deposited */ + private InputStream file; + + private String contentType; + + private int contentLength; + + private String username; + + private String password; + + private String onBehalfOf; + + private String slug; + + private String md5; + + private boolean verbose; + + private boolean noOp; + + private String formatNamespace; + + private String depositID; + + private String IPAddress; + + private String location; + + private String filename; + + /** + * Submission created + */ + public static final int CREATED = HttpServletResponse.SC_CREATED; + + /** + * Submission accepted. + */ + public static final int ACCEPTED = HttpServletResponse.SC_ACCEPTED; + + /** + * @return the authenticatedUserName + */ + public String getUsername() { + return username; + } + + /** + * @param authenticatedUserName the authenticatedUserName to set + */ + public void setUsername(String username) { + this.username = username; + } + + /** + * @return the authenticatedUserPassword + */ + public String getPassword() { + return password; + } + + /** + * @param password the password to set + */ + public void setPassword(String password) { + this.password = password; + } + + /** + * @return the contentLength + */ + public int getContentLength() { + return contentLength; + } + + /** + * @param contentLength the contentLength to set + */ + public void setContentLength(int contentLength) { + this.contentLength = contentLength; + } + + /** + * @return the contentType + */ + public String getContentType() { + return contentType; + } + + /** + * @param contentType the contentType to set + */ + public void setContentType(String contentType) { + this.contentType = contentType; + } + + /** + * @return the depositID + */ + public String getDepositID() { + return depositID; + } + + /** + * @param depositID the depositID to set + */ + public void setDepositID(String depositID) { + this.depositID = depositID; + } + + /** + * @return the file + */ + public InputStream getFile() { + return file; + } + + /** + * @param file the file to set + */ + public void setFile(InputStream file) { + this.file = file; + } + + /** + * @return the formatNamespace + */ + public String getFormatNamespace() { + return formatNamespace; + } + + /** + * @param formatNamespace the formatNamespace to set + */ + public void setFormatNamespace(String formatNamespace) { + this.formatNamespace = formatNamespace; + } + + /** + * @return the md5 + */ + public String getMd5() { + return md5; + } + + /** + * @param md5 the md5 to set + */ + public void setMd5(String md5) { + this.md5 = md5; + } + + /** + * @return the noOp + */ + public boolean isNoOp() { + return noOp; + } + + /** + * @param noOp the noOp to set + */ + public void setNoOp(boolean noOp) { + this.noOp = noOp; + } + + /** + * @return the onBehalfOf + */ + public String getOnBehalfOf() { + return onBehalfOf; + } + + /** + * @param onBehalfOf the onBehalfOf to set + */ + public void setOnBehalfOf(String onBehalfOf) { + this.onBehalfOf = onBehalfOf; + } + + /** + * @return the slug + */ + public String getSlug() { + return slug; + } + + /** + * @param slug the slug to set + */ + public void setSlug(String slug) { + this.slug = slug; + } + + /** + * @return the verbose + */ + public boolean isVerbose() { + return verbose; + } + + /** + * @param verbose the verbose to set + */ + public void setVerbose(boolean verbose) { + this.verbose = verbose; + } + + /** + * Get the IP address of the user + * + * @return the IP address + */ + public String getIPAddress() { + return IPAddress; + } + + /** + * Set the IP address of the user + * + * @param String the IP address + */ + public void setIPAddress(String IPAddress) { + this.IPAddress = IPAddress; + } + + /** + * Get the location of the deposit + * + * @return the location of the deposit + */ + public String getLocation() { + return location; + } + + /** + * Set the location of the deposit + * + * @param String the location + */ + public void setLocation(String location) { + this.location = location; + } + + /** + * Retrieve the filename that is associated with this deposit. + * + * @return The filename. + */ + public String getFilename() + { + return filename; + } + + /** + * Set the filename that is to be used for this deposit. + * + * @param filename The filename. + */ + public void setFilename(String filename) + { + this.filename = filename; + } + +} \ No newline at end of file diff --git a/dspace-sword/dspace-sword-api/src/main/java/org/purl/sword/base/DepositResponse.java b/dspace-sword/dspace-sword-api/src/main/java/org/purl/sword/base/DepositResponse.java new file mode 100755 index 0000000000..af29f50af3 --- /dev/null +++ b/dspace-sword/dspace-sword-api/src/main/java/org/purl/sword/base/DepositResponse.java @@ -0,0 +1,163 @@ +/** + * Copyright (c) 2007, Aberystwyth University + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the + * following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * - Neither the name of the Centre for Advanced Software and + * Intelligent Systems (CASIS) nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +package org.purl.sword.base; + +/** + * Author : $Author: nst $ + * Date : $Date: 2007/09/21 15:18:55 $ + * Revision : $Revision: 1.3 $ + * Name : $Name: $ + */ + +import java.io.ByteArrayOutputStream; +import java.io.IOException; + +import javax.servlet.http.HttpServletResponse; + +import nu.xom.Builder; +import nu.xom.Document; +import nu.xom.Element; +import nu.xom.ParsingException; +import nu.xom.Serializer; + +/** + * + * @author Stuart Lewis + * @author Neil Taylor + * + */ +public class DepositResponse +{ + private SWORDEntry entry; + + private int httpResponse; + + public DepositResponse( int httpResponse ) + { + entry = new SWORDEntry(); + this.httpResponse = httpResponse; + } + + /** + * + * @param entry + */ + public void setEntry( SWORDEntry entry ) + { + this.entry = entry; + } + + /** + * + * @param entry + */ + public SWORDEntry getEntry( ) + { + return entry; + } + + public int getHttpResponse() { + return httpResponse; + } + + public void setHttpResponse(int httpResponse) { + this.httpResponse = httpResponse; + } + + /** + * + * @return + */ + public String marshall( ) + { + try + { + ByteArrayOutputStream stream = new ByteArrayOutputStream(); + Serializer serializer = new Serializer(stream, "UTF-8"); + serializer.setIndent(3); + serializer.setMaxLength(64); + + if( entry != null ) + { + Document doc = new Document(entry.marshall()); + serializer.write(doc); + System.out.println(stream.toString()); + return stream.toString(); + } + } + catch (IOException ex) + { + System.err.println(ex); + } + + return null; // default return value. + } + + /** + * + * @param xml + * @throws UnmarshallException + */ + public void unmarshall(String xml) + throws UnmarshallException + { + try + { + Builder builder = new Builder(); + Document doc = builder.build(xml, "http://something.com/here"); + Element root = doc.getRootElement(); + + entry = new SWORDEntry( ); + entry.unmarshall(root); + + } + catch( ParsingException ex ) + { + throw new UnmarshallException("Unable to parse the XML", ex ); + } + catch( IOException ex ) + { + throw new UnmarshallException("Error acessing the file?", ex); + } + } + + public String toString() + { + return marshall(); + } + +} diff --git a/dspace-sword/dspace-sword-api/src/main/java/org/purl/sword/base/ErrorCodes.java b/dspace-sword/dspace-sword-api/src/main/java/org/purl/sword/base/ErrorCodes.java new file mode 100755 index 0000000000..8efdf77ade --- /dev/null +++ b/dspace-sword/dspace-sword-api/src/main/java/org/purl/sword/base/ErrorCodes.java @@ -0,0 +1,76 @@ +/** + * Copyright (c) 2007, Aberystwyth University + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the + * following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * - Neither the name of the Centre for Advanced Software and + * Intelligent Systems (CASIS) nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +package org.purl.sword.base; + +/** + * Definition of the error codes that will be used in + * the SWORD protocol (in X-Error-Code). + * + * @author Stuart Lewis + */ +public interface ErrorCodes +{ + /** + * ErrorContent - where the supplied format is not the same as that + * identified in the X-Format-Namespace and/or that supported by the + * server + */ + public static final String ERROR_CONTENT = "ErrorContent"; + + /** + * ErrorChecksumMismatch - where the checksum of the file recevied does + * not match the checksum given in the header + */ + public static final String ERROR_CHECKSUM_MISMATCH = "ErrorChecksumMismatch"; + + /** + * ErrorBadRequest - where parameters are not understood + */ + public static final String ERROR_BAD_REQUEST = "ErrorBadRequest"; + + /** + * TargetOwnerUnknown - where the server cannot identify the specified + * TargetOwner + */ + public static final String TARGET_OWNER_UKNOWN = "TargetOwnerUnknown"; + + /** + * MediationNotAllowed - where a client has attempted a mediated deposit, + * but this is not supported by the server + */ + public static final String MEDIATION_NOT_ALLOWED = "MediationNotAllowed"; +} diff --git a/dspace-sword/dspace-sword-api/src/main/java/org/purl/sword/base/HttpHeaders.java b/dspace-sword/dspace-sword-api/src/main/java/org/purl/sword/base/HttpHeaders.java new file mode 100755 index 0000000000..6c7da4a614 --- /dev/null +++ b/dspace-sword/dspace-sword-api/src/main/java/org/purl/sword/base/HttpHeaders.java @@ -0,0 +1,105 @@ +/** + * Copyright (c) 2007, Aberystwyth University + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the + * following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * - Neither the name of the Centre for Advanced Software and + * Intelligent Systems (CASIS) nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +package org.purl.sword.base; + +import javax.servlet.http.HttpServletResponse; + +/** + * Definition of the additional HTTP Header tags that will be used in + * the SWORD protocol. + * + * @author Neil Taylor + * + */ +public interface HttpHeaders +{ + /** + * The HTTP Header label that specifies the MD5 label. + */ + public static final String CONTENT_MD5 = "Content-MD5"; + + /** + * The HTTP Header label that specifies the MD5 label. + */ + public static final String CONTENT_LENGTH = "Content-Length"; + + /** + * The HTTP Header label that specifies the On Behalf Of information. + */ + public static final String X_ON_BEHALF_OF = "X-On-Behalf-Of"; + + /** + * The HTTP Header label that specifies the Format Namespace information. + */ + public static final String X_FORMAT_NAMESPACE = "X-Format-Namespace"; + + /** + * The HTTP Header label that specifies the desired Verbose status. + */ + public static final String X_VERBOSE = "X-Verbose"; + + /** + * The HTTP Header label that specifies the desired NoOp status. + */ + public static final String X_NO_OP = "X-No-Op"; + + /** + * The HTTP Header that specifies the error code information. + */ + public static final String X_ERROR_CODE = "X-Error-Code"; + + /** + * The Slug header. + */ + public static final String SLUG = "Slug"; + + /** + * Submission created + */ + public static final int CREATED = HttpServletResponse.SC_CREATED; + + /** + * Submission accepted. + */ + public static final int ACCEPTED = HttpServletResponse.SC_ACCEPTED; + + /** + * The HTTP Header that specifies the content disposition item. This is + * used by the SWORD profile to identify the name for the deposit. + */ + public static final String CONTENT_DISPOSITION = "Content-Disposition"; +} diff --git a/dspace-sword/dspace-sword-api/src/main/java/org/purl/sword/base/InfoLogger.java b/dspace-sword/dspace-sword-api/src/main/java/org/purl/sword/base/InfoLogger.java new file mode 100755 index 0000000000..3b42656623 --- /dev/null +++ b/dspace-sword/dspace-sword-api/src/main/java/org/purl/sword/base/InfoLogger.java @@ -0,0 +1,106 @@ +/** + * Copyright (c) 2007, Aberystwyth University + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the + * following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * - Neither the name of the Centre for Advanced Software and + * Intelligent Systems (CASIS) nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +package org.purl.sword.base; + +/** + * Author : $Author: nst $ + * Date : $Date: 2007/09/21 15:18:55 $ + * Revision : $Revision: 1.3 $ + * Name : $Name: $ + */ + +/** + * + */ +public class InfoLogger +{ + /** + * Single instance of the InfoLogger that can be used by all + * calling classes. + */ + private static InfoLogger logger = null; + + /** + * Set the default level to ERROR messages only. + */ + private InfoLoggerLevel level = InfoLoggerLevel.ERROR; + + /** + * + */ + public void setLevel( InfoLoggerLevel level ) + { + this.level = level; + } + + /** + * Returns the single instance of this class. If this is the first call, + * the logger is created on this call. + * + * @return The InfoLogger. + */ + public static InfoLogger getLogger() + { + if( logger == null ) + { + logger = new InfoLogger(); + } + return logger; + } + + public void writeError(String message) + { + // always log errors + System.err.println("Error: " + message ); + } + + public void writeInfo(String message) + { + if( level == InfoLoggerLevel.ERROR_WARNING_INFO ) + { + System.err.println("Info: " + message ); + } + } + + public void writeWarning(String message) + { + if( level != InfoLoggerLevel.ERROR ) + { + System.err.println("Warning: " + message ); + } + } +} diff --git a/dspace-sword/dspace-sword-api/src/main/java/org/purl/sword/base/InfoLoggerLevel.java b/dspace-sword/dspace-sword-api/src/main/java/org/purl/sword/base/InfoLoggerLevel.java new file mode 100755 index 0000000000..a581a6712f --- /dev/null +++ b/dspace-sword/dspace-sword-api/src/main/java/org/purl/sword/base/InfoLoggerLevel.java @@ -0,0 +1,49 @@ +/** + * Copyright (c) 2007, Aberystwyth University + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the + * following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * - Neither the name of the Centre for Advanced Software and + * Intelligent Systems (CASIS) nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +package org.purl.sword.base; + +/** + * This enumeration determines the logging level to be used for the InfoLogger class. + * + * @author Neil Taylor (nst@aber.ac.uk) + */ +public enum InfoLoggerLevel +{ + ERROR, + ERROR_WARNING, + ERROR_WARNING_INFO +} diff --git a/dspace-sword/dspace-sword-api/src/main/java/org/purl/sword/base/Namespaces.java b/dspace-sword/dspace-sword-api/src/main/java/org/purl/sword/base/Namespaces.java new file mode 100755 index 0000000000..85caf67cc6 --- /dev/null +++ b/dspace-sword/dspace-sword-api/src/main/java/org/purl/sword/base/Namespaces.java @@ -0,0 +1,70 @@ +/** + * Copyright (c) 2007, Aberystwyth University + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the + * following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * - Neither the name of the Centre for Advanced Software and + * Intelligent Systems (CASIS) nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +package org.purl.sword.base; + +/** + * List of the namespaces that are used by SWORD. + * + * Last updated on: $Date: 2007/09/21 15:18:55 $ + * + * @author Neil Taylor + * @version $Revision: 1.3 $ + * + */ +public interface Namespaces { + + /** + * Atom Publishing Protocol (APP) Namespace. + */ + public static final String NS_APP = "http://purl.org/atom/app#"; + + /** + * ATOM Namespace. + */ + public static final String NS_ATOM = "http://www.w3.org/2005/Atom"; + + /** + * Sword Namespace. + */ + public static final String NS_SWORD = "http://purl.org/net/sword/"; + + /** + * DC Terms Namespace. + */ + public static final String NS_DC_TERMS = "http://purl.org/dc/terms/"; + +} diff --git a/dspace-sword/dspace-sword-api/src/main/java/org/purl/sword/base/SWORDAuthenticationException.java b/dspace-sword/dspace-sword-api/src/main/java/org/purl/sword/base/SWORDAuthenticationException.java new file mode 100755 index 0000000000..df2f30560f --- /dev/null +++ b/dspace-sword/dspace-sword-api/src/main/java/org/purl/sword/base/SWORDAuthenticationException.java @@ -0,0 +1,70 @@ +/** + * Copyright (c) 2007, Aberystwyth University + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the + * following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * - Neither the name of the Centre for Advanced Software and + * Intelligent Systems (CASIS) nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +package org.purl.sword.base; + +/** + * Represents a SWORD exception to be thrown if bad authentication credentials + * are passed to a repository. + * + * @author Stuart Lewis + * @author Neil Taylor + */ +public class SWORDAuthenticationException extends Exception +{ + /** + * Create a new instance and store the specified message and source data. + * + * @param message The message for the exception. + * @param source The original exception that lead to this exception. This + * can be null. + */ + public SWORDAuthenticationException(String message, Exception source) + { + super(message, source); + } + + /** + * Create a new instance and store the specified message. + * + * @param message The message for the exception. + */ + public SWORDAuthenticationException(String message) + { + super(message); + } + +} diff --git a/dspace-sword/dspace-sword-api/src/main/java/org/purl/sword/base/SWORDContentTypeException.java b/dspace-sword/dspace-sword-api/src/main/java/org/purl/sword/base/SWORDContentTypeException.java new file mode 100755 index 0000000000..abf2757b07 --- /dev/null +++ b/dspace-sword/dspace-sword-api/src/main/java/org/purl/sword/base/SWORDContentTypeException.java @@ -0,0 +1,48 @@ +/** + * Copyright (c) 2007, Aberystwyth University + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the + * following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * - Neither the name of the Centre for Advanced Software and + * Intelligent Systems (CASIS) nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +package org.purl.sword.base; + +/** + * Represents a SWORD exception to be thrown if a format supplied in a deposit + * is not accepted by the repository. + * + * @author Stuart Lewis + */ +public class SWORDContentTypeException extends Exception +{ + +} \ No newline at end of file diff --git a/dspace-sword/dspace-sword-api/src/main/java/org/purl/sword/base/SWORDEntry.java b/dspace-sword/dspace-sword-api/src/main/java/org/purl/sword/base/SWORDEntry.java new file mode 100755 index 0000000000..b390d3d5cb --- /dev/null +++ b/dspace-sword/dspace-sword-api/src/main/java/org/purl/sword/base/SWORDEntry.java @@ -0,0 +1,282 @@ +/** + * Copyright (c) 2007, Aberystwyth University + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the + * following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * - Neither the name of the Centre for Advanced Software and + * Intelligent Systems (CASIS) nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +package org.purl.sword.base; + +import nu.xom.Element; +import nu.xom.Elements; + +import org.purl.sword.base.Namespaces; +import org.w3.atom.Entry; + +/** + * Extension of the ATOM Entry class. This adds support for the additional + * SWORD elements. These elements reside inside the ATOM Entry object, + * created in org.w3.atom.Entry class. + * + * @author Neil Taylor + */ +public class SWORDEntry extends Entry +{ + /** + * Specifies whether the document was run in noOp mode, i.e. + * if the document records that no operation was taken for the + * deposit other than to generate a response. + */ + private boolean noOp; + + /** + * Use to supply a verbose description. + */ + private String verboseDescription; + + /** + * Used for a human readable statement about what treatment + * the deposited resource has received. Include either a + * text description or a URI. + */ + private String treatment; + + /** + * Used to record the format namespace. + */ + private String formatNamespace; + + /** + * Used to determine if the noOp value has been set. + */ + private boolean noOpSet; + + + /** + * Create a new instance of the class. + */ + public SWORDEntry() + { + // NO BODY + } + + /** + * Get the current value of NoOp. + * + * @return True if the value is set, false otherwise. + */ + public boolean isNoOp() + { + return noOp; + } + + /** + * Call this method to set noOp. It should be called even by internal + * methods so that the object can determine if the value has been set + * or whether it just holds the default value. + * + * @param noOp + */ + public void setNoOp(boolean noOp) + { + this.noOp = noOp; + this.noOpSet = true; + } + + /** + * Determine if the noOp value has been set. This should be called + * if you want to know whether false for noOp means that it is the + * default value (i.e. no code has set it) or it is a value that + * has been actively set. + * + * @return True if the value has been set. Otherwise, false. + */ + public boolean isNoOpSet() + { + return noOpSet; + } + + /** + * Get the Verbose Description for this entry. + * + * @return The description. + */ + public String getVerboseDescription() + { + return verboseDescription; + } + + /** + * Set the verbose description. + * + * @param verboseDescription The description. + */ + public void setVerboseDescription(String verboseDescription) + { + this.verboseDescription = verboseDescription; + } + + /** + * Get the treatment value. + * + * @return The treatment. + */ + public String getTreatment() + { + return treatment; + } + + /** + * Set the treatment value. + * + * @param treatment The treatment. + */ + public void setTreatment(String treatment) + { + this.treatment = treatment; + } + + /** + * Overrides the marshall method in the parent Entry. This will + * call the parent marshall method and then add the additional + * elements that have been added in this subclass. + */ + public Element marshall() + { + Element entry = super.marshall(); + + if( treatment != null ) + { + Element treatmentElement = new Element("sword:treatment", Namespaces.NS_SWORD); + treatmentElement.appendChild(treatment); + entry.appendChild(treatmentElement); + } + + if( formatNamespace != null ) + { + Element formatNamespaceElement = new Element("sword:formatNamespace", Namespaces.NS_SWORD); + formatNamespaceElement.appendChild(formatNamespace); + entry.appendChild(formatNamespaceElement); + } + + if( verboseDescription != null ) + { + Element verboseDescriptionElement = new Element("sword:verboseDescription", Namespaces.NS_SWORD); + verboseDescriptionElement.appendChild(verboseDescription); + entry.appendChild(verboseDescriptionElement); + } + + if( noOpSet ) + { + Element noOpElement = new Element("sword:noOp", Namespaces.NS_SWORD); + noOpElement.appendChild(Boolean.toString(noOp)); + entry.appendChild(noOpElement); + } + + return entry; + } + + /** + * Overrides the unmarshall method in the parent Entry. This will + * call the parent method to parse the general Atom elements and + * attributes. This method will then parse the remaining sword + * extensions that exist in the element. + * + * @param entry The entry to parse. + * + * @throws UnmarshallException If the entry is not an atom:entry + * or if there is an exception extracting the data. + */ + public void unmarshall(Element entry) + throws UnmarshallException + { + super.unmarshall(entry); + + try + { + // retrieve all of the sub-elements + Elements elements = entry.getChildElements(); + Element element = null; + int length = elements.size(); + + for(int i = 0; i < length; i++ ) + { + element = elements.get(i); + + if( isInstanceOf(element, "treatment", Namespaces.NS_SWORD )) + { + treatment = unmarshallString(element); + } + else if( isInstanceOf(element, "formatNamespace", Namespaces.NS_SWORD )) + { + formatNamespace = unmarshallString(element); + } + else if( isInstanceOf(element, "noOp", Namespaces.NS_SWORD )) + { + setNoOp(unmarshallBoolean(element)); + } + else if( isInstanceOf(element, "verboseDescription", Namespaces.NS_SWORD )) + { + verboseDescription = unmarshallString(element); + } + } // for + + } + catch (UnmarshallException ex) + { + InfoLogger.getLogger().writeError("Error parsing SWORDEntry. " + ex.getMessage()); + throw new UnmarshallException("Error parsing SWORD Entry", ex); + } + + } + + /** + * Get the format namespace. + * + * @return The format namespace. + */ + public String getFormatNamespace() + { + return formatNamespace; + } + + /** + * Set the format namespace. + * + * @param formatNamespace The format namespace. + */ + public void setFormatNamespace(String formatNamespace) + { + this.formatNamespace = formatNamespace; + } + +} diff --git a/dspace-sword/dspace-sword-api/src/main/java/org/purl/sword/base/SWORDException.java b/dspace-sword/dspace-sword-api/src/main/java/org/purl/sword/base/SWORDException.java new file mode 100755 index 0000000000..bf7d0e5d58 --- /dev/null +++ b/dspace-sword/dspace-sword-api/src/main/java/org/purl/sword/base/SWORDException.java @@ -0,0 +1,94 @@ +/** + * Copyright (c) 2007, Aberystwyth University + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the + * following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * - Neither the name of the Centre for Advanced Software and + * Intelligent Systems (CASIS) nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +package org.purl.sword.base; + +/** + * Represents a generic SWORD exception. If this thrown by a repository, + * it would result in a HTTP 500 message being returned to the user. + * + * @author Stuart Lewis + * @author Neil Taylor + */ +public class SWORDException extends Exception +{ + private String errorCode; + + /** + * Create a new instance and store the specified message and source data. + * + * @param message The message for the exception. + * @param source The original exception that lead to this exception. This + * can be null. + */ + public SWORDException(String message, Exception source) + { + super(message, source); + } + + /** + * Create a new instance and store the specified message and source data. + * + * @param message The message for the exception. + * @param source The original exception that lead to this exception. This + * can be null. + * @param errorCode The error code to sed back with the request + */ + public SWORDException(String message, Exception source, String errorCode) + { + super(message, source); + this.errorCode = errorCode; + } + + /** + * Create a new instance and store the specified message. + * + * @param message The message for the exception. + */ + public SWORDException(String message) + { + super(message); + } + + /** + * Get the error code + * + * @return The error code + */ + public String getErrorCode() { + return errorCode; + } +} \ No newline at end of file diff --git a/dspace-sword/dspace-sword-api/src/main/java/org/purl/sword/base/Service.java b/dspace-sword/dspace-sword-api/src/main/java/org/purl/sword/base/Service.java new file mode 100755 index 0000000000..990517987e --- /dev/null +++ b/dspace-sword/dspace-sword-api/src/main/java/org/purl/sword/base/Service.java @@ -0,0 +1,396 @@ +/** + * Copyright (c) 2007, Aberystwyth University + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the + * following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * - Neither the name of the Centre for Advanced Software and + * Intelligent Systems (CASIS) nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +package org.purl.sword.base; + +/** + * Author : $Author: nst $ + * Date : $Date: 2007/09/21 15:18:54 $ + * Revision : $Revision: 1.4 $ + * Name : $Name: $ + */ + +import java.util.ArrayList; +import java.util.List; +import java.util.Iterator; + +import org.purl.sword.base.Namespaces; + +import nu.xom.Element; +import nu.xom.Elements; + +/** + * Represents an Atom Publishing Protocol Service element, with + * SWORD extensions. + * + * @author Neil Taylor + */ +public class Service extends XmlElement implements SwordElementInterface +{ + /** + * The local name for the element. + */ + private static final String ELEMENT_NAME = "service"; + + /** + * The service compliance level. + */ + private ServiceLevel complianceLevel; + + /** + * The noOp value. + */ + private boolean noOp; + + /** + * Flag to record if the noOp value has been set by a user of the class. + */ + private boolean isNoOp; + + /** + * The verbose value. + */ + private boolean verbose; + + /** + * Flag to record if the verbose value has been set by a user of the class. + */ + private boolean isVerbose; + + /** + * List of Workspaces. + */ + private List workspaces; + + /** + * Create a new instance. + */ + public Service() + { + super("app"); + + isVerbose = false; + isNoOp = false; + workspaces = new ArrayList(); + complianceLevel = ServiceLevel.UNDEFINED; + } + + /** + * Create a new instance. + * + * @param complianceLevel The service compliance level. + */ + public Service( ServiceLevel complianceLevel) + { + this(); + this.complianceLevel = complianceLevel; + } + + /** + * Create a new instance with the specified compliance level, noOp and + * verbose values. + * + * @param complianceLevel The service compliance level. + * @param noOp The noOp. + * @param verbose The verbose element. + */ + public Service( ServiceLevel complianceLevel, boolean noOp, boolean verbose ) + { + this(); + this.complianceLevel = complianceLevel; + setNoOp(noOp); + setVerbose(verbose); + } + + /** + * Get the service compliance level. + * + * @return The compliance level. + */ + public ServiceLevel getComplianceLevel() + { + return complianceLevel; + } + + /** + * Set the service compliance level. + * + * @param The compliance level. + */ + public void setComplianceLevel(ServiceLevel complianceLevel) + { + this.complianceLevel = complianceLevel; + } + + /** + * Get the NoOp value. + * + * @return The value. + */ + public boolean isNoOp() + { + return noOp; + } + + /** + * Set the NoOp value. + * + * @param noOp The value. + */ + public void setNoOp(boolean noOp) + { + this.noOp = noOp; + isNoOp = true; + } + + /** + * Determine if the NoOp value has been set. This should be called to + * check if an item has been programatically set and does not have a + * default value. + * + * @return True if it has been set programmatically. Otherwise, false. + */ + public boolean isNoOpSet() + { + return isNoOp; + } + + /** + * Get the Verbose setting. + * + * @return The value. + */ + public boolean isVerbose() + { + return verbose; + } + + /** + * Set the Verbose value. + * + * @param verbose The value. + */ + public void setVerbose(boolean verbose) + { + this.verbose = verbose; + isVerbose = true; + } + + /** + * Determine if the Verbose value has been set. This should be called to + * check if an item has been programatically set and does not have a + * default value. + * + * @return True if it has been set programmatically. Otherwise, false. + */ + public boolean isVerboseSet() + { + return isVerbose; + } + + /** + * Get an Iterator over the workspaces. + * + * @return The workspace. + */ + public Iterator getWorkspaces() + { + return workspaces.iterator(); + } + + /** + * Get a List of workspaces + * + * @return The workspaces in a List + */ + public List getWorkspacesList() + { + return workspaces; + } + + /** + * Add a workspace. + * + * @param workspace The workspace. + */ + public void addWorkspace(Workspace workspace) + { + this.workspaces.add(workspace); + } + + /** + * Clear the list of workspaces. + */ + public void clearWorkspaces() + { + this.workspaces.clear(); + } + + /** + * Marshall the data in this object to an Element object. + * + * @return A XOM Element that holds the data for this Content element. + */ + public Element marshall( ) + { + Element service = new Element(ELEMENT_NAME, Namespaces.NS_APP); + service.addNamespaceDeclaration("atom", Namespaces.NS_ATOM); + service.addNamespaceDeclaration("dcterms", Namespaces.NS_DC_TERMS); + service.addNamespaceDeclaration("sword", Namespaces.NS_SWORD); + + + if( complianceLevel != ServiceLevel.UNDEFINED ) + { + //System.out.println("The compliance level is: " + complianceLevel ); + Element compliance = new Element("sword:level", Namespaces.NS_SWORD); + compliance.appendChild(Integer.toString(complianceLevel.number())); + service.appendChild(compliance); + } + + if( isVerboseSet() ) + { + Element verboseElement = new Element("sword:verbose", Namespaces.NS_SWORD); + verboseElement.appendChild(Boolean.toString(verbose)); + service.appendChild(verboseElement); + } + + if( isNoOpSet() ) + { + Element noOpElement = new Element("sword:noOp", Namespaces.NS_SWORD); + noOpElement.appendChild(Boolean.toString(noOp)); + service.appendChild(noOpElement); + } + + for( Workspace item : workspaces ) + { + service.appendChild(item.marshall()); + } + + return service; + } + + + /** + * Get a service level that corresponds to the specified value. Used + * during the unmarshall process. + * + * @param level The integer version of a service level. + * + * @return If the parameter matches one of the defined levels, + * a ServiceLevel of ONE or ZERO is returned. Otherwise, + * ServiceLevel.UNDEFINED is returned. + */ + private ServiceLevel getServiceLevel( int level ) + { + ServiceLevel theLevel = ServiceLevel.UNDEFINED; + + switch( level ) + { + case 0: + theLevel = ServiceLevel.ZERO; + break; + + case 1: + theLevel = ServiceLevel.ONE; + break; + + default: + // FIXME - should this default to ZERO instead? + theLevel = ServiceLevel.UNDEFINED; + InfoLogger.getLogger().writeError("Invalid value for sword:level"); + break; + } + + return theLevel; + + } + + /** + * Unmarshall the content element into the data in this object. + * + * @throws UnmarshallException If the element does not contain a + * content element or if there are problems + * accessing the data. + */ + public void unmarshall( Element service ) + throws UnmarshallException + { + if( ! isInstanceOf(service, "service", Namespaces.NS_APP)) + { + throw new UnmarshallException( "Not an app:service element" ); + } + + try + { + workspaces.clear(); + + // retrieve all of the sub-elements + Elements elements = service.getChildElements(); + Element element = null; + int length = elements.size(); + + for(int i = 0; i < length; i++ ) + { + element = elements.get(i); + + if( isInstanceOf(element, "level", Namespaces.NS_SWORD ) ) + { + int level = unmarshallInteger(element); + complianceLevel = getServiceLevel(level); + } + else if( isInstanceOf(element, "verbose", Namespaces.NS_SWORD)) + { + setVerbose(unmarshallBoolean(element)); + } + else if( isInstanceOf(element, "noOp", Namespaces.NS_SWORD)) + { + setNoOp(unmarshallBoolean(element)); + } + else if( isInstanceOf(element, "workspace", Namespaces.NS_APP )) + { + Workspace workspace = new Workspace( ); + workspace.unmarshall(element); + workspaces.add(workspace); + } + } + } + catch( Exception ex ) + { + InfoLogger.getLogger().writeError("Unable to parse an element in Service: " + ex.getMessage()); + throw new UnmarshallException("Unable to parse element in Service", ex); + } + } +} \ No newline at end of file diff --git a/dspace-sword/dspace-sword-api/src/main/java/org/purl/sword/base/ServiceDocument.java b/dspace-sword/dspace-sword-api/src/main/java/org/purl/sword/base/ServiceDocument.java new file mode 100755 index 0000000000..7ae35db27e --- /dev/null +++ b/dspace-sword/dspace-sword-api/src/main/java/org/purl/sword/base/ServiceDocument.java @@ -0,0 +1,308 @@ +/** + * Copyright (c) 2007, Aberystwyth University + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the + * following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * - Neither the name of the Centre for Advanced Software and + * Intelligent Systems (CASIS) nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +package org.purl.sword.base; + +/** + * Author : $Author: nst $ + * Date : $Date: 2007/09/21 15:18:55 $ + * Revision : $Revision: 1.2 $ + * Name : $Name: $ + */ + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.util.Iterator; + +import nu.xom.Builder; +import nu.xom.Document; +import nu.xom.Element; +import nu.xom.ParsingException; +import nu.xom.Serializer; + + +/** + * A representation of a SWORD Service Document. + * + * http://www.ukoln.ac.uk/repositories/digirep/index/SWORD_APP_Profile_0.5 + * + * @author Stuart Lewis + * @author Neil Taylor + */ +public class ServiceDocument +{ + /** + * The Service object that is held by this object. + */ + private Service service; + + /** + * Create a new instance and set the initial service level to Zero. + */ + public ServiceDocument() + { + this(ServiceLevel.ZERO); + } + + /** + * Create a new instance and set the specified service level. + * + * @param complianceLevel The service compliance level. + */ + public ServiceDocument(ServiceLevel complianceLevel) + { + service = new Service(complianceLevel); + } + + /** + * Create a new instance and store the specified Service document. + * + * @param service The Service object. + */ + public ServiceDocument(Service service) + { + this.service = service; + } + + /** + * Instantiate a ServiceDocument + * + * @param complianceLevel The compliance level of this implementation + * @param noOp Whether or not the NOOP option is available + * @param verbose Whether or not the verbose option is available + * @param workspaceTitle The name of the workspace title + * @param workspaceCollections A Collection of workspaces + * + * @deprecated Please use the other constructors. + */ + public ServiceDocument(ServiceLevel complianceLevel, + boolean noOp, + boolean verbose, + String workspaceTitle, + Collection workspaceCollections) { + + service = new Service( complianceLevel, noOp, verbose ); + Workspace workspace = new Workspace( workspaceTitle ); + workspace.addCollection(workspaceCollections); // FIXME - not quite right? + service.addWorkspace(workspace); + } + + /** + * Get the compliance level from this Service Document + * + * @return The compliance level + * + * @deprecated Please access the compliance level directly from the service. + */ + public ServiceLevel getComplianceLevel() { + // Return the compliance level + return service.getComplianceLevel(); + } + + /** + * Returns a boolean depending on whether or not the Service Document + * says the server supports the NOOP option + * + * @return The NOOP option status + * + * @deprecated Please access the value directly from the service. + */ + public boolean supportsNoOp() { + // Return the NOOP option status + return service.isNoOp(); + } + + /** + * + * @param noOp + * + * @deprecated Please access the value directly from the service. + */ + public void setNoOp(boolean noOp) + { + service.setNoOp(noOp); + } + + /** + * Returns a boolean depending on whether or not the Service Document + * says the server supports the verbose option + * + * @return The verbose option status + * + * @deprecated Please access the value directly from the service. + */ + public boolean supportsVerbose() { + // Return the verbose option status + return service.isVerbose(); + } + + /** + * + * @param verbose + * + * @deprecated Please access the value directly from the service. + */ + public void setVerbose(boolean verbose) + { + service.setVerbose(verbose); + } + + /** + * Returns the Collectinos in the workspace described by the Service Document + * + * @return The workspaces + * + * @deprecated Please access the value directly from the service. + */ + public Iterator getWorkspaceCollections() { + // Return the collections + return null; // FIXME service.getWorkspaces().collectionIterator(); + } + + /** + * @deprecated Please access the value directly from the service. + */ + public Iterator getWorkspaces() + { + return service.getWorkspaces(); + } + + /** + * + * @param workspace + * + * @deprecated Please access the value directly from the service. + */ + public void addWorkspace(Workspace workspace) + { + service.addWorkspace(workspace); + } + + /** + * Set the service object associated with this document. + * + * @param service The new Service object. + */ + public void setService(Service service) + { + this.service = service; + } + + /** + * Retrieve the Service object associated with this document. + * + * @return The Service object. + */ + public Service getService() + { + return service; + } + + /** + * Return the Service Document in it's XML form. + * + * @return The ServiceDocument + */ + public String toString() + { + return marshall(); + } + + /** + * Marshall the data in the Service element and generate a String representation. + * The returned string is UTF-8 format. + * + * @return A string of XML, or null if there was an error + * marshalling the data. + */ + public String marshall( ) + { + try + { + ByteArrayOutputStream stream = new ByteArrayOutputStream(); + Serializer serializer = new Serializer(stream, "UTF-8"); + serializer.setIndent(3); + serializer.setMaxLength(64); + + Document doc = new Document(service.marshall()); + serializer.write(doc); + + return stream.toString(); + } + catch (IOException ex) { + System.err.println(ex); + } + + return null; + } + + /** + * Convert the specified XML string into a set of objects + * used within the service. A new Service object will be + * created and stored. This will dispose of any previous + * Service object associated with this object. + * + * @param xml The XML string. + * @throws UnmarshallException If there was a problem unmarshalling the + * data. This might be as a result of an + * error in parsing the XML string, + * extracting information. + */ + public void unmarshall( String xml ) + throws UnmarshallException + { + // + try + { + Builder builder = new Builder(); + Document doc = builder.build(xml, "http://something.com/here"); + Element root = doc.getRootElement(); + + service = new Service( ); + service.unmarshall(root); + + } + catch( ParsingException ex ) + { + throw new UnmarshallException("Unable to parse the XML", ex ); + } + catch( IOException ex ) + { + throw new UnmarshallException("Error acessing the file?", ex); + + } + } +} \ No newline at end of file diff --git a/dspace-sword/dspace-sword-api/src/main/java/org/purl/sword/base/ServiceDocumentRequest.java b/dspace-sword/dspace-sword-api/src/main/java/org/purl/sword/base/ServiceDocumentRequest.java new file mode 100755 index 0000000000..e85d372482 --- /dev/null +++ b/dspace-sword/dspace-sword-api/src/main/java/org/purl/sword/base/ServiceDocumentRequest.java @@ -0,0 +1,115 @@ +/** + * Copyright (c) 2007, Aberystwyth University + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the + * following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * - Neither the name of the Centre for Advanced Software and + * Intelligent Systems (CASIS) nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +package org.purl.sword.base; + +/** + * Represents a ServiceDocumentRequest. + * + * @author Stuart Lewis + * + */ +public class ServiceDocumentRequest +{ + + private String username; + + private String password; + + private String onBehalfOf; + + private String IPAddress; + + /** + * @return the authenticatedUserName + */ + public String getUsername() { + return username; + } + + /** + * @param authenticatedUserName the authenticatedUserName to set + */ + public void setUsername(String username) { + this.username = username; + } + + /** + * @return the authenticatedUserPassword + */ + public String getPassword() { + return password; + } + + /** + * @param password the password to set + */ + public void setPassword(String password) { + this.password = password; + } + + /** + * @return the onBehalfOf + */ + public String getOnBehalfOf() { + return onBehalfOf; + } + + /** + * @param onBehalfOf the onBehalfOf to set + */ + public void setOnBehalfOf(String onBehalfOf) { + this.onBehalfOf = onBehalfOf; + } + + /** + * Get the IP address of the user + * + * @return the the IP address + */ + public String getIPAddress() { + return IPAddress; + } + + /** + * Set the IP address of the user + * + * @param String the IP address + */ + public void setIPAddress(String IPAddress) { + this.IPAddress = IPAddress; + } +} diff --git a/dspace-sword/dspace-sword-api/src/main/java/org/purl/sword/base/ServiceLevel.java b/dspace-sword/dspace-sword-api/src/main/java/org/purl/sword/base/ServiceLevel.java new file mode 100755 index 0000000000..5d847d72c7 --- /dev/null +++ b/dspace-sword/dspace-sword-api/src/main/java/org/purl/sword/base/ServiceLevel.java @@ -0,0 +1,79 @@ +/** + * Copyright (c) 2007, Aberystwyth University + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the + * following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * - Neither the name of the Centre for Advanced Software and + * Intelligent Systems (CASIS) nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +package org.purl.sword.base; + +/** + * Author : $Author: nst $ + * Date : $Date: 2007/09/21 15:18:53 $ + * Revision : $Revision: 1.2 $ + * Name : $Name: $ + */ + +/** + * Represents the SWORD Service Level. + * + * @author Neil Taylor + * + */ +public enum ServiceLevel +{ + ZERO (0), + ONE (1), + UNDEFINED (-1); + + /** + * Holds the number associated with the ServiceLevel object. + */ + private final int number; + + /** + * Create a new ServiceLevel with the specified number. + * + * @param num The number to be associated with the service level. + */ + private ServiceLevel(int num) + { + this.number = num; + } + + /** + * Get the number for the ServiceLevel object. + * + * @return The number. + */ + public int number() { return this.number; } +} diff --git a/dspace-sword/dspace-sword-api/src/main/java/org/purl/sword/base/SwordElementInterface.java b/dspace-sword/dspace-sword-api/src/main/java/org/purl/sword/base/SwordElementInterface.java new file mode 100755 index 0000000000..30e9797ae3 --- /dev/null +++ b/dspace-sword/dspace-sword-api/src/main/java/org/purl/sword/base/SwordElementInterface.java @@ -0,0 +1,73 @@ +/** + * Copyright (c) 2007, Aberystwyth University + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the + * following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * - Neither the name of the Centre for Advanced Software and + * Intelligent Systems (CASIS) nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +package org.purl.sword.base; + +/** + * Author : $Author: nst $ + * Date : $Date: 2007/09/21 15:18:53 $ + * Revision : $Revision: 1.2 $ + * Name : $Name: $ + */ + +import nu.xom.Element; + +/** + * Common methods that should be supported by all classes that + * represent data in the SWORD api. + * + * @author Neil Taylor + */ +public interface SwordElementInterface +{ + /** + * Marshall the data in the object to the XOM Element. + * + * @return The Element. + */ + public Element marshall( ); + + /** + * Unmarshall the data in the specified element and store it + * in the object. + * + * @param element The data to unmarshall. + * @throws UnmarshallException If the element is not of the + * correct type, or if there is an error unmarshalling the data. + */ + public void unmarshall( Element element ) + throws UnmarshallException; +} diff --git a/dspace-sword/dspace-sword-api/src/main/java/org/purl/sword/base/UnmarshallException.java b/dspace-sword/dspace-sword-api/src/main/java/org/purl/sword/base/UnmarshallException.java new file mode 100755 index 0000000000..c46e79ce7c --- /dev/null +++ b/dspace-sword/dspace-sword-api/src/main/java/org/purl/sword/base/UnmarshallException.java @@ -0,0 +1,76 @@ +/** + * Copyright (c) 2007, Aberystwyth University + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the + * following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * - Neither the name of the Centre for Advanced Software and + * Intelligent Systems (CASIS) nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +package org.purl.sword.base; + +/** + * Author : $Author: nst $ + * Date : $Date: 2007/09/21 15:18:56 $ + * Revision : $Revision: 1.2 $ + * Name : $Name: $ + */ + +/** + * Represents information about an exception that is generated during + * the Unmarshall process. + * + * @author Neil Taylor + */ +public class UnmarshallException extends Exception +{ + /** + * Create a new instance and store the specified message and source data. + * + * @param message The message for the exception. + * @param source The original exception that lead to this exception. This + * can be null. + */ + public UnmarshallException(String message, Exception source) + { + super(message, source); + } + + /** + * Create a new instance and store the specified message. + * + * @param message The message for the exception. + */ + public UnmarshallException(String message) + { + super(message); + } + +} diff --git a/dspace-sword/dspace-sword-api/src/main/java/org/purl/sword/base/Workspace.java b/dspace-sword/dspace-sword-api/src/main/java/org/purl/sword/base/Workspace.java new file mode 100755 index 0000000000..865959f8ca --- /dev/null +++ b/dspace-sword/dspace-sword-api/src/main/java/org/purl/sword/base/Workspace.java @@ -0,0 +1,230 @@ +/** + * Copyright (c) 2007, Aberystwyth University + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the + * following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * - Neither the name of the Centre for Advanced Software and + * Intelligent Systems (CASIS) nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +package org.purl.sword.base; + +/** + * Author : $Author: nst $ + * Date : $Date: 2007/09/21 15:18:53 $ + * Revision : $Revision: 1.4 $ + * Name : $Name: $ + */ + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import org.purl.sword.base.Namespaces; +import org.w3.atom.ContentType; +import org.w3.atom.Title; + +import nu.xom.Element; +import nu.xom.Elements; + +/** + * Represents an Atom Publishing Protocol Workspace element. + * + * @author Neil Taylor + */ +public class Workspace extends XmlElement implements SwordElementInterface +{ + /** + * The element name that is used in the textual representatin of the XML data. + */ + public static final String ELEMENT_NAME = "workspace"; + + /** + * The title for the workspace. + */ + private Title title; + + /** + * A list of collections associated with this workspace. + */ + private List collections; + + /** + * Create a new instance of the workspace, with no title. + */ + public Workspace( ) + { + this(null); + } + + /** + * Create a new instance of the workspace with the specified title. + * + * @param title The title. + */ + public Workspace( String title ) + { + super("app", ELEMENT_NAME); + setTitle(title); + collections = new ArrayList(); + } + + /** + * Set the title. The type for the title will be set to + * ContentType.TEXT + * + * @param title The title. + */ + public void setTitle( String title ) + { + if( this.title == null) + { + this.title = new Title(); + } + this.title.setContent(title); + this.title.setType(ContentType.TEXT); + + } + + /** + * Get the content of the Title element. + * + * @return The title. + */ + public String getTitle( ) + { + if( title == null ) + { + return null; + } + + return title.getContent(); + } + + /** + * Add a collection to the Workspace. + * + * @param collection The collection. + */ + public void addCollection( Collection collection ) + { + collections.add(collection); + } + + /** + * Get an Iterator over the collections. + * + * @return An iterator. + */ + public Iterator collectionIterator( ) + { + return collections.iterator(); + } + + /** + * Get a list of the collections + * + * @ return A list. + */ + public List getCollections( ) + { + return collections; + } + + /** + * Marshall the data in this element to an Element. + * + * @return An element that contains the data in this object. + */ + public Element marshall( ) + { + // convert data into XOM elements and return the 'root', i.e. the one + // that represents the collection. + Element workspace = new Element(ELEMENT_NAME, Namespaces.NS_APP); + + workspace.appendChild(title.marshall()); + + for( Collection item : collections ) + { + workspace.appendChild(item.marshall()); + } + + return workspace; + } + + /** + * Unmarshall the workspace element into the data in this object. + * + * @throws UnmarshallException If the element does not contain a + * workspace element or if there are problems + * accessing the data. + */ + public void unmarshall( Element workspace ) + throws UnmarshallException + { + if( ! isInstanceOf(workspace, ELEMENT_NAME, Namespaces.NS_APP)) + { + throw new UnmarshallException( "Not an app:workspace element" ); + } + + try + { + collections.clear(); + + // retrieve all of the sub-elements + Elements elements = workspace.getChildElements(); + Element element = null; + int length = elements.size(); + + for(int i = 0; i < length; i++ ) + { + element = elements.get(i); + // FIXME - atom assumes that it has been defined. WHAT DID I MEAN??? + if( isInstanceOf(element, "title", Namespaces.NS_ATOM ) ) + { + title = new Title(); + title.unmarshall(element); + } + else if( isInstanceOf(element, "collection", Namespaces.NS_APP )) + { + Collection collection = new Collection( ); + collection.unmarshall(element); + collections.add(collection); + } + } + } + catch( Exception ex ) + { + InfoLogger.getLogger().writeError("Unable to parse an element in workspace: " + ex.getMessage()); + throw new UnmarshallException("Unable to parse element in workspace.", ex); + } + } + +} diff --git a/dspace-sword/dspace-sword-api/src/main/java/org/purl/sword/base/XmlElement.java b/dspace-sword/dspace-sword-api/src/main/java/org/purl/sword/base/XmlElement.java new file mode 100755 index 0000000000..40bfac0435 --- /dev/null +++ b/dspace-sword/dspace-sword-api/src/main/java/org/purl/sword/base/XmlElement.java @@ -0,0 +1,309 @@ +/** + * Copyright (c) 2007, Aberystwyth University + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the + * following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * - Neither the name of the Centre for Advanced Software and + * Intelligent Systems (CASIS) nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +package org.purl.sword.base; + +/** + * Author : $Author: nst $ + * Date : $Date: 2007/09/21 15:18:55 $ + * Revision : $Revision: 1.3 $ + * Name : $Name: $ + */ + +import nu.xom.Element; +import nu.xom.Node; + +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.GregorianCalendar; + +/** + * Parent class for all classes that represent an XML element. This provides + * some common utility methods that are useful for marshalling and + * unmarshalling data. + * + * @author Neil Taylor + */ +public class XmlElement +{ + /** + * The name to use for the prefix. + */ + protected String prefix; + + /** + * + */ + protected String localName; + + /** + * + * @param localName + */ + public XmlElement(String localName) + { + this.localName = localName; + } + + /** + * Create a new instance. + */ + public XmlElement(String prefix, String localName) + { + this.prefix = prefix; + this.localName = localName; + } + + + /** + * The Date format that is used to parse dates to and from the ISO format + * in the XML data. + */ + protected static final String DATE_FORMAT = "yyyy-MM-dd'T'HH:mm:ss'Z'"; + + /** + * Extract a boolean value from the specified element. The boolean value + * is represented as the string 'true' or 'false' as the only child + * of the specified element. + * + * @param element The element that contains the boolean value. + * @return True or false, based on the string in the element's content. + * @throws UnmarshallException If the element does not contain a single child, or if + * the child does not contain the value 'true' or 'false'. + */ + protected boolean unmarshallBoolean( Element element ) + throws UnmarshallException + { + if( element.getChildCount() != 1 ) + { + throw new UnmarshallException("Missing Boolean Value", null); + } + + // ok to get the single child element. This should be a text element. + try + { + Node child = element.getChild(0); + String value = child.getValue(); + if( "true".equals(value) ) + { + return true; + } + else if( "false".equals(value)) + { + return false; + } + else + { + throw new UnmarshallException("Illegal Value"); + } + } + catch( IndexOutOfBoundsException ex ) + { + throw new UnmarshallException("Error accessing Boolean element", ex); + } + + } + + /** + * Extract a string value from the specified element. The value + * is the only child of the specified element. + * + * @param element The element that contains the string value. + * @return The string. + * @throws UnmarshallException If the element does not contain a single child. + */ + protected String unmarshallString( Element element ) + throws UnmarshallException + { + if( element.getChildCount() != 1 ) + { + throw new UnmarshallException("Missing String Value", null); + } + + // ok to get the single child element. This should be a text element. + try + { + Node child = element.getChild(0); + return child.getValue(); + } + catch( IndexOutOfBoundsException ex ) + { + throw new UnmarshallException("Error accessing Boolean element", ex); + } + + } + + /** + * Extract an integer value from the specified element. The integer value + * is represented as a string in the only child + * of the specified element. + * + * @param element The element that contains the integer. + * @return The integer. + * @throws UnmarshallException If the element does not contain a single child, or if + * the child does not contain the valid integer. + */ + protected int unmarshallInteger( Element element ) + throws UnmarshallException + { + if( element.getChildCount() != 1 ) + { + throw new UnmarshallException("Missing Integer Value", null); + } + + // ok to get the single child element. This should be a text element. + try + { + Node child = element.getChild(0); + return Integer.parseInt( child.getValue() ); + } + catch( IndexOutOfBoundsException ex ) + { + throw new UnmarshallException("Error accessing Boolean element", ex); + } + catch( NumberFormatException nfex ) + { + throw new UnmarshallException("Error fomratting the number", nfex); + } + } + + /** + * Extract an date value from the specified element. The date value + * is represented as a string in the only child of the element. + * + * @param element The element that contains the date. + * @return The date. + * @throws UnmarshallException If the element does not contain a single child, or if + * the child does not contain the valid date. + */ + protected Date unmarshallDate(Element element) + throws UnmarshallException + { + try + { + String content = unmarshallString(element); + return stringToDate(content); + } + catch( UnmarshallException ue ) + { + throw new UnmarshallException("Error accessing the date.", ue); + } + catch (ParseException pe) + { + throw new UnmarshallException("Error accessing the date.", pe); + } + } + + /** + * Convert the date to a string. If the date is null, + * the result will result to a default date of 1st January 1970. + * FIXME - is this sensible? + * + * @param date The Date object. + * @return The Date, expressed as a string in the format + * yyyy-MM-ddTHH:mm:ssZ. + */ + protected String dateToString(Date date) + { + if( date == null ) + { + GregorianCalendar cal = new GregorianCalendar(1970, 0, 1, 0, 0, 0); + date = cal.getTime(); + } + SimpleDateFormat formatter = new SimpleDateFormat(DATE_FORMAT); + return formatter.format(date); + } + + /** + * Convert the string into a Date object. + * + * @param date The date, represented as a string. + * @return A Date. + * @throws ParseException If the string does not match the format + * of yyyy-MM-ddTHH:mm:ssZ. + */ + protected Date stringToDate(String date) + throws ParseException + { + SimpleDateFormat formatter = new SimpleDateFormat(DATE_FORMAT); + return formatter.parse(date); + } + + /** + * Determines if the specified element is an instance of the element name. If + * you are checking the name title in the ATOM namespace, then the local name + * should be 'title' and the namespaceURI is the URI for the ATOM namespace. + * + * @param element The specified element. + * @param localName The local name for the element. + * @param namespaceURI The namespace for the element. + * @return True if the element matches the localname and namespace. Otherwise, false. + */ + protected boolean isInstanceOf(Element element, String localName, String namespaceURI ) + { + return (localName.equals(element.getLocalName()) && + namespaceURI.equals(element.getNamespaceURI()) ); + } + + /** + * Retrieve the qualified name for this object. This uses the + * prefix and local name stored in this object. + * + * @return A string of the format 'prefix:localName' + */ + public String getQualifiedName() + { + return getQualifiedName(localName); + } + + /** + * Retrieve the qualified name. The prefix for this object is prepended + * onto the specified local name. + * + * @param name the specified local name. + * @return A string of the format 'prefix:name' + */ + public String getQualifiedName(String name) + { + String p = prefix; + if( p != null ) + { + p += ":"; + } + return p + name; + } +} diff --git a/dspace-sword/dspace-sword-api/src/main/java/org/purl/sword/server/DepositServlet.java b/dspace-sword/dspace-sword-api/src/main/java/org/purl/sword/server/DepositServlet.java new file mode 100644 index 0000000000..28d75af53c --- /dev/null +++ b/dspace-sword/dspace-sword-api/src/main/java/org/purl/sword/server/DepositServlet.java @@ -0,0 +1,283 @@ +package org.purl.sword.server; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.PrintWriter; +import java.security.NoSuchAlgorithmException; +import java.util.Date; +import java.util.StringTokenizer; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.apache.commons.codec.binary.Base64; +import org.apache.log4j.Logger; +import org.purl.sword.base.ChecksumUtils; +import org.purl.sword.base.Deposit; +import org.purl.sword.base.DepositResponse; +import org.purl.sword.base.HttpHeaders; +import org.purl.sword.base.SWORDAuthenticationException; +import org.purl.sword.base.SWORDContentTypeException; +import org.purl.sword.base.SWORDException; + +public class DepositServlet extends HttpServlet { + + private SWORDServer myRepository; + + private String authN; + + private String tempDirectory; + + private static int counter = 0; + + private static Logger log = Logger.getLogger(DepositServlet.class); + + public void init() { + // Instantiate the correct SWORD Server class + String className = getServletContext().getInitParameter("server-class"); + if (className == null) { + log.fatal("Unable to read value of 'sword-server-class' from Servlet context"); + } else { + try { + myRepository = (SWORDServer)Class.forName(className).newInstance(); + log.info("Using " + className + " as the SWORDServer"); + } catch (Exception e) { + log.fatal("Unable to instantiate class from 'sword-server-class': " + className); + } + } + + authN = getServletContext().getInitParameter("authentication-method"); + if ((authN == null) || (authN.equals(""))) { + authN = "None"; + } + log.info("Authentication type set to: " + authN); + + tempDirectory = getServletContext().getInitParameter("upload-temp-directory"); + if ((tempDirectory == null) || (tempDirectory.equals(""))) { + tempDirectory = System.getProperty("java.io.tmpdir"); + } + File tempDir = new File(tempDirectory); + log.info("Upload temporary directory set to: " + tempDir); + if (!tempDir.isDirectory()) { + log.fatal("Upload temporary directory is not a directory: " + tempDir); + } + if (!tempDir.canWrite()) { + log.fatal("Upload temporary directory cannot be written to: " + tempDir); + } + } + + protected void doGet(HttpServletRequest request, + HttpServletResponse response) throws ServletException, IOException + { + // Send a '501 Not Implemented' + response.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED); + } + + protected void doPost(HttpServletRequest request, + HttpServletResponse response) throws ServletException, IOException + { + // Create the Deposit request + Deposit d = new Deposit(); + Date date = new Date(); + log.debug("Starting deposit processing at " + date.toString() + " by " + request.getRemoteAddr()); + + // Are there any authentication details? + String usernamePassword = getUsernamePassword(request); + if ((usernamePassword != null) && (!usernamePassword.equals(""))) { + int p = usernamePassword.indexOf(":"); + if (p != -1) { + d.setUsername(usernamePassword.substring(0, p)); + d.setPassword(usernamePassword.substring(p+1)); + } + } else if (authenticateWithBasic()) { + String s = "Basic realm=\"SWORD\""; + response.setHeader("WWW-Authenticate", s); + response.setStatus(401); + return; + } + + // Do the processing + try { + // Write the file to the temp directory + // TODO: Improve the filename creation + String filename = tempDirectory + "SWORD-" + + request.getRemoteAddr() + "-" + counter++; + InputStream inputStream = request.getInputStream(); + OutputStream outputStream = new FileOutputStream(filename); + int data; + while((data = inputStream.read()) != -1) + { + outputStream.write(data); + } + inputStream.close(); + outputStream.close(); + + // Check the MD5 hash + String receivedMD5 = ChecksumUtils.generateMD5(filename); + log.debug("Received filechecksum: " + receivedMD5); + d.setMd5(receivedMD5); + String md5 = request.getHeader("Content-MD5"); + log.debug("Received file checksum header: " + md5); + if ((md5 != null) && (!md5.equals(receivedMD5))) { + response.sendError(HttpServletResponse.SC_PRECONDITION_FAILED); + response.setHeader(HttpHeaders.X_ERROR_CODE, "ErrorChecksumMismatch"); + log.debug("Bad MD5 for file. Aborting with appropriate error message"); + } else { + // Set the file + File f = new File(filename); + FileInputStream fis = new FileInputStream(f); + d.setFile(fis); + + // Set the X-On-Behalf-Of header + d.setOnBehalfOf(request.getHeader(HttpHeaders.X_ON_BEHALF_OF.toString())); + + // Set the X-Format-Namespace header + d.setFormatNamespace(request.getHeader(HttpHeaders.X_FORMAT_NAMESPACE)); + + // Set the X-No-Op header + String noop = request.getHeader(HttpHeaders.X_NO_OP); + if ((noop != null) && (noop.equals("true"))) { + d.setNoOp(true); + } else { + d.setNoOp(false); + } + + // Set the X-Verbose header + String verbose = request.getHeader(HttpHeaders.X_VERBOSE); + if ((verbose != null) && (verbose.equals("true"))) { + d.setVerbose(true); + } else { + d.setVerbose(false); + } + + // Set the slug + String slug = request.getHeader(HttpHeaders.SLUG); + if (slug != null) { + d.setSlug(slug); + } + + // Set the content disposition + d.setFilename(request.getHeader(HttpHeaders.CONTENT_DISPOSITION)); + + // Set the IP address + d.setIPAddress(request.getRemoteAddr()); + + // Set the deposit location + d.setLocation(getUrl(request)); + + // Set the content type + d.setContentType(request.getContentType()); + + // Set the content length + String cl = request.getHeader(HttpHeaders.CONTENT_LENGTH); + if ((cl != null) && (!cl.equals(""))) { + d.setContentLength(Integer.parseInt(cl)); + } + + // Get the DepositResponse + DepositResponse dr = myRepository.doDeposit(d); + + // Print out the Deposit Response + response.setStatus(dr.getHttpResponse()); + // response.setContentType("application/atomserv+xml"); + response.setContentType("application/xml"); + PrintWriter out = response.getWriter(); + out.write(dr.marshall()); + out.flush(); + + // Close the input stream if it still open + fis.close(); + + // Try deleting the temp file + f = new File(filename); + f.delete(); + } + } catch (SWORDAuthenticationException sae) { + // Ask for credentials + if (authN.equals("Basic")) { + String s = "Basic realm=\"SWORD\""; + response.setHeader("WWW-Authenticate", s); + response.setStatus(401); + } + } catch (SWORDContentTypeException scte) { + // Throw a 415 + response.sendError(HttpServletResponse.SC_UNSUPPORTED_MEDIA_TYPE); + } catch (SWORDException se) { + // Throw a HTTP 500 + response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); + + // Is there an appropriate error header to return? + if (se.getErrorCode() != null) { + response.setHeader(HttpHeaders.X_ERROR_CODE, se.getErrorCode()); + } + System.out.println(se.toString()); + log.error(se.toString()); + } catch (IOException ioe) { + response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); + log.error(ioe.toString()); + } catch (NoSuchAlgorithmException nsae) { + response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); + log.error(nsae.toString()); + } + } + + /** + * Utiliy method to return the username and password (separated by a colon ':') + * + * @param request + * @return The username and password combination + */ + private String getUsernamePassword(HttpServletRequest request) { + try { + String authHeader = request.getHeader("Authorization"); + if (authHeader != null) { + StringTokenizer st = new StringTokenizer(authHeader); + if (st.hasMoreTokens()) { + String basic = st.nextToken(); + if (basic.equalsIgnoreCase("Basic")) { + String credentials = st.nextToken(); + String userPass = new String(Base64.decodeBase64(credentials.getBytes())); + return userPass; + } + } + } + } catch (Exception e) { + log.debug(e.toString()); + } + return null; + } + + /** + * Utility method to deicde if we are using HTTP Basic authentication + * + * @return if HTTP Basic authentication is in use or not + */ + private boolean authenticateWithBasic() { + if (authN.equalsIgnoreCase("Basic")) { + return true; + } else { + return false; + } + } + + /** + * Utility method to construct the URL called for this Servlet + * + * @param req The request object + * @return The URL + */ + private static String getUrl(HttpServletRequest req) { + String reqUrl = req.getRequestURL().toString(); + String queryString = req.getQueryString(); // d=789 + if (queryString != null) { + reqUrl += "?"+queryString; + } + return reqUrl; + } +} diff --git a/dspace-sword/dspace-sword-api/src/main/java/org/purl/sword/server/DummyServer.java b/dspace-sword/dspace-sword-api/src/main/java/org/purl/sword/server/DummyServer.java new file mode 100644 index 0000000000..61f3b99037 --- /dev/null +++ b/dspace-sword/dspace-sword-api/src/main/java/org/purl/sword/server/DummyServer.java @@ -0,0 +1,236 @@ +package org.purl.sword.server; + +import java.io.IOException; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.TimeZone; +import java.util.zip.ZipEntry; +import java.util.zip.ZipInputStream; + +import org.purl.sword.base.Collection; +import org.purl.sword.base.Deposit; +import org.purl.sword.base.DepositResponse; +import org.purl.sword.base.ErrorCodes; +import org.purl.sword.base.SWORDAuthenticationException; +import org.purl.sword.base.SWORDEntry; +import org.purl.sword.base.SWORDException; +import org.purl.sword.base.Service; +import org.purl.sword.base.ServiceDocument; +import org.purl.sword.base.ServiceDocumentRequest; +import org.purl.sword.base.ServiceLevel; +import org.purl.sword.base.Workspace; +import org.w3.atom.Author; +import org.w3.atom.Content; +import org.w3.atom.Contributor; +import org.w3.atom.Generator; +import org.w3.atom.InvalidMediaTypeException; +import org.w3.atom.Link; +import org.w3.atom.Source; +import org.w3.atom.Summary; +import org.w3.atom.Title; + +/** + * A 'dummy server' which acts as dumb repository which implements the + * SWORD ServerInterface. It accepts any type of deposit, and tries to + * return appropriate responses. + * + * It supports authentication: if the username and password match + * (case sensitive) it authenticates the user, if not, the authentication + * fails. + * + * @author Stuart Lewis + */ +public class DummyServer implements SWORDServer { + + /** A counter to count submissions, so the response to a deposito can increment */ + private static int counter = 0; + + /** + * Provides a dumb but plausible service document - it contains + * an anonymous workspace and collection, and one personalised + * for the onBehalfOf user. + * + * @param onBehalfOf The user that the client is acting on behalf of + * @throws SWORDAuthenticationException + */ + public ServiceDocument doServiceDocument(ServiceDocumentRequest sdr) throws SWORDAuthenticationException { + // Authenticate the user + String username = sdr.getUsername(); + String password = sdr.getPassword(); + if ((username != null) && (password != null) && + (((username.equals("")) && (password.equals(""))) || + (!username.equalsIgnoreCase(password))) ) { + // User not authenticated + throw new SWORDAuthenticationException("Bad credentials"); + } + + // Create and return a dummy ServiceDocument + ServiceDocument document = new ServiceDocument(); + Service service = new Service(ServiceLevel.ZERO, true, true); + document.setService(service); + + Workspace workspace = new Workspace(); + workspace.setTitle("Anonymous submitters workspace"); + Collection collection = new Collection(); + collection.setTitle("Anonymous submitters collection"); + collection.setLocation("http://sword.aber.ac.uk/sword/deposit?user=anon"); + workspace.addCollection(collection); + collection = new Collection(); + collection.setTitle("Anonymous submitters other collection"); + collection.setLocation("http://sword.aber.ac.uk/sword/deposit?user=anonymous"); + workspace.addCollection(collection); + service.addWorkspace(workspace); + + if (sdr.getUsername() != null) { + workspace = new Workspace(); + workspace.setTitle("Authenticated workspace for " + username); + collection = new Collection(); + collection.setTitle("Authenticated collection for " + username); + collection.setLocation("http://sword.aber.ac.uk/sword/deposit?user=" + username); + workspace.addCollection(collection); + collection = new Collection(); + collection.setTitle("Second authenticated collection for " + username); + collection.setLocation("http://sword.aber.ac.uk/sword/deposit?user=" + username + "-2"); + workspace.addCollection(collection); + service.addWorkspace(workspace); + } + + String onBehalfOf = sdr.getOnBehalfOf(); + if ((onBehalfOf != null) && (!onBehalfOf.equals(""))) { + workspace = new Workspace(); + workspace.setTitle("Personal workspace for " + onBehalfOf); + collection = new Collection(); + collection.setTitle("Personal collection for " + onBehalfOf); + collection.setLocation("http://sword.aber.ac.uk/sword/deposit?user=" + onBehalfOf); + collection.addAccepts("application/zip"); + collection.addAccepts("application/xml"); + collection.setAbstract("An abstract goes in here"); + collection.setCollectionPolicy("A collection policy"); + collection.setMediation(true); + collection.setTreatment("treatment in here too"); + workspace.addCollection(collection); + service.addWorkspace(workspace); + } + + return document; + } + + public DepositResponse doDeposit(Deposit deposit) throws SWORDAuthenticationException, SWORDException { + // Authenticate the user + String username = deposit.getUsername(); + String password = deposit.getPassword(); + if ((username != null) && (password != null) && + (((username.equals("")) && (password.equals(""))) || + (!username.equalsIgnoreCase(password))) ) { + // User not authenticated + throw new SWORDAuthenticationException("Bad credentials"); + } + + // Get the filenames + StringBuffer filenames = new StringBuffer("Deposit file contained: "); + if (deposit.getFilename() != null) { + filenames.append("(filename = " + deposit.getFilename() + ") "); + } + if (deposit.getSlug() != null) { + filenames.append("(slug = " + deposit.getSlug() + ") "); + } + try { + ZipInputStream zip = new ZipInputStream(deposit.getFile()); + ZipEntry ze; + while ((ze = zip.getNextEntry()) != null) { + filenames.append(" " + ze.toString()); + } + } catch (IOException ioe) { + throw new SWORDException("Failed to open deposited zip file", null, ErrorCodes.ERROR_CONTENT); + } + + // Handle the deposit + if (!deposit.isNoOp()) { + counter++; + } + DepositResponse dr = new DepositResponse(Deposit.ACCEPTED); + SWORDEntry se = new SWORDEntry(); + + Title t = new Title(); + t.setContent("DummyServer Deposit: #" + counter); + se.setTitle(t); + + se.addCategory("Category"); + + if (deposit.getSlug() != null) { + se.setId(deposit.getSlug() + " - ID: " + counter); + } else { + se.setId("ID: " + counter); + } + + SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'"); + TimeZone utc = TimeZone.getTimeZone("UTC"); + sdf.setTimeZone (utc); + String milliFormat = sdf.format(new Date()); + try { + se.setUpdated(milliFormat); + } catch (ParseException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + + Summary s = new Summary(); + s.setContent(filenames.toString()); + se.setSummary(s); + Author a = new Author(); + if (username != null) { + a.setName(username); + } else { + a.setName("unknown"); + } + se.addAuthors(a); + + Link em = new Link(); + em.setRel("edit-media"); + em.setHref("http://www.myrepository.ac.uk/sdl/workflow/my deposit"); + se.addLink(em); + + Link e = new Link(); + e.setRel("edit"); + e.setHref("http://www.myrepository.ac.uk/sdl/workflow/my deposit.atom"); + se.addLink(e); + + if (deposit.getOnBehalfOf() != null) { + Contributor c = new Contributor(); + c.setName(deposit.getOnBehalfOf()); + c.setEmail(deposit.getOnBehalfOf() + "@myrepository.ac.uk"); + se.addContributor(c); + } + + Source source = new Source(); + Generator generator = new Generator(); + generator.setContent("org.purl.sword.server.DummyServer"); + source.setGenerator(generator); + se.setSource(source); + + Content content = new Content(); + try { + content.setType("application/zip"); + } catch (InvalidMediaTypeException e1) { + // TODO Auto-generated catch block + e1.printStackTrace(); + } + content.setSource("http://www.myrepository.ac.uk/sdl/uploads/upload-" + counter + ".zip"); + se.setContent(content); + + se.setTreatment("Short back and sides"); + + if (deposit.isVerbose()) { + se.setVerboseDescription("I've done a lot of hard work to get this far!"); + } + + se.setNoOp(deposit.isNoOp()); + + se.setFormatNamespace("http://www.standards-body.com/standardXYZ/v1/"); + + dr.setEntry(se); + + return dr; + } +} diff --git a/dspace-sword/dspace-sword-api/src/main/java/org/purl/sword/server/SWORDServer.java b/dspace-sword/dspace-sword-api/src/main/java/org/purl/sword/server/SWORDServer.java new file mode 100644 index 0000000000..1ad8f7d1e4 --- /dev/null +++ b/dspace-sword/dspace-sword-api/src/main/java/org/purl/sword/server/SWORDServer.java @@ -0,0 +1,48 @@ +package org.purl.sword.server; + +import org.purl.sword.base.Deposit; +import org.purl.sword.base.DepositResponse; +import org.purl.sword.base.SWORDAuthenticationException; +import org.purl.sword.base.SWORDContentTypeException; +import org.purl.sword.base.SWORDException; +import org.purl.sword.base.ServiceDocument; +import org.purl.sword.base.ServiceDocumentRequest; + +/** + * An abstract interface to be implemnted by repositories wishing to provide + * a SWORD compliant service. + * + * http://www.ukoln.ac.uk/repositories/digirep/index/SWORD_APP_Profile_0.5 + * + * @author Stuart Lewis + */ +public interface SWORDServer { + + /** + * Answer a Service Document request sent on behalf of a user + * + * @param sdr The Service Document Request object + * + * @exception SWORDAuthenticationException Thrown if the authentication fails + * @exception SWORDException Thrown in an un-handalable Exception occurs. + * This will be dealt with by sending a HTTP 500 Server Exception + * + * @return The ServiceDocument representing the service document + */ + public ServiceDocument doServiceDocument(ServiceDocumentRequest sdr) + throws SWORDAuthenticationException, SWORDException; + + /** + * Answer a SWORD deposit + * + * @param deposit The Deposit object + * + * @exception SWORDAuthenticationException Thrown if the authentication fails + * @exception SWORDException Thrown in an un-handalable Exception occurs. + * This will be dealt with by sending a HTTP 500 Server Exception + * + * @return The response to the deposit + */ + public DepositResponse doDeposit(Deposit deposit) + throws SWORDAuthenticationException, SWORDContentTypeException, SWORDException; +} diff --git a/dspace-sword/dspace-sword-api/src/main/java/org/purl/sword/server/ServiceDocumentServlet.java b/dspace-sword/dspace-sword-api/src/main/java/org/purl/sword/server/ServiceDocumentServlet.java new file mode 100644 index 0000000000..76a7b7bebe --- /dev/null +++ b/dspace-sword/dspace-sword-api/src/main/java/org/purl/sword/server/ServiceDocumentServlet.java @@ -0,0 +1,145 @@ +package org.purl.sword.server; + +import java.io.IOException; +import java.io.PrintWriter; +import java.util.StringTokenizer; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.apache.commons.codec.binary.Base64; +import org.apache.log4j.Logger; +import org.purl.sword.base.HttpHeaders; +import org.purl.sword.base.SWORDAuthenticationException; +import org.purl.sword.base.SWORDException; +import org.purl.sword.server.SWORDServer; +import org.purl.sword.base.ServiceDocument; +import org.purl.sword.base.ServiceDocumentRequest; + +public class ServiceDocumentServlet extends HttpServlet { + + private SWORDServer myRepository; + + private String authN; + + private static Logger log = Logger.getLogger(ServiceDocumentServlet.class); + + public void init() { + // Instantiate the correct SWORD Server class + String className = getServletContext().getInitParameter("server-class"); + if (className == null) { + log.fatal("Unable to read value of 'sword-server-class' from Servlet context"); + } else { + try { + myRepository = (SWORDServer)Class.forName(className).newInstance(); + log.info("Using " + className + " as the SWORDServer"); + } catch (Exception e) { + log.fatal("Unable to instantiate class from 'sword-server-class': " + className); + } + } + + // Set the authentication method + authN = getServletContext().getInitParameter("authentication-method"); + if ((authN == null) || (authN == "")) { + authN = "None"; + } + log.info("Authentication type set to: " + authN); + } + + protected void doGet(HttpServletRequest request, + HttpServletResponse response) throws ServletException, IOException + { + // Create the ServiceDocumentRequest + ServiceDocumentRequest sdr = new ServiceDocumentRequest(); + + // Are there any authentication details? + String usernamePassword = getUsernamePassword(request); + if ((usernamePassword != null) && (!usernamePassword.equals(""))) { + int p = usernamePassword.indexOf(":"); + if (p != -1) { + sdr.setUsername(usernamePassword.substring(0, p)); + sdr.setPassword(usernamePassword.substring(p+1)); + } + } else if (authenticateWithBasic()) { + String s = "Basic realm=\"SWORD\""; + response.setHeader("WWW-Authenticate", s); + response.setStatus(401); + return; + } + + // Set the x-on-behalf-of header + sdr.setOnBehalfOf(request.getHeader(HttpHeaders.X_ON_BEHALF_OF.toString())); + + // Set the IP address + sdr.setIPAddress(request.getRemoteAddr()); + + // Get the ServiceDocument + try { + ServiceDocument sd = myRepository.doServiceDocument(sdr); + + // Print out the Service Document + // response.setContentType("application/atomserv+xml"); + response.setContentType("application/xml"); + PrintWriter out = response.getWriter(); + out.write(sd.marshall()); + out.flush(); + } catch (SWORDAuthenticationException sae) { + if (authN.equals("Basic")) { + String s = "Basic realm=\"SWORD\""; + response.setHeader("WWW-Authenticate", s); + response.setStatus(401); + } + } catch (SWORDException se) { + // Throw a HTTP 500 + response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); + } + } + + protected void doPost(HttpServletRequest request, + HttpServletResponse response) throws ServletException, IOException + { + // Send a '501 Not Implemented' + response.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED); + } + + /** + * Utiliy method to return the username and password (separated by a colon ':') + * + * @param request + * @return The username and password combination + */ + private String getUsernamePassword(HttpServletRequest request) { + try { + String authHeader = request.getHeader("Authorization"); + if (authHeader != null) { + StringTokenizer st = new StringTokenizer(authHeader); + if (st.hasMoreTokens()) { + String basic = st.nextToken(); + if (basic.equalsIgnoreCase("Basic")) { + String credentials = st.nextToken(); + String userPass = new String(Base64.decodeBase64(credentials.getBytes())); + return userPass; + } + } + } + } catch (Exception e) { + log.debug(e.toString()); + } + return null; + } + + /** + * Utility method to deicde if we are using HTTP Basic authentication + * + * @return if HTTP Basic authentication is in use or not + */ + private boolean authenticateWithBasic() { + if (authN.equalsIgnoreCase("Basic")) { + return true; + } else { + return false; + } + } +} diff --git a/dspace-sword/dspace-sword-api/src/main/java/org/purl/sword/test/DummyServerTest.java b/dspace-sword/dspace-sword-api/src/main/java/org/purl/sword/test/DummyServerTest.java new file mode 100644 index 0000000000..3fd5a1b010 --- /dev/null +++ b/dspace-sword/dspace-sword-api/src/main/java/org/purl/sword/test/DummyServerTest.java @@ -0,0 +1,36 @@ +package org.purl.sword.test; + +import org.purl.sword.server.SWORDServer; +import org.purl.sword.server.DummyServer; + +public class DummyServerTest { + + /** + * A main method to test the dummy SWORD server. + */ + public static void main(String[] args) { + //Instantiate the dummy server + SWORDServer ss = new DummyServer(); + + // Test the normal service document + System.out.println("Testing doServiceDocument():"); + System.out.println("============================"); + //System.out.println(ss.doServiceDocument(null)); + + // Test the normal service document authenticated as 'sdl' + System.out.println("Testing doServiceDocument(sdl):"); + System.out.println("============================"); + //System.out.println(ss.doServiceDocument("sdl")); + + // Test the 'on behalf of' service document + System.out.println("Testing doServiceDocument(onBehalfOf):"); + System.out.println("======================================"); + //System.out.println(ss.doServiceDocument(null, "Stuart Lewis")); + + // Test the 'on behalf of' service document authenticated as 'sdl' + System.out.println("Testing doServiceDocument(sdl, onBehalfOf):"); + System.out.println("======================================"); + //System.out.println(ss.doServiceDocument("sdl", "Stuart Lewis")); + } + +} diff --git a/dspace-sword/dspace-sword-api/src/main/java/org/purl/sword/test/SwordTest.java b/dspace-sword/dspace-sword-api/src/main/java/org/purl/sword/test/SwordTest.java new file mode 100755 index 0000000000..9cf0d71b48 --- /dev/null +++ b/dspace-sword/dspace-sword-api/src/main/java/org/purl/sword/test/SwordTest.java @@ -0,0 +1,242 @@ +/** + * Copyright (c) 2007, Aberystwyth University + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the + * following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * - Neither the name of the Centre for Advanced Software and + * Intelligent Systems (CASIS) nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +package org.purl.sword.test; + +/** + * Author : $Author: nst $ + * Date : $Date: 2007/09/21 15:18:57 $ + * Revision : $Revision: 1.3 $ + * Name : $Name: $ + */ + +import org.purl.sword.base.*; + +import org.w3.atom.*; + +/** + * Simple test class for the ServiceDocument and DepositResponse + * classes in the SWORD common classes. + * + * @author Neil Taylor + */ +public class SwordTest +{ + /** + * Start the test of the ServiceDocument followed by the + * DepositResponse. + * + * @param args + */ + public static void main(String[] args) + { + SwordTest test = new SwordTest(); + test.serviceDocumentTest(); + + test.depositResponseTest(); + } + + /** + * Create a test ServiceDocument class. Marshall and Unmarshall the + * data to ensure that it is preserved when transformed to and + * from an XML string. + */ + public void serviceDocumentTest() + { + // create a new service document + Service service = new Service(ServiceLevel.ZERO); + service.setVerbose(true); + service.setNoOp(false); + + // add some workspace/collections + Workspace workspace = new Workspace(); + workspace.setTitle("This is a test"); + + Collection collection = new Collection(); + collection.setTitle("The first collection"); + collection.setLocation("http://www.somewhere.com/here"); + + workspace.addCollection(collection); + + service.addWorkspace(workspace); + + workspace = new Workspace(); + workspace.setTitle("This is a second test"); + + collection = new Collection(); + collection.setTitle("The second collection"); + collection.setLocation("http://www.somewhere.com/here/something"); + collection.addAccepts("application/zip"); + collection.addAccepts("application/xml"); + collection.setAbstract("An abstract goes in here"); + collection.setCollectionPolicy("A collection policy"); + collection.setMediation(true); + collection.setFormatNamespace("a namespace in here"); + collection.setTreatment("treatment in here too"); + + workspace.addCollection(collection); + + service.addWorkspace(workspace); + + // create the service document, marshall, unmarshall and marshall again. + ServiceDocument document = new ServiceDocument(service); + + // display the XML document that has been constructed + String doc = document.marshall(); + System.out.println(doc); + + try + { + ServiceDocument unmarshalledDocument = new ServiceDocument(); + unmarshalledDocument.unmarshall(doc); + + System.out.println(unmarshalledDocument.marshall()); + } + catch( Exception e ) + { + e.printStackTrace(); + } + + } + + /** + * Create a test DepositResponse class. Marshall and Unmarshall the + * data to ensure that it is preserved when transformed to and + * from an XML string. + */ + public void depositResponseTest() + { + try + { + + DepositResponse response = new DepositResponse(HttpHeaders.ACCEPTED); + SWORDEntry entry = new SWORDEntry(); + entry.setId("atom:com.intrallect.atomTest3p0:60"); + Title title = new Title(); + title.setContent("Burning Stubble"); + title.setType(ContentType.TEXT); + entry.setTitle(title); + + // add authors + Author author = new Author(); + author.setName("Sword Tester"); + author.setEmail("sword@ukoln.ac.uk"); + author.setUri("http://www.ukoln.ac.uk/repositories/digirep/index/SWORD"); + entry.addAuthors(author); + + author = new Author(); + author.setName("CASIS Tester"); + author.setEmail("nst@aber.ac.uk"); + author.setUri("http://www.aber.ac.uk/casis/"); + entry.addAuthors(author); + + // add links + Link link = new Link(); + link.setRel("edit-media"); + link.setHref("http://bagel.intrallect.com:5555/intralibrary3p0/IntraLibrary-Deposit/edit-media/learning_object_id60"); + link.setHreflang("en"); + link.setTitle("Edit Media Title"); + link.setType("edit media type"); + link.setContent("some content in here"); + + entry.addLink(link); + + link = new Link(); + link.setRel("edit"); + link.setHref("http://bagel.intrallect.com:5555/intralibrary3p0/IntraLibrary-Deposit/edit/learning_object_id60"); + link.setHreflang("en"); + link.setTitle("Edit Title"); + link.setType("edit type"); + link.setContent("some content in here"); + + entry.addLink(link); + + entry.addCategory("test category"); + entry.addCategory("second test category"); + + Contributor contributor = new Contributor(); + contributor.setName("Neil Taylor"); + contributor.setEmail("nst@aber.ac.uk"); + contributor.setUri("http://www.aber.ac.uk/casis/"); + + entry.addContributor(contributor); + + Rights rights = new Rights(); + rights.setType(ContentType.TEXT); + rights.setContent("Rights declaration."); + entry.setRights(rights); + + Content content = new Content(); + content.setSource("http://bagel.intrallect.com:5555/intralibrary3p0/IntraLibrary?command=open-preview&learning_object_key=i189n4207t"); + content.setType("application/zip"); + entry.setContent(content); + + Generator generator = new Generator(); + generator.setContent("Test Generator ID"); + generator.setUri("http://www.somewhere.com/"); + generator.setVersion("1.1"); + + Source source = new Source(); + source.setGenerator(generator); + entry.setSource(source); + + entry.setPublished("2007-08-02T10:13:14Z"); + entry.setUpdated("2007-08-02T10:22:17Z"); + + + entry.setFormatNamespace("Test format namespace"); + entry.setTreatment("Treatment description"); + entry.setNoOp(true); + entry.setVerboseDescription("A Verbose Description."); + + + response.setEntry( entry ); + + String test = response.marshall(); + System.out.println(test); + System.out.println("================="); + + DepositResponse unmarshalledDocument = new DepositResponse(HttpHeaders.CREATED); + unmarshalledDocument.unmarshall(test); + System.out.println(unmarshalledDocument.marshall()); + } + catch( Exception e ) + { + e.printStackTrace(); + } + } + +} diff --git a/dspace-sword/dspace-sword-api/src/main/java/org/w3/atom/Author.java b/dspace-sword/dspace-sword-api/src/main/java/org/w3/atom/Author.java new file mode 100755 index 0000000000..fd5effdb10 --- /dev/null +++ b/dspace-sword/dspace-sword-api/src/main/java/org/w3/atom/Author.java @@ -0,0 +1,240 @@ +/** + * Copyright (c) 2007, Aberystwyth University + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the + * following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * - Neither the name of the Centre for Advanced Software and + * Intelligent Systems (CASIS) nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +package org.w3.atom; + +import nu.xom.Element; +import nu.xom.Elements; + +import org.purl.sword.base.InfoLogger; +import org.purl.sword.base.Namespaces; +import org.purl.sword.base.SwordElementInterface; +import org.purl.sword.base.UnmarshallException; +import org.purl.sword.base.XmlElement; + +/** + * Represents an Author type, as used in ATOM. This class is used as the + * base class for the different areas of ATOM that represent information + * about people. This includes the atom:author and atom:contributor + * elements. + * + * @author Neil Taylor + */ +public class Author extends XmlElement implements SwordElementInterface +{ + /** + * The author's name. + */ + private String name; + + /** + * The author's URI. + */ + private String uri; + + /** + * The author's email. + */ + private String email; + + + /** + * Create a new instance and set the prefix to + * 'atom' and the local name to 'author'. + */ + public Author() + { + this("atom", "author"); + } + + /** + * Create a new instance and set the element name. + * + * @param prefix The prefix to use when marshalling the data. + * @param localName The localName to use when marshalling the data. + */ + public Author(String prefix, String localName ) + { + super(prefix, localName); + } + + /** + * Marshall the data in this object to a XOM Element. The element + * will have the full name that is specified in the constructor. + * + * @return A XOM Element. + */ + public Element marshall() + { + Element element = new Element(getQualifiedName(), Namespaces.NS_ATOM); + + if( name != null ) + { + Element nameElement = new Element(getQualifiedName("name"), Namespaces.NS_ATOM); + nameElement.appendChild(name); + element.appendChild(nameElement); + } + + if( uri != null ) + { + Element uriElement = new Element(getQualifiedName("uri"), Namespaces.NS_ATOM); + uriElement.appendChild(uri); + element.appendChild(uriElement); + } + + if( email != null ) + { + Element emailElement = new Element(getQualifiedName("email"), Namespaces.NS_ATOM); + emailElement.appendChild(email); + element.appendChild(emailElement); + } + + return element; + } + + + /** + * Unmarshall the author details from the specified element. The element + * is a XOM element. + * + */ + public void unmarshall(Element author) + throws UnmarshallException + { + if( ! isInstanceOf( author, localName, Namespaces.NS_ATOM)) + { + throw new UnmarshallException("Element is not of the correct type"); + } + + try + { + // retrieve all of the sub-elements + Elements elements = author.getChildElements(); + Element element = null; + int length = elements.size(); + + for(int i = 0; i < length; i++ ) + { + element = elements.get(i); + + if( isInstanceOf(element, "name", Namespaces.NS_ATOM )) + { + name = unmarshallString(element); + } + if( isInstanceOf(element, "uri", Namespaces.NS_ATOM )) + { + uri = unmarshallString(element); + } + if( isInstanceOf(element, "email", Namespaces.NS_ATOM )) + { + email = unmarshallString(element); + } + else + { + // unknown element type + //counter.other++; + } + } // for + } + catch( UnmarshallException ex ) + { + InfoLogger.getLogger().writeError("Unable to parse an element in " + getQualifiedName() + ": " + ex.getMessage()); + throw ex; + } + + } + + /** + * Retrieve the author name. + * + * @return The name. + */ + public String getName() + { + return name; + } + + /** + * Set the author name. + * + * @param name The name. + */ + public void setName(String name) + { + this.name = name; + } + + /** + * Get the author URI. + * + * @return The URI. + */ + public String getUri() + { + return uri; + } + + /** + * Set the author URI. + * + * @param uri the URI. + */ + public void setUri(String uri) + { + this.uri = uri; + } + + /** + * Get the author email. + * + * @return The email. + */ + public String getEmail() + { + return email; + } + + /** + * Set the author email. + * + * @param email The email. + */ + public void setEmail(String email) + { + this.email = email; + } + +} diff --git a/dspace-sword/dspace-sword-api/src/main/java/org/w3/atom/Content.java b/dspace-sword/dspace-sword-api/src/main/java/org/w3/atom/Content.java new file mode 100755 index 0000000000..98e0708184 --- /dev/null +++ b/dspace-sword/dspace-sword-api/src/main/java/org/w3/atom/Content.java @@ -0,0 +1,203 @@ +/** + * Copyright (c) 2007, Aberystwyth University + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the + * following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * - Neither the name of the Centre for Advanced Software and + * Intelligent Systems (CASIS) nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +package org.w3.atom; + +import nu.xom.Attribute; +import nu.xom.Element; + +import org.purl.sword.base.InfoLogger; +import org.purl.sword.base.Namespaces; +import org.purl.sword.base.SwordElementInterface; +import org.purl.sword.base.UnmarshallException; +import org.purl.sword.base.XmlElement; + +/** + * Represents an ATOM Content element. + * + * @author Neil Taylor + * + */ +public class Content extends XmlElement implements SwordElementInterface +{ + /** + * The identifier for the src attribute. + */ + public static final String ATTR_SRC = "src"; + + /** + * The identifier for the type attribute. + */ + public static final String ATTR_TYPE = "type"; + + /** + * The data for the type attribute. + */ + private String type; + + /** + * The data for the source attribute. + */ + private String source; + + /** + * Create a new instance and set the prefix to + * 'atom' and the local name to 'content'. + */ + public Content() + { + super("atom", "content"); + } + + /** + * Get the Source. + * + * @return The Source. + */ + public String getSource() + { + return source; + } + + /** + * Set the Source. + * + * @param source The source. + */ + public void setSource(String source) + { + this.source = source; + } + + /** + * Get the type. + * + * @return The type. + */ + public String getType() + { + return type; + } + + /** + * Set the type for the content. This should match the pattern + * ".* /.*" [Note, there is no space before the /, this has been added + * to allow this text to be written in a Java comment.]. + * + * An example of the type is application/zip. + * + * @param type The specified type. + * @throws InvalidMediaTypeException If the specified type is null or + * it does not match the specified pattern. + */ + public void setType(String type) + throws InvalidMediaTypeException + { + if( type == null || ! type.matches(".*/.*") ) + { + throw new InvalidMediaTypeException("Type: '" + type + "' does not match .*/.*"); + } + + this.type = type; + } + + /** + * Marshall the data in this object to an Element object. + * + * @return A XOM Element that holds the data for this Content element. + */ + public Element marshall() + { + Element content = new Element(getQualifiedName(), Namespaces.NS_ATOM); + + if( type != null ) + { + Attribute typeAttribute = new Attribute(ATTR_TYPE, type); + content.addAttribute(typeAttribute); + } + + if( source != null ) + { + Attribute typeAttribute = new Attribute(ATTR_SRC, source); + content.addAttribute(typeAttribute); + } + + return content; + } + + /** + * Unmarshall the content element into the data in this object. + * + * @throws UnmarshallException If the element does not contain a + * content element or if there are problems + * accessing the data. + */ + public void unmarshall(Element content) + throws UnmarshallException + { + if( ! isInstanceOf( content, localName, Namespaces.NS_ATOM)) + { + throw new UnmarshallException("Element is not of the correct type"); + } + + try + { + // get the attributes + int attributeCount = content.getAttributeCount(); + Attribute attribute = null; + for( int i = 0; i < attributeCount; i++ ) + { + attribute = content.getAttribute(i); + String name = attribute.getQualifiedName(); + if( ATTR_TYPE.equals(name)) + { + type = attribute.getValue(); + } + + if( ATTR_SRC.equals(name) ) + { + source = attribute.getValue(); + } + } + } + catch( Exception ex ) + { + InfoLogger.getLogger().writeError("Unable to parse an element in Content: " + ex.getMessage()); + throw new UnmarshallException("Error parsing Content", ex); + } + } + +} diff --git a/dspace-sword/dspace-sword-api/src/main/java/org/w3/atom/ContentType.java b/dspace-sword/dspace-sword-api/src/main/java/org/w3/atom/ContentType.java new file mode 100755 index 0000000000..cb4cf5fcfb --- /dev/null +++ b/dspace-sword/dspace-sword-api/src/main/java/org/w3/atom/ContentType.java @@ -0,0 +1,72 @@ +/** + * Copyright (c) 2007, Aberystwyth University + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the + * following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * - Neither the name of the Centre for Advanced Software and + * Intelligent Systems (CASIS) nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +package org.w3.atom; + +/** + * + * @author Neil Taylor + * + */ +public enum ContentType +{ + TEXT ("text"), + HTML ("html"), + XHTML ("xhtml"); + + /** + * String representation of the type. + */ + private final String type; + + /** + * Create a new instance and set the string + * representation of the type. + * + * @param type The type, expressed as a string. + */ + private ContentType(String type) + { + this.type = type; + } + + /** + * Retrieve a string representation of this object. + * + * @return A string. + */ + public String toString() { return this.type; } +} diff --git a/dspace-sword/dspace-sword-api/src/main/java/org/w3/atom/Contributor.java b/dspace-sword/dspace-sword-api/src/main/java/org/w3/atom/Contributor.java new file mode 100755 index 0000000000..d479f29027 --- /dev/null +++ b/dspace-sword/dspace-sword-api/src/main/java/org/w3/atom/Contributor.java @@ -0,0 +1,56 @@ +/** + * Copyright (c) 2007, Aberystwyth University + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the + * following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * - Neither the name of the Centre for Advanced Software and + * Intelligent Systems (CASIS) nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +package org.w3.atom; + +import org.w3.atom.Author; + +/** + * Represents an ATOM Contributor. + * + * @author Neil Taylor + */ +public class Contributor extends Author +{ + /** + * Create a new instance and set the prefix to + * 'atom' and the local name to 'contributor'. + */ + public Contributor() + { + super("atom", "contributor"); + } +} diff --git a/dspace-sword/dspace-sword-api/src/main/java/org/w3/atom/Entry.java b/dspace-sword/dspace-sword-api/src/main/java/org/w3/atom/Entry.java new file mode 100755 index 0000000000..467c25cbd3 --- /dev/null +++ b/dspace-sword/dspace-sword-api/src/main/java/org/w3/atom/Entry.java @@ -0,0 +1,598 @@ +/** + * Copyright (c) 2007, Aberystwyth University + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the + * following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * - Neither the name of the Centre for Advanced Software and + * Intelligent Systems (CASIS) nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +package org.w3.atom; + +import java.text.ParseException; +import java.util.ArrayList; +import java.util.Date; +import java.util.Iterator; +import java.util.List; + +import nu.xom.Element; +import nu.xom.Elements; + +import org.purl.sword.base.InfoLogger; +import org.purl.sword.base.Namespaces; +import org.purl.sword.base.SwordElementInterface; +import org.purl.sword.base.UnmarshallException; +import org.purl.sword.base.XmlElement; + +/** + * Represents an ATOM entry. + * + * @author Neil Taylor + * + */ +public class Entry extends XmlElement implements SwordElementInterface +{ + /** + * A list of authors associated with this entry. There can be 0 + * or more of these elements. + */ + private List authors; + + /** + * The atom:category data. There can be 0 or more of these elements. + * FIXME - this does not accommodate the idea of 'anyForeignElement' + */ + private List categories; // FIXME - needed? + + /** + * A single content element for the Entry. + */ + private Content content; + + /** + * A list of contributors associated with this entry. There can be 0 or + * more of these elements. + */ + private List contributors; + + /** + * This is a simplified version. The ID can also have atomCommonAttributes, + * but these have not been modelled in this version. The content of + * ID is an unconstrained string, which is intended to represent a URI. + */ + private String id; + + /** + * A list of link elements. This can contain 0 or more entries. + */ + private List links; + + /** + * Simplified version of the atom:published element. This implementation + * does not record the general atomCommonAttributes. The date is + * taken from an xsd:dateTime value. + * + * This item is optional. + */ + private Date published; + + /** + * A single, optional, content element for the Entry. + * FIXME - this does not cater for the different content types. + */ + private Rights rights; + + /** + * A single, optional, content element for the Entry. + */ + private Source source; + + /** + * A single, optional, summary element for the Entry. + * FIXME - this does not cater for the different content types. + */ + private Summary summary; + + /** + * A required title element for the entry. + * FIXME - this does not cater for the different content types. + */ + private Title title; + + /** + * The date on which the entry was last updated. + */ + private Date updated; + + /** + * Create a new instance of the class and initialise it. + * Also, set the prefix to 'atom' and the local name to 'entry'. + */ + public Entry() + { + super("atom", "entry"); + + authors = new ArrayList(); + categories = new ArrayList(); + contributors = new ArrayList(); + links = new ArrayList(); + } + + /** + * Mashall the data stored in this object into Element objects. + * + * @return An element that holds the data associated with this object. + */ + public Element marshall() + { + Element entry = new Element(getQualifiedName(), Namespaces.NS_ATOM); + entry.addNamespaceDeclaration("sword", Namespaces.NS_SWORD); + entry.addNamespaceDeclaration("atom", Namespaces.NS_ATOM); + + if( id != null ) + { + Element idElement = new Element(getQualifiedName("id"), Namespaces.NS_ATOM); + idElement.appendChild(id); + entry.appendChild(idElement); + } + + for( Author author : authors ) + { + entry.appendChild(author.marshall()); + } + + if( content != null ) + { + entry.appendChild(content.marshall()); + } + + for( Author contributor : contributors ) + { + entry.appendChild(contributor.marshall()); + } + + for( Link link : links ) + { + entry.appendChild(link.marshall()); + } + + if( published != null ) + { + Element publishedElement = new Element(getQualifiedName("published"), Namespaces.NS_ATOM); + publishedElement.appendChild(dateToString(published)); + entry.appendChild(publishedElement); + } + + if( rights != null ) + { + entry.appendChild(rights.marshall()); + } + + if( summary != null ) + { + entry.appendChild(summary.marshall()); + } + + if( title != null ) + { + entry.appendChild(title.marshall()); + } + + if( source != null ) + { + entry.appendChild(source.marshall()); + } + + if( updated != null ) + { + Element updatedElement = new Element(getQualifiedName("updated"), Namespaces.NS_ATOM); + updatedElement.appendChild(dateToString(updated)); + entry.appendChild(updatedElement); + } + + Element categoryElement = null; + for( String category : categories ) + { + categoryElement = new Element(getQualifiedName("category"), Namespaces.NS_ATOM ); + categoryElement.appendChild(category); + entry.appendChild(categoryElement); + } + + return entry; + } + + /** + * Unmarshall the contents of the Entry element into the internal data objects + * in this object. + * + * @param entry The Entry element to process. + * + * @throws UnmarshallException If the element does not contain an ATOM entry + * element, or if there is a problem processing the element or any + * subelements. + */ + public void unmarshall(Element entry) + throws UnmarshallException + { + if( ! isInstanceOf(entry, localName, Namespaces.NS_ATOM)) + { + throw new UnmarshallException( "Not a " + getQualifiedName() + " element" ); + } + + try + { + authors.clear(); + categories.clear(); + contributors.clear(); + links.clear(); + + // retrieve all of the sub-elements + Elements elements = entry.getChildElements(); + Element element = null; + int length = elements.size(); + + for(int i = 0; i < length; i++ ) + { + element = elements.get(i); + + if( isInstanceOf(element, "author", Namespaces.NS_ATOM ) ) + { + Author author = new Author(); + author.unmarshall(element); + authors.add(author); + } + else if( isInstanceOf(element, "category", Namespaces.NS_ATOM )) + { + categories.add(unmarshallString(element)); + } + else if( isInstanceOf(element, "content", Namespaces.NS_ATOM)) + { + content = new Content(); + content.unmarshall(element); + } + else if( isInstanceOf(element, "contributor", Namespaces.NS_ATOM)) + { + Contributor contributor = new Contributor(); + contributor.unmarshall(element); + contributors.add(contributor); + } + else if( isInstanceOf(element, "id", Namespaces.NS_ATOM )) + { + id = unmarshallString(element); + } + else if( isInstanceOf(element, "link", Namespaces.NS_ATOM)) + { + Link link = new Link(); + link.unmarshall(element); + links.add(link); + } + else if( isInstanceOf(element, "published", Namespaces.NS_ATOM) ) + { + published = unmarshallDate(element); + } + else if( isInstanceOf(element, "rights", Namespaces.NS_ATOM)) + { + rights = new Rights(); + rights.unmarshall(element); + } + else if( isInstanceOf(element, "summary", Namespaces.NS_ATOM)) + { + summary = new Summary(); + summary.unmarshall(element); + } + else if( isInstanceOf(element, "title", Namespaces.NS_ATOM)) + { + title = new Title(); + title.unmarshall(element); + } + else if( isInstanceOf(element, "updated", Namespaces.NS_ATOM) ) + { + updated = unmarshallDate(element); + } + else if( isInstanceOf(element, "source", Namespaces.NS_ATOM)) + { + source = new Source(); + source.unmarshall(element); + } + else + { + // unknown element type + //counter.other++; + } + } // for + } + catch( Exception ex ) + { + InfoLogger.getLogger().writeError("Unable to parse an element in Entry: " + ex.getMessage()); + ex.printStackTrace(); + throw new UnmarshallException("Unable to parse an element in " + getQualifiedName(), ex); + } + } + + /** + * Get an iterator for the authors in the Entry. + * + * @return An iterator. + */ + public Iterator getAuthors() + { + return authors.iterator(); + } + + /** + * Add an author to the Entry. + * + * @param author The author to add. + */ + public void addAuthors(Author author) + { + this.authors.add(author); + } + + /** + * Clear the list of authors. + */ + public void clearAuthors() + { + this.authors.clear(); + } + + /** + * Get an iterator for the categories in this Entry. + * + * @return An iterator. + */ + public Iterator getCategories() { + return categories.iterator(); + } + + /** + * Add a category. + * + * @param category the category to add. + */ + public void addCategory(String category) { + this.categories.add(category); + } + + /** + * Clear the list of categories. + */ + public void clearCategories() + { + this.categories.clear(); + } + + /** + * Get the content element for this Entry. + * + * @return The content element. + */ + public Content getContent() { + return content; + } + + /** + * Set the content element for this Entry. + * @param content + */ + public void setContent(Content content) { + this.content = content; + } + + /** + * Get a list of contributors. + * + * @return An iterator. + */ + public Iterator getContributors() { + return contributors.iterator(); + } + + /** + * Add a contributor. + * + * @param contributor The contributor. + */ + public void addContributor(Contributor contributor) { + this.contributors.add(contributor); + } + + /** + * Clear the list of contributors. + */ + public void clearContributors() + { + this.contributors.clear(); + } + + /** + * Get the ID for this Entry. + * + * @return The ID. + */ + public String getId() { + return id; + } + + /** + * Set the ID for this Entry. + * + * @param id The ID. + */ + public void setId(String id) { + this.id = id; + } + + /** + * Get the list of links for this Entry. + * + * @return An iterator. + */ + public Iterator getLinks() { + return links.iterator(); + } + + /** + * Get the link for this Entry. + * + * @param link The link. + */ + public void addLink(Link link) { + this.links.add(link); + } + + /** + * Clear the list of links. + */ + public void clearLinks() + { + this.links.clear(); + } + + /** + * Get the published date, expressed as a String. + * + * @return The date. + */ + public String getPublished() { + return dateToString(published); + } + + /** + * Set the published date. Converts the date string into a date. + * The date should be expressed as a string with the format: + * 'yyyy-mm-ddThh:mm:ssZ'. + * + * @param published The string. + * + * @throws ParseException, if the date does not match format. + */ + public void setPublished(String published) + throws ParseException + { + this.published = stringToDate(published); + } + + /** + * Get the rights for this Entry. + * @return The rights. + */ + public Rights getRights() { + return rights; + } + + /** + * Set the rights for this Entry. + * + * @param rights The rights. + */ + public void setRights(Rights rights) { + this.rights = rights; + } + + /** + * Get the source for this Entry. + * @return The source. + */ + public Source getSource() { + return source; + } + + /** + * Set the source for this entry. + * + * @param source The source. + */ + public void setSource(Source source) { + this.source = source; + } + + /** + * Get the summary. + * + * @return The summary. + */ + public Summary getSummary() { + return summary; + } + + /** + * Set the summary. + * + * @param summary The summary. + */ + public void setSummary(Summary summary) { + this.summary = summary; + } + + /** + * Get the title. + * + * @return The title. + */ + public Title getTitle() { + return title; + } + + /** + * Set the title. + * + * @param title The title. + */ + public void setTitle(Title title) { + this.title = title; + } + + /** + * Get the updated date, expressed as a String. + * + * @return The date. + */ + public String getUpdated() { + return dateToString(updated); + } + + /** + * Set the updated date. Converts the date string into a date. + * The date should be expressed as a string with the format: + * 'yyyy-mm-ddThh:mm:ssZ'. + * + * @param updated The string. + * + * @throws ParseException, if the date does not match format. + */ + public void setUpdated(String updated) + throws ParseException + { + this.updated = stringToDate(updated); + } + +} diff --git a/dspace-sword/dspace-sword-api/src/main/java/org/w3/atom/Generator.java b/dspace-sword/dspace-sword-api/src/main/java/org/w3/atom/Generator.java new file mode 100755 index 0000000000..8c3a7cc9cd --- /dev/null +++ b/dspace-sword/dspace-sword-api/src/main/java/org/w3/atom/Generator.java @@ -0,0 +1,210 @@ +/** + * Copyright (c) 2007, Aberystwyth University + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the + * following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * - Neither the name of the Centre for Advanced Software and + * Intelligent Systems (CASIS) nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +package org.w3.atom; + +import nu.xom.Attribute; +import nu.xom.Element; + +import org.purl.sword.base.InfoLogger; +import org.purl.sword.base.Namespaces; +import org.purl.sword.base.SwordElementInterface; +import org.purl.sword.base.UnmarshallException; +import org.purl.sword.base.XmlElement; + +/** + * Represents an ATOM Generator element. + * + * @author Neil Taylor + */ +public class Generator extends XmlElement implements SwordElementInterface +{ + /** + * The content for the element. + */ + private String content; + + /** + * The URI attribute. + */ + private String uri; + + /** + * The version attribute. + */ + private String version; + + /** + * Create a new instance and set the prefix to + * 'atom' and the local name to 'generator'. + */ + public Generator() + { + super("atom", "generator"); + } + + /** + * Marshall the data in the object to an Element object. + * + * @return The element. + */ + public Element marshall() + { + Element element = new Element(getQualifiedName(), Namespaces.NS_ATOM); + + if( content != null ) + { + element.appendChild(content); + } + + if( uri != null ) + { + Attribute uriAttribute = new Attribute("uri", uri); + element.addAttribute(uriAttribute); + } + + if( version != null ) + { + Attribute versionAttribute = new Attribute("version", version); + element.addAttribute(versionAttribute); + } + + + return element; + } + + /** + * Unmarshall the specified Generator element into the data in this object. + * + * @param generator The generator element. + * + * @throws UnmarshallException If the specified element is not an atom:generator + * element, or if there is an error accessing the data. + */ + public void unmarshall(Element generator) + throws UnmarshallException + { + if( ! isInstanceOf(generator, localName, Namespaces.NS_ATOM)) + { + throw new UnmarshallException( "Not an atom:generator element" ); + } + + try + { + // get the attributes + int attributeCount = generator.getAttributeCount(); + Attribute attribute = null; + for( int i = 0; i < attributeCount; i++ ) + { + attribute = generator.getAttribute(i); + if( "uri".equals(attribute.getQualifiedName())) + { + uri = attribute.getValue(); + } + else if( "version".equals(attribute.getQualifiedName())) + { + version = attribute.getValue(); + } + } + + int length = generator.getChildCount(); + if( length > 0 ) + { + content = unmarshallString(generator); + } + } + catch( Exception ex ) + { + InfoLogger.getLogger().writeError("Unable to parse an element in Generator: " + ex.getMessage()); + throw new UnmarshallException("Unable to parse element in Generator", ex); + } + } + + /** + * Get the content. + * + * @return The content. + */ + public String getContent() { + return content; + } + + /** + * Set the content. + * + * @param content The content. + */ + public void setContent(String content) { + this.content = content; + } + + /** + * Get the URI. + * + * @return The URI. + */ + public String getUri() { + return uri; + } + + /** + * Set the URI. + * + * @param uri The URI. + */ + public void setUri(String uri) { + this.uri = uri; + } + + /** + * Get the version. + * + * @return The version. + */ + public String getVersion() { + return version; + } + + /** + * Set the version. + * + * @param version The version. + */ + public void setVersion(String version) { + this.version = version; + } + +} diff --git a/dspace-sword/dspace-sword-api/src/main/java/org/w3/atom/InvalidMediaTypeException.java b/dspace-sword/dspace-sword-api/src/main/java/org/w3/atom/InvalidMediaTypeException.java new file mode 100755 index 0000000000..6ead4a4154 --- /dev/null +++ b/dspace-sword/dspace-sword-api/src/main/java/org/w3/atom/InvalidMediaTypeException.java @@ -0,0 +1,55 @@ +/** + * Copyright (c) 2007, Aberystwyth University + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the + * following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * - Neither the name of the Centre for Advanced Software and + * Intelligent Systems (CASIS) nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +package org.w3.atom; + +/** + * An invalid media type has been detected during parsing. + * + * @author Neil Taylor + */ +public class InvalidMediaTypeException extends Exception +{ + /** + * Create a new instance and store the message. + * + * @param message The exception's message. + */ + public InvalidMediaTypeException( String message ) + { + super(message); + } +} diff --git a/dspace-sword/dspace-sword-api/src/main/java/org/w3/atom/Link.java b/dspace-sword/dspace-sword-api/src/main/java/org/w3/atom/Link.java new file mode 100755 index 0000000000..7d945700af --- /dev/null +++ b/dspace-sword/dspace-sword-api/src/main/java/org/w3/atom/Link.java @@ -0,0 +1,351 @@ +/** + * Copyright (c) 2007, Aberystwyth University + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the + * following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * - Neither the name of the Centre for Advanced Software and + * Intelligent Systems (CASIS) nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +package org.w3.atom; + +import nu.xom.Attribute; +import nu.xom.Element; +import nu.xom.Elements; + +import org.purl.sword.base.InfoLogger; +import org.purl.sword.base.Namespaces; +import org.purl.sword.base.SwordElementInterface; +import org.purl.sword.base.UnmarshallException; +import org.purl.sword.base.XmlElement; + +/** + * Represents an ATOM Link element. + * + * @author Neil Taylor + */ +public class Link extends XmlElement implements SwordElementInterface +{ + /** + * Stores the href. + */ + private String href; + + /** + * Stores the Rel attribute. + */ + private String rel; + + /** + * Stores the type. + */ + private String type; + + /** + * Stores the HREF lang. + */ + private String hreflang; + + /** + * Stores the title. + */ + private String title; + + /** + * Stores the length. + */ + private String length; + + /** + * Stores the content. + */ + private String content; + + /** + * Create a new instance and set prefix and local name to 'atom' and 'link', + * respectively. + */ + public Link() + { + super("atom", "link"); + } + + /** + * Mashall the data stored in this object into Element objects. + * + * @return An element that holds the data associated with this object. + */ + public Element marshall() + { + Element element = new Element(getQualifiedName(), Namespaces.NS_ATOM); + + if( content != null ) + { + element.appendChild(content); + } + + if( href != null ) + { + Attribute hrefAttribute = new Attribute("href", href); + element.addAttribute(hrefAttribute); + } + + if( rel != null ) + { + Attribute relAttribute = new Attribute("rel", rel); + element.addAttribute(relAttribute); + } + + if( type != null ) + { + Attribute typeAttribute = new Attribute("type", type); + element.addAttribute(typeAttribute); + } + + if( hreflang != null ) + { + Attribute hreflangAttribute = new Attribute("hreflang", hreflang); + element.addAttribute(hreflangAttribute); + } + + if( title != null ) + { + Attribute titleAttribute = new Attribute("title", title); + element.addAttribute(titleAttribute); + } + + if( length != null ) + { + Attribute lengthAttribute = new Attribute("length", length); + element.addAttribute(lengthAttribute); + } + + return element; + } + + /** + * Unmarshall the contents of the Link element into the internal data objects + * in this object. + * + * @param link The Link element to process. + * + * @throws UnmarshallException If the element does not contain an ATOM link + * element, or if there is a problem processing the element or any + * subelements. + */ + public void unmarshall(Element link) + throws UnmarshallException + { + if( ! isInstanceOf(link, localName, Namespaces.NS_ATOM)) + { + throw new UnmarshallException( "Not an atom:link element" ); + } + + try + { + // get the attributes + int attributeCount = link.getAttributeCount(); + Attribute attribute = null; + for( int i = 0; i < attributeCount; i++ ) + { + attribute = link.getAttribute(i); + if( "href".equals(attribute.getQualifiedName())) + { + href = attribute.getValue(); + } + else if( "rel".equals(attribute.getQualifiedName())) + { + rel = attribute.getValue(); + } + else if( "type".equals(attribute.getQualifiedName())) + { + type = attribute.getValue(); + } + else if( "hreflang".equals(attribute.getQualifiedName())) + { + // FIXME - is this the correct element? + hreflang = attribute.getValue(); + } + else if( "title".equals(attribute.getQualifiedName())) + { + title = attribute.getValue(); + } + else if( "length".equals(attribute.getQualifiedName())) + { + length = attribute.getValue(); + } + } + + // retrieve all of the sub-elements + Elements elements = link.getChildElements(); + Element element = null; + int length = elements.size(); + + for(int i = 0; i < length; i++ ) + { + element = elements.get(i); + content = unmarshallString(element); + // FIXME - is this correct? + + } // for + } + catch( Exception ex ) + { + InfoLogger.getLogger().writeError("Unable to parse an element in Link: " + ex.getMessage()); + throw new UnmarshallException("Unable to parse element in link", ex); + } + } + + /** + * Get the HREF attribute. + * + * @return The HREF. + */ + public String getHref() { + return href; + } + + /** + * Set the HREF attribute. + * + * @param href The href. + */ + public void setHref(String href) { + this.href = href; + } + + /** + * Get the Rel attribute. + * + * @return The Rel. + */ + public String getRel() { + return rel; + } + + /** + * Set the Rel attribute. + * + * @param rel The Rel. + */ + public void setRel(String rel) { + this.rel = rel; + } + + /** + * Get the type. + * + * @return The type. + */ + public String getType() { + return type; + } + + /** + * Set the type. + * @param type The type. + */ + public void setType(String type) { + this.type = type; + } + + /** + * Get the HREF Lang attribute. + * + * @return The HREF Lang. + */ + public String getHreflang() { + return hreflang; + } + + /** + * Set the HREF Lang attribute. + * + * @param hreflang The HREF Lang. + */ + public void setHreflang(String hreflang) { + this.hreflang = hreflang; + } + + /** + * Get the title. + * + * @return The title. + */ + public String getTitle() { + return title; + } + + /** + * Set the title. + * + * @param title The title. + */ + public void setTitle(String title) { + this.title = title; + } + + /** + * Get the length. + * + * @return The length. + */ + public String getLength() { + return length; + } + + /** + * Set the length. + * + * @param length The length. + */ + public void setLength(String length) { + this.length = length; + } + + /** + * Get the content. + * + * @return The content. + */ + public String getContent() { + return content; + } + + /** + * Set the content. + * + * @param content The content. + */ + public void setContent(String content) { + this.content = content; + } + +} diff --git a/dspace-sword/dspace-sword-api/src/main/java/org/w3/atom/Rights.java b/dspace-sword/dspace-sword-api/src/main/java/org/w3/atom/Rights.java new file mode 100755 index 0000000000..cf6401e45b --- /dev/null +++ b/dspace-sword/dspace-sword-api/src/main/java/org/w3/atom/Rights.java @@ -0,0 +1,57 @@ +/** + * Copyright (c) 2007, Aberystwyth University + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the + * following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * - Neither the name of the Centre for Advanced Software and + * Intelligent Systems (CASIS) nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +package org.w3.atom; + +/** + * Represents an ATOM Rights element. This is a simple subclass of the + * TextConstruct class. + * + * @author Neil Taylor + */ +public class Rights extends TextConstruct +{ + + /** + * Create a new instance and set the prefix to + * 'atom' and the local name to 'rights'. + */ + public Rights() + { + super("atom", "rights"); + } + +} diff --git a/dspace-sword/dspace-sword-api/src/main/java/org/w3/atom/Source.java b/dspace-sword/dspace-sword-api/src/main/java/org/w3/atom/Source.java new file mode 100755 index 0000000000..75dbbff4c3 --- /dev/null +++ b/dspace-sword/dspace-sword-api/src/main/java/org/w3/atom/Source.java @@ -0,0 +1,148 @@ +/** + * Copyright (c) 2007, Aberystwyth University + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the + * following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * - Neither the name of the Centre for Advanced Software and + * Intelligent Systems (CASIS) nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +package org.w3.atom; + +import nu.xom.Element; +import nu.xom.Elements; + +import org.purl.sword.base.InfoLogger; +import org.purl.sword.base.Namespaces; +import org.purl.sword.base.SwordElementInterface; +import org.purl.sword.base.UnmarshallException; +import org.purl.sword.base.XmlElement; + +/** + * Represents an ATOM Generator element. + * + * @author Neil Taylor + */ +public class Source extends XmlElement implements SwordElementInterface +{ + /** + * The generator data for this object. + */ + private Generator generator; + + /** + * Create a new instance and set the prefix to + * 'atom' and the local name to 'source'. + */ + public Source() + { + super("atom", "source"); + } + + /** + * Marshall the data stored in this object into Element objects. + * + * @return An element that holds the data associated with this object. + */ + public Element marshall() + { + Element source = new Element(getQualifiedName(), Namespaces.NS_ATOM); + + if( generator != null ) + { + source.appendChild(generator.marshall()); + } + + return source; + } + + /** + * Unmarshall the contents of the source element into the internal data objects + * in this object. + * + * @param source The Source element to process. + * + * @throws UnmarshallException If the element does not contain an ATOM Source + * element, or if there is a problem processing the element or any + * sub-elements. + */ + public void unmarshall(Element source) + throws UnmarshallException + { + if( ! isInstanceOf(source, localName, Namespaces.NS_ATOM)) + { + throw new UnmarshallException( "Not an atom:source element" ); + } + + try + { + // retrieve all of the sub-elements + Elements elements = source.getChildElements(); + Element element = null; + int length = elements.size(); + + for(int i = 0; i < length; i++ ) + { + element = elements.get(i); + if( isInstanceOf(element, "generator", Namespaces.NS_ATOM ) ) + { + generator = new Generator(); + generator.unmarshall(element); + } + } + } + catch( Exception ex ) + { + InfoLogger.getLogger().writeError("Unable to parse an element in Source: " + ex.getMessage()); + ex.printStackTrace(); + throw new UnmarshallException("Unable to parse an element in Source", ex); + } + } + + /** + * Get the generator. + * + * @return The generator. + */ + public Generator getGenerator() + { + return generator; + } + + /** + * Set the generator. + * + * @param generator The generator. + */ + public void setGenerator(Generator generator) + { + this.generator = generator; + } +} diff --git a/dspace-sword/dspace-sword-api/src/main/java/org/w3/atom/Summary.java b/dspace-sword/dspace-sword-api/src/main/java/org/w3/atom/Summary.java new file mode 100755 index 0000000000..b78272f255 --- /dev/null +++ b/dspace-sword/dspace-sword-api/src/main/java/org/w3/atom/Summary.java @@ -0,0 +1,55 @@ +/** + * Copyright (c) 2007, Aberystwyth University + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the + * following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * - Neither the name of the Centre for Advanced Software and + * Intelligent Systems (CASIS) nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +package org.w3.atom; + +/** + * Represents an ATOM Summary element. This is a simple subclass of the + * TextConstruct class. + * + * @author Neil Taylor + */ +public class Summary extends TextConstruct +{ + /** + * Create a new instance and set the prefix to + * 'atom' and the local name to 'summary'. + */ + public Summary() + { + super("atom", "summary"); + } +} diff --git a/dspace-sword/dspace-sword-api/src/main/java/org/w3/atom/TextConstruct.java b/dspace-sword/dspace-sword-api/src/main/java/org/w3/atom/TextConstruct.java new file mode 100755 index 0000000000..0dae4b4fd0 --- /dev/null +++ b/dspace-sword/dspace-sword-api/src/main/java/org/w3/atom/TextConstruct.java @@ -0,0 +1,213 @@ +/** + * Copyright (c) 2007, Aberystwyth University + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the + * following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * - Neither the name of the Centre for Advanced Software and + * Intelligent Systems (CASIS) nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +package org.w3.atom; + +import nu.xom.Attribute; +import nu.xom.Element; + +import org.purl.sword.base.InfoLogger; +import org.purl.sword.base.Namespaces; +import org.purl.sword.base.SwordElementInterface; +import org.purl.sword.base.UnmarshallException; +import org.purl.sword.base.XmlElement; + +/** + * Represents a text construct in the ATOM elements. This is a superclass of + * several elements within this implementation. + * + * @author Neil Taylor + */ +public class TextConstruct extends XmlElement implements SwordElementInterface +{ + /** + * The content in the element. + */ + private String content; + + /** + * The type of the element. + */ + private ContentType type; + + /** + * Create a new instance, specifying the prefix and local name. + * + * @param prefix The prefix. + * @param name The local name. + */ + public TextConstruct(String prefix, String name) + { + super(prefix, name); + } + + /** + * Create a new instance. Set the default type to TextConstructType.TEXT. + * + * @param name The name that will be applied. + */ + public TextConstruct(String name) + { + super(name); + this.type = ContentType.TEXT; + } + + /** + * Marshall the data in this object to an Element object. + * + * @return The data expressed in an Element. + */ + public Element marshall() + { + Element element = new Element(getQualifiedName(), Namespaces.NS_ATOM); + if( type != null ) + { + Attribute typeAttribute = new Attribute("type", type.toString()); + element.addAttribute(typeAttribute); + } + + if( content != null ) + { + element.appendChild(content); + } + return element; + } + + /** + * Unmarshall the text element into this object. + * + * This unmarshaller only handles plain text content, although it can + * recognise the three different type elements of text, html and xhtml. This + * is an area that can be improved in a future implementation, if necessary. + * + * @param text The text element. + * + * @throws UnmarshallException If the specified element is not of + * the correct type, where the localname is used + * to specify the valid name. Also thrown + * if there is an issue accessing the data. + */ + public void unmarshall(Element text) + throws UnmarshallException + { + if( ! isInstanceOf(text, localName, Namespaces.NS_ATOM)) + { + throw new UnmarshallException( "Not a " + getQualifiedName() + " element" ); + } + try + { + // get the attributes + int attributeCount = text.getAttributeCount(); + Attribute attribute = null; + for( int i = 0; i < attributeCount; i++ ) + { + attribute = text.getAttribute(i); + if( "type".equals(attribute.getQualifiedName())) + { + String value = attribute.getValue(); + if( ContentType.TEXT.toString().equals(value) ) + { + type = ContentType.TEXT; + } + else if( ContentType.HTML.toString().equals(value) ) + { + type = ContentType.HTML; + } + else if( ContentType.XHTML.toString().equals(value) ) + { + type = ContentType.XHTML; + } + else + { + InfoLogger.getLogger().writeError("Unable to parse extract type in " + getQualifiedName() ); + // FIXME - check error handling here + } + } + } + + // retrieve all of the sub-elements + int length = text.getChildCount(); + if( length > 0 ) + { + content = unmarshallString(text); + } + // FIXME - the above only handles plain text content. + } + catch( Exception ex ) + { + InfoLogger.getLogger().writeError("Unable to parse an element in " + getQualifiedName() + ": " + ex.getMessage()); + throw new UnmarshallException("Unable to parse an element in " + getQualifiedName(), ex); + } + } + + /** + * Get the content in this TextConstruct. + * + * @return The content, expressed as a string. + */ + public String getContent() { + return content; + } + + /** + * Set the content. This only supports text content. + * + * @param content The content. + */ + public void setContent(String content) { + this.content = content; + } + + /** + * Get the type. + * + * @return The type. + */ + public ContentType getType() + { + return type; + } + + /** + * Set the type. + * + * @param type The type. + */ + public void setType(ContentType type) + { + this.type = type; + } +} diff --git a/dspace-sword/dspace-sword-api/src/main/java/org/w3/atom/Title.java b/dspace-sword/dspace-sword-api/src/main/java/org/w3/atom/Title.java new file mode 100755 index 0000000000..30f414772c --- /dev/null +++ b/dspace-sword/dspace-sword-api/src/main/java/org/w3/atom/Title.java @@ -0,0 +1,55 @@ +/** + * Copyright (c) 2007, Aberystwyth University + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the + * following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * - Neither the name of the Centre for Advanced Software and + * Intelligent Systems (CASIS) nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +package org.w3.atom; + +/** + * Represents an ATOM Title element. This is a simple subclass of the + * TextConstruct class. + * + * @author Neil Taylor + */ +public class Title extends TextConstruct +{ + /** + * Create a new instance and set the prefix to + * 'atom' and the local name to 'title'. + */ + public Title() + { + super("atom", "title"); + } +} diff --git a/dspace-sword/dspace-sword-webapp/pom.xml b/dspace-sword/dspace-sword-webapp/pom.xml new file mode 100644 index 0000000000..15897ee331 --- /dev/null +++ b/dspace-sword/dspace-sword-webapp/pom.xml @@ -0,0 +1,162 @@ + + + 4.0.0 + org.dspace + dspace-sword-webapp + war + DSpace SWORD :: Web Application + DSpace SWORD Deposit Service Provider Web Application + http://www.ukoln.ac.uk/repositories/digirep/index/SWORD + + + + org.dspace + dspace-sword + 1.5-SNAPSHOT + + + + + maven.dspace.org/snapshot + DSpace Maven Snapshot Repository + http://maven.dspace.org/snapshot + + false + fail + + + true + fail + + + + + + + maven.dspace.org/snapshot + DSpace Maven Repository + http://maven.dspace.org/snapshot + + never + fail + + + always + fail + + + + + + + + + scm:svn:http://dspace.svn.sourceforge.net/svnroot/dspace/branches/dspace-1_5_x/dspace-sword/dspace-sword-webapp + + + scm:svn:https://dspace.svn.sourceforge.net/svnroot/dspace/branches/dspace-1_5_x/dspace-sword/dspace-sword-webapp + + + http://dspace.svn.sourceforge.net/viewvc/dspace/branches/dspace-1_5_x/dspace-sword/dspace-sword-webapp + + + + + + + + org.apache.maven.plugins + maven-war-plugin + + false + WEB-INF/lib/*.jar + + + true + ${basedir}/src/main/webapp + + WEB-INF/web.xml + + + + + + + prepare-package + + + + + + + + + + + + + dspace.config + + + + + ${dspace.config} + + + + + oracle-support + + + db.name + oracle + + + + + com.oracle + ojdbc14 + + + + + postgres-support + + + !db.name + + + + + postgresql + postgresql + + + + + + + + org.dspace + dspace-sword-api + 1.5-SNAPSHOT + + + + diff --git a/dspace-sword/dspace-sword-webapp/src/main/webapp/WEB-INF/web.xml b/dspace-sword/dspace-sword-webapp/src/main/webapp/WEB-INF/web.xml new file mode 100644 index 0000000000..0f2efab4a7 --- /dev/null +++ b/dspace-sword/dspace-sword-webapp/src/main/webapp/WEB-INF/web.xml @@ -0,0 +1,72 @@ + + + + + + + + DSpace SWORD Server + + + + + dspace-config + ${dspace.dir}/config/dspace.cfg + + The location of the main DSpace configuration file + + + + + server-class + org.dspace.sword.DSpaceSWORDServer + + The SWORDServer class name + + + + + authentication-method + Basic + + The type of authentication used : [Basic|None] + + + + + + + + load-dspace-config + org.dspace.sword.LoadDSpaceConfig + 1 + + + + servicedocument + org.purl.sword.server.ServiceDocumentServlet + + + + deposit + org.purl.sword.server.DepositServlet + + + + + + servicedocument + /servicedocument + + + + deposit + /deposit/* + + + diff --git a/dspace-sword/example/example.zip b/dspace-sword/example/example.zip new file mode 100644 index 0000000000..3426c6730e Binary files /dev/null and b/dspace-sword/example/example.zip differ diff --git a/dspace-sword/example/mets.xml b/dspace-sword/example/mets.xml new file mode 100644 index 0000000000..7c1c934ea1 --- /dev/null +++ b/dspace-sword/example/mets.xml @@ -0,0 +1,164 @@ + + + + + + Richard Jones + + + + + + + + + + + + + + Attempts to detect retrotransposition + and de novo deletion of Alus and other + dispersed repeats at specific loci in + the human genome + + + + + Dispersed repeat elements contribute to + genome instability by de novo insertion + and unequal recombination between + repeats. To study the dynamics of these + processes, we have developed single DNA + molecule approaches to detect de novo + insertions at a single locus and + Alu-mediated deletions at two different + loci in human genomic DNA. Validation + experiments showed these approaches + could detect insertions and deletions at + frequencies below 10(-6) per cell. + However, bulk analysis of germline + (sperm) and somatic DNA showed no + evidence for genuine mutant molecules, + placing an upper limit of insertion and + deletion rates of 2 x 10(-7) and 3 x + 10(-7), respectively, in the individuals + tested. Such re-arrangements at these + loci therefore occur at a rate lower + than that detectable by the most + sensitive methods currently available. + + + + + Hollies, C.R. + + + + + Monckton, D.G. + + + + + Jeffreys, A.J. + + + + + http://www.myu.ac.uk/some/identifier + + + + + + + + + en + + + + + 2001-02 + + + + + + Nature Publishing Group + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ +
+
+ +
+
+
+ +
diff --git a/dspace-sword/example/pdf1.pdf b/dspace-sword/example/pdf1.pdf new file mode 100644 index 0000000000..fba463b539 Binary files /dev/null and b/dspace-sword/example/pdf1.pdf differ diff --git a/dspace-sword/example/pdf2.pdf b/dspace-sword/example/pdf2.pdf new file mode 100644 index 0000000000..f996f74231 Binary files /dev/null and b/dspace-sword/example/pdf2.pdf differ diff --git a/dspace-sword/example/pdf3.pdf b/dspace-sword/example/pdf3.pdf new file mode 100644 index 0000000000..733ee72393 Binary files /dev/null and b/dspace-sword/example/pdf3.pdf differ diff --git a/dspace-sword/pom.xml b/dspace-sword/pom.xml new file mode 100644 index 0000000000..c164d93880 --- /dev/null +++ b/dspace-sword/pom.xml @@ -0,0 +1,79 @@ + + + 4.0.0 + org.dspace + dspace-sword + pom + DSpace SWORD + DSpace SWORD Deposit Service Provider Web Application + http://www.ukoln.ac.uk/repositories/digirep/index/SWORD + + + + org.dspace + dspace-parent + 1.5-SNAPSHOT + + + + + maven.dspace.org/snapshot + DSpace Maven Snapshot Repository + http://maven.dspace.org/snapshot + + false + fail + + + true + fail + + + + + + + maven.dspace.org/snapshot + DSpace Maven Repository + http://maven.dspace.org/snapshot + + never + fail + + + always + fail + + + + + + + + + scm:svn:http://dspace.svn.sourceforge.net/svnroot/dspace/branches/dspace-1_5_x/dspace-sword + + + scm:svn:https://dspace.svn.sourceforge.net/svnroot/dspace/branches/dspace-1_5_x/dspace-sword + + + http://dspace.svn.sourceforge.net/viewvc/dspace/branches/dspace-1_5_x/dspace-sword + + + + + + dspace-sword-api + dspace-sword-webapp + + + diff --git a/dspace/config/crosswalks/sword-swap-ingest.xsl b/dspace/config/crosswalks/sword-swap-ingest.xsl new file mode 100644 index 0000000000..943a304fb7 --- /dev/null +++ b/dspace/config/crosswalks/sword-swap-ingest.xsl @@ -0,0 +1,144 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + dc + identifier + + uri + + + + + + + + + + + + + + + + + Journal Article + + + + + + + + + + + + + + + + Peer Reviewed + + + + + + + + + + + + + + \ No newline at end of file diff --git a/dspace/config/dspace.cfg b/dspace/config/dspace.cfg index 3a6fdc4d7a..506db13a41 100644 --- a/dspace/config/dspace.cfg +++ b/dspace/config/dspace.cfg @@ -1068,3 +1068,58 @@ event.consumer.eperson.filters = EPerson+Create #xmlui.bitstream.mets = true +##################################################################### +# SWORD Configuration +##################################################################### +# +# tell the SWORD METS implementation which package ingester to use +# to install deposited content. This should refer to one of the +# classes configured for: +# +# plugin.named.org.dspace.content.packager.PackageIngester +# +# The value of sword.mets-ingester.package-ingester tells the +# system which named plugin for this interface should be used +# to ingest SWORD METS packages +# +# The default is METS +# +# sword.mets-ingester.package-ingester = METS + +# Define the metadata type EPDCX (EPrints DC XML) +# to be handled by the SWORD crosswalk configuration +# +mets.submission.crosswalk.EPDCX = SWORD + +# define the stylesheet which will be used by the self-named +# XSLTIngestionCrosswalk class when asked to load the SWORD +# configuration (as specified above). This will use the +# specified stylesheet to crosswalk the incoming SWAP metadata +# to the DIM format for ingestion +# +crosswalk.submission.SWORD.stylesheet = crosswalks/sword-swap-ingest.xsl + +# The base URL of the SWORD deposit. This is the URL from +# which DSpace will construct the deposit location urls for +# collections. +# +# The default is {dspace.url}/dspace-sword/deposit +# +# In the event that you are not deploying DSpace as the ROOT +# application in the servlet container, this will generate +# incorrect URLs, and you should override the functionality +# by specifying in full as below: +# +# sword.deposit.url = http://www.myu.ac.uk/dspace-sword/deposit + +# The metadata field in which to store the updated date for +# items deposited via SWORD. +# +sword.updated.field = dc.date.updated + +# The metadata field in which to store the value of the slug +# header if it is supplied +# +sword.slug.field = dc.identifier.slug + +##################################################################### \ No newline at end of file diff --git a/dspace/pom.xml b/dspace/pom.xml index 46e0fc1abc..02f2f534f9 100644 --- a/dspace/pom.xml +++ b/dspace/pom.xml @@ -212,6 +212,54 @@ + + + + sword-api + + + ../dspace-sword-api/pom.xml + + + + ../dspace-sword-api + + + + + + + sword-webapp + + + ../dspace-sword-webapp/pom.xml + + + + ../dspace-sword-webapp + + + + + + + sword + + + ../dspace-sword/pom.xml + + + + ../dspace-sword + + + diff --git a/dspace/src/main/config/build.xml b/dspace/src/main/config/build.xml index 5df443bd63..a9ce600345 100644 --- a/dspace/src/main/config/build.xml +++ b/dspace/src/main/config/build.xml @@ -226,7 +226,7 @@ Common usage: UI web applications from ${dspace.dir}/webapps/, then you can jump straight to restarting your Web servlet container - * Otherwise, you will need to copy any web applications from + * Otherwise, you will need to copy any web applications from ${dspace.dir}/webapps/ to the appropriate place for your servlet container. (e.g. '$CATALINA_HOME/webapps' for Tomcat) @@ -364,8 +364,17 @@ Common usage: - - + + + + + + + + + + + diff --git a/pom.xml b/pom.xml index 0262ae44f0..15c9b529d7 100644 --- a/pom.xml +++ b/pom.xml @@ -21,6 +21,7 @@ dspace-xmlui dspace-lni dspace-oai + dspace-sword @@ -135,6 +136,16 @@ language-packs ${project.version} + + org.dspace + dspace-sword-api + ${project.version} + + + org.dspace + dspace-sword-webapp + ${project.version} + org.dspace dspace-jspui-api