diff --git a/dspace-api/src/main/java/org/dspace/content/InstallItemServiceImpl.java b/dspace-api/src/main/java/org/dspace/content/InstallItemServiceImpl.java index 11cd4c107c..a4913d1360 100644 --- a/dspace-api/src/main/java/org/dspace/content/InstallItemServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/content/InstallItemServiceImpl.java @@ -9,10 +9,17 @@ package org.dspace.content; import java.io.IOException; import java.sql.SQLException; +import java.util.HashMap; import java.util.List; +import java.util.Map; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.dspace.authorize.AuthorizeException; import org.dspace.content.factory.ContentServiceFactory; +import org.dspace.content.logic.Filter; +import org.dspace.content.logic.FilterUtils; +import org.dspace.content.logic.TrueFilter; import org.dspace.content.service.CollectionService; import org.dspace.content.service.InstallItemService; import org.dspace.content.service.ItemService; @@ -20,8 +27,12 @@ import org.dspace.core.Constants; import org.dspace.core.Context; import org.dspace.embargo.service.EmbargoService; import org.dspace.event.Event; +import org.dspace.identifier.DOI; +import org.dspace.identifier.Handle; +import org.dspace.identifier.Identifier; import org.dspace.identifier.IdentifierException; import org.dspace.identifier.service.IdentifierService; +import org.dspace.services.factory.DSpaceServicesFactory; import org.springframework.beans.factory.annotation.Autowired; /** @@ -42,9 +53,11 @@ public class InstallItemServiceImpl implements InstallItemService { protected IdentifierService identifierService; @Autowired(required = true) protected ItemService itemService; + @Autowired(required = false) + + Logger log = LogManager.getLogger(InstallItemServiceImpl.class); protected InstallItemServiceImpl() { - } @Override @@ -59,11 +72,17 @@ public class InstallItemServiceImpl implements InstallItemService { AuthorizeException { Item item = is.getItem(); Collection collection = is.getCollection(); + // Get map of filters to use for identifier types + Map, Filter> filters = FilterUtils.getIdentifierFilters("install"); try { if (suppliedHandle == null) { - identifierService.register(c, item); + // Register with the filters we've set up + identifierService.register(c, item, filters); } else { + // This will register the handle but a pending DOI won't be compatible and so won't be registered identifierService.register(c, item, suppliedHandle); + // Continue to register just a DOI + identifierService.register(c, item, DOI.class, filters.get(DOI.class)); } } catch (IdentifierException e) { throw new RuntimeException("Can't create an Identifier!", e); diff --git a/dspace-api/src/main/java/org/dspace/content/WorkspaceItemServiceImpl.java b/dspace-api/src/main/java/org/dspace/content/WorkspaceItemServiceImpl.java index eb64d8a43d..6bcf65106d 100644 --- a/dspace-api/src/main/java/org/dspace/content/WorkspaceItemServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/content/WorkspaceItemServiceImpl.java @@ -24,6 +24,8 @@ import org.dspace.authorize.AuthorizeException; import org.dspace.authorize.ResourcePolicy; import org.dspace.authorize.service.AuthorizeService; import org.dspace.content.dao.WorkspaceItemDAO; +import org.dspace.content.logic.Filter; +import org.dspace.content.logic.FilterUtils; import org.dspace.content.service.CollectionService; import org.dspace.content.service.ItemService; import org.dspace.content.service.WorkspaceItemService; @@ -32,8 +34,12 @@ import org.dspace.core.Context; import org.dspace.core.LogHelper; import org.dspace.eperson.EPerson; import org.dspace.event.Event; +import org.dspace.identifier.DOI; +import org.dspace.identifier.DOIIdentifierProvider; +import org.dspace.identifier.Identifier; import org.dspace.identifier.IdentifierException; import org.dspace.identifier.factory.IdentifierServiceFactory; +import org.dspace.identifier.service.DOIService; import org.dspace.services.factory.DSpaceServicesFactory; import org.dspace.workflow.WorkflowItem; import org.dspace.workflow.WorkflowService; @@ -61,6 +67,8 @@ public class WorkspaceItemServiceImpl implements WorkspaceItemService { protected ItemService itemService; @Autowired(required = true) protected WorkflowService workflowService; + @Autowired(required = true) + protected DOIService doiService; protected WorkspaceItemServiceImpl() { @@ -169,7 +177,15 @@ public class WorkspaceItemServiceImpl implements WorkspaceItemService { if (DSpaceServicesFactory.getInstance().getConfigurationService() .getBooleanProperty("identifiers.submission.register", false)) { try { - IdentifierServiceFactory.getInstance().getIdentifierService().register(context, item); + // Get map of filters to use for identifier types + Map, Filter> filters = FilterUtils.getIdentifierFilters("workspace"); + IdentifierServiceFactory.getInstance().getIdentifierService().register(context, item, filters); + // Look for a DOI and move it to PENDING + DOI doi = doiService.findDOIByDSpaceObject(context, item); + if (doi != null) { + doi.setStatus(DOIIdentifierProvider.PENDING); + doiService.update(context, doi); + } } catch (IdentifierException e) { log.error("Could not register identifier(s) for item {}: {}", item.getID(), e.getMessage()); } diff --git a/dspace-api/src/main/java/org/dspace/content/logic/DefaultFilter.java b/dspace-api/src/main/java/org/dspace/content/logic/DefaultFilter.java index 490c3949ea..936d12987f 100644 --- a/dspace-api/src/main/java/org/dspace/content/logic/DefaultFilter.java +++ b/dspace-api/src/main/java/org/dspace/content/logic/DefaultFilter.java @@ -22,6 +22,7 @@ import org.dspace.core.Context; */ public class DefaultFilter implements Filter { private LogicalStatement statement; + private String name; private final static Logger log = LogManager.getLogger(); /** @@ -44,4 +45,15 @@ public class DefaultFilter implements Filter { public boolean getResult(Context context, Item item) throws LogicalStatementException { return this.statement.getResult(context, item); } + + @Override + public void setBeanName(String name) { + log.debug("Initialize bean " + name); + this.name = name; + } + + @Override + public String getName() { + return name; + } } diff --git a/dspace-api/src/main/java/org/dspace/content/logic/Filter.java b/dspace-api/src/main/java/org/dspace/content/logic/Filter.java index 84e9d6bc08..97ab73ddfa 100644 --- a/dspace-api/src/main/java/org/dspace/content/logic/Filter.java +++ b/dspace-api/src/main/java/org/dspace/content/logic/Filter.java @@ -9,6 +9,7 @@ package org.dspace.content.logic; import org.dspace.content.Item; import org.dspace.core.Context; +import org.springframework.beans.factory.BeanNameAware; /** * The interface for Filter currently doesn't add anything to LogicalStatement but inherits from it @@ -22,7 +23,7 @@ import org.dspace.core.Context; * @author Kim Shepherd * @see org.dspace.content.logic.DefaultFilter */ -public interface Filter extends LogicalStatement { +public interface Filter extends LogicalStatement, BeanNameAware { /** * Get the result of logical evaluation for an item * @param context DSpace context @@ -32,4 +33,6 @@ public interface Filter extends LogicalStatement { */ @Override boolean getResult(Context context, Item item) throws LogicalStatementException; + + String getName(); } diff --git a/dspace-api/src/main/java/org/dspace/content/logic/FilterUtils.java b/dspace-api/src/main/java/org/dspace/content/logic/FilterUtils.java new file mode 100644 index 0000000000..006d45de28 --- /dev/null +++ b/dspace-api/src/main/java/org/dspace/content/logic/FilterUtils.java @@ -0,0 +1,74 @@ +/** + * 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.content.logic; + +import java.util.HashMap; +import java.util.Map; + +import org.dspace.identifier.DOI; +import org.dspace.identifier.Handle; +import org.dspace.identifier.Identifier; +import org.dspace.services.ConfigurationService; +import org.dspace.services.factory.DSpaceServicesFactory; +import org.springframework.beans.factory.annotation.Autowired; + +/** + * General utility methods for logical item filtering + * + * @author Kim Shepherd + */ +public class FilterUtils { + + @Autowired(required = true) + ConfigurationService configurationService; + + /** + * Get a Filter by name + * @param property DSpace configuration property name (Apache Commons config) + * @return Filter or null + */ + public static Filter getFilterFromConfiguration(String property) { + String filterName = DSpaceServicesFactory.getInstance().getConfigurationService().getProperty(property); + if (filterName != null) { + return DSpaceServicesFactory.getInstance().getServiceManager().getServiceByName(filterName, Filter.class); + } + return null; + } + + /** + * Get a Filter by name + * @param property DSpace configuration property name (Apache Commons config) + * @return Filter or null + */ + public static Filter getFilterFromConfiguration(String property, Filter defaultFilter) { + Filter filter = getFilterFromConfiguration(property); + if (filter != null) { + return filter; + } + return defaultFilter; + } + + /** + * Get a map of identifier types and filters to use when creating workspace or archived items + * @return + */ + public static Map, Filter> getIdentifierFilters(String status) { + if (status == null) { + status = "install"; + } + Map, Filter> filters = new HashMap<>(); + // Put DOI 'can we create DOI on install / workspace?' filter + Filter filter = FilterUtils.getFilterFromConfiguration("identifiers.submission.filter." + status); + // A null filter should be handled safely by the identifier provier (default, or "always true") + filters.put(DOI.class, filter); + // This won't have an affect until handle providers implement filtering, but is an example of + // how the filters can be used for other types + filters.put(Handle.class, new TrueFilter()); + return filters; + } +} diff --git a/dspace-api/src/main/java/org/dspace/content/logic/TrueFilter.java b/dspace-api/src/main/java/org/dspace/content/logic/TrueFilter.java new file mode 100644 index 0000000000..b15ab4eaaa --- /dev/null +++ b/dspace-api/src/main/java/org/dspace/content/logic/TrueFilter.java @@ -0,0 +1,41 @@ +/** + * 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.content.logic; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.dspace.content.Item; +import org.dspace.core.Context; + +/** + * Extremely simple filter that always returns true! + * Useful to pass to methods that expect a filter, in order to effectively say "all items". + * This could be configured in Spring XML but it is more stable and reliable to have it hard-coded here + * so that any broken configuration doesn't silently break parts of DSpace that expect it to work. + * + * @author Kim Shepherd + */ +public class TrueFilter implements Filter { + private String name; + private final static Logger log = LogManager.getLogger(); + + public boolean getResult(Context context, Item item) throws LogicalStatementException { + return true; + } + + @Override + public void setBeanName(String name) { + log.debug("Initialize bean " + name); + this.name = name; + } + + @Override + public String getName() { + return name; + } +} diff --git a/dspace-api/src/main/java/org/dspace/content/logic/condition/IsArchivedCondition.java b/dspace-api/src/main/java/org/dspace/content/logic/condition/IsArchivedCondition.java new file mode 100644 index 0000000000..4ae1c7dc91 --- /dev/null +++ b/dspace-api/src/main/java/org/dspace/content/logic/condition/IsArchivedCondition.java @@ -0,0 +1,38 @@ +/** + * 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.content.logic.condition; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.dspace.content.Item; +import org.dspace.content.logic.LogicalStatementException; +import org.dspace.core.Context; + +/** + * A condition that returns true if the item is withdrawn + * + * @author Kim Shepherd + * @version $Revision$ + */ +public class IsArchivedCondition extends AbstractCondition { + private final static Logger log = LogManager.getLogger(); + + /** + * Return true if item is withdrawn + * Return false if not + * @param context DSpace context + * @param item Item to evaluate + * @return boolean result of evaluation + * @throws LogicalStatementException + */ + @Override + public boolean getResult(Context context, Item item) throws LogicalStatementException { + log.debug("Result of isWithdrawn is " + item.isArchived()); + return item.isArchived(); + } +} diff --git a/dspace-api/src/main/java/org/dspace/ctask/general/RegisterDOI.java b/dspace-api/src/main/java/org/dspace/ctask/general/RegisterDOI.java index 4e777d70a8..8cc5f85d05 100644 --- a/dspace-api/src/main/java/org/dspace/ctask/general/RegisterDOI.java +++ b/dspace-api/src/main/java/org/dspace/ctask/general/RegisterDOI.java @@ -13,6 +13,9 @@ import java.sql.SQLException; import org.apache.logging.log4j.Logger; import org.dspace.content.DSpaceObject; import org.dspace.content.Item; +import org.dspace.content.logic.Filter; +import org.dspace.content.logic.FilterUtils; +import org.dspace.content.logic.TrueFilter; import org.dspace.curate.AbstractCurationTask; import org.dspace.curate.Curator; import org.dspace.identifier.DOIIdentifierProvider; @@ -46,8 +49,6 @@ public class RegisterDOI extends AbstractCurationTask { @Override public void init(Curator curator, String taskId) throws IOException { super.init(curator, taskId); - // Get 'skip filter' behaviour from configuration, with a default value of 'true' - skipFilter = configurationService.getBooleanProperty(PLUGIN_PREFIX + ".skip-filter", true); // Get distribution behaviour from configuration, with a default value of 'false' distributed = configurationService.getBooleanProperty(PLUGIN_PREFIX + ".distributed", false); log.debug("PLUGIN_PREFIX = " + PLUGIN_PREFIX + ", skipFilter = " + skipFilter + @@ -118,8 +119,9 @@ public class RegisterDOI extends AbstractCurationTask { String doi = null; // Attempt DOI registration and report successes and failures try { - log.debug("Registering DOI with skipFilter = " + skipFilter); - doi = provider.register(Curator.curationContext(), item, skipFilter); + Filter filter = FilterUtils.getFilterFromConfiguration("identifiers.submission.filter.curation", + new TrueFilter()); + doi = provider.register(Curator.curationContext(), item, filter); if (doi != null) { String message = "New DOI minted in database for item " + item.getHandle() + ": " + doi + ". This DOI will be registered online with the DOI provider when the queue is next run"; diff --git a/dspace-api/src/main/java/org/dspace/identifier/DOIIdentifierProvider.java b/dspace-api/src/main/java/org/dspace/identifier/DOIIdentifierProvider.java index 66e7b94a4b..f5008d575e 100644 --- a/dspace-api/src/main/java/org/dspace/identifier/DOIIdentifierProvider.java +++ b/dspace-api/src/main/java/org/dspace/identifier/DOIIdentifierProvider.java @@ -21,6 +21,7 @@ import org.dspace.content.MetadataValue; import org.dspace.content.factory.ContentServiceFactory; import org.dspace.content.logic.Filter; import org.dspace.content.logic.LogicalStatementException; +import org.dspace.content.logic.TrueFilter; import org.dspace.content.service.ItemService; import org.dspace.core.Constants; import org.dspace.core.Context; @@ -44,6 +45,7 @@ import org.springframework.beans.factory.annotation.Autowired; *

Any identifier a method of this class returns is a string in the following format: doi:10.123/456.

* * @author Pascal-Nicolas Becker + * @author Kim Shepherd */ public class DOIIdentifierProvider extends FilteredIdentifierProvider { private static final Logger log = LoggerFactory.getLogger(DOIIdentifierProvider.class); @@ -71,16 +73,29 @@ public class DOIIdentifierProvider extends FilteredIdentifierProvider { public static final String MD_SCHEMA = "dc"; public static final String DOI_ELEMENT = "identifier"; public static final String DOI_QUALIFIER = "uri"; - + // The DOI is queued for registered with the service provider public static final Integer TO_BE_REGISTERED = 1; + // The DOI is queued for reservation with the service provider public static final Integer TO_BE_RESERVED = 2; + // The DOI has been registered online public static final Integer IS_REGISTERED = 3; + // The DOI has been reserved online public static final Integer IS_RESERVED = 4; + // The DOI is reserved and requires an updated metadata record to be sent to the service provider public static final Integer UPDATE_RESERVED = 5; + // The DOI is registered and requires an updated metadata record to be sent to the service provider public static final Integer UPDATE_REGISTERED = 6; + // The DOI metadata record should be updated before performing online registration public static final Integer UPDATE_BEFORE_REGISTRATION = 7; + // The DOI will be deleted locally and marked as deleted in the DOI service provider public static final Integer TO_BE_DELETED = 8; + // The DOI has been deleted and is no longer associated with an item public static final Integer DELETED = 9; + // The DOI is created in the database and is waiting for either successful filter check on item install or + // manual intervention by an administrator to proceed to reservation or registration + public static final Integer PENDING = 10; + // The DOI is created in the database, but no more context is known + public static final Integer MINTED = 11; @Autowired(required = true) protected DOIService doiService; @@ -89,8 +104,6 @@ public class DOIIdentifierProvider extends FilteredIdentifierProvider { @Autowired(required = true) protected ItemService itemService; - protected Filter filterService; - /** * Empty / default constructor for Spring */ @@ -153,16 +166,6 @@ public class DOIIdentifierProvider extends FilteredIdentifierProvider { this.connector = connector; } - /** - * Set the Filter to use when testing items to see if a DOI should be registered - * Spring will use this setter to set the filter from the configured property in identifier-services.xml - * @param filterService - an object implementing the org.dspace.content.logic.Filter interface - */ - @Override - public void setFilterService(Filter filterService) { - this.filterService = filterService; - } - /** * This identifier provider supports identifiers of type * {@link org.dspace.identifier.DOI}. @@ -206,7 +209,7 @@ public class DOIIdentifierProvider extends FilteredIdentifierProvider { @Override public String register(Context context, DSpaceObject dso) throws IdentifierException { - return register(context, dso, false); + return register(context, dso, this.filter); } /** @@ -219,29 +222,29 @@ public class DOIIdentifierProvider extends FilteredIdentifierProvider { @Override public void register(Context context, DSpaceObject dso, String identifier) throws IdentifierException { - register(context, dso, identifier, false); + register(context, dso, identifier, this.filter); } /** * Register a new DOI for a given DSpaceObject * @param context - DSpace context * @param dso - DSpaceObject identified by the new DOI - * @param skipFilter - boolean indicating whether to skip any filtering of items before performing registration + * @param filter - Logical item filter to determine whether this identifier should be registered * @throws IdentifierException */ @Override - public String register(Context context, DSpaceObject dso, boolean skipFilter) + public String register(Context context, DSpaceObject dso, Filter filter) throws IdentifierException { if (!(dso instanceof Item)) { // DOI are currently assigned only to Item return null; } - String doi = mint(context, dso, skipFilter); + String doi = mint(context, dso, filter); // register tries to reserve doi if it's not already. // So we don't have to reserve it here. - register(context, dso, doi, skipFilter); + register(context, dso, doi, filter); return doi; } @@ -250,11 +253,11 @@ public class DOIIdentifierProvider extends FilteredIdentifierProvider { * @param context - DSpace context * @param dso - DSpaceObject identified by the new DOI * @param identifier - String containing the DOI to register - * @param skipFilter - boolean indicating whether to skip any filtering of items before performing registration + * @param filter - Logical item filter to determine whether this identifier should be registered * @throws IdentifierException */ @Override - public void register(Context context, DSpaceObject dso, String identifier, boolean skipFilter) + public void register(Context context, DSpaceObject dso, String identifier, Filter filter) throws IdentifierException { if (!(dso instanceof Item)) { // DOI are currently assigned only to Item @@ -265,7 +268,7 @@ public class DOIIdentifierProvider extends FilteredIdentifierProvider { // search DOI in our db try { - doiRow = loadOrCreateDOI(context, dso, doi, skipFilter); + doiRow = loadOrCreateDOI(context, dso, doi, filter); } catch (SQLException ex) { log.error("Error in databse connection: " + ex.getMessage()); throw new RuntimeException("Error in database conncetion.", ex); @@ -277,7 +280,6 @@ public class DOIIdentifierProvider extends FilteredIdentifierProvider { + "is marked as DELETED.", DOIIdentifierException.DOI_IS_DELETED); } - // Check status of DOI if (IS_REGISTERED.equals(doiRow.getStatus())) { return; } @@ -290,6 +292,7 @@ public class DOIIdentifierProvider extends FilteredIdentifierProvider { log.warn("SQLException while changing status of DOI {} to be registered.", doi); throw new RuntimeException(sqle); } + } /** @@ -309,7 +312,7 @@ public class DOIIdentifierProvider extends FilteredIdentifierProvider { @Override public void reserve(Context context, DSpaceObject dso, String identifier) throws IdentifierException, IllegalArgumentException { - reserve(context, dso, identifier, false); + reserve(context, dso, identifier, this.filter); } /** @@ -317,20 +320,18 @@ public class DOIIdentifierProvider extends FilteredIdentifierProvider { * @param context - DSpace context * @param dso - DSpaceObject identified by this DOI * @param identifier - String containing the DOI to reserve - * @param skipFilter - boolean indicating whether to skip any filtering of items before performing reservation + * @param filter - Logical item filter to determine whether this identifier should be reserved * @throws IdentifierException * @throws IllegalArgumentException */ @Override - public void reserve(Context context, DSpaceObject dso, String identifier, boolean skipFilter) + public void reserve(Context context, DSpaceObject dso, String identifier, Filter filter) throws IdentifierException, IllegalArgumentException { String doi = doiService.formatIdentifier(identifier); DOI doiRow = null; try { - // if the doi is in our db already loadOrCreateDOI just returns. - // if it is not loadOrCreateDOI safes the doi. - doiRow = loadOrCreateDOI(context, dso, doi, skipFilter); + doiRow = loadOrCreateDOI(context, dso, doi, filter); } catch (SQLException sqle) { throw new RuntimeException(sqle); } @@ -359,7 +360,7 @@ public class DOIIdentifierProvider extends FilteredIdentifierProvider { */ public void reserveOnline(Context context, DSpaceObject dso, String identifier) throws IdentifierException, IllegalArgumentException, SQLException { - reserveOnline(context, dso, identifier, false); + reserveOnline(context, dso, identifier, this.filter); } /** @@ -367,16 +368,16 @@ public class DOIIdentifierProvider extends FilteredIdentifierProvider { * @param context - DSpace context * @param dso - DSpaceObject identified by this DOI * @param identifier - String containing the DOI to reserve - * @param skipFilter - skip the filters for {@link checkMintable(Context, DSpaceObject)} + * @param filter - Logical item filter to determine whether this identifier should be reserved online * @throws IdentifierException * @throws IllegalArgumentException * @throws SQLException */ - public void reserveOnline(Context context, DSpaceObject dso, String identifier, boolean skipFilter) + public void reserveOnline(Context context, DSpaceObject dso, String identifier, Filter filter) throws IdentifierException, IllegalArgumentException, SQLException { String doi = doiService.formatIdentifier(identifier); // get TableRow and ensure DOI belongs to dso regarding our db - DOI doiRow = loadOrCreateDOI(context, dso, doi, skipFilter); + DOI doiRow = loadOrCreateDOI(context, dso, doi, filter); if (DELETED.equals(doiRow.getStatus()) || TO_BE_DELETED.equals(doiRow.getStatus())) { throw new DOIIdentifierException("You tried to reserve a DOI that " @@ -402,7 +403,7 @@ public class DOIIdentifierProvider extends FilteredIdentifierProvider { public void registerOnline(Context context, DSpaceObject dso, String identifier) throws IdentifierException, IllegalArgumentException, SQLException { - registerOnline(context, dso, identifier, false); + registerOnline(context, dso, identifier, this.filter); } @@ -411,18 +412,17 @@ public class DOIIdentifierProvider extends FilteredIdentifierProvider { * @param context - DSpace context * @param dso - DSpaceObject identified by this DOI * @param identifier - String containing the DOI to register - * @param skipFilter - skip filters for {@link checkMintable(Context, DSpaceObject)} + * @param filter - Logical item filter to determine whether this identifier should be registered online * @throws IdentifierException * @throws IllegalArgumentException * @throws SQLException */ - public void registerOnline(Context context, DSpaceObject dso, String identifier, boolean skipFilter) + public void registerOnline(Context context, DSpaceObject dso, String identifier, Filter filter) throws IdentifierException, IllegalArgumentException, SQLException { - log.debug("registerOnline: skipFilter is " + skipFilter); String doi = doiService.formatIdentifier(identifier); // get TableRow and ensure DOI belongs to dso regarding our db - DOI doiRow = loadOrCreateDOI(context, dso, doi, skipFilter); + DOI doiRow = loadOrCreateDOI(context, dso, doi, filter); if (DELETED.equals(doiRow.getStatus()) || TO_BE_DELETED.equals(doiRow.getStatus())) { throw new DOIIdentifierException("You tried to register a DOI that " @@ -435,7 +435,7 @@ public class DOIIdentifierProvider extends FilteredIdentifierProvider { } catch (DOIIdentifierException die) { // do we have to reserve DOI before we can register it? if (die.getCode() == DOIIdentifierException.RESERVE_FIRST) { - this.reserveOnline(context, dso, identifier, skipFilter); + this.reserveOnline(context, dso, identifier, filter); connector.registerDOI(context, dso, doi); } else { throw die; @@ -471,17 +471,22 @@ public class DOIIdentifierProvider extends FilteredIdentifierProvider { throws IdentifierException, IllegalArgumentException, SQLException { String doi = doiService.formatIdentifier(identifier); - - boolean skipFilter = false; + // Use the default filter unless we find the object + Filter updateFilter = this.filter; if (doiService.findDOIByDSpaceObject(context, dso) != null) { // We can skip the filter here since we know the DOI already exists for the item log.debug("updateMetadata: found DOIByDSpaceObject: " + doiService.findDOIByDSpaceObject(context, dso).getDoi()); - skipFilter = true; + updateFilter = new TrueFilter(); } - DOI doiRow = loadOrCreateDOI(context, dso, doi, skipFilter); + DOI doiRow = loadOrCreateDOI(context, dso, doi, updateFilter); + + if (PENDING.equals(doiRow.getStatus())) { + log.info("Not updating metadata for PENDING doi: " + doi); + return; + } if (DELETED.equals(doiRow.getStatus()) || TO_BE_DELETED.equals(doiRow.getStatus())) { throw new DOIIdentifierException("You tried to register a DOI that " @@ -571,19 +576,19 @@ public class DOIIdentifierProvider extends FilteredIdentifierProvider { @Override public String mint(Context context, DSpaceObject dso) throws IdentifierException { - return mint(context, dso, false); + return mint(context, dso, this.filter); } /** * Mint a new DOI in DSpace - this is usually the first step of registration * @param context - DSpace context * @param dso - DSpaceObject identified by the new identifier - * @param skipFilter - boolean indicating whether to skip any filtering of items before minting. + * @param filter - Logical item filter to determine whether this identifier should be registered * @return a String containing the new identifier * @throws IdentifierException */ @Override - public String mint(Context context, DSpaceObject dso, boolean skipFilter) throws IdentifierException { + public String mint(Context context, DSpaceObject dso, Filter filter) throws IdentifierException { String doi = null; try { @@ -597,7 +602,7 @@ public class DOIIdentifierProvider extends FilteredIdentifierProvider { } if (null == doi) { try { - DOI doiRow = loadOrCreateDOI(context, dso, null, skipFilter); + DOI doiRow = loadOrCreateDOI(context, dso, null, filter); doi = DOI.SCHEME + doiRow.getDoi(); } catch (SQLException e) { @@ -895,7 +900,7 @@ public class DOIIdentifierProvider extends FilteredIdentifierProvider { */ protected DOI loadOrCreateDOI(Context context, DSpaceObject dso, String doiIdentifier) throws SQLException, DOIIdentifierException, IdentifierNotApplicableException { - return loadOrCreateDOI(context, dso, doiIdentifier, false); + return loadOrCreateDOI(context, dso, doiIdentifier, this.filter); } /** @@ -910,13 +915,13 @@ public class DOIIdentifierProvider extends FilteredIdentifierProvider { * @param context - DSpace context * @param dso - DSpaceObject to identify * @param doiIdentifier - DOI to load or create (null to mint a new one) - * @param skipFilter - Whether or not to skip the filters for the checkMintable() check + * @param filter - Logical item filter to determine whether this identifier should be registered * @return * @throws SQLException * @throws DOIIdentifierException * @throws org.dspace.identifier.IdentifierNotApplicableException passed through. */ - protected DOI loadOrCreateDOI(Context context, DSpaceObject dso, String doiIdentifier, boolean skipFilter) + protected DOI loadOrCreateDOI(Context context, DSpaceObject dso, String doiIdentifier, Filter filter) throws SQLException, DOIIdentifierException, IdentifierNotApplicableException { DOI doi = null; @@ -954,6 +959,8 @@ public class DOIIdentifierProvider extends FilteredIdentifierProvider { // doi is assigned to a DSO; is it assigned to our specific dso? // check if DOI already belongs to dso if (dso.getID().equals(doi.getDSpaceObject().getID())) { + // Before we return this, check the filter + checkMintable(context, filter, dso); return doi; } else { throw new DOIIdentifierException("Trying to create a DOI " + @@ -963,15 +970,8 @@ public class DOIIdentifierProvider extends FilteredIdentifierProvider { } } - // we did not find the doi in the database or shall reassign it. Before doing so, we should check if a - // filter is in place to prevent the creation of new DOIs for certain items. - if (skipFilter) { - log.warn("loadOrCreateDOI: Skipping default item filter"); - } else { - // Find out if we're allowed to create a DOI - // throws an exception if creation of a new DOI is prohibited by a filter - checkMintable(context, dso); - } + // Check if this item is eligible for minting. An IdentifierNotApplicableException will be thrown if not. + checkMintable(context, filter, dso); // check prefix if (!doiIdentifier.startsWith(this.getPrefix() + "/")) { @@ -984,15 +984,8 @@ public class DOIIdentifierProvider extends FilteredIdentifierProvider { doi = doiService.create(context); } } else { - // We need to generate a new DOI. Before doing so, we should check if a - // filter is in place to prevent the creation of new DOIs for certain items. - if (skipFilter) { - log.warn("loadOrCreateDOI: Skipping default item filter"); - } else { - // Find out if we're allowed to create a DOI - // throws an exception if creation of a new DOI is prohibited by a filter - checkMintable(context, dso); - } + // Check if this item is eligible for minting. An IdentifierNotApplicableException will be thrown if not. + checkMintable(context, filter, dso); doi = doiService.create(context); doiIdentifier = this.getPrefix() + "/" + this.getNamespaceSeparator() + @@ -1002,7 +995,7 @@ public class DOIIdentifierProvider extends FilteredIdentifierProvider { // prepare new doiRow doi.setDoi(doiIdentifier); doi.setDSpaceObject(dso); - doi.setStatus(null); + doi.setStatus(MINTED); try { doiService.update(context, doi); } catch (SQLException e) { @@ -1102,20 +1095,26 @@ public class DOIIdentifierProvider extends FilteredIdentifierProvider { /** * Checks to see if an item can have a DOI minted, using the configured logical filter * @param context + * @param filter Logical item filter to apply * @param dso The item to be evaluated * @throws DOIIdentifierNotApplicableException */ @Override - public void checkMintable(Context context, DSpaceObject dso) throws DOIIdentifierNotApplicableException { + public void checkMintable(Context context, Filter filter, DSpaceObject dso) + throws DOIIdentifierNotApplicableException { + if (filter == null) { + // If a null filter was passed and we have a good default filter to apply, apply it. + // Otherwise set to TrueFilter which means "no filtering" + filter = Objects.requireNonNullElseGet(this.filter, TrueFilter::new); + } // If the check fails, an exception will be thrown to be caught by the calling method - if (this.filterService != null && contentServiceFactory - .getDSpaceObjectService(dso).getTypeText(dso).equals("ITEM")) { + if (contentServiceFactory.getDSpaceObjectService(dso).getTypeText(dso).equals("ITEM")) { try { - boolean result = filterService.getResult(context, (Item) dso); + boolean result = filter.getResult(context, (Item) dso); log.debug("Result of filter for " + dso.getHandle() + " is " + result); if (!result) { throw new DOIIdentifierNotApplicableException("Item " + dso.getHandle() + - " was evaluated as 'false' by the item filter, not minting"); + " was evaluated as 'false' by the item filter, not minting"); } } catch (LogicalStatementException e) { log.error("Error evaluating item with logical filter: " + e.getLocalizedMessage()); @@ -1125,4 +1124,16 @@ public class DOIIdentifierProvider extends FilteredIdentifierProvider { log.debug("DOI Identifier Provider: filterService is null (ie. don't prevent DOI minting)"); } } + + /** + * Checks to see if an item can have a DOI minted, using the configured logical filter + * @param context + * @param dso The item to be evaluated + * @throws DOIIdentifierNotApplicableException + */ + @Override + public void checkMintable(Context context, DSpaceObject dso) throws DOIIdentifierNotApplicableException { + checkMintable(context, this.filter, dso); + } + } \ No newline at end of file diff --git a/dspace-api/src/main/java/org/dspace/identifier/FilteredIdentifierProvider.java b/dspace-api/src/main/java/org/dspace/identifier/FilteredIdentifierProvider.java index e5f222ff29..440ee3100f 100644 --- a/dspace-api/src/main/java/org/dspace/identifier/FilteredIdentifierProvider.java +++ b/dspace-api/src/main/java/org/dspace/identifier/FilteredIdentifierProvider.java @@ -12,8 +12,8 @@ import java.sql.SQLException; import org.dspace.content.DSpaceObject; import org.dspace.content.logic.Filter; +import org.dspace.content.logic.TrueFilter; import org.dspace.core.Context; -import org.springframework.beans.factory.annotation.Autowired; /** * This abstract class adds extra method signatures so that implementing IdentifierProviders can @@ -24,26 +24,28 @@ import org.springframework.beans.factory.annotation.Autowired; */ public abstract class FilteredIdentifierProvider extends IdentifierProvider { - protected Filter filterService; + protected Filter filter = new TrueFilter(); /** - * Setter for spring to set the filter service from the property in configuration XML - * @param filterService - an object implementing the org.dspace.content.logic.Filter interface + * Setter for spring to set the default filter from the property in configuration XML + * @param filter - an object implementing the org.dspace.content.logic.Filter interface */ - @Autowired - public void setFilterService(Filter filterService) { - this.filterService = filterService; + public void setFilter(Filter filter) { + this.filter = filter; + if (this.filter == null) { + this.filter = new TrueFilter(); + } } /** * Register a new identifier for a given DSpaceObject * @param context - DSpace context * @param dso - DSpaceObject to use for identifier registration - * @param skipFilter - boolean indicating whether to skip any filtering of items before performing registration + * @param filter - Logical item filter to determine whether this identifier should be registered * @return identifier * @throws IdentifierException */ - public abstract String register(Context context, DSpaceObject dso, boolean skipFilter) + public abstract String register(Context context, DSpaceObject dso, Filter filter) throws IdentifierException; /** @@ -51,10 +53,10 @@ public abstract class FilteredIdentifierProvider extends IdentifierProvider { * @param context - DSpace context * @param dso - DSpaceObject identified by the new identifier * @param identifier - String containing the identifier to register - * @param skipFilter - boolean indicating whether to skip any filtering of items before performing registration + * @param filter - Logical item filter to determine whether this identifier should be registered * @throws IdentifierException */ - public abstract void register(Context context, DSpaceObject dso, String identifier, boolean skipFilter) + public abstract void register(Context context, DSpaceObject dso, String identifier, Filter filter) throws IdentifierException; /** @@ -62,23 +64,23 @@ public abstract class FilteredIdentifierProvider extends IdentifierProvider { * @param context - DSpace context * @param dso - DSpaceObject identified by this identifier * @param identifier - String containing the identifier to reserve - * @param skipFilter - boolean indicating whether to skip any filtering of items before performing reservation + * @param filter - Logical item filter to determine whether this identifier should be reserved * @throws IdentifierException * @throws IllegalArgumentException * @throws SQLException */ - public abstract void reserve(Context context, DSpaceObject dso, String identifier, boolean skipFilter) + public abstract void reserve(Context context, DSpaceObject dso, String identifier, Filter filter) throws IdentifierException, IllegalArgumentException, SQLException; /** * Mint a new identifier in DSpace - this is usually the first step of registration * @param context - DSpace context * @param dso - DSpaceObject identified by the new identifier - * @param skipFilter - boolean indicating whether to skip any filtering of items before minting. + * @param filter - Logical item filter to determine whether this identifier should be registered * @return a String containing the new identifier * @throws IdentifierException */ - public abstract String mint(Context context, DSpaceObject dso, boolean skipFilter) throws IdentifierException; + public abstract String mint(Context context, DSpaceObject dso, Filter filter) throws IdentifierException; /** * Check configured item filters to see if this identifier is allowed to be minted @@ -88,5 +90,13 @@ public abstract class FilteredIdentifierProvider extends IdentifierProvider { */ public abstract void checkMintable(Context context, DSpaceObject dso) throws IdentifierException; + /** + * Check configured item filters to see if this identifier is allowed to be minted + * @param context - DSpace context + * @param filter - Logical item filter + * @param dso - DSpaceObject to be inspected + * @throws IdentifierException + */ + public abstract void checkMintable(Context context, Filter filter, DSpaceObject dso) throws IdentifierException; } diff --git a/dspace-api/src/main/java/org/dspace/identifier/IdentifierServiceImpl.java b/dspace-api/src/main/java/org/dspace/identifier/IdentifierServiceImpl.java index d0b6e4417e..2d41be0daa 100644 --- a/dspace-api/src/main/java/org/dspace/identifier/IdentifierServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/identifier/IdentifierServiceImpl.java @@ -10,6 +10,7 @@ package org.dspace.identifier; import java.sql.SQLException; import java.util.ArrayList; import java.util.List; +import java.util.Map; import org.apache.commons.lang3.StringUtils; import org.apache.logging.log4j.LogManager; @@ -17,6 +18,8 @@ import org.apache.logging.log4j.Logger; import org.dspace.authorize.AuthorizeException; import org.dspace.content.DSpaceObject; import org.dspace.content.factory.ContentServiceFactory; +import org.dspace.content.logic.Filter; +import org.dspace.content.logic.TrueFilter; import org.dspace.core.Context; import org.dspace.handle.service.HandleService; import org.dspace.identifier.service.IdentifierService; @@ -98,7 +101,7 @@ public class IdentifierServiceImpl implements IdentifierService { @Override public void register(Context context, DSpaceObject dso) - throws AuthorizeException, SQLException, IdentifierException { + throws AuthorizeException, SQLException, IdentifierException { //We need to commit our context because one of the providers might require the handle created above // Next resolve all other services for (IdentifierProvider service : providers) { @@ -112,6 +115,98 @@ public class IdentifierServiceImpl implements IdentifierService { contentServiceFactory.getDSpaceObjectService(dso).update(context, dso); } + @Override + public void register(Context context, DSpaceObject dso, Class type, Filter filter) + throws AuthorizeException, SQLException, IdentifierException { + //We need to commit our context because one of the providers might require the handle created above + // Next resolve all other services + boolean registered = false; + for (IdentifierProvider service : providers) { + if (service.supports(type)) { + try { + if (service instanceof FilteredIdentifierProvider) { + FilteredIdentifierProvider filteredService = (FilteredIdentifierProvider)service; + filteredService.register(context, dso, filter); + } else { + service.register(context, dso); + } + registered = true; + } catch (IdentifierNotApplicableException e) { + log.warn("Identifier not registered (inapplicable): " + e.getMessage()); + } + } + } + if (!registered) { + throw new IdentifierException("Cannot register identifier: Didn't " + + "find a provider that supports this identifier."); + } + //Update our item / collection / community + contentServiceFactory.getDSpaceObjectService(dso).update(context, dso); + } + + @Override + public void register(Context context, DSpaceObject dso, Class type) + throws AuthorizeException, SQLException, IdentifierException { + //We need to commit our context because one of the providers might require the handle created above + // Next resolve all other services + boolean registered = false; + for (IdentifierProvider service : providers) { + if (service.supports(type)) { + try { + service.register(context, dso); + registered = true; + } catch (IdentifierNotApplicableException e) { + log.warn("Identifier not registered (inapplicable): " + e.getMessage()); + } + } + } + if (!registered) { + throw new IdentifierException("Cannot register identifier: Didn't " + + "find a provider that supports this identifier."); + } + //Update our item / collection / community + contentServiceFactory.getDSpaceObjectService(dso).update(context, dso); + } + + @Override + public void register(Context context, DSpaceObject dso, Map, Filter> typeFilters) + throws AuthorizeException, SQLException, IdentifierException { + //We need to commit our context because one of the providers might require the handle created above + // Next resolve all other services + for (IdentifierProvider service : providers) { + try { + // If the service supports filtering, look through the map and the first supported class + // we find, set the filter and break. If no filter was seen for this type, just let the provider + // use its own implementation. + if (service instanceof FilteredIdentifierProvider) { + FilteredIdentifierProvider filteredService = (FilteredIdentifierProvider)service; + Filter filter = null; + for (Class type : typeFilters.keySet()) { + if (service.supports(type)) { + filter = typeFilters.get(type); + break; + } + } + if (filter != null) { + // Pass the found filter to the provider + filteredService.register(context, dso, filter); + } else { + // Let the provider use the default filter / behaviour + filteredService.register(context, dso); + } + } else { + service.register(context, dso); + } + } catch (IdentifierNotApplicableException e) { + log.warn("Identifier not registered (inapplicable): " + e.getMessage()); + } + } + //Update our item / collection / community + contentServiceFactory.getDSpaceObjectService(dso).update(context, dso); + } + + + @Override public void register(Context context, DSpaceObject object, String identifier) throws AuthorizeException, SQLException, IdentifierException { diff --git a/dspace-api/src/main/java/org/dspace/identifier/VersionedDOIIdentifierProvider.java b/dspace-api/src/main/java/org/dspace/identifier/VersionedDOIIdentifierProvider.java index a864b4be4b..e7c786d5f8 100644 --- a/dspace-api/src/main/java/org/dspace/identifier/VersionedDOIIdentifierProvider.java +++ b/dspace-api/src/main/java/org/dspace/identifier/VersionedDOIIdentifierProvider.java @@ -18,6 +18,7 @@ import org.dspace.authorize.AuthorizeException; import org.dspace.content.DSpaceObject; import org.dspace.content.Item; import org.dspace.content.MetadataValue; +import org.dspace.content.logic.Filter; import org.dspace.core.Context; import org.dspace.identifier.doi.DOIConnector; import org.dspace.identifier.doi.DOIIdentifierException; @@ -49,7 +50,12 @@ public class VersionedDOIIdentifierProvider extends DOIIdentifierProvider { protected VersionHistoryService versionHistoryService; @Override - public String mint(Context context, DSpaceObject dso) + public String mint(Context context, DSpaceObject dso) throws IdentifierException { + return mint(context, dso, this.filter); + } + + @Override + public String mint(Context context, DSpaceObject dso, Filter filter) throws IdentifierException { if (!(dso instanceof Item)) { throw new IdentifierException("Currently only Items are supported for DOIs."); @@ -79,6 +85,9 @@ public class VersionedDOIIdentifierProvider extends DOIIdentifierProvider { + " with ID " + dso.getID() + ".", ex); } + // Make a call to the filter here to throw an exception instead of carrying on with removal + creation + checkMintable(context, filter, dso); + // check whether we have a DOI in the metadata and if we have to remove it String metadataDOI = getDOIOutOfObject(dso); if (metadataDOI != null) { @@ -111,7 +120,7 @@ public class VersionedDOIIdentifierProvider extends DOIIdentifierProvider { // ensure DOI exists in our database as well and return. // this also checks that the doi is not assigned to another dso already. try { - loadOrCreateDOI(context, dso, versionedDOI); + loadOrCreateDOI(context, dso, versionedDOI, filter); } catch (SQLException ex) { log.error( "A problem with the database connection occurd while processing DOI " + versionedDOI + ".", ex); @@ -127,7 +136,7 @@ public class VersionedDOIIdentifierProvider extends DOIIdentifierProvider { // if we have a history, we have a item doi = makeIdentifierBasedOnHistory(context, dso, history); } else { - doi = loadOrCreateDOI(context, dso, null).getDoi(); + doi = loadOrCreateDOI(context, dso, null, filter).getDoi(); } } catch (SQLException ex) { log.error("SQLException while creating a new DOI: ", ex); @@ -140,7 +149,12 @@ public class VersionedDOIIdentifierProvider extends DOIIdentifierProvider { } @Override - public void register(Context context, DSpaceObject dso, String identifier) + public void register(Context context, DSpaceObject dso, String identifier) throws IdentifierException { + register(context, dso, identifier, this.filter); + } + + @Override + public void register(Context context, DSpaceObject dso, String identifier, Filter filter) throws IdentifierException { if (!(dso instanceof Item)) { throw new IdentifierException("Currently only Items are supported for DOIs."); @@ -220,8 +234,14 @@ public class VersionedDOIIdentifierProvider extends DOIIdentifierProvider { return doiPostfix; } - // Should never return null! protected String makeIdentifierBasedOnHistory(Context context, DSpaceObject dso, VersionHistory history) + throws AuthorizeException, SQLException, DOIIdentifierException, IdentifierNotApplicableException { + return makeIdentifierBasedOnHistory(context, dso, history, this.filter); + } + + // Should never return null! + protected String makeIdentifierBasedOnHistory(Context context, DSpaceObject dso, VersionHistory history, + Filter filter) throws AuthorizeException, SQLException, DOIIdentifierException, IdentifierNotApplicableException { // Mint foreach new version an identifier like: 12345/100.versionNumber // use the bare handle (g.e. 12345/100) for the first version. @@ -244,6 +264,9 @@ public class VersionedDOIIdentifierProvider extends DOIIdentifierProvider { } if (previousVersionDOI == null) { + // Before continuing with any new DOI creation, apply the filter + checkMintable(context, filter, dso); + // We need to generate a new DOI. DOI doi = doiService.create(context); @@ -269,7 +292,7 @@ public class VersionedDOIIdentifierProvider extends DOIIdentifierProvider { String.valueOf(versionHistoryService.getVersion(context, history, item).getVersionNumber())); } - loadOrCreateDOI(context, dso, identifier); + loadOrCreateDOI(context, dso, identifier, filter); return identifier; } diff --git a/dspace-api/src/main/java/org/dspace/identifier/doi/DOIConsumer.java b/dspace-api/src/main/java/org/dspace/identifier/doi/DOIConsumer.java index 622b546def..1961ce8274 100644 --- a/dspace-api/src/main/java/org/dspace/identifier/doi/DOIConsumer.java +++ b/dspace-api/src/main/java/org/dspace/identifier/doi/DOIConsumer.java @@ -7,23 +7,32 @@ */ package org.dspace.identifier.doi; +import java.sql.SQLException; + import org.apache.logging.log4j.Logger; import org.dspace.content.DSpaceObject; import org.dspace.content.Item; import org.dspace.content.factory.ContentServiceFactory; +import org.dspace.content.logic.Filter; +import org.dspace.content.logic.FilterUtils; import org.dspace.core.Constants; import org.dspace.core.Context; import org.dspace.event.Consumer; import org.dspace.event.Event; +import org.dspace.identifier.DOI; import org.dspace.identifier.DOIIdentifierProvider; import org.dspace.identifier.IdentifierException; -import org.dspace.identifier.IdentifierNotFoundException; +import org.dspace.identifier.IdentifierNotApplicableException; +import org.dspace.identifier.factory.IdentifierServiceFactory; +import org.dspace.identifier.service.DOIService; +import org.dspace.services.ConfigurationService; import org.dspace.services.factory.DSpaceServicesFactory; import org.dspace.utils.DSpace; import org.dspace.workflow.factory.WorkflowServiceFactory; /** * @author Pascal-Nicolas Becker (p dot becker at tu hyphen berlin dot de) + * @author Kim Shepherd */ public class DOIConsumer implements Consumer { /** @@ -31,12 +40,15 @@ public class DOIConsumer implements Consumer { */ private static Logger log = org.apache.logging.log4j.LogManager.getLogger(DOIConsumer.class); + ConfigurationService configurationService; + @Override public void initialize() throws Exception { // nothing to do // we can ask spring to give as a properly setuped instance of // DOIIdentifierProvider. Doing so we don't have to configure it and // can load it in consume method as this is not very expensive. + configurationService = DSpaceServicesFactory.getInstance().getConfigurationService(); } @@ -63,77 +75,73 @@ public class DOIConsumer implements Consumer { return; } Item item = (Item) dso; + DOIIdentifierProvider provider = new DSpace().getSingletonService(DOIIdentifierProvider.class); + boolean inProgress = (ContentServiceFactory.getInstance().getWorkspaceItemService().findByItem(ctx, item) + != null || WorkflowServiceFactory.getInstance().getWorkflowItemService().findByItem(ctx, item) != null); + boolean identifiersInSubmission = configurationService.getBooleanProperty("identifiers.submission.register", + false); + DOIService doiService = IdentifierServiceFactory.getInstance().getDOIService(); + Filter workspaceFilter = null; + if (identifiersInSubmission) { + workspaceFilter = FilterUtils.getFilterFromConfiguration("identifiers.submission.filter.workspace"); + } - /* - if (ContentServiceFactory.getInstance().getWorkspaceItemService().findByItem(ctx, item) != null - || WorkflowServiceFactory.getInstance().getWorkflowItemService().findByItem(ctx, item) != null) { - // ignore workflow and workspace items, DOI will be minted when item is installed + if (inProgress && !identifiersInSubmission) { + // ignore workflow and workspace items, DOI will be minted and updated when item is installed + // UNLESS special pending filter is set return; } - */ - DOIIdentifierProvider provider = new DSpace().getSingletonService( - DOIIdentifierProvider.class); - - String doi = null; + DOI doi = null; try { - doi = provider.lookup(ctx, dso); - } catch (IdentifierNotFoundException ex) { + doi = doiService.findDOIByDSpaceObject(ctx, dso); + } catch (SQLException ex) { // nothing to do here, next if clause will stop us from processing // items without dois. } if (doi == null) { - // if the item is workflow or workspace, there is a special case here - the ShowIdentifersStep - // needs this consumer to reserve DOIs in the event that the item now meets criteria for a pre-mint - if (ContentServiceFactory.getInstance().getWorkspaceItemService().findByItem(ctx, item) != null - || WorkflowServiceFactory.getInstance().getWorkflowItemService().findByItem(ctx, item) != null) { - if (DSpaceServicesFactory.getInstance().getConfigurationService() - .getBooleanProperty("identifiers.submission.register", false)) { - try { - String newDoi = provider.mint(ctx, item); - if (newDoi != null) { - provider.reserve(ctx, item, newDoi); - log.debug("Consumer minting and reserving new DOI for in-progress item: " + newDoi); - } else { - return; - } - } catch (DOIIdentifierNotApplicableException e) { - log.debug("Consumer skipping mint for item as it was filtered out: " + item.getID()); - } - } else { - log.debug("Consumer skipping mint for item as it is in-progress and" + - "identifiers.submission.register=false: " + item.getID()); + // No DOI. The only time something should be minted is if we have enabled submission reg'n and + // it passes the workspace filter. We also need to update status to PENDING straight after. + if (inProgress) { + provider.mint(ctx, dso, workspaceFilter); + DOI newDoi = doiService.findDOIByDSpaceObject(ctx, dso); + if (newDoi != null) { + newDoi.setStatus(DOIIdentifierProvider.PENDING); + doiService.update(ctx, newDoi); } } else { - log.debug("DOIConsumer cannot handles items without DOIs, skipping: " - + event.toString()); - return; + log.debug("DOIConsumer cannot handles items without DOIs, skipping: " + event.toString()); } - } else if (ContentServiceFactory.getInstance().getWorkspaceItemService().findByItem(ctx, item) != null - || WorkflowServiceFactory.getInstance().getWorkflowItemService().findByItem(ctx, item) != null) { - if (DSpaceServicesFactory.getInstance().getConfigurationService() - .getBooleanProperty("identifiers.submission.register", false)) { - // We have a DOI for an in-progress submission item -- if the filter says "no", we should delete - // the minted DOI so it doesn't get registered on item install. + } else { + // If in progress, we can also switch PENDING and MINTED status depending on the latest filter + // evaluation + if (inProgress) { try { - provider.checkMintable(ctx, item); - } catch (DOIIdentifierNotApplicableException e) { - log.debug("Consumer deleting DOI for in-progress item: " + doi); - provider.delete(ctx, item, doi); - return; + // Check the filter + provider.checkMintable(ctx, workspaceFilter, dso); + // If we made it here, the existing doi should be back to PENDING + if (DOIIdentifierProvider.MINTED.equals(doi.getStatus())) { + doi.setStatus(DOIIdentifierProvider.PENDING); + } + } catch (IdentifierNotApplicableException e) { + // Set status to MINTED if configured to downgrade existing DOIs + if (configurationService + .getBooleanProperty("identifiers.submission.strip_pending_during_submission", true)) { + doi.setStatus(DOIIdentifierProvider.MINTED); + } } + doiService.update(ctx, doi); } else { - log.debug("Consumer skipping mint for item as it is in-progress and" + - "identifiers.submission.register=false: " + item.getID()); + try { + provider.updateMetadata(ctx, dso, doi.getDoi()); + } catch (IllegalArgumentException ex) { + // should not happen, as we got the DOI from the DOIProvider + log.warn("DOIConsumer caught an IdentifierException.", ex); + } catch (IdentifierException ex) { + log.warn("DOIConsumer cannot update metadata for Item with ID " + + item.getID() + " and DOI " + doi + ".", ex); + } } - } - try { - provider.updateMetadata(ctx, dso, doi); - } catch (IllegalArgumentException ex) { - // should not happen, as we got the DOI from the DOIProvider - log.warn("DOIConsumer caught an IdentifierException.", ex); - } catch (IdentifierException ex) { - log.warn("DOIConsumer cannot update metadata for Item with ID " - + item.getID() + " and DOI " + doi + ".", ex); + ctx.commit(); } } diff --git a/dspace-api/src/main/java/org/dspace/identifier/doi/DOIOrganiser.java b/dspace-api/src/main/java/org/dspace/identifier/doi/DOIOrganiser.java index e0e0da9440..8af7f49e30 100644 --- a/dspace-api/src/main/java/org/dspace/identifier/doi/DOIOrganiser.java +++ b/dspace-api/src/main/java/org/dspace/identifier/doi/DOIOrganiser.java @@ -30,6 +30,9 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.dspace.content.DSpaceObject; import org.dspace.content.factory.ContentServiceFactory; +import org.dspace.content.logic.Filter; +import org.dspace.content.logic.FilterUtils; +import org.dspace.content.logic.TrueFilter; import org.dspace.content.service.ItemService; import org.dspace.core.Constants; import org.dspace.core.Context; @@ -61,7 +64,8 @@ public class DOIOrganiser { protected ItemService itemService; protected DOIService doiService; protected ConfigurationService configurationService; - protected boolean skipFilter; + // This filter will override the default provider filter / behaviour + protected Filter filter; /** * Constructor to be called within the main() method @@ -76,7 +80,7 @@ public class DOIOrganiser { this.itemService = ContentServiceFactory.getInstance().getItemService(); this.doiService = IdentifierServiceFactory.getInstance().getDOIService(); this.configurationService = DSpaceServicesFactory.getInstance().getConfigurationService(); - this.skipFilter = false; + this.filter = new TrueFilter(); } /** @@ -121,12 +125,13 @@ public class DOIOrganiser { "Perform online metadata update for all identifiers queued for metadata update."); options.addOption("d", "delete-all", false, "Perform online deletion for all identifiers queued for deletion."); - options.addOption("q", "quiet", false, "Turn the command line output off."); - options.addOption(null, "skip-filter", false, - "Skip the configured item filter when registering or reserving."); + Option filterDoi = Option.builder().optionalArg(true).longOpt("filter").hasArg().argName("filterName") + .desc("Use the specified filter name instead of the provider's filter. Defaults to a special " + + "'always true' filter to force operations").build(); + options.addOption(filterDoi); Option registerDoi = Option.builder() .longOpt("register-doi") @@ -203,10 +208,12 @@ public class DOIOrganiser { } DOIService doiService = IdentifierServiceFactory.getInstance().getDOIService(); - // Should we skip the filter? - if (line.hasOption("skip-filter")) { - System.out.println("Skipping the item filter"); - organiser.skipFilter = true; + // Do we get a filter? + if (line.hasOption("filter")) { + String filter = line.getOptionValue("filter"); + if (null != filter) { + organiser.filter = FilterUtils.getFilterFromConfiguration(filter); + } } if (line.hasOption('s')) { @@ -394,19 +401,18 @@ public class DOIOrganiser { /** * Register DOI with the provider * @param doiRow - doi to register - * @param skipFilter - whether filters should be skipped before registration + * @param filter - logical item filter to override * @throws SQLException * @throws DOIIdentifierException */ - public void register(DOI doiRow, boolean skipFilter) throws SQLException, DOIIdentifierException { + public void register(DOI doiRow, Filter filter) throws SQLException, DOIIdentifierException { DSpaceObject dso = doiRow.getDSpaceObject(); if (Constants.ITEM != dso.getType()) { throw new IllegalArgumentException("Currenty DSpace supports DOIs for Items only."); } try { - provider.registerOnline(context, dso, - DOI.SCHEME + doiRow.getDoi()); + provider.registerOnline(context, dso, DOI.SCHEME + doiRow.getDoi(), filter); if (!quiet) { System.out.println("This identifier: " @@ -466,29 +472,23 @@ public class DOIOrganiser { } /** - * Register DOI with the provider, always applying (ie. never skipping) any configured filters + * Register DOI with the provider * @param doiRow - doi to register * @throws SQLException * @throws DOIIdentifierException */ public void register(DOI doiRow) throws SQLException, DOIIdentifierException { - if (this.skipFilter) { - System.out.println("Skipping the filter for " + doiRow.getDoi()); - } - register(doiRow, this.skipFilter); + register(doiRow, this.filter); } /** - * Reserve DOI with the provider, always applying (ie. never skipping) any configured filters + * Reserve DOI with the provider, * @param doiRow - doi to reserve * @throws SQLException * @throws DOIIdentifierException */ public void reserve(DOI doiRow) { - if (this.skipFilter) { - System.out.println("Skipping the filter for " + doiRow.getDoi()); - } - reserve(doiRow, this.skipFilter); + reserve(doiRow, this.filter); } /** @@ -497,14 +497,14 @@ public class DOIOrganiser { * @throws SQLException * @throws DOIIdentifierException */ - public void reserve(DOI doiRow, boolean skipFilter) { + public void reserve(DOI doiRow, Filter filter) { DSpaceObject dso = doiRow.getDSpaceObject(); if (Constants.ITEM != dso.getType()) { throw new IllegalArgumentException("Currently DSpace supports DOIs for Items only."); } try { - provider.reserveOnline(context, dso, DOI.SCHEME + doiRow.getDoi(), skipFilter); + provider.reserveOnline(context, dso, DOI.SCHEME + doiRow.getDoi(), filter); if (!quiet) { System.out.println("This identifier : " + DOI.SCHEME + doiRow.getDoi() + " is successfully reserved."); @@ -699,7 +699,7 @@ public class DOIOrganiser { //Check if this Item has an Identifier, mint one if it doesn't if (null == doiRow) { - doi = provider.mint(context, dso, this.skipFilter); + doi = provider.mint(context, dso, this.filter); doiRow = doiService.findByDoi(context, doi.substring(DOI.SCHEME.length())); return doiRow; @@ -723,7 +723,7 @@ public class DOIOrganiser { doiRow = doiService.findDOIByDSpaceObject(context, dso); if (null == doiRow) { - doi = provider.mint(context, dso, this.skipFilter); + doi = provider.mint(context, dso, this.filter); doiRow = doiService.findByDoi(context, doi.substring(DOI.SCHEME.length())); } diff --git a/dspace-api/src/main/java/org/dspace/identifier/service/IdentifierService.java b/dspace-api/src/main/java/org/dspace/identifier/service/IdentifierService.java index 64eee1dfcf..568e4e25a5 100644 --- a/dspace-api/src/main/java/org/dspace/identifier/service/IdentifierService.java +++ b/dspace-api/src/main/java/org/dspace/identifier/service/IdentifierService.java @@ -9,9 +9,11 @@ package org.dspace.identifier.service; import java.sql.SQLException; import java.util.List; +import java.util.Map; import org.dspace.authorize.AuthorizeException; import org.dspace.content.DSpaceObject; +import org.dspace.content.logic.Filter; import org.dspace.core.Context; import org.dspace.identifier.Identifier; import org.dspace.identifier.IdentifierException; @@ -103,6 +105,40 @@ public interface IdentifierService { */ void register(Context context, DSpaceObject dso) throws AuthorizeException, SQLException, IdentifierException; + /** + * @param context The relevant DSpace Context. + * @param dso DSpace object to be registered + * @param typeFilters If a service supports a given Identifier implementation, apply the associated filter + * @throws AuthorizeException if authorization error + * @throws SQLException if database error + * @throws IdentifierException if identifier error + */ + void register(Context context, DSpaceObject dso, Map, Filter> typeFilters) + throws AuthorizeException, SQLException, IdentifierException; + + /** + * @param context The relevant DSpace Context. + * @param dso DSpace object to be registered + * @param type Type of identifier to register + * @param filter If a service supports a given Identifier implementation, apply this specific filter + * @throws AuthorizeException if authorization error + * @throws SQLException if database error + * @throws IdentifierException if identifier error + */ + void register(Context context, DSpaceObject dso, Class type, Filter filter) + throws AuthorizeException, SQLException, IdentifierException; + + /** + * @param context The relevant DSpace Context. + * @param dso DSpace object to be registered + * @param type Type of identifier to register + * @throws AuthorizeException if authorization error + * @throws SQLException if database error + * @throws IdentifierException if identifier error + */ + void register(Context context, DSpaceObject dso, Class type) + throws AuthorizeException, SQLException, IdentifierException; + /** * Used to Register a specific Identifier (for example a Handle, hdl:1234.5/6). * The provider is responsible for detecting and processing the appropriate diff --git a/dspace-api/src/test/java/org/dspace/identifier/DOIIdentifierProviderTest.java b/dspace-api/src/test/java/org/dspace/identifier/DOIIdentifierProviderTest.java index b9dbbba647..c891589cff 100644 --- a/dspace-api/src/test/java/org/dspace/identifier/DOIIdentifierProviderTest.java +++ b/dspace-api/src/test/java/org/dspace/identifier/DOIIdentifierProviderTest.java @@ -36,6 +36,7 @@ import org.dspace.content.WorkspaceItem; import org.dspace.content.factory.ContentServiceFactory; import org.dspace.content.logic.DefaultFilter; import org.dspace.content.logic.LogicalStatement; +import org.dspace.content.logic.TrueFilter; import org.dspace.content.service.CollectionService; import org.dspace.content.service.CommunityService; import org.dspace.content.service.ItemService; @@ -128,7 +129,7 @@ public class DOIIdentifierProviderTest provider.itemService = itemService; provider.setConfigurationService(config); provider.setDOIConnector(connector); - provider.setFilterService(null); + provider.setFilter(null); } catch (AuthorizeException ex) { log.error("Authorization Error in init", ex); fail("Authorization Error in init: " + ex.getMessage()); @@ -504,7 +505,7 @@ public class DOIIdentifierProviderTest String doi = null; try { // get a DOI (skipping any filters) - doi = provider.mint(context, item, true); + doi = provider.mint(context, item); } catch (IdentifierException e) { e.printStackTrace(System.err); fail("Got an IdentifierException: " + e.getMessage()); @@ -544,23 +545,18 @@ public class DOIIdentifierProviderTest Item item = newItem(); boolean wasFiltered = false; try { - // Temporarily set the provider to have a filter that always returns false for an item - // (therefore, the item should be 'filtered' out and not apply to this minting request) + // Mint this with the filter DefaultFilter doiFilter = new DefaultFilter(); LogicalStatement alwaysFalse = (context, i) -> false; doiFilter.setStatement(alwaysFalse); - provider.setFilterService(doiFilter); // get a DOI with the method that applies filters by default - provider.mint(context, item); + provider.mint(context, item, doiFilter); } catch (DOIIdentifierNotApplicableException e) { // This is what we wanted to see - we can return safely wasFiltered = true; } catch (IdentifierException e) { e.printStackTrace(); fail("Got an IdentifierException: " + e.getMessage()); - } finally { - // Set filter service back to null - provider.setFilterService(null); } // Fail the test if the filter didn't throw a "not applicable" exception assertTrue("DOI minting attempt was not filtered by filter service", wasFiltered); @@ -583,17 +579,14 @@ public class DOIIdentifierProviderTest DefaultFilter doiFilter = new DefaultFilter(); LogicalStatement alwaysTrue = (context, i) -> true; doiFilter.setStatement(alwaysTrue); - provider.setFilterService(doiFilter); // get a DOI with the method that applies filters by default - doi = provider.mint(context, item); + doi = provider.mint(context, item, doiFilter); } catch (DOIIdentifierNotApplicableException e) { // This is what we wanted to see - we can return safely wasFiltered = true; } catch (IdentifierException e) { e.printStackTrace(); fail("Got an IdentifierException: " + e.getMessage()); - } finally { - provider.setFilterService(null); } // If the attempt was filtered, fail assertFalse("DOI minting attempt was incorrectly filtered by filter service", wasFiltered); @@ -665,7 +658,7 @@ public class DOIIdentifierProviderTest Item item = newItem(); // Register, skipping the filter - String doi = provider.register(context, item, true); + String doi = provider.register(context, item, new TrueFilter()); // we want the created DOI to be returned in the following format: // doi:10./. diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/ItemIdentifierController.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/ItemIdentifierController.java new file mode 100644 index 0000000000..1321ebe992 --- /dev/null +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/ItemIdentifierController.java @@ -0,0 +1,144 @@ +/** + * 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.app.rest; + +import static org.dspace.app.rest.utils.RegexUtils.REGEX_REQUESTMAPPING_IDENTIFIER_AS_UUID; + +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.dspace.app.rest.converter.ConverterService; +import org.dspace.app.rest.converter.MetadataConverter; +import org.dspace.app.rest.model.IdentifierRest; +import org.dspace.app.rest.model.IdentifiersRest; +import org.dspace.app.rest.model.ItemRest; +import org.dspace.app.rest.repository.ItemRestRepository; +import org.dspace.app.rest.utils.ContextUtil; +import org.dspace.app.rest.utils.Utils; +import org.dspace.authorize.AuthorizeException; +import org.dspace.content.Item; +import org.dspace.content.logic.TrueFilter; +import org.dspace.content.service.ItemService; +import org.dspace.core.Context; +import org.dspace.handle.factory.HandleServiceFactory; +import org.dspace.identifier.DOI; +import org.dspace.identifier.DOIIdentifierProvider; +import org.dspace.identifier.IdentifierException; +import org.dspace.identifier.IdentifierNotFoundException; +import org.dspace.identifier.service.DOIService; +import org.dspace.identifier.service.IdentifierService; +import org.dspace.services.factory.DSpaceServicesFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.rest.webmvc.ControllerUtils; +import org.springframework.data.rest.webmvc.ResourceNotFoundException; +import org.springframework.hateoas.RepresentationModel; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RestController; + +/** + * Controller to register identifiers + */ +@RestController +@RequestMapping("/api/" + ItemRest.CATEGORY + "/" + ItemRest.PLURAL_NAME + REGEX_REQUESTMAPPING_IDENTIFIER_AS_UUID + + "/" + IdentifiersRest.PLURAL_NAME) +public class ItemIdentifierController { + + @Autowired + ConverterService converter; + + @Autowired + ItemService itemService; + + @Autowired + ItemRestRepository itemRestRepository; + + @Autowired + MetadataConverter metadataConverter; + + @Autowired + IdentifierService identifierService; + + @Autowired + DOIService doiService; + + @Autowired + Utils utils; + + public IdentifiersRest get(@PathVariable UUID uuid, HttpServletRequest request, + HttpServletResponse response) + throws SQLException, AuthorizeException { + Context context = ContextUtil.obtainContext(request); + Item item = itemService.find(context, uuid); + if (item == null) { + throw new ResourceNotFoundException("Could not find item with id " + uuid); + } + IdentifiersRest identifiersRest = new IdentifiersRest(); + List identifierRests = new ArrayList<>(); + DOI doi = doiService.findDOIByDSpaceObject(context, item); + String handle = HandleServiceFactory.getInstance().getHandleService().findHandle(context, item); + try { + if (doi != null) { + String doiUrl = doiService.DOIToExternalForm(doi.getDoi()); + IdentifierRest identifierRest = new IdentifierRest(doiUrl, "doi", String.valueOf(doi.getStatus())); + identifierRests.add(identifierRest); + } + if (handle != null) { + identifierRests.add(new IdentifierRest(handle, "handle", null)); + } + } catch (IdentifierException e) { + throw new IllegalStateException("Failed to register identifier: " + e.getMessage()); + } + + return identifiersRest; + } + + /** + * Method to register an identifier for an item. + * + * @return OK + */ + @RequestMapping(method = RequestMethod.POST) + @PreAuthorize("hasPermission(#uuid, 'ITEM', 'ADMIN')") + public ResponseEntity> registerIdentifierForItem(@PathVariable UUID uuid, + HttpServletRequest request, + HttpServletResponse response) + throws SQLException, AuthorizeException { + Context context = ContextUtil.obtainContext(request); + + Item item = itemService.find(context, uuid); + + if (item == null) { + throw new ResourceNotFoundException("Could not find item with id " + uuid); + } + + String identifier = null; + try { + DOIIdentifierProvider doiIdentifierProvider = DSpaceServicesFactory.getInstance().getServiceManager() + .getServiceByName("org.dspace.identifier.DOIIdentifierProvider", DOIIdentifierProvider.class); + doiIdentifierProvider.register(context, item, new TrueFilter()); + if (context != null) { + context.commit(); + } + } catch (IdentifierNotFoundException e) { + return ControllerUtils.toEmptyResponse(HttpStatus.NO_CONTENT); + } catch (IdentifierException e) { + throw new IllegalStateException("Failed to register identifier: " + identifier); + } + return ControllerUtils.toEmptyResponse(HttpStatus.CREATED); + } + +} diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/IdentifierRest.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/IdentifierRest.java new file mode 100644 index 0000000000..dc0e150311 --- /dev/null +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/IdentifierRest.java @@ -0,0 +1,67 @@ +/** + * 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.app.rest.model; + +import com.fasterxml.jackson.annotation.JsonProperty; + +/** + * Implementation of IdentifierRest REST resource, representing some DSpace identifier + * for use with the REST API + * + * @author Kim Shepherd + */ +public class IdentifierRest implements RestModel { + + // Set names used in component wiring + public static final String NAME = "identifier"; + public static final String PLURAL_NAME = "identifiers"; + private String value; + private String identifierType; + private String identifierStatus; + + // Empty constructor + public IdentifierRest() { + } + + public IdentifierRest(String value, String identifierType, String identifierStatus) { + this.value = value; + this.identifierType = identifierType; + this.identifierStatus = identifierStatus; + } + + // Return name for getType() + @Override + @JsonProperty(access = JsonProperty.Access.READ_ONLY) + public String getType() { + return NAME; + } + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } + + public String getIdentifierType() { + return identifierType; + } + + public void setIdentifierType(String identifierType) { + this.identifierType = identifierType; + } + + public String getIdentifierStatus() { + return identifierStatus; + } + + public void setIdentifierStatus(String identifierStatus) { + this.identifierStatus = identifierStatus; + } +} diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/IdentifiersRest.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/IdentifiersRest.java new file mode 100644 index 0000000000..bd7ead511f --- /dev/null +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/IdentifiersRest.java @@ -0,0 +1,47 @@ +/** + * 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.app.rest.model; + +import java.util.ArrayList; +import java.util.List; + +import com.fasterxml.jackson.annotation.JsonProperty; + +/** + * Implementation of IdentifierRest REST resource, representing a list of all identifiers + * for use with the REST API + * + * @author Kim Shepherd + */ +public class IdentifiersRest implements RestModel { + + // Set names used in component wiring + public static final String NAME = "identifier"; + public static final String PLURAL_NAME = "identifiers"; + private List identifiers; + + // Empty constructor + public IdentifiersRest() { + identifiers = new ArrayList<>(); + } + + // Return name for getType() + @Override + @JsonProperty(access = JsonProperty.Access.READ_ONLY) + public String getType() { + return NAME; + } + + public List getIdentifiers() { + return identifiers; + } + + public void setIdentifiers(List identifiers) { + this.identifiers = identifiers; + } +} diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/ItemRest.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/ItemRest.java index 63004b68d2..dcb68793a7 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/ItemRest.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/ItemRest.java @@ -25,6 +25,10 @@ import com.fasterxml.jackson.annotation.JsonProperty; name = ItemRest.BUNDLES, method = "getBundles" ), + //@LinkRest( + // name = ItemRest.IDENTIFIERS, + // method = "getIdentifiers" + //), @LinkRest( name = ItemRest.MAPPED_COLLECTIONS, method = "getMappedCollections" @@ -57,6 +61,7 @@ public class ItemRest extends DSpaceObjectRest { public static final String ACCESS_STATUS = "accessStatus"; public static final String BUNDLES = "bundles"; + public static final String IDENTIFIERS = "identifiers"; public static final String MAPPED_COLLECTIONS = "mappedCollections"; public static final String OWNING_COLLECTION = "owningCollection"; public static final String RELATIONSHIPS = "relationships"; diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/step/DataIdentifiers.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/step/DataIdentifiers.java index c487d3769e..7e90970377 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/step/DataIdentifiers.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/step/DataIdentifiers.java @@ -20,6 +20,10 @@ public class DataIdentifiers implements SectionData { String doi; List otherIdentifiers; + public DataIdentifiers() { + + } + public String getHandle() { return handle; } diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/ItemRestRepository.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/ItemRestRepository.java index 16ce8629d1..42e4425e2c 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/ItemRestRepository.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/ItemRestRepository.java @@ -45,6 +45,10 @@ import org.dspace.content.service.RelationshipService; import org.dspace.content.service.RelationshipTypeService; import org.dspace.content.service.WorkspaceItemService; import org.dspace.core.Context; +import org.dspace.identifier.IdentifierException; +import org.dspace.identifier.VersionedHandleIdentifierProviderWithCanonicalHandles; +import org.dspace.identifier.service.IdentifierService; +import org.dspace.services.factory.DSpaceServicesFactory; import org.dspace.util.UUIDUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.Page; @@ -92,6 +96,9 @@ public class ItemRestRepository extends DSpaceObjectRestRepository stringList) throws AuthorizeException, SQLException, RepositoryMethodNotImplementedException { diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/step/ShowIdentifiersStep.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/step/ShowIdentifiersStep.java index 2bdfbbde80..173ac4c500 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/step/ShowIdentifiersStep.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/step/ShowIdentifiersStep.java @@ -7,6 +7,7 @@ */ package org.dspace.app.rest.submit.step; +import java.sql.SQLException; import java.util.ArrayList; import java.util.List; import javax.servlet.http.HttpServletRequest; @@ -26,6 +27,7 @@ import org.dspace.core.Context; import org.dspace.handle.factory.HandleServiceFactory; import org.dspace.handle.service.HandleService; import org.dspace.identifier.DOI; +import org.dspace.identifier.DOIIdentifierProvider; import org.dspace.identifier.Handle; import org.dspace.identifier.IdentifierException; import org.dspace.identifier.factory.IdentifierServiceFactory; @@ -90,20 +92,31 @@ public class ShowIdentifiersStep extends AbstractProcessingStep { IdentifierServiceFactory.getInstance().getIdentifierService(); // Attempt to look up handle and DOI identifiers for this item String handle = identifierService.lookup(context, obj.getItem(), Handle.class); - String doi = identifierService.lookup(context, obj.getItem(), DOI.class); + String simpleDoi = identifierService.lookup(context, obj.getItem(), DOI.class); + DOI doi = null; + String doiString = null; + try { + doi = IdentifierServiceFactory.getInstance().getDOIService().findDOIByDSpaceObject(context, obj.getItem()); + if (doi != null && !DOIIdentifierProvider.MINTED.equals(doi.getStatus()) + && !DOIIdentifierProvider.MINTED.equals(doi.getStatus())) { + doiString = doi.getDoi(); + } + } catch (SQLException e) { + log.error(e.getMessage()); + } // Look up all identifiers and if they're not the DOI or handle, add them to the 'other' list List otherIdentifiers = new ArrayList<>(); for (String identifier : identifierService.lookup(context, obj.getItem())) { - if (!StringUtils.equals(doi, identifier) && !StringUtils.equals(handle, identifier)) { + if (!StringUtils.equals(simpleDoi, identifier) && !StringUtils.equals(handle, identifier)) { otherIdentifiers.add(identifier); } } // If we got a DOI, format it to its external form - if (StringUtils.isNotEmpty(doi)) { + if (StringUtils.isNotEmpty(doiString)) { try { - doi = IdentifierServiceFactory.getInstance().getDOIService().DOIToExternalForm(doi); + doiString = IdentifierServiceFactory.getInstance().getDOIService().DOIToExternalForm(doiString); } catch (IdentifierException e) { log.error("Error formatting DOI: " + doi); } @@ -114,7 +127,7 @@ public class ShowIdentifiersStep extends AbstractProcessingStep { } // Populate bean with data and return - result.setDoi(doi); + result.setDoi(doiString); result.setHandle(handle); result.setOtherIdentifiers(otherIdentifiers); diff --git a/dspace/config/item-submission.xml b/dspace/config/item-submission.xml index 3ad984ec97..2aa1646bfa 100644 --- a/dspace/config/item-submission.xml +++ b/dspace/config/item-submission.xml @@ -240,7 +240,7 @@ - + diff --git a/dspace/config/modules/identifiers.cfg b/dspace/config/modules/identifiers.cfg index 3e0992e090..e0e23a5a2c 100644 --- a/dspace/config/modules/identifiers.cfg +++ b/dspace/config/modules/identifiers.cfg @@ -5,7 +5,35 @@ # as the Show Identifiers step which can "pre-mint" DOIs and Handles # #----------------------------------------------------------------------# -# Should identifiers be registered in the Show Identifiers step and minted / updated / deleted as appropriate -# in the DOI Consumer? Setting this property to 'true' is recommended +# Should DOIs be minted for (future) registration at workspace item creation? +# A DOI created at this stage will be in a 'PENDING' status while in workspace and workflow. +# At the time of item install, the DOI filter (if any) will be applied and if the item matches the filter, the DOI +# status will be updated to TO_BE_REGISTERED. An administrator can also manually progress the DOI status, overriding +# any filters, in the item status page. +# This option doesn't require the Show Identifiers submission step to be visible. # Default: false -#identifiers.submission.register = false \ No newline at end of file +#identifiers.submission.register = true + +# This configuration property can be set to a filter name to determine if a PENDING DOI for an item +# should be queued for registration. If the filter doesn't match, the DOI will stay in PENDING or MINTED status +# so that the identifier itself persists in case it is considered for registration in the future. +# See doi-filter and other example filters in item-filters.xml. +# Default (always_true_filter) +#identifiers.submission.filter.install = doi-filter + +# This optional configuration property can be set to a filter name, in case there are some initial rules to apply +# when first deciding whether a DOI should be be created for a new workspace item with a PENDING status. +# This filter is only applied if identifiers.submission.register is true. +# This filter is updated as submission data is saved. +# Default: (always_true_filter) +#identifiers.submission.filter.workspace = always_true_filter + +# If true, the workspace filter will be applied as submission data is saved. If the filter no longer +# matches the item, the DOI will be shifted into a MINTED status and not displayed in the submission section. +# If false, then once a DOI has been created with PENDING status it will remain that way until final item install +# Default: true +#identifiers.submission.strip_pending_during_submission = true + +# This configuration property can be set to a filter name to determine if an item processed by RegisterDOI curation +# task should be eligible for a DOI +#identifiers.submission.filter.curation = always_true_filter \ No newline at end of file diff --git a/dspace/config/spring/api/identifier-service.xml b/dspace/config/spring/api/identifier-service.xml index e9f08003bd..dbcd49df0e 100644 --- a/dspace/config/spring/api/identifier-service.xml +++ b/dspace/config/spring/api/identifier-service.xml @@ -13,7 +13,7 @@ scope="singleton"/> @@ -42,7 +42,7 @@ a DOIConnector that handles all API calls to your DOI registration agency. Please configure a DOIConnector as well! --> - - + - - + + + + + + - - - + @@ -226,16 +241,12 @@ - - - + - - - + + + + + + + + + + + + + + + +