diff --git a/dspace-api/src/main/java/org/dspace/curate/Curation.java b/dspace-api/src/main/java/org/dspace/curate/Curation.java new file mode 100644 index 0000000000..50ad2d1197 --- /dev/null +++ b/dspace-api/src/main/java/org/dspace/curate/Curation.java @@ -0,0 +1,364 @@ +/** + * 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.curate; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileReader; +import java.io.IOException; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.io.PrintStream; +import java.io.Writer; +import java.sql.SQLException; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import java.util.UUID; + +import org.apache.commons.cli.ParseException; +import org.apache.commons.io.output.NullOutputStream; +import org.dspace.authorize.AuthorizeException; +import org.dspace.content.DSpaceObject; +import org.dspace.content.factory.ContentServiceFactory; +import org.dspace.core.Context; +import org.dspace.core.factory.CoreServiceFactory; +import org.dspace.curate.factory.CurateServiceFactory; +import org.dspace.eperson.EPerson; +import org.dspace.eperson.factory.EPersonServiceFactory; +import org.dspace.eperson.service.EPersonService; +import org.dspace.handle.factory.HandleServiceFactory; +import org.dspace.handle.service.HandleService; +import org.dspace.scripts.DSpaceRunnable; +import org.dspace.utils.DSpace; + +/** + * CurationCli provides command-line access to Curation tools and processes. + * + * @author richardrodgers + */ +public class Curation extends DSpaceRunnable { + + protected EPersonService ePersonService = EPersonServiceFactory.getInstance().getEPersonService(); + + protected Context context; + private CurationClientOptions curationClientOptions; + + private String task; + private String taskFile; + private String id; + private String queue; + private String scope; + private String reporter; + private Map parameters; + private boolean verbose; + + @Override + public void internalRun() throws Exception { + if (curationClientOptions == CurationClientOptions.HELP) { + printHelp(); + return; + } + + Curator curator = initCurator(); + + // load curation tasks + if (curationClientOptions == CurationClientOptions.TASK) { + long start = System.currentTimeMillis(); + handleCurationTask(curator); + this.endScript(start); + } + + // process task queue + if (curationClientOptions == CurationClientOptions.QUEUE) { + // process the task queue + TaskQueue taskQueue = (TaskQueue) CoreServiceFactory.getInstance().getPluginService() + .getSinglePlugin(TaskQueue.class); + if (taskQueue == null) { + super.handler.logError("No implementation configured for queue"); + throw new UnsupportedOperationException("No queue service available"); + } + long timeRun = this.runQueue(taskQueue, curator); + this.endScript(timeRun); + } + } + + /** + * Does the curation task (-t) or the task in the given file (-T). + * Checks: + * - if required option -i is missing. + * - if option -t has a valid task option + */ + private void handleCurationTask(Curator curator) throws IOException, SQLException { + String taskName; + if (commandLine.hasOption('t')) { + if (verbose) { + handler.logInfo("Adding task: " + this.task); + } + curator.addTask(this.task); + if (verbose && !curator.hasTask(this.task)) { + handler.logInfo("Task: " + this.task + " not resolved"); + } + } else if (commandLine.hasOption('T')) { + // load taskFile + BufferedReader reader = null; + try { + reader = new BufferedReader(new FileReader(this.taskFile)); + while ((taskName = reader.readLine()) != null) { + if (verbose) { + super.handler.logInfo("Adding task: " + taskName); + } + curator.addTask(taskName); + } + } finally { + if (reader != null) { + reader.close(); + } + } + } + // run tasks against object + if (verbose) { + super.handler.logInfo("Starting curation"); + super.handler.logInfo("Curating id: " + this.id); + } + if ("all".equals(this.id)) { + // run on whole Site + curator.curate(context, + ContentServiceFactory.getInstance().getSiteService().findSite(context).getHandle()); + } else { + curator.curate(context, this.id); + } + } + + /** + * Runs task queue (-q set) + * + * @param queue The task queue + * @param curator The curator + * @return Time when queue started + */ + private long runQueue(TaskQueue queue, Curator curator) throws SQLException, AuthorizeException, IOException { + // use current time as our reader 'ticket' + long ticket = System.currentTimeMillis(); + Iterator entryIter = queue.dequeue(this.queue, ticket).iterator(); + while (entryIter.hasNext()) { + TaskQueueEntry entry = entryIter.next(); + if (verbose) { + super.handler.logInfo("Curating id: " + entry.getObjectId()); + } + curator.clear(); + // does entry relate to a DSO or workflow object? + if (entry.getObjectId().indexOf('/') > 0) { + for (String taskName : entry.getTaskNames()) { + curator.addTask(taskName); + } + curator.curate(context, entry.getObjectId()); + } else { + // make eperson who queued task the effective user + EPerson agent = ePersonService.findByEmail(context, entry.getEpersonId()); + if (agent != null) { + context.setCurrentUser(agent); + } + CurateServiceFactory.getInstance().getWorkflowCuratorService() + .curate(curator, context, entry.getObjectId()); + } + } + queue.release(this.queue, ticket, true); + return ticket; + } + + /** + * End of curation script; logs script time if -v verbose is set + * + * @param timeRun Time script was started + * @throws SQLException If DSpace contextx can't complete + */ + private void endScript(long timeRun) throws SQLException { + context.complete(); + if (verbose) { + long elapsed = System.currentTimeMillis() - timeRun; + this.handler.logInfo("Ending curation. Elapsed time: " + elapsed); + } + } + + /** + * Initialize the curator with command line variables + * + * @return Initialised curator + * @throws FileNotFoundException If file of command line variable -r reporter is not found + */ + private Curator initCurator() throws FileNotFoundException { + Curator curator = new Curator(); + OutputStream reporterStream; + if (null == this.reporter) { + reporterStream = new NullOutputStream(); + } else if ("-".equals(this.reporter)) { + reporterStream = System.out; + } else { + reporterStream = new PrintStream(this.reporter); + } + Writer reportWriter = new OutputStreamWriter(reporterStream); + curator.setReporter(reportWriter); + + if (this.scope != null) { + Curator.TxScope txScope = Curator.TxScope.valueOf(this.scope.toUpperCase()); + curator.setTransactionScope(txScope); + } + + curator.addParameters(parameters); + // we are operating in batch mode, if anyone cares. + curator.setInvoked(Curator.Invoked.BATCH); + return curator; + } + + @Override + public void printHelp() { + super.printHelp(); + super.handler.logInfo("\nwhole repo: CurationCli -t estimate -i all"); + super.handler.logInfo("single item: CurationCli -t generate -i itemId"); + super.handler.logInfo("task queue: CurationCli -q monthly"); + } + + @Override + public CurationScriptConfiguration getScriptConfiguration() { + return new DSpace().getServiceManager().getServiceByName("curate", CurationScriptConfiguration.class); + } + + @Override + public void setup() throws ParseException { + assignCurrentUserInContext(); + this.curationClientOptions = CurationClientOptions.getClientOption(commandLine); + + if (this.curationClientOptions != null) { + this.initGeneralLineOptionsAndCheckIfValid(); + if (curationClientOptions == CurationClientOptions.TASK) { + this.initTaskLineOptionsAndCheckIfValid(); + } else if (curationClientOptions == CurationClientOptions.QUEUE) { + this.queue = this.commandLine.getOptionValue('q'); + } + } else { + throw new IllegalArgumentException("[--help || --task|--taskfile <> -identifier <> || -queue <> ] must be" + + " specified"); + } + } + + protected void assignCurrentUserInContext() throws ParseException { + UUID currentUserUuid = this.getEpersonIdentifier(); + try { + this.context = new Context(Context.Mode.BATCH_EDIT); + EPerson eperson = ePersonService.find(context, currentUserUuid); + if (eperson == null) { + super.handler.logError("EPerson not found: " + currentUserUuid); + throw new IllegalArgumentException("Unable to find a user with uuid: " + currentUserUuid); + } + this.context.setCurrentUser(eperson); + } catch (SQLException e) { + handler.handleException("Something went wrong trying to fetch eperson for uuid: " + currentUserUuid, e); + } + } + + /** + * Fills in some optional command line options. + * Checks if there are missing required options or invalid values for options. + */ + private void initGeneralLineOptionsAndCheckIfValid() { + // report file + if (this.commandLine.hasOption('r')) { + this.reporter = this.commandLine.getOptionValue('r'); + } + + // parameters + this.parameters = new HashMap<>(); + if (this.commandLine.hasOption('p')) { + for (String parameter : this.commandLine.getOptionValues('p')) { + String[] parts = parameter.split("=", 2); + String name = parts[0].trim(); + String value; + if (parts.length > 1) { + value = parts[1].trim(); + } else { + value = "true"; + } + this.parameters.put(name, value); + } + } + + // verbose + verbose = false; + if (commandLine.hasOption('v')) { + verbose = true; + } + + // scope + if (this.commandLine.getOptionValue('s') != null) { + this.scope = this.commandLine.getOptionValue('s'); + if (this.scope != null && Curator.TxScope.valueOf(this.scope.toUpperCase()) == null) { + this.handler.logError("Bad transaction scope '" + this.scope + "': only 'object', 'curation' or " + + "'open' recognized"); + throw new IllegalArgumentException( + "Bad transaction scope '" + this.scope + "': only 'object', 'curation' or " + + "'open' recognized"); + } + } + } + + /** + * Fills in required command line options for the task or taskFile option. + * Checks if there are is a missing required -i option and if -i is either 'all' or a valid dso handle. + * Checks if -t task has a valid task option. + * Checks if -T taskfile is a valid file. + */ + private void initTaskLineOptionsAndCheckIfValid() { + // task or taskFile + if (this.commandLine.hasOption('t')) { + this.task = this.commandLine.getOptionValue('t'); + if (!CurationClientOptions.getTaskOptions().contains(this.task)) { + super.handler + .logError("-t task must be one of: " + CurationClientOptions.getTaskOptions()); + throw new IllegalArgumentException( + "-t task must be one of: " + CurationClientOptions.getTaskOptions()); + } + } else if (this.commandLine.hasOption('T')) { + this.taskFile = this.commandLine.getOptionValue('T'); + if (!(new File(this.taskFile).isFile())) { + super.handler + .logError("-T taskFile must be valid file: " + this.taskFile); + throw new IllegalArgumentException("-T taskFile must be valid file: " + this.taskFile); + } + } + + if (this.commandLine.hasOption('i')) { + this.id = this.commandLine.getOptionValue('i').toLowerCase(); + if (!this.id.equalsIgnoreCase("all")) { + HandleService handleService = HandleServiceFactory.getInstance().getHandleService(); + DSpaceObject dso; + try { + dso = handleService.resolveToObject(this.context, id); + } catch (SQLException e) { + super.handler.logError("SQLException trying to resolve handle " + id + " to a valid dso"); + throw new IllegalArgumentException( + "SQLException trying to resolve handle " + id + " to a valid dso"); + } + if (dso == null) { + super.handler.logError("Id must be specified: a valid dso handle or 'all'; " + this.id + " could " + + "not be resolved to valid dso handle"); + throw new IllegalArgumentException( + "Id must be specified: a valid dso handle or 'all'; " + this.id + " could " + + "not be resolved to valid dso handle"); + } + } + } else { + super.handler.logError("Id must be specified: a handle, 'all', or no -i and a -q task queue (-h for " + + "help)"); + throw new IllegalArgumentException( + "Id must be specified: a handle, 'all', or no -i and a -q task queue (-h for " + + "help)"); + } + } +} diff --git a/dspace-api/src/main/java/org/dspace/curate/CurationCli.java b/dspace-api/src/main/java/org/dspace/curate/CurationCli.java index 8f5d91cc1c..cd197b5a16 100644 --- a/dspace-api/src/main/java/org/dspace/curate/CurationCli.java +++ b/dspace-api/src/main/java/org/dspace/curate/CurationCli.java @@ -7,229 +7,16 @@ */ package org.dspace.curate; -import java.io.BufferedReader; -import java.io.File; -import java.io.FileNotFoundException; -import java.io.FileReader; -import java.io.IOException; -import java.io.OutputStream; -import java.io.OutputStreamWriter; -import java.io.PrintStream; -import java.io.Writer; import java.sql.SQLException; -import java.util.HashMap; -import java.util.Iterator; -import java.util.Map; -import org.apache.commons.io.output.NullOutputStream; -import org.dspace.authorize.AuthorizeException; -import org.dspace.content.DSpaceObject; -import org.dspace.content.factory.ContentServiceFactory; +import org.apache.commons.cli.ParseException; import org.dspace.core.Context; -import org.dspace.core.factory.CoreServiceFactory; -import org.dspace.curate.factory.CurateServiceFactory; import org.dspace.eperson.EPerson; -import org.dspace.eperson.factory.EPersonServiceFactory; -import org.dspace.eperson.service.EPersonService; -import org.dspace.handle.factory.HandleServiceFactory; -import org.dspace.handle.service.HandleService; -import org.dspace.scripts.DSpaceRunnable; -import org.dspace.utils.DSpace; -/** - * CurationCli provides command-line access to Curation tools and processes. - * - * @author richardrodgers - */ -public class CurationCli extends DSpaceRunnable { - - private EPersonService ePersonService = EPersonServiceFactory.getInstance().getEPersonService(); - - private Context context; - private CurationClientOptions curationClientOptions; - - private String task; - private String taskFile; - private String id; - private String queue; - private String scope; - private String reporter; - private Map parameters; - private boolean verbose; +public class CurationCli extends Curation { @Override - public void internalRun() throws Exception { - if (curationClientOptions == CurationClientOptions.HELP) { - printHelp(); - return; - } - - Curator curator = initCurator(); - - // load curation tasks - if (curationClientOptions == CurationClientOptions.TASK) { - long start = System.currentTimeMillis(); - handleCurationTask(curator); - this.endScript(start); - } - - // process task queue - if (curationClientOptions == CurationClientOptions.QUEUE) { - // process the task queue - TaskQueue taskQueue = (TaskQueue) CoreServiceFactory.getInstance().getPluginService() - .getSinglePlugin(TaskQueue.class); - if (taskQueue == null) { - super.handler.logError("No implementation configured for queue"); - throw new UnsupportedOperationException("No queue service available"); - } - long timeRun = this.runQueue(taskQueue, curator); - this.endScript(timeRun); - } - } - - /** - * Does the curation task (-t) or the task in the given file (-T). - * Checks: - * - if required option -i is missing. - * - if option -t has a valid task option - */ - private void handleCurationTask(Curator curator) throws IOException, SQLException { - String taskName; - if (commandLine.hasOption('t')) { - if (verbose) { - handler.logInfo("Adding task: " + this.task); - } - curator.addTask(this.task); - if (verbose && !curator.hasTask(this.task)) { - handler.logInfo("Task: " + this.task + " not resolved"); - } - } else if (commandLine.hasOption('T')) { - // load taskFile - BufferedReader reader = null; - try { - reader = new BufferedReader(new FileReader(this.taskFile)); - while ((taskName = reader.readLine()) != null) { - if (verbose) { - super.handler.logInfo("Adding task: " + taskName); - } - curator.addTask(taskName); - } - } finally { - if (reader != null) { - reader.close(); - } - } - } - // run tasks against object - if (verbose) { - super.handler.logInfo("Starting curation"); - super.handler.logInfo("Curating id: " + this.id); - } - if ("all".equals(this.id)) { - // run on whole Site - curator.curate(context, - ContentServiceFactory.getInstance().getSiteService().findSite(context).getHandle()); - } else { - curator.curate(context, this.id); - } - } - - /** - * Runs task queue (-q set) - * - * @param queue The task queue - * @param curator The curator - * @return Time when queue started - */ - private long runQueue(TaskQueue queue, Curator curator) throws SQLException, AuthorizeException, IOException { - // use current time as our reader 'ticket' - long ticket = System.currentTimeMillis(); - Iterator entryIter = queue.dequeue(this.queue, ticket).iterator(); - while (entryIter.hasNext()) { - TaskQueueEntry entry = entryIter.next(); - if (verbose) { - super.handler.logInfo("Curating id: " + entry.getObjectId()); - } - curator.clear(); - // does entry relate to a DSO or workflow object? - if (entry.getObjectId().indexOf('/') > 0) { - for (String taskName : entry.getTaskNames()) { - curator.addTask(taskName); - } - curator.curate(context, entry.getObjectId()); - } else { - // make eperson who queued task the effective user - EPerson agent = ePersonService.findByEmail(context, entry.getEpersonId()); - if (agent != null) { - context.setCurrentUser(agent); - } - CurateServiceFactory.getInstance().getWorkflowCuratorService() - .curate(curator, context, entry.getObjectId()); - } - } - queue.release(this.queue, ticket, true); - return ticket; - } - - /** - * End of curation script; logs script time if -v verbose is set - * - * @param timeRun Time script was started - * @throws SQLException If DSpace contextx can't complete - */ - private void endScript(long timeRun) throws SQLException { - context.complete(); - if (verbose) { - long elapsed = System.currentTimeMillis() - timeRun; - this.handler.logInfo("Ending curation. Elapsed time: " + elapsed); - } - } - - /** - * Initialize the curator with command line variables - * - * @return Initialised curator - * @throws FileNotFoundException If file of command line variable -r reporter is not found - */ - private Curator initCurator() throws FileNotFoundException { - Curator curator = new Curator(); - OutputStream reporterStream; - if (null == this.reporter) { - reporterStream = new NullOutputStream(); - } else if ("-".equals(this.reporter)) { - reporterStream = System.out; - } else { - reporterStream = new PrintStream(this.reporter); - } - Writer reportWriter = new OutputStreamWriter(reporterStream); - curator.setReporter(reportWriter); - - if (this.scope != null) { - Curator.TxScope txScope = Curator.TxScope.valueOf(this.scope.toUpperCase()); - curator.setTransactionScope(txScope); - } - - curator.addParameters(parameters); - // we are operating in batch mode, if anyone cares. - curator.setInvoked(Curator.Invoked.BATCH); - return curator; - } - - @Override - public void printHelp() { - super.printHelp(); - super.handler.logInfo("\nwhole repo: CurationCli -t estimate -i all"); - super.handler.logInfo("single item: CurationCli -t generate -i itemId"); - super.handler.logInfo("task queue: CurationCli -q monthly"); - } - - @Override - public CurationScriptConfiguration getScriptConfiguration() { - return new DSpace().getServiceManager().getServiceByName("curate", CurationScriptConfiguration.class); - } - - @Override - public void setup() { + protected void assignCurrentUserInContext() throws ParseException { if (this.commandLine.hasOption('e')) { String ePersonEmail = this.commandLine.getOptionValue('e'); this.context = new Context(Context.Mode.BATCH_EDIT); @@ -244,119 +31,7 @@ public class CurationCli extends DSpaceRunnable { throw new IllegalArgumentException("SQLException trying to find user with email: " + ePersonEmail); } } else { - throw new IllegalArgumentException("Needs an -e to set eperson (admin)"); - } - this.curationClientOptions = CurationClientOptions.getClientOption(commandLine); - - if (this.curationClientOptions != null) { - this.initGeneralLineOptionsAndCheckIfValid(); - if (curationClientOptions == CurationClientOptions.TASK) { - this.initTaskLineOptionsAndCheckIfValid(); - } else if (curationClientOptions == CurationClientOptions.QUEUE) { - this.queue = this.commandLine.getOptionValue('q'); - } - } else { - throw new IllegalArgumentException("[--help || --task|--taskfile <> -identifier <> || -queue <> ] must be" + - " specified"); - } - } - - /** - * Fills in some optional command line options. - * Checks if there are missing required options or invalid values for options. - */ - private void initGeneralLineOptionsAndCheckIfValid() { - // report file - if (this.commandLine.hasOption('r')) { - this.reporter = this.commandLine.getOptionValue('r'); - } - - // parameters - this.parameters = new HashMap<>(); - if (this.commandLine.hasOption('p')) { - for (String parameter : this.commandLine.getOptionValues('p')) { - String[] parts = parameter.split("=", 2); - String name = parts[0].trim(); - String value; - if (parts.length > 1) { - value = parts[1].trim(); - } else { - value = "true"; - } - this.parameters.put(name, value); - } - } - - // verbose - verbose = false; - if (commandLine.hasOption('v')) { - verbose = true; - } - - // scope - if (this.commandLine.getOptionValue('s') != null) { - this.scope = this.commandLine.getOptionValue('s'); - if (this.scope != null && Curator.TxScope.valueOf(this.scope.toUpperCase()) == null) { - this.handler.logError("Bad transaction scope '" + this.scope + "': only 'object', 'curation' or " + - "'open' recognized"); - throw new IllegalArgumentException( - "Bad transaction scope '" + this.scope + "': only 'object', 'curation' or " + - "'open' recognized"); - } - } - } - - /** - * Fills in required command line options for the task or taskFile option. - * Checks if there are is a missing required -i option and if -i is either 'all' or a valid dso handle. - * Checks if -t task has a valid task option. - * Checks if -T taskfile is a valid file. - */ - private void initTaskLineOptionsAndCheckIfValid() { - // task or taskFile - if (this.commandLine.hasOption('t')) { - this.task = this.commandLine.getOptionValue('t'); - if (!CurationClientOptions.getTaskOptions().contains(this.task)) { - super.handler - .logError("-t task must be one of: " + CurationClientOptions.getTaskOptions()); - throw new IllegalArgumentException( - "-t task must be one of: " + CurationClientOptions.getTaskOptions()); - } - } else if (this.commandLine.hasOption('T')) { - this.taskFile = this.commandLine.getOptionValue('T'); - if (!(new File(this.taskFile).isFile())) { - super.handler - .logError("-T taskFile must be valid file: " + this.taskFile); - throw new IllegalArgumentException("-T taskFile must be valid file: " + this.taskFile); - } - } - - if (this.commandLine.hasOption('i')) { - this.id = this.commandLine.getOptionValue('i').toLowerCase(); - if (!this.id.equalsIgnoreCase("all")) { - HandleService handleService = HandleServiceFactory.getInstance().getHandleService(); - DSpaceObject dso; - try { - dso = handleService.resolveToObject(this.context, id); - } catch (SQLException e) { - super.handler.logError("SQLException trying to resolve handle " + id + " to a valid dso"); - throw new IllegalArgumentException( - "SQLException trying to resolve handle " + id + " to a valid dso"); - } - if (dso == null) { - super.handler.logError("Id must be specified: a valid dso handle or 'all'; " + this.id + " could " + - "not be resolved to valid dso handle"); - throw new IllegalArgumentException( - "Id must be specified: a valid dso handle or 'all'; " + this.id + " could " + - "not be resolved to valid dso handle"); - } - } - } else { - super.handler.logError("Id must be specified: a handle, 'all', or no -i and a -q task queue (-h for " + - "help)"); - throw new IllegalArgumentException( - "Id must be specified: a handle, 'all', or no -i and a -q task queue (-h for " + - "help)"); + throw new ParseException("Required parameter -e missing!"); } } } diff --git a/dspace-api/src/main/java/org/dspace/curate/CurationCliScriptConfiguration.java b/dspace-api/src/main/java/org/dspace/curate/CurationCliScriptConfiguration.java new file mode 100644 index 0000000000..5e1b159bba --- /dev/null +++ b/dspace-api/src/main/java/org/dspace/curate/CurationCliScriptConfiguration.java @@ -0,0 +1,22 @@ +/** + * 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.curate; + +import org.apache.commons.cli.Options; + +public class CurationCliScriptConfiguration extends CurationScriptConfiguration { + + @Override + public Options getOptions() { + options = super.getOptions(); + options.addOption("e", "eperson", true, "email address of curating eperson"); + options.getOption("e").setType(String.class); + options.getOption("e").setRequired(true); + return options; + } +} diff --git a/dspace-api/src/main/java/org/dspace/curate/CurationClientOptions.java b/dspace-api/src/main/java/org/dspace/curate/CurationClientOptions.java index 7daf107aad..1db1ac600a 100644 --- a/dspace-api/src/main/java/org/dspace/curate/CurationClientOptions.java +++ b/dspace-api/src/main/java/org/dspace/curate/CurationClientOptions.java @@ -54,7 +54,6 @@ public enum CurationClientOptions { "Id (handle) of object to perform task on, or 'all' to perform on whole repository"); options.addOption("p", "parameter", true, "a task parameter 'NAME=VALUE'"); options.addOption("q", "queue", true, "name of task queue to process"); - options.addOption("e", "eperson", true, "email address of curating eperson"); options.addOption("r", "reporter", true, "relative or absolute path to the desired report file. Use '-' to report to console. If absent, no " + "reporting"); diff --git a/dspace-api/src/main/java/org/dspace/curate/CurationScriptConfiguration.java b/dspace-api/src/main/java/org/dspace/curate/CurationScriptConfiguration.java index 785926908e..fefb4eb768 100644 --- a/dspace-api/src/main/java/org/dspace/curate/CurationScriptConfiguration.java +++ b/dspace-api/src/main/java/org/dspace/curate/CurationScriptConfiguration.java @@ -16,11 +16,11 @@ import org.dspace.scripts.configuration.ScriptConfiguration; import org.springframework.beans.factory.annotation.Autowired; /** - * The {@link ScriptConfiguration} for the {@link CurationCli} script + * The {@link ScriptConfiguration} for the {@link Curation} script * * @author Maria Verdonck (Atmire) on 23/06/2020 */ -public class CurationScriptConfiguration extends ScriptConfiguration { +public class CurationScriptConfiguration extends ScriptConfiguration { @Autowired private AuthorizeService authorizeService; diff --git a/dspace-api/src/test/data/dspaceFolder/config/spring/api/scripts.xml b/dspace-api/src/test/data/dspaceFolder/config/spring/api/scripts.xml index 76cb57a40d..f9043dca04 100644 --- a/dspace-api/src/test/data/dspaceFolder/config/spring/api/scripts.xml +++ b/dspace-api/src/test/data/dspaceFolder/config/spring/api/scripts.xml @@ -19,7 +19,7 @@ - + diff --git a/dspace-api/src/test/java/org/dspace/curate/CurationTest.java b/dspace-api/src/test/java/org/dspace/curate/CurationTest.java new file mode 100644 index 0000000000..184bde0b0c --- /dev/null +++ b/dspace-api/src/test/java/org/dspace/curate/CurationTest.java @@ -0,0 +1,78 @@ +/** + * 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.curate; + +import org.apache.commons.cli.ParseException; +import org.dspace.AbstractIntegrationTestWithDatabase; +import org.dspace.app.scripts.handler.impl.TestDSpaceRunnableHandler; +import org.dspace.builder.CollectionBuilder; +import org.dspace.builder.CommunityBuilder; +import org.dspace.content.Collection; +import org.dspace.content.Community; +import org.dspace.scripts.DSpaceRunnable; +import org.dspace.scripts.configuration.ScriptConfiguration; +import org.dspace.scripts.factory.ScriptServiceFactory; +import org.dspace.scripts.service.ScriptService; +import org.junit.Ignore; +import org.junit.Test; + +public class CurationTest extends AbstractIntegrationTestWithDatabase { + + @Test(expected = ParseException.class) + public void curationWithoutEPersonParameterTest() throws Exception { + + context.turnOffAuthorisationSystem(); + Community community = CommunityBuilder.createCommunity(context) + .build(); + Collection collection = CollectionBuilder.createCollection(context, community) + .build(); + context.restoreAuthSystemState(); + String[] args = new String[] {"curate", "-t", CurationClientOptions.getTaskOptions().get(0), + "-i", collection.getHandle()}; + TestDSpaceRunnableHandler testDSpaceRunnableHandler = new TestDSpaceRunnableHandler(); + + ScriptService scriptService = ScriptServiceFactory.getInstance().getScriptService(); + ScriptConfiguration scriptConfiguration = scriptService.getScriptConfiguration(args[0]); + + DSpaceRunnable script = null; + if (scriptConfiguration != null) { + script = scriptService.createDSpaceRunnableForScriptConfiguration(scriptConfiguration); + } + if (script != null) { + script.initialize(args, testDSpaceRunnableHandler, null); + script.run(); + } + } + + @Test + @Ignore + public void curationWithEPersonParameterTest() throws Exception { + + context.turnOffAuthorisationSystem(); + Community community = CommunityBuilder.createCommunity(context) + .build(); + Collection collection = CollectionBuilder.createCollection(context, community) + .build(); + context.restoreAuthSystemState(); + String[] args = new String[] {"curate", "-e", "admin@email.com", "-t", + CurationClientOptions.getTaskOptions().get(0), "-i", collection.getHandle()}; + TestDSpaceRunnableHandler testDSpaceRunnableHandler = new TestDSpaceRunnableHandler(); + + ScriptService scriptService = ScriptServiceFactory.getInstance().getScriptService(); + ScriptConfiguration scriptConfiguration = scriptService.getScriptConfiguration(args[0]); + + DSpaceRunnable script = null; + if (scriptConfiguration != null) { + script = scriptService.createDSpaceRunnableForScriptConfiguration(scriptConfiguration); + } + if (script != null) { + script.initialize(args, testDSpaceRunnableHandler, null); + script.run(); + } + } +} diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/scripts/handler/impl/RestDSpaceRunnableHandler.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/scripts/handler/impl/RestDSpaceRunnableHandler.java index f2080dcd84..d61b87c7d4 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/scripts/handler/impl/RestDSpaceRunnableHandler.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/scripts/handler/impl/RestDSpaceRunnableHandler.java @@ -33,7 +33,7 @@ import org.dspace.scripts.factory.ScriptServiceFactory; import org.dspace.scripts.handler.DSpaceRunnableHandler; import org.dspace.scripts.service.ProcessService; import org.dspace.utils.DSpace; -import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; +import org.springframework.core.task.TaskExecutor; /** * The {@link DSpaceRunnableHandler} dealing with Scripts started from the REST api @@ -231,9 +231,8 @@ public class RestDSpaceRunnableHandler implements DSpaceRunnableHandler { * @param script The script to be ran */ public void schedule(DSpaceRunnable script) { - ThreadPoolTaskExecutor taskExecutor = new DSpace().getServiceManager() - .getServiceByName("dspaceRunnableThreadExecutor", - ThreadPoolTaskExecutor.class); + TaskExecutor taskExecutor = new DSpace().getServiceManager() + .getServiceByName("dspaceRunnableThreadExecutor", TaskExecutor.class); Context context = new Context(); try { Process process = processService.find(context, processId); diff --git a/dspace-server-webapp/src/test/data/dspaceFolder/config/spring/rest/scripts.xml b/dspace-server-webapp/src/test/data/dspaceFolder/config/spring/rest/scripts.xml new file mode 100644 index 0000000000..5ddbe7e24d --- /dev/null +++ b/dspace-server-webapp/src/test/data/dspaceFolder/config/spring/rest/scripts.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/dspace-server-webapp/src/test/java/org/dspace/curate/CurationScriptIT.java b/dspace-server-webapp/src/test/java/org/dspace/curate/CurationScriptIT.java index 58a92f7505..66c0319857 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/curate/CurationScriptIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/curate/CurationScriptIT.java @@ -40,7 +40,7 @@ import org.junit.Test; import org.springframework.beans.factory.annotation.Autowired; /** - * IT for {@link CurationCli} + * IT for {@link Curation} * * @author Maria Verdonck (Atmire) on 24/06/2020 */ @@ -75,7 +75,6 @@ public class CurationScriptIT extends AbstractControllerIntegrationTest { LinkedList parameters = new LinkedList<>(); - parameters.add(new DSpaceCommandLineParameter("-e", admin.getEmail())); parameters.add(new DSpaceCommandLineParameter("-i", publicItem1.getHandle())); parameters.add(new DSpaceCommandLineParameter("-t", "invalidTaskOption")); @@ -95,98 +94,12 @@ public class CurationScriptIT extends AbstractControllerIntegrationTest { .andExpect(status().isBadRequest()); } - @Test - public void curateScript_MissingEperson() throws Exception { - context.turnOffAuthorisationSystem(); - - String token = getAuthToken(admin.getEmail(), password); - - parentCommunity = CommunityBuilder.createCommunity(context) - .withName("Parent Community") - .build(); - Community child1 = CommunityBuilder.createSubCommunity(context, parentCommunity) - .withName("Sub Community") - .build(); - Collection col1 = CollectionBuilder.createCollection(context, child1).withName("Collection 1").build(); - - Item publicItem1 = ItemBuilder.createItem(context, col1) - .withTitle("Public item 1") - .withIssueDate("2017-10-17") - .withAuthor("Smith, Donald").withAuthor("Doe, John") - .withSubject("ExtraEntry") - .build(); - - LinkedList parameters = new LinkedList<>(); - - parameters.add(new DSpaceCommandLineParameter("-i", publicItem1.getHandle())); - parameters.add(new DSpaceCommandLineParameter("-t", CurationClientOptions.getTaskOptions().get(0))); - - List list = parameters.stream() - .map(dSpaceCommandLineParameter -> dSpaceRunnableParameterConverter - .convert(dSpaceCommandLineParameter, Projection.DEFAULT)) - .collect(Collectors.toList()); - - context.restoreAuthSystemState(); - - // Request with missing required -e - getClient(token) - .perform(post(CURATE_SCRIPT_ENDPOINT).contentType("multipart/form-data") - .param("properties", - new Gson().toJson(list))) - // Illegal Argument Exception - .andExpect(status().isBadRequest()); - } - - @Test - public void curateScript_NonExistentEPerson() throws Exception { - context.turnOffAuthorisationSystem(); - - String token = getAuthToken(admin.getEmail(), password); - - parentCommunity = CommunityBuilder.createCommunity(context) - .withName("Parent Community") - .build(); - Community child1 = CommunityBuilder.createSubCommunity(context, parentCommunity) - .withName("Sub Community") - .build(); - Collection col1 = CollectionBuilder.createCollection(context, child1).withName("Collection 1").build(); - - Item publicItem1 = ItemBuilder.createItem(context, col1) - .withTitle("Public item 1") - .withIssueDate("2017-10-17") - .withAuthor("Smith, Donald").withAuthor("Doe, John") - .withSubject("ExtraEntry") - .build(); - - LinkedList parameters = new LinkedList<>(); - - parameters.add(new DSpaceCommandLineParameter("-e", "nonExistentEmail@test.com")); - parameters.add(new DSpaceCommandLineParameter("-i", publicItem1.getHandle())); - parameters.add(new DSpaceCommandLineParameter("-t", CurationClientOptions.getTaskOptions().get(0))); - - List list = parameters.stream() - .map(dSpaceCommandLineParameter -> dSpaceRunnableParameterConverter - .convert(dSpaceCommandLineParameter, Projection.DEFAULT)) - .collect(Collectors.toList()); - - context.restoreAuthSystemState(); - - // Request with -e - getClient(token) - .perform(post(CURATE_SCRIPT_ENDPOINT).contentType("multipart/form-data") - .param("properties", - new Gson().toJson(list))) - // Illegal Argument Exception - .andExpect(status().isBadRequest()); - } - @Test public void curateScript_MissingHandle() throws Exception { String token = getAuthToken(admin.getEmail(), password); LinkedList parameters = new LinkedList<>(); - parameters.add(new DSpaceCommandLineParameter("-e", admin.getEmail())); parameters.add(new DSpaceCommandLineParameter("-t", CurationClientOptions.getTaskOptions().get(0))); List list = parameters.stream() @@ -210,7 +123,6 @@ public class CurationScriptIT extends AbstractControllerIntegrationTest { LinkedList parameters = new LinkedList<>(); parameters.add(new DSpaceCommandLineParameter("-i", "invalidhandle")); - parameters.add(new DSpaceCommandLineParameter("-e", admin.getEmail())); parameters.add(new DSpaceCommandLineParameter("-t", CurationClientOptions.getTaskOptions().get(0))); List list = parameters.stream() @@ -250,7 +162,6 @@ public class CurationScriptIT extends AbstractControllerIntegrationTest { LinkedList parameters = new LinkedList<>(); - parameters.add(new DSpaceCommandLineParameter("-e", admin.getEmail())); parameters.add(new DSpaceCommandLineParameter("-i", publicItem1.getHandle())); List list = parameters.stream() @@ -275,7 +186,6 @@ public class CurationScriptIT extends AbstractControllerIntegrationTest { LinkedList parameters = new LinkedList<>(); - parameters.add(new DSpaceCommandLineParameter("-e", admin.getEmail())); parameters.add(new DSpaceCommandLineParameter("-i", "all")); parameters.add(new DSpaceCommandLineParameter("-s", "invalidScope")); @@ -299,7 +209,6 @@ public class CurationScriptIT extends AbstractControllerIntegrationTest { LinkedList parameters = new LinkedList<>(); - parameters.add(new DSpaceCommandLineParameter("-e", admin.getEmail())); parameters.add(new DSpaceCommandLineParameter("-i", "all")); parameters.add(new DSpaceCommandLineParameter("-T", "invalidTaskFile")); @@ -341,7 +250,6 @@ public class CurationScriptIT extends AbstractControllerIntegrationTest { LinkedList parameters = new LinkedList<>(); - parameters.add(new DSpaceCommandLineParameter("-e", admin.getEmail())); parameters.add(new DSpaceCommandLineParameter("-i", publicItem1.getHandle())); parameters.add(new DSpaceCommandLineParameter("-t", CurationClientOptions.getTaskOptions().get(0))); @@ -361,7 +269,7 @@ public class CurationScriptIT extends AbstractControllerIntegrationTest { .andExpect(jsonPath("$", is( ProcessMatcher.matchProcess("curate", String.valueOf(admin.getID()), parameters, - ProcessStatus.SCHEDULED)))) + ProcessStatus.COMPLETED)))) .andDo(result -> idRef .set(read(result.getResponse().getContentAsString(), "$.processId"))); } finally { @@ -394,7 +302,6 @@ public class CurationScriptIT extends AbstractControllerIntegrationTest { File taskFile = new File(testProps.get("test.curateTaskFile").toString()); LinkedList parameters = new LinkedList<>(); - parameters.add(new DSpaceCommandLineParameter("-e", admin.getEmail())); parameters.add(new DSpaceCommandLineParameter("-i", publicItem1.getHandle())); parameters.add(new DSpaceCommandLineParameter("-T", taskFile.getAbsolutePath())); @@ -414,7 +321,7 @@ public class CurationScriptIT extends AbstractControllerIntegrationTest { .andExpect(jsonPath("$", is( ProcessMatcher.matchProcess("curate", String.valueOf(admin.getID()), parameters, - ProcessStatus.SCHEDULED)))) + ProcessStatus.COMPLETED)))) .andDo(result -> idRef .set(read(result.getResponse().getContentAsString(), "$.processId"))); } finally { @@ -422,4 +329,57 @@ public class CurationScriptIT extends AbstractControllerIntegrationTest { } } + @Test + public void curateScript_EPersonInParametersFails() throws Exception { + context.turnOffAuthorisationSystem(); + + String token = getAuthToken(admin.getEmail(), password); + + parentCommunity = CommunityBuilder.createCommunity(context) + .withName("Parent Community") + .build(); + Community child1 = CommunityBuilder.createSubCommunity(context, parentCommunity) + .withName("Sub Community") + .build(); + Collection col1 = CollectionBuilder.createCollection(context, child1).withName("Collection 1").build(); + + Item publicItem1 = ItemBuilder.createItem(context, col1) + .withTitle("Public item 1") + .withIssueDate("2017-10-17") + .withAuthor("Smith, Donald").withAuthor("Doe, John") + .withSubject("ExtraEntry") + .build(); + + LinkedList parameters = new LinkedList<>(); + + parameters.add(new DSpaceCommandLineParameter("-e", eperson.getEmail())); + parameters.add(new DSpaceCommandLineParameter("-i", publicItem1.getHandle())); + parameters.add(new DSpaceCommandLineParameter("-t", CurationClientOptions.getTaskOptions().get(0))); + + List list = parameters.stream() + .map(dSpaceCommandLineParameter -> dSpaceRunnableParameterConverter + .convert(dSpaceCommandLineParameter, Projection.DEFAULT)) + .collect(Collectors.toList()); + AtomicReference idRef = new AtomicReference<>(); + + context.restoreAuthSystemState(); + try { + + getClient(token) + .perform(post(CURATE_SCRIPT_ENDPOINT).contentType("multipart/form-data") + .param("properties", + new Gson().toJson(list))) + .andExpect(jsonPath("$", is( + ProcessMatcher.matchProcess("curate", + String.valueOf(admin.getID()), parameters, + ProcessStatus.FAILED)))) + .andDo(result -> idRef + .set(read(result.getResponse().getContentAsString(), "$.processId"))); + } finally { + ProcessBuilder.deleteProcess(idRef.get()); + } + } + + + } diff --git a/dspace/config/spring/api/scripts.xml b/dspace/config/spring/api/scripts.xml index 31d457f09d..cdd2a43d32 100644 --- a/dspace/config/spring/api/scripts.xml +++ b/dspace/config/spring/api/scripts.xml @@ -19,7 +19,7 @@ - + diff --git a/dspace/config/spring/rest/scripts.xml b/dspace/config/spring/rest/scripts.xml index 04cacb4930..d3d7ff437e 100644 --- a/dspace/config/spring/rest/scripts.xml +++ b/dspace/config/spring/rest/scripts.xml @@ -12,4 +12,9 @@ + + + + + \ No newline at end of file