/** * 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.log4j.Logger; import org.dspace.content.BitstreamFormat; import org.dspace.content.Collection; import org.dspace.content.DSpaceObject; import org.dspace.content.Item; import org.dspace.content.factory.ContentServiceFactory; import org.dspace.content.service.BitstreamFormatService; import org.dspace.core.Context; import org.swordapp.server.SwordConfiguration; import org.swordapp.server.SwordError; import java.sql.SQLException; import java.util.ArrayList; import java.util.List; import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.ArrayUtils; import org.dspace.services.ConfigurationService; import org.dspace.services.factory.DSpaceServicesFactory; public class SwordConfigurationDSpace implements SwordConfiguration { /** logger */ public static final Logger log = Logger .getLogger(SwordConfigurationDSpace.class); protected BitstreamFormatService bitstreamFormatService = ContentServiceFactory .getInstance().getBitstreamFormatService(); protected ConfigurationService configurationService = DSpaceServicesFactory .getInstance().getConfigurationService(); /** whether we can be verbose */ private boolean verbose = true; /** what our default max upload size is */ private int maxUploadSize = -1; /** do we support mediation */ private boolean mediated = false; /** should we keep the original package as bitstream */ private boolean keepOriginal = false; /** item bundle in which sword deposits are stored */ private String swordBundle = "SWORD"; /** should we keep the original package as a file on ingest error */ private boolean keepPackageOnFailedIngest = false; /** location of directory to store packages on ingest error */ private String failedPackageDir = null; private boolean allowCommunityDeposit = false; private boolean entryFirst = false; /** Accepted formats */ private List<String> swordaccepts; /** * Initialise the sword configuration. It is at this stage that the * object will interrogate the DSpace Configuration for details */ public SwordConfigurationDSpace() { // set the max upload size int mus = configurationService .getIntProperty("swordv2-server.max-upload-size"); if (mus > 0) { this.maxUploadSize = mus; } // set the mediation value this.mediated = configurationService .getBooleanProperty("swordv2-server.on-behalf-of.enable", false); // find out if we keep the original as bitstream this.keepOriginal = configurationService .getBooleanProperty("swordv2-server.keep-original-package"); // get the sword bundle String bundle = configurationService .getProperty("swordv2-server.bundle.name"); if (StringUtils.isBlank(bundle)) { this.swordBundle = bundle; } // find out if we keep the package as a file in specified directory this.keepPackageOnFailedIngest = configurationService .getBooleanProperty("swordv2-server.keep-package-on-fail", false); // get directory path and name this.failedPackageDir = configurationService .getProperty("swordv2-server.failed-package.dir"); // Get the accepted formats String[] acceptsFormats = configurationService .getArrayProperty("swordv2-server.accepts"); swordaccepts = new ArrayList<String>(); if (ArrayUtils.isEmpty(acceptsFormats)) { acceptsFormats = new String[]{"application/zip"}; } for (String element : acceptsFormats) { swordaccepts.add(element.trim()); } // find out if community deposit is allowed this.allowCommunityDeposit = configurationService .getBooleanProperty("swordv2-server.allow-community-deposit"); // find out if we keep the package as a file in specified directory this.entryFirst = configurationService .getBooleanProperty("swordv2-server.multipart.entry-first", false); } //////////////////////////////////////////////////////////////////////////////////// // Utilities /////////////////////////////////////////////////////////////////////////////////// public String getStringProperty(String propName, String defaultValue, String[] allowedValues) { String cfg = configurationService.getProperty(propName); if (StringUtils.isBlank(cfg)) { return defaultValue; } boolean allowed = false; if (allowedValues != null) { for (String value : allowedValues) { if (cfg.equals(value)) { allowed = true; } } } else { allowed = true; } if (allowed) { return cfg; } return defaultValue; } public String getStringProperty(String propName, String defaultValue) { return this.getStringProperty(propName, defaultValue, null); } /////////////////////////////////////////////////////////////////////////////////// // Required by the SwordConfiguration interface ////////////////////////////////////////////////////////////////////////////////// public boolean returnDepositReceipt() { return true; } public boolean returnStackTraceInError() { return configurationService.getBooleanProperty("swordv2-server.verbose-description.error.enable"); } public boolean returnErrorBody() { return true; } public String generator() { return this.getStringProperty("swordv2-server.generator.url", DSpaceUriRegistry.DSPACE_SWORD_NS); } public String generatorVersion() { return this.getStringProperty("swordv2-server.generator.version", "2.0"); } public String administratorEmail() { return this.getStringProperty("mail.admin", null); } public String getAuthType() { return this.getStringProperty("swordv2-server.auth-type", "Basic", new String[] { "Basic", "None" }); } public boolean storeAndCheckBinary() { return true; } public String getTempDirectory() { return this.getStringProperty("swordv2-server.upload.tempdir", null); } public String getAlternateUrl() { return configurationService .getProperty("swordv2-server.error.alternate.url"); } public String getAlternateUrlContentType() { return configurationService .getProperty("swordv2-server.error.alternate.content-type"); } /////////////////////////////////////////////////////////////////////////////////// // Required by DSpace-side implementation /////////////////////////////////////////////////////////////////////////////////// public SwordUrlManager getUrlManager(Context context, SwordConfigurationDSpace config) { return new SwordUrlManager(config, context); } public List<String> getDisseminatePackaging() throws DSpaceSwordException, SwordError { List<String> dps = new ArrayList<String>(); List<String> packagingFormats = configurationService.getPropertyKeys("swordv2-server.disseminate-packaging"); for (String key : packagingFormats) { String value = configurationService.getProperty(key); // now we want to ensure that the packaging format we offer has a disseminator // associated with it boolean disseminable = true; try { SwordContentDisseminator disseminator = SwordDisseminatorFactory .getContentInstance(null, value); } catch (SwordError e) { disseminable = false; } if (disseminable) { dps.add(value); } } return dps; } public boolean isEntryFirst() { return this.entryFirst; } public boolean allowCommunityDeposit() { return this.allowCommunityDeposit; } /** * Get the bundle name that SWORD will store its original deposit packages in, when * storing them inside an item * * @return SWORD bundle name */ public String getSwordBundle() { return swordBundle; } /** * Set the bundle name that SWORD will store its original deposit packages in, when * storing them inside an item * * @param swordBundle SWORD bundle name */ public void setSwordBundle(String swordBundle) { this.swordBundle = swordBundle; } /** * Is this a verbose deposit? * * @return true if this is verbose deposit */ public boolean isVerbose() { return verbose; } /** * Set whether this is a verbose deposit. * * @param verbose * verbose deposit */ public void setVerbose(boolean verbose) { this.verbose = verbose; } /** * What is the max upload size (in bytes) for the SWORD interface? * * @return max upload size */ public int getMaxUploadSize() { return maxUploadSize; } /** * Set the max upload size (in bytes) for the SWORD interface. * * @param maxUploadSize * max upload size to set */ public void setMaxUploadSize(int maxUploadSize) { this.maxUploadSize = maxUploadSize; } /** * Does the server support mediated deposit (aka on-behalf-of)? * * @return true if server supports mediated deposit */ public boolean isMediated() { return mediated; } /** * Set whether the server supports mediated deposit (aka on-behalf-of). * * @param mediated * mediated deposit state to set */ public void setMediated(boolean mediated) { this.mediated = mediated; } /** * Should the repository keep the original package? * * @return true if should keep original package */ public boolean isKeepOriginal() { return keepOriginal; } /** * set whether the repository should keep copies of the original package * * @param keepOriginal * should keep original package? */ public void setKeepOriginal(boolean keepOriginal) { this.keepOriginal = keepOriginal; } /** * set whether the repository should write file of the original package if ingest fails * * @param keepOriginalOnFail * should keep original package if ingest fails? */ public void setKeepPackageOnFailedIngest(boolean keepOriginalOnFail) { keepPackageOnFailedIngest = keepOriginalOnFail; } /** * should the repository write file of the original package if ingest fails * * @return whether the repository should keep original package if ingest fails */ public boolean isKeepPackageOnFailedIngest() { return keepPackageOnFailedIngest; } /** * set the directory to write file of the original package * * @param dir * directory where to store the original package */ public void setFailedPackageDir(String dir) { failedPackageDir = dir; } /** * directory location of the files with original packages * for failed ingests * * @return directory where to store the original package */ public String getFailedPackageDir() { return failedPackageDir; } /** * Get the list of MIME types that the given DSpace object will * accept as packages. * * @param context * The relevant DSpace Context. * @param dso * DSpace object to check * @return list of MIME types that the given DSpace object will * accept as packages. * @throws DSpaceSwordException * can be thrown by the internals of the DSpace SWORD implementation */ public List<String> getAccepts(Context context, DSpaceObject dso) throws DSpaceSwordException { try { List<String> accepts = new ArrayList<String>(); if (dso instanceof Collection) { for (String format : swordaccepts) { accepts.add(format); } } else if (dso instanceof Item) { // items will take any of the bitstream formats registered, plus // any swordaccepts mimetypes List<BitstreamFormat> bfs = bitstreamFormatService .findNonInternal(context); for (BitstreamFormat bf : bfs) { accepts.add(bf.getMIMEType()); } for (String format : swordaccepts) { if (!accepts.contains(format)) { accepts.add(format); } } } return accepts; } catch (SQLException e) { throw new DSpaceSwordException(e); } } /** * Get the list of mime types that a Collection will accept as packages * * @return the list of mime types * @throws DSpaceSwordException * can be thrown by the internals of the DSpace SWORD implementation */ public List<String> getCollectionAccepts() throws DSpaceSwordException { List<String> accepts = new ArrayList<String>(); for (String format : swordaccepts) { accepts.add(format); } return accepts; } /** * Get a map of packaging URIs to Q values for the packaging types which * the given collection will accept. * * The URI should be a unique identifier for the packaging type, * such as: * * http://purl.org/net/sword-types/METSDSpaceSIP * * and the Q value is a floating point between 0 and 1 which defines * how much the server "likes" this packaging type. * * @param col * target collection * @return map of packaging URIs to Q values for the packaging types which * the given collection will accept. */ public List<String> getAcceptPackaging(Collection col) { // accept-packaging.METSDSpaceSIP = http://purl.org/net/sword-types/METSDSpaceSIP // accept-packaging.[handle].METSDSpaceSIP = http://purl.org/net/sword-types/METSDSpaceSIP String handle = col.getHandle(); List<String> aps = new ArrayList<String>(); // build the holding maps of identifiers String acceptPackagingPrefix = "swordv2-server.accept-packaging.collection"; List<String> acceptFormats = configurationService.getPropertyKeys(acceptPackagingPrefix); for (String key : acceptFormats) { // extract the configuration into the holding Maps // the suffix will be [typeid] or [handle].[typeid] String suffix = key.substring(acceptPackagingPrefix.length()+1); // is there a handle which represents this collection? boolean withHandle = false; if (suffix.startsWith(handle)) { withHandle = true; } // is there NO handle boolean general = false; if (suffix.indexOf(".") == -1) { // a handle would be separated from the identifier of the package type general = true; } if (withHandle || general) { String value = configurationService.getProperty(key); aps.add(value); } } return aps; } public List<String> getItemAcceptPackaging() { List<String> aps = new ArrayList<String>(); // build the holding maps of identifiers String acceptPackagingPrefix = "swordv2-server.accept-packaging.item"; List<String> acceptFormats = configurationService.getPropertyKeys(acceptPackagingPrefix); for (String key : acceptFormats) { // extract the configuration into the holding Maps String value = configurationService.getProperty(key); aps.add(value); } return aps; } /** * Is the given packaging/media type supported by the given DSpace * object? * * @param packageFormat * packaging/media type to check * @param dso * DSpace object to check * @return true if supported * @throws DSpaceSwordException * can be thrown by the internals of the DSpace SWORD implementation * @throws SwordError * SWORD error per SWORD spec */ public boolean isAcceptedPackaging(String packageFormat, DSpaceObject dso) throws DSpaceSwordException, SwordError { if (packageFormat == null || "".equals(packageFormat)) { return true; } if (dso instanceof Collection) { List<String> accepts = this.getAcceptPackaging((Collection) dso); for (String accept : accepts) { if (accept.equals(packageFormat)) { return true; } } } else if (dso instanceof Item) { List<String> accepts = this.getItemAcceptPackaging(); for (String accept : accepts) { if (accept.equals(packageFormat)) { return true; } } } return false; } /** * Is the given content MIME type acceptable to the given DSpace object. * * @param context * The relevant DSpace Context. * @param type * MIME type to check * @param dso * DSpace object to compare to * @return true if acceptable * @throws DSpaceSwordException * can be thrown by the internals of the DSpace SWORD implementation * can be thrown by the internals of the DSpace SWORD implementation */ public boolean isAcceptableContentType(Context context, String type, DSpaceObject dso) throws DSpaceSwordException { List<String> accepts = this.getAccepts(context, dso); for (String acc : accepts) { if (this.contentTypeMatches(type, acc)) { return true; } } return accepts.contains(type); } private boolean contentTypeMatches(String type, String pattern) { if ("*/*".equals(pattern.trim())) { return true; } // get the prefix and suffix match patterns String[] bits = pattern.trim().split("/"); String prefixPattern = bits.length > 0 ? bits[0] : "*"; String suffixPattern = bits.length > 1 ? bits[1] : "*"; // get the incoming type prefix and suffix String[] tbits = type.trim().split("/"); String typePrefix = tbits.length > 0 ? tbits[0] : "*"; String typeSuffix = tbits.length > 1 ? tbits[1] : "*"; boolean prefixMatch = false; boolean suffixMatch = false; if ("*".equals(prefixPattern) || prefixPattern.equals(typePrefix)) { prefixMatch = true; } if ("*".equals(suffixPattern) || suffixPattern.equals(typeSuffix)) { suffixMatch = true; } return prefixMatch && suffixMatch; } public String getStateUri(String state) { return configurationService .getProperty("swordv2-server.state." + state + ".uri"); } public String getStateDescription(String state) { return configurationService .getProperty("swordv2-server.state." + state + ".description"); } public boolean allowUnauthenticatedMediaAccess() { return false; } }