Merge remote-tracking branch 'upstream/main' into w2p-72472_Delete-eperson

This commit is contained in:
Yana De Pauw
2020-10-01 10:49:10 +02:00
101 changed files with 6983 additions and 864 deletions

29
.codecov.yml Normal file
View File

@@ -0,0 +1,29 @@
# DSpace configuration for Codecov.io coverage reports
# These override the default YAML settings at
# https://docs.codecov.io/docs/codecov-yaml#section-default-yaml
# Can be validated via instructions at:
# https://docs.codecov.io/docs/codecov-yaml#validate-your-repository-yaml
# Settings related to code coverage analysis
coverage:
status:
# Configuration for project-level checks. This checks how the PR changes overall coverage.
project:
default:
# For each PR, auto compare coverage to previous commit.
# Require that overall (project) coverage does NOT drop more than 0.5%
target: auto
threshold: 0.5%
# Configuration for patch-level checks. This checks the relative coverage of the new PR code ONLY.
patch:
default:
# For each PR, make sure the coverage of the new code is within 1% of current overall coverage.
# We let 'patch' be more lenient as we only require *project* coverage to not drop significantly.
target: auto
threshold: 1%
# Turn PR comments "off". This feature adds the code coverage summary as a
# comment on each PR. See https://docs.codecov.io/docs/pull-request-comments
# However, this same info is available from the Codecov checks in the PR's
# "Checks" tab in GitHub. So, the comment is unnecessary.
comment: false

View File

@@ -1,46 +1,55 @@
# DSpace's Travis CI Configuration
# Builds: https://travis-ci.com/github/DSpace/DSpace
# Travis configuration guide/validation: https://config.travis-ci.com/explore
language: java
sudo: false
# TODO: Upgrade to Bionic
dist: trusty
env:
# Give Maven 1GB of memory to work with
- MAVEN_OPTS=-Xmx1024M
os: linux
jdk:
# DS-3384 Oracle JDK has DocLint enabled by default.
# Let's use this to catch any newly introduced DocLint issues.
- oraclejdk11
## Should we run into any problems with oraclejdk8 on Travis, we may try the following workaround.
## https://docs.travis-ci.com/user/languages/java#Testing-Against-Multiple-JDKs
## https://github.com/travis-ci/travis-ci/issues/3259#issuecomment-130860338
#addons:
# apt:
# packages:
# - oracle-java8-installer
# Define global environment variables (shared across all jobs)
env:
global:
# Suppress all Maven "downloading" messages in Travis logs (see https://stackoverflow.com/a/35653426)
# This also slightly speeds builds in Travis, as there is less logging
- HIDE_MAVEN_DOWNLOADS="-Dorg.slf4j.simpleLogger.log.org.apache.maven.cli.transfer.Slf4jMavenTransferListener=warn"
# Give Maven 1GB of memory to work with
- MAVEN_OPTS="-Xmx1024M $HIDE_MAVEN_DOWNLOADS"
# Maven options which will skip ALL code validation checks. Includes skipping:
# - enforcer.skip => Skip maven-enforcer-plugin rules
# - checkstyle.skip => Skip all checkstyle checks by maven-checkstyle-plugin
# - license.skip => Skip all license header checks by license-maven-plugin
# - xml.skip => Skip all XML/XSLT validation by xml-maven-plugin
# (Useful for builds which don't need to repeat code checks)
- SKIP_CODE_CHECKS="-Denforcer.skip=true -Dcheckstyle.skip=true -Dlicense.skip=true -Dxml.skip=true"
before_install:
# Remove outdated settings.xml from Travis builds. Workaround for https://github.com/travis-ci/travis-ci/issues/4629
- rm ~/.m2/settings.xml
# Create two jobs to run Unit & Integration tests in parallel.
# These jobs only differ in the TEST_FLAGS defined below,
# and otherwise share all the other configs in this file
jobs:
include:
- name: "Run Unit Tests & Check Code"
# NOTE: unit tests include deprecated REST API v6 (as it has unit tests)
env: TEST_FLAGS="-DskipUnitTests=false -Pdspace-rest"
- name: "Run Integration Tests"
# NOTE: skips code checks, as they are already done by Unit Test job
env: TEST_FLAGS="-DskipIntegrationTests=false $SKIP_CODE_CHECKS"
# Skip install stage, as we'll do it below
install: "echo 'Skipping install stage, dependencies will be downloaded during build and test stages.'"
# Skip 'install' process to save time. We build/install/test all at once in "script" below.
install: skip
# Build DSpace and run both Unit and Integration Tests
script:
# Summary of flags used (below):
# license:check => Validate all source code license headers
# -DskipTests=false => Enable DSpace Unit Tests
# -DskipITs=false => Enable DSpace Integration Tests
# -Pdspace-rest => Enable optional dspace-rest module as part of build
# -P !assembly => Skip assembly of "dspace-installer" directory (as it can be memory intensive)
# -B => Maven batch/non-interactive mode (recommended for CI)
# -V => Display Maven version info before build
# -Dsurefire.rerunFailingTestsCount=2 => try again for flakey tests, and keep track of/report on number of retries
- "mvn clean install license:check -DskipTests=false -DskipITs=false -Pdspace-rest -P !assembly -B -V -Dsurefire.rerunFailingTestsCount=2"
# Build DSpace and run configured tests (see 'jobs' above)
# Notes on flags used:
# -B => Maven batch/non-interactive mode (recommended for CI)
# -V => Display Maven version info before build
# -P-assembly => Disable build of dspace-installer in [src]/dspace/, as it can be memory intensive
# -Pcoverage-report => Enable aggregate code coverage report (across all modules) via JaCoCo
script: mvn install -B -V -P-assembly -Pcoverage-report $TEST_FLAGS
# After a successful build and test (see 'script'), send code coverage reports to coveralls.io
# These code coverage reports are generated by jacoco-maven-plugin (during test process above).
after_success:
# Run "verify", enabling the "coveralls" profile. This sends our reports to coveralls.io (see coveralls-maven-plugin)
- "cd dspace && mvn verify -P coveralls"
# After a successful build and test (see 'script'), send aggregate code coverage reports
# (generated by -Pcoverage-report above) to CodeCov.io
after_success: bash <(curl -s https://codecov.io/bash)

View File

@@ -90,33 +90,33 @@ run automatically by [Travis CI](https://travis-ci.com/DSpace/DSpace/) for all P
* How to run both Unit Tests (via `maven-surefire-plugin`) and Integration Tests (via `maven-failsafe-plugin`):
```
mvn clean test -DskipTests=false -DskipITs=false
mvn install -DskipUnitTests=false -DskipIntegrationTests=false
```
* How to run just Unit Tests:
* How to run _only_ Unit Tests:
```
mvn test -DskipTests=false
mvn test -DskipUnitTests=false
```
* How to run a *single* Unit Test
```
# Run all tests in a specific test class
# NOTE: failIfNoTests=false is required to skip tests in other modules
mvn test -DskipTests=false -Dtest=[full.package.testClassName] -DfailIfNoTests=false
mvn test -DskipUnitTests=false -Dtest=[full.package.testClassName] -DfailIfNoTests=false
# Run one test method in a specific test class
mvn test -DskipTests=false -Dtest=[full.package.testClassName]#[testMethodName] -DfailIfNoTests=false
mvn test -DskipUnitTests=false -Dtest=[full.package.testClassName]#[testMethodName] -DfailIfNoTests=false
```
* How to run Integration Tests (requires enabling Unit tests too)
* How to run _only_ Integration Tests
```
mvn verify -DskipTests=false -DskipITs=false
mvn install -DskipIntegrationTests=false
```
* How to run a *single* Integration Test (requires enabling Unit tests too)
* How to run a *single* Integration Test
```
# Run all integration tests in a specific test class
# NOTE: failIfNoTests=false is required to skip tests in other modules
mvn test -DskipTests=false -DskipITs=false -Dtest=[full.package.testClassName] -DfailIfNoTests=false
mvn install -DskipIntegrationTests=false -Dit.test=[full.package.testClassName] -DfailIfNoTests=false
# Run one test method in a specific test class
mvn test -DskipTests=false -DskipITs=false -Dtest=[full.package.testClassName]#[testMethodName] -DfailIfNoTests=false
mvn install -DskipIntegrationTests=false -Dit.test=[full.package.testClassName]#[testMethodName] -DfailIfNoTests=false
```
* How to run only tests of a specific DSpace module
```

View File

@@ -127,44 +127,69 @@
</executions>
</plugin>
<!-- This plugin allows us to run a Groovy script in our Maven POM
(see: https://groovy.github.io/gmaven/groovy-maven-plugin/execute.html )
We are generating a OS-agnostic version (agnostic.build.dir) of
the ${project.build.directory} property (full path of target dir).
This is needed by the Surefire & Failsafe plugins (see below)
to initialize the Unit Test environment's dspace.cfg file.
Otherwise, the Unit Test Framework will not work on Windows OS.
This Groovy code was mostly borrowed from:
http://stackoverflow.com/questions/3872355/how-to-convert-file-separator-in-maven
-->
<plugin>
<groupId>org.codehaus.gmaven</groupId>
<artifactId>groovy-maven-plugin</artifactId>
<executions>
<execution>
<id>setproperty</id>
<phase>initialize</phase>
<goals>
<goal>execute</goal>
</goals>
<configuration>
<source>
project.properties['agnostic.build.dir'] = project.build.directory.replace(File.separator, '/');
log.info("Initializing Maven property 'agnostic.build.dir' to: {}", project.properties['agnostic.build.dir']);
</source>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
<profiles>
<profile>
<id>findbugs</id>
<id>spotbugs</id>
<activation>
<activeByDefault>false</activeByDefault>
<!-- property>
<name>skipTests</name>
<value>false</value>
</property -->
</activation>
<build>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>findbugs-maven-plugin</artifactId>
<groupId>com.github.spotbugs</groupId>
<artifactId>spotbugs-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</profile>
<!-- If Unit Testing is enabled, then setup the Unit Test Environment.
See also the 'skiptests' profile in Parent POM. -->
<!-- Setup the Unit Test Environment (when -DskipUnitTests=false) -->
<profile>
<id>test-environment</id>
<id>unit-test-environment</id>
<activation>
<activeByDefault>false</activeByDefault>
<property>
<name>skipTests</name>
<name>skipUnitTests</name>
<value>false</value>
</property>
</activation>
<build>
<plugins>
<!-- Unit/Integration Testing setup: This plugin unzips the
<!-- Unit Testing setup: This plugin unzips the
'testEnvironment.zip' file (created by dspace-parent POM), into
the 'target/testing/' folder, to essentially create a test
install of DSpace, against which Tests can be run. -->
@@ -184,53 +209,16 @@
</configuration>
<executions>
<execution>
<id>setupTestEnvironment</id>
<id>setupUnitTestEnvironment</id>
<phase>generate-test-resources</phase>
<goals>
<goal>unpack</goal>
</goals>
</execution>
<execution>
<id>setupIntegrationTestEnvironment</id>
<phase>pre-integration-test</phase>
<goals>
<goal>unpack</goal>
</goals>
</execution>
</executions>
</plugin>
<!-- This plugin allows us to run a Groovy script in our Maven POM
(see: http://gmaven.codehaus.org/Executing+Groovy+Code )
We are generating a OS-agnostic version (agnostic.build.dir) of
the ${project.build.directory} property (full path of target dir).
This is needed by the Surefire & Failsafe plugins (see below)
to initialize the Unit Test environment's dspace.cfg file.
Otherwise, the Unit Test Framework will not work on Windows OS.
This Groovy code was mostly borrowed from:
http://stackoverflow.com/questions/3872355/how-to-convert-file-separator-in-maven
-->
<plugin>
<groupId>org.codehaus.gmaven</groupId>
<artifactId>groovy-maven-plugin</artifactId>
<executions>
<execution>
<id>setproperty</id>
<phase>initialize</phase>
<goals>
<goal>execute</goal>
</goals>
<configuration>
<source>
project.properties['agnostic.build.dir'] = project.build.directory.replace(File.separator, '/');
log.info("Initializing Maven property 'agnostic.build.dir' to: {}", project.properties['agnostic.build.dir']);
</source>
</configuration>
</execution>
</executions>
</plugin>
<!-- Run Unit Testing! This plugin just kicks off the tests (when enabled). -->
<!-- Run Unit Testing! This plugin just kicks off the tests. -->
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
@@ -245,8 +233,52 @@
</systemPropertyVariables>
</configuration>
</plugin>
</plugins>
</build>
</profile>
<!-- Run Integration Testing! This plugin just kicks off the tests (when enabled). -->
<!-- Setup the Integration Test Environment (when -DskipIntegrationTests=false) -->
<profile>
<id>integration-test-environment</id>
<activation>
<activeByDefault>false</activeByDefault>
<property>
<name>skipIntegrationTests</name>
<value>false</value>
</property>
</activation>
<build>
<plugins>
<!-- Integration Testing setup: This plugin unzips the
'testEnvironment.zip' file (created by dspace-parent POM), into
the 'target/testing/' folder, to essentially create a test
install of DSpace, against which Tests can be run. -->
<plugin>
<artifactId>maven-dependency-plugin</artifactId>
<configuration>
<outputDirectory>${project.build.directory}/testing</outputDirectory>
<artifactItems>
<artifactItem>
<groupId>org.dspace</groupId>
<artifactId>dspace-parent</artifactId>
<version>${project.version}</version>
<type>zip</type>
<classifier>testEnvironment</classifier>
</artifactItem>
</artifactItems>
</configuration>
<executions>
<execution>
<id>setupIntegrationTestEnvironment</id>
<phase>pre-integration-test</phase>
<goals>
<goal>unpack</goal>
</goals>
</execution>
</executions>
</plugin>
<!-- Run Integration Testing! This plugin just kicks off the tests. -->
<plugin>
<artifactId>maven-failsafe-plugin</artifactId>
<configuration>
@@ -262,7 +294,6 @@
</plugin>
</plugins>
</build>
</profile>
</profiles>
@@ -332,7 +363,7 @@
<artifactId>jersey-hk2</artifactId>
<version>${jersey.version}</version>
</dependency>
<dependency>
<groupId>commons-cli</groupId>
<artifactId>commons-cli</artifactId>
@@ -732,6 +763,7 @@
<groupId>com.google.oauth-client</groupId>
<artifactId>google-oauth-client</artifactId>
</dependency>
<!-- FindBugs -->
<dependency>
<groupId>com.google.code.findbugs</groupId>
@@ -741,6 +773,7 @@
<groupId>com.google.code.findbugs</groupId>
<artifactId>annotations</artifactId>
</dependency>
<dependency>
<groupId>joda-time</groupId>
<artifactId>joda-time</artifactId>

View File

@@ -629,6 +629,10 @@ public class CommunityServiceImpl extends DSpaceObjectServiceImpl<Community> imp
case Constants.DELETE:
if (AuthorizeConfiguration.canCommunityAdminPerformSubelementDeletion()) {
adminObject = getParentObject(context, community);
if (adminObject == null) {
//top-level community, has to be admin of the current community
adminObject = community;
}
}
break;
case Constants.ADD:

View File

@@ -7,6 +7,7 @@
*/
package org.dspace.content;
import java.util.Objects;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
@@ -15,6 +16,8 @@ import javax.persistence.Id;
import javax.persistence.SequenceGenerator;
import javax.persistence.Table;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.builder.HashCodeBuilder;
import org.dspace.core.ReloadableEntity;
/**
@@ -45,7 +48,8 @@ public class EntityType implements ReloadableEntity<Integer> {
/**
* The standard setter for the ID of this EntityType
* @param id The ID that this EntityType's ID will be set to
*
* @param id The ID that this EntityType's ID will be set to
*/
public void setId(Integer id) {
this.id = id;
@@ -53,7 +57,8 @@ public class EntityType implements ReloadableEntity<Integer> {
/**
* The standard getter for the label of this EntityType
* @return The label for this EntityType
*
* @return The label for this EntityType
*/
public String getLabel() {
return label;
@@ -61,6 +66,7 @@ public class EntityType implements ReloadableEntity<Integer> {
/**
* The standard setter for the label of this EntityType
*
* @param label The label that this EntityType's label will be set to
*/
public void setLabel(String label) {
@@ -69,9 +75,40 @@ public class EntityType implements ReloadableEntity<Integer> {
/**
* The standard getter for the ID of this EntityType
* @return The ID for this EntityType
*
* @return The ID for this EntityType
*/
public Integer getID() {
return id;
}
/**
* Determines whether two entity types are equal based on the id and the label
* @param obj object to be compared
* @return
*/
public boolean equals(Object obj) {
if (!(obj instanceof EntityType)) {
return false;
}
EntityType entityType = (EntityType) obj;
if (!Objects.equals(this.getID(), entityType.getID())) {
return false;
}
if (!StringUtils.equals(this.getLabel(), entityType.getLabel())) {
return false;
}
return true;
}
/**
* Returns a hash code value for the object.
* @return hash code value
*/
@Override
public int hashCode() {
return new HashCodeBuilder().append(getID()).toHashCode();
}
}

View File

@@ -1106,19 +1106,7 @@ prevent the generation of resource policy entry values with null dspace_object a
}
break;
case Constants.DELETE:
if (item.getOwningCollection() != null) {
if (AuthorizeConfiguration.canCollectionAdminPerformItemDeletion()) {
adminObject = collection;
} else if (AuthorizeConfiguration.canCommunityAdminPerformItemDeletion()) {
adminObject = community;
}
} else {
if (AuthorizeConfiguration.canCollectionAdminManageTemplateItem()) {
adminObject = collection;
} else if (AuthorizeConfiguration.canCommunityAdminManageCollectionTemplateItem()) {
adminObject = community;
}
}
adminObject = item;
break;
case Constants.WRITE:
// if it is a template item we need to check the

View File

@@ -13,6 +13,7 @@ import java.util.List;
import org.dspace.core.Context;
import org.dspace.core.GenericDAO;
import org.dspace.scripts.Process;
import org.dspace.scripts.ProcessQueryParameterContainer;
/**
* This is the Data Access Object for the {@link Process} object
@@ -54,4 +55,30 @@ public interface ProcessDAO extends GenericDAO<Process> {
*/
int countRows(Context context) throws SQLException;
/**
* Returns a list of all Processes in the database which match the given field requirements. If the
* requirements are not null, they will be combined with an AND operation.
* @param context The relevant DSpace context
* @param processQueryParameterContainer The {@link ProcessQueryParameterContainer} containing all the values
* that the returned {@link Process} objects must adhere to
* @param limit The limit for the amount of Processes returned
* @param offset The offset for the Processes to be returned
* @return The list of all Processes which match the metadata requirements
* @throws SQLException If something goes wrong
*/
List<Process> search(Context context, ProcessQueryParameterContainer processQueryParameterContainer, int limit,
int offset) throws SQLException;
/**
* Count all the processes which match the requirements. The requirements are evaluated like the search
* method.
* @param context The relevant DSpace context
* @param processQueryParameterContainer The {@link ProcessQueryParameterContainer} containing all the values
* that the returned {@link Process} objects must adhere to
* @return The number of results matching the query
* @throws SQLException If something goes wrong
*/
int countTotalWithParameters(Context context, ProcessQueryParameterContainer processQueryParameterContainer)
throws SQLException;
}

View File

@@ -8,15 +8,20 @@
package org.dspace.content.dao.impl;
import java.sql.SQLException;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import org.apache.commons.lang3.StringUtils;
import org.dspace.content.dao.ProcessDAO;
import org.dspace.core.AbstractHibernateDAO;
import org.dspace.core.Context;
import org.dspace.scripts.Process;
import org.dspace.scripts.ProcessQueryParameterContainer;
import org.dspace.scripts.Process_;
/**
@@ -56,6 +61,7 @@ public class ProcessDAOImpl extends AbstractHibernateDAO<Process> implements Pro
CriteriaQuery criteriaQuery = getCriteriaQuery(criteriaBuilder, Process.class);
Root<Process> processRoot = criteriaQuery.from(Process.class);
criteriaQuery.select(processRoot);
criteriaQuery.orderBy(criteriaBuilder.desc(processRoot.get(Process_.processId)));
return list(context, criteriaQuery, false, Process.class, limit, offset);
}
@@ -71,6 +77,76 @@ public class ProcessDAOImpl extends AbstractHibernateDAO<Process> implements Pro
return count(context, criteriaQuery, criteriaBuilder, processRoot);
}
@Override
public List<Process> search(Context context, ProcessQueryParameterContainer processQueryParameterContainer,
int limit, int offset) throws SQLException {
CriteriaBuilder criteriaBuilder = getCriteriaBuilder(context);
CriteriaQuery criteriaQuery = getCriteriaQuery(criteriaBuilder, Process.class);
Root<Process> processRoot = criteriaQuery.from(Process.class);
criteriaQuery.select(processRoot);
handleProcessQueryParameters(processQueryParameterContainer, criteriaBuilder, criteriaQuery, processRoot);
return list(context, criteriaQuery, false, Process.class, limit, offset);
}
/**
* This method will ensure that the params contained in the {@link ProcessQueryParameterContainer} are transferred
* to the ProcessRoot and that the correct conditions apply to the query
* @param processQueryParameterContainer The object containing the conditions that need to be met
* @param criteriaBuilder The criteriaBuilder to be used
* @param criteriaQuery The criteriaQuery to be used
* @param processRoot The processRoot to be used
*/
private void handleProcessQueryParameters(ProcessQueryParameterContainer processQueryParameterContainer,
CriteriaBuilder criteriaBuilder, CriteriaQuery criteriaQuery,
Root<Process> processRoot) {
addProcessQueryParameters(processQueryParameterContainer, criteriaBuilder, criteriaQuery, processRoot);
if (StringUtils.equalsIgnoreCase(processQueryParameterContainer.getSortOrder(), "asc")) {
criteriaQuery
.orderBy(criteriaBuilder.asc(processRoot.get(processQueryParameterContainer.getSortProperty())));
} else if (StringUtils.equalsIgnoreCase(processQueryParameterContainer.getSortOrder(), "desc")) {
criteriaQuery
.orderBy(criteriaBuilder.desc(processRoot.get(processQueryParameterContainer.getSortProperty())));
}
}
/**
* This method will apply the variables in the {@link ProcessQueryParameterContainer} as criteria for the
* {@link Process} objects to the given CriteriaQuery.
* They'll need to adhere to these variables in order to be eligible for return
* @param processQueryParameterContainer The object containing the variables for the {@link Process}
* to adhere to
* @param criteriaBuilder The current CriteriaBuilder
* @param criteriaQuery The current CriteriaQuery
* @param processRoot The processRoot
*/
private void addProcessQueryParameters(ProcessQueryParameterContainer processQueryParameterContainer,
CriteriaBuilder criteriaBuilder, CriteriaQuery criteriaQuery,
Root<Process> processRoot) {
List<Predicate> andPredicates = new LinkedList<>();
for (Map.Entry<String, Object> entry : processQueryParameterContainer.getQueryParameterMap().entrySet()) {
andPredicates.add(criteriaBuilder.equal(processRoot.get(entry.getKey()), entry.getValue()));
}
criteriaQuery.where(criteriaBuilder.and(andPredicates.toArray(new Predicate[]{})));
}
@Override
public int countTotalWithParameters(Context context, ProcessQueryParameterContainer processQueryParameterContainer)
throws SQLException {
CriteriaBuilder criteriaBuilder = getCriteriaBuilder(context);
CriteriaQuery criteriaQuery = getCriteriaQuery(criteriaBuilder, Process.class);
Root<Process> processRoot = criteriaQuery.from(Process.class);
criteriaQuery.select(processRoot);
addProcessQueryParameters(processQueryParameterContainer, criteriaBuilder, criteriaQuery, processRoot);
return count(context, criteriaQuery, criteriaBuilder, processRoot);
}
}

View File

@@ -0,0 +1,43 @@
/**
* 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.discovery.indexobject;
import java.io.Serializable;
import org.dspace.core.ReloadableEntity;
import org.dspace.discovery.IndexableObject;
/**
* This class exists in order to provide a default implementation for the equals and hashCode methods.
* Since IndexableObjects can be made multiple times for the same underlying object, we needed a more finetuned
* equals and hashcode methods. We're simply checking that the underlying objects are equal and generating the hashcode
* for the underlying object. This way, we'll always get a proper result when calling equals or hashcode on an
* IndexableObject because it'll depend on the underlying object
* @param <T> Refers to the underlying entity that is linked to this object
* @param <PK> The type of ID that this entity uses
*/
public abstract class AbstractIndexableObject<T extends ReloadableEntity<PK>, PK extends Serializable>
implements IndexableObject<T,PK> {
@Override
public boolean equals(Object obj) {
//Two IndexableObjects of the same DSpaceObject are considered equal
if (!(obj instanceof AbstractIndexableObject)) {
return false;
}
IndexableDSpaceObject other = (IndexableDSpaceObject) obj;
return other.getIndexedObject().equals(getIndexedObject());
}
@Override
public int hashCode() {
//Two IndexableObjects of the same DSpaceObject are considered equal
return getIndexedObject().hashCode();
}
}

View File

@@ -7,7 +7,6 @@
*/
package org.dspace.discovery.indexobject;
import org.dspace.discovery.IndexableObject;
import org.dspace.xmlworkflow.storedcomponents.ClaimedTask;
/**
@@ -15,7 +14,7 @@ import org.dspace.xmlworkflow.storedcomponents.ClaimedTask;
*
* @author Kevin Van de Velde (kevin at atmire dot com)
*/
public class IndexableClaimedTask implements IndexableObject<ClaimedTask, Integer> {
public class IndexableClaimedTask extends AbstractIndexableObject<ClaimedTask, Integer> {
private ClaimedTask claimedTask;
public static final String TYPE = ClaimedTask.class.getSimpleName();

View File

@@ -10,7 +10,6 @@ package org.dspace.discovery.indexobject;
import java.util.UUID;
import org.dspace.content.DSpaceObject;
import org.dspace.discovery.IndexableObject;
/**
* DSpaceObject implementation for the IndexableObject, contains methods used by all DSpaceObject methods
@@ -18,7 +17,7 @@ import org.dspace.discovery.IndexableObject;
*
* @author Kevin Van de Velde (kevin at atmire dot com)
*/
public abstract class IndexableDSpaceObject<T extends DSpaceObject> implements IndexableObject<T, UUID> {
public abstract class IndexableDSpaceObject<T extends DSpaceObject> extends AbstractIndexableObject<T, UUID> {
private T dso;
@@ -40,4 +39,6 @@ public abstract class IndexableDSpaceObject<T extends DSpaceObject> implements I
public UUID getID() {
return dso.getID();
}
}
}

View File

@@ -8,14 +8,13 @@
package org.dspace.discovery.indexobject;
import org.dspace.content.InProgressSubmission;
import org.dspace.discovery.IndexableObject;
/**
* InProgressSubmission implementation for the IndexableObject
* @author Kevin Van de Velde (kevin at atmire dot com)
*/
public abstract class IndexableInProgressSubmission<T extends InProgressSubmission>
implements IndexableObject<T, Integer> {
extends AbstractIndexableObject<T, Integer> {
protected T inProgressSubmission;

View File

@@ -15,7 +15,7 @@ import org.dspace.discovery.IndexableObject;
*
* @author Maria Verdonck (Atmire) on 14/07/2020
*/
public class IndexableMetadataField implements IndexableObject<MetadataField, Integer> {
public class IndexableMetadataField extends AbstractIndexableObject<MetadataField, Integer> {
private MetadataField metadataField;
public static final String TYPE = MetadataField.class.getSimpleName();

View File

@@ -7,14 +7,13 @@
*/
package org.dspace.discovery.indexobject;
import org.dspace.discovery.IndexableObject;
import org.dspace.xmlworkflow.storedcomponents.PoolTask;
/**
* PoolTask implementation for the IndexableObject
* @author Kevin Van de Velde (kevin at atmire dot com)
*/
public class IndexablePoolTask implements IndexableObject<PoolTask, Integer> {
public class IndexablePoolTask extends AbstractIndexableObject<PoolTask, Integer> {
public static final String TYPE = PoolTask.class.getSimpleName();

View File

@@ -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.scripts;
import java.util.HashMap;
import java.util.Map;
/**
* This is a container class in which the variables can be stored that a {@link Process} must adhere to when being
* retrieved from the DB through the search methods
*/
public class ProcessQueryParameterContainer {
private Map<String, Object> queryParameterMap = new HashMap<>();
/**
* Generic getter for the queryParameterMap
* @return the queryParameterMap value of this ProcessQueryParameterContainer
*/
public Map<String, Object> getQueryParameterMap() {
return queryParameterMap;
}
private String sortProperty = "startTime";
private String sortOrder = "desc";
/**
* Generic setter for the queryParameterMap
* @param queryParameterMap The queryParameterMap to be set on this ProcessQueryParameterContainer
*/
public void setQueryParameterMap(Map<String, Object> queryParameterMap) {
this.queryParameterMap = queryParameterMap;
}
public void addToQueryParameterMap(String key, Object object) {
if (queryParameterMap == null) {
queryParameterMap = new HashMap<>();
}
queryParameterMap.put(key, object);
}
/**
* Generic getter for the sortProperty
* @return the sortProperty value of this ProcessQueryParameterContainer
*/
public String getSortProperty() {
return sortProperty;
}
/**
* Generic setter for the sortProperty
* @param sortProperty The sortProperty to be set on this ProcessQueryParameterContainer
*/
public void setSortProperty(String sortProperty) {
this.sortProperty = sortProperty;
}
/**
* Generic getter for the sortOrder
* @return the sortOrder value of this ProcessQueryParameterContainer
*/
public String getSortOrder() {
return sortOrder;
}
/**
* Generic setter for the sortOrder
* @param sortOrder The sortOrder to be set on this ProcessQueryParameterContainer
*/
public void setSortOrder(String sortOrder) {
this.sortOrder = sortOrder;
}
}

View File

@@ -255,6 +255,19 @@ public class ProcessServiceImpl implements ProcessService {
return new ArrayList<>(fileTypesSet);
}
@Override
public List<Process> search(Context context, ProcessQueryParameterContainer processQueryParameterContainer,
int limit, int offset) throws SQLException {
return processDAO.search(context, processQueryParameterContainer, limit, offset);
}
@Override
public int countSearch(Context context, ProcessQueryParameterContainer processQueryParameterContainer)
throws SQLException {
return processDAO.countTotalWithParameters(context, processQueryParameterContainer);
}
@Override
public void appendLog(int processId, String scriptName, String output, ProcessLogLevel processLogLevel)
throws IOException {

View File

@@ -19,6 +19,7 @@ import org.dspace.eperson.EPerson;
import org.dspace.scripts.DSpaceCommandLineParameter;
import org.dspace.scripts.Process;
import org.dspace.scripts.ProcessLogLevel;
import org.dspace.scripts.ProcessQueryParameterContainer;
/**
* An interface for the ProcessService with methods regarding the Process workload
@@ -190,6 +191,30 @@ public interface ProcessService {
*/
public List<String> getFileTypesForProcessBitstreams(Context context, Process process);
/**
* Returns a list of all Processes in the database which match the given field requirements. If the
* requirements are not null, they will be combined with an AND operation.
* @param context The relevant DSpace context
* @param processQueryParameterContainer The {@link ProcessQueryParameterContainer} containing all the values
* that the returned {@link Process} objects must adhere to
* @param limit The limit for the amount of Processes returned
* @param offset The offset for the Processes to be returned
* @return The list of all Processes which match the metadata requirements
* @throws SQLException If something goes wrong
*/
List<Process> search(Context context, ProcessQueryParameterContainer processQueryParameterContainer, int limit,
int offset) throws SQLException;
/**
* Count all the processes which match the requirements. The requirements are evaluated like the search
* method.
* @param context The relevant DSpace context
* @param processQueryParameterContainer The {@link ProcessQueryParameterContainer} containing all the values
* that the returned {@link Process} objects must adhere to
* @return The number of results matching the query
* @throws SQLException If something goes wrong
*/
int countSearch(Context context, ProcessQueryParameterContainer processQueryParameterContainer) throws SQLException;
/**
* This method will append the given output to the {@link Process} its logs
* @param processId The ID of the {@link Process} to append the log for

View File

@@ -0,0 +1,59 @@
/**
* 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.statistics.export;
import java.sql.SQLException;
import java.util.List;
import org.dspace.core.Context;
import org.dspace.statistics.export.dao.OpenURLTrackerDAO;
import org.dspace.statistics.export.service.FailedOpenURLTrackerService;
import org.springframework.beans.factory.annotation.Autowired;
/**
* Implementation of the service that handles the OpenURLTracker database operations
*/
public class FailedOpenURLTrackerServiceImpl implements FailedOpenURLTrackerService {
@Autowired(required = true)
protected OpenURLTrackerDAO openURLTrackerDAO;
/**
* Removes an OpenURLTracker from the database
* @param context
* @param openURLTracker
* @throws SQLException
*/
@Override
public void remove(Context context, OpenURLTracker openURLTracker) throws SQLException {
openURLTrackerDAO.delete(context, openURLTracker);
}
/**
* Returns all OpenURLTrackers from the database
* @param context
* @return all OpenURLTrackers
* @throws SQLException
*/
@Override
public List<OpenURLTracker> findAll(Context context) throws SQLException {
return openURLTrackerDAO.findAll(context, OpenURLTracker.class);
}
/**
* Creates a new OpenURLTracker
* @param context
* @return the creatred OpenURLTracker
* @throws SQLException
*/
@Override
public OpenURLTracker create(Context context) throws SQLException {
OpenURLTracker openURLTracker = openURLTrackerDAO.create(context, new OpenURLTracker());
return openURLTracker;
}
}

View File

@@ -0,0 +1,74 @@
/**
* The contents of this file are subject to the license and copyright
* detailed in the LICENSE and NOTICE files at the root of the source
* tree and available online at
*
* http://www.dspace.org/license/
*/
package org.dspace.statistics.export;
import java.util.UUID;
import org.apache.log4j.Logger;
import org.dspace.content.Bitstream;
import org.dspace.content.Item;
import org.dspace.core.Context;
import org.dspace.core.LogManager;
import org.dspace.services.ConfigurationService;
import org.dspace.services.model.Event;
import org.dspace.statistics.export.processor.BitstreamEventProcessor;
import org.dspace.statistics.export.processor.ItemEventProcessor;
import org.dspace.usage.AbstractUsageEventListener;
import org.dspace.usage.UsageEvent;
import org.springframework.beans.factory.annotation.Autowired;
/**
* Class to receive usage events and send corresponding data to IRUS
*/
public class IrusExportUsageEventListener extends AbstractUsageEventListener {
/* Log4j logger*/
private static Logger log = Logger.getLogger(IrusExportUsageEventListener.class);
@Autowired
ConfigurationService configurationService;
/**
* Receives an event and processes to create a URL to send to IRUS when certain conditions are met
*
* @param event includes all the information related to the event that occurred
*/
public void receiveEvent(Event event) {
if (configurationService.getBooleanProperty("irus.statistics.tracker.enabled", false)) {
if (event instanceof UsageEvent) {
UsageEvent ue = (UsageEvent) event;
Context context = ue.getContext();
try {
//Check for item investigation
if (ue.getObject() instanceof Item) {
ItemEventProcessor itemEventProcessor = new ItemEventProcessor(context, ue.getRequest(),
(Item) ue.getObject());
itemEventProcessor.processEvent();
} else if (ue.getObject() instanceof Bitstream) {
BitstreamEventProcessor bitstreamEventProcessor =
new BitstreamEventProcessor(context, ue.getRequest(), (Bitstream) ue.getObject());
bitstreamEventProcessor.processEvent();
}
} catch (Exception e) {
UUID id;
id = ue.getObject().getID();
int type;
try {
type = ue.getObject().getType();
} catch (Exception e1) {
type = -1;
}
log.error(LogManager.getHeader(ue.getContext(), "Error while processing export of use event",
"Id: " + id + " type: " + type), e);
}
}
}
}
}

View File

@@ -0,0 +1,121 @@
/**
* 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.statistics.export;
import java.util.Date;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.SequenceGenerator;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import org.dspace.core.ReloadableEntity;
import org.hibernate.proxy.HibernateProxyHelper;
/**
* Class that represents an OpenURLTracker which tracks a failed transmission to IRUS
*/
@Entity
@Table(name = "OpenUrlTracker")
public class OpenURLTracker implements ReloadableEntity<Integer> {
@Id
@Column(name = "tracker_id")
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "openurltracker_seq")
@SequenceGenerator(name = "openurltracker_seq", sequenceName = "openurltracker_seq", allocationSize = 1)
private Integer id;
@Column(name = "tracker_url", length = 1000)
private String url;
@Column(name = "uploaddate")
@Temporal(TemporalType.DATE)
private Date uploadDate;
protected OpenURLTracker() {
}
/**
* Gets the OpenURLTracker id
* @return the id
*/
@Override
public Integer getID() {
return id;
}
/**
* Gets the OpenURLTracker url
* @return the url
*/
public String getUrl() {
return url;
}
/**
* Sets the OpenURLTracker url
* @param url
*/
public void setUrl(String url) {
this.url = url;
}
/**
* Returns the upload date
* @return upload date
*/
public Date getUploadDate() {
return uploadDate;
}
/**
* Set the upload date
* @param uploadDate
*/
public void setUploadDate(Date uploadDate) {
this.uploadDate = uploadDate;
}
/**
* Determines whether two objects of this class are equal by comparing the ID
* @param o - object to compare
* @return whether the objects are equal
*/
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
Class<?> objClass = HibernateProxyHelper.getClassWithoutInitializingProxy(o);
if (getClass() != objClass) {
return false;
}
final OpenURLTracker that = (OpenURLTracker) o;
if (!this.getID().equals(that.getID())) {
return false;
}
return true;
}
/**
* Returns the hash code value for the object
* @return hash code
*/
@Override
public int hashCode() {
int hash = 8;
hash = 74 * hash + this.getID();
return hash;
}
}

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.statistics.export;
import org.apache.commons.cli.ParseException;
import org.apache.commons.lang3.StringUtils;
import org.dspace.core.Context;
import org.dspace.scripts.DSpaceRunnable;
import org.dspace.statistics.export.factory.OpenURLTrackerLoggerServiceFactory;
import org.dspace.statistics.export.service.OpenUrlService;
import org.dspace.utils.DSpace;
/**
* Script to retry the failed url transmissions to IRUS
* This script also has an option to add new failed urls for testing purposes
*/
public class RetryFailedOpenUrlTracker extends DSpaceRunnable<RetryFailedOpenUrlTrackerScriptConfiguration> {
private String lineToAdd = null;
private boolean help = false;
private boolean retryFailed = false;
private OpenUrlService openUrlService;
/**
* Run the script
* When the -a option is used, a new "failed" url will be added to the database
*
* @throws Exception
*/
public void internalRun() throws Exception {
if (help) {
printHelp();
return;
}
Context context = new Context();
context.turnOffAuthorisationSystem();
if (StringUtils.isNotBlank(lineToAdd)) {
openUrlService.logfailed(context, lineToAdd);
handler.logInfo("Created dummy entry in OpenUrlTracker with URL: " + lineToAdd);
}
if (retryFailed) {
handler.logInfo("Reprocessing failed URLs stored in the db");
openUrlService.reprocessFailedQueue(context);
}
context.restoreAuthSystemState();
context.complete();
}
public RetryFailedOpenUrlTrackerScriptConfiguration getScriptConfiguration() {
return new DSpace().getServiceManager().getServiceByName("retry-tracker",
RetryFailedOpenUrlTrackerScriptConfiguration.class);
}
/**
* Setups the parameters
*
* @throws ParseException
*/
public void setup() throws ParseException {
openUrlService = OpenURLTrackerLoggerServiceFactory.getInstance().getOpenUrlService();
if (!(commandLine.hasOption('a') || commandLine.hasOption('r') || commandLine.hasOption('h'))) {
throw new ParseException("At least one of the parameters (-a, -r, -h) is required!");
}
if (commandLine.hasOption('h')) {
help = true;
}
if (commandLine.hasOption('a')) {
lineToAdd = commandLine.getOptionValue('a');
}
if (commandLine.hasOption('r')) {
retryFailed = true;
}
}
}

View File

@@ -0,0 +1,74 @@
/**
* The contents of this file are subject to the license and copyright
* detailed in the LICENSE and NOTICE files at the root of the source
* tree and available online at
*
* http://www.dspace.org/license/
*/
package org.dspace.statistics.export;
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 RetryFailedOpenUrlTracker} script
*/
public class RetryFailedOpenUrlTrackerScriptConfiguration<T extends RetryFailedOpenUrlTracker>
extends ScriptConfiguration<T> {
@Autowired
private AuthorizeService authorizeService;
private Class<T> dspaceRunnableClass;
@Override
public Class<T> getDspaceRunnableClass() {
return dspaceRunnableClass;
}
/**
* Generic setter for the dspaceRunnableClass
*
* @param dspaceRunnableClass The dspaceRunnableClass to be set on this RetryFailedOpenUrlTrackerScriptConfiguration
*/
@Override
public void setDspaceRunnableClass(Class<T> dspaceRunnableClass) {
this.dspaceRunnableClass = 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("a", true, "Add a new \"failed\" row to the table with a url (test purposes only)");
options.getOption("a").setType(String.class);
options.addOption("r", false,
"Retry sending requests to all urls stored in the table with failed requests. " +
"This includes the url that can be added through the -a option.");
options.getOption("r").setType(boolean.class);
options.addOption("h", "help", false, "print this help message");
options.getOption("h").setType(boolean.class);
super.options = options;
}
return options;
}
}

View File

@@ -0,0 +1,21 @@
/**
* 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.statistics.export.dao;
import org.dspace.core.GenericDAO;
import org.dspace.statistics.export.OpenURLTracker;
/**
* Database Access Object interface class for the OpenURLTracker object.
* The implementation of this class is responsible for all database calls for the OpenURLTracker object and is
* autowired by spring
* This class should only be accessed from a single service and should never be exposed outside of the API
*/
public interface OpenURLTrackerDAO extends GenericDAO<OpenURLTracker> {
}

View File

@@ -0,0 +1,26 @@
/**
* 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.statistics.export.dao.impl;
import org.dspace.core.AbstractHibernateDAO;
import org.dspace.statistics.export.OpenURLTracker;
import org.dspace.statistics.export.dao.OpenURLTrackerDAO;
/**
* Hibernate implementation of the Database Access Object interface class for the OpenURLTracker object.
* This class is responsible for all database calls for the OpenURLTracker object and is autowired by spring
* This class should never be accessed directly.
*
*/
public class OpenURLTrackerDAOImpl extends AbstractHibernateDAO<OpenURLTracker> implements OpenURLTrackerDAO {
protected OpenURLTrackerDAOImpl() {
super();
}
}

View File

@@ -0,0 +1,41 @@
/**
* The contents of this file are subject to the license and copyright
* detailed in the LICENSE and NOTICE files at the root of the source
* tree and available online at
*
* http://www.dspace.org/license/
*/
package org.dspace.statistics.export.factory;
import org.dspace.services.factory.DSpaceServicesFactory;
import org.dspace.statistics.export.service.FailedOpenURLTrackerService;
import org.dspace.statistics.export.service.OpenUrlService;
/**
* The service factory for the OpenUrlTracker related services
*/
public abstract class OpenURLTrackerLoggerServiceFactory {
/**
* Returns the FailedOpenURLTrackerService
* @return FailedOpenURLTrackerService instance
*/
public abstract FailedOpenURLTrackerService getOpenUrlTrackerLoggerService();
/**
* Retrieve the OpenURLTrackerLoggerServiceFactory
* @return OpenURLTrackerLoggerServiceFactory instance
*/
public static OpenURLTrackerLoggerServiceFactory getInstance() {
return DSpaceServicesFactory.getInstance().getServiceManager()
.getServiceByName("openURLTrackerLoggerServiceFactory",
OpenURLTrackerLoggerServiceFactory.class);
}
/**
* Returns the OpenUrlService
* @return OpenUrlService instance
*/
public abstract OpenUrlService getOpenUrlService();
}

View File

@@ -0,0 +1,42 @@
/**
* 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.statistics.export.factory;
import org.dspace.statistics.export.service.FailedOpenURLTrackerService;
import org.dspace.statistics.export.service.OpenUrlService;
import org.springframework.beans.factory.annotation.Autowired;
/**
* The service factory implementation for the OpenUrlTracker related services
*/
public class OpenURLTrackerLoggerServiceFactoryImpl extends OpenURLTrackerLoggerServiceFactory {
@Autowired(required = true)
private FailedOpenURLTrackerService failedOpenURLTrackerService;
@Autowired(required = true)
private OpenUrlService openUrlService;
/**
* Returns the FailedOpenURLTrackerService
* @return FailedOpenURLTrackerService instance
*/
@Override
public FailedOpenURLTrackerService getOpenUrlTrackerLoggerService() {
return failedOpenURLTrackerService;
}
/**
* Returns the OpenUrlService
* @return OpenUrlService instance
*/
@Override
public OpenUrlService getOpenUrlService() {
return openUrlService;
}
}

View File

@@ -0,0 +1,129 @@
/**
* 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.statistics.export.processor;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.sql.SQLException;
import javax.servlet.http.HttpServletRequest;
import org.dspace.content.Bitstream;
import org.dspace.content.Bundle;
import org.dspace.content.Item;
import org.dspace.core.Context;
import org.dspace.services.ConfigurationService;
import org.dspace.services.factory.DSpaceServicesFactory;
import org.dspace.statistics.util.SpiderDetector;
/**
* Processor that handles Bitstream events from the IrusExportUsageEventListener
*/
public class BitstreamEventProcessor extends ExportEventProcessor {
private ConfigurationService configurationService = DSpaceServicesFactory.getInstance().getConfigurationService();
private Item item;
private Bitstream bitstream;
/**
* Creates a new BitstreamEventProcessor that will set the params and obtain the parent item of the bitstream
*
* @param context
* @param request
* @param bitstream
* @throws SQLException
*/
public BitstreamEventProcessor(Context context, HttpServletRequest request, Bitstream bitstream)
throws SQLException {
super(context, request);
this.bitstream = bitstream;
this.item = getItem(request);
}
/**
* Returns the parent item of the bitsream
*
* @return parent item of the bitstream
* @throws SQLException
*/
private Item getItem(HttpServletRequest request) throws SQLException {
if (0 < bitstream.getBundles().size()) {
if (!SpiderDetector.isSpider(request)) {
Bundle bundle = bitstream.getBundles().get(0);
if (bundle.getName() == null || !bundle.getName().equals("ORIGINAL")) {
return null;
}
if (0 < bundle.getItems().size()) {
Item item = bundle.getItems().get(0);
return item;
}
}
}
return null;
}
/**
* Process the event
* Check if the item should be processed
* Create the url to be transmitted based on item and bitstream data
*
* @throws SQLException
* @throws IOException
*/
public void processEvent() throws SQLException, IOException {
if (shouldProcessItem(item)) {
String baseParam = getBaseParameters(item);
String fullParam = addObjectSpecificData(baseParam, bitstream);
processObject(fullParam);
}
}
/**
* Adds additional item and bitstream data to the url
*
* @param string to which the additional data needs to be added
* @param bitstream
* @return the string with additional data
* @throws UnsupportedEncodingException
*/
protected String addObjectSpecificData(final String string, Bitstream bitstream)
throws UnsupportedEncodingException {
StringBuilder data = new StringBuilder(string);
String bitstreamInfo = getBitstreamInfo(bitstream);
data.append("&").append(URLEncoder.encode("svc_dat", UTF_8)).append("=")
.append(URLEncoder.encode(bitstreamInfo, UTF_8));
data.append("&").append(URLEncoder.encode("rft_dat", UTF_8)).append("=")
.append(URLEncoder.encode(BITSTREAM_DOWNLOAD, UTF_8));
return data.toString();
}
/**
* Get Bitstream info used for the url
*
* @param bitstream
* @return bitstream info
*/
private String getBitstreamInfo(final Bitstream bitstream) {
String dspaceRestUrl = configurationService.getProperty("dspace.server.url");
StringBuilder sb = new StringBuilder();
sb.append(dspaceRestUrl);
sb.append("/api/core/bitstreams/");
sb.append(bitstream.getID());
sb.append("/content");
return sb.toString();
}
}

View File

@@ -0,0 +1,258 @@
/**
* 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.statistics.export.processor;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import org.apache.commons.codec.CharEncoding;
import org.apache.commons.lang3.StringUtils;
import org.apache.log4j.Logger;
import org.dspace.content.DCDate;
import org.dspace.content.Entity;
import org.dspace.content.EntityType;
import org.dspace.content.Item;
import org.dspace.content.MetadataValue;
import org.dspace.content.factory.ContentServiceFactory;
import org.dspace.content.service.EntityService;
import org.dspace.content.service.ItemService;
import org.dspace.core.Context;
import org.dspace.core.Utils;
import org.dspace.services.ConfigurationService;
import org.dspace.services.factory.DSpaceServicesFactory;
import org.dspace.statistics.export.factory.OpenURLTrackerLoggerServiceFactory;
import org.dspace.statistics.export.service.OpenUrlService;
/**
* Abstract export event processor that contains all shared logic to handle both Items and Bitstreams
* from the IrusExportUsageEventListener
*/
public abstract class ExportEventProcessor {
private static Logger log = Logger.getLogger(ExportEventProcessor.class);
protected static final String ENTITY_TYPE_DEFAULT = "Publication";
protected static final String ITEM_VIEW = "Investigation";
protected static final String BITSTREAM_DOWNLOAD = "Request";
protected final static String UTF_8 = CharEncoding.UTF_8;
private ConfigurationService configurationService = DSpaceServicesFactory.getInstance().getConfigurationService();
private EntityService entityService = ContentServiceFactory.getInstance().getEntityService();
private ItemService itemService = ContentServiceFactory.getInstance().getItemService();
private OpenUrlService openUrlService = OpenURLTrackerLoggerServiceFactory.getInstance().getOpenUrlService();
private Context context;
private HttpServletRequest request;
/**
* Creates a new ExportEventProcessor based on the params and initializes the services
*
* @param context
* @param request
*/
ExportEventProcessor(Context context, HttpServletRequest request) {
this.context = context;
this.request = request;
}
/**
* Processes the event
*
* @throws SQLException
* @throws IOException
*/
public abstract void processEvent() throws SQLException, IOException;
/**
* Process the url obtained from the object to be transmitted
*
* @param urlParameters
* @throws IOException
* @throws SQLException
*/
protected void processObject(String urlParameters) throws IOException, SQLException {
String baseUrl;
if (StringUtils.equals(configurationService.getProperty("irus.statistics.tracker.environment"), "production")) {
baseUrl = configurationService.getProperty("irus.statistics.tracker.produrl");
} else {
baseUrl = configurationService.getProperty("irus.statistics.tracker.testurl");
}
openUrlService.processUrl(context, baseUrl + "?" + urlParameters);
}
/**
* Get the base parameters for the url to be transmitted
*
* @param item
* @return the parameter string to be used in the url
* @throws UnsupportedEncodingException
*/
protected String getBaseParameters(Item item)
throws UnsupportedEncodingException {
//We have a valid url collect the rest of the data
String clientIP = request.getRemoteAddr();
if (configurationService.getBooleanProperty("useProxies", false) && request
.getHeader("X-Forwarded-For") != null) {
/* This header is a comma delimited list */
for (String xfip : request.getHeader("X-Forwarded-For").split(",")) {
/* proxy itself will sometime populate this header with the same value in
remote address. ordering in spec is vague, we'll just take the last
not equal to the proxy
*/
if (!request.getHeader("X-Forwarded-For").contains(clientIP)) {
clientIP = xfip.trim();
}
}
}
String clientUA = StringUtils.defaultIfBlank(request.getHeader("USER-AGENT"), "");
String referer = StringUtils.defaultIfBlank(request.getHeader("referer"), "");
//Start adding our data
StringBuilder data = new StringBuilder();
data.append(URLEncoder.encode("url_ver", UTF_8) + "=" +
URLEncoder.encode(configurationService.getProperty("irus.statistics.tracker.urlversion"), UTF_8));
data.append("&").append(URLEncoder.encode("req_id", UTF_8)).append("=")
.append(URLEncoder.encode(clientIP, UTF_8));
data.append("&").append(URLEncoder.encode("req_dat", UTF_8)).append("=")
.append(URLEncoder.encode(clientUA, UTF_8));
String hostName = Utils.getHostName(configurationService.getProperty("dspace.ui.url"));
data.append("&").append(URLEncoder.encode("rft.artnum", UTF_8)).append("=").
append(URLEncoder.encode("oai:" + hostName + ":" + item
.getHandle(), UTF_8));
data.append("&").append(URLEncoder.encode("rfr_dat", UTF_8)).append("=")
.append(URLEncoder.encode(referer, UTF_8));
data.append("&").append(URLEncoder.encode("rfr_id", UTF_8)).append("=")
.append(URLEncoder.encode(hostName, UTF_8));
data.append("&").append(URLEncoder.encode("url_tim", UTF_8)).append("=")
.append(URLEncoder.encode(getCurrentDateString(), UTF_8));
return data.toString();
}
/**
* Get the current date
*
* @return the current date as a string
*/
protected String getCurrentDateString() {
return new DCDate(new Date()).toString();
}
/**
* Checks if an item should be processed
*
* @param item to be checked
* @return whether the item should be processed
* @throws SQLException
*/
protected boolean shouldProcessItem(Item item) throws SQLException {
if (item == null) {
return false;
}
if (!item.isArchived()) {
return false;
}
if (itemService.canEdit(context, item)) {
return false;
}
if (!shouldProcessItemType(item)) {
return false;
}
if (!shouldProcessEntityType(item)) {
return false;
}
return true;
}
/**
* Checks if the item's entity type should be processed
*
* @param item to be checked
* @return whether the item should be processed
* @throws SQLException
*/
protected boolean shouldProcessEntityType(Item item) throws SQLException {
Entity entity = entityService.findByItemId(context, item.getID());
EntityType type = entityService.getType(context, entity);
String[] entityTypeStrings = configurationService.getArrayProperty("irus.statistics.tracker.entity-types");
List<String> entityTypes = new ArrayList<>();
if (entityTypeStrings.length != 0) {
entityTypes.addAll(Arrays.asList(entityTypeStrings));
} else {
entityTypes.add(ENTITY_TYPE_DEFAULT);
}
if (type != null && entityTypes.contains(type.getLabel())) {
return true;
}
return false;
}
/**
* Checks if the item should be excluded based on the its type
*
* @param item to be checked
* @return whether the item should be processed
*/
protected boolean shouldProcessItemType(Item item) {
String trackerTypeMetadataField = configurationService.getProperty("irus.statistics.tracker.type-field");
String[] metadataValues = configurationService.getArrayProperty("irus.statistics.tracker.type-value");
List<String> trackerTypeMetadataValues;
if (metadataValues.length > 0) {
trackerTypeMetadataValues = new ArrayList<>();
for (String metadataValue : metadataValues) {
trackerTypeMetadataValues.add(metadataValue.toLowerCase());
}
} else {
trackerTypeMetadataValues = null;
}
if (trackerTypeMetadataField != null && trackerTypeMetadataValues != null) {
// Contains the schema, element and if present qualifier of the metadataField
String[] metadataFieldSplit = trackerTypeMetadataField.split("\\.");
List<MetadataValue> types = itemService
.getMetadata(item, metadataFieldSplit[0], metadataFieldSplit[1],
metadataFieldSplit.length == 2 ? null : metadataFieldSplit[2], Item.ANY);
if (!types.isEmpty()) {
//Find out if we have a type that needs to be excluded
for (MetadataValue type : types) {
if (trackerTypeMetadataValues.contains(type.getValue().toLowerCase())) {
//We have found no type so process this item
return false;
}
}
return true;
} else {
// No types in this item, so not excluded
return true;
}
} else {
// No types to be excluded
return true;
}
}
}

View File

@@ -0,0 +1,91 @@
/**
* 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.statistics.export.processor;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.sql.SQLException;
import javax.servlet.http.HttpServletRequest;
import org.dspace.content.Item;
import org.dspace.core.Context;
import org.dspace.services.ConfigurationService;
import org.dspace.services.factory.DSpaceServicesFactory;
/**
* Processor that handles Item events from the IrusExportUsageEventListener
*/
public class ItemEventProcessor extends ExportEventProcessor {
private ConfigurationService configurationService = DSpaceServicesFactory.getInstance().getConfigurationService();
private Item item;
/**
* Creates a new ItemEventProcessor that will set the params
*
* @param context
* @param request
* @param item
*/
public ItemEventProcessor(Context context, HttpServletRequest request, Item item) {
super(context, request);
this.item = item;
}
/**
* Process the event
* Check if the item should be processed
* Create the url to be transmitted based on item data
*
* @throws SQLException
* @throws IOException
*/
public void processEvent() throws SQLException, IOException {
if (shouldProcessItem(item)) {
String baseParam = getBaseParameters(item);
String fullParam = addObjectSpecificData(baseParam, item);
processObject(fullParam);
}
}
/**
* Adds additional item data to the url
*
* @param string to which the additional data needs to be added
* @param item
* @return the string with additional data
* @throws UnsupportedEncodingException
*/
protected String addObjectSpecificData(final String string, Item item) throws UnsupportedEncodingException {
StringBuilder data = new StringBuilder(string);
String itemInfo = getItemInfo(item);
data.append("&").append(URLEncoder.encode("svc_dat", UTF_8)).append("=")
.append(URLEncoder.encode(itemInfo, UTF_8));
data.append("&").append(URLEncoder.encode("rft_dat", UTF_8)).append("=")
.append(URLEncoder.encode(ITEM_VIEW, UTF_8));
return data.toString();
}
/**
* Get Item info used for the url
*
* @param item
* @return item info
*/
private String getItemInfo(final Item item) {
StringBuilder sb = new StringBuilder(configurationService.getProperty("dspace.ui.url"));
sb.append("/handle/").append(item.getHandle());
return sb.toString();
}
}

View File

@@ -0,0 +1,44 @@
/**
* 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.statistics.export.service;
import java.sql.SQLException;
import java.util.List;
import org.dspace.core.Context;
import org.dspace.statistics.export.OpenURLTracker;
/**
* Interface of the service that handles the OpenURLTracker database operations
*/
public interface FailedOpenURLTrackerService {
/**
* Removes an OpenURLTracker from the database
* @param context
* @param openURLTracker
* @throws SQLException
*/
void remove(Context context, OpenURLTracker openURLTracker) throws SQLException;
/**
* Returns all OpenURLTrackers from the database
* @param context
* @return all OpenURLTrackers
* @throws SQLException
*/
List<OpenURLTracker> findAll(Context context) throws SQLException;
/**
* Creates a new OpenURLTracker
* @param context
* @return the creatred OpenURLTracker
* @throws SQLException
*/
OpenURLTracker create(Context context) throws SQLException;
}

View File

@@ -0,0 +1,44 @@
/**
* 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.statistics.export.service;
import java.io.IOException;
import java.sql.SQLException;
import org.dspace.core.Context;
/**
* The Service responsible for processing urls
*/
public interface OpenUrlService {
/**
* Process the url
* @param c - the context
* @param urlStr - the url to be processed
* @throws IOException
* @throws SQLException
*/
void processUrl(Context c, String urlStr) throws SQLException;
/**
* Will process all urls stored in the database and try contacting them again
* @param context
* @throws SQLException
*/
void reprocessFailedQueue(Context context) throws SQLException;
/**
* Will log the failed url in the database
* @param context
* @param url
* @throws SQLException
*/
void logfailed(Context context, String url) throws SQLException;
}

View File

@@ -0,0 +1,139 @@
/**
* 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.statistics.export.service;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLConnection;
import java.sql.SQLException;
import java.util.Date;
import java.util.List;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
import org.dspace.core.Context;
import org.dspace.statistics.export.OpenURLTracker;
import org.springframework.beans.factory.annotation.Autowired;
/**
* Implementation of the OpenUrlService interface
*/
public class OpenUrlServiceImpl implements OpenUrlService {
private Logger log = Logger.getLogger(OpenUrlService.class);
@Autowired
protected FailedOpenURLTrackerService failedOpenUrlTrackerService;
/**
* Processes the url
* When the contacting the url fails, the url will be logged in a db table
* @param c - the context
* @param urlStr - the url to be processed
* @throws SQLException
*/
public void processUrl(Context c, String urlStr) throws SQLException {
log.debug("Prepared to send url to tracker URL: " + urlStr);
try {
int responseCode = getResponseCodeFromUrl(urlStr);
if (responseCode != HttpURLConnection.HTTP_OK) {
logfailed(c, urlStr);
} else if (log.isDebugEnabled()) {
log.debug("Successfully posted " + urlStr + " on " + new Date());
}
} catch (Exception e) {
log.error("Failed to send url to tracker URL: " + urlStr);
logfailed(c, urlStr);
}
}
/**
* Returns the response code from accessing the url
* @param urlStr
* @return response code from the url
* @throws IOException
*/
protected int getResponseCodeFromUrl(final String urlStr) throws IOException {
URLConnection conn;
URL url = new URL(urlStr);
conn = url.openConnection();
HttpURLConnection httpURLConnection = (HttpURLConnection) conn;
int responseCode = httpURLConnection.getResponseCode();
httpURLConnection.disconnect();
return responseCode;
}
/**
* Retry to send a failed url
* @param context
* @param tracker - db object containing the failed url
* @throws SQLException
*/
protected void tryReprocessFailed(Context context, OpenURLTracker tracker) throws SQLException {
boolean success = false;
try {
int responseCode = getResponseCodeFromUrl(tracker.getUrl());
if (responseCode == HttpURLConnection.HTTP_OK) {
success = true;
}
} catch (Exception e) {
success = false;
} finally {
if (success) {
failedOpenUrlTrackerService
.remove(context, tracker);
// If the tracker was able to post successfully, we remove it from the database
log.info("Successfully posted " + tracker.getUrl() + " from " + tracker.getUploadDate());
} else {
// Still no luck - write an error msg but keep the entry in the table for future executions
log.error("Failed attempt from " + tracker.getUrl() + " originating from " + tracker.getUploadDate());
}
}
}
/**
* Reprocess all url trackers present in the database
* @param context
* @throws SQLException
*/
public void reprocessFailedQueue(Context context) throws SQLException {
if (failedOpenUrlTrackerService == null) {
log.error("Error retrieving the \"failedOpenUrlTrackerService\" instance, aborting the processing");
return;
}
List<OpenURLTracker> openURLTrackers = failedOpenUrlTrackerService.findAll(context);
for (OpenURLTracker openURLTracker : openURLTrackers) {
tryReprocessFailed(context, openURLTracker);
}
}
/**
* Log a failed url in the database
* @param context
* @param url
* @throws SQLException
*/
public void logfailed(Context context, String url) throws SQLException {
Date now = new Date();
if (StringUtils.isBlank(url)) {
return;
}
OpenURLTracker tracker = failedOpenUrlTrackerService.create(context);
tracker.setUploadDate(now);
tracker.setUrl(url);
}
}

View File

@@ -10,8 +10,13 @@ package org.dspace.storage.rdbms;
import java.sql.Connection;
import org.apache.logging.log4j.Logger;
import org.dspace.authorize.service.AuthorizeService;
import org.dspace.content.Site;
import org.dspace.content.service.SiteService;
import org.dspace.core.Constants;
import org.dspace.core.Context;
import org.dspace.eperson.Group;
import org.dspace.eperson.service.GroupService;
import org.flywaydb.core.api.MigrationInfo;
import org.flywaydb.core.api.callback.FlywayCallback;
import org.springframework.beans.factory.annotation.Autowired;
@@ -29,6 +34,12 @@ public class SiteServiceInitializer implements FlywayCallback {
@Autowired(required = true)
protected SiteService siteService;
@Autowired
private AuthorizeService authorizeService;
@Autowired
private GroupService groupService;
public void initializeSiteObject() {
// After every migrate, ensure default Site is setup correctly.
Context context = null;
@@ -37,10 +48,19 @@ public class SiteServiceInitializer implements FlywayCallback {
context.turnOffAuthorisationSystem();
// While it's not really a formal "registry", we need to ensure the
// default, required Groups exist in the DSpace database
Site site = null;
if (siteService.findSite(context) == null) {
siteService.createSite(context);
site = siteService.createSite(context);
}
context.restoreAuthSystemState();
if (!authorizeService.authorizeActionBoolean(context, site, Constants.READ)) {
context.turnOffAuthorisationSystem();
Group anonGroup = groupService.findByName(context, Group.ANONYMOUS);
if (anonGroup != null) {
authorizeService.addPolicy(context, site, Constants.READ, anonGroup);
}
context.restoreAuthSystemState();
}
// Commit changes and close context
context.complete();
} catch (Exception e) {

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/
--
-- ===============================================================
-- WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING
--
-- DO NOT MANUALLY RUN THIS DATABASE MIGRATION. IT WILL BE EXECUTED
-- AUTOMATICALLY (IF NEEDED) BY "FLYWAY" WHEN YOU STARTUP DSPACE.
-- http://flywaydb.org/
-- ===============================================================
-------------------------------------------------------------
-- This will create the setup for the IRUS statistics harvester
-------------------------------------------------------------
CREATE SEQUENCE openurltracker_seq;
CREATE TABLE openurltracker
(
tracker_id INTEGER,
tracker_url VARCHAR(1000),
uploaddate DATE,
CONSTRAINT openurltracker_PK PRIMARY KEY (tracker_id)
);

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/
--
-- ===============================================================
-- WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING
--
-- DO NOT MANUALLY RUN THIS DATABASE MIGRATION. IT WILL BE EXECUTED
-- AUTOMATICALLY (IF NEEDED) BY "FLYWAY" WHEN YOU STARTUP DSPACE.
-- http://flywaydb.org/
-- ===============================================================
-------------------------------------------------------------
-- This will create the setup for the IRUS statistics harvester
-------------------------------------------------------------
CREATE SEQUENCE openurltracker_seq;
CREATE TABLE openurltracker
(
tracker_id NUMBER,
tracker_url VARCHAR2(1000),
uploaddate DATE,
CONSTRAINT openurltracker_PK PRIMARY KEY (tracker_id)
);

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/
--
-- ===============================================================
-- WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING
--
-- DO NOT MANUALLY RUN THIS DATABASE MIGRATION. IT WILL BE EXECUTED
-- AUTOMATICALLY (IF NEEDED) BY "FLYWAY" WHEN YOU STARTUP DSPACE.
-- http://flywaydb.org/
-- ===============================================================
-------------------------------------------------------------
-- This will create the setup for the IRUS statistics harvester
-------------------------------------------------------------
CREATE SEQUENCE openurltracker_seq;
CREATE TABLE openurltracker
(
tracker_id INTEGER,
tracker_url VARCHAR(1000),
uploaddate DATE,
CONSTRAINT openurltracker_PK PRIMARY KEY (tracker_id)
);

View File

@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<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-4.3.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.3.xsd">
<!-- Irus statistics tracking -->
<bean class="org.dspace.statistics.export.IrusExportUsageEventListener">
<property name="eventService" ref="org.dspace.services.EventService"/>
</bean>
</beans>

View File

@@ -0,0 +1,13 @@
<?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"
default-lazy-init="true">
<bean class="org.dspace.statistics.export.FailedOpenURLTrackerServiceImpl"/>
<bean class="org.dspace.statistics.export.service.MockOpenUrlServiceImpl"
id="org.dspace.statistics.export.service.OpenUrlService"/>
<bean id="testProcessedUrls" class="java.util.ArrayList"/>
</beans>

View File

@@ -27,9 +27,19 @@
<property name="dspaceRunnableClass" value="org.dspace.curate.CurationCli"/>
</bean>
<bean id="retry-tracker" class="org.dspace.statistics.export.RetryFailedOpenUrlTrackerScriptConfiguration" scope="prototype">
<property name="description" value="Retry all failed commits to the OpenURLTracker"/>
<property name="dspaceRunnableClass" value="org.dspace.statistics.export.RetryFailedOpenUrlTracker"/>
</bean>
<bean id="another-mock-script" class="org.dspace.scripts.MockDSpaceRunnableScriptConfiguration" scope="prototype">
<property name="description" value="Mocking a script for testing purposes" />
<property name="dspaceRunnableClass" value="org.dspace.scripts.impl.MockDSpaceRunnableScript"/>
</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" />
<property name="dspaceRunnableClass" value="org.dspace.scripts.impl.MockDSpaceRunnableScript"/>
</bean>
</beans>

View File

@@ -90,6 +90,10 @@ public class ItemBuilder extends AbstractDSpaceObjectBuilder<Item> {
return addMetadataValue(item, "relationship", "type", null, relationshipType);
}
public ItemBuilder withType(final String type) {
return addMetadataValue(item, "dc", "type", null, type);
}
public ItemBuilder withPublicationIssueNumber(final String issueNumber) {
return addMetadataValue(item, "publicationissue", "issueNumber", null, issueNumber);
}

View File

@@ -9,6 +9,8 @@ package org.dspace.builder;
import java.io.IOException;
import java.sql.SQLException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.List;
import org.dspace.authorize.AuthorizeException;
@@ -43,6 +45,18 @@ public class ProcessBuilder extends AbstractBuilder<Process, ProcessService> {
return this;
}
public ProcessBuilder withProcessStatus(ProcessStatus processStatus) {
process.setProcessStatus(processStatus);
return this;
}
public ProcessBuilder withStartAndEndTime(String startTime, String endTime) throws ParseException {
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("dd/MM/yyyy");
process.setStartTime(simpleDateFormat.parse(startTime));
process.setFinishedTime(simpleDateFormat.parse(endTime));
return this;
}
@Override
public void cleanup() throws Exception {
try (Context c = new Context()) {

View File

@@ -1013,7 +1013,8 @@ public class CommunityTest extends AbstractDSpaceObjectTest {
equalTo(c));
assertThat("testGetAdminObject 1", (Community) communityService.getAdminObject(context, c, Constants.ADD),
equalTo(c));
assertThat("testGetAdminObject 2", communityService.getAdminObject(context, c, Constants.DELETE), nullValue());
assertThat("testGetAdminObject 2", (Community) communityService.getAdminObject(context, c, Constants.DELETE),
equalTo(c));
assertThat("testGetAdminObject 3", (Community) communityService.getAdminObject(context, c, Constants.ADMIN),
equalTo(c));
}

View File

@@ -1598,8 +1598,8 @@ public class ItemTest extends AbstractDSpaceObjectTest {
assertThat("testGetAdminObject 0", (Item) itemService.getAdminObject(context, it, Constants.REMOVE),
equalTo(it));
assertThat("testGetAdminObject 1", (Item) itemService.getAdminObject(context, it, Constants.ADD), equalTo(it));
assertThat("testGetAdminObject 2", (Collection) itemService.getAdminObject(context, it, Constants.DELETE),
equalTo(collection));
assertThat("testGetAdminObject 2", (Item) itemService.getAdminObject(context, it, Constants.DELETE),
equalTo(it));
assertThat("testGetAdminObject 3", (Item) itemService.getAdminObject(context, it, Constants.ADMIN),
equalTo(it));
}

View File

@@ -14,13 +14,16 @@ import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.sql.SQLException;
import java.util.List;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.Logger;
import org.dspace.AbstractUnitTest;
import org.dspace.content.factory.ContentServiceFactory;
import org.dspace.content.service.SiteService;
import org.dspace.core.ConfigurationManager;
import org.dspace.core.Constants;
import org.dspace.eperson.Group;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
@@ -143,4 +146,17 @@ public class SiteTest extends AbstractUnitTest {
assertThat("testGetURL 0", s.getURL(), equalTo(ConfigurationManager.getProperty("dspace.ui.url")));
}
@Test
public void testAnonymousReadRights() throws Exception {
List<Group> groupList = authorizeService.getAuthorizedGroups(context, s, Constants.READ);
boolean foundAnonInList = false;
for (Group group : groupList) {
if (StringUtils.equalsIgnoreCase(group.getName(), "Anonymous")) {
foundAnonInList = true;
}
}
assertTrue(foundAnonInList);
}
}

View File

@@ -0,0 +1,85 @@
/**
* 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.statistics.export;
import static org.junit.Assert.assertEquals;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.when;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import org.dspace.core.Context;
import org.dspace.statistics.export.dao.OpenURLTrackerDAO;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.runners.MockitoJUnitRunner;
/**
* Class to test the FailedOpenURLTrackerServiceImpl
*/
@RunWith(MockitoJUnitRunner.class)
public class FailedOpenURLTrackerServiceImplTest {
@InjectMocks
private FailedOpenURLTrackerServiceImpl openURLTrackerLoggerService;
@Mock
private Context context;
@Mock
private OpenURLTracker openURLTracker;
@Mock
private OpenURLTrackerDAO openURLTrackerDAO;
/**
* Tests the remove method
* @throws SQLException
*/
@Test
public void testRemove() throws SQLException {
openURLTrackerLoggerService.remove(context, openURLTracker);
Mockito.verify(openURLTrackerDAO, times(1)).delete(context, openURLTracker);
}
/**
* Tests the findAll method
* @throws SQLException
*/
@Test
public void testFindAll() throws SQLException {
List<OpenURLTracker> trackers = new ArrayList<>();
when(openURLTrackerDAO.findAll(context, OpenURLTracker.class)).thenReturn(trackers);
assertEquals("TestFindAll 0", trackers, openURLTrackerLoggerService.findAll(context));
}
/**
* Tests the create method
* @throws SQLException
*/
@Test
public void testCreate() throws SQLException {
OpenURLTracker tracker = new OpenURLTracker();
when(openURLTrackerDAO.create(any(), any())).thenReturn(tracker);
assertEquals("TestCreate 0", tracker, openURLTrackerLoggerService.create(context));
}
}

View File

@@ -0,0 +1,418 @@
/**
* 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.statistics.export;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.mockito.Matchers.anyString;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import java.io.File;
import java.io.FileInputStream;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Pattern;
import javax.servlet.http.HttpServletRequest;
import org.apache.commons.codec.CharEncoding;
import org.apache.log4j.Logger;
import org.dspace.AbstractIntegrationTestWithDatabase;
import org.dspace.authorize.AuthorizeException;
import org.dspace.builder.BitstreamBuilder;
import org.dspace.builder.CollectionBuilder;
import org.dspace.builder.CommunityBuilder;
import org.dspace.builder.EntityTypeBuilder;
import org.dspace.builder.ItemBuilder;
import org.dspace.content.Bitstream;
import org.dspace.content.Collection;
import org.dspace.content.Community;
import org.dspace.content.EntityType;
import org.dspace.content.Item;
import org.dspace.content.factory.ContentServiceFactory;
import org.dspace.content.service.BitstreamService;
import org.dspace.content.service.BundleService;
import org.dspace.content.service.CollectionService;
import org.dspace.content.service.CommunityService;
import org.dspace.content.service.EntityTypeService;
import org.dspace.content.service.InstallItemService;
import org.dspace.content.service.ItemService;
import org.dspace.content.service.WorkspaceItemService;
import org.dspace.core.Context;
import org.dspace.eperson.factory.EPersonServiceFactory;
import org.dspace.eperson.service.EPersonService;
import org.dspace.eperson.service.GroupService;
import org.dspace.services.ConfigurationService;
import org.dspace.services.factory.DSpaceServicesFactory;
import org.dspace.statistics.export.factory.OpenURLTrackerLoggerServiceFactory;
import org.dspace.statistics.export.service.FailedOpenURLTrackerService;
import org.dspace.usage.UsageEvent;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
/**
* Test class for the IrusExportUsageEventListener
*/
//@RunWith(MockitoJUnitRunner.class)
public class ITIrusExportUsageEventListener extends AbstractIntegrationTestWithDatabase {
private static Logger log = Logger.getLogger(ITIrusExportUsageEventListener.class);
protected CommunityService communityService = ContentServiceFactory.getInstance().getCommunityService();
protected ConfigurationService configurationService = DSpaceServicesFactory.getInstance().getConfigurationService();
protected CollectionService collectionService = ContentServiceFactory.getInstance().getCollectionService();
protected ItemService itemService = ContentServiceFactory.getInstance().getItemService();
protected InstallItemService installItemService = ContentServiceFactory.getInstance().getInstallItemService();
protected WorkspaceItemService workspaceItemService = ContentServiceFactory.getInstance().getWorkspaceItemService();
protected EPersonService ePersonService = EPersonServiceFactory.getInstance().getEPersonService();
protected GroupService groupService = EPersonServiceFactory.getInstance().getGroupService();
protected BundleService bundleService = ContentServiceFactory.getInstance().getBundleService();
protected BitstreamService bitstreamService = ContentServiceFactory.getInstance().getBitstreamService();
protected EntityTypeService entityTypeService = ContentServiceFactory.getInstance().getEntityTypeService();
protected FailedOpenURLTrackerService failedOpenURLTrackerService =
OpenURLTrackerLoggerServiceFactory.getInstance().getOpenUrlTrackerLoggerService();
protected ArrayList testProcessedUrls = DSpaceServicesFactory.getInstance().getServiceManager()
.getServiceByName("testProcessedUrls",
ArrayList.class);
private IrusExportUsageEventListener exportUsageEventListener =
DSpaceServicesFactory.getInstance()
.getServiceManager()
.getServicesByType(IrusExportUsageEventListener.class)
.get(0);
private Item item;
private Item itemNotToBeProcessed;
private Bitstream bitstream;
private Bitstream bitstreamNotToBeProcessed;
private EntityType entityType;
private Community community;
private Collection collection;
private String encodedUrl;
private String encodedUIUrl;
/**
* Initializes the test by setting up all objects needed to create a test item
*/
@Before()
public void setUp() throws Exception {
super.setUp();
configurationService.setProperty("irus.statistics.tracker.enabled", true);
configurationService.setProperty("irus.statistics.tracker.type-field", "dc.type");
configurationService.setProperty("irus.statistics.tracker.type-value", "Excluded type");
context.turnOffAuthorisationSystem();
try {
entityType = EntityTypeBuilder.createEntityTypeBuilder(context, "Publication").build();
community = CommunityBuilder.createCommunity(context).build();
collection = CollectionBuilder.createCollection(context, community).build();
item = ItemBuilder.createItem(context, collection)
.withRelationshipType(entityType.getLabel())
.build();
File f = new File(testProps.get("test.bitstream").toString());
bitstream = BitstreamBuilder.createBitstream(context, item, new FileInputStream(f)).build();
itemNotToBeProcessed = ItemBuilder.createItem(context, collection)
.withRelationshipType(entityType.getLabel())
.withType("Excluded type")
.build();
File itemNotToBeProcessedFile = new File(testProps.get("test.bitstream").toString());
bitstreamNotToBeProcessed = BitstreamBuilder
.createBitstream(context, itemNotToBeProcessed, new FileInputStream(itemNotToBeProcessedFile))
.build();
String dspaceUrl = configurationService.getProperty("dspace.server.url");
encodedUrl = URLEncoder.encode(dspaceUrl, CharEncoding.UTF_8);
String dspaceUIUrl = configurationService.getProperty("dspace.ui.url");
encodedUIUrl = URLEncoder.encode(dspaceUIUrl, CharEncoding.UTF_8);
} catch (Exception e) {
log.error(e.getMessage(), e);
} finally {
context.restoreAuthSystemState();
}
}
/**
* Clean up the created objects
* Empty the testProcessedUrls used to store succeeded urls
* Empty the database table where the failed urls are logged
*/
@After
public void destroy() throws Exception {
try {
context.turnOffAuthorisationSystem();
List<OpenURLTracker> all = failedOpenURLTrackerService.findAll(context);
for (OpenURLTracker tracker : all) {
failedOpenURLTrackerService.remove(context, tracker);
}
// Clear the list of processedUrls
testProcessedUrls.clear();
} catch (Exception e) {
log.error(e.getMessage(), e);
} finally {
try {
context.complete();
} catch (SQLException e) {
log.error(e);
}
}
super.destroy();
}
/**
* Test whether the usage event of an item meeting all conditions is processed and succeeds
*/
@Test
public void testReceiveEventOnItemThatShouldBeProcessed() throws UnsupportedEncodingException, SQLException {
HttpServletRequest request = mock(HttpServletRequest.class);
when(request.getRemoteAddr()).thenReturn("client-ip");
when(request.getHeader(anyString())).thenReturn(null);
UsageEvent usageEvent = mock(UsageEvent.class);
when(usageEvent.getObject()).thenReturn(item);
when(usageEvent.getRequest()).thenReturn(request);
when(usageEvent.getContext()).thenReturn(new Context());
exportUsageEventListener.receiveEvent(usageEvent);
List<OpenURLTracker> all = failedOpenURLTrackerService.findAll(context);
String regex = "https://irus.jisc.ac.uk/counter/test/\\?url_ver=Z39.88-2004&req_id=" +
URLEncoder.encode(request.getRemoteAddr(), "UTF-8") + "&req_dat=&rft" +
".artnum=oai%3Alocalhost%3A" + URLEncoder.encode(item.getHandle(), "UTF-8") + "&rfr_dat=&rfr_id" +
"=localhost&url_tim=" + ".*" + "?&svc_dat=" + encodedUIUrl + "%2Fhandle%2F" + URLEncoder
.encode(item.getHandle(), "UTF-8") + "&rft_dat=Investigation";
boolean isMatch = matchesString(String.valueOf(testProcessedUrls.get(0)), regex);
assertEquals(1, testProcessedUrls.size());
assertTrue(isMatch);
assertEquals(0, all.size());
}
/**
* Test whether the usage event of an item meeting all conditions is processed but fails
*/
@Test
public void testReceiveEventOnItemThatShouldBeProcessedFailed() throws SQLException, UnsupportedEncodingException {
HttpServletRequest request = mock(HttpServletRequest.class);
when(request.getRemoteAddr()).thenReturn("client-ip-fail");
when(request.getHeader(anyString())).thenReturn(null);
UsageEvent usageEvent = mock(UsageEvent.class);
when(usageEvent.getObject()).thenReturn(item);
when(usageEvent.getRequest()).thenReturn(request);
when(usageEvent.getContext()).thenReturn(new Context());
exportUsageEventListener.receiveEvent(usageEvent);
List<OpenURLTracker> all = failedOpenURLTrackerService.findAll(context);
String regex = "https://irus.jisc.ac.uk/counter/test/\\?url_ver=Z39.88-2004&req_id=" +
URLEncoder.encode(request.getRemoteAddr(), "UTF-8") + "&req_dat=&rft" +
".artnum=oai%3Alocalhost%3A" + URLEncoder.encode(item.getHandle(), "UTF-8") + "&rfr_dat=&rfr_id" +
"=localhost&url_tim=" + ".*" + "?&svc_dat=" + encodedUIUrl + "%2Fhandle%2F" + URLEncoder
.encode(item.getHandle(), "UTF-8") + "&rft_dat=Investigation";
boolean isMatch = matchesString(all.get(0).getUrl(), regex);
assertEquals(0, testProcessedUrls.size());
assertEquals(1, all.size());
assertTrue(isMatch);
}
/**
* Test whether the usage event of an item that does not meet all conditions is not processed
*/
@Test
public void testReceiveEventOnItemThatShouldNotBeProcessed() throws SQLException, AuthorizeException {
context.turnOffAuthorisationSystem();
HttpServletRequest request = mock(HttpServletRequest.class);
UsageEvent usageEvent = mock(UsageEvent.class);
when(usageEvent.getObject()).thenReturn(itemNotToBeProcessed);
when(usageEvent.getRequest()).thenReturn(request);
when(usageEvent.getContext()).thenReturn(new Context());
itemService.clearMetadata(context, item, "relationship", "type", null, Item.ANY);
itemService.addMetadata(context, item, "relationship", "type", null, null, "OrgUnit");
itemService.update(context, item);
context.restoreAuthSystemState();
// doCallRealMethod().when(IrusExportUsageEventListener).receiveEvent(usageEvent);
exportUsageEventListener.receiveEvent(usageEvent);
List<OpenURLTracker> all = failedOpenURLTrackerService.findAll(context);
assertEquals(0, testProcessedUrls.size());
assertEquals(0, all.size());
}
/**
* Test whether the usage event of a bitstream meeting all conditions is processed and succeeds
*/
@Test
public void testReceiveEventOnBitstreamThatShouldBeProcessed() throws SQLException, UnsupportedEncodingException {
HttpServletRequest request = mock(HttpServletRequest.class);
when(request.getRemoteAddr()).thenReturn("client-ip");
when(request.getHeader(anyString())).thenReturn(null);
UsageEvent usageEvent = mock(UsageEvent.class);
when(usageEvent.getObject()).thenReturn(bitstream);
when(usageEvent.getRequest()).thenReturn(request);
when(usageEvent.getContext()).thenReturn(new Context());
exportUsageEventListener.receiveEvent(usageEvent);
String regex = "https://irus.jisc.ac.uk/counter/test/\\?url_ver=Z39.88-2004&req_id=" +
URLEncoder.encode(request.getRemoteAddr(), "UTF-8") + "&req_dat=&rft" +
".artnum=oai%3Alocalhost%3A" + URLEncoder.encode(item.getHandle(), "UTF-8") + "&rfr_dat=&rfr_id" +
"=localhost&url_tim=" + ".*" + "?&svc_dat=" + encodedUrl + "%2Fapi%2Fcore%2Fbitstreams" +
"%2F" + bitstream.getID() + "%2Fcontent" + "&rft_dat=Request";
boolean isMatch = matchesString(String.valueOf(testProcessedUrls.get(0)), regex);
assertEquals(1, testProcessedUrls.size());
assertTrue(isMatch);
List<OpenURLTracker> all = failedOpenURLTrackerService.findAll(context);
assertEquals(0, all.size());
}
/**
* Test whether the usage event of a bitstream meeting all conditions is processed but fails
*/
@Test
public void testReceiveEventOnBitstreamThatShouldBeProcessedFail() throws UnsupportedEncodingException,
SQLException {
HttpServletRequest request = mock(HttpServletRequest.class);
when(request.getRemoteAddr()).thenReturn("client-ip-fail");
when(request.getHeader(anyString())).thenReturn(null);
UsageEvent usageEvent = mock(UsageEvent.class);
when(usageEvent.getObject()).thenReturn(bitstream);
when(usageEvent.getRequest()).thenReturn(request);
when(usageEvent.getContext()).thenReturn(new Context());
exportUsageEventListener.receiveEvent(usageEvent);
List<OpenURLTracker> all = failedOpenURLTrackerService.findAll(context);
String regex = "https://irus.jisc.ac.uk/counter/test/\\?url_ver=Z39.88-2004&req_id=" +
URLEncoder.encode(request.getRemoteAddr(), "UTF-8") + "&req_dat=&rft" +
".artnum=oai%3Alocalhost%3A" + URLEncoder.encode(item.getHandle(), "UTF-8") + "&rfr_dat=&rfr_id" +
"=localhost&url_tim=" + ".*" + "?&svc_dat=" + encodedUrl + "%2Fapi%2Fcore%2Fbitstreams" +
"%2F" + bitstream.getID() + "%2Fcontent" + "&rft_dat=Request";
boolean isMatch = matchesString(all.get(0).getUrl(), regex);
assertEquals(1, all.size());
assertEquals(true, isMatch);
assertEquals(0, testProcessedUrls.size());
}
/**
* Test whether the usage event of a bitstream that does not meet all conditions is not processed
*/
@Test
public void testReceiveEventOnBitstreamThatShouldNotBeProcessed() throws SQLException, AuthorizeException {
context.turnOffAuthorisationSystem();
HttpServletRequest request = mock(HttpServletRequest.class);
when(request.getRemoteAddr()).thenReturn("client-ip-fail");
when(request.getHeader(anyString())).thenReturn(null);
UsageEvent usageEvent = mock(UsageEvent.class);
when(usageEvent.getObject()).thenReturn(bitstreamNotToBeProcessed);
when(usageEvent.getRequest()).thenReturn(request);
when(usageEvent.getContext()).thenReturn(new Context());
itemService.clearMetadata(context, item, "relationship", "type", null, Item.ANY);
itemService.addMetadata(context, item, "relationship", "type", null, null, "OrgUnit");
itemService.update(context, item);
context.restoreAuthSystemState();
exportUsageEventListener.receiveEvent(usageEvent);
List<OpenURLTracker> all = failedOpenURLTrackerService.findAll(context);
assertEquals(0, all.size());
assertEquals(0, testProcessedUrls.size());
}
/**
* Test that an object that is not an Item or Bitstream is not processed
*/
@Test
public void testReceiveEventOnNonRelevantObject() throws SQLException {
HttpServletRequest request = mock(HttpServletRequest.class);
UsageEvent usageEvent = mock(UsageEvent.class);
when(usageEvent.getObject()).thenReturn(community);
when(usageEvent.getContext()).thenReturn(new Context());
exportUsageEventListener.receiveEvent(usageEvent);
List<OpenURLTracker> all = failedOpenURLTrackerService.findAll(context);
assertEquals(0, all.size());
assertEquals(0, testProcessedUrls.size());
}
/**
* Method to test if a string matches a regex
*
* @param string
* @param regex
* @return whether the regex matches the string
*/
private boolean matchesString(String string, String regex) {
Pattern p = Pattern.compile(regex);
if (p.matcher(string).matches()) {
return true;
}
return false;
}
}

View File

@@ -0,0 +1,182 @@
/**
* 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.statistics.export;
import static org.junit.Assert.assertEquals;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import org.apache.log4j.Logger;
import org.dspace.AbstractIntegrationTest;
import org.dspace.app.scripts.handler.impl.TestDSpaceRunnableHandler;
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.dspace.services.factory.DSpaceServicesFactory;
import org.dspace.statistics.export.factory.OpenURLTrackerLoggerServiceFactory;
import org.dspace.statistics.export.service.FailedOpenURLTrackerService;
import org.junit.After;
import org.junit.Test;
/**
* Class to test the RetryFailedOpenUrlTracker
*/
public class ITRetryFailedOpenUrlTracker extends AbstractIntegrationTest {
private static Logger log = Logger.getLogger(ITRetryFailedOpenUrlTracker.class);
protected FailedOpenURLTrackerService failedOpenURLTrackerService =
OpenURLTrackerLoggerServiceFactory.getInstance().getOpenUrlTrackerLoggerService();
protected ArrayList testProcessedUrls = DSpaceServicesFactory.getInstance().getServiceManager()
.getServiceByName("testProcessedUrls",
ArrayList.class);
private ScriptService scriptService = ScriptServiceFactory.getInstance().getScriptService();
/**
* Clean up the logged entries from the db after each test
*/
@After
@Override
public void destroy() {
try {
context.turnOffAuthorisationSystem();
List<OpenURLTracker> all = failedOpenURLTrackerService.findAll(context);
for (OpenURLTracker tracker : all) {
failedOpenURLTrackerService.remove(context, tracker);
}
// Clear the list of processedUrls
testProcessedUrls.clear();
} catch (Exception e) {
log.error(e.getMessage(), e);
} finally {
try {
context.complete();
} catch (SQLException e) {
log.error(e);
}
}
super.destroy();
}
/**
* Test the mode of the script that allows the user to add a failed url to the database
*
* @throws Exception
*/
@Test
public void testAddNewFailedUrl() throws Exception {
TestDSpaceRunnableHandler testDSpaceRunnableHandler = new TestDSpaceRunnableHandler();
ScriptConfiguration retryOpenUrlTrackerConfig = scriptService.getScriptConfiguration("retry-tracker");
DSpaceRunnable retryOpenUrlTracker =
scriptService.createDSpaceRunnableForScriptConfiguration(retryOpenUrlTrackerConfig);
String urlToAdd = "test-failed-url";
String[] args = {"-a", urlToAdd};
retryOpenUrlTracker.initialize(args, testDSpaceRunnableHandler, eperson);
retryOpenUrlTracker.internalRun();
List<OpenURLTracker> all = failedOpenURLTrackerService.findAll(context);
assertEquals(0, testProcessedUrls.size());
assertEquals(1, all.size());
assertEquals(urlToAdd, all.get(0).getUrl());
}
/**
* Test to check that all logged failed urls are reprocessed succesfully and removed from the db
*
* @throws Exception
*/
@Test
public void testReprocessAllUrls() throws Exception {
TestDSpaceRunnableHandler testDSpaceRunnableHandler = new TestDSpaceRunnableHandler();
ScriptConfiguration retryOpenUrlTrackerConfig = scriptService.getScriptConfiguration("retry-tracker");
DSpaceRunnable retryOpenUrlTracker =
scriptService.createDSpaceRunnableForScriptConfiguration(retryOpenUrlTrackerConfig);
String[] args = {"-r"};
OpenURLTracker tracker1 = failedOpenURLTrackerService.create(context);
tracker1.setUrl("test-url-1");
OpenURLTracker tracker2 = failedOpenURLTrackerService.create(context);
tracker2.setUrl("test-url-2");
OpenURLTracker tracker3 = failedOpenURLTrackerService.create(context);
tracker3.setUrl("test-url-3");
retryOpenUrlTracker.initialize(args, testDSpaceRunnableHandler, eperson);
retryOpenUrlTracker.internalRun();
List<OpenURLTracker> all = failedOpenURLTrackerService.findAll(context);
assertEquals(3, testProcessedUrls.size());
assertEquals(true, testProcessedUrls.contains("test-url-1"));
assertEquals(true, testProcessedUrls.contains("test-url-2"));
assertEquals(true, testProcessedUrls.contains("test-url-3"));
assertEquals(0, all.size());
}
/**
* Test to check that the successful retries are removed, but the failed retries remain in the db
*
* @throws Exception
*/
@Test
public void testReprocessPartOfUrls() throws Exception {
TestDSpaceRunnableHandler testDSpaceRunnableHandler = new TestDSpaceRunnableHandler();
ScriptConfiguration retryOpenUrlTrackerConfig = scriptService.getScriptConfiguration("retry-tracker");
DSpaceRunnable retryOpenUrlTracker =
scriptService.createDSpaceRunnableForScriptConfiguration(retryOpenUrlTrackerConfig);
String[] args = {"-r"};
OpenURLTracker tracker1 = failedOpenURLTrackerService.create(context);
tracker1.setUrl("test-url-1");
OpenURLTracker tracker2 = failedOpenURLTrackerService.create(context);
tracker2.setUrl("test-url-2-fail");
OpenURLTracker tracker3 = failedOpenURLTrackerService.create(context);
tracker3.setUrl("test-url-3-fail");
OpenURLTracker tracker4 = failedOpenURLTrackerService.create(context);
tracker4.setUrl("test-url-4-fail");
OpenURLTracker tracker5 = failedOpenURLTrackerService.create(context);
tracker5.setUrl("test-url-5");
retryOpenUrlTracker.initialize(args, testDSpaceRunnableHandler, eperson);
retryOpenUrlTracker.internalRun();
List<OpenURLTracker> all = failedOpenURLTrackerService.findAll(context);
List<String> storedTrackerUrls = new ArrayList<>();
for (OpenURLTracker tracker : all) {
storedTrackerUrls.add(tracker.getUrl());
}
assertEquals(2, testProcessedUrls.size());
assertEquals(true, testProcessedUrls.contains("test-url-1"));
assertEquals(true, testProcessedUrls.contains("test-url-5"));
assertEquals(3, all.size());
assertEquals(true, storedTrackerUrls.contains("test-url-2-fail"));
assertEquals(true, storedTrackerUrls.contains("test-url-3-fail"));
assertEquals(true, storedTrackerUrls.contains("test-url-4-fail"));
}
}

View File

@@ -0,0 +1,87 @@
/**
* 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.statistics.export.processor;
import static org.hamcrest.core.Is.is;
import static org.junit.Assert.assertThat;
import static org.mockito.Mockito.mock;
import java.io.File;
import java.io.FileInputStream;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import javax.servlet.http.HttpServletRequest;
import org.apache.commons.codec.CharEncoding;
import org.dspace.AbstractIntegrationTestWithDatabase;
import org.dspace.builder.BitstreamBuilder;
import org.dspace.builder.CollectionBuilder;
import org.dspace.builder.CommunityBuilder;
import org.dspace.builder.ItemBuilder;
import org.dspace.content.Bitstream;
import org.dspace.content.Collection;
import org.dspace.content.Community;
import org.dspace.content.Item;
import org.dspace.services.ConfigurationService;
import org.dspace.services.factory.DSpaceServicesFactory;
import org.junit.Before;
import org.junit.Test;
/**
* Test class for the BitstreamEventProcessor
*/
public class BitstreamEventProcessorTest extends AbstractIntegrationTestWithDatabase {
private ConfigurationService configurationService = DSpaceServicesFactory.getInstance().getConfigurationService();
private String encodedUrl;
@Before
public void setUp() throws Exception {
super.setUp();
configurationService.setProperty("irus.statistics.tracker.enabled", true);
String dspaceUrl = configurationService.getProperty("dspace.server.url");
try {
encodedUrl = URLEncoder.encode(dspaceUrl, CharEncoding.UTF_8);
} catch (UnsupportedEncodingException e) {
throw new AssertionError("Error occurred in setup()", e);
}
}
@Test
/**
* Test the method that adds data based on the object types
*/
public void testAddObectSpecificData() throws Exception {
HttpServletRequest request = mock(HttpServletRequest.class);
context.turnOffAuthorisationSystem();
Community community = CommunityBuilder.createCommunity(context).build();
Collection collection = CollectionBuilder.createCollection(context, community).build();
Item item = ItemBuilder.createItem(context, collection).build();
File f = new File(testProps.get("test.bitstream").toString());
Bitstream bitstream = BitstreamBuilder.createBitstream(context, item, new FileInputStream(f)).build();
context.restoreAuthSystemState();
BitstreamEventProcessor bitstreamEventProcessor = new BitstreamEventProcessor(context, request, bitstream);
String result = bitstreamEventProcessor.addObjectSpecificData("existing-string", bitstream);
assertThat(result,
is("existing-string&svc_dat=" + encodedUrl + "%2Fapi%2Fcore%2Fbitstreams%2F" + bitstream.getID()
+ "%2Fcontent&rft_dat=Request"));
}
}

View File

@@ -0,0 +1,281 @@
/**
* 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.statistics.export.processor;
import static org.hamcrest.CoreMatchers.startsWith;
import static org.hamcrest.core.Is.is;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.sql.SQLException;
import javax.servlet.http.HttpServletRequest;
import org.apache.commons.codec.CharEncoding;
import org.dspace.AbstractIntegrationTestWithDatabase;
import org.dspace.builder.CollectionBuilder;
import org.dspace.builder.CommunityBuilder;
import org.dspace.builder.EntityTypeBuilder;
import org.dspace.builder.ItemBuilder;
import org.dspace.builder.WorkspaceItemBuilder;
import org.dspace.content.Collection;
import org.dspace.content.Community;
import org.dspace.content.EntityType;
import org.dspace.content.Item;
import org.dspace.content.WorkspaceItem;
import org.dspace.services.ConfigurationService;
import org.dspace.services.factory.DSpaceServicesFactory;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mock;
/**
* Test for the ExportEventProcessor class
*/
public class ExportEventProcessorTest extends AbstractIntegrationTestWithDatabase {
@Mock
private HttpServletRequest request = mock(HttpServletRequest.class);
private ConfigurationService configurationService = DSpaceServicesFactory.getInstance().getConfigurationService();
private EntityType publication;
private EntityType otherEntity;
private final String excluded_type = "Excluded type";
@Before
public void setUp() throws Exception {
super.setUp();
configurationService.setProperty("irus.statistics.tracker.urlversion", "Z39.88-2004");
configurationService.setProperty("irus.statistics.tracker.enabled", true);
configurationService.setProperty("irus.statistics.tracker.type-field", "dc.type");
configurationService.setProperty("irus.statistics.tracker.type-value", "Excluded type");
context.turnOffAuthorisationSystem();
publication = EntityTypeBuilder.createEntityTypeBuilder(context, "Publication").build();
otherEntity = EntityTypeBuilder.createEntityTypeBuilder(context, "Other").build();
context.restoreAuthSystemState();
}
@Test
/**
* Test the getBaseParameters method
*/
public void testGetBaseParameters() throws UnsupportedEncodingException {
context.turnOffAuthorisationSystem();
Community community = CommunityBuilder.createCommunity(context).build();
Collection collection = CollectionBuilder.createCollection(context, community).build();
Item item = ItemBuilder.createItem(context, collection).build();
String encodedHandle = URLEncoder.encode(item.getHandle(), CharEncoding.UTF_8);
context.restoreAuthSystemState();
ExportEventProcessor exportEventProcessor = new ItemEventProcessor(context, request, item);
when(request.getRemoteAddr()).thenReturn("test-client-ip");
when(request.getHeader("USER-AGENT")).thenReturn("test-user-agent");
when(request.getHeader("referer")).thenReturn("test-referer");
String result = exportEventProcessor.getBaseParameters(item);
String expected = "url_ver=Z39.88-2004&req_id=test-client-ip&req_dat=test-user-agent&rft.artnum=" +
"oai%3Alocalhost%3A" + encodedHandle + "&rfr_dat=test-referer&rfr_id=localhost&url_tim=";
assertThat(result, startsWith(expected));
}
@Test
/**
* Test the ShouldProcessItem method where the item is null
*/
public void testShouldProcessItemWhenNull() throws SQLException {
ExportEventProcessor exportEventProcessor = new ItemEventProcessor(context, request, null);
boolean result = exportEventProcessor.shouldProcessItem(null);
assertThat(result, is(false));
}
@Test
/**
* Test the ShouldProcessItem method where the item is not archived
*/
public void testShouldProcessItemWhenNotArchived() throws SQLException {
context.turnOffAuthorisationSystem();
Community community = CommunityBuilder.createCommunity(context).build();
Collection collection = CollectionBuilder.createCollection(context, community).build();
WorkspaceItem workspaceItem = WorkspaceItemBuilder.createWorkspaceItem(context, collection).build();
context.restoreAuthSystemState();
ExportEventProcessor exportEventProcessor = new ItemEventProcessor(context, request, workspaceItem.getItem());
boolean result = exportEventProcessor.shouldProcessItem(workspaceItem.getItem());
assertFalse(result);
}
@Test
/**
* Test the ShouldProcessItem method where the item can be edit by the current user
*/
public void testShouldProcessItemWhenCanEdit() throws SQLException {
context.turnOffAuthorisationSystem();
Community community = CommunityBuilder.createCommunity(context).build();
Collection collection = CollectionBuilder.createCollection(context, community).build();
Item item = ItemBuilder.createItem(context, collection).withRelationshipType(otherEntity.getLabel()).build();
context.restoreAuthSystemState();
context.setCurrentUser(admin);
ExportEventProcessor exportEventProcessor = new ItemEventProcessor(context, request, item);
boolean result = exportEventProcessor.shouldProcessItem(item);
assertFalse(result);
}
@Test
/**
* Test the ShouldProcessItem method where the item type should be excluded
*/
public void testShouldProcessItemWhenShouldNotProcessType() throws Exception {
context.turnOffAuthorisationSystem();
Community community = CommunityBuilder.createCommunity(context).build();
Collection collection = CollectionBuilder.createCollection(context, community).build();
Item item = ItemBuilder.createItem(context, collection)
.withType("Excluded type")
.withRelationshipType(publication.getLabel())
.build();
context.restoreAuthSystemState();
ExportEventProcessor exportEventProcessor = new ItemEventProcessor(context, request, item);
boolean result = exportEventProcessor.shouldProcessItem(item);
assertFalse(result);
}
@Test
/**
* Test the ShouldProcessItem method where the item entity type should not be processed
*/
public void testShouldProcessItemWhenShouldNotProcessEntity() throws SQLException {
context.turnOffAuthorisationSystem();
Community community = CommunityBuilder.createCommunity(context).build();
Collection collection = CollectionBuilder.createCollection(context, community).build();
Item item = ItemBuilder.createItem(context, collection).withRelationshipType(otherEntity.getLabel()).build();
context.restoreAuthSystemState();
ExportEventProcessor exportEventProcessor = new ItemEventProcessor(context, request, item);
boolean result = exportEventProcessor.shouldProcessItem(item);
assertFalse(result);
}
@Test
/**
* Test the ShouldProcessItem method where all conditions are met
*/
public void testShouldProcessItem() throws SQLException {
context.turnOffAuthorisationSystem();
Community community = CommunityBuilder.createCommunity(context).build();
Collection collection = CollectionBuilder.createCollection(context, community).build();
Item item = ItemBuilder.createItem(context, collection).withRelationshipType(publication.getLabel()).build();
context.restoreAuthSystemState();
ExportEventProcessor exportEventProcessor = new ItemEventProcessor(context, request, item);
boolean result = exportEventProcessor.shouldProcessItem(item);
assertTrue(result);
}
@Test
/**
* Test the ShouldProcessEntityType method where all conditions are met
*/
public void testShouldProcessEntityType() throws SQLException {
context.turnOffAuthorisationSystem();
Community community = CommunityBuilder.createCommunity(context).build();
Collection collection = CollectionBuilder.createCollection(context, community).build();
Item item = ItemBuilder.createItem(context, collection).withRelationshipType(publication.getLabel()).build();
context.restoreAuthSystemState();
ExportEventProcessor exportEventProcessor = new ItemEventProcessor(context, request, item);
boolean result = exportEventProcessor.shouldProcessEntityType(item);
assertTrue(result);
}
@Test
/**
* Test the ShouldProcessEntityType method where the item entity type is not present in the configured list
*/
public void testShouldProcessEntityTypeWhenNotInList() throws SQLException {
context.turnOffAuthorisationSystem();
Community community = CommunityBuilder.createCommunity(context).build();
Collection collection = CollectionBuilder.createCollection(context, community).build();
Item item = ItemBuilder.createItem(context, collection).withRelationshipType(otherEntity.getLabel()).build();
context.restoreAuthSystemState();
ExportEventProcessor exportEventProcessor = new ItemEventProcessor(context, request, item);
boolean result = exportEventProcessor.shouldProcessEntityType(item);
assertFalse(result);
}
@Test
/**
* Test the shouldProcessItemType method where the item type is present in the list of excluded types
*/
public void testShouldProcessItemTypeInExcludeTrackerTypeList() {
context.turnOffAuthorisationSystem();
Community community = CommunityBuilder.createCommunity(context).build();
Collection collection = CollectionBuilder.createCollection(context, community).build();
Item item = ItemBuilder.createItem(context, collection).withType(excluded_type).build();
context.restoreAuthSystemState();
ExportEventProcessor exportEventProcessor = new ItemEventProcessor(context, request, item);
boolean result = exportEventProcessor.shouldProcessItemType(item);
assertFalse(result);
}
@Test
/**
* Test the shouldProcessItemType method where the item type is not present in the list of excluded types
*/
public void testShouldProcessItemTypeNotInExcludeTrackerTypeList() {
context.turnOffAuthorisationSystem();
Community community = CommunityBuilder.createCommunity(context).build();
Collection collection = CollectionBuilder.createCollection(context, community).build();
Item item = ItemBuilder.createItem(context, collection).withType("Not excluded type").build();
context.restoreAuthSystemState();
ExportEventProcessor exportEventProcessor = new ItemEventProcessor(context, request, item);
boolean result = exportEventProcessor.shouldProcessItemType(item);
assertTrue(result);
}
}

View File

@@ -0,0 +1,76 @@
/**
* 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.statistics.export.processor;
import static org.hamcrest.core.Is.is;
import static org.junit.Assert.assertThat;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import org.apache.commons.codec.CharEncoding;
import org.dspace.AbstractIntegrationTestWithDatabase;
import org.dspace.builder.CollectionBuilder;
import org.dspace.builder.CommunityBuilder;
import org.dspace.builder.ItemBuilder;
import org.dspace.content.Collection;
import org.dspace.content.Community;
import org.dspace.content.Item;
import org.dspace.services.ConfigurationService;
import org.dspace.services.factory.DSpaceServicesFactory;
import org.junit.Before;
import org.junit.Test;
/**
* Test class for the ItemEventProcessor
*/
public class ItemEventProcessorTest extends AbstractIntegrationTestWithDatabase {
private ConfigurationService configurationService = DSpaceServicesFactory.getInstance().getConfigurationService();
private String encodedUrl;
@Before
public void setUp() throws Exception {
super.setUp();
configurationService.setProperty("irus.statistics.tracker.enabled", true);
String dspaceUrl = configurationService.getProperty("dspace.ui.url");
try {
encodedUrl = URLEncoder.encode(dspaceUrl, CharEncoding.UTF_8);
} catch (UnsupportedEncodingException e) {
throw new AssertionError("Error occurred in setup()", e);
}
}
@Test
/**
* Test the method that adds data based on the object types
*/
public void testAddObectSpecificData() throws UnsupportedEncodingException {
context.turnOffAuthorisationSystem();
Community community = CommunityBuilder.createCommunity(context).build();
Collection collection = CollectionBuilder.createCollection(context, community).build();
Item item = ItemBuilder.createItem(context, collection).build();
context.restoreAuthSystemState();
String encodedHandle = URLEncoder.encode(item.getHandle(), CharEncoding.UTF_8);
ItemEventProcessor itemEventProcessor = new ItemEventProcessor(context, null, item);
String result = itemEventProcessor.addObjectSpecificData("existing-string", item);
assertThat(result,
is("existing-string&svc_dat=" + encodedUrl + "%2Fhandle%2F" + encodedHandle +
"&rft_dat=Investigation"));
}
}

View File

@@ -0,0 +1,41 @@
/**
* The contents of this file are subject to the license and copyright
* detailed in the LICENSE and NOTICE files at the root of the source
* tree and available online at
*
* http://www.dspace.org/license/
*/
package org.dspace.statistics.export.service;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.util.ArrayList;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
/**
* Mock OpenUrlService that will ensure that IRUS tracker does need to be contacted in order to test the functionality
*/
public class MockOpenUrlServiceImpl extends OpenUrlServiceImpl {
@Autowired
ArrayList testProcessedUrls;
/**
* Returns a response code to simulate contact to the external url
* When the url contains "fail", a fail code 500 will be returned
* Otherwise the success code 200 will be returned
* @param urlStr
* @return 200 or 500 depending on whether the "fail" keyword is present in the url
* @throws IOException
*/
protected int getResponseCodeFromUrl(final String urlStr) throws IOException {
if (StringUtils.contains(urlStr, "fail")) {
return HttpURLConnection.HTTP_INTERNAL_ERROR;
} else {
testProcessedUrls.add(urlStr);
return HttpURLConnection.HTTP_OK;
}
}
}

View File

@@ -0,0 +1,134 @@
/**
* 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.statistics.export.service;
import static org.hamcrest.CoreMatchers.is;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.doCallRealMethod;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import org.dspace.core.Context;
import org.dspace.statistics.export.OpenURLTracker;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Spy;
import org.mockito.junit.MockitoJUnitRunner;
/**
* Test class for the OpenUrlServiceImpl
*/
@RunWith(MockitoJUnitRunner.class)
public class OpenUrlServiceImplTest {
@InjectMocks
@Spy
private OpenUrlServiceImpl openUrlService;
@Mock
private FailedOpenURLTrackerService failedOpenURLTrackerService;
/**
* Test the processUrl method
* @throws IOException
* @throws SQLException
*/
@Test
public void testProcessUrl() throws IOException, SQLException {
Context context = mock(Context.class);
doReturn(HttpURLConnection.HTTP_OK).when(openUrlService)
.getResponseCodeFromUrl(anyString());
openUrlService.processUrl(context, "test-url");
verify(openUrlService, times(0)).logfailed(context, "test-url");
}
/**
* Test the processUrl method when the url connection fails
* @throws IOException
* @throws SQLException
*/
@Test
public void testProcessUrlOnFail() throws IOException, SQLException {
Context context = mock(Context.class);
doReturn(HttpURLConnection.HTTP_INTERNAL_ERROR).when(openUrlService)
.getResponseCodeFromUrl(anyString());
doNothing().when(openUrlService).logfailed(any(Context.class), anyString());
openUrlService.processUrl(context, "test-url");
verify(openUrlService, times(1)).logfailed(context, "test-url");
}
/**
* Test the ReprocessFailedQueue method
* @throws SQLException
*/
@Test
public void testReprocessFailedQueue() throws SQLException {
Context context = mock(Context.class);
List<OpenURLTracker> trackers = new ArrayList<>();
OpenURLTracker tracker1 = mock(OpenURLTracker.class);
OpenURLTracker tracker2 = mock(OpenURLTracker.class);
OpenURLTracker tracker3 = mock(OpenURLTracker.class);
trackers.add(tracker1);
trackers.add(tracker2);
trackers.add(tracker3);
when(failedOpenURLTrackerService.findAll(any(Context.class))).thenReturn(trackers);
doNothing().when(openUrlService).tryReprocessFailed(any(Context.class), any(OpenURLTracker.class));
openUrlService.reprocessFailedQueue(context);
verify(openUrlService, times(3)).tryReprocessFailed(any(Context.class), any(OpenURLTracker.class));
}
/**
* Test the method that logs the failed urls in the db
* @throws SQLException
*/
@Test
public void testLogfailed() throws SQLException {
Context context = mock(Context.class);
OpenURLTracker tracker1 = mock(OpenURLTracker.class);
doCallRealMethod().when(tracker1).setUrl(anyString());
when(tracker1.getUrl()).thenCallRealMethod();
when(failedOpenURLTrackerService.create(any(Context.class))).thenReturn(tracker1);
String failedUrl = "failed-url";
openUrlService.logfailed(context, failedUrl);
Assert.assertThat(tracker1.getUrl(), is(failedUrl));
}
}

View File

@@ -29,121 +29,6 @@
<start-class>org.dspace.app.rest.Application</start-class>
</properties>
<profiles>
<!-- If Unit Testing is enabled, then setup the Unit Test Environment.
See also the 'skiptests' profile in Parent POM. -->
<profile>
<id>test-environment</id>
<activation>
<activeByDefault>false</activeByDefault>
<property>
<name>skipTests</name>
<value>false</value>
</property>
</activation>
<build>
<plugins>
<!-- Unit/Integration Testing setup: This plugin unzips the
'testEnvironment.zip' file (created by dspace-parent POM), into
the 'target/testing/' folder, to essentially create a test
install of DSpace, against which Tests can be run. -->
<plugin>
<artifactId>maven-dependency-plugin</artifactId>
<configuration>
<outputDirectory>${project.build.directory}/testing</outputDirectory>
<artifactItems>
<artifactItem>
<groupId>org.dspace</groupId>
<artifactId>dspace-parent</artifactId>
<version>${project.version}</version>
<type>zip</type>
<classifier>testEnvironment</classifier>
</artifactItem>
</artifactItems>
</configuration>
<executions>
<execution>
<id>setupTestEnvironment</id>
<phase>generate-test-resources</phase>
<goals>
<goal>unpack</goal>
</goals>
</execution>
<execution>
<id>setupIntegrationTestEnvironment</id>
<phase>pre-integration-test</phase>
<goals>
<goal>unpack</goal>
</goals>
</execution>
</executions>
</plugin>
<!-- This plugin allows us to run a Groovy script in our Maven POM
(see: http://gmaven.codehaus.org/Executing+Groovy+Code )
We are generating a OS-agnostic version (agnostic.build.dir) of
the ${project.build.directory} property (full path of target dir).
This is needed by the FileWeaver & Surefire plugins (see below)
to initialize the Unit Test environment's dspace.cfg file.
Otherwise, the Unit Test Framework will not work on Windows OS.
This Groovy code was mostly borrowed from:
http://stackoverflow.com/questions/3872355/how-to-convert-file-separator-in-maven
-->
<plugin>
<groupId>org.codehaus.gmaven</groupId>
<artifactId>groovy-maven-plugin</artifactId>
<executions>
<execution>
<id>setproperty</id>
<phase>generate-test-resources
</phase> <!-- XXX I think this should be 'initialize' - MHW -->
<goals>
<goal>execute</goal>
</goals>
<configuration>
<source>
project.properties['agnostic.build.dir'] = project.build.directory.replace(File.separator, '/');
println("Initializing Maven property 'agnostic.build.dir' to: " + project.properties['agnostic.build.dir']);
</source>
</configuration>
</execution>
</executions>
</plugin>
<!-- Run Unit Testing! This plugin just kicks off the tests (when enabled). -->
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<systemPropertyVariables>
<!-- Specify the dspace.dir to use for test environment -->
<!-- This system property is loaded by AbstractDSpaceTest to initialize the test environment -->
<dspace.dir>${agnostic.build.dir}/testing/dspace/</dspace.dir>
<!-- Turn off any DSpace logging -->
<dspace.log.init.disable>true</dspace.log.init.disable>
<solr.install.dir>${agnostic.build.dir}/testing/dspace/solr/</solr.install.dir>
</systemPropertyVariables>
</configuration>
</plugin>
<!-- Run Integration Testing! This plugin just kicks off the tests (when enabled). -->
<plugin>
<artifactId>maven-failsafe-plugin</artifactId>
<configuration>
<systemPropertyVariables>
<!-- Specify the dspace.dir to use for test environment -->
<dspace.dir>${agnostic.build.dir}/testing/dspace/</dspace.dir>
<!-- Turn off any DSpace logging -->
<dspace.log.init.disable>true</dspace.log.init.disable>
<solr.install.dir>${agnostic.build.dir}/testing/dspace/solr/</solr.install.dir>
</systemPropertyVariables>
</configuration>
</plugin>
</plugins>
</build>
</profile>
</profiles>
<build>
<plugins>
<plugin>
@@ -187,9 +72,160 @@
</excludes>
</configuration>
</plugin>
<!-- This plugin allows us to run a Groovy script in our Maven POM
(see: https://groovy.github.io/gmaven/groovy-maven-plugin/execute.html )
We are generating a OS-agnostic version (agnostic.build.dir) of
the ${project.build.directory} property (full path of target dir).
This is needed by the Surefire & Failsafe plugins (see below)
to initialize the Unit Test environment's dspace.cfg file.
Otherwise, the Unit Test Framework will not work on Windows OS.
This Groovy code was mostly borrowed from:
http://stackoverflow.com/questions/3872355/how-to-convert-file-separator-in-maven
-->
<plugin>
<groupId>org.codehaus.gmaven</groupId>
<artifactId>groovy-maven-plugin</artifactId>
<executions>
<execution>
<id>setproperty</id>
<phase>initialize</phase>
<goals>
<goal>execute</goal>
</goals>
<configuration>
<source>
project.properties['agnostic.build.dir'] = project.build.directory.replace(File.separator, '/');
log.info("Initializing Maven property 'agnostic.build.dir' to: {}", project.properties['agnostic.build.dir']);
</source>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
<profiles>
<!-- Setup the Unit Test Environment (when -DskipUnitTests=false) -->
<profile>
<id>unit-test-environment</id>
<activation>
<activeByDefault>false</activeByDefault>
<property>
<name>skipUnitTests</name>
<value>false</value>
</property>
</activation>
<build>
<plugins>
<!-- Unit Testing setup: This plugin unzips the
'testEnvironment.zip' file (created by dspace-parent POM), into
the 'target/testing/' folder, to essentially create a test
install of DSpace, against which Tests can be run. -->
<plugin>
<artifactId>maven-dependency-plugin</artifactId>
<configuration>
<outputDirectory>${project.build.directory}/testing</outputDirectory>
<artifactItems>
<artifactItem>
<groupId>org.dspace</groupId>
<artifactId>dspace-parent</artifactId>
<version>${project.version}</version>
<type>zip</type>
<classifier>testEnvironment</classifier>
</artifactItem>
</artifactItems>
</configuration>
<executions>
<execution>
<id>setupUnitTestEnvironment</id>
<phase>generate-test-resources</phase>
<goals>
<goal>unpack</goal>
</goals>
</execution>
</executions>
</plugin>
<!-- Run Unit Testing! This plugin just kicks off the tests -->
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<systemPropertyVariables>
<!-- Specify the dspace.dir to use for test environment -->
<!-- This system property is loaded by AbstractDSpaceTest to initialize the test environment -->
<dspace.dir>${agnostic.build.dir}/testing/dspace/</dspace.dir>
<!-- Turn off any DSpace logging -->
<dspace.log.init.disable>true</dspace.log.init.disable>
<solr.install.dir>${agnostic.build.dir}/testing/dspace/solr/</solr.install.dir>
</systemPropertyVariables>
</configuration>
</plugin>
</plugins>
</build>
</profile>
<!-- Setup the Integration Test Environment (when -DskipIntegrationTests=false) -->
<profile>
<id>integration-test-environment</id>
<activation>
<activeByDefault>false</activeByDefault>
<property>
<name>skipIntegrationTests</name>
<value>false</value>
</property>
</activation>
<build>
<plugins>
<!-- Integration Testing setup: This plugin unzips the
'testEnvironment.zip' file (created by dspace-parent POM), into
the 'target/testing/' folder, to essentially create a test
install of DSpace, against which Tests can be run. -->
<plugin>
<artifactId>maven-dependency-plugin</artifactId>
<configuration>
<outputDirectory>${project.build.directory}/testing</outputDirectory>
<artifactItems>
<artifactItem>
<groupId>org.dspace</groupId>
<artifactId>dspace-parent</artifactId>
<version>${project.version}</version>
<type>zip</type>
<classifier>testEnvironment</classifier>
</artifactItem>
</artifactItems>
</configuration>
<executions>
<execution>
<id>setupIntegrationTestEnvironment</id>
<phase>pre-integration-test</phase>
<goals>
<goal>unpack</goal>
</goals>
</execution>
</executions>
</plugin>
<!-- Run Integration Testing! This plugin just kicks off the tests (when enabled). -->
<plugin>
<artifactId>maven-failsafe-plugin</artifactId>
<configuration>
<systemPropertyVariables>
<!-- Specify the dspace.dir to use for test environment -->
<dspace.dir>${agnostic.build.dir}/testing/dspace/</dspace.dir>
<!-- Turn off any DSpace logging -->
<dspace.log.init.disable>true</dspace.log.init.disable>
<solr.install.dir>${agnostic.build.dir}/testing/dspace/solr/</solr.install.dir>
</systemPropertyVariables>
</configuration>
</plugin>
</plugins>
</build>
</profile>
</profiles>
<dependencyManagement>
<dependencies>
<dependency>

View File

@@ -8,6 +8,7 @@
package org.dspace.app.rest;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import javax.servlet.http.HttpServletResponse;
@@ -62,8 +63,14 @@ public class ShibbolethRestController implements InitializingBean {
// Validate that the redirectURL matches either the server or UI hostname. It *cannot* be an arbitrary URL.
String redirectHostName = Utils.getHostName(redirectUrl);
String serverHostName = Utils.getHostName(configurationService.getProperty("dspace.server.url"));
String clientHostName = Utils.getHostName(configurationService.getProperty("dspace.ui.url"));
if (StringUtils.equalsAnyIgnoreCase(redirectHostName, serverHostName, clientHostName)) {
ArrayList<String> allowedHostNames = new ArrayList<String>();
allowedHostNames.add(serverHostName);
String[] allowedUrls = configurationService.getArrayProperty("rest.cors.allowed-origins");
for (String url : allowedUrls) {
allowedHostNames.add(Utils.getHostName(url));
}
if (StringUtils.equalsAnyIgnoreCase(redirectHostName, allowedHostNames.toArray(new String[0]))) {
log.debug("Shibboleth redirecting to " + redirectUrl);
response.sendRedirect(redirectUrl);
} else {

View File

@@ -0,0 +1,72 @@
/**
* The contents of this file are subject to the license and copyright
* detailed in the LICENSE and NOTICE files at the root of the source
* tree and available online at
*
* http://www.dspace.org/license/
*/
package org.dspace.app.rest.authorization;
import java.sql.SQLException;
import org.dspace.app.rest.model.BaseObjectRest;
import org.dspace.app.rest.security.DSpaceRestPermission;
import org.dspace.app.rest.utils.Utils;
import org.dspace.authorize.service.AuthorizeService;
import org.dspace.content.DSpaceObject;
import org.dspace.content.Item;
import org.dspace.content.factory.ContentServiceFactory;
import org.dspace.content.service.DSpaceObjectService;
import org.dspace.core.Context;
import org.dspace.eperson.EPerson;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
/**
* This class is a wrapper around the AuthorizationService which takes Rest objects instead of dspace objects
*/
@Component
public class AuthorizeServiceRestUtil {
@Autowired
private AuthorizeService authorizeService;
@Autowired
private Utils utils;
@Autowired
private ContentServiceFactory contentServiceFactory;
/**
* Checks that the specified eperson can perform the given action on the rest given object.
*
* @param context DSpace context
* @param object The Rest object to test the action against
* @param dSpaceRestPermission The permission to check
* @return A boolean indicating if the action is allowed by the logged in ePerson on the given object
* @throws SQLException
*/
public boolean authorizeActionBoolean(Context context, BaseObjectRest object,
DSpaceRestPermission dSpaceRestPermission)
throws SQLException {
DSpaceObject dSpaceObject = (DSpaceObject)utils.getDSpaceAPIObjectFromRest(context, object);
if (dSpaceObject == null) {
return false;
}
DSpaceObjectService<DSpaceObject> dSpaceObjectService =
contentServiceFactory.getDSpaceObjectService(dSpaceObject.getType());
EPerson ePerson = context.getCurrentUser();
// If the item is still inprogress we can process here only the READ permission.
// Other actions need to be evaluated against the wrapper object (workspace or workflow item)
if (dSpaceObject instanceof Item) {
if (!DSpaceRestPermission.READ.equals(dSpaceRestPermission)
&& !((Item) dSpaceObject).isArchived() && !((Item) dSpaceObject).isWithdrawn()) {
return false;
}
}
return authorizeService.authorizeActionBoolean(context, ePerson, dSpaceObject,
dSpaceRestPermission.getDspaceApiActionId(), true);
}
}

View File

@@ -0,0 +1,89 @@
/**
* The contents of this file are subject to the license and copyright
* detailed in the LICENSE and NOTICE files at the root of the source
* tree and available online at
*
* http://www.dspace.org/license/
*/
package org.dspace.app.rest.authorization.impl;
import java.sql.SQLException;
import org.apache.log4j.Logger;
import org.dspace.app.rest.authorization.AuthorizationFeature;
import org.dspace.app.rest.authorization.AuthorizationFeatureDocumentation;
import org.dspace.app.rest.authorization.AuthorizeServiceRestUtil;
import org.dspace.app.rest.model.BaseObjectRest;
import org.dspace.app.rest.model.BundleRest;
import org.dspace.app.rest.security.DSpaceRestPermission;
import org.dspace.app.rest.utils.Utils;
import org.dspace.authorize.service.AuthorizeService;
import org.dspace.content.Bundle;
import org.dspace.content.DSpaceObject;
import org.dspace.content.Item;
import org.dspace.content.service.BundleService;
import org.dspace.core.Constants;
import org.dspace.core.Context;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
/**
* The create bitstream feature. It can be used to verify if bitstreams can be created in a specific bundle.
*
* Authorization is granted if the current user has ADD & WRITE permissions on the given bundle AND the item
*/
@Component
@AuthorizationFeatureDocumentation(name = CreateBitstreamFeature.NAME,
description = "It can be used to verify if bitstreams can be created in a specific bundle")
public class CreateBitstreamFeature implements AuthorizationFeature {
Logger log = Logger.getLogger(CreateBitstreamFeature.class);
public final static String NAME = "canCreateBitstream";
@Autowired
private AuthorizeServiceRestUtil authorizeServiceRestUtil;
@Autowired
private BundleService bundleService;
@Autowired
private Utils utils;
@Autowired
private AuthorizeService authorizeService;
@Override
public boolean isAuthorized(Context context, BaseObjectRest object) throws SQLException {
if (object instanceof BundleRest) {
if (!authorizeServiceRestUtil.authorizeActionBoolean(context, object, DSpaceRestPermission.WRITE)) {
return false;
}
if (!authorizeServiceRestUtil.authorizeActionBoolean(context, object, DSpaceRestPermission.ADD)) {
return false;
}
DSpaceObject owningObject = bundleService.getParentObject(context,
(Bundle)utils.getDSpaceAPIObjectFromRest(context, object));
// Safety check. In case this is ever not true, this method should be revised.
if (!(owningObject instanceof Item)) {
log.error("The parent object of bundle " + object.getType() + " is not an item");
return false;
}
if (!authorizeService.authorizeActionBoolean(context, context.getCurrentUser(), owningObject,
Constants.WRITE, true)) {
return false;
}
return authorizeService.authorizeActionBoolean(context, context.getCurrentUser(), owningObject,
Constants.ADD, true);
}
return false;
}
@Override
public String[] getSupportedTypes() {
return new String[]{
BundleRest.CATEGORY + "." + BundleRest.NAME
};
}
}

View File

@@ -0,0 +1,54 @@
/**
* The contents of this file are subject to the license and copyright
* detailed in the LICENSE and NOTICE files at the root of the source
* tree and available online at
*
* http://www.dspace.org/license/
*/
package org.dspace.app.rest.authorization.impl;
import java.sql.SQLException;
import org.dspace.app.rest.authorization.AuthorizationFeature;
import org.dspace.app.rest.authorization.AuthorizationFeatureDocumentation;
import org.dspace.app.rest.authorization.AuthorizeServiceRestUtil;
import org.dspace.app.rest.model.BaseObjectRest;
import org.dspace.app.rest.model.ItemRest;
import org.dspace.app.rest.security.DSpaceRestPermission;
import org.dspace.core.Context;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
/**
* The create bundle feature. It can be used to verify if bundles can be created in a specific item.
*
* Authorization is granted if the current user has ADD & WRITE permissions on the given item
*/
@Component
@AuthorizationFeatureDocumentation(name = CreateBundleFeature.NAME,
description = "It can be used to verify if bundles can be created in a specific item")
public class CreateBundleFeature implements AuthorizationFeature {
public final static String NAME = "canCreateBundle";
@Autowired
private AuthorizeServiceRestUtil authorizeServiceRestUtil;
@Override
public boolean isAuthorized(Context context, BaseObjectRest object) throws SQLException {
if (object instanceof ItemRest) {
if (!authorizeServiceRestUtil.authorizeActionBoolean(context, object, DSpaceRestPermission.WRITE)) {
return false;
}
return authorizeServiceRestUtil.authorizeActionBoolean(context, object, DSpaceRestPermission.ADD);
}
return false;
}
@Override
public String[] getSupportedTypes() {
return new String[]{
ItemRest.CATEGORY + "." + ItemRest.NAME
};
}
}

View File

@@ -0,0 +1,138 @@
/**
* The contents of this file are subject to the license and copyright
* detailed in the LICENSE and NOTICE files at the root of the source
* tree and available online at
*
* http://www.dspace.org/license/
*/
package org.dspace.app.rest.authorization.impl;
import java.sql.SQLException;
import org.dspace.app.rest.authorization.AuthorizationFeature;
import org.dspace.app.rest.authorization.AuthorizationFeatureDocumentation;
import org.dspace.app.rest.authorization.AuthorizeServiceRestUtil;
import org.dspace.app.rest.model.BaseObjectRest;
import org.dspace.app.rest.model.BitstreamRest;
import org.dspace.app.rest.model.BundleRest;
import org.dspace.app.rest.model.CollectionRest;
import org.dspace.app.rest.model.CommunityRest;
import org.dspace.app.rest.model.EPersonRest;
import org.dspace.app.rest.model.GroupRest;
import org.dspace.app.rest.model.ItemRest;
import org.dspace.app.rest.model.WorkspaceItemRest;
import org.dspace.app.rest.security.DSpaceRestPermission;
import org.dspace.app.rest.utils.Utils;
import org.dspace.authorize.service.AuthorizeService;
import org.dspace.content.DSpaceObject;
import org.dspace.content.Item;
import org.dspace.content.WorkspaceItem;
import org.dspace.content.factory.ContentServiceFactory;
import org.dspace.core.Constants;
import org.dspace.core.Context;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
/**
* The delete feature. It can be used to verify if specific content can be deleted/expunged.
*
* Authorization is granted
* - for a bitstream if the current used has REMOVE permissions on both the Item and the Bundle
* - for a bundle if the current user has REMOVE permissions on the Item
* - for an item if the current user has REMOVE permissions on the collection AND and DELETE permissions on the item
* - for a collection if the current user has REMOVE permissions on the community
* - for a community with a parent community if the current user has REMOVE permissions on the parent community
* - for a community without a parent community if the current user has DELETE permissions on the current community
* - for other objects if the current user has REMOVE permissions on the parent object if there is one. Otherwise if the
* current user has DELETE permissions on the current object
*/
@Component
@AuthorizationFeatureDocumentation(name = DeleteFeature.NAME,
description = "It can be used to verify if specific content can be deleted/expunged")
public class DeleteFeature implements AuthorizationFeature {
public final static String NAME = "canDelete";
@Autowired
private AuthorizeServiceRestUtil authorizeServiceRestUtil;
@Autowired
private Utils utils;
@Autowired
private AuthorizeService authorizeService;
@Autowired
private ContentServiceFactory contentServiceFactory;
@Override
public boolean isAuthorized(Context context, BaseObjectRest object) throws SQLException {
if (object instanceof BaseObjectRest) {
if (object.getType().equals(WorkspaceItemRest.NAME)) {
object = ((WorkspaceItemRest)object).getItem();
}
DSpaceObject dSpaceObject = (DSpaceObject) utils.getDSpaceAPIObjectFromRest(context, object);
DSpaceObject parentObject = getParentObject(context, dSpaceObject);
switch (object.getType()) {
case BitstreamRest.NAME:
return (
authorizeService.authorizeActionBoolean(context, context.getCurrentUser(), parentObject,
Constants.REMOVE, true)
&& authorizeService.authorizeActionBoolean(context, context.getCurrentUser(), dSpaceObject,
Constants.REMOVE, true)
);
case ItemRest.NAME:
return (
authorizeService.authorizeActionBoolean(context, context.getCurrentUser(), parentObject,
Constants.REMOVE, true)
&& authorizeServiceRestUtil.authorizeActionBoolean(context, object,
DSpaceRestPermission.DELETE)
);
case CollectionRest.NAME:
case CommunityRest.NAME:
case BundleRest.NAME:
case WorkspaceItemRest.NAME:
case EPersonRest.NAME:
case GroupRest.NAME:
default:
if (parentObject != null) {
return authorizeService.authorizeActionBoolean(context, context.getCurrentUser(), parentObject,
Constants.REMOVE, true);
}
return authorizeServiceRestUtil.authorizeActionBoolean(context, object,
DSpaceRestPermission.DELETE);
}
}
return false;
}
private DSpaceObject getParentObject(Context context, DSpaceObject object) throws SQLException {
DSpaceObject parentObject
= contentServiceFactory.getDSpaceObjectService(object.getType()).getParentObject(context, object);
if (object.getType() == Constants.ITEM && parentObject == null) {
Item item = (Item) object;
parentObject = item.getOwningCollection();
WorkspaceItem byItem = ContentServiceFactory.getInstance()
.getWorkspaceItemService()
.findByItem(context, item);
if (byItem != null) {
parentObject = byItem.getCollection();
}
}
return parentObject;
}
@Override
public String[] getSupportedTypes() {
return new String[]{
CommunityRest.CATEGORY + "." + CommunityRest.NAME,
CollectionRest.CATEGORY + "." + CollectionRest.NAME,
ItemRest.CATEGORY + "." + ItemRest.NAME,
BundleRest.CATEGORY + "." + BundleRest.NAME,
BitstreamRest.CATEGORY + "." + BitstreamRest.NAME,
WorkspaceItemRest.CATEGORY + "." + WorkspaceItemRest.NAME,
EPersonRest.CATEGORY + "." + EPersonRest.NAME,
GroupRest.CATEGORY + "." + GroupRest.NAME
};
}
}

View File

@@ -0,0 +1,67 @@
/**
* The contents of this file are subject to the license and copyright
* detailed in the LICENSE and NOTICE files at the root of the source
* tree and available online at
*
* http://www.dspace.org/license/
*/
package org.dspace.app.rest.authorization.impl;
import java.sql.SQLException;
import org.dspace.app.rest.authorization.AuthorizationFeature;
import org.dspace.app.rest.authorization.AuthorizationFeatureDocumentation;
import org.dspace.app.rest.authorization.AuthorizeServiceRestUtil;
import org.dspace.app.rest.model.BaseObjectRest;
import org.dspace.app.rest.model.BitstreamRest;
import org.dspace.app.rest.model.BundleRest;
import org.dspace.app.rest.model.CollectionRest;
import org.dspace.app.rest.model.CommunityRest;
import org.dspace.app.rest.model.ItemRest;
import org.dspace.app.rest.model.SiteRest;
import org.dspace.app.rest.security.DSpaceRestPermission;
import org.dspace.core.Context;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
/**
* The edit metadata feature. It can be used to verify if the metadata of the specified objects can be edited.
*
* Authorization is granted if the current user has WRITE permissions on the given DSO
*/
@Component
@AuthorizationFeatureDocumentation(name = EditMetadataFeature.NAME,
description = "It can be used to verify if the metadata of the specified objects can be edited")
public class EditMetadataFeature implements AuthorizationFeature {
public final static String NAME = "canEditMetadata";
@Autowired
private AuthorizeServiceRestUtil authorizeServiceRestUtil;
@Override
public boolean isAuthorized(Context context, BaseObjectRest object) throws SQLException {
if (object instanceof CommunityRest
|| object instanceof CollectionRest
|| object instanceof ItemRest
|| object instanceof BundleRest
|| object instanceof BitstreamRest
|| object instanceof SiteRest
) {
return authorizeServiceRestUtil.authorizeActionBoolean(context, object, DSpaceRestPermission.WRITE);
}
return false;
}
@Override
public String[] getSupportedTypes() {
return new String[]{
CommunityRest.CATEGORY + "." + CommunityRest.NAME,
CollectionRest.CATEGORY + "." + CollectionRest.NAME,
ItemRest.CATEGORY + "." + ItemRest.NAME,
BundleRest.CATEGORY + "." + BundleRest.NAME,
BitstreamRest.CATEGORY + "." + BitstreamRest.NAME,
SiteRest.CATEGORY + "." + SiteRest.NAME
};
}
}

View File

@@ -0,0 +1,51 @@
/**
* The contents of this file are subject to the license and copyright
* detailed in the LICENSE and NOTICE files at the root of the source
* tree and available online at
*
* http://www.dspace.org/license/
*/
package org.dspace.app.rest.authorization.impl;
import java.sql.SQLException;
import org.dspace.app.rest.authorization.AuthorizationFeature;
import org.dspace.app.rest.authorization.AuthorizationFeatureDocumentation;
import org.dspace.app.rest.authorization.AuthorizeServiceRestUtil;
import org.dspace.app.rest.model.BaseObjectRest;
import org.dspace.app.rest.model.ItemRest;
import org.dspace.app.rest.security.DSpaceRestPermission;
import org.dspace.core.Context;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
/**
* The make discoverable feature. It can be used to verify if an item can be made discoverable.
*
* Authorization is granted if the current user has WRITE permissions on the given item
*/
@Component
@AuthorizationFeatureDocumentation(name = MakeDiscoverableFeature.NAME,
description = "It can be used to verify if an item can be made discoverable")
public class MakeDiscoverableFeature implements AuthorizationFeature {
public final static String NAME = "canMakeDiscoverable";
@Autowired
private AuthorizeServiceRestUtil authorizeServiceRestUtil;
@Override
public boolean isAuthorized(Context context, BaseObjectRest object) throws SQLException {
if (object instanceof ItemRest) {
return authorizeServiceRestUtil.authorizeActionBoolean(context, object, DSpaceRestPermission.WRITE);
}
return false;
}
@Override
public String[] getSupportedTypes() {
return new String[]{
ItemRest.CATEGORY + "." + ItemRest.NAME
};
}
}

View File

@@ -0,0 +1,51 @@
/**
* The contents of this file are subject to the license and copyright
* detailed in the LICENSE and NOTICE files at the root of the source
* tree and available online at
*
* http://www.dspace.org/license/
*/
package org.dspace.app.rest.authorization.impl;
import java.sql.SQLException;
import org.dspace.app.rest.authorization.AuthorizationFeature;
import org.dspace.app.rest.authorization.AuthorizationFeatureDocumentation;
import org.dspace.app.rest.authorization.AuthorizeServiceRestUtil;
import org.dspace.app.rest.model.BaseObjectRest;
import org.dspace.app.rest.model.ItemRest;
import org.dspace.app.rest.security.DSpaceRestPermission;
import org.dspace.core.Context;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
/**
* The make private feature. It can be used to verify if an item can be made private.
*
* Authorization is granted if the current user has WRITE permissions on the given item
*/
@Component
@AuthorizationFeatureDocumentation(name = MakePrivateFeature.NAME,
description = "It can be used to verify if an item can be made private")
public class MakePrivateFeature implements AuthorizationFeature {
public final static String NAME = "canMakePrivate";
@Autowired
private AuthorizeServiceRestUtil authorizeServiceRestUtil;
@Override
public boolean isAuthorized(Context context, BaseObjectRest object) throws SQLException {
if (object instanceof ItemRest) {
return authorizeServiceRestUtil.authorizeActionBoolean(context, object, DSpaceRestPermission.WRITE);
}
return false;
}
@Override
public String[] getSupportedTypes() {
return new String[]{
ItemRest.CATEGORY + "." + ItemRest.NAME
};
}
}

View File

@@ -0,0 +1,81 @@
/**
* The contents of this file are subject to the license and copyright
* detailed in the LICENSE and NOTICE files at the root of the source
* tree and available online at
*
* http://www.dspace.org/license/
*/
package org.dspace.app.rest.authorization.impl;
import java.sql.SQLException;
import org.apache.log4j.Logger;
import org.dspace.app.rest.authorization.AuthorizationFeature;
import org.dspace.app.rest.authorization.AuthorizationFeatureDocumentation;
import org.dspace.app.rest.authorization.AuthorizeServiceRestUtil;
import org.dspace.app.rest.model.BaseObjectRest;
import org.dspace.app.rest.model.ItemRest;
import org.dspace.app.rest.security.DSpaceRestPermission;
import org.dspace.app.rest.utils.Utils;
import org.dspace.authorize.service.AuthorizeService;
import org.dspace.content.Collection;
import org.dspace.content.DSpaceObject;
import org.dspace.content.Item;
import org.dspace.content.service.ItemService;
import org.dspace.core.Constants;
import org.dspace.core.Context;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
/**
* The move feature. It can be used to verify if item can be moved to a different collection.
*
* Authorization is granted if the current user has WRITE permissions on the given item and REMOVE permissions on the
* items owning collection
*/
@Component
@AuthorizationFeatureDocumentation(name = MoveFeature.NAME,
description = "It can be used to verify if item can be moved to a different collection")
public class MoveFeature implements AuthorizationFeature {
Logger log = Logger.getLogger(MoveFeature.class);
public final static String NAME = "canMove";
@Autowired
private AuthorizeServiceRestUtil authorizeServiceRestUtil;
@Autowired
private Utils utils;
@Autowired
private ItemService itemService;
@Autowired
private AuthorizeService authorizeService;
@Override
public boolean isAuthorized(Context context, BaseObjectRest object) throws SQLException {
if (object instanceof ItemRest) {
if (!authorizeServiceRestUtil.authorizeActionBoolean(context, object, DSpaceRestPermission.WRITE)) {
return false;
}
DSpaceObject owningObject = itemService.getParentObject(context,
(Item)utils.getDSpaceAPIObjectFromRest(context, object));
if (!(owningObject instanceof Collection)) {
log.error("The partent object of item " + object.getType() + " is not a collection");
return false;
}
return authorizeService.authorizeActionBoolean(context, context.getCurrentUser(), owningObject,
Constants.REMOVE, true);
}
return false;
}
@Override
public String[] getSupportedTypes() {
return new String[]{
ItemRest.CATEGORY + "." + ItemRest.NAME
};
}
}

View File

@@ -0,0 +1,104 @@
/**
* The contents of this file are subject to the license and copyright
* detailed in the LICENSE and NOTICE files at the root of the source
* tree and available online at
*
* http://www.dspace.org/license/
*/
package org.dspace.app.rest.authorization.impl;
import java.sql.SQLException;
import org.dspace.app.rest.authorization.AuthorizationFeature;
import org.dspace.app.rest.authorization.AuthorizationFeatureDocumentation;
import org.dspace.app.rest.model.BaseObjectRest;
import org.dspace.app.rest.model.BitstreamRest;
import org.dspace.app.rest.model.BundleRest;
import org.dspace.app.rest.model.CollectionRest;
import org.dspace.app.rest.model.CommunityRest;
import org.dspace.app.rest.model.ItemRest;
import org.dspace.app.rest.model.SiteRest;
import org.dspace.app.rest.utils.Utils;
import org.dspace.app.util.AuthorizeUtil;
import org.dspace.authorize.AuthorizeException;
import org.dspace.authorize.service.AuthorizeService;
import org.dspace.content.Bitstream;
import org.dspace.content.Bundle;
import org.dspace.content.Collection;
import org.dspace.content.Community;
import org.dspace.content.Item;
import org.dspace.core.Context;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
/**
* The policy feature. It can be used by administrators (or community/collection delegate) to manage resource policies
*
* Authorization is granted
* - for the site if the current user is administrator
* - for other objects if the current user has ADMIN permissions on the object
*/
@Component
@AuthorizationFeatureDocumentation(name = PolicyFeature.NAME,
description = "It can be used to verify if the resourcepolicies of the specified objects can be managed")
public class PolicyFeature implements AuthorizationFeature {
public static final String NAME = "canManagePolicies";
@Autowired
AuthorizeService authService;
@Autowired
private Utils utils;
@Override
public boolean isAuthorized(Context context, BaseObjectRest object) throws SQLException {
if (object != null) {
try {
if (object instanceof SiteRest) {
return authService.isAdmin(context);
}
if (object instanceof CommunityRest) {
AuthorizeUtil.authorizeManageCommunityPolicy(context,
(Community)utils.getDSpaceAPIObjectFromRest(context, object));
return true;
}
if (object instanceof CollectionRest) {
AuthorizeUtil.authorizeManageCollectionPolicy(context,
(Collection) utils.getDSpaceAPIObjectFromRest(context, object));
return true;
}
if (object instanceof ItemRest) {
AuthorizeUtil.authorizeManageItemPolicy(context,
(Item)utils.getDSpaceAPIObjectFromRest(context, object));
return true;
}
if (object instanceof BundleRest) {
AuthorizeUtil.authorizeManageBundlePolicy(context,
(Bundle)utils.getDSpaceAPIObjectFromRest(context, object));
return true;
}
if (object instanceof BitstreamRest) {
AuthorizeUtil.authorizeManageBitstreamPolicy(context,
(Bitstream)utils.getDSpaceAPIObjectFromRest(context, object));
return true;
}
} catch (AuthorizeException e) {
return false;
}
}
return false;
}
@Override
public String[] getSupportedTypes() {
return new String[]{
SiteRest.CATEGORY + "." + SiteRest.NAME,
CommunityRest.CATEGORY + "." + CommunityRest.NAME,
CollectionRest.CATEGORY + "." + CollectionRest.NAME,
ItemRest.CATEGORY + "." + ItemRest.NAME,
BundleRest.CATEGORY + "." + BundleRest.NAME,
BitstreamRest.CATEGORY + "." + BitstreamRest.NAME
};
}
}

View File

@@ -0,0 +1,51 @@
/**
* The contents of this file are subject to the license and copyright
* detailed in the LICENSE and NOTICE files at the root of the source
* tree and available online at
*
* http://www.dspace.org/license/
*/
package org.dspace.app.rest.authorization.impl;
import java.sql.SQLException;
import org.dspace.app.rest.authorization.AuthorizationFeature;
import org.dspace.app.rest.authorization.AuthorizationFeatureDocumentation;
import org.dspace.app.rest.authorization.AuthorizeServiceRestUtil;
import org.dspace.app.rest.model.BaseObjectRest;
import org.dspace.app.rest.model.BundleRest;
import org.dspace.app.rest.security.DSpaceRestPermission;
import org.dspace.core.Context;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
/**
* The reorder bitstream feature. It can be used to verify if bitstreams can be reordered in a specific bundle.
*
* Authorization is granted if the current user has WRITE permissions on the given bundle
*/
@Component
@AuthorizationFeatureDocumentation(name = ReorderBitstreamFeature.NAME,
description = "It can be used to verify if bitstreams can be reordered in a specific bundle")
public class ReorderBitstreamFeature implements AuthorizationFeature {
public final static String NAME = "canReorderBitstreams";
@Autowired
private AuthorizeServiceRestUtil authorizeServiceRestUtil;
@Override
public boolean isAuthorized(Context context, BaseObjectRest object) throws SQLException {
if (object instanceof BundleRest) {
return authorizeServiceRestUtil.authorizeActionBoolean(context, object, DSpaceRestPermission.WRITE);
}
return false;
}
@Override
public String[] getSupportedTypes() {
return new String[]{
BundleRest.CATEGORY + "." + BundleRest.NAME
};
}
}

View File

@@ -9,11 +9,17 @@ package org.dspace.app.rest.repository;
import java.io.IOException;
import java.sql.SQLException;
import java.util.Iterator;
import java.util.List;
import java.util.UUID;
import java.util.stream.Collectors;
import org.apache.commons.lang3.StringUtils;
import org.apache.log4j.Logger;
import org.dspace.app.rest.Parameter;
import org.dspace.app.rest.SearchRestMethod;
import org.dspace.app.rest.converter.ConverterService;
import org.dspace.app.rest.exception.DSpaceBadRequestException;
import org.dspace.app.rest.exception.RepositoryMethodNotImplementedException;
import org.dspace.app.rest.model.BitstreamRest;
import org.dspace.app.rest.model.ProcessRest;
@@ -21,12 +27,18 @@ import org.dspace.app.rest.projection.Projection;
import org.dspace.authorize.AuthorizeException;
import org.dspace.authorize.service.AuthorizeService;
import org.dspace.content.Bitstream;
import org.dspace.content.ProcessStatus;
import org.dspace.core.Context;
import org.dspace.eperson.EPerson;
import org.dspace.eperson.service.EPersonService;
import org.dspace.scripts.Process;
import org.dspace.scripts.ProcessQueryParameterContainer;
import org.dspace.scripts.Process_;
import org.dspace.scripts.service.ProcessService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.rest.webmvc.ResourceNotFoundException;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.stereotype.Component;
@@ -49,6 +61,9 @@ public class ProcessRestRepository extends DSpaceRestRepository<ProcessRest, Int
@Autowired
private AuthorizeService authorizeService;
@Autowired
private EPersonService epersonService;
@Override
@PreAuthorize("hasPermission(#id, 'PROCESS', 'READ')")
@@ -135,6 +150,104 @@ public class ProcessRestRepository extends DSpaceRestRepository<ProcessRest, Int
}
}
/**
* Search method that will take Parameters and return a list of {@link ProcessRest} objects
* based on the {@link Process} objects that were in the databank that adhere to these params
* @param ePersonUuid The UUID for the EPerson that started the Process
* @param scriptName The name of the Script for which the Process belongs to
* @param processStatusString The status of the Process
* @param pageable The pageable
* @return A page of {@link ProcessRest} objects adhering to the params
* @throws SQLException If something goes wrong
*/
@SearchRestMethod(name = "byProperty")
@PreAuthorize("hasAuthority('ADMIN')")
public Page<ProcessRest> findProcessesByProperty(@Parameter(value = "userId") UUID ePersonUuid,
@Parameter(value = "scriptName") String scriptName,
@Parameter(value = "processStatus") String processStatusString,
Pageable pageable)
throws SQLException {
if (StringUtils.isBlank(scriptName) && ePersonUuid == null && StringUtils.isBlank(processStatusString)) {
throw new DSpaceBadRequestException("Either a name, user UUID or ProcessStatus should be provided");
}
Context context = obtainContext();
EPerson ePerson = null;
if (ePersonUuid != null) {
ePerson = epersonService.find(context, ePersonUuid);
if (ePerson == null) {
throw new DSpaceBadRequestException("No EPerson with the given UUID is found");
}
}
ProcessStatus processStatus = StringUtils.isBlank(processStatusString) ? null :
ProcessStatus.valueOf(processStatusString);
ProcessQueryParameterContainer processQueryParameterContainer = createProcessQueryParameterContainer(scriptName,
ePerson, processStatus);
handleSearchSort(pageable, processQueryParameterContainer);
List<Process> processes = processService.search(context, processQueryParameterContainer, pageable.getPageSize(),
Math.toIntExact(pageable.getOffset()));
return converterService.toRestPage(processes, pageable,
processService.countSearch(context, processQueryParameterContainer),
utils.obtainProjection());
}
/**
* This method will retrieve the {@link Sort} from the given {@link Pageable} and it'll create the sortOrder and
* sortProperty Strings on the {@link ProcessQueryParameterContainer} object so that we can store how the sorting
* should be done
* @param pageable The pageable object
* @param processQueryParameterContainer The object in which the sorting will be filled in
*/
private void handleSearchSort(Pageable pageable, ProcessQueryParameterContainer processQueryParameterContainer) {
Sort sort = pageable.getSort();
if (sort != null) {
Iterator<Sort.Order> iterator = sort.iterator();
if (iterator.hasNext()) {
Sort.Order order = iterator.next();
if (StringUtils.equalsIgnoreCase(order.getProperty(), "startTime")) {
processQueryParameterContainer.setSortProperty(Process_.START_TIME);
processQueryParameterContainer.setSortOrder(order.getDirection().name());
} else if (StringUtils.equalsIgnoreCase(order.getProperty(), "endTime")) {
processQueryParameterContainer.setSortProperty(Process_.FINISHED_TIME);
processQueryParameterContainer.setSortOrder(order.getDirection().name());
} else {
throw new DSpaceBadRequestException("The given sort option was invalid: " + order.getProperty());
}
if (iterator.hasNext()) {
throw new DSpaceBadRequestException("Only one sort method is supported, can't give multiples");
}
}
}
}
/**
* This method will create a new {@link ProcessQueryParameterContainer} object and return it.
* This object will contain a map which is filled in with the database column reference as key and the value that
* it should contain when searching as the value of the entry
* @param scriptName The name that the script of the process should have
* @param ePerson The eperson that the process should have
* @param processStatus The status that the process should have
* @return The newly created {@link ProcessQueryParameterContainer}
*/
private ProcessQueryParameterContainer createProcessQueryParameterContainer(String scriptName, EPerson ePerson,
ProcessStatus processStatus) {
ProcessQueryParameterContainer processQueryParameterContainer =
new ProcessQueryParameterContainer();
if (StringUtils.isNotBlank(scriptName)) {
processQueryParameterContainer.addToQueryParameterMap(Process_.NAME, scriptName);
}
if (ePerson != null) {
processQueryParameterContainer.addToQueryParameterMap(Process_.E_PERSON, ePerson);
}
if (processStatus != null) {
processQueryParameterContainer.addToQueryParameterMap(Process_.PROCESS_STATUS, processStatus);
}
return processQueryParameterContainer;
}
@Override
public Class<ProcessRest> getDomainClass() {
return ProcessRest.class;

View File

@@ -7,6 +7,7 @@
*/
package org.dspace.app.rest;
import static org.hamcrest.Matchers.contains;
import static org.hamcrest.Matchers.containsInAnyOrder;
import static org.hamcrest.Matchers.is;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
@@ -18,6 +19,7 @@ import java.sql.SQLException;
import java.util.LinkedList;
import java.util.List;
import java.util.Random;
import java.util.UUID;
import org.apache.commons.codec.CharEncoding;
import org.apache.commons.collections4.CollectionUtils;
@@ -158,39 +160,40 @@ public class ProcessRestRepositoryIT extends AbstractControllerIntegrationTest {
getClient(token).perform(get("/api/system/processes/"))
.andExpect(status().isOk())
.andExpect(jsonPath("$._embedded.processes", containsInAnyOrder(
ProcessMatcher.matchProcess(process.getName(), String.valueOf(process.getEPerson().getID()),
process.getID(), parameters, ProcessStatus.SCHEDULED),
ProcessMatcher.matchProcess(newProcess.getName(),
String.valueOf(newProcess.getEPerson().getID()),
newProcess.getID(), parameters, ProcessStatus.SCHEDULED),
ProcessMatcher.matchProcess(newProcess1.getName(),
String.valueOf(newProcess1.getEPerson().getID()),
newProcess1.getID(), parameters, ProcessStatus.SCHEDULED),
ProcessMatcher.matchProcess(newProcess2.getName(),
String.valueOf(newProcess2.getEPerson().getID()),
newProcess2.getID(), parameters, ProcessStatus.SCHEDULED),
ProcessMatcher.matchProcess(newProcess3.getName(),
String.valueOf(newProcess3.getEPerson().getID()),
newProcess3.getID(), parameters, ProcessStatus.SCHEDULED),
ProcessMatcher.matchProcess(newProcess4.getName(),
String.valueOf(newProcess4.getEPerson().getID()),
newProcess4.getID(), parameters, ProcessStatus.SCHEDULED),
ProcessMatcher.matchProcess(newProcess5.getName(),
String.valueOf(newProcess5.getEPerson().getID()),
newProcess5.getID(), parameters, ProcessStatus.SCHEDULED),
ProcessMatcher.matchProcess(newProcess6.getName(),
String.valueOf(newProcess6.getEPerson().getID()),
newProcess6.getID(), parameters, ProcessStatus.SCHEDULED),
ProcessMatcher.matchProcess(newProcess7.getName(),
String.valueOf(newProcess7.getEPerson().getID()),
newProcess7.getID(), parameters, ProcessStatus.SCHEDULED),
// Expect all processes to be returned, newest to oldest
.andExpect(jsonPath("$._embedded.processes", contains(
ProcessMatcher.matchProcess(newProcess9.getName(),
String.valueOf(newProcess9.getEPerson().getID()),
newProcess9.getID(), parameters, ProcessStatus.SCHEDULED),
ProcessMatcher.matchProcess(newProcess8.getName(),
String.valueOf(newProcess8.getEPerson().getID()),
newProcess8.getID(), parameters, ProcessStatus.SCHEDULED),
ProcessMatcher.matchProcess(newProcess9.getName(),
String.valueOf(newProcess9.getEPerson().getID()),
newProcess9.getID(), parameters, ProcessStatus.SCHEDULED)
ProcessMatcher.matchProcess(newProcess7.getName(),
String.valueOf(newProcess7.getEPerson().getID()),
newProcess7.getID(), parameters, ProcessStatus.SCHEDULED),
ProcessMatcher.matchProcess(newProcess6.getName(),
String.valueOf(newProcess6.getEPerson().getID()),
newProcess6.getID(), parameters, ProcessStatus.SCHEDULED),
ProcessMatcher.matchProcess(newProcess5.getName(),
String.valueOf(newProcess5.getEPerson().getID()),
newProcess5.getID(), parameters, ProcessStatus.SCHEDULED),
ProcessMatcher.matchProcess(newProcess4.getName(),
String.valueOf(newProcess4.getEPerson().getID()),
newProcess4.getID(), parameters, ProcessStatus.SCHEDULED),
ProcessMatcher.matchProcess(newProcess3.getName(),
String.valueOf(newProcess3.getEPerson().getID()),
newProcess3.getID(), parameters, ProcessStatus.SCHEDULED),
ProcessMatcher.matchProcess(newProcess2.getName(),
String.valueOf(newProcess2.getEPerson().getID()),
newProcess2.getID(), parameters, ProcessStatus.SCHEDULED),
ProcessMatcher.matchProcess(newProcess1.getName(),
String.valueOf(newProcess1.getEPerson().getID()),
newProcess1.getID(), parameters, ProcessStatus.SCHEDULED),
ProcessMatcher.matchProcess(newProcess.getName(),
String.valueOf(newProcess.getEPerson().getID()),
newProcess.getID(), parameters, ProcessStatus.SCHEDULED),
ProcessMatcher.matchProcess(process.getName(), String.valueOf(process.getEPerson().getID()),
process.getID(), parameters, ProcessStatus.SCHEDULED)
)))
.andExpect(jsonPath("$.page", is(
PageMatcher.pageEntryWithTotalPagesAndElements(0, 20, 1, 11))));
@@ -323,6 +326,464 @@ public class ProcessRestRepositoryIT extends AbstractControllerIntegrationTest {
}
@Test
public void searchProcessTestForbidden() throws Exception {
String token = getAuthToken(eperson.getEmail(), password);
getClient(token).perform(get("/api/system/processes/search/byProperty"))
.andExpect(status().isForbidden());
}
@Test
public void searchProcessTestUnauthorized() throws Exception {
getClient().perform(get("/api/system/processes/search/byProperty"))
.andExpect(status().isUnauthorized());
}
@Test
public void searchProcessTestByUser() throws Exception {
Process newProcess = ProcessBuilder.createProcess(context, eperson, "mock-script", parameters).build();
Process newProcess1 = ProcessBuilder.createProcess(context, eperson, "mock-script", parameters).build();
Process newProcess2 = ProcessBuilder.createProcess(context, eperson, "mock-script", parameters).build();
Process newProcess3 = ProcessBuilder.createProcess(context, eperson, "mock-script", parameters).build();
Process newProcess4 = ProcessBuilder.createProcess(context, eperson, "mock-script", parameters).build();
Process newProcess5 = ProcessBuilder.createProcess(context, eperson, "mock-script", parameters).build();
Process newProcess6 = ProcessBuilder.createProcess(context, eperson, "mock-script", parameters).build();
Process newProcess7 = ProcessBuilder.createProcess(context, admin, "mock-script", parameters).build();
Process newProcess8 = ProcessBuilder.createProcess(context, admin, "mock-script", parameters).build();
Process newProcess9 = ProcessBuilder.createProcess(context, admin, "mock-script", parameters).build();
String token = getAuthToken(admin.getEmail(), password);
getClient(token).perform(get("/api/system/processes/search/byProperty")
.param("userId", admin.getID().toString()))
.andExpect(status().isOk())
.andExpect(jsonPath("$._embedded.processes", containsInAnyOrder(
ProcessMatcher.matchProcess(process.getName(),
String.valueOf(admin.getID().toString()),
process.getID(), parameters, ProcessStatus.SCHEDULED),
ProcessMatcher.matchProcess(newProcess7.getName(),
String.valueOf(admin.getID().toString()),
newProcess7.getID(), parameters, ProcessStatus.SCHEDULED),
ProcessMatcher.matchProcess(newProcess8.getName(),
String.valueOf(admin.getID().toString()),
newProcess8.getID(), parameters, ProcessStatus.SCHEDULED),
ProcessMatcher.matchProcess(newProcess9.getName(),
String.valueOf(admin.getID().toString()),
newProcess9.getID(), parameters, ProcessStatus.SCHEDULED)
)))
.andExpect(jsonPath("$.page", is(
PageMatcher.pageEntryWithTotalPagesAndElements(0, 20, 1, 4))));
getClient(token).perform(get("/api/system/processes/search/byProperty")
.param("userId", eperson.getID().toString()))
.andExpect(status().isOk())
.andExpect(jsonPath("$._embedded.processes", containsInAnyOrder(
ProcessMatcher.matchProcess(newProcess.getName(),
String.valueOf(eperson.getID().toString()),
newProcess.getID(), parameters, ProcessStatus.SCHEDULED),
ProcessMatcher.matchProcess(newProcess1.getName(),
String.valueOf(eperson.getID().toString()),
newProcess1.getID(), parameters, ProcessStatus.SCHEDULED),
ProcessMatcher.matchProcess(newProcess2.getName(),
String.valueOf(eperson.getID().toString()),
newProcess2.getID(), parameters, ProcessStatus.SCHEDULED),
ProcessMatcher.matchProcess(newProcess3.getName(),
String.valueOf(eperson.getID().toString()),
newProcess3.getID(), parameters, ProcessStatus.SCHEDULED),
ProcessMatcher.matchProcess(newProcess4.getName(),
String.valueOf(eperson.getID().toString()),
newProcess4.getID(), parameters, ProcessStatus.SCHEDULED),
ProcessMatcher.matchProcess(newProcess5.getName(),
String.valueOf(eperson.getID().toString()),
newProcess5.getID(), parameters, ProcessStatus.SCHEDULED),
ProcessMatcher.matchProcess(newProcess6.getName(),
String.valueOf(eperson.getID().toString()),
newProcess6.getID(), parameters, ProcessStatus.SCHEDULED)
)))
.andExpect(jsonPath("$.page", is(
PageMatcher.pageEntryWithTotalPagesAndElements(0, 20, 1, 7))));
}
@Test
public void searchProcessTestByProcessStatus() throws Exception {
Process newProcess = ProcessBuilder.createProcess(context, eperson, "mock-script", parameters).build();
Process newProcess1 = ProcessBuilder.createProcess(context, eperson, "mock-script", parameters).build();
Process newProcess2 = ProcessBuilder.createProcess(context, eperson, "mock-script", parameters).build();
Process newProcess3 = ProcessBuilder.createProcess(context, eperson, "mock-script", parameters).build();
Process newProcess4 = ProcessBuilder.createProcess(context, eperson, "mock-script", parameters).build();
Process newProcess5 = ProcessBuilder.createProcess(context, eperson, "mock-script", parameters).build();
Process newProcess6 = ProcessBuilder.createProcess(context, eperson, "mock-script", parameters).build();
Process newProcess7 = ProcessBuilder.createProcess(context, admin, "mock-script", parameters)
.withProcessStatus(ProcessStatus.FAILED).build();
Process newProcess8 = ProcessBuilder.createProcess(context, admin, "mock-script", parameters).build();
Process newProcess9 = ProcessBuilder.createProcess(context, admin, "mock-script", parameters)
.withProcessStatus(ProcessStatus.FAILED).build();
String token = getAuthToken(admin.getEmail(), password);
getClient(token).perform(get("/api/system/processes/search/byProperty")
.param("processStatus", "FAILED"))
.andExpect(status().isOk())
.andExpect(jsonPath("$._embedded.processes", containsInAnyOrder(
ProcessMatcher.matchProcess(newProcess7.getName(),
String.valueOf(admin.getID().toString()),
newProcess7.getID(), parameters, ProcessStatus.FAILED),
ProcessMatcher.matchProcess(newProcess9.getName(),
String.valueOf(admin.getID().toString()),
newProcess9.getID(), parameters, ProcessStatus.FAILED)
)))
.andExpect(jsonPath("$.page", is(
PageMatcher.pageEntryWithTotalPagesAndElements(0, 20, 1, 2))));
}
@Test
public void searchProcessTestByScriptName() throws Exception {
Process newProcess = ProcessBuilder.createProcess(context, eperson, "mock-script", parameters).build();
Process newProcess1 = ProcessBuilder.createProcess(context, eperson, "mock-script", parameters).build();
Process newProcess2 = ProcessBuilder.createProcess(context, eperson, "mock-script", parameters).build();
Process newProcess3 = ProcessBuilder.createProcess(context, eperson, "mock-script", parameters).build();
Process newProcess4 = ProcessBuilder.createProcess(context, eperson, "mock-script", parameters).build();
Process newProcess5 = ProcessBuilder.createProcess(context, eperson, "mock-script", parameters).build();
Process newProcess6 = ProcessBuilder.createProcess(context, eperson, "mock-script", parameters).build();
Process newProcess7 = ProcessBuilder.createProcess(context, admin, "mock-script", parameters)
.withProcessStatus(ProcessStatus.FAILED).build();
Process newProcess8 = ProcessBuilder.createProcess(context, admin, "another-mock-script", parameters).build();
Process newProcess9 = ProcessBuilder.createProcess(context, admin, "mock-script", parameters)
.withProcessStatus(ProcessStatus.FAILED).build();
String token = getAuthToken(admin.getEmail(), password);
getClient(token).perform(get("/api/system/processes/search/byProperty")
.param("scriptName", "another-mock-script"))
.andExpect(status().isOk())
.andExpect(jsonPath("$._embedded.processes", containsInAnyOrder(
ProcessMatcher.matchProcess(newProcess8.getName(),
String.valueOf(admin.getID().toString()),
newProcess8.getID(), parameters, ProcessStatus.SCHEDULED)
)))
.andExpect(jsonPath("$.page", is(
PageMatcher.pageEntryWithTotalPagesAndElements(0, 20, 1, 1))));
}
@Test
public void searchProcessTestByScriptNameAndUserId() throws Exception {
Process newProcess = ProcessBuilder.createProcess(context, eperson, "mock-script", parameters).build();
Process newProcess1 = ProcessBuilder.createProcess(context, eperson, "mock-script", parameters).build();
Process newProcess2 = ProcessBuilder.createProcess(context, eperson, "mock-script", parameters).build();
Process newProcess3 = ProcessBuilder.createProcess(context, eperson, "mock-script", parameters).build();
Process newProcess4 = ProcessBuilder.createProcess(context, eperson, "another-mock-script", parameters).build();
Process newProcess5 = ProcessBuilder.createProcess(context, eperson, "another-mock-script", parameters).build();
Process newProcess6 = ProcessBuilder.createProcess(context, eperson, "another-mock-script", parameters).build();
Process newProcess7 = ProcessBuilder.createProcess(context, admin, "another-mock-script", parameters)
.withProcessStatus(ProcessStatus.FAILED).build();
Process newProcess8 = ProcessBuilder.createProcess(context, admin, "another-mock-script", parameters).build();
Process newProcess9 = ProcessBuilder.createProcess(context, admin, "mock-script", parameters)
.withProcessStatus(ProcessStatus.FAILED).build();
String token = getAuthToken(admin.getEmail(), password);
getClient(token).perform(get("/api/system/processes/search/byProperty")
.param("scriptName", "another-mock-script")
.param("userId", admin.getID().toString()))
.andExpect(status().isOk())
.andExpect(jsonPath("$._embedded.processes", containsInAnyOrder(
ProcessMatcher.matchProcess(newProcess7.getName(),
String.valueOf(admin.getID().toString()),
newProcess7.getID(), parameters, ProcessStatus.FAILED),
ProcessMatcher.matchProcess(newProcess8.getName(),
String.valueOf(admin.getID().toString()),
newProcess8.getID(), parameters, ProcessStatus.SCHEDULED)
)))
.andExpect(jsonPath("$.page", is(
PageMatcher.pageEntryWithTotalPagesAndElements(0, 20, 1, 2))));
}
@Test
public void searchProcessTestByUserIdAndProcessStatus() throws Exception {
Process newProcess = ProcessBuilder.createProcess(context, eperson, "mock-script", parameters).build();
Process newProcess1 = ProcessBuilder.createProcess(context, eperson, "mock-script", parameters).build();
Process newProcess2 = ProcessBuilder.createProcess(context, eperson, "mock-script", parameters).build();
Process newProcess3 = ProcessBuilder.createProcess(context, eperson, "mock-script", parameters)
.withProcessStatus(ProcessStatus.FAILED).build();
Process newProcess4 = ProcessBuilder.createProcess(context, eperson, "another-mock-script", parameters).build();
Process newProcess5 = ProcessBuilder.createProcess(context, eperson, "another-mock-script", parameters).build();
Process newProcess6 = ProcessBuilder.createProcess(context, eperson, "another-mock-script", parameters).build();
Process newProcess7 = ProcessBuilder.createProcess(context, admin, "another-mock-script", parameters).build();
Process newProcess8 = ProcessBuilder.createProcess(context, admin, "another-mock-script", parameters)
.withProcessStatus(ProcessStatus.FAILED).build();
Process newProcess9 = ProcessBuilder.createProcess(context, admin, "mock-script", parameters)
.withProcessStatus(ProcessStatus.FAILED).build();
String token = getAuthToken(admin.getEmail(), password);
getClient(token).perform(get("/api/system/processes/search/byProperty")
.param("processStatus", "FAILED")
.param("userId", admin.getID().toString()))
.andExpect(status().isOk())
.andExpect(jsonPath("$._embedded.processes", containsInAnyOrder(
ProcessMatcher.matchProcess(newProcess9.getName(),
String.valueOf(admin.getID().toString()),
newProcess9.getID(), parameters, ProcessStatus.FAILED),
ProcessMatcher.matchProcess(newProcess8.getName(),
String.valueOf(admin.getID().toString()),
newProcess8.getID(), parameters, ProcessStatus.FAILED)
)))
.andExpect(jsonPath("$.page", is(
PageMatcher.pageEntryWithTotalPagesAndElements(0, 20, 1, 2))));
}
@Test
public void searchProcessTestByUserIdAndProcessStatusAndScriptName() throws Exception {
Process newProcess = ProcessBuilder.createProcess(context, eperson, "mock-script", parameters).build();
Process newProcess1 = ProcessBuilder.createProcess(context, eperson, "mock-script", parameters)
.withProcessStatus(ProcessStatus.FAILED).build();
Process newProcess2 = ProcessBuilder.createProcess(context, eperson, "mock-script", parameters).build();
Process newProcess3 = ProcessBuilder.createProcess(context, eperson, "mock-script", parameters)
.withProcessStatus(ProcessStatus.FAILED).build();
Process newProcess4 = ProcessBuilder.createProcess(context, eperson, "mock-script", parameters).build();
Process newProcess5 = ProcessBuilder.createProcess(context, eperson, "another-mock-script", parameters).build();
Process newProcess6 = ProcessBuilder.createProcess(context, eperson, "another-mock-script", parameters).build();
Process newProcess7 = ProcessBuilder.createProcess(context, admin, "another-mock-script", parameters).build();
Process newProcess8 = ProcessBuilder.createProcess(context, admin, "another-mock-script", parameters)
.withProcessStatus(ProcessStatus.FAILED).build();
Process newProcess9 = ProcessBuilder.createProcess(context, admin, "mock-script", parameters).build();
String token = getAuthToken(admin.getEmail(), password);
getClient(token).perform(get("/api/system/processes/search/byProperty")
.param("processStatus", "FAILED")
.param("userId", eperson.getID().toString())
.param("scriptName", "mock-script"))
.andExpect(status().isOk())
.andExpect(jsonPath("$._embedded.processes", containsInAnyOrder(
ProcessMatcher.matchProcess("mock-script",
String.valueOf(eperson.getID().toString()),
newProcess1.getID(), parameters, ProcessStatus.FAILED),
ProcessMatcher.matchProcess("mock-script",
String.valueOf(eperson.getID().toString()),
newProcess3.getID(), parameters, ProcessStatus.FAILED)
)))
.andExpect(jsonPath("$.page", is(
PageMatcher.pageEntryWithTotalPagesAndElements(0, 20, 1, 2))));
}
@Test
public void searchProcessTestNoParametersBadRequest() throws Exception {
String token = getAuthToken(admin.getEmail(), password);
getClient(token).perform(get("/api/system/processes/search/byProperty"))
.andExpect(status().isBadRequest());
}
@Test
public void searchProcessTestUnparseableProcessStatusParamBadRequest() throws Exception {
String token = getAuthToken(admin.getEmail(), password);
getClient(token).perform(get("/api/system/processes/search/byProperty")
.param("processStatus", "not-a-valid-status"))
.andExpect(status().isBadRequest());
}
@Test
public void searchProcessTestInvalidEPersonUuid() throws Exception {
String token = getAuthToken(admin.getEmail(), password);
getClient(token).perform(get("/api/system/processes/search/byProperty")
.param("userId", UUID.randomUUID().toString()))
.andExpect(status().isBadRequest());
}
@Test
public void searchProcessTestByUserSortedOnStartTimeAsc() throws Exception {
Process newProcess1 = ProcessBuilder.createProcess(context, eperson, "mock-script", parameters)
.withStartAndEndTime("10/01/1990", "20/01/1990").build();
Process newProcess2 = ProcessBuilder.createProcess(context, eperson, "mock-script", parameters)
.withStartAndEndTime("11/01/1990", "19/01/1990").build();
Process newProcess3 = ProcessBuilder.createProcess(context, eperson, "mock-script", parameters)
.withStartAndEndTime("12/01/1990", "18/01/1990").build();
String token = getAuthToken(admin.getEmail(), password);
getClient(token).perform(get("/api/system/processes/search/byProperty")
.param("userId", eperson.getID().toString())
.param("sort", "startTime,asc"))
.andExpect(status().isOk())
.andExpect(jsonPath("$._embedded.processes", contains(
ProcessMatcher.matchProcess(newProcess1.getName(),
String.valueOf(eperson.getID().toString()),
newProcess1.getID(), parameters, ProcessStatus.SCHEDULED),
ProcessMatcher.matchProcess(newProcess2.getName(),
String.valueOf(eperson.getID().toString()),
newProcess2.getID(), parameters, ProcessStatus.SCHEDULED),
ProcessMatcher.matchProcess(newProcess3.getName(),
String.valueOf(eperson.getID().toString()),
newProcess3.getID(), parameters, ProcessStatus.SCHEDULED)
)))
.andExpect(jsonPath("$.page", is(
PageMatcher.pageEntryWithTotalPagesAndElements(0, 20, 1, 3))));
}
@Test
public void searchProcessTestByUserSortedOnStartTimeDesc() throws Exception {
Process newProcess1 = ProcessBuilder.createProcess(context, eperson, "mock-script", parameters)
.withStartAndEndTime("10/01/1990", "20/01/1990").build();
Process newProcess2 = ProcessBuilder.createProcess(context, eperson, "mock-script", parameters)
.withStartAndEndTime("11/01/1990", "19/01/1990").build();
Process newProcess3 = ProcessBuilder.createProcess(context, eperson, "mock-script", parameters)
.withStartAndEndTime("12/01/1990", "18/01/1990").build();
String token = getAuthToken(admin.getEmail(), password);
getClient(token).perform(get("/api/system/processes/search/byProperty")
.param("userId", eperson.getID().toString())
.param("sort", "startTime,desc"))
.andExpect(status().isOk())
.andExpect(jsonPath("$._embedded.processes", contains(
ProcessMatcher.matchProcess(newProcess3.getName(),
String.valueOf(eperson.getID().toString()),
newProcess3.getID(), parameters, ProcessStatus.SCHEDULED),
ProcessMatcher.matchProcess(newProcess2.getName(),
String.valueOf(eperson.getID().toString()),
newProcess2.getID(), parameters, ProcessStatus.SCHEDULED),
ProcessMatcher.matchProcess(newProcess1.getName(),
String.valueOf(eperson.getID().toString()),
newProcess1.getID(), parameters, ProcessStatus.SCHEDULED)
)))
.andExpect(jsonPath("$.page", is(
PageMatcher.pageEntryWithTotalPagesAndElements(0, 20, 1, 3))));
}
@Test
public void searchProcessTestByUserSortedOnEndTimeAsc() throws Exception {
Process newProcess1 = ProcessBuilder.createProcess(context, eperson, "mock-script", parameters)
.withStartAndEndTime("10/01/1990", "20/01/1990").build();
Process newProcess2 = ProcessBuilder.createProcess(context, eperson, "mock-script", parameters)
.withStartAndEndTime("11/01/1990", "19/01/1990").build();
Process newProcess3 = ProcessBuilder.createProcess(context, eperson, "mock-script", parameters)
.withStartAndEndTime("12/01/1990", "18/01/1990").build();
String token = getAuthToken(admin.getEmail(), password);
getClient(token).perform(get("/api/system/processes/search/byProperty")
.param("userId", eperson.getID().toString())
.param("sort", "endTime,asc"))
.andExpect(status().isOk())
.andExpect(jsonPath("$._embedded.processes", contains(
ProcessMatcher.matchProcess(newProcess3.getName(),
String.valueOf(eperson.getID().toString()),
newProcess3.getID(), parameters, ProcessStatus.SCHEDULED),
ProcessMatcher.matchProcess(newProcess2.getName(),
String.valueOf(eperson.getID().toString()),
newProcess2.getID(), parameters, ProcessStatus.SCHEDULED),
ProcessMatcher.matchProcess(newProcess1.getName(),
String.valueOf(eperson.getID().toString()),
newProcess1.getID(), parameters, ProcessStatus.SCHEDULED)
)))
.andExpect(jsonPath("$.page", is(
PageMatcher.pageEntryWithTotalPagesAndElements(0, 20, 1, 3))));
}
@Test
public void searchProcessTestByUserSortedOnEndTimeDesc() throws Exception {
Process newProcess1 = ProcessBuilder.createProcess(context, eperson, "mock-script", parameters)
.withStartAndEndTime("10/01/1990", "20/01/1990").build();
Process newProcess2 = ProcessBuilder.createProcess(context, eperson, "mock-script", parameters)
.withStartAndEndTime("11/01/1990", "19/01/1990").build();
Process newProcess3 = ProcessBuilder.createProcess(context, eperson, "mock-script", parameters)
.withStartAndEndTime("12/01/1990", "18/01/1990").build();
String token = getAuthToken(admin.getEmail(), password);
getClient(token).perform(get("/api/system/processes/search/byProperty")
.param("userId", eperson.getID().toString())
.param("sort", "endTime,desc"))
.andExpect(status().isOk())
.andExpect(jsonPath("$._embedded.processes", contains(
ProcessMatcher.matchProcess(newProcess1.getName(),
String.valueOf(eperson.getID().toString()),
newProcess1.getID(), parameters, ProcessStatus.SCHEDULED),
ProcessMatcher.matchProcess(newProcess2.getName(),
String.valueOf(eperson.getID().toString()),
newProcess2.getID(), parameters, ProcessStatus.SCHEDULED),
ProcessMatcher.matchProcess(newProcess3.getName(),
String.valueOf(eperson.getID().toString()),
newProcess3.getID(), parameters, ProcessStatus.SCHEDULED)
)))
.andExpect(jsonPath("$.page", is(
PageMatcher.pageEntryWithTotalPagesAndElements(0, 20, 1, 3))));
}
@Test
public void searchProcessTestByUserSortedOnMultipleBadRequest() throws Exception {
Process newProcess1 = ProcessBuilder.createProcess(context, eperson, "mock-script", parameters)
.withStartAndEndTime("10/01/1990", "20/01/1990").build();
Process newProcess2 = ProcessBuilder.createProcess(context, eperson, "mock-script", parameters)
.withStartAndEndTime("11/01/1990", "19/01/1990").build();
Process newProcess3 = ProcessBuilder.createProcess(context, eperson, "mock-script", parameters)
.withStartAndEndTime("12/01/1990", "18/01/1990").build();
String token = getAuthToken(admin.getEmail(), password);
getClient(token).perform(get("/api/system/processes/search/byProperty")
.param("userId", eperson.getID().toString())
.param("sort", "endTime,desc")
.param("sort", "startTime,desc"))
.andExpect(status().isBadRequest());
}
@Test
public void searchProcessTestByUserSortedOnDefault() throws Exception {
Process newProcess1 = ProcessBuilder.createProcess(context, eperson, "mock-script", parameters)
.withStartAndEndTime("10/01/1990", "20/01/1990").build();
Process newProcess2 = ProcessBuilder.createProcess(context, eperson, "mock-script", parameters)
.withStartAndEndTime("11/01/1990", "19/01/1990").build();
Process newProcess3 = ProcessBuilder.createProcess(context, eperson, "mock-script", parameters)
.withStartAndEndTime("12/01/1990", "18/01/1990").build();
String token = getAuthToken(admin.getEmail(), password);
getClient(token).perform(get("/api/system/processes/search/byProperty")
.param("userId", eperson.getID().toString()))
.andExpect(status().isOk())
.andExpect(jsonPath("$._embedded.processes", contains(
ProcessMatcher.matchProcess(newProcess3.getName(),
String.valueOf(eperson.getID().toString()),
newProcess3.getID(), parameters, ProcessStatus.SCHEDULED),
ProcessMatcher.matchProcess(newProcess2.getName(),
String.valueOf(eperson.getID().toString()),
newProcess2.getID(), parameters, ProcessStatus.SCHEDULED),
ProcessMatcher.matchProcess(newProcess1.getName(),
String.valueOf(eperson.getID().toString()),
newProcess1.getID(), parameters, ProcessStatus.SCHEDULED)
)))
.andExpect(jsonPath("$.page", is(
PageMatcher.pageEntryWithTotalPagesAndElements(0, 20, 1, 3))));
}
@Test
public void searchProcessTestByUserSortedOnNonExistingIsSortedAsDefault() throws Exception {
Process newProcess1 = ProcessBuilder.createProcess(context, eperson, "mock-script", parameters)
.withStartAndEndTime("10/01/1990", "20/01/1990").build();
Process newProcess2 = ProcessBuilder.createProcess(context, eperson, "mock-script", parameters)
.withStartAndEndTime("11/01/1990", "19/01/1990").build();
Process newProcess3 = ProcessBuilder.createProcess(context, eperson, "mock-script", parameters)
.withStartAndEndTime("12/01/1990", "18/01/1990").build();
String token = getAuthToken(admin.getEmail(), password);
getClient(token).perform(get("/api/system/processes/search/byProperty")
.param("userId", eperson.getID().toString())
.param("sort", "eaz,desc"))
.andExpect(status().isBadRequest());
}
@Test
public void getProcessOutput() throws Exception {
try (InputStream is = IOUtils.toInputStream("Test File For Process", CharEncoding.UTF_8)) {

View File

@@ -91,7 +91,11 @@ public class ScriptRestRepositoryIT extends AbstractControllerIntegrationTest {
ScriptMatcher.matchScript(scriptConfigurations.get(3).getName(),
scriptConfigurations.get(3).getDescription()),
ScriptMatcher.matchScript(scriptConfigurations.get(4).getName(),
scriptConfigurations.get(4).getDescription())
scriptConfigurations.get(4).getDescription()),
ScriptMatcher.matchScript(scriptConfigurations.get(5).getName(),
scriptConfigurations.get(5).getDescription()),
ScriptMatcher.matchScript(scriptConfigurations.get(6).getName(),
scriptConfigurations.get(6).getDescription())
)));
}

View File

@@ -12,7 +12,10 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import org.dspace.app.rest.test.AbstractControllerIntegrationTest;
import org.dspace.services.ConfigurationService;
import org.junit.Before;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
/**
* Integration test that cover ShibbolethRestController
@@ -21,6 +24,17 @@ import org.junit.Test;
*/
public class ShibbolethRestControllerIT extends AbstractControllerIntegrationTest {
@Autowired
ConfigurationService configurationService;
@Before
public void setup() throws Exception {
super.setUp();
configurationService.setProperty("rest.cors.allowed-origins",
"${dspace.ui.url}, http://anotherdspacehost:4000");
}
@Test
public void testRedirectToDefaultDspaceUrl() throws Exception {
String token = getAuthToken(eperson.getEmail(), password);
@@ -32,6 +46,7 @@ public class ShibbolethRestControllerIT extends AbstractControllerIntegrationTes
@Test
public void testRedirectToGivenTrustedUrl() throws Exception {
String token = getAuthToken(eperson.getEmail(), password);
getClient(token).perform(get("/api/authn/shibboleth")
@@ -40,6 +55,16 @@ public class ShibbolethRestControllerIT extends AbstractControllerIntegrationTes
.andExpect(redirectedUrl("http://localhost:8080/server/api/authn/status"));
}
@Test
public void testRedirectToAnotherGivenTrustedUrl() throws Exception {
String token = getAuthToken(eperson.getEmail(), password);
getClient(token).perform(get("/api/authn/shibboleth")
.param("redirectUrl", "http://anotherdspacehost:4000/home"))
.andExpect(status().is3xxRedirection())
.andExpect(redirectedUrl("http://anotherdspacehost:4000/home"));
}
@Test
public void testRedirectToGivenUntrustedUrl() throws Exception {
String token = getAuthToken(eperson.getEmail(), password);

View File

@@ -0,0 +1,41 @@
/**
* The contents of this file are subject to the license and copyright
* detailed in the LICENSE and NOTICE files at the root of the source
* tree and available online at
*
* http://www.dspace.org/license/
*/
package org.dspace.statistics.export.service;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.util.ArrayList;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
/**
* Mock OpenUrlService that will ensure that IRUS tracker does need to be contacted in order to test the functionality
*/
public class MockOpenUrlServiceImpl extends OpenUrlServiceImpl {
@Autowired
ArrayList testProcessedUrls;
/**
* Returns a response code to simulate contact to the external url
* When the url contains "fail", a fail code 500 will be returned
* Otherwise the success code 200 will be returned
* @param urlStr
* @return 200 or 500 depending on whether the "fail" keyword is present in the url
* @throws IOException
*/
protected int getResponseCodeFromUrl(final String urlStr) throws IOException {
if (StringUtils.contains(urlStr, "fail")) {
return HttpURLConnection.HTTP_INTERNAL_ERROR;
} else {
testProcessedUrls.add(urlStr);
return HttpURLConnection.HTTP_OK;
}
}
}

View File

@@ -18,21 +18,20 @@
</properties>
<profiles>
<!-- If Unit Testing is enabled, then setup the Unit Test Environment.
See also the 'skiptests' profile in Parent POM. -->
<!-- Setup the Unit Test Environment (when -DskipUnitTests=false) -->
<profile>
<id>test-environment</id>
<id>unit-test-environment</id>
<activation>
<activeByDefault>false</activeByDefault>
<property>
<name>skipTests</name>
<name>skipUnitTests</name>
<value>false</value>
</property>
</activation>
<build>
<plugins>
<!-- This plugin allows us to run a Groovy script in our Maven POM
(see: http://gmaven.codehaus.org/Executing+Groovy+Code )
(see: https://groovy.github.io/gmaven/groovy-maven-plugin/execute.html )
We are generating a OS-agnostic version (agnostic.build.dir) of
the ${project.build.directory} property (full path of target dir).
This is needed by the Surefire plugin (see below) to
@@ -59,21 +58,9 @@
</configuration>
</execution>
</executions>
<!-- TODO: When Groovy 3.0.0 is released, we should upgrade to fix this warning
(which appears in Maven builds) https://issues.apache.org/jira/browse/GROOVY-8339
The below (commented out) dependency should let us upgrade to v3.0.0 once released. -->
<!--<dependencies>
<dependency>
<groupId>org.codehaus.groovy</groupId>
<artifactId>groovy-all</artifactId>
<version>3.0.0</version>
<scope>runtime</scope>
<type>pom</type>
</dependency>
</dependencies>-->
</plugin>
<!-- Run Unit Testing! This plugin just kicks off the tests (when enabled). -->
<!-- Run Unit Testing! This plugin just kicks off the tests. -->
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>

View File

@@ -21,7 +21,8 @@ import org.springframework.context.ConfigurableApplicationContext;
public interface ServiceManager {
/**
* Get the application context
* Get the application context.
* @return the Spring application context.
*/
public ConfigurableApplicationContext getApplicationContext();
@@ -46,18 +47,14 @@ public interface ServiceManager {
* service manager objects. If using Spring this allows access to the
* underlying ApplicationContext object like so:<br>
* {@code getServiceByName(ApplicationContext.class.getName(), ApplicationContext.class);}
* If using Guice then the same applies like so:<br>
* {@code getServiceByName(Injector.class.getName(), Injector.class);}
* It is also possible to register a module and cause Guice to fill
* in any injected core services (see register method).
* </p>
*
* @param <T> Class type
* @param <T> Class type.
* @param name (optional) the unique name for this service.
* If null then the bean will be returned if there is only one
* service of this type.
* @param type the type for the requested service (this will typically be the interface class but can be concrete
* as well)
* @param type the type for the requested service (this will typically be
* the interface class but can be concrete as well).
* @return the service singleton OR null if none is found
*/
public <T> T getServiceByName(String name, Class<T> type);
@@ -90,12 +87,6 @@ public interface ServiceManager {
* down the context (webapp, etc.) that registered the service so
* that the full lifecycle completes correctly.
* </p>
* <p>
* <em>NOTE:</em> if using Guice it is possible to register a Guice
* Module as a service, which will not actually register it but will
* cause anything in the Module to have existing core services injected
* into it. You can use anything as the name in this case.
* </p>
*
* @param name the name of the service (must be unique)
* @param service the object to register as a singleton service
@@ -103,6 +94,14 @@ public interface ServiceManager {
*/
public void registerService(String name, Object service);
/**
* Add a singleton service at runtime, but do not inject dependencies.
* Typically used with a service instance that has already had all
* dependencies injected explicitly, for example in test code.
*
* @param name the name of the service (must be unique).
* @param service the instance to register as a singleton service.
*/
public void registerServiceNoAutowire(String name, Object service);
/**
@@ -112,7 +111,7 @@ public interface ServiceManager {
* except that it allows the core service manager to startup your
* service for you instead of you providing a service to the core.
* In general, it is better if you use your own service manager
* (like Spring or Guice) to manage your services and simply
* (like Spring) to manage your services and simply
* inherit the core service beans from the DSpace core service
* manager using the special capabilities of
* {@link #getServiceByName(String, Class)}.

View File

@@ -1,26 +0,0 @@
/**
* 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.kernel.mixins;
/**
* Allow the service or provider to be initialized when it is started
* by the service manager. After all injections are complete the init
* method will be called. Any initialization that a service needs to do
* should happen here.
*
* @author Aaron Zeckoski (azeckoski @ gmail.com)
*/
public interface InitializedService {
/**
* Executed after the service is created and all dependencies and
* configurations injected.
*/
public void init();
}

View File

@@ -1,26 +0,0 @@
/**
* 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.kernel.mixins;
/**
* Allow the service to be notified when the service manager is shutting
* it down. This will typically be called when the kernel is stopped or
* destroyed. Any cleanup that a service needs to do when it is
* shut down should happen here.
*
* @author Aaron Zeckoski (azeckoski @ gmail.com)
*/
public interface ShutdownService {
/**
* Called as the service manager is stopping or shutting down.
*/
public void shutdown();
}

View File

@@ -8,6 +8,7 @@
package org.dspace.servicemanager;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
@@ -17,23 +18,19 @@ import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import javax.annotation.PreDestroy;
import org.apache.commons.lang3.ArrayUtils;
import org.dspace.kernel.Activator;
import org.dspace.kernel.config.SpringLoader;
import org.dspace.kernel.mixins.ConfigChangeListener;
import org.dspace.kernel.mixins.InitializedService;
import org.dspace.kernel.mixins.ServiceChangeListener;
import org.dspace.kernel.mixins.ServiceManagerReadyAware;
import org.dspace.kernel.mixins.ShutdownService;
import org.dspace.servicemanager.config.DSpaceConfigurationService;
import org.dspace.servicemanager.spring.DSpaceBeanFactoryPostProcessor;
import org.dspace.services.ConfigurationService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanWrapper;
import org.springframework.beans.BeansException;
import org.springframework.beans.PropertyAccessorFactory;
import org.springframework.beans.factory.ListableBeanFactory;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
@@ -374,7 +371,18 @@ public final class DSpaceServiceManager implements ServiceManagerSystem {
applicationContext.getBeanFactory().destroyBean(name, beanInstance);
} catch (NoSuchBeanDefinitionException e) {
// this happens if the bean was registered manually (annoyingly)
DSpaceServiceManager.shutdownService(beanInstance);
for (final Method method : beanInstance.getClass().getMethods()) {
if (method.isAnnotationPresent(PreDestroy.class)) {
try {
method.invoke(beanInstance);
} catch (IllegalAccessException
| IllegalArgumentException
| InvocationTargetException ex) {
log.warn("Failed to call declared @PreDestroy method of {} service",
name, ex);
}
}
}
}
} catch (BeansException e) {
// nothing to do here, could not find the bean
@@ -578,79 +586,6 @@ public final class DSpaceServiceManager implements ServiceManagerSystem {
// STATICS
/**
* Configures a given service (i.e. bean) based on any DSpace configuration
* settings which refer to it by name. .
* <P>
* NOTE: Any configurations related to a specific service MUST be prefixed
* with the given service's name (e.g. [serviceName].setting = value)
* <P>
* This method logs an error if it encounters configs which refer to a
* service by name, but is an invalid setting for that service.
*
* @param serviceName the name of the service
* @param service the service object (which will be configured)
* @param config the running configuration service
*/
public static void configureService(String serviceName, Object service, ConfigurationService config) {
// Check if the configuration has any properties whose prefix
// corresponds to this service's name
List<String> configKeys = config.getPropertyKeys(serviceName);
if (configKeys != null && !configKeys.isEmpty()) {
BeanWrapper beanWrapper = PropertyAccessorFactory.forBeanPropertyAccess(service);
for (String key : configKeys) {
// Remove serviceName prefix from key. This is the name of the actual bean's parameter
// This removes the first x chars, where x is length of serviceName + 1 char
// Format of Key: [serviceName].[param]
String param = key.substring(serviceName.length() + 1);
try {
// Attempt to set this configuration on the given service's bean
beanWrapper.setPropertyValue(param, config.getProperty(key));
log.info("Set param (" + param + ") on service bean (" + serviceName + ") to: " + config
.getProperty(key));
} catch (RuntimeException e) {
// If an error occurs, just log it
log.error("Unable to set param (" + param + ") on service bean (" + serviceName + ") to: " + config
.getProperty(key), e);
}
}
}
}
/**
* Initializes a service if it asks to be initialized or does nothing.
*
* @param service any bean
* @throws IllegalStateException if the service init fails
*/
public static void initService(Object service) {
if (service instanceof InitializedService) {
try {
((InitializedService) service).init();
} catch (Exception e) {
throw new IllegalStateException(
"Failure attempting to initialize service (" + service + "): " + e.getMessage(), e);
}
}
}
/**
* Shuts down a service if it asks to be shutdown or does nothing.
*
* @param service any bean
*/
public static void shutdownService(Object service) {
if (service instanceof ShutdownService) {
try {
((ShutdownService) service).shutdown();
} catch (Exception e) {
log.error("Failure shutting down service: {}", service, e);
}
}
}
/**
* Build the complete list of Spring configuration paths, including
* hard-wired paths.
@@ -702,5 +637,4 @@ public final class DSpaceServiceManager implements ServiceManagerSystem {
}
return pathList.toArray(new String[pathList.size()]);
}
}

View File

@@ -1,72 +0,0 @@
/**
* 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.servicemanager.spring;
import org.dspace.servicemanager.DSpaceServiceManager;
import org.dspace.servicemanager.config.DSpaceConfigurationService;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.beans.factory.config.DestructionAwareBeanPostProcessor;
/**
* This processes beans as they are loaded into the system by spring.
* Allows us to handle the init method and also push config options.
*
* @author Aaron Zeckoski (azeckoski @ gmail.com)
*/
public final class DSpaceBeanPostProcessor implements BeanPostProcessor, DestructionAwareBeanPostProcessor {
private DSpaceConfigurationService configurationService;
@Autowired
public DSpaceBeanPostProcessor(DSpaceConfigurationService configurationService) {
if (configurationService == null) {
throw new IllegalArgumentException("configuration service cannot be null");
}
this.configurationService = configurationService;
}
/* (non-Javadoc)
* @see org.springframework.beans.factory.config.BeanPostProcessor#postProcessBeforeInitialization(java.lang
* .Object, java.lang.String)
*/
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName)
throws BeansException {
// Before initializing the service, first configure it based on any related settings in the configurationService
// NOTE: configs related to this bean MUST be prefixed with the bean's name (e.g. [beanName].setting = value)
DSpaceServiceManager.configureService(beanName, bean, configurationService);
return bean;
}
/* (non-Javadoc)
* @see org.springframework.beans.factory.config.BeanPostProcessor#postProcessAfterInitialization(java.lang
* .Object, java.lang.String)
*/
@Override
public Object postProcessAfterInitialization(Object bean, String beanName)
throws BeansException {
DSpaceServiceManager.initService(bean);
return bean;
}
/* (non-Javadoc)
* @see org.springframework.beans.factory.config.DestructionAwareBeanPostProcessor#postProcessBeforeDestruction
* (java.lang.Object, java.lang.String)
*/
@Override
public void postProcessBeforeDestruction(Object bean, String beanName) throws BeansException {
DSpaceServiceManager.shutdownService(bean);
}
// @Override
public boolean requiresDestruction(Object arg0) {
return false;
}
}

View File

@@ -16,14 +16,14 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import net.sf.ehcache.Ehcache;
import net.sf.ehcache.Statistics;
import org.dspace.kernel.ServiceManager;
import org.dspace.kernel.mixins.ConfigChangeListener;
import org.dspace.kernel.mixins.InitializedService;
import org.dspace.kernel.mixins.ServiceChangeListener;
import org.dspace.kernel.mixins.ShutdownService;
import org.dspace.providers.CacheProvider;
import org.dspace.services.CachingService;
import org.dspace.services.ConfigurationService;
@@ -38,7 +38,6 @@ import org.dspace.utils.servicemanager.ProviderHolder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Required;
/**
* Implementation of the core caching service, which is available for
@@ -47,16 +46,16 @@ import org.springframework.beans.factory.annotation.Required;
* @author Aaron Zeckoski (azeckoski @ gmail.com)
*/
public final class CachingServiceImpl
implements CachingService, InitializedService, ShutdownService, ConfigChangeListener, ServiceChangeListener {
implements CachingService, ConfigChangeListener, ServiceChangeListener {
private static Logger log = LoggerFactory.getLogger(CachingServiceImpl.class);
private static final Logger log = LoggerFactory.getLogger(CachingServiceImpl.class);
/**
* This is the event key for a full cache reset.
*/
protected static final String EVENT_RESET = "caching.reset";
/**
* The default config location.
* The default configuration location.
*/
protected static final String DEFAULT_CONFIG = "org/dspace/services/caching/ehcache-config.xml";
@@ -64,15 +63,14 @@ public final class CachingServiceImpl
* All the non-thread caches that we know about.
* Mostly used for tracking purposes.
*/
private Map<String, EhcacheCache> cacheRecord = new ConcurrentHashMap<String, EhcacheCache>();
private final Map<String, EhcacheCache> cacheRecord = new ConcurrentHashMap<>();
/**
* All the request caches. This is bound to the thread.
* The initial value of this TL is set automatically when it is
* created.
*/
private Map<String, Map<String, MapCache>> requestCachesMap = new ConcurrentHashMap<String, Map<String,
MapCache>>();
private final Map<String, Map<String, MapCache>> requestCachesMap = new ConcurrentHashMap<>();
/**
* @return the current request map which is bound to the current thread
@@ -84,7 +82,7 @@ public final class CachingServiceImpl
Map<String, MapCache> requestCaches = requestCachesMap.get(requestService.getCurrentRequestId());
if (requestCaches == null) {
requestCaches = new HashMap<String, MapCache>();
requestCaches = new HashMap<>();
requestCachesMap.put(requestService.getCurrentRequestId(), requestCaches);
}
@@ -94,6 +92,7 @@ public final class CachingServiceImpl
/**
* Unbinds all request caches. Destroys the caches completely.
*/
@Override
public void unbindRequestCaches() {
if (requestService != null) {
requestCachesMap.remove(requestService.getCurrentRequestId());
@@ -102,8 +101,7 @@ public final class CachingServiceImpl
private ConfigurationService configurationService;
@Autowired
@Required
@Autowired(required = true)
public void setConfigurationService(ConfigurationService configurationService) {
this.configurationService = configurationService;
}
@@ -117,8 +115,7 @@ public final class CachingServiceImpl
private ServiceManager serviceManager;
@Autowired
@Required
@Autowired(required = true)
public void setServiceManager(ServiceManager serviceManager) {
this.serviceManager = serviceManager;
}
@@ -128,8 +125,7 @@ public final class CachingServiceImpl
*/
protected net.sf.ehcache.CacheManager cacheManager;
@Autowired
@Required
@Autowired(required = true)
public void setCacheManager(net.sf.ehcache.CacheManager cacheManager) {
this.cacheManager = cacheManager;
}
@@ -145,7 +141,7 @@ public final class CachingServiceImpl
private int timeToIdleSecs = 600;
/**
* Reloads the config settings from the configuration service.
* Reloads the configuration settings from the configuration service.
*/
protected void reloadConfig() {
// Reload caching configurations, but have sane default values if unspecified in configs
@@ -160,7 +156,7 @@ public final class CachingServiceImpl
* WARNING: Do not change the order of these! <br/>
* If you do, you have to fix the {@link #reloadConfig()} method -AZ
*/
private String[] knownConfigNames = {
private final String[] knownConfigNames = {
"caching.use.clustering", // bool - whether to use clustering
"caching.default.use.disk.store", // whether to use the disk store
"caching.default.max.elements", // the maximum number of elements in memory, before they are evicted
@@ -172,13 +168,15 @@ public final class CachingServiceImpl
/* (non-Javadoc)
* @see org.dspace.kernel.mixins.ConfigChangeListener#notifyForConfigNames()
*/
@Override
public String[] notifyForConfigNames() {
return knownConfigNames == null ? null : knownConfigNames.clone();
return knownConfigNames.clone();
}
/* (non-Javadoc)
* @see org.dspace.kernel.mixins.ConfigChangeListener#configurationChanged(java.util.List, java.util.Map)
*/
@Override
public void configurationChanged(List<String> changedSettingNames, Map<String, String> changedSettings) {
reloadConfig();
}
@@ -187,7 +185,7 @@ public final class CachingServiceImpl
* This will make it easier to handle a provider which might go away
* because the classloader is gone.
*/
private ProviderHolder<CacheProvider> provider = new ProviderHolder<CacheProvider>();
private final ProviderHolder<CacheProvider> provider = new ProviderHolder<>();
public CacheProvider getCacheProvider() {
return provider.getProvider();
@@ -211,6 +209,7 @@ public final class CachingServiceImpl
/* (non-Javadoc)
* @see org.dspace.kernel.mixins.ServiceChangeListener#notifyForTypes()
*/
@Override
public Class<?>[] notifyForTypes() {
return new Class<?>[] {CacheProvider.class};
}
@@ -219,6 +218,7 @@ public final class CachingServiceImpl
* @see org.dspace.kernel.mixins.ServiceChangeListener#serviceRegistered(java.lang.String, java.lang.Object, java
* .util.List)
*/
@Override
public void serviceRegistered(String serviceName, Object service, List<Class<?>> implementedTypes) {
provider.setProvider((CacheProvider) service);
}
@@ -226,14 +226,12 @@ public final class CachingServiceImpl
/* (non-Javadoc)
* @see org.dspace.kernel.mixins.ServiceChangeListener#serviceUnregistered(java.lang.String, java.lang.Object)
*/
@Override
public void serviceUnregistered(String serviceName, Object service) {
provider.setProvider(null);
}
/* (non-Javadoc)
* @see org.dspace.kernel.mixins.InitializedService#init()
*/
@Override
@PostConstruct
public void init() {
log.info("init()");
// get settings
@@ -256,17 +254,13 @@ public final class CachingServiceImpl
log.info("Caching service initialized:\n" + getStatus(null));
}
/* (non-Javadoc)
* @see org.dspace.kernel.mixins.ShutdownService#shutdown()
*/
@PreDestroy
public void shutdown() {
log.info("destroy()");
// for some reason this causes lots of errors so not using it for now -AZ
//ehCacheManagementService.dispose();
try {
if (cacheRecord != null) {
cacheRecord.clear();
}
cacheRecord.clear();
} catch (RuntimeException e) {
// whatever
}
@@ -290,6 +284,7 @@ public final class CachingServiceImpl
/* (non-Javadoc)
* @see org.dspace.services.CachingService#destroyCache(java.lang.String)
*/
@Override
public void destroyCache(String cacheName) {
if (cacheName == null || "".equals(cacheName)) {
throw new IllegalArgumentException("cacheName cannot be null or empty string");
@@ -319,6 +314,7 @@ public final class CachingServiceImpl
/* (non-Javadoc)
* @see org.dspace.services.CachingService#getCache(java.lang.String, org.dspace.services.model.CacheConfig)
*/
@Override
public Cache getCache(String cacheName, CacheConfig cacheConfig) {
Cache cache = null;
@@ -359,8 +355,9 @@ public final class CachingServiceImpl
/* (non-Javadoc)
* @see org.dspace.services.CachingService#getCaches()
*/
@Override
public List<Cache> getCaches() {
List<Cache> caches = new ArrayList<Cache>(this.cacheRecord.values());
List<Cache> caches = new ArrayList<>(this.cacheRecord.values());
if (getCacheProvider() != null) {
try {
caches.addAll(getCacheProvider().getCaches());
@@ -377,6 +374,7 @@ public final class CachingServiceImpl
/* (non-Javadoc)
* @see org.dspace.services.CachingService#getStatus(java.lang.String)
*/
@Override
public String getStatus(String cacheName) {
final StringBuilder sb = new StringBuilder();
@@ -433,6 +431,7 @@ public final class CachingServiceImpl
/* (non-Javadoc)
* @see org.dspace.services.CachingService#resetCaches()
*/
@Override
public void resetCaches() {
log.debug("resetCaches()");
@@ -468,7 +467,7 @@ public final class CachingServiceImpl
if (sorted) {
Arrays.sort(cacheNames);
}
final List<Ehcache> caches = new ArrayList<Ehcache>(cacheNames.length);
final List<Ehcache> caches = new ArrayList<>(cacheNames.length);
for (String cacheName : cacheNames) {
caches.add(cacheManager.getEhcache(cacheName));
}
@@ -612,6 +611,7 @@ public final class CachingServiceImpl
public static final class NameComparator implements Comparator<Cache>, Serializable {
public static final long serialVersionUID = 1l;
@Override
public int compare(Cache o1, Cache o2) {
return o1.getName().compareTo(o2.getName());
}
@@ -619,22 +619,25 @@ public final class CachingServiceImpl
private class CachingServiceRequestInterceptor implements RequestInterceptor {
@Override
public void onStart(String requestId) {
if (requestId != null) {
Map<String, MapCache> requestCaches = requestCachesMap.get(requestId);
if (requestCaches == null) {
requestCaches = new HashMap<String, MapCache>();
requestCaches = new HashMap<>();
requestCachesMap.put(requestId, requestCaches);
}
}
}
@Override
public void onEnd(String requestId, boolean succeeded, Exception failure) {
if (requestId != null) {
requestCachesMap.remove(requestId);
}
}
@Override
public int getOrder() {
return 1;
}

View File

@@ -8,6 +8,7 @@
package org.dspace.services.email;
import java.util.Properties;
import javax.annotation.PostConstruct;
import javax.mail.Authenticator;
import javax.mail.PasswordAuthentication;
import javax.mail.Session;
@@ -17,14 +18,12 @@ import javax.naming.NamingException;
import javax.naming.NoInitialContextException;
import org.apache.commons.lang3.StringUtils;
import org.dspace.kernel.mixins.InitializedService;
import org.dspace.services.ConfigurationService;
import org.dspace.services.EmailService;
import org.dspace.services.factory.DSpaceServicesFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Required;
/**
* Provides mail sending services through JavaMail. If a {@link javax.mail.Session}
@@ -35,7 +34,7 @@ import org.springframework.beans.factory.annotation.Required;
*/
public class EmailServiceImpl
extends Authenticator
implements EmailService, InitializedService {
implements EmailService {
private static final Logger logger = LoggerFactory.getLogger(EmailServiceImpl.class);
private Session session = null;
@@ -47,8 +46,7 @@ public class EmailServiceImpl
*
* @param cfg the configurationService object
*/
@Autowired
@Required
@Autowired(required = true)
public void setCfg(ConfigurationService cfg) {
this.cfg = cfg;
}
@@ -63,7 +61,7 @@ public class EmailServiceImpl
return session;
}
@Override
@PostConstruct
public void init() {
// See if there is already a Session in our environment
String sessionName = cfg.getProperty("mail.session.name");

View File

@@ -12,9 +12,9 @@ import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.concurrent.ConcurrentHashMap;
import javax.annotation.PreDestroy;
import org.apache.commons.lang3.ArrayUtils;
import org.dspace.kernel.mixins.ShutdownService;
import org.dspace.services.CachingService;
import org.dspace.services.EventService;
import org.dspace.services.RequestService;
@@ -36,7 +36,7 @@ import org.springframework.beans.factory.annotation.Autowired;
*
* @author Aaron Zeckoski (azeckoski@gmail.com) - azeckoski - 4:02:31 PM Nov 19, 2008
*/
public final class SystemEventService implements EventService, ShutdownService {
public final class SystemEventService implements EventService {
private final Logger log = LoggerFactory.getLogger(SystemEventService.class);
@@ -45,7 +45,7 @@ public final class SystemEventService implements EventService, ShutdownService {
/**
* Map for holding onto the listeners which is ClassLoader safe.
*/
private Map<String, EventListener> listenersMap = new ConcurrentHashMap<String, EventListener>();
private final Map<String, EventListener> listenersMap = new ConcurrentHashMap<>();
private final RequestService requestService;
private final CachingService cachingService;
@@ -64,9 +64,7 @@ public final class SystemEventService implements EventService, ShutdownService {
this.requestService.registerRequestInterceptor(this.requestInterceptor);
}
/* (non-Javadoc)
* @see org.dspace.kernel.mixins.ShutdownService#shutdown()
*/
@PreDestroy
public void shutdown() {
this.requestInterceptor = null; // clear the interceptor
this.listenersMap.clear();
@@ -76,6 +74,7 @@ public final class SystemEventService implements EventService, ShutdownService {
/* (non-Javadoc)
* @see org.dspace.services.EventService#fireEvent(org.dspace.services.model.Event)
*/
@Override
public void fireEvent(Event event) {
validateEvent(event);
// check scopes for this event
@@ -97,6 +96,7 @@ public final class SystemEventService implements EventService, ShutdownService {
/* (non-Javadoc)
* @see org.dspace.services.EventService#queueEvent(org.dspace.services.model.Event)
*/
@Override
public void queueEvent(Event event) {
validateEvent(event);
@@ -118,6 +118,7 @@ public final class SystemEventService implements EventService, ShutdownService {
/* (non-Javadoc)
* @see org.dspace.services.EventService#registerEventListener(org.dspace.services.model.EventListener)
*/
@Override
public void registerEventListener(EventListener listener) {
if (listener == null) {
throw new IllegalArgumentException("Cannot register a listener that is null");
@@ -293,7 +294,7 @@ public final class SystemEventService implements EventService, ShutdownService {
return allowName && allowResource;
}
private Random random = new Random();
private final Random random = new Random();
/**
* Generate an event ID used to identify and track this event uniquely.
@@ -316,6 +317,7 @@ public final class SystemEventService implements EventService, ShutdownService {
* @see org.dspace.services.model.RequestInterceptor#onStart(java.lang.String, org.dspace.services.model
* .Session)
*/
@Override
public void onStart(String requestId) {
// nothing to really do here unless we decide we should purge out any existing events? -AZ
}
@@ -324,6 +326,7 @@ public final class SystemEventService implements EventService, ShutdownService {
* @see org.dspace.services.model.RequestInterceptor#onEnd(java.lang.String, org.dspace.services.model
* .Session, boolean, java.lang.Exception)
*/
@Override
public void onEnd(String requestId, boolean succeeded, Exception failure) {
if (succeeded) {
int fired = fireQueuedEvents();
@@ -338,6 +341,7 @@ public final class SystemEventService implements EventService, ShutdownService {
/* (non-Javadoc)
* @see org.dspace.kernel.mixins.OrderedService#getOrder()
*/
@Override
public int getOrder() {
return 20; // this should fire pretty late
}

View File

@@ -15,12 +15,12 @@ import java.util.Map;
import java.util.Objects;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import org.apache.commons.lang3.StringUtils;
import org.dspace.kernel.mixins.InitializedService;
import org.dspace.kernel.mixins.ShutdownService;
import org.dspace.services.ConfigurationService;
import org.dspace.services.RequestService;
import org.dspace.services.model.Request;
@@ -32,7 +32,6 @@ import org.dspace.utils.servicemanager.OrderedServiceComparator;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Required;
/**
@@ -45,14 +44,13 @@ import org.springframework.beans.factory.annotation.Required;
* @author Aaron Zeckoski (azeckoski @ gmail.com)
* @author Tom Desair (tom dot desair at atmire dot com)
*/
public final class StatelessRequestServiceImpl implements RequestService, InitializedService, ShutdownService {
public final class StatelessRequestServiceImpl implements RequestService {
private static Logger log = LoggerFactory.getLogger(StatelessRequestServiceImpl.class);
private static final Logger log = LoggerFactory.getLogger(StatelessRequestServiceImpl.class);
private ConfigurationService configurationService;
@Autowired
@Required
@Autowired(required = true)
public void setConfigurationService(ConfigurationService configurationService) {
this.configurationService = configurationService;
}
@@ -60,18 +58,14 @@ public final class StatelessRequestServiceImpl implements RequestService, Initia
/**
* map for holding onto the request interceptors which is classloader safe.
*/
private Map<String, RequestInterceptor> interceptorsMap = new HashMap<String, RequestInterceptor>();
private final Map<String, RequestInterceptor> interceptorsMap = new HashMap<>();
/* (non-Javadoc)
* @see org.dspace.kernel.mixins.InitializedService#init()
*/
@PostConstruct
public void init() {
log.info("init");
}
/* (non-Javadoc)
* @see org.dspace.kernel.mixins.ShutdownService#shutdown()
*/
@PreDestroy
public void shutdown() {
log.info("shutdown");
clear();
@@ -90,6 +84,7 @@ public final class StatelessRequestServiceImpl implements RequestService, Initia
/* (non-Javadoc)
* @see org.dspace.services.RequestService#startRequest()
*/
@Override
public String startRequest() {
return startRequest(new InternalRequestImpl());
}
@@ -97,6 +92,7 @@ public final class StatelessRequestServiceImpl implements RequestService, Initia
/* (non-Javadoc)
* @see org.dspace.services.RequestService#startRequest()
*/
@Override
public String startRequest(ServletRequest request, ServletResponse response) {
return startRequest(new HttpRequestImpl(request, response));
}
@@ -128,6 +124,7 @@ public final class StatelessRequestServiceImpl implements RequestService, Initia
/* (non-Javadoc)
* @see org.dspace.services.RequestService#endRequest(java.lang.Exception)
*/
@Override
public String endRequest(Exception failure) {
String requestId = null;
try {
@@ -175,7 +172,7 @@ public final class StatelessRequestServiceImpl implements RequestService, Initia
* @return the current list of interceptors in the correct order
*/
private List<RequestInterceptor> getInterceptors(boolean reverse) {
ArrayList<RequestInterceptor> l = new ArrayList<RequestInterceptor>(this.interceptorsMap.values());
ArrayList<RequestInterceptor> l = new ArrayList<>(this.interceptorsMap.values());
OrderedServiceComparator comparator = new OrderedServiceComparator();
Collections.sort(l, comparator);
if (reverse) {
@@ -187,6 +184,7 @@ public final class StatelessRequestServiceImpl implements RequestService, Initia
/* (non-Javadoc)
* @see org.dspace.services.RequestService#registerRequestListener(org.dspace.services.model.RequestInterceptor)
*/
@Override
public void registerRequestInterceptor(RequestInterceptor interceptor) {
if (interceptor == null) {
throw new IllegalArgumentException("Cannot register an interceptor that is null");
@@ -198,11 +196,12 @@ public final class StatelessRequestServiceImpl implements RequestService, Initia
this.interceptorsMap.put(key, interceptor);
}
/**
/*
* (non-Javadoc)
*
* @see org.dspace.services.RequestService#getCurrentUserId()
*/
@Override
public String getCurrentUserId() {
Request currentRequest = getCurrentRequest();
if (currentRequest == null) {
@@ -212,11 +211,12 @@ public final class StatelessRequestServiceImpl implements RequestService, Initia
}
}
/**
/*
* (non-Javadoc)
*
* @see org.dspace.services.RequestService#setCurrentUserId()
*/
@Override
public void setCurrentUserId(UUID epersonId) {
Request currentRequest = getCurrentRequest();
if (currentRequest != null) {
@@ -227,6 +227,7 @@ public final class StatelessRequestServiceImpl implements RequestService, Initia
/* (non-Javadoc)
* @see org.dspace.services.RequestService#getCurrentRequestId()
*/
@Override
public String getCurrentRequestId() {
Request req = requests.getCurrent();
if (req != null) {
@@ -239,6 +240,7 @@ public final class StatelessRequestServiceImpl implements RequestService, Initia
/* (non-Javadoc)
* @see org.dspace.services.RequestService#getCurrentRequest()
*/
@Override
public Request getCurrentRequest() {
return requests.getCurrent();
}
@@ -247,7 +249,7 @@ public final class StatelessRequestServiceImpl implements RequestService, Initia
* Class to hold the current request. Uses Map keyed on current thread id.
*/
private class RequestHolder {
Map<Long, Request> requestMap = new ConcurrentHashMap<Long, Request>();
Map<Long, Request> requestMap = new ConcurrentHashMap<>();
Request getCurrent() {
return requestMap.get(Thread.currentThread().getId());
@@ -298,5 +300,5 @@ public final class StatelessRequestServiceImpl implements RequestService, Initia
}
}
private RequestHolder requests = new RequestHolder();
private final RequestHolder requests = new RequestHolder();
}

View File

@@ -19,7 +19,4 @@
<context:annotation-config/> <!-- allows us to use spring annotations in beans -->
<!-- the bean processor interceptor -->
<bean class="org.dspace.servicemanager.spring.DSpaceBeanPostProcessor"/>
</beans>

View File

@@ -16,9 +16,9 @@ import static org.junit.Assert.fail;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import org.dspace.kernel.mixins.InitializedService;
import org.dspace.kernel.mixins.ShutdownService;
import org.dspace.servicemanager.config.DSpaceConfigurationService;
import org.dspace.servicemanager.example.ConcreteExample;
import org.dspace.servicemanager.fakeservices.FakeService1;
@@ -28,7 +28,7 @@ import org.junit.Before;
import org.junit.Test;
/**
* testing the main dspace service manager
* Testing the main DSpace service manager.
*
* @author Aaron Zeckoski (azeckoski @ gmail.com)
*/
@@ -42,10 +42,6 @@ public class DSpaceServiceManagerTest {
public void init() {
configurationService = new DSpaceConfigurationService();
// Set some sample configurations relating to services/beans
configurationService.loadConfig(SampleAnnotationBean.class.getName() + ".sampleValue", "beckyz");
configurationService.loadConfig("fakeBean.fakeParam", "beckyz");
dsm = new DSpaceServiceManager(configurationService, SPRING_TEST_CONFIG_FILE);
}
@@ -175,16 +171,6 @@ public class DSpaceServiceManagerTest {
ConcreteExample concrete = dsm.getServiceByName(ConcreteExample.class.getName(), ConcreteExample.class);
assertNotNull(concrete);
assertEquals("azeckoski", concrete.getName());
concrete = null;
// initialize a SampleAnnotationBean
SampleAnnotationBean sab = dsm
.getServiceByName(SampleAnnotationBean.class.getName(), SampleAnnotationBean.class);
assertNotNull(sab);
// Based on the configuration for "sampleValue" in the init() method above,
// a value should be pre-set!
assertEquals("beckyz", sab.getSampleValue());
sab = null;
SpringAnnotationBean spr = dsm.getServiceByName(
SpringAnnotationBean.class.getName(), SpringAnnotationBean.class);
@@ -192,7 +178,6 @@ public class DSpaceServiceManagerTest {
assertEquals("azeckoski", spr.getConcreteName());
assertEquals("aaronz", spr.getExampleName());
assertEquals(null, spr.getSampleValue());
spr = null;
}
/**
@@ -271,25 +256,6 @@ public class DSpaceServiceManagerTest {
// TODO need to do a better test here
}
@Test
public void testInitAndShutdown() {
dsm.startup();
SampleAnnotationBean sab = dsm
.getServiceByName(SampleAnnotationBean.class.getName(), SampleAnnotationBean.class);
assertNotNull(sab);
assertEquals(1, sab.initCounter);
sab = null;
TestService ts = new TestService();
assertEquals(0, ts.value);
dsm.registerService(TestService.class.getName(), ts);
assertEquals(1, ts.value);
dsm.unregisterService(TestService.class.getName());
assertEquals(2, ts.value);
ts = null;
}
@Test
public void testRegisterProviderLifecycle() {
dsm.startup();
@@ -321,16 +287,16 @@ public class DSpaceServiceManagerTest {
properties = null;
}
public static class TestService implements InitializedService, ShutdownService {
public static class TestService {
public int value = 0;
@Override
@PostConstruct
public void init() {
value++;
}
@Override
@PreDestroy
public void shutdown() {
value++;
}

View File

@@ -7,44 +7,45 @@
*/
package org.dspace.servicemanager;
import org.dspace.kernel.mixins.InitializedService;
import org.dspace.kernel.mixins.ShutdownService;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import org.dspace.servicemanager.example.ConcreteExample;
import org.dspace.servicemanager.example.ServiceExample;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Required;
import org.springframework.stereotype.Service;
/**
* This bean is a simple example of a bean which is annotated as a spring bean and should be found when the AC starts up
* This bean is a simple example of a bean which is annotated as a Spring Bean
* and should be found when the AC starts up.
*
* @author Aaron Zeckoski (azeckoski @ gmail.com)
*/
@Service
public class SampleAnnotationBean implements InitializedService, ShutdownService {
public class SampleAnnotationBean {
public int initCounter = 0;
@PostConstruct
public void init() {
initCounter++;
}
@PreDestroy
public void shutdown() {
initCounter++;
}
private ServiceExample serviceExample;
@Autowired
@Required
@Autowired(required = true)
public void setServiceExample(ServiceExample serviceExample) {
this.serviceExample = serviceExample;
}
private ConcreteExample concreteExample;
@Autowired
@Required
@Autowired(required = true)
public void setConcreteExample(ConcreteExample concreteExample) {
this.concreteExample = concreteExample;
}

View File

@@ -10,23 +10,23 @@ package org.dspace.servicemanager.fakeservices;
import java.io.Serializable;
import java.util.List;
import java.util.Map;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import org.dspace.kernel.mixins.ConfigChangeListener;
import org.dspace.kernel.mixins.InitializedService;
import org.dspace.kernel.mixins.ServiceChangeListener;
import org.dspace.kernel.mixins.ShutdownService;
import org.dspace.services.ConfigurationService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Required;
/**
* This is just testing a fake service and running it through some paces to see if the lifecycles work
* This is just testing a fake service and running it through some paces to see
* if the lifecycles work.
*
* @author Aaron Zeckoski (azeckoski @ gmail.com)
*/
public class FakeService1 implements ConfigChangeListener, ServiceChangeListener,
InitializedService, ShutdownService, Serializable {
Serializable {
private static final long serialVersionUID = 1L;
public int triggers = 0;
@@ -56,8 +56,7 @@ public class FakeService1 implements ConfigChangeListener, ServiceChangeListener
private ConfigurationService configurationService;
@Autowired
@Required
@Autowired(required = true)
public void setConfigurationService(ConfigurationService configurationService) {
this.configurationService = configurationService;
}
@@ -69,6 +68,7 @@ public class FakeService1 implements ConfigChangeListener, ServiceChangeListener
/* (non-Javadoc)
* @see org.dspace.kernel.mixins.ConfigChangeListener#configurationChanged(java.util.List, java.util.Map)
*/
@Override
public void configurationChanged(List<String> changedSettingNames,
Map<String, String> changedSettings) {
something = "config:" + changedSettings.get("azeckoski.FakeService1.something");
@@ -79,6 +79,7 @@ public class FakeService1 implements ConfigChangeListener, ServiceChangeListener
* @see org.dspace.kernel.mixins.ServiceChangeListener#serviceRegistered(java.lang.String, java.lang.Object, java
* .util.List)
*/
@Override
public void serviceRegistered(String serviceName, Object service,
List<Class<?>> implementedTypes) {
something = "registered:" + serviceName;
@@ -88,22 +89,19 @@ public class FakeService1 implements ConfigChangeListener, ServiceChangeListener
/* (non-Javadoc)
* @see org.dspace.kernel.mixins.ServiceChangeListener#serviceUnregistered(java.lang.String, java.lang.Object)
*/
@Override
public void serviceUnregistered(String serviceName, Object service) {
something = "unregistered:" + serviceName;
triggers++;
}
/* (non-Javadoc)
* @see org.dspace.kernel.mixins.InitializedService#init()
*/
@PostConstruct
public void init() {
something = "init";
triggers = 1; // RESET to 1
}
/* (non-Javadoc)
* @see org.dspace.kernel.mixins.ShutdownService#shutdown()
*/
@PreDestroy
public void shutdown() {
something = "shutdown";
triggers++;
@@ -112,6 +110,7 @@ public class FakeService1 implements ConfigChangeListener, ServiceChangeListener
/* (non-Javadoc)
* @see org.dspace.kernel.mixins.ConfigChangeListener#notifyForConfigNames()
*/
@Override
public String[] notifyForConfigNames() {
return null; // ALL
}
@@ -119,6 +118,7 @@ public class FakeService1 implements ConfigChangeListener, ServiceChangeListener
/* (non-Javadoc)
* @see org.dspace.kernel.mixins.ServiceChangeListener#notifyForTypes()
*/
@Override
public Class<?>[] notifyForTypes() {
return null; // ALL
}

View File

@@ -8,16 +8,14 @@
package org.dspace.servicemanager.fakeservices;
import java.io.Serializable;
import org.dspace.kernel.mixins.InitializedService;
import javax.annotation.PostConstruct;
/**
* Simple fake service 2
*
* @author Aaron Zeckoski (azeckoski @ gmail.com)
*/
public class FakeService2 implements InitializedService, Comparable<FakeService2>, Serializable {
public class FakeService2 implements Comparable<FakeService2>, Serializable {
private static final long serialVersionUID = 1L;
public String data = "data";
@@ -30,13 +28,12 @@ public class FakeService2 implements InitializedService, Comparable<FakeService2
this.data = data;
}
/* (non-Javadoc)
* @see org.dspace.kernel.mixins.InitializedService#init()
*/
@PostConstruct
public void init() {
data = "initData";
}
@Override
public int compareTo(FakeService2 o) {
return data.compareTo(o.data);
}

View File

@@ -2026,3 +2026,4 @@ include = ${module_dir}/translator.cfg
include = ${module_dir}/usage-statistics.cfg
include = ${module_dir}/versioning.cfg
include = ${module_dir}/workflow.cfg
include = ${module_dir}/irus-statistics.cfg

View File

@@ -82,5 +82,7 @@
<mapping class="org.dspace.xmlworkflow.storedcomponents.WorkflowItemRole"/>
<mapping class="org.dspace.xmlworkflow.storedcomponents.XmlWorkflowItem"/>
<mapping class="org.dspace.statistics.export.OpenURLTracker"/>
</session-factory>
</hibernate-configuration>

View File

@@ -187,6 +187,13 @@
<class>org.dspace.administer.RegistryLoader</class>
</step>
</command>
<command>
<name>retry-tracker</name>
<description>Retry all failed commits to the OpenURLTracker</description>
<step>
<class>org.dspace.statistics.export.RetryFailedOpenUrlTracker</class>
</step>
</command>
<command>
<name>solr-export-statistics</name>
<description>Export usage statistics data from Solr for back-up purposes</description>

View File

@@ -0,0 +1,35 @@
# Enable the IRUS tracker. By default or when omitted, the tracker will be disabled
irus.statistics.tracker.enabled = false
# OPTIONAL metadata field used for filtering.
# If items with specific values for the "dc.type" field should be excluded, "dc.type" should be placed here.
# This should comply to the syntax schema.element.qualified or schema.element if the qualifier is null.
# irus.statistics.tracker.type-field = dc.type
# If "tracker.type-field" is set, the list of values must be defined in "tracker.type-value".
# This lists a comma separated list of values that will be excluded for the given field.
# irus.statistics.tracker.type-value = Article, Postprint
# This lists a comma separated list of entities that will be included
# When no list is provided, the default value "Publication" will be used
# irus.statistics.tracker.entity-types = Publication
# Set the tracker environment to "test" or "production". Defaults to "test" if empty.
# The URL used by the test environment can be configured in property tracker.testurl
# The URL used by the production environment can be configured in property tracker.produrl
irus.statistics.tracker.environment = test
# The url used to test the submission of tracking info to.
irus.statistics.tracker.testurl = https://irus.jisc.ac.uk/counter/test/
# The base url for submitting the tracking info to.
irus.statistics.tracker.produrl = https://irus.jisc.ac.uk/counter/
# Identifies data as OpenURL 1.0
irus.statistics.tracker.urlversion = Z39.88-2004
# Add the agentregex configuration below uncommented to local.cfg to include the bot agents list by
# Project COUNTER when filtering bots in DSpace. The agents file is downloaded by the Apache ant
# stage of the build process.
# Location of the COUNTER agents file
# irus.statistics.spider.agentregex.regexfile = ${dspace.dir}/config/spiders/agents/COUNTER_Robots_list.txt
# External URL to COUNTER the agents file
# irus.statistics.spider.agentregex.url = https://raw.githubusercontent.com/atmire/COUNTER-Robots/master/generated/COUNTER_Robots_list.txt

View File

@@ -62,6 +62,7 @@
<bean class="org.dspace.xmlworkflow.storedcomponents.dao.impl.PoolTaskDAOImpl"/>
<bean class="org.dspace.xmlworkflow.storedcomponents.dao.impl.WorkflowItemRoleDAOImpl"/>
<bean class="org.dspace.xmlworkflow.storedcomponents.dao.impl.XmlWorkflowItemDAOImpl"/>
<bean class="org.dspace.statistics.export.dao.impl.OpenURLTrackerDAOImpl"/>

View File

@@ -49,4 +49,6 @@
<bean id="indexObjectFactoryFactory" class="org.dspace.discovery.indexobject.factory.IndexObjectFactoryFactoryImpl"/>
<bean id="openURLTrackerLoggerServiceFactory" class="org.dspace.statistics.export.factory.OpenURLTrackerLoggerServiceFactoryImpl"/>
</beans>

View File

@@ -0,0 +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"
default-lazy-init="true">
<bean class="org.dspace.statistics.export.FailedOpenURLTrackerServiceImpl"/>
<bean class="org.dspace.statistics.export.service.OpenUrlServiceImpl"/>
</beans>

View File

@@ -18,6 +18,11 @@
<property name="dspaceRunnableClass" value="org.dspace.app.bulkedit.MetadataExportCli"/>
</bean>
<bean id="retry-tracker" class="org.dspace.statistics.export.RetryFailedOpenUrlTrackerScriptConfiguration" scope="prototype">
<property name="description" value="Retry all failed commits to the OpenURLTracker"/>
<property name="dspaceRunnableClass" value="org.dspace.statistics.export.RetryFailedOpenUrlTracker"/>
</bean>
<bean id="curate" class="org.dspace.curate.CurationCliScriptConfiguration">
<property name="description" value="Curation tasks"/>
<property name="dspaceRunnableClass" value="org.dspace.curate.CurationCli"/>

View File

@@ -21,4 +21,9 @@
<property name="eventService" ref="org.dspace.services.EventService"/>
</bean>
<!-- Irus statistics tracking -->
<bean class="org.dspace.statistics.export.IrusExportUsageEventListener">
<property name="eventService" ref="org.dspace.services.EventService"/>
</bean>
</beans>

View File

@@ -26,6 +26,40 @@
<root.basedir>${basedir}/../../..</root.basedir>
</properties>
<build>
<plugins>
<!-- This plugin allows us to run a Groovy script in our Maven POM
(see: https://groovy.github.io/gmaven/groovy-maven-plugin/execute.html )
We are generating a OS-agnostic version (agnostic.build.dir) of
the ${project.build.directory} property (full path of target dir).
This is needed by the Surefire & Failsafe plugins (see below)
to initialize the Unit Test environment's dspace.cfg file.
Otherwise, the Unit Test Framework will not work on Windows OS.
This Groovy code was mostly borrowed from:
http://stackoverflow.com/questions/3872355/how-to-convert-file-separator-in-maven
-->
<plugin>
<groupId>org.codehaus.gmaven</groupId>
<artifactId>groovy-maven-plugin</artifactId>
<executions>
<execution>
<id>setproperty</id>
<phase>initialize</phase>
<goals>
<goal>execute</goal>
</goals>
<configuration>
<source>
project.properties['agnostic.build.dir'] = project.build.directory.replace(File.separator, '/');
log.info("Initializing Maven property 'agnostic.build.dir' to: {}", project.properties['agnostic.build.dir']);
</source>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
<profiles>
<profile>
<id>oracle-support</id>
@@ -42,20 +76,20 @@
</dependency>
</dependencies>
</profile>
<!-- If Unit Testing is enabled, then setup the Unit Test Environment.
See also the 'skiptests' profile in Parent POM. -->
<!-- Setup the Unit Test Environment (when -DskipUnitTests=false) -->
<profile>
<id>test-environment</id>
<id>unit-test-environment</id>
<activation>
<activeByDefault>false</activeByDefault>
<property>
<name>skipTests</name>
<name>skipUnitTests</name>
<value>false</value>
</property>
</activation>
<build>
<plugins>
<!-- Unit/Integration Testing setup: This plugin unzips the
<!-- Unit Testing setup: This plugin unzips the
'testEnvironment.zip' file (created by dspace-parent POM), into
the 'target/testing/' folder, to essentially create a test
install of DSpace, against which Tests can be run. -->
@@ -75,50 +109,12 @@
</configuration>
<executions>
<execution>
<id>setupTestEnvironment</id>
<id>setupUnitTestEnvironment</id>
<phase>generate-test-resources</phase>
<goals>
<goal>unpack</goal>
</goals>
</execution>
<execution>
<id>setupIntegrationTestEnvironment</id>
<phase>pre-integration-test</phase>
<goals>
<goal>unpack</goal>
</goals>
</execution>
</executions>
</plugin>
<!-- This plugin allows us to run a Groovy script in our Maven POM
(see: http://gmaven.codehaus.org/Executing+Groovy+Code )
We are generating a OS-agnostic version (agnostic.build.dir) of
the ${project.build.directory} property (full path of target dir).
This is needed by the FileWeaver & Surefire plugins (see below)
to initialize the Unit Test environment's dspace.cfg file.
Otherwise, the Unit Test Framework will not work on Windows OS.
This Groovy code was mostly borrowed from:
http://stackoverflow.com/questions/3872355/how-to-convert-file-separator-in-maven
-->
<plugin>
<groupId>org.codehaus.gmaven</groupId>
<artifactId>groovy-maven-plugin</artifactId>
<executions>
<execution>
<id>setproperty</id>
<phase>generate-test-resources
</phase> <!-- XXX I think this should be 'initialize' - MHW -->
<goals>
<goal>execute</goal>
</goals>
<configuration>
<source>
project.properties['agnostic.build.dir'] = project.build.directory.replace(File.separator, '/');
println("Initializing Maven property 'agnostic.build.dir' to: " + project.properties['agnostic.build.dir']);
</source>
</configuration>
</execution>
</executions>
</plugin>
@@ -136,6 +132,60 @@
</systemPropertyVariables>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
<!-- When running tests, also include test classes from dspace-api
(this test-jar is only built when tests are enabled). -->
<dependency>
<groupId>org.dspace</groupId>
<artifactId>dspace-api</artifactId>
<type>test-jar</type>
<scope>test</scope>
</dependency>
</dependencies>
</profile>
<!-- Setup the Integration Test Environment (when -DskipIntegrationTests=false) -->
<profile>
<id>integration-test-environment</id>
<activation>
<activeByDefault>false</activeByDefault>
<property>
<name>skipIntegrationTests</name>
<value>false</value>
</property>
</activation>
<build>
<plugins>
<!-- Integration Testing setup: This plugin unzips the
'testEnvironment.zip' file (created by dspace-parent POM), into
the 'target/testing/' folder, to essentially create a test
install of DSpace, against which Tests can be run. -->
<plugin>
<artifactId>maven-dependency-plugin</artifactId>
<configuration>
<outputDirectory>${project.build.directory}/testing</outputDirectory>
<artifactItems>
<artifactItem>
<groupId>org.dspace</groupId>
<artifactId>dspace-parent</artifactId>
<version>${project.version}</version>
<type>zip</type>
<classifier>testEnvironment</classifier>
</artifactItem>
</artifactItems>
</configuration>
<executions>
<execution>
<id>setupIntegrationTestEnvironment</id>
<phase>pre-integration-test</phase>
<goals>
<goal>unpack</goal>
</goals>
</execution>
</executions>
</plugin>
<!-- Run Integration Testing! This plugin just kicks off the tests (when enabled). -->
<plugin>
@@ -163,6 +213,7 @@
</dependency>
</dependencies>
</profile>
</profiles>
<!--

View File

@@ -73,24 +73,52 @@ just adding new jar in the classloader</description>
</execution>
</executions>
</plugin>
<!-- This plugin allows us to run a Groovy script in our Maven POM
(see: https://groovy.github.io/gmaven/groovy-maven-plugin/execute.html )
We are generating a OS-agnostic version (agnostic.build.dir) of
the ${project.build.directory} property (full path of target dir).
This is needed by the Surefire & Failsafe plugins (see below)
to initialize the Unit Test environment's dspace.cfg file.
Otherwise, the Unit Test Framework will not work on Windows OS.
This Groovy code was mostly borrowed from:
http://stackoverflow.com/questions/3872355/how-to-convert-file-separator-in-maven
-->
<plugin>
<groupId>org.codehaus.gmaven</groupId>
<artifactId>groovy-maven-plugin</artifactId>
<executions>
<execution>
<id>setproperty</id>
<phase>initialize</phase>
<goals>
<goal>execute</goal>
</goals>
<configuration>
<source>
project.properties['agnostic.build.dir'] = project.build.directory.replace(File.separator, '/');
log.info("Initializing Maven property 'agnostic.build.dir' to: {}", project.properties['agnostic.build.dir']);
</source>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
<profiles>
<!-- If Unit Testing is enabled, then setup the Unit Test Environment.
See also the 'skiptests' profile in Parent POM. -->
<!-- Setup the Unit Test Environment (when -DskipUnitTests=false) -->
<profile>
<id>test-environment</id>
<id>unit-test-environment</id>
<activation>
<activeByDefault>false</activeByDefault>
<property>
<name>skipTests</name>
<name>skipUnitTests</name>
<value>false</value>
</property>
</activation>
<build>
<plugins>
<!-- Unit/Integration Testing setup: This plugin unzips the
<!-- Unit Testing setup: This plugin unzips the
'testEnvironment.zip' file (created by dspace-parent POM), into
the 'target/testing/' folder, to essentially create a test
install of DSpace, against which Tests can be run. -->
@@ -110,49 +138,12 @@ just adding new jar in the classloader</description>
</configuration>
<executions>
<execution>
<id>setupTestEnvironment</id>
<id>setupUnitTestEnvironment</id>
<phase>generate-test-resources</phase>
<goals>
<goal>unpack</goal>
</goals>
</execution>
<execution>
<id>setupIntegrationTestEnvironment</id>
<phase>pre-integration-test</phase>
<goals>
<goal>unpack</goal>
</goals>
</execution>
</executions>
</plugin>
<!-- This plugin allows us to run a Groovy script in our Maven POM
(see: http://gmaven.codehaus.org/Executing+Groovy+Code )
We are generating a OS-agnostic version (agnostic.build.dir) of
the ${project.build.directory} property (full path of target dir).
This is needed by the FileWeaver & Surefire plugins (see below)
to initialize the Unit Test environment's dspace.cfg file.
Otherwise, the Unit Test Framework will not work on Windows OS.
This Groovy code was mostly borrowed from:
http://stackoverflow.com/questions/3872355/how-to-convert-file-separator-in-maven
-->
<plugin>
<groupId>org.codehaus.gmaven</groupId>
<artifactId>groovy-maven-plugin</artifactId>
<executions>
<execution>
<id>setproperty</id>
<phase>initialize</phase>
<goals>
<goal>execute</goal>
</goals>
<configuration>
<source>
project.properties['agnostic.build.dir'] = project.build.directory.replace(File.separator, '/');
log.info("Initializing Maven property 'agnostic.build.dir' to: {}", project.properties['agnostic.build.dir']);
</source>
</configuration>
</execution>
</executions>
</plugin>
@@ -171,6 +162,60 @@ just adding new jar in the classloader</description>
</systemPropertyVariables>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
<!-- When running tests, also include test classes from dspace-server-webapp
(this test-jar is only built when tests are enabled). -->
<dependency>
<groupId>org.dspace</groupId>
<artifactId>dspace-server-webapp</artifactId>
<type>test-jar</type>
<scope>test</scope>
</dependency>
</dependencies>
</profile>
<!-- Setup the Integration Test Environment (when -DskipIntegrationTests=false) -->
<profile>
<id>integration-test-environment</id>
<activation>
<activeByDefault>false</activeByDefault>
<property>
<name>skipIntegrationTests</name>
<value>false</value>
</property>
</activation>
<build>
<plugins>
<!-- Integration Testing setup: This plugin unzips the
'testEnvironment.zip' file (created by dspace-parent POM), into
the 'target/testing/' folder, to essentially create a test
install of DSpace, against which Tests can be run. -->
<plugin>
<artifactId>maven-dependency-plugin</artifactId>
<configuration>
<outputDirectory>${project.build.directory}/testing</outputDirectory>
<artifactItems>
<artifactItem>
<groupId>org.dspace</groupId>
<artifactId>dspace-parent</artifactId>
<version>${project.version}</version>
<type>zip</type>
<classifier>testEnvironment</classifier>
</artifactItem>
</artifactItems>
</configuration>
<executions>
<execution>
<id>setupIntegrationTestEnvironment</id>
<phase>pre-integration-test</phase>
<goals>
<goal>unpack</goal>
</goals>
</execution>
</executions>
</plugin>
<!-- Run Integration Testing! This plugin just kicks off the tests (when enabled). -->
<plugin>
@@ -199,6 +244,7 @@ just adding new jar in the classloader</description>
</dependency>
</dependencies>
</profile>
<profile>
<id>oracle-support</id>
<activation>

View File

@@ -148,30 +148,28 @@
</build>
</profile>
<!-- Profile to aggregate (local) code coverage reports via JaCoCo
(jacoco.org) during the Unit and Integration testing process.
This profile is only enabled when tests are (skipTests=false).
<!-- Profile to aggregate (local) code coverage reports created by JaCoCo (jacoco.org)
during the Unit and Integration testing processes.
This plugin is disabled by default, as it is triggered by name in [src]/.travis.yml
See also jacoco-maven-plugin settings in Parent POM, as those
settings generate the reports aggregated here. -->
settings generate the module-specific reports that are aggregated here. -->
<profile>
<id>test-coverage-report</id>
<id>coverage-report</id>
<activation>
<activeByDefault>false</activeByDefault>
<property>
<name>skipTests</name>
<value>false</value>
</property>
</activation>
<build>
<plugins>
<!-- Aggregate unit test code coverage reports -->
<!-- Aggregate all test code coverage reports from the individual module coverage reports
that are generated during unit & integration testing.
Results (HTML and XML) are written to ./target/site/jacoco-aggregate/ -->
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<executions>
<execution>
<id>aggregate-test-report</id>
<phase>post-integration-test</phase>
<phase>verify</phase>
<goals>
<goal>report-aggregate</goal>
</goals>
@@ -182,8 +180,6 @@
<dataFileInclude>**/jacoco-ut.exec</dataFileInclude>
<dataFileInclude>**/jacoco-it.exec</dataFileInclude>
</dataFileIncludes>
<!-- Sets the output directory for the code coverage report. -->
<outputDirectory>${project.reporting.outputDirectory}/jacoco-aggregated</outputDirectory>
</configuration>
</execution>
</executions>
@@ -231,50 +227,6 @@
</dependency>
</dependencies>
</profile>
<!-- Send coverage reports (generated by jacoco-maven-plugin) to coveralls.io. See 'test-coverage-report'
profile above for more details on aggregating of code coverage reports.
This plugin is disabled by default, as it is triggered by name in [src]/.travis.yml -->
<profile>
<id>coveralls</id>
<activation>
<activeByDefault>false</activeByDefault>
</activation>
<build>
<plugins>
<plugin>
<groupId>org.eluder.coveralls</groupId>
<artifactId>coveralls-maven-plugin</artifactId>
<executions>
<execution>
<id>report-test-coverage</id>
<phase>verify</phase>
<goals>
<goal>report</goal>
</goals>
<configuration>
<failOnServiceError>false</failOnServiceError>
<jacocoReports>
<jacocoReport>${project.reporting.outputDirectory}/jacoco-aggregated/jacoco.xml</jacocoReport>
</jacocoReports>
<sourceDirectories>
<sourceDirectory>${project.parent.basedir}/dspace-api/src/main/java</sourceDirectory>
<sourceDirectory>${project.parent.basedir}/dspace-api/target/generated-sources/annotations</sourceDirectory>
<sourceDirectory>${project.parent.basedir}/dspace-oai/src/main/java</sourceDirectory>
<sourceDirectory>${project.parent.basedir}/dspace-rdf/src/main/java</sourceDirectory>
<sourceDirectory>${project.parent.basedir}/dspace-rest/src/main/java</sourceDirectory>
<sourceDirectory>${project.parent.basedir}/dspace-services/src/main/java</sourceDirectory>
<sourceDirectory>${project.parent.basedir}/dspace-server-webapp/src/main/java</sourceDirectory>
<sourceDirectory>${project.parent.basedir}/dspace-sword/src/main/java</sourceDirectory>
<sourceDirectory>${project.parent.basedir}/dspace-swordv2/src/main/java</sourceDirectory>
</sourceDirectories>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
</profiles>
<dependencies>

View File

@@ -114,6 +114,7 @@ Common usage:
<echo message="update --> Update ${dspace.dir} config, etc, lib and web applications without " />
<echo message=" touching your data" />
<echo message="update_configs --> Update your configs directory with new configuration files"/>
<echo message="update_spiders --> Dowload and install Spider Robots database into ${dspace.dir}/config" />
<echo message="update_code --> Update compiled code (bin, lib, and etc directories)" />
<echo message="update_webapps --> Update web applications" />
<echo message="" />
@@ -177,6 +178,7 @@ Common usage:
<target name="update_configs"
depends="overwrite_configs,overwrite_solr_configs"
description="Updates the Configuration Directory">
<antcall target="init_spiders" />
</target>
<target name="overwrite_configs" description="Overwrites a configuration directory." if="${overwrite}" depends="copy_configs_keep">
@@ -827,6 +829,8 @@ Common usage:
<antcall target="copy_webapps" />
<antcall target="init_spiders" />
<echo>
====================================================================
The DSpace code has been installed.
@@ -856,4 +860,27 @@ Common usage:
</target>
<!-- installs and/or updates Project Counter Robot List resolution database -->
<target name="update_spiders">
<echo>Downloading: ${irus.statistics.spider.agentregex.url}</echo>
<get src="${irus.statistics.spider.agentregex.url}" dest="${irus.statistics.spider.agentregex.regexfile}" verbose="true" />
</target>
<target name="check_spiders">
<condition property="need.spiders">
<and>
<not>
<available file="${irus.statistics.spider.agentregex.regexfile}" />
</not>
<not>
<contains string="${irus.statistics.spider.agentregex.url}" substring="irus.statistics.spider.agentregex.url"/>
</not>
</and>
</condition>
</target>
<target name="init_spiders" depends="check_spiders" if="need.spiders">
<antcall target="update_spiders" />
</target>
</project>

View File

@@ -13,6 +13,6 @@ export const environment = {
host: 'localhost',
port: 8080,
// NOTE: Space is capitalized because 'namespace' is a reserved string in TypeScript
nameSpace: '/server/api'
nameSpace: '/server'
}
};

Some files were not shown because too many files have changed in this diff Show More