mirror of
https://github.com/DSpace/DSpace.git
synced 2025-10-07 18:14:26 +00:00
Merge branch 'main' of https://github.com/DSpace/DSpace into DURACOM-225
This commit is contained in:
4
.github/workflows/build.yml
vendored
4
.github/workflows/build.yml
vendored
@@ -21,11 +21,11 @@ jobs:
|
||||
# Also specify version of Java to use (this can allow us to optionally run tests on multiple JDKs in future)
|
||||
matrix:
|
||||
include:
|
||||
# NOTE: Unit Tests include deprecated REST API v6 (as it has unit tests)
|
||||
# NOTE: Unit Tests include a retry for occasionally failing tests
|
||||
# - surefire.rerunFailingTestsCount => try again for flakey tests, and keep track of/report on number of retries
|
||||
- type: "Unit Tests"
|
||||
java: 11
|
||||
mvnflags: "-DskipUnitTests=false -Pdspace-rest -Dsurefire.rerunFailingTestsCount=2"
|
||||
mvnflags: "-DskipUnitTests=false -Dsurefire.rerunFailingTestsCount=2"
|
||||
resultsdir: "**/target/surefire-reports/**"
|
||||
# NOTE: ITs skip all code validation checks, as they are already done by Unit Test job.
|
||||
# - enforcer.skip => Skip maven-enforcer-plugin rules
|
||||
|
2
.github/workflows/docker.yml
vendored
2
.github/workflows/docker.yml
vendored
@@ -102,6 +102,8 @@ jobs:
|
||||
build_id: dspace-solr
|
||||
image_name: dspace/dspace-solr
|
||||
dockerfile_path: ./dspace/src/main/docker/dspace-solr/Dockerfile
|
||||
# Must pass solrconfigs to the Dockerfile so that it can find the required Solr config files
|
||||
dockerfile_additional_contexts: 'solrconfigs=./dspace/solr/'
|
||||
secrets:
|
||||
DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }}
|
||||
DOCKER_ACCESS_TOKEN: ${{ secrets.DOCKER_ACCESS_TOKEN }}
|
||||
|
10
.github/workflows/reusable-docker-build.yml
vendored
10
.github/workflows/reusable-docker-build.yml
vendored
@@ -24,6 +24,12 @@ on:
|
||||
dockerfile_context:
|
||||
required: false
|
||||
type: string
|
||||
default: '.'
|
||||
# Optionally a list of "additional_contexts" to pass to Dockerfile. Defaults to empty
|
||||
dockerfile_additional_contexts:
|
||||
required: false
|
||||
type: string
|
||||
default: ''
|
||||
# If Docker image should have additional tag flavor details (e.g. a suffix), it may be passed in.
|
||||
tags_flavor:
|
||||
required: false
|
||||
@@ -123,7 +129,9 @@ jobs:
|
||||
id: docker_build
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
context: ${{ inputs.dockerfile_context || '.' }}
|
||||
build-contexts: |
|
||||
${{ inputs.dockerfile_additional_contexts }}
|
||||
context: ${{ inputs.dockerfile_context }}
|
||||
file: ${{ inputs.dockerfile_path }}
|
||||
platforms: ${{ matrix.arch }}
|
||||
# For pull requests, we run the Docker build (to ensure no PR changes break the build),
|
||||
|
@@ -19,7 +19,7 @@ RUN mkdir /install \
|
||||
USER dspace
|
||||
# Copy the DSpace source code (from local machine) into the workdir (excluding .dockerignore contents)
|
||||
ADD --chown=dspace . /app/
|
||||
# Build DSpace (note: this build doesn't include the optional, deprecated "dspace-rest" webapp)
|
||||
# Build DSpace
|
||||
# Copy the dspace-installer directory to /install. Clean up the build to keep the docker image small
|
||||
# Maven flags here ensure that we skip building test environment and skip all code verification checks.
|
||||
# These flags speed up this compilation as much as reasonably possible.
|
||||
|
@@ -21,9 +21,9 @@ RUN mkdir /install \
|
||||
USER dspace
|
||||
# Copy the DSpace source code (from local machine) into the workdir (excluding .dockerignore contents)
|
||||
ADD --chown=dspace . /app/
|
||||
# Build DSpace (INCLUDING the optional, deprecated "dspace-rest" webapp)
|
||||
# Build DSpace
|
||||
# Copy the dspace-installer directory to /install. Clean up the build to keep the docker image small
|
||||
RUN mvn --no-transfer-progress package -Pdspace-rest && \
|
||||
RUN mvn --no-transfer-progress package && \
|
||||
mv /app/dspace/target/${TARGET_DIR}/* /install && \
|
||||
mvn clean
|
||||
|
||||
@@ -67,17 +67,10 @@ ENV CATALINA_OPTS=-Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=*:800
|
||||
|
||||
# Link the DSpace 'server' webapp into Tomcat's webapps directory.
|
||||
# This ensures that when we start Tomcat, it runs from /server path (e.g. http://localhost:8080/server/)
|
||||
# Also link the v6.x (deprecated) REST API off the "/rest" path
|
||||
RUN ln -s $DSPACE_INSTALL/webapps/server /usr/local/tomcat/webapps/server && \
|
||||
ln -s $DSPACE_INSTALL/webapps/rest /usr/local/tomcat/webapps/rest
|
||||
RUN ln -s $DSPACE_INSTALL/webapps/server /usr/local/tomcat/webapps/server
|
||||
# If you wish to run "server" webapp off the ROOT path, then comment out the above RUN, and uncomment the below RUN.
|
||||
# You also MUST update the 'dspace.server.url' configuration to match.
|
||||
# Please note that server webapp should only run on one path at a time.
|
||||
#RUN mv /usr/local/tomcat/webapps/ROOT /usr/local/tomcat/webapps/ROOT.bk && \
|
||||
# ln -s $DSPACE_INSTALL/webapps/server /usr/local/tomcat/webapps/ROOT && \
|
||||
# ln -s $DSPACE_INSTALL/webapps/rest /usr/local/tomcat/webapps/rest
|
||||
# ln -s $DSPACE_INSTALL/webapps/server /usr/local/tomcat/webapps/ROOT
|
||||
|
||||
# Overwrite the v6.x (deprecated) REST API's web.xml, so that we can run it on HTTP (defaults to requiring HTTPS)
|
||||
# WARNING: THIS IS OBVIOUSLY INSECURE. NEVER DO THIS IN PRODUCTION.
|
||||
COPY dspace/src/main/docker/test/rest_web.xml $DSPACE_INSTALL/webapps/rest/WEB-INF/web.xml
|
||||
RUN sed -i -e "s|\${dspace.dir}|$DSPACE_INSTALL|" $DSPACE_INSTALL/webapps/rest/WEB-INF/web.xml
|
||||
|
@@ -1,5 +1,10 @@
|
||||
version: "3.7"
|
||||
|
||||
networks:
|
||||
# Default to using network named 'dspacenet' from docker-compose.yml.
|
||||
# Its full name will be prepended with the project name (e.g. "-p d7" means it will be named "d7_dspacenet")
|
||||
default:
|
||||
name: ${COMPOSE_PROJECT_NAME}_dspacenet
|
||||
external: true
|
||||
services:
|
||||
dspace-cli:
|
||||
image: "${DOCKER_OWNER:-dspace}/dspace-cli:${DSPACE_VER:-latest}"
|
||||
@@ -26,13 +31,8 @@ services:
|
||||
- ./dspace/config:/dspace/config
|
||||
entrypoint: /dspace/bin/dspace
|
||||
command: help
|
||||
networks:
|
||||
- dspacenet
|
||||
tty: true
|
||||
stdin_open: true
|
||||
|
||||
volumes:
|
||||
assetstore:
|
||||
|
||||
networks:
|
||||
dspacenet:
|
||||
|
@@ -36,7 +36,7 @@ services:
|
||||
depends_on:
|
||||
- dspacedb
|
||||
networks:
|
||||
dspacenet:
|
||||
- dspacenet
|
||||
ports:
|
||||
- published: 8080
|
||||
target: 8080
|
||||
@@ -89,8 +89,10 @@ services:
|
||||
container_name: dspacesolr
|
||||
image: "${DOCKER_OWNER:-dspace}/dspace-solr:${DSPACE_VER:-latest}"
|
||||
build:
|
||||
context: .
|
||||
dockerfile: ./dspace/src/main/docker/dspace-solr/Dockerfile
|
||||
context: ./dspace/src/main/docker/dspace-solr/
|
||||
# Provide path to Solr configs necessary to build Docker image
|
||||
additional_contexts:
|
||||
solrconfigs: ./dspace/solr/
|
||||
args:
|
||||
SOLR_VERSION: "${SOLR_VER:-8.11}"
|
||||
networks:
|
||||
@@ -123,6 +125,8 @@ services:
|
||||
cp -r /opt/solr/server/solr/configsets/statistics/* statistics
|
||||
precreate-core qaevent /opt/solr/server/solr/configsets/qaevent
|
||||
cp -r /opt/solr/server/solr/configsets/qaevent/* qaevent
|
||||
precreate-core suggestion /opt/solr/server/solr/configsets/suggestion
|
||||
cp -r /opt/solr/server/solr/configsets/suggestion/* suggestion
|
||||
exec solr -f
|
||||
volumes:
|
||||
assetstore:
|
||||
|
@@ -528,7 +528,7 @@
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.hamcrest</groupId>
|
||||
<artifactId>hamcrest-all</artifactId>
|
||||
<artifactId>hamcrest</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
@@ -620,7 +620,7 @@
|
||||
<dependency>
|
||||
<groupId>com.maxmind.geoip2</groupId>
|
||||
<artifactId>geoip2</artifactId>
|
||||
<version>2.11.0</version>
|
||||
<version>2.17.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.ant</groupId>
|
||||
@@ -784,7 +784,7 @@
|
||||
<dependency>
|
||||
<groupId>com.opencsv</groupId>
|
||||
<artifactId>opencsv</artifactId>
|
||||
<version>5.7.1</version>
|
||||
<version>5.9</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Email templating -->
|
||||
@@ -867,32 +867,32 @@
|
||||
<dependency>
|
||||
<groupId>io.netty</groupId>
|
||||
<artifactId>netty-buffer</artifactId>
|
||||
<version>4.1.94.Final</version>
|
||||
<version>4.1.106.Final</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.netty</groupId>
|
||||
<artifactId>netty-transport</artifactId>
|
||||
<version>4.1.94.Final</version>
|
||||
<version>4.1.106.Final</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.netty</groupId>
|
||||
<artifactId>netty-transport-native-unix-common</artifactId>
|
||||
<version>4.1.94.Final</version>
|
||||
<version>4.1.106.Final</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.netty</groupId>
|
||||
<artifactId>netty-common</artifactId>
|
||||
<version>4.1.94.Final</version>
|
||||
<version>4.1.106.Final</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.netty</groupId>
|
||||
<artifactId>netty-handler</artifactId>
|
||||
<version>4.1.94.Final</version>
|
||||
<version>4.1.106.Final</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.netty</groupId>
|
||||
<artifactId>netty-codec</artifactId>
|
||||
<version>4.1.94.Final</version>
|
||||
<version>4.1.106.Final</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.velocity</groupId>
|
||||
@@ -902,7 +902,7 @@
|
||||
<dependency>
|
||||
<groupId>org.xmlunit</groupId>
|
||||
<artifactId>xmlunit-core</artifactId>
|
||||
<version>2.8.0</version>
|
||||
<version>2.9.1</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
|
@@ -116,6 +116,17 @@ public final class CreateAdministrator {
|
||||
protected CreateAdministrator()
|
||||
throws Exception {
|
||||
context = new Context();
|
||||
try {
|
||||
context.getDBConfig();
|
||||
} catch (NullPointerException npr) {
|
||||
// if database is null, there is no point in continuing. Prior to this exception and catch,
|
||||
// NullPointerException was thrown, that wasn't very helpful.
|
||||
throw new IllegalStateException("Problem connecting to database. This " +
|
||||
"indicates issue with either network or version (or possibly some other). " +
|
||||
"If you are running this in docker-compose, please make sure dspace-cli was " +
|
||||
"built from the same sources as running dspace container AND that they are in " +
|
||||
"the same project/network.");
|
||||
}
|
||||
groupService = EPersonServiceFactory.getInstance().getGroupService();
|
||||
ePersonService = EPersonServiceFactory.getInstance().getEPersonService();
|
||||
}
|
||||
|
@@ -0,0 +1,140 @@
|
||||
/**
|
||||
* The contents of this file are subject to the license and copyright
|
||||
* detailed in the LICENSE and NOTICE files at the root of the source
|
||||
* tree and available online at
|
||||
*
|
||||
* http://www.dspace.org/license/
|
||||
*/
|
||||
package org.dspace.app.suggestion;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.apache.solr.client.solrj.SolrServerException;
|
||||
import org.dspace.content.service.ItemService;
|
||||
import org.dspace.core.Context;
|
||||
import org.dspace.external.model.ExternalDataObject;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
/**
|
||||
* Suggestion provider that read the suggestion from the local suggestion solr
|
||||
* core
|
||||
*
|
||||
* @author Andrea Bollini (andrea.bollini at 4science dot it)
|
||||
*
|
||||
*/
|
||||
public abstract class SolrSuggestionProvider implements SuggestionProvider {
|
||||
private static final Logger log = org.apache.logging.log4j.LogManager.getLogger(SolrSuggestionProvider.class);
|
||||
|
||||
@Autowired
|
||||
protected ItemService itemService;
|
||||
|
||||
@Autowired
|
||||
protected SolrSuggestionStorageService solrSuggestionStorageService;
|
||||
|
||||
private String sourceName;
|
||||
|
||||
public String getSourceName() {
|
||||
return sourceName;
|
||||
}
|
||||
|
||||
public void setSourceName(String sourceName) {
|
||||
this.sourceName = sourceName;
|
||||
}
|
||||
|
||||
public void setItemService(ItemService itemService) {
|
||||
this.itemService = itemService;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long countAllTargets(Context context) {
|
||||
try {
|
||||
return this.solrSuggestionStorageService.countAllTargets(context, sourceName);
|
||||
} catch (SolrServerException | IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public long countUnprocessedSuggestionByTarget(Context context, UUID target) {
|
||||
try {
|
||||
return this.solrSuggestionStorageService.countUnprocessedSuggestionByTarget(context, sourceName, target);
|
||||
} catch (SolrServerException | IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Suggestion> findAllUnprocessedSuggestions(Context context, UUID target, int pageSize, long offset,
|
||||
boolean ascending) {
|
||||
|
||||
try {
|
||||
return this.solrSuggestionStorageService.findAllUnprocessedSuggestions(context, sourceName,
|
||||
target, pageSize, offset, ascending);
|
||||
} catch (SolrServerException | IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<SuggestionTarget> findAllTargets(Context context, int pageSize, long offset) {
|
||||
try {
|
||||
return this.solrSuggestionStorageService.findAllTargets(context, sourceName, pageSize, offset);
|
||||
} catch (SolrServerException | IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Suggestion findUnprocessedSuggestion(Context context, UUID target, String id) {
|
||||
try {
|
||||
return this.solrSuggestionStorageService.findUnprocessedSuggestion(context, sourceName, target, id);
|
||||
} catch (SolrServerException | IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public SuggestionTarget findTarget(Context context, UUID target) {
|
||||
try {
|
||||
return this.solrSuggestionStorageService.findTarget(context, sourceName, target);
|
||||
} catch (SolrServerException | IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void rejectSuggestion(Context context, UUID target, String idPart) {
|
||||
Suggestion suggestion = findUnprocessedSuggestion(context, target, idPart);
|
||||
try {
|
||||
solrSuggestionStorageService.flagSuggestionAsProcessed(suggestion);
|
||||
} catch (SolrServerException | IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void flagRelatedSuggestionsAsProcessed(Context context, ExternalDataObject externalDataObject) {
|
||||
if (!isExternalDataObjectPotentiallySuggested(context, externalDataObject)) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
solrSuggestionStorageService.flagAllSuggestionAsProcessed(sourceName, externalDataObject.getId());
|
||||
} catch (SolrServerException | IOException e) {
|
||||
log.error(e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* check if the externalDataObject may have suggestion
|
||||
* @param context
|
||||
* @param externalDataObject
|
||||
* @return true if the externalDataObject could be suggested by this provider
|
||||
* (i.e. it comes from a DataProvider used by this suggestor)
|
||||
*/
|
||||
protected abstract boolean isExternalDataObjectPotentiallySuggested(Context context,
|
||||
ExternalDataObject externalDataObject);
|
||||
}
|
@@ -0,0 +1,191 @@
|
||||
/**
|
||||
* 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.suggestion;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
import org.apache.solr.client.solrj.SolrServerException;
|
||||
import org.dspace.core.Context;
|
||||
|
||||
/**
|
||||
* Service to deal with the local suggestion solr core used by the
|
||||
* SolrSuggestionProvider(s)
|
||||
*
|
||||
* @author Andrea Bollini (andrea.bollini at 4science dot it)
|
||||
* @author Luca Giamminonni (luca.giamminonni at 4science dot it)
|
||||
*
|
||||
*/
|
||||
public interface SolrSuggestionStorageService {
|
||||
public static final String SOURCE = "source";
|
||||
/** This is the URI Part of the suggestion source:target:id */
|
||||
public static final String SUGGESTION_FULLID = "suggestion_fullid";
|
||||
public static final String SUGGESTION_ID = "suggestion_id";
|
||||
public static final String TARGET_ID = "target_id";
|
||||
public static final String TITLE = "title";
|
||||
public static final String DATE = "date";
|
||||
public static final String DISPLAY = "display";
|
||||
public static final String CONTRIBUTORS = "contributors";
|
||||
public static final String ABSTRACT = "abstract";
|
||||
public static final String CATEGORY = "category";
|
||||
public static final String EXTERNAL_URI = "external-uri";
|
||||
public static final String PROCESSED = "processed";
|
||||
public static final String SCORE = "trust";
|
||||
public static final String EVIDENCES = "evidences";
|
||||
|
||||
/**
|
||||
* Add a new suggestion to SOLR
|
||||
*
|
||||
* @param suggestion
|
||||
* @param force true if the suggestion must be reindexed
|
||||
* @param commit
|
||||
* @throws IOException
|
||||
* @throws SolrServerException
|
||||
*/
|
||||
public void addSuggestion(Suggestion suggestion, boolean force, boolean commit)
|
||||
throws SolrServerException, IOException;
|
||||
|
||||
/**
|
||||
* Return true if the suggestion is already in SOLR and flagged as processed
|
||||
*
|
||||
* @param suggestion
|
||||
* @return true if the suggestion is already in SOLR and flagged as processed
|
||||
* @throws IOException
|
||||
* @throws SolrServerException
|
||||
*/
|
||||
public boolean exist(Suggestion suggestion) throws SolrServerException, IOException;
|
||||
|
||||
/**
|
||||
* Delete a suggestion from SOLR if any
|
||||
*
|
||||
* @param suggestion
|
||||
* @throws IOException
|
||||
* @throws SolrServerException
|
||||
*/
|
||||
public void deleteSuggestion(Suggestion suggestion) throws SolrServerException, IOException;
|
||||
|
||||
/**
|
||||
* Flag a suggestion as processed in SOLR if any
|
||||
*
|
||||
* @param suggestion
|
||||
* @throws IOException
|
||||
* @throws SolrServerException
|
||||
*/
|
||||
public void flagSuggestionAsProcessed(Suggestion suggestion) throws SolrServerException, IOException;
|
||||
|
||||
/**
|
||||
* Delete all the suggestions from SOLR if any related to a specific target
|
||||
*
|
||||
* @param target
|
||||
* @throws IOException
|
||||
* @throws SolrServerException
|
||||
*/
|
||||
public void deleteTarget(SuggestionTarget target) throws SolrServerException, IOException;
|
||||
|
||||
/**
|
||||
* Performs an explicit commit, causing pending documents to be committed for
|
||||
* indexing.
|
||||
*
|
||||
* @throws SolrServerException
|
||||
* @throws IOException
|
||||
*/
|
||||
void commit() throws SolrServerException, IOException;
|
||||
|
||||
/**
|
||||
* Flag all the suggestion related to the given source and id as processed.
|
||||
*
|
||||
* @param source the source name
|
||||
* @param idPart the id's last part
|
||||
* @throws SolrServerException
|
||||
* @throws IOException
|
||||
*/
|
||||
void flagAllSuggestionAsProcessed(String source, String idPart) throws SolrServerException, IOException;
|
||||
|
||||
/**
|
||||
* Count all the targets related to the given source.
|
||||
*
|
||||
* @param source the source name
|
||||
* @return the target's count
|
||||
* @throws IOException
|
||||
* @throws SolrServerException
|
||||
*/
|
||||
long countAllTargets(Context context, String source) throws SolrServerException, IOException;
|
||||
|
||||
/**
|
||||
* Count all the unprocessed suggestions related to the given source and target.
|
||||
*
|
||||
* @param context the DSpace Context
|
||||
* @param source the source name
|
||||
* @param target the target id
|
||||
* @return the suggestion count
|
||||
* @throws SolrServerException
|
||||
* @throws IOException
|
||||
*/
|
||||
long countUnprocessedSuggestionByTarget(Context context, String source, UUID target)
|
||||
throws SolrServerException, IOException;
|
||||
|
||||
/**
|
||||
* Find all the unprocessed suggestions related to the given source and target.
|
||||
*
|
||||
* @param context the DSpace Context
|
||||
* @param source the source name
|
||||
* @param target the target id
|
||||
* @param pageSize the page size
|
||||
* @param offset the page offset
|
||||
* @param ascending true to retrieve the suggestions ordered by score
|
||||
* ascending
|
||||
* @return the found suggestions
|
||||
* @throws SolrServerException
|
||||
* @throws IOException
|
||||
*/
|
||||
List<Suggestion> findAllUnprocessedSuggestions(Context context, String source, UUID target,
|
||||
int pageSize, long offset, boolean ascending) throws SolrServerException, IOException;
|
||||
|
||||
/**
|
||||
*
|
||||
* Find all the suggestion targets related to the given source.
|
||||
*
|
||||
* @param context the DSpace Context
|
||||
* @param source the source name
|
||||
* @param pageSize the page size
|
||||
* @param offset the page offset
|
||||
* @return the found suggestion targets
|
||||
* @throws SolrServerException
|
||||
* @throws IOException
|
||||
*/
|
||||
List<SuggestionTarget> findAllTargets(Context context, String source, int pageSize, long offset)
|
||||
throws SolrServerException, IOException;
|
||||
|
||||
/**
|
||||
* Find an unprocessed suggestion by the given source, target id and suggestion
|
||||
* id.
|
||||
*
|
||||
* @param context the DSpace Context
|
||||
* @param source the source name
|
||||
* @param target the target id
|
||||
* @param id the suggestion id
|
||||
* @return the suggestion, if any
|
||||
* @throws SolrServerException
|
||||
* @throws IOException
|
||||
*/
|
||||
Suggestion findUnprocessedSuggestion(Context context, String source, UUID target, String id)
|
||||
throws SolrServerException, IOException;
|
||||
|
||||
/**
|
||||
* Find a suggestion target by the given source and target.
|
||||
*
|
||||
* @param context the DSpace Context
|
||||
* @param source the source name
|
||||
* @param target the target id
|
||||
* @return the suggestion target, if any
|
||||
* @throws SolrServerException
|
||||
* @throws IOException
|
||||
*/
|
||||
SuggestionTarget findTarget(Context context, String source, UUID target) throws SolrServerException, IOException;
|
||||
}
|
@@ -0,0 +1,360 @@
|
||||
/**
|
||||
* 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.suggestion;
|
||||
|
||||
import static org.apache.commons.collections.CollectionUtils.isEmpty;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import com.fasterxml.jackson.databind.DeserializationFeature;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.json.JsonMapper;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.apache.solr.client.solrj.SolrClient;
|
||||
import org.apache.solr.client.solrj.SolrQuery;
|
||||
import org.apache.solr.client.solrj.SolrQuery.SortClause;
|
||||
import org.apache.solr.client.solrj.SolrServerException;
|
||||
import org.apache.solr.client.solrj.impl.HttpSolrClient;
|
||||
import org.apache.solr.client.solrj.response.FacetField;
|
||||
import org.apache.solr.client.solrj.response.FacetField.Count;
|
||||
import org.apache.solr.client.solrj.response.QueryResponse;
|
||||
import org.apache.solr.common.SolrDocument;
|
||||
import org.apache.solr.common.SolrDocumentList;
|
||||
import org.apache.solr.common.SolrInputDocument;
|
||||
import org.apache.solr.common.params.FacetParams;
|
||||
import org.dspace.content.Item;
|
||||
import org.dspace.content.dto.MetadataValueDTO;
|
||||
import org.dspace.content.service.ItemService;
|
||||
import org.dspace.core.Context;
|
||||
import org.dspace.services.factory.DSpaceServicesFactory;
|
||||
import org.dspace.util.UUIDUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
|
||||
/**
|
||||
* Service to deal with the local suggestion solr core used by the
|
||||
* SolrSuggestionProvider(s)
|
||||
*
|
||||
* @author Andrea Bollini (andrea.bollini at 4science dot it)
|
||||
*
|
||||
*/
|
||||
public class SolrSuggestionStorageServiceImpl implements SolrSuggestionStorageService {
|
||||
private static final Logger log = LogManager.getLogger(SolrSuggestionStorageServiceImpl.class);
|
||||
|
||||
protected SolrClient solrSuggestionClient;
|
||||
|
||||
@Autowired
|
||||
private ItemService itemService;
|
||||
|
||||
/**
|
||||
* Get solr client which use suggestion core
|
||||
*
|
||||
* @return solr client
|
||||
*/
|
||||
protected SolrClient getSolr() {
|
||||
if (solrSuggestionClient == null) {
|
||||
String solrService = DSpaceServicesFactory.getInstance().getConfigurationService()
|
||||
.getProperty("suggestion.solr.server", "http://localhost:8983/solr/suggestion");
|
||||
solrSuggestionClient = new HttpSolrClient.Builder(solrService).build();
|
||||
}
|
||||
return solrSuggestionClient;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addSuggestion(Suggestion suggestion, boolean force, boolean commit)
|
||||
throws SolrServerException, IOException {
|
||||
if (force || !exist(suggestion)) {
|
||||
ObjectMapper jsonMapper = new JsonMapper();
|
||||
jsonMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
|
||||
SolrInputDocument document = new SolrInputDocument();
|
||||
document.addField(SOURCE, suggestion.getSource());
|
||||
// suggestion id is written as concatenation of
|
||||
// source + ":" + targetID + ":" + idPart (of externalDataObj)
|
||||
String suggestionFullID = suggestion.getID();
|
||||
document.addField(SUGGESTION_FULLID, suggestionFullID);
|
||||
document.addField(SUGGESTION_ID, suggestionFullID.split(":", 3)[2]);
|
||||
document.addField(TARGET_ID, suggestion.getTarget().getID().toString());
|
||||
document.addField(DISPLAY, suggestion.getDisplay());
|
||||
document.addField(TITLE, getFirstValue(suggestion, "dc", "title", null));
|
||||
document.addField(DATE, getFirstValue(suggestion, "dc", "date", "issued"));
|
||||
document.addField(CONTRIBUTORS, getAllValues(suggestion, "dc", "contributor", "author"));
|
||||
document.addField(ABSTRACT, getFirstValue(suggestion, "dc", "description", "abstract"));
|
||||
document.addField(CATEGORY, getAllValues(suggestion, "dc", "source", null));
|
||||
document.addField(EXTERNAL_URI, suggestion.getExternalSourceUri());
|
||||
document.addField(SCORE, suggestion.getScore());
|
||||
document.addField(PROCESSED, false);
|
||||
document.addField(EVIDENCES, jsonMapper.writeValueAsString(suggestion.getEvidences()));
|
||||
getSolr().add(document);
|
||||
if (commit) {
|
||||
getSolr().commit();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void commit() throws SolrServerException, IOException {
|
||||
getSolr().commit();
|
||||
}
|
||||
|
||||
private List<String> getAllValues(Suggestion suggestion, String schema, String element, String qualifier) {
|
||||
return suggestion.getMetadata().stream()
|
||||
.filter(st -> StringUtils.isNotBlank(st.getValue()) && StringUtils.equals(st.getSchema(), schema)
|
||||
&& StringUtils.equals(st.getElement(), element)
|
||||
&& StringUtils.equals(st.getQualifier(), qualifier))
|
||||
.map(st -> st.getValue()).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
private String getFirstValue(Suggestion suggestion, String schema, String element, String qualifier) {
|
||||
return suggestion.getMetadata().stream()
|
||||
.filter(st -> StringUtils.isNotBlank(st.getValue())
|
||||
&& StringUtils.equals(st.getSchema(), schema)
|
||||
&& StringUtils.equals(st.getElement(), element)
|
||||
&& StringUtils.equals(st.getQualifier(), qualifier))
|
||||
.map(st -> st.getValue()).findFirst().orElse(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean exist(Suggestion suggestion) throws SolrServerException, IOException {
|
||||
SolrQuery query = new SolrQuery(
|
||||
SUGGESTION_FULLID + ":\"" + suggestion.getID() + "\" AND " + PROCESSED + ":true");
|
||||
return getSolr().query(query).getResults().getNumFound() == 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteSuggestion(Suggestion suggestion) throws SolrServerException, IOException {
|
||||
getSolr().deleteById(suggestion.getID());
|
||||
getSolr().commit();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void flagSuggestionAsProcessed(Suggestion suggestion) throws SolrServerException, IOException {
|
||||
SolrInputDocument sdoc = new SolrInputDocument();
|
||||
sdoc.addField(SUGGESTION_FULLID, suggestion.getID());
|
||||
Map<String, Object> fieldModifier = new HashMap<>(1);
|
||||
fieldModifier.put("set", true);
|
||||
sdoc.addField(PROCESSED, fieldModifier); // add the map as the field value
|
||||
getSolr().add(sdoc);
|
||||
getSolr().commit();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void flagAllSuggestionAsProcessed(String source, String idPart) throws SolrServerException, IOException {
|
||||
SolrQuery query = new SolrQuery(SOURCE + ":" + source + " AND " + SUGGESTION_ID + ":\"" + idPart + "\"");
|
||||
query.setRows(Integer.MAX_VALUE);
|
||||
query.setFields(SUGGESTION_FULLID);
|
||||
SolrDocumentList results = getSolr().query(query).getResults();
|
||||
if (results.getNumFound() > 0) {
|
||||
for (SolrDocument rDoc : results) {
|
||||
SolrInputDocument sdoc = new SolrInputDocument();
|
||||
sdoc.addField(SUGGESTION_FULLID, rDoc.getFieldValue(SUGGESTION_FULLID));
|
||||
Map<String, Object> fieldModifier = new HashMap<>(1);
|
||||
fieldModifier.put("set", true);
|
||||
sdoc.addField(PROCESSED, fieldModifier); // add the map as the field value
|
||||
getSolr().add(sdoc);
|
||||
}
|
||||
}
|
||||
getSolr().commit();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteTarget(SuggestionTarget target) throws SolrServerException, IOException {
|
||||
getSolr().deleteByQuery(
|
||||
SOURCE + ":" + target.getSource() + " AND " + TARGET_ID + ":" + target.getTarget().getID().toString());
|
||||
getSolr().commit();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long countAllTargets(Context context, String source) throws SolrServerException, IOException {
|
||||
SolrQuery solrQuery = new SolrQuery();
|
||||
solrQuery.setRows(0);
|
||||
solrQuery.setQuery(SOURCE + ":" + source);
|
||||
solrQuery.addFilterQuery(PROCESSED + ":false");
|
||||
solrQuery.setFacet(true);
|
||||
solrQuery.setFacetMinCount(1);
|
||||
solrQuery.addFacetField(TARGET_ID);
|
||||
solrQuery.setFacetLimit(Integer.MAX_VALUE);
|
||||
QueryResponse response = getSolr().query(solrQuery);
|
||||
return response.getFacetField(TARGET_ID).getValueCount();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long countUnprocessedSuggestionByTarget(Context context, String source, UUID target)
|
||||
throws SolrServerException, IOException {
|
||||
SolrQuery solrQuery = new SolrQuery();
|
||||
solrQuery.setRows(0);
|
||||
solrQuery.setQuery("*:*");
|
||||
solrQuery.addFilterQuery(
|
||||
SOURCE + ":" + source,
|
||||
TARGET_ID + ":" + target.toString(),
|
||||
PROCESSED + ":false");
|
||||
|
||||
QueryResponse response = getSolr().query(solrQuery);
|
||||
return response.getResults().getNumFound();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Suggestion> findAllUnprocessedSuggestions(Context context, String source, UUID target,
|
||||
int pageSize, long offset, boolean ascending) throws SolrServerException, IOException {
|
||||
|
||||
SolrQuery solrQuery = new SolrQuery();
|
||||
solrQuery.setRows(pageSize);
|
||||
solrQuery.setStart((int) offset);
|
||||
solrQuery.setQuery("*:*");
|
||||
solrQuery.addFilterQuery(
|
||||
SOURCE + ":" + source,
|
||||
TARGET_ID + ":" + target.toString(),
|
||||
PROCESSED + ":false");
|
||||
|
||||
if (ascending) {
|
||||
solrQuery.addSort(SortClause.asc("trust"));
|
||||
} else {
|
||||
solrQuery.addSort(SortClause.desc("trust"));
|
||||
}
|
||||
|
||||
solrQuery.addSort(SortClause.desc("date"));
|
||||
solrQuery.addSort(SortClause.asc("title"));
|
||||
|
||||
QueryResponse response = getSolr().query(solrQuery);
|
||||
List<Suggestion> suggestions = new ArrayList<Suggestion>();
|
||||
for (SolrDocument solrDoc : response.getResults()) {
|
||||
suggestions.add(convertSolrDoc(context, solrDoc, source));
|
||||
}
|
||||
return suggestions;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<SuggestionTarget> findAllTargets(Context context, String source, int pageSize, long offset)
|
||||
throws SolrServerException, IOException {
|
||||
|
||||
SolrQuery solrQuery = new SolrQuery();
|
||||
solrQuery.setRows(0);
|
||||
solrQuery.setQuery(SOURCE + ":" + source);
|
||||
solrQuery.addFilterQuery(PROCESSED + ":false");
|
||||
solrQuery.setFacet(true);
|
||||
solrQuery.setFacetMinCount(1);
|
||||
solrQuery.addFacetField(TARGET_ID);
|
||||
solrQuery.setParam(FacetParams.FACET_OFFSET, String.valueOf(offset));
|
||||
solrQuery.setFacetLimit((int) (pageSize));
|
||||
QueryResponse response = getSolr().query(solrQuery);
|
||||
FacetField facetField = response.getFacetField(TARGET_ID);
|
||||
List<SuggestionTarget> suggestionTargets = new ArrayList<SuggestionTarget>();
|
||||
int idx = 0;
|
||||
for (Count c : facetField.getValues()) {
|
||||
SuggestionTarget target = new SuggestionTarget();
|
||||
target.setSource(source);
|
||||
target.setTotal((int) c.getCount());
|
||||
target.setTarget(findItem(context, c.getName()));
|
||||
suggestionTargets.add(target);
|
||||
idx++;
|
||||
}
|
||||
return suggestionTargets;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public Suggestion findUnprocessedSuggestion(Context context, String source, UUID target, String id)
|
||||
throws SolrServerException, IOException {
|
||||
|
||||
SolrQuery solrQuery = new SolrQuery();
|
||||
solrQuery.setRows(1);
|
||||
solrQuery.setQuery("*:*");
|
||||
solrQuery.addFilterQuery(
|
||||
SOURCE + ":" + source,
|
||||
TARGET_ID + ":" + target.toString(),
|
||||
SUGGESTION_ID + ":\"" + id + "\"",
|
||||
PROCESSED + ":false");
|
||||
|
||||
SolrDocumentList results = getSolr().query(solrQuery).getResults();
|
||||
return isEmpty(results) ? null : convertSolrDoc(context, results.get(0), source);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SuggestionTarget findTarget(Context context, String source, UUID target)
|
||||
throws SolrServerException, IOException {
|
||||
SolrQuery solrQuery = new SolrQuery();
|
||||
solrQuery.setRows(0);
|
||||
solrQuery.setQuery(SOURCE + ":" + source);
|
||||
solrQuery.addFilterQuery(
|
||||
TARGET_ID + ":" + target.toString(),
|
||||
PROCESSED + ":false");
|
||||
QueryResponse response = getSolr().query(solrQuery);
|
||||
SuggestionTarget sTarget = new SuggestionTarget();
|
||||
sTarget.setSource(source);
|
||||
sTarget.setTotal((int) response.getResults().getNumFound());
|
||||
Item itemTarget = findItem(context, target);
|
||||
if (itemTarget != null) {
|
||||
sTarget.setTarget(itemTarget);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
return sTarget;
|
||||
}
|
||||
|
||||
private Suggestion convertSolrDoc(Context context, SolrDocument solrDoc, String sourceName) {
|
||||
Item target = findItem(context, (String) solrDoc.getFieldValue(TARGET_ID));
|
||||
|
||||
Suggestion suggestion = new Suggestion(sourceName, target, (String) solrDoc.getFieldValue(SUGGESTION_ID));
|
||||
suggestion.setDisplay((String) solrDoc.getFieldValue(DISPLAY));
|
||||
suggestion.getMetadata()
|
||||
.add(new MetadataValueDTO("dc", "title", null, null, (String) solrDoc.getFieldValue(TITLE)));
|
||||
suggestion.getMetadata()
|
||||
.add(new MetadataValueDTO("dc", "date", "issued", null, (String) solrDoc.getFieldValue(DATE)));
|
||||
suggestion.getMetadata().add(
|
||||
new MetadataValueDTO("dc", "description", "abstract", null, (String) solrDoc.getFieldValue(ABSTRACT)));
|
||||
|
||||
suggestion.setExternalSourceUri((String) solrDoc.getFieldValue(EXTERNAL_URI));
|
||||
if (solrDoc.containsKey(CATEGORY)) {
|
||||
for (Object o : solrDoc.getFieldValues(CATEGORY)) {
|
||||
suggestion.getMetadata().add(
|
||||
new MetadataValueDTO("dc", "source", null, null, (String) o));
|
||||
}
|
||||
}
|
||||
if (solrDoc.containsKey(CONTRIBUTORS)) {
|
||||
for (Object o : solrDoc.getFieldValues(CONTRIBUTORS)) {
|
||||
suggestion.getMetadata().add(
|
||||
new MetadataValueDTO("dc", "contributor", "author", null, (String) o));
|
||||
}
|
||||
}
|
||||
String evidencesJson = (String) solrDoc.getFieldValue(EVIDENCES);
|
||||
ObjectMapper jsonMapper = new JsonMapper();
|
||||
jsonMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
|
||||
List<SuggestionEvidence> evidences = new LinkedList<SuggestionEvidence>();
|
||||
try {
|
||||
evidences = jsonMapper.readValue(evidencesJson, new TypeReference<List<SuggestionEvidence>>() {});
|
||||
} catch (JsonProcessingException e) {
|
||||
log.error(e);
|
||||
}
|
||||
suggestion.getEvidences().addAll(evidences);
|
||||
return suggestion;
|
||||
}
|
||||
|
||||
private Item findItem(Context context, UUID itemId) {
|
||||
try {
|
||||
return itemService.find(context, itemId);
|
||||
} catch (SQLException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private Item findItem(Context context, String itemId) {
|
||||
return findItem(context, UUIDUtils.fromString(itemId));
|
||||
}
|
||||
}
|
@@ -0,0 +1,99 @@
|
||||
/**
|
||||
* 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.suggestion;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
import org.dspace.content.Item;
|
||||
import org.dspace.content.dto.MetadataValueDTO;
|
||||
|
||||
/**
|
||||
* This entity contains metadatas that should be added to the targeted Item
|
||||
*
|
||||
* @author Andrea Bollini (andrea.bollini at 4science.it)
|
||||
*/
|
||||
public class Suggestion {
|
||||
|
||||
/** id of the suggestion */
|
||||
private String id;
|
||||
|
||||
/** the dc.title of the item */
|
||||
private String display;
|
||||
|
||||
/** the external source name the suggestion comes from */
|
||||
private String source;
|
||||
|
||||
/** external uri of the item */
|
||||
private String externalSourceUri;
|
||||
|
||||
/** item targeted by this suggestion */
|
||||
private Item target;
|
||||
|
||||
private List<SuggestionEvidence> evidences = new LinkedList<SuggestionEvidence>();
|
||||
|
||||
private List<MetadataValueDTO> metadata = new LinkedList<MetadataValueDTO>();
|
||||
|
||||
/** suggestion creation
|
||||
* @param source name of the external source
|
||||
* @param target the targeted item in repository
|
||||
* @param idPart external item id, used mainly for suggestion @see #id creation
|
||||
* */
|
||||
public Suggestion(String source, Item target, String idPart) {
|
||||
this.source = source;
|
||||
this.target = target;
|
||||
this.id = source + ":" + target.getID().toString() + ":" + idPart;
|
||||
}
|
||||
|
||||
public String getDisplay() {
|
||||
return display;
|
||||
}
|
||||
|
||||
public void setDisplay(String display) {
|
||||
this.display = display;
|
||||
}
|
||||
|
||||
public String getSource() {
|
||||
return source;
|
||||
}
|
||||
|
||||
public String getExternalSourceUri() {
|
||||
return externalSourceUri;
|
||||
}
|
||||
|
||||
public void setExternalSourceUri(String externalSourceUri) {
|
||||
this.externalSourceUri = externalSourceUri;
|
||||
}
|
||||
|
||||
public List<SuggestionEvidence> getEvidences() {
|
||||
return evidences;
|
||||
}
|
||||
|
||||
public List<MetadataValueDTO> getMetadata() {
|
||||
return metadata;
|
||||
}
|
||||
|
||||
public Item getTarget() {
|
||||
return target;
|
||||
}
|
||||
|
||||
public String getID() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public Double getScore() {
|
||||
if (evidences != null && evidences.size() > 0) {
|
||||
double score = 0;
|
||||
for (SuggestionEvidence evidence : evidences) {
|
||||
score += evidence.getScore();
|
||||
}
|
||||
return score;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
@@ -0,0 +1,61 @@
|
||||
/**
|
||||
* The contents of this file are subject to the license and copyright
|
||||
* detailed in the LICENSE and NOTICE files at the root of the source
|
||||
* tree and available online at
|
||||
*
|
||||
* http://www.dspace.org/license/
|
||||
*/
|
||||
package org.dspace.app.suggestion;
|
||||
|
||||
/**
|
||||
* This DTO class is returned by an {@link org.dspace.app.suggestion.openaire.EvidenceScorer} to model the concept of
|
||||
* an evidence / fact that has been used to evaluate the precision of a suggestion increasing or decreasing the score
|
||||
* of the suggestion.
|
||||
*
|
||||
* @author Andrea Bollini (andrea.bollini at 4science.it)
|
||||
*/
|
||||
public class SuggestionEvidence {
|
||||
|
||||
/** name of the evidence */
|
||||
private String name;
|
||||
|
||||
/** positive or negative value to influence the score of the suggestion */
|
||||
private double score;
|
||||
|
||||
/** additional notes */
|
||||
private String notes;
|
||||
|
||||
public SuggestionEvidence() {
|
||||
}
|
||||
|
||||
public SuggestionEvidence(String name, double score, String notes) {
|
||||
this.name = name;
|
||||
this.score = score;
|
||||
this.notes = notes;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public double getScore() {
|
||||
return score;
|
||||
}
|
||||
|
||||
public void setScore(double score) {
|
||||
this.score = score;
|
||||
}
|
||||
|
||||
public String getNotes() {
|
||||
return notes;
|
||||
}
|
||||
|
||||
public void setNotes(String notes) {
|
||||
this.notes = notes;
|
||||
}
|
||||
|
||||
}
|
@@ -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.suggestion;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
import org.dspace.core.Context;
|
||||
import org.dspace.external.model.ExternalDataObject;
|
||||
|
||||
/**
|
||||
*
|
||||
* Interface for suggestion management like finding and counting.
|
||||
* @see org.dspace.app.suggestion.SuggestionTarget
|
||||
* @author Francesco Bacchelli (francesco.bacchelli at 4science.com)
|
||||
*
|
||||
*/
|
||||
public interface SuggestionProvider {
|
||||
|
||||
/** find all suggestion targets
|
||||
* @see org.dspace.app.suggestion.SuggestionTarget
|
||||
* */
|
||||
public List<SuggestionTarget> findAllTargets(Context context, int pageSize, long offset);
|
||||
|
||||
/** count all suggestion targets */
|
||||
public long countAllTargets(Context context);
|
||||
|
||||
/** find a suggestion target by UUID */
|
||||
public SuggestionTarget findTarget(Context context, UUID target);
|
||||
|
||||
/** find unprocessed suggestions (paged) by target UUID
|
||||
* @see org.dspace.app.suggestion.Suggestion
|
||||
* */
|
||||
public List<Suggestion> findAllUnprocessedSuggestions(Context context, UUID target, int pageSize, long offset,
|
||||
boolean ascending);
|
||||
|
||||
/** find unprocessed suggestions by target UUID */
|
||||
public long countUnprocessedSuggestionByTarget(Context context, UUID target);
|
||||
|
||||
/** find an unprocessed suggestion by target UUID and suggestion id */
|
||||
public Suggestion findUnprocessedSuggestion(Context context, UUID target, String id);
|
||||
|
||||
/** reject a specific suggestion by target @param target and by suggestion id @param idPart */
|
||||
public void rejectSuggestion(Context context, UUID target, String idPart);
|
||||
|
||||
/** flag a suggestion as processed */
|
||||
public void flagRelatedSuggestionsAsProcessed(Context context, ExternalDataObject externalDataObject);
|
||||
|
||||
}
|
@@ -0,0 +1,61 @@
|
||||
/**
|
||||
* The contents of this file are subject to the license and copyright
|
||||
* detailed in the LICENSE and NOTICE files at the root of the source
|
||||
* tree and available online at
|
||||
*
|
||||
* http://www.dspace.org/license/
|
||||
*/
|
||||
package org.dspace.app.suggestion;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
import org.dspace.core.Context;
|
||||
|
||||
/**
|
||||
* Service that handles {@link Suggestion}.
|
||||
*
|
||||
* @author Andrea Bollini (andrea.bollini at 4science.it)
|
||||
*/
|
||||
public interface SuggestionService {
|
||||
|
||||
/** find a {@link SuggestionTarget } by source name and suggestion id */
|
||||
public SuggestionTarget find(Context context, String source, UUID id);
|
||||
|
||||
/** count all suggetion targets by suggestion source */
|
||||
public long countAll(Context context, String source);
|
||||
|
||||
/** find all suggestion targets by source (paged) */
|
||||
public List<SuggestionTarget> findAllTargets(Context context, String source, int pageSize, long offset);
|
||||
|
||||
/** count all (unprocessed) suggestions by the given target uuid */
|
||||
public long countAllByTarget(Context context, UUID target);
|
||||
|
||||
/** find suggestion target by targeted item (paged) */
|
||||
public List<SuggestionTarget> findByTarget(Context context, UUID target, int pageSize, long offset);
|
||||
|
||||
/** find suggestion source by source name */
|
||||
public SuggestionSource findSource(Context context, String source);
|
||||
|
||||
/** count all suggestion sources */
|
||||
public long countSources(Context context);
|
||||
|
||||
/** find all suggestion sources (paged) */
|
||||
public List<SuggestionSource> findAllSources(Context context, int pageSize, long offset);
|
||||
|
||||
/** find unprocessed suggestion by id */
|
||||
public Suggestion findUnprocessedSuggestion(Context context, String id);
|
||||
|
||||
/** reject a specific suggestion by its id */
|
||||
public void rejectSuggestion(Context context, String id);
|
||||
|
||||
/** find all suggestions by targeted item and external source */
|
||||
public List<Suggestion> findByTargetAndSource(Context context, UUID target, String source, int pageSize,
|
||||
long offset, boolean ascending);
|
||||
|
||||
/** count all suggestions by targeted item id and source name */
|
||||
public long countAllByTargetAndSource(Context context, String source, UUID target);
|
||||
|
||||
/** returns all suggestion providers */
|
||||
public List<SuggestionProvider> getSuggestionProviders();
|
||||
}
|
@@ -0,0 +1,194 @@
|
||||
/**
|
||||
* 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.suggestion;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Collectors;
|
||||
import javax.annotation.Resource;
|
||||
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.dspace.core.Context;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Service
|
||||
public class SuggestionServiceImpl implements SuggestionService {
|
||||
private static final Logger log = org.apache.logging.log4j.LogManager.getLogger(SuggestionServiceImpl.class);
|
||||
|
||||
@Resource(name = "suggestionProviders")
|
||||
private Map<String, SuggestionProvider> providersMap;
|
||||
|
||||
@Override
|
||||
public List<SuggestionProvider> getSuggestionProviders() {
|
||||
if (providersMap != null) {
|
||||
return providersMap.values().stream().collect(Collectors.toList());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SuggestionTarget find(Context context, String source, UUID id) {
|
||||
if (providersMap.containsKey(source)) {
|
||||
return providersMap.get(source).findTarget(context, id);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public long countAll(Context context, String source) {
|
||||
if (providersMap.containsKey(source)) {
|
||||
return providersMap.get(source).countAllTargets(context);
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<SuggestionTarget> findAllTargets(Context context, String source, int pageSize, long offset) {
|
||||
if (providersMap.containsKey(source)) {
|
||||
return providersMap.get(source).findAllTargets(context, pageSize, offset);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public long countAllByTarget(Context context, UUID target) {
|
||||
int count = 0;
|
||||
for (String provider : providersMap.keySet()) {
|
||||
if (providersMap.get(provider).countUnprocessedSuggestionByTarget(context, target) > 0) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<SuggestionTarget> findByTarget(Context context, UUID target, int pageSize, long offset) {
|
||||
List<SuggestionTarget> fullSourceTargets = new ArrayList<SuggestionTarget>();
|
||||
for (String source : providersMap.keySet()) {
|
||||
// all the suggestion target will be related to the same target (i.e. the same researcher - person item)
|
||||
SuggestionTarget sTarget = providersMap.get(source).findTarget(context, target);
|
||||
if (sTarget != null && sTarget.getTotal() > 0) {
|
||||
fullSourceTargets.add(sTarget);
|
||||
}
|
||||
}
|
||||
fullSourceTargets.sort(new Comparator<SuggestionTarget>() {
|
||||
@Override
|
||||
public int compare(SuggestionTarget arg0, SuggestionTarget arg1) {
|
||||
return -(arg0.getTotal() - arg1.getTotal());
|
||||
}
|
||||
}
|
||||
);
|
||||
// this list will be as large as the number of sources available in the repository so it is unlikely that
|
||||
// real pagination will occur
|
||||
return fullSourceTargets.stream().skip(offset).limit(pageSize).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public long countSources(Context context) {
|
||||
return providersMap.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public SuggestionSource findSource(Context context, String source) {
|
||||
if (providersMap.containsKey(source)) {
|
||||
SuggestionSource ssource = new SuggestionSource(source);
|
||||
ssource.setTotal((int) providersMap.get(source).countAllTargets(context));
|
||||
return ssource;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<SuggestionSource> findAllSources(Context context, int pageSize, long offset) {
|
||||
List<SuggestionSource> fullSources = getSources(context).stream().skip(offset).limit(pageSize)
|
||||
.collect(Collectors.toList());
|
||||
return fullSources;
|
||||
}
|
||||
|
||||
private List<SuggestionSource> getSources(Context context) {
|
||||
List<SuggestionSource> results = new ArrayList<SuggestionSource>();
|
||||
for (String source : providersMap.keySet()) {
|
||||
SuggestionSource ssource = new SuggestionSource(source);
|
||||
ssource.setTotal((int) providersMap.get(source).countAllTargets(context));
|
||||
results.add(ssource);
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long countAllByTargetAndSource(Context context, String source, UUID target) {
|
||||
if (providersMap.containsKey(source)) {
|
||||
return providersMap.get(source).countUnprocessedSuggestionByTarget(context, target);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Suggestion> findByTargetAndSource(Context context, UUID target, String source, int pageSize,
|
||||
long offset, boolean ascending) {
|
||||
if (providersMap.containsKey(source)) {
|
||||
return providersMap.get(source).findAllUnprocessedSuggestions(context, target, pageSize, offset, ascending);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Suggestion findUnprocessedSuggestion(Context context, String id) {
|
||||
String source = null;
|
||||
UUID target = null;
|
||||
String idPart = null;
|
||||
String[] split;
|
||||
try {
|
||||
split = id.split(":", 3);
|
||||
source = split[0];
|
||||
target = UUID.fromString(split[1]);
|
||||
idPart = split[2];
|
||||
} catch (Exception e) {
|
||||
log.warn("findSuggestion got an invalid id " + id + ", return null");
|
||||
return null;
|
||||
}
|
||||
if (split.length != 3) {
|
||||
return null;
|
||||
}
|
||||
if (providersMap.containsKey(source)) {
|
||||
return providersMap.get(source).findUnprocessedSuggestion(context, target, idPart);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void rejectSuggestion(Context context, String id) {
|
||||
String source = null;
|
||||
UUID target = null;
|
||||
String idPart = null;
|
||||
String[] split;
|
||||
try {
|
||||
split = id.split(":", 3);
|
||||
source = split[0];
|
||||
target = UUID.fromString(split[1]);
|
||||
idPart = split[2];
|
||||
} catch (Exception e) {
|
||||
log.warn("rejectSuggestion got an invalid id " + id + ", doing nothing");
|
||||
return;
|
||||
}
|
||||
if (split.length != 3) {
|
||||
return;
|
||||
}
|
||||
if (providersMap.containsKey(source)) {
|
||||
providersMap.get(source).rejectSuggestion(context, target, idPart);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@@ -0,0 +1,49 @@
|
||||
/**
|
||||
* 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.suggestion;
|
||||
|
||||
/**
|
||||
* This DTO class is used to pass around the number of items interested by suggestion provided by a specific source
|
||||
* (i.e. openaire)
|
||||
*
|
||||
* @author Andrea Bollini (andrea.bollini at 4science.it)
|
||||
*/
|
||||
public class SuggestionSource {
|
||||
|
||||
/** source name of the suggestion */
|
||||
private String name;
|
||||
|
||||
/** number of targeted items */
|
||||
private int total;
|
||||
|
||||
public SuggestionSource() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Summarize the available suggestions from a source.
|
||||
*
|
||||
* @param name the name must be not null
|
||||
*/
|
||||
public SuggestionSource(String name) {
|
||||
super();
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getID() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public int getTotal() {
|
||||
return total;
|
||||
}
|
||||
|
||||
public void setTotal(int total) {
|
||||
this.total = total;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,75 @@
|
||||
/**
|
||||
* 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.suggestion;
|
||||
|
||||
import org.dspace.content.Item;
|
||||
|
||||
/**
|
||||
* This DTO class is used to pass around the number of suggestions available from a specific source for a target
|
||||
* repository item
|
||||
*
|
||||
* @author Andrea Bollini (andrea.bollini at 4science.it)
|
||||
*/
|
||||
public class SuggestionTarget {
|
||||
|
||||
/** the item targeted */
|
||||
private Item target;
|
||||
|
||||
/** source name of the suggestion */
|
||||
private String source;
|
||||
|
||||
/** total count of suggestions for same target and source */
|
||||
private int total;
|
||||
|
||||
public SuggestionTarget() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrap a target repository item (usually a person item) into a suggestion target.
|
||||
*
|
||||
* @param item must be not null
|
||||
*/
|
||||
public SuggestionTarget(Item item) {
|
||||
super();
|
||||
this.target = item;
|
||||
}
|
||||
|
||||
/**
|
||||
* The suggestion target uses the concatenation of the source and target uuid separated by colon as id
|
||||
*
|
||||
* @return the source:uuid of the wrapped item
|
||||
*/
|
||||
public String getID() {
|
||||
return source + ":" + (target != null ? target.getID() : "");
|
||||
}
|
||||
|
||||
public Item getTarget() {
|
||||
return target;
|
||||
}
|
||||
|
||||
public void setTarget(Item target) {
|
||||
this.target = target;
|
||||
}
|
||||
|
||||
public String getSource() {
|
||||
return source;
|
||||
}
|
||||
|
||||
public void setSource(String source) {
|
||||
this.source = source;
|
||||
}
|
||||
|
||||
public int getTotal() {
|
||||
return total;
|
||||
}
|
||||
|
||||
public void setTotal(int total) {
|
||||
this.total = total;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,111 @@
|
||||
/**
|
||||
* 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.suggestion;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.dspace.external.model.ExternalDataObject;
|
||||
|
||||
/**
|
||||
* This utility class provides convenient methods to deal with the
|
||||
* {@link ExternalDataObject} for the purpose of the Suggestion framework
|
||||
*
|
||||
* @author Andrea Bollini (andrea.bollini at 4science.it)
|
||||
*/
|
||||
public class SuggestionUtils {
|
||||
private SuggestionUtils() {
|
||||
}
|
||||
/**
|
||||
* This method receive an ExternalDataObject and a metadatum key.
|
||||
* It return only the values of the Metadata associated with the key.
|
||||
*
|
||||
* @param record the ExternalDataObject to extract metadata from
|
||||
* @param schema schema of the searching record
|
||||
* @param element element of the searching record
|
||||
* @param qualifier qualifier of the searching record
|
||||
* @return value of the first matching record
|
||||
*/
|
||||
public static List<String> getAllEntriesByMetadatum(ExternalDataObject record, String schema, String element,
|
||||
String qualifier) {
|
||||
return record.getMetadata().stream()
|
||||
.filter(x ->
|
||||
StringUtils.equals(x.getSchema(), schema)
|
||||
&& StringUtils.equals(x.getElement(), element)
|
||||
&& StringUtils.equals(x.getQualifier(), qualifier))
|
||||
.map(x -> x.getValue()).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
/**
|
||||
* This method receive an ExternalDataObject and a metadatum key.
|
||||
* It return only the values of the Metadata associated with the key.
|
||||
*
|
||||
* @param record the ExternalDataObject to extract metadata from
|
||||
* @param metadataFieldKey the metadata field key (i.e. dc.title or dc.contributor.author),
|
||||
* the jolly char is not supported
|
||||
* @return value of the first matching record
|
||||
*/
|
||||
public static List<String> getAllEntriesByMetadatum(ExternalDataObject record, String metadataFieldKey) {
|
||||
if (metadataFieldKey == null) {
|
||||
return Collections.EMPTY_LIST;
|
||||
}
|
||||
String[] fields = metadataFieldKey.split("\\.");
|
||||
String schema = fields[0];
|
||||
String element = fields[1];
|
||||
String qualifier = null;
|
||||
if (fields.length == 3) {
|
||||
qualifier = fields[2];
|
||||
}
|
||||
return getAllEntriesByMetadatum(record, schema, element, qualifier);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method receive and ExternalDataObject and a metadatum key.
|
||||
* It return only the value of the first Metadatum from the list associated with the key.
|
||||
*
|
||||
* @param record the ExternalDataObject to extract metadata from
|
||||
* @param schema schema of the searching record
|
||||
* @param element element of the searching record
|
||||
* @param qualifier qualifier of the searching record
|
||||
* @return value of the first matching record
|
||||
*/
|
||||
public static String getFirstEntryByMetadatum(ExternalDataObject record, String schema, String element,
|
||||
String qualifier) {
|
||||
return record.getMetadata().stream()
|
||||
.filter(x ->
|
||||
StringUtils.equals(x.getSchema(), schema)
|
||||
&& StringUtils.equals(x.getElement(), element)
|
||||
&& StringUtils.equals(x.getQualifier(), qualifier))
|
||||
.map(x -> x.getValue()).findFirst().orElse(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method receive and ExternalDataObject and a metadatum key.
|
||||
* It return only the value of the first Metadatum from the list associated with the key.
|
||||
*
|
||||
* @param record the ExternalDataObject to extract metadata from
|
||||
* @param metadataFieldKey the metadata field key (i.e. dc.title or dc.contributor.author),
|
||||
* the jolly char is not supported
|
||||
* @return value of the first matching record
|
||||
*/
|
||||
public static String getFirstEntryByMetadatum(ExternalDataObject record, String metadataFieldKey) {
|
||||
if (metadataFieldKey == null) {
|
||||
return null;
|
||||
}
|
||||
String[] fields = metadataFieldKey.split("\\.");
|
||||
String schema = fields[0];
|
||||
String element = fields[1];
|
||||
String qualifier = null;
|
||||
if (fields.length == 3) {
|
||||
qualifier = fields[2];
|
||||
}
|
||||
return getFirstEntryByMetadatum(record, schema, element, qualifier);
|
||||
}
|
||||
}
|
@@ -0,0 +1,151 @@
|
||||
/**
|
||||
* 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.suggestion.openaire;
|
||||
|
||||
import static org.dspace.app.suggestion.SuggestionUtils.getAllEntriesByMetadatum;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import com.ibm.icu.text.CharsetDetector;
|
||||
import com.ibm.icu.text.CharsetMatch;
|
||||
import com.ibm.icu.text.Normalizer;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.dspace.app.suggestion.SuggestionEvidence;
|
||||
import org.dspace.content.Item;
|
||||
import org.dspace.content.MetadataValue;
|
||||
import org.dspace.content.service.ItemService;
|
||||
import org.dspace.external.model.ExternalDataObject;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
/**
|
||||
* Implementation of {@see org.dspace.app.suggestion.oaire.EvidenceScorer} which evaluate ImportRecords
|
||||
* based on Author's name.
|
||||
*
|
||||
* @author Andrea Bollini (andrea.bollini at 4science dot it)
|
||||
* @author Pasquale Cavallo (pasquale.cavallo at 4science dot it)
|
||||
*
|
||||
*/
|
||||
public class AuthorNamesScorer implements EvidenceScorer {
|
||||
|
||||
private List<String> contributorMetadata;
|
||||
|
||||
private List<String> names;
|
||||
|
||||
@Autowired
|
||||
private ItemService itemService;
|
||||
|
||||
/**
|
||||
* returns the metadata key of the Item which to base the filter on
|
||||
* @return metadata key
|
||||
*/
|
||||
public List<String> getContributorMetadata() {
|
||||
return contributorMetadata;
|
||||
}
|
||||
|
||||
/**
|
||||
* set the metadata key of the Item which to base the filter on
|
||||
*/
|
||||
public void setContributorMetadata(List<String> contributorMetadata) {
|
||||
this.contributorMetadata = contributorMetadata;
|
||||
}
|
||||
|
||||
/**
|
||||
* return the metadata key of ImportRecord which to base the filter on
|
||||
* @return
|
||||
*/
|
||||
public List<String> getNames() {
|
||||
return names;
|
||||
}
|
||||
|
||||
/**
|
||||
* set the metadata key of ImportRecord which to base the filter on
|
||||
*/
|
||||
public void setNames(List<String> names) {
|
||||
this.names = names;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method which is responsible to evaluate ImportRecord based on authors name.
|
||||
* This method extract the researcher name from Item using contributorMetadata fields
|
||||
* and try to match them with values extract from ImportRecord using metadata keys defined
|
||||
* in names.
|
||||
* ImportRecords which don't match will be discarded.
|
||||
*
|
||||
* @param importRecord the import record to check
|
||||
* @param researcher DSpace item
|
||||
* @return the generated evidence or null if the record must be discarded
|
||||
*/
|
||||
@Override
|
||||
public SuggestionEvidence computeEvidence(Item researcher, ExternalDataObject importRecord) {
|
||||
List<String[]> names = searchMetadataValues(researcher);
|
||||
int maxNameLenght = names.stream().mapToInt(n -> n[0].length()).max().orElse(1);
|
||||
List<String> metadataAuthors = new ArrayList<>();
|
||||
for (String contributorMetadatum : contributorMetadata) {
|
||||
metadataAuthors.addAll(getAllEntriesByMetadatum(importRecord, contributorMetadatum));
|
||||
}
|
||||
List<String> normalizedMetadataAuthors = metadataAuthors.stream().map(x -> normalize(x))
|
||||
.collect(Collectors.toList());
|
||||
int idx = 0;
|
||||
for (String nMetadataAuthor : normalizedMetadataAuthors) {
|
||||
Optional<String[]> found = names.stream()
|
||||
.filter(a -> StringUtils.equalsIgnoreCase(a[0], nMetadataAuthor)).findFirst();
|
||||
if (found.isPresent()) {
|
||||
return new SuggestionEvidence(this.getClass().getSimpleName(),
|
||||
100 * ((double) nMetadataAuthor.length() / (double) maxNameLenght),
|
||||
"The author " + metadataAuthors.get(idx) + " at position " + (idx + 1)
|
||||
+ " in the authors list matches the name " + found.get()[1]
|
||||
+ " in the researcher profile");
|
||||
}
|
||||
idx++;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return list of Item metadata values starting from metadata keys defined in class level variable names.
|
||||
*
|
||||
* @param researcher DSpace item
|
||||
* @return list of metadata values
|
||||
*/
|
||||
private List<String[]> searchMetadataValues(Item researcher) {
|
||||
List<String[]> authors = new ArrayList<String[]>();
|
||||
for (String name : names) {
|
||||
List<MetadataValue> values = itemService.getMetadataByMetadataString(researcher, name);
|
||||
if (values != null) {
|
||||
for (MetadataValue v : values) {
|
||||
authors.add(new String[] {normalize(v.getValue()), v.getValue()});
|
||||
}
|
||||
}
|
||||
}
|
||||
return authors;
|
||||
}
|
||||
|
||||
/**
|
||||
* cleans up undesired characters
|
||||
* @param value the string to clean up
|
||||
* @return cleaned up string
|
||||
* */
|
||||
private String normalize(String value) {
|
||||
String norm = Normalizer.normalize(value, Normalizer.NFD);
|
||||
CharsetDetector cd = new CharsetDetector();
|
||||
cd.setText(value.getBytes());
|
||||
CharsetMatch detect = cd.detect();
|
||||
if (detect != null && detect.getLanguage() != null) {
|
||||
norm = norm.replaceAll("[^\\p{L}]", " ").toLowerCase(new Locale(detect.getLanguage()));
|
||||
} else {
|
||||
norm = norm.replaceAll("[^\\p{L}]", " ").toLowerCase();
|
||||
}
|
||||
return Arrays.asList(norm.split("\\s+")).stream().sorted().collect(Collectors.joining());
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,214 @@
|
||||
/**
|
||||
* 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.suggestion.openaire;
|
||||
|
||||
import java.util.Calendar;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.GregorianCalendar;
|
||||
import java.util.List;
|
||||
|
||||
import org.dspace.app.suggestion.SuggestionEvidence;
|
||||
import org.dspace.app.suggestion.SuggestionUtils;
|
||||
import org.dspace.content.Item;
|
||||
import org.dspace.content.MetadataValue;
|
||||
import org.dspace.content.service.ItemService;
|
||||
import org.dspace.external.model.ExternalDataObject;
|
||||
import org.dspace.util.MultiFormatDateParser;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
/**
|
||||
* Implementation of {@see org.dspace.app.suggestion.oaire.EvidenceScorer} which evaluate ImportRecords
|
||||
* based on the distance from a date extracted from the ResearcherProfile (birthday / graduation date)
|
||||
*
|
||||
* @author Andrea Bollini (andrea.bollini at 4science dot it)
|
||||
*
|
||||
*/
|
||||
public class DateScorer implements EvidenceScorer {
|
||||
|
||||
/**
|
||||
* if available it should contains the metadata field key in the form (schema.element[.qualifier]) that contains
|
||||
* the birth date of the researcher
|
||||
*/
|
||||
private String birthDateMetadata;
|
||||
|
||||
/**
|
||||
* if available it should contains the metadata field key in the form (schema.element[.qualifier]) that contains
|
||||
* the date of graduation of the researcher. If the metadata has multiple values the min will be used
|
||||
*/
|
||||
private String educationDateMetadata;
|
||||
|
||||
/**
|
||||
* The minimal age that is expected for a researcher to be a potential author of a scholarly contribution
|
||||
* (i.e. the minimum delta from the publication date and the birth date)
|
||||
*/
|
||||
private int birthDateDelta = 20;
|
||||
|
||||
/**
|
||||
* The maximum age that is expected for a researcher to be a potential author of a scholarly contribution
|
||||
* (i.e. the maximum delta from the publication date and the birth date)
|
||||
*/
|
||||
private int birthDateRange = 50;
|
||||
|
||||
/**
|
||||
* The number of year from/before the graduation that is expected for a researcher to be a potential
|
||||
* author of a scholarly contribution (i.e. the minimum delta from the publication date and the first
|
||||
* graduation date)
|
||||
*/
|
||||
private int educationDateDelta = -3;
|
||||
|
||||
/**
|
||||
* The maximum scientific longevity that is expected for a researcher from its graduation to be a potential
|
||||
* author of a scholarly contribution (i.e. the maximum delta from the publication date and the first
|
||||
* graduation date)
|
||||
*/
|
||||
private int educationDateRange = 50;
|
||||
|
||||
@Autowired
|
||||
private ItemService itemService;
|
||||
|
||||
/**
|
||||
* the metadata used in the publication to track the publication date (i.e. dc.date.issued)
|
||||
*/
|
||||
private String publicationDateMetadata;
|
||||
|
||||
public void setItemService(ItemService itemService) {
|
||||
this.itemService = itemService;
|
||||
}
|
||||
|
||||
public void setBirthDateMetadata(String birthDate) {
|
||||
this.birthDateMetadata = birthDate;
|
||||
}
|
||||
|
||||
public String getBirthDateMetadata() {
|
||||
return birthDateMetadata;
|
||||
}
|
||||
|
||||
public void setEducationDateMetadata(String educationDate) {
|
||||
this.educationDateMetadata = educationDate;
|
||||
}
|
||||
|
||||
public String getEducationDateMetadata() {
|
||||
return educationDateMetadata;
|
||||
}
|
||||
|
||||
public void setBirthDateDelta(int birthDateDelta) {
|
||||
this.birthDateDelta = birthDateDelta;
|
||||
}
|
||||
|
||||
public void setBirthDateRange(int birthDateRange) {
|
||||
this.birthDateRange = birthDateRange;
|
||||
}
|
||||
|
||||
public void setEducationDateDelta(int educationDateDelta) {
|
||||
this.educationDateDelta = educationDateDelta;
|
||||
}
|
||||
|
||||
public void setEducationDateRange(int educationDateRange) {
|
||||
this.educationDateRange = educationDateRange;
|
||||
}
|
||||
|
||||
public void setPublicationDateMetadata(String publicationDateMetadata) {
|
||||
this.publicationDateMetadata = publicationDateMetadata;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method which is responsible to evaluate ImportRecord based on the publication date.
|
||||
* ImportRecords which have a date outside the defined or calculated expected range will be discarded.
|
||||
* {@link DateScorer#birthDateMetadata}, {@link DateScorer#educationDateMetadata}
|
||||
*
|
||||
* @param importRecord the ExternalDataObject to check
|
||||
* @param researcher DSpace item
|
||||
* @return the generated evidence or null if the record must be discarded
|
||||
*/
|
||||
@Override
|
||||
public SuggestionEvidence computeEvidence(Item researcher, ExternalDataObject importRecord) {
|
||||
Integer[] range = calculateRange(researcher);
|
||||
if (range == null) {
|
||||
return new SuggestionEvidence(this.getClass().getSimpleName(),
|
||||
0,
|
||||
"No assumption was possible about the publication year range. "
|
||||
+ "Please consider setting your birthday in your profile.");
|
||||
} else {
|
||||
String optDate = SuggestionUtils.getFirstEntryByMetadatum(importRecord, publicationDateMetadata);
|
||||
int year = getYear(optDate);
|
||||
if (year > 0) {
|
||||
if ((range[0] == null || year >= range[0]) &&
|
||||
(range[1] == null || year <= range[1])) {
|
||||
return new SuggestionEvidence(this.getClass().getSimpleName(),
|
||||
10,
|
||||
"The publication date is within the expected range [" + range[0] + ", "
|
||||
+ range[1] + "]");
|
||||
} else {
|
||||
// outside the range, discard the suggestion
|
||||
return null;
|
||||
}
|
||||
} else {
|
||||
return new SuggestionEvidence(this.getClass().getSimpleName(),
|
||||
0,
|
||||
"No assumption was possible as the publication date is " + (optDate != null
|
||||
? "unprocessable [" + optDate + "]"
|
||||
: "unknown"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* returns min and max year interval in between it's probably that the researcher
|
||||
* actually contributed to the suggested item
|
||||
* @param researcher
|
||||
* @return
|
||||
*/
|
||||
private Integer[] calculateRange(Item researcher) {
|
||||
String birthDateStr = getSingleValue(researcher, birthDateMetadata);
|
||||
int birthDateYear = getYear(birthDateStr);
|
||||
int educationDateYear = getListMetadataValues(researcher, educationDateMetadata).stream()
|
||||
.mapToInt(x -> getYear(x.getValue())).filter(d -> d > 0).min().orElse(-1);
|
||||
if (educationDateYear > 0) {
|
||||
return new Integer[] {
|
||||
educationDateYear + educationDateDelta,
|
||||
educationDateYear + educationDateDelta + educationDateRange
|
||||
};
|
||||
} else if (birthDateYear > 0) {
|
||||
return new Integer[] {
|
||||
birthDateYear + birthDateDelta,
|
||||
birthDateYear + birthDateDelta + birthDateRange
|
||||
};
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private List<MetadataValue> getListMetadataValues(Item researcher, String metadataKey) {
|
||||
if (metadataKey != null) {
|
||||
return itemService.getMetadataByMetadataString(researcher, metadataKey);
|
||||
} else {
|
||||
return Collections.EMPTY_LIST;
|
||||
}
|
||||
}
|
||||
|
||||
private String getSingleValue(Item researcher, String metadataKey) {
|
||||
if (metadataKey != null) {
|
||||
return itemService.getMetadata(researcher, metadataKey);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private int getYear(String birthDateStr) {
|
||||
int birthDateYear = -1;
|
||||
if (birthDateStr != null) {
|
||||
Date birthDate = MultiFormatDateParser.parse(birthDateStr);
|
||||
if (birthDate != null) {
|
||||
Calendar calendar = new GregorianCalendar();
|
||||
calendar.setTime(birthDate);
|
||||
birthDateYear = calendar.get(Calendar.YEAR);
|
||||
}
|
||||
}
|
||||
return birthDateYear;
|
||||
}
|
||||
}
|
@@ -0,0 +1,37 @@
|
||||
/**
|
||||
* 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.suggestion.openaire;
|
||||
|
||||
import org.dspace.app.suggestion.SuggestionEvidence;
|
||||
import org.dspace.content.Item;
|
||||
import org.dspace.external.model.ExternalDataObject;
|
||||
|
||||
/**
|
||||
* Interface used in {@see org.dspace.app.suggestion.oaire.PublicationApproverServiceImpl}
|
||||
* to construct filtering pipeline.
|
||||
*
|
||||
* For each EvidenceScorer, the service call computeEvidence method.
|
||||
*
|
||||
* @author Andrea Bollini (andrea.bollini at 4science dot it)
|
||||
* @author Pasquale Cavallo (pasquale.cavallo at 4science dot it)
|
||||
*
|
||||
*/
|
||||
public interface EvidenceScorer {
|
||||
|
||||
/**
|
||||
* Method to compute the suggestion evidence of an ImportRecord, a null evidence
|
||||
* would lead the record to be discarded.
|
||||
*
|
||||
* @param researcher DSpace item
|
||||
* @param importRecord the record to evaluate
|
||||
* @return the generated suggestion evidence or null if the record should be
|
||||
* discarded
|
||||
*/
|
||||
public SuggestionEvidence computeEvidence(Item researcher, ExternalDataObject importRecord);
|
||||
|
||||
}
|
@@ -0,0 +1,256 @@
|
||||
/**
|
||||
* 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.suggestion.openaire;
|
||||
|
||||
import static org.dspace.app.suggestion.SuggestionUtils.getAllEntriesByMetadatum;
|
||||
import static org.dspace.app.suggestion.SuggestionUtils.getFirstEntryByMetadatum;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.solr.client.solrj.SolrServerException;
|
||||
import org.dspace.app.suggestion.SolrSuggestionProvider;
|
||||
import org.dspace.app.suggestion.Suggestion;
|
||||
import org.dspace.app.suggestion.SuggestionEvidence;
|
||||
import org.dspace.content.Item;
|
||||
import org.dspace.content.dto.MetadataValueDTO;
|
||||
import org.dspace.core.Context;
|
||||
import org.dspace.external.model.ExternalDataObject;
|
||||
import org.dspace.external.provider.ExternalDataProvider;
|
||||
import org.dspace.services.ConfigurationService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
/**
|
||||
* Class responsible to load and manage ImportRecords from OpenAIRE
|
||||
*
|
||||
* @author Pasquale Cavallo (pasquale.cavallo at 4science dot it)
|
||||
*
|
||||
*/
|
||||
public class PublicationLoader extends SolrSuggestionProvider {
|
||||
|
||||
private List<String> names;
|
||||
|
||||
private ExternalDataProvider primaryProvider;
|
||||
|
||||
private List<ExternalDataProvider> otherProviders;
|
||||
|
||||
@Autowired
|
||||
private ConfigurationService configurationService;
|
||||
|
||||
private List<EvidenceScorer> pipeline;
|
||||
|
||||
public void setPrimaryProvider(ExternalDataProvider primaryProvider) {
|
||||
this.primaryProvider = primaryProvider;
|
||||
}
|
||||
|
||||
public void setOtherProviders(List<ExternalDataProvider> otherProviders) {
|
||||
this.otherProviders = otherProviders;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the pipeline of Approver
|
||||
* @param pipeline list Approver
|
||||
*/
|
||||
public void setPipeline(List<EvidenceScorer> pipeline) {
|
||||
this.pipeline = pipeline;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method filter a list of ImportRecords using a pipeline of AuthorNamesApprover
|
||||
* and return a filtered list of ImportRecords.
|
||||
*
|
||||
* @see org.dspace.app.suggestion.openaire.AuthorNamesScorer
|
||||
* @param researcher the researcher Item
|
||||
* @param importRecords List of import record
|
||||
* @return a list of filtered import records
|
||||
*/
|
||||
public List<Suggestion> reduceAndTransform(Item researcher, List<ExternalDataObject> importRecords) {
|
||||
List<Suggestion> results = new ArrayList<>();
|
||||
for (ExternalDataObject r : importRecords) {
|
||||
boolean skip = false;
|
||||
List<SuggestionEvidence> evidences = new ArrayList<SuggestionEvidence>();
|
||||
for (EvidenceScorer authorNameApprover : pipeline) {
|
||||
SuggestionEvidence evidence = authorNameApprover.computeEvidence(researcher, r);
|
||||
if (evidence != null) {
|
||||
evidences.add(evidence);
|
||||
} else {
|
||||
skip = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!skip) {
|
||||
Suggestion suggestion = translateImportRecordToSuggestion(researcher, r);
|
||||
suggestion.getEvidences().addAll(evidences);
|
||||
results.add(suggestion);
|
||||
}
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
/**
|
||||
* Save a List of ImportRecord into Solr.
|
||||
* ImportRecord will be translate into a SolrDocument by the method translateImportRecordToSolrDocument.
|
||||
*
|
||||
* @param context the DSpace Context
|
||||
* @param researcher a DSpace Item
|
||||
* @throws SolrServerException
|
||||
* @throws IOException
|
||||
*/
|
||||
public void importAuthorRecords(Context context, Item researcher)
|
||||
throws SolrServerException, IOException {
|
||||
int offset = 0;
|
||||
int limit = 10;
|
||||
int loaded = limit;
|
||||
List<String> searchValues = searchMetadataValues(researcher);
|
||||
while (loaded > 0) {
|
||||
List<ExternalDataObject> metadata = getImportRecords(searchValues, researcher, offset, limit);
|
||||
if (metadata.isEmpty()) {
|
||||
loaded = 0;
|
||||
continue;
|
||||
}
|
||||
offset += limit;
|
||||
loaded = metadata.size();
|
||||
List<Suggestion> records = reduceAndTransform(researcher, metadata);
|
||||
for (Suggestion record : records) {
|
||||
solrSuggestionStorageService.addSuggestion(record, false, false);
|
||||
}
|
||||
}
|
||||
solrSuggestionStorageService.commit();
|
||||
}
|
||||
|
||||
/**
|
||||
* Translate an ImportRecord into a Suggestion
|
||||
* @param item DSpace item
|
||||
* @param record ImportRecord
|
||||
* @return Suggestion
|
||||
*/
|
||||
private Suggestion translateImportRecordToSuggestion(Item item, ExternalDataObject record) {
|
||||
String openAireId = record.getId();
|
||||
Suggestion suggestion = new Suggestion(getSourceName(), item, openAireId);
|
||||
suggestion.setDisplay(getFirstEntryByMetadatum(record, "dc", "title", null));
|
||||
suggestion.getMetadata().add(
|
||||
new MetadataValueDTO("dc", "title", null, null, getFirstEntryByMetadatum(record, "dc", "title", null)));
|
||||
suggestion.getMetadata().add(new MetadataValueDTO("dc", "date", "issued", null,
|
||||
getFirstEntryByMetadatum(record, "dc", "date", "issued")));
|
||||
suggestion.getMetadata().add(new MetadataValueDTO("dc", "description", "abstract", null,
|
||||
getFirstEntryByMetadatum(record, "dc", "description", "abstract")));
|
||||
suggestion.setExternalSourceUri(configurationService.getProperty("dspace.server.url")
|
||||
+ "/api/integration/externalsources/" + primaryProvider.getSourceIdentifier() + "/entryValues/"
|
||||
+ openAireId);
|
||||
for (String o : getAllEntriesByMetadatum(record, "dc", "source", null)) {
|
||||
suggestion.getMetadata().add(new MetadataValueDTO("dc", "source", null, null, o));
|
||||
}
|
||||
for (String o : getAllEntriesByMetadatum(record, "dc", "contributor", "author")) {
|
||||
suggestion.getMetadata().add(new MetadataValueDTO("dc", "contributor", "author", null, o));
|
||||
}
|
||||
return suggestion;
|
||||
}
|
||||
|
||||
public List<String> getNames() {
|
||||
return names;
|
||||
}
|
||||
|
||||
public void setNames(List<String> names) {
|
||||
this.names = names;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load metadata from OpenAIRE using the import service. The service use the value
|
||||
* get from metadata key defined in class level variable names as author to query OpenAIRE.
|
||||
*
|
||||
* @see org.dspace.importer.external.openaire.service.OpenAireImportMetadataSourceServiceImpl
|
||||
* @param searchValues query
|
||||
* @param researcher item to extract metadata from
|
||||
* @param limit for pagination purpose
|
||||
* @param offset for pagination purpose
|
||||
* @return list of ImportRecord
|
||||
*/
|
||||
private List<ExternalDataObject> getImportRecords(List<String> searchValues,
|
||||
Item researcher, int offset, int limit) {
|
||||
List<ExternalDataObject> matchingRecords = new ArrayList<>();
|
||||
for (String searchValue : searchValues) {
|
||||
matchingRecords.addAll(
|
||||
primaryProvider.searchExternalDataObjects(searchValue, offset, limit));
|
||||
}
|
||||
List<ExternalDataObject> toReturn = removeDuplicates(matchingRecords);
|
||||
return toReturn;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method remove duplicates from importRecords list.
|
||||
* An element is a duplicate if in the list exist another element
|
||||
* with the same value of the metadatum 'dc.identifier.other'
|
||||
*
|
||||
* @param importRecords list of ImportRecord
|
||||
* @return list of ImportRecords without duplicates
|
||||
*/
|
||||
private List<ExternalDataObject> removeDuplicates(List<ExternalDataObject> importRecords) {
|
||||
List<ExternalDataObject> filteredRecords = new ArrayList<>();
|
||||
for (ExternalDataObject currentRecord : importRecords) {
|
||||
if (!isDuplicate(currentRecord, filteredRecords)) {
|
||||
filteredRecords.add(currentRecord);
|
||||
}
|
||||
}
|
||||
return filteredRecords;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Check if the ImportRecord is already present in the list.
|
||||
* The comparison is made on the value of metadatum with key 'dc.identifier.other'
|
||||
*
|
||||
* @param dto An importRecord instance
|
||||
* @param importRecords a list of importRecord
|
||||
* @return true if dto is already present in importRecords, false otherwise
|
||||
*/
|
||||
private boolean isDuplicate(ExternalDataObject dto, List<ExternalDataObject> importRecords) {
|
||||
String currentItemId = dto.getId();
|
||||
if (currentItemId == null) {
|
||||
return true;
|
||||
}
|
||||
for (ExternalDataObject importRecord : importRecords) {
|
||||
if (currentItemId.equals(importRecord.getId())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return list of Item metadata values starting from metadata keys defined in class level variable names.
|
||||
*
|
||||
* @param researcher DSpace item
|
||||
* @return list of metadata values
|
||||
*/
|
||||
private List<String> searchMetadataValues(Item researcher) {
|
||||
List<String> authors = new ArrayList<String>();
|
||||
for (String name : names) {
|
||||
String value = itemService.getMetadata(researcher, name);
|
||||
if (value != null) {
|
||||
authors.add(value);
|
||||
}
|
||||
}
|
||||
return authors;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isExternalDataObjectPotentiallySuggested(Context context, ExternalDataObject externalDataObject) {
|
||||
if (StringUtils.equals(externalDataObject.getSource(), primaryProvider.getSourceIdentifier())) {
|
||||
return true;
|
||||
} else if (otherProviders != null) {
|
||||
return otherProviders.stream()
|
||||
.anyMatch(x -> StringUtils.equals(externalDataObject.getSource(), x.getSourceIdentifier()));
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,29 @@
|
||||
/**
|
||||
* The contents of this file are subject to the license and copyright
|
||||
* detailed in the LICENSE and NOTICE files at the root of the source
|
||||
* tree and available online at
|
||||
*
|
||||
* http://www.dspace.org/license/
|
||||
*/
|
||||
package org.dspace.app.suggestion.openaire;
|
||||
|
||||
import org.apache.commons.cli.Options;
|
||||
|
||||
/**
|
||||
* Extension of {@link PublicationLoaderScriptConfiguration} for CLI.
|
||||
*
|
||||
* @author Alessandro Martelli (alessandro.martelli at 4science.it)
|
||||
*/
|
||||
public class PublicationLoaderCliScriptConfiguration<T extends PublicationLoaderRunnable>
|
||||
extends PublicationLoaderScriptConfiguration<T> {
|
||||
|
||||
@Override
|
||||
public Options getOptions() {
|
||||
Options options = super.getOptions();
|
||||
options.addOption("h", "help", false, "help");
|
||||
options.getOption("h").setType(boolean.class);
|
||||
super.options = options;
|
||||
return options;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,115 @@
|
||||
/**
|
||||
* 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.suggestion.openaire;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.commons.cli.ParseException;
|
||||
import org.dspace.content.Item;
|
||||
import org.dspace.core.Context;
|
||||
import org.dspace.discovery.DiscoverQuery;
|
||||
import org.dspace.discovery.SearchService;
|
||||
import org.dspace.discovery.SearchServiceException;
|
||||
import org.dspace.discovery.SearchUtils;
|
||||
import org.dspace.discovery.utils.DiscoverQueryBuilder;
|
||||
import org.dspace.discovery.utils.parameter.QueryBuilderSearchFilter;
|
||||
import org.dspace.scripts.DSpaceRunnable;
|
||||
import org.dspace.sort.SortOption;
|
||||
import org.dspace.utils.DSpace;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Runner responsible to import metadata about authors from OpenAIRE to Solr.
|
||||
* This runner works in two ways:
|
||||
* If -s parameter with a valid UUID is received, then the specific researcher
|
||||
* with this UUID will be used.
|
||||
* Invocation without any parameter results in massive import, processing all
|
||||
* authors registered in DSpace.
|
||||
*
|
||||
* @author Alessandro Martelli (alessandro.martelli at 4science.it)
|
||||
*/
|
||||
public class PublicationLoaderRunnable
|
||||
extends DSpaceRunnable<PublicationLoaderScriptConfiguration<PublicationLoaderRunnable>> {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(PublicationLoaderRunnable.class);
|
||||
|
||||
private PublicationLoader oairePublicationLoader = null;
|
||||
|
||||
protected Context context;
|
||||
|
||||
protected String profile;
|
||||
|
||||
@Override
|
||||
@SuppressWarnings({ "rawtypes", "unchecked" })
|
||||
public PublicationLoaderScriptConfiguration<PublicationLoaderRunnable> getScriptConfiguration() {
|
||||
PublicationLoaderScriptConfiguration configuration = new DSpace().getServiceManager()
|
||||
.getServiceByName("import-openaire-suggestions", PublicationLoaderScriptConfiguration.class);
|
||||
return configuration;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setup() throws ParseException {
|
||||
|
||||
oairePublicationLoader = new DSpace().getServiceManager().getServiceByName(
|
||||
"OpenairePublicationLoader", PublicationLoader.class);
|
||||
|
||||
profile = commandLine.getOptionValue("s");
|
||||
if (profile == null) {
|
||||
LOGGER.info("No argument for -s, process all profile");
|
||||
} else {
|
||||
LOGGER.info("Process eperson item with UUID " + profile);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void internalRun() throws Exception {
|
||||
|
||||
context = new Context();
|
||||
|
||||
Iterator<Item> researchers = getResearchers(profile);
|
||||
while (researchers.hasNext()) {
|
||||
Item researcher = researchers.next();
|
||||
oairePublicationLoader.importAuthorRecords(context, researcher);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Item(s) which map a researcher from Solr. If the uuid is specified,
|
||||
* the researcher with this UUID will be chosen. If the uuid doesn't match any
|
||||
* researcher, the method returns an empty array list. If uuid is null, all
|
||||
* research will be return.
|
||||
*
|
||||
* @param profileUUID uuid of the researcher. If null, all researcher will be
|
||||
* returned.
|
||||
* @return the researcher with specified UUID or all researchers
|
||||
*/
|
||||
@SuppressWarnings("rawtypes")
|
||||
private Iterator<Item> getResearchers(String profileUUID) {
|
||||
SearchService searchService = new DSpace().getSingletonService(SearchService.class);
|
||||
DiscoverQueryBuilder queryBuilder = SearchUtils.getQueryBuilder();
|
||||
List<QueryBuilderSearchFilter> filters = new ArrayList<QueryBuilderSearchFilter>();
|
||||
String query = "*:*";
|
||||
if (profileUUID != null) {
|
||||
query = "search.resourceid:" + profileUUID.toString();
|
||||
}
|
||||
try {
|
||||
DiscoverQuery discoverQuery = queryBuilder.buildQuery(context, null,
|
||||
SearchUtils.getDiscoveryConfigurationByName("person"),
|
||||
query, filters,
|
||||
"Item", 10, Long.getLong("0"), null, SortOption.DESCENDING);
|
||||
return searchService.iteratorSearch(context, null, discoverQuery);
|
||||
} catch (SearchServiceException e) {
|
||||
LOGGER.error("Unable to read researcher on solr", e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
@@ -0,0 +1,36 @@
|
||||
/**
|
||||
* 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.suggestion.openaire;
|
||||
|
||||
import org.apache.commons.cli.HelpFormatter;
|
||||
import org.apache.commons.cli.ParseException;
|
||||
import org.dspace.utils.DSpace;
|
||||
|
||||
public class PublicationLoaderRunnableCli extends PublicationLoaderRunnable {
|
||||
|
||||
@Override
|
||||
@SuppressWarnings({ "rawtypes", "unchecked" })
|
||||
public PublicationLoaderCliScriptConfiguration getScriptConfiguration() {
|
||||
PublicationLoaderCliScriptConfiguration configuration = new DSpace().getServiceManager()
|
||||
.getServiceByName("import-openaire-suggestions", PublicationLoaderCliScriptConfiguration.class);
|
||||
return configuration;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setup() throws ParseException {
|
||||
super.setup();
|
||||
|
||||
// in case of CLI we show the help prompt
|
||||
if (commandLine.hasOption('h')) {
|
||||
HelpFormatter formatter = new HelpFormatter();
|
||||
formatter.printHelp("Import Researchers Suggestions", getScriptConfiguration().getOptions());
|
||||
System.exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,56 @@
|
||||
/**
|
||||
* 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.suggestion.openaire;
|
||||
|
||||
import org.apache.commons.cli.Options;
|
||||
import org.dspace.scripts.configuration.ScriptConfiguration;
|
||||
|
||||
public class PublicationLoaderScriptConfiguration<T extends PublicationLoaderRunnable>
|
||||
extends ScriptConfiguration<T> {
|
||||
|
||||
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 PublicationLoaderScriptConfiguration
|
||||
*/
|
||||
@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("s", "single-researcher", true, "Single researcher UUID");
|
||||
options.getOption("s").setType(String.class);
|
||||
|
||||
super.options = options;
|
||||
}
|
||||
return options;
|
||||
}
|
||||
|
||||
}
|
@@ -628,12 +628,23 @@ public class AuthorizeUtil {
|
||||
// actually expected to be returning true.
|
||||
// For example the LDAP canSelfRegister will return true due to auto-register, while that
|
||||
// does not imply a new user can register explicitly
|
||||
return AuthenticateServiceFactory.getInstance().getAuthenticationService()
|
||||
.allowSetPassword(context, request, null);
|
||||
return authorizePasswordChange(context, request);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method will return a boolean indicating whether the current user is allowed to reset the password
|
||||
* or not
|
||||
*
|
||||
* @return A boolean indicating whether the current user can reset its password or not
|
||||
* @throws SQLException If something goes wrong
|
||||
*/
|
||||
public static boolean authorizeForgotPassword() {
|
||||
return DSpaceServicesFactory.getInstance().getConfigurationService()
|
||||
.getBooleanProperty("user.forgot-password", true);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method will return a boolean indicating whether it's allowed to update the password for the EPerson
|
||||
* with the given email and canLogin property
|
||||
@@ -647,8 +658,7 @@ public class AuthorizeUtil {
|
||||
if (eperson != null && eperson.canLogIn()) {
|
||||
HttpServletRequest request = new DSpace().getRequestService().getCurrentRequest()
|
||||
.getHttpServletRequest();
|
||||
return AuthenticateServiceFactory.getInstance().getAuthenticationService()
|
||||
.allowSetPassword(context, request, null);
|
||||
return authorizePasswordChange(context, request);
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
log.error("Something went wrong trying to retrieve EPerson for email: " + email, e);
|
||||
@@ -656,6 +666,19 @@ public class AuthorizeUtil {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the current configuration has at least one password based authentication method
|
||||
*
|
||||
* @param context Dspace Context
|
||||
* @param request Current Request
|
||||
* @return True if the password change is enabled
|
||||
* @throws SQLException
|
||||
*/
|
||||
protected static boolean authorizePasswordChange(Context context, HttpServletRequest request) throws SQLException {
|
||||
return AuthenticateServiceFactory.getInstance().getAuthenticationService()
|
||||
.allowSetPassword(context, request, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method checks if the community Admin can manage accounts
|
||||
*
|
||||
|
@@ -14,7 +14,6 @@ import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.xml.parsers.DocumentBuilder;
|
||||
import javax.xml.parsers.DocumentBuilderFactory;
|
||||
import javax.xml.parsers.FactoryConfigurationError;
|
||||
@@ -150,17 +149,16 @@ public class DCInputsReader {
|
||||
* Returns the set of DC inputs used for a particular collection, or the
|
||||
* default set if no inputs defined for the collection
|
||||
*
|
||||
* @param collectionHandle collection's unique Handle
|
||||
* @param collection collection for which search the set of DC inputs
|
||||
* @return DC input set
|
||||
* @throws DCInputsReaderException if no default set defined
|
||||
* @throws ServletException
|
||||
*/
|
||||
public List<DCInputSet> getInputsByCollectionHandle(String collectionHandle)
|
||||
public List<DCInputSet> getInputsByCollection(Collection collection)
|
||||
throws DCInputsReaderException {
|
||||
SubmissionConfig config;
|
||||
try {
|
||||
config = SubmissionServiceFactory.getInstance().getSubmissionConfigService()
|
||||
.getSubmissionConfigByCollection(collectionHandle);
|
||||
.getSubmissionConfigByCollection(collection);
|
||||
String formName = config.getSubmissionName();
|
||||
if (formName == null) {
|
||||
throw new DCInputsReaderException("No form designated as default");
|
||||
@@ -691,7 +689,7 @@ public class DCInputsReader {
|
||||
|
||||
public String getInputFormNameByCollectionAndField(Collection collection, String field)
|
||||
throws DCInputsReaderException {
|
||||
List<DCInputSet> inputSets = getInputsByCollectionHandle(collection.getHandle());
|
||||
List<DCInputSet> inputSets = getInputsByCollection(collection);
|
||||
for (DCInputSet inputSet : inputSets) {
|
||||
String[] tokenized = Utils.tokenize(field);
|
||||
String schema = tokenized[0];
|
||||
|
@@ -11,6 +11,7 @@ import java.io.File;
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@@ -21,6 +22,7 @@ import javax.xml.parsers.FactoryConfigurationError;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.dspace.content.Collection;
|
||||
import org.dspace.content.Community;
|
||||
import org.dspace.content.DSpaceObject;
|
||||
import org.dspace.content.factory.ContentServiceFactory;
|
||||
import org.dspace.content.service.CollectionService;
|
||||
@@ -90,6 +92,13 @@ public class SubmissionConfigReader {
|
||||
*/
|
||||
private Map<String, String> collectionToSubmissionConfig = null;
|
||||
|
||||
/**
|
||||
* Hashmap which stores which submission process configuration is used by
|
||||
* which community, computed from the item submission config file
|
||||
* (specifically, the 'submission-map' tag)
|
||||
*/
|
||||
private Map<String, String> communityToSubmissionConfig = null;
|
||||
|
||||
/**
|
||||
* Reference to the global submission step definitions defined in the
|
||||
* "step-definitions" section
|
||||
@@ -127,6 +136,7 @@ public class SubmissionConfigReader {
|
||||
|
||||
public void reload() throws SubmissionConfigReaderException {
|
||||
collectionToSubmissionConfig = null;
|
||||
communityToSubmissionConfig = null;
|
||||
stepDefns = null;
|
||||
submitDefns = null;
|
||||
buildInputs(configDir + SUBMIT_DEF_FILE_PREFIX + SUBMIT_DEF_FILE_SUFFIX);
|
||||
@@ -145,7 +155,8 @@ public class SubmissionConfigReader {
|
||||
*/
|
||||
private void buildInputs(String fileName) throws SubmissionConfigReaderException {
|
||||
collectionToSubmissionConfig = new HashMap<String, String>();
|
||||
submitDefns = new HashMap<String, List<Map<String, String>>>();
|
||||
communityToSubmissionConfig = new HashMap<String, String>();
|
||||
submitDefns = new LinkedHashMap<String, List<Map<String, String>>>();
|
||||
|
||||
String uri = "file:" + new File(fileName).getAbsolutePath();
|
||||
|
||||
@@ -210,18 +221,41 @@ public class SubmissionConfigReader {
|
||||
* Returns the Item Submission process config used for a particular
|
||||
* collection, or the default if none is defined for the collection
|
||||
*
|
||||
* @param collectionHandle collection's unique Handle
|
||||
* @param col collection for which search Submission process config
|
||||
* @return the SubmissionConfig representing the item submission config
|
||||
* @throws SubmissionConfigReaderException if no default submission process configuration defined
|
||||
* @throws IllegalStateException if no default submission process configuration defined
|
||||
*/
|
||||
public SubmissionConfig getSubmissionConfigByCollection(String collectionHandle) {
|
||||
// get the name of the submission process config for this collection
|
||||
String submitName = collectionToSubmissionConfig
|
||||
.get(collectionHandle);
|
||||
if (submitName == null) {
|
||||
public SubmissionConfig getSubmissionConfigByCollection(Collection col) {
|
||||
|
||||
String submitName;
|
||||
|
||||
if (col != null) {
|
||||
|
||||
// get the name of the submission process config for this collection
|
||||
submitName = collectionToSubmissionConfig
|
||||
.get(DEFAULT_COLLECTION);
|
||||
.get(col.getHandle());
|
||||
if (submitName != null) {
|
||||
return getSubmissionConfigByName(submitName);
|
||||
}
|
||||
|
||||
if (!communityToSubmissionConfig.isEmpty()) {
|
||||
try {
|
||||
List<Community> communities = col.getCommunities();
|
||||
for (Community com : communities) {
|
||||
submitName = getSubmissionConfigByCommunity(com);
|
||||
if (submitName != null) {
|
||||
return getSubmissionConfigByName(submitName);
|
||||
}
|
||||
}
|
||||
} catch (SQLException sqle) {
|
||||
throw new IllegalStateException("Error occurred while getting item submission configured " +
|
||||
"by community", sqle);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
submitName = collectionToSubmissionConfig.get(DEFAULT_COLLECTION);
|
||||
|
||||
if (submitName == null) {
|
||||
throw new IllegalStateException(
|
||||
"No item submission process configuration designated as 'default' in 'submission-map' section of " +
|
||||
@@ -230,6 +264,30 @@ public class SubmissionConfigReader {
|
||||
return getSubmissionConfigByName(submitName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursive function to return the Item Submission process config
|
||||
* used for a community or the closest community parent, or null
|
||||
* if none is defined
|
||||
*
|
||||
* @param com community for which search Submission process config
|
||||
* @return the SubmissionConfig representing the item submission config
|
||||
*/
|
||||
private String getSubmissionConfigByCommunity(Community com) {
|
||||
String submitName = communityToSubmissionConfig
|
||||
.get(com.getHandle());
|
||||
if (submitName != null) {
|
||||
return submitName;
|
||||
}
|
||||
List<Community> communities = com.getParentCommunities();
|
||||
for (Community parentCom : communities) {
|
||||
submitName = getSubmissionConfigByCommunity(parentCom);
|
||||
if (submitName != null) {
|
||||
return submitName;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Item Submission process config
|
||||
*
|
||||
@@ -357,13 +415,14 @@ public class SubmissionConfigReader {
|
||||
Node nd = nl.item(i);
|
||||
if (nd.getNodeName().equals("name-map")) {
|
||||
String id = getAttribute(nd, "collection-handle");
|
||||
String communityId = getAttribute(nd, "community-handle");
|
||||
String entityType = getAttribute(nd, "collection-entity-type");
|
||||
String value = getAttribute(nd, "submission-name");
|
||||
String content = getValue(nd);
|
||||
if (id == null && entityType == null) {
|
||||
if (id == null && communityId == null && entityType == null) {
|
||||
throw new SAXException(
|
||||
"name-map element is missing collection-handle or collection-entity-type attribute " +
|
||||
"in 'item-submission.xml'");
|
||||
"name-map element is missing collection-handle or community-handle or collection-entity-type " +
|
||||
"attribute in 'item-submission.xml'");
|
||||
}
|
||||
if (value == null) {
|
||||
throw new SAXException(
|
||||
@@ -375,7 +434,8 @@ public class SubmissionConfigReader {
|
||||
}
|
||||
if (id != null) {
|
||||
collectionToSubmissionConfig.put(id, value);
|
||||
|
||||
} else if (communityId != null) {
|
||||
communityToSubmissionConfig.put(communityId, value);
|
||||
} else {
|
||||
// get all collections for this entity-type
|
||||
List<Collection> collections = collectionService.findAllCollectionsByEntityType( context,
|
||||
|
@@ -405,21 +405,13 @@ public class Util {
|
||||
DCInput myInputs = null;
|
||||
boolean myInputsFound = false;
|
||||
String formFileName = I18nUtil.getInputFormsFileName(locale);
|
||||
String col_handle = "";
|
||||
|
||||
Collection collection = item.getOwningCollection();
|
||||
|
||||
if (collection == null) {
|
||||
// set an empty handle so to get the default input set
|
||||
col_handle = "";
|
||||
} else {
|
||||
col_handle = collection.getHandle();
|
||||
}
|
||||
|
||||
// Read the input form file for the specific collection
|
||||
DCInputsReader inputsReader = new DCInputsReader(formFileName);
|
||||
|
||||
List<DCInputSet> inputSets = inputsReader.getInputsByCollectionHandle(col_handle);
|
||||
List<DCInputSet> inputSets = inputsReader.getInputsByCollection(collection);
|
||||
|
||||
// Replace the values of Metadatum[] with the correct ones in case
|
||||
// of
|
||||
@@ -500,8 +492,8 @@ public class Util {
|
||||
public static List<String> differenceInSubmissionFields(Collection fromCollection, Collection toCollection)
|
||||
throws DCInputsReaderException {
|
||||
DCInputsReader reader = new DCInputsReader();
|
||||
List<DCInputSet> from = reader.getInputsByCollectionHandle(fromCollection.getHandle());
|
||||
List<DCInputSet> to = reader.getInputsByCollectionHandle(toCollection.getHandle());
|
||||
List<DCInputSet> from = reader.getInputsByCollection(fromCollection);
|
||||
List<DCInputSet> to = reader.getInputsByCollection(toCollection);
|
||||
|
||||
Set<String> fromFieldName = new HashSet<>();
|
||||
Set<String> toFieldName = new HashSet<>();
|
||||
|
@@ -150,7 +150,6 @@ public class InstallItemServiceImpl implements InstallItemService {
|
||||
return finishItem(c, item, is);
|
||||
}
|
||||
|
||||
|
||||
protected void populateMetadata(Context c, Item item)
|
||||
throws SQLException, AuthorizeException {
|
||||
// create accession date
|
||||
@@ -158,15 +157,6 @@ public class InstallItemServiceImpl implements InstallItemService {
|
||||
itemService.addMetadata(c, item, MetadataSchemaEnum.DC.getName(),
|
||||
"date", "accessioned", null, now.toString());
|
||||
|
||||
// add date available if not under embargo, otherwise it will
|
||||
// be set when the embargo is lifted.
|
||||
// this will flush out fatal embargo metadata
|
||||
// problems before we set inArchive.
|
||||
if (embargoService.getEmbargoTermsAsDate(c, item) == null) {
|
||||
itemService.addMetadata(c, item, MetadataSchemaEnum.DC.getName(),
|
||||
"date", "available", null, now.toString());
|
||||
}
|
||||
|
||||
// If issue date is set as "today" (literal string), then set it to current date
|
||||
// In the below loop, we temporarily clear all issued dates and re-add, one-by-one,
|
||||
// replacing "today" with today's date.
|
||||
|
@@ -1435,16 +1435,6 @@ prevent the generation of resource policy entry values with null dspace_object a
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<Item> findByMetadataQuery(Context context, List<List<MetadataField>> listFieldList,
|
||||
List<String> query_op, List<String> query_val, List<UUID> collectionUuids,
|
||||
String regexClause, int offset, int limit)
|
||||
throws SQLException, AuthorizeException, IOException {
|
||||
return itemDAO
|
||||
.findByMetadataQuery(context, listFieldList, query_op, query_val, collectionUuids, regexClause, offset,
|
||||
limit);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DSpaceObject getAdminObject(Context context, Item item, int action) throws SQLException {
|
||||
DSpaceObject adminObject = null;
|
||||
|
@@ -242,7 +242,7 @@ public final class ChoiceAuthorityServiceImpl implements ChoiceAuthorityService
|
||||
// check if it is the requested collection
|
||||
Map<String, ChoiceAuthority> controllerFormDef = controllerFormDefinitions.get(fieldKey);
|
||||
SubmissionConfig submissionConfig = submissionConfigService
|
||||
.getSubmissionConfigByCollection(collection.getHandle());
|
||||
.getSubmissionConfigByCollection(collection);
|
||||
String submissionName = submissionConfig.getSubmissionName();
|
||||
// check if the requested collection has a submission definition that use an authority for the metadata
|
||||
if (controllerFormDef.containsKey(submissionName)) {
|
||||
@@ -495,7 +495,7 @@ public final class ChoiceAuthorityServiceImpl implements ChoiceAuthorityService
|
||||
try {
|
||||
configReaderService = SubmissionServiceFactory.getInstance().getSubmissionConfigService();
|
||||
SubmissionConfig submissionName = configReaderService
|
||||
.getSubmissionConfigByCollection(collection.getHandle());
|
||||
.getSubmissionConfigByCollection(collection);
|
||||
ma = controllerFormDefinitions.get(fieldKey).get(submissionName.getSubmissionName());
|
||||
} catch (SubmissionConfigReaderException e) {
|
||||
// the system is in an illegal state as the submission definition is not valid
|
||||
|
@@ -156,7 +156,8 @@ public class DCInputAuthority extends SelfNamedPlugin implements ChoiceAuthority
|
||||
int found = 0;
|
||||
List<Choice> v = new ArrayList<Choice>();
|
||||
for (int i = 0; i < valuesLocale.length; ++i) {
|
||||
if (query == null || StringUtils.containsIgnoreCase(valuesLocale[i], query)) {
|
||||
// In a DCInputAuthority context, a user will want to query the labels, not the values
|
||||
if (query == null || StringUtils.containsIgnoreCase(labelsLocale[i], query)) {
|
||||
if (found >= start && v.size() < limit) {
|
||||
v.add(new Choice(null, valuesLocale[i], labelsLocale[i]));
|
||||
if (valuesLocale[i].equalsIgnoreCase(query)) {
|
||||
|
@@ -11,7 +11,6 @@ import java.sql.SQLException;
|
||||
import java.util.Date;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
import org.dspace.content.Collection;
|
||||
import org.dspace.content.Community;
|
||||
@@ -80,10 +79,6 @@ public interface ItemDAO extends DSpaceObjectLegacySupportDAO<Item> {
|
||||
public Iterator<Item> findByMetadataField(Context context, MetadataField metadataField, String value,
|
||||
boolean inArchive) throws SQLException;
|
||||
|
||||
public Iterator<Item> findByMetadataQuery(Context context, List<List<MetadataField>> listFieldList,
|
||||
List<String> query_op, List<String> query_val, List<UUID> collectionUuids,
|
||||
String regexClause, int offset, int limit) throws SQLException;
|
||||
|
||||
public Iterator<Item> findByAuthorityValue(Context context, MetadataField metadataField, String authority,
|
||||
boolean inArchive) throws SQLException;
|
||||
|
||||
|
@@ -12,7 +12,6 @@ import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import javax.persistence.Query;
|
||||
import javax.persistence.TemporalType;
|
||||
import javax.persistence.criteria.CriteriaBuilder;
|
||||
@@ -24,21 +23,11 @@ import org.dspace.content.Collection;
|
||||
import org.dspace.content.Item;
|
||||
import org.dspace.content.Item_;
|
||||
import org.dspace.content.MetadataField;
|
||||
import org.dspace.content.MetadataValue;
|
||||
import org.dspace.content.dao.ItemDAO;
|
||||
import org.dspace.core.AbstractHibernateDSODAO;
|
||||
import org.dspace.core.Context;
|
||||
import org.dspace.core.UUIDIterator;
|
||||
import org.dspace.eperson.EPerson;
|
||||
import org.hibernate.Criteria;
|
||||
import org.hibernate.criterion.Criterion;
|
||||
import org.hibernate.criterion.DetachedCriteria;
|
||||
import org.hibernate.criterion.Order;
|
||||
import org.hibernate.criterion.Projections;
|
||||
import org.hibernate.criterion.Property;
|
||||
import org.hibernate.criterion.Restrictions;
|
||||
import org.hibernate.criterion.Subqueries;
|
||||
import org.hibernate.type.StandardBasicTypes;
|
||||
|
||||
/**
|
||||
* Hibernate implementation of the Database Access Object interface class for the Item object.
|
||||
@@ -193,120 +182,6 @@ public class ItemDAOImpl extends AbstractHibernateDSODAO<Item> implements ItemDA
|
||||
return new UUIDIterator<Item>(context, uuids, Item.class, this);
|
||||
}
|
||||
|
||||
enum OP {
|
||||
equals {
|
||||
public Criterion buildPredicate(String val, String regexClause) {
|
||||
return Property.forName("mv.value").eq(val);
|
||||
}
|
||||
},
|
||||
not_equals {
|
||||
public Criterion buildPredicate(String val, String regexClause) {
|
||||
return OP.equals.buildPredicate(val, regexClause);
|
||||
}
|
||||
},
|
||||
like {
|
||||
public Criterion buildPredicate(String val, String regexClause) {
|
||||
return Property.forName("mv.value").like(val);
|
||||
}
|
||||
},
|
||||
not_like {
|
||||
public Criterion buildPredicate(String val, String regexClause) {
|
||||
return OP.like.buildPredicate(val, regexClause);
|
||||
}
|
||||
},
|
||||
contains {
|
||||
public Criterion buildPredicate(String val, String regexClause) {
|
||||
return Property.forName("mv.value").like("%" + val + "%");
|
||||
}
|
||||
},
|
||||
doesnt_contain {
|
||||
public Criterion buildPredicate(String val, String regexClause) {
|
||||
return OP.contains.buildPredicate(val, regexClause);
|
||||
}
|
||||
},
|
||||
exists {
|
||||
public Criterion buildPredicate(String val, String regexClause) {
|
||||
return Property.forName("mv.value").isNotNull();
|
||||
}
|
||||
},
|
||||
doesnt_exist {
|
||||
public Criterion buildPredicate(String val, String regexClause) {
|
||||
return OP.exists.buildPredicate(val, regexClause);
|
||||
}
|
||||
},
|
||||
matches {
|
||||
public Criterion buildPredicate(String val, String regexClause) {
|
||||
return Restrictions.sqlRestriction(regexClause, val, StandardBasicTypes.STRING);
|
||||
}
|
||||
},
|
||||
doesnt_match {
|
||||
public Criterion buildPredicate(String val, String regexClause) {
|
||||
return OP.matches.buildPredicate(val, regexClause);
|
||||
}
|
||||
|
||||
};
|
||||
public abstract Criterion buildPredicate(String val, String regexClause);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Deprecated
|
||||
public Iterator<Item> findByMetadataQuery(Context context, List<List<MetadataField>> listFieldList,
|
||||
List<String> query_op, List<String> query_val, List<UUID> collectionUuids,
|
||||
String regexClause, int offset, int limit) throws SQLException {
|
||||
|
||||
Criteria criteria = getHibernateSession(context).createCriteria(Item.class, "item");
|
||||
criteria.setFirstResult(offset);
|
||||
criteria.setMaxResults(limit);
|
||||
|
||||
if (!collectionUuids.isEmpty()) {
|
||||
DetachedCriteria dcollCriteria = DetachedCriteria.forClass(Collection.class, "coll");
|
||||
dcollCriteria.setProjection(Projections.property("coll.id"));
|
||||
dcollCriteria.add(Restrictions.eqProperty("coll.id", "item.owningCollection"));
|
||||
dcollCriteria.add(Restrictions.in("coll.id", collectionUuids));
|
||||
criteria.add(Subqueries.exists(dcollCriteria));
|
||||
}
|
||||
|
||||
int index = Math.min(listFieldList.size(), Math.min(query_op.size(), query_val.size()));
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
for (int i = 0; i < index; i++) {
|
||||
OP op = OP.valueOf(query_op.get(i));
|
||||
if (op == null) {
|
||||
log.warn("Skipping Invalid Operator: " + query_op.get(i));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (op == OP.matches || op == OP.doesnt_match) {
|
||||
if (regexClause.isEmpty()) {
|
||||
log.warn("Skipping Unsupported Regex Operator: " + query_op.get(i));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
DetachedCriteria subcriteria = DetachedCriteria.forClass(MetadataValue.class, "mv");
|
||||
subcriteria.add(Property.forName("mv.dSpaceObject").eqProperty("item.id"));
|
||||
subcriteria.setProjection(Projections.property("mv.dSpaceObject"));
|
||||
|
||||
if (!listFieldList.get(i).isEmpty()) {
|
||||
subcriteria.add(Restrictions.in("metadataField", listFieldList.get(i)));
|
||||
}
|
||||
|
||||
subcriteria.add(op.buildPredicate(query_val.get(i), regexClause));
|
||||
|
||||
if (op == OP.exists || op == OP.equals || op == OP.like || op == OP.contains || op == OP.matches) {
|
||||
criteria.add(Subqueries.exists(subcriteria));
|
||||
} else {
|
||||
criteria.add(Subqueries.notExists(subcriteria));
|
||||
}
|
||||
}
|
||||
criteria.addOrder(Order.asc("item.id"));
|
||||
|
||||
log.debug(String.format("Running custom query with %d filters", index));
|
||||
|
||||
return ((List<Item>) criteria.list()).iterator();
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<Item> findByAuthorityValue(Context context, MetadataField metadataField, String authority,
|
||||
boolean inArchive) throws SQLException {
|
||||
|
@@ -23,7 +23,6 @@ import org.dspace.content.Collection;
|
||||
import org.dspace.content.Community;
|
||||
import org.dspace.content.EntityType;
|
||||
import org.dspace.content.Item;
|
||||
import org.dspace.content.MetadataField;
|
||||
import org.dspace.content.MetadataValue;
|
||||
import org.dspace.content.Thumbnail;
|
||||
import org.dspace.content.WorkspaceItem;
|
||||
@@ -749,11 +748,6 @@ public interface ItemService
|
||||
String schema, String element, String qualifier, String value)
|
||||
throws SQLException, AuthorizeException, IOException;
|
||||
|
||||
public Iterator<Item> findByMetadataQuery(Context context, List<List<MetadataField>> listFieldList,
|
||||
List<String> query_op, List<String> query_val, List<UUID> collectionUuids,
|
||||
String regexClause, int offset, int limit)
|
||||
throws SQLException, AuthorizeException, IOException;
|
||||
|
||||
/**
|
||||
* Find all the items in the archive with a given authority key value
|
||||
* in the indicated metadata field.
|
||||
|
@@ -17,6 +17,7 @@ import org.dspace.app.util.DCInput;
|
||||
import org.dspace.app.util.DCInputSet;
|
||||
import org.dspace.app.util.DCInputsReader;
|
||||
import org.dspace.app.util.DCInputsReaderException;
|
||||
import org.dspace.content.Collection;
|
||||
import org.dspace.content.DSpaceObject;
|
||||
import org.dspace.content.Item;
|
||||
import org.dspace.content.MetadataValue;
|
||||
@@ -69,7 +70,7 @@ public class RequiredMetadata extends AbstractCurationTask {
|
||||
handle = "in workflow";
|
||||
}
|
||||
sb.append("Item: ").append(handle);
|
||||
for (String req : getReqList(item.getOwningCollection().getHandle())) {
|
||||
for (String req : getReqList(item.getOwningCollection())) {
|
||||
List<MetadataValue> vals = itemService.getMetadataByMetadataString(item, req);
|
||||
if (vals.size() == 0) {
|
||||
sb.append(" missing required field: ").append(req);
|
||||
@@ -91,14 +92,14 @@ public class RequiredMetadata extends AbstractCurationTask {
|
||||
}
|
||||
}
|
||||
|
||||
protected List<String> getReqList(String handle) throws DCInputsReaderException {
|
||||
List<String> reqList = reqMap.get(handle);
|
||||
protected List<String> getReqList(Collection collection) throws DCInputsReaderException {
|
||||
List<String> reqList = reqMap.get(collection.getHandle());
|
||||
if (reqList == null) {
|
||||
reqList = reqMap.get("default");
|
||||
}
|
||||
if (reqList == null) {
|
||||
reqList = new ArrayList<String>();
|
||||
List<DCInputSet> inputSet = reader.getInputsByCollectionHandle(handle);
|
||||
List<DCInputSet> inputSet = reader.getInputsByCollection(collection);
|
||||
for (DCInputSet inputs : inputSet) {
|
||||
for (DCInput[] row : inputs.getFields()) {
|
||||
for (DCInput input : row) {
|
||||
|
@@ -7,14 +7,20 @@
|
||||
*/
|
||||
package org.dspace.discovery;
|
||||
|
||||
import static org.dspace.discovery.IndexClientOptions.TYPE_OPTION;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.sql.SQLException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.apache.commons.cli.CommandLine;
|
||||
import org.apache.commons.cli.ParseException;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.dspace.content.Collection;
|
||||
import org.dspace.content.Community;
|
||||
import org.dspace.content.DSpaceObject;
|
||||
@@ -51,6 +57,17 @@ public class IndexClient extends DSpaceRunnable<IndexDiscoveryScriptConfiguratio
|
||||
return;
|
||||
}
|
||||
|
||||
String type = null;
|
||||
if (commandLine.hasOption(TYPE_OPTION)) {
|
||||
List<String> indexableObjectTypes = IndexObjectFactoryFactory.getInstance().getIndexFactories().stream()
|
||||
.map((indexFactory -> indexFactory.getType())).collect(Collectors.toList());
|
||||
type = commandLine.getOptionValue(TYPE_OPTION);
|
||||
if (!indexableObjectTypes.contains(type)) {
|
||||
handler.handleException(String.format("%s is not a valid indexable object type, options: %s",
|
||||
type, Arrays.toString(indexableObjectTypes.toArray())));
|
||||
}
|
||||
}
|
||||
|
||||
/** Acquire from dspace-services in future */
|
||||
/**
|
||||
* new DSpace.getServiceManager().getServiceByName("org.dspace.discovery.SolrIndexer");
|
||||
@@ -113,6 +130,10 @@ public class IndexClient extends DSpaceRunnable<IndexDiscoveryScriptConfiguratio
|
||||
} else if (indexClientOptions == IndexClientOptions.BUILD ||
|
||||
indexClientOptions == IndexClientOptions.BUILDANDSPELLCHECK) {
|
||||
handler.logInfo("(Re)building index from scratch.");
|
||||
if (StringUtils.isNotBlank(type)) {
|
||||
handler.logWarning(String.format("Type option, %s, not applicable for entire index rebuild option, b" +
|
||||
", type will be ignored", TYPE_OPTION));
|
||||
}
|
||||
indexer.deleteIndex();
|
||||
indexer.createIndex(context);
|
||||
if (indexClientOptions == IndexClientOptions.BUILDANDSPELLCHECK) {
|
||||
@@ -133,14 +154,14 @@ public class IndexClient extends DSpaceRunnable<IndexDiscoveryScriptConfiguratio
|
||||
} else if (indexClientOptions == IndexClientOptions.UPDATE ||
|
||||
indexClientOptions == IndexClientOptions.UPDATEANDSPELLCHECK) {
|
||||
handler.logInfo("Updating Index");
|
||||
indexer.updateIndex(context, false);
|
||||
indexer.updateIndex(context, false, type);
|
||||
if (indexClientOptions == IndexClientOptions.UPDATEANDSPELLCHECK) {
|
||||
checkRebuildSpellCheck(commandLine, indexer);
|
||||
}
|
||||
} else if (indexClientOptions == IndexClientOptions.FORCEUPDATE ||
|
||||
indexClientOptions == IndexClientOptions.FORCEUPDATEANDSPELLCHECK) {
|
||||
handler.logInfo("Updating Index");
|
||||
indexer.updateIndex(context, true);
|
||||
indexer.updateIndex(context, true, type);
|
||||
if (indexClientOptions == IndexClientOptions.FORCEUPDATEANDSPELLCHECK) {
|
||||
checkRebuildSpellCheck(commandLine, indexer);
|
||||
}
|
||||
|
@@ -8,8 +8,13 @@
|
||||
|
||||
package org.dspace.discovery;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.apache.commons.cli.CommandLine;
|
||||
import org.apache.commons.cli.Options;
|
||||
import org.dspace.discovery.indexobject.factory.IndexObjectFactoryFactory;
|
||||
|
||||
/**
|
||||
* This Enum holds all the possible options and combinations for the Index discovery script
|
||||
@@ -29,6 +34,8 @@ public enum IndexClientOptions {
|
||||
FORCEUPDATEANDSPELLCHECK,
|
||||
HELP;
|
||||
|
||||
public static final String TYPE_OPTION = "t";
|
||||
|
||||
/**
|
||||
* This method resolves the CommandLine parameters to figure out which action the index-discovery script should
|
||||
* perform
|
||||
@@ -71,11 +78,15 @@ public enum IndexClientOptions {
|
||||
|
||||
protected static Options constructOptions() {
|
||||
Options options = new Options();
|
||||
List<String> indexableObjectTypes = IndexObjectFactoryFactory.getInstance().getIndexFactories().stream()
|
||||
.map((indexFactory -> indexFactory.getType())).collect(Collectors.toList());
|
||||
|
||||
options
|
||||
.addOption("r", "remove", true, "remove an Item, Collection or Community from index based on its handle");
|
||||
options.addOption("i", "index", true,
|
||||
"add or update an Item, Collection or Community based on its handle or uuid");
|
||||
options.addOption(TYPE_OPTION, "type", true, "reindex only specific type of " +
|
||||
"(re)indexable objects; options: " + Arrays.toString(indexableObjectTypes.toArray()));
|
||||
options.addOption("c", "clean", false,
|
||||
"clean existing index removing any documents that no longer exist in the db");
|
||||
options.addOption("d", "delete", false,
|
||||
|
@@ -64,7 +64,14 @@ public abstract class IndexFactoryImpl<T extends IndexableObject, S> implements
|
||||
|
||||
//Do any additional indexing, depends on the plugins
|
||||
for (SolrServiceIndexPlugin solrServiceIndexPlugin : ListUtils.emptyIfNull(solrServiceIndexPlugins)) {
|
||||
solrServiceIndexPlugin.additionalIndex(context, indexableObject, doc);
|
||||
try {
|
||||
solrServiceIndexPlugin.additionalIndex(context, indexableObject, doc);
|
||||
} catch (Exception e) {
|
||||
log.error("An error occurred while indexing additional fields. " +
|
||||
"Could not fully index item with UUID: {}. Plugin: {}",
|
||||
indexableObject.getUniqueIndexID(), solrServiceIndexPlugin.getClass().getSimpleName());
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return doc;
|
||||
@@ -82,7 +89,7 @@ public abstract class IndexFactoryImpl<T extends IndexableObject, S> implements
|
||||
writeDocument(solrInputDocument, null);
|
||||
} catch (Exception e) {
|
||||
log.error("Error occurred while writing SOLR document for {} object {}",
|
||||
indexableObject.getType(), indexableObject.getID(), e);
|
||||
indexableObject.getType(), indexableObject.getID(), e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -101,8 +108,8 @@ public abstract class IndexFactoryImpl<T extends IndexableObject, S> implements
|
||||
if (streams != null && !streams.isEmpty()) {
|
||||
// limit full text indexing to first 100,000 characters unless configured otherwise
|
||||
final int charLimit = DSpaceServicesFactory.getInstance().getConfigurationService()
|
||||
.getIntProperty("discovery.solr.fulltext.charLimit",
|
||||
100000);
|
||||
.getIntProperty("discovery.solr.fulltext.charLimit",
|
||||
100000);
|
||||
|
||||
// Use Tika's Text parser as the streams are always from the TEXT bundle (i.e. already extracted text)
|
||||
TextAndCSVParser tikaParser = new TextAndCSVParser();
|
||||
@@ -113,6 +120,18 @@ public abstract class IndexFactoryImpl<T extends IndexableObject, S> implements
|
||||
// Use Apache Tika to parse the full text stream(s)
|
||||
try (InputStream fullTextStreams = streams.getStream()) {
|
||||
tikaParser.parse(fullTextStreams, tikaHandler, tikaMetadata, tikaContext);
|
||||
|
||||
// Write Tika metadata to "tika_meta_*" fields.
|
||||
// This metadata is not very useful right now,
|
||||
// but we'll keep it just in case it becomes more useful.
|
||||
for (String name : tikaMetadata.names()) {
|
||||
for (String value : tikaMetadata.getValues(name)) {
|
||||
doc.addField("tika_meta_" + name, value);
|
||||
}
|
||||
}
|
||||
|
||||
// Save (parsed) full text to "fulltext" field
|
||||
doc.addField("fulltext", tikaHandler.toString());
|
||||
} catch (SAXException saxe) {
|
||||
// Check if this SAXException is just a notice that this file was longer than the character limit.
|
||||
// Unfortunately there is not a unique, public exception type to catch here. This error is thrown
|
||||
@@ -121,30 +140,23 @@ public abstract class IndexFactoryImpl<T extends IndexableObject, S> implements
|
||||
if (saxe.getMessage().contains("limit has been reached")) {
|
||||
// log that we only indexed up to that configured limit
|
||||
log.info("Full text is larger than the configured limit (discovery.solr.fulltext.charLimit)."
|
||||
+ " Only the first {} characters were indexed.", charLimit);
|
||||
+ " Only the first {} characters were indexed.", charLimit);
|
||||
} else {
|
||||
log.error("Tika parsing error. Could not index full text.", saxe);
|
||||
throw new IOException("Tika parsing error. Could not index full text.", saxe);
|
||||
}
|
||||
} catch (TikaException ex) {
|
||||
} catch (TikaException | IOException ex) {
|
||||
log.error("Tika parsing error. Could not index full text.", ex);
|
||||
throw new IOException("Tika parsing error. Could not index full text.", ex);
|
||||
} finally {
|
||||
// Add document to index
|
||||
solr.add(doc);
|
||||
}
|
||||
|
||||
// Write Tika metadata to "tika_meta_*" fields.
|
||||
// This metadata is not very useful right now, but we'll keep it just in case it becomes more useful.
|
||||
for (String name : tikaMetadata.names()) {
|
||||
for (String value : tikaMetadata.getValues(name)) {
|
||||
doc.addField("tika_meta_" + name, value);
|
||||
}
|
||||
}
|
||||
|
||||
// Save (parsed) full text to "fulltext" field
|
||||
doc.addField("fulltext", tikaHandler.toString());
|
||||
return;
|
||||
}
|
||||
|
||||
// Add document to index
|
||||
solr.add(doc);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -13,6 +13,8 @@ import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.dspace.app.suggestion.SuggestionProvider;
|
||||
import org.dspace.app.suggestion.SuggestionService;
|
||||
import org.dspace.authorize.AuthorizeException;
|
||||
import org.dspace.content.Collection;
|
||||
import org.dspace.content.Item;
|
||||
@@ -44,6 +46,9 @@ public class ExternalDataServiceImpl implements ExternalDataService {
|
||||
@Autowired
|
||||
private WorkspaceItemService workspaceItemService;
|
||||
|
||||
@Autowired
|
||||
private SuggestionService suggestionService;
|
||||
|
||||
@Override
|
||||
public Optional<ExternalDataObject> getExternalDataObject(String source, String id) {
|
||||
ExternalDataProvider provider = getExternalDataProvider(source);
|
||||
@@ -105,6 +110,16 @@ public class ExternalDataServiceImpl implements ExternalDataService {
|
||||
log.info(LogHelper.getHeader(context, "create_item_from_externalDataObject", "Created item" +
|
||||
"with id: " + item.getID() + " from source: " + externalDataObject.getSource() + " with identifier: " +
|
||||
externalDataObject.getId()));
|
||||
try {
|
||||
List<SuggestionProvider> providers = suggestionService.getSuggestionProviders();
|
||||
if (providers != null) {
|
||||
for (SuggestionProvider p : providers) {
|
||||
p.flagRelatedSuggestionsAsProcessed(context, externalDataObject);
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("Got problems with the solr suggestion storage service: " + e.getMessage(), e);
|
||||
}
|
||||
return workspaceItem;
|
||||
}
|
||||
|
||||
|
@@ -11,7 +11,9 @@ import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.dspace.content.MetadataFieldName;
|
||||
import org.dspace.importer.external.metadatamapping.MetadatumDTO;
|
||||
|
||||
/**
|
||||
@@ -94,6 +96,31 @@ public class ImportRecord {
|
||||
return values;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an {@code Optional<String>} representing the value
|
||||
* of the metadata {@code field} found inside the {@code valueList}.
|
||||
* @param field String of the MetadataField to search
|
||||
* @return {@code Optional<String>} non empty if found.
|
||||
*/
|
||||
public Optional<String> getSingleValue(String field) {
|
||||
MetadataFieldName metadataFieldName = new MetadataFieldName(field);
|
||||
return getSingleValue(metadataFieldName.schema, metadataFieldName.element, metadataFieldName.qualifier);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a single value for the given schema, element, and qualifier.
|
||||
*
|
||||
* @param schema the schema for the value
|
||||
* @param element the element for the value
|
||||
* @param qualifier the qualifier for the value
|
||||
* @return an optional containing the single value, if present
|
||||
*/
|
||||
public Optional<String> getSingleValue(String schema, String element, String qualifier) {
|
||||
return getValue(schema, element, qualifier).stream()
|
||||
.map(MetadatumDTO::getValue)
|
||||
.findFirst();
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a value to the valueList
|
||||
*
|
||||
|
@@ -0,0 +1,130 @@
|
||||
/**
|
||||
* The contents of this file are subject to the license and copyright
|
||||
* detailed in the LICENSE and NOTICE files at the root of the source
|
||||
* tree and available online at
|
||||
*
|
||||
* http://www.dspace.org/license/
|
||||
*/
|
||||
package org.dspace.importer.external.metadatamapping.contributor;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import org.dspace.importer.external.metadatamapping.MetadatumDTO;
|
||||
|
||||
/**
|
||||
* A ROR JsonPath Metadata processor that should be configured inside the {@code ror-integration.xml} file.
|
||||
* This allows the extraction of a given contributor with a specific mappings from the ROR JSON response.
|
||||
*
|
||||
* @author Vincenzo Mecca (vins01-4science - vincenzo.mecca at 4science.com)
|
||||
*/
|
||||
public class RorParentOrgUnitMetadataContributor extends SimpleJsonPathMetadataContributor {
|
||||
|
||||
/**
|
||||
* Determines which field of the JSON detains the {@code type} of this
|
||||
* specific node (that needs to be mapped).
|
||||
*
|
||||
*/
|
||||
private String typeField;
|
||||
|
||||
/**
|
||||
* Determines which is the type of the main parent node that needs to be mapped.
|
||||
* It should match the value of the {@code typeField} of the JSON node.
|
||||
*
|
||||
*/
|
||||
private String parentType;
|
||||
|
||||
/**
|
||||
* Determines which is the field of the JSON that contains the value
|
||||
* that needs to be mapped into a {@code MetadatumDTO}.
|
||||
*/
|
||||
private String labelField;
|
||||
|
||||
/**
|
||||
* Creates a {@code MetadatumDTO} for each correctly mapped JSON node
|
||||
* of the ROR response.
|
||||
* Partial / Unmatched parent-type metadatum will be ignored from this mapping.
|
||||
*
|
||||
* @param fullJson ROR response
|
||||
* @return a collection of read ROR metadata.
|
||||
*/
|
||||
@Override
|
||||
public Collection<MetadatumDTO> contributeMetadata(String fullJson) {
|
||||
|
||||
Collection<MetadatumDTO> metadata = new ArrayList<>();
|
||||
Collection<String> metadataValue = new ArrayList<>();
|
||||
|
||||
JsonNode jsonNode = convertStringJsonToJsonNode(fullJson);
|
||||
JsonNode array = jsonNode.at(getQuery());
|
||||
if (!array.isArray()) {
|
||||
return metadata;
|
||||
}
|
||||
|
||||
Iterator<JsonNode> nodes = array.iterator();
|
||||
while (nodes.hasNext()) {
|
||||
JsonNode node = nodes.next();
|
||||
|
||||
if (!node.has(labelField)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
String type = node.has(typeField) ? node.get(typeField).asText() : null;
|
||||
String label = node.get(labelField).asText();
|
||||
|
||||
if (parentType.equalsIgnoreCase(type)) {
|
||||
metadataValue.add(label);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
for (String value : metadataValue) {
|
||||
MetadatumDTO metadatumDto = new MetadatumDTO();
|
||||
metadatumDto.setValue(value);
|
||||
metadatumDto.setElement(getField().getElement());
|
||||
metadatumDto.setQualifier(getField().getQualifier());
|
||||
metadatumDto.setSchema(getField().getSchema());
|
||||
metadata.add(metadatumDto);
|
||||
}
|
||||
return metadata;
|
||||
}
|
||||
|
||||
private JsonNode convertStringJsonToJsonNode(String json) {
|
||||
ObjectMapper mapper = new ObjectMapper();
|
||||
JsonNode body = null;
|
||||
try {
|
||||
body = mapper.readTree(json);
|
||||
} catch (JsonProcessingException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
return body;
|
||||
}
|
||||
|
||||
public String getTypeField() {
|
||||
return typeField;
|
||||
}
|
||||
|
||||
public void setTypeField(String typeField) {
|
||||
this.typeField = typeField;
|
||||
}
|
||||
|
||||
public String getLabelField() {
|
||||
return labelField;
|
||||
}
|
||||
|
||||
public void setLabelField(String labelField) {
|
||||
this.labelField = labelField;
|
||||
}
|
||||
|
||||
public String getParentType() {
|
||||
return parentType;
|
||||
}
|
||||
|
||||
public void setParentType(String parentType) {
|
||||
this.parentType = parentType;
|
||||
}
|
||||
|
||||
}
|
@@ -87,5 +87,4 @@ public class SimpleXpathDateFormatMetadataContributor extends SimpleXpathMetadat
|
||||
dcValue.setSchema(field.getSchema());
|
||||
return dcValue;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,29 @@
|
||||
/**
|
||||
* The contents of this file are subject to the license and copyright
|
||||
* detailed in the LICENSE and NOTICE files at the root of the source
|
||||
* tree and available online at
|
||||
*
|
||||
* http://www.dspace.org/license/
|
||||
*/
|
||||
package org.dspace.importer.external.openaire.metadatamapping;
|
||||
|
||||
import java.util.Map;
|
||||
import javax.annotation.Resource;
|
||||
|
||||
import org.dspace.importer.external.metadatamapping.AbstractMetadataFieldMapping;
|
||||
|
||||
/**
|
||||
* An implementation of {@link AbstractMetadataFieldMapping} responsible for
|
||||
* defining the mapping of the OpenAIRE metadatum fields on the DSpace metadatum
|
||||
* fields
|
||||
*
|
||||
* @author Mykhaylo Boychuk (4science.it)
|
||||
*/
|
||||
public class OpenAIREPublicationFieldMapping extends AbstractMetadataFieldMapping {
|
||||
|
||||
@Override
|
||||
@Resource(name = "openairePublicationsMetadataFieldMap")
|
||||
public void setMetadataFieldMap(Map metadataFieldMap) {
|
||||
super.setMetadataFieldMap(metadataFieldMap);
|
||||
}
|
||||
}
|
@@ -0,0 +1,353 @@
|
||||
/**
|
||||
* The contents of this file are subject to the license and copyright
|
||||
* detailed in the LICENSE and NOTICE files at the root of the source
|
||||
* tree and available online at
|
||||
*
|
||||
* http://www.dspace.org/license/
|
||||
*/
|
||||
package org.dspace.importer.external.openaire.service;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.StringReader;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Callable;
|
||||
import javax.el.MethodNotFoundException;
|
||||
import javax.ws.rs.client.Client;
|
||||
import javax.ws.rs.client.ClientBuilder;
|
||||
import javax.ws.rs.client.Invocation;
|
||||
import javax.ws.rs.client.WebTarget;
|
||||
import javax.ws.rs.core.Response;
|
||||
|
||||
import org.dspace.content.Item;
|
||||
import org.dspace.importer.external.datamodel.ImportRecord;
|
||||
import org.dspace.importer.external.datamodel.Query;
|
||||
import org.dspace.importer.external.exception.MetadataSourceException;
|
||||
import org.dspace.importer.external.metadatamapping.MetadatumDTO;
|
||||
import org.dspace.importer.external.service.AbstractImportMetadataSourceService;
|
||||
import org.dspace.importer.external.service.components.QuerySource;
|
||||
import org.dspace.services.ConfigurationService;
|
||||
import org.jdom2.Document;
|
||||
import org.jdom2.Element;
|
||||
import org.jdom2.JDOMException;
|
||||
import org.jdom2.Namespace;
|
||||
import org.jdom2.filter.Filters;
|
||||
import org.jdom2.input.SAXBuilder;
|
||||
import org.jdom2.xpath.XPathExpression;
|
||||
import org.jdom2.xpath.XPathFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
/**
|
||||
* Implements a data source for querying OpenAIRE
|
||||
*
|
||||
* @author Pasquale Cavallo (pasquale.cavallo at 4science dot it)
|
||||
*/
|
||||
public class OpenAireImportMetadataSourceServiceImpl extends AbstractImportMetadataSourceService<Element>
|
||||
implements QuerySource {
|
||||
|
||||
@Autowired(required = true)
|
||||
protected ConfigurationService configurationService;
|
||||
|
||||
private String baseAddress;
|
||||
|
||||
private WebTarget webTarget;
|
||||
|
||||
private String queryParam;
|
||||
|
||||
@Override
|
||||
public String getImportSource() {
|
||||
return "openaire";
|
||||
}
|
||||
|
||||
/**
|
||||
* The string that identifies this import implementation. Preferable a URI
|
||||
*
|
||||
* @return the identifying uri
|
||||
*/
|
||||
@Override
|
||||
public ImportRecord getRecord(String id) throws MetadataSourceException {
|
||||
return retry(new SearchByIdCallable(id));
|
||||
}
|
||||
|
||||
/**
|
||||
* The string that identifies this import implementation. Preferable a URI
|
||||
*
|
||||
* @return the identifying uri
|
||||
*/
|
||||
@Override
|
||||
public ImportRecord getRecord(Query query) throws MetadataSourceException {
|
||||
return retry(new SearchByIdCallable(query));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Find the number of records matching a query;
|
||||
*
|
||||
* @param query a query string to base the search on.
|
||||
* @return the sum of the matching records over this import source
|
||||
* @throws MetadataSourceException if the underlying methods throw any exception.
|
||||
*/
|
||||
@Override
|
||||
public int getRecordsCount(String query) throws MetadataSourceException {
|
||||
return retry(new CountByQueryCallable(query));
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the number of records matching a query;
|
||||
*
|
||||
* @param query a query object to base the search on.
|
||||
* @return the sum of the matching records over this import source
|
||||
* @throws MetadataSourceException if the underlying methods throw any exception.
|
||||
*/
|
||||
@Override
|
||||
public int getRecordsCount(Query query) throws MetadataSourceException {
|
||||
return retry(new CountByQueryCallable(query));
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the number of records matching a string query. Supports pagination
|
||||
*
|
||||
* @param query a query string to base the search on.
|
||||
* @param start offset to start at
|
||||
* @param count number of records to retrieve.
|
||||
* @return a set of records. Fully transformed.
|
||||
* @throws MetadataSourceException if the underlying methods throw any exception.
|
||||
*/
|
||||
@Override
|
||||
public Collection<ImportRecord> getRecords(String query, int start, int count) throws MetadataSourceException {
|
||||
return retry(new SearchByQueryCallable(query, start, count));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Find records based on a object query.
|
||||
*
|
||||
* @param query a query object to base the search on.
|
||||
* @return a set of records. Fully transformed.
|
||||
* @throws MetadataSourceException if the underlying methods throw any exception.
|
||||
*/
|
||||
@Override
|
||||
public Collection<ImportRecord> getRecords(Query query) throws MetadataSourceException {
|
||||
return retry(new SearchByQueryCallable(query));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<ImportRecord> findMatchingRecords(Query query) throws MetadataSourceException {
|
||||
throw new MethodNotFoundException("This method is not implemented for OpenAIRE");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<ImportRecord> findMatchingRecords(Item item) throws MetadataSourceException {
|
||||
throw new MethodNotFoundException("This method is not implemented for OpenAIRE");
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the baseAddress to this object
|
||||
*
|
||||
* @param baseAddress The String object that represents the baseAddress of this object
|
||||
*/
|
||||
public void setBaseAddress(String baseAddress) {
|
||||
this.baseAddress = baseAddress;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the baseAddress set to this object
|
||||
*
|
||||
* @return The String object that represents the baseAddress of this object
|
||||
*/
|
||||
public String getBaseAddress() {
|
||||
return baseAddress;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the name of the query param, this correspond to the index used (title, author)
|
||||
*
|
||||
* @param queryParam on which index make the query
|
||||
*/
|
||||
public void setQueryParam(String queryParam) {
|
||||
this.queryParam = queryParam;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the name of the query param for the rest call
|
||||
*
|
||||
* @return the name of the query param, i.e. the index (title, author) to use
|
||||
*/
|
||||
public String getQueryParam() {
|
||||
return queryParam;
|
||||
}
|
||||
/**
|
||||
* Initialize the class
|
||||
*
|
||||
* @throws Exception on generic exception
|
||||
*/
|
||||
@Override
|
||||
public void init() throws Exception {
|
||||
Client client = ClientBuilder.newClient();
|
||||
if (baseAddress == null) {
|
||||
baseAddress = configurationService.getProperty("openaire.base.url");
|
||||
}
|
||||
if (queryParam == null) {
|
||||
queryParam = "title";
|
||||
}
|
||||
webTarget = client.target(baseAddress);
|
||||
}
|
||||
|
||||
public class SearchByIdCallable implements Callable<ImportRecord> {
|
||||
|
||||
String id = null;
|
||||
|
||||
public SearchByIdCallable(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public SearchByIdCallable(Query query) {
|
||||
this.id = query.getParameterAsClass("id", String.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ImportRecord call() throws Exception {
|
||||
List<ImportRecord> results = new ArrayList<ImportRecord>();
|
||||
WebTarget localTarget = webTarget.queryParam("openairePublicationID", id);
|
||||
Invocation.Builder invocationBuilder = localTarget.request();
|
||||
Response response = invocationBuilder.get();
|
||||
if (response.getStatus() == 200) {
|
||||
String responseString = response.readEntity(String.class);
|
||||
List<Element> omElements = splitToRecords(responseString);
|
||||
if (omElements != null) {
|
||||
for (Element record : omElements) {
|
||||
results.add(filterMultipleTitles(transformSourceRecords(record)));
|
||||
}
|
||||
}
|
||||
return results != null ? results.get(0) : null;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class CountByQueryCallable implements Callable<Integer> {
|
||||
|
||||
String q;
|
||||
|
||||
public CountByQueryCallable(String query) {
|
||||
q = query;
|
||||
}
|
||||
|
||||
public CountByQueryCallable(Query query) {
|
||||
q = query.getParameterAsClass("query", String.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer call() throws Exception {
|
||||
WebTarget localTarget = webTarget.queryParam(queryParam, q);
|
||||
Invocation.Builder invocationBuilder = localTarget.request();
|
||||
Response response = invocationBuilder.get();
|
||||
if (response.getStatus() == 200) {
|
||||
String responseString = response.readEntity(String.class);
|
||||
|
||||
SAXBuilder saxBuilder = new SAXBuilder();
|
||||
Document document = saxBuilder.build(new StringReader(responseString));
|
||||
Element root = document.getRootElement();
|
||||
|
||||
XPathExpression<Element> xpath = XPathFactory.instance().compile("/header/total",
|
||||
Filters.element(), null);
|
||||
|
||||
Element totalItem = (Element) xpath.evaluateFirst(root);
|
||||
return totalItem != null ? Integer.parseInt(totalItem.getText()) : null;
|
||||
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class SearchByQueryCallable implements Callable<List<ImportRecord>> {
|
||||
|
||||
String q;
|
||||
int page;
|
||||
int count;
|
||||
|
||||
public SearchByQueryCallable(String query, int start, int count) {
|
||||
this.q = query;
|
||||
this.page = start / count;
|
||||
this.count = count;
|
||||
}
|
||||
|
||||
public SearchByQueryCallable(Query query) {
|
||||
this.q = query.getParameterAsClass("query", String.class);
|
||||
this.page = query.getParameterAsClass("start", Integer.class) /
|
||||
query.getParameterAsClass("count", Integer.class);
|
||||
this.count = query.getParameterAsClass("count", Integer.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ImportRecord> call() throws Exception {
|
||||
WebTarget localTarget = webTarget.queryParam(queryParam, q);
|
||||
localTarget = localTarget.queryParam("page", page + 1);
|
||||
localTarget = localTarget.queryParam("size", count);
|
||||
List<ImportRecord> results = new ArrayList<ImportRecord>();
|
||||
Invocation.Builder invocationBuilder = localTarget.request();
|
||||
Response response = invocationBuilder.get();
|
||||
if (response.getStatus() == 200) {
|
||||
String responseString = response.readEntity(String.class);
|
||||
List<Element> omElements = splitToRecords(responseString);
|
||||
if (omElements != null) {
|
||||
for (Element record : omElements) {
|
||||
results.add(filterMultipleTitles(transformSourceRecords(record)));
|
||||
}
|
||||
}
|
||||
}
|
||||
return results;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method remove multiple titles occurrences
|
||||
*
|
||||
* @param transformSourceRecords
|
||||
* @return ImportRecord with one or zero title
|
||||
*/
|
||||
private ImportRecord filterMultipleTitles(ImportRecord transformSourceRecords) {
|
||||
List<MetadatumDTO> metadata = (List<MetadatumDTO>)transformSourceRecords.getValueList();
|
||||
ArrayList<MetadatumDTO> nextSourceRecord = new ArrayList<>();
|
||||
boolean found = false;
|
||||
for (MetadatumDTO dto : metadata) {
|
||||
if ("dc".equals(dto.getSchema()) && "title".equals(dto.getElement()) && dto.getQualifier() == null) {
|
||||
if (!found) {
|
||||
nextSourceRecord.add(dto);
|
||||
found = true;
|
||||
}
|
||||
} else {
|
||||
nextSourceRecord.add(dto);
|
||||
}
|
||||
}
|
||||
return new ImportRecord(nextSourceRecord);
|
||||
}
|
||||
|
||||
private List<Element> splitToRecords(String recordsSrc) {
|
||||
|
||||
try {
|
||||
SAXBuilder saxBuilder = new SAXBuilder();
|
||||
Document document = saxBuilder.build(new StringReader(recordsSrc));
|
||||
Element root = document.getRootElement();
|
||||
|
||||
List namespaces = Arrays.asList(
|
||||
Namespace.getNamespace("dri", "http://www.driver-repository.eu/namespace/dri"),
|
||||
Namespace.getNamespace("oaf", "http://namespace.openaire.eu/oaf"),
|
||||
Namespace.getNamespace("xsi", "http://www.w3.org/2001/XMLSchema-instance"));
|
||||
XPathExpression<Element> xpath = XPathFactory.instance().compile("//results/result",
|
||||
Filters.element(), null, namespaces);
|
||||
|
||||
List<Element> recordsList = xpath.evaluate(root);
|
||||
return recordsList;
|
||||
} catch (JDOMException | IOException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
38
dspace-api/src/main/java/org/dspace/importer/external/ror/service/RorFieldMapping.java
vendored
Normal file
38
dspace-api/src/main/java/org/dspace/importer/external/ror/service/RorFieldMapping.java
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
/**
|
||||
* The contents of this file are subject to the license and copyright
|
||||
* detailed in the LICENSE and NOTICE files at the root of the source
|
||||
* tree and available online at
|
||||
*
|
||||
* http://www.dspace.org/license/
|
||||
*/
|
||||
package org.dspace.importer.external.ror.service;
|
||||
|
||||
import java.util.Map;
|
||||
import javax.annotation.Resource;
|
||||
|
||||
import org.dspace.importer.external.metadatamapping.AbstractMetadataFieldMapping;
|
||||
|
||||
|
||||
/**
|
||||
* An implementation of {@link AbstractMetadataFieldMapping}
|
||||
* Responsible for defining the mapping of the ROR metadatum fields on the DSpace metadatum fields
|
||||
*
|
||||
* @author Vincenzo Mecca (vins01-4science - vincenzo.mecca at 4science.com)
|
||||
*/
|
||||
public class RorFieldMapping extends AbstractMetadataFieldMapping {
|
||||
|
||||
/**
|
||||
* Defines which metadatum is mapped on which metadatum. Note that while the key must be unique it
|
||||
* only matters here for postprocessing of the value. The mapped MetadatumContributor has full control over
|
||||
* what metadatafield is generated.
|
||||
*
|
||||
* @param metadataFieldMap The map containing the link between retrieve metadata and metadata that will be set to
|
||||
* the item.
|
||||
*/
|
||||
@Override
|
||||
@Resource(name = "rorMetadataFieldMap")
|
||||
public void setMetadataFieldMap(Map metadataFieldMap) {
|
||||
super.setMetadataFieldMap(metadataFieldMap);
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,278 @@
|
||||
/**
|
||||
* The contents of this file are subject to the license and copyright
|
||||
* detailed in the LICENSE and NOTICE files at the root of the source
|
||||
* tree and available online at
|
||||
*
|
||||
* http://www.dspace.org/license/
|
||||
*/
|
||||
package org.dspace.importer.external.ror.service;
|
||||
|
||||
import java.net.URISyntaxException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.Callable;
|
||||
import javax.el.MethodNotFoundException;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import org.apache.commons.collections.CollectionUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.http.client.utils.URIBuilder;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.dspace.content.Item;
|
||||
import org.dspace.importer.external.datamodel.ImportRecord;
|
||||
import org.dspace.importer.external.datamodel.Query;
|
||||
import org.dspace.importer.external.exception.MetadataSourceException;
|
||||
import org.dspace.importer.external.liveimportclient.service.LiveImportClient;
|
||||
import org.dspace.importer.external.service.AbstractImportMetadataSourceService;
|
||||
import org.dspace.importer.external.service.components.QuerySource;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
/**
|
||||
* Implements a {@code AbstractImportMetadataSourceService} for querying ROR services.
|
||||
*
|
||||
* @author Vincenzo Mecca (vins01-4science - vincenzo.mecca at 4science.com)
|
||||
*/
|
||||
public class RorImportMetadataSourceServiceImpl extends AbstractImportMetadataSourceService<String>
|
||||
implements QuerySource {
|
||||
|
||||
private final static Logger log = LogManager.getLogger();
|
||||
protected static final String ROR_IDENTIFIER_PREFIX = "https://ror.org/";
|
||||
|
||||
private String url;
|
||||
|
||||
private int timeout = 1000;
|
||||
|
||||
@Autowired
|
||||
private LiveImportClient liveImportClient;
|
||||
|
||||
@Override
|
||||
public String getImportSource() {
|
||||
return "ror";
|
||||
}
|
||||
|
||||
@Override
|
||||
public ImportRecord getRecord(String id) throws MetadataSourceException {
|
||||
List<ImportRecord> records = retry(new SearchByIdCallable(id));
|
||||
return CollectionUtils.isEmpty(records) ? null : records.get(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getRecordsCount(String query) throws MetadataSourceException {
|
||||
return retry(new CountByQueryCallable(query));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getRecordsCount(Query query) throws MetadataSourceException {
|
||||
return retry(new CountByQueryCallable(query));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<ImportRecord> getRecords(String query, int start, int count) throws MetadataSourceException {
|
||||
return retry(new SearchByQueryCallable(query));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<ImportRecord> getRecords(Query query) throws MetadataSourceException {
|
||||
return retry(new SearchByQueryCallable(query));
|
||||
}
|
||||
|
||||
@Override
|
||||
public ImportRecord getRecord(Query query) throws MetadataSourceException {
|
||||
List<ImportRecord> records = retry(new SearchByIdCallable(query));
|
||||
return CollectionUtils.isEmpty(records) ? null : records.get(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<ImportRecord> findMatchingRecords(Query query) throws MetadataSourceException {
|
||||
throw new MethodNotFoundException("This method is not implemented for ROR");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<ImportRecord> findMatchingRecords(Item item) throws MetadataSourceException {
|
||||
throw new MethodNotFoundException("This method is not implemented for ROR");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init() throws Exception {
|
||||
}
|
||||
|
||||
/**
|
||||
* This class is a Callable implementation to get ROR entries based on query
|
||||
* object. This Callable use as query value the string queryString passed to
|
||||
* constructor. If the object will be construct through Query.class instance, a
|
||||
* Query's map entry with key "query" will be used. Pagination is supported too,
|
||||
* using the value of the Query's map with keys "start" and "count".
|
||||
*
|
||||
* @author Mykhaylo Boychuk (mykhaylo.boychuk@4science.com)
|
||||
*/
|
||||
private class SearchByQueryCallable implements Callable<List<ImportRecord>> {
|
||||
|
||||
private Query query;
|
||||
|
||||
private SearchByQueryCallable(String queryString) {
|
||||
query = new Query();
|
||||
query.addParameter("query", queryString);
|
||||
}
|
||||
|
||||
private SearchByQueryCallable(Query query) {
|
||||
this.query = query;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ImportRecord> call() throws Exception {
|
||||
return search(query.getParameterAsClass("query", String.class));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This class is a Callable implementation to get an ROR entry using bibcode The
|
||||
* bibcode to use can be passed through the constructor as a String or as
|
||||
* Query's map entry, with the key "id".
|
||||
*
|
||||
* @author Mykhaylo Boychuk (mykhaylo.boychuk@4science.com)
|
||||
*/
|
||||
private class SearchByIdCallable implements Callable<List<ImportRecord>> {
|
||||
private Query query;
|
||||
|
||||
private SearchByIdCallable(Query query) {
|
||||
this.query = query;
|
||||
}
|
||||
|
||||
private SearchByIdCallable(String id) {
|
||||
this.query = new Query();
|
||||
query.addParameter("id", id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ImportRecord> call() throws Exception {
|
||||
return searchById(query.getParameterAsClass("id", String.class));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This class is a Callable implementation to count the number of entries for a
|
||||
* ROR query. This Callable uses as query value to ROR the string queryString
|
||||
* passed to constructor. If the object will be construct through {@code Query}
|
||||
* instance, the value of the Query's map with the key "query" will be used.
|
||||
*
|
||||
* @author Vincenzo Mecca (vins01-4science - vincenzo.mecca at 4science.com)
|
||||
*/
|
||||
private class CountByQueryCallable implements Callable<Integer> {
|
||||
private Query query;
|
||||
|
||||
private CountByQueryCallable(String queryString) {
|
||||
query = new Query();
|
||||
query.addParameter("query", queryString);
|
||||
}
|
||||
|
||||
private CountByQueryCallable(Query query) {
|
||||
this.query = query;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer call() throws Exception {
|
||||
return count(query.getParameterAsClass("query", String.class));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Counts the number of results for the given query.
|
||||
*
|
||||
* @param query the query string to count results for
|
||||
* @return the number of results for the given query
|
||||
*/
|
||||
public Integer count(String query) {
|
||||
try {
|
||||
Map<String, Map<String, String>> params = new HashMap<String, Map<String, String>>();
|
||||
|
||||
URIBuilder uriBuilder = new URIBuilder(this.url);
|
||||
uriBuilder.addParameter("query", query);
|
||||
|
||||
String resp = liveImportClient.executeHttpGetRequest(timeout, uriBuilder.toString(), params);
|
||||
if (StringUtils.isEmpty(resp)) {
|
||||
return 0;
|
||||
}
|
||||
JsonNode jsonNode = convertStringJsonToJsonNode(resp);
|
||||
return jsonNode.at("/number_of_results").asInt();
|
||||
} catch (URISyntaxException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
private List<ImportRecord> searchById(String id) {
|
||||
|
||||
List<ImportRecord> importResults = new ArrayList<>();
|
||||
|
||||
id = StringUtils.removeStart(id, ROR_IDENTIFIER_PREFIX);
|
||||
|
||||
try {
|
||||
Map<String, Map<String, String>> params = new HashMap<String, Map<String, String>>();
|
||||
|
||||
URIBuilder uriBuilder = new URIBuilder(this.url + "/" + id);
|
||||
|
||||
String resp = liveImportClient.executeHttpGetRequest(timeout, uriBuilder.toString(), params);
|
||||
if (StringUtils.isEmpty(resp)) {
|
||||
return importResults;
|
||||
}
|
||||
|
||||
JsonNode jsonNode = convertStringJsonToJsonNode(resp);
|
||||
importResults.add(transformSourceRecords(jsonNode.toString()));
|
||||
|
||||
} catch (URISyntaxException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return importResults;
|
||||
}
|
||||
|
||||
private List<ImportRecord> search(String query) {
|
||||
List<ImportRecord> importResults = new ArrayList<>();
|
||||
try {
|
||||
Map<String, Map<String, String>> params = new HashMap<String, Map<String, String>>();
|
||||
|
||||
URIBuilder uriBuilder = new URIBuilder(this.url);
|
||||
uriBuilder.addParameter("query", query);
|
||||
|
||||
String resp = liveImportClient.executeHttpGetRequest(timeout, uriBuilder.toString(), params);
|
||||
if (StringUtils.isEmpty(resp)) {
|
||||
return importResults;
|
||||
}
|
||||
|
||||
JsonNode jsonNode = convertStringJsonToJsonNode(resp);
|
||||
JsonNode docs = jsonNode.at("/items");
|
||||
if (docs.isArray()) {
|
||||
Iterator<JsonNode> nodes = docs.elements();
|
||||
while (nodes.hasNext()) {
|
||||
JsonNode node = nodes.next();
|
||||
importResults.add(transformSourceRecords(node.toString()));
|
||||
}
|
||||
} else {
|
||||
importResults.add(transformSourceRecords(docs.toString()));
|
||||
}
|
||||
} catch (URISyntaxException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return importResults;
|
||||
}
|
||||
|
||||
private JsonNode convertStringJsonToJsonNode(String json) {
|
||||
try {
|
||||
return new ObjectMapper().readTree(json);
|
||||
} catch (JsonProcessingException e) {
|
||||
log.error("Unable to process json response.", e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public void setUrl(String url) {
|
||||
this.url = url;
|
||||
}
|
||||
|
||||
}
|
@@ -34,7 +34,7 @@ public interface SubmissionConfigService {
|
||||
|
||||
public int countSubmissionConfigs();
|
||||
|
||||
public SubmissionConfig getSubmissionConfigByCollection(String collectionHandle);
|
||||
public SubmissionConfig getSubmissionConfigByCollection(Collection collection);
|
||||
|
||||
public SubmissionConfig getSubmissionConfigByName(String submitName);
|
||||
|
||||
|
@@ -57,8 +57,8 @@ public class SubmissionConfigServiceImpl implements SubmissionConfigService, Ini
|
||||
}
|
||||
|
||||
@Override
|
||||
public SubmissionConfig getSubmissionConfigByCollection(String collectionHandle) {
|
||||
return submissionConfigReader.getSubmissionConfigByCollection(collectionHandle);
|
||||
public SubmissionConfig getSubmissionConfigByCollection(Collection collection) {
|
||||
return submissionConfigReader.getSubmissionConfigByCollection(collection);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@@ -123,6 +123,20 @@
|
||||
</property>
|
||||
</bean>
|
||||
|
||||
<bean id="openaireImportServiceByAuthor"
|
||||
class="org.dspace.importer.external.openaire.service.OpenAireImportMetadataSourceServiceImpl" scope="singleton">
|
||||
<property name="metadataFieldMapping" ref="openairePublicationMetadataFieldMapping"/>
|
||||
<property name="queryParam" value="author"/>
|
||||
</bean>
|
||||
<bean id="openaireImportServiceByTitle"
|
||||
class="org.dspace.importer.external.openaire.service.OpenAireImportMetadataSourceServiceImpl" scope="singleton">
|
||||
<property name="metadataFieldMapping" ref="openairePublicationMetadataFieldMapping"/>
|
||||
<property name="queryParam" value="title"/>
|
||||
</bean>
|
||||
<bean id="openairePublicationMetadataFieldMapping"
|
||||
class="org.dspace.importer.external.openaire.metadatamapping.OpenAIREPublicationFieldMapping">
|
||||
</bean>
|
||||
|
||||
<bean id="CrossRefImportService" class="org.dspace.importer.external.crossref.CrossRefImportMetadataSourceServiceImpl" scope="singleton">
|
||||
<property name="metadataFieldMapping" ref="CrossRefMetadataFieldMapping"/>
|
||||
<property name="url" value="${crossref.url}"/>
|
||||
@@ -150,6 +164,12 @@
|
||||
<property name="viewMode" value="${scopus.search-api.viewMode}"/>
|
||||
</bean>
|
||||
<bean id="scopusMetadataFieldMapping" class="org.dspace.importer.external.scopus.service.ScopusFieldMapping"/>
|
||||
|
||||
<bean id="rorImportService" class="org.dspace.importer.external.ror.service.RorImportMetadataSourceServiceImpl">
|
||||
<property name="metadataFieldMapping" ref="rorMetadataFieldMapping"/>
|
||||
<property name="url" value="${ror.orgunit-import.api-url}"/>
|
||||
</bean>
|
||||
<bean id="rorMetadataFieldMapping" class="org.dspace.importer.external.ror.service.RorFieldMapping"/>
|
||||
|
||||
<bean id="vufindImportService" class="org.dspace.importer.external.vufind.VuFindImportMetadataSourceServiceImpl" scope="singleton">
|
||||
<!-- Set to empty to use the default set of fields -->
|
||||
|
@@ -0,0 +1,23 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
|
||||
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/
|
||||
|
||||
-->
|
||||
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:context="http://www.springframework.org/schema/context"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/beans
|
||||
http://www.springframework.org/schema/beans/spring-beans.xsd
|
||||
http://www.springframework.org/schema/context
|
||||
http://www.springframework.org/schema/context/spring-context.xsd"
|
||||
default-autowire-candidates="*Service,*DAO,javax.sql.DataSource">
|
||||
|
||||
<context:annotation-config/> <!-- allows us to use spring annotations in beans -->
|
||||
|
||||
<bean class="org.dspace.app.suggestion.SuggestionServiceImpl" id="org.dspace.app.suggestion.SuggestionService" />
|
||||
</beans>
|
@@ -24,6 +24,9 @@
|
||||
<name-map collection-handle="123456789/typebind-test" submission-name="typebindtest"/>
|
||||
<name-map collection-handle="123456789/accessCondition-not-discoverable" submission-name="accessConditionNotDiscoverable"/>
|
||||
<name-map collection-handle="123456789/test-hidden" submission-name="test-hidden"/>
|
||||
<name-map community-handle="123456789/topcommunity-test" submission-name="topcommunitytest"/>
|
||||
<name-map community-handle="123456789/subcommunity-test" submission-name="subcommunitytest"/>
|
||||
<name-map collection-handle="123456789/collection-test" submission-name="collectiontest"/>
|
||||
</submission-map>
|
||||
|
||||
|
||||
@@ -257,6 +260,18 @@
|
||||
<step id="test-always-hidden"/>
|
||||
</submission-process>
|
||||
|
||||
<submission-process name="topcommunitytest">
|
||||
<step id="collection"/>
|
||||
</submission-process>
|
||||
|
||||
<submission-process name="subcommunitytest">
|
||||
<step id="collection"/>
|
||||
</submission-process>
|
||||
|
||||
<submission-process name="collectiontest">
|
||||
<step id="collection"/>
|
||||
</submission-process>
|
||||
|
||||
</submission-definitions>
|
||||
|
||||
</item-submission>
|
||||
|
@@ -90,6 +90,8 @@
|
||||
</list>
|
||||
</property>
|
||||
</bean>
|
||||
|
||||
<bean class="org.dspace.app.suggestion.MockSuggestionExternalDataSource" />
|
||||
|
||||
<bean id="dataciteLiveImportDataProvider" class="org.dspace.external.provider.impl.LiveImportDataProvider">
|
||||
<property name="metadataSource" ref="DataCiteImportService"/>
|
||||
|
@@ -47,12 +47,14 @@
|
||||
<bean id="org.dspace.statistics.SolrStatisticsCore"
|
||||
class="org.dspace.statistics.MockSolrStatisticsCore"
|
||||
autowire-candidate="true"/>
|
||||
|
||||
|
||||
<!-- qa events -->
|
||||
<bean class="org.dspace.qaevent.MockQAEventService"
|
||||
id="org.dspace.qaevent.service.QAEventService" />
|
||||
|
||||
<bean class="org.dspace.statistics.GeoIpService" autowire-candidate="true"/>
|
||||
|
||||
|
||||
<!-- suggestion service for solr providers -->
|
||||
<bean id="org.dspace.app.suggestion.SolrSuggestionStorageService" class="org.dspace.app.suggestion.MockSolrSuggestionStorageService" />
|
||||
|
||||
</beans>
|
||||
|
@@ -0,0 +1,33 @@
|
||||
<?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:context="http://www.springframework.org/schema/context"
|
||||
xmlns:util="http://www.springframework.org/schema/util"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/beans
|
||||
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
|
||||
http://www.springframework.org/schema/context
|
||||
http://www.springframework.org/schema/context/spring-context-2.5.xsd
|
||||
http://www.springframework.org/schema/util
|
||||
http://www.springframework.org/schema/util/spring-util-3.0.xsd">
|
||||
|
||||
<context:annotation-config /> <!-- allows us to use spring annotations in beans -->
|
||||
|
||||
<!-- This is defined in the solr-services.xml
|
||||
<bean id="org.dspace.app.nbevent.service.NBEventService"
|
||||
class="org.dspace.app.nbevent.service.impl.NBEventServiceImpl" /> -->
|
||||
|
||||
<util:map id="suggestionProviders" map-class="java.util.HashMap"
|
||||
key-type="java.lang.String" value-type="org.dspace.app.suggestion.SuggestionProvider">
|
||||
<entry key="scopus">
|
||||
<bean class="org.dspace.app.suggestion.MockSolrSuggestionProvider">
|
||||
<property name="sourceName" value="scopus"></property>
|
||||
</bean>
|
||||
</entry>
|
||||
<entry key="reciter">
|
||||
<bean class="org.dspace.app.suggestion.MockSolrSuggestionProvider">
|
||||
<property name="sourceName" value="reciter"></property>
|
||||
</bean>
|
||||
</entry>
|
||||
</util:map>
|
||||
|
||||
</beans>
|
@@ -0,0 +1,20 @@
|
||||
/**
|
||||
* 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.suggestion;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.dspace.core.Context;
|
||||
import org.dspace.external.model.ExternalDataObject;
|
||||
|
||||
public class MockSolrSuggestionProvider extends SolrSuggestionProvider {
|
||||
|
||||
@Override
|
||||
protected boolean isExternalDataObjectPotentiallySuggested(Context context, ExternalDataObject externalDataObject) {
|
||||
return StringUtils.equals(MockSuggestionExternalDataSource.NAME, externalDataObject.getSource());
|
||||
}
|
||||
}
|
@@ -0,0 +1,38 @@
|
||||
/**
|
||||
* The contents of this file are subject to the license and copyright
|
||||
* detailed in the LICENSE and NOTICE files at the root of the source
|
||||
* tree and available online at
|
||||
*
|
||||
* http://www.dspace.org/license/
|
||||
*/
|
||||
package org.dspace.app.suggestion;
|
||||
|
||||
import org.dspace.solr.MockSolrServer;
|
||||
import org.springframework.beans.factory.DisposableBean;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
/**
|
||||
* Mock SOLR service for the suggestion Core.
|
||||
*/
|
||||
@Service
|
||||
public class MockSolrSuggestionStorageService extends SolrSuggestionStorageServiceImpl
|
||||
implements InitializingBean, DisposableBean {
|
||||
private MockSolrServer mockSolrServer;
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
mockSolrServer = new MockSolrServer("suggestion");
|
||||
solrSuggestionClient = mockSolrServer.getSolrServer();
|
||||
}
|
||||
|
||||
/** Clear all records from the search core. */
|
||||
public void reset() {
|
||||
mockSolrServer.reset();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroy() throws Exception {
|
||||
mockSolrServer.destroy();
|
||||
}
|
||||
}
|
@@ -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.suggestion;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.apache.commons.codec.binary.StringUtils;
|
||||
import org.dspace.core.Context;
|
||||
import org.dspace.external.model.ExternalDataObject;
|
||||
import org.dspace.external.provider.AbstractExternalDataProvider;
|
||||
import org.dspace.services.RequestService;
|
||||
import org.dspace.services.model.Request;
|
||||
import org.dspace.utils.DSpace;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
public class MockSuggestionExternalDataSource extends AbstractExternalDataProvider {
|
||||
public static final String NAME = "suggestion";
|
||||
|
||||
@Autowired
|
||||
private SuggestionService suggestionService;
|
||||
|
||||
@Override
|
||||
public String getSourceIdentifier() {
|
||||
return NAME;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<ExternalDataObject> getExternalDataObject(String id) {
|
||||
RequestService requestService = new DSpace().getRequestService();
|
||||
Request currentRequest = requestService.getCurrentRequest();
|
||||
Context context = (Context) currentRequest.getAttribute("dspace.context");
|
||||
Suggestion suggestion = suggestionService.findUnprocessedSuggestion(context, id);
|
||||
if (suggestion != null) {
|
||||
ExternalDataObject extDataObj = new ExternalDataObject(NAME);
|
||||
extDataObj.setDisplayValue(suggestion.getDisplay());
|
||||
extDataObj.setId(suggestion.getExternalSourceUri()
|
||||
.substring(suggestion.getExternalSourceUri().lastIndexOf("/") + 1));
|
||||
extDataObj.setMetadata(suggestion.getMetadata());
|
||||
return Optional.of(extDataObj);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ExternalDataObject> searchExternalDataObjects(String query, int start, int limit) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supports(String source) {
|
||||
return StringUtils.equals(NAME, source);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getNumberOfResults(String query) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,218 @@
|
||||
/**
|
||||
* 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.suggestion;
|
||||
|
||||
import static java.util.Optional.of;
|
||||
import static org.dspace.app.suggestion.SuggestionUtils.getFirstEntryByMetadatum;
|
||||
import static org.dspace.orcid.model.OrcidProfileSectionType.EXTERNAL_IDS;
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.Matchers.greaterThan;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import java.io.File;
|
||||
import java.net.URL;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import javax.xml.bind.JAXBContext;
|
||||
import javax.xml.bind.Unmarshaller;
|
||||
|
||||
import org.apache.commons.collections.CollectionUtils;
|
||||
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.Item;
|
||||
import org.dspace.content.MetadataValue;
|
||||
import org.dspace.content.dto.MetadataValueDTO;
|
||||
import org.dspace.content.factory.ContentServiceFactory;
|
||||
import org.dspace.content.service.ItemService;
|
||||
import org.dspace.external.factory.ExternalServiceFactory;
|
||||
import org.dspace.external.model.ExternalDataObject;
|
||||
import org.dspace.external.provider.ExternalDataProvider;
|
||||
import org.dspace.external.provider.impl.OrcidPublicationDataProvider;
|
||||
import org.dspace.external.service.ExternalDataService;
|
||||
import org.dspace.kernel.ServiceManager;
|
||||
import org.dspace.orcid.client.OrcidClient;
|
||||
import org.dspace.orcid.client.OrcidConfiguration;
|
||||
import org.dspace.orcid.factory.OrcidServiceFactory;
|
||||
import org.dspace.orcid.model.OrcidTokenResponseDTO;
|
||||
import org.dspace.orcid.service.OrcidProfileSectionFactoryService;
|
||||
import org.dspace.services.ConfigurationService;
|
||||
import org.dspace.services.factory.DSpaceServicesFactory;
|
||||
import org.dspace.utils.DSpace;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.orcid.jaxb.model.v3.release.record.Work;
|
||||
import org.orcid.jaxb.model.v3.release.record.WorkBulk;
|
||||
import org.orcid.jaxb.model.v3.release.record.summary.Works;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
/**
|
||||
* Tests for suggestion utilities @see SuggestionUtils
|
||||
* @author Francesco Bacchelli (francesco.bacchelli at 4science.it)
|
||||
*/
|
||||
public class SuggestionUtilsIT extends AbstractIntegrationTestWithDatabase {
|
||||
|
||||
private static ConfigurationService cfg;
|
||||
private static final String ORCID = "0000-1111-2222-3333";
|
||||
private static final String ACCESS_TOKEN = "32c83ccb-c6d5-4981-b6ea-6a34a36de8ab";
|
||||
private static final String BASE_XML_DIR_PATH = "org/dspace/app/orcid-works/";
|
||||
private OrcidPublicationDataProvider dataProvider;
|
||||
private SolrSuggestionProvider solrSuggestionProvider;
|
||||
private OrcidProfileSectionFactoryService profileSectionFactoryService;
|
||||
private ItemService itemService;
|
||||
private Collection collection;
|
||||
private ExternalDataProvider primaryProvider;
|
||||
private Collection persons;
|
||||
private OrcidConfiguration orcidConfiguration;
|
||||
private OrcidClient orcidClientMock;
|
||||
private OrcidClient orcidClient;
|
||||
private String originalClientId;
|
||||
|
||||
@Autowired
|
||||
private SuggestionService suggestionService;
|
||||
|
||||
@Before
|
||||
public void setup() throws Exception {
|
||||
context.turnOffAuthorisationSystem();
|
||||
|
||||
parentCommunity = CommunityBuilder.createCommunity(context)
|
||||
.withName("Parent Community")
|
||||
.build();
|
||||
|
||||
persons = CollectionBuilder.createCollection(context, parentCommunity)
|
||||
.withEntityType("Person")
|
||||
.withName("Profiles")
|
||||
.build();
|
||||
|
||||
profileSectionFactoryService = OrcidServiceFactory.getInstance().getOrcidProfileSectionFactoryService();
|
||||
itemService = ContentServiceFactory.getInstance().getItemService();
|
||||
|
||||
context.restoreAuthSystemState();
|
||||
|
||||
cfg = DSpaceServicesFactory.getInstance().getConfigurationService();
|
||||
|
||||
ServiceManager serviceManager = DSpaceServicesFactory.getInstance().getServiceManager();
|
||||
HashMap<String,SuggestionProvider> providers = serviceManager.getServiceByName("suggestionProviders",
|
||||
HashMap.class);
|
||||
solrSuggestionProvider = (SolrSuggestionProvider) providers.get("scopus");
|
||||
dataProvider = new DSpace().getServiceManager()
|
||||
.getServiceByName("orcidPublicationDataProvider", OrcidPublicationDataProvider.class);
|
||||
ExternalDataService externalDataService = ExternalServiceFactory.getInstance().getExternalDataService();
|
||||
primaryProvider = externalDataService.getExternalDataProvider("openaireFunding");
|
||||
|
||||
orcidConfiguration = new DSpace().getServiceManager()
|
||||
.getServiceByName("org.dspace.orcid.client.OrcidConfiguration", OrcidConfiguration.class);
|
||||
|
||||
orcidClientMock = mock(OrcidClient.class);
|
||||
orcidClient = dataProvider.getOrcidClient();
|
||||
|
||||
dataProvider.setReadPublicAccessToken(null);
|
||||
dataProvider.setOrcidClient(orcidClientMock);
|
||||
|
||||
originalClientId = orcidConfiguration.getClientId();
|
||||
orcidConfiguration.setClientId("DSPACE-CLIENT-ID");
|
||||
orcidConfiguration.setClientSecret("DSPACE-CLIENT-SECRET");
|
||||
|
||||
when(orcidClientMock.getReadPublicAccessToken()).thenReturn(buildTokenResponse(ACCESS_TOKEN));
|
||||
|
||||
when(orcidClientMock.getWorks(any(), eq(ORCID))).thenReturn(unmarshall("works.xml", Works.class));
|
||||
when(orcidClientMock.getWorks(eq(ORCID))).thenReturn(unmarshall("works.xml", Works.class));
|
||||
|
||||
when(orcidClientMock.getObject(any(), eq(ORCID), any(), eq(Work.class)))
|
||||
.then((invocation) -> of(unmarshall("work-" + invocation.getArgument(2) + ".xml", Work.class)));
|
||||
when(orcidClientMock.getObject(eq(ORCID), any(), eq(Work.class)))
|
||||
.then((invocation) -> of(unmarshall("work-" + invocation.getArgument(1) + ".xml", Work.class)));
|
||||
|
||||
when(orcidClientMock.getWorkBulk(any(), eq(ORCID), any()))
|
||||
.then((invocation) -> unmarshallWorkBulk(invocation.getArgument(2)));
|
||||
when(orcidClientMock.getWorkBulk(eq(ORCID), any()))
|
||||
.then((invocation) -> unmarshallWorkBulk(invocation.getArgument(1)));
|
||||
}
|
||||
|
||||
@After
|
||||
public void after() {
|
||||
dataProvider.setOrcidClient(orcidClient);
|
||||
orcidConfiguration.setClientId(originalClientId);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetAllEntriesByMetadatum() {
|
||||
context.turnOffAuthorisationSystem();
|
||||
Item item = ItemBuilder.createItem(context, persons)
|
||||
.withTitle("Test profile")
|
||||
.withScopusAuthorIdentifier("SCOPUS-123456")
|
||||
.withResearcherIdentifier("R-ID-01")
|
||||
.build();
|
||||
context.restoreAuthSystemState();
|
||||
|
||||
List<MetadataValue> values = List.of(getMetadata(item, "person.identifier.scopus-author-id", 0));
|
||||
|
||||
Object firstOrcidObject = profileSectionFactoryService.createOrcidObject(context, values, EXTERNAL_IDS);
|
||||
Optional<ExternalDataObject> optional = dataProvider.getExternalDataObject(ORCID + "::277902");
|
||||
|
||||
ExternalDataObject externalDataObject = optional.get();
|
||||
String openAireId = externalDataObject.getId();
|
||||
Suggestion suggestion = new Suggestion(solrSuggestionProvider.getSourceName(), item, openAireId);
|
||||
suggestion.getMetadata().add(
|
||||
new MetadataValueDTO("dc", "title", null, null, "dcTitle"));
|
||||
suggestion.setDisplay(getFirstEntryByMetadatum(externalDataObject, "dc", "title", null));
|
||||
suggestion.getMetadata().add(new MetadataValueDTO("dc", "date", "issued", null, new Date().toString()));
|
||||
suggestion.getMetadata().add(new MetadataValueDTO("dc", "description", "abstract", null, "description"));
|
||||
suggestion.setExternalSourceUri(cfg.getProperty("dspace.server.url")
|
||||
+ "/api/integration/externalsources/" + primaryProvider.getSourceIdentifier() + "/entryValues/"
|
||||
+ openAireId);
|
||||
List<String> result = SuggestionUtils.getAllEntriesByMetadatum(externalDataObject, "dc", "title", null);
|
||||
|
||||
assertTrue(result != null && !result.isEmpty());
|
||||
|
||||
assertTrue(CollectionUtils.isEqualCollection(
|
||||
SuggestionUtils.getAllEntriesByMetadatum(externalDataObject, "dc.title"),
|
||||
result));
|
||||
|
||||
String firstResult = SuggestionUtils.getFirstEntryByMetadatum(externalDataObject, "dc", "title", null);
|
||||
assertTrue("Another cautionary tale.".equalsIgnoreCase(firstResult));
|
||||
firstResult = SuggestionUtils.getFirstEntryByMetadatum(externalDataObject, "dc.title");
|
||||
assertTrue("Another cautionary tale.".equalsIgnoreCase(firstResult));
|
||||
}
|
||||
|
||||
private MetadataValue getMetadata(Item item, String metadataField, int place) {
|
||||
List<MetadataValue> values = itemService.getMetadataByMetadataString(item, metadataField);
|
||||
assertThat(values.size(), greaterThan(place));
|
||||
return values.get(place);
|
||||
}
|
||||
|
||||
private OrcidTokenResponseDTO buildTokenResponse(String accessToken) {
|
||||
OrcidTokenResponseDTO response = new OrcidTokenResponseDTO();
|
||||
response.setAccessToken(accessToken);
|
||||
return response;
|
||||
}
|
||||
|
||||
private WorkBulk unmarshallWorkBulk(List<String> putCodes) throws Exception {
|
||||
return unmarshall("workBulk-" + String.join("-", putCodes) + ".xml", WorkBulk.class);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private <T> T unmarshall(String fileName, Class<T> clazz) throws Exception {
|
||||
JAXBContext jaxbContext = JAXBContext.newInstance(clazz);
|
||||
Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
|
||||
URL resource = getClass().getClassLoader().getResource(BASE_XML_DIR_PATH + fileName);
|
||||
if (resource == null) {
|
||||
throw new IllegalStateException("No resource found named " + BASE_XML_DIR_PATH + fileName);
|
||||
}
|
||||
return (T) unmarshaller.unmarshal(new File(resource.getFile()));
|
||||
}
|
||||
}
|
@@ -0,0 +1,73 @@
|
||||
/**
|
||||
* 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.util;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import org.dspace.AbstractIntegrationTestWithDatabase;
|
||||
import org.dspace.builder.CollectionBuilder;
|
||||
import org.dspace.builder.CommunityBuilder;
|
||||
import org.dspace.content.Collection;
|
||||
import org.dspace.content.Community;
|
||||
import org.dspace.submit.factory.SubmissionServiceFactory;
|
||||
import org.dspace.submit.service.SubmissionConfigService;
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
* Integration Tests for parsing and utilities on submission config forms / readers
|
||||
*
|
||||
* @author Toni Prieto
|
||||
*/
|
||||
public class SubmissionConfigIT extends AbstractIntegrationTestWithDatabase {
|
||||
|
||||
@Test
|
||||
public void testSubmissionMapByCommunityHandleSubmissionConfig()
|
||||
throws SubmissionConfigReaderException {
|
||||
|
||||
context.turnOffAuthorisationSystem();
|
||||
// Sep up a structure with one top community and two subcommunities with collections
|
||||
Community topcom = CommunityBuilder.createCommunity(context, "123456789/topcommunity-test")
|
||||
.withName("Parent Community")
|
||||
.build();
|
||||
Community subcom1 = CommunityBuilder.createSubCommunity(context, topcom, "123456789/subcommunity-test")
|
||||
.withName("Subcommunity 1")
|
||||
.build();
|
||||
Community subcom2 = CommunityBuilder.createSubCommunity(context, topcom, "123456789/not-mapped3")
|
||||
.withName("Subcommunity 2")
|
||||
.build();
|
||||
// col1 should use the form item submission form mapped for subcom1
|
||||
Collection col1 = CollectionBuilder.createCollection(context, subcom1, "123456789/not-mapped1")
|
||||
.withName("Collection 1")
|
||||
.build();
|
||||
// col2 should use the item submission form mapped for the top community
|
||||
Collection col2 = CollectionBuilder.createCollection(context, subcom2, "123456789/not-mapped2")
|
||||
.withName("Collection 2")
|
||||
.build();
|
||||
// col3 should use the item submission form directly mapped for this collection
|
||||
Collection col3 = CollectionBuilder.createCollection(context, subcom1, "123456789/collection-test")
|
||||
.withName("Collection 3")
|
||||
.build();
|
||||
context.restoreAuthSystemState();
|
||||
|
||||
SubmissionConfigService submissionConfigService = SubmissionServiceFactory.getInstance()
|
||||
.getSubmissionConfigService();
|
||||
|
||||
// for col1, it should return the item submission form defined for their parent subcom1
|
||||
SubmissionConfig submissionConfig1 = submissionConfigService.getSubmissionConfigByCollection(col1);
|
||||
assertEquals("subcommunitytest", submissionConfig1.getSubmissionName());
|
||||
|
||||
// for col2, it should return the item submission form defined for topcom
|
||||
SubmissionConfig submissionConfig2 = submissionConfigService.getSubmissionConfigByCollection(col2);
|
||||
assertEquals("topcommunitytest", submissionConfig2.getSubmissionName());
|
||||
|
||||
// for col3, it should return the item submission form defined directly for the collection
|
||||
SubmissionConfig submissionConfig3 = submissionConfigService.getSubmissionConfigByCollection(col3);
|
||||
assertEquals("collectiontest", submissionConfig3.getSubmissionName());
|
||||
|
||||
}
|
||||
}
|
@@ -9,17 +9,20 @@ package org.dspace.app.util;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.dspace.AbstractUnitTest;
|
||||
import org.dspace.content.Collection;
|
||||
import org.dspace.submit.factory.SubmissionServiceFactory;
|
||||
import org.junit.After;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.Before;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
import org.mockito.Mock;
|
||||
|
||||
/**
|
||||
* Tests for parsing and utilities on submission config forms / readers
|
||||
@@ -30,6 +33,9 @@ public class SubmissionConfigTest extends AbstractUnitTest {
|
||||
|
||||
DCInputsReader inputReader;
|
||||
|
||||
@Mock
|
||||
private Collection col1;
|
||||
|
||||
@BeforeClass
|
||||
public static void setUpClass() {
|
||||
}
|
||||
@@ -56,6 +62,8 @@ public class SubmissionConfigTest extends AbstractUnitTest {
|
||||
String typeBindSubmissionName = "typebindtest";
|
||||
String typeBindSubmissionStepName = "typebindtest";
|
||||
|
||||
when(col1.getHandle()).thenReturn(typeBindHandle);
|
||||
|
||||
// Expected field lists from typebindtest form
|
||||
List<String> allConfiguredFields = new ArrayList<>();
|
||||
allConfiguredFields.add("dc.title");
|
||||
@@ -67,7 +75,7 @@ public class SubmissionConfigTest extends AbstractUnitTest {
|
||||
// Get submission configuration
|
||||
SubmissionConfig submissionConfig =
|
||||
SubmissionServiceFactory.getInstance().getSubmissionConfigService()
|
||||
.getSubmissionConfigByCollection(typeBindHandle);
|
||||
.getSubmissionConfigByCollection(col1);
|
||||
// Submission name should match name defined in item-submission.xml
|
||||
assertEquals(typeBindSubmissionName, submissionConfig.getSubmissionName());
|
||||
// Step 0 - our process only has one step. It should not be null and have the ID typebindtest
|
||||
|
@@ -16,6 +16,7 @@ import org.apache.logging.log4j.Logger;
|
||||
import org.dspace.alerts.service.SystemWideAlertService;
|
||||
import org.dspace.app.requestitem.factory.RequestItemServiceFactory;
|
||||
import org.dspace.app.requestitem.service.RequestItemService;
|
||||
import org.dspace.app.suggestion.SolrSuggestionStorageService;
|
||||
import org.dspace.app.util.SubmissionConfigReaderException;
|
||||
import org.dspace.authorize.AuthorizeException;
|
||||
import org.dspace.authorize.factory.AuthorizeServiceFactory;
|
||||
@@ -116,6 +117,7 @@ public abstract class AbstractBuilder<T, S> {
|
||||
static SubscribeService subscribeService;
|
||||
static SupervisionOrderService supervisionOrderService;
|
||||
static QAEventService qaEventService;
|
||||
static SolrSuggestionStorageService solrSuggestionService;
|
||||
|
||||
protected Context context;
|
||||
|
||||
@@ -185,6 +187,7 @@ public abstract class AbstractBuilder<T, S> {
|
||||
subscribeService = ContentServiceFactory.getInstance().getSubscribeService();
|
||||
supervisionOrderService = SupervisionOrderServiceFactory.getInstance().getSupervisionOrderService();
|
||||
qaEventService = new DSpace().getSingletonService(QAEventService.class);
|
||||
solrSuggestionService = new DSpace().getSingletonService(SolrSuggestionStorageService.class);
|
||||
}
|
||||
|
||||
|
||||
|
@@ -186,6 +186,10 @@ public class ItemBuilder extends AbstractDSpaceObjectBuilder<Item> {
|
||||
return addMetadataValue(item, "iiif", "image", "height", String.valueOf(i));
|
||||
}
|
||||
|
||||
public ItemBuilder withDSpaceObjectOwner(String name, String authority) {
|
||||
return addMetadataValue(item, "dspace", "object", "owner", null, name, authority, 600);
|
||||
}
|
||||
|
||||
public ItemBuilder withMetadata(final String schema, final String element, final String qualifier,
|
||||
final String value) {
|
||||
return addMetadataValue(item, schema, element, qualifier, value);
|
||||
|
@@ -68,8 +68,8 @@ public class ProcessBuilder extends AbstractBuilder<Process, ProcessService> {
|
||||
|
||||
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));
|
||||
process.setStartTime(startTime == null ? null : simpleDateFormat.parse(startTime));
|
||||
process.setFinishedTime(endTime == null ? null : simpleDateFormat.parse(endTime));
|
||||
return this;
|
||||
}
|
||||
|
||||
|
@@ -0,0 +1,161 @@
|
||||
/**
|
||||
* 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.builder;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.solr.client.solrj.SolrServerException;
|
||||
import org.dspace.app.suggestion.MockSuggestionExternalDataSource;
|
||||
import org.dspace.app.suggestion.SolrSuggestionStorageService;
|
||||
import org.dspace.app.suggestion.Suggestion;
|
||||
import org.dspace.app.suggestion.SuggestionEvidence;
|
||||
import org.dspace.app.suggestion.SuggestionTarget;
|
||||
import org.dspace.content.Collection;
|
||||
import org.dspace.content.Item;
|
||||
import org.dspace.content.dto.MetadataValueDTO;
|
||||
import org.dspace.core.Context;
|
||||
import org.dspace.eperson.EPerson;
|
||||
|
||||
/**
|
||||
* Builder to construct Item objects
|
||||
*
|
||||
* @author Andrea Bollini (andrea.bollini at 4science.it)
|
||||
*/
|
||||
public class SuggestionTargetBuilder extends AbstractBuilder<SuggestionTarget, SolrSuggestionStorageService> {
|
||||
public final static String EVIDENCE_MOCK_NAME = "MockEvidence";
|
||||
public final static String EVIDENCE_MOCK_NOTE = "Generated for testing purpose...";
|
||||
private Item item;
|
||||
private SuggestionTarget target;
|
||||
private List<Suggestion> suggestions;
|
||||
private String source;
|
||||
private int total;
|
||||
|
||||
protected SuggestionTargetBuilder(Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
public static SuggestionTargetBuilder createTarget(final Context context, final Collection col, final String name) {
|
||||
return createTarget(context, col, name, null);
|
||||
}
|
||||
|
||||
public static SuggestionTargetBuilder createTarget(final Context context, final Collection col, final String name,
|
||||
final EPerson eperson) {
|
||||
SuggestionTargetBuilder builder = new SuggestionTargetBuilder(context);
|
||||
return builder.create(context, col, name, eperson);
|
||||
}
|
||||
|
||||
public static SuggestionTargetBuilder createTarget(final Context context, final Item item) {
|
||||
SuggestionTargetBuilder builder = new SuggestionTargetBuilder(context);
|
||||
return builder.create(context, item);
|
||||
}
|
||||
|
||||
private SuggestionTargetBuilder create(final Context context, final Collection col, final String name) {
|
||||
return create(context, col, name, null);
|
||||
}
|
||||
|
||||
private SuggestionTargetBuilder create(final Context context, final Collection col, final String name,
|
||||
final EPerson eperson) {
|
||||
this.context = context;
|
||||
|
||||
try {
|
||||
ItemBuilder itemBuilder = ItemBuilder.createItem(context, col).withTitle(name);
|
||||
if (eperson != null) {
|
||||
itemBuilder = itemBuilder.withDSpaceObjectOwner(eperson.getFullName(), eperson.getID().toString());
|
||||
}
|
||||
item = itemBuilder.build();
|
||||
context.dispatchEvents();
|
||||
indexingService.commit();
|
||||
} catch (Exception e) {
|
||||
return handleException(e);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
private SuggestionTargetBuilder create(final Context context, final Item item) {
|
||||
this.context = context;
|
||||
this.item = item;
|
||||
return this;
|
||||
}
|
||||
|
||||
public SuggestionTargetBuilder withSuggestionCount(final String source, final int total) {
|
||||
this.source = source;
|
||||
this.total = total;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SuggestionTarget build() {
|
||||
target = new SuggestionTarget(item);
|
||||
target.setTotal(total);
|
||||
target.setSource(source);
|
||||
suggestions = generateAllSuggestion();
|
||||
try {
|
||||
for (Suggestion s : suggestions) {
|
||||
solrSuggestionService.addSuggestion(s, false, false);
|
||||
}
|
||||
solrSuggestionService.commit();
|
||||
} catch (SolrServerException | IOException e) {
|
||||
e.printStackTrace();
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
return target;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cleanup() throws Exception {
|
||||
solrSuggestionService.deleteTarget(target);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected SolrSuggestionStorageService getService() {
|
||||
return solrSuggestionService;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delete(Context c, SuggestionTarget dso) throws Exception {
|
||||
solrSuggestionService.deleteTarget(dso);
|
||||
}
|
||||
|
||||
private List<Suggestion> generateAllSuggestion() {
|
||||
List<Suggestion> allSuggestions = new ArrayList<Suggestion>();
|
||||
for (int idx = 0; idx < target.getTotal(); idx++) {
|
||||
String idPartStr = String.valueOf(idx + 1);
|
||||
Suggestion sug = new Suggestion(source, item, idPartStr);
|
||||
sug.setDisplay("Suggestion " + source + " " + idPartStr);
|
||||
MetadataValueDTO mTitle = new MetadataValueDTO();
|
||||
mTitle.setSchema("dc");
|
||||
mTitle.setElement("title");
|
||||
mTitle.setValue("Title Suggestion " + idPartStr);
|
||||
|
||||
MetadataValueDTO mSource1 = new MetadataValueDTO();
|
||||
mSource1.setSchema("dc");
|
||||
mSource1.setElement("source");
|
||||
mSource1.setValue("Source 1");
|
||||
|
||||
MetadataValueDTO mSource2 = new MetadataValueDTO();
|
||||
mSource2.setSchema("dc");
|
||||
mSource2.setElement("source");
|
||||
mSource2.setValue("Source 2");
|
||||
|
||||
sug.getMetadata().add(mTitle);
|
||||
sug.getMetadata().add(mSource1);
|
||||
sug.getMetadata().add(mSource2);
|
||||
|
||||
sug.setExternalSourceUri(
|
||||
"http://localhost/api/integration/externalsources/" + MockSuggestionExternalDataSource.NAME
|
||||
+ "/entryValues/" + idPartStr);
|
||||
sug.getEvidences().add(new SuggestionEvidence(EVIDENCE_MOCK_NAME,
|
||||
idx % 2 == 0 ? 100 - idx : (double) idx / 2, EVIDENCE_MOCK_NOTE));
|
||||
allSuggestions.add(sug);
|
||||
}
|
||||
return allSuggestions;
|
||||
}
|
||||
|
||||
}
|
@@ -77,6 +77,7 @@
|
||||
</common:source>
|
||||
<work:title>
|
||||
<common:title>Another cautionary tale.</common:title>
|
||||
<common:title>Second title</common:title>
|
||||
</work:title>
|
||||
<work:type>journal-article</work:type>
|
||||
<common:publication-date>
|
||||
|
@@ -156,7 +156,7 @@
|
||||
<!-- As our Parent POM sets this to 'test' scope, we must override it -->
|
||||
<dependency>
|
||||
<groupId>org.hamcrest</groupId>
|
||||
<artifactId>hamcrest-all</artifactId>
|
||||
<artifactId>hamcrest</artifactId>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
|
@@ -450,6 +450,16 @@ public class XOAI {
|
||||
doc.addField("item.communities", "com_" + com.getHandle().replace("/", "_"));
|
||||
}
|
||||
|
||||
boolean hasBitstream = false;
|
||||
|
||||
for (Bundle b : item.getBundles("ORIGINAL")) {
|
||||
if (b.getBitstreams().size() > 0) {
|
||||
hasBitstream = true;
|
||||
}
|
||||
}
|
||||
|
||||
doc.addField("item.hasbitstream", hasBitstream);
|
||||
|
||||
List<MetadataValue> allData = itemService.getMetadata(item, Item.ANY, Item.ANY, Item.ANY, Item.ANY);
|
||||
for (MetadataValue dc : allData) {
|
||||
MetadataField field = dc.getMetadataField();
|
||||
|
@@ -0,0 +1,57 @@
|
||||
/**
|
||||
* 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.xoai.filter;
|
||||
|
||||
import java.sql.SQLException;
|
||||
|
||||
import org.apache.log4j.LogManager;
|
||||
import org.apache.log4j.Logger;
|
||||
import org.dspace.content.Bundle;
|
||||
import org.dspace.content.Item;
|
||||
import org.dspace.handle.factory.HandleServiceFactory;
|
||||
import org.dspace.handle.service.HandleService;
|
||||
import org.dspace.xoai.data.DSpaceItem;
|
||||
import org.dspace.xoai.filter.results.SolrFilterResult;
|
||||
|
||||
|
||||
/**
|
||||
* Created by Philip Vissenaekens (philip at atmire dot com)
|
||||
* Date: 21/04/15
|
||||
* Time: 15:18
|
||||
*/
|
||||
public class ItemsWithBitstreamFilter extends DSpaceFilter {
|
||||
|
||||
private static Logger log = LogManager.getLogger(ItemsWithBitstreamFilter.class);
|
||||
|
||||
private static final HandleService handleService
|
||||
= HandleServiceFactory.getInstance().getHandleService();
|
||||
|
||||
@Override
|
||||
public SolrFilterResult buildSolrQuery() {
|
||||
return new SolrFilterResult("item.hasbitstream:true");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isShown(DSpaceItem item) {
|
||||
try {
|
||||
String handle = DSpaceItem.parseHandle(item.getIdentifier());
|
||||
if (handle == null) {
|
||||
return false;
|
||||
}
|
||||
Item dspaceItem = (Item) handleService.resolveToObject(context, handle);
|
||||
for (Bundle b : dspaceItem.getBundles("ORIGINAL")) {
|
||||
if (b.getBitstreams().size() > 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
log.error(e.getMessage(), e);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
@@ -11,6 +11,8 @@ import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.sql.SQLException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
import com.lyncode.xoai.dataprovider.xml.xoai.Element;
|
||||
@@ -21,6 +23,7 @@ import org.apache.logging.log4j.Logger;
|
||||
import org.dspace.app.util.factory.UtilServiceFactory;
|
||||
import org.dspace.app.util.service.MetadataExposureService;
|
||||
import org.dspace.authorize.AuthorizeException;
|
||||
import org.dspace.authorize.ResourcePolicy;
|
||||
import org.dspace.authorize.factory.AuthorizeServiceFactory;
|
||||
import org.dspace.authorize.service.AuthorizeService;
|
||||
import org.dspace.content.Bitstream;
|
||||
@@ -114,23 +117,21 @@ public class ItemUtils {
|
||||
log.error("Null bitstream found, check item uuid: " + item.getID());
|
||||
break;
|
||||
}
|
||||
boolean primary = false;
|
||||
// Check if current bitstream is in original bundle + 1 of the 2 following
|
||||
// Bitstream = primary bitstream in bundle -> true
|
||||
// No primary bitstream found in bundle-> only the first one gets flagged as "primary"
|
||||
if (b.getName() != null && b.getName().equals("ORIGINAL") && (b.getPrimaryBitstream() != null
|
||||
&& b.getPrimaryBitstream().getID() == bit.getID()
|
||||
|| b.getPrimaryBitstream() == null && bit.getID() == bits.get(0).getID())) {
|
||||
primary = true;
|
||||
}
|
||||
|
||||
Element bitstream = create("bitstream");
|
||||
bitstreams.getElement().add(bitstream);
|
||||
String url = "";
|
||||
String bsName = bit.getName();
|
||||
String sid = String.valueOf(bit.getSequenceID());
|
||||
|
||||
String baseUrl = configurationService.getProperty("oai.bitstream.baseUrl");
|
||||
String handle = null;
|
||||
// get handle of parent Item of this bitstream, if there
|
||||
// is one:
|
||||
List<Bundle> bn = bit.getBundles();
|
||||
if (!bn.isEmpty()) {
|
||||
List<Item> bi = bn.get(0).getItems();
|
||||
if (!bi.isEmpty()) {
|
||||
handle = bi.get(0).getHandle();
|
||||
}
|
||||
}
|
||||
url = baseUrl + "/bitstreams/" + bit.getID().toString() + "/download";
|
||||
String url = baseUrl + "/bitstreams/" + bit.getID().toString() + "/download";
|
||||
|
||||
String cks = bit.getChecksum();
|
||||
String cka = bit.getChecksumAlgorithm();
|
||||
@@ -147,18 +148,65 @@ public class ItemUtils {
|
||||
if (description != null) {
|
||||
bitstream.getField().add(createValue("description", description));
|
||||
}
|
||||
// Add bitstream embargo information (READ policy present, for Anonymous group with a start date)
|
||||
addResourcePolicyInformation(context, bit, bitstream);
|
||||
|
||||
bitstream.getField().add(createValue("format", bit.getFormat(context).getMIMEType()));
|
||||
bitstream.getField().add(createValue("size", "" + bit.getSizeBytes()));
|
||||
bitstream.getField().add(createValue("url", url));
|
||||
bitstream.getField().add(createValue("checksum", cks));
|
||||
bitstream.getField().add(createValue("checksumAlgorithm", cka));
|
||||
bitstream.getField().add(createValue("sid", bit.getSequenceID() + ""));
|
||||
// Add primary bitstream field to allow locating easily the primary bitstream information
|
||||
bitstream.getField().add(createValue("primary", primary + ""));
|
||||
}
|
||||
}
|
||||
|
||||
return bundles;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method will add metadata information about associated resource policies for a give bitstream.
|
||||
* It will parse of relevant policies and add metadata information
|
||||
* @param context
|
||||
* @param bitstream the bitstream object
|
||||
* @param bitstreamEl the bitstream metadata object to add resource policy information to
|
||||
* @throws SQLException
|
||||
*/
|
||||
private static void addResourcePolicyInformation(Context context, Bitstream bitstream, Element bitstreamEl)
|
||||
throws SQLException {
|
||||
// Pre-filter access policies by DSO (bitstream) and Action (READ)
|
||||
List<ResourcePolicy> policies = authorizeService.getPoliciesActionFilter(context, bitstream, Constants.READ);
|
||||
|
||||
// Create resourcePolicies container
|
||||
Element resourcePolicies = create("resourcePolicies");
|
||||
|
||||
for (ResourcePolicy policy : policies) {
|
||||
String groupName = policy.getGroup() != null ? policy.getGroup().getName() : null;
|
||||
String user = policy.getEPerson() != null ? policy.getEPerson().getName() : null;
|
||||
String action = Constants.actionText[policy.getAction()];
|
||||
Date startDate = policy.getStartDate();
|
||||
Date endDate = policy.getEndDate();
|
||||
|
||||
SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd");
|
||||
|
||||
Element resourcePolicyEl = create("resourcePolicy");
|
||||
resourcePolicyEl.getField().add(createValue("group", groupName));
|
||||
resourcePolicyEl.getField().add(createValue("user", user));
|
||||
resourcePolicyEl.getField().add(createValue("action", action));
|
||||
if (startDate != null) {
|
||||
resourcePolicyEl.getField().add(createValue("start-date", formatter.format(startDate)));
|
||||
}
|
||||
if (endDate != null) {
|
||||
resourcePolicyEl.getField().add(createValue("end-date", formatter.format(endDate)));
|
||||
}
|
||||
// Add resourcePolicy to list of resourcePolicies
|
||||
resourcePolicies.getElement().add(resourcePolicyEl);
|
||||
}
|
||||
// Add list of resource policies to the corresponding Bitstream XML Element
|
||||
bitstreamEl.getElement().add(resourcePolicies);
|
||||
}
|
||||
|
||||
private static Element createLicenseElement(Context context, Item item)
|
||||
throws SQLException, AuthorizeException, IOException {
|
||||
Element license = create("license");
|
||||
@@ -178,7 +226,7 @@ public class ItemUtils {
|
||||
license.getField().add(createValue("bin", Base64Utils.encode(out.toString())));
|
||||
} else {
|
||||
log.info("Missing READ rights for license bitstream. Did not include license bitstream for item: "
|
||||
+ item.getID() + ".");
|
||||
+ item.getID() + ".");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,35 @@
|
||||
/**
|
||||
* 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.xoai.tests.stylesheets;
|
||||
|
||||
import static org.dspace.xoai.tests.support.XmlMatcherBuilder.xml;
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.core.Is.is;
|
||||
import static org.hamcrest.core.IsEqual.equalTo;
|
||||
|
||||
import org.dspace.xoai.tests.support.XmlMatcherBuilder;
|
||||
import org.junit.Test;
|
||||
|
||||
public class RioxxXslTest extends AbstractXSLTest {
|
||||
@Test
|
||||
public void rioxxCanTransformInput() throws Exception {
|
||||
String result = apply("rioxx.xsl").to(resource("xoai-rioxx-test.xml"));
|
||||
|
||||
assertThat(result, is(rioxx().withXPath("//dc:title", equalTo("The Intercorrelation Between " +
|
||||
"Executive Function, Physics Problem Solving, Mathematical, and Matrix Reasoning Skills: " +
|
||||
"Reflections from a Small-Scale Experiment"))));
|
||||
}
|
||||
|
||||
private XmlMatcherBuilder rioxx() {
|
||||
return xml()
|
||||
.withNamespace("rioxx", "http://www.rioxx.net/schema/v3.0/rioxx/")
|
||||
.withNamespace("rioxxterms", "http://docs.rioxx.net/schema/v3.0/rioxxterms/")
|
||||
.withNamespace("dcterms", "http://purl.org/dc/terms/")
|
||||
.withNamespace("dc", "http://purl.org/dc/elements/1.1/");
|
||||
}
|
||||
}
|
89
dspace-oai/src/test/resources/rioxx-test-invalid.xml
Normal file
89
dspace-oai/src/test/resources/rioxx-test-invalid.xml
Normal file
@@ -0,0 +1,89 @@
|
||||
<?xml version='1.0' encoding='UTF-8'?><!--
|
||||
|
||||
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/
|
||||
|
||||
-->
|
||||
<rioxx xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.rioxx.net/schema/v3.0/rioxx/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:dcterms="http://purl.org/dc/terms/" xmlns:rioxxterms="http://docs.rioxx.net/schema/v3.0/rioxxterms/">
|
||||
<dc:description>Data on Secchi disc depth (the depth at which a standard white disc lowered into the water just becomes invisible to a surface observer) show that water clarity in the North Sea declined during the 20th century, with likely consequences for marine primary production. However, the causes of this trend remain unknown. Here we analyse the hypothesis that changes in the North Sea's wave climate were largely responsible by causing an increase in the concentrations of suspended particulate matter (SPM) in the water column through the resuspension of seabed sediments. First, we analysed the broad-scale statistical relationships between SPM and bed shear stress due to waves and tides. We used hindcasts of wave and current data to construct a space–time dataset of bed shear stress between 1997 and 2017 across the northwest European Continental Shelf and compared the results with satellite-derived SPM concentrations. Bed shear stress was found to drive most of the inter-annual variation in SPM in the hydrographically mixed waters of the central and southern North Sea. We then used a long-term wave reanalysis to construct a time series of bed shear stress from 1900 to 2010. This shows that bed shear stress increased significantly across much of the shelf during this period, with increases of over 20 % in the southeastern North Sea. An increase in bed shear stress of this magnitude would have resulted in a large reduction in water clarity. Wave-driven processes are rarely included in projections of climate change impacts on marine ecosystems, but our analysis indicates that this should be reconsidered for shelf sea regions.</dc:description>
|
||||
|
||||
<dc:language>en</dc:language>
|
||||
|
||||
<rioxxterms:publisher>
|
||||
<rioxxterms:name>European Geosciences Union</rioxxterms:name>
|
||||
<rioxxterms:id>https://isni.org/isni/0000000110927289</rioxxterms:id>
|
||||
</rioxxterms:publisher>
|
||||
|
||||
<dc:source>1812-0792</dc:source>
|
||||
|
||||
<dc:title>Increasing turbidity in the North Sea during the 20th century due to changing wave climate</dc:title>
|
||||
|
||||
<dcterms:dateAccepted>2019-10-02</dcterms:dateAccepted>
|
||||
|
||||
<rioxxterms:creator
|
||||
first-named-author="true">
|
||||
<rioxxterms:name>Wilson, Robert J.</rioxxterms:name>
|
||||
<rioxxterms:id>https://orcid.org/0000-0002-0592-366X</rioxxterms:id>
|
||||
</rioxxterms:creator>
|
||||
|
||||
<rioxxterms:creator>
|
||||
<rioxxterms:name>Heath, Michael R.</rioxxterms:name>
|
||||
<rioxxterms:id>https://orcid.org/0000-0001-6602-3107</rioxxterms:id>
|
||||
<rioxxterms:id> https://viaf.org/viaf/15147423189944882613</rioxxterms:id>
|
||||
</rioxxterms:creator>
|
||||
|
||||
<rioxxterms:publication_date>2019-12-09</rioxxterms:publication_date>
|
||||
|
||||
<rioxxterms:record_public_release_date>2019-10-15</rioxxterms:record_public_release_date>
|
||||
|
||||
<dc:type>https://purl.org/coar/resource_type/c_2df8fbb1</dc:type>
|
||||
|
||||
<rioxxterms:grant
|
||||
funder_name="Australian Research Council"
|
||||
funder_id="https://ror.org/05mmh0f86">
|
||||
DP190101507
|
||||
</rioxxterms:grant>
|
||||
|
||||
<rioxxterms:grant
|
||||
funder_name="John Templeton Foundation"
|
||||
funder_id="https://ror.org/035tnyy05">
|
||||
61387
|
||||
</rioxxterms:grant>
|
||||
|
||||
<dc:relation
|
||||
rel="item"
|
||||
type="application/pdf"
|
||||
coar_type="https://purl.org/coar/resource_type/c_6501"
|
||||
coar_version="https://purl.org/coar/version/c_ab4af688f83e57aa"
|
||||
deposit_date="2019-12-11"
|
||||
resource_exposed_date="2019-12-11"
|
||||
access_rights_="https://purl.org/coar/access_right/c_abf2"
|
||||
license_ref="https://creativecommons.org/licenses/by-nc-nd/4.0/">
|
||||
https://strathprints.strath.ac.uk/70117/7/Wilson_Heath_OS2019_Increasing_turbidity_in_the_North_Sea_during_the_20th_century.pdf
|
||||
</dc:relation>
|
||||
|
||||
<!-- Other expressions (or 'instances') - publisher version -->
|
||||
<rioxxterms:ext_relation
|
||||
rel="cite-as"
|
||||
coar_type="https://purl.org/coar/resource_type/c_6501"
|
||||
coar_version="https://purl.org/coar/version/c_970fb48d4fbd8a85">
|
||||
https://doi.org/10.1007/s11229-020-02724-x
|
||||
</rioxxterms:ext_relation>
|
||||
|
||||
<!-- related dataset -->
|
||||
<rioxxterms:ext_relation
|
||||
rel="cite-as"
|
||||
coar_type="https://purl.org/coar/resource_type/c_ddb1">
|
||||
https://doi.org/10.15129/5d28213e-8f9f-402a-b550-fc588518cb8b
|
||||
</rioxxterms:ext_relation >
|
||||
|
||||
<!-- related software -->
|
||||
<rioxxterms:ext_relation
|
||||
rel="cite-as"
|
||||
coar_type="https://purl.org/coar/resource_type/QH80-2R4E">
|
||||
https://doi.org/10.5281/zenodo.3478185
|
||||
</rioxxterms:ext_relation>
|
||||
</rioxx>
|
92
dspace-oai/src/test/resources/rioxx-test-valid.xml
Normal file
92
dspace-oai/src/test/resources/rioxx-test-valid.xml
Normal file
@@ -0,0 +1,92 @@
|
||||
<?xml version='1.0' encoding='UTF-8'?>
|
||||
<!--
|
||||
|
||||
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/
|
||||
|
||||
-->
|
||||
<rioxx xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.rioxx.net/schema/v3.0/rioxx/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:dcterms="http://purl.org/dc/terms/" xmlns:rioxxterms="http://docs.rioxx.net/schema/v3.0/rioxxterms/">
|
||||
<dc:description>Data on Secchi disc depth (the depth at which a standard white disc lowered into the water just becomes invisible to a surface observer) show that water clarity in the North Sea declined during the 20th century, with likely consequences for marine primary production. However, the causes of this trend remain unknown. Here we analyse the hypothesis that changes in the North Sea's wave climate were largely responsible by causing an increase in the concentrations of suspended particulate matter (SPM) in the water column through the resuspension of seabed sediments. First, we analysed the broad-scale statistical relationships between SPM and bed shear stress due to waves and tides. We used hindcasts of wave and current data to construct a space–time dataset of bed shear stress between 1997 and 2017 across the northwest European Continental Shelf and compared the results with satellite-derived SPM concentrations. Bed shear stress was found to drive most of the inter-annual variation in SPM in the hydrographically mixed waters of the central and southern North Sea. We then used a long-term wave reanalysis to construct a time series of bed shear stress from 1900 to 2010. This shows that bed shear stress increased significantly across much of the shelf during this period, with increases of over 20 % in the southeastern North Sea. An increase in bed shear stress of this magnitude would have resulted in a large reduction in water clarity. Wave-driven processes are rarely included in projections of climate change impacts on marine ecosystems, but our analysis indicates that this should be reconsidered for shelf sea regions.</dc:description>
|
||||
|
||||
<dc:language>en</dc:language>
|
||||
|
||||
<rioxxterms:publisher>
|
||||
<rioxxterms:name>European Geosciences Union</rioxxterms:name>
|
||||
<rioxxterms:id>https://isni.org/isni/0000000110927289</rioxxterms:id>
|
||||
</rioxxterms:publisher>
|
||||
|
||||
<dc:source>1812-0792</dc:source>
|
||||
|
||||
<dc:title>Increasing turbidity in the North Sea during the 20th century due to changing wave climate</dc:title>
|
||||
|
||||
<dcterms:dateAccepted>2019-10-02</dcterms:dateAccepted>
|
||||
|
||||
<rioxxterms:creator
|
||||
first-named-author="true">
|
||||
<rioxxterms:name>Wilson, Robert J.</rioxxterms:name>
|
||||
<rioxxterms:id>https://orcid.org/0000-0002-0592-366X</rioxxterms:id>
|
||||
</rioxxterms:creator>
|
||||
|
||||
<rioxxterms:creator>
|
||||
<rioxxterms:name>Heath, Michael R.</rioxxterms:name>
|
||||
<rioxxterms:id>https://orcid.org/0000-0001-6602-3107</rioxxterms:id>
|
||||
<rioxxterms:id> https://viaf.org/viaf/15147423189944882613</rioxxterms:id>
|
||||
</rioxxterms:creator>
|
||||
|
||||
<rioxxterms:publication_date>2019-12-09</rioxxterms:publication_date>
|
||||
|
||||
<rioxxterms:record_public_release_date>2019-10-15</rioxxterms:record_public_release_date>
|
||||
|
||||
<dc:type>https://purl.org/coar/resource_type/c_2df8fbb1</dc:type>
|
||||
|
||||
<rioxxterms:grant
|
||||
funder_name="Australian Research Council"
|
||||
funder_id="https://ror.org/05mmh0f86">
|
||||
DP190101507
|
||||
</rioxxterms:grant>
|
||||
|
||||
<rioxxterms:grant
|
||||
funder_name="John Templeton Foundation"
|
||||
funder_id="https://ror.org/035tnyy05">
|
||||
61387
|
||||
</rioxxterms:grant>
|
||||
|
||||
<dc:identifier>https://strathprints.strath.ac.uk/70117/</dc:identifier>
|
||||
|
||||
<dc:relation
|
||||
rel="item"
|
||||
type="application/pdf"
|
||||
coar_type="https://purl.org/coar/resource_type/c_6501"
|
||||
coar_version="https://purl.org/coar/version/c_ab4af688f83e57aa"
|
||||
deposit_date="2019-12-11"
|
||||
resource_exposed_date="2019-12-11"
|
||||
access_rights_="https://purl.org/coar/access_right/c_abf2"
|
||||
license_ref="https://creativecommons.org/licenses/by-nc-nd/4.0/">
|
||||
https://strathprints.strath.ac.uk/70117/7/Wilson_Heath_OS2019_Increasing_turbidity_in_the_North_Sea_during_the_20th_century.pdf
|
||||
</dc:relation>
|
||||
|
||||
<!-- Other expressions (or 'instances') - publisher version -->
|
||||
<rioxxterms:ext_relation
|
||||
rel="cite-as"
|
||||
coar_type="https://purl.org/coar/resource_type/c_6501"
|
||||
coar_version="https://purl.org/coar/version/c_970fb48d4fbd8a85">
|
||||
https://doi.org/10.1007/s11229-020-02724-x
|
||||
</rioxxterms:ext_relation>
|
||||
|
||||
<!-- related dataset -->
|
||||
<rioxxterms:ext_relation
|
||||
rel="cite-as"
|
||||
coar_type="https://purl.org/coar/resource_type/c_ddb1">
|
||||
https://doi.org/10.15129/5d28213e-8f9f-402a-b550-fc588518cb8b
|
||||
</rioxxterms:ext_relation >
|
||||
|
||||
<!-- related software -->
|
||||
<rioxxterms:ext_relation
|
||||
rel="cite-as"
|
||||
coar_type="https://purl.org/coar/resource_type/QH80-2R4E">
|
||||
https://doi.org/10.5281/zenodo.3478185
|
||||
</rioxxterms:ext_relation>
|
||||
</rioxx>
|
217
dspace-oai/src/test/resources/xoai-rioxx-test.xml
Normal file
217
dspace-oai/src/test/resources/xoai-rioxx-test.xml
Normal file
@@ -0,0 +1,217 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
|
||||
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/
|
||||
|
||||
-->
|
||||
<metadata
|
||||
xmlns="http://www.lyncode.com/xoai"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.lyncode.com/xoai http://www.lyncode.com/xsd/xoai.xsd">
|
||||
<element name="dspace">
|
||||
<element name="entity">
|
||||
<element name="type">
|
||||
<element name="none">
|
||||
<field name="value">Publication</field>
|
||||
</element>
|
||||
</element>
|
||||
</element>
|
||||
</element>
|
||||
<element name="dcterms">
|
||||
<element name="dateAccepted">
|
||||
<element name="none">
|
||||
<field name="value">2023-11-07</field>
|
||||
</element>
|
||||
</element>
|
||||
</element>
|
||||
<element name="dc">
|
||||
<element name="contributor">
|
||||
<element name="author">
|
||||
<element name="none">
|
||||
<field name="value">Tsigaridis, Konstantinos G.</field>
|
||||
<field name="authority">virtual::44</field>
|
||||
<field name="confidence">-1</field>
|
||||
<field name="value">Wang, Rui</field>
|
||||
<field name="authority">virtual::46</field>
|
||||
<field name="confidence">-1</field>
|
||||
<field name="value">Ellefson, Michelle R.</field>
|
||||
<field name="authority">virtual::47</field>
|
||||
<field name="confidence">-1</field>
|
||||
</element>
|
||||
</element>
|
||||
</element>
|
||||
<element name="date">
|
||||
<element name="accessioned">
|
||||
<element name="none">
|
||||
<field name="value">2023-11-07T11:34:10Z</field>
|
||||
</element>
|
||||
</element>
|
||||
<element name="available">
|
||||
<element name="none">
|
||||
<field name="value">2023-11-07T11:34:10Z</field>
|
||||
</element>
|
||||
</element>
|
||||
<element name="issued">
|
||||
<element name="none">
|
||||
<field name="value">2022-11-30</field>
|
||||
</element>
|
||||
</element>
|
||||
</element>
|
||||
<element name="identifier">
|
||||
<element name="uri">
|
||||
<element name="none">
|
||||
<field name="value">https://example.org/handle/1811/160</field>
|
||||
</element>
|
||||
</element>
|
||||
</element>
|
||||
<element name="language">
|
||||
<element name="iso">
|
||||
<element name="none">
|
||||
<field name="value">eng</field>
|
||||
</element>
|
||||
</element>
|
||||
</element>
|
||||
<element name="title">
|
||||
<element name="none">
|
||||
<field name="value">The Intercorrelation Between Executive Function, Physics Problem Solving, Mathematical, and Matrix Reasoning Skills: Reflections from a Small-Scale Experiment</field>
|
||||
</element>
|
||||
</element>
|
||||
<element name="type">
|
||||
<element name="none">
|
||||
<field name="value">Article</field>
|
||||
</element>
|
||||
</element>
|
||||
</element>
|
||||
<element name="relation">
|
||||
<element name="isAuthorOfPublication">
|
||||
<element name="none">
|
||||
<field name="value">a57363fa-f82e-4684-bd76-f7bc1e893603</field>
|
||||
<field name="authority">virtual::44</field>
|
||||
<field name="confidence">-1</field>
|
||||
<field name="value">e00b3d0d-65e2-4c30-825d-1a4839845790</field>
|
||||
<field name="authority">virtual::46</field>
|
||||
<field name="confidence">-1</field>
|
||||
<field name="value">bdd38a03-206d-4f9b-bafb-70e060ad176f</field>
|
||||
<field name="authority">virtual::47</field>
|
||||
<field name="confidence">-1</field>
|
||||
</element>
|
||||
<element name="latestForDiscovery">
|
||||
<element name="none">
|
||||
<field name="value">a57363fa-f82e-4684-bd76-f7bc1e893603</field>
|
||||
<field name="authority">virtual::44</field>
|
||||
<field name="confidence">-1</field>
|
||||
<field name="value">e00b3d0d-65e2-4c30-825d-1a4839845790</field>
|
||||
<field name="authority">virtual::46</field>
|
||||
<field name="confidence">-1</field>
|
||||
<field name="value">bdd38a03-206d-4f9b-bafb-70e060ad176f</field>
|
||||
<field name="authority">virtual::47</field>
|
||||
<field name="confidence">-1</field>
|
||||
</element>
|
||||
</element>
|
||||
</element>
|
||||
<element name="isJournalOfPublication">
|
||||
<element name="none">
|
||||
<field name="value">05a400b1-ff0b-4e40-80cd-a7d1b712ace2</field>
|
||||
<field name="authority">virtual::71</field>
|
||||
<field name="confidence">-1</field>
|
||||
</element>
|
||||
</element>
|
||||
<element name="isJournalIssueOfPublication">
|
||||
<element name="none">
|
||||
<field name="value">7524a0cf-3ea2-40c7-a265-d583425ed4d7</field>
|
||||
<field name="authority">virtual::71</field>
|
||||
<field name="confidence">-1</field>
|
||||
</element>
|
||||
<element name="latestForDiscovery">
|
||||
<element name="none">
|
||||
<field name="value">7524a0cf-3ea2-40c7-a265-d583425ed4d7</field>
|
||||
<field name="authority">virtual::71</field>
|
||||
<field name="confidence">-1</field>
|
||||
</element>
|
||||
</element>
|
||||
</element>
|
||||
</element>
|
||||
<element name="person">
|
||||
<element name="identifier">
|
||||
<element name="orcid">
|
||||
<element name="none">
|
||||
<field name="value">0000-0003-0407-9767</field>
|
||||
<field name="authority">virtual::47</field>
|
||||
<field name="confidence">-1</field>
|
||||
</element>
|
||||
</element>
|
||||
</element>
|
||||
</element>
|
||||
<element name="creativeworkseries">
|
||||
<element name="issn">
|
||||
<element name="none">
|
||||
<field name="value">2634-9876</field>
|
||||
<field name="authority">virtual::71</field>
|
||||
<field name="confidence">-1</field>
|
||||
</element>
|
||||
</element>
|
||||
</element>
|
||||
<element name="bundles">
|
||||
<element name="bundle">
|
||||
<field name="name">ORIGINAL</field>
|
||||
<element name="bitstreams">
|
||||
<element name="bitstream">
|
||||
<field name="name">Tsigaridis et al., 2022.pdf</field>
|
||||
<field name="format">application/pdf</field>
|
||||
<field name="size">1554917</field>
|
||||
<field name="url">https://example.org/bitstreams/9121e795-0af3-4ff3-be2a-4b28418fb269/download</field>
|
||||
<field name="checksum">42d8cd076931e43e02d0af70a36d704e</field>
|
||||
<field name="checksumAlgorithm">MD5</field>
|
||||
<field name="sid">1</field>
|
||||
<field name="primary">true</field>
|
||||
<element name="resourcePolicies">
|
||||
<element name="resourcePolicy">
|
||||
<field name="group">Anonymous</field>
|
||||
<field name="user">Anonymous</field>
|
||||
<field name="action">READ</field>
|
||||
</element>
|
||||
</element>
|
||||
</element>
|
||||
</element>
|
||||
</element>
|
||||
<element name="bundle">
|
||||
<field name="name">THUMBNAIL</field>
|
||||
<element name="bitstreams">
|
||||
<element name="bitstream">
|
||||
<field name="name">cerj_volume_9_thumbnail.jpg</field>
|
||||
<field name="format">image/jpeg</field>
|
||||
<field name="size">14513</field>
|
||||
<field name="url">https://example.org/bitstreams/16245937-10bb-46db-9817-683a5ebd8d63/download</field>
|
||||
<field name="checksum">8c39d691daa8e5f9d668668db7910cd6</field>
|
||||
<field name="checksumAlgorithm">MD5</field>
|
||||
<field name="sid">2</field>
|
||||
<field name="primary">false</field>
|
||||
<element name="resourcePolicies">
|
||||
<element name="resourcePolicy">
|
||||
<field name="group">Anonymous</field>
|
||||
<field name="user">Anonymous</field>
|
||||
<field name="action">READ</field>
|
||||
</element>
|
||||
</element>
|
||||
</element>
|
||||
</element>
|
||||
</element>
|
||||
</element>
|
||||
<element name="others">
|
||||
<field name="handle">1811/160</field>
|
||||
<field name="identifier">oai:example.org:1811/160</field>
|
||||
<field name="lastModifyDate">2023-12-13 13:07:56.51</field>
|
||||
<element name="access-status">
|
||||
<field name="value">open.access</field>
|
||||
</element>
|
||||
</element>
|
||||
<element name="repository">
|
||||
<field name="url">https://example.org</field>
|
||||
<field name="name">Diamond DSpace (dev)</field>
|
||||
<field name="mail">support@example.org</field>
|
||||
</element>
|
||||
<element name="license"/>
|
||||
</metadata>
|
@@ -1,194 +0,0 @@
|
||||
#DSpace REST API (Jersey) - DEPRECATED
|
||||
|
||||
A RESTful web services API for DSpace, built using JAX-RS1 JERSEY.
|
||||
|
||||
_This REST API has been deprecated and will be removed in v8. Please use the Server API (/server) webapp instead._
|
||||
|
||||
##Getting Started
|
||||
This REST API is integrated directly into the DSpace codebase.
|
||||
|
||||
* Rebuild as usual: mvn + ant
|
||||
* Deploy the webapp (i.e to Tomcat)
|
||||
* ```<Context path="/rest" docBase="/dspace/webapps/rest" />```
|
||||
|
||||
|
||||
REST API can do all CRUD (create, read, update, delete) operations over communities, collections, items, bitstream and bitstream policies. Without logging into the REST API, you have read access as an anonymous user (member of the Anonymous group). If you want to make changes in DSpace using the REST API, you must log into the API using the "login" endpoint and then use the returned token in request header of your subsequent API calls.
|
||||
|
||||
##Endpoints
|
||||
|
||||
| Resource |CREATE|READ list|READ single|Edit|Delete|Search|
|
||||
| ------------- |------|:-------:|-----------|----|------|------|
|
||||
| /communities | Y | Y | Y | Y | Y | |
|
||||
| /collections | Y | Y | Y | Y | Y | Y |
|
||||
| /items | Y | Y | Y | Y | Y | Y |
|
||||
| /bitstreams | Y | Y | Y | Y | Y | ||
|
||||
|
||||
Search in collections is possible only by name and search in items only by metadata field.
|
||||
|
||||
###Index
|
||||
Get information on how to use the API
|
||||
- GET http://localhost:8080
|
||||
|
||||
Test whether the REST API is running and available
|
||||
- GET http://localhost:8080/rest/test
|
||||
|
||||
Log into REST API
|
||||
- POST http://localhost:8080/rest/login
|
||||
|
||||
Logout from REST API
|
||||
- POST http://localhost:8080/rest/logout
|
||||
|
||||
Get status of REST API and the logged-in user
|
||||
- GET http://localhost:8080/rest/status
|
||||
|
||||
|
||||
###Communities
|
||||
View the list of top-level communities
|
||||
- GET http://localhost:8080/rest/communities/top-communities
|
||||
|
||||
View the list of all communities
|
||||
- GET http://localhost:8080/rest/communities[?expand={collections,parentCommunity,subCommunities,logo,all}]
|
||||
|
||||
View a specific community
|
||||
- GET http://localhost:8080/rest/communities/:ID[?expand={collections,parentCommunity,subCommunities,logo,all}]
|
||||
|
||||
View the list of subcollections in community
|
||||
- GET http://localhost:8080/rest/communities/:ID/collections[?expand={items,parentCommunityList,license,logo,all}]
|
||||
|
||||
View the list of subcommunities in community
|
||||
- GET http://localhost:8080/rest/communities/:ID/communities[?expand={collections,parentCommunity,subCommunities,logo,all}]
|
||||
|
||||
Create new top-level community
|
||||
- POST http://localhost:8080/rest/communities
|
||||
|
||||
Create new subcollection in community
|
||||
- POST http://localhost:8080/rest/communities/:ID/collections
|
||||
|
||||
Create new subcommunity in community
|
||||
- POST http://localhost:8080/rest/communities/:ID/communities
|
||||
|
||||
Update community
|
||||
- PUT http://localhost:8080/rest/communities/:ID
|
||||
|
||||
Delete community
|
||||
- DELETE http://localhost:8080/rest/communities/:ID
|
||||
|
||||
Delete subcollection in community
|
||||
- DELETE http://localhost:8080/rest/communities/:ID/collections/:ID
|
||||
|
||||
Delete subcommunity in community
|
||||
- DELETE http://localhost:8080/rest/communities/:ID/communities/:ID
|
||||
|
||||
|
||||
###Collections
|
||||
View the list of collections
|
||||
- GET http://localhost:8080/rest/collections[?expand={items,parentCommunityList,license,logo,all}]
|
||||
|
||||
View a specific collection
|
||||
- GET http://localhost:8080/rest/collections/:ID[?expand={items,parentCommunityList,license,logo,all}]
|
||||
|
||||
View items in collection
|
||||
- GET http://localhost:8080/rest/collections/:ID/items[?expand={metadata,parentCollection,parentcollectionList,parentCommunityList,bitstreams,all}]
|
||||
|
||||
Create item in collection
|
||||
- POST http://localhost:8080/rest/collections/:ID/items
|
||||
|
||||
Find collection by name
|
||||
- POST http://localhost:8080/rest/collections/find-collection
|
||||
|
||||
Update collection
|
||||
- PUT http://localhost:8080/rest/collections/:ID
|
||||
|
||||
Delete collection
|
||||
- DELETE http://localhost:8080/rest/collections/:ID
|
||||
|
||||
Delete item in collection
|
||||
- DELETE http://localhost:8080/rest/collections/:ID/items/:ID
|
||||
|
||||
|
||||
###Items
|
||||
View the list of items
|
||||
- GET http://localhost:8080/rest/items[?expand={metadata,parentCollection,parentcollectionList,parentCommunityList,bitstreams,all}]
|
||||
|
||||
View speciific item
|
||||
- GET http://localhost:8080/rest/items/:ID[?expand={metadata,parentCollection,parentcollectionList,parentCommunityList,bitstreams,all}]
|
||||
|
||||
View an Item and view its bitstreams
|
||||
- GET http://localhost:8080/rest/items/:ID/bitstreams[?expand={parent,policies,all}]
|
||||
|
||||
View an Item, and view its metadata
|
||||
- GET http://localhost:8080/rest/items/:ID/metadata
|
||||
|
||||
Find item by metadata
|
||||
- POST http://localhost:8080/rest/items/find-by-metadata-field
|
||||
|
||||
Add metadata to item
|
||||
- POST http://localhost:8080/rest/items/:ID/metadata
|
||||
|
||||
Create bitstream in item
|
||||
- POST http://localhost:8080/rest/items/:ID/bitstreams
|
||||
|
||||
Update metadata in item
|
||||
- PUT http://localhost:8080/rest/items/:ID/metadata
|
||||
|
||||
Delete item
|
||||
- DELETE http://localhost:8080/rest/items/:ID
|
||||
|
||||
Delete all metadata in item
|
||||
- DELETE http://localhost:8080/rest/items/:ID/metadata
|
||||
|
||||
Delete bitstream in item
|
||||
- DELETE http://localhost:8080/rest/items/:ID/bitstreams/:ID
|
||||
|
||||
|
||||
###Bitstreams
|
||||
View the list of bitstreams
|
||||
- GET http://localhost:8080/rest/bitstreams[?expand={parent,policies,all}]
|
||||
|
||||
View information about a bitstream
|
||||
- GET http://localhost:8080/rest/bitstreams/:ID[?expand={parent,policies,all}]
|
||||
|
||||
View/Download a specific Bitstream
|
||||
- GET http://localhost:8080/rest/bitstreams/:ID/retrieve
|
||||
|
||||
View the list of policies of bitstream
|
||||
- GET http://localhost:8080/rest/bitstreams/:ID/policy
|
||||
|
||||
Add policy to bitstream
|
||||
- POST http://localhost:8080/rest/bitstreams/:ID/policy
|
||||
|
||||
Update bitstream
|
||||
- PUT http://localhost:8080/rest/bitstreams/:ID
|
||||
|
||||
Update data of bitstream
|
||||
- PUT http://localhost:8080/rest/bitstreams/:ID/data
|
||||
|
||||
Delete bitstream
|
||||
- DELETE http://localhost:8080/rest/bitstreams/:ID
|
||||
|
||||
Delete policy of bitstream
|
||||
- DELETE http://localhost:8080/rest/bitstreams/:ID/policy/:ID
|
||||
|
||||
|
||||
####Statistics
|
||||
Recording view events of items and download events of bitstreams (set stats = true in rest.cfg to enable recording of events)
|
||||
http://localhost:8080/rest/items/:ID?userIP=ip&userAgent=userAgent&xforwardedfor=xforwardedfor
|
||||
If no parameters are given, the details of the HTTP request sender are used in statistics.
|
||||
This enables tools like proxies to supply the details of their user rather than themselves.
|
||||
|
||||
|
||||
###Handles
|
||||
Lookup a DSpaceObject by its Handle, this produces the name/ID that you look up in /bitstreams, /items, /collections, /communities
|
||||
- http://localhost:8080/rest/handle/{prefix}/{suffix}
|
||||
|
||||
##Expand
|
||||
There is an ?expand= query parameter for more expensive operations. You can add it at the end of the request URL.
|
||||
It is optional, all, some or none. The response will usually indicate what the available "expand" options are.
|
||||
|
||||
##HTTP Responses
|
||||
* 200 OK - The requested object/objects exists
|
||||
* 401 Unauthorized - The anonymous user does not have READ access to that object
|
||||
* 404 Not Found - The specified object doesn't exist
|
||||
* 405 Method Not Allowed - Wrong request method (GET,POST,PUT,DELETE) or wrong data format (JSON/XML).
|
||||
* 415 Unsupported Media Type - Missing "Content-Type: application/json" or "Content-Type: application/xml" request header
|
||||
* 500 Server Error - Likely a SQLException, IOException, more details in the logs.
|
@@ -1,202 +0,0 @@
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>org.dspace</groupId>
|
||||
<artifactId>dspace-rest</artifactId>
|
||||
<packaging>war</packaging>
|
||||
<version>8.0-SNAPSHOT</version>
|
||||
<name>DSpace (Deprecated) REST Webapp</name>
|
||||
<description>DSpace RESTful Web Services API. NOTE: this REST API is DEPRECATED.
|
||||
Please consider using the REST API in the dspace-server-webapp instead!</description>
|
||||
<url>http://demo.dspace.org</url>
|
||||
|
||||
<parent>
|
||||
<groupId>org.dspace</groupId>
|
||||
<artifactId>dspace-parent</artifactId>
|
||||
<version>8.0-SNAPSHOT</version>
|
||||
<relativePath>..</relativePath>
|
||||
</parent>
|
||||
|
||||
<properties>
|
||||
<!-- This is the path to the root [dspace-src] directory. -->
|
||||
<root.basedir>${basedir}/..</root.basedir>
|
||||
</properties>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-war-plugin</artifactId>
|
||||
<configuration>
|
||||
<attachClasses>true</attachClasses>
|
||||
<!-- Filter the web.xml (needed for IDE compatibility/debugging) -->
|
||||
<filteringDeploymentDescriptors>true</filteringDeploymentDescriptors>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>com.mycila</groupId>
|
||||
<artifactId>license-maven-plugin</artifactId>
|
||||
<configuration>
|
||||
<!--Exclude license check for third-party files which don't need it-->
|
||||
<excludes>
|
||||
<exclude>**/static/reports/spin.js</exclude>
|
||||
<exclude>**/static/reports/README.md</exclude>
|
||||
<exclude>**/*.xsd</exclude>
|
||||
</excludes>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
<dependencies>
|
||||
<!-- Jersey, for RESTful web services -->
|
||||
<dependency>
|
||||
<groupId>org.glassfish.jersey.core</groupId>
|
||||
<artifactId>jersey-server</artifactId>
|
||||
<version>${jersey.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.glassfish.jersey.containers</groupId>
|
||||
<artifactId>jersey-container-servlet</artifactId>
|
||||
<version>${jersey.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.glassfish.jersey.media</groupId>
|
||||
<artifactId>jersey-media-json-jackson</artifactId>
|
||||
<version>${jersey.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.glassfish.jersey.media</groupId>
|
||||
<artifactId>jersey-media-jaxb</artifactId>
|
||||
<version>${jersey.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Spring dependencies -->
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-core</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-context</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-web</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Jersey + Spring -->
|
||||
<dependency>
|
||||
<groupId>org.glassfish.jersey.ext</groupId>
|
||||
<artifactId>jersey-spring5</artifactId>
|
||||
<version>${jersey.version}</version>
|
||||
<exclusions>
|
||||
<!-- We provide our own version of Spring framework dependencies -->
|
||||
<exclusion>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-core</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-web</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-beans</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-context</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-aop</artifactId>
|
||||
</exclusion>
|
||||
<!-- Newer version provided by jersey-server above -->
|
||||
<exclusion>
|
||||
<groupId>jakarta.annotation</groupId>
|
||||
<artifactId>jakarta.annotation-api</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.security</groupId>
|
||||
<artifactId>spring-security-core</artifactId>
|
||||
<version>${spring-security.version}</version>
|
||||
<exclusions>
|
||||
<!-- Latest version will be provided by spring-context-->
|
||||
<exclusion>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-expression</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.security</groupId>
|
||||
<artifactId>spring-security-web</artifactId>
|
||||
<version>${spring-security.version}</version>
|
||||
<exclusions>
|
||||
<!-- Latest version will be provided by spring-context-->
|
||||
<exclusion>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-expression</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.security</groupId>
|
||||
<artifactId>spring-security-config</artifactId>
|
||||
<version>${spring-security.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.dspace</groupId>
|
||||
<artifactId>dspace-api</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Connecting to DSpace datasource sets a dependency on Postgres DB-->
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-dbcp2</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.postgresql</groupId>
|
||||
<artifactId>postgresql</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>javax.servlet</groupId>
|
||||
<artifactId>javax.servlet-api</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.atteo</groupId>
|
||||
<artifactId>evo-inflector</artifactId>
|
||||
<version>1.2.1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.logging.log4j</groupId>
|
||||
<artifactId>log4j-api</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.logging.log4j</groupId>
|
||||
<artifactId>log4j-core</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.logging.log4j</groupId>
|
||||
<artifactId>log4j-web</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.dspace</groupId>
|
||||
<artifactId>dspace-services</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
@@ -1,783 +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.rest;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.URLConnection;
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.ws.rs.Consumes;
|
||||
import javax.ws.rs.DELETE;
|
||||
import javax.ws.rs.DefaultValue;
|
||||
import javax.ws.rs.GET;
|
||||
import javax.ws.rs.POST;
|
||||
import javax.ws.rs.PUT;
|
||||
import javax.ws.rs.Path;
|
||||
import javax.ws.rs.PathParam;
|
||||
import javax.ws.rs.Produces;
|
||||
import javax.ws.rs.QueryParam;
|
||||
import javax.ws.rs.WebApplicationException;
|
||||
import javax.ws.rs.core.Context;
|
||||
import javax.ws.rs.core.HttpHeaders;
|
||||
import javax.ws.rs.core.MediaType;
|
||||
import javax.ws.rs.core.Response;
|
||||
import javax.ws.rs.core.Response.Status;
|
||||
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.dspace.authorize.AuthorizeException;
|
||||
import org.dspace.authorize.factory.AuthorizeServiceFactory;
|
||||
import org.dspace.authorize.service.AuthorizeService;
|
||||
import org.dspace.authorize.service.ResourcePolicyService;
|
||||
import org.dspace.content.BitstreamFormat;
|
||||
import org.dspace.content.factory.ContentServiceFactory;
|
||||
import org.dspace.content.service.BitstreamFormatService;
|
||||
import org.dspace.content.service.BitstreamService;
|
||||
import org.dspace.content.service.BundleService;
|
||||
import org.dspace.eperson.factory.EPersonServiceFactory;
|
||||
import org.dspace.eperson.service.GroupService;
|
||||
import org.dspace.rest.common.Bitstream;
|
||||
import org.dspace.rest.common.ResourcePolicy;
|
||||
import org.dspace.rest.exceptions.ContextException;
|
||||
import org.dspace.storage.bitstore.factory.StorageServiceFactory;
|
||||
import org.dspace.storage.bitstore.service.BitstreamStorageService;
|
||||
import org.dspace.usage.UsageEvent;
|
||||
|
||||
/**
|
||||
* @author Rostislav Novak (Computing and Information Centre, CTU in Prague)
|
||||
*/
|
||||
// Every DSpace class used without namespace is from package
|
||||
// org.dspace.rest.common.*. Otherwise namespace is defined.
|
||||
@Path("/bitstreams")
|
||||
public class BitstreamResource extends Resource {
|
||||
protected BitstreamService bitstreamService = ContentServiceFactory.getInstance().getBitstreamService();
|
||||
protected BundleService bundleService = ContentServiceFactory.getInstance().getBundleService();
|
||||
protected AuthorizeService authorizeService = AuthorizeServiceFactory.getInstance().getAuthorizeService();
|
||||
protected BitstreamFormatService bitstreamFormatService = ContentServiceFactory.getInstance()
|
||||
.getBitstreamFormatService();
|
||||
protected BitstreamStorageService bitstreamStorageService = StorageServiceFactory.getInstance()
|
||||
.getBitstreamStorageService();
|
||||
protected ResourcePolicyService resourcePolicyService = AuthorizeServiceFactory.getInstance()
|
||||
.getResourcePolicyService();
|
||||
protected GroupService groupService = EPersonServiceFactory.getInstance().getGroupService();
|
||||
|
||||
private static Logger log = org.apache.logging.log4j.LogManager.getLogger(BitstreamResource.class);
|
||||
|
||||
/**
|
||||
* Return bitstream properties without file data. It can throw
|
||||
* WebApplicationException with three response codes. Response code
|
||||
* NOT_FOUND(404) or UNAUTHORIZED(401) or INTERNAL_SERVER_ERROR(500). Bad
|
||||
* request is when the bitstream id does not exist. UNAUTHORIZED if the user
|
||||
* logged into the DSpace context does not have the permission to access the
|
||||
* bitstream. Server error when something went wrong.
|
||||
*
|
||||
* @param bitstreamId Id of bitstream in DSpace.
|
||||
* @param expand This string defines which additional optional fields will be added
|
||||
* to bitstream response. Individual options are separated by commas without
|
||||
* spaces. The options are: "all", "parent".
|
||||
* @param user_ip User's IP address.
|
||||
* @param user_agent User agent string (specifies browser used and its version).
|
||||
* @param xforwardedfor When accessed via a reverse proxy, the application sees the proxy's IP as the
|
||||
* source of the request. The proxy may be configured to add the
|
||||
* "X-Forwarded-For" HTTP header containing the original IP of the client
|
||||
* so that the reverse-proxied application can get the client's IP.
|
||||
* @param headers If you want to access the item as the user logged into the
|
||||
* context. The value of the "rest-dspace-token" header must be set
|
||||
* to the token received from the login method response.
|
||||
* @param request Servlet's HTTP request object.
|
||||
* @return If user is allowed to read bitstream, it returns instance of
|
||||
* bitstream. Otherwise, it throws WebApplicationException with
|
||||
* response code UNAUTHORIZED.
|
||||
* @throws WebApplicationException It can happen on: Bad request, unauthorized, SQL exception
|
||||
* and context exception(could not create context).
|
||||
*/
|
||||
@GET
|
||||
@Path("/{bitstream_id}")
|
||||
@Produces( {MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
|
||||
public Bitstream getBitstream(@PathParam("bitstream_id") String bitstreamId, @QueryParam("expand") String expand,
|
||||
@QueryParam("userIP") String user_ip, @QueryParam("userAgent") String user_agent,
|
||||
@QueryParam("xforwardedfor") String xforwardedfor, @Context HttpHeaders headers,
|
||||
@Context HttpServletRequest request)
|
||||
throws WebApplicationException {
|
||||
|
||||
log.info("Reading bitstream(id=" + bitstreamId + ") metadata.");
|
||||
org.dspace.core.Context context = null;
|
||||
Bitstream bitstream = null;
|
||||
|
||||
try {
|
||||
context = createContext();
|
||||
org.dspace.content.Bitstream dspaceBitstream = findBitstream(context, bitstreamId,
|
||||
org.dspace.core.Constants.READ);
|
||||
|
||||
writeStats(dspaceBitstream, UsageEvent.Action.VIEW, user_ip, user_agent, xforwardedfor, headers,
|
||||
request, context);
|
||||
|
||||
bitstream = new Bitstream(dspaceBitstream, servletContext, expand, context);
|
||||
context.complete();
|
||||
log.trace("Bitstream(id=" + bitstreamId + ") was successfully read.");
|
||||
|
||||
} catch (SQLException e) {
|
||||
processException(
|
||||
"Someting went wrong while reading bitstream(id=" + bitstreamId + ") from database! Message: " + e,
|
||||
context);
|
||||
} catch (ContextException e) {
|
||||
processException(
|
||||
"Someting went wrong while reading bitstream(id=" + bitstreamId + "), ContextException. Message: "
|
||||
+ e.getMessage(), context);
|
||||
} finally {
|
||||
processFinally(context);
|
||||
}
|
||||
|
||||
return bitstream;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return all bitstream resource policies from all bundles, in which
|
||||
* the bitstream is present.
|
||||
*
|
||||
* @param bitstreamId Id of bitstream in DSpace.
|
||||
* @param headers If you want to access the item as the user logged into the context.
|
||||
* The header "rest-dspace-token" with the token passed
|
||||
* from the login method must be set.
|
||||
* @return Returns an array of ResourcePolicy objects.
|
||||
*/
|
||||
@GET
|
||||
@Path("/{bitstream_id}/policy")
|
||||
@Produces( {MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
|
||||
public ResourcePolicy[] getBitstreamPolicies(@PathParam("bitstream_id") String bitstreamId,
|
||||
@Context HttpHeaders headers) {
|
||||
|
||||
log.info("Reading bitstream(id=" + bitstreamId + ") policies.");
|
||||
org.dspace.core.Context context = null;
|
||||
ResourcePolicy[] policies = null;
|
||||
|
||||
try {
|
||||
context = createContext();
|
||||
org.dspace.content.Bitstream dspaceBitstream = findBitstream(context, bitstreamId,
|
||||
org.dspace.core.Constants.READ);
|
||||
policies = new Bitstream(dspaceBitstream, servletContext, "policies", context).getPolicies();
|
||||
|
||||
context.complete();
|
||||
log.trace("Policies for bitstream(id=" + bitstreamId + ") was successfully read.");
|
||||
|
||||
} catch (SQLException e) {
|
||||
processException("Someting went wrong while reading policies of bitstream(id=" + bitstreamId
|
||||
+ "), SQLException! Message: " + e, context);
|
||||
} catch (ContextException e) {
|
||||
processException("Someting went wrong while reading policies of bitstream(id=" + bitstreamId
|
||||
+ "), ContextException. Message: " + e.getMessage(), context);
|
||||
} finally {
|
||||
processFinally(context);
|
||||
}
|
||||
|
||||
return policies;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read list of bitstreams. It throws WebApplicationException with response
|
||||
* code INTERNAL_SERVER_ERROR(500), if there was problem while reading
|
||||
* bitstreams from database.
|
||||
*
|
||||
* @param expand This string defines which additional optional fields will be added
|
||||
* to bitstream response. Individual options are separated by commas without
|
||||
* spaces. The options are: "all", "parent".
|
||||
* @param limit How many bitstreams will be in the list. Default value is 100.
|
||||
* @param offset On which offset (item) the list starts. Default value is 0.
|
||||
* @param user_ip User's IP address.
|
||||
* @param user_agent User agent string (specifies browser used and its version).
|
||||
* @param xforwardedfor When accessed via a reverse proxy, the application sees the proxy's IP as the
|
||||
* source of the request. The proxy may be configured to add the
|
||||
* "X-Forwarded-For" HTTP header containing the original IP of the client
|
||||
* so that the reverse-proxied application can get the client's IP.
|
||||
* @param headers If you want to access the item as the user logged into the context.
|
||||
* The header "rest-dspace-token" with the token passed
|
||||
* from the login method must be set.
|
||||
* @param request Servlet's HTTP request object.
|
||||
* @return Returns an array of bistreams. Array doesn't contain bitstreams for
|
||||
* which the user doesn't have read permission.
|
||||
* @throws WebApplicationException Thrown in case of a problem with reading the database or with
|
||||
* creating a context.
|
||||
*/
|
||||
@GET
|
||||
@Produces( {MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
|
||||
public Bitstream[] getBitstreams(@QueryParam("expand") String expand,
|
||||
@QueryParam("limit") @DefaultValue("100") Integer limit,
|
||||
@QueryParam("offset") @DefaultValue("0") Integer offset,
|
||||
@QueryParam("userIP") String user_ip, @QueryParam("userAgent") String user_agent,
|
||||
@QueryParam("xforwardedfor") String xforwardedfor, @Context HttpHeaders headers,
|
||||
@Context HttpServletRequest request)
|
||||
throws WebApplicationException {
|
||||
|
||||
log.info("Reading bitstreams.(offset=" + offset + ",limit=" + limit + ")");
|
||||
org.dspace.core.Context context = null;
|
||||
List<Bitstream> bitstreams = new ArrayList<Bitstream>();
|
||||
|
||||
try {
|
||||
context = createContext();
|
||||
List<org.dspace.content.Bitstream> dspaceBitstreams = bitstreamService.findAll(context);
|
||||
|
||||
if (!((limit != null) && (limit >= 0) && (offset != null) && (offset >= 0))) {
|
||||
log.warn("Paging was badly set.");
|
||||
limit = 100;
|
||||
offset = 0;
|
||||
}
|
||||
|
||||
// TODO If bitstream doesn't exist, throws exception.
|
||||
for (int i = offset; (i < (offset + limit)) && (i < dspaceBitstreams.size()); i++) {
|
||||
if (authorizeService
|
||||
.authorizeActionBoolean(context, dspaceBitstreams.get(i), org.dspace.core.Constants.READ)) {
|
||||
if (bitstreamService.getParentObject(context, dspaceBitstreams
|
||||
.get(i)) != null) { // To eliminate bitstreams which cause exception, because of
|
||||
// reading under administrator permissions
|
||||
bitstreams.add(new Bitstream(dspaceBitstreams.get(i), servletContext, expand, context));
|
||||
writeStats(dspaceBitstreams.get(i), UsageEvent.Action.VIEW, user_ip, user_agent,
|
||||
xforwardedfor, headers, request, context);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
context.complete();
|
||||
log.trace("Bitstreams were successfully read.");
|
||||
|
||||
} catch (SQLException e) {
|
||||
processException("Something went wrong while reading bitstreams from database!. Message: " + e, context);
|
||||
} catch (ContextException e) {
|
||||
processException(
|
||||
"Something went wrong while reading bitstreams, ContextException. Message: " + e.getMessage(),
|
||||
context);
|
||||
} finally {
|
||||
processFinally(context);
|
||||
}
|
||||
|
||||
return bitstreams.toArray(new Bitstream[0]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read bitstream data. May throw WebApplicationException with the
|
||||
* INTERNAL_SERVER_ERROR(500) code. Caused by three exceptions: IOException if
|
||||
* there was a problem with reading bitstream file. SQLException if there was
|
||||
* a problem while reading from database. And AuthorizeException if there was
|
||||
* a problem with authorization of user logged to DSpace context.
|
||||
*
|
||||
* @param bitstreamId Id of the bitstream, whose data will be read.
|
||||
* @param user_ip User's IP address.
|
||||
* @param user_agent User agent string (specifies browser used and its version).
|
||||
* @param xforwardedfor When accessed via a reverse proxy, the application sees the proxy's IP as the
|
||||
* source of the request. The proxy may be configured to add the
|
||||
* "X-Forwarded-For" HTTP header containing the original IP of the client
|
||||
* so that the reverse-proxied application can get the client's IP.
|
||||
* @param headers If you want to access the item as the user logged into the context.
|
||||
* The header "rest-dspace-token" with the token passed
|
||||
* from the login method must be set.
|
||||
* @param request Servlet's HTTP request object.
|
||||
* @return Returns response with data with file content type. It can
|
||||
* return the NOT_FOUND(404) response code in case of wrong bitstream
|
||||
* id. Or response code UNAUTHORIZED(401) if user is not
|
||||
* allowed to read bitstream.
|
||||
* @throws WebApplicationException Thrown if there was a problem: reading the file data; or reading
|
||||
* the database; or creating the context; or with authorization.
|
||||
*/
|
||||
@GET
|
||||
@Path("/{bitstream_id}/retrieve")
|
||||
public javax.ws.rs.core.Response getBitstreamData(@PathParam("bitstream_id") String bitstreamId,
|
||||
@QueryParam("userIP") String user_ip,
|
||||
@QueryParam("userAgent") String user_agent,
|
||||
@QueryParam("xforwardedfor") String xforwardedfor,
|
||||
@Context HttpHeaders headers, @Context HttpServletRequest request)
|
||||
throws WebApplicationException {
|
||||
|
||||
log.info("Reading data of bitstream(id=" + bitstreamId + ").");
|
||||
org.dspace.core.Context context = null;
|
||||
InputStream inputStream = null;
|
||||
String type = null;
|
||||
String name = null;
|
||||
|
||||
try {
|
||||
context = createContext();
|
||||
org.dspace.content.Bitstream dspaceBitstream = findBitstream(context, bitstreamId,
|
||||
org.dspace.core.Constants.READ);
|
||||
|
||||
writeStats(dspaceBitstream, UsageEvent.Action.VIEW, user_ip, user_agent, xforwardedfor, headers,
|
||||
request, context);
|
||||
|
||||
log.trace("Bitstream(id=" + bitstreamId + ") data was successfully read.");
|
||||
inputStream = bitstreamService.retrieve(context, dspaceBitstream);
|
||||
type = dspaceBitstream.getFormat(context).getMIMEType();
|
||||
name = dspaceBitstream.getName();
|
||||
|
||||
context.complete();
|
||||
} catch (IOException e) {
|
||||
processException("Could not read file of bitstream(id=" + bitstreamId + ")! Message: " + e, context);
|
||||
} catch (SQLException e) {
|
||||
processException(
|
||||
"Something went wrong while reading bitstream(id=" + bitstreamId + ") from database! Message: " + e,
|
||||
context);
|
||||
} catch (AuthorizeException e) {
|
||||
processException(
|
||||
"Could not retrieve file of bitstream(id=" + bitstreamId + "), AuthorizeException! Message: " + e,
|
||||
context);
|
||||
} catch (ContextException e) {
|
||||
processException(
|
||||
"Could not retrieve file of bitstream(id=" + bitstreamId + "), ContextException! Message: " + e
|
||||
.getMessage(),
|
||||
context);
|
||||
} finally {
|
||||
processFinally(context);
|
||||
}
|
||||
|
||||
return Response.ok(inputStream).type(type)
|
||||
.header("Content-Disposition", "attachment; filename=\"" + name + "\"")
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Add bitstream policy to all bundles containing the bitstream.
|
||||
*
|
||||
* @param bitstreamId Id of bitstream in DSpace.
|
||||
* @param policy Policy to be added. The following attributes are not
|
||||
* applied: epersonId,
|
||||
* @param user_ip User's IP address.
|
||||
* @param user_agent User agent string (specifies browser used and its version).
|
||||
* @param xforwardedfor When accessed via a reverse proxy, the application sees the proxy's IP as the
|
||||
* source of the request. The proxy may be configured to add the
|
||||
* "X-Forwarded-For" HTTP header containing the original IP of the client
|
||||
* so that the reverse-proxied application can get the client's IP.
|
||||
* @param headers If you want to access the item as the user logged into the context.
|
||||
* The header "rest-dspace-token" with the token passed
|
||||
* from the login method must be set.
|
||||
* @param request Servlet's HTTP request object.
|
||||
* @return Returns ok, if all was ok. Otherwise status code 500.
|
||||
*/
|
||||
@POST
|
||||
@Path("/{bitstream_id}/policy")
|
||||
@Consumes( {MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
|
||||
public javax.ws.rs.core.Response addBitstreamPolicy(@PathParam("bitstream_id") String bitstreamId,
|
||||
ResourcePolicy policy,
|
||||
@QueryParam("userIP") String user_ip,
|
||||
@QueryParam("userAgent") String user_agent,
|
||||
@QueryParam("xforwardedfor") String xforwardedfor,
|
||||
@Context HttpHeaders headers,
|
||||
@Context HttpServletRequest request)
|
||||
throws WebApplicationException {
|
||||
|
||||
log.info("Adding bitstream(id=" + bitstreamId + ") " + policy
|
||||
.getAction() + " policy with permission for group(id=" + policy.getGroupId()
|
||||
+ ").");
|
||||
org.dspace.core.Context context = null;
|
||||
|
||||
try {
|
||||
context = createContext();
|
||||
org.dspace.content.Bitstream dspaceBitstream = findBitstream(context, bitstreamId,
|
||||
org.dspace.core.Constants.WRITE);
|
||||
|
||||
writeStats(dspaceBitstream, UsageEvent.Action.UPDATE, user_ip, user_agent, xforwardedfor, headers,
|
||||
request, context);
|
||||
|
||||
addPolicyToBitstream(context, policy, dspaceBitstream);
|
||||
|
||||
context.complete();
|
||||
log.trace("Policy for bitstream(id=" + bitstreamId + ") was successfully added.");
|
||||
|
||||
} catch (SQLException e) {
|
||||
processException("Someting went wrong while adding policy to bitstream(id=" + bitstreamId
|
||||
+ "), SQLException! Message: " + e, context);
|
||||
} catch (ContextException e) {
|
||||
processException("Someting went wrong while adding policy to bitstream(id=" + bitstreamId
|
||||
+ "), ContextException. Message: " + e.getMessage(), context);
|
||||
} catch (AuthorizeException e) {
|
||||
processException("Someting went wrong while adding policy to bitstream(id=" + bitstreamId
|
||||
+ "), AuthorizeException! Message: " + e, context);
|
||||
} finally {
|
||||
processFinally(context);
|
||||
}
|
||||
return Response.status(Status.OK).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Update bitstream metadata. Replaces everything on targeted bitstream.
|
||||
* May throw WebApplicationException caused by two exceptions:
|
||||
* SQLException, if there was a problem with the database. AuthorizeException if
|
||||
* there was a problem with the authorization to edit bitstream metadata.
|
||||
*
|
||||
* @param bitstreamId Id of bistream to be updated.
|
||||
* @param bitstream Bitstream with will be placed. It must have filled user
|
||||
* credentials.
|
||||
* @param user_ip User's IP address.
|
||||
* @param user_agent User agent string (specifies browser used and its version).
|
||||
* @param xforwardedfor When accessed via a reverse proxy, the application sees the proxy's IP as the
|
||||
* source of the request. The proxy may be configured to add the
|
||||
* "X-Forwarded-For" HTTP header containing the original IP of the client
|
||||
* so that the reverse-proxied application can get the client's IP.
|
||||
* @param headers If you want to access the item as the user logged into the context.
|
||||
* The header "rest-dspace-token" with the token passed
|
||||
* from the login method must be set.
|
||||
* @param request Servlet's HTTP request object.
|
||||
* @return Return response codes: OK(200), NOT_FOUND(404) if bitstream does
|
||||
* not exist and UNAUTHORIZED(401) if user is not allowed to write
|
||||
* to bitstream.
|
||||
* @throws WebApplicationException Thrown when: Error reading from database; or error
|
||||
* creating context; or error regarding bitstream authorization.
|
||||
*/
|
||||
@PUT
|
||||
@Path("/{bitstream_id}")
|
||||
@Consumes( {MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
|
||||
public Response updateBitstream(@PathParam("bitstream_id") String bitstreamId, Bitstream bitstream,
|
||||
@QueryParam("userIP") String user_ip, @QueryParam("userAgent") String user_agent,
|
||||
@QueryParam("xforwardedfor") String xforwardedfor, @Context HttpHeaders headers,
|
||||
@Context HttpServletRequest request)
|
||||
throws WebApplicationException {
|
||||
|
||||
log.info("Updating bitstream(id=" + bitstreamId + ") metadata.");
|
||||
org.dspace.core.Context context = null;
|
||||
|
||||
try {
|
||||
context = createContext();
|
||||
org.dspace.content.Bitstream dspaceBitstream = findBitstream(context, bitstreamId,
|
||||
org.dspace.core.Constants.WRITE);
|
||||
|
||||
writeStats(dspaceBitstream, UsageEvent.Action.UPDATE, user_ip, user_agent, xforwardedfor,
|
||||
headers, request, context);
|
||||
|
||||
log.trace("Updating bitstream metadata.");
|
||||
|
||||
dspaceBitstream.setDescription(context, bitstream.getDescription());
|
||||
if (getMimeType(bitstream.getName()) == null) {
|
||||
BitstreamFormat unknownFormat = bitstreamFormatService.findUnknown(context);
|
||||
bitstreamService.setFormat(context, dspaceBitstream, unknownFormat);
|
||||
} else {
|
||||
BitstreamFormat guessedFormat = bitstreamFormatService
|
||||
.findByMIMEType(context, getMimeType(bitstream.getName()));
|
||||
bitstreamService.setFormat(context, dspaceBitstream, guessedFormat);
|
||||
}
|
||||
dspaceBitstream.setName(context, bitstream.getName());
|
||||
Integer sequenceId = bitstream.getSequenceId();
|
||||
if (sequenceId != null && sequenceId.intValue() != -1) {
|
||||
dspaceBitstream.setSequenceID(sequenceId);
|
||||
}
|
||||
|
||||
bitstreamService.update(context, dspaceBitstream);
|
||||
|
||||
if (bitstream.getPolicies() != null) {
|
||||
log.trace("Updating bitstream policies.");
|
||||
|
||||
// Remove all old bitstream policies.
|
||||
authorizeService.removeAllPolicies(context, dspaceBitstream);
|
||||
|
||||
// Add all new bitstream policies
|
||||
for (ResourcePolicy policy : bitstream.getPolicies()) {
|
||||
addPolicyToBitstream(context, policy, dspaceBitstream);
|
||||
}
|
||||
}
|
||||
|
||||
context.complete();
|
||||
|
||||
} catch (SQLException e) {
|
||||
processException("Could not update bitstream(id=" + bitstreamId + ") metadata, SQLException. Message: " + e,
|
||||
context);
|
||||
} catch (AuthorizeException e) {
|
||||
processException(
|
||||
"Could not update bitstream(id=" + bitstreamId + ") metadata, AuthorizeException. Message: " + e,
|
||||
context);
|
||||
} catch (ContextException e) {
|
||||
processException(
|
||||
"Could not update bitstream(id=" + bitstreamId + ") metadata, ContextException. Message: " + e
|
||||
.getMessage(),
|
||||
context);
|
||||
} finally {
|
||||
processFinally(context);
|
||||
}
|
||||
|
||||
log.info("Bitstream metadata(id=" + bitstreamId + ") were successfully updated.");
|
||||
return Response.ok().build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Update bitstream data. Changes bitstream data by editing database rows.
|
||||
* May throw WebApplicationException caused by: SQLException if there was
|
||||
* a problem editing or reading the database, IOException if there was
|
||||
* a problem with reading from InputStream, Exception if there was another
|
||||
* problem.
|
||||
*
|
||||
* @param bitstreamId Id of bistream to be updated.
|
||||
* @param is InputStream filled with new data.
|
||||
* @param user_ip User's IP address.
|
||||
* @param user_agent User agent string (specifies browser used and its version).
|
||||
* @param xforwardedfor When accessed via a reverse proxy, the application sees the proxy's IP as the
|
||||
* source of the request. The proxy may be configured to add the
|
||||
* "X-Forwarded-For" HTTP header containing the original IP of the client
|
||||
* so that the reverse-proxied application can get the client's IP.
|
||||
* @param headers If you want to access the item as the user logged into the context.
|
||||
* The header "rest-dspace-token" with the token passed
|
||||
* from the login method must be set.
|
||||
* @param request Servlet's HTTP request object.
|
||||
* @return Return response if bitstream was updated. Response codes:
|
||||
* OK(200), NOT_FOUND(404) if id of bitstream was bad. And
|
||||
* UNAUTHORIZED(401) if user is not allowed to update bitstream.
|
||||
* @throws WebApplicationException This exception can be thrown in this cases: Problem with
|
||||
* reading or writing to database. Or problem with reading from
|
||||
* InputStream.
|
||||
*/
|
||||
// TODO Change to better logic, without editing database.
|
||||
@PUT
|
||||
@Path("/{bitstream_id}/data")
|
||||
public Response updateBitstreamData(@PathParam("bitstream_id") String bitstreamId, InputStream is,
|
||||
@QueryParam("userIP") String user_ip,
|
||||
@QueryParam("userAgent") String user_agent,
|
||||
@QueryParam("xforwardedfor") String xforwardedfor, @Context HttpHeaders headers,
|
||||
@Context HttpServletRequest request)
|
||||
throws WebApplicationException {
|
||||
|
||||
log.info("Updating bitstream(id=" + bitstreamId + ") data.");
|
||||
org.dspace.core.Context context = null;
|
||||
|
||||
try {
|
||||
context = createContext();
|
||||
org.dspace.content.Bitstream dspaceBitstream = findBitstream(context, bitstreamId,
|
||||
org.dspace.core.Constants.WRITE);
|
||||
|
||||
writeStats(dspaceBitstream, UsageEvent.Action.UPDATE, user_ip, user_agent, xforwardedfor,
|
||||
headers, request, context);
|
||||
|
||||
log.trace("Creating new bitstream.");
|
||||
|
||||
UUID newBitstreamId = bitstreamStorageService.store(context, dspaceBitstream, is);
|
||||
log.trace("Bitstream data stored: " + newBitstreamId);
|
||||
context.complete();
|
||||
} catch (SQLException e) {
|
||||
processException("Could not update bitstream(id=" + bitstreamId + ") data, SQLException. Message: " + e,
|
||||
context);
|
||||
} catch (IOException e) {
|
||||
processException("Could not update bitstream(id=" + bitstreamId + ") data, IOException. Message: " + e,
|
||||
context);
|
||||
} catch (ContextException e) {
|
||||
processException(
|
||||
"Could not update bitstream(id=" + bitstreamId + ") data, ContextException. Message: " + e.getMessage(),
|
||||
context);
|
||||
} finally {
|
||||
processFinally(context);
|
||||
}
|
||||
|
||||
log.info("Bitstream(id=" + bitstreamId + ") data was successfully updated.");
|
||||
return Response.ok().build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete bitstream from all bundles in DSpace. May throw
|
||||
* WebApplicationException, which can be caused by three exceptions.
|
||||
* SQLException if there was a problem reading from database or removing
|
||||
* from database. AuthorizeException, if user doesn't have permission to delete
|
||||
* the bitstream or file. IOException, if there was a problem deleting the file.
|
||||
*
|
||||
* @param bitstreamId Id of bitstream to be deleted.
|
||||
* @param user_ip User's IP address.
|
||||
* @param user_agent User agent string (specifies browser used and its version).
|
||||
* @param xforwardedfor When accessed via a reverse proxy, the application sees the proxy's IP as the
|
||||
* source of the request. The proxy may be configured to add the
|
||||
* "X-Forwarded-For" HTTP header containing the original IP of the client
|
||||
* so that the reverse-proxied application can get the client's IP.
|
||||
* @param headers If you want to access the item as the user logged into the context.
|
||||
* The header "rest-dspace-token" with the token passed
|
||||
* from the login method must be set.
|
||||
* @param request Servlet's HTTP request object.
|
||||
* @return Return response codes: OK(200), NOT_FOUND(404) if bitstream of
|
||||
* that id does not exist and UNAUTHORIZED(401) if user is not
|
||||
* allowed to delete bitstream.
|
||||
* @throws WebApplicationException Can be thrown if there was a problem reading or editing
|
||||
* the database. Or problem deleting the file. Or problem with
|
||||
* authorization to bitstream and bundles. Or problem with
|
||||
* creating context.
|
||||
*/
|
||||
@DELETE
|
||||
@Path("/{bitstream_id}")
|
||||
public Response deleteBitstream(@PathParam("bitstream_id") String bitstreamId, @QueryParam("userIP") String user_ip,
|
||||
@QueryParam("userAgent") String user_agent,
|
||||
@QueryParam("xforwardedfor") String xforwardedfor,
|
||||
@Context HttpHeaders headers, @Context HttpServletRequest request)
|
||||
throws WebApplicationException {
|
||||
|
||||
log.info("Deleting bitstream(id=" + bitstreamId + ").");
|
||||
org.dspace.core.Context context = null;
|
||||
|
||||
try {
|
||||
context = createContext();
|
||||
org.dspace.content.Bitstream dspaceBitstream = findBitstream(context, bitstreamId,
|
||||
org.dspace.core.Constants.DELETE);
|
||||
|
||||
writeStats(dspaceBitstream, UsageEvent.Action.DELETE, user_ip, user_agent, xforwardedfor,
|
||||
headers, request, context);
|
||||
|
||||
log.trace("Deleting bitstream from all bundles.");
|
||||
bitstreamService.delete(context, dspaceBitstream);
|
||||
|
||||
context.complete();
|
||||
} catch (SQLException e) {
|
||||
processException("Could not delete bitstream(id=" + bitstreamId + "), SQLException. Message: " + e,
|
||||
context);
|
||||
} catch (AuthorizeException e) {
|
||||
processException("Could not delete bitstream(id=" + bitstreamId + "), AuthorizeException. Message: " + e,
|
||||
context);
|
||||
} catch (IOException e) {
|
||||
processException("Could not delete bitstream(id=" + bitstreamId + "), IOException. Message: " + e, context);
|
||||
} catch (ContextException e) {
|
||||
processException(
|
||||
"Could not delete bitstream(id=" + bitstreamId + "), ContextException. Message:" + e.getMessage(),
|
||||
context);
|
||||
} finally {
|
||||
processFinally(context);
|
||||
}
|
||||
|
||||
log.info("Bitstream(id=" + bitstreamId + ") was successfully deleted.");
|
||||
return Response.ok().build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete policy.
|
||||
*
|
||||
* @param bitstreamId Id of the DSpace bitstream whose policy will be deleted.
|
||||
* @param policyId Id of the policy to delete.
|
||||
* @param user_ip User's IP address.
|
||||
* @param user_agent User agent string (specifies browser used and its version).
|
||||
* @param xforwardedfor When accessed via a reverse proxy, the application sees the proxy's IP as the
|
||||
* source of the request. The proxy may be configured to add the
|
||||
* "X-Forwarded-For" HTTP header containing the original IP of the client
|
||||
* so that the reverse-proxied application can get the client's IP.
|
||||
* @param headers If you want to access the item as the user logged into the context.
|
||||
* The header "rest-dspace-token" with the token passed
|
||||
* from the login method must be set.
|
||||
* @param request Servlet's HTTP request object.
|
||||
* @return It returns Ok, if all was ok. Otherwise status code 500.
|
||||
*/
|
||||
@DELETE
|
||||
@Path("/{bitstream_id}/policy/{policy_id}")
|
||||
public javax.ws.rs.core.Response deleteBitstreamPolicy(@PathParam("bitstream_id") String bitstreamId,
|
||||
@PathParam("policy_id") Integer policyId,
|
||||
@QueryParam("userIP") String user_ip,
|
||||
@QueryParam("userAgent") String user_agent,
|
||||
@QueryParam("xforwardedfor") String xforwardedfor,
|
||||
@Context HttpHeaders headers,
|
||||
@Context HttpServletRequest request)
|
||||
throws WebApplicationException {
|
||||
log.info("Deleting policy(id=" + policyId + ") from bitstream(id=" + bitstreamId + ").");
|
||||
org.dspace.core.Context context = null;
|
||||
|
||||
try {
|
||||
context = createContext();
|
||||
org.dspace.content.Bitstream dspaceBitstream = findBitstream(context, bitstreamId,
|
||||
org.dspace.core.Constants.WRITE);
|
||||
|
||||
writeStats(dspaceBitstream, UsageEvent.Action.UPDATE, user_ip, user_agent, xforwardedfor, headers,
|
||||
request, context);
|
||||
|
||||
org.dspace.authorize.ResourcePolicy resourcePolicy = resourcePolicyService.find(context, policyId);
|
||||
if (resourcePolicy.getdSpaceObject().getID().equals(dspaceBitstream.getID()) && authorizeService
|
||||
.authorizeActionBoolean(context, dspaceBitstream, org.dspace.core.Constants.REMOVE)) {
|
||||
|
||||
try {
|
||||
resourcePolicyService.delete(context, resourcePolicy);
|
||||
} catch (AuthorizeException e) {
|
||||
processException(
|
||||
"Someting went wrong while deleting policy(id=" + policyId + ") to bitstream(id=" + bitstreamId
|
||||
+ "), AuthorizeException! Message: " + e, context);
|
||||
}
|
||||
log.trace("Policy for bitstream(id=" + bitstreamId + ") was successfully removed.");
|
||||
}
|
||||
|
||||
context.complete();
|
||||
} catch (SQLException e) {
|
||||
processException(
|
||||
"Someting went wrong while deleting policy(id=" + policyId + ") to bitstream(id=" + bitstreamId
|
||||
+ "), SQLException! Message: " + e, context);
|
||||
} catch (ContextException e) {
|
||||
processException(
|
||||
"Someting went wrong while deleting policy(id=" + policyId + ") to bitstream(id=" + bitstreamId
|
||||
+ "), ContextException. Message: " + e.getMessage(), context);
|
||||
} finally {
|
||||
processFinally(context);
|
||||
}
|
||||
|
||||
return Response.status(Status.OK).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the MIME type of the file, by file extension.
|
||||
*
|
||||
* @param name Name of file.
|
||||
* @return String filled with type of file in MIME style.
|
||||
*/
|
||||
static String getMimeType(String name) {
|
||||
return URLConnection.guessContentTypeFromName(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add policy(org.dspace.rest.common.ResourcePolicy) to bitstream.
|
||||
*
|
||||
* @param context Context to create DSpace ResourcePolicy.
|
||||
* @param policy Policy which will be added to bitstream.
|
||||
* @param dspaceBitstream DSpace Bitstream object.
|
||||
* @throws SQLException An exception that provides information on a database access error or other errors.
|
||||
* @throws AuthorizeException Exception indicating the current user of the context does not have permission
|
||||
* to perform a particular action.
|
||||
*/
|
||||
private void addPolicyToBitstream(org.dspace.core.Context context, ResourcePolicy policy,
|
||||
org.dspace.content.Bitstream dspaceBitstream)
|
||||
throws SQLException, AuthorizeException {
|
||||
org.dspace.authorize.ResourcePolicy dspacePolicy = resourcePolicyService.create(context);
|
||||
dspacePolicy.setAction(policy.getActionInt());
|
||||
dspacePolicy.setGroup(groupService.findByIdOrLegacyId(context, policy.getGroupId()));
|
||||
dspacePolicy.setdSpaceObject(dspaceBitstream);
|
||||
dspacePolicy.setStartDate(policy.getStartDate());
|
||||
dspacePolicy.setEndDate(policy.getEndDate());
|
||||
dspacePolicy.setRpDescription(policy.getRpDescription());
|
||||
dspacePolicy.setRpName(policy.getRpName());
|
||||
|
||||
resourcePolicyService.update(context, dspacePolicy);
|
||||
}
|
||||
|
||||
/**
|
||||
* Find bitstream from DSpace database. This encapsulates the
|
||||
* org.dspace.content.Bitstream.find method with a check whether the item exists and
|
||||
* whether the user logged into the context has permission to preform the requested action.
|
||||
*
|
||||
* @param context Context of actual logged user.
|
||||
* @param id Id of bitstream in DSpace.
|
||||
* @param action Constant from org.dspace.core.Constants.
|
||||
* @return Returns DSpace bitstream.
|
||||
* @throws WebApplicationException Is thrown when item with passed id is not exists and if user
|
||||
* has no permission to do passed action.
|
||||
*/
|
||||
private org.dspace.content.Bitstream findBitstream(org.dspace.core.Context context, String id, int action)
|
||||
throws WebApplicationException {
|
||||
org.dspace.content.Bitstream bitstream = null;
|
||||
try {
|
||||
bitstream = bitstreamService.findByIdOrLegacyId(context, id);
|
||||
|
||||
if ((bitstream == null) || (bitstreamService.getParentObject(context, bitstream) == null)) {
|
||||
context.abort();
|
||||
log.warn("Bitstream(id=" + id + ") was not found!");
|
||||
throw new WebApplicationException(Response.Status.NOT_FOUND);
|
||||
} else if (!authorizeService.authorizeActionBoolean(context, bitstream, action)) {
|
||||
context.abort();
|
||||
if (context.getCurrentUser() != null) {
|
||||
log.error("User(" + context.getCurrentUser().getEmail() + ") doesn't have the permission to "
|
||||
+ getActionString(action) + " bitstream!");
|
||||
} else {
|
||||
log.error(
|
||||
"User(anonymous) doesn't have the permission to " + getActionString(action) + " bitsteam!");
|
||||
}
|
||||
throw new WebApplicationException(Response.Status.UNAUTHORIZED);
|
||||
}
|
||||
|
||||
} catch (SQLException e) {
|
||||
processException("Something went wrong while finding bitstream. SQLException, Message:" + e, context);
|
||||
}
|
||||
return bitstream;
|
||||
}
|
||||
}
|
@@ -1,755 +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.rest;
|
||||
|
||||
import static org.dspace.content.service.DSpaceObjectService.MD_COPYRIGHT_TEXT;
|
||||
import static org.dspace.content.service.DSpaceObjectService.MD_INTRODUCTORY_TEXT;
|
||||
import static org.dspace.content.service.DSpaceObjectService.MD_LICENSE;
|
||||
import static org.dspace.content.service.DSpaceObjectService.MD_NAME;
|
||||
import static org.dspace.content.service.DSpaceObjectService.MD_SHORT_DESCRIPTION;
|
||||
import static org.dspace.content.service.DSpaceObjectService.MD_SIDEBAR_TEXT;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.ws.rs.Consumes;
|
||||
import javax.ws.rs.DELETE;
|
||||
import javax.ws.rs.DefaultValue;
|
||||
import javax.ws.rs.GET;
|
||||
import javax.ws.rs.POST;
|
||||
import javax.ws.rs.PUT;
|
||||
import javax.ws.rs.Path;
|
||||
import javax.ws.rs.PathParam;
|
||||
import javax.ws.rs.Produces;
|
||||
import javax.ws.rs.QueryParam;
|
||||
import javax.ws.rs.WebApplicationException;
|
||||
import javax.ws.rs.core.Context;
|
||||
import javax.ws.rs.core.HttpHeaders;
|
||||
import javax.ws.rs.core.MediaType;
|
||||
import javax.ws.rs.core.Response;
|
||||
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.dspace.authorize.AuthorizeException;
|
||||
import org.dspace.authorize.factory.AuthorizeServiceFactory;
|
||||
import org.dspace.authorize.service.AuthorizeService;
|
||||
import org.dspace.content.factory.ContentServiceFactory;
|
||||
import org.dspace.content.service.CollectionService;
|
||||
import org.dspace.content.service.ItemService;
|
||||
import org.dspace.content.service.WorkspaceItemService;
|
||||
import org.dspace.core.Constants;
|
||||
import org.dspace.core.LogHelper;
|
||||
import org.dspace.rest.common.Collection;
|
||||
import org.dspace.rest.common.Item;
|
||||
import org.dspace.rest.common.MetadataEntry;
|
||||
import org.dspace.rest.exceptions.ContextException;
|
||||
import org.dspace.usage.UsageEvent;
|
||||
import org.dspace.workflow.WorkflowService;
|
||||
import org.dspace.workflow.factory.WorkflowServiceFactory;
|
||||
|
||||
/**
|
||||
* This class provides all CRUD operation over collections.
|
||||
*
|
||||
* @author Rostislav Novak (Computing and Information Centre, CTU in Prague)
|
||||
*/
|
||||
@Path("/collections")
|
||||
public class CollectionsResource extends Resource {
|
||||
protected CollectionService collectionService = ContentServiceFactory.getInstance().getCollectionService();
|
||||
protected ItemService itemService = ContentServiceFactory.getInstance().getItemService();
|
||||
protected AuthorizeService authorizeService = AuthorizeServiceFactory.getInstance().getAuthorizeService();
|
||||
protected WorkspaceItemService workspaceItemService = ContentServiceFactory.getInstance().getWorkspaceItemService();
|
||||
protected WorkflowService workflowService = WorkflowServiceFactory.getInstance().getWorkflowService();
|
||||
|
||||
private static final Logger log = org.apache.logging.log4j.LogManager.getLogger(CollectionsResource.class);
|
||||
|
||||
/**
|
||||
* Return instance of collection with passed id. You can add more properties
|
||||
* through expand parameter.
|
||||
*
|
||||
* @param collectionId Id of collection in DSpace.
|
||||
* @param expand String in which is what you want to add to returned instance
|
||||
* of collection. Options are: "all", "parentCommunityList",
|
||||
* "parentCommunity", "items", "license" and "logo". If you want
|
||||
* to use multiple options, it must be separated by commas.
|
||||
* @param limit Limit value for items in list in collection. Default value is
|
||||
* 100.
|
||||
* @param offset Offset of start index in list of items of collection. Default
|
||||
* value is 0.
|
||||
* @param user_ip User's IP address.
|
||||
* @param user_agent User agent string (specifies browser used and its version).
|
||||
* @param xforwardedfor When accessed via a reverse proxy, the application sees the proxy's IP as the
|
||||
* source of the request. The proxy may be configured to add the
|
||||
* "X-Forwarded-For" HTTP header containing the original IP of the client
|
||||
* so that the reverse-proxied application can get the client's IP.
|
||||
* @param headers If you want to access the collection as the user logged into the
|
||||
* context. The value of the "rest-dspace-token" header must be set
|
||||
* to the token received from the login method response.
|
||||
* @param request Servlet's HTTP request object.
|
||||
* @return Return instance of collection. It can also return status code
|
||||
* NOT_FOUND(404) if id of collection is incorrect or status code
|
||||
* UNATHORIZED(401) if user has no permission to read collection.
|
||||
* @throws WebApplicationException It is thrown when was problem with database reading
|
||||
* (SQLException) or problem with creating
|
||||
* context(ContextException). It is thrown by NOT_FOUND and
|
||||
* UNATHORIZED status codes, too.
|
||||
*/
|
||||
@GET
|
||||
@Path("/{collection_id}")
|
||||
@Produces( {MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
|
||||
public org.dspace.rest.common.Collection getCollection(@PathParam("collection_id") String collectionId,
|
||||
@QueryParam("expand") String expand,
|
||||
@QueryParam("limit") @DefaultValue("100") Integer limit,
|
||||
@QueryParam("offset") @DefaultValue("0") Integer offset,
|
||||
@QueryParam("userIP") String user_ip,
|
||||
@QueryParam("userAgent") String user_agent,
|
||||
@QueryParam("xforwardedfor") String xforwardedfor,
|
||||
@Context HttpHeaders headers,
|
||||
@Context HttpServletRequest request)
|
||||
throws WebApplicationException {
|
||||
|
||||
log.info("Reading collection(id=" + collectionId + ").");
|
||||
org.dspace.core.Context context = null;
|
||||
Collection collection = null;
|
||||
|
||||
try {
|
||||
context = createContext();
|
||||
|
||||
org.dspace.content.Collection dspaceCollection = findCollection(context, collectionId,
|
||||
org.dspace.core.Constants.READ);
|
||||
writeStats(dspaceCollection, UsageEvent.Action.VIEW, user_ip, user_agent, xforwardedfor,
|
||||
headers, request, context);
|
||||
|
||||
collection = new Collection(dspaceCollection, servletContext, expand, context, limit, offset);
|
||||
context.complete();
|
||||
|
||||
} catch (SQLException e) {
|
||||
processException("Could not read collection(id=" + collectionId + "), SQLException. Message: " + e,
|
||||
context);
|
||||
} catch (ContextException e) {
|
||||
processException(
|
||||
"Could not read collection(id=" + collectionId + "), ContextException. Message: " + e.getMessage(),
|
||||
context);
|
||||
} finally {
|
||||
processFinally(context);
|
||||
}
|
||||
|
||||
log.trace("Collection(id=" + collectionId + ") has been successfully read.");
|
||||
return collection;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return array of all collections in DSpace. You can add more properties
|
||||
* through expand parameter.
|
||||
*
|
||||
* @param expand String in which is what you want to add to returned instance
|
||||
* of collection. Options are: "all", "parentCommunityList",
|
||||
* "parentCommunity", "items", "license" and "logo". If you want
|
||||
* to use multiple options, it must be separated by commas.
|
||||
* @param limit Limit value for items in list in collection. Default value is
|
||||
* 100.
|
||||
* @param offset Offset of start index in list of items of collection. Default
|
||||
* value is 0.
|
||||
* @param user_ip User's IP address.
|
||||
* @param user_agent User agent string (specifies browser used and its version).
|
||||
* @param xforwardedfor When accessed via a reverse proxy, the application sees the proxy's IP as the
|
||||
* source of the request. The proxy may be configured to add the
|
||||
* "X-Forwarded-For" HTTP header containing the original IP of the client
|
||||
* so that the reverse-proxied application can get the client's IP.
|
||||
* @param headers If you want to access the collections as the user logged into the
|
||||
* context. The value of the "rest-dspace-token" header must be set
|
||||
* to the token received from the login method response.
|
||||
* @param request Servlet's HTTP request object.
|
||||
* @return Return array of collection, on which has logged user permission
|
||||
* to view.
|
||||
* @throws WebApplicationException It is thrown when was problem with database reading
|
||||
* (SQLException) or problem with creating
|
||||
* context(ContextException).
|
||||
*/
|
||||
@GET
|
||||
@Produces( {MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
|
||||
public org.dspace.rest.common.Collection[] getCollections(@QueryParam("expand") String expand,
|
||||
@QueryParam("limit") @DefaultValue("100") Integer limit,
|
||||
@QueryParam("offset") @DefaultValue("0") Integer offset,
|
||||
@QueryParam("userIP") String user_ip,
|
||||
@QueryParam("userAgent") String user_agent,
|
||||
@QueryParam("xforwardedfor") String xforwardedfor,
|
||||
@Context HttpHeaders headers,
|
||||
@Context HttpServletRequest request)
|
||||
throws WebApplicationException {
|
||||
|
||||
log.info("Reading all collections.(offset=" + offset + ",limit=" + limit + ")");
|
||||
org.dspace.core.Context context = null;
|
||||
List<Collection> collections = new ArrayList<>();
|
||||
|
||||
try {
|
||||
context = createContext();
|
||||
|
||||
if (!((limit != null) && (limit >= 0) && (offset != null) && (offset >= 0))) {
|
||||
log.warn("Paging was badly set.");
|
||||
limit = 100;
|
||||
offset = 0;
|
||||
}
|
||||
|
||||
List<org.dspace.content.Collection> dspaceCollections = collectionService.findAll(context, limit, offset);
|
||||
for (org.dspace.content.Collection dspaceCollection : dspaceCollections) {
|
||||
if (authorizeService
|
||||
.authorizeActionBoolean(context, dspaceCollection, org.dspace.core.Constants.READ)) {
|
||||
Collection collection = new org.dspace.rest.common.Collection(dspaceCollection, servletContext,
|
||||
null, context, limit,
|
||||
offset);
|
||||
collections.add(collection);
|
||||
writeStats(dspaceCollection, UsageEvent.Action.VIEW, user_ip, user_agent,
|
||||
xforwardedfor, headers, request, context);
|
||||
}
|
||||
}
|
||||
context.complete();
|
||||
} catch (SQLException e) {
|
||||
processException("Something went wrong while reading collections from database. Message: " + e, context);
|
||||
} catch (ContextException e) {
|
||||
processException("Something went wrong while reading collections, ContextError. Message: " + e.getMessage(),
|
||||
context);
|
||||
} finally {
|
||||
processFinally(context);
|
||||
}
|
||||
|
||||
log.trace("All collections were successfully read.");
|
||||
return collections.toArray(new org.dspace.rest.common.Collection[0]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return array of items in collection. You can add more properties to items
|
||||
* with expand parameter.
|
||||
*
|
||||
* @param collectionId Id of collection in DSpace.
|
||||
* @param expand String which define, what additional properties will be in
|
||||
* returned item. Options are separeted by commas and are: "all",
|
||||
* "metadata", "parentCollection", "parentCollectionList",
|
||||
* "parentCommunityList" and "bitstreams".
|
||||
* @param limit Limit value for items in array. Default value is 100.
|
||||
* @param offset Offset of start index in array of items of collection. Default
|
||||
* value is 0.
|
||||
* @param user_ip User's IP address.
|
||||
* @param user_agent User agent string (specifies browser used and its version).
|
||||
* @param xforwardedfor When accessed via a reverse proxy, the application sees the proxy's IP as the
|
||||
* source of the request. The proxy may be configured to add the
|
||||
* "X-Forwarded-For" HTTP header containing the original IP of the client
|
||||
* so that the reverse-proxied application can get the client's IP.
|
||||
* @param headers If you want to access the collection as the user logged into the
|
||||
* context. The value of the "rest-dspace-token" header must be set
|
||||
* to the token received from the login method response.
|
||||
* @param request Servlet's HTTP request object.
|
||||
* @return Return array of items, on which has logged user permission to
|
||||
* read. It can also return status code NOT_FOUND(404) if id of
|
||||
* collection is incorrect or status code UNATHORIZED(401) if user
|
||||
* has no permission to read collection.
|
||||
* @throws WebApplicationException It is thrown when was problem with database reading
|
||||
* (SQLException) or problem with creating
|
||||
* context(ContextException). It is thrown by NOT_FOUND and
|
||||
* UNATHORIZED status codes, too.
|
||||
*/
|
||||
@GET
|
||||
@Path("/{collection_id}/items")
|
||||
@Produces( {MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
|
||||
public org.dspace.rest.common.Item[] getCollectionItems(@PathParam("collection_id") String collectionId,
|
||||
@QueryParam("expand") String expand,
|
||||
@QueryParam("limit") @DefaultValue("100") Integer limit,
|
||||
@QueryParam("offset") @DefaultValue("0") Integer offset,
|
||||
@QueryParam("userIP") String user_ip,
|
||||
@QueryParam("userAgent") String user_agent,
|
||||
@QueryParam("xforwardedfor") String xforwardedfor,
|
||||
@Context HttpHeaders headers,
|
||||
@Context HttpServletRequest request)
|
||||
throws WebApplicationException {
|
||||
|
||||
log.info("Reading collection(id=" + collectionId + ") items.");
|
||||
org.dspace.core.Context context = null;
|
||||
List<Item> items = null;
|
||||
|
||||
try {
|
||||
context = createContext();
|
||||
|
||||
org.dspace.content.Collection dspaceCollection = findCollection(context, collectionId,
|
||||
org.dspace.core.Constants.READ);
|
||||
writeStats(dspaceCollection, UsageEvent.Action.VIEW, user_ip, user_agent, xforwardedfor,
|
||||
headers, request, context);
|
||||
|
||||
items = new ArrayList<>();
|
||||
Iterator<org.dspace.content.Item> dspaceItems = itemService.findByCollection(context, dspaceCollection,
|
||||
limit, offset);
|
||||
|
||||
while (dspaceItems.hasNext()) {
|
||||
org.dspace.content.Item dspaceItem = dspaceItems.next();
|
||||
|
||||
if (itemService.isItemListedForUser(context, dspaceItem)) {
|
||||
items.add(new Item(dspaceItem, servletContext, expand, context));
|
||||
writeStats(dspaceItem, UsageEvent.Action.VIEW, user_ip, user_agent, xforwardedfor,
|
||||
headers, request, context);
|
||||
}
|
||||
}
|
||||
|
||||
context.complete();
|
||||
} catch (SQLException e) {
|
||||
processException("Could not read collection items, SQLException. Message: " + e, context);
|
||||
} catch (ContextException e) {
|
||||
processException("Could not read collection items, ContextException. Message: " + e.getMessage(), context);
|
||||
} finally {
|
||||
processFinally(context);
|
||||
}
|
||||
|
||||
log.trace("All items in collection(id=" + collectionId + ") were successfully read.");
|
||||
return items.toArray(new Item[0]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create item in collection. Item can be without filled metadata.
|
||||
*
|
||||
* @param collectionId Id of collection in which will be item created.
|
||||
* @param item Item filled only with metadata, other variables are ignored.
|
||||
* @param user_ip User's IP address.
|
||||
* @param user_agent User agent string (specifies browser used and its version).
|
||||
* @param xforwardedfor When accessed via a reverse proxy, the application sees the proxy's IP as the
|
||||
* source of the request. The proxy may be configured to add the
|
||||
* "X-Forwarded-For" HTTP header containing the original IP of the client
|
||||
* so that the reverse-proxied application can get the client's IP.
|
||||
* @param headers If you want to access the collection as the user logged into the
|
||||
* context. The value of the "rest-dspace-token" header must be set
|
||||
* to the token received from the login method response.
|
||||
* @param request Servlet's HTTP request object.
|
||||
* @return Return status code with item. Return status (OK)200 if item was
|
||||
* created. NOT_FOUND(404) if id of collection does not exists.
|
||||
* UNAUTHORIZED(401) if user have not permission to write items in
|
||||
* collection.
|
||||
* @throws WebApplicationException It is thrown when was problem with database reading or
|
||||
* writing (SQLException) or problem with creating
|
||||
* context(ContextException) or problem with authorization to
|
||||
* collection or IOException or problem with index item into
|
||||
* browse index. It is thrown by NOT_FOUND and UNATHORIZED
|
||||
* status codes, too.
|
||||
*/
|
||||
@POST
|
||||
@Path("/{collection_id}/items")
|
||||
@Consumes( {MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
|
||||
public Item addCollectionItem(@PathParam("collection_id") String collectionId, Item item,
|
||||
@QueryParam("userIP") String user_ip, @QueryParam("userAgent") String user_agent,
|
||||
@QueryParam("xforwardedfor") String xforwardedfor, @Context HttpHeaders headers,
|
||||
@Context HttpServletRequest request)
|
||||
throws WebApplicationException {
|
||||
|
||||
log.info("Create item in collection(id=" + collectionId + ").");
|
||||
org.dspace.core.Context context = null;
|
||||
Item returnItem = null;
|
||||
|
||||
try {
|
||||
context = createContext();
|
||||
org.dspace.content.Collection dspaceCollection = findCollection(context, collectionId,
|
||||
org.dspace.core.Constants.WRITE);
|
||||
|
||||
writeStats(dspaceCollection, UsageEvent.Action.UPDATE, user_ip, user_agent, xforwardedfor,
|
||||
headers, request, context);
|
||||
|
||||
log.trace("Creating item in collection(id=" + collectionId + ").");
|
||||
org.dspace.content.WorkspaceItem workspaceItem = workspaceItemService
|
||||
.create(context, dspaceCollection, false);
|
||||
org.dspace.content.Item dspaceItem = workspaceItem.getItem();
|
||||
|
||||
log.trace("Adding metadata to item(id=" + dspaceItem.getID() + ").");
|
||||
if (item.getMetadata() != null) {
|
||||
for (MetadataEntry entry : item.getMetadata()) {
|
||||
String data[] = mySplit(entry.getKey());
|
||||
itemService.addMetadata(context, dspaceItem, data[0], data[1], data[2], entry.getLanguage(),
|
||||
entry.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
workspaceItemService.update(context, workspaceItem);
|
||||
|
||||
try {
|
||||
// Must insert the item into workflow
|
||||
log.trace("Starting workflow for item(id=" + dspaceItem.getID() + ").");
|
||||
workflowService.start(context, workspaceItem);
|
||||
} catch (Exception e) {
|
||||
log.error(
|
||||
LogHelper.getHeader(context, "Error while starting workflow",
|
||||
"Item id: " + dspaceItem.getID()),
|
||||
e);
|
||||
throw new ContextException("Error while starting workflow for item(id=" + dspaceItem.getID() + ")", e);
|
||||
}
|
||||
|
||||
returnItem = new Item(workspaceItem.getItem(), servletContext, "", context);
|
||||
|
||||
context.complete();
|
||||
|
||||
} catch (SQLException e) {
|
||||
processException("Could not add item into collection(id=" + collectionId + "), SQLException. Message: " + e,
|
||||
context);
|
||||
} catch (AuthorizeException e) {
|
||||
processException(
|
||||
"Could not add item into collection(id=" + collectionId + "), AuthorizeException. Message: " + e,
|
||||
context);
|
||||
} catch (ContextException e) {
|
||||
processException(
|
||||
"Could not add item into collection(id=" + collectionId + "), ContextException. Message: " + e
|
||||
.getMessage(),
|
||||
context);
|
||||
} finally {
|
||||
processFinally(context);
|
||||
}
|
||||
|
||||
log.info(
|
||||
"Item successfully created in collection(id=" + collectionId + "). Item handle=" + returnItem.getHandle());
|
||||
return returnItem;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update collection. It replace all properties.
|
||||
*
|
||||
* @param collectionId Id of collection in DSpace.
|
||||
* @param collection Collection which will replace properties of actual collection.
|
||||
* @param user_ip User's IP address.
|
||||
* @param user_agent User agent string (specifies browser used and its version).
|
||||
* @param xforwardedfor When accessed via a reverse proxy, the application sees the proxy's IP as the
|
||||
* source of the request. The proxy may be configured to add the
|
||||
* "X-Forwarded-For" HTTP header containing the original IP of the client
|
||||
* so that the reverse-proxied application can get the client's IP.
|
||||
* @param headers If you want to access the collection as the user logged into the
|
||||
* context. The value of the "rest-dspace-token" header must be set
|
||||
* to the token received from the login method response.
|
||||
* @param request Servlet's HTTP request object.
|
||||
* @return Return response 200 if was everything all right. Otherwise 400
|
||||
* when id of community was incorrect or 401 if was problem with
|
||||
* permission to write into collection.
|
||||
* @throws WebApplicationException It is thrown when was problem with database reading or
|
||||
* writing. Or problem with authorization to collection. Or
|
||||
* problem with creating context.
|
||||
*/
|
||||
@PUT
|
||||
@Path("/{collection_id}")
|
||||
@Consumes( {MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
|
||||
public Response updateCollection(@PathParam("collection_id") String collectionId,
|
||||
org.dspace.rest.common.Collection collection, @QueryParam("userIP") String user_ip,
|
||||
@QueryParam("userAgent") String user_agent,
|
||||
@QueryParam("xforwardedfor") String xforwardedfor,
|
||||
@Context HttpHeaders headers, @Context HttpServletRequest request)
|
||||
throws WebApplicationException {
|
||||
|
||||
log.info("Updating collection(id=" + collectionId + ").");
|
||||
org.dspace.core.Context context = null;
|
||||
|
||||
try {
|
||||
context = createContext();
|
||||
org.dspace.content.Collection dspaceCollection = findCollection(context, collectionId,
|
||||
org.dspace.core.Constants.WRITE);
|
||||
|
||||
writeStats(dspaceCollection, UsageEvent.Action.UPDATE, user_ip, user_agent, xforwardedfor,
|
||||
headers, request, context);
|
||||
|
||||
collectionService.setMetadataSingleValue(context, dspaceCollection,
|
||||
MD_NAME, collection.getName(), null);
|
||||
collectionService.setMetadataSingleValue(context, dspaceCollection,
|
||||
MD_LICENSE, collection.getLicense(), null);
|
||||
|
||||
// dspaceCollection.setLogo(collection.getLogo()); // TODO Add this option.
|
||||
collectionService.setMetadataSingleValue(context, dspaceCollection,
|
||||
MD_COPYRIGHT_TEXT, collection.getCopyrightText(), null);
|
||||
collectionService.setMetadataSingleValue(context, dspaceCollection,
|
||||
MD_INTRODUCTORY_TEXT, collection.getIntroductoryText(), null);
|
||||
collectionService.setMetadataSingleValue(context, dspaceCollection,
|
||||
MD_SHORT_DESCRIPTION, collection.getShortDescription(), null);
|
||||
collectionService.setMetadataSingleValue(context, dspaceCollection,
|
||||
MD_SIDEBAR_TEXT, collection.getSidebarText(), null);
|
||||
collectionService.update(context, dspaceCollection);
|
||||
|
||||
context.complete();
|
||||
|
||||
} catch (ContextException e) {
|
||||
processException(
|
||||
"Could not update collection(id=" + collectionId + "), ContextException. Message: " + e.getMessage(),
|
||||
context);
|
||||
} catch (SQLException e) {
|
||||
processException("Could not update collection(id=" + collectionId + "), SQLException. Message: " + e,
|
||||
context);
|
||||
} catch (AuthorizeException e) {
|
||||
processException("Could not update collection(id=" + collectionId + "), AuthorizeException. Message: " + e,
|
||||
context);
|
||||
} finally {
|
||||
processFinally(context);
|
||||
}
|
||||
|
||||
log.info("Collection(id=" + collectionId + ") successfully updated.");
|
||||
return Response.ok().build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete collection.
|
||||
*
|
||||
* @param collectionId Id of collection which will be deleted.
|
||||
* @param user_ip User's IP address.
|
||||
* @param user_agent User agent string (specifies browser used and its version).
|
||||
* @param xforwardedfor When accessed via a reverse proxy, the application sees the proxy's IP as the
|
||||
* source of the request. The proxy may be configured to add the
|
||||
* "X-Forwarded-For" HTTP header containing the original IP of the client
|
||||
* so that the reverse-proxied application can get the client's IP.
|
||||
* @param headers If you want to access the collection as the user logged into the
|
||||
* context. The value of the "rest-dspace-token" header must be set
|
||||
* to the token received from the login method response.
|
||||
* @param request Servlet's HTTP request object.
|
||||
* @return Return response code OK(200) if was everything all right.
|
||||
* Otherwise return NOT_FOUND(404) if was id of community or
|
||||
* collection incorrect. Or (UNAUTHORIZED)401 if was problem with
|
||||
* permission to community or collection.
|
||||
* @throws WebApplicationException Thrown if there was a problem with creating context or problem
|
||||
* with database reading or writing. Or problem with deleting
|
||||
* collection caused by IOException or authorization.
|
||||
*/
|
||||
@DELETE
|
||||
@Path("/{collection_id}")
|
||||
@Consumes( {MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
|
||||
public Response deleteCollection(@PathParam("collection_id") String collectionId,
|
||||
@QueryParam("userIP") String user_ip,
|
||||
@QueryParam("userAgent") String user_agent,
|
||||
@QueryParam("xforwardedfor") String xforwardedfor,
|
||||
@Context HttpHeaders headers, @Context HttpServletRequest request)
|
||||
throws WebApplicationException {
|
||||
|
||||
log.info("Delete collection(id=" + collectionId + ").");
|
||||
org.dspace.core.Context context = null;
|
||||
|
||||
try {
|
||||
context = createContext();
|
||||
org.dspace.content.Collection dspaceCollection = findCollection(context, collectionId,
|
||||
org.dspace.core.Constants.DELETE);
|
||||
|
||||
writeStats(dspaceCollection, UsageEvent.Action.REMOVE, user_ip, user_agent, xforwardedfor,
|
||||
headers, request, context);
|
||||
|
||||
collectionService.delete(context, dspaceCollection);
|
||||
collectionService.update(context, dspaceCollection);
|
||||
|
||||
context.complete();
|
||||
} catch (ContextException e) {
|
||||
processException(
|
||||
"Could not delete collection(id=" + collectionId + "), ContextException. Message: " + e.getMessage(),
|
||||
context);
|
||||
} catch (SQLException e) {
|
||||
processException("Could not delete collection(id=" + collectionId + "), SQLException. Message: " + e,
|
||||
context);
|
||||
} catch (AuthorizeException e) {
|
||||
processException("Could not delete collection(id=" + collectionId + "), AuthorizeException. Message: " + e,
|
||||
context);
|
||||
} catch (IOException e) {
|
||||
processException("Could not delete collection(id=" + collectionId + "), IOException. Message: " + e,
|
||||
context);
|
||||
} finally {
|
||||
processFinally(context);
|
||||
}
|
||||
|
||||
log.info("Collection(id=" + collectionId + ") was successfully deleted.");
|
||||
return Response.ok().build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete item in collection.
|
||||
*
|
||||
* @param collectionId Id of collection which will be deleted.
|
||||
* @param itemId Id of item in colletion.
|
||||
* @param user_ip User's IP address.
|
||||
* @param user_agent User agent string (specifies browser used and its version).
|
||||
* @param xforwardedfor When accessed via a reverse proxy, the application sees the proxy's IP as the
|
||||
* source of the request. The proxy may be configured to add the
|
||||
* "X-Forwarded-For" HTTP header containing the original IP of the client
|
||||
* so that the reverse-proxied application can get the client's IP.
|
||||
* @param headers If you want to access the collection as the user logged into the
|
||||
* context. The value of the "rest-dspace-token" header must be set
|
||||
* to the token received from the login method response.
|
||||
* @param request Servlet's HTTP request object.
|
||||
* @return It returns status code: OK(200). NOT_FOUND(404) if item or
|
||||
* collection was not found, UNAUTHORIZED(401) if user is not
|
||||
* allowed to delete item or permission to write into collection.
|
||||
* @throws WebApplicationException It can be thrown by: SQLException, when was problem with
|
||||
* database reading or writting. AuthorizeException, when was
|
||||
* problem with authorization to item or collection.
|
||||
* IOException, when was problem with removing item.
|
||||
* ContextException, when was problem with creating context of
|
||||
* DSpace.
|
||||
*/
|
||||
@DELETE
|
||||
@Path("/{collection_id}/items/{item_id}")
|
||||
@Consumes( {MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
|
||||
public Response deleteCollectionItem(@PathParam("collection_id") String collectionId,
|
||||
@PathParam("item_id") String itemId,
|
||||
@QueryParam("userIP") String user_ip,
|
||||
@QueryParam("userAgent") String user_agent,
|
||||
@QueryParam("xforwardedfor") String xforwardedfor,
|
||||
@Context HttpHeaders headers, @Context HttpServletRequest request)
|
||||
throws WebApplicationException {
|
||||
|
||||
log.info("Delete item(id=" + itemId + ") in collection(id=" + collectionId + ").");
|
||||
org.dspace.core.Context context = null;
|
||||
|
||||
try {
|
||||
context = createContext();
|
||||
|
||||
org.dspace.content.Collection dspaceCollection = collectionService
|
||||
.findByIdOrLegacyId(context, collectionId);
|
||||
org.dspace.content.Item item = itemService.findByIdOrLegacyId(context, itemId);
|
||||
|
||||
|
||||
if (dspaceCollection == null) {
|
||||
//throw collection not exist
|
||||
log.warn("Collection(id=" + itemId + ") was not found!");
|
||||
throw new WebApplicationException(Response.Status.NOT_FOUND);
|
||||
}
|
||||
|
||||
if (item == null) {
|
||||
//throw item not exist
|
||||
log.warn("Item(id=" + itemId + ") was not found!");
|
||||
throw new WebApplicationException(Response.Status.NOT_FOUND);
|
||||
}
|
||||
|
||||
if (!authorizeService.authorizeActionBoolean(context, item, Constants.REMOVE)
|
||||
|| !authorizeService.authorizeActionBoolean(context, dspaceCollection, Constants.REMOVE)) {
|
||||
//throw auth
|
||||
if (context.getCurrentUser() != null) {
|
||||
log.error(
|
||||
"User(" + context.getCurrentUser().getEmail() + ") does not have permission to delete item!");
|
||||
} else {
|
||||
log.error("User(anonymous) has not permission to delete item!");
|
||||
}
|
||||
throw new WebApplicationException(Response.Status.UNAUTHORIZED);
|
||||
}
|
||||
|
||||
collectionService.removeItem(context, dspaceCollection, item);
|
||||
collectionService.update(context, dspaceCollection);
|
||||
itemService.update(context, item);
|
||||
|
||||
writeStats(dspaceCollection, UsageEvent.Action.UPDATE, user_ip, user_agent, xforwardedfor,
|
||||
headers, request, context);
|
||||
writeStats(item, UsageEvent.Action.REMOVE, user_ip, user_agent, xforwardedfor, headers, request, context);
|
||||
|
||||
context.complete();
|
||||
|
||||
} catch (ContextException e) {
|
||||
processException("Could not delete item(id=" + itemId + ") in collection(id=" + collectionId
|
||||
+ "), ContextException. Message: " + e.getMessage(), context);
|
||||
} catch (SQLException e) {
|
||||
processException("Could not delete item(id=" + itemId + ") in collection(id=" + collectionId
|
||||
+ "), SQLException. Message: " + e, context);
|
||||
} catch (AuthorizeException e) {
|
||||
processException("Could not delete item(id=" + itemId + ") in collection(id=" + collectionId
|
||||
+ "), AuthorizeException. Message: " + e, context);
|
||||
} catch (IOException e) {
|
||||
processException("Could not delete item(id=" + itemId + ") in collection(id=" + collectionId
|
||||
+ "), IOException. Message: " + e, context);
|
||||
} finally {
|
||||
processFinally(context);
|
||||
}
|
||||
|
||||
log.info("Item(id=" + itemId + ") in collection(id=" + collectionId + ") was successfully deleted.");
|
||||
return Response.ok().build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Search for first collection with passed name.
|
||||
*
|
||||
* @param name Name of collection.
|
||||
* @param headers If you want to access the collection as the user logged into the
|
||||
* context. The value of the "rest-dspace-token" header must be set
|
||||
* to the token received from the login method response.
|
||||
* @return It returns null if collection was not found. Otherwise returns
|
||||
* first founded collection.
|
||||
* @throws WebApplicationException A general exception a servlet can throw when it encounters difficulty.
|
||||
*/
|
||||
@POST
|
||||
@Path("/find-collection")
|
||||
@Consumes( {MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
|
||||
@Produces( {MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
|
||||
public Collection findCollectionByName(String name, @Context HttpHeaders headers) throws WebApplicationException {
|
||||
log.info("Searching for first collection with name=" + name + ".");
|
||||
org.dspace.core.Context context = null;
|
||||
Collection collection = null;
|
||||
|
||||
try {
|
||||
context = createContext();
|
||||
|
||||
List<org.dspace.content.Collection> dspaceCollections = collectionService.findAll(context);
|
||||
//TODO, this would be more efficient with a findByName query
|
||||
|
||||
for (org.dspace.content.Collection dspaceCollection : dspaceCollections) {
|
||||
if (authorizeService
|
||||
.authorizeActionBoolean(context, dspaceCollection, org.dspace.core.Constants.READ)) {
|
||||
if (dspaceCollection.getName().equals(name)) {
|
||||
collection = new Collection(dspaceCollection, servletContext, "", context, 100, 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
context.complete();
|
||||
|
||||
} catch (SQLException e) {
|
||||
processException(
|
||||
"Something went wrong while searching for collection(name=" + name + ") from database. Message: "
|
||||
+ e, context);
|
||||
} catch (ContextException e) {
|
||||
processException(
|
||||
"Something went wrong while searching for collection(name=" + name + "), ContextError. Message: "
|
||||
+ e.getMessage(), context);
|
||||
} finally {
|
||||
processFinally(context);
|
||||
}
|
||||
|
||||
if (collection == null) {
|
||||
log.info("Collection was not found.");
|
||||
} else {
|
||||
log.info("Collection was found with id(" + collection.getUUID() + ").");
|
||||
}
|
||||
return collection;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find collection from DSpace database. It is encapsulation of method
|
||||
* org.dspace.content.Collection.find with checking if item exist and if
|
||||
* user logged into context has permission to do passed action.
|
||||
*
|
||||
* @param context Context of actual logged user.
|
||||
* @param id Id of collection in DSpace.
|
||||
* @param action Constant from org.dspace.core.Constants.
|
||||
* @return It returns DSpace collection.
|
||||
* @throws WebApplicationException Is thrown when item with passed id is not exists and if user
|
||||
* has no permission to do passed action.
|
||||
*/
|
||||
private org.dspace.content.Collection findCollection(org.dspace.core.Context context, String id, int action)
|
||||
throws WebApplicationException {
|
||||
org.dspace.content.Collection collection = null;
|
||||
try {
|
||||
collection = collectionService.findByIdOrLegacyId(context, id);
|
||||
|
||||
if (collection == null) {
|
||||
context.abort();
|
||||
log.warn("Collection(id=" + id + ") was not found!");
|
||||
throw new WebApplicationException(Response.Status.NOT_FOUND);
|
||||
} else if (!authorizeService.authorizeActionBoolean(context, collection, action)) {
|
||||
context.abort();
|
||||
if (context.getCurrentUser() != null) {
|
||||
log.error("User(" + context.getCurrentUser().getEmail() + ") has not permission to "
|
||||
+ getActionString(action) + " collection!");
|
||||
} else {
|
||||
log.error("User(anonymous) has not permission to " + getActionString(action) + " collection!");
|
||||
}
|
||||
throw new WebApplicationException(Response.Status.UNAUTHORIZED);
|
||||
}
|
||||
|
||||
} catch (SQLException e) {
|
||||
processException("Something get wrong while finding collection(id=" + id + "). SQLException, Message: " + e,
|
||||
context);
|
||||
}
|
||||
return collection;
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@@ -1,19 +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.rest;
|
||||
|
||||
import org.glassfish.jersey.jackson.JacksonFeature;
|
||||
import org.glassfish.jersey.server.ResourceConfig;
|
||||
|
||||
public class DSpaceRestApplication extends ResourceConfig {
|
||||
|
||||
public DSpaceRestApplication() {
|
||||
register(JacksonFeature.class);
|
||||
packages("org.dspace.rest");
|
||||
}
|
||||
}
|
@@ -1,215 +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.rest;
|
||||
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import javax.servlet.ServletContext;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.ws.rs.DefaultValue;
|
||||
import javax.ws.rs.GET;
|
||||
import javax.ws.rs.Path;
|
||||
import javax.ws.rs.PathParam;
|
||||
import javax.ws.rs.Produces;
|
||||
import javax.ws.rs.QueryParam;
|
||||
import javax.ws.rs.WebApplicationException;
|
||||
import javax.ws.rs.core.Context;
|
||||
import javax.ws.rs.core.HttpHeaders;
|
||||
import javax.ws.rs.core.MediaType;
|
||||
import javax.ws.rs.core.Response;
|
||||
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.dspace.authorize.factory.AuthorizeServiceFactory;
|
||||
import org.dspace.authorize.service.AuthorizeService;
|
||||
import org.dspace.content.factory.ContentServiceFactory;
|
||||
import org.dspace.content.service.CollectionService;
|
||||
import org.dspace.rest.common.FilteredCollection;
|
||||
import org.dspace.rest.exceptions.ContextException;
|
||||
import org.dspace.services.ConfigurationService;
|
||||
import org.dspace.services.factory.DSpaceServicesFactory;
|
||||
import org.dspace.usage.UsageEvent;
|
||||
|
||||
/*
|
||||
* This class provides the items within a collection evaluated against a set of Item Filters.
|
||||
*
|
||||
* @author Terry Brady, Georgetown University
|
||||
*/
|
||||
@Path("/filtered-collections")
|
||||
public class FilteredCollectionsResource extends Resource {
|
||||
protected AuthorizeService authorizeService = AuthorizeServiceFactory.getInstance().getAuthorizeService();
|
||||
protected CollectionService collectionService = ContentServiceFactory.getInstance().getCollectionService();
|
||||
protected ConfigurationService configurationService = DSpaceServicesFactory.getInstance().getConfigurationService();
|
||||
private static Logger log = org.apache.logging.log4j.LogManager.getLogger(FilteredCollectionsResource.class);
|
||||
|
||||
/**
|
||||
* Return array of all collections in DSpace. You can add more properties
|
||||
* through expand parameter.
|
||||
*
|
||||
* @param expand String in which is what you want to add to returned instance
|
||||
* of collection. Options are: "all", "parentCommunityList",
|
||||
* "parentCommunity", "topCommunity", "items", "license" and "logo".
|
||||
* If you want to use multiple options, it must be separated by commas.
|
||||
* @param limit Limit value for items in list in collection. Default value is
|
||||
* 100.
|
||||
* @param offset Offset of start index in list of items of collection. Default
|
||||
* value is 0.
|
||||
* @param user_ip User's IP address.
|
||||
* @param user_agent User agent string (specifies browser used and its version).
|
||||
* @param filters Comma separated list of Item Filters to use to evaluate against
|
||||
* the items in a collection
|
||||
* @param xforwardedfor When accessed via a reverse proxy, the application sees the proxy's IP as the
|
||||
* source of the request. The proxy may be configured to add the
|
||||
* "X-Forwarded-For" HTTP header containing the original IP of the client
|
||||
* so that the reverse-proxied application can get the client's IP.
|
||||
* @param servletContext Context of the servlet container.
|
||||
* @param headers If you want to access the collections as the user logged into the
|
||||
* context. The value of the "rest-dspace-token" header must be set
|
||||
* to the token received from the login method response.
|
||||
* @param request Servlet's HTTP request object.
|
||||
* @return Return array of collection, on which has logged user permission
|
||||
* to view.
|
||||
* @throws WebApplicationException It is thrown when was problem with database reading
|
||||
* (SQLException) or problem with creating
|
||||
* context(ContextException).
|
||||
*/
|
||||
@GET
|
||||
@Produces( {MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
|
||||
public org.dspace.rest.common.FilteredCollection[] getCollections(@QueryParam("expand") String expand,
|
||||
@QueryParam("limit") @DefaultValue("100")
|
||||
Integer limit,
|
||||
@QueryParam("offset") @DefaultValue("0")
|
||||
Integer offset,
|
||||
@QueryParam("userIP") String user_ip,
|
||||
@QueryParam("userAgent") String user_agent,
|
||||
@QueryParam("filters") @DefaultValue("is_item")
|
||||
String filters,
|
||||
@QueryParam("xforwardedfor") String xforwardedfor,
|
||||
@Context ServletContext servletContext,
|
||||
@Context HttpHeaders headers,
|
||||
@Context HttpServletRequest request)
|
||||
throws WebApplicationException {
|
||||
|
||||
log.info("Reading all filtered collections.(offset=" + offset + ",limit=" + limit + ")");
|
||||
org.dspace.core.Context context = null;
|
||||
List<FilteredCollection> collections = new ArrayList<FilteredCollection>();
|
||||
|
||||
try {
|
||||
context = createContext();
|
||||
|
||||
if (!((limit != null) && (limit >= 0) && (offset != null) && (offset >= 0))) {
|
||||
log.warn("Paging was badly set.");
|
||||
limit = 100;
|
||||
offset = 0;
|
||||
}
|
||||
|
||||
List<org.dspace.content.Collection> dspaceCollections = collectionService.findAll(context, limit, offset);
|
||||
for (org.dspace.content.Collection dspaceCollection : dspaceCollections) {
|
||||
if (authorizeService
|
||||
.authorizeActionBoolean(context, dspaceCollection, org.dspace.core.Constants.READ)) {
|
||||
FilteredCollection collection = new org.dspace.rest.common.FilteredCollection(dspaceCollection,
|
||||
servletContext,
|
||||
filters, expand,
|
||||
context, limit,
|
||||
offset);
|
||||
collections.add(collection);
|
||||
writeStats(dspaceCollection, UsageEvent.Action.VIEW, user_ip, user_agent,
|
||||
xforwardedfor, headers, request, context);
|
||||
}
|
||||
}
|
||||
context.complete();
|
||||
} catch (SQLException e) {
|
||||
processException("Something went wrong while reading collections from database. Message: " + e, context);
|
||||
} catch (ContextException e) {
|
||||
processException("Something went wrong while reading collections, ContextError. Message: " + e.getMessage(),
|
||||
context);
|
||||
} finally {
|
||||
processFinally(context);
|
||||
}
|
||||
|
||||
log.trace("All collections were successfully read.");
|
||||
return collections.toArray(new org.dspace.rest.common.FilteredCollection[0]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return instance of collection with passed id. You can add more properties
|
||||
* through expand parameter.
|
||||
*
|
||||
* @param collection_id Id of collection in DSpace.
|
||||
* @param expand String in which is what you want to add to returned instance
|
||||
* of collection. Options are: "all", "parentCommunityList",
|
||||
* "parentCommunity", "topCommunity", "items", "license" and "logo".
|
||||
* If you want to use multiple options, it must be separated by commas.
|
||||
* @param limit Limit value for items in list in collection. Default value is
|
||||
* 100.
|
||||
* @param offset Offset of start index in list of items of collection. Default
|
||||
* value is 0.
|
||||
* @param user_ip User's IP address.
|
||||
* @param user_agent User agent string (specifies browser used and its version).
|
||||
* @param xforwardedfor When accessed via a reverse proxy, the application sees the proxy's IP as the
|
||||
* source of the request. The proxy may be configured to add the
|
||||
* "X-Forwarded-For" HTTP header containing the original IP of the client
|
||||
* so that the reverse-proxied application can get the client's IP.
|
||||
* @param filters Comma separated list of Item Filters to use to evaluate against
|
||||
* the items in a collection
|
||||
* @param headers If you want to access the collection as the user logged into the
|
||||
* context. The value of the "rest-dspace-token" header must be set
|
||||
* to the token received from the login method response.
|
||||
* @param request Servlet's HTTP request object.
|
||||
* @param servletContext Context of the servlet container.
|
||||
* @return Return instance of collection. It can also return status code
|
||||
* NOT_FOUND(404) if id of collection is incorrect or status code
|
||||
* UNATHORIZED(401) if user has no permission to read collection.
|
||||
* @throws WebApplicationException It is thrown when was problem with database reading
|
||||
* (SQLException) or problem with creating
|
||||
* context(ContextException). It is thrown by NOT_FOUND and
|
||||
* UNATHORIZED status codes, too.
|
||||
*/
|
||||
@GET
|
||||
@Path("/{collection_id}")
|
||||
@Produces( {MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
|
||||
public org.dspace.rest.common.FilteredCollection getCollection(@PathParam("collection_id") String collection_id,
|
||||
@QueryParam("expand") String expand,
|
||||
@QueryParam("limit") @DefaultValue("1000") Integer
|
||||
limit,
|
||||
@QueryParam("offset") @DefaultValue("0") Integer
|
||||
offset,
|
||||
@QueryParam("userIP") String user_ip,
|
||||
@QueryParam("userAgent") String user_agent,
|
||||
@QueryParam("xforwardedfor") String xforwardedfor,
|
||||
@QueryParam("filters") @DefaultValue("is_item")
|
||||
String filters,
|
||||
@Context HttpHeaders headers,
|
||||
@Context HttpServletRequest request,
|
||||
@Context ServletContext servletContext) {
|
||||
org.dspace.core.Context context = null;
|
||||
FilteredCollection retColl = new org.dspace.rest.common.FilteredCollection();
|
||||
try {
|
||||
context = createContext();
|
||||
|
||||
org.dspace.content.Collection collection = collectionService.findByIdOrLegacyId(context, collection_id);
|
||||
if (authorizeService.authorizeActionBoolean(context, collection, org.dspace.core.Constants.READ)) {
|
||||
writeStats(collection, UsageEvent.Action.VIEW, user_ip,
|
||||
user_agent, xforwardedfor, headers, request, context);
|
||||
retColl = new org.dspace.rest.common.FilteredCollection(
|
||||
collection, servletContext, filters, expand, context, limit, offset);
|
||||
} else {
|
||||
throw new WebApplicationException(Response.Status.UNAUTHORIZED);
|
||||
}
|
||||
context.complete();
|
||||
} catch (SQLException e) {
|
||||
processException(e.getMessage(), context);
|
||||
} catch (ContextException e) {
|
||||
processException(String.format("Could not read collection %s. %s", collection_id, e.getMessage()),
|
||||
context);
|
||||
} finally {
|
||||
processFinally(context);
|
||||
}
|
||||
return retColl;
|
||||
}
|
||||
}
|
@@ -1,217 +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.rest;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import javax.servlet.ServletContext;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.ws.rs.DefaultValue;
|
||||
import javax.ws.rs.GET;
|
||||
import javax.ws.rs.Path;
|
||||
import javax.ws.rs.Produces;
|
||||
import javax.ws.rs.QueryParam;
|
||||
import javax.ws.rs.WebApplicationException;
|
||||
import javax.ws.rs.core.Context;
|
||||
import javax.ws.rs.core.HttpHeaders;
|
||||
import javax.ws.rs.core.MediaType;
|
||||
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.dspace.authorize.AuthorizeException;
|
||||
import org.dspace.content.Item;
|
||||
import org.dspace.content.MetadataField;
|
||||
import org.dspace.content.factory.ContentServiceFactory;
|
||||
import org.dspace.content.service.CollectionService;
|
||||
import org.dspace.content.service.ItemService;
|
||||
import org.dspace.content.service.MetadataFieldService;
|
||||
import org.dspace.content.service.MetadataSchemaService;
|
||||
import org.dspace.content.service.SiteService;
|
||||
import org.dspace.rest.common.ItemFilter;
|
||||
import org.dspace.rest.common.ItemFilterQuery;
|
||||
import org.dspace.rest.exceptions.ContextException;
|
||||
import org.dspace.rest.filter.ItemFilterSet;
|
||||
import org.dspace.services.ConfigurationService;
|
||||
import org.dspace.services.factory.DSpaceServicesFactory;
|
||||
import org.dspace.usage.UsageEvent;
|
||||
|
||||
/*
|
||||
* This class retrieves items by a constructed metadata query evaluated against a set of Item Filters.
|
||||
*
|
||||
* @author Terry Brady, Georgetown University
|
||||
*/
|
||||
@Path("/filtered-items")
|
||||
public class FilteredItemsResource extends Resource {
|
||||
protected ItemService itemService = ContentServiceFactory.getInstance().getItemService();
|
||||
protected MetadataFieldService metadataFieldService = ContentServiceFactory.getInstance().getMetadataFieldService();
|
||||
protected MetadataSchemaService metadataSchemaService = ContentServiceFactory.getInstance()
|
||||
.getMetadataSchemaService();
|
||||
protected CollectionService collectionService = ContentServiceFactory.getInstance().getCollectionService();
|
||||
protected SiteService siteService = ContentServiceFactory.getInstance().getSiteService();
|
||||
protected ConfigurationService configurationService = DSpaceServicesFactory.getInstance().getConfigurationService();
|
||||
|
||||
private static Logger log = org.apache.logging.log4j.LogManager.getLogger(FilteredItemsResource.class);
|
||||
|
||||
/**
|
||||
* Return instance of collection with passed id. You can add more properties
|
||||
* through expand parameter.
|
||||
*
|
||||
* @param expand String in which is what you want to add to returned instance
|
||||
* of collection. Options are: "all", "parentCommunityList",
|
||||
* "parentCommunity", "items", "license" and "logo". If you want
|
||||
* to use multiple options, it must be separated by commas.
|
||||
* @param limit Limit value for items in list in collection. Default value is
|
||||
* 100.
|
||||
* @param offset Offset of start index in list of items of collection. Default
|
||||
* value is 0.
|
||||
* @param user_ip User's IP address.
|
||||
* @param user_agent User agent string (specifies browser used and its version).
|
||||
* @param xforwardedfor When accessed via a reverse proxy, the application sees the proxy's IP as the
|
||||
* source of the request. The proxy may be configured to add the
|
||||
* "X-Forwarded-For" HTTP header containing the original IP of the client
|
||||
* so that the reverse-proxied application can get the client's IP.
|
||||
* @param filters Comma separated list of Item Filters to use to evaluate against
|
||||
* the items in a collection
|
||||
* @param query_field List of metadata fields to evaluate in a metadata query.
|
||||
* Each list value is used in conjunction with a query_op and query_field.
|
||||
* @param query_op List of metadata operators to use in a metadata query.
|
||||
* Each list value is used in conjunction with a query_field and query_field.
|
||||
* @param query_val List of metadata values to evaluate in a metadata query.
|
||||
* Each list value is used in conjunction with a query_value and query_op.
|
||||
* @param collSel List of collections to query.
|
||||
* @param headers If you want to access the collection as the user logged into the
|
||||
* context. The value of the "rest-dspace-token" header must be set
|
||||
* to the token received from the login method response.
|
||||
* @param request Servlet's HTTP request object.
|
||||
* @param servletContext Context of the servlet container.
|
||||
* @return Return instance of collection. It can also return status code
|
||||
* NOT_FOUND(404) if id of collection is incorrect or status code
|
||||
* UNATHORIZED(401) if user has no permission to read collection.
|
||||
* @throws WebApplicationException It is thrown when was problem with database reading
|
||||
* (SQLException) or problem with creating
|
||||
* context(ContextException). It is thrown by NOT_FOUND and
|
||||
* UNATHORIZED status codes, too.
|
||||
*/
|
||||
@GET
|
||||
@Produces( {MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
|
||||
public org.dspace.rest.common.ItemFilter getItemQuery(@QueryParam("expand") String expand,
|
||||
@QueryParam("limit") @DefaultValue("100") Integer limit,
|
||||
@QueryParam("offset") @DefaultValue("0") Integer offset,
|
||||
@QueryParam("userIP") String user_ip,
|
||||
@QueryParam("userAgent") String user_agent,
|
||||
@QueryParam("xforwardedfor") String xforwardedfor,
|
||||
@QueryParam("filters") @DefaultValue("is_item,all_filters")
|
||||
String filters,
|
||||
@QueryParam("query_field[]") @DefaultValue("dc.title")
|
||||
List<String> query_field,
|
||||
@QueryParam("query_op[]") @DefaultValue("exists")
|
||||
List<String> query_op,
|
||||
@QueryParam("query_val[]") @DefaultValue("") List<String>
|
||||
query_val,
|
||||
@QueryParam("collSel[]") @DefaultValue("") List<String>
|
||||
collSel,
|
||||
@Context HttpHeaders headers,
|
||||
@Context HttpServletRequest request,
|
||||
@Context ServletContext servletContext) {
|
||||
org.dspace.core.Context context = null;
|
||||
ItemFilterSet itemFilterSet = new ItemFilterSet(filters, true);
|
||||
ItemFilter result = itemFilterSet.getAllFiltersFilter();
|
||||
try {
|
||||
context = createContext();
|
||||
|
||||
int index = Math.min(query_field.size(), Math.min(query_op.size(), query_val.size()));
|
||||
List<ItemFilterQuery> itemFilterQueries = new ArrayList<ItemFilterQuery>();
|
||||
for (int i = 0; i < index; i++) {
|
||||
itemFilterQueries.add(new ItemFilterQuery(query_field.get(i), query_op.get(i), query_val.get(i)));
|
||||
}
|
||||
|
||||
String regexClause = configurationService.getProperty("rest.regex-clause");
|
||||
if (regexClause == null) {
|
||||
regexClause = "";
|
||||
}
|
||||
|
||||
List<UUID> uuids = getUuidsFromStrings(collSel);
|
||||
List<List<MetadataField>> listFieldList = getMetadataFieldsList(context, query_field);
|
||||
|
||||
Iterator<org.dspace.content.Item> childItems = itemService
|
||||
.findByMetadataQuery(context, listFieldList, query_op, query_val, uuids, regexClause, offset, limit);
|
||||
|
||||
int count = itemFilterSet.processSaveItems(context, servletContext, childItems, true, expand);
|
||||
writeStats(siteService.findSite(context), UsageEvent.Action.VIEW, user_ip, user_agent, xforwardedfor,
|
||||
headers, request, context);
|
||||
result.annotateQuery(query_field, query_op, query_val);
|
||||
result.setUnfilteredItemCount(count);
|
||||
context.complete();
|
||||
} catch (IOException e) {
|
||||
processException(e.getMessage(), context);
|
||||
} catch (SQLException e) {
|
||||
processException(e.getMessage(), context);
|
||||
} catch (AuthorizeException e) {
|
||||
processException(e.getMessage(), context);
|
||||
} catch (ContextException e) {
|
||||
processException("Unauthorized filtered item query. " + e.getMessage(), context);
|
||||
} finally {
|
||||
processFinally(context);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private List<List<MetadataField>> getMetadataFieldsList(org.dspace.core.Context context, List<String> query_field)
|
||||
throws SQLException {
|
||||
List<List<MetadataField>> listFieldList = new ArrayList<List<MetadataField>>();
|
||||
for (String s : query_field) {
|
||||
ArrayList<MetadataField> fields = new ArrayList<MetadataField>();
|
||||
listFieldList.add(fields);
|
||||
if (s.equals("*")) {
|
||||
continue;
|
||||
}
|
||||
String schema = "";
|
||||
String element = "";
|
||||
String qualifier = null;
|
||||
String[] parts = s.split("\\.");
|
||||
if (parts.length > 0) {
|
||||
schema = parts[0];
|
||||
}
|
||||
if (parts.length > 1) {
|
||||
element = parts[1];
|
||||
}
|
||||
if (parts.length > 2) {
|
||||
qualifier = parts[2];
|
||||
}
|
||||
|
||||
if (Item.ANY.equals(qualifier)) {
|
||||
for (MetadataField mf : metadataFieldService
|
||||
.findFieldsByElementNameUnqualified(context, schema, element)) {
|
||||
fields.add(mf);
|
||||
}
|
||||
} else {
|
||||
MetadataField mf = metadataFieldService.findByElement(context, schema, element, qualifier);
|
||||
if (mf != null) {
|
||||
fields.add(mf);
|
||||
}
|
||||
}
|
||||
}
|
||||
return listFieldList;
|
||||
}
|
||||
|
||||
private List<UUID> getUuidsFromStrings(List<String> collSel) {
|
||||
List<UUID> uuids = new ArrayList<UUID>();
|
||||
for (String s : collSel) {
|
||||
try {
|
||||
uuids.add(UUID.fromString(s));
|
||||
} catch (IllegalArgumentException e) {
|
||||
log.warn("Invalid collection UUID: " + s);
|
||||
}
|
||||
}
|
||||
return uuids;
|
||||
}
|
||||
|
||||
}
|
@@ -1,60 +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.rest;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.ws.rs.GET;
|
||||
import javax.ws.rs.Path;
|
||||
import javax.ws.rs.Produces;
|
||||
import javax.ws.rs.QueryParam;
|
||||
import javax.ws.rs.WebApplicationException;
|
||||
import javax.ws.rs.core.Context;
|
||||
import javax.ws.rs.core.HttpHeaders;
|
||||
import javax.ws.rs.core.MediaType;
|
||||
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.dspace.rest.common.ItemFilter;
|
||||
|
||||
/**
|
||||
* Class which provides read methods over the metadata registry.
|
||||
*
|
||||
* @author Terry Brady, Georgetown University
|
||||
*/
|
||||
@Path("/filters")
|
||||
public class FiltersResource {
|
||||
private static Logger log = org.apache.logging.log4j.LogManager.getLogger(FiltersResource.class);
|
||||
|
||||
/**
|
||||
* Return all Use Case Item Filters in DSpace.
|
||||
*
|
||||
* @param user_ip User's IP address.
|
||||
* @param user_agent User agent string (specifies browser used and its version).
|
||||
* @param xforwardedfor When accessed via a reverse proxy, the application sees the proxy's IP as the
|
||||
* source of the request. The proxy may be configured to add the
|
||||
* "X-Forwarded-For" HTTP header containing the original IP of the client
|
||||
* so that the reverse-proxied application can get the client's IP.
|
||||
* @param headers If you want to access the collection as the user logged into the
|
||||
* context. The value of the "rest-dspace-token" header must be set
|
||||
* to the token received from the login method response.
|
||||
* @param request Servlet's HTTP request object.
|
||||
* @return Return array of metadata schemas.
|
||||
* @throws WebApplicationException It can be caused by creating context or while was problem
|
||||
* with reading community from database(SQLException).
|
||||
*/
|
||||
@GET
|
||||
@Produces( {MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
|
||||
public ItemFilter[] getFilters(@QueryParam("userIP") String user_ip, @QueryParam("userAgent") String user_agent,
|
||||
@QueryParam("xforwardedfor") String xforwardedfor, @Context HttpHeaders headers,
|
||||
@Context HttpServletRequest request)
|
||||
throws WebApplicationException {
|
||||
|
||||
log.info("Reading all Item Filters.");
|
||||
return ItemFilter.getItemFilters(ItemFilter.ALL, false).toArray(new ItemFilter[0]);
|
||||
}
|
||||
|
||||
}
|
@@ -1,109 +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.rest;
|
||||
|
||||
import java.sql.SQLException;
|
||||
import javax.ws.rs.GET;
|
||||
import javax.ws.rs.Path;
|
||||
import javax.ws.rs.PathParam;
|
||||
import javax.ws.rs.Produces;
|
||||
import javax.ws.rs.QueryParam;
|
||||
import javax.ws.rs.WebApplicationException;
|
||||
import javax.ws.rs.core.HttpHeaders;
|
||||
import javax.ws.rs.core.MediaType;
|
||||
import javax.ws.rs.core.Response;
|
||||
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.dspace.authorize.factory.AuthorizeServiceFactory;
|
||||
import org.dspace.authorize.service.AuthorizeService;
|
||||
import org.dspace.content.factory.ContentServiceFactory;
|
||||
import org.dspace.content.service.DSpaceObjectService;
|
||||
import org.dspace.core.Constants;
|
||||
import org.dspace.handle.factory.HandleServiceFactory;
|
||||
import org.dspace.handle.service.HandleService;
|
||||
import org.dspace.rest.common.Collection;
|
||||
import org.dspace.rest.common.Community;
|
||||
import org.dspace.rest.common.DSpaceObject;
|
||||
import org.dspace.rest.common.Item;
|
||||
import org.dspace.rest.exceptions.ContextException;
|
||||
|
||||
/**
|
||||
* Created with IntelliJ IDEA.
|
||||
* User: peterdietz
|
||||
* Date: 10/7/13
|
||||
* Time: 1:54 PM
|
||||
* To change this template use File | Settings | File Templates.
|
||||
*/
|
||||
@Path("/handle")
|
||||
public class HandleResource extends Resource {
|
||||
protected HandleService handleService = HandleServiceFactory.getInstance().getHandleService();
|
||||
protected AuthorizeService authorizeService = AuthorizeServiceFactory.getInstance().getAuthorizeService();
|
||||
|
||||
private static Logger log = org.apache.logging.log4j.LogManager.getLogger(HandleResource.class);
|
||||
|
||||
@GET
|
||||
@Path("/{prefix}/{suffix}")
|
||||
@Produces( {MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
|
||||
public org.dspace.rest.common.DSpaceObject getObject(@PathParam("prefix") String prefix,
|
||||
@PathParam("suffix") String suffix,
|
||||
@QueryParam("expand") String expand,
|
||||
@javax.ws.rs.core.Context HttpHeaders headers) {
|
||||
DSpaceObject dSpaceObject = new DSpaceObject();
|
||||
org.dspace.core.Context context = null;
|
||||
|
||||
try {
|
||||
context = createContext();
|
||||
|
||||
org.dspace.content.DSpaceObject dso = handleService.resolveToObject(context, prefix + "/" + suffix);
|
||||
|
||||
if (dso == null) {
|
||||
throw new WebApplicationException(Response.Status.NOT_FOUND);
|
||||
}
|
||||
DSpaceObjectService dSpaceObjectService = ContentServiceFactory.getInstance().getDSpaceObjectService(dso);
|
||||
log.info("DSO Lookup by handle: [" + prefix + "] / [" + suffix + "] got result of: " + dSpaceObjectService
|
||||
.getTypeText(dso) + "_" + dso.getID());
|
||||
|
||||
if (authorizeService.authorizeActionBoolean(context, dso, org.dspace.core.Constants.READ)) {
|
||||
switch (dso.getType()) {
|
||||
case Constants.COMMUNITY:
|
||||
dSpaceObject = new Community((org.dspace.content.Community) dso, servletContext, expand,
|
||||
context);
|
||||
break;
|
||||
case Constants.COLLECTION:
|
||||
dSpaceObject = new Collection((org.dspace.content.Collection) dso, servletContext, expand,
|
||||
context, null, null);
|
||||
break;
|
||||
case Constants.ITEM:
|
||||
dSpaceObject = new Item((org.dspace.content.Item) dso, servletContext, expand, context);
|
||||
break;
|
||||
default:
|
||||
dSpaceObject = new DSpaceObject(dso, servletContext);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
throw new WebApplicationException(Response.Status.UNAUTHORIZED);
|
||||
}
|
||||
|
||||
context.complete();
|
||||
|
||||
} catch (SQLException e) {
|
||||
log.error(e.getMessage());
|
||||
throw new WebApplicationException(Response.Status.INTERNAL_SERVER_ERROR);
|
||||
} catch (ContextException e) {
|
||||
processException(
|
||||
"Could not read handle(prefix=" + prefix + "), (suffix=" + suffix + ") ContextException. Message:" + e
|
||||
.getMessage(),
|
||||
context);
|
||||
} finally {
|
||||
processFinally(context);
|
||||
}
|
||||
|
||||
return dSpaceObject;
|
||||
|
||||
}
|
||||
}
|
@@ -1,140 +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.rest;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.ws.rs.GET;
|
||||
import javax.ws.rs.Path;
|
||||
import javax.ws.rs.Produces;
|
||||
import javax.ws.rs.QueryParam;
|
||||
import javax.ws.rs.WebApplicationException;
|
||||
import javax.ws.rs.core.Context;
|
||||
import javax.ws.rs.core.HttpHeaders;
|
||||
import javax.ws.rs.core.MediaType;
|
||||
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.dspace.authorize.factory.AuthorizeServiceFactory;
|
||||
import org.dspace.authorize.service.AuthorizeService;
|
||||
import org.dspace.content.Collection;
|
||||
import org.dspace.content.Community;
|
||||
import org.dspace.content.Site;
|
||||
import org.dspace.content.factory.ContentServiceFactory;
|
||||
import org.dspace.content.service.CommunityService;
|
||||
import org.dspace.content.service.SiteService;
|
||||
import org.dspace.rest.common.HierarchyCollection;
|
||||
import org.dspace.rest.common.HierarchyCommunity;
|
||||
import org.dspace.rest.common.HierarchySite;
|
||||
import org.dspace.services.ConfigurationService;
|
||||
import org.dspace.services.factory.DSpaceServicesFactory;
|
||||
|
||||
|
||||
/*
|
||||
* This class retrieves the community hierarchy in an optimized format.
|
||||
*
|
||||
* @author Terry Brady, Georgetown University
|
||||
*/
|
||||
@Path("/hierarchy")
|
||||
@Produces( {MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
|
||||
public class HierarchyResource extends Resource {
|
||||
private static Logger log = org.apache.logging.log4j.LogManager.getLogger(HierarchyResource.class);
|
||||
protected SiteService siteService = ContentServiceFactory.getInstance().getSiteService();
|
||||
protected CommunityService communityService = ContentServiceFactory.getInstance().getCommunityService();
|
||||
protected AuthorizeService authorizeService = AuthorizeServiceFactory.getInstance().getAuthorizeService();
|
||||
protected ConfigurationService configurationService = DSpaceServicesFactory.getInstance().getConfigurationService();
|
||||
|
||||
/**
|
||||
* @param user_agent User agent string (specifies browser used and its version).
|
||||
* @param xforwardedfor When accessed via a reverse proxy, the application sees the proxy's IP as the
|
||||
* source of the request. The proxy may be configured to add the
|
||||
* "X-Forwarded-For" HTTP header containing the original IP of the client
|
||||
* so that the reverse-proxied application can get the client's IP.
|
||||
* @param headers If you want to access the collection as the user logged into the
|
||||
* context. The value of the "rest-dspace-token" header must be set
|
||||
* to the token received from the login method response.
|
||||
* @param request Servlet's HTTP request object.
|
||||
* @return Return instance of collection. It can also return status code
|
||||
* NOT_FOUND(404) if id of collection is incorrect or status code
|
||||
* @throws UnsupportedEncodingException The Character Encoding is not supported.
|
||||
* @throws WebApplicationException It is thrown when was problem with database reading
|
||||
* (SQLException) or problem with creating
|
||||
* context(ContextException). It is thrown by NOT_FOUND and
|
||||
* UNATHORIZED status codes, too.
|
||||
*/
|
||||
@GET
|
||||
@Produces( {MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
|
||||
public HierarchySite getHierarchy(
|
||||
@QueryParam("userAgent") String user_agent, @QueryParam("xforwardedfor") String xforwardedfor,
|
||||
@Context HttpHeaders headers, @Context HttpServletRequest request)
|
||||
throws UnsupportedEncodingException, WebApplicationException {
|
||||
|
||||
org.dspace.core.Context context = null;
|
||||
HierarchySite repo = new HierarchySite();
|
||||
|
||||
try {
|
||||
context = createContext();
|
||||
|
||||
Site site = siteService.findSite(context);
|
||||
repo.setId(site.getID().toString());
|
||||
repo.setName(site.getName());
|
||||
repo.setHandle(site.getHandle());
|
||||
List<Community> dspaceCommunities = communityService.findAllTop(context);
|
||||
processCommunity(context, repo, dspaceCommunities);
|
||||
} catch (Exception e) {
|
||||
processException(e.getMessage(), context);
|
||||
} finally {
|
||||
if (context != null) {
|
||||
try {
|
||||
context.complete();
|
||||
} catch (SQLException e) {
|
||||
log.error(e.getMessage() + " occurred while trying to close");
|
||||
}
|
||||
}
|
||||
}
|
||||
return repo;
|
||||
}
|
||||
|
||||
|
||||
private void processCommunity(org.dspace.core.Context context, HierarchyCommunity parent,
|
||||
List<Community> communities) throws SQLException {
|
||||
if (communities == null) {
|
||||
return;
|
||||
}
|
||||
if (communities.size() == 0) {
|
||||
return;
|
||||
}
|
||||
List<HierarchyCommunity> parentComms = new ArrayList<HierarchyCommunity>();
|
||||
parent.setCommunities(parentComms);
|
||||
for (Community comm : communities) {
|
||||
if (!authorizeService.authorizeActionBoolean(context, comm, org.dspace.core.Constants.READ)) {
|
||||
continue;
|
||||
}
|
||||
HierarchyCommunity mycomm = new HierarchyCommunity(comm.getID().toString(), comm.getName(),
|
||||
comm.getHandle());
|
||||
parentComms.add(mycomm);
|
||||
List<Collection> colls = comm.getCollections();
|
||||
if (colls.size() > 0) {
|
||||
List<HierarchyCollection> myColls = new ArrayList<HierarchyCollection>();
|
||||
mycomm.setCollections(myColls);
|
||||
for (Collection coll : colls) {
|
||||
if (!authorizeService.authorizeActionBoolean(context, coll, org.dspace.core.Constants.READ)) {
|
||||
continue;
|
||||
}
|
||||
HierarchyCollection mycoll = new HierarchyCollection(coll.getID().toString(), coll.getName(),
|
||||
coll.getHandle());
|
||||
myColls.add(mycoll);
|
||||
}
|
||||
}
|
||||
processCommunity(context, mycomm, comm.getSubcommunities());
|
||||
}
|
||||
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@@ -1,738 +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.rest;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.ws.rs.Consumes;
|
||||
import javax.ws.rs.DELETE;
|
||||
import javax.ws.rs.DefaultValue;
|
||||
import javax.ws.rs.GET;
|
||||
import javax.ws.rs.POST;
|
||||
import javax.ws.rs.PUT;
|
||||
import javax.ws.rs.Path;
|
||||
import javax.ws.rs.PathParam;
|
||||
import javax.ws.rs.Produces;
|
||||
import javax.ws.rs.QueryParam;
|
||||
import javax.ws.rs.WebApplicationException;
|
||||
import javax.ws.rs.core.Context;
|
||||
import javax.ws.rs.core.HttpHeaders;
|
||||
import javax.ws.rs.core.MediaType;
|
||||
import javax.ws.rs.core.Response;
|
||||
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.dspace.authorize.AuthorizeException;
|
||||
import org.dspace.authorize.factory.AuthorizeServiceFactory;
|
||||
import org.dspace.authorize.service.AuthorizeService;
|
||||
import org.dspace.content.NonUniqueMetadataException;
|
||||
import org.dspace.content.factory.ContentServiceFactory;
|
||||
import org.dspace.content.service.MetadataFieldService;
|
||||
import org.dspace.content.service.MetadataSchemaService;
|
||||
import org.dspace.content.service.SiteService;
|
||||
import org.dspace.rest.common.MetadataField;
|
||||
import org.dspace.rest.common.MetadataSchema;
|
||||
import org.dspace.rest.exceptions.ContextException;
|
||||
import org.dspace.usage.UsageEvent;
|
||||
|
||||
/**
|
||||
* Class which provides read methods over the metadata registry.
|
||||
*
|
||||
* @author Terry Brady, Georgetown University
|
||||
*
|
||||
* GET /registries/schema - Return the list of schemas in the registry
|
||||
* GET /registries/schema/{schema_prefix} - Returns the specified schema
|
||||
* GET /registries/schema/{schema_prefix}/metadata-fields/{element} - Returns the metadata field within a schema
|
||||
* with an unqualified element name
|
||||
* GET /registries/schema/{schema_prefix}/metadata-fields/{element}/{qualifier} - Returns the metadata field
|
||||
* within a schema with a qualified element name
|
||||
* POST /registries/schema/ - Add a schema to the schema registry
|
||||
* POST /registries/schema/{schema_prefix}/metadata-fields - Add a metadata field to the specified schema
|
||||
* GET /registries/metadata-fields/{field_id} - Return the specified metadata field
|
||||
* PUT /registries/metadata-fields/{field_id} - Update the specified metadata field
|
||||
* DELETE /registries/metadata-fields/{field_id} - Delete the specified metadata field from the metadata field registry
|
||||
* DELETE /registries/schema/{schema_id} - Delete the specified schema from the schema registry
|
||||
*
|
||||
* Note: intentionally not providing since there is no date to update other than the namespace
|
||||
* PUT /registries/schema/{schema_id}
|
||||
*/
|
||||
@Path("/registries")
|
||||
public class MetadataRegistryResource extends Resource {
|
||||
protected AuthorizeService authorizeService = AuthorizeServiceFactory.getInstance().getAuthorizeService();
|
||||
protected MetadataFieldService metadataFieldService = ContentServiceFactory.getInstance().getMetadataFieldService();
|
||||
protected MetadataSchemaService metadataSchemaService = ContentServiceFactory.getInstance()
|
||||
.getMetadataSchemaService();
|
||||
protected SiteService siteService = ContentServiceFactory.getInstance().getSiteService();
|
||||
private static Logger log = org.apache.logging.log4j.LogManager.getLogger(MetadataRegistryResource.class);
|
||||
|
||||
/**
|
||||
* Return all metadata registry items in DSpace.
|
||||
*
|
||||
* @param expand String in which is what you want to add to returned instance
|
||||
* of metadata schema. Options are: "all", "fields". Default value "fields".
|
||||
* @param user_ip User's IP address.
|
||||
* @param user_agent User agent string (specifies browser used and its version).
|
||||
* @param xforwardedfor When accessed via a reverse proxy, the application sees the proxy's IP as the
|
||||
* source of the request. The proxy may be configured to add the
|
||||
* "X-Forwarded-For" HTTP header containing the original IP of the client
|
||||
* so that the reverse-proxied application can get the client's IP.
|
||||
* @param headers If you want to access the metadata schema as the user logged into the
|
||||
* context. The value of the "rest-dspace-token" header must be set
|
||||
* to the token received from the login method response.
|
||||
* @param request Servlet's HTTP request object.
|
||||
* @return Return array of metadata schemas.
|
||||
* @throws WebApplicationException It can be caused by creating context or while was problem
|
||||
* with reading schema from database(SQLException).
|
||||
*/
|
||||
@GET
|
||||
@Path("/schema")
|
||||
@Produces( {MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
|
||||
public MetadataSchema[] getSchemas(@QueryParam("expand") @DefaultValue("fields") String expand,
|
||||
@QueryParam("userIP") String user_ip, @QueryParam("userAgent") String user_agent,
|
||||
@QueryParam("xforwardedfor") String xforwardedfor, @Context HttpHeaders headers,
|
||||
@Context HttpServletRequest request)
|
||||
throws WebApplicationException {
|
||||
|
||||
log.info("Reading all metadata schemas.");
|
||||
org.dspace.core.Context context = null;
|
||||
ArrayList<MetadataSchema> metadataSchemas = null;
|
||||
|
||||
try {
|
||||
context = createContext();
|
||||
|
||||
List<org.dspace.content.MetadataSchema> schemas = metadataSchemaService.findAll(context);
|
||||
metadataSchemas = new ArrayList<MetadataSchema>();
|
||||
for (org.dspace.content.MetadataSchema schema : schemas) {
|
||||
metadataSchemas.add(new MetadataSchema(schema, expand, context));
|
||||
}
|
||||
|
||||
context.complete();
|
||||
} catch (SQLException e) {
|
||||
processException("Could not read metadata schemas, SQLException. Message:" + e, context);
|
||||
} catch (ContextException e) {
|
||||
processException("Could not read metadata schemas, ContextException. Message:" + e.getMessage(), context);
|
||||
} finally {
|
||||
processFinally(context);
|
||||
}
|
||||
|
||||
log.trace("All metadata schemas successfully read.");
|
||||
return metadataSchemas.toArray(new MetadataSchema[0]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns metadata schema with basic properties. If you want more, use expand
|
||||
* parameter or method for metadata fields.
|
||||
*
|
||||
* @param schemaPrefix Prefix for schema in DSpace.
|
||||
* @param expand String in which is what you want to add to returned instance
|
||||
* of metadata schema. Options are: "all", "fields". Default value "fields".
|
||||
* @param user_ip User's IP address.
|
||||
* @param user_agent User agent string (specifies browser used and its version).
|
||||
* @param xforwardedfor When accessed via a reverse proxy, the application sees the proxy's IP as the
|
||||
* source of the request. The proxy may be configured to add the
|
||||
* "X-Forwarded-For" HTTP header containing the original IP of the client
|
||||
* so that the reverse-proxied application can get the client's IP.
|
||||
* @param headers If you want to access the metadata schema as the user logged into the
|
||||
* context. The value of the "rest-dspace-token" header must be set
|
||||
* to the token received from the login method response.
|
||||
* @param request Servlet's HTTP request object.
|
||||
* @return Return instance of org.dspace.rest.common.MetadataSchema.
|
||||
* @throws WebApplicationException Thrown if there was a problem with creating context or problem
|
||||
* with database reading. Also if id/prefix of schema is incorrect
|
||||
* or logged user into context has no permission to read.
|
||||
*/
|
||||
@GET
|
||||
@Path("/schema/{schema_prefix}")
|
||||
@Produces( {MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
|
||||
public MetadataSchema getSchema(@PathParam("schema_prefix") String schemaPrefix,
|
||||
@QueryParam("expand") @DefaultValue("fields") String expand,
|
||||
@QueryParam("userIP") String user_ip, @QueryParam("userAgent") String user_agent,
|
||||
@QueryParam("xforwardedfor") String xforwardedfor, @Context HttpHeaders headers,
|
||||
@Context HttpServletRequest request)
|
||||
throws WebApplicationException {
|
||||
|
||||
log.info("Reading metadata schemas.");
|
||||
org.dspace.core.Context context = null;
|
||||
MetadataSchema metadataSchema = null;
|
||||
|
||||
try {
|
||||
context = createContext();
|
||||
|
||||
org.dspace.content.MetadataSchema schema = metadataSchemaService.find(context, schemaPrefix);
|
||||
metadataSchema = new MetadataSchema(schema, expand, context);
|
||||
if (schema == null) {
|
||||
processException(String.format("Schema not found for index %s", schemaPrefix), context);
|
||||
}
|
||||
|
||||
context.complete();
|
||||
} catch (SQLException e) {
|
||||
processException("Could not read metadata schema, SQLException. Message:" + e, context);
|
||||
} catch (ContextException e) {
|
||||
processException("Could not read metadata schema, ContextException. Message:" + e.getMessage(), context);
|
||||
} finally {
|
||||
processFinally(context);
|
||||
}
|
||||
|
||||
log.trace("Metadata schemas successfully read.");
|
||||
return metadataSchema;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns metadata field with basic properties.
|
||||
*
|
||||
* @param schemaPrefix Prefix for schema in DSpace.
|
||||
* @param element Unqualified element name for field in the metadata registry.
|
||||
* @param expand String in which is what you want to add to returned instance
|
||||
* of the metadata field. Options are: "all", "parentSchema". Default value "".
|
||||
* @param user_ip User's IP address.
|
||||
* @param user_agent User agent string (specifies browser used and its version).
|
||||
* @param xforwardedfor When accessed via a reverse proxy, the application sees the proxy's IP as the
|
||||
* source of the request. The proxy may be configured to add the
|
||||
* "X-Forwarded-For" HTTP header containing the original IP of the client
|
||||
* so that the reverse-proxied application can get the client's IP.
|
||||
* @param headers If you want to access the community as the user logged into the
|
||||
* context. The value of the "rest-dspace-token" header must be set
|
||||
* to the token received from the login method response.
|
||||
* @param request Servlet's HTTP request object.
|
||||
* @return Return instance of org.dspace.rest.common.MetadataField.
|
||||
* @throws WebApplicationException Thrown if there was a problem with creating context or problem
|
||||
* with database reading. Also if id of field is incorrect
|
||||
* or logged user into context has no permission to read.
|
||||
*/
|
||||
@GET
|
||||
@Path("/schema/{schema_prefix}/metadata-fields/{element}")
|
||||
@Produces( {MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
|
||||
public MetadataField getMetadataFieldUnqualified(@PathParam("schema_prefix") String schemaPrefix,
|
||||
@PathParam("element") String element,
|
||||
@QueryParam("expand") String expand,
|
||||
@QueryParam("userIP") String user_ip,
|
||||
@QueryParam("userAgent") String user_agent,
|
||||
@QueryParam("xforwardedfor") String xforwardedfor,
|
||||
@Context HttpHeaders headers, @Context HttpServletRequest request)
|
||||
throws WebApplicationException {
|
||||
return getMetadataFieldQualified(schemaPrefix, element, "", expand, user_ip, user_agent, xforwardedfor, headers,
|
||||
request);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns metadata field with basic properties.
|
||||
*
|
||||
* @param schemaPrefix Prefix for schema in DSpace.
|
||||
* @param element Element name for field in the metadata registry.
|
||||
* @param qualifier Element name qualifier for field in the metadata registry.
|
||||
* @param expand String in which is what you want to add to returned instance
|
||||
* of the metadata field. Options are: "all", "parentSchema". Default value "".
|
||||
* @param user_ip User's IP address.
|
||||
* @param user_agent User agent string (specifies browser used and its version).
|
||||
* @param xforwardedfor When accessed via a reverse proxy, the application sees the proxy's IP as the
|
||||
* source of the request. The proxy may be configured to add the
|
||||
* "X-Forwarded-For" HTTP header containing the original IP of the client
|
||||
* so that the reverse-proxied application can get the client's IP.
|
||||
* @param headers If you want to access the community as the user logged into the
|
||||
* context. The value of the "rest-dspace-token" header must be set
|
||||
* to the token received from the login method response.
|
||||
* @param request Servlet's HTTP request object.
|
||||
* @return Return instance of org.dspace.rest.common.MetadataField.
|
||||
* @throws WebApplicationException Thrown if there was a problem with creating context or problem
|
||||
* with database reading. Also if id of field is incorrect
|
||||
* or logged user into context has no permission to read.
|
||||
*/
|
||||
@GET
|
||||
@Path("/schema/{schema_prefix}/metadata-fields/{element}/{qualifier}")
|
||||
@Produces( {MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
|
||||
public MetadataField getMetadataFieldQualified(@PathParam("schema_prefix") String schemaPrefix,
|
||||
@PathParam("element") String element,
|
||||
@PathParam("qualifier") @DefaultValue("") String qualifier,
|
||||
@QueryParam("expand") String expand,
|
||||
@QueryParam("userIP") String user_ip,
|
||||
@QueryParam("userAgent") String user_agent,
|
||||
@QueryParam("xforwardedfor") String xforwardedfor,
|
||||
@Context HttpHeaders headers, @Context HttpServletRequest request)
|
||||
throws WebApplicationException {
|
||||
|
||||
log.info("Reading metadata field.");
|
||||
org.dspace.core.Context context = null;
|
||||
MetadataField metadataField = null;
|
||||
|
||||
try {
|
||||
context = createContext();
|
||||
|
||||
org.dspace.content.MetadataSchema schema = metadataSchemaService.find(context, schemaPrefix);
|
||||
|
||||
if (schema == null) {
|
||||
log.error(String.format("Schema not found for prefix %s", schemaPrefix));
|
||||
throw new WebApplicationException(Response.Status.NOT_FOUND);
|
||||
}
|
||||
|
||||
org.dspace.content.MetadataField field = metadataFieldService
|
||||
.findByElement(context, schema, element, qualifier);
|
||||
if (field == null) {
|
||||
log.error(String.format("Field %s.%s.%s not found", schemaPrefix, element, qualifier));
|
||||
throw new WebApplicationException(Response.Status.NOT_FOUND);
|
||||
}
|
||||
metadataField = new MetadataField(schema, field, expand, context);
|
||||
|
||||
context.complete();
|
||||
} catch (SQLException e) {
|
||||
processException("Could not read metadata field, SQLException. Message:" + e, context);
|
||||
} catch (ContextException e) {
|
||||
processException("Could not read metadata field, ContextException. Message:" + e.getMessage(), context);
|
||||
} finally {
|
||||
processFinally(context);
|
||||
}
|
||||
|
||||
log.trace("Metadata field successfully read.");
|
||||
return metadataField;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns metadata field with basic properties.
|
||||
*
|
||||
* @param fieldId Id of metadata field in DSpace.
|
||||
* @param expand String in which is what you want to add to returned instance
|
||||
* of the metadata field. Options are: "all", "parentSchema". Default value "parentSchema".
|
||||
* @param user_ip User's IP address.
|
||||
* @param user_agent User agent string (specifies browser used and its version).
|
||||
* @param xforwardedfor When accessed via a reverse proxy, the application sees the proxy's IP as the
|
||||
* source of the request. The proxy may be configured to add the
|
||||
* "X-Forwarded-For" HTTP header containing the original IP of the client
|
||||
* so that the reverse-proxied application can get the client's IP.
|
||||
* @param headers If you want to access the community as the user logged into the
|
||||
* context. The value of the "rest-dspace-token" header must be set
|
||||
* to the token received from the login method response.
|
||||
* @param request Servlet's HTTP request object.
|
||||
* @return Return instance of org.dspace.rest.common.MetadataField.
|
||||
* @throws WebApplicationException Thrown if there was a problem with creating context or problem
|
||||
* with database reading. Also if id of field is incorrect
|
||||
* or logged user into context has no permission to read.
|
||||
*/
|
||||
@GET
|
||||
@Path("/metadata-fields/{field_id}")
|
||||
@Produces( {MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
|
||||
public MetadataField getMetadataField(@PathParam("field_id") Integer fieldId,
|
||||
@QueryParam("expand") @DefaultValue("parentSchema") String expand,
|
||||
@QueryParam("userIP") String user_ip,
|
||||
@QueryParam("userAgent") String user_agent,
|
||||
@QueryParam("xforwardedfor") String xforwardedfor,
|
||||
@Context HttpHeaders headers, @Context HttpServletRequest request)
|
||||
throws WebApplicationException {
|
||||
|
||||
log.info("Reading metadata field.");
|
||||
org.dspace.core.Context context = null;
|
||||
MetadataField metadataField = null;
|
||||
|
||||
try {
|
||||
context = createContext();
|
||||
|
||||
org.dspace.content.MetadataField field = metadataFieldService.find(context, fieldId);
|
||||
if (field == null) {
|
||||
log.error(String.format("Metadata Field %d not found", fieldId));
|
||||
throw new WebApplicationException(Response.Status.NOT_FOUND);
|
||||
}
|
||||
org.dspace.content.MetadataSchema schema = field.getMetadataSchema();
|
||||
if (schema == null) {
|
||||
log.error(String.format("Parent Schema not found for Metadata Field %d not found", fieldId));
|
||||
throw new WebApplicationException(Response.Status.NOT_FOUND);
|
||||
}
|
||||
metadataField = new MetadataField(schema, field, expand, context);
|
||||
|
||||
context.complete();
|
||||
} catch (SQLException e) {
|
||||
processException("Could not read metadata field, SQLException. Message:" + e, context);
|
||||
} catch (ContextException e) {
|
||||
processException("Could not read metadata field, ContextException. Message:" + e.getMessage(), context);
|
||||
} finally {
|
||||
processFinally(context);
|
||||
}
|
||||
|
||||
log.trace("Metadata field successfully read.");
|
||||
return metadataField;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create schema in the schema registry. Creating a schema is restricted to admin users.
|
||||
*
|
||||
* @param schema Schema that will be added to the metadata registry.
|
||||
* @param user_ip User's IP address.
|
||||
* @param user_agent User agent string (specifies browser used and its version).
|
||||
* @param xforwardedfor When accessed via a reverse proxy, the application sees the proxy's IP as the
|
||||
* source of the request. The proxy may be configured to add the
|
||||
* "X-Forwarded-For" HTTP header containing the original IP of the client
|
||||
* so that the reverse-proxied application can get the client's IP.
|
||||
* @param headers If you want to access the schema as the user logged into the
|
||||
* context. The value of the "rest-dspace-token" header must be set
|
||||
* to the token received from the login method response.
|
||||
* @param request Servlet's HTTP request object.
|
||||
* @return Return response 200 if was everything all right. Otherwise 400
|
||||
* when id of community was incorrect or 401 if was problem with
|
||||
* permission to write into collection.
|
||||
* Returns the schema (schemaId), if was all ok.
|
||||
* @throws WebApplicationException It can be thrown by SQLException, AuthorizeException and
|
||||
* ContextException.
|
||||
*/
|
||||
@POST
|
||||
@Path("/schema")
|
||||
@Consumes( {MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
|
||||
public MetadataSchema createSchema(MetadataSchema schema, @QueryParam("userIP") String user_ip,
|
||||
@QueryParam("userAgent") String user_agent,
|
||||
@QueryParam("xforwardedfor") String xforwardedfor,
|
||||
@Context HttpHeaders headers, @Context HttpServletRequest request)
|
||||
throws WebApplicationException {
|
||||
|
||||
log.info("Creating a schema.");
|
||||
org.dspace.core.Context context = null;
|
||||
MetadataSchema retSchema = null;
|
||||
|
||||
try {
|
||||
context = createContext();
|
||||
|
||||
if (!authorizeService.isAdmin(context)) {
|
||||
context.abort();
|
||||
String user = "anonymous";
|
||||
if (context.getCurrentUser() != null) {
|
||||
user = context.getCurrentUser().getEmail();
|
||||
}
|
||||
log.error("User(" + user + ") does not have permission to create a metadata schema!");
|
||||
throw new WebApplicationException(Response.Status.UNAUTHORIZED);
|
||||
}
|
||||
|
||||
log.debug(String.format("Admin user creating schema with namespace %s and prefix %s", schema.getNamespace(),
|
||||
schema.getPrefix()));
|
||||
|
||||
org.dspace.content.MetadataSchema dspaceSchema = metadataSchemaService
|
||||
.create(context, schema.getPrefix(), schema.getNamespace());
|
||||
log.debug("Creating return object.");
|
||||
retSchema = new MetadataSchema(dspaceSchema, "", context);
|
||||
|
||||
writeStats(siteService.findSite(context), UsageEvent.Action.CREATE, user_ip, user_agent, xforwardedfor,
|
||||
headers, request, context);
|
||||
|
||||
context.complete();
|
||||
log.info("Schema created" + retSchema.getPrefix());
|
||||
|
||||
} catch (SQLException e) {
|
||||
processException("Could not create new metadata schema, SQLException. Message: " + e, context);
|
||||
} catch (ContextException e) {
|
||||
processException("Could not create new metadata schema, ContextException. Message: " + e.getMessage(),
|
||||
context);
|
||||
} catch (AuthorizeException e) {
|
||||
processException("Could not create new metadata schema, AuthorizeException. Message: " + e.getMessage(),
|
||||
context);
|
||||
} catch (NonUniqueMetadataException e) {
|
||||
processException(
|
||||
"Could not create new metadata schema, NonUniqueMetadataException. Message: " + e.getMessage(),
|
||||
context);
|
||||
} catch (Exception e) {
|
||||
processException("Could not create new metadata schema, Exception. Class: " + e.getClass(), context);
|
||||
} finally {
|
||||
processFinally(context);
|
||||
}
|
||||
|
||||
return retSchema;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Create a new metadata field within a schema.
|
||||
* Creating a metadata field is restricted to admin users.
|
||||
*
|
||||
* @param schemaPrefix Prefix for schema in DSpace.
|
||||
* @param field Field that will be added to the metadata registry for a schema.
|
||||
* @param user_ip User's IP address.
|
||||
* @param user_agent User agent string (specifies browser used and its version).
|
||||
* @param xforwardedfor When accessed via a reverse proxy, the application sees the proxy's IP as the
|
||||
* source of the request. The proxy may be configured to add the
|
||||
* "X-Forwarded-For" HTTP header containing the original IP of the client
|
||||
* so that the reverse-proxied application can get the client's IP.
|
||||
* @param headers If you want to access the schema as the user logged into the
|
||||
* context. The value of the "rest-dspace-token" header must be set
|
||||
* to the token received from the login method response.
|
||||
* @param request Servlet's HTTP request object.
|
||||
* @return Return response 200 if was everything all right. Otherwise 400
|
||||
* when id of community was incorrect or 401 if was problem with
|
||||
* permission to write into collection.
|
||||
* Returns the field (with fieldId), if was all ok.
|
||||
* @throws WebApplicationException It can be thrown by SQLException, AuthorizeException and
|
||||
* ContextException.
|
||||
*/
|
||||
@POST
|
||||
@Path("/schema/{schema_prefix}/metadata-fields")
|
||||
@Consumes( {MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
|
||||
public MetadataField createMetadataField(@PathParam("schema_prefix") String schemaPrefix,
|
||||
MetadataField field, @QueryParam("userIP") String user_ip,
|
||||
@QueryParam("userAgent") String user_agent,
|
||||
@QueryParam("xforwardedfor") String xforwardedfor,
|
||||
@Context HttpHeaders headers, @Context HttpServletRequest request)
|
||||
throws WebApplicationException {
|
||||
|
||||
log.info(String.format("Creating metadataField within schema %s.", schemaPrefix));
|
||||
org.dspace.core.Context context = null;
|
||||
MetadataField retField = null;
|
||||
|
||||
try {
|
||||
context = createContext();
|
||||
|
||||
if (!authorizeService.isAdmin(context)) {
|
||||
context.abort();
|
||||
String user = "anonymous";
|
||||
if (context.getCurrentUser() != null) {
|
||||
user = context.getCurrentUser().getEmail();
|
||||
}
|
||||
log.error("User(" + user + ") does not have permission to create a metadata field!");
|
||||
throw new WebApplicationException(Response.Status.UNAUTHORIZED);
|
||||
}
|
||||
|
||||
org.dspace.content.MetadataSchema schema = metadataSchemaService.find(context, schemaPrefix);
|
||||
if (schema == null) {
|
||||
log.error(String.format("Schema not found for prefix %s", schemaPrefix));
|
||||
throw new WebApplicationException(Response.Status.NOT_FOUND);
|
||||
}
|
||||
org.dspace.content.MetadataField dspaceField = metadataFieldService
|
||||
.create(context, schema, field.getElement(), field.getQualifier(), field.getDescription());
|
||||
writeStats(siteService.findSite(context), UsageEvent.Action.CREATE, user_ip, user_agent, xforwardedfor,
|
||||
headers, request, context);
|
||||
|
||||
retField = new MetadataField(schema, dspaceField, "", context);
|
||||
context.complete();
|
||||
log.info("Metadata field created within schema" + retField.getName());
|
||||
} catch (SQLException e) {
|
||||
processException("Could not create new metadata field, SQLException. Message: " + e, context);
|
||||
} catch (ContextException e) {
|
||||
processException("Could not create new metadata field, ContextException. Message: " + e.getMessage(),
|
||||
context);
|
||||
} catch (AuthorizeException e) {
|
||||
processException("Could not create new metadata field, AuthorizeException. Message: " + e.getMessage(),
|
||||
context);
|
||||
} catch (NonUniqueMetadataException e) {
|
||||
processException(
|
||||
"Could not create new metadata field, NonUniqueMetadataException. Message: " + e.getMessage(), context);
|
||||
} catch (Exception e) {
|
||||
processException("Could not create new metadata field, Exception. Message: " + e.getMessage(), context);
|
||||
} finally {
|
||||
processFinally(context);
|
||||
}
|
||||
|
||||
return retField;
|
||||
}
|
||||
|
||||
//@PUT
|
||||
//@Path("/schema/{schema_prefix}")
|
||||
//Assumption - there are no meaningful fields to update for a schema
|
||||
|
||||
/**
|
||||
* Update metadata field. Replace all information about community except the id and the containing schema.
|
||||
*
|
||||
* @param fieldId Id of the field in the DSpace metdata registry.
|
||||
* @param field Instance of the metadata field which will replace actual metadata field in
|
||||
* DSpace.
|
||||
* @param user_ip User's IP address.
|
||||
* @param user_agent User agent string (specifies browser used and its version).
|
||||
* @param xforwardedfor When accessed via a reverse proxy, the application sees the proxy's IP as the
|
||||
* source of the request. The proxy may be configured to add the
|
||||
* "X-Forwarded-For" HTTP header containing the original IP of the client
|
||||
* so that the reverse-proxied application can get the client's IP.
|
||||
* @param headers If you want to access the metadata field as the user logged into the
|
||||
* context. The value of the "rest-dspace-token" header must be set
|
||||
* to the token received from the login method response.
|
||||
* @param request Servlet's HTTP request object.
|
||||
* @return Response 200 if was all ok. Otherwise 400 if was id incorrect or
|
||||
* 401 if logged user has no permission to update the metadata field.
|
||||
* @throws WebApplicationException Thrown if there was a problem with creating context or problem
|
||||
* with database reading or writing. Or problem with writing to
|
||||
* community caused by authorization.
|
||||
*/
|
||||
@PUT
|
||||
@Path("/metadata-fields/{field_id}")
|
||||
@Consumes( {MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
|
||||
public Response updateMetadataField(@PathParam("field_id") Integer fieldId, MetadataField field,
|
||||
@QueryParam("userIP") String user_ip,
|
||||
@QueryParam("userAgent") String user_agent,
|
||||
@QueryParam("xforwardedfor") String xforwardedfor, @Context HttpHeaders headers,
|
||||
@Context HttpServletRequest request)
|
||||
throws WebApplicationException {
|
||||
|
||||
log.info("Updating metadata field(id=" + fieldId + ").");
|
||||
org.dspace.core.Context context = null;
|
||||
|
||||
try {
|
||||
context = createContext();
|
||||
|
||||
org.dspace.content.MetadataField dspaceField = metadataFieldService.find(context, fieldId);
|
||||
if (field == null) {
|
||||
log.error(String.format("Metadata Field %d not found", fieldId));
|
||||
throw new WebApplicationException(Response.Status.NOT_FOUND);
|
||||
}
|
||||
|
||||
writeStats(siteService.findSite(context), UsageEvent.Action.UPDATE, user_ip, user_agent, xforwardedfor,
|
||||
headers, request, context);
|
||||
|
||||
dspaceField.setElement(field.getElement());
|
||||
dspaceField.setQualifier(field.getQualifier());
|
||||
dspaceField.setScopeNote(field.getDescription());
|
||||
metadataFieldService.update(context, dspaceField);
|
||||
|
||||
context.complete();
|
||||
|
||||
} catch (SQLException e) {
|
||||
processException("Could not update metadata field(id=" + fieldId + "), AuthorizeException. Message:" + e,
|
||||
context);
|
||||
} catch (ContextException e) {
|
||||
processException("Could not update metadata field(id=" + fieldId + "), ContextException Message:" + e,
|
||||
context);
|
||||
} catch (AuthorizeException e) {
|
||||
processException("Could not update metadata field(id=" + fieldId + "), AuthorizeException. Message:" + e,
|
||||
context);
|
||||
} catch (NonUniqueMetadataException e) {
|
||||
processException(
|
||||
"Could not update metadata field(id=" + fieldId + "), NonUniqueMetadataException. Message:" + e,
|
||||
context);
|
||||
} catch (IOException e) {
|
||||
processException("Could not update metadata field(id=" + fieldId + "), IOException. Message:" + e, context);
|
||||
} finally {
|
||||
processFinally(context);
|
||||
}
|
||||
|
||||
log.info("Metadata Field(id=" + fieldId + ") has been successfully updated.");
|
||||
return Response.ok().build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete metadata field from the DSpace metadata registry
|
||||
*
|
||||
* @param fieldId Id of the metadata field in DSpace.
|
||||
* @param user_ip User's IP address.
|
||||
* @param user_agent User agent string (specifies browser used and its version).
|
||||
* @param xforwardedfor When accessed via a reverse proxy, the application sees the proxy's IP as the
|
||||
* source of the request. The proxy may be configured to add the
|
||||
* "X-Forwarded-For" HTTP header containing the original IP of the client
|
||||
* so that the reverse-proxied application can get the client's IP.
|
||||
* @param headers If you want to access the metadata field as the user logged into the
|
||||
* context. The value of the "rest-dspace-token" header must be set
|
||||
* to the token received from the login method response.
|
||||
* @param request Servlet's HTTP request object.
|
||||
* @return Return response code OK(200) if was everything all right.
|
||||
* Otherwise return NOT_FOUND(404) if was id of metadata field is incorrect.
|
||||
* Or (UNAUTHORIZED)401 if was problem with permission to metadata field.
|
||||
* @throws WebApplicationException Thrown if there was a problem with creating context or problem
|
||||
* with database reading or deleting. Or problem with deleting
|
||||
* metadata field caused by IOException or authorization.
|
||||
*/
|
||||
@DELETE
|
||||
@Path("/metadata-fields/{field_id}")
|
||||
public Response deleteMetadataField(@PathParam("field_id") Integer fieldId, @QueryParam("userIP") String user_ip,
|
||||
@QueryParam("userAgent") String user_agent,
|
||||
@QueryParam("xforwardedfor") String xforwardedfor,
|
||||
@Context HttpHeaders headers, @Context HttpServletRequest request)
|
||||
throws WebApplicationException {
|
||||
|
||||
log.info("Deleting metadata field(id=" + fieldId + ").");
|
||||
org.dspace.core.Context context = null;
|
||||
|
||||
try {
|
||||
context = createContext();
|
||||
|
||||
org.dspace.content.MetadataField dspaceField = metadataFieldService.find(context, fieldId);
|
||||
if (dspaceField == null) {
|
||||
log.error(String.format("Metadata Field %d not found", fieldId));
|
||||
throw new WebApplicationException(Response.Status.NOT_FOUND);
|
||||
}
|
||||
writeStats(siteService.findSite(context), UsageEvent.Action.DELETE, user_ip, user_agent, xforwardedfor,
|
||||
headers,
|
||||
request, context);
|
||||
|
||||
metadataFieldService.delete(context, dspaceField);
|
||||
context.complete();
|
||||
|
||||
} catch (SQLException e) {
|
||||
processException("Could not delete metadata field(id=" + fieldId + "), SQLException. Message:" + e,
|
||||
context);
|
||||
} catch (AuthorizeException e) {
|
||||
processException("Could not delete metadata field(id=" + fieldId + "), AuthorizeException. Message:" + e,
|
||||
context);
|
||||
} catch (ContextException e) {
|
||||
processException(
|
||||
"Could not delete metadata field(id=" + fieldId + "), ContextException. Message:" + e.getMessage(),
|
||||
context);
|
||||
} finally {
|
||||
processFinally(context);
|
||||
}
|
||||
|
||||
|
||||
log.info("Metadata field(id=" + fieldId + ") was successfully deleted.");
|
||||
return Response.status(Response.Status.OK).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete metadata schema from the DSpace metadata registry
|
||||
*
|
||||
* @param schemaId Id of the metadata schema in DSpace.
|
||||
* @param user_ip User's IP address.
|
||||
* @param user_agent User agent string (specifies browser used and its version).
|
||||
* @param xforwardedfor When accessed via a reverse proxy, the application sees the proxy's IP as the
|
||||
* source of the request. The proxy may be configured to add the
|
||||
* "X-Forwarded-For" HTTP header containing the original IP of the client
|
||||
* so that the reverse-proxied application can get the client's IP.
|
||||
* @param headers If you want to access the metadata schema as the user logged into the
|
||||
* context. The value of the "rest-dspace-token" header must be set
|
||||
* to the token received from the login method response.
|
||||
* @param request Servlet's HTTP request object.
|
||||
* @return Return response code OK(200) if was everything all right.
|
||||
* Otherwise return NOT_FOUND(404) if was id of metadata schema is incorrect.
|
||||
* Or (UNAUTHORIZED)401 if was problem with permission to metadata schema.
|
||||
* @throws WebApplicationException Thrown if there was a problem with creating context or problem
|
||||
* with database reading or deleting. Or problem with deleting
|
||||
* metadata schema caused by IOException or authorization.
|
||||
*/
|
||||
@DELETE
|
||||
@Path("/schema/{schema_id}")
|
||||
public Response deleteSchema(@PathParam("schema_id") Integer schemaId, @QueryParam("userIP") String user_ip,
|
||||
@QueryParam("userAgent") String user_agent,
|
||||
@QueryParam("xforwardedfor") String xforwardedfor,
|
||||
@Context HttpHeaders headers, @Context HttpServletRequest request)
|
||||
throws WebApplicationException {
|
||||
|
||||
log.info("Deleting metadata schema(id=" + schemaId + ").");
|
||||
org.dspace.core.Context context = null;
|
||||
|
||||
try {
|
||||
context = createContext();
|
||||
|
||||
org.dspace.content.MetadataSchema dspaceSchema = metadataSchemaService.find(context, schemaId);
|
||||
if (dspaceSchema == null) {
|
||||
log.error(String.format("Metadata Schema %d not found", schemaId));
|
||||
throw new WebApplicationException(Response.Status.NOT_FOUND);
|
||||
}
|
||||
writeStats(siteService.findSite(context), UsageEvent.Action.DELETE, user_ip, user_agent, xforwardedfor,
|
||||
headers,
|
||||
request, context);
|
||||
|
||||
metadataSchemaService.delete(context, dspaceSchema);
|
||||
context.complete();
|
||||
|
||||
} catch (SQLException e) {
|
||||
processException("Could not delete metadata schema(id=" + schemaId + "), SQLException. Message:" + e,
|
||||
context);
|
||||
} catch (AuthorizeException e) {
|
||||
processException("Could not delete metadata schema(id=" + schemaId + "), AuthorizeException. Message:" + e,
|
||||
context);
|
||||
} catch (ContextException e) {
|
||||
processException(
|
||||
"Could not delete metadata schema(id=" + schemaId + "), ContextException. Message:" + e.getMessage(),
|
||||
context);
|
||||
} finally {
|
||||
processFinally(context);
|
||||
}
|
||||
|
||||
|
||||
log.info("Metadata schema(id=" + schemaId + ") was successfully deleted.");
|
||||
return Response.status(Response.Status.OK).build();
|
||||
}
|
||||
|
||||
}
|
@@ -1,212 +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.rest;
|
||||
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import javax.servlet.ServletContext;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.ws.rs.WebApplicationException;
|
||||
import javax.ws.rs.core.HttpHeaders;
|
||||
import javax.ws.rs.core.Response;
|
||||
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.dspace.content.DSpaceObject;
|
||||
import org.dspace.core.Context;
|
||||
import org.dspace.eperson.factory.EPersonServiceFactory;
|
||||
import org.dspace.rest.exceptions.ContextException;
|
||||
import org.dspace.services.factory.DSpaceServicesFactory;
|
||||
import org.dspace.usage.UsageEvent;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
|
||||
/**
|
||||
* Superclass of all resource classes in REST API. It has methods for creating
|
||||
* context, write statistics, processsing exceptions, splitting a key of
|
||||
* metadata, string representation of action and method for getting the logged
|
||||
* in user from the token in request header.
|
||||
*
|
||||
* @author Rostislav Novak (Computing and Information Centre, CTU in Prague)
|
||||
*/
|
||||
public class Resource {
|
||||
|
||||
@javax.ws.rs.core.Context
|
||||
public ServletContext servletContext;
|
||||
|
||||
private static Logger log = org.apache.logging.log4j.LogManager.getLogger(Resource.class);
|
||||
|
||||
private static final boolean writeStatistics;
|
||||
|
||||
static {
|
||||
writeStatistics = DSpaceServicesFactory.getInstance().getConfigurationService()
|
||||
.getBooleanProperty("rest.stats", false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create context to work with DSpace database. It can create context
|
||||
* with or without a logged in user (retrieved from SecurityContextHolder). Throws
|
||||
* WebApplicationException caused by: SQLException if there was a problem
|
||||
* with reading from database. Throws AuthorizeException if there was
|
||||
* a problem with authorization to read from the database. Throws Exception
|
||||
* if there was a problem creating context.
|
||||
*
|
||||
* @return Newly created context with the logged in user unless the specified user was null.
|
||||
* If user is null, create the context without a logged in user.
|
||||
* @throws ContextException Thrown in case of a problem creating context. Can be caused by
|
||||
* SQLException error in creating context or finding the user to
|
||||
* log in. Can be caused by AuthorizeException if there was a
|
||||
* problem authorizing the found user.
|
||||
* @throws SQLException An exception that provides information on a database access error or other errors.
|
||||
*/
|
||||
protected static org.dspace.core.Context createContext() throws ContextException, SQLException {
|
||||
org.dspace.core.Context context = new org.dspace.core.Context();
|
||||
//context.getDBConnection().setAutoCommit(false); // Disable autocommit.
|
||||
|
||||
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
|
||||
if (authentication != null) {
|
||||
Collection<SimpleGrantedAuthority> specialGroups = (Collection<SimpleGrantedAuthority>) authentication
|
||||
.getAuthorities();
|
||||
for (SimpleGrantedAuthority grantedAuthority : specialGroups) {
|
||||
context.setSpecialGroup(EPersonServiceFactory.getInstance().getGroupService()
|
||||
.findByName(context, grantedAuthority.getAuthority())
|
||||
.getID());
|
||||
}
|
||||
context.setCurrentUser(
|
||||
EPersonServiceFactory.getInstance().getEPersonService().findByEmail(context, authentication.getName()));
|
||||
}
|
||||
|
||||
return context;
|
||||
}
|
||||
|
||||
/**
|
||||
* Records a statistics event about an object used via REST API.
|
||||
*
|
||||
* @param dspaceObject DSpace object on which a request was performed.
|
||||
* @param action Action that was performed.
|
||||
* @param user_ip User's IP address.
|
||||
* @param user_agent User agent string (specifies browser used and its version).
|
||||
* @param xforwardedfor When accessed via a reverse proxy, the application sees the proxy's IP as the
|
||||
* source of the request. The proxy may be configured to add the
|
||||
* "X-Forwarded-For" HTTP header containing the original IP of the client
|
||||
* so that the reverse-proxied application can get the client's IP.
|
||||
* @param headers If you want to access the item as the user logged into the
|
||||
* context. The header "rest-dspace-token" with the token passed
|
||||
* from the login method must be set.
|
||||
* @param request Servlet's HTTP request object.
|
||||
* @param context Context which must be aborted.
|
||||
*/
|
||||
protected void writeStats(DSpaceObject dspaceObject, UsageEvent.Action action,
|
||||
String user_ip, String user_agent, String xforwardedfor, HttpHeaders headers,
|
||||
HttpServletRequest request, Context context) {
|
||||
if (!writeStatistics) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ((user_ip == null) || (user_ip.length() == 0)) {
|
||||
DSpaceServicesFactory.getInstance().getEventService()
|
||||
.fireEvent(new UsageEvent(action, request, context, dspaceObject));
|
||||
} else {
|
||||
DSpaceServicesFactory.getInstance().getEventService().fireEvent(
|
||||
new UsageEvent(action, user_ip, user_agent, xforwardedfor, context, dspaceObject));
|
||||
}
|
||||
|
||||
log.debug("fired event");
|
||||
}
|
||||
|
||||
/**
|
||||
* Process exception, print message to logger error stream and abort DSpace
|
||||
* context.
|
||||
*
|
||||
* @param message Message, which will be printed to error stream.
|
||||
* @param context Context which must be aborted.
|
||||
* @throws WebApplicationException This exception is throw for user of REST api.
|
||||
*/
|
||||
protected static void processException(String message, org.dspace.core.Context context)
|
||||
throws WebApplicationException {
|
||||
if ((context != null) && (context.isValid())) {
|
||||
context.abort();
|
||||
}
|
||||
log.error(message);
|
||||
throw new WebApplicationException(Response.Status.INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
|
||||
/**
|
||||
* Process finally statement. It will print message to logger error stream
|
||||
* and abort DSpace context, if was not properly ended.
|
||||
*
|
||||
* @param context Context which must be aborted.
|
||||
* @throws WebApplicationException This exception is thrown for user of REST API.
|
||||
*/
|
||||
protected void processFinally(org.dspace.core.Context context) throws WebApplicationException {
|
||||
if ((context != null) && (context.isValid())) {
|
||||
context.abort();
|
||||
log.error("Something get wrong. Aborting context in finally statement.");
|
||||
throw new WebApplicationException(Response.Status.INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Split string with regex ".".
|
||||
*
|
||||
* @param key String which will be splitted.
|
||||
* @return String array filed with separated string.
|
||||
*/
|
||||
protected String[] mySplit(String key) {
|
||||
ArrayList<String> list = new ArrayList<String>();
|
||||
int prev = 0;
|
||||
for (int i = 0; i < key.length(); i++) {
|
||||
if (key.charAt(i) == '.') {
|
||||
list.add(key.substring(prev, i));
|
||||
prev = i + 1;
|
||||
} else if (i + 1 == key.length()) {
|
||||
list.add(key.substring(prev, i + 1));
|
||||
}
|
||||
}
|
||||
|
||||
if (list.size() == 2) {
|
||||
list.add(null);
|
||||
}
|
||||
|
||||
return list.toArray(new String[0]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return string representation of values
|
||||
* org.dspace.core.Constants.{READ,WRITE,DELETE}.
|
||||
*
|
||||
* @param action Constant from org.dspace.core.Constants.*
|
||||
* @return String representation. read or write or delete.
|
||||
*/
|
||||
protected String getActionString(int action) {
|
||||
String actionStr;
|
||||
switch (action) {
|
||||
case org.dspace.core.Constants.READ:
|
||||
actionStr = "read";
|
||||
break;
|
||||
case org.dspace.core.Constants.WRITE:
|
||||
actionStr = "write";
|
||||
break;
|
||||
case org.dspace.core.Constants.DELETE:
|
||||
actionStr = "delete";
|
||||
break;
|
||||
case org.dspace.core.Constants.REMOVE:
|
||||
actionStr = "remove";
|
||||
break;
|
||||
case org.dspace.core.Constants.ADD:
|
||||
actionStr = "add";
|
||||
break;
|
||||
default:
|
||||
actionStr = "(?action?)";
|
||||
break;
|
||||
}
|
||||
return actionStr;
|
||||
}
|
||||
|
||||
}
|
@@ -1,301 +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.rest;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.sql.SQLException;
|
||||
import java.util.Iterator;
|
||||
import javax.servlet.ServletContext;
|
||||
import javax.ws.rs.Consumes;
|
||||
import javax.ws.rs.GET;
|
||||
import javax.ws.rs.POST;
|
||||
import javax.ws.rs.Path;
|
||||
import javax.ws.rs.Produces;
|
||||
import javax.ws.rs.core.Context;
|
||||
import javax.ws.rs.core.HttpHeaders;
|
||||
import javax.ws.rs.core.MediaType;
|
||||
import javax.ws.rs.core.Response;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.dspace.authenticate.AuthenticationMethod;
|
||||
import org.dspace.authenticate.ShibAuthentication;
|
||||
import org.dspace.authenticate.factory.AuthenticateServiceFactory;
|
||||
import org.dspace.authenticate.service.AuthenticationService;
|
||||
import org.dspace.eperson.EPerson;
|
||||
import org.dspace.eperson.factory.EPersonServiceFactory;
|
||||
import org.dspace.eperson.service.EPersonService;
|
||||
import org.dspace.rest.common.Status;
|
||||
import org.dspace.rest.exceptions.ContextException;
|
||||
import org.dspace.utils.DSpace;
|
||||
|
||||
/**
|
||||
* Root of RESTful api. It provides login and logout. Also have method for
|
||||
* printing every method which is provides by RESTful api.
|
||||
*
|
||||
* @author Rostislav Novak (Computing and Information Centre, CTU in Prague)
|
||||
*/
|
||||
@Path("/")
|
||||
public class RestIndex {
|
||||
protected EPersonService epersonService = EPersonServiceFactory.getInstance().getEPersonService();
|
||||
private static Logger log = org.apache.logging.log4j.LogManager.getLogger(RestIndex.class);
|
||||
|
||||
/**
|
||||
* Return html page with information about REST api. It contains methods all
|
||||
* methods provide by REST api.
|
||||
*
|
||||
* @param servletContext Context of the servlet container.
|
||||
* @return HTML page which has information about all methods of REST API.
|
||||
*/
|
||||
@GET
|
||||
@Produces(MediaType.TEXT_HTML)
|
||||
public String sayHtmlHello(@Context ServletContext servletContext) {
|
||||
// TODO Better graphics, add arguments to all methods. (limit, offset, item and so on)
|
||||
return "<html><title>DSpace REST - index</title>" +
|
||||
"<body>"
|
||||
+ "<h1>DSpace REST API (Deprecated)</h1>" +
|
||||
"<em>This REST API is deprecated and will be removed in v8." +
|
||||
" Please use the new Server API webapp instead.</em><br/>" +
|
||||
"Server path: " + servletContext.getContextPath() +
|
||||
"<h2>Index</h2>" +
|
||||
"<ul>" +
|
||||
"<li>GET / - Return this page.</li>" +
|
||||
"<li>GET /test - Return the string \"REST api is running\" for testing purposes.</li>" +
|
||||
"<li>POST /login - Method for logging into the DSpace RESTful API. You must post the parameters \"email\"" +
|
||||
" and \"password\". Example: \"email=test@dspace&password=pass\". Returns a JSESSIONID cookie which can " +
|
||||
"be used for future authenticated requests.</li>" +
|
||||
"<li>POST /logout - Method for logging out of the DSpace RESTful API. The request must include the " +
|
||||
"\"rest-dspace-token\" token</li> header." +
|
||||
"</ul>" +
|
||||
"<h2>Communities</h2>" +
|
||||
"<ul>" +
|
||||
"<li>GET /communities - Return an array of all communities in DSpace.</li>" +
|
||||
"<li>GET /communities/top-communities - Returns an array of all top-leve communities in DSpace.</li>" +
|
||||
"<li>GET /communities/{communityId} - Returns a community with the specified ID.</li>" +
|
||||
"<li>GET /communities/{communityId}/collections - Returns an array of collections of the specified " +
|
||||
"community.</li>" +
|
||||
"<li>GET /communities/{communityId}/communities - Returns an array of subcommunities of the specified " +
|
||||
"community.</li>" +
|
||||
"<li>POST /communities - Create a new top-level community. You must post a community.</li>" +
|
||||
"<li>POST /communities/{communityId}/collections - Create a new collection in the specified community. " +
|
||||
"You must post a collection.</li>" +
|
||||
"<li>POST /communities/{communityId}/communities - Create a new subcommunity in the specified community. " +
|
||||
"You must post a community.</li>" +
|
||||
"<li>PUT /communities/{communityId} - Update the specified community.</li>" +
|
||||
"<li>DELETE /communities/{communityId} - Delete the specified community.</li>" +
|
||||
"<li>DELETE /communities/{communityId}/collections/{collectionId} - Delete the specified collection in " +
|
||||
"the specified community.</li>" +
|
||||
"<li>DELETE /communities/{communityId}/communities/{communityId2} - Delete the specified subcommunity " +
|
||||
"(communityId2) in the specified community (communityId).</li>" +
|
||||
"</ul>" +
|
||||
"<h2>Collections</h2>" +
|
||||
"<ul>" +
|
||||
"<li>GET /collections - Return all DSpace collections in array.</li>" +
|
||||
"<li>GET /collections/{collectionId} - Return a collection with the specified ID.</li>" +
|
||||
"<li>GET /collections/{collectionId}/items - Return all items of the specified collection.</li>" +
|
||||
"<li>POST /collections/{collectionId}/items - Create an item in the specified collection. You must post " +
|
||||
"an item.</li>" +
|
||||
"<li>POST /collections/find-collection - Find a collection by name.</li>" +
|
||||
"<li>PUT /collections/{collectionId} </li> - Update the specified collection. You must post a collection." +
|
||||
"<li>DELETE /collections/{collectionId} - Delete the specified collection from DSpace.</li>" +
|
||||
"<li>DELETE /collections/{collectionId}/items/{itemId} - Delete the specified item (itemId) in the " +
|
||||
"specified collection (collectionId). </li>" +
|
||||
"</ul>" +
|
||||
"<h2>Items</h2>" +
|
||||
"<ul>" +
|
||||
"<li>GET /items - Return a list of items.</li>" +
|
||||
"<li>GET /items/{item id} - Return the specified item.</li>" +
|
||||
"<li>GET /items/{item id}/metadata - Return metadata of the specified item.</li>" +
|
||||
"<li>GET /items/{item id}/bitstreams - Return bitstreams of the specified item.</li>" +
|
||||
"<li>POST /items/find-by-metadata-field - Find items by the specified metadata value.</li>" +
|
||||
"<li>POST /items/{item id}/metadata - Add metadata to the specified item.</li>" +
|
||||
"<li>POST /items/{item id}/bitstreams - Add a bitstream to the specified item.</li>" +
|
||||
"<li>PUT /items/{item id}/metadata - Update metadata in the specified item.</li>" +
|
||||
"<li>DELETE /items/{item id} - Delete the specified item.</li>" +
|
||||
"<li>DELETE /items/{item id}/metadata - Clear metadata of the specified item.</li>" +
|
||||
"<li>DELETE /items/{item id}/bitstreams/{bitstream id} - Delete the specified bitstream of the specified " +
|
||||
"item.</li>" +
|
||||
"</ul>" +
|
||||
"<h2>Bitstreams</h2>" +
|
||||
"<ul>" +
|
||||
"<li>GET /bitstreams - Return all bitstreams in DSpace.</li>" +
|
||||
"<li>GET /bitstreams/{bitstream id} - Return the specified bitstream.</li>" +
|
||||
"<li>GET /bitstreams/{bitstream id}/policy - Return policies of the specified bitstream.</li>" +
|
||||
"<li>GET /bitstreams/{bitstream id}/retrieve - Return the contents of the specified bitstream.</li>" +
|
||||
"<li>POST /bitstreams/{bitstream id}/policy - Add a policy to the specified bitstream.</li>" +
|
||||
"<li>PUT /bitstreams/{bitstream id}/data - Update the contents of the specified bitstream.</li>" +
|
||||
"<li>PUT /bitstreams/{bitstream id} - Update metadata of the specified bitstream.</li>" +
|
||||
"<li>DELETE /bitstreams/{bitstream id} - Delete the specified bitstream from DSpace.</li>" +
|
||||
"<li>DELETE /bitstreams/{bitstream id}/policy/{policy_id} - Delete the specified bitstream policy.</li>" +
|
||||
"</ul>" +
|
||||
"<h2>Hierarchy</h2>" +
|
||||
"<ul>" +
|
||||
"<li>GET /hierarchy - Return hierarchy of communities and collections in tree form. Each object is " +
|
||||
"minimally populated (name, handle, id) for efficient retrieval.</li>" +
|
||||
"</ul>" +
|
||||
"<h2>Metadata and Schema Registry</h2>" +
|
||||
"<ul>" +
|
||||
"<li>GET /registries/schema - Return the list of metadata schemas in the registry</li>" +
|
||||
"<li>GET /registries/schema/{schema_prefix} - Returns the specified metadata schema</li>" +
|
||||
"<li>GET /registries/schema/{schema_prefix}/metadata-fields/{element} - Returns the metadata field within" +
|
||||
" a schema with an unqualified element name</li>" +
|
||||
"<li>GET /registries/schema/{schema_prefix}/metadata-fields/{element}/{qualifier} - Returns the metadata " +
|
||||
"field within a schema with a qualified element name</li>" +
|
||||
"<li>POST /registries/schema/ - Add a schema to the schema registry</li>" +
|
||||
"<li>POST /registries/schema/{schema_prefix}/metadata-fields - Add a metadata field to the specified " +
|
||||
"schema</li>" +
|
||||
"<li>GET /registries/metadata-fields/{field_id} - Return the specified metadata field</li>" +
|
||||
"<li>PUT /registries/metadata-fields/{field_id} - Update the specified metadata field</li>" +
|
||||
"<li>DELETE /registries/metadata-fields/{field_id} - Delete the specified metadata field from the " +
|
||||
"metadata field registry</li>" +
|
||||
"<li>DELETE /registries/schema/{schema_id} - Delete the specified schema from the schema registry</li>" +
|
||||
"</ul>" +
|
||||
"<h2>Query/Reporting Tools</h2>" +
|
||||
"<ul>" +
|
||||
"<li>GET /reports - Return a list of report tools built on the rest api</li>" +
|
||||
"<li>GET /reports/{nickname} - Return a redirect to a specific report</li>" +
|
||||
"<li>GET /filters - Return a list of use case filters available for quality control reporting</li>" +
|
||||
"<li>GET /filtered-collections - Return collections and item counts based on pre-defined filters</li>" +
|
||||
"<li>GET /filtered-collections/{collection_id} - Return items and item counts for a collection based on " +
|
||||
"pre-defined filters</li>" +
|
||||
"<li>GET /filtered-items - Retrieve a set of items based on a metadata query and a set of filters</li>" +
|
||||
"</ul>" +
|
||||
"</body></html> ";
|
||||
}
|
||||
|
||||
/**
|
||||
* Method only for testing whether the REST API is running.
|
||||
*
|
||||
* @return String "REST api is running."
|
||||
*/
|
||||
@GET
|
||||
@Path("/test")
|
||||
public String test() {
|
||||
return "REST api is running.";
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to login a user into REST API.
|
||||
*
|
||||
* @return Returns response code OK and a token. Otherwise returns response
|
||||
* code FORBIDDEN(403).
|
||||
*/
|
||||
@POST
|
||||
@Path("/login")
|
||||
@Consumes( {MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
|
||||
public Response login() {
|
||||
//If you can get here, you are authenticated, the actual login is handled by spring security
|
||||
return Response.ok().build();
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("/shibboleth-login")
|
||||
@Consumes( {MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
|
||||
public Response shibbolethLogin() {
|
||||
//If you can get here, you are authenticated, the actual login is handled by spring security
|
||||
return Response.ok().build();
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("/login-shibboleth")
|
||||
@Consumes( {MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
|
||||
public Response shibbolethLoginEndPoint() {
|
||||
org.dspace.core.Context context = null;
|
||||
try {
|
||||
context = Resource.createContext();
|
||||
AuthenticationService authenticationService = AuthenticateServiceFactory.getInstance()
|
||||
.getAuthenticationService();
|
||||
Iterator<AuthenticationMethod> authenticationMethodIterator = authenticationService
|
||||
.authenticationMethodIterator();
|
||||
while (authenticationMethodIterator.hasNext()) {
|
||||
AuthenticationMethod authenticationMethod = authenticationMethodIterator.next();
|
||||
if (authenticationMethod instanceof ShibAuthentication) {
|
||||
//TODO: Perhaps look for a better way of handling this ?
|
||||
org.dspace.services.model.Request currentRequest = new DSpace().getRequestService()
|
||||
.getCurrentRequest();
|
||||
String loginPageURL = authenticationMethod
|
||||
.loginPageURL(context, currentRequest.getHttpServletRequest(),
|
||||
currentRequest.getHttpServletResponse());
|
||||
if (StringUtils.isNotBlank(loginPageURL)) {
|
||||
currentRequest.getHttpServletResponse().sendRedirect(loginPageURL);
|
||||
}
|
||||
}
|
||||
}
|
||||
context.abort();
|
||||
} catch (ContextException | SQLException | IOException e) {
|
||||
Resource.processException("Shibboleth endpoint error: " + e.getMessage(), context);
|
||||
} finally {
|
||||
if (context != null && context.isValid()) {
|
||||
context.abort();
|
||||
}
|
||||
|
||||
}
|
||||
return Response.ok().build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to logout a user from DSpace REST API. Removes the token and user from
|
||||
* TokenHolder.
|
||||
*
|
||||
* @param headers Request header which contains the header named
|
||||
* "rest-dspace-token" containing the token as value.
|
||||
* @return Return response OK, otherwise BAD_REQUEST, if there was a problem with
|
||||
* logout or the token is incorrect.
|
||||
*/
|
||||
@POST
|
||||
@Path("/logout")
|
||||
@Consumes( {MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
|
||||
public Response logout(@Context HttpHeaders headers) {
|
||||
//If you can get here, you are logged out, this actual logout is handled by spring security
|
||||
return Response.ok().build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to check current status of the service and logged in user.
|
||||
*
|
||||
* okay: true | false
|
||||
* authenticated: true | false
|
||||
* epersonEMAIL: user@example.com
|
||||
* epersonNAME: John Doe
|
||||
*
|
||||
* @param headers Request header which contains the header named
|
||||
* "rest-dspace-token" containing the token as value.
|
||||
* @return status the Status object with information about REST API
|
||||
* @throws UnsupportedEncodingException The Character Encoding is not supported.
|
||||
*/
|
||||
@GET
|
||||
@Path("/status")
|
||||
@Consumes( {MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
|
||||
public Status status(@Context HttpHeaders headers)
|
||||
throws UnsupportedEncodingException {
|
||||
org.dspace.core.Context context = null;
|
||||
|
||||
try {
|
||||
context = Resource.createContext();
|
||||
EPerson ePerson = context.getCurrentUser();
|
||||
|
||||
if (ePerson != null) {
|
||||
//DB EPerson needed since token won't have full info, need context
|
||||
EPerson dbEPerson = epersonService.findByEmail(context, ePerson.getEmail());
|
||||
|
||||
Status status = new Status(dbEPerson.getEmail(), dbEPerson.getFullName());
|
||||
return status;
|
||||
}
|
||||
} catch (ContextException e) {
|
||||
Resource.processException("Status context error: " + e.getMessage(), context);
|
||||
} catch (SQLException e) {
|
||||
Resource.processException("Status eperson db lookup error: " + e.getMessage(), context);
|
||||
} finally {
|
||||
context.abort();
|
||||
}
|
||||
|
||||
//fallback status, unauth
|
||||
return new Status();
|
||||
}
|
||||
}
|
@@ -1,86 +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.rest;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import javax.ws.rs.GET;
|
||||
import javax.ws.rs.Path;
|
||||
import javax.ws.rs.PathParam;
|
||||
import javax.ws.rs.Produces;
|
||||
import javax.ws.rs.WebApplicationException;
|
||||
import javax.ws.rs.core.Context;
|
||||
import javax.ws.rs.core.MediaType;
|
||||
import javax.ws.rs.core.Response;
|
||||
import javax.ws.rs.core.UriInfo;
|
||||
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.dspace.rest.common.Report;
|
||||
import org.dspace.services.ConfigurationService;
|
||||
import org.dspace.services.factory.DSpaceServicesFactory;
|
||||
|
||||
|
||||
/**
|
||||
* Root of RESTful api. It provides login and logout. Also have method for
|
||||
* printing every method which is provides by RESTful api.
|
||||
*
|
||||
* @author Terry Brady, Georgetown University
|
||||
*/
|
||||
@Path("/reports")
|
||||
public class RestReports {
|
||||
private static Logger log = org.apache.logging.log4j.LogManager.getLogger(RestReports.class);
|
||||
|
||||
protected ConfigurationService configurationService = DSpaceServicesFactory.getInstance().getConfigurationService();
|
||||
public static final String REST_RPT_URL = "rest.report-url.";
|
||||
|
||||
/**
|
||||
* Return html page with information about REST api. It contains methods all
|
||||
* methods provide by REST api.
|
||||
*
|
||||
* @return HTML page which has information about all methods of REST api.
|
||||
*/
|
||||
@GET
|
||||
@Produces(MediaType.APPLICATION_XML)
|
||||
public Report[] reportIndex()
|
||||
throws WebApplicationException {
|
||||
ArrayList<Report> reports = new ArrayList<Report>();
|
||||
List<String> propNames = configurationService.getPropertyKeys("rest");
|
||||
for (String propName : propNames) {
|
||||
if (propName.startsWith(REST_RPT_URL)) {
|
||||
String nickname = propName.substring(REST_RPT_URL.length());
|
||||
String url = configurationService.getProperty(propName);
|
||||
reports.add(new Report(nickname, url));
|
||||
}
|
||||
}
|
||||
return reports.toArray(new Report[0]);
|
||||
}
|
||||
|
||||
@Path("/{report_nickname}")
|
||||
@GET
|
||||
public Response customReport(@PathParam("report_nickname") String report_nickname, @Context UriInfo uriInfo)
|
||||
throws WebApplicationException {
|
||||
URI uri = null;
|
||||
if (!report_nickname.isEmpty()) {
|
||||
log.info(String.format("Seeking report %s", report_nickname));
|
||||
String url = configurationService.getProperty(REST_RPT_URL + report_nickname);
|
||||
|
||||
log.info(String.format("URL for report %s found: [%s]", report_nickname, url));
|
||||
if (!url.isEmpty()) {
|
||||
uri = uriInfo.getBaseUriBuilder().path(url).build("");
|
||||
log.info(String.format("URI for report %s", uri));
|
||||
}
|
||||
}
|
||||
|
||||
if (uri != null) {
|
||||
return Response.temporaryRedirect(uri).build();
|
||||
}
|
||||
|
||||
return Response.noContent().build();
|
||||
}
|
||||
}
|
@@ -1,129 +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.rest.authentication;
|
||||
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.dspace.authenticate.AuthenticationMethod;
|
||||
import org.dspace.authenticate.factory.AuthenticateServiceFactory;
|
||||
import org.dspace.authenticate.service.AuthenticationService;
|
||||
import org.dspace.core.Context;
|
||||
import org.dspace.core.LogHelper;
|
||||
import org.dspace.eperson.EPerson;
|
||||
import org.dspace.eperson.Group;
|
||||
import org.dspace.utils.DSpace;
|
||||
import org.springframework.security.authentication.AuthenticationProvider;
|
||||
import org.springframework.security.authentication.BadCredentialsException;
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.AuthenticationException;
|
||||
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||
|
||||
/**
|
||||
* The core authentication & authorization provider, this provider is called when logging in & will process
|
||||
*
|
||||
* @author Roeland Dillen (roeland at atmire dot com)
|
||||
* @author kevinvandevelde at atmire.com
|
||||
*
|
||||
* FIXME This provider handles both the authorization as well as the authentication,
|
||||
* due to the way that the DSpace authentication is implemented there is currently no other way to do this.
|
||||
*/
|
||||
public class DSpaceAuthenticationProvider implements AuthenticationProvider {
|
||||
|
||||
private static final Logger log = LogManager.getLogger();
|
||||
|
||||
protected AuthenticationService authenticationService = AuthenticateServiceFactory.getInstance()
|
||||
.getAuthenticationService();
|
||||
|
||||
@Override
|
||||
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
|
||||
Context context = null;
|
||||
|
||||
try {
|
||||
context = new Context();
|
||||
String name = authentication.getName();
|
||||
String password = authentication.getCredentials().toString();
|
||||
HttpServletRequest httpServletRequest = new DSpace().getRequestService().getCurrentRequest()
|
||||
.getHttpServletRequest();
|
||||
List<SimpleGrantedAuthority> grantedAuthorities = new ArrayList<>();
|
||||
|
||||
|
||||
int implicitStatus = authenticationService
|
||||
.authenticateImplicit(context, null, null, null, httpServletRequest);
|
||||
|
||||
if (implicitStatus == AuthenticationMethod.SUCCESS) {
|
||||
log.info(LogHelper.getHeader(context, "login", "type=implicit"));
|
||||
addSpecialGroupsToGrantedAuthorityList(context, httpServletRequest, grantedAuthorities);
|
||||
return createAuthenticationToken(password, context, grantedAuthorities);
|
||||
|
||||
} else {
|
||||
int authenticateResult = authenticationService
|
||||
.authenticate(context, name, password, null, httpServletRequest);
|
||||
if (AuthenticationMethod.SUCCESS == authenticateResult) {
|
||||
addSpecialGroupsToGrantedAuthorityList(context, httpServletRequest, grantedAuthorities);
|
||||
|
||||
log.info(LogHelper.getHeader(context, "login", "type=explicit"));
|
||||
|
||||
return createAuthenticationToken(password, context, grantedAuthorities);
|
||||
|
||||
} else {
|
||||
log.info(LogHelper.getHeader(context, "failed_login",
|
||||
"email=" + name + ", result=" + authenticateResult));
|
||||
throw new BadCredentialsException("Login failed");
|
||||
}
|
||||
}
|
||||
} catch (BadCredentialsException e) {
|
||||
throw e;
|
||||
} catch (Exception e) {
|
||||
log.error("Error while authenticating in the rest api", e);
|
||||
} finally {
|
||||
if (context != null && context.isValid()) {
|
||||
try {
|
||||
context.complete();
|
||||
} catch (SQLException e) {
|
||||
log.error(e.getMessage() + " occurred while trying to close", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
protected void addSpecialGroupsToGrantedAuthorityList(Context context, HttpServletRequest httpServletRequest,
|
||||
List<SimpleGrantedAuthority> grantedAuthorities)
|
||||
throws SQLException {
|
||||
List<Group> groups = authenticationService.getSpecialGroups(context, httpServletRequest);
|
||||
for (Group group : groups) {
|
||||
grantedAuthorities.add(new SimpleGrantedAuthority(group.getName()));
|
||||
}
|
||||
}
|
||||
|
||||
private Authentication createAuthenticationToken(final String password, final Context context,
|
||||
final List<SimpleGrantedAuthority> grantedAuthorities) {
|
||||
EPerson ePerson = context.getCurrentUser();
|
||||
if (ePerson != null && StringUtils.isNotBlank(ePerson.getEmail())) {
|
||||
return new UsernamePasswordAuthenticationToken(ePerson.getEmail(), password, grantedAuthorities);
|
||||
|
||||
} else {
|
||||
log.info(LogHelper.getHeader(context, "failed_login",
|
||||
"No eperson with an non-blank e-mail address found"));
|
||||
throw new BadCredentialsException("Login failed");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supports(Class<?> authentication) {
|
||||
return (UsernamePasswordAuthenticationToken.class.isAssignableFrom(authentication));
|
||||
}
|
||||
}
|
@@ -1,41 +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.rest.authentication;
|
||||
|
||||
import java.io.IOException;
|
||||
import javax.annotation.PostConstruct;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.springframework.security.web.RedirectStrategy;
|
||||
import org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler;
|
||||
|
||||
/**
|
||||
* @author kevinvandevelde at atmire.com
|
||||
*
|
||||
* Spring redirects to the home page after a successfull login. This success handles ensures that this is NOT the case.
|
||||
*/
|
||||
public class NoRedirectAuthenticationLoginSuccessHandler extends SimpleUrlAuthenticationSuccessHandler {
|
||||
|
||||
@PostConstruct
|
||||
public void afterPropertiesSet() {
|
||||
setRedirectStrategy(new NoRedirectStrategy());
|
||||
}
|
||||
|
||||
protected class NoRedirectStrategy implements RedirectStrategy {
|
||||
|
||||
@Override
|
||||
public void sendRedirect(HttpServletRequest request,
|
||||
HttpServletResponse response, String url) throws IOException {
|
||||
// no redirect
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@@ -1,39 +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.rest.authentication;
|
||||
|
||||
import java.io.IOException;
|
||||
import javax.annotation.PostConstruct;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.springframework.security.web.RedirectStrategy;
|
||||
import org.springframework.security.web.authentication.logout.SimpleUrlLogoutSuccessHandler;
|
||||
|
||||
/**
|
||||
* @author kevinvandevelde at atmire.com
|
||||
*
|
||||
* Spring redirects to the home page after a successfull logout. This success handles ensures that this is NOT the case.
|
||||
*/
|
||||
public class NoRedirectAuthenticationLogoutSuccessHandler extends SimpleUrlLogoutSuccessHandler {
|
||||
@PostConstruct
|
||||
public void afterPropertiesSet() {
|
||||
setRedirectStrategy(new NoRedirectStrategy());
|
||||
}
|
||||
|
||||
protected class NoRedirectStrategy implements RedirectStrategy {
|
||||
|
||||
@Override
|
||||
public void sendRedirect(HttpServletRequest request,
|
||||
HttpServletResponse response, String url) throws IOException {
|
||||
// no redirect
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@@ -1,199 +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.rest.common;
|
||||
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import javax.servlet.ServletContext;
|
||||
import javax.xml.bind.annotation.XmlRootElement;
|
||||
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.dspace.content.Bundle;
|
||||
import org.dspace.content.factory.ContentServiceFactory;
|
||||
import org.dspace.content.service.BitstreamService;
|
||||
import org.dspace.content.service.BundleService;
|
||||
import org.dspace.core.Constants;
|
||||
import org.dspace.core.Context;
|
||||
import org.dspace.utils.DSpace;
|
||||
|
||||
/**
|
||||
* Created with IntelliJ IDEA.
|
||||
* User: peterdietz
|
||||
* Date: 9/21/13
|
||||
* Time: 12:54 AM
|
||||
* To change this template use File | Settings | File Templates.
|
||||
*/
|
||||
@XmlRootElement(name = "bitstream")
|
||||
public class Bitstream extends DSpaceObject {
|
||||
protected BitstreamService bitstreamService = ContentServiceFactory.getInstance().getBitstreamService();
|
||||
protected BundleService bundleService = ContentServiceFactory.getInstance().getBundleService();
|
||||
|
||||
Logger log = org.apache.logging.log4j.LogManager.getLogger(Bitstream.class);
|
||||
|
||||
private String bundleName;
|
||||
private String description;
|
||||
private String format;
|
||||
private String mimeType;
|
||||
private Long sizeBytes;
|
||||
private DSpaceObject parentObject;
|
||||
private String retrieveLink;
|
||||
private CheckSum checkSum;
|
||||
private Integer sequenceId;
|
||||
|
||||
private ResourcePolicy[] policies = null;
|
||||
|
||||
public Bitstream() {
|
||||
|
||||
}
|
||||
|
||||
public Bitstream(org.dspace.content.Bitstream bitstream, ServletContext servletContext, String expand,
|
||||
Context context)
|
||||
throws SQLException {
|
||||
super(bitstream, servletContext);
|
||||
setup(bitstream, servletContext, expand, context);
|
||||
}
|
||||
|
||||
public void setup(org.dspace.content.Bitstream bitstream, ServletContext servletContext, String expand,
|
||||
Context context)
|
||||
throws SQLException {
|
||||
List<String> expandFields = new ArrayList<String>();
|
||||
if (expand != null) {
|
||||
expandFields = Arrays.asList(expand.split(","));
|
||||
}
|
||||
|
||||
//A logo bitstream might not have a bundle...
|
||||
if (bitstream.getBundles() != null && !bitstream.getBundles().isEmpty()) {
|
||||
if (bitstreamService.getParentObject(context, bitstream).getType() == Constants.ITEM) {
|
||||
bundleName = bitstream.getBundles().get(0).getName();
|
||||
}
|
||||
}
|
||||
|
||||
description = bitstream.getDescription();
|
||||
format = bitstreamService.getFormatDescription(context, bitstream);
|
||||
sizeBytes = bitstream.getSizeBytes();
|
||||
String path = new DSpace().getRequestService().getCurrentRequest().getHttpServletRequest().getContextPath();
|
||||
retrieveLink = path + "/bitstreams/" + bitstream.getID() + "/retrieve";
|
||||
mimeType = bitstreamService.getFormat(context, bitstream).getMIMEType();
|
||||
sequenceId = bitstream.getSequenceID();
|
||||
CheckSum checkSum = new CheckSum();
|
||||
checkSum.setCheckSumAlgorith(bitstream.getChecksumAlgorithm());
|
||||
checkSum.setValue(bitstream.getChecksum());
|
||||
this.setCheckSum(checkSum);
|
||||
|
||||
if (expandFields.contains("parent") || expandFields.contains("all")) {
|
||||
parentObject = new DSpaceObject(bitstreamService.getParentObject(context, bitstream), servletContext);
|
||||
} else {
|
||||
this.addExpand("parent");
|
||||
}
|
||||
|
||||
if (expandFields.contains("policies") || expandFields.contains("all")) {
|
||||
// Find policies without context.
|
||||
List<ResourcePolicy> tempPolicies = new ArrayList<ResourcePolicy>();
|
||||
List<Bundle> bundles = bitstream.getBundles();
|
||||
for (Bundle bundle : bundles) {
|
||||
List<org.dspace.authorize.ResourcePolicy> bitstreamsPolicies = bundleService
|
||||
.getBitstreamPolicies(context, bundle);
|
||||
for (org.dspace.authorize.ResourcePolicy policy : bitstreamsPolicies) {
|
||||
if (policy.getdSpaceObject().equals(bitstream)) {
|
||||
tempPolicies.add(new ResourcePolicy(policy));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
policies = tempPolicies.toArray(new ResourcePolicy[0]);
|
||||
} else {
|
||||
this.addExpand("policies");
|
||||
}
|
||||
|
||||
if (!expandFields.contains("all")) {
|
||||
this.addExpand("all");
|
||||
}
|
||||
}
|
||||
|
||||
public Integer getSequenceId() {
|
||||
return sequenceId;
|
||||
}
|
||||
|
||||
public void setSequenceId(Integer sequenceId) {
|
||||
this.sequenceId = sequenceId;
|
||||
}
|
||||
|
||||
public String getBundleName() {
|
||||
return bundleName;
|
||||
}
|
||||
|
||||
public void setBundleName(String bundleName) {
|
||||
this.bundleName = bundleName;
|
||||
}
|
||||
|
||||
public void setDescription(String description) {
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
public void setFormat(String format) {
|
||||
this.format = format;
|
||||
}
|
||||
|
||||
public void setMimeType(String mimeType) {
|
||||
this.mimeType = mimeType;
|
||||
}
|
||||
|
||||
public void setSizeBytes(Long sizeBytes) {
|
||||
this.sizeBytes = sizeBytes;
|
||||
}
|
||||
|
||||
public void setParentObject(DSpaceObject parentObject) {
|
||||
this.parentObject = parentObject;
|
||||
}
|
||||
|
||||
public void setRetrieveLink(String retrieveLink) {
|
||||
this.retrieveLink = retrieveLink;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
public String getFormat() {
|
||||
return format;
|
||||
}
|
||||
|
||||
public String getMimeType() {
|
||||
return mimeType;
|
||||
}
|
||||
|
||||
public Long getSizeBytes() {
|
||||
return sizeBytes;
|
||||
}
|
||||
|
||||
public String getRetrieveLink() {
|
||||
return retrieveLink;
|
||||
}
|
||||
|
||||
public DSpaceObject getParentObject() {
|
||||
return parentObject;
|
||||
}
|
||||
|
||||
public CheckSum getCheckSum() {
|
||||
return checkSum;
|
||||
}
|
||||
|
||||
public void setCheckSum(CheckSum checkSum) {
|
||||
this.checkSum = checkSum;
|
||||
}
|
||||
|
||||
public ResourcePolicy[] getPolicies() {
|
||||
return policies;
|
||||
}
|
||||
|
||||
public void setPolicies(ResourcePolicy[] policies) {
|
||||
this.policies = policies;
|
||||
}
|
||||
}
|
@@ -1,40 +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.rest.common;
|
||||
|
||||
import javax.xml.bind.annotation.XmlAttribute;
|
||||
import javax.xml.bind.annotation.XmlType;
|
||||
import javax.xml.bind.annotation.XmlValue;
|
||||
|
||||
@XmlType
|
||||
public class CheckSum {
|
||||
String checkSumAlgorithm;
|
||||
String value;
|
||||
|
||||
public CheckSum() {
|
||||
}
|
||||
|
||||
@XmlAttribute(name = "checkSumAlgorithm")
|
||||
public String getCheckSumAlgorith() {
|
||||
return checkSumAlgorithm;
|
||||
}
|
||||
|
||||
public void setCheckSumAlgorith(String checkSumAlgorith) {
|
||||
this.checkSumAlgorithm = checkSumAlgorith;
|
||||
}
|
||||
|
||||
@XmlValue
|
||||
public String getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public void setValue(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
}
|
@@ -1,225 +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.rest.common;
|
||||
|
||||
import static org.dspace.content.service.DSpaceObjectService.MD_COPYRIGHT_TEXT;
|
||||
import static org.dspace.content.service.DSpaceObjectService.MD_INTRODUCTORY_TEXT;
|
||||
import static org.dspace.content.service.DSpaceObjectService.MD_SHORT_DESCRIPTION;
|
||||
import static org.dspace.content.service.DSpaceObjectService.MD_SIDEBAR_TEXT;
|
||||
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import javax.servlet.ServletContext;
|
||||
import javax.ws.rs.WebApplicationException;
|
||||
import javax.xml.bind.annotation.XmlRootElement;
|
||||
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.dspace.content.factory.ContentServiceFactory;
|
||||
import org.dspace.content.service.CollectionService;
|
||||
import org.dspace.content.service.CommunityService;
|
||||
import org.dspace.content.service.ItemService;
|
||||
import org.dspace.core.Context;
|
||||
|
||||
/**
|
||||
* Created with IntelliJ IDEA.
|
||||
* User: peterdietz
|
||||
* Date: 5/22/13
|
||||
* Time: 9:41 AM
|
||||
*/
|
||||
@XmlRootElement(name = "collection")
|
||||
public class Collection extends DSpaceObject {
|
||||
protected CommunityService communityService = ContentServiceFactory.getInstance().getCommunityService();
|
||||
protected CollectionService collectionService = ContentServiceFactory.getInstance().getCollectionService();
|
||||
protected ItemService itemService = ContentServiceFactory.getInstance().getItemService();
|
||||
|
||||
Logger log = org.apache.logging.log4j.LogManager.getLogger(Collection.class);
|
||||
|
||||
//Relationships
|
||||
private Bitstream logo;
|
||||
private Community parentCommunity;
|
||||
private List<Community> parentCommunityList = new ArrayList<>();
|
||||
|
||||
private List<Item> items = new ArrayList<>();
|
||||
|
||||
//Collection-Metadata
|
||||
private String license;
|
||||
private String copyrightText;
|
||||
private String introductoryText;
|
||||
private String shortDescription;
|
||||
private String sidebarText;
|
||||
|
||||
//Calculated
|
||||
private Integer numberItems;
|
||||
|
||||
public Collection() {
|
||||
}
|
||||
|
||||
public Collection(org.dspace.content.Collection collection, ServletContext servletContext, String expand,
|
||||
Context context, Integer limit, Integer offset)
|
||||
throws SQLException, WebApplicationException {
|
||||
super(collection, servletContext);
|
||||
setup(collection, servletContext, expand, context, limit, offset);
|
||||
}
|
||||
|
||||
private void setup(org.dspace.content.Collection collection, ServletContext servletContext, String expand,
|
||||
Context context, Integer limit, Integer offset)
|
||||
throws SQLException {
|
||||
List<String> expandFields = new ArrayList<>();
|
||||
if (expand != null) {
|
||||
expandFields = Arrays.asList(expand.split(","));
|
||||
}
|
||||
|
||||
this.setCopyrightText(collectionService.getMetadataFirstValue(collection,
|
||||
MD_COPYRIGHT_TEXT, org.dspace.content.Item.ANY));
|
||||
this.setIntroductoryText(collectionService.getMetadataFirstValue(collection,
|
||||
MD_INTRODUCTORY_TEXT, org.dspace.content.Item.ANY));
|
||||
this.setShortDescription(collectionService.getMetadataFirstValue(collection,
|
||||
MD_SHORT_DESCRIPTION, org.dspace.content.Item.ANY));
|
||||
this.setSidebarText(collectionService.getMetadataFirstValue(collection,
|
||||
MD_SIDEBAR_TEXT, org.dspace.content.Item.ANY));
|
||||
|
||||
if (expandFields.contains("parentCommunityList") || expandFields.contains("all")) {
|
||||
List<org.dspace.content.Community> parentCommunities = communityService.getAllParents(context, collection);
|
||||
for (org.dspace.content.Community parentCommunity : parentCommunities) {
|
||||
this.addParentCommunityList(new Community(parentCommunity, servletContext, null, context));
|
||||
}
|
||||
} else {
|
||||
this.addExpand("parentCommunityList");
|
||||
}
|
||||
|
||||
if (expandFields.contains("parentCommunity") | expandFields.contains("all")) {
|
||||
org.dspace.content.Community parentCommunity =
|
||||
(org.dspace.content.Community) collectionService
|
||||
.getParentObject(context, collection);
|
||||
this.setParentCommunity(new Community(
|
||||
parentCommunity, servletContext, null, context));
|
||||
} else {
|
||||
this.addExpand("parentCommunity");
|
||||
}
|
||||
|
||||
//TODO: Item paging. limit, offset/page
|
||||
if (expandFields.contains("items") || expandFields.contains("all")) {
|
||||
Iterator<org.dspace.content.Item> childItems =
|
||||
itemService.findByCollection(context, collection, limit, offset);
|
||||
|
||||
items = new ArrayList<>();
|
||||
while (childItems.hasNext()) {
|
||||
org.dspace.content.Item item = childItems.next();
|
||||
|
||||
if (itemService.isItemListedForUser(context, item)) {
|
||||
items.add(new Item(item, servletContext, null, context));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
this.addExpand("items");
|
||||
}
|
||||
|
||||
if (expandFields.contains("license") || expandFields.contains("all")) {
|
||||
setLicense(collectionService.getLicense(collection));
|
||||
} else {
|
||||
this.addExpand("license");
|
||||
}
|
||||
|
||||
if (expandFields.contains("logo") || expandFields.contains("all")) {
|
||||
if (collection.getLogo() != null) {
|
||||
this.logo = new Bitstream(collection.getLogo(), servletContext, null, context);
|
||||
}
|
||||
} else {
|
||||
this.addExpand("logo");
|
||||
}
|
||||
|
||||
if (!expandFields.contains("all")) {
|
||||
this.addExpand("all");
|
||||
}
|
||||
|
||||
this.setNumberItems(itemService.countItems(context, collection));
|
||||
}
|
||||
|
||||
public Bitstream getLogo() {
|
||||
return logo;
|
||||
}
|
||||
|
||||
public Integer getNumberItems() {
|
||||
return numberItems;
|
||||
}
|
||||
|
||||
public void setNumberItems(Integer numberItems) {
|
||||
this.numberItems = numberItems;
|
||||
}
|
||||
|
||||
public Community getParentCommunity() {
|
||||
return parentCommunity;
|
||||
}
|
||||
|
||||
public void setParentCommunity(Community parentCommunity) {
|
||||
this.parentCommunity = parentCommunity;
|
||||
}
|
||||
|
||||
public List<Item> getItems() {
|
||||
return items;
|
||||
}
|
||||
|
||||
public void setItems(List<Item> items) {
|
||||
this.items = items;
|
||||
}
|
||||
|
||||
public void setParentCommunityList(List<Community> parentCommunityList) {
|
||||
this.parentCommunityList = parentCommunityList;
|
||||
}
|
||||
|
||||
public List<Community> getParentCommunityList() {
|
||||
return parentCommunityList;
|
||||
}
|
||||
|
||||
public void addParentCommunityList(Community parentCommunity) {
|
||||
this.parentCommunityList.add(parentCommunity);
|
||||
}
|
||||
|
||||
public String getLicense() {
|
||||
return license;
|
||||
}
|
||||
|
||||
public void setLicense(String license) {
|
||||
this.license = license;
|
||||
}
|
||||
|
||||
public String getCopyrightText() {
|
||||
return copyrightText;
|
||||
}
|
||||
|
||||
public void setCopyrightText(String copyrightText) {
|
||||
this.copyrightText = copyrightText;
|
||||
}
|
||||
|
||||
public String getIntroductoryText() {
|
||||
return introductoryText;
|
||||
}
|
||||
|
||||
public void setIntroductoryText(String introductoryText) {
|
||||
this.introductoryText = introductoryText;
|
||||
}
|
||||
|
||||
public String getShortDescription() {
|
||||
return shortDescription;
|
||||
}
|
||||
|
||||
public void setShortDescription(String shortDescription) {
|
||||
this.shortDescription = shortDescription;
|
||||
}
|
||||
|
||||
public String getSidebarText() {
|
||||
return sidebarText;
|
||||
}
|
||||
|
||||
public void setSidebarText(String sidebarText) {
|
||||
this.sidebarText = sidebarText;
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user