/** * 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.authorize.AuthorizeException; import org.dspace.content.*; import org.dspace.content.factory.ContentServiceFactory; import org.dspace.content.service.WorkspaceItemService; import org.dspace.core.Context; import org.dspace.services.ConfigurationService; import org.dspace.services.factory.DSpaceServicesFactory; import org.swordapp.server.*; import java.sql.SQLException; import java.util.Date; import java.util.List; import java.util.Map; import org.apache.commons.lang.StringUtils; public class SimpleDCEntryIngester extends AbstractSimpleDC implements SwordEntryIngester { private static final Logger log = Logger.getLogger(SimpleDCEntryIngester.class); protected WorkspaceItemService workspaceItemService = ContentServiceFactory.getInstance().getWorkspaceItemService(); protected ConfigurationService configurationService = DSpaceServicesFactory.getInstance().getConfigurationService(); public SimpleDCEntryIngester() { this.loadMetadataMaps(); } public DepositResult ingest(Context context, Deposit deposit, DSpaceObject dso, VerboseDescription verboseDescription) throws DSpaceSwordException, SwordError, SwordAuthException, SwordServerException { return this.ingest( context, deposit, dso, verboseDescription, null, false); } public DepositResult ingest(Context context, Deposit deposit, DSpaceObject dso, VerboseDescription verboseDescription, DepositResult result, boolean replace) throws DSpaceSwordException, SwordError, SwordAuthException, SwordServerException { if (dso instanceof Collection) { return this.ingestToCollection(context, deposit, (Collection) dso, verboseDescription, result); } else if (dso instanceof Item) { return this.ingestToItem(context, deposit, (Item) dso, verboseDescription, result, replace); } return null; } public DepositResult ingestToItem(Context context, Deposit deposit, Item item, VerboseDescription verboseDescription, DepositResult result, boolean replace) throws DSpaceSwordException, SwordError, SwordAuthException, SwordServerException { try { if (result == null) { result = new DepositResult(); } result.setItem(item); // clean out any existing item metadata which is allowed to be replaced if (replace) { this.removeMetadata(context, item); } // add the metadata to the item this.addMetadataToItem(context, deposit, item); // update the item metadata to inclue the current time as // the updated date this.setUpdatedDate(context, item, verboseDescription); // 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. context.turnOffAuthorisationSystem(); itemService.update(context, item); context.restoreAuthSystemState(); verboseDescription.append("Update successful"); result.setItem(item); result.setTreatment(this.getTreatment()); return result; } catch (SQLException | AuthorizeException e) { throw new DSpaceSwordException(e); } } private void removeMetadata(Context context, Item item) throws DSpaceSwordException { String[] replaceableMetadata = configurationService .getArrayProperty("swordv2-server.metadata.replaceable"); for (String part : replaceableMetadata) { MetadataValueInfo info = this.makeMetadataValueInfo(part.trim(), null); try { itemService.clearMetadata(context, item, info.schema, info.element, info.qualifier, Item.ANY); } catch (SQLException e) { log.error("Caught exception trying to remove metadata", e); throw new DSpaceSwordException(e); } } } private void addUniqueMetadata(Context context, MetadataValueInfo info, Item item) throws SQLException { String qual = info.qualifier; if (info.qualifier == null) { qual = Item.ANY; } String lang = info.language; if (info.language == null) { lang = Item.ANY; } List<MetadataValue> existing = itemService.getMetadata( item, info.schema, info.element, qual, lang); for (MetadataValue dcValue : existing) { // FIXME: probably we want to be slightly more careful about qualifiers and languages // // if the submitted value is already attached to the item, just skip it if (dcValue.getValue().equals(info.value)) { return; } } // if we get to here, go on and add the metadata itemService.addMetadata(context, item, info.schema, info.element, info.qualifier, info.language, info.value); } private void addMetadataToItem(Context context, Deposit deposit, Item item) throws DSpaceSwordException { // now, go through and get the metadata from the EntryPart and put it in DSpace SwordEntry se = deposit.getSwordEntry(); // first do the standard atom terms (which may get overridden later) String title = se.getTitle(); String summary = se.getSummary(); if (title != null) { String titleField = this.dcMap.get("title"); if (titleField != null) { MetadataValueInfo info = this.makeMetadataValueInfo(titleField, title); try { this.addUniqueMetadata(context, info, item); } catch (SQLException e) { log.error("Caught exception trying to add title", e); throw new DSpaceSwordException(e); } } } if (summary != null) { String abstractField = this.dcMap.get("abstract"); if (abstractField != null) { MetadataValueInfo info = this.makeMetadataValueInfo(abstractField, summary); try { this.addUniqueMetadata(context, info, item); } catch (SQLException e) { log.error("Caught exception trying to set abstract", e); throw new DSpaceSwordException(e); } } } Map<String, List<String>> dc = se.getDublinCore(); for (String term : dc.keySet()) { String dsTerm = this.dcMap.get(term); if (dsTerm == null) { // ignore anything we don't understand continue; } // now add all the metadata terms MetadataValueInfo info = this.makeMetadataValueInfo(dsTerm, null); for (String value : dc.get(term)) { info.value = value; try { this.addUniqueMetadata(context, info, item); } catch (SQLException e) { log.error("Caught exception trying to add metadata", e); throw new DSpaceSwordException(e); } } } } public DepositResult ingestToCollection(Context context, Deposit deposit, Collection collection, VerboseDescription verboseDescription, DepositResult result) throws DSpaceSwordException, SwordError, SwordAuthException, SwordServerException { try { // decide whether we have a new item or an existing one Item item = null; WorkspaceItem wsi = null; if (result != null) { item = result.getItem(); } else { result = new DepositResult(); } if (item == null) { // simple zip ingester uses the item template, since there is no native metadata wsi = workspaceItemService.create(context, collection, true); item = wsi.getItem(); } // add the metadata to the item this.addMetadataToItem(context, deposit, item); // update the item metadata to inclue the current time as // the updated date this.setUpdatedDate(context, item, verboseDescription); // DSpace ignores the slug value as suggested identifier, but // it does store it in the metadata this.setSlug(context, item, deposit.getSlug(), verboseDescription); // 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. context.turnOffAuthorisationSystem(); itemService.update(context, item); context.restoreAuthSystemState(); verboseDescription.append("Ingest successful"); verboseDescription.append( "Item created with internal identifier: " + item.getID()); result.setItem(item); result.setTreatment(this.getTreatment()); return result; } catch (AuthorizeException e) { throw new SwordAuthException(e); } catch (SQLException e) { throw new DSpaceSwordException(e); } } public MetadataValueInfo makeMetadataValueInfo(String field, String value) throws DSpaceSwordException { MetadataValueInfo dcv = new MetadataValueInfo(); String[] bits = field.split("\\."); if (bits.length < 2 || bits.length > 3) { throw new DSpaceSwordException("invalid DC value: " + field); } dcv.schema = bits[0]; dcv.element = bits[1]; if (bits.length == 3) { dcv.qualifier = bits[2]; } dcv.value = value; return dcv; } /** * 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 context * The relevant DSpace Context. * @param item * item to add date metadata to * @param verboseDescription * The description. * @throws DSpaceSwordException * can be thrown by the internals of the DSpace SWORD implementation */ protected void setUpdatedDate(Context context, Item item, VerboseDescription verboseDescription) throws DSpaceSwordException { String field = configurationService .getProperty("swordv2-server.updated.field"); if (StringUtils.isBlank(field)) { throw new DSpaceSwordException( "No configuration, or configuration is invalid for: swordv2-server.updated.field"); } MetadataValueInfo info = this.makeMetadataValueInfo(field, null); try { itemService.clearMetadata(context, item, info.schema, info.element, info.qualifier, Item.ANY); DCDate date = new DCDate(new Date()); itemService.addMetadata(context, item, info.schema, info.element, info.qualifier, null, date.toString()); } catch (SQLException e) { log.error("Exception caught trying to set updated date", e); throw new DSpaceSwordException(e); } verboseDescription.append( "Updated date added to response from item metadata where available"); } /** * 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 context * The relevant DSpace Context. * @param item * the item to modify * @param slugVal * the value to store to metadata * @param verboseDescription * The description. * @throws DSpaceSwordException * can be thrown by the internals of the DSpace SWORD implementation */ protected void setSlug(Context context, Item item, String slugVal, VerboseDescription verboseDescription) throws DSpaceSwordException { // if there isn't a slug value, don't set it if (slugVal == null) { return; } String field = configurationService.getProperty( "swordv2-server.slug.field"); if (StringUtils.isBlank(field)) { throw new DSpaceSwordException( "No configuration, or configuration is invalid for: swordv2-server.slug.field"); } MetadataValueInfo info = this.makeMetadataValueInfo(field, null); try { itemService.clearMetadata(context, item, info.schema, info.element, info.qualifier, Item.ANY); itemService.addMetadata(context, item, info.schema, info.element, info.qualifier, null, slugVal); } catch (SQLException e) { log.error("Caught exception trying to set slug", e); throw new DSpaceSwordException(e); } verboseDescription.append("Slug value set in response where available"); } /** * The human readable description of the treatment this ingester has * put the deposit through * * @return human readable description * @throws DSpaceSwordException * can be thrown by the internals of the DSpace SWORD implementation */ private String getTreatment() throws DSpaceSwordException { return "A metadata only item has been created"; } private class MetadataValueInfo { private String schema; private String element; private String qualifier; private String language; private String value; } }