/** * The contents of this file are subject to the license and copyright * detailed in the LICENSE and NOTICE files at the root of the source * tree and available online at * * http://www.dspace.org/license/ */ package org.dspace.sword2; import org.apache.abdera.i18n.iri.IRI; import org.dspace.content.Bitstream; import org.dspace.content.Bundle; import org.dspace.content.Collection; import org.dspace.content.Community; import org.dspace.content.DSpaceObject; import org.dspace.content.Item; import org.dspace.handle.HandleManager; import org.dspace.core.ConfigurationManager; import org.dspace.core.Context; import org.swordapp.server.SwordError; import java.sql.SQLException; import java.net.URL; import java.net.MalformedURLException; /** * @author Richard Jones * * Class responsible for constructing and de-constructing sword url space * urls */ public class SwordUrlManager { /** the sword configuration */ private SwordConfigurationDSpace config; /** the active dspace context */ private Context context; public SwordUrlManager(SwordConfigurationDSpace config, Context context) { this.config = config; this.context = context; } /** * 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 getDepositLocation(Collection collection) throws DSpaceSwordException { return this.getBaseCollectionUrl() + "/" + collection.getHandle(); } /** * Obtain the deposit URL for the given community. These URLs * should not be considered persistent, but will remain consistent * unless configuration changes are made to DSpace * * @param community * @return The Deposit URL * @throws DSpaceSwordException */ public String getDepositLocation(Community community) throws DSpaceSwordException { if (this.config.allowCommunityDeposit()) { return this.getBaseCollectionUrl() + "/" + community.getHandle(); } return null; } public String getSwordBaseUrl() throws DSpaceSwordException { String sUrl = ConfigurationManager.getProperty("swordv2-server", "url"); if (sUrl == null || "".equals(sUrl)) { String dspaceUrl = ConfigurationManager.getProperty("dspace.baseUrl"); if (dspaceUrl == null || "".equals(dspaceUrl)) { throw new DSpaceSwordException("Unable to construct service document urls, due to missing/invalid " + "config in sword2.url and/or dspace.baseUrl"); } try { URL url = new URL(dspaceUrl); sUrl = new URL(url.getProtocol(), url.getHost(), url.getPort(), "/swordv2").toString(); } catch (MalformedURLException e) { throw new DSpaceSwordException("Unable to construct service document urls, due to invalid dspace.baseUrl " + e.getMessage(),e); } } return sUrl; } public Item getItem(Context context, String location) throws DSpaceSwordException, SwordError { try { String baseUrl = this.getSwordBaseUrl(); String emBaseUrl = baseUrl + "/edit-media/"; String eBaseUrl = baseUrl + "/edit/"; String sBaseUrl = baseUrl + "/statement/"; String cBaseUrl = null; if (location.startsWith(emBaseUrl)) { cBaseUrl = emBaseUrl; } else if (location.startsWith(eBaseUrl)) { cBaseUrl = eBaseUrl; } else if (location.startsWith(sBaseUrl)) { cBaseUrl = sBaseUrl; } else { throw new SwordError(DSpaceUriRegistry.BAD_URL, "The item URL is invalid"); } String iid = location.substring(cBaseUrl.length()); if (iid.endsWith(".atom")) { // this is the atom url, so we need to strip that to ge tthe item id iid = iid.substring(0, iid.length() - ".atom".length()); } else if (iid.endsWith(".rdf")) { // this is the rdf url so we need to strip that to get the item id iid = iid.substring(0, iid.length() - ".rdf".length()); } int itemId = Integer.parseInt(iid); Item item = Item.find(context, itemId); return item; } catch (SQLException e) { // log.error("Caught exception:", e); throw new DSpaceSwordException("There was a problem resolving the collection", e); } } public String getTypeSuffix(Context context, String location) { String tail = location.substring(location.lastIndexOf("/")); int typeSeparator = tail.lastIndexOf("."); if (typeSeparator == -1) { return null; } return tail.substring(typeSeparator + 1); } public boolean isFeedRequest(Context context, String url) { return url.endsWith(".atom"); } /** * 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 */ // FIXME: we need to generalise this to DSpaceObjects, so that we can support // Communities, Collections and Items separately public Collection getCollection(Context context, String location) throws DSpaceSwordException, SwordError { try { String baseUrl = this.getBaseCollectionUrl(); if (baseUrl.length() == location.length()) { throw new SwordError(DSpaceUriRegistry.BAD_URL, "The deposit URL is incomplete"); } String handle = location.substring(baseUrl.length()); if (handle.startsWith("/")) { handle = handle.substring(1); } if ("".equals(handle)) { throw new SwordError(DSpaceUriRegistry.BAD_URL, "The deposit URL is incomplete"); } DSpaceObject dso = HandleManager.resolveToObject(context, handle); if (!(dso instanceof Collection)) { throw new SwordError(DSpaceUriRegistry.BAD_URL, "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); } } /** * Construct the service document url for the given object, which will * be supplied in the sword:service element of other service document entries * * @param community * @return * @throws DSpaceSwordException */ public String constructSubServiceUrl(Community community) throws DSpaceSwordException { String base = this.getBaseServiceDocumentUrl(); String handle = community.getHandle(); return base + "/" + handle; } /** * Construct the service document url for the given object, which will * be supplied in the sword:service element of other service document entries * * @param collection * @return * @throws DSpaceSwordException */ public String constructSubServiceUrl(Collection collection) throws DSpaceSwordException { String base = this.getBaseServiceDocumentUrl(); String handle = collection.getHandle(); return base + "/" + handle; } /** * Extract a DSpaceObject from the given url. If this method is unable to * locate a meaningful and appropriate dspace object it will throw the * appropriate sword error * @param url * @return * @throws DSpaceSwordException * @throws SwordError */ public DSpaceObject extractDSpaceObject(String url) throws DSpaceSwordException, SwordError { try { String sdBase = this.getBaseServiceDocumentUrl(); // String mlBase = this.getBaseMediaLinkUrl(); if (url.startsWith(sdBase)) { // we are dealing with a service document request // first, let's find the beginning of the handle url = url.substring(sdBase.length()); if (url.startsWith("/")) { url = url.substring(1); } if (url.endsWith("/")) { url = url.substring(0, url.length() - 1); } DSpaceObject dso = HandleManager.resolveToObject(context, url); if (dso instanceof Collection || dso instanceof Community) { return dso; } else { throw new SwordError(DSpaceUriRegistry.BAD_URL, "Service Document request does not refer to a DSpace Collection or Community"); } } else { throw new SwordError(DSpaceUriRegistry.BAD_URL, "Unable to recognise URL as a valid service document: " + url); } } catch (SQLException e) { throw new DSpaceSwordException(e); } } /** * get the base url for service document requests * * @return * @throws DSpaceSwordException */ public String getBaseServiceDocumentUrl() throws DSpaceSwordException { String sdUrl = ConfigurationManager.getProperty("swordv2-server", "servicedocument.url"); if (sdUrl == null || "".equals(sdUrl)) { String dspaceUrl = ConfigurationManager.getProperty("dspace.baseUrl"); if (dspaceUrl == null || "".equals(dspaceUrl)) { throw new DSpaceSwordException("Unable to construct service document urls, due to missing/invalid " + "config in swordv2-server.cfg servicedocument.url and/or dspace.baseUrl"); } try { URL url = new URL(dspaceUrl); sdUrl = new URL(url.getProtocol(), url.getHost(), url.getPort(), "/swordv2/servicedocument").toString(); } catch (MalformedURLException e) { throw new DSpaceSwordException("Unable to construct service document urls, due to invalid dspace.baseUrl " + e.getMessage(),e); } } return sdUrl; } /** * 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.baseUrl]/sword/deposit * * where dspace.baseUrl is also in the configuration file. * * @return the base URL for sword deposit * @throws DSpaceSwordException */ public String getBaseCollectionUrl() throws DSpaceSwordException { String depositUrl = ConfigurationManager.getProperty("swordv2-server", "collection.url"); if (depositUrl == null || "".equals(depositUrl)) { String dspaceUrl = ConfigurationManager.getProperty("dspace.baseUrl"); if (dspaceUrl == null || "".equals(dspaceUrl)) { throw new DSpaceSwordException("Unable to construct deposit urls, due to missing/invalid config in " + "swordv2-server.cfg deposit.url and/or dspace.baseUrl"); } try { URL url = new URL(dspaceUrl); depositUrl = new URL(url.getProtocol(), url.getHost(), url.getPort(), "/swordv2/collection").toString(); } catch (MalformedURLException e) { throw new DSpaceSwordException("Unable to construct deposit urls, due to invalid dspace.baseUrl " + e.getMessage(),e); } } return depositUrl; } /** * is the given url the base service document url * * @param url * @return * @throws DSpaceSwordException */ public boolean isBaseServiceDocumentUrl(String url) throws DSpaceSwordException { return this.getBaseServiceDocumentUrl().equals(url); } /** * Central location for constructing usable urls for dspace bitstreams. There * is no place in the main DSpace code base for doing this. * * @param bitstream * @return * @throws DSpaceSwordException */ public String getBitstreamUrl(Bitstream bitstream) throws DSpaceSwordException { try { Bundle[] bundles = bitstream.getBundles(); Bundle parent = null; if (bundles.length > 0) { parent = bundles[0]; } else { throw new DSpaceSwordException("Encountered orphaned bitstream"); } Item[] items = parent.getItems(); Item item; if (items.length > 0) { item = items[0]; } else { throw new DSpaceSwordException("Encountered orphaned bundle"); } String handle = item.getHandle(); String bsLink = ConfigurationManager.getProperty("dspace.url"); if (handle != null && !"".equals(handle)) { bsLink = bsLink + "/bitstream/" + handle + "/" + bitstream.getSequenceID() + "/" + bitstream.getName(); } else { bsLink = bsLink + "/retrieve/" + bitstream.getID() + "/" + bitstream.getName(); } return bsLink; } catch (SQLException e) { throw new DSpaceSwordException(e); } } public String getActionableBitstreamUrl(Bitstream bitstream) throws DSpaceSwordException { return this.getSwordBaseUrl() + "/edit-media/bitstream/" + bitstream.getID() + "/" + bitstream.getName(); } public boolean isActionableBitstreamUrl(Context context, String url) { return url.contains("/edit-media/bitstream/"); } public Bitstream getBitstream(Context context, String location) throws DSpaceSwordException, SwordError { try { String baseUrl = this.getSwordBaseUrl(); String emBaseUrl = baseUrl + "/edit-media/bitstream/"; if (!location.startsWith(emBaseUrl)) { throw new SwordError(DSpaceUriRegistry.BAD_URL, "The bitstream URL is invalid"); } String bitstreamParts = location.substring(emBaseUrl.length()); // the bitstream id is the part up to the first "/" int firstSlash = bitstreamParts.indexOf("/"); int bid = Integer.parseInt(bitstreamParts.substring(0, firstSlash)); String fn = bitstreamParts.substring(firstSlash + 1); Bitstream bitstream = Bitstream.find(context, bid); return bitstream; } catch (SQLException e) { // log.error("Caught exception:", e); throw new DSpaceSwordException("There was a problem resolving the collection", e); } } /** * get the media link url for the given bitstream * * @param bitstream * @return * @throws DSpaceSwordException */ public String getMediaLink(Bitstream bitstream) throws DSpaceSwordException { // try // { // Bundle[] bundles = bitstream.getBundles(); // Bundle parent = null; // if (bundles.length > 0) // { // parent = bundles[0]; // } // else // { // throw new DSpaceSwordException("Encountered orphaned bitstream"); // } // // Item[] items = parent.getItems(); // Item item; // if (items.length > 0) // { // item = items[0]; // } // else // { // throw new DSpaceSwordException("Encountered orphaned bundle"); // } // // String itemUrl = this.getMediaLink(item); // if (itemUrl.equals(this.getBaseMediaLinkUrl())) // { // return itemUrl; // } // // String bsUrl = itemUrl + "/bitstream/" + bitstream.getID(); // // return bsUrl; // } // catch (SQLException e) // { // throw new DSpaceSWORDException(e); // } return null; } // FIXME: we need a totally new kind of URL scheme; perhaps we write the identifier into the item public String getAtomStatementUri(Item item) throws DSpaceSwordException { return this.getSwordBaseUrl() + "/statement/" + item.getID() + ".atom"; } public String getOreStatementUri(Item item) throws DSpaceSwordException { return this.getSwordBaseUrl() + "/statement/" + item.getID() + ".rdf"; } public String getAggregationUrl(Item item) throws DSpaceSwordException { return this.getOreStatementUri(item) + "#aggregation"; } public IRI getEditIRI(Item item) throws DSpaceSwordException { return new IRI(this.getSwordBaseUrl() + "/edit/" + item.getID()); } public String getSplashUrl(Item item) { // FIXME: this appears not to return the item's handle return HandleManager.getCanonicalForm(item.getHandle()); } public IRI getContentUrl(Item item) throws DSpaceSwordException { return new IRI(this.getSwordBaseUrl() + "/edit-media/" + item.getID()); } public IRI getMediaFeedUrl(Item item) throws DSpaceSwordException { return new IRI(this.getSwordBaseUrl() + "/edit-media/" + item.getID() + ".atom"); } }