Merge remote-tracking branch '4science-bitbucket/main' into CST-6751

This commit is contained in:
Luca Giamminonni
2022-09-22 17:32:34 +02:00
55 changed files with 2140 additions and 323 deletions

View File

@@ -0,0 +1,140 @@
/**
* 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.administer;
import java.io.IOException;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import org.apache.commons.cli.ParseException;
import org.apache.commons.lang.time.DateUtils;
import org.dspace.authorize.AuthorizeException;
import org.dspace.content.ProcessStatus;
import org.dspace.core.Context;
import org.dspace.scripts.DSpaceRunnable;
import org.dspace.scripts.Process;
import org.dspace.scripts.factory.ScriptServiceFactory;
import org.dspace.scripts.service.ProcessService;
import org.dspace.services.ConfigurationService;
import org.dspace.services.factory.DSpaceServicesFactory;
import org.dspace.utils.DSpace;
/**
* Script to cleanup the old processes in the specified state.
*
* @author Luca Giamminonni (luca.giamminonni at 4science.it)
*
*/
public class ProcessCleaner extends DSpaceRunnable<ProcessCleanerConfiguration<ProcessCleaner>> {
private ConfigurationService configurationService;
private ProcessService processService;
private boolean cleanCompleted = false;
private boolean cleanFailed = false;
private boolean cleanRunning = false;
private boolean help = false;
private Integer days;
@Override
public void setup() throws ParseException {
this.configurationService = DSpaceServicesFactory.getInstance().getConfigurationService();
this.processService = ScriptServiceFactory.getInstance().getProcessService();
this.help = commandLine.hasOption('h');
this.cleanFailed = commandLine.hasOption('f');
this.cleanRunning = commandLine.hasOption('r');
this.cleanCompleted = commandLine.hasOption('c') || (!cleanFailed && !cleanRunning);
this.days = configurationService.getIntProperty("process-cleaner.days", 14);
if (this.days <= 0) {
throw new IllegalStateException("The number of days must be a positive integer.");
}
}
@Override
public void internalRun() throws Exception {
if (help) {
printHelp();
return;
}
Context context = new Context();
try {
context.turnOffAuthorisationSystem();
performDeletion(context);
} finally {
context.restoreAuthSystemState();
context.complete();
}
}
/**
* Delete the processes based on the specified statuses and the configured days
* from their creation.
*/
private void performDeletion(Context context) throws SQLException, IOException, AuthorizeException {
List<ProcessStatus> statuses = getProcessToDeleteStatuses();
Date creationDate = calculateCreationDate();
handler.logInfo("Searching for processes with status: " + statuses);
List<Process> processes = processService.findByStatusAndCreationTimeOlderThan(context, statuses, creationDate);
handler.logInfo("Found " + processes.size() + " processes to be deleted");
for (Process process : processes) {
processService.delete(context, process);
}
handler.logInfo("Process cleanup completed");
}
/**
* Returns the list of Process statuses do be deleted.
*/
private List<ProcessStatus> getProcessToDeleteStatuses() {
List<ProcessStatus> statuses = new ArrayList<ProcessStatus>();
if (cleanCompleted) {
statuses.add(ProcessStatus.COMPLETED);
}
if (cleanFailed) {
statuses.add(ProcessStatus.FAILED);
}
if (cleanRunning) {
statuses.add(ProcessStatus.RUNNING);
}
return statuses;
}
private Date calculateCreationDate() {
return DateUtils.addDays(new Date(), -days);
}
@Override
@SuppressWarnings("unchecked")
public ProcessCleanerConfiguration<ProcessCleaner> getScriptConfiguration() {
return new DSpace().getServiceManager()
.getServiceByName("process-cleaner", ProcessCleanerConfiguration.class);
}
}

View File

@@ -0,0 +1,18 @@
/**
* 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.administer;
/**
* The {@link ProcessCleaner} for CLI.
*
* @author Luca Giamminonni (luca.giamminonni at 4science.it)
*
*/
public class ProcessCleanerCli extends ProcessCleaner {
}

View File

@@ -0,0 +1,18 @@
/**
* 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.administer;
/**
* The {@link ProcessCleanerConfiguration} for CLI.
*
* @author Luca Giamminonni (luca.giamminonni at 4science.it)
*
*/
public class ProcessCleanerCliConfiguration extends ProcessCleanerConfiguration<ProcessCleanerCli> {
}

View File

@@ -0,0 +1,70 @@
/**
* 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.administer;
import java.sql.SQLException;
import org.apache.commons.cli.Options;
import org.dspace.authorize.service.AuthorizeService;
import org.dspace.core.Context;
import org.dspace.scripts.configuration.ScriptConfiguration;
import org.springframework.beans.factory.annotation.Autowired;
/**
* The {@link ScriptConfiguration} for the {@link ProcessCleaner} script.
*/
public class ProcessCleanerConfiguration<T extends ProcessCleaner> extends ScriptConfiguration<T> {
@Autowired
private AuthorizeService authorizeService;
private Class<T> dspaceRunnableClass;
@Override
public boolean isAllowedToExecute(Context context) {
try {
return authorizeService.isAdmin(context);
} catch (SQLException e) {
throw new RuntimeException("SQLException occurred when checking if the current user is an admin", e);
}
}
@Override
public Options getOptions() {
if (options == null) {
Options options = new Options();
options.addOption("h", "help", false, "help");
options.addOption("r", "running", false, "delete the process with RUNNING status");
options.getOption("r").setType(boolean.class);
options.addOption("f", "failed", false, "delete the process with FAILED status");
options.getOption("f").setType(boolean.class);
options.addOption("c", "completed", false,
"delete the process with COMPLETED status (default if no statuses are specified)");
options.getOption("c").setType(boolean.class);
super.options = options;
}
return options;
}
@Override
public Class<T> getDspaceRunnableClass() {
return dspaceRunnableClass;
}
@Override
public void setDspaceRunnableClass(Class<T> dspaceRunnableClass) {
this.dspaceRunnableClass = dspaceRunnableClass;
}
}

View File

@@ -0,0 +1,46 @@
/**
* 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.requestitem;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import org.dspace.content.Collection;
import org.dspace.content.Item;
import org.dspace.core.Context;
import org.dspace.eperson.EPerson;
import org.springframework.lang.NonNull;
/**
* Derive request recipients from groups of the Collection which owns an Item.
* The list will include all members of the administrators group. If the
* resulting list is empty, delegates to {@link RequestItemHelpdeskStrategy}.
*
* @author Mark H. Wood <mwood@iupui.edu>
*/
public class CollectionAdministratorsRequestItemStrategy
extends RequestItemHelpdeskStrategy {
@Override
@NonNull
public List<RequestItemAuthor> getRequestItemAuthor(Context context,
Item item)
throws SQLException {
List<RequestItemAuthor> recipients = new ArrayList<>();
Collection collection = item.getOwningCollection();
for (EPerson admin : collection.getAdministrators().getMembers()) {
recipients.add(new RequestItemAuthor(admin));
}
if (recipients.isEmpty()) {
return super.getRequestItemAuthor(context, item);
} else {
return recipients;
}
}
}

View File

@@ -0,0 +1,61 @@
/**
* 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.requestitem;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import org.dspace.content.Item;
import org.dspace.core.Context;
import org.springframework.lang.NonNull;
import org.springframework.util.Assert;
/**
* Assemble a list of recipients from the results of other strategies.
* The list of strategy classes is injected as the constructor argument
* {@code strategies}.
* If the strategy list is not configured, returns an empty List.
*
* @author Mark H. Wood <mwood@iupui.edu>
*/
public class CombiningRequestItemStrategy
implements RequestItemAuthorExtractor {
/** The strategies to combine. */
private final List<RequestItemAuthorExtractor> strategies;
/**
* Initialize a combination of strategies.
* @param strategies the author extraction strategies to combine.
*/
public CombiningRequestItemStrategy(@NonNull List<RequestItemAuthorExtractor> strategies) {
Assert.notNull(strategies, "Strategy list may not be null");
this.strategies = strategies;
}
/**
* Do not call.
* @throws IllegalArgumentException always
*/
private CombiningRequestItemStrategy() {
throw new IllegalArgumentException();
}
@Override
@NonNull
public List<RequestItemAuthor> getRequestItemAuthor(Context context, Item item)
throws SQLException {
List<RequestItemAuthor> recipients = new ArrayList<>();
for (RequestItemAuthorExtractor strategy : strategies) {
recipients.addAll(strategy.getRequestItemAuthor(context, item));
}
return recipients;
}
}

View File

@@ -27,7 +27,7 @@ import org.dspace.core.Context;
import org.dspace.core.ReloadableEntity;
/**
* Object representing an Item Request
* Object representing an Item Request.
*/
@Entity
@Table(name = "requestitem")
@@ -94,6 +94,9 @@ public class RequestItem implements ReloadableEntity<Integer> {
this.allfiles = allfiles;
}
/**
* @return {@code true} if all of the Item's files are requested.
*/
public boolean isAllfiles() {
return allfiles;
}
@@ -102,6 +105,9 @@ public class RequestItem implements ReloadableEntity<Integer> {
this.reqMessage = reqMessage;
}
/**
* @return a message from the requester.
*/
public String getReqMessage() {
return reqMessage;
}
@@ -110,6 +116,9 @@ public class RequestItem implements ReloadableEntity<Integer> {
this.reqName = reqName;
}
/**
* @return Human-readable name of the user requesting access.
*/
public String getReqName() {
return reqName;
}
@@ -118,6 +127,9 @@ public class RequestItem implements ReloadableEntity<Integer> {
this.reqEmail = reqEmail;
}
/**
* @return address of the user requesting access.
*/
public String getReqEmail() {
return reqEmail;
}
@@ -126,6 +138,9 @@ public class RequestItem implements ReloadableEntity<Integer> {
this.token = token;
}
/**
* @return a unique request identifier which can be emailed.
*/
public String getToken() {
return token;
}

View File

@@ -11,20 +11,31 @@ import org.dspace.eperson.EPerson;
/**
* Simple DTO to transfer data about the corresponding author for the Request
* Copy feature
* Copy feature.
*
* @author Andrea Bollini
*/
public class RequestItemAuthor {
private String fullName;
private String email;
private final String fullName;
private final String email;
/**
* Construct an author record from given data.
*
* @param fullName the author's full name.
* @param email the author's email address.
*/
public RequestItemAuthor(String fullName, String email) {
super();
this.fullName = fullName;
this.email = email;
}
/**
* Construct an author from an EPerson's metadata.
*
* @param ePerson the EPerson.
*/
public RequestItemAuthor(EPerson ePerson) {
super();
this.fullName = ePerson.getFullName();

View File

@@ -8,26 +8,28 @@
package org.dspace.app.requestitem;
import java.sql.SQLException;
import java.util.List;
import org.dspace.content.Item;
import org.dspace.core.Context;
import org.springframework.lang.NonNull;
/**
* Interface to abstract the strategy for select the author to contact for
* request copy
* Interface to abstract the strategy for selecting the author to contact for
* request copy.
*
* @author Andrea Bollini
*/
public interface RequestItemAuthorExtractor {
/**
* Retrieve the auhtor to contact for a request copy of the give item.
* Retrieve the author to contact for requesting a copy of the given item.
*
* @param context DSpace context object
* @param item item to request
* @return An object containing name an email address to send the request to
* or null if no valid email address was found.
* @return Names and email addresses to send the request to.
* @throws SQLException if database error
*/
public RequestItemAuthor getRequestItemAuthor(Context context, Item item) throws SQLException;
@NonNull
public List<RequestItemAuthor> getRequestItemAuthor(Context context, Item item)
throws SQLException;
}

View File

@@ -72,28 +72,48 @@ public class RequestItemEmailNotifier {
static public void sendRequest(Context context, RequestItem ri, String responseLink)
throws IOException, SQLException {
// Who is making this request?
RequestItemAuthor author = requestItemAuthorExtractor
List<RequestItemAuthor> authors = requestItemAuthorExtractor
.getRequestItemAuthor(context, ri.getItem());
String authorEmail = author.getEmail();
String authorName = author.getFullName();
// Build an email to the approver.
Email email = Email.getEmail(I18nUtil.getEmailFilename(context.getCurrentLocale(),
"request_item.author"));
email.addRecipient(authorEmail);
for (RequestItemAuthor author : authors) {
email.addRecipient(author.getEmail());
}
email.setReplyTo(ri.getReqEmail()); // Requester's address
email.addArgument(ri.getReqName()); // {0} Requester's name
email.addArgument(ri.getReqEmail()); // {1} Requester's address
email.addArgument(ri.isAllfiles() // {2} All bitstreams or just one?
? I18nUtil.getMessage("itemRequest.all") : ri.getBitstream().getName());
email.addArgument(handleService.getCanonicalForm(ri.getItem().getHandle()));
email.addArgument(handleService.getCanonicalForm(ri.getItem().getHandle())); // {3}
email.addArgument(ri.getItem().getName()); // {4} requested item's title
email.addArgument(ri.getReqMessage()); // {5} message from requester
email.addArgument(responseLink); // {6} Link back to DSpace for action
email.addArgument(authorName); // {7} corresponding author name
email.addArgument(authorEmail); // {8} corresponding author email
email.addArgument(configurationService.getProperty("dspace.name"));
email.addArgument(configurationService.getProperty("mail.helpdesk"));
StringBuilder names = new StringBuilder();
StringBuilder addresses = new StringBuilder();
for (RequestItemAuthor author : authors) {
if (names.length() > 0) {
names.append("; ");
addresses.append("; ");
}
names.append(author.getFullName());
addresses.append(author.getEmail());
}
email.addArgument(names.toString()); // {7} corresponding author name
email.addArgument(addresses.toString()); // {8} corresponding author email
email.addArgument(configurationService.getProperty("dspace.name")); // {9}
email.addArgument(configurationService.getProperty("mail.helpdesk")); // {10}
// Send the email.
try {

View File

@@ -8,6 +8,8 @@
package org.dspace.app.requestitem;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.lang3.StringUtils;
import org.dspace.content.Item;
@@ -16,11 +18,11 @@ import org.dspace.core.I18nUtil;
import org.dspace.eperson.EPerson;
import org.dspace.eperson.service.EPersonService;
import org.dspace.services.ConfigurationService;
import org.dspace.services.factory.DSpaceServicesFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.lang.NonNull;
/**
* RequestItem strategy to allow DSpace support team's helpdesk to receive requestItem request
* RequestItem strategy to allow DSpace support team's helpdesk to receive requestItem request.
* With this enabled, then the Item author/submitter doesn't receive the request, but the helpdesk instead does.
*
* Failover to the RequestItemSubmitterStrategy, which means the submitter would get the request if there is no
@@ -33,19 +35,24 @@ public class RequestItemHelpdeskStrategy extends RequestItemSubmitterStrategy {
@Autowired(required = true)
protected EPersonService ePersonService;
@Autowired(required = true)
private ConfigurationService configuration;
public RequestItemHelpdeskStrategy() {
}
@Override
public RequestItemAuthor getRequestItemAuthor(Context context, Item item) throws SQLException {
ConfigurationService configurationService
= DSpaceServicesFactory.getInstance().getConfigurationService();
boolean helpdeskOverridesSubmitter = configurationService
@NonNull
public List<RequestItemAuthor> getRequestItemAuthor(Context context, Item item)
throws SQLException {
boolean helpdeskOverridesSubmitter = configuration
.getBooleanProperty("request.item.helpdesk.override", false);
String helpDeskEmail = configurationService.getProperty("mail.helpdesk");
String helpDeskEmail = configuration.getProperty("mail.helpdesk");
if (helpdeskOverridesSubmitter && StringUtils.isNotBlank(helpDeskEmail)) {
return getHelpDeskPerson(context, helpDeskEmail);
List<RequestItemAuthor> authors = new ArrayList<>(1);
authors.add(getHelpDeskPerson(context, helpDeskEmail));
return authors;
} else {
//Fallback to default logic (author of Item) if helpdesk isn't fully enabled or setup
return super.getRequestItemAuthor(context, item);

View File

@@ -8,6 +8,8 @@
package org.dspace.app.requestitem;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.apache.commons.lang3.StringUtils;
@@ -16,12 +18,13 @@ import org.dspace.content.MetadataValue;
import org.dspace.content.service.ItemService;
import org.dspace.core.Context;
import org.dspace.core.I18nUtil;
import org.dspace.services.factory.DSpaceServicesFactory;
import org.dspace.services.ConfigurationService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.lang.NonNull;
/**
* Try to look to an item metadata for the corresponding author name and email.
* Failover to the RequestItemSubmitterStrategy
* Failover to the RequestItemSubmitterStrategy.
*
* @author Andrea Bollini
*/
@@ -30,6 +33,9 @@ public class RequestItemMetadataStrategy extends RequestItemSubmitterStrategy {
protected String emailMetadata;
protected String fullNameMetadata;
@Autowired(required = true)
protected ConfigurationService configurationService;
@Autowired(required = true)
protected ItemService itemService;
@@ -37,59 +43,72 @@ public class RequestItemMetadataStrategy extends RequestItemSubmitterStrategy {
}
@Override
public RequestItemAuthor getRequestItemAuthor(Context context, Item item)
@NonNull
public List<RequestItemAuthor> getRequestItemAuthor(Context context, Item item)
throws SQLException {
RequestItemAuthor author = null;
List<RequestItemAuthor> authors;
if (emailMetadata != null) {
List<MetadataValue> vals = itemService.getMetadataByMetadataString(item, emailMetadata);
if (vals.size() > 0) {
String email = vals.iterator().next().getValue();
String fullname = null;
if (fullNameMetadata != null) {
List<MetadataValue> nameVals = itemService.getMetadataByMetadataString(item, fullNameMetadata);
if (nameVals.size() > 0) {
fullname = nameVals.iterator().next().getValue();
List<MetadataValue> nameVals;
if (null != fullNameMetadata) {
nameVals = itemService.getMetadataByMetadataString(item, fullNameMetadata);
} else {
nameVals = Collections.EMPTY_LIST;
}
boolean useNames = vals.size() == nameVals.size();
if (!vals.isEmpty()) {
authors = new ArrayList<>(vals.size());
for (int authorIndex = 0; authorIndex < vals.size(); authorIndex++) {
String email = vals.get(authorIndex).getValue();
String fullname = null;
if (useNames) {
fullname = nameVals.get(authorIndex).getValue();
}
if (StringUtils.isBlank(fullname)) {
fullname = I18nUtil.getMessage(
"org.dspace.app.requestitem.RequestItemMetadataStrategy.unnamed",
context);
}
RequestItemAuthor author = new RequestItemAuthor(
fullname, email);
authors.add(author);
}
if (StringUtils.isBlank(fullname)) {
fullname = I18nUtil
.getMessage(
"org.dspace.app.requestitem.RequestItemMetadataStrategy.unnamed",
context);
}
author = new RequestItemAuthor(fullname, email);
return author;
return authors;
} else {
return Collections.EMPTY_LIST;
}
} else {
// Uses the basic strategy to look for the original submitter
author = super.getRequestItemAuthor(context, item);
// Is the author or their email null. If so get the help desk or admin name and email
if (null == author || null == author.getEmail()) {
String email = null;
String name = null;
authors = super.getRequestItemAuthor(context, item);
// Remove from the list authors that do not have email addresses.
for (RequestItemAuthor author : authors) {
if (null == author.getEmail()) {
authors.remove(author);
}
}
if (authors.isEmpty()) { // No author email addresses! Fall back
//First get help desk name and email
email = DSpaceServicesFactory.getInstance()
.getConfigurationService().getProperty("mail.helpdesk");
name = DSpaceServicesFactory.getInstance()
.getConfigurationService().getProperty("mail.helpdesk.name");
String email = configurationService.getProperty("mail.helpdesk");
String name = configurationService.getProperty("mail.helpdesk.name");
// If help desk mail is null get the mail and name of admin
if (email == null) {
email = DSpaceServicesFactory.getInstance()
.getConfigurationService().getProperty("mail.admin");
name = DSpaceServicesFactory.getInstance()
.getConfigurationService().getProperty("mail.admin.name");
email = configurationService.getProperty("mail.admin");
name = configurationService.getProperty("mail.admin.name");
}
author = new RequestItemAuthor(name, email);
authors.add(new RequestItemAuthor(name, email));
}
return authors;
}
return author;
}
public void setEmailMetadata(String emailMetadata) {
public void setEmailMetadata(@NonNull String emailMetadata) {
this.emailMetadata = emailMetadata;
}
public void setFullNameMetadata(String fullNameMetadata) {
public void setFullNameMetadata(@NonNull String fullNameMetadata) {
this.fullNameMetadata = fullNameMetadata;
}

View File

@@ -8,10 +8,13 @@
package org.dspace.app.requestitem;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import org.dspace.content.Item;
import org.dspace.core.Context;
import org.dspace.eperson.EPerson;
import org.springframework.lang.NonNull;
/**
* Basic strategy that looks to the original submitter.
@@ -24,21 +27,23 @@ public class RequestItemSubmitterStrategy implements RequestItemAuthorExtractor
}
/**
* Returns the submitter of an Item as RequestItemAuthor or null if the
* Submitter is deleted.
* Returns the submitter of an Item as RequestItemAuthor or an empty List if
* the Submitter is deleted.
*
* @return The submitter of the item or null if the submitter is deleted
* @return The submitter of the item or empty List if the submitter is deleted
* @throws SQLException if database error
*/
@Override
public RequestItemAuthor getRequestItemAuthor(Context context, Item item)
@NonNull
public List<RequestItemAuthor> getRequestItemAuthor(Context context, Item item)
throws SQLException {
EPerson submitter = item.getSubmitter();
RequestItemAuthor author = null;
List<RequestItemAuthor> authors = new ArrayList<>(1);
if (null != submitter) {
author = new RequestItemAuthor(
submitter.getFullName(), submitter.getEmail());
RequestItemAuthor author = new RequestItemAuthor(
submitter.getFullName(), submitter.getEmail());
authors.add(author);
}
return author;
return authors;
}
}

View File

@@ -20,6 +20,7 @@ import com.rometools.modules.opensearch.OpenSearchModule;
import com.rometools.modules.opensearch.entity.OSQuery;
import com.rometools.modules.opensearch.impl.OpenSearchModuleImpl;
import com.rometools.rome.io.FeedException;
import org.apache.commons.lang.StringUtils;
import org.apache.logging.log4j.Logger;
import org.dspace.app.util.service.OpenSearchService;
import org.dspace.content.DSpaceObject;
@@ -96,7 +97,7 @@ public class OpenSearchServiceImpl implements OpenSearchService {
* Get base search UI URL (websvc.opensearch.uicontext)
*/
protected String getBaseSearchUIURL() {
return configurationService.getProperty("dspace.server.url") + "/" +
return configurationService.getProperty("dspace.ui.url") + "/" +
configurationService.getProperty("websvc.opensearch.uicontext");
}
@@ -177,7 +178,9 @@ public class OpenSearchServiceImpl implements OpenSearchService {
OSQuery osq = new OSQuery();
osq.setRole("request");
try {
osq.setSearchTerms(URLEncoder.encode(query, "UTF-8"));
if (StringUtils.isNotBlank(query)) {
osq.setSearchTerms(URLEncoder.encode(query, "UTF-8"));
}
} catch (UnsupportedEncodingException e) {
log.error(e);
}

View File

@@ -0,0 +1,48 @@
/**
* 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.authorize;
import static org.apache.commons.lang.StringUtils.isNotBlank;
import java.util.regex.Pattern;
import org.dspace.authorize.service.PasswordValidatorService;
import org.dspace.services.ConfigurationService;
import org.springframework.beans.factory.annotation.Autowired;
/**
* Implementation of {@link PasswordValidatorService} that verifies if the given
* passowrd matches the configured pattern.
*
* @author Luca Giamminonni (luca.giamminonni at 4science.it)
*/
public class RegexPasswordValidator implements PasswordValidatorService {
@Autowired
private ConfigurationService configurationService;
@Override
public boolean isPasswordValidationEnabled() {
return isNotBlank(getPasswordValidationPattern());
}
@Override
public boolean isPasswordValid(String password) {
if (!isPasswordValidationEnabled()) {
return true;
}
Pattern pattern = Pattern.compile(getPasswordValidationPattern());
return pattern.matcher(password).find();
}
private String getPasswordValidationPattern() {
return configurationService.getProperty("authentication-password.regex-validation.pattern");
}
}

View File

@@ -0,0 +1,33 @@
/**
* 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.authorize;
import java.util.List;
import org.dspace.authorize.service.PasswordValidatorService;
import org.dspace.authorize.service.ValidatePasswordService;
import org.springframework.beans.factory.annotation.Autowired;
/**
* Basic implementation for validation password robustness.
*
* @author Mykhaylo Boychuk (mykhaylo.boychuk@4science.com)
*/
public class ValidatePasswordServiceImpl implements ValidatePasswordService {
@Autowired
private List<PasswordValidatorService> validators;
@Override
public boolean isPasswordValid(String password) {
return validators.stream()
.filter(passwordValidator -> passwordValidator.isPasswordValidationEnabled())
.allMatch(passwordValidator -> passwordValidator.isPasswordValid(password));
}
}

View File

@@ -0,0 +1,29 @@
/**
* 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.authorize.service;
/**
* Interface for classes that validate a given password with a specific
* strategy.
*
* @author Luca Giamminonni (luca.giamminonni at 4science.it)
*/
public interface PasswordValidatorService {
/**
* Check if the password validator is active.
*/
public boolean isPasswordValidationEnabled();
/**
* This method checks whether the password is valid
*
* @param password password to validate
*/
public boolean isPasswordValid(String password);
}

View File

@@ -0,0 +1,25 @@
/**
* 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.authorize.service;
/**
* Services to use during Validating of password.
*
* @author Mykhaylo Boychuk (mykhaylo.boychuk@4science.com)
*/
public interface ValidatePasswordService {
/**
* This method checks whether the password is valid based on the configured
* rules/strategies.
*
* @param password password to validate
*/
public boolean isPasswordValid(String password);
}

View File

@@ -8,8 +8,10 @@
package org.dspace.content.dao;
import java.sql.SQLException;
import java.util.Date;
import java.util.List;
import org.dspace.content.ProcessStatus;
import org.dspace.core.Context;
import org.dspace.core.GenericDAO;
import org.dspace.scripts.Process;
@@ -81,4 +83,18 @@ public interface ProcessDAO extends GenericDAO<Process> {
int countTotalWithParameters(Context context, ProcessQueryParameterContainer processQueryParameterContainer)
throws SQLException;
/**
* Find all the processes with one of the given status and with a creation time
* older than the specified date.
*
* @param context The relevant DSpace context
* @param statuses the statuses of the processes to search for
* @param date the creation date to search for
* @return The list of all Processes which match requirements
* @throws SQLException If something goes wrong
*/
List<Process> findByStatusAndCreationTimeOlderThan(Context context, List<ProcessStatus> statuses, Date date)
throws SQLException;
}

View File

@@ -7,7 +7,10 @@
*/
package org.dspace.content.dao.impl;
import static org.dspace.scripts.Process_.CREATION_TIME;
import java.sql.SQLException;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
@@ -17,6 +20,7 @@ import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import org.apache.commons.lang3.StringUtils;
import org.dspace.content.ProcessStatus;
import org.dspace.content.dao.ProcessDAO;
import org.dspace.core.AbstractHibernateDAO;
import org.dspace.core.Context;
@@ -147,6 +151,23 @@ public class ProcessDAOImpl extends AbstractHibernateDAO<Process> implements Pro
}
@Override
public List<Process> findByStatusAndCreationTimeOlderThan(Context context, List<ProcessStatus> statuses,
Date date) throws SQLException {
CriteriaBuilder criteriaBuilder = getCriteriaBuilder(context);
CriteriaQuery<Process> criteriaQuery = getCriteriaQuery(criteriaBuilder, Process.class);
Root<Process> processRoot = criteriaQuery.from(Process.class);
criteriaQuery.select(processRoot);
Predicate creationTimeLessThanGivenDate = criteriaBuilder.lessThan(processRoot.get(CREATION_TIME), date);
Predicate statusIn = processRoot.get(Process_.PROCESS_STATUS).in(statuses);
criteriaQuery.where(criteriaBuilder.and(creationTimeLessThanGivenDate, statusIn));
return list(context, criteriaQuery, false, Process.class, -1, -1);
}
}

View File

@@ -5,7 +5,7 @@
*
* http://www.dspace.org/license/
*/
package org.dspace.curate;
package org.dspace.ctask.general;
import java.io.ByteArrayInputStream;
import java.io.IOException;
@@ -18,6 +18,9 @@ import java.util.Map;
import org.apache.logging.log4j.Logger;
import org.dspace.authorize.AuthorizeException;
import org.dspace.authorize.ResourcePolicy;
import org.dspace.authorize.factory.AuthorizeServiceFactory;
import org.dspace.authorize.service.ResourcePolicyService;
import org.dspace.content.Bitstream;
import org.dspace.content.Bundle;
import org.dspace.content.DSpaceObject;
@@ -26,6 +29,10 @@ import org.dspace.content.factory.ContentServiceFactory;
import org.dspace.content.service.BitstreamService;
import org.dspace.content.service.BundleService;
import org.dspace.core.Context;
import org.dspace.curate.AbstractCurationTask;
import org.dspace.curate.Curator;
import org.dspace.curate.Distributive;
import org.dspace.curate.Mutative;
import org.dspace.disseminate.factory.DisseminateServiceFactory;
import org.dspace.disseminate.service.CitationDocumentService;
@@ -67,6 +74,10 @@ public class CitationPage extends AbstractCurationTask {
protected BitstreamService bitstreamService = ContentServiceFactory.getInstance().getBitstreamService();
protected BundleService bundleService = ContentServiceFactory.getInstance().getBundleService();
protected ResourcePolicyService resourcePolicyService = AuthorizeServiceFactory.getInstance()
.getResourcePolicyService();
private Map<String,Bitstream> displayMap = new HashMap<String,Bitstream>();
/**
* {@inheritDoc}
@@ -95,10 +106,13 @@ public class CitationPage extends AbstractCurationTask {
protected void performItem(Item item) throws SQLException {
//Determine if the DISPLAY bundle exits. If not, create it.
List<Bundle> dBundles = itemService.getBundles(item, CitationPage.DISPLAY_BUNDLE_NAME);
Bundle original = itemService.getBundles(item, "ORIGINAL").get(0);
Bundle dBundle = null;
if (dBundles == null || dBundles.isEmpty()) {
try {
dBundle = bundleService.create(Curator.curationContext(), item, CitationPage.DISPLAY_BUNDLE_NAME);
// don't inherit now otherwise they will be copied over the moved bitstreams
resourcePolicyService.removeAllPolicies(Curator.curationContext(), dBundle);
} catch (AuthorizeException e) {
log.error("User not authroized to create bundle on item \"{}\": {}",
item::getName, e::getMessage);
@@ -110,7 +124,6 @@ public class CitationPage extends AbstractCurationTask {
//Create a map of the bitstreams in the displayBundle. This is used to
//check if the bundle being cited is already in the display bundle.
Map<String, Bitstream> displayMap = new HashMap<>();
for (Bitstream bs : dBundle.getBitstreams()) {
displayMap.put(bs.getName(), bs);
}
@@ -128,6 +141,8 @@ public class CitationPage extends AbstractCurationTask {
} else {
try {
pBundle = bundleService.create(Curator.curationContext(), item, CitationPage.PRESERVATION_BUNDLE_NAME);
// don't inherit now otherwise they will be copied over the moved bitstreams
resourcePolicyService.removeAllPolicies(Curator.curationContext(), pBundle);
} catch (AuthorizeException e) {
log.error("User not authroized to create bundle on item \""
+ item.getName() + "\": " + e.getMessage());
@@ -160,7 +175,10 @@ public class CitationPage extends AbstractCurationTask {
citationDocument.makeCitedDocument(Curator.curationContext(), bitstream).getLeft());
//Add the cited document to the approiate bundle
this.addCitedPageToItem(citedInputStream, bundle, pBundle,
dBundle, displayMap, item, bitstream);
dBundle, item, bitstream);
// now set the policies of the preservation and display bundle
clonePolicies(Curator.curationContext(), original, pBundle);
clonePolicies(Curator.curationContext(), original, dBundle);
} catch (Exception e) {
//Could be many things, but nothing that should be
//expected.
@@ -203,8 +221,6 @@ public class CitationPage extends AbstractCurationTask {
* @param pBundle The preservation bundle. The original document should be
* put in here if it is not already.
* @param dBundle The display bundle. The cited document gets put in here.
* @param displayMap The map of bitstream names to bitstreams in the display
* bundle.
* @param item The item containing the bundles being used.
* @param bitstream The original source bitstream.
* @throws SQLException if database error
@@ -212,7 +228,7 @@ public class CitationPage extends AbstractCurationTask {
* @throws IOException if IO error
*/
protected void addCitedPageToItem(InputStream citedDoc, Bundle bundle, Bundle pBundle,
Bundle dBundle, Map<String,Bitstream> displayMap, Item item,
Bundle dBundle, Item item,
Bitstream bitstream) throws SQLException, AuthorizeException, IOException {
//If we are modifying a file that is not in the
//preservation bundle then we have to move it there.
@@ -240,7 +256,8 @@ public class CitationPage extends AbstractCurationTask {
citedBitstream.setName(context, bitstream.getName());
bitstreamService.setFormat(context, citedBitstream, bitstream.getFormat(Curator.curationContext()));
citedBitstream.setDescription(context, bitstream.getDescription());
displayMap.put(bitstream.getName(), citedBitstream);
clonePolicies(context, bitstream, citedBitstream);
this.resBuilder.append(" Added ")
.append(citedBitstream.getName())
.append(" to the ")
@@ -252,4 +269,16 @@ public class CitationPage extends AbstractCurationTask {
itemService.update(context, item);
this.status = Curator.CURATE_SUCCESS;
}
private void clonePolicies(Context context, DSpaceObject source,DSpaceObject target)
throws SQLException, AuthorizeException {
resourcePolicyService.removeAllPolicies(context, target);
for (ResourcePolicy rp: source.getResourcePolicies()) {
ResourcePolicy newPolicy = resourcePolicyService.clone(context, rp);
newPolicy.setdSpaceObject(target);
newPolicy.setAction(rp.getAction());
resourcePolicyService.update(context, newPolicy);
}
}
}

View File

@@ -15,6 +15,7 @@ import java.util.ArrayList;
import java.util.Base64;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
@@ -33,6 +34,7 @@ import org.dspace.content.dto.MetadataValueDTO;
import org.dspace.external.OpenAIRERestConnector;
import org.dspace.external.model.ExternalDataObject;
import org.dspace.external.provider.AbstractExternalDataProvider;
import org.dspace.importer.external.metadatamapping.MetadataFieldConfig;
import org.springframework.beans.factory.annotation.Autowired;
/**
@@ -40,13 +42,9 @@ import org.springframework.beans.factory.annotation.Autowired;
* will deal with the OpenAIRE External Data lookup
*
* @author paulo-graca
*
*/
public class OpenAIREFundingDataProvider extends AbstractExternalDataProvider {
/**
* log4j logger
*/
private static Logger log = org.apache.logging.log4j.LogManager.getLogger(OpenAIREFundingDataProvider.class);
/**
@@ -54,6 +52,16 @@ public class OpenAIREFundingDataProvider extends AbstractExternalDataProvider {
*/
protected static final String PREFIX = "info:eu-repo/grantAgreement";
private static final String TITLE = "dcTitle";
private static final String SUBJECT = "dcSubject";
private static final String AWARD_URI = "awardURI";
private static final String FUNDER_NAME = "funderName";
private static final String SPATIAL = "coverageSpatial";
private static final String AWARD_NUMBER = "awardNumber";
private static final String FUNDER_ID = "funderIdentifier";
private static final String FUNDING_STREAM = "fundingStream";
private static final String TITLE_ALTERNATIVE = "titleAlternative";
/**
* rows default limit
*/
@@ -69,11 +77,9 @@ public class OpenAIREFundingDataProvider extends AbstractExternalDataProvider {
*/
protected OpenAIRERestConnector connector;
/**
* required method
*/
public void init() throws IOException {
}
protected Map<String, MetadataFieldConfig> metadataFields;
public void init() throws IOException {}
@Override
public String getSourceIdentifier() {
@@ -266,14 +272,22 @@ public class OpenAIREFundingDataProvider extends AbstractExternalDataProvider {
}
}
public Map<String, MetadataFieldConfig> getMetadataFields() {
return metadataFields;
}
public void setMetadataFields(Map<String, MetadataFieldConfig> metadataFields) {
this.metadataFields = metadataFields;
}
/**
* OpenAIRE Funding External Data Builder Class
*
* @author pgraca
*
*/
public static class ExternalDataObjectBuilder {
ExternalDataObject externalDataObject;
public class ExternalDataObjectBuilder {
private ExternalDataObject externalDataObject;
public ExternalDataObjectBuilder(Project project) {
String funderIdPrefix = "urn:openaire:";
@@ -283,46 +297,42 @@ public class OpenAIREFundingDataProvider extends AbstractExternalDataProvider {
for (FundingTreeType fundingTree : projectHelper.getFundingTreeTypes()) {
FunderType funder = fundingTree.getFunder();
// Funder name
this.addFunderName(funder.getName());
this.addMetadata(metadataFields.get(FUNDER_NAME), funder.getName());
// Funder Id - convert it to an urn
this.addFunderID(funderIdPrefix + funder.getId());
this.addMetadata(metadataFields.get(FUNDER_ID), funderIdPrefix + funder.getId());
// Jurisdiction
this.addFunderJuristiction(funder.getJurisdiction());
this.addMetadata(metadataFields.get(SPATIAL), funder.getJurisdiction());
FundingHelper fundingHelper = new FundingHelper(
fundingTree.getFundingLevel2OrFundingLevel1OrFundingLevel0());
fundingTree.getFundingLevel2OrFundingLevel1OrFundingLevel0());
// Funding description
for (FundingType funding : fundingHelper.getFirstAvailableFunding()) {
this.addFundingStream(funding.getDescription());
this.addMetadata(metadataFields.get(FUNDING_STREAM), funding.getDescription());
}
}
// Title
for (String title : projectHelper.getTitles()) {
this.addAwardTitle(title);
this.addMetadata(metadataFields.get(TITLE), title);
this.setDisplayValue(title);
this.setValue(title);
}
// Code
for (String code : projectHelper.getCodes()) {
this.addAwardNumber(code);
this.addMetadata(metadataFields.get(AWARD_NUMBER), code);
}
// Website url
for (String url : projectHelper.getWebsiteUrls()) {
this.addAwardURI(url);
this.addMetadata(metadataFields.get(AWARD_URI), url);
}
// Acronyms
for (String acronym : projectHelper.getAcronyms()) {
this.addFundingItemAcronym(acronym);
this.addMetadata(metadataFields.get(TITLE_ALTERNATIVE), acronym);
}
// Keywords
for (String keyword : projectHelper.getKeywords()) {
this.addSubject(keyword);
this.addMetadata(metadataFields.get(SUBJECT), keyword);
}
}
@@ -366,7 +376,6 @@ public class OpenAIREFundingDataProvider extends AbstractExternalDataProvider {
* @return ExternalDataObjectBuilder
*/
public ExternalDataObjectBuilder setId(String id) {
// we use base64 encoding in order to use slashes / and other
// characters that must be escaped for the <:entry-id>
String base64Id = Base64.getEncoder().encodeToString(id.getBytes());
@@ -374,128 +383,10 @@ public class OpenAIREFundingDataProvider extends AbstractExternalDataProvider {
return this;
}
/**
* Add metadata dc.identifier
*
* @param metadata identifier
* @return ExternalDataObjectBuilder
*/
public ExternalDataObjectBuilder addIdentifier(String identifier) {
this.externalDataObject.addMetadata(new MetadataValueDTO("dc", "identifier", null, null, identifier));
return this;
}
/**
* Add metadata project.funder.name
*
* @param metadata funderName
* @return ExternalDataObjectBuilder
*/
public ExternalDataObjectBuilder addFunderName(String funderName) {
this.externalDataObject.addMetadata(new MetadataValueDTO("project", "funder", "name", null, funderName));
return this;
}
/**
* Add metadata project.funder.identifier
*
* @param metadata funderId
* @return ExternalDataObjectBuilder
*/
public ExternalDataObjectBuilder addFunderID(String funderID) {
this.externalDataObject
.addMetadata(new MetadataValueDTO("project", "funder", "identifier", null, funderID));
return this;
}
/**
* Add metadata dc.title
*
* @param metadata awardTitle
* @return ExternalDataObjectBuilder
*/
public ExternalDataObjectBuilder addAwardTitle(String awardTitle) {
this.externalDataObject.addMetadata(new MetadataValueDTO("dc", "title", null, null, awardTitle));
return this;
}
/**
* Add metadata oaire.fundingStream
*
* @param metadata fundingStream
* @return ExternalDataObjectBuilder
*/
public ExternalDataObjectBuilder addFundingStream(String fundingStream) {
this.externalDataObject
.addMetadata(new MetadataValueDTO("oaire", "fundingStream", null, null, fundingStream));
return this;
}
/**
* Add metadata oaire.awardNumber
*
* @param metadata awardNumber
* @return ExternalDataObjectBuilder
*/
public ExternalDataObjectBuilder addAwardNumber(String awardNumber) {
this.externalDataObject.addMetadata(new MetadataValueDTO("oaire", "awardNumber", null, null, awardNumber));
return this;
}
/**
* Add metadata oaire.awardURI
*
* @param metadata websiteUrl
* @return ExternalDataObjectBuilder
*/
public ExternalDataObjectBuilder addAwardURI(String websiteUrl) {
this.externalDataObject.addMetadata(new MetadataValueDTO("oaire", "awardURI", null, null, websiteUrl));
return this;
}
/**
* Add metadata dc.title.alternative
*
* @param metadata fundingItemAcronym
* @return ExternalDataObjectBuilder
*/
public ExternalDataObjectBuilder addFundingItemAcronym(String fundingItemAcronym) {
this.externalDataObject
.addMetadata(new MetadataValueDTO("dc", "title", "alternative", null, fundingItemAcronym));
return this;
}
/**
* Add metadata dc.coverage.spatial
*
* @param metadata funderJuristiction
* @return ExternalDataObjectBuilder
*/
public ExternalDataObjectBuilder addFunderJuristiction(String funderJuristiction) {
this.externalDataObject
.addMetadata(new MetadataValueDTO("dc", "coverage", "spatial", null, funderJuristiction));
return this;
}
/**
* Add metadata dc.description
*
* @param metadata description
* @return ExternalDataObjectBuilder
*/
public ExternalDataObjectBuilder addDescription(String description) {
this.externalDataObject.addMetadata(new MetadataValueDTO("dc", "description", null, null, description));
return this;
}
/**
* Add metadata dc.subject
*
* @param metadata subject
* @return ExternalDataObjectBuilder
*/
public ExternalDataObjectBuilder addSubject(String subject) {
this.externalDataObject.addMetadata(new MetadataValueDTO("dc", "subject", null, null, subject));
public ExternalDataObjectBuilder addMetadata(MetadataFieldConfig metadataField, String value) {
this.externalDataObject.addMetadata(new MetadataValueDTO(metadataField.getSchema(),
metadataField.getElement(),
metadataField.getQualifier(), null, value));
return this;
}
@@ -508,4 +399,5 @@ public class OpenAIREFundingDataProvider extends AbstractExternalDataProvider {
return this.externalDataObject;
}
}
}
}

View File

@@ -0,0 +1,65 @@
/**
* 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.importer.external.metadatamapping.contributor;
import java.util.ArrayList;
import java.util.Collection;
import org.dspace.importer.external.metadatamapping.MetadataFieldMapping;
import org.dspace.importer.external.metadatamapping.MetadatumDTO;
/**
* Wrapper class used to split another MetadataContributor's output into distinct values.
* The split is performed by matching a regular expression against the wrapped MetadataContributor's output.
*
* @author Philipp Rumpf (philipp.rumpf@uni-bamberg.de)
*/
public class SplitMetadataContributor<T> implements MetadataContributor<T> {
private final MetadataContributor<T> innerContributor;
private final String regex;
/**
* @param innerContributor The MetadataContributor whose output is split
* @param regex A regular expression matching the separator between different values
*/
public SplitMetadataContributor(MetadataContributor<T> innerContributor, String regex) {
this.innerContributor = innerContributor;
this.regex = regex;
}
@Override
public void setMetadataFieldMapping(MetadataFieldMapping<T, MetadataContributor<T>> rt) {
}
/**
* Each metadatum returned by the wrapped MetadataContributor is split into one or more metadata values
* based on the provided regular expression.
*
* @param t The recordType object to retrieve metadata from
* @return The collection of processed metadata values
*/
@Override
public Collection<MetadatumDTO> contributeMetadata(T t) {
Collection<MetadatumDTO> metadata = innerContributor.contributeMetadata(t);
ArrayList<MetadatumDTO> splitMetadata = new ArrayList<>();
for (MetadatumDTO metadatumDTO : metadata) {
String[] split = metadatumDTO.getValue().split(regex);
for (String splitItem : split) {
MetadatumDTO splitMetadatumDTO = new MetadatumDTO();
splitMetadatumDTO.setSchema(metadatumDTO.getSchema());
splitMetadatumDTO.setElement(metadatumDTO.getElement());
splitMetadatumDTO.setQualifier(metadatumDTO.getQualifier());
splitMetadatumDTO.setValue(splitItem);
splitMetadata.add(splitMetadatumDTO);
}
}
return splitMetadata;
}
}

View File

@@ -0,0 +1,29 @@
/**
* 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.passwordvalidation.factory;
import org.dspace.authorize.service.PasswordValidatorService;
import org.dspace.services.factory.DSpaceServicesFactory;
/**
* Abstract factory to get services for the passwordvalidation package,
* use PasswordValidationFactory.getInstance() to retrieve an implementation.
*
* @author Mykhaylo Boychuk (mykhaylo.boychuk@4science.com)
*/
public abstract class PasswordValidationFactory {
public abstract PasswordValidatorService getPasswordValidationService();
public static PasswordValidationFactory getInstance() {
return DSpaceServicesFactory.getInstance()
.getServiceManager()
.getServiceByName("validationPasswordFactory", PasswordValidationFactory.class);
}
}

View File

@@ -0,0 +1,29 @@
/**
* 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.passwordvalidation.factory;
import org.dspace.authorize.service.PasswordValidatorService;
import org.springframework.beans.factory.annotation.Autowired;
/**
* Factory implementation to get services for the PasswordValidation package,
* use PasswordValidationFactory.getInstance() to retrieve an implementation.
*
* @author Mykhaylo Boychuk (mykhaylo.boychuk@4science.com)
*/
public class PasswordValidationFactoryImpl extends PasswordValidationFactory {
@Autowired(required = true)
private PasswordValidatorService PasswordValidatorService;
@Override
public PasswordValidatorService getPasswordValidationService() {
return PasswordValidatorService;
}
}

View File

@@ -305,6 +305,12 @@ public class ProcessServiceImpl implements ProcessService {
tempFile.delete();
}
@Override
public List<Process> findByStatusAndCreationTimeOlderThan(Context context, List<ProcessStatus> statuses,
Date date) throws SQLException {
return this.processDAO.findByStatusAndCreationTimeOlderThan(context, statuses, date);
}
private String formatLogLine(int processId, String scriptName, String output, ProcessLogLevel processLogLevel) {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
StringBuilder sb = new StringBuilder();

View File

@@ -10,11 +10,13 @@ package org.dspace.scripts.service;
import java.io.IOException;
import java.io.InputStream;
import java.sql.SQLException;
import java.util.Date;
import java.util.List;
import java.util.Set;
import org.dspace.authorize.AuthorizeException;
import org.dspace.content.Bitstream;
import org.dspace.content.ProcessStatus;
import org.dspace.core.Context;
import org.dspace.eperson.EPerson;
import org.dspace.eperson.Group;
@@ -240,4 +242,17 @@ public interface ProcessService {
*/
void createLogBitstream(Context context, Process process)
throws IOException, SQLException, AuthorizeException;
/**
* Find all the processes with one of the given status and with a creation time
* older than the specified date.
*
* @param context The relevant DSpace context
* @param statuses the statuses of the processes to search for
* @param date the creation date to search for
* @return The list of all Processes which match requirements
* @throws AuthorizeException If something goes wrong
*/
List<Process> findByStatusAndCreationTimeOlderThan(Context context, List<ProcessStatus> statuses, Date date)
throws SQLException;
}

View File

@@ -120,3 +120,4 @@ org.dspace.app.rest.exception.RESTEmptyWorkflowGroupException.message = Refused
org.dspace.app.rest.exception.EPersonNameNotProvidedException.message = The eperson.firstname and eperson.lastname values need to be filled in
org.dspace.app.rest.exception.GroupNameNotProvidedException.message = Cannot create group, no group name is provided
org.dspace.app.rest.exception.GroupHasPendingWorkflowTasksException.message = Cannot delete group, the associated workflow role still has pending tasks
org.dspace.app.rest.exception.PasswordNotValidException.message = New password is invalid. Valid passwords must be at least 8 characters long!

View File

@@ -1,6 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util.xsd"
default-lazy-init="true">
<bean id="mockOpenAIRERestConnector" class="org.dspace.external.MockOpenAIRERestConnector">
@@ -15,11 +19,71 @@
init-method="init">
<property name="sourceIdentifier" value="openAIREFunding" />
<property name="connector" ref="mockOpenAIRERestConnector" />
<property name="metadataFields" ref="mapOfmetadata"/>
<property name="supportedEntityTypes">
<list>
<value>Project</value>
</list>
</property>
</bean>
<util:map id="mapOfmetadata"
map-class="java.util.HashMap" key-type="java.lang.String" value-type="org.dspace.importer.external.metadatamapping.MetadataFieldConfig">
<entry key="awardNumber" value-ref="openaireAwardNumber" />
<entry key="fundingStream" value-ref="openaireFundingStream" />
<entry key="awardURI" value-ref="openaireAwardURI" />
<entry key="funderName" value-ref="openaireFunderName" />
<entry key="funderIdentifier" value-ref="openaireFunderIdentifier" />
<entry key="dcTitle" value-ref="openaireTitle" />
<entry key="titleAlternative" value-ref="openaireTitleAlternative" />
<entry key="dcIdentifier" value-ref="openaireIdentifier" />
<entry key="coverageSpatial" value-ref="openaireSpatial" />
<entry key="dcDescription" value-ref="openaireDescription" />
<entry key="dcSubject" value-ref="openaireSubject" />
</util:map>
<bean id="openaireAwardNumber" class="org.dspace.importer.external.metadatamapping.MetadataFieldConfig">
<constructor-arg value="oaire.awardNumber"/>
</bean>
<bean id="openaireFundingStream" class="org.dspace.importer.external.metadatamapping.MetadataFieldConfig">
<constructor-arg value="oaire.fundingStream"/>
</bean>
<bean id="openaireAwardURI" class="org.dspace.importer.external.metadatamapping.MetadataFieldConfig">
<constructor-arg value="oaire.awardURI"/>
</bean>
<bean id="openaireFunderName" class="org.dspace.importer.external.metadatamapping.MetadataFieldConfig">
<constructor-arg value="project.funder.name"/>
</bean>
<bean id="openaireFunderIdentifier" class="org.dspace.importer.external.metadatamapping.MetadataFieldConfig">
<constructor-arg value="project.funder.identifier"/>
</bean>
<bean id="openaireTitle" class="org.dspace.importer.external.metadatamapping.MetadataFieldConfig">
<constructor-arg value="dc.title"/>
</bean>
<bean id="openaireTitleAlternative" class="org.dspace.importer.external.metadatamapping.MetadataFieldConfig">
<constructor-arg value="dc.title.alternative"/>
</bean>
<bean id="openaireIdentifier" class="org.dspace.importer.external.metadatamapping.MetadataFieldConfig">
<constructor-arg value="dc.identifier"/>
</bean>
<bean id="openaireSpatial" class="org.dspace.importer.external.metadatamapping.MetadataFieldConfig">
<constructor-arg value="dc.coverage.spatial"/>
</bean>
<bean id="openaireDescription" class="org.dspace.importer.external.metadatamapping.MetadataFieldConfig">
<constructor-arg value="dc.description"/>
</bean>
<bean id="openaireSubject" class="org.dspace.importer.external.metadatamapping.MetadataFieldConfig">
<constructor-arg value="dc.subject"/>
</bean>
</beans>

View File

@@ -64,7 +64,12 @@
<property name="description" value="Perform the bulk synchronization of all the BATCH configured ORCID entities placed in the ORCID queue"/>
<property name="dspaceRunnableClass" value="org.dspace.orcid.script.OrcidBulkPush"/>
</bean>
<bean id="process-cleaner" class="org.dspace.administer.ProcessCleanerCliConfiguration">
<property name="description" value="Cleanup all the old processes in the specified state"/>
<property name="dspaceRunnableClass" value="org.dspace.administer.ProcessCleanerCli"/>
</bean>
<!-- Keep as last script; for test ScriptRestRepository#findOneScriptByNameTest -->
<bean id="mock-script" class="org.dspace.scripts.MockDSpaceRunnableScriptConfiguration" scope="prototype">
<property name="description" value="Mocking a script for testing purposes" />

View File

@@ -0,0 +1,380 @@
/**
* 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.administer;
import static org.apache.commons.lang.time.DateUtils.addDays;
import static org.dspace.content.ProcessStatus.COMPLETED;
import static org.dspace.content.ProcessStatus.FAILED;
import static org.dspace.content.ProcessStatus.RUNNING;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.empty;
import static org.hamcrest.Matchers.hasItem;
import static org.hamcrest.Matchers.hasSize;
import static org.hamcrest.Matchers.notNullValue;
import static org.hamcrest.Matchers.nullValue;
import java.sql.SQLException;
import java.util.Date;
import java.util.List;
import org.dspace.AbstractIntegrationTestWithDatabase;
import org.dspace.app.launcher.ScriptLauncher;
import org.dspace.app.scripts.handler.impl.TestDSpaceRunnableHandler;
import org.dspace.builder.ProcessBuilder;
import org.dspace.content.ProcessStatus;
import org.dspace.scripts.Process;
import org.dspace.scripts.factory.ScriptServiceFactory;
import org.dspace.scripts.service.ProcessService;
import org.dspace.services.ConfigurationService;
import org.dspace.services.factory.DSpaceServicesFactory;
import org.junit.Test;
/**
* Integration tests for {@link ProcessCleaner}.
*
* @author Luca Giamminonni (luca.giamminonni at 4science.it)
*
*/
public class ProcessCleanerIT extends AbstractIntegrationTestWithDatabase {
private ConfigurationService configurationService = DSpaceServicesFactory.getInstance().getConfigurationService();
private ProcessService processService = ScriptServiceFactory.getInstance().getProcessService();
@Test
public void testWithoutProcessToDelete() throws Exception {
Process process_1 = buildProcess(COMPLETED, addDays(new Date(), -2));
Process process_2 = buildProcess(RUNNING, addDays(new Date(), -1));
Process process_3 = buildProcess(FAILED, addDays(new Date(), -3));
configurationService.setProperty("process-cleaner.days", 5);
TestDSpaceRunnableHandler testDSpaceRunnableHandler = new TestDSpaceRunnableHandler();
String[] args = new String[] { "process-cleaner" };
ScriptLauncher.handleScript(args, ScriptLauncher.getConfig(kernelImpl), testDSpaceRunnableHandler, kernelImpl);
assertThat(testDSpaceRunnableHandler.getErrorMessages(), empty());
assertThat(testDSpaceRunnableHandler.getWarningMessages(), empty());
List<String> messages = testDSpaceRunnableHandler.getInfoMessages();
assertThat(messages, hasSize(3));
assertThat(messages, hasItem("Searching for processes with status: [COMPLETED]"));
assertThat(messages, hasItem("Found 0 processes to be deleted"));
assertThat(messages, hasItem("Process cleanup completed"));
assertThat(processService.find(context, process_1.getID()), notNullValue());
assertThat(processService.find(context, process_2.getID()), notNullValue());
assertThat(processService.find(context, process_3.getID()), notNullValue());
}
@Test
public void testWithoutSpecifiedStatus() throws Exception {
Process process_1 = buildProcess(COMPLETED, addDays(new Date(), -2));
Process process_2 = buildProcess(RUNNING, addDays(new Date(), -1));
Process process_3 = buildProcess(FAILED, addDays(new Date(), -3));
Process process_4 = buildProcess(COMPLETED, addDays(new Date(), -6));
Process process_5 = buildProcess(COMPLETED, addDays(new Date(), -8));
Process process_6 = buildProcess(RUNNING, addDays(new Date(), -7));
Process process_7 = buildProcess(FAILED, addDays(new Date(), -8));
configurationService.setProperty("process-cleaner.days", 5);
TestDSpaceRunnableHandler testDSpaceRunnableHandler = new TestDSpaceRunnableHandler();
String[] args = new String[] { "process-cleaner" };
ScriptLauncher.handleScript(args, ScriptLauncher.getConfig(kernelImpl), testDSpaceRunnableHandler, kernelImpl);
assertThat(testDSpaceRunnableHandler.getErrorMessages(), empty());
assertThat(testDSpaceRunnableHandler.getWarningMessages(), empty());
List<String> messages = testDSpaceRunnableHandler.getInfoMessages();
assertThat(messages, hasSize(3));
assertThat(messages, hasItem("Searching for processes with status: [COMPLETED]"));
assertThat(messages, hasItem("Found 2 processes to be deleted"));
assertThat(messages, hasItem("Process cleanup completed"));
assertThat(processService.find(context, process_1.getID()), notNullValue());
assertThat(processService.find(context, process_2.getID()), notNullValue());
assertThat(processService.find(context, process_3.getID()), notNullValue());
assertThat(processService.find(context, process_4.getID()), nullValue());
assertThat(processService.find(context, process_5.getID()), nullValue());
assertThat(processService.find(context, process_6.getID()), notNullValue());
assertThat(processService.find(context, process_7.getID()), notNullValue());
}
@Test
public void testWithCompletedStatus() throws Exception {
Process process_1 = buildProcess(COMPLETED, addDays(new Date(), -2));
Process process_2 = buildProcess(RUNNING, addDays(new Date(), -1));
Process process_3 = buildProcess(FAILED, addDays(new Date(), -3));
Process process_4 = buildProcess(COMPLETED, addDays(new Date(), -6));
Process process_5 = buildProcess(COMPLETED, addDays(new Date(), -8));
Process process_6 = buildProcess(RUNNING, addDays(new Date(), -7));
Process process_7 = buildProcess(FAILED, addDays(new Date(), -8));
configurationService.setProperty("process-cleaner.days", 5);
TestDSpaceRunnableHandler testDSpaceRunnableHandler = new TestDSpaceRunnableHandler();
String[] args = new String[] { "process-cleaner", "-c" };
ScriptLauncher.handleScript(args, ScriptLauncher.getConfig(kernelImpl), testDSpaceRunnableHandler, kernelImpl);
assertThat(testDSpaceRunnableHandler.getErrorMessages(), empty());
assertThat(testDSpaceRunnableHandler.getWarningMessages(), empty());
List<String> messages = testDSpaceRunnableHandler.getInfoMessages();
assertThat(messages, hasSize(3));
assertThat(messages, hasItem("Searching for processes with status: [COMPLETED]"));
assertThat(messages, hasItem("Found 2 processes to be deleted"));
assertThat(messages, hasItem("Process cleanup completed"));
assertThat(processService.find(context, process_1.getID()), notNullValue());
assertThat(processService.find(context, process_2.getID()), notNullValue());
assertThat(processService.find(context, process_3.getID()), notNullValue());
assertThat(processService.find(context, process_4.getID()), nullValue());
assertThat(processService.find(context, process_5.getID()), nullValue());
assertThat(processService.find(context, process_6.getID()), notNullValue());
assertThat(processService.find(context, process_7.getID()), notNullValue());
}
@Test
public void testWithRunningStatus() throws Exception {
Process process_1 = buildProcess(COMPLETED, addDays(new Date(), -2));
Process process_2 = buildProcess(RUNNING, addDays(new Date(), -1));
Process process_3 = buildProcess(FAILED, addDays(new Date(), -3));
Process process_4 = buildProcess(COMPLETED, addDays(new Date(), -6));
Process process_5 = buildProcess(COMPLETED, addDays(new Date(), -8));
Process process_6 = buildProcess(RUNNING, addDays(new Date(), -7));
Process process_7 = buildProcess(FAILED, addDays(new Date(), -8));
Process process_8 = buildProcess(RUNNING, addDays(new Date(), -9));
configurationService.setProperty("process-cleaner.days", 5);
TestDSpaceRunnableHandler testDSpaceRunnableHandler = new TestDSpaceRunnableHandler();
String[] args = new String[] { "process-cleaner", "-r" };
ScriptLauncher.handleScript(args, ScriptLauncher.getConfig(kernelImpl), testDSpaceRunnableHandler, kernelImpl);
assertThat(testDSpaceRunnableHandler.getErrorMessages(), empty());
assertThat(testDSpaceRunnableHandler.getWarningMessages(), empty());
List<String> messages = testDSpaceRunnableHandler.getInfoMessages();
assertThat(messages, hasSize(3));
assertThat(messages, hasItem("Searching for processes with status: [RUNNING]"));
assertThat(messages, hasItem("Found 2 processes to be deleted"));
assertThat(messages, hasItem("Process cleanup completed"));
assertThat(processService.find(context, process_1.getID()), notNullValue());
assertThat(processService.find(context, process_2.getID()), notNullValue());
assertThat(processService.find(context, process_3.getID()), notNullValue());
assertThat(processService.find(context, process_4.getID()), notNullValue());
assertThat(processService.find(context, process_5.getID()), notNullValue());
assertThat(processService.find(context, process_6.getID()), nullValue());
assertThat(processService.find(context, process_7.getID()), notNullValue());
assertThat(processService.find(context, process_8.getID()), nullValue());
}
@Test
public void testWithFailedStatus() throws Exception {
Process process_1 = buildProcess(COMPLETED, addDays(new Date(), -2));
Process process_2 = buildProcess(RUNNING, addDays(new Date(), -1));
Process process_3 = buildProcess(FAILED, addDays(new Date(), -3));
Process process_4 = buildProcess(COMPLETED, addDays(new Date(), -6));
Process process_5 = buildProcess(COMPLETED, addDays(new Date(), -8));
Process process_6 = buildProcess(RUNNING, addDays(new Date(), -7));
Process process_7 = buildProcess(FAILED, addDays(new Date(), -8));
Process process_8 = buildProcess(FAILED, addDays(new Date(), -9));
configurationService.setProperty("process-cleaner.days", 5);
TestDSpaceRunnableHandler testDSpaceRunnableHandler = new TestDSpaceRunnableHandler();
String[] args = new String[] { "process-cleaner", "-f" };
ScriptLauncher.handleScript(args, ScriptLauncher.getConfig(kernelImpl), testDSpaceRunnableHandler, kernelImpl);
assertThat(testDSpaceRunnableHandler.getErrorMessages(), empty());
assertThat(testDSpaceRunnableHandler.getWarningMessages(), empty());
List<String> messages = testDSpaceRunnableHandler.getInfoMessages();
assertThat(messages, hasSize(3));
assertThat(messages, hasItem("Searching for processes with status: [FAILED]"));
assertThat(messages, hasItem("Found 2 processes to be deleted"));
assertThat(messages, hasItem("Process cleanup completed"));
assertThat(processService.find(context, process_1.getID()), notNullValue());
assertThat(processService.find(context, process_2.getID()), notNullValue());
assertThat(processService.find(context, process_3.getID()), notNullValue());
assertThat(processService.find(context, process_4.getID()), notNullValue());
assertThat(processService.find(context, process_5.getID()), notNullValue());
assertThat(processService.find(context, process_6.getID()), notNullValue());
assertThat(processService.find(context, process_7.getID()), nullValue());
assertThat(processService.find(context, process_8.getID()), nullValue());
}
@Test
public void testWithCompletedAndFailedStatus() throws Exception {
Process process_1 = buildProcess(COMPLETED, addDays(new Date(), -2));
Process process_2 = buildProcess(RUNNING, addDays(new Date(), -1));
Process process_3 = buildProcess(FAILED, addDays(new Date(), -3));
Process process_4 = buildProcess(COMPLETED, addDays(new Date(), -6));
Process process_5 = buildProcess(COMPLETED, addDays(new Date(), -8));
Process process_6 = buildProcess(RUNNING, addDays(new Date(), -7));
Process process_7 = buildProcess(FAILED, addDays(new Date(), -8));
Process process_8 = buildProcess(FAILED, addDays(new Date(), -9));
configurationService.setProperty("process-cleaner.days", 5);
TestDSpaceRunnableHandler testDSpaceRunnableHandler = new TestDSpaceRunnableHandler();
String[] args = new String[] { "process-cleaner", "-c", "-f" };
ScriptLauncher.handleScript(args, ScriptLauncher.getConfig(kernelImpl), testDSpaceRunnableHandler, kernelImpl);
List<String> messages = testDSpaceRunnableHandler.getInfoMessages();
assertThat(messages, hasSize(3));
assertThat(messages, hasItem("Searching for processes with status: [COMPLETED, FAILED]"));
assertThat(messages, hasItem("Found 4 processes to be deleted"));
assertThat(messages, hasItem("Process cleanup completed"));
assertThat(processService.find(context, process_1.getID()), notNullValue());
assertThat(processService.find(context, process_2.getID()), notNullValue());
assertThat(processService.find(context, process_3.getID()), notNullValue());
assertThat(processService.find(context, process_4.getID()), nullValue());
assertThat(processService.find(context, process_5.getID()), nullValue());
assertThat(processService.find(context, process_6.getID()), notNullValue());
assertThat(processService.find(context, process_7.getID()), nullValue());
assertThat(processService.find(context, process_8.getID()), nullValue());
}
@Test
public void testWithCompletedAndRunningStatus() throws Exception {
Process process_1 = buildProcess(COMPLETED, addDays(new Date(), -2));
Process process_2 = buildProcess(RUNNING, addDays(new Date(), -1));
Process process_3 = buildProcess(FAILED, addDays(new Date(), -3));
Process process_4 = buildProcess(COMPLETED, addDays(new Date(), -6));
Process process_5 = buildProcess(COMPLETED, addDays(new Date(), -8));
Process process_6 = buildProcess(RUNNING, addDays(new Date(), -7));
Process process_7 = buildProcess(FAILED, addDays(new Date(), -8));
Process process_8 = buildProcess(RUNNING, addDays(new Date(), -9));
configurationService.setProperty("process-cleaner.days", 5);
TestDSpaceRunnableHandler testDSpaceRunnableHandler = new TestDSpaceRunnableHandler();
String[] args = new String[] { "process-cleaner", "-c", "-r" };
ScriptLauncher.handleScript(args, ScriptLauncher.getConfig(kernelImpl), testDSpaceRunnableHandler, kernelImpl);
List<String> messages = testDSpaceRunnableHandler.getInfoMessages();
assertThat(messages, hasSize(3));
assertThat(messages, hasItem("Searching for processes with status: [COMPLETED, RUNNING]"));
assertThat(messages, hasItem("Found 4 processes to be deleted"));
assertThat(messages, hasItem("Process cleanup completed"));
assertThat(processService.find(context, process_1.getID()), notNullValue());
assertThat(processService.find(context, process_2.getID()), notNullValue());
assertThat(processService.find(context, process_3.getID()), notNullValue());
assertThat(processService.find(context, process_4.getID()), nullValue());
assertThat(processService.find(context, process_5.getID()), nullValue());
assertThat(processService.find(context, process_6.getID()), nullValue());
assertThat(processService.find(context, process_7.getID()), notNullValue());
assertThat(processService.find(context, process_8.getID()), nullValue());
}
@Test
public void testWithFailedAndRunningStatus() throws Exception {
Process process_1 = buildProcess(COMPLETED, addDays(new Date(), -2));
Process process_2 = buildProcess(RUNNING, addDays(new Date(), -1));
Process process_3 = buildProcess(FAILED, addDays(new Date(), -3));
Process process_4 = buildProcess(COMPLETED, addDays(new Date(), -6));
Process process_5 = buildProcess(COMPLETED, addDays(new Date(), -8));
Process process_6 = buildProcess(RUNNING, addDays(new Date(), -7));
Process process_7 = buildProcess(FAILED, addDays(new Date(), -8));
Process process_8 = buildProcess(RUNNING, addDays(new Date(), -9));
configurationService.setProperty("process-cleaner.days", 5);
TestDSpaceRunnableHandler testDSpaceRunnableHandler = new TestDSpaceRunnableHandler();
String[] args = new String[] { "process-cleaner", "-f", "-r" };
ScriptLauncher.handleScript(args, ScriptLauncher.getConfig(kernelImpl), testDSpaceRunnableHandler, kernelImpl);
List<String> messages = testDSpaceRunnableHandler.getInfoMessages();
assertThat(messages, hasSize(3));
assertThat(messages, hasItem("Searching for processes with status: [FAILED, RUNNING]"));
assertThat(messages, hasItem("Found 3 processes to be deleted"));
assertThat(messages, hasItem("Process cleanup completed"));
assertThat(processService.find(context, process_1.getID()), notNullValue());
assertThat(processService.find(context, process_2.getID()), notNullValue());
assertThat(processService.find(context, process_3.getID()), notNullValue());
assertThat(processService.find(context, process_4.getID()), notNullValue());
assertThat(processService.find(context, process_5.getID()), notNullValue());
assertThat(processService.find(context, process_6.getID()), nullValue());
assertThat(processService.find(context, process_7.getID()), nullValue());
assertThat(processService.find(context, process_8.getID()), nullValue());
}
@Test
public void testWithCompletedFailedAndRunningStatus() throws Exception {
Process process_1 = buildProcess(COMPLETED, addDays(new Date(), -2));
Process process_2 = buildProcess(RUNNING, addDays(new Date(), -1));
Process process_3 = buildProcess(FAILED, addDays(new Date(), -3));
Process process_4 = buildProcess(COMPLETED, addDays(new Date(), -6));
Process process_5 = buildProcess(COMPLETED, addDays(new Date(), -8));
Process process_6 = buildProcess(RUNNING, addDays(new Date(), -7));
Process process_7 = buildProcess(FAILED, addDays(new Date(), -8));
Process process_8 = buildProcess(RUNNING, addDays(new Date(), -9));
configurationService.setProperty("process-cleaner.days", 5);
TestDSpaceRunnableHandler testDSpaceRunnableHandler = new TestDSpaceRunnableHandler();
String[] args = new String[] { "process-cleaner", "-f", "-r", "-c" };
ScriptLauncher.handleScript(args, ScriptLauncher.getConfig(kernelImpl), testDSpaceRunnableHandler, kernelImpl);
List<String> messages = testDSpaceRunnableHandler.getInfoMessages();
assertThat(messages, hasSize(3));
assertThat(messages, hasItem("Searching for processes with status: [COMPLETED, FAILED, RUNNING]"));
assertThat(messages, hasItem("Found 5 processes to be deleted"));
assertThat(messages, hasItem("Process cleanup completed"));
assertThat(processService.find(context, process_1.getID()), notNullValue());
assertThat(processService.find(context, process_2.getID()), notNullValue());
assertThat(processService.find(context, process_3.getID()), notNullValue());
assertThat(processService.find(context, process_4.getID()), nullValue());
assertThat(processService.find(context, process_5.getID()), nullValue());
assertThat(processService.find(context, process_6.getID()), nullValue());
assertThat(processService.find(context, process_7.getID()), nullValue());
assertThat(processService.find(context, process_8.getID()), nullValue());
}
private Process buildProcess(ProcessStatus processStatus, Date creationTime) throws SQLException {
return ProcessBuilder.createProcess(context, admin, "test", List.of())
.withProcessStatus(processStatus)
.withCreationTime(creationTime)
.build();
}
}

View File

@@ -0,0 +1,62 @@
/**
* 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.requestitem;
import static org.junit.Assert.assertEquals;
import java.util.List;
import org.dspace.content.Collection;
import org.dspace.content.Item;
import org.dspace.core.Context;
import org.dspace.eperson.EPerson;
import org.dspace.eperson.Group;
import org.junit.Test;
import org.mockito.Mockito;
/**
*
* @author Mark H. Wood <mwood@iupui.edu>
*/
public class CollectionAdministratorsRequestItemStrategyTest {
private static final String NAME = "John Q. Public";
private static final String EMAIL = "jqpublic@example.com";
/**
* Test of getRequestItemAuthor method, of class CollectionAdministratorsRequestItemStrategy.
* @throws java.lang.Exception passed through.
*/
@Test
public void testGetRequestItemAuthor()
throws Exception {
System.out.println("getRequestItemAuthor");
Context context = Mockito.mock(Context.class);
EPerson eperson1 = Mockito.mock(EPerson.class);
Mockito.when(eperson1.getEmail()).thenReturn(EMAIL);
Mockito.when(eperson1.getFullName()).thenReturn(NAME);
Group group1 = Mockito.mock(Group.class);
Mockito.when(group1.getMembers()).thenReturn(List.of(eperson1));
Collection collection1 = Mockito.mock(Collection.class);
Mockito.when(collection1.getAdministrators()).thenReturn(group1);
Item item = Mockito.mock(Item.class);
Mockito.when(item.getOwningCollection()).thenReturn(collection1);
Mockito.when(item.getSubmitter()).thenReturn(eperson1);
CollectionAdministratorsRequestItemStrategy instance = new CollectionAdministratorsRequestItemStrategy();
List<RequestItemAuthor> result = instance.getRequestItemAuthor(context,
item);
assertEquals("Should be one author", 1, result.size());
assertEquals("Name should match " + NAME, NAME, result.get(0).getFullName());
assertEquals("Email should match " + EMAIL, EMAIL, result.get(0).getEmail());
}
}

View File

@@ -0,0 +1,53 @@
/**
* 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.requestitem;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.collection.IsIterableContainingInAnyOrder.containsInAnyOrder;
import java.util.List;
import org.dspace.content.Item;
import org.dspace.core.Context;
import org.junit.Test;
import org.mockito.Mockito;
/**
*
* @author Mark H. Wood <mwood@iupui.edu>
*/
public class CombiningRequestItemStrategyTest {
/**
* Test of getRequestItemAuthor method, of class CombiningRequestItemStrategy.
* @throws java.lang.Exception passed through.
*/
@Test
public void testGetRequestItemAuthor()
throws Exception {
System.out.println("getRequestItemAuthor");
Context context = null;
Item item = Mockito.mock(Item.class);
RequestItemAuthor author1 = new RequestItemAuthor("Pat Paulsen", "ppaulsen@example.com");
RequestItemAuthor author2 = new RequestItemAuthor("Alfred E. Neuman", "aeneuman@example.com");
RequestItemAuthor author3 = new RequestItemAuthor("Alias Undercover", "aundercover@example.com");
RequestItemAuthorExtractor strategy1 = Mockito.mock(RequestItemHelpdeskStrategy.class);
Mockito.when(strategy1.getRequestItemAuthor(context, item)).thenReturn(List.of(author1));
RequestItemAuthorExtractor strategy2 = Mockito.mock(RequestItemMetadataStrategy.class);
Mockito.when(strategy2.getRequestItemAuthor(context, item)).thenReturn(List.of(author2, author3));
List<RequestItemAuthorExtractor> strategies = List.of(strategy1, strategy2);
CombiningRequestItemStrategy instance = new CombiningRequestItemStrategy(strategies);
List<RequestItemAuthor> result = instance.getRequestItemAuthor(context,
item);
assertThat(result, containsInAnyOrder(author1, author2, author3));
}
}

View File

@@ -0,0 +1,84 @@
/**
* 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.authorize;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.is;
import static org.mockito.Mockito.when;
import org.dspace.AbstractIntegrationTest;
import org.dspace.services.ConfigurationService;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
/**
* Unit tests for {@link RegexPasswordValidator}.
*
* @author Luca Giamminonni (luca.giamminonni at 4science.it)
*/
@RunWith(MockitoJUnitRunner.class)
public class RegexPasswordValidatorTest extends AbstractIntegrationTest {
@Mock
private ConfigurationService configurationService;
@InjectMocks
private RegexPasswordValidator regexPasswordValidator;
@Before
public void setup() {
when(configurationService.getProperty("authentication-password.regex-validation.pattern"))
.thenReturn("^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d)(?=.*[^\\da-zA-Z]).{8,15}$");
}
@Test
public void testValidPassword() {
assertThat(regexPasswordValidator.isPasswordValid("TestPassword01!"), is(true));
}
@Test
public void testInvalidPasswordForMissingSpecialCharacter() {
assertThat(regexPasswordValidator.isPasswordValid("TestPassword01"), is(false));
assertThat(regexPasswordValidator.isPasswordValid("TestPassword01?"), is(true));
}
@Test
public void testInvalidPasswordForMissingNumber() {
assertThat(regexPasswordValidator.isPasswordValid("TestPassword!"), is(false));
assertThat(regexPasswordValidator.isPasswordValid("TestPassword1!"), is(true));
}
@Test
public void testInvalidPasswordForMissingUppercaseCharacter() {
assertThat(regexPasswordValidator.isPasswordValid("testpassword01!"), is(false));
assertThat(regexPasswordValidator.isPasswordValid("testPassword01!"), is(true));
}
@Test
public void testInvalidPasswordForMissingLowercaseCharacter() {
assertThat(regexPasswordValidator.isPasswordValid("TESTPASSWORD01!"), is(false));
assertThat(regexPasswordValidator.isPasswordValid("TESTPASSWORd01!"), is(true));
}
@Test
public void testInvalidPasswordForTooShortValue() {
assertThat(regexPasswordValidator.isPasswordValid("Test01!"), is(false));
assertThat(regexPasswordValidator.isPasswordValid("Test012!"), is(true));
}
@Test
public void testInvalidPasswordForTooLongValue() {
assertThat(regexPasswordValidator.isPasswordValid("ThisIsAVeryLongPassword01!"), is(false));
assertThat(regexPasswordValidator.isPasswordValid("IsAPassword012!"), is(true));
}
}

View File

@@ -11,6 +11,7 @@ import java.io.IOException;
import java.sql.SQLException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.Set;
@@ -60,6 +61,11 @@ public class ProcessBuilder extends AbstractBuilder<Process, ProcessService> {
return this;
}
public ProcessBuilder withCreationTime(Date creationTime) {
process.setCreationTime(creationTime);
return this;
}
public ProcessBuilder withStartAndEndTime(String startTime, String endTime) throws ParseException {
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("dd/MM/yyyy");
process.setStartTime(simpleDateFormat.parse(startTime));