mirror of
https://github.com/DSpace/DSpace.git
synced 2025-10-07 18:14:26 +00:00
Merge remote-tracking branch 'community/main' into feature-curation-link-checker-customizations-7.x
This commit is contained in:
8
.github/ISSUE_TEMPLATE/bug_report.md
vendored
8
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@@ -7,16 +7,16 @@ assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Describe the bug**
|
||||
## Describe the bug
|
||||
A clear and concise description of what the bug is. Include the version(s) of DSpace where you've seen this problem. Link to examples if they are public.
|
||||
|
||||
**To Reproduce**
|
||||
## To Reproduce
|
||||
Steps to reproduce the behavior:
|
||||
1. Do this
|
||||
2. Then this...
|
||||
|
||||
**Expected behavior**
|
||||
## Expected behavior
|
||||
A clear and concise description of what you expected to happen.
|
||||
|
||||
**Related work**
|
||||
## Related work
|
||||
Link to any related tickets or PRs here.
|
||||
|
12
.github/ISSUE_TEMPLATE/feature_request.md
vendored
12
.github/ISSUE_TEMPLATE/feature_request.md
vendored
@@ -7,14 +7,14 @@ assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Is your feature request related to a problem? Please describe.**
|
||||
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
||||
## Is your feature request related to a problem? Please describe.
|
||||
A clear and concise description of what the problem or use case is. For example, I'm always frustrated when [...]
|
||||
|
||||
**Describe the solution you'd like**
|
||||
## Describe the solution you'd like
|
||||
A clear and concise description of what you want to happen.
|
||||
|
||||
**Describe alternatives or workarounds you've considered**
|
||||
## Describe alternatives or workarounds you've considered
|
||||
A clear and concise description of any alternative solutions or features you've considered.
|
||||
|
||||
**Additional context**
|
||||
Add any other context or screenshots about the feature request here.
|
||||
## Additional information
|
||||
Add any other information, related tickets or screenshots about the feature request here.
|
||||
|
118
.github/dependabot.yml
vendored
Normal file
118
.github/dependabot.yml
vendored
Normal file
@@ -0,0 +1,118 @@
|
||||
#-------------------
|
||||
# DSpace's dependabot rules. Enables maven updates for all dependencies on a weekly basis
|
||||
# for main and any maintenance branches. Security updates only apply to main.
|
||||
#-------------------
|
||||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: "maven"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
# Allow up to 10 open PRs for dependencies
|
||||
open-pull-requests-limit: 10
|
||||
# Group together some upgrades in a single PR
|
||||
groups:
|
||||
# Group together all Build Tools in a single PR
|
||||
build-tools:
|
||||
applies-to: version-updates
|
||||
patterns:
|
||||
- "org.apache.maven.plugins:*"
|
||||
- "*:*-maven-plugin"
|
||||
- "*:maven-*-plugin"
|
||||
- "com.github.spotbugs:spotbugs"
|
||||
- "com.google.code.findbugs:*"
|
||||
- "com.google.errorprone:*"
|
||||
- "com.puppycrawl.tools:checkstyle"
|
||||
- "org.sonatype.plugins:*"
|
||||
exclude-patterns:
|
||||
# Exclude anything from Spring, as that is in a separate group
|
||||
- "org.springframework.*:*"
|
||||
update-types:
|
||||
- "minor"
|
||||
- "patch"
|
||||
test-tools:
|
||||
applies-to: version-updates
|
||||
patterns:
|
||||
- "junit:*"
|
||||
- "com.github.stefanbirker:system-rules"
|
||||
- "com.h2database:*"
|
||||
- "io.findify:s3mock*"
|
||||
- "io.netty:*"
|
||||
- "org.hamcrest:*"
|
||||
- "org.mock-server:*"
|
||||
- "org.mockito:*"
|
||||
update-types:
|
||||
- "minor"
|
||||
- "patch"
|
||||
# Group together all Apache Commons deps in a single PR
|
||||
apache-commons:
|
||||
applies-to: version-updates
|
||||
patterns:
|
||||
- "org.apache.commons:*"
|
||||
- "commons-*:commons-*"
|
||||
update-types:
|
||||
- "minor"
|
||||
- "patch"
|
||||
# Group together all fasterxml deps in a single PR
|
||||
fasterxml:
|
||||
applies-to: version-updates
|
||||
patterns:
|
||||
- "com.fasterxml:*"
|
||||
- "com.fasterxml.*:*"
|
||||
update-types:
|
||||
- "minor"
|
||||
- "patch"
|
||||
# Group together all Hibernate deps in a single PR
|
||||
hibernate:
|
||||
applies-to: version-updates
|
||||
patterns:
|
||||
- "org.hibernate.*:*"
|
||||
update-types:
|
||||
- "minor"
|
||||
- "patch"
|
||||
# Group together all Jakarta deps in a single PR
|
||||
jakarta:
|
||||
applies-to: version-updates
|
||||
patterns:
|
||||
- "jakarta.*:*"
|
||||
- "org.eclipse.angus:jakarta.mail"
|
||||
- "org.glassfish.jaxb:jaxb-runtime"
|
||||
update-types:
|
||||
- "minor"
|
||||
- "patch"
|
||||
# Group together all Google deps in a single PR
|
||||
google-apis:
|
||||
applies-to: version-updates
|
||||
patterns:
|
||||
- "com.google.apis:*"
|
||||
- "com.google.api-client:*"
|
||||
- "com.google.http-client:*"
|
||||
- "com.google.oauth-client:*"
|
||||
update-types:
|
||||
- "minor"
|
||||
- "patch"
|
||||
# Group together all Spring deps in a single PR
|
||||
spring:
|
||||
applies-to: version-updates
|
||||
patterns:
|
||||
- "org.springframework:*"
|
||||
- "org.springframework.*:*"
|
||||
update-types:
|
||||
- "minor"
|
||||
- "patch"
|
||||
# Group together all WebJARs deps in a single PR
|
||||
webjars:
|
||||
applies-to: version-updates
|
||||
patterns:
|
||||
- "org.webjars:*"
|
||||
- "org.webjars.*:*"
|
||||
update-types:
|
||||
- "minor"
|
||||
- "patch"
|
||||
ignore:
|
||||
# Don't try to auto-update any DSpace dependencies
|
||||
- dependency-name: "org.dspace:*"
|
||||
- dependency-name: "org.dspace.*:*"
|
||||
# Ignore all major version updates for all dependencies. We'll only automate minor/patch updates.
|
||||
- dependency-name: "*"
|
||||
update-types: ["version-update:semver-major"]
|
17
.github/pull_request_template.md
vendored
17
.github/pull_request_template.md
vendored
@@ -1,7 +1,7 @@
|
||||
## References
|
||||
_Add references/links to any related issues or PRs. These may include:_
|
||||
* Fixes #`issue-number` (if this fixes an issue ticket)
|
||||
* Related to DSpace/RestContract#`pr-number` (if a corresponding REST Contract PR exists)
|
||||
* Fixes #issue-number (if this fixes an issue ticket)
|
||||
* Related to DSpace/RestContract#pr-number (if a corresponding REST Contract PR exists)
|
||||
|
||||
## Description
|
||||
Short summary of changes (1-2 sentences).
|
||||
@@ -16,12 +16,15 @@ List of changes in this PR:
|
||||
**Include guidance for how to test or review your PR.** This may include: steps to reproduce a bug, screenshots or description of a new feature, or reasons behind specific changes.
|
||||
|
||||
## Checklist
|
||||
_This checklist provides a reminder of what we are going to look for when reviewing your PR. You need not complete this checklist prior to creating your PR (draft PRs are always welcome). If you are unsure about an item in the checklist, don't hesitate to ask. We're here to help!_
|
||||
_This checklist provides a reminder of what we are going to look for when reviewing your PR. You need not complete this checklist prior to creating your PR (draft PRs are always welcome).
|
||||
However, reviewers may request that you complete any actions in this list if you have not done so. If you are unsure about an item in the checklist, don't hesitate to ask. We're here to help!_
|
||||
|
||||
- [ ] My PR is small in size (e.g. less than 1,000 lines of code, not including comments & integration tests). Exceptions may be made if previously agreed upon.
|
||||
- [ ] My PR passes Checkstyle validation based on the [Code Style Guide](https://wiki.lyrasis.org/display/DSPACE/Code+Style+Guide).
|
||||
- [ ] My PR includes Javadoc for _all new (or modified) public methods and classes_. It also includes Javadoc for large or complex private methods.
|
||||
- [ ] My PR passes all tests and includes new/updated Unit or Integration Tests based on the [Code Testing Guide](https://wiki.lyrasis.org/display/DSPACE/Code+Testing+Guide).
|
||||
- [ ] My PR is **created against the `main` branch** of code (unless it is a backport or is fixing an issue specific to an older branch).
|
||||
- [ ] My PR is **small in size** (e.g. less than 1,000 lines of code, not including comments & integration tests). Exceptions may be made if previously agreed upon.
|
||||
- [ ] My PR **passes Checkstyle** validation based on the [Code Style Guide](https://wiki.lyrasis.org/display/DSPACE/Code+Style+Guide).
|
||||
- [ ] My PR **includes Javadoc** for _all new (or modified) public methods and classes_. It also includes Javadoc for large or complex private methods.
|
||||
- [ ] My PR **passes all tests and includes new/updated Unit or Integration Tests** based on the [Code Testing Guide](https://wiki.lyrasis.org/display/DSPACE/Code+Testing+Guide).
|
||||
- [ ] My PR **includes details on how to test it**. I've provided clear instructions to reviewers on how to successfully test this fix or feature.
|
||||
- [ ] If my PR includes new libraries/dependencies (in any `pom.xml`), I've made sure their licenses align with the [DSpace BSD License](https://github.com/DSpace/DSpace/blob/main/LICENSE) based on the [Licensing of Contributions](https://wiki.lyrasis.org/display/DSPACE/Code+Contribution+Guidelines#CodeContributionGuidelines-LicensingofContributions) documentation.
|
||||
- [ ] If my PR modifies REST API endpoints, I've opened a separate [REST Contract](https://github.com/DSpace/RestContract/blob/main/README.md) PR related to this change.
|
||||
- [ ] If my PR includes new configurations, I've provided basic technical documentation in the PR itself.
|
||||
|
8
.github/workflows/build.yml
vendored
8
.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"
|
||||
java: 17
|
||||
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
|
||||
@@ -34,7 +34,7 @@ jobs:
|
||||
# - xml.skip => Skip all XML/XSLT validation by xml-maven-plugin
|
||||
# - failsafe.rerunFailingTestsCount => try again for flakey tests, and keep track of/report on number of retries
|
||||
- type: "Integration Tests"
|
||||
java: 11
|
||||
java: 17
|
||||
mvnflags: "-DskipIntegrationTests=false -Denforcer.skip=true -Dcheckstyle.skip=true -Dlicense.skip=true -Dxml.skip=true -Dfailsafe.rerunFailingTestsCount=2"
|
||||
resultsdir: "**/target/failsafe-reports/**"
|
||||
# Do NOT exit immediately if one matrix job fails
|
||||
|
2
.github/workflows/codescan.yml
vendored
2
.github/workflows/codescan.yml
vendored
@@ -41,7 +41,7 @@ jobs:
|
||||
- name: Install JDK
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
java-version: 11
|
||||
java-version: 17
|
||||
distribution: 'temurin'
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
|
8
.github/workflows/reusable-docker-build.yml
vendored
8
.github/workflows/reusable-docker-build.yml
vendored
@@ -68,9 +68,9 @@ env:
|
||||
# See "Redeploy" steps below for more details.
|
||||
REDEPLOY_SANDBOX_URL: ${{ secrets.REDEPLOY_SANDBOX_URL }}
|
||||
REDEPLOY_DEMO_URL: ${{ secrets.REDEPLOY_DEMO_URL }}
|
||||
# Current DSpace maintenance branch (and architecture) which is deployed to demo.dspace.org / sandbox.dspace.org
|
||||
# (NOTE: No deployment branch specified for sandbox.dspace.org as it uses the default_branch)
|
||||
DEPLOY_DEMO_BRANCH: 'dspace-7_x'
|
||||
# Current DSpace branches (and architecture) which are deployed to demo.dspace.org & sandbox.dspace.org respectively
|
||||
DEPLOY_DEMO_BRANCH: 'dspace-8_x'
|
||||
DEPLOY_SANDBOX_BRANCH: 'main'
|
||||
DEPLOY_ARCH: 'linux/amd64'
|
||||
|
||||
jobs:
|
||||
@@ -174,7 +174,7 @@ jobs:
|
||||
!matrix.isPR &&
|
||||
env.REDEPLOY_SANDBOX_URL != '' &&
|
||||
matrix.arch == env.DEPLOY_ARCH &&
|
||||
github.ref_name == github.event.repository.default_branch
|
||||
github.ref_name == env.DEPLOY_SANDBOX_BRANCH
|
||||
run: |
|
||||
curl -X POST $REDEPLOY_SANDBOX_URL
|
||||
|
||||
|
1
.gitignore
vendored
1
.gitignore
vendored
@@ -10,6 +10,7 @@ tags
|
||||
.project
|
||||
.classpath
|
||||
.checkstyle
|
||||
.factorypath
|
||||
|
||||
## Ignore project files created by IntelliJ IDEA
|
||||
*.iml
|
||||
|
@@ -1,9 +0,0 @@
|
||||
# LGTM Settings (https://lgtm.com/)
|
||||
# For reference, see https://lgtm.com/help/lgtm/lgtm.yml-configuration-file
|
||||
# or template at https://lgtm.com/static/downloads/lgtm.template.yml
|
||||
|
||||
extraction:
|
||||
java:
|
||||
index:
|
||||
# Specify the Java version required to build the project
|
||||
java_version: 11
|
@@ -10,13 +10,14 @@ DSpace is a community built and supported project. We do not have a centralized
|
||||
## Contribute new code via a Pull Request
|
||||
|
||||
We accept [GitHub Pull Requests (PRs)](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/creating-a-pull-request-from-a-fork) at any time from anyone.
|
||||
Contributors to each release are recognized in our [Release Notes](https://wiki.lyrasis.org/display/DSDOC7x/Release+Notes).
|
||||
Contributors to each release are recognized in our [Release Notes](https://wiki.lyrasis.org/display/DSDOC8x/Release+Notes).
|
||||
|
||||
Code Contribution Checklist
|
||||
- [ ] PRs _should_ be smaller in size (ideally less than 1,000 lines of code, not including comments & tests)
|
||||
- [ ] PRs **must** pass Checkstyle validation based on our [Code Style Guide](https://wiki.lyrasis.org/display/DSPACE/Code+Style+Guide).
|
||||
- [ ] PRs **must** include Javadoc for _all new/modified public methods and classes_. Larger private methods should also have Javadoc
|
||||
- [ ] PRs **must** pass all automated tests and include new/updated Unit or Integration tests based on our [Code Testing Guide](https://wiki.lyrasis.org/display/DSPACE/Code+Testing+Guide).
|
||||
- [ ] Details on how to test the PR **must** be provided. Reviewers must be aware of any steps they need to take to successfully test your fix or feature.
|
||||
- [ ] If a PR includes new libraries/dependencies (in any `pom.xml`), then their software licenses **must** align with the [DSpace BSD License](https://github.com/DSpace/DSpace/blob/main/LICENSE) based on the [Licensing of Contributions](https://wiki.lyrasis.org/display/DSPACE/Code+Contribution+Guidelines#CodeContributionGuidelines-LicensingofContributions) documentation.
|
||||
- [ ] Basic technical documentation _should_ be provided for any new features or changes to the REST API. REST API changes should be documented in our [Rest Contract](https://github.com/DSpace/RestContract).
|
||||
- [ ] If a PR fixes an issue ticket, please [link them together](https://docs.github.com/en/issues/tracking-your-work-with-issues/linking-a-pull-request-to-an-issue).
|
||||
@@ -25,7 +26,7 @@ Additional details on the code contribution process can be found in our [Code Co
|
||||
|
||||
## Contribute documentation
|
||||
|
||||
DSpace Documentation is a collaborative effort in a shared Wiki. The latest documentation is at https://wiki.lyrasis.org/display/DSDOC7x
|
||||
DSpace Documentation is a collaborative effort in a shared Wiki. The latest documentation is at https://wiki.lyrasis.org/display/DSDOC
|
||||
|
||||
If you find areas of the DSpace Documentation which you wish to improve, please request a Wiki account by emailing wikihelp@lyrasis.org.
|
||||
Once you have an account setup, contact @tdonohue (via [Slack](https://wiki.lyrasis.org/display/DSPACE/Slack) or email) for access to edit our Documentation.
|
||||
@@ -33,7 +34,7 @@ Once you have an account setup, contact @tdonohue (via [Slack](https://wiki.lyra
|
||||
## Help others on mailing lists or Slack
|
||||
|
||||
DSpace has our own [Slack](https://wiki.lyrasis.org/display/DSPACE/Slack) community and [Mailing Lists](https://wiki.lyrasis.org/display/DSPACE/Mailing+Lists) where discussions take place and questions are answered.
|
||||
Anyone is welcome to join and help others. We just ask you to follow our [Code of Conduct](https://www.lyrasis.org/about/Pages/Code-of-Conduct.aspx) (adopted via LYRASIS).
|
||||
Anyone is welcome to join and help others. We just ask you to follow our [Code of Conduct](https://www.lyrasis.org/about/Pages/Code-of-Conduct.aspx) (adopted via Lyrasis).
|
||||
|
||||
## Join a working or interest group
|
||||
|
||||
@@ -41,5 +42,5 @@ Most of the work in building/improving DSpace comes via [Working Groups](https:/
|
||||
|
||||
All working/interest groups are open to anyone to join and participate. A few key groups to be aware of include:
|
||||
|
||||
* [DSpace 7 Working Group](https://wiki.lyrasis.org/display/DSPACE/DSpace+7+Working+Group) - This is the main (mostly volunteer) development team. We meet weekly to review our current development [project board](https://github.com/orgs/DSpace/projects), assigning tickets and/or PRs.
|
||||
* [DSpace Community Advisory Team (DCAT)](https://wiki.lyrasis.org/display/cmtygp/DSpace+Community+Advisory+Team) - This is an interest group for repository managers/administrators. We meet monthly to discuss DSpace, share tips & provide feedback back to developers.
|
||||
* [DSpace Developer Team](https://wiki.lyrasis.org/display/DSPACE/Developer+Meetings) - This is the primary, volunteer development team. We meet weekly to review our current development [project board](https://github.com/orgs/DSpace/projects), assigning tickets and/or PRs. This is also were discussions of the next release or major issues occur. Anyone is welcome to attend.
|
||||
* [DSpace Community Advisory Team (DCAT)](https://wiki.lyrasis.org/display/cmtygp/DSpace+Community+Advisory+Team) - This is an interest group for repository managers/administrators. We meet monthly to discuss DSpace, share tips & provide feedback back to developers. Anyone is welcome to attend.
|
||||
|
38
Dockerfile
38
Dockerfile
@@ -1,14 +1,15 @@
|
||||
# This image will be published as dspace/dspace
|
||||
# See https://github.com/DSpace/DSpace/tree/main/dspace/src/main/docker for usage details
|
||||
#
|
||||
# - note: default tag for branch: dspace/dspace: dspace/dspace:dspace-7_x
|
||||
# - note: default tag for branch: dspace/dspace: dspace/dspace:latest
|
||||
|
||||
# This Dockerfile uses JDK11 by default, but has also been tested with JDK17.
|
||||
# To build with JDK17, use "--build-arg JDK_VERSION=17"
|
||||
ARG JDK_VERSION=11
|
||||
# This Dockerfile uses JDK17 by default.
|
||||
# To build with other versions, use "--build-arg JDK_VERSION=[value]"
|
||||
ARG JDK_VERSION=17
|
||||
ARG DSPACE_VERSION=latest
|
||||
|
||||
# Step 1 - Run Maven Build
|
||||
FROM dspace/dspace-dependencies:dspace-7_x as build
|
||||
FROM dspace/dspace-dependencies:${DSPACE_VERSION} AS build
|
||||
ARG TARGET_DIR=dspace-installer
|
||||
WORKDIR /app
|
||||
# The dspace-installer directory will be written to /install
|
||||
@@ -18,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.
|
||||
@@ -26,9 +27,11 @@ ENV MAVEN_FLAGS="-P-test-environment -Denforcer.skip=true -Dcheckstyle.skip=true
|
||||
RUN mvn --no-transfer-progress package ${MAVEN_FLAGS} && \
|
||||
mv /app/dspace/target/${TARGET_DIR}/* /install && \
|
||||
mvn clean
|
||||
# Remove the server webapp to keep image small.
|
||||
RUN rm -rf /install/webapps/server/
|
||||
|
||||
# Step 2 - Run Ant Deploy
|
||||
FROM eclipse-temurin:${JDK_VERSION} as ant_build
|
||||
FROM eclipse-temurin:${JDK_VERSION} AS ant_build
|
||||
ARG TARGET_DIR=dspace-installer
|
||||
# COPY the /install directory from 'build' container to /dspace-src in this container
|
||||
COPY --from=build /install /dspace-src
|
||||
@@ -48,23 +51,16 @@ RUN mkdir $ANT_HOME && \
|
||||
# Run necessary 'ant' deploy scripts
|
||||
RUN ant init_installation update_configs update_code update_webapps
|
||||
|
||||
# Step 3 - Run tomcat
|
||||
# Create a new tomcat image that does not retain the the build directory contents
|
||||
FROM tomcat:9-jdk${JDK_VERSION}
|
||||
# Step 3 - Start up DSpace via Runnable JAR
|
||||
FROM eclipse-temurin:${JDK_VERSION}
|
||||
# NOTE: DSPACE_INSTALL must align with the "dspace.dir" default configuration.
|
||||
ENV DSPACE_INSTALL=/dspace
|
||||
# Copy the /dspace directory from 'ant_build' container to /dspace in this container
|
||||
COPY --from=ant_build /dspace $DSPACE_INSTALL
|
||||
# Expose Tomcat port and AJP port
|
||||
EXPOSE 8080 8009
|
||||
WORKDIR $DSPACE_INSTALL
|
||||
# Expose Tomcat port
|
||||
EXPOSE 8080
|
||||
# Give java extra memory (2GB)
|
||||
ENV JAVA_OPTS=-Xmx2000m
|
||||
|
||||
# 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/)
|
||||
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
|
||||
# On startup, run DSpace Runnable JAR
|
||||
ENTRYPOINT ["java", "-jar", "webapps/server-boot.jar", "--dspace.dir=$DSPACE_INSTALL"]
|
||||
|
@@ -1,14 +1,15 @@
|
||||
# This image will be published as dspace/dspace-cli
|
||||
# See https://github.com/DSpace/DSpace/tree/main/dspace/src/main/docker for usage details
|
||||
#
|
||||
# - note: default tag for branch: dspace/dspace-cli: dspace/dspace-cli:dspace-7_x
|
||||
# - note: default tag for branch: dspace/dspace-cli: dspace/dspace-cli:latest
|
||||
|
||||
# This Dockerfile uses JDK11 by default, but has also been tested with JDK17.
|
||||
# To build with JDK17, use "--build-arg JDK_VERSION=17"
|
||||
ARG JDK_VERSION=11
|
||||
# This Dockerfile uses JDK17 by default.
|
||||
# To build with other versions, use "--build-arg JDK_VERSION=[value]"
|
||||
ARG JDK_VERSION=17
|
||||
ARG DSPACE_VERSION=latest
|
||||
|
||||
# Step 1 - Run Maven Build
|
||||
FROM dspace/dspace-dependencies:dspace-7_x as build
|
||||
FROM dspace/dspace-dependencies:${DSPACE_VERSION} AS build
|
||||
ARG TARGET_DIR=dspace-installer
|
||||
WORKDIR /app
|
||||
# The dspace-installer directory will be written to /install
|
||||
@@ -24,7 +25,7 @@ RUN mvn --no-transfer-progress package && \
|
||||
mvn clean
|
||||
|
||||
# Step 2 - Run Ant Deploy
|
||||
FROM eclipse-temurin:${JDK_VERSION} as ant_build
|
||||
FROM eclipse-temurin:${JDK_VERSION} AS ant_build
|
||||
ARG TARGET_DIR=dspace-installer
|
||||
# COPY the /install directory from 'build' container to /dspace-src in this container
|
||||
COPY --from=build /install /dspace-src
|
||||
|
@@ -2,12 +2,12 @@
|
||||
# The purpose of this image is to make the build for dspace/dspace run faster
|
||||
#
|
||||
|
||||
# This Dockerfile uses JDK11 by default, but has also been tested with JDK17.
|
||||
# To build with JDK17, use "--build-arg JDK_VERSION=17"
|
||||
ARG JDK_VERSION=11
|
||||
# This Dockerfile uses JDK17 by default.
|
||||
# To build with other versions, use "--build-arg JDK_VERSION=[value]"
|
||||
ARG JDK_VERSION=17
|
||||
|
||||
# Step 1 - Run Maven Build
|
||||
FROM maven:3-eclipse-temurin-${JDK_VERSION} as build
|
||||
FROM maven:3-eclipse-temurin-${JDK_VERSION} AS build
|
||||
ARG TARGET_DIR=dspace-installer
|
||||
WORKDIR /app
|
||||
# Create the 'dspace' user account & home directory
|
||||
|
@@ -1,16 +1,17 @@
|
||||
# This image will be published as dspace/dspace
|
||||
# See https://github.com/DSpace/DSpace/tree/main/dspace/src/main/docker for usage details
|
||||
#
|
||||
# - note: default tag for branch: dspace/dspace: dspace/dspace:dspace-7_x-test
|
||||
# - note: default tag for branch: dspace/dspace: dspace/dspace:latest-test
|
||||
#
|
||||
# This image is meant for TESTING/DEVELOPMENT ONLY as it deploys the old v6 REST API under HTTP (not HTTPS)
|
||||
|
||||
# This Dockerfile uses JDK11 by default, but has also been tested with JDK17.
|
||||
# To build with JDK17, use "--build-arg JDK_VERSION=17"
|
||||
ARG JDK_VERSION=11
|
||||
# This Dockerfile uses JDK17 by default.
|
||||
# To build with other versions, use "--build-arg JDK_VERSION=[value]"
|
||||
ARG JDK_VERSION=17
|
||||
ARG DSPACE_VERSION=latest
|
||||
|
||||
# Step 1 - Run Maven Build
|
||||
FROM dspace/dspace-dependencies:dspace-7_x as build
|
||||
FROM dspace/dspace-dependencies:${DSPACE_VERSION} AS build
|
||||
ARG TARGET_DIR=dspace-installer
|
||||
WORKDIR /app
|
||||
# The dspace-installer directory will be written to /install
|
||||
@@ -20,14 +21,16 @@ 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
|
||||
# Remove the server webapp to keep image small. Rename runnable JAR to server-boot.jar.
|
||||
RUN rm -rf /install/webapps/server/
|
||||
|
||||
# Step 2 - Run Ant Deploy
|
||||
FROM eclipse-temurin:${JDK_VERSION} as ant_build
|
||||
FROM eclipse-temurin:${JDK_VERSION} AS ant_build
|
||||
ARG TARGET_DIR=dspace-installer
|
||||
# COPY the /install directory from 'build' container to /dspace-src in this container
|
||||
COPY --from=build /install /dspace-src
|
||||
@@ -47,36 +50,18 @@ RUN mkdir $ANT_HOME && \
|
||||
# Run necessary 'ant' deploy scripts
|
||||
RUN ant init_installation update_configs update_code update_webapps
|
||||
|
||||
# Step 3 - Run tomcat
|
||||
# Create a new tomcat image that does not retain the the build directory contents
|
||||
FROM tomcat:9-jdk${JDK_VERSION}
|
||||
# Step 3 - Start up DSpace via Runnable JAR
|
||||
FROM eclipse-temurin:${JDK_VERSION}
|
||||
# NOTE: DSPACE_INSTALL must align with the "dspace.dir" default configuration.
|
||||
ENV DSPACE_INSTALL=/dspace
|
||||
ENV TOMCAT_INSTALL=/usr/local/tomcat
|
||||
# Copy the /dspace directory from 'ant_build' containger to /dspace in this container
|
||||
# Copy the /dspace directory from 'ant_build' container to /dspace in this container
|
||||
COPY --from=ant_build /dspace $DSPACE_INSTALL
|
||||
# Enable the AJP connector in Tomcat's server.xml
|
||||
# NOTE: secretRequired="false" should only be used when AJP is NOT accessible from an external network. But, secretRequired="true" isn't supported by mod_proxy_ajp until Apache 2.5
|
||||
RUN sed -i '/Service name="Catalina".*/a \\n <Connector protocol="AJP/1.3" port="8009" address="0.0.0.0" redirectPort="8443" URIEncoding="UTF-8" secretRequired="false" />' $TOMCAT_INSTALL/conf/server.xml
|
||||
# Expose Tomcat port and AJP port
|
||||
EXPOSE 8080 8009 8000
|
||||
WORKDIR $DSPACE_INSTALL
|
||||
# Expose Tomcat port and debugging port
|
||||
EXPOSE 8080 8000
|
||||
# Give java extra memory (2GB)
|
||||
ENV JAVA_OPTS=-Xmx2000m
|
||||
# Set up debugging
|
||||
ENV CATALINA_OPTS=-Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=*:8000
|
||||
|
||||
# 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
|
||||
# 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
|
||||
|
||||
# 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
|
||||
# On startup, run DSpace Runnable JAR
|
||||
ENTRYPOINT ["java", "-jar", "webapps/server-boot.jar", "--dspace.dir=$DSPACE_INSTALL"]
|
||||
|
File diff suppressed because it is too large
Load Diff
12
README.md
12
README.md
@@ -20,7 +20,7 @@ DSpace consists of both a Java-based backend and an Angular-based frontend.
|
||||
* The REST Contract is at https://github.com/DSpace/RestContract
|
||||
* Frontend (https://github.com/DSpace/dspace-angular/) is the User Interface built on the REST API
|
||||
|
||||
Prior versions of DSpace (v6.x and below) used two different UIs (XMLUI and JSPUI). Those UIs are no longer supported in v7 (and above).
|
||||
Prior versions of DSpace (v6.x and below) used two different UIs (XMLUI and JSPUI). Those UIs are no longer supported in v7 and above.
|
||||
* A maintenance branch for older versions is still available, see `dspace-6_x` for 6.x maintenance.
|
||||
|
||||
## Downloads
|
||||
@@ -33,18 +33,18 @@ Prior versions of DSpace (v6.x and below) used two different UIs (XMLUI and JSPU
|
||||
Documentation for each release may be viewed online or downloaded via our [Documentation Wiki](https://wiki.lyrasis.org/display/DSDOC/).
|
||||
|
||||
The latest DSpace Installation instructions are available at:
|
||||
https://wiki.lyrasis.org/display/DSDOC7x/Installing+DSpace
|
||||
https://wiki.lyrasis.org/display/DSDOC8x/Installing+DSpace
|
||||
|
||||
Please be aware that, as a Java web application, DSpace requires a database (PostgreSQL)
|
||||
and a servlet container (usually Tomcat) in order to function.
|
||||
More information about these and all other prerequisites can be found in the Installation instructions above.
|
||||
|
||||
## Running DSpace 7 in Docker
|
||||
## Running DSpace 8 in Docker
|
||||
|
||||
NOTE: At this time, we do not have production-ready Docker images for DSpace.
|
||||
That said, we do have quick-start Docker Compose scripts for development or testing purposes.
|
||||
|
||||
See [Running DSpace 7 with Docker Compose](dspace/src/main/docker-compose/README.md)
|
||||
See [Running DSpace 8 with Docker Compose](dspace/src/main/docker-compose/README.md)
|
||||
|
||||
## Contributing
|
||||
|
||||
@@ -64,7 +64,7 @@ Great Q&A is also available under the [DSpace tag on Stackoverflow](http://stack
|
||||
Additional support options are at https://wiki.lyrasis.org/display/DSPACE/Support
|
||||
|
||||
DSpace also has an active service provider network. If you'd rather hire a service provider to
|
||||
install, upgrade, customize or host DSpace, then we recommend getting in touch with one of our
|
||||
install, upgrade, customize, or host DSpace, then we recommend getting in touch with one of our
|
||||
[Registered Service Providers](http://www.dspace.org/service-providers).
|
||||
|
||||
## Issue Tracker
|
||||
@@ -112,7 +112,7 @@ run automatically by [GitHub Actions](https://github.com/DSpace/DSpace/actions?q
|
||||
```
|
||||
* How to run only tests of a specific DSpace module
|
||||
```
|
||||
# Before you can run only one module's tests, other modules may need installing into your ~/.m2
|
||||
# Before you can run only one module's tests, other modules may need to be installed into your ~/.m2
|
||||
cd [dspace-src]
|
||||
mvn clean install
|
||||
|
||||
|
@@ -92,7 +92,7 @@ For more information on CheckStyle configurations below, see: http://checkstyle.
|
||||
<!-- Requirements for Javadocs for methods -->
|
||||
<module name="JavadocMethod">
|
||||
<!-- All public methods MUST HAVE Javadocs -->
|
||||
<property name="scope" value="public"/>
|
||||
<property name="accessModifiers" value="public"/>
|
||||
<!-- Allow params, throws and return tags to be optional -->
|
||||
<property name="allowMissingParamTags" value="true"/>
|
||||
<property name="allowMissingReturnTag" value="true"/>
|
||||
|
@@ -6,7 +6,7 @@ networks:
|
||||
external: true
|
||||
services:
|
||||
dspace-cli:
|
||||
image: "${DOCKER_OWNER:-dspace}/dspace-cli:${DSPACE_VER:-dspace-7_x}"
|
||||
image: "${DOCKER_OWNER:-dspace}/dspace-cli:${DSPACE_VER:-latest}"
|
||||
container_name: dspace-cli
|
||||
build:
|
||||
context: .
|
||||
|
@@ -28,7 +28,7 @@ services:
|
||||
# from the host machine. This IP range MUST correspond to the 'dspacenet' subnet defined above.
|
||||
proxies__P__trusted__P__ipranges: '172.23.0'
|
||||
LOGGING_CONFIG: /dspace/config/log4j2-container.xml
|
||||
image: "${DOCKER_OWNER:-dspace}/dspace:${DSPACE_VER:-dspace-7_x-test}"
|
||||
image: "${DOCKER_OWNER:-dspace}/dspace:${DSPACE_VER:-latest-test}"
|
||||
build:
|
||||
context: .
|
||||
dockerfile: Dockerfile.test
|
||||
@@ -39,8 +39,6 @@ services:
|
||||
ports:
|
||||
- published: 8080
|
||||
target: 8080
|
||||
- published: 8009
|
||||
target: 8009
|
||||
- published: 8000
|
||||
target: 8000
|
||||
stdin_open: true
|
||||
@@ -54,19 +52,19 @@ services:
|
||||
# Ensure that the database is ready BEFORE starting tomcat
|
||||
# 1. While a TCP connection to dspacedb port 5432 is not available, continue to sleep
|
||||
# 2. Then, run database migration to init database tables
|
||||
# 3. Finally, start Tomcat
|
||||
# 3. Finally, start DSpace
|
||||
entrypoint:
|
||||
- /bin/bash
|
||||
- '-c'
|
||||
- |
|
||||
while (!</dev/tcp/dspacedb/5432) > /dev/null 2>&1; do sleep 1; done;
|
||||
/dspace/bin/dspace database migrate
|
||||
catalina.sh run
|
||||
java -jar /dspace/webapps/server-boot.jar --dspace.dir=/dspace
|
||||
# DSpace PostgreSQL database container
|
||||
dspacedb:
|
||||
container_name: dspacedb
|
||||
# Uses a custom Postgres image with pgcrypto installed
|
||||
image: "${DOCKER_OWNER:-dspace}/dspace-postgres-pgcrypto:${DSPACE_VER:-dspace-7_x}"
|
||||
image: "${DOCKER_OWNER:-dspace}/dspace-postgres-pgcrypto:${DSPACE_VER:-latest}"
|
||||
build:
|
||||
# Must build out of subdirectory to have access to install script for pgcrypto
|
||||
context: ./dspace/src/main/docker/dspace-postgres-pgcrypto/
|
||||
@@ -86,7 +84,7 @@ services:
|
||||
# DSpace Solr container
|
||||
dspacesolr:
|
||||
container_name: dspacesolr
|
||||
image: "${DOCKER_OWNER:-dspace}/dspace-solr:${DSPACE_VER:-dspace-7_x}"
|
||||
image: "${DOCKER_OWNER:-dspace}/dspace-solr:${DSPACE_VER:-latest}"
|
||||
build:
|
||||
context: ./dspace/src/main/docker/dspace-solr/
|
||||
# Provide path to Solr configs necessary to build Docker image
|
||||
@@ -122,6 +120,10 @@ services:
|
||||
cp -r /opt/solr/server/solr/configsets/search/* search
|
||||
precreate-core statistics /opt/solr/server/solr/configsets/statistics
|
||||
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:
|
||||
|
@@ -12,7 +12,7 @@
|
||||
<parent>
|
||||
<groupId>org.dspace</groupId>
|
||||
<artifactId>dspace-parent</artifactId>
|
||||
<version>7.6.2-SNAPSHOT</version>
|
||||
<version>9.0-SNAPSHOT</version>
|
||||
<relativePath>..</relativePath>
|
||||
</parent>
|
||||
|
||||
@@ -54,21 +54,21 @@
|
||||
<!-- Enable Hibernate's Metamodel Generator to generate metadata model classes
|
||||
(ending in _ suffix) for more type-safe Criteria queries -->
|
||||
<path>
|
||||
<groupId>org.hibernate</groupId>
|
||||
<groupId>org.hibernate.orm</groupId>
|
||||
<artifactId>hibernate-jpamodelgen</artifactId>
|
||||
<version>${hibernate.version}</version>
|
||||
</path>
|
||||
<!-- Enable JAXB -->
|
||||
<path>
|
||||
<groupId>javax.xml.bind</groupId>
|
||||
<artifactId>jaxb-api</artifactId>
|
||||
<groupId>jakarta.xml.bind</groupId>
|
||||
<artifactId>jakarta.xml.bind-api</artifactId>
|
||||
<version>${jaxb-api.version}</version>
|
||||
</path>
|
||||
<!-- Enable Commons Annotations -->
|
||||
<path>
|
||||
<groupId>javax.annotation</groupId>
|
||||
<artifactId>javax.annotation-api</artifactId>
|
||||
<version>${javax-annotation.version}</version>
|
||||
<groupId>jakarta.annotation</groupId>
|
||||
<artifactId>jakarta.annotation-api</artifactId>
|
||||
<version>${jakarta-annotation.version}</version>
|
||||
</path>
|
||||
<!-- Enable http://errorprone.info -->
|
||||
<path>
|
||||
@@ -102,7 +102,7 @@
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>build-helper-maven-plugin</artifactId>
|
||||
<version>3.4.0</version>
|
||||
<version>3.6.0</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>validate</phase>
|
||||
@@ -116,7 +116,7 @@
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>buildnumber-maven-plugin</artifactId>
|
||||
<version>3.2.0</version>
|
||||
<version>3.2.1</version>
|
||||
<configuration>
|
||||
<revisionOnScmFailure>UNKNOWN_REVISION</revisionOnScmFailure>
|
||||
</configuration>
|
||||
@@ -177,7 +177,7 @@
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>jaxb2-maven-plugin</artifactId>
|
||||
<version>2.5.0</version>
|
||||
<version>3.2.0</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>workflow-curation</id>
|
||||
@@ -265,7 +265,7 @@
|
||||
<!-- Specify the dspace.dir to use for test environment -->
|
||||
<!-- ${agnostic.build.dir} is set dynamically by groovy-maven-plugin above -->
|
||||
<!-- This system property is loaded by AbstractDSpaceTest to initialize the test environment -->
|
||||
<dspace.dir>${agnostic.build.dir}/testing/dspace/</dspace.dir>
|
||||
<dspace.dir>${agnostic.build.dir}/testing/dspace</dspace.dir>
|
||||
<!-- Turn off any DSpace logging -->
|
||||
<dspace.log.init.disable>true</dspace.log.init.disable>
|
||||
<solr.install.dir>${agnostic.build.dir}/testing/dspace/solr/</solr.install.dir>
|
||||
@@ -324,7 +324,7 @@
|
||||
<systemPropertyVariables>
|
||||
<!-- Specify the dspace.dir to use for test environment -->
|
||||
<!-- ${agnostic.build.dir} is set dynamically by groovy-maven-plugin above -->
|
||||
<dspace.dir>${agnostic.build.dir}/testing/dspace/</dspace.dir>
|
||||
<dspace.dir>${agnostic.build.dir}/testing/dspace</dspace.dir>
|
||||
<!-- Turn off any DSpace logging -->
|
||||
<dspace.log.init.disable>true</dspace.log.init.disable>
|
||||
<solr.install.dir>${agnostic.build.dir}/testing/dspace/solr/</solr.install.dir>
|
||||
@@ -342,18 +342,15 @@
|
||||
<artifactId>log4j-api</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.hibernate</groupId>
|
||||
<groupId>org.hibernate.orm</groupId>
|
||||
<artifactId>hibernate-core</artifactId>
|
||||
<exclusions>
|
||||
<!-- Newer version pulled in via Jersey below -->
|
||||
<exclusion>
|
||||
<groupId>org.javassist</groupId>
|
||||
<artifactId>javassist</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.hibernate</groupId>
|
||||
<groupId>org.hibernate.orm</groupId>
|
||||
<artifactId>hibernate-jpamodelgen</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.hibernate.orm</groupId>
|
||||
<artifactId>hibernate-jcache</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
@@ -375,23 +372,18 @@
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>javax.cache</groupId>
|
||||
<artifactId>cache-api</artifactId>
|
||||
<groupId>com.fasterxml.jackson.datatype</groupId>
|
||||
<artifactId>jackson-datatype-jsr310</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.hibernate</groupId>
|
||||
<artifactId>hibernate-jpamodelgen</artifactId>
|
||||
<groupId>javax.cache</groupId>
|
||||
<artifactId>cache-api</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.hibernate.validator</groupId>
|
||||
<artifactId>hibernate-validator-cdi</artifactId>
|
||||
<version>${hibernate-validator.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.hibernate.javax.persistence</groupId>
|
||||
<artifactId>hibernate-jpa-2.1-api</artifactId>
|
||||
<version>1.0.2.Final</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
@@ -402,30 +394,26 @@
|
||||
<groupId>net.handle</groupId>
|
||||
<artifactId>handle</artifactId>
|
||||
</dependency>
|
||||
<!-- Only necessary to run Handle Server from commandline. This is why it is a runtime dependency. -->
|
||||
<dependency>
|
||||
<groupId>net.cnri</groupId>
|
||||
<artifactId>cnri-servlet-container</artifactId>
|
||||
<scope>runtime</scope>
|
||||
<exclusions>
|
||||
<!-- Newer versions provided in our parent POM -->
|
||||
<!-- Excluded because this library is incompatible with Jakarta EE. It causes errors in DSpace during
|
||||
startup & integration testing. Excluding doesn't seem to effect Handle Server startup. -->
|
||||
<exclusion>
|
||||
<groupId>org.ow2.asm</groupId>
|
||||
<artifactId>asm-commons</artifactId>
|
||||
</exclusion>
|
||||
<!-- Newer version of Bouncycastle brought in via Tika -->
|
||||
<exclusion>
|
||||
<groupId>org.bouncycastle</groupId>
|
||||
<artifactId>bcpkix-jdk15on</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>org.bouncycastle</groupId>
|
||||
<artifactId>bcprov-jdk15on</artifactId>
|
||||
<groupId>org.mortbay.jasper</groupId>
|
||||
<artifactId>apache-jsp</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<!-- Jetty is needed to run Handle Server -->
|
||||
|
||||
<!-- Jetty is needed to run Handle Server ONLY. This is why it is a runtime dependency. -->
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>jetty-server</artifactId>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.dspace</groupId>
|
||||
@@ -435,12 +423,6 @@
|
||||
<groupId>org.apache.jena</groupId>
|
||||
<artifactId>apache-jena-libs</artifactId>
|
||||
<type>pom</type>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>log4j</groupId>
|
||||
<artifactId>log4j</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>commons-cli</groupId>
|
||||
@@ -458,10 +440,6 @@
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-dbcp2</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>commons-fileupload</groupId>
|
||||
<artifactId>commons-fileupload</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>commons-io</groupId>
|
||||
@@ -480,17 +458,26 @@
|
||||
<artifactId>commons-validator</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.sun.mail</groupId>
|
||||
<artifactId>javax.mail</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>javax.servlet</groupId>
|
||||
<artifactId>javax.servlet-api</artifactId>
|
||||
<groupId>jakarta.mail</groupId>
|
||||
<artifactId>jakarta.mail-api</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>javax.annotation</groupId>
|
||||
<artifactId>javax.annotation-api</artifactId>
|
||||
<groupId>org.eclipse.angus</groupId>
|
||||
<artifactId>jakarta.mail</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>jakarta.servlet</groupId>
|
||||
<artifactId>jakarta.servlet-api</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>jakarta.annotation</groupId>
|
||||
<artifactId>jakarta.annotation-api</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>jakarta.el</groupId>
|
||||
<artifactId>jakarta.el-api</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>jaxen</groupId>
|
||||
@@ -586,6 +573,13 @@
|
||||
<artifactId>solr-core</artifactId>
|
||||
<scope>test</scope>
|
||||
<version>${solr.client.version}</version>
|
||||
<exclusions>
|
||||
<!-- Later version provided by Hibernate -->
|
||||
<exclusion>
|
||||
<groupId>org.antlr</groupId>
|
||||
<artifactId>antlr4-runtime</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.lucene</groupId>
|
||||
@@ -629,7 +623,7 @@
|
||||
<dependency>
|
||||
<groupId>dnsjava</groupId>
|
||||
<artifactId>dnsjava</artifactId>
|
||||
<version>2.1.9</version>
|
||||
<version>3.6.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
@@ -665,7 +659,12 @@
|
||||
<dependency>
|
||||
<groupId>org.flywaydb</groupId>
|
||||
<artifactId>flyway-core</artifactId>
|
||||
<version>8.5.13</version>
|
||||
<version>${flyway.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.flywaydb</groupId>
|
||||
<artifactId>flyway-database-postgresql</artifactId>
|
||||
<version>${flyway.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Google Analytics -->
|
||||
@@ -701,27 +700,22 @@
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>joda-time</groupId>
|
||||
<artifactId>joda-time</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>javax.inject</groupId>
|
||||
<artifactId>javax.inject</artifactId>
|
||||
<version>1</version>
|
||||
<type>jar</type>
|
||||
<groupId>jakarta.inject</groupId>
|
||||
<artifactId>jakarta.inject-api</artifactId>
|
||||
<version>2.0.1</version>
|
||||
</dependency>
|
||||
|
||||
<!-- JAXB API and implementation (no longer bundled as of Java 11) -->
|
||||
<dependency>
|
||||
<groupId>javax.xml.bind</groupId>
|
||||
<artifactId>jaxb-api</artifactId>
|
||||
<groupId>jakarta.xml.bind</groupId>
|
||||
<artifactId>jakarta.xml.bind-api</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.glassfish.jaxb</groupId>
|
||||
<artifactId>jaxb-runtime</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Jersey / JAX-RS client (javax.ws.rs.*) dependencies needed to integrate with external sources/services -->
|
||||
<!-- Jersey / JAX-RS client (jakarta.ws.rs.*) dependencies needed to integrate with external sources/services -->
|
||||
<dependency>
|
||||
<groupId>org.glassfish.jersey.core</groupId>
|
||||
<artifactId>jersey-client</artifactId>
|
||||
@@ -742,31 +736,18 @@
|
||||
<version>1.12.261</version>
|
||||
</dependency>
|
||||
|
||||
<!-- TODO: This may need to be replaced with the "orcid-model" artifact once this ticket is resolved:
|
||||
https://github.com/ORCID/orcid-model/issues/50 -->
|
||||
<!-- Maintained at https://github.com/DSpace/orcid-model -->
|
||||
<dependency>
|
||||
<groupId>org.orcid</groupId>
|
||||
<artifactId>orcid-model</artifactId>
|
||||
<version>3.0.2</version>
|
||||
<groupId>org.dspace</groupId>
|
||||
<artifactId>orcid-model-jakarta</artifactId>
|
||||
<version>3.3.0</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>javax.validation</groupId>
|
||||
<artifactId>validation-api</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>com.fasterxml.jackson.jaxrs</groupId>
|
||||
<artifactId>jackson-jaxrs-json-provider</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>org.yaml</groupId>
|
||||
<artifactId>snakeyaml</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>org.javassist</groupId>
|
||||
<artifactId>javassist</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>io.swagger</groupId>
|
||||
<artifactId>swagger-jersey-jaxrs</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
@@ -824,10 +805,16 @@
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>eu.openaire</groupId>
|
||||
<artifactId>broker-client</artifactId>
|
||||
<version>1.1.2</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.mock-server</groupId>
|
||||
<artifactId>mockserver-junit-rule</artifactId>
|
||||
<version>5.11.2</version>
|
||||
<version>5.15.0</version>
|
||||
<scope>test</scope>
|
||||
<exclusions>
|
||||
<!-- Exclude snakeyaml to avoid conflicts with: spring-boot-starter-cache -->
|
||||
@@ -835,6 +822,20 @@
|
||||
<groupId>org.yaml</groupId>
|
||||
<artifactId>snakeyaml</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>com.fasterxml.jackson.datatype</groupId>
|
||||
<artifactId>jackson-datatype-jsr310</artifactId>
|
||||
</exclusion>
|
||||
<!-- Resolve dependency conflicts with Hibernate -->
|
||||
<exclusion>
|
||||
<groupId>javax.xml.bind</groupId>
|
||||
<artifactId>jaxb-api</artifactId>
|
||||
</exclusion>
|
||||
<!-- Resolve dependency conflicts with Solr -->
|
||||
<exclusion>
|
||||
<groupId>javax.servlet</groupId>
|
||||
<artifactId>javax.servlet-api</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
@@ -854,7 +855,6 @@
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
<dependencyManagement>
|
||||
@@ -865,32 +865,32 @@
|
||||
<dependency>
|
||||
<groupId>io.netty</groupId>
|
||||
<artifactId>netty-buffer</artifactId>
|
||||
<version>4.1.106.Final</version>
|
||||
<version>4.1.114.Final</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.netty</groupId>
|
||||
<artifactId>netty-transport</artifactId>
|
||||
<version>4.1.106.Final</version>
|
||||
<version>4.1.114.Final</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.netty</groupId>
|
||||
<artifactId>netty-transport-native-unix-common</artifactId>
|
||||
<version>4.1.106.Final</version>
|
||||
<version>4.1.114.Final</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.netty</groupId>
|
||||
<artifactId>netty-common</artifactId>
|
||||
<version>4.1.106.Final</version>
|
||||
<version>4.1.114.Final</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.netty</groupId>
|
||||
<artifactId>netty-handler</artifactId>
|
||||
<version>4.1.106.Final</version>
|
||||
<version>4.1.114.Final</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.netty</groupId>
|
||||
<artifactId>netty-codec</artifactId>
|
||||
<version>4.1.106.Final</version>
|
||||
<version>4.1.114.Final</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.velocity</groupId>
|
||||
@@ -909,14 +909,9 @@
|
||||
<version>2.2.14</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>jakarta.xml.bind</groupId>
|
||||
<artifactId>jakarta.xml.bind-api</artifactId>
|
||||
<version>2.3.3</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>javax.validation</groupId>
|
||||
<artifactId>validation-api</artifactId>
|
||||
<version>2.0.1.Final</version>
|
||||
<groupId>jakarta.validation</groupId>
|
||||
<artifactId>jakarta.validation-api</artifactId>
|
||||
<version>3.1.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.swagger</groupId>
|
||||
|
@@ -8,6 +8,8 @@
|
||||
package org.dspace.access.status;
|
||||
|
||||
import java.sql.SQLException;
|
||||
import java.time.LocalDate;
|
||||
import java.time.ZoneId;
|
||||
import java.util.Date;
|
||||
|
||||
import org.dspace.access.status.service.AccessStatusService;
|
||||
@@ -15,7 +17,6 @@ import org.dspace.content.Item;
|
||||
import org.dspace.core.Context;
|
||||
import org.dspace.core.service.PluginService;
|
||||
import org.dspace.services.ConfigurationService;
|
||||
import org.joda.time.LocalDate;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
/**
|
||||
@@ -55,7 +56,10 @@ public class AccessStatusServiceImpl implements AccessStatusService {
|
||||
int month = configurationService.getIntProperty("access.status.embargo.forever.month");
|
||||
int day = configurationService.getIntProperty("access.status.embargo.forever.day");
|
||||
|
||||
forever_date = new LocalDate(year, month, day).toDate();
|
||||
forever_date = Date.from(LocalDate.of(year, month, day)
|
||||
.atStartOfDay()
|
||||
.atZone(ZoneId.systemDefault())
|
||||
.toInstant());
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -18,7 +18,7 @@ import org.dspace.core.Context;
|
||||
* Configuration properties: (with examples)
|
||||
* {@code
|
||||
* # values for the forever embargo date threshold
|
||||
* # This threshold date is used in the default access status helper to dermine if an item is
|
||||
* # This threshold date is used in the default access status helper to determine if an item is
|
||||
* # restricted or embargoed based on the start date of the primary (or first) file policies.
|
||||
* # In this case, if the policy start date is inferior to the threshold date, the status will
|
||||
* # be embargo, else it will be restricted.
|
||||
|
@@ -21,6 +21,8 @@ import org.apache.commons.cli.CommandLineParser;
|
||||
import org.apache.commons.cli.DefaultParser;
|
||||
import org.apache.commons.cli.Options;
|
||||
import org.apache.commons.cli.ParseException;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.dspace.authorize.AuthorizeException;
|
||||
import org.dspace.content.MetadataField;
|
||||
import org.dspace.content.MetadataSchema;
|
||||
@@ -30,8 +32,6 @@ import org.dspace.content.factory.ContentServiceFactory;
|
||||
import org.dspace.content.service.MetadataFieldService;
|
||||
import org.dspace.content.service.MetadataSchemaService;
|
||||
import org.dspace.core.Context;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.w3c.dom.Document;
|
||||
import org.w3c.dom.Node;
|
||||
import org.w3c.dom.NodeList;
|
||||
@@ -40,9 +40,9 @@ import org.xml.sax.SAXException;
|
||||
/**
|
||||
* @author Richard Jones
|
||||
*
|
||||
* This class takes an xml document as passed in the arguments and
|
||||
* This class takes an XML document as passed in the arguments and
|
||||
* uses it to create metadata elements in the Metadata Registry if
|
||||
* they do not already exist
|
||||
* they do not already exist.
|
||||
*
|
||||
* The format of the XML file is as follows:
|
||||
*
|
||||
@@ -69,7 +69,7 @@ public class MetadataImporter {
|
||||
/**
|
||||
* logging category
|
||||
*/
|
||||
private static final Logger log = LoggerFactory.getLogger(MetadataImporter.class);
|
||||
private static final Logger log = LogManager.getLogger();
|
||||
|
||||
/**
|
||||
* Default constructor
|
||||
@@ -89,6 +89,7 @@ public class MetadataImporter {
|
||||
* @throws SAXException if parser error
|
||||
* @throws NonUniqueMetadataException if duplicate metadata
|
||||
* @throws RegistryImportException if import fails
|
||||
* @throws XPathExpressionException passed through
|
||||
**/
|
||||
public static void main(String[] args)
|
||||
throws ParseException, SQLException, IOException, TransformerException,
|
||||
@@ -125,6 +126,7 @@ public class MetadataImporter {
|
||||
* @throws SAXException if parser error
|
||||
* @throws NonUniqueMetadataException if duplicate metadata
|
||||
* @throws RegistryImportException if import fails
|
||||
* @throws XPathExpressionException passed through
|
||||
*/
|
||||
public static void loadRegistry(String file, boolean forceUpdate)
|
||||
throws SQLException, IOException, TransformerException, ParserConfigurationException, AuthorizeException,
|
||||
@@ -203,7 +205,7 @@ public class MetadataImporter {
|
||||
|
||||
if (s == null) {
|
||||
// Schema does not exist - create
|
||||
log.info("Registering Schema " + name + " (" + namespace + ")");
|
||||
log.info("Registering Schema {}({})", name, namespace);
|
||||
metadataSchemaService.create(context, name, namespace);
|
||||
} else {
|
||||
// Schema exists - if it's the same namespace, allow the type imports to continue
|
||||
@@ -215,7 +217,7 @@ public class MetadataImporter {
|
||||
// It's a different namespace - have we been told to update?
|
||||
if (updateExisting) {
|
||||
// Update the existing schema namespace and continue to type import
|
||||
log.info("Updating Schema " + name + ": New namespace " + namespace);
|
||||
log.info("Updating Schema {}: New namespace {}", name, namespace);
|
||||
s.setNamespace(namespace);
|
||||
metadataSchemaService.update(context, s);
|
||||
} else {
|
||||
@@ -274,7 +276,7 @@ public class MetadataImporter {
|
||||
if (qualifier == null) {
|
||||
fieldName = schema + "." + element;
|
||||
}
|
||||
log.info("Registering metadata field " + fieldName);
|
||||
log.info("Registering metadata field {}", fieldName);
|
||||
MetadataField field = metadataFieldService.create(context, schemaObj, element, qualifier, scopeNote);
|
||||
metadataFieldService.update(context, field);
|
||||
}
|
||||
|
@@ -802,7 +802,7 @@ public class StructBuilder {
|
||||
|
||||
// default the short description to the empty string
|
||||
collectionService.setMetadataSingleValue(context, collection,
|
||||
MD_SHORT_DESCRIPTION, Item.ANY, " ");
|
||||
MD_SHORT_DESCRIPTION, null, " ");
|
||||
|
||||
// import the rest of the metadata
|
||||
for (Map.Entry<String, MetadataFieldName> entry : collectionMap.entrySet()) {
|
||||
|
@@ -8,17 +8,17 @@
|
||||
package org.dspace.alerts;
|
||||
|
||||
import java.util.Date;
|
||||
import javax.persistence.Cacheable;
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.GeneratedValue;
|
||||
import javax.persistence.GenerationType;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.SequenceGenerator;
|
||||
import javax.persistence.Table;
|
||||
import javax.persistence.Temporal;
|
||||
import javax.persistence.TemporalType;
|
||||
|
||||
import jakarta.persistence.Cacheable;
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.GenerationType;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.SequenceGenerator;
|
||||
import jakarta.persistence.Table;
|
||||
import jakarta.persistence.Temporal;
|
||||
import jakarta.persistence.TemporalType;
|
||||
import org.apache.commons.lang3.builder.EqualsBuilder;
|
||||
import org.apache.commons.lang3.builder.HashCodeBuilder;
|
||||
import org.dspace.core.ReloadableEntity;
|
||||
|
@@ -9,10 +9,10 @@ package org.dspace.alerts.dao.impl;
|
||||
|
||||
import java.sql.SQLException;
|
||||
import java.util.List;
|
||||
import javax.persistence.criteria.CriteriaBuilder;
|
||||
import javax.persistence.criteria.CriteriaQuery;
|
||||
import javax.persistence.criteria.Root;
|
||||
|
||||
import jakarta.persistence.criteria.CriteriaBuilder;
|
||||
import jakarta.persistence.criteria.CriteriaQuery;
|
||||
import jakarta.persistence.criteria.Root;
|
||||
import org.dspace.alerts.SystemWideAlert;
|
||||
import org.dspace.alerts.SystemWideAlert_;
|
||||
import org.dspace.alerts.dao.SystemWideAlertDAO;
|
||||
|
@@ -188,6 +188,15 @@ public class DSpaceCSV implements Serializable {
|
||||
// Verify that the heading is valid in the metadata registry
|
||||
String[] clean = element.split("\\[");
|
||||
String[] parts = clean[0].split("\\.");
|
||||
// Check language if present, if it's ANY then throw an exception
|
||||
if (clean.length > 1 && clean[1].equals(Item.ANY + "]")) {
|
||||
throw new MetadataImportInvalidHeadingException("Language ANY (*) was found in the heading " +
|
||||
"of the metadata value to import, " +
|
||||
"this should never be the case",
|
||||
MetadataImportInvalidHeadingException.ENTRY,
|
||||
columnCounter);
|
||||
|
||||
}
|
||||
|
||||
if (parts.length < 2) {
|
||||
throw new MetadataImportInvalidHeadingException(element,
|
||||
@@ -223,6 +232,15 @@ public class DSpaceCSV implements Serializable {
|
||||
}
|
||||
}
|
||||
|
||||
// Verify there isn’t already a header that is the same; if it already exists,
|
||||
// throw MetadataImportInvalidHeadingException
|
||||
String header = authorityPrefix + element;
|
||||
if (headings.contains(header)) {
|
||||
throw new MetadataImportInvalidHeadingException("Duplicate heading found: " + header,
|
||||
MetadataImportInvalidHeadingException.ENTRY,
|
||||
columnCounter);
|
||||
}
|
||||
|
||||
// Store the heading
|
||||
headings.add(authorityPrefix + element);
|
||||
}
|
||||
@@ -457,7 +475,7 @@ public class DSpaceCSV implements Serializable {
|
||||
key = key + "." + metadataField.getQualifier();
|
||||
}
|
||||
|
||||
// Add the language if there is one (schema.element.qualifier[langauge])
|
||||
// Add the language if there is one (schema.element.qualifier[language])
|
||||
//if ((value.language != null) && (!"".equals(value.language)))
|
||||
if (value.getLanguage() != null) {
|
||||
key = key + "[" + value.getLanguage() + "]";
|
||||
|
@@ -20,8 +20,8 @@ import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import jakarta.annotation.Nullable;
|
||||
import org.apache.commons.cli.ParseException;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
@@ -253,7 +253,7 @@ public class MetadataImport extends DSpaceRunnable<MetadataImportScriptConfigura
|
||||
displayChanges(changes, true);
|
||||
}
|
||||
|
||||
// Finsh off and tidy up
|
||||
// Finish off and tidy up
|
||||
c.restoreAuthSystemState();
|
||||
c.complete();
|
||||
} catch (Exception e) {
|
||||
@@ -1653,7 +1653,7 @@ public class MetadataImport extends DSpaceRunnable<MetadataImportScriptConfigura
|
||||
.getLabel();
|
||||
} else {
|
||||
// Target item may be archived; check there.
|
||||
// Add to errors if Realtionship.type cannot be derived
|
||||
// Add to errors if Relationship.type cannot be derived
|
||||
Item targetItem = null;
|
||||
if (itemService.find(c, UUID.fromString(targetUUID)) != null) {
|
||||
targetItem = itemService.find(c, UUID.fromString(targetUUID));
|
||||
@@ -1698,7 +1698,7 @@ public class MetadataImport extends DSpaceRunnable<MetadataImportScriptConfigura
|
||||
validateTypesByTypeByTypeName(c, targetType, originType, typeName, originRow);
|
||||
} else {
|
||||
// Origin item may be archived; check there.
|
||||
// Add to errors if Realtionship.type cannot be derived.
|
||||
// Add to errors if Relationship.type cannot be derived.
|
||||
Item originItem = null;
|
||||
if (itemService.find(c, UUID.fromString(targetUUID)) != null) {
|
||||
DSpaceCSVLine dSpaceCSVLine = this.csv.getCSVLines()
|
||||
|
@@ -31,8 +31,8 @@ import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipOutputStream;
|
||||
import javax.mail.MessagingException;
|
||||
|
||||
import jakarta.mail.MessagingException;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.dspace.app.itemexport.service.ItemExportService;
|
||||
@@ -725,7 +725,7 @@ public class ItemExportServiceImpl implements ItemExportService {
|
||||
try {
|
||||
emailErrorMessage(eperson, e1.getMessage());
|
||||
} catch (Exception e) {
|
||||
// wont throw here
|
||||
// won't throw here
|
||||
}
|
||||
throw new IllegalStateException(e1);
|
||||
} finally {
|
||||
|
@@ -11,8 +11,8 @@ import java.io.InputStream;
|
||||
import java.util.Date;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import javax.mail.MessagingException;
|
||||
|
||||
import jakarta.mail.MessagingException;
|
||||
import org.dspace.content.DSpaceObject;
|
||||
import org.dspace.content.Item;
|
||||
import org.dspace.core.Context;
|
||||
@@ -69,7 +69,7 @@ public interface ItemExportService {
|
||||
boolean excludeBitstreams) throws Exception;
|
||||
|
||||
/**
|
||||
* Convenience methot to create export a single Community, Collection, or
|
||||
* Convenience method to create export a single Community, Collection, or
|
||||
* Item
|
||||
*
|
||||
* @param dso - the dspace object to export
|
||||
@@ -93,7 +93,7 @@ public interface ItemExportService {
|
||||
Context context, boolean migrate) throws Exception;
|
||||
|
||||
/**
|
||||
* Convenience methot to create export a single Community, Collection, or
|
||||
* Convenience method to create export a single Community, Collection, or
|
||||
* Item
|
||||
*
|
||||
* @param dso - the dspace object to export
|
||||
@@ -156,7 +156,7 @@ public interface ItemExportService {
|
||||
public String getExportWorkDirectory() throws Exception;
|
||||
|
||||
/**
|
||||
* Used to read the export archived. Inteded for download.
|
||||
* Used to read the export archived. Intended for download.
|
||||
*
|
||||
* @param fileName the name of the file to download
|
||||
* @param eperson the eperson requesting the download
|
||||
@@ -233,7 +233,7 @@ public interface ItemExportService {
|
||||
|
||||
/**
|
||||
* Since the archive is created in a new thread we are unable to communicate
|
||||
* with calling method about success or failure. We accomplis this
|
||||
* with calling method about success or failure. We accomplish this
|
||||
* communication with email instead. Send a success email once the export
|
||||
* archive is complete and ready for download
|
||||
*
|
||||
@@ -248,7 +248,7 @@ public interface ItemExportService {
|
||||
|
||||
/**
|
||||
* Since the archive is created in a new thread we are unable to communicate
|
||||
* with calling method about success or failure. We accomplis this
|
||||
* with calling method about success or failure. We accomplish this
|
||||
* communication with email instead. Send an error email if the export
|
||||
* archive fails
|
||||
*
|
||||
|
@@ -46,7 +46,6 @@ import java.util.TreeMap;
|
||||
import java.util.UUID;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipFile;
|
||||
import javax.mail.MessagingException;
|
||||
import javax.xml.parsers.DocumentBuilder;
|
||||
import javax.xml.parsers.DocumentBuilderFactory;
|
||||
import javax.xml.parsers.ParserConfigurationException;
|
||||
@@ -56,6 +55,7 @@ import javax.xml.xpath.XPathConstants;
|
||||
import javax.xml.xpath.XPathExpressionException;
|
||||
import javax.xml.xpath.XPathFactory;
|
||||
|
||||
import jakarta.mail.MessagingException;
|
||||
import org.apache.commons.collections4.ComparatorUtils;
|
||||
import org.apache.commons.io.FileDeleteStrategy;
|
||||
import org.apache.commons.io.FileUtils;
|
||||
@@ -2210,7 +2210,7 @@ public class ItemImportServiceImpl implements ItemImportService, InitializingBea
|
||||
emailErrorMessage(eperson, exceptionString);
|
||||
throw new Exception(e.getMessage());
|
||||
} catch (Exception e2) {
|
||||
// wont throw here
|
||||
// won't throw here
|
||||
}
|
||||
} finally {
|
||||
// Make sure the database connection gets closed in all conditions.
|
||||
|
@@ -10,8 +10,8 @@ package org.dspace.app.itemimport.service;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import javax.mail.MessagingException;
|
||||
|
||||
import jakarta.mail.MessagingException;
|
||||
import org.dspace.app.itemimport.BatchUpload;
|
||||
import org.dspace.content.Collection;
|
||||
import org.dspace.core.Context;
|
||||
@@ -121,7 +121,7 @@ public interface ItemImportService {
|
||||
|
||||
/**
|
||||
* If a batch import is done in a new thread we are unable to communicate
|
||||
* with calling method about success or failure. We accomplis this
|
||||
* with calling method about success or failure. We accomplish this
|
||||
* communication with email instead. Send an error email if the batch
|
||||
* import fails
|
||||
*
|
||||
|
@@ -217,7 +217,7 @@ public class ItemArchive {
|
||||
throws SQLException, Exception {
|
||||
DtoMetadata dtom = getMetadataField("dc.identifier.uri");
|
||||
if (dtom == null) {
|
||||
throw new Exception("No dc.identier.uri field found for handle");
|
||||
throw new Exception("No dc.identifier.uri field found for handle");
|
||||
}
|
||||
|
||||
this.addUndoMetadataField(dtom); //seed the undo list with the uri
|
||||
|
@@ -10,7 +10,7 @@ package org.dspace.app.itemupdate;
|
||||
import java.util.Properties;
|
||||
|
||||
/**
|
||||
* Bitstream filter targetting the THUMBNAIL bundle
|
||||
* Bitstream filter targeting the THUMBNAIL bundle
|
||||
*/
|
||||
public class ThumbnailBitstreamFilter extends BitstreamFilterByBundleName {
|
||||
|
||||
|
30
dspace-api/src/main/java/org/dspace/app/ldn/ItemFilter.java
Normal file
30
dspace-api/src/main/java/org/dspace/app/ldn/ItemFilter.java
Normal file
@@ -0,0 +1,30 @@
|
||||
/**
|
||||
* 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.ldn;
|
||||
|
||||
/**
|
||||
* model class for the item filters configured into item-filters.xml
|
||||
*
|
||||
* @author Mohamed Eskander (mohamed.eskander at 4science.com)
|
||||
*/
|
||||
public class ItemFilter {
|
||||
|
||||
private String id;
|
||||
|
||||
public ItemFilter(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
}
|
@@ -0,0 +1,243 @@
|
||||
/**
|
||||
* 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.ldn;
|
||||
|
||||
import static java.lang.String.format;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.JsonMappingException;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import org.apache.commons.collections4.CollectionUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.dspace.app.ldn.factory.NotifyServiceFactory;
|
||||
import org.dspace.app.ldn.model.Notification;
|
||||
import org.dspace.app.ldn.service.LDNMessageService;
|
||||
import org.dspace.app.ldn.service.NotifyPatternToTriggerService;
|
||||
import org.dspace.app.ldn.service.NotifyServiceInboundPatternService;
|
||||
import org.dspace.content.Bitstream;
|
||||
import org.dspace.content.BitstreamFormat;
|
||||
import org.dspace.content.Bundle;
|
||||
import org.dspace.content.Item;
|
||||
import org.dspace.content.MetadataValue;
|
||||
import org.dspace.content.factory.ContentServiceFactory;
|
||||
import org.dspace.content.logic.LogicalStatement;
|
||||
import org.dspace.content.service.BitstreamService;
|
||||
import org.dspace.content.service.ItemService;
|
||||
import org.dspace.core.Constants;
|
||||
import org.dspace.core.Context;
|
||||
import org.dspace.core.I18nUtil;
|
||||
import org.dspace.core.LDN;
|
||||
import org.dspace.event.Consumer;
|
||||
import org.dspace.event.Event;
|
||||
import org.dspace.services.ConfigurationService;
|
||||
import org.dspace.services.factory.DSpaceServicesFactory;
|
||||
import org.dspace.utils.DSpace;
|
||||
import org.dspace.web.ContextUtil;
|
||||
|
||||
/**
|
||||
* class for creating a new LDN Messages of installed item
|
||||
*
|
||||
* @author Mohamed Eskander (mohamed.eskander at 4science.com)
|
||||
*/
|
||||
public class LDNMessageConsumer implements Consumer {
|
||||
|
||||
private NotifyPatternToTriggerService notifyPatternToTriggerService;
|
||||
private NotifyServiceInboundPatternService inboundPatternService;
|
||||
private LDNMessageService ldnMessageService;
|
||||
private ConfigurationService configurationService;
|
||||
private ItemService itemService;
|
||||
private BitstreamService bitstreamService;
|
||||
|
||||
@Override
|
||||
public void initialize() throws Exception {
|
||||
notifyPatternToTriggerService = NotifyServiceFactory.getInstance().getNotifyPatternToTriggerService();
|
||||
ldnMessageService = NotifyServiceFactory.getInstance().getLDNMessageService();
|
||||
configurationService = DSpaceServicesFactory.getInstance().getConfigurationService();
|
||||
itemService = ContentServiceFactory.getInstance().getItemService();
|
||||
bitstreamService = ContentServiceFactory.getInstance().getBitstreamService();
|
||||
inboundPatternService = NotifyServiceFactory.getInstance().getNotifyServiceInboundPatternService();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void consume(Context context, Event event) throws Exception {
|
||||
|
||||
if (event.getSubjectType() != Constants.ITEM ||
|
||||
event.getEventType() != Event.INSTALL) {
|
||||
return;
|
||||
}
|
||||
|
||||
Item item = (Item) event.getSubject(context);
|
||||
createManualLDNMessages(context, item);
|
||||
createAutomaticLDNMessages(context, item);
|
||||
}
|
||||
|
||||
private void createManualLDNMessages(Context context, Item item) throws SQLException, JsonProcessingException {
|
||||
List<NotifyPatternToTrigger> patternsToTrigger =
|
||||
notifyPatternToTriggerService.findByItem(context, item);
|
||||
|
||||
for (NotifyPatternToTrigger patternToTrigger : patternsToTrigger) {
|
||||
createLDNMessage(context,patternToTrigger.getItem(),
|
||||
patternToTrigger.getNotifyService(), patternToTrigger.getPattern());
|
||||
}
|
||||
}
|
||||
|
||||
private void createAutomaticLDNMessages(Context context, Item item) throws SQLException, JsonProcessingException {
|
||||
|
||||
List<NotifyServiceInboundPattern> inboundPatterns = inboundPatternService.findAutomaticPatterns(context);
|
||||
|
||||
for (NotifyServiceInboundPattern inboundPattern : inboundPatterns) {
|
||||
if (StringUtils.isEmpty(inboundPattern.getConstraint()) ||
|
||||
evaluateFilter(context, item, inboundPattern.getConstraint())) {
|
||||
createLDNMessage(context, item, inboundPattern.getNotifyService(), inboundPattern.getPattern());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean evaluateFilter(Context context, Item item, String constraint) {
|
||||
LogicalStatement filter =
|
||||
new DSpace().getServiceManager().getServiceByName(constraint, LogicalStatement.class);
|
||||
|
||||
return filter != null && filter.getResult(context, item);
|
||||
}
|
||||
|
||||
private void createLDNMessage(Context context, Item item, NotifyServiceEntity service, String pattern)
|
||||
throws SQLException, JsonMappingException, JsonProcessingException {
|
||||
|
||||
LDN ldn = getLDNMessage(pattern);
|
||||
LDNMessageEntity ldnMessage =
|
||||
ldnMessageService.create(context, format("urn:uuid:%s", UUID.randomUUID()));
|
||||
|
||||
ldnMessage.setObject(item);
|
||||
ldnMessage.setTarget(service);
|
||||
ldnMessage.setQueueStatus(LDNMessageEntity.QUEUE_STATUS_QUEUED);
|
||||
ldnMessage.setQueueTimeout(new Date());
|
||||
|
||||
appendGeneratedMessage(ldn, ldnMessage, pattern);
|
||||
|
||||
ObjectMapper mapper = new ObjectMapper();
|
||||
Notification notification = mapper.readValue(ldnMessage.getMessage(), Notification.class);
|
||||
ldnMessage.setType(StringUtils.joinWith(",", notification.getType()));
|
||||
|
||||
ArrayList<String> notificationTypeArrayList = new ArrayList<String>(notification.getType());
|
||||
// sorting the list
|
||||
Collections.sort(notificationTypeArrayList);
|
||||
ldnMessage.setActivityStreamType(notificationTypeArrayList.get(0));
|
||||
ldnMessage.setCoarNotifyType(notificationTypeArrayList.get(1));
|
||||
|
||||
ldnMessageService.update(context, ldnMessage);
|
||||
}
|
||||
|
||||
private LDN getLDNMessage(String pattern) {
|
||||
try {
|
||||
return LDN.getLDNMessage(I18nUtil.getLDNFilename(Locale.getDefault(), pattern));
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private void appendGeneratedMessage(LDN ldn, LDNMessageEntity ldnMessage, String pattern) {
|
||||
Item item = (Item) ldnMessage.getObject();
|
||||
ldn.addArgument(getUiUrl());
|
||||
ldn.addArgument(configurationService.getProperty("ldn.notify.inbox"));
|
||||
ldn.addArgument(configurationService.getProperty("dspace.name"));
|
||||
ldn.addArgument(Objects.requireNonNullElse(ldnMessage.getTarget().getUrl(), ""));
|
||||
ldn.addArgument(Objects.requireNonNullElse(ldnMessage.getTarget().getLdnUrl(), ""));
|
||||
ldn.addArgument(getUiUrl() + "/handle/" + ldnMessage.getObject().getHandle());
|
||||
ldn.addArgument(getIdentifierUri(item));
|
||||
ldn.addArgument(generateBitstreamDownloadUrl(item));
|
||||
ldn.addArgument(getBitstreamMimeType(findPrimaryBitstream(item)));
|
||||
ldn.addArgument(ldnMessage.getID());
|
||||
ldn.addArgument(getRelationUri(item));
|
||||
ldn.addArgument("http://purl.org/vocab/frbr/core#supplement");
|
||||
ldn.addArgument(format("urn:uuid:%s", UUID.randomUUID()));
|
||||
|
||||
ldnMessage.setMessage(ldn.generateLDNMessage());
|
||||
}
|
||||
|
||||
private String getUiUrl() {
|
||||
return configurationService.getProperty("dspace.ui.url");
|
||||
}
|
||||
|
||||
private String getIdentifierUri(Item item) {
|
||||
return itemService.getMetadataByMetadataString(item, "dc.identifier.uri")
|
||||
.stream()
|
||||
.findFirst()
|
||||
.map(MetadataValue::getValue)
|
||||
.orElse("");
|
||||
}
|
||||
|
||||
private String getRelationUri(Item item) {
|
||||
String relationMetadata = configurationService.getProperty("ldn.notify.relation.metadata", "dc.relation");
|
||||
return itemService.getMetadataByMetadataString(item, relationMetadata)
|
||||
.stream()
|
||||
.findFirst()
|
||||
.map(MetadataValue::getValue)
|
||||
.orElse("");
|
||||
}
|
||||
|
||||
private String generateBitstreamDownloadUrl(Item item) {
|
||||
String uiUrl = getUiUrl();
|
||||
return findPrimaryBitstream(item)
|
||||
.map(bs -> uiUrl + "/bitstreams/" + bs.getID() + "/download")
|
||||
.orElse("");
|
||||
}
|
||||
|
||||
private Optional<Bitstream> findPrimaryBitstream(Item item) {
|
||||
List<Bundle> bundles = item.getBundles(Constants.CONTENT_BUNDLE_NAME);
|
||||
return bundles.stream()
|
||||
.findFirst()
|
||||
.map(Bundle::getPrimaryBitstream)
|
||||
.or(() -> bundles.stream()
|
||||
.findFirst()
|
||||
.flatMap(bundle -> CollectionUtils.isNotEmpty(bundle.getBitstreams())
|
||||
? Optional.of(bundle.getBitstreams().get(0))
|
||||
: Optional.empty()));
|
||||
}
|
||||
|
||||
private String getBitstreamMimeType(Optional<Bitstream> bitstream) {
|
||||
return bitstream.map(bs -> {
|
||||
try {
|
||||
Context context = ContextUtil.obtainCurrentRequestContext();
|
||||
BitstreamFormat bitstreamFormat = bs.getFormat(context);
|
||||
if (bitstreamFormat.getShortDescription().equals("Unknown")) {
|
||||
return getUserFormatMimeType(bs);
|
||||
}
|
||||
return bitstreamFormat.getMIMEType();
|
||||
} catch (SQLException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}).orElse("");
|
||||
}
|
||||
|
||||
private String getUserFormatMimeType(Bitstream bitstream) {
|
||||
return bitstreamService.getMetadataFirstValue(bitstream,
|
||||
"dc", "format", "mimetype", Item.ANY);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void end(Context ctx) throws Exception {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void finish(Context ctx) throws Exception {
|
||||
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,319 @@
|
||||
/**
|
||||
* 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.ldn;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.Date;
|
||||
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.JoinColumn;
|
||||
import jakarta.persistence.ManyToOne;
|
||||
import jakarta.persistence.Table;
|
||||
import jakarta.persistence.Temporal;
|
||||
import jakarta.persistence.TemporalType;
|
||||
import org.dspace.content.DSpaceObject;
|
||||
import org.dspace.core.ReloadableEntity;
|
||||
|
||||
/**
|
||||
* Class representing ldnMessages stored in the DSpace system and, when locally resolvable,
|
||||
* some information are stored as dedicated attributes.
|
||||
*
|
||||
* @author Mohamed Eskander (mohamed.eskander at 4science.com)
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "ldn_message")
|
||||
public class LDNMessageEntity implements ReloadableEntity<String> {
|
||||
|
||||
/**
|
||||
* LDN messages interact with a fictitious queue. Scheduled tasks manage the queue.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Notification Type constants
|
||||
*/
|
||||
public static final String TYPE_INCOMING = "Incoming";
|
||||
public static final String TYPE_OUTGOING = "Outgoing";
|
||||
|
||||
/**
|
||||
* Message must not be processed.
|
||||
*/
|
||||
public static final Integer QUEUE_STATUS_UNTRUSTED_IP = 0;
|
||||
|
||||
/**
|
||||
* Message queued, it has to be elaborated.
|
||||
*/
|
||||
public static final Integer QUEUE_STATUS_QUEUED = 1;
|
||||
|
||||
/**
|
||||
* Message has been taken from the queue and it's elaboration is in progress.
|
||||
*/
|
||||
public static final Integer QUEUE_STATUS_PROCESSING = 2;
|
||||
|
||||
/**
|
||||
* Message has been correctly elaborated.
|
||||
*/
|
||||
public static final Integer QUEUE_STATUS_PROCESSED = 3;
|
||||
|
||||
/**
|
||||
* Message has not been correctly elaborated - despite more than "ldn.processor.max.attempts" retryies
|
||||
*/
|
||||
public static final Integer QUEUE_STATUS_FAILED = 4;
|
||||
|
||||
/**
|
||||
* Message must not be processed
|
||||
*/
|
||||
public static final Integer QUEUE_STATUS_UNTRUSTED = 5;
|
||||
|
||||
/**
|
||||
* Message is not processed since action is not mapped
|
||||
*/
|
||||
public static final Integer QUEUE_STATUS_UNMAPPED_ACTION = 6;
|
||||
|
||||
/**
|
||||
* Message queued for retry, it has to be elaborated.
|
||||
*/
|
||||
public static final Integer QUEUE_STATUS_QUEUED_FOR_RETRY = 7;
|
||||
|
||||
@Id
|
||||
private String id;
|
||||
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "object", referencedColumnName = "uuid")
|
||||
private DSpaceObject object;
|
||||
|
||||
@Column(name = "message", columnDefinition = "text")
|
||||
private String message;
|
||||
|
||||
@Column(name = "type")
|
||||
private String type;
|
||||
|
||||
@Column(name = "queue_status")
|
||||
private Integer queueStatus;
|
||||
|
||||
@Column(name = "queue_attempts")
|
||||
private Integer queueAttempts = 0;
|
||||
|
||||
@Temporal(TemporalType.TIMESTAMP)
|
||||
@Column(name = "queue_last_start_time")
|
||||
private Date queueLastStartTime = null;
|
||||
|
||||
@Temporal(TemporalType.TIMESTAMP)
|
||||
@Column(name = "queue_timeout")
|
||||
private Date queueTimeout = null;
|
||||
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "origin", referencedColumnName = "id")
|
||||
private NotifyServiceEntity origin;
|
||||
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "target", referencedColumnName = "id")
|
||||
private NotifyServiceEntity target;
|
||||
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "inReplyTo", referencedColumnName = "id")
|
||||
private LDNMessageEntity inReplyTo;
|
||||
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "context", referencedColumnName = "uuid")
|
||||
private DSpaceObject context;
|
||||
|
||||
@Column(name = "activity_stream_type")
|
||||
private String activityStreamType;
|
||||
|
||||
@Column(name = "coar_notify_type")
|
||||
private String coarNotifyType;
|
||||
|
||||
@Column(name = "source_ip")
|
||||
private String sourceIp;
|
||||
|
||||
protected LDNMessageEntity() {
|
||||
|
||||
}
|
||||
|
||||
public LDNMessageEntity(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getID() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return the DSpace item related to this message
|
||||
*/
|
||||
public DSpaceObject getObject() {
|
||||
return object;
|
||||
}
|
||||
|
||||
public void setObject(DSpaceObject object) {
|
||||
this.object = object;
|
||||
}
|
||||
|
||||
public String getMessage() {
|
||||
return message;
|
||||
}
|
||||
|
||||
public void setMessage(String message) {
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public void setType(String type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public String getActivityStreamType() {
|
||||
return activityStreamType;
|
||||
}
|
||||
|
||||
public void setActivityStreamType(String activityStreamType) {
|
||||
this.activityStreamType = activityStreamType;
|
||||
}
|
||||
|
||||
public String getCoarNotifyType() {
|
||||
return coarNotifyType;
|
||||
}
|
||||
|
||||
public void setCoarNotifyType(String coarNotifyType) {
|
||||
this.coarNotifyType = coarNotifyType;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return The originator of the activity, typically the service responsible for sending the notification
|
||||
*/
|
||||
public NotifyServiceEntity getOrigin() {
|
||||
return origin;
|
||||
}
|
||||
|
||||
public void setOrigin(NotifyServiceEntity origin) {
|
||||
this.origin = origin;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return The intended destination of the activity, typically the service which consumes the notification
|
||||
*/
|
||||
public NotifyServiceEntity getTarget() {
|
||||
return target;
|
||||
}
|
||||
|
||||
public void setTarget(NotifyServiceEntity target) {
|
||||
this.target = target;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return This property is used when the notification is a direct response to a previous notification;
|
||||
* contains an {@link org.dspace.app.ldn.LDNMessageEntity#inReplyTo id}
|
||||
*/
|
||||
public LDNMessageEntity getInReplyTo() {
|
||||
return inReplyTo;
|
||||
}
|
||||
|
||||
public void setInReplyTo(LDNMessageEntity inReplyTo) {
|
||||
this.inReplyTo = inReplyTo;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return This identifies another resource which is relevant to understanding the notification
|
||||
*/
|
||||
public DSpaceObject getContext() {
|
||||
return context;
|
||||
}
|
||||
|
||||
public void setContext(DSpaceObject context) {
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
public Integer getQueueStatus() {
|
||||
return queueStatus;
|
||||
}
|
||||
|
||||
public void setQueueStatus(Integer queueStatus) {
|
||||
this.queueStatus = queueStatus;
|
||||
}
|
||||
|
||||
public Integer getQueueAttempts() {
|
||||
return queueAttempts;
|
||||
}
|
||||
|
||||
public void setQueueAttempts(Integer queueAttempts) {
|
||||
this.queueAttempts = queueAttempts;
|
||||
}
|
||||
|
||||
public Date getQueueLastStartTime() {
|
||||
return queueLastStartTime;
|
||||
}
|
||||
|
||||
public void setQueueLastStartTime(Date queueLastStartTime) {
|
||||
this.queueLastStartTime = queueLastStartTime;
|
||||
}
|
||||
|
||||
public Date getQueueTimeout() {
|
||||
return queueTimeout;
|
||||
}
|
||||
|
||||
public void setQueueTimeout(Date queueTimeout) {
|
||||
this.queueTimeout = queueTimeout;
|
||||
}
|
||||
|
||||
public String getSourceIp() {
|
||||
return sourceIp;
|
||||
}
|
||||
|
||||
public void setSourceIp(String sourceIp) {
|
||||
this.sourceIp = sourceIp;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "LDNMessage id:" + this.getID() + " typed:" + this.getType();
|
||||
}
|
||||
|
||||
public static String getNotificationType(LDNMessageEntity ldnMessage) {
|
||||
if (ldnMessage.getInReplyTo() != null || ldnMessage.getOrigin() != null) {
|
||||
return TYPE_INCOMING;
|
||||
}
|
||||
return TYPE_OUTGOING;
|
||||
}
|
||||
|
||||
public static String getServiceNameForNotifyServ(NotifyServiceEntity serviceEntity) {
|
||||
if (serviceEntity != null) {
|
||||
return serviceEntity.getName();
|
||||
}
|
||||
return "self";
|
||||
}
|
||||
|
||||
public static String getQueueStatus(LDNMessageEntity ldnMessage) {
|
||||
Class<LDNMessageEntity> cl = LDNMessageEntity.class;
|
||||
try {
|
||||
for (Field f : cl.getDeclaredFields()) {
|
||||
String fieldName = f.getName();
|
||||
if (fieldName.startsWith("QUEUE_") && (f.get(null) == ldnMessage.getQueueStatus())) {
|
||||
return fieldName;
|
||||
}
|
||||
}
|
||||
} catch (IllegalArgumentException | IllegalAccessException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
@@ -5,10 +5,12 @@
|
||||
*
|
||||
* http://www.dspace.org/license/
|
||||
*/
|
||||
package org.dspace.app.ldn;
|
||||
|
||||
/**
|
||||
* Support for using DSpace Services in a servlet context. This is how the
|
||||
* kernel and services get started by the servlet container.
|
||||
*/
|
||||
public enum LDNMessageQueueStatus {
|
||||
|
||||
package org.dspace.servicemanager.servlet;
|
||||
/**
|
||||
* Resulting processing status of an LDN Message (aka queue management)
|
||||
*/
|
||||
QUEUED, PROCESSING, PROCESSED, FAILED;
|
||||
}
|
@@ -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.ldn;
|
||||
|
||||
/**
|
||||
* Constants for LDN metadata fields
|
||||
*
|
||||
* @author Francesco Bacchelli (francesco.bacchelli at 4science.com)
|
||||
*/
|
||||
public final class LDNMetadataFields {
|
||||
|
||||
// schema and element are the same for each metadata of LDN coar-notify
|
||||
public static final String SCHEMA = "coar";
|
||||
public static final String ELEMENT = "notify";
|
||||
|
||||
// qualifiers
|
||||
public static final String INITIALIZE = "initialize";
|
||||
public static final String REQUEST_REVIEW = "requestreview";
|
||||
public static final String REQUEST_ENDORSEMENT = "requestendorsement";
|
||||
public static final String EXAMINATION = "examination";
|
||||
public static final String REFUSED = "refused";
|
||||
public static final String REVIEW = "review";
|
||||
public static final String ENDORSMENT = "endorsement";
|
||||
public static final String RELEASE = "release";
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private LDNMetadataFields() {
|
||||
|
||||
}
|
||||
|
||||
}
|
@@ -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.ldn;
|
||||
|
||||
import java.sql.SQLException;
|
||||
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.dspace.app.ldn.factory.LDNMessageServiceFactory;
|
||||
import org.dspace.app.ldn.service.LDNMessageService;
|
||||
import org.dspace.core.Context;
|
||||
|
||||
/**
|
||||
* LDN Message manager: scheduled task invoking extractAndProcessMessageFromQueue() of {@link LDNMessageService}
|
||||
*
|
||||
* @author Francesco Bacchelli (francesco.bacchelli at 4science dot it)
|
||||
*/
|
||||
public class LDNQueueExtractor {
|
||||
|
||||
private static final LDNMessageService ldnMessageService = LDNMessageServiceFactory.getInstance()
|
||||
.getLDNMessageService();
|
||||
private static final Logger log = org.apache.logging.log4j.LogManager.getLogger(LDNQueueExtractor.class);
|
||||
|
||||
/**
|
||||
* Default constructor
|
||||
*/
|
||||
private LDNQueueExtractor() {
|
||||
}
|
||||
|
||||
/**
|
||||
* invokes
|
||||
* @see org.dspace.app.ldn.service.impl.LDNMessageServiceImpl#extractAndProcessMessageFromQueue(Context)
|
||||
* to process the oldest ldn messages from the queue. An LdnMessage is processed when is routed to a
|
||||
* @see org.dspace.app.ldn.processor.LDNProcessor
|
||||
* Also a +1 is added to the ldnMessage entity
|
||||
* @see org.dspace.app.ldn.LDNMessageEntity#getQueueAttempts()
|
||||
* @return the number of processed ldnMessages.
|
||||
* @throws SQLException
|
||||
*/
|
||||
public static int extractMessageFromQueue() throws SQLException {
|
||||
Context context = new Context(Context.Mode.READ_WRITE);
|
||||
int processed_messages = ldnMessageService.extractAndProcessMessageFromQueue(context);
|
||||
if (processed_messages > 0) {
|
||||
log.info("Processed Messages x" + processed_messages);
|
||||
}
|
||||
context.complete();
|
||||
return processed_messages;
|
||||
}
|
||||
|
||||
};
|
@@ -0,0 +1,53 @@
|
||||
/**
|
||||
* The contents of this file are subject to the license and copyright
|
||||
* detailed in the LICENSE and NOTICE files at the root of the source
|
||||
* tree and available online at
|
||||
*
|
||||
* http://www.dspace.org/license/
|
||||
*/
|
||||
package org.dspace.app.ldn;
|
||||
|
||||
import java.sql.SQLException;
|
||||
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.dspace.app.ldn.factory.LDNMessageServiceFactory;
|
||||
import org.dspace.app.ldn.service.LDNMessageService;
|
||||
import org.dspace.core.Context;
|
||||
|
||||
/**
|
||||
* LDN Message manager: scheduled task invoking checkQueueMessageTimeout() of {@link LDNMessageService}
|
||||
*
|
||||
* @author Francesco Bacchelli (francesco.bacchelli at 4science dot it)
|
||||
*/
|
||||
public class LDNQueueTimeoutChecker {
|
||||
|
||||
private static final LDNMessageService ldnMessageService = LDNMessageServiceFactory.getInstance()
|
||||
.getLDNMessageService();
|
||||
private static final Logger log = org.apache.logging.log4j.LogManager.getLogger(LDNQueueTimeoutChecker.class);
|
||||
|
||||
/**
|
||||
* Default constructor
|
||||
*/
|
||||
private LDNQueueTimeoutChecker() {
|
||||
}
|
||||
|
||||
/**
|
||||
* invokes
|
||||
* @see org.dspace.app.ldn.service.impl.LDNMessageServiceImpl#checkQueueMessageTimeout(Context)
|
||||
* to refresh the queue status of timed-out and in progressing status ldn messages:
|
||||
* according to their attempts put them back in queue or set their status as failed if maxAttempts
|
||||
* reached.
|
||||
* @return the number of managed ldnMessages.
|
||||
* @throws SQLException
|
||||
*/
|
||||
public static int checkQueueMessageTimeout() throws SQLException {
|
||||
Context context = new Context(Context.Mode.READ_WRITE);
|
||||
int fixed_messages = 0;
|
||||
fixed_messages = ldnMessageService.checkQueueMessageTimeout(context);
|
||||
if (fixed_messages > 0) {
|
||||
log.info("Managed Messages x" + fixed_messages);
|
||||
}
|
||||
context.complete();
|
||||
return fixed_messages;
|
||||
}
|
||||
}
|
91
dspace-api/src/main/java/org/dspace/app/ldn/LDNRouter.java
Normal file
91
dspace-api/src/main/java/org/dspace/app/ldn/LDNRouter.java
Normal file
@@ -0,0 +1,91 @@
|
||||
/**
|
||||
* The contents of this file are subject to the license and copyright
|
||||
* detailed in the LICENSE and NOTICE files at the root of the source
|
||||
* tree and available online at
|
||||
*
|
||||
* http://www.dspace.org/license/
|
||||
*/
|
||||
package org.dspace.app.ldn;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.dspace.app.ldn.processor.LDNProcessor;
|
||||
|
||||
/**
|
||||
* Linked Data Notification router.
|
||||
*/
|
||||
public class LDNRouter {
|
||||
|
||||
private Map<Set<String>, LDNProcessor> incomingProcessors = new HashMap<>();
|
||||
private Map<Set<String>, LDNProcessor> outcomingProcessors = new HashMap<>();
|
||||
private static final Logger log = org.apache.logging.log4j.LogManager.getLogger(LDNRouter.class);
|
||||
|
||||
/**
|
||||
* Route notification to processor
|
||||
*
|
||||
* @return LDNProcessor processor to process notification, can be null
|
||||
*/
|
||||
public LDNProcessor route(LDNMessageEntity ldnMessage) {
|
||||
if (ldnMessage == null) {
|
||||
log.warn("A null LDNMessage was received and could not be routed.");
|
||||
return null;
|
||||
}
|
||||
if (StringUtils.isEmpty(ldnMessage.getType())) {
|
||||
log.warn("LDNMessage " + ldnMessage + " was received. It has no type, so it couldn't be routed.");
|
||||
return null;
|
||||
}
|
||||
Set<String> ldnMessageTypeSet = new HashSet<String>();
|
||||
ldnMessageTypeSet.add(ldnMessage.getActivityStreamType());
|
||||
ldnMessageTypeSet.add(ldnMessage.getCoarNotifyType());
|
||||
|
||||
LDNProcessor processor = null;
|
||||
if (ldnMessage.getTarget() == null) {
|
||||
processor = incomingProcessors.get(ldnMessageTypeSet);
|
||||
} else if (ldnMessage.getOrigin() == null) {
|
||||
processor = outcomingProcessors.get(ldnMessageTypeSet);
|
||||
}
|
||||
|
||||
return processor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all incoming routes.
|
||||
*
|
||||
* @return Map<Set<String>, LDNProcessor>
|
||||
*/
|
||||
public Map<Set<String>, LDNProcessor> getIncomingProcessors() {
|
||||
return incomingProcessors;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set all incoming routes.
|
||||
*
|
||||
* @param incomingProcessors
|
||||
*/
|
||||
public void setIncomingProcessors(Map<Set<String>, LDNProcessor> incomingProcessors) {
|
||||
this.incomingProcessors = incomingProcessors;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all outcoming routes.
|
||||
*
|
||||
* @return Map<Set<String>, LDNProcessor>
|
||||
*/
|
||||
public Map<Set<String>, LDNProcessor> getOutcomingProcessors() {
|
||||
return outcomingProcessors;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set all outcoming routes.
|
||||
*
|
||||
* @param outcomingProcessors
|
||||
*/
|
||||
public void setOutcomingProcessors(Map<Set<String>, LDNProcessor> outcomingProcessors) {
|
||||
this.outcomingProcessors = outcomingProcessors;
|
||||
}
|
||||
}
|
@@ -0,0 +1,82 @@
|
||||
/**
|
||||
* 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.ldn;
|
||||
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.GenerationType;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.JoinColumn;
|
||||
import jakarta.persistence.ManyToOne;
|
||||
import jakarta.persistence.SequenceGenerator;
|
||||
import jakarta.persistence.Table;
|
||||
import org.dspace.content.Item;
|
||||
import org.dspace.core.ReloadableEntity;
|
||||
|
||||
/**
|
||||
* Database object representing notify patterns to be triggered
|
||||
*
|
||||
* @author Mohamed Eskander (mohamed.eskander at 4science.com)
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "notifypatterns_to_trigger")
|
||||
public class NotifyPatternToTrigger implements ReloadableEntity<Integer> {
|
||||
|
||||
@Id
|
||||
@Column(name = "id")
|
||||
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "notifypatterns_to_trigger_id_seq")
|
||||
@SequenceGenerator(name = "notifypatterns_to_trigger_id_seq",
|
||||
sequenceName = "notifypatterns_to_trigger_id_seq",
|
||||
allocationSize = 1)
|
||||
private Integer id;
|
||||
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "item_id", referencedColumnName = "uuid")
|
||||
private Item item;
|
||||
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "service_id", referencedColumnName = "id")
|
||||
private NotifyServiceEntity notifyService;
|
||||
|
||||
@Column(name = "pattern")
|
||||
private String pattern;
|
||||
|
||||
public void setId(Integer id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public Item getItem() {
|
||||
return item;
|
||||
}
|
||||
|
||||
public void setItem(Item item) {
|
||||
this.item = item;
|
||||
}
|
||||
|
||||
public NotifyServiceEntity getNotifyService() {
|
||||
return notifyService;
|
||||
}
|
||||
|
||||
public void setNotifyService(NotifyServiceEntity notifyService) {
|
||||
this.notifyService = notifyService;
|
||||
}
|
||||
|
||||
public String getPattern() {
|
||||
return pattern;
|
||||
}
|
||||
|
||||
public void setPattern(String pattern) {
|
||||
this.pattern = pattern;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer getID() {
|
||||
return id;
|
||||
}
|
||||
}
|
@@ -0,0 +1,156 @@
|
||||
/**
|
||||
* 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.ldn;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.List;
|
||||
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.GenerationType;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.OneToMany;
|
||||
import jakarta.persistence.SequenceGenerator;
|
||||
import jakarta.persistence.Table;
|
||||
import org.dspace.core.ReloadableEntity;
|
||||
|
||||
/**
|
||||
* Database object representing notify services
|
||||
*
|
||||
* @author Mohamed Eskander (mohamed.eskander at 4science.com)
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "notifyservice")
|
||||
public class NotifyServiceEntity implements ReloadableEntity<Integer> {
|
||||
|
||||
@Id
|
||||
@Column(name = "id")
|
||||
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "notifyservice_id_seq")
|
||||
@SequenceGenerator(name = "notifyservice_id_seq", sequenceName = "notifyservice_id_seq",
|
||||
allocationSize = 1)
|
||||
private Integer id;
|
||||
|
||||
@Column(name = "name", nullable = false)
|
||||
private String name;
|
||||
|
||||
@Column(name = "description", columnDefinition = "text")
|
||||
private String description;
|
||||
|
||||
@Column(name = "url")
|
||||
private String url;
|
||||
|
||||
@Column(name = "ldn_url")
|
||||
private String ldnUrl;
|
||||
|
||||
@OneToMany(mappedBy = "notifyService")
|
||||
private List<NotifyServiceInboundPattern> inboundPatterns;
|
||||
|
||||
@Column(name = "enabled")
|
||||
private boolean enabled = false;
|
||||
|
||||
@Column(name = "score")
|
||||
private BigDecimal score;
|
||||
|
||||
@Column(name = "lower_ip")
|
||||
private String lowerIp;
|
||||
|
||||
@Column(name = "upper_ip")
|
||||
private String upperIp;
|
||||
|
||||
public void setId(Integer id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
public void setDescription(String description) {
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return URL of an informative website
|
||||
*/
|
||||
public String getUrl() {
|
||||
return url;
|
||||
}
|
||||
|
||||
public void setUrl(String url) {
|
||||
this.url = url;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return URL of the LDN InBox
|
||||
*/
|
||||
public String getLdnUrl() {
|
||||
return ldnUrl;
|
||||
}
|
||||
|
||||
public void setLdnUrl(String ldnUrl) {
|
||||
this.ldnUrl = ldnUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The list of the inbound patterns configuration supported by the service
|
||||
*/
|
||||
public List<NotifyServiceInboundPattern> getInboundPatterns() {
|
||||
return inboundPatterns;
|
||||
}
|
||||
|
||||
public void setInboundPatterns(List<NotifyServiceInboundPattern> inboundPatterns) {
|
||||
this.inboundPatterns = inboundPatterns;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer getID() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public boolean isEnabled() {
|
||||
return enabled;
|
||||
}
|
||||
|
||||
public void setEnabled(boolean enabled) {
|
||||
this.enabled = enabled;
|
||||
}
|
||||
|
||||
public BigDecimal getScore() {
|
||||
return score;
|
||||
}
|
||||
|
||||
public void setScore(BigDecimal score) {
|
||||
this.score = score;
|
||||
}
|
||||
|
||||
public String getLowerIp() {
|
||||
return lowerIp;
|
||||
}
|
||||
|
||||
public void setLowerIp(String lowerIp) {
|
||||
this.lowerIp = lowerIp;
|
||||
}
|
||||
|
||||
public String getUpperIp() {
|
||||
return upperIp;
|
||||
}
|
||||
|
||||
public void setUpperIp(String upperIp) {
|
||||
this.upperIp = upperIp;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,103 @@
|
||||
/**
|
||||
* 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.ldn;
|
||||
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.GenerationType;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.JoinColumn;
|
||||
import jakarta.persistence.ManyToOne;
|
||||
import jakarta.persistence.SequenceGenerator;
|
||||
import jakarta.persistence.Table;
|
||||
import org.dspace.core.ReloadableEntity;
|
||||
|
||||
/**
|
||||
* Database object representing notify service inbound patterns. Every {@link org.dspace.app.ldn.NotifyServiceEntity}
|
||||
* may have inbounds and outbounds. Inbounds are to be sent to the external service.
|
||||
*
|
||||
* @author Mohamed Eskander (mohamed.eskander at 4science.com)
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "notifyservice_inbound_pattern")
|
||||
public class NotifyServiceInboundPattern implements ReloadableEntity<Integer> {
|
||||
|
||||
@Id
|
||||
@Column(name = "id")
|
||||
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "notifyservice_inbound_pattern_id_seq")
|
||||
@SequenceGenerator(name = "notifyservice_inbound_pattern_id_seq",
|
||||
sequenceName = "notifyservice_inbound_pattern_id_seq",
|
||||
allocationSize = 1)
|
||||
private Integer id;
|
||||
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "service_id", referencedColumnName = "id")
|
||||
private NotifyServiceEntity notifyService;
|
||||
|
||||
@Column(name = "pattern")
|
||||
private String pattern;
|
||||
|
||||
@Column(name = "constraint_name")
|
||||
private String constraint;
|
||||
|
||||
@Column(name = "automatic")
|
||||
private boolean automatic;
|
||||
|
||||
@Override
|
||||
public Integer getID() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Integer id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public NotifyServiceEntity getNotifyService() {
|
||||
return notifyService;
|
||||
}
|
||||
|
||||
public void setNotifyService(NotifyServiceEntity notifyService) {
|
||||
this.notifyService = notifyService;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see <a href="https://notify.coar-repositories.org/patterns">coar documentation</a>
|
||||
* @return pattern of the inbound notification
|
||||
*/
|
||||
public String getPattern() {
|
||||
return pattern;
|
||||
}
|
||||
|
||||
public void setPattern(String pattern) {
|
||||
this.pattern = pattern;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the condition checked for automatic evaluation
|
||||
*/
|
||||
public String getConstraint() {
|
||||
return constraint;
|
||||
}
|
||||
|
||||
public void setConstraint(String constraint) {
|
||||
this.constraint = constraint;
|
||||
}
|
||||
|
||||
/**
|
||||
* when true - the notification is automatically when constraints are respected.
|
||||
* @return the automatic flag
|
||||
*/
|
||||
public boolean isAutomatic() {
|
||||
return automatic;
|
||||
}
|
||||
|
||||
public void setAutomatic(boolean automatic) {
|
||||
this.automatic = automatic;
|
||||
}
|
||||
}
|
@@ -0,0 +1,31 @@
|
||||
/**
|
||||
* 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.ldn.action;
|
||||
|
||||
import org.dspace.app.ldn.model.Notification;
|
||||
import org.dspace.content.Item;
|
||||
import org.dspace.core.Context;
|
||||
|
||||
/**
|
||||
* An action that is run after a notification has been processed.
|
||||
*/
|
||||
public interface LDNAction {
|
||||
|
||||
/**
|
||||
* Execute action for provided notification and item corresponding to the
|
||||
* notification context.
|
||||
*
|
||||
*@param context the context
|
||||
* @param notification the processed notification to perform action against
|
||||
* @param item the item corresponding to the notification context
|
||||
* @return ActionStatus the resulting status of the action
|
||||
* @throws Exception general exception that can be thrown while executing action
|
||||
*/
|
||||
public LDNActionStatus execute(Context context, Notification notification, Item item) throws Exception;
|
||||
|
||||
}
|
@@ -5,8 +5,11 @@
|
||||
*
|
||||
* http://www.dspace.org/license/
|
||||
*/
|
||||
package org.dspace.rest.filter;
|
||||
package org.dspace.app.ldn.action;
|
||||
|
||||
public interface ItemFilterList {
|
||||
public ItemFilterTest[] getFilters();
|
||||
/**
|
||||
* Resulting status of an execution of an action.
|
||||
*/
|
||||
public enum LDNActionStatus {
|
||||
CONTINUE, ABORT;
|
||||
}
|
@@ -0,0 +1,108 @@
|
||||
/**
|
||||
* 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.ldn.action;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.sql.SQLException;
|
||||
import java.util.Date;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.dspace.app.ldn.NotifyServiceEntity;
|
||||
import org.dspace.app.ldn.model.Notification;
|
||||
import org.dspace.app.ldn.service.LDNMessageService;
|
||||
import org.dspace.content.Item;
|
||||
import org.dspace.content.QAEvent;
|
||||
import org.dspace.content.service.ItemService;
|
||||
import org.dspace.core.Context;
|
||||
import org.dspace.handle.service.HandleService;
|
||||
import org.dspace.qaevent.service.QAEventService;
|
||||
import org.dspace.qaevent.service.dto.NotifyMessageDTO;
|
||||
import org.dspace.services.ConfigurationService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
|
||||
/**
|
||||
* Implementation for LDN Correction Action. It creates a QA Event according to the LDN Message received *
|
||||
* @author Francesco Bacchelli (francesco.bacchelli at 4science.it)
|
||||
*
|
||||
*/
|
||||
public class LDNCorrectionAction implements LDNAction {
|
||||
|
||||
private static final Logger log = LogManager.getLogger(LDNEmailAction.class);
|
||||
|
||||
private String qaEventTopic;
|
||||
|
||||
@Autowired
|
||||
private ConfigurationService configurationService;
|
||||
@Autowired
|
||||
protected ItemService itemService;
|
||||
@Autowired
|
||||
private QAEventService qaEventService;
|
||||
@Autowired
|
||||
private LDNMessageService ldnMessageService;
|
||||
@Autowired
|
||||
private HandleService handleService;
|
||||
|
||||
@Override
|
||||
public LDNActionStatus execute(Context context, Notification notification, Item item) throws Exception {
|
||||
LDNActionStatus result = LDNActionStatus.ABORT;
|
||||
String itemName = itemService.getName(item);
|
||||
QAEvent qaEvent = null;
|
||||
if (notification.getObject() != null) {
|
||||
String citeAs = notification.getObject().getIetfCiteAs();
|
||||
if (citeAs == null || citeAs.isEmpty()) {
|
||||
citeAs = notification.getObject().getId();
|
||||
}
|
||||
NotifyMessageDTO message = new NotifyMessageDTO();
|
||||
message.setHref(citeAs);
|
||||
message.setRelationship(notification.getObject().getAsRelationship());
|
||||
if (notification.getOrigin() != null) {
|
||||
message.setServiceId(notification.getOrigin().getId());
|
||||
message.setServiceName(notification.getOrigin().getInbox());
|
||||
}
|
||||
BigDecimal score = getScore(context, notification);
|
||||
double doubleScoreValue = score != null ? score.doubleValue() : 0d;
|
||||
ObjectMapper mapper = new ObjectMapper();
|
||||
qaEvent = new QAEvent(QAEvent.COAR_NOTIFY_SOURCE,
|
||||
handleService.findHandle(context, item), item.getID().toString(), itemName,
|
||||
this.getQaEventTopic(), doubleScoreValue,
|
||||
mapper.writeValueAsString(message),
|
||||
new Date());
|
||||
qaEventService.store(context, qaEvent);
|
||||
result = LDNActionStatus.CONTINUE;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private BigDecimal getScore(Context context, Notification notification) throws SQLException {
|
||||
|
||||
if (notification.getOrigin() == null) {
|
||||
return BigDecimal.ZERO;
|
||||
}
|
||||
|
||||
NotifyServiceEntity service = ldnMessageService.findNotifyService(context, notification.getOrigin());
|
||||
|
||||
if (service == null) {
|
||||
return BigDecimal.ZERO;
|
||||
}
|
||||
|
||||
return service.getScore();
|
||||
}
|
||||
|
||||
public String getQaEventTopic() {
|
||||
return qaEventTopic;
|
||||
}
|
||||
|
||||
public void setQaEventTopic(String qaEventTopic) {
|
||||
this.qaEventTopic = qaEventTopic;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,155 @@
|
||||
/**
|
||||
* 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.ldn.action;
|
||||
|
||||
import static java.lang.String.format;
|
||||
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Arrays;
|
||||
import java.util.Calendar;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.dspace.app.ldn.model.Notification;
|
||||
import org.dspace.content.Item;
|
||||
import org.dspace.core.Context;
|
||||
import org.dspace.core.Email;
|
||||
import org.dspace.core.I18nUtil;
|
||||
import org.dspace.services.ConfigurationService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
/**
|
||||
* Action to send email to recipients provided in actionSendFilter. The email
|
||||
* body will be result of templating actionSendFilter.
|
||||
*/
|
||||
public class LDNEmailAction implements LDNAction {
|
||||
|
||||
private static final Logger log = LogManager.getLogger(LDNEmailAction.class);
|
||||
|
||||
private final static String DATE_PATTERN = "dd-MM-yyyy HH:mm:ss";
|
||||
|
||||
@Autowired
|
||||
private ConfigurationService configurationService;
|
||||
|
||||
/*
|
||||
* Supported for actionSendFilter are:
|
||||
* - <single email>
|
||||
* - GROUP:<group_name>
|
||||
* - SUBMITTER
|
||||
*/
|
||||
private String actionSendFilter;
|
||||
|
||||
// The file name for the requested email
|
||||
private String actionSendEmailTextFile;
|
||||
|
||||
/**
|
||||
* Execute sending an email.
|
||||
*
|
||||
* Template context parameters:
|
||||
*
|
||||
* {0} Service Name
|
||||
* {1} Item Name
|
||||
* {2} Service URL
|
||||
* {3} Item URL
|
||||
* {4} Submitter's Name
|
||||
* {5} Date of the received LDN notification
|
||||
* {6} LDN notification
|
||||
* {7} Item
|
||||
*
|
||||
* @param notification
|
||||
* @param item
|
||||
* @return ActionStatus
|
||||
* @throws Exception
|
||||
*/
|
||||
@Override
|
||||
public LDNActionStatus execute(Context context, Notification notification, Item item) throws Exception {
|
||||
try {
|
||||
Locale supportedLocale = I18nUtil.getEPersonLocale(context.getCurrentUser());
|
||||
Email email = Email.getEmail(I18nUtil.getEmailFilename(supportedLocale, actionSendEmailTextFile));
|
||||
|
||||
// Setting recipients email
|
||||
for (String recipient : retrieveRecipientsEmail(item)) {
|
||||
email.addRecipient(recipient);
|
||||
}
|
||||
|
||||
String date = new SimpleDateFormat(DATE_PATTERN).format(Calendar.getInstance().getTime());
|
||||
|
||||
email.addArgument(notification.getActor().getName());
|
||||
email.addArgument(item.getName());
|
||||
email.addArgument(notification.getActor().getId());
|
||||
email.addArgument(notification.getContext() != null ?
|
||||
notification.getContext().getId() : notification.getObject().getId());
|
||||
email.addArgument(item.getSubmitter().getFullName());
|
||||
email.addArgument(date);
|
||||
email.addArgument(notification);
|
||||
email.addArgument(item);
|
||||
|
||||
email.send();
|
||||
} catch (Exception e) {
|
||||
log.error("An Error Occurred while sending a notification email", e);
|
||||
}
|
||||
|
||||
return LDNActionStatus.CONTINUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return String
|
||||
*/
|
||||
public String getActionSendFilter() {
|
||||
return actionSendFilter;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param actionSendFilter
|
||||
*/
|
||||
public void setActionSendFilter(String actionSendFilter) {
|
||||
this.actionSendFilter = actionSendFilter;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return String
|
||||
*/
|
||||
public String getActionSendEmailTextFile() {
|
||||
return actionSendEmailTextFile;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param actionSendEmailTextFile
|
||||
*/
|
||||
public void setActionSendEmailTextFile(String actionSendEmailTextFile) {
|
||||
this.actionSendEmailTextFile = actionSendEmailTextFile;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses actionSendFilter for reserved tokens and returns list of email
|
||||
* recipients.
|
||||
*
|
||||
* @param item the item which to get submitter email
|
||||
* @return List<String> list of email recipients
|
||||
*/
|
||||
private List<String> retrieveRecipientsEmail(Item item) {
|
||||
List<String> recipients = new LinkedList<String>();
|
||||
|
||||
if (actionSendFilter.startsWith("SUBMITTER")) {
|
||||
recipients.add(item.getSubmitter().getEmail());
|
||||
} else if (actionSendFilter.startsWith("GROUP:")) {
|
||||
String groupName = actionSendFilter.replace("GROUP:", "");
|
||||
String property = format("email.%s.list", groupName);
|
||||
String[] groupEmails = configurationService.getArrayProperty(property);
|
||||
recipients = Arrays.asList(groupEmails);
|
||||
} else {
|
||||
recipients.add(actionSendFilter);
|
||||
}
|
||||
|
||||
return recipients;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,110 @@
|
||||
/**
|
||||
* 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.ldn.action;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.sql.SQLException;
|
||||
import java.util.Date;
|
||||
import java.util.Set;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.dspace.app.ldn.NotifyServiceEntity;
|
||||
import org.dspace.app.ldn.model.Notification;
|
||||
import org.dspace.app.ldn.service.LDNMessageService;
|
||||
import org.dspace.content.Item;
|
||||
import org.dspace.content.QAEvent;
|
||||
import org.dspace.content.service.ItemService;
|
||||
import org.dspace.core.Context;
|
||||
import org.dspace.handle.service.HandleService;
|
||||
import org.dspace.qaevent.service.QAEventService;
|
||||
import org.dspace.qaevent.service.dto.NotifyMessageDTO;
|
||||
import org.dspace.services.ConfigurationService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
|
||||
/**
|
||||
* Implementation for LDN Correction Action. It creates a QA Event according to the LDN Message received *
|
||||
* @author Francesco Bacchelli (francesco.bacchelli at 4science.it)
|
||||
*
|
||||
*/
|
||||
public class LDNRelationCorrectionAction implements LDNAction {
|
||||
|
||||
private static final Logger log = LogManager.getLogger(LDNEmailAction.class);
|
||||
|
||||
private String qaEventTopic;
|
||||
|
||||
@Autowired
|
||||
private ConfigurationService configurationService;
|
||||
@Autowired
|
||||
protected ItemService itemService;
|
||||
@Autowired
|
||||
private QAEventService qaEventService;
|
||||
@Autowired
|
||||
private LDNMessageService ldnMessageService;
|
||||
@Autowired
|
||||
private HandleService handleService;
|
||||
|
||||
@Override
|
||||
public LDNActionStatus execute(Context context, Notification notification, Item item) throws Exception {
|
||||
LDNActionStatus result = LDNActionStatus.ABORT;
|
||||
String itemName = itemService.getName(item);
|
||||
QAEvent qaEvent = null;
|
||||
if (notification.getObject() != null) {
|
||||
NotifyMessageDTO message = new NotifyMessageDTO();
|
||||
if (notification.getType().containsAll(Set.of("Announce",
|
||||
"coar-notify:RelationshipAction"))) {
|
||||
message.setHref(notification.getObject().getAsSubject());
|
||||
} else {
|
||||
message.setHref(notification.getObject().getAsObject());
|
||||
}
|
||||
message.setRelationship(notification.getObject().getAsRelationship());
|
||||
if (notification.getOrigin() != null) {
|
||||
message.setServiceId(notification.getOrigin().getId());
|
||||
message.setServiceName(notification.getOrigin().getInbox());
|
||||
}
|
||||
BigDecimal score = getScore(context, notification);
|
||||
double doubleScoreValue = score != null ? score.doubleValue() : 0d;
|
||||
ObjectMapper mapper = new ObjectMapper();
|
||||
qaEvent = new QAEvent(QAEvent.COAR_NOTIFY_SOURCE,
|
||||
handleService.findHandle(context, item), item.getID().toString(), itemName,
|
||||
this.getQaEventTopic(), doubleScoreValue,
|
||||
mapper.writeValueAsString(message),
|
||||
new Date());
|
||||
qaEventService.store(context, qaEvent);
|
||||
result = LDNActionStatus.CONTINUE;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private BigDecimal getScore(Context context, Notification notification) throws SQLException {
|
||||
|
||||
if (notification.getOrigin() == null) {
|
||||
return BigDecimal.ZERO;
|
||||
}
|
||||
|
||||
NotifyServiceEntity service = ldnMessageService.findNotifyService(context, notification.getOrigin());
|
||||
|
||||
if (service == null) {
|
||||
return BigDecimal.ZERO;
|
||||
}
|
||||
|
||||
return service.getScore();
|
||||
}
|
||||
|
||||
public String getQaEventTopic() {
|
||||
return qaEventTopic;
|
||||
}
|
||||
|
||||
public void setQaEventTopic(String qaEventTopic) {
|
||||
this.qaEventTopic = qaEventTopic;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,118 @@
|
||||
/**
|
||||
* 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.ldn.action;
|
||||
|
||||
import java.net.URI;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import org.apache.http.Header;
|
||||
import org.apache.http.HttpException;
|
||||
import org.apache.http.HttpHeaders;
|
||||
import org.apache.http.HttpStatus;
|
||||
import org.apache.http.client.methods.CloseableHttpResponse;
|
||||
import org.apache.http.client.methods.HttpPost;
|
||||
import org.apache.http.entity.StringEntity;
|
||||
import org.apache.http.impl.client.CloseableHttpClient;
|
||||
import org.apache.http.impl.client.HttpClientBuilder;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.dspace.app.ldn.model.Notification;
|
||||
import org.dspace.content.Item;
|
||||
import org.dspace.core.Context;
|
||||
|
||||
/**
|
||||
* Action to send LDN Message
|
||||
*
|
||||
* @author Mohamed Eskander (mohamed.eskander at 4science.com)
|
||||
*/
|
||||
public class SendLDNMessageAction implements LDNAction {
|
||||
|
||||
private static final Logger log = LogManager.getLogger(SendLDNMessageAction.class);
|
||||
|
||||
private CloseableHttpClient client = null;
|
||||
|
||||
public SendLDNMessageAction() {
|
||||
HttpClientBuilder builder = HttpClientBuilder.create();
|
||||
client = builder
|
||||
.disableAutomaticRetries()
|
||||
.setMaxConnTotal(5)
|
||||
.build();
|
||||
}
|
||||
|
||||
public SendLDNMessageAction(CloseableHttpClient client) {
|
||||
this();
|
||||
if (client != null) {
|
||||
this.client = client;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public LDNActionStatus execute(Context context, Notification notification, Item item) throws Exception {
|
||||
//TODO authorization with Bearer token should be supported.
|
||||
|
||||
String url = notification.getTarget().getInbox();
|
||||
|
||||
HttpPost httpPost = new HttpPost(url);
|
||||
httpPost.addHeader("Content-Type", "application/ld+json");
|
||||
ObjectMapper mapper = new ObjectMapper();
|
||||
httpPost.setEntity(new StringEntity(mapper.writeValueAsString(notification), "UTF-8"));
|
||||
|
||||
LDNActionStatus result = LDNActionStatus.ABORT;
|
||||
// NOTE: Github believes there is a "Potential server-side request forgery due to a user-provided value"
|
||||
// This is a false positive because the LDN Service URL is configured by the user from DSpace.
|
||||
// See the frontend configuration at [dspace.ui.url]/admin/ldn/services
|
||||
try (
|
||||
CloseableHttpResponse response = client.execute(httpPost);
|
||||
) {
|
||||
if (isSuccessful(response.getStatusLine().getStatusCode())) {
|
||||
result = LDNActionStatus.CONTINUE;
|
||||
} else if (isRedirect(response.getStatusLine().getStatusCode())) {
|
||||
result = handleRedirect(response, httpPost);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error(e);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private boolean isSuccessful(int statusCode) {
|
||||
return statusCode == HttpStatus.SC_ACCEPTED ||
|
||||
statusCode == HttpStatus.SC_CREATED;
|
||||
}
|
||||
|
||||
private boolean isRedirect(int statusCode) {
|
||||
//org.apache.http.HttpStatus has no enum value for 308!
|
||||
return statusCode == (HttpStatus.SC_TEMPORARY_REDIRECT + 1) ||
|
||||
statusCode == HttpStatus.SC_TEMPORARY_REDIRECT;
|
||||
}
|
||||
|
||||
private LDNActionStatus handleRedirect(CloseableHttpResponse oldresponse,
|
||||
HttpPost request) throws HttpException {
|
||||
Header[] urls = oldresponse.getHeaders(HttpHeaders.LOCATION);
|
||||
String url = urls.length > 0 && urls[0] != null ? urls[0].getValue() : null;
|
||||
if (url == null) {
|
||||
throw new HttpException("Error following redirect, unable to reach"
|
||||
+ " the correct url.");
|
||||
}
|
||||
LDNActionStatus result = LDNActionStatus.ABORT;
|
||||
try {
|
||||
request.setURI(new URI(url));
|
||||
try (
|
||||
CloseableHttpResponse response = client.execute(request);
|
||||
) {
|
||||
if (isSuccessful(response.getStatusLine().getStatusCode())) {
|
||||
return LDNActionStatus.CONTINUE;
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("Error following redirect:", e);
|
||||
}
|
||||
|
||||
return LDNActionStatus.ABORT;
|
||||
}
|
||||
}
|
@@ -0,0 +1,76 @@
|
||||
/**
|
||||
* The contents of this file are subject to the license and copyright
|
||||
* detailed in the LICENSE and NOTICE files at the root of the source
|
||||
* tree and available online at
|
||||
*
|
||||
* http://www.dspace.org/license/
|
||||
*/
|
||||
package org.dspace.app.ldn.dao;
|
||||
|
||||
import java.sql.SQLException;
|
||||
import java.util.List;
|
||||
|
||||
import org.dspace.app.ldn.LDNMessageEntity;
|
||||
import org.dspace.content.Item;
|
||||
import org.dspace.core.Context;
|
||||
import org.dspace.core.GenericDAO;
|
||||
|
||||
/**
|
||||
* Database Access Object interface class for the LDNMessage object.
|
||||
*
|
||||
* The implementation of this class is responsible for all database calls for
|
||||
* the LDNMessage object and is autowired by spring
|
||||
*
|
||||
* @author Mohamed Eskander (mohamed.eskander at 4science.com)
|
||||
*/
|
||||
public interface LDNMessageDao extends GenericDAO<LDNMessageEntity> {
|
||||
|
||||
/**
|
||||
* load the oldest ldn messages considering their {@link org.dspace.app.ldn.LDNMessageEntity#queueLastStartTime}
|
||||
* @param context
|
||||
* @param max_attempts consider ldn_message entity with queue_attempts <= max_attempts
|
||||
* @return ldn message entities to be routed
|
||||
* @throws SQLException
|
||||
*/
|
||||
public List<LDNMessageEntity> findOldestMessageToProcess(Context context, int max_attempts) throws SQLException;
|
||||
|
||||
/**
|
||||
* find ldn message entties in processing status and already timed out.
|
||||
* @param context
|
||||
* @param max_attempts consider ldn_message entity with queue_attempts <= max_attempts
|
||||
* @return ldn message entities
|
||||
* @throws SQLException
|
||||
*/
|
||||
public List<LDNMessageEntity> findProcessingTimedoutMessages(Context context, int max_attempts) throws SQLException;
|
||||
|
||||
/**
|
||||
* find all ldn messages related to an item
|
||||
* @param context
|
||||
* @param item item related to the returned ldn messages
|
||||
* @param activities involves only this specific group of activities
|
||||
* @return all ldn messages related to the given item
|
||||
* @throws SQLException
|
||||
*/
|
||||
public List<LDNMessageEntity> findAllMessagesByItem(
|
||||
Context context, Item item, String... activities) throws SQLException;
|
||||
|
||||
/**
|
||||
* find all ldn messages related to an item and to a specific ldn message
|
||||
* @param context
|
||||
* @param msg the referring ldn message
|
||||
* @param item the referring repository item
|
||||
* @param relatedTypes filter for @see org.dspace.app.ldn.LDNMessageEntity#activityStreamType
|
||||
* @return all related ldn messages
|
||||
* @throws SQLException
|
||||
*/
|
||||
public List<LDNMessageEntity> findAllRelatedMessagesByItem(
|
||||
Context context, LDNMessageEntity msg, Item item, String... relatedTypes) throws SQLException;
|
||||
|
||||
/**
|
||||
*
|
||||
* @param context
|
||||
* @return the list of messages in need to be reprocessed - with queue_status as QUEUE_STATUS_QUEUED_FOR_RETRY
|
||||
* @throws SQLException
|
||||
*/
|
||||
public List<LDNMessageEntity> findMessagesToBeReprocessed(Context context) throws SQLException;
|
||||
}
|
@@ -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.ldn.dao;
|
||||
|
||||
import java.sql.SQLException;
|
||||
import java.util.List;
|
||||
|
||||
import org.dspace.app.ldn.NotifyPatternToTrigger;
|
||||
import org.dspace.content.Item;
|
||||
import org.dspace.core.Context;
|
||||
import org.dspace.core.GenericDAO;
|
||||
|
||||
/**
|
||||
* This is the Data Access Object for the {@link NotifyPatternToTrigger} object
|
||||
*
|
||||
* @author Mohamed Eskander (mohamed.eskander at 4science.com)
|
||||
*/
|
||||
public interface NotifyPatternToTriggerDao extends GenericDAO<NotifyPatternToTrigger> {
|
||||
|
||||
/**
|
||||
* find the NotifyPatternToTrigger matched with the provided item
|
||||
*
|
||||
* @param context the context
|
||||
* @param item the item
|
||||
* @return the NotifyPatternToTrigger matched the provided item
|
||||
* @throws SQLException if database error
|
||||
*/
|
||||
public List<NotifyPatternToTrigger> findByItem(Context context, Item item) throws SQLException;
|
||||
|
||||
/**
|
||||
* find the NotifyPatternToTrigger matched with the provided
|
||||
* item and pattern
|
||||
*
|
||||
* @param context the context
|
||||
* @param item the item
|
||||
* @param pattern the pattern
|
||||
* @return the NotifyPatternToTrigger matched the provided
|
||||
* item and pattern
|
||||
* @throws SQLException if database error
|
||||
*/
|
||||
public List<NotifyPatternToTrigger> findByItemAndPattern(Context context, Item item, String pattern)
|
||||
throws SQLException;
|
||||
|
||||
}
|
@@ -0,0 +1,45 @@
|
||||
/**
|
||||
* 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.ldn.dao;
|
||||
|
||||
import java.sql.SQLException;
|
||||
import java.util.List;
|
||||
|
||||
import org.dspace.app.ldn.NotifyServiceEntity;
|
||||
import org.dspace.core.Context;
|
||||
import org.dspace.core.GenericDAO;
|
||||
|
||||
/**
|
||||
* This is the Data Access Object for the {@link NotifyServiceEntity} object
|
||||
*
|
||||
* @author Mohamed Eskander (mohamed.eskander at 4science.com)
|
||||
*/
|
||||
public interface NotifyServiceDao extends GenericDAO<NotifyServiceEntity> {
|
||||
/**
|
||||
* find the NotifyServiceEntity matched with the provided ldnUrl
|
||||
*
|
||||
* @param context the context
|
||||
* @param ldnUrl the ldnUrl
|
||||
* @return the NotifyServiceEntity matched the provided ldnUrl
|
||||
* @throws SQLException if database error
|
||||
*/
|
||||
public NotifyServiceEntity findByLdnUrl(Context context, String ldnUrl) throws SQLException;
|
||||
|
||||
/**
|
||||
* find all NotifyServiceEntity matched the provided inbound pattern
|
||||
* from the related notifyServiceInboundPatterns
|
||||
* also with 'automatic' equals to false
|
||||
*
|
||||
* @param context the context
|
||||
* @param pattern the ldnUrl
|
||||
* @return all NotifyServiceEntity matched the provided pattern
|
||||
* @throws SQLException if database error
|
||||
*/
|
||||
public List<NotifyServiceEntity> findManualServicesByInboundPattern(Context context, String pattern)
|
||||
throws SQLException;
|
||||
}
|
@@ -0,0 +1,47 @@
|
||||
/**
|
||||
* The contents of this file are subject to the license and copyright
|
||||
* detailed in the LICENSE and NOTICE files at the root of the source
|
||||
* tree and available online at
|
||||
*
|
||||
* http://www.dspace.org/license/
|
||||
*/
|
||||
package org.dspace.app.ldn.dao;
|
||||
|
||||
import java.sql.SQLException;
|
||||
import java.util.List;
|
||||
|
||||
import org.dspace.app.ldn.NotifyServiceEntity;
|
||||
import org.dspace.app.ldn.NotifyServiceInboundPattern;
|
||||
import org.dspace.core.Context;
|
||||
import org.dspace.core.GenericDAO;
|
||||
|
||||
/**
|
||||
* This is the Data Access Object for the {@link NotifyServiceInboundPattern} object
|
||||
*
|
||||
* @author Mohamed Eskander (mohamed.eskander at 4science.com)
|
||||
*/
|
||||
public interface NotifyServiceInboundPatternDao extends GenericDAO<NotifyServiceInboundPattern> {
|
||||
|
||||
/**
|
||||
* find all notifyServiceInboundPatterns matched with
|
||||
* the provided notifyServiceEntity and pattern
|
||||
*
|
||||
* @param context the context
|
||||
* @param notifyServiceEntity the notifyServiceEntity
|
||||
* @param pattern the pattern
|
||||
* @return all notifyServiceInboundPatterns matched with
|
||||
* the provided notifyServiceEntity and pattern
|
||||
* @throws SQLException if database error
|
||||
*/
|
||||
public NotifyServiceInboundPattern findByServiceAndPattern(Context context,
|
||||
NotifyServiceEntity notifyServiceEntity,
|
||||
String pattern) throws SQLException;
|
||||
/**
|
||||
* find all automatic notifyServiceInboundPatterns
|
||||
*
|
||||
* @param context the context
|
||||
* @return all automatic notifyServiceInboundPatterns
|
||||
* @throws SQLException if database error
|
||||
*/
|
||||
List<NotifyServiceInboundPattern> findAutomaticPatterns(Context context) throws SQLException;
|
||||
}
|
@@ -0,0 +1,169 @@
|
||||
/**
|
||||
* 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.ldn.dao.impl;
|
||||
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
import jakarta.persistence.criteria.CriteriaBuilder;
|
||||
import jakarta.persistence.criteria.CriteriaQuery;
|
||||
import jakarta.persistence.criteria.Order;
|
||||
import jakarta.persistence.criteria.Predicate;
|
||||
import jakarta.persistence.criteria.Root;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.dspace.app.ldn.LDNMessageEntity;
|
||||
import org.dspace.app.ldn.LDNMessageEntity_;
|
||||
import org.dspace.app.ldn.dao.LDNMessageDao;
|
||||
import org.dspace.content.Item;
|
||||
import org.dspace.core.AbstractHibernateDAO;
|
||||
import org.dspace.core.Context;
|
||||
|
||||
/**
|
||||
* Hibernate implementation of the Database Access Object interface class for
|
||||
* the LDNMessage object. This class is responsible for all database calls for
|
||||
* the LDNMessage object and is autowired by spring
|
||||
*
|
||||
* @author Mohamed Eskander (mohamed.eskander at 4science.com)
|
||||
*/
|
||||
public class LDNMessageDaoImpl extends AbstractHibernateDAO<LDNMessageEntity> implements LDNMessageDao {
|
||||
|
||||
private static final Logger log = org.apache.logging.log4j.LogManager.getLogger(LDNMessageDaoImpl.class);
|
||||
|
||||
@Override
|
||||
public List<LDNMessageEntity> findOldestMessageToProcess(Context context, int max_attempts) throws SQLException {
|
||||
CriteriaBuilder criteriaBuilder = getCriteriaBuilder(context);
|
||||
CriteriaQuery<LDNMessageEntity> criteriaQuery = getCriteriaQuery(criteriaBuilder, LDNMessageEntity.class);
|
||||
Root<LDNMessageEntity> root = criteriaQuery.from(LDNMessageEntity.class);
|
||||
criteriaQuery.select(root);
|
||||
List<Predicate> andPredicates = new ArrayList<>(3);
|
||||
andPredicates
|
||||
.add(criteriaBuilder.equal(root.get(LDNMessageEntity_.queueStatus), LDNMessageEntity.QUEUE_STATUS_QUEUED));
|
||||
andPredicates.add(criteriaBuilder.lessThan(root.get(LDNMessageEntity_.queueAttempts), max_attempts));
|
||||
andPredicates.add(criteriaBuilder.lessThan(root.get(LDNMessageEntity_.queueTimeout), new Date()));
|
||||
criteriaQuery.where(criteriaBuilder.and(andPredicates.toArray(new Predicate[] {})));
|
||||
List<Order> orderList = new LinkedList<>();
|
||||
orderList.add(criteriaBuilder.desc(root.get(LDNMessageEntity_.queueAttempts)));
|
||||
orderList.add(criteriaBuilder.asc(root.get(LDNMessageEntity_.queueLastStartTime)));
|
||||
criteriaQuery.orderBy(orderList);
|
||||
List<LDNMessageEntity> result = list(context, criteriaQuery, false, LDNMessageEntity.class, -1, -1);
|
||||
if (result == null || result.isEmpty()) {
|
||||
log.debug("No LDN messages found to be processed");
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<LDNMessageEntity> findMessagesToBeReprocessed(Context context) throws SQLException {
|
||||
// looking for LDN Messages to be reprocessed message
|
||||
CriteriaBuilder criteriaBuilder = getCriteriaBuilder(context);
|
||||
CriteriaQuery<LDNMessageEntity> criteriaQuery = getCriteriaQuery(criteriaBuilder, LDNMessageEntity.class);
|
||||
Root<LDNMessageEntity> root = criteriaQuery.from(LDNMessageEntity.class);
|
||||
criteriaQuery.select(root);
|
||||
List<Predicate> andPredicates = new ArrayList<>(1);
|
||||
andPredicates
|
||||
.add(criteriaBuilder.equal(root.get(LDNMessageEntity_.queueStatus),
|
||||
LDNMessageEntity.QUEUE_STATUS_QUEUED_FOR_RETRY));
|
||||
criteriaQuery.where(criteriaBuilder.and(andPredicates.toArray(new Predicate[] {})));
|
||||
List<Order> orderList = new LinkedList<>();
|
||||
orderList.add(criteriaBuilder.desc(root.get(LDNMessageEntity_.queueAttempts)));
|
||||
orderList.add(criteriaBuilder.asc(root.get(LDNMessageEntity_.queueLastStartTime)));
|
||||
criteriaQuery.orderBy(orderList);
|
||||
List<LDNMessageEntity> result = list(context, criteriaQuery, false, LDNMessageEntity.class, -1, -1);
|
||||
if (result == null || result.isEmpty()) {
|
||||
log.debug("No LDN messages found to be processed");
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<LDNMessageEntity> findProcessingTimedoutMessages(Context context, int max_attempts)
|
||||
throws SQLException {
|
||||
CriteriaBuilder criteriaBuilder = getCriteriaBuilder(context);
|
||||
CriteriaQuery<LDNMessageEntity> criteriaQuery = getCriteriaQuery(criteriaBuilder, LDNMessageEntity.class);
|
||||
Root<LDNMessageEntity> root = criteriaQuery.from(LDNMessageEntity.class);
|
||||
criteriaQuery.select(root);
|
||||
List<Predicate> andPredicates = new ArrayList<>(3);
|
||||
andPredicates.add(
|
||||
criteriaBuilder.equal(root.get(LDNMessageEntity_.queueStatus), LDNMessageEntity.QUEUE_STATUS_PROCESSING));
|
||||
andPredicates.add(criteriaBuilder.lessThanOrEqualTo(root.get(LDNMessageEntity_.queueAttempts), max_attempts));
|
||||
andPredicates.add(criteriaBuilder.lessThan(root.get(LDNMessageEntity_.queueTimeout), new Date()));
|
||||
criteriaQuery.where(criteriaBuilder.and(andPredicates.toArray(new Predicate[] {})));
|
||||
List<Order> orderList = new LinkedList<>();
|
||||
orderList.add(criteriaBuilder.desc(root.get(LDNMessageEntity_.queueAttempts)));
|
||||
orderList.add(criteriaBuilder.asc(root.get(LDNMessageEntity_.queueLastStartTime)));
|
||||
criteriaQuery.orderBy(orderList);
|
||||
List<LDNMessageEntity> result = list(context, criteriaQuery, false, LDNMessageEntity.class, -1, -1);
|
||||
if (result == null || result.isEmpty()) {
|
||||
log.debug("No LDN messages found to be processed");
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<LDNMessageEntity> findAllRelatedMessagesByItem(
|
||||
Context context, LDNMessageEntity msg, Item item, String... relatedTypes) throws SQLException {
|
||||
CriteriaBuilder criteriaBuilder = getCriteriaBuilder(context);
|
||||
CriteriaQuery<LDNMessageEntity> criteriaQuery = getCriteriaQuery(criteriaBuilder, LDNMessageEntity.class);
|
||||
Root<LDNMessageEntity> root = criteriaQuery.from(LDNMessageEntity.class);
|
||||
criteriaQuery.select(root);
|
||||
List<Predicate> andPredicates = new ArrayList<>();
|
||||
Predicate relatedtypePredicate = null;
|
||||
andPredicates.add(
|
||||
criteriaBuilder.equal(root.get(LDNMessageEntity_.queueStatus), LDNMessageEntity.QUEUE_STATUS_PROCESSED));
|
||||
andPredicates.add(
|
||||
criteriaBuilder.isNull(root.get(LDNMessageEntity_.target)));
|
||||
andPredicates.add(
|
||||
criteriaBuilder.equal(root.get(LDNMessageEntity_.inReplyTo), msg));
|
||||
if (relatedTypes != null && relatedTypes.length > 0) {
|
||||
relatedtypePredicate = root.get(LDNMessageEntity_.activityStreamType).in(relatedTypes);
|
||||
andPredicates.add(relatedtypePredicate);
|
||||
}
|
||||
criteriaQuery.where(criteriaBuilder.and(andPredicates.toArray(new Predicate[] {})));
|
||||
List<Order> orderList = new LinkedList<>();
|
||||
orderList.add(criteriaBuilder.asc(root.get(LDNMessageEntity_.queueLastStartTime)));
|
||||
orderList.add(criteriaBuilder.desc(root.get(LDNMessageEntity_.queueAttempts)));
|
||||
criteriaQuery.orderBy(orderList);
|
||||
List<LDNMessageEntity> result = list(context, criteriaQuery, false, LDNMessageEntity.class, -1, -1);
|
||||
if (result == null || result.isEmpty()) {
|
||||
log.debug("No LDN messages ACK found to be processed");
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<LDNMessageEntity> findAllMessagesByItem(
|
||||
Context context, Item item, String... activities) throws SQLException {
|
||||
CriteriaBuilder criteriaBuilder = getCriteriaBuilder(context);
|
||||
CriteriaQuery<LDNMessageEntity> criteriaQuery = getCriteriaQuery(criteriaBuilder, LDNMessageEntity.class);
|
||||
Root<LDNMessageEntity> root = criteriaQuery.from(LDNMessageEntity.class);
|
||||
criteriaQuery.select(root);
|
||||
List<Predicate> andPredicates = new ArrayList<>();
|
||||
Predicate activityPredicate = null;
|
||||
andPredicates.add(
|
||||
criteriaBuilder.equal(root.get(LDNMessageEntity_.queueStatus), LDNMessageEntity.QUEUE_STATUS_PROCESSED));
|
||||
andPredicates.add(
|
||||
criteriaBuilder.equal(root.get(LDNMessageEntity_.object), item));
|
||||
if (activities != null && activities.length > 0) {
|
||||
activityPredicate = root.get(LDNMessageEntity_.activityStreamType).in(activities);
|
||||
andPredicates.add(activityPredicate);
|
||||
}
|
||||
criteriaQuery.where(criteriaBuilder.and(andPredicates.toArray(new Predicate[] {})));
|
||||
List<Order> orderList = new LinkedList<>();
|
||||
orderList.add(criteriaBuilder.asc(root.get(LDNMessageEntity_.queueLastStartTime)));
|
||||
orderList.add(criteriaBuilder.desc(root.get(LDNMessageEntity_.queueAttempts)));
|
||||
criteriaQuery.orderBy(orderList);
|
||||
List<LDNMessageEntity> result = list(context, criteriaQuery, false, LDNMessageEntity.class, -1, -1);
|
||||
if (result == null || result.isEmpty()) {
|
||||
log.debug("No LDN messages found");
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
@@ -0,0 +1,58 @@
|
||||
/**
|
||||
* 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.ldn.dao.impl;
|
||||
|
||||
import java.sql.SQLException;
|
||||
import java.util.List;
|
||||
|
||||
import jakarta.persistence.criteria.CriteriaBuilder;
|
||||
import jakarta.persistence.criteria.CriteriaQuery;
|
||||
import jakarta.persistence.criteria.Root;
|
||||
import org.dspace.app.ldn.NotifyPatternToTrigger;
|
||||
import org.dspace.app.ldn.NotifyPatternToTrigger_;
|
||||
import org.dspace.app.ldn.dao.NotifyPatternToTriggerDao;
|
||||
import org.dspace.content.Item;
|
||||
import org.dspace.core.AbstractHibernateDAO;
|
||||
import org.dspace.core.Context;
|
||||
|
||||
/**
|
||||
* Implementation of {@link NotifyPatternToTriggerDao}.
|
||||
*
|
||||
* @author Mohamed Eskander (mohamed.eskander at 4science.com)
|
||||
*/
|
||||
public class NotifyPatternToTriggerDaoImpl extends AbstractHibernateDAO<NotifyPatternToTrigger>
|
||||
implements NotifyPatternToTriggerDao {
|
||||
|
||||
@Override
|
||||
public List<NotifyPatternToTrigger> findByItem(Context context, Item item)
|
||||
throws SQLException {
|
||||
CriteriaBuilder criteriaBuilder = getCriteriaBuilder(context);
|
||||
CriteriaQuery criteriaQuery = getCriteriaQuery(criteriaBuilder, NotifyPatternToTrigger.class);
|
||||
Root<NotifyPatternToTrigger> notifyServiceEntityRoot = criteriaQuery.from(NotifyPatternToTrigger.class);
|
||||
criteriaQuery.select(notifyServiceEntityRoot);
|
||||
criteriaQuery.where(criteriaBuilder.equal(
|
||||
notifyServiceEntityRoot.get(NotifyPatternToTrigger_.item), item));
|
||||
return list(context, criteriaQuery, false, NotifyPatternToTrigger.class, -1, -1);
|
||||
}
|
||||
@Override
|
||||
public List<NotifyPatternToTrigger> findByItemAndPattern(Context context, Item item, String pattern)
|
||||
throws SQLException {
|
||||
CriteriaBuilder criteriaBuilder = getCriteriaBuilder(context);
|
||||
CriteriaQuery criteriaQuery = getCriteriaQuery(criteriaBuilder, NotifyPatternToTrigger.class);
|
||||
Root<NotifyPatternToTrigger> notifyServiceEntityRoot = criteriaQuery.from(NotifyPatternToTrigger.class);
|
||||
criteriaQuery.select(notifyServiceEntityRoot);
|
||||
criteriaQuery.where(criteriaBuilder.and(
|
||||
criteriaBuilder.equal(
|
||||
notifyServiceEntityRoot.get(NotifyPatternToTrigger_.item), item),
|
||||
criteriaBuilder.equal(
|
||||
notifyServiceEntityRoot.get(NotifyPatternToTrigger_.pattern), pattern)
|
||||
));
|
||||
return list(context, criteriaQuery, false, NotifyPatternToTrigger.class, -1, -1);
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,62 @@
|
||||
/**
|
||||
* The contents of this file are subject to the license and copyright
|
||||
* detailed in the LICENSE and NOTICE files at the root of the source
|
||||
* tree and available online at
|
||||
*
|
||||
* http://www.dspace.org/license/
|
||||
*/
|
||||
package org.dspace.app.ldn.dao.impl;
|
||||
|
||||
import java.sql.SQLException;
|
||||
import java.util.List;
|
||||
|
||||
import jakarta.persistence.criteria.CriteriaBuilder;
|
||||
import jakarta.persistence.criteria.CriteriaQuery;
|
||||
import jakarta.persistence.criteria.Join;
|
||||
import jakarta.persistence.criteria.Root;
|
||||
import org.dspace.app.ldn.NotifyServiceEntity;
|
||||
import org.dspace.app.ldn.NotifyServiceEntity_;
|
||||
import org.dspace.app.ldn.NotifyServiceInboundPattern;
|
||||
import org.dspace.app.ldn.NotifyServiceInboundPattern_;
|
||||
import org.dspace.app.ldn.dao.NotifyServiceDao;
|
||||
import org.dspace.core.AbstractHibernateDAO;
|
||||
import org.dspace.core.Context;
|
||||
|
||||
/**
|
||||
* Implementation of {@link NotifyServiceDao}.
|
||||
*
|
||||
* @author Mohamed Eskander (mohamed.eskander at 4science.com)
|
||||
*/
|
||||
public class NotifyServiceDaoImpl extends AbstractHibernateDAO<NotifyServiceEntity> implements NotifyServiceDao {
|
||||
|
||||
@Override
|
||||
public NotifyServiceEntity findByLdnUrl(Context context, String ldnUrl) throws SQLException {
|
||||
CriteriaBuilder criteriaBuilder = getCriteriaBuilder(context);
|
||||
CriteriaQuery criteriaQuery = getCriteriaQuery(criteriaBuilder, NotifyServiceEntity.class);
|
||||
Root<NotifyServiceEntity> notifyServiceEntityRoot = criteriaQuery.from(NotifyServiceEntity.class);
|
||||
criteriaQuery.select(notifyServiceEntityRoot);
|
||||
criteriaQuery.where(criteriaBuilder.equal(
|
||||
notifyServiceEntityRoot.get(NotifyServiceEntity_.ldnUrl), ldnUrl));
|
||||
return uniqueResult(context, criteriaQuery, false, NotifyServiceEntity.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<NotifyServiceEntity> findManualServicesByInboundPattern(Context context, String pattern)
|
||||
throws SQLException {
|
||||
CriteriaBuilder criteriaBuilder = getCriteriaBuilder(context);
|
||||
CriteriaQuery criteriaQuery = getCriteriaQuery(criteriaBuilder, NotifyServiceEntity.class);
|
||||
Root<NotifyServiceEntity> notifyServiceEntityRoot = criteriaQuery.from(NotifyServiceEntity.class);
|
||||
|
||||
Join<NotifyServiceEntity, NotifyServiceInboundPattern> notifyServiceInboundPatternJoin =
|
||||
notifyServiceEntityRoot.join(NotifyServiceEntity_.inboundPatterns);
|
||||
|
||||
criteriaQuery.select(notifyServiceEntityRoot);
|
||||
criteriaQuery.where(criteriaBuilder.and(
|
||||
criteriaBuilder.equal(
|
||||
notifyServiceInboundPatternJoin.get(NotifyServiceInboundPattern_.pattern), pattern),
|
||||
criteriaBuilder.equal(
|
||||
notifyServiceInboundPatternJoin.get(NotifyServiceInboundPattern_.automatic), false)));
|
||||
|
||||
return list(context, criteriaQuery, false, NotifyServiceEntity.class, -1, -1);
|
||||
}
|
||||
}
|
@@ -0,0 +1,59 @@
|
||||
/**
|
||||
* The contents of this file are subject to the license and copyright
|
||||
* detailed in the LICENSE and NOTICE files at the root of the source
|
||||
* tree and available online at
|
||||
*
|
||||
* http://www.dspace.org/license/
|
||||
*/
|
||||
package org.dspace.app.ldn.dao.impl;
|
||||
|
||||
import java.sql.SQLException;
|
||||
import java.util.List;
|
||||
|
||||
import jakarta.persistence.criteria.CriteriaBuilder;
|
||||
import jakarta.persistence.criteria.CriteriaQuery;
|
||||
import jakarta.persistence.criteria.Root;
|
||||
import org.dspace.app.ldn.NotifyServiceEntity;
|
||||
import org.dspace.app.ldn.NotifyServiceInboundPattern;
|
||||
import org.dspace.app.ldn.NotifyServiceInboundPattern_;
|
||||
import org.dspace.app.ldn.dao.NotifyServiceInboundPatternDao;
|
||||
import org.dspace.core.AbstractHibernateDAO;
|
||||
import org.dspace.core.Context;
|
||||
|
||||
/**
|
||||
* Implementation of {@link NotifyServiceInboundPatternDao}.
|
||||
*
|
||||
* @author Mohamed Eskander (mohamed.eskander at 4science.com)
|
||||
*/
|
||||
public class NotifyServiceInboundPatternDaoImpl
|
||||
extends AbstractHibernateDAO<NotifyServiceInboundPattern> implements NotifyServiceInboundPatternDao {
|
||||
|
||||
@Override
|
||||
public NotifyServiceInboundPattern findByServiceAndPattern(Context context, NotifyServiceEntity notifyServiceEntity,
|
||||
String pattern) throws SQLException {
|
||||
CriteriaBuilder criteriaBuilder = getCriteriaBuilder(context);
|
||||
CriteriaQuery criteriaQuery = getCriteriaQuery(criteriaBuilder, NotifyServiceInboundPattern.class);
|
||||
Root<NotifyServiceInboundPattern> inboundPatternRoot = criteriaQuery.from(NotifyServiceInboundPattern.class);
|
||||
criteriaQuery.select(inboundPatternRoot);
|
||||
criteriaQuery.where(criteriaBuilder.and(
|
||||
criteriaBuilder.equal(
|
||||
inboundPatternRoot.get(NotifyServiceInboundPattern_.notifyService), notifyServiceEntity),
|
||||
criteriaBuilder.equal(
|
||||
inboundPatternRoot.get(NotifyServiceInboundPattern_.pattern), pattern)
|
||||
));
|
||||
return uniqueResult(context, criteriaQuery, false, NotifyServiceInboundPattern.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<NotifyServiceInboundPattern> findAutomaticPatterns(Context context) throws SQLException {
|
||||
CriteriaBuilder criteriaBuilder = getCriteriaBuilder(context);
|
||||
CriteriaQuery criteriaQuery = getCriteriaQuery(criteriaBuilder, NotifyServiceInboundPattern.class);
|
||||
Root<NotifyServiceInboundPattern> inboundPatternRoot = criteriaQuery.from(NotifyServiceInboundPattern.class);
|
||||
criteriaQuery.select(inboundPatternRoot);
|
||||
criteriaQuery.where(
|
||||
criteriaBuilder.equal(
|
||||
inboundPatternRoot.get(NotifyServiceInboundPattern_.automatic), true)
|
||||
);
|
||||
return list(context, criteriaQuery, false, NotifyServiceInboundPattern.class, -1, -1);
|
||||
}
|
||||
}
|
@@ -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.ldn.factory;
|
||||
|
||||
import org.dspace.app.ldn.service.LDNMessageService;
|
||||
import org.dspace.services.factory.DSpaceServicesFactory;
|
||||
|
||||
/**
|
||||
* Abstract factory to get services for the NotifyService package,
|
||||
* use NotifyServiceFactory.getInstance() to retrieve an implementation
|
||||
*
|
||||
* @author Francesco Bacchelli (francesco.bacchelli at 4science.com)
|
||||
*/
|
||||
public abstract class LDNMessageServiceFactory {
|
||||
|
||||
public abstract LDNMessageService getLDNMessageService();
|
||||
|
||||
public static LDNMessageServiceFactory getInstance() {
|
||||
return DSpaceServicesFactory.getInstance()
|
||||
.getServiceManager()
|
||||
.getServiceByName("ldnMessageServiceFactory",
|
||||
LDNMessageServiceFactory.class);
|
||||
}
|
||||
}
|
@@ -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.ldn.factory;
|
||||
|
||||
import org.dspace.app.ldn.service.LDNMessageService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
/**
|
||||
* Factory implementation to get services for the notifyservices package, use
|
||||
* NotifyServiceFactory.getInstance() to retrieve an implementation
|
||||
*
|
||||
* @author Francesco Bacchelli (francesco.bacchelli at 4science.com)
|
||||
*/
|
||||
public class LDNMessageServiceFactoryImpl extends LDNMessageServiceFactory {
|
||||
|
||||
@Autowired(required = true)
|
||||
private LDNMessageService ldnMessageService;
|
||||
|
||||
@Override
|
||||
public LDNMessageService getLDNMessageService() {
|
||||
return ldnMessageService;
|
||||
}
|
||||
|
||||
}
|
@@ -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.ldn.factory;
|
||||
|
||||
import org.dspace.app.ldn.LDNRouter;
|
||||
import org.dspace.services.factory.DSpaceServicesFactory;
|
||||
|
||||
/**
|
||||
* Abstract factory to get services for the ldn package, use
|
||||
* LDNRouterFactory.getInstance() to retrieve an implementation
|
||||
*
|
||||
* @author Francesco Bacchelli (francesco.bacchelli at 4science.com)
|
||||
*/
|
||||
public abstract class LDNRouterFactory {
|
||||
|
||||
public abstract LDNRouter getLDNRouter();
|
||||
|
||||
public static LDNRouterFactory getInstance() {
|
||||
return DSpaceServicesFactory.getInstance()
|
||||
.getServiceManager()
|
||||
.getServiceByName("ldnRouter",
|
||||
LDNRouterFactory.class);
|
||||
}
|
||||
}
|
@@ -0,0 +1,28 @@
|
||||
/**
|
||||
* 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.ldn.factory;
|
||||
|
||||
import org.dspace.app.ldn.LDNRouter;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
/**
|
||||
* Factory implementation to get services for the ldn package,
|
||||
* use ldnRouter spring bean instance to retrieve an implementation
|
||||
*
|
||||
* @author Francesco Bacchelli (mohamed.eskander at 4science.com)
|
||||
*/
|
||||
public class LDNRouterFactoryImpl extends LDNRouterFactory {
|
||||
|
||||
@Autowired(required = true)
|
||||
private LDNRouter ldnRouter;
|
||||
|
||||
@Override
|
||||
public LDNRouter getLDNRouter() {
|
||||
return ldnRouter;
|
||||
}
|
||||
|
||||
}
|
@@ -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.ldn.factory;
|
||||
|
||||
import org.dspace.app.ldn.service.LDNMessageService;
|
||||
import org.dspace.app.ldn.service.NotifyPatternToTriggerService;
|
||||
import org.dspace.app.ldn.service.NotifyService;
|
||||
import org.dspace.app.ldn.service.NotifyServiceInboundPatternService;
|
||||
import org.dspace.services.factory.DSpaceServicesFactory;
|
||||
|
||||
/**
|
||||
* Abstract factory to get services for the NotifyService package,
|
||||
* use NotifyServiceFactory.getInstance() to retrieve an implementation
|
||||
*
|
||||
* @author Mohamed Eskander (mohamed.eskander at 4science.com)
|
||||
*/
|
||||
public abstract class NotifyServiceFactory {
|
||||
|
||||
public abstract NotifyService getNotifyService();
|
||||
|
||||
public abstract NotifyServiceInboundPatternService getNotifyServiceInboundPatternService();
|
||||
|
||||
public abstract NotifyPatternToTriggerService getNotifyPatternToTriggerService();
|
||||
|
||||
public abstract LDNMessageService getLDNMessageService();
|
||||
|
||||
public static NotifyServiceFactory getInstance() {
|
||||
return DSpaceServicesFactory.getInstance().getServiceManager().getServiceByName(
|
||||
"notifyServiceFactory", NotifyServiceFactory.class);
|
||||
}
|
||||
}
|
@@ -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.ldn.factory;
|
||||
|
||||
import org.dspace.app.ldn.service.LDNMessageService;
|
||||
import org.dspace.app.ldn.service.NotifyPatternToTriggerService;
|
||||
import org.dspace.app.ldn.service.NotifyService;
|
||||
import org.dspace.app.ldn.service.NotifyServiceInboundPatternService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
/**
|
||||
* Factory implementation to get services for the notifyservices package,
|
||||
* use NotifyServiceFactory.getInstance() to retrieve an implementation
|
||||
*
|
||||
* @author Mohamed Eskander (mohamed.eskander at 4science.com)
|
||||
*/
|
||||
public class NotifyServiceFactoryImpl extends NotifyServiceFactory {
|
||||
|
||||
@Autowired(required = true)
|
||||
private NotifyService notifyService;
|
||||
|
||||
@Autowired(required = true)
|
||||
private NotifyServiceInboundPatternService notifyServiceInboundPatternService;
|
||||
|
||||
@Autowired(required = true)
|
||||
private NotifyPatternToTriggerService notifyPatternToTriggerService;
|
||||
|
||||
@Autowired(required = true)
|
||||
private LDNMessageService ldnMessageService;
|
||||
|
||||
@Override
|
||||
public NotifyService getNotifyService() {
|
||||
return notifyService;
|
||||
}
|
||||
|
||||
@Override
|
||||
public NotifyServiceInboundPatternService getNotifyServiceInboundPatternService() {
|
||||
return notifyServiceInboundPatternService;
|
||||
}
|
||||
|
||||
@Override
|
||||
public NotifyPatternToTriggerService getNotifyPatternToTriggerService() {
|
||||
return notifyPatternToTriggerService;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LDNMessageService getLDNMessageService() {
|
||||
return ldnMessageService;
|
||||
}
|
||||
|
||||
}
|
41
dspace-api/src/main/java/org/dspace/app/ldn/model/Actor.java
Normal file
41
dspace-api/src/main/java/org/dspace/app/ldn/model/Actor.java
Normal file
@@ -0,0 +1,41 @@
|
||||
/**
|
||||
* The contents of this file are subject to the license and copyright
|
||||
* detailed in the LICENSE and NOTICE files at the root of the source
|
||||
* tree and available online at
|
||||
*
|
||||
* http://www.dspace.org/license/
|
||||
*/
|
||||
package org.dspace.app.ldn.model;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
/**
|
||||
* used to map @see org.dspace.app.ldn.model.Notification
|
||||
*/
|
||||
public class Actor extends Base {
|
||||
|
||||
@JsonProperty("name")
|
||||
private String name;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public Actor() {
|
||||
super();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return String
|
||||
*/
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param name
|
||||
*/
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
}
|
115
dspace-api/src/main/java/org/dspace/app/ldn/model/Base.java
Normal file
115
dspace-api/src/main/java/org/dspace/app/ldn/model/Base.java
Normal file
@@ -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.ldn.model;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import com.fasterxml.jackson.annotation.JsonInclude.Include;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
/**
|
||||
* used to map @see org.dspace.app.ldn.model.Notification
|
||||
*/
|
||||
@JsonInclude(Include.NON_EMPTY)
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public class Base {
|
||||
|
||||
@JsonProperty("id")
|
||||
private String id;
|
||||
|
||||
@JsonProperty("type")
|
||||
private Set<String> type;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public Base() {
|
||||
type = new HashSet<>();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return String
|
||||
*/
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param id
|
||||
*/
|
||||
public void setId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Set<String>
|
||||
*/
|
||||
public Set<String> getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param type
|
||||
*/
|
||||
public void setType(java.lang.Object type) {
|
||||
if (type instanceof String) {
|
||||
this.type.add((String) type);
|
||||
} else if (type instanceof Collection) {
|
||||
this.type.addAll((Collection) type);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param type
|
||||
*/
|
||||
public void addType(String type) {
|
||||
this.type.add(type);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result + ((id == null) ? 0 : id.hashCode());
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param obj
|
||||
* @return boolean
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(java.lang.Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
if (getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
Base other = (Base) obj;
|
||||
if (id == null) {
|
||||
if (other.id != null) {
|
||||
return false;
|
||||
}
|
||||
} else if (!id.equals(other.id)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,59 @@
|
||||
/**
|
||||
* The contents of this file are subject to the license and copyright
|
||||
* detailed in the LICENSE and NOTICE files at the root of the source
|
||||
* tree and available online at
|
||||
*
|
||||
* http://www.dspace.org/license/
|
||||
*/
|
||||
package org.dspace.app.ldn.model;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
|
||||
/**
|
||||
* used to map @see org.dspace.app.ldn.model.Notification
|
||||
*/
|
||||
public class Citation extends Base {
|
||||
|
||||
@JsonProperty("ietf:cite-as")
|
||||
private String ietfCiteAs;
|
||||
|
||||
@JsonProperty("ietf:item")
|
||||
private Url url;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public Citation() {
|
||||
super();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return String
|
||||
*/
|
||||
public String getIetfCiteAs() {
|
||||
return ietfCiteAs;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ietfCiteAs
|
||||
*/
|
||||
public void setIetfCiteAs(String ietfCiteAs) {
|
||||
this.ietfCiteAs = ietfCiteAs;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Url
|
||||
*/
|
||||
public Url getUrl() {
|
||||
return url;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param url
|
||||
*/
|
||||
public void setUrl(Url url) {
|
||||
this.url = url;
|
||||
}
|
||||
|
||||
}
|
@@ -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.ldn.model;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
|
||||
/**
|
||||
* used to map @see org.dspace.app.ldn.model.Notification
|
||||
*/
|
||||
public class Context extends Citation {
|
||||
|
||||
@JsonProperty("IsSupplementedBy")
|
||||
private List<Context> isSupplementedBy;
|
||||
|
||||
@JsonProperty("IsSupplementTo")
|
||||
private List<Context> isSupplementTo;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public Context() {
|
||||
super();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return List<Context>
|
||||
*/
|
||||
public List<Context> getIsSupplementedBy() {
|
||||
return isSupplementedBy;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param isSupplementedBy
|
||||
*/
|
||||
public void setIsSupplementedBy(List<Context> isSupplementedBy) {
|
||||
this.isSupplementedBy = isSupplementedBy;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return List<Context>
|
||||
*/
|
||||
public List<Context> getIsSupplementTo() {
|
||||
return isSupplementTo;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param isSupplementTo
|
||||
*/
|
||||
public void setIsSupplementTo(List<Context> isSupplementTo) {
|
||||
this.isSupplementTo = isSupplementTo;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,159 @@
|
||||
/**
|
||||
* 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.ldn.model;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
|
||||
|
||||
/**
|
||||
* the json object from witch @see org.dspace.app.ldn.LDNMessageEntity are created.
|
||||
* <a href="https://notify.coar-repositories.org/patterns/">see official coar doc</a>
|
||||
*/
|
||||
@JsonPropertyOrder(value = {
|
||||
"@context",
|
||||
"id",
|
||||
"type",
|
||||
"actor",
|
||||
"context",
|
||||
"object",
|
||||
"origin",
|
||||
"target",
|
||||
"inReplyTo"
|
||||
})
|
||||
public class Notification extends Base {
|
||||
|
||||
@JsonProperty("@context")
|
||||
private String[] c = new String[] {
|
||||
"https://purl.org/coar/notify",
|
||||
"https://www.w3.org/ns/activitystreams"
|
||||
};
|
||||
|
||||
@JsonProperty("actor")
|
||||
private Actor actor;
|
||||
|
||||
@JsonProperty("context")
|
||||
private Context context;
|
||||
|
||||
@JsonProperty("object")
|
||||
private Object object;
|
||||
|
||||
@JsonProperty("origin")
|
||||
private Service origin;
|
||||
|
||||
@JsonProperty("target")
|
||||
private Service target;
|
||||
|
||||
@JsonProperty("inReplyTo")
|
||||
private String inReplyTo;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public Notification() {
|
||||
super();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return String[]
|
||||
*/
|
||||
public String[] getC() {
|
||||
return c;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param c
|
||||
*/
|
||||
public void setC(String[] c) {
|
||||
this.c = c;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Actor
|
||||
*/
|
||||
public Actor getActor() {
|
||||
return actor;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param actor
|
||||
*/
|
||||
public void setActor(Actor actor) {
|
||||
this.actor = actor;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Context
|
||||
*/
|
||||
public Context getContext() {
|
||||
return context;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param context
|
||||
*/
|
||||
public void setContext(Context context) {
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Object
|
||||
*/
|
||||
public Object getObject() {
|
||||
return object;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param object
|
||||
*/
|
||||
public void setObject(Object object) {
|
||||
this.object = object;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Service
|
||||
*/
|
||||
public Service getOrigin() {
|
||||
return origin;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param origin
|
||||
*/
|
||||
public void setOrigin(Service origin) {
|
||||
this.origin = origin;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Service
|
||||
*/
|
||||
public Service getTarget() {
|
||||
return target;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param target
|
||||
*/
|
||||
public void setTarget(Service target) {
|
||||
this.target = target;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return String
|
||||
*/
|
||||
public String getInReplyTo() {
|
||||
return inReplyTo;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param inReplyTo
|
||||
*/
|
||||
public void setInReplyTo(String inReplyTo) {
|
||||
this.inReplyTo = inReplyTo;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,63 @@
|
||||
/**
|
||||
* 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.ldn.model;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
|
||||
|
||||
@JsonPropertyOrder(value = {
|
||||
"itemuuid",
|
||||
"notifyStatus"
|
||||
})
|
||||
|
||||
/**
|
||||
* item requests of LDN messages of type
|
||||
*
|
||||
* "Offer", "coar-notify:EndorsementAction"
|
||||
* "Offer", "coar-notify:IngestAction"
|
||||
* "Offer", "coar-notify:ReviewAction"
|
||||
*
|
||||
* and their acknowledgements - if any
|
||||
*
|
||||
* @author Francesco Bacchelli (francesco.bacchelli at 4science dot it)
|
||||
*/
|
||||
public class NotifyRequestStatus extends Base {
|
||||
|
||||
private UUID itemUuid;
|
||||
|
||||
private List<RequestStatus> notifyStatus;
|
||||
|
||||
public NotifyRequestStatus() {
|
||||
super();
|
||||
this.notifyStatus = new ArrayList<RequestStatus>();
|
||||
}
|
||||
|
||||
public UUID getItemUuid() {
|
||||
return itemUuid;
|
||||
}
|
||||
|
||||
public void setItemUuid(UUID itemUuid) {
|
||||
this.itemUuid = itemUuid;
|
||||
}
|
||||
|
||||
public void addRequestStatus(RequestStatus rs) {
|
||||
this.notifyStatus.add(rs);
|
||||
}
|
||||
|
||||
public List<RequestStatus> getNotifyStatus() {
|
||||
return notifyStatus;
|
||||
}
|
||||
|
||||
public void setNotifyStatus(List<RequestStatus> notifyStatus) {
|
||||
this.notifyStatus = notifyStatus;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,18 @@
|
||||
/**
|
||||
* The contents of this file are subject to the license and copyright
|
||||
* detailed in the LICENSE and NOTICE files at the root of the source
|
||||
* tree and available online at
|
||||
*
|
||||
* http://www.dspace.org/license/
|
||||
*/
|
||||
package org.dspace.app.ldn.model;
|
||||
/**
|
||||
* REQUESTED means acknowledgements not received yet
|
||||
* ACCEPTED means acknowledgements of "Accept" type received
|
||||
* REJECTED means ack of "TentativeReject" type received
|
||||
*
|
||||
* @author Francesco Bacchelli (francesco.bacchelli at 4science.com)
|
||||
*/
|
||||
public enum NotifyRequestStatusEnum {
|
||||
REJECTED, ACCEPTED, REQUESTED
|
||||
}
|
@@ -0,0 +1,74 @@
|
||||
/**
|
||||
* The contents of this file are subject to the license and copyright
|
||||
* detailed in the LICENSE and NOTICE files at the root of the source
|
||||
* tree and available online at
|
||||
*
|
||||
* http://www.dspace.org/license/
|
||||
*/
|
||||
package org.dspace.app.ldn.model;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
/**
|
||||
* used to map @see org.dspace.app.ldn.model.Notification
|
||||
*/
|
||||
public class Object extends Citation {
|
||||
|
||||
@JsonProperty("as:object")
|
||||
private String asObject;
|
||||
|
||||
@JsonProperty("as:relationship")
|
||||
private String asRelationship;
|
||||
|
||||
@JsonProperty("as:subject")
|
||||
private String asSubject;
|
||||
|
||||
@JsonProperty("sorg:name")
|
||||
private String title;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public Object() {
|
||||
super();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return String
|
||||
*/
|
||||
public String getTitle() {
|
||||
return title;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param title
|
||||
*/
|
||||
public void setTitle(String title) {
|
||||
this.title = title;
|
||||
}
|
||||
|
||||
public String getAsObject() {
|
||||
return asObject;
|
||||
}
|
||||
|
||||
public void setAsObject(String asObject) {
|
||||
this.asObject = asObject;
|
||||
}
|
||||
|
||||
public String getAsRelationship() {
|
||||
return asRelationship;
|
||||
}
|
||||
|
||||
public void setAsRelationship(String asRelationship) {
|
||||
this.asRelationship = asRelationship;
|
||||
}
|
||||
|
||||
public String getAsSubject() {
|
||||
return asSubject;
|
||||
}
|
||||
|
||||
public void setAsSubject(String asSubject) {
|
||||
this.asSubject = asSubject;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,47 @@
|
||||
/**
|
||||
* The contents of this file are subject to the license and copyright
|
||||
* detailed in the LICENSE and NOTICE files at the root of the source
|
||||
* tree and available online at
|
||||
*
|
||||
* http://www.dspace.org/license/
|
||||
*/
|
||||
package org.dspace.app.ldn.model;
|
||||
|
||||
/**
|
||||
* Information about the Offer and Acknowledgements targeting a specified Item
|
||||
*
|
||||
* @author Francesco Bacchelli (francesco.bacchelli at 4science.com)
|
||||
*/
|
||||
public class RequestStatus {
|
||||
|
||||
private String serviceName;
|
||||
private String serviceUrl;
|
||||
private String offerType;
|
||||
private NotifyRequestStatusEnum status;
|
||||
|
||||
public String getServiceName() {
|
||||
return serviceName;
|
||||
}
|
||||
public void setServiceName(String serviceName) {
|
||||
this.serviceName = serviceName;
|
||||
}
|
||||
public String getServiceUrl() {
|
||||
return serviceUrl;
|
||||
}
|
||||
public void setServiceUrl(String serviceUrl) {
|
||||
this.serviceUrl = serviceUrl;
|
||||
}
|
||||
public NotifyRequestStatusEnum getStatus() {
|
||||
return status;
|
||||
}
|
||||
public void setStatus(NotifyRequestStatusEnum status) {
|
||||
this.status = status;
|
||||
}
|
||||
public String getOfferType() {
|
||||
return offerType;
|
||||
}
|
||||
public void setOfferType(String offerType) {
|
||||
this.offerType = offerType;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,41 @@
|
||||
/**
|
||||
* The contents of this file are subject to the license and copyright
|
||||
* detailed in the LICENSE and NOTICE files at the root of the source
|
||||
* tree and available online at
|
||||
*
|
||||
* http://www.dspace.org/license/
|
||||
*/
|
||||
package org.dspace.app.ldn.model;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
/**
|
||||
* used to map @see org.dspace.app.ldn.model.Notification
|
||||
*/
|
||||
public class Service extends Base {
|
||||
|
||||
@JsonProperty("inbox")
|
||||
private String inbox;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public Service() {
|
||||
super();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return String
|
||||
*/
|
||||
public String getInbox() {
|
||||
return inbox;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param inbox
|
||||
*/
|
||||
public void setInbox(String inbox) {
|
||||
this.inbox = inbox;
|
||||
}
|
||||
|
||||
}
|
41
dspace-api/src/main/java/org/dspace/app/ldn/model/Url.java
Normal file
41
dspace-api/src/main/java/org/dspace/app/ldn/model/Url.java
Normal file
@@ -0,0 +1,41 @@
|
||||
/**
|
||||
* The contents of this file are subject to the license and copyright
|
||||
* detailed in the LICENSE and NOTICE files at the root of the source
|
||||
* tree and available online at
|
||||
*
|
||||
* http://www.dspace.org/license/
|
||||
*/
|
||||
package org.dspace.app.ldn.model;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
/**
|
||||
* used to map @see org.dspace.app.ldn.model.Citation
|
||||
*/
|
||||
public class Url extends Base {
|
||||
|
||||
@JsonProperty("mediaType")
|
||||
private String mediaType;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public Url() {
|
||||
super();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return String
|
||||
*/
|
||||
public String getMediaType() {
|
||||
return mediaType;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mediaType
|
||||
*/
|
||||
public void setMediaType(String mediaType) {
|
||||
this.mediaType = mediaType;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,141 @@
|
||||
/**
|
||||
* 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.ldn.processor;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.node.ArrayNode;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.dspace.app.ldn.model.Context;
|
||||
import org.dspace.app.ldn.model.Notification;
|
||||
|
||||
/**
|
||||
* Context repeater to iterate over array context properties of a received
|
||||
* notification. The returned notification iterator is a notification with
|
||||
* context array elements hoisted onto the root of the notification context.
|
||||
*/
|
||||
public class LDNContextRepeater {
|
||||
|
||||
private final static Logger log = LogManager.getLogger(LDNContextRepeater.class);
|
||||
|
||||
private final static String CONTEXT = "context";
|
||||
|
||||
private String repeatOver;
|
||||
|
||||
/**
|
||||
* @return String
|
||||
*/
|
||||
public String getRepeatOver() {
|
||||
return repeatOver;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param repeatOver
|
||||
*/
|
||||
public void setRepeatOver(String repeatOver) {
|
||||
this.repeatOver = repeatOver;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param notification
|
||||
* @return Iterator<Notification>
|
||||
*/
|
||||
public Iterator<Notification> iterator(Notification notification) {
|
||||
return new NotificationIterator(notification, repeatOver);
|
||||
}
|
||||
|
||||
/**
|
||||
* Private inner class defining the notification iterator.
|
||||
*/
|
||||
private class NotificationIterator implements Iterator<Notification> {
|
||||
|
||||
private final List<Notification> notifications;
|
||||
|
||||
/**
|
||||
* Convert notification to JsonNode in order to clone for each context array
|
||||
* element. Each element is then hoisted to the root of the cloned notification
|
||||
* context.
|
||||
*
|
||||
* @param notification received notification
|
||||
* @param repeatOver which context property to repeat over
|
||||
*/
|
||||
private NotificationIterator(Notification notification, String repeatOver) {
|
||||
this.notifications = new ArrayList<>();
|
||||
|
||||
if (Objects.nonNull(repeatOver)) {
|
||||
ObjectMapper objectMapper = new ObjectMapper();
|
||||
|
||||
JsonNode notificationNode = objectMapper.valueToTree(notification);
|
||||
|
||||
log.debug("Notification {}", notificationNode);
|
||||
|
||||
JsonNode topContextNode = notificationNode.get(CONTEXT);
|
||||
if (topContextNode.isNull()) {
|
||||
log.warn("Notification is missing context");
|
||||
return;
|
||||
}
|
||||
|
||||
JsonNode contextArrayNode = topContextNode.get(repeatOver);
|
||||
if (contextArrayNode == null || contextArrayNode.isNull()) {
|
||||
log.error("Notification context {} is not defined", repeatOver);
|
||||
return;
|
||||
}
|
||||
|
||||
if (contextArrayNode.isArray()) {
|
||||
|
||||
for (JsonNode contextNode : ((ArrayNode) contextArrayNode)) {
|
||||
|
||||
try {
|
||||
Context context = objectMapper.treeToValue(contextNode, Context.class);
|
||||
|
||||
Notification copy = objectMapper.treeToValue(notificationNode, Notification.class);
|
||||
|
||||
copy.setContext(context);
|
||||
|
||||
this.notifications.add(copy);
|
||||
} catch (JsonProcessingException e) {
|
||||
log.error("Failed to copy notification");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
log.error("Notification context {} is not an array", repeatOver);
|
||||
}
|
||||
|
||||
} else {
|
||||
this.notifications.add(notification);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return boolean
|
||||
*/
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return !this.notifications.isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Notification
|
||||
*/
|
||||
@Override
|
||||
public Notification next() {
|
||||
return this.notifications.remove(0);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,231 @@
|
||||
/**
|
||||
* 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.ldn.processor;
|
||||
|
||||
import static java.lang.String.format;
|
||||
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
import org.apache.http.HttpStatus;
|
||||
import org.apache.http.client.HttpResponseException;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.dspace.app.ldn.action.LDNAction;
|
||||
import org.dspace.app.ldn.action.LDNActionStatus;
|
||||
import org.dspace.app.ldn.model.Notification;
|
||||
import org.dspace.app.ldn.service.LDNMessageService;
|
||||
import org.dspace.app.ldn.utility.LDNUtils;
|
||||
import org.dspace.content.DSpaceObject;
|
||||
import org.dspace.content.Item;
|
||||
import org.dspace.content.service.ItemService;
|
||||
import org.dspace.core.Constants;
|
||||
import org.dspace.core.Context;
|
||||
import org.dspace.handle.service.HandleService;
|
||||
import org.dspace.services.ConfigurationService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
/**
|
||||
* Linked Data Notification metadata processor for consuming notifications. The
|
||||
* storage of notification details are within item metadata.
|
||||
*/
|
||||
public class LDNMetadataProcessor implements LDNProcessor {
|
||||
|
||||
private final static Logger log = LogManager.getLogger(LDNMetadataProcessor.class);
|
||||
|
||||
@Autowired
|
||||
private ItemService itemService;
|
||||
|
||||
@Autowired
|
||||
private LDNMessageService ldnMessageService;
|
||||
|
||||
@Autowired
|
||||
private ConfigurationService configurationService;
|
||||
|
||||
private static final Set<String> OBJECT_SUBJECT_ITEM_TYPES = Set.of(
|
||||
"Announce",
|
||||
"coar-notify:RelationshipAction");
|
||||
|
||||
private static final Set<String> CONTEXT_ID_ITEM_TYPES = Set.of(
|
||||
"Announce",
|
||||
"TentativeReject",
|
||||
"Accept",
|
||||
"coar-notify:ReviewAction",
|
||||
"coar-notify:IngestAction",
|
||||
"coar-notify:EndorsementAction");
|
||||
|
||||
private static final Set<String> OBJECT_ID_ITEM_TYPES = Set.of(
|
||||
"Offer",
|
||||
"coar-notify:ReviewAction",
|
||||
"coar-notify:EndorsementAction",
|
||||
"coar-notify:IngestAction");
|
||||
|
||||
@Autowired
|
||||
private HandleService handleService;
|
||||
|
||||
private LDNContextRepeater repeater = new LDNContextRepeater();
|
||||
|
||||
private List<LDNAction> actions = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* Initialize velocity engine for templating.
|
||||
*/
|
||||
private LDNMetadataProcessor() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Process notification by repeating over context, processing each context
|
||||
* notification, and running actions post processing.
|
||||
*
|
||||
* @param notification received notification
|
||||
* @throws Exception something went wrong processing the notification
|
||||
*/
|
||||
@Override
|
||||
public void process(Context context, Notification notification) throws Exception {
|
||||
Item item = lookupItem(context, notification);
|
||||
runActions(context, notification, item);
|
||||
}
|
||||
|
||||
/**
|
||||
* Run all actions defined for the processor.
|
||||
*
|
||||
* @param notification current context notification
|
||||
* @param item associated item
|
||||
*
|
||||
* @return ActionStatus result status of running the action
|
||||
*
|
||||
* @throws Exception failed execute the action
|
||||
*/
|
||||
private LDNActionStatus runActions(Context context, Notification notification, Item item) throws Exception {
|
||||
LDNActionStatus operation = LDNActionStatus.CONTINUE;
|
||||
for (LDNAction action : actions) {
|
||||
log.info("Running action {} for notification {} {}",
|
||||
action.getClass().getSimpleName(),
|
||||
notification.getId(),
|
||||
notification.getType());
|
||||
|
||||
operation = action.execute(context, notification, item);
|
||||
if (operation == LDNActionStatus.ABORT) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return operation;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return LDNContextRepeater
|
||||
*/
|
||||
public LDNContextRepeater getRepeater() {
|
||||
return repeater;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param repeater
|
||||
*/
|
||||
public void setRepeater(LDNContextRepeater repeater) {
|
||||
this.repeater = repeater;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return List<LDNAction>
|
||||
*/
|
||||
public List<LDNAction> getActions() {
|
||||
return actions;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param actions
|
||||
*/
|
||||
public void setActions(List<LDNAction> actions) {
|
||||
this.actions = actions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Lookup associated item to the notification context. If UUID in URL, lookup by
|
||||
* UUID, else lookup by handle.
|
||||
*
|
||||
* @param context current context
|
||||
* @param notification current context notification
|
||||
*
|
||||
* @return Item associated item
|
||||
*
|
||||
* @throws SQLException failed to lookup item
|
||||
* @throws HttpResponseException redirect failure
|
||||
*/
|
||||
private Item lookupItem(Context context, Notification notification) throws SQLException, HttpResponseException {
|
||||
Item item = null;
|
||||
String url = null;
|
||||
|
||||
if (CONTEXT_ID_ITEM_TYPES.containsAll(notification.getType())) {
|
||||
url = notification.getContext().getId();
|
||||
} else if (OBJECT_ID_ITEM_TYPES.containsAll(notification.getType())) {
|
||||
url = notification.getObject().getId();
|
||||
} else if (OBJECT_SUBJECT_ITEM_TYPES.containsAll(notification.getType())) {
|
||||
// need to understand if we're sender or receiver
|
||||
if (ldnMessageService.isTargetCurrent(notification)) {
|
||||
// this means we're sending the notification
|
||||
url = notification.getObject().getAsObject();
|
||||
// use as:object for sender
|
||||
} else {
|
||||
// this means we're receiving the notification
|
||||
url = notification.getObject().getAsSubject();
|
||||
// use as:subject for receiver
|
||||
}
|
||||
}
|
||||
|
||||
log.info("Looking up item {}", url);
|
||||
|
||||
item = resolveItemByUrl(context, url, notification);
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
private Item resolveItemByUrl(Context context, String url, Notification notification)
|
||||
throws SQLException, HttpResponseException {
|
||||
Item item = null;
|
||||
if (LDNUtils.hasUUIDInURL(url)) {
|
||||
UUID uuid = LDNUtils.getUUIDFromURL(url);
|
||||
|
||||
item = itemService.find(context, uuid);
|
||||
|
||||
if (Objects.isNull(item)) {
|
||||
throw new HttpResponseException(HttpStatus.SC_NOT_FOUND,
|
||||
format("Item with uuid %s not found", uuid));
|
||||
}
|
||||
return item;
|
||||
}
|
||||
String handle = handleService.resolveUrlToHandle(context, url);
|
||||
|
||||
if (Objects.isNull(handle)) {
|
||||
throw new HttpResponseException(HttpStatus.SC_NOT_FOUND,
|
||||
format("Handle not found for %s", url));
|
||||
}
|
||||
|
||||
DSpaceObject object = handleService.resolveToObject(context, handle);
|
||||
|
||||
if (Objects.isNull(object)) {
|
||||
throw new HttpResponseException(HttpStatus.SC_NOT_FOUND,
|
||||
format("Item with handle %s not found", handle));
|
||||
}
|
||||
|
||||
if (object.getType() == Constants.ITEM) {
|
||||
item = (Item) object;
|
||||
} else {
|
||||
throw new HttpResponseException(HttpStatus.SC_UNPROCESSABLE_ENTITY,
|
||||
format("Handle %s does not resolve to an item", handle));
|
||||
}
|
||||
return item;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,25 @@
|
||||
/**
|
||||
* The contents of this file are subject to the license and copyright
|
||||
* detailed in the LICENSE and NOTICE files at the root of the source
|
||||
* tree and available online at
|
||||
*
|
||||
* http://www.dspace.org/license/
|
||||
*/
|
||||
package org.dspace.app.ldn.processor;
|
||||
|
||||
import org.dspace.app.ldn.model.Notification;
|
||||
import org.dspace.core.Context;
|
||||
|
||||
/**
|
||||
* Processor interface to allow for custom implementations of process.
|
||||
*/
|
||||
public interface LDNProcessor {
|
||||
|
||||
/**
|
||||
* Process received notification.
|
||||
*
|
||||
* @param notification received notification
|
||||
* @throws Exception something went wrong processing the notification
|
||||
*/
|
||||
public void process(Context context, Notification notification) throws Exception;
|
||||
}
|
@@ -0,0 +1,165 @@
|
||||
/**
|
||||
* 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.ldn.service;
|
||||
|
||||
import java.sql.SQLException;
|
||||
import java.util.List;
|
||||
|
||||
import org.dspace.app.ldn.LDNMessageEntity;
|
||||
import org.dspace.app.ldn.NotifyServiceEntity;
|
||||
import org.dspace.app.ldn.model.Notification;
|
||||
import org.dspace.app.ldn.model.NotifyRequestStatus;
|
||||
import org.dspace.app.ldn.model.Service;
|
||||
import org.dspace.content.Item;
|
||||
import org.dspace.core.Context;
|
||||
|
||||
/**
|
||||
* Service interface class for the {@link LDNMessageEntity} object.
|
||||
*
|
||||
* @author Mohamed Eskander (mohamed.eskander at 4science dot it)
|
||||
*/
|
||||
public interface LDNMessageService {
|
||||
|
||||
/**
|
||||
* find the ldn message by id
|
||||
*
|
||||
* @param context the context
|
||||
* @param id the uri
|
||||
* @return the ldn message by id
|
||||
* @throws SQLException If something goes wrong in the database
|
||||
*/
|
||||
public LDNMessageEntity find(Context context, String id) throws SQLException;
|
||||
|
||||
/**
|
||||
* find all ldn messages
|
||||
*
|
||||
* @param context the context
|
||||
* @return all ldn messages by id
|
||||
* @throws SQLException If something goes wrong in the database
|
||||
*/
|
||||
public List<LDNMessageEntity> findAll(Context context) throws SQLException;
|
||||
|
||||
/**
|
||||
* Creates a new LDNMessage
|
||||
*
|
||||
* @param context The DSpace context
|
||||
* @param id the uri
|
||||
* @return the created LDN Message
|
||||
* @throws SQLException If something goes wrong in the database
|
||||
*/
|
||||
public LDNMessageEntity create(Context context, String id) throws SQLException;
|
||||
|
||||
/**
|
||||
* Creates a new LDNMessage
|
||||
*
|
||||
* @param context The DSpace context
|
||||
* @param notification the requested notification
|
||||
* @param sourceIp the source ip
|
||||
* @return the created LDN Message
|
||||
* @throws SQLException If something goes wrong in the database
|
||||
*/
|
||||
public LDNMessageEntity create(Context context, Notification notification, String sourceIp) throws SQLException;
|
||||
|
||||
/**
|
||||
* Update the provided LDNMessage
|
||||
*
|
||||
* @param context The DSpace context
|
||||
* @param ldnMessage the LDNMessage
|
||||
* @throws SQLException If something goes wrong in the database
|
||||
*/
|
||||
public void update(Context context, LDNMessageEntity ldnMessage) throws SQLException;
|
||||
|
||||
/**
|
||||
* Find the oldest queued LDNMessages that still can be elaborated
|
||||
*
|
||||
* @return list of LDN messages
|
||||
* @param context The DSpace context
|
||||
* @throws SQLException If something goes wrong in the database
|
||||
*/
|
||||
public List<LDNMessageEntity> findOldestMessagesToProcess(Context context) throws SQLException;
|
||||
|
||||
/**
|
||||
* Find all messages in the queue with the Processing status but timed-out
|
||||
*
|
||||
* @return all the LDN Messages to be fixed on their queue_ attributes
|
||||
* @param context The DSpace context
|
||||
* @throws SQLException If something goes wrong in the database
|
||||
*/
|
||||
public List<LDNMessageEntity> findProcessingTimedoutMessages(Context context) throws SQLException;
|
||||
|
||||
/**
|
||||
* Find all messages in the queue with the Processing status but timed-out and modify their queue_status
|
||||
* considering the queue_attempts
|
||||
*
|
||||
* @return number of messages fixed
|
||||
* @param context The DSpace context
|
||||
* @throws SQLException
|
||||
*/
|
||||
public int checkQueueMessageTimeout(Context context) throws SQLException;
|
||||
|
||||
/**
|
||||
* Elaborates the oldest enqueued message
|
||||
*
|
||||
* @return number of messages fixed
|
||||
* @param context The DSpace context
|
||||
*/
|
||||
public int extractAndProcessMessageFromQueue(Context context) throws SQLException;
|
||||
|
||||
/**
|
||||
* find the related notify service entity
|
||||
*
|
||||
* @param context the context
|
||||
* @param service the service
|
||||
* @return the NotifyServiceEntity
|
||||
* @throws SQLException if something goes wrong
|
||||
*/
|
||||
public NotifyServiceEntity findNotifyService(Context context, Service service) throws SQLException;
|
||||
|
||||
/**
|
||||
* find the ldn messages of Requests by item uuid
|
||||
*
|
||||
* @param context the context
|
||||
* @param item the item
|
||||
* @return the item requests object
|
||||
* @throws SQLException If something goes wrong in the database
|
||||
*/
|
||||
public NotifyRequestStatus findRequestsByItem(Context context, Item item) throws SQLException;
|
||||
|
||||
/**
|
||||
* delete the provided ldn message
|
||||
*
|
||||
* @param context the context
|
||||
* @param ldnMessage the ldn message
|
||||
* @throws SQLException if something goes wrong
|
||||
*/
|
||||
public void delete(Context context, LDNMessageEntity ldnMessage) throws SQLException;
|
||||
|
||||
/**
|
||||
* find the ldn messages to be reprocessed
|
||||
*
|
||||
* @param context the context
|
||||
* @throws SQLException if something goes wrong
|
||||
*/
|
||||
public List<LDNMessageEntity> findMessagesToBeReprocessed(Context context) throws SQLException;
|
||||
|
||||
/**
|
||||
* check if IP number is included in the configured ip-range on the Notify
|
||||
* Service
|
||||
*
|
||||
* @param origin the Notify Service entity
|
||||
* @param sourceIp the ip to evaluate
|
||||
*/
|
||||
public boolean isValidIp(NotifyServiceEntity origin, String sourceIp);
|
||||
|
||||
/**
|
||||
* check if the notification is targeting the current system
|
||||
*
|
||||
* @param notification the LDN Message entity
|
||||
*/
|
||||
boolean isTargetCurrent(Notification notification);
|
||||
}
|
@@ -0,0 +1,84 @@
|
||||
/**
|
||||
* The contents of this file are subject to the license and copyright
|
||||
* detailed in the LICENSE and NOTICE files at the root of the source
|
||||
* tree and available online at
|
||||
*
|
||||
* http://www.dspace.org/license/
|
||||
*/
|
||||
package org.dspace.app.ldn.service;
|
||||
|
||||
import java.sql.SQLException;
|
||||
import java.util.List;
|
||||
|
||||
import org.dspace.app.ldn.NotifyPatternToTrigger;
|
||||
import org.dspace.content.Item;
|
||||
import org.dspace.core.Context;
|
||||
|
||||
/**
|
||||
* Service interface class for the {@link NotifyPatternToTrigger} object.
|
||||
*
|
||||
* @author Mohamed Eskander (mohamed.eskander at 4science.com)
|
||||
*/
|
||||
public interface NotifyPatternToTriggerService {
|
||||
|
||||
/**
|
||||
* find all notify patterns to be triggered
|
||||
*
|
||||
* @param context the context
|
||||
* @return all notify patterns to be trigger
|
||||
* @throws SQLException if database error
|
||||
*/
|
||||
public List<NotifyPatternToTrigger> findAll(Context context) throws SQLException;
|
||||
|
||||
/**
|
||||
* find list of Notify Patterns To be Triggered by item
|
||||
*
|
||||
* @param context the context
|
||||
* @param item the item of NotifyPatternToTrigger
|
||||
* @return the matched NotifyPatternToTrigger list by item
|
||||
* @throws SQLException if database error
|
||||
*/
|
||||
public List<NotifyPatternToTrigger> findByItem(Context context, Item item)
|
||||
throws SQLException;
|
||||
|
||||
/**
|
||||
* find list of Notify Patterns To be Triggered by item and pattern
|
||||
*
|
||||
* @param context the context
|
||||
* @param item the item of NotifyPatternToTrigger
|
||||
* @param pattern the pattern of NotifyPatternToTrigger
|
||||
*
|
||||
* @return the matched NotifyPatternToTrigger list by item and pattern
|
||||
* @throws SQLException if database error
|
||||
*/
|
||||
public List<NotifyPatternToTrigger> findByItemAndPattern(Context context, Item item, String pattern)
|
||||
throws SQLException;
|
||||
|
||||
/**
|
||||
* create new notifyPatternToTrigger
|
||||
*
|
||||
* @param context the context
|
||||
* @return the created NotifyPatternToTrigger
|
||||
* @throws SQLException if database error
|
||||
*/
|
||||
public NotifyPatternToTrigger create(Context context) throws SQLException;
|
||||
|
||||
/**
|
||||
* update the provided notifyPatternToTrigger
|
||||
*
|
||||
* @param context the context
|
||||
* @param notifyPatternToTrigger the notifyPatternToTrigger
|
||||
* @throws SQLException if database error
|
||||
*/
|
||||
public void update(Context context, NotifyPatternToTrigger notifyPatternToTrigger) throws SQLException;
|
||||
|
||||
/**
|
||||
* delete the provided notifyPatternToTrigger
|
||||
*
|
||||
* @param context the context
|
||||
* @param notifyPatternToTrigger the notifyPatternToTrigger
|
||||
* @throws SQLException if database error
|
||||
*/
|
||||
public void delete(Context context, NotifyPatternToTrigger notifyPatternToTrigger) throws SQLException;
|
||||
|
||||
}
|
@@ -0,0 +1,92 @@
|
||||
/**
|
||||
* 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.ldn.service;
|
||||
|
||||
import java.sql.SQLException;
|
||||
import java.util.List;
|
||||
|
||||
import org.dspace.app.ldn.NotifyServiceEntity;
|
||||
import org.dspace.core.Context;
|
||||
|
||||
/**
|
||||
* Service interface class for the {@link NotifyServiceEntity} object.
|
||||
*
|
||||
* @author Mohamed Eskander (mohamed.eskander at 4science.com)
|
||||
*/
|
||||
public interface NotifyService {
|
||||
|
||||
/**
|
||||
* find all notify service entities
|
||||
*
|
||||
* @param context the context
|
||||
* @return all notify service entities
|
||||
* @throws SQLException if database error
|
||||
*/
|
||||
public List<NotifyServiceEntity> findAll(Context context) throws SQLException;
|
||||
|
||||
/**
|
||||
* find one NotifyServiceEntity by id
|
||||
*
|
||||
* @param context the context
|
||||
* @param id the id of NotifyServiceEntity
|
||||
* @return the matched NotifyServiceEntity by id
|
||||
* @throws SQLException if database error
|
||||
*/
|
||||
public NotifyServiceEntity find(Context context, Integer id) throws SQLException;
|
||||
|
||||
/**
|
||||
* create new notifyServiceEntity
|
||||
*
|
||||
* @param context the context
|
||||
* @param name name of the service
|
||||
* @return the created NotifyServiceEntity
|
||||
* @throws SQLException if database error
|
||||
*/
|
||||
public NotifyServiceEntity create(Context context, String name) throws SQLException;
|
||||
|
||||
/**
|
||||
* update the provided notifyServiceEntity
|
||||
*
|
||||
* @param context the context
|
||||
* @param notifyServiceEntity the notifyServiceEntity
|
||||
* @throws SQLException if database error
|
||||
*/
|
||||
public void update(Context context, NotifyServiceEntity notifyServiceEntity) throws SQLException;
|
||||
|
||||
/**
|
||||
* delete the provided notifyServiceEntity
|
||||
*
|
||||
* @param context the context
|
||||
* @param notifyServiceEntity the notifyServiceEntity
|
||||
* @throws SQLException if database error
|
||||
*/
|
||||
public void delete(Context context, NotifyServiceEntity notifyServiceEntity) throws SQLException;
|
||||
|
||||
/**
|
||||
* find the NotifyServiceEntity matched with the provided ldnUrl
|
||||
*
|
||||
* @param context the context
|
||||
* @param ldnUrl the ldnUrl
|
||||
* @return the NotifyServiceEntity matched the provided ldnUrl
|
||||
* @throws SQLException if database error
|
||||
*/
|
||||
public NotifyServiceEntity findByLdnUrl(Context context, String ldnUrl) throws SQLException;
|
||||
|
||||
/**
|
||||
* find all NotifyServiceEntity matched the provided inbound pattern
|
||||
* from its related notifyServiceInboundPatterns
|
||||
* also with 'automatic' equals to false
|
||||
*
|
||||
* @param context the context
|
||||
* @param pattern the ldnUrl
|
||||
* @return all NotifyServiceEntity matched the provided pattern
|
||||
* @throws SQLException if database error
|
||||
*/
|
||||
public List<NotifyServiceEntity> findManualServicesByInboundPattern(Context context, String pattern)
|
||||
throws SQLException;
|
||||
}
|
@@ -0,0 +1,76 @@
|
||||
/**
|
||||
* The contents of this file are subject to the license and copyright
|
||||
* detailed in the LICENSE and NOTICE files at the root of the source
|
||||
* tree and available online at
|
||||
*
|
||||
* http://www.dspace.org/license/
|
||||
*/
|
||||
package org.dspace.app.ldn.service;
|
||||
|
||||
import java.sql.SQLException;
|
||||
import java.util.List;
|
||||
|
||||
import org.dspace.app.ldn.NotifyServiceEntity;
|
||||
import org.dspace.app.ldn.NotifyServiceInboundPattern;
|
||||
import org.dspace.core.Context;
|
||||
|
||||
/**
|
||||
* Service interface class for the {@link NotifyServiceInboundPattern} object.
|
||||
*
|
||||
* @author Mohamed Eskander (mohamed.eskander at 4science.com)
|
||||
*/
|
||||
public interface NotifyServiceInboundPatternService {
|
||||
|
||||
/**
|
||||
* find all notifyServiceInboundPatterns matched with
|
||||
* the provided notifyServiceEntity and pattern
|
||||
*
|
||||
* @param context the context
|
||||
* @param notifyServiceEntity the notifyServiceEntity
|
||||
* @param pattern the pattern
|
||||
* @return all notifyServiceInboundPatterns matched with
|
||||
* the provided notifyServiceEntity and pattern
|
||||
* @throws SQLException if database error
|
||||
*/
|
||||
public NotifyServiceInboundPattern findByServiceAndPattern(Context context,
|
||||
NotifyServiceEntity notifyServiceEntity,
|
||||
String pattern) throws SQLException;
|
||||
|
||||
/**
|
||||
* find all automatic notifyServiceInboundPatterns
|
||||
*
|
||||
* @param context the context
|
||||
* @return all automatic notifyServiceInboundPatterns
|
||||
* @throws SQLException if database error
|
||||
*/
|
||||
public List<NotifyServiceInboundPattern> findAutomaticPatterns(Context context) throws SQLException;
|
||||
|
||||
/**
|
||||
* create new notifyServiceInboundPattern
|
||||
*
|
||||
* @param context the context
|
||||
* @param notifyServiceEntity the notifyServiceEntity
|
||||
* @return the created notifyServiceInboundPattern
|
||||
* @throws SQLException if database error
|
||||
*/
|
||||
public NotifyServiceInboundPattern create(Context context, NotifyServiceEntity notifyServiceEntity)
|
||||
throws SQLException;
|
||||
|
||||
/**
|
||||
* update the provided notifyServiceInboundPattern
|
||||
*
|
||||
* @param context the context
|
||||
* @param inboundPattern the notifyServiceInboundPattern
|
||||
* @throws SQLException if database error
|
||||
*/
|
||||
public void update(Context context, NotifyServiceInboundPattern inboundPattern) throws SQLException;
|
||||
|
||||
/**
|
||||
* delete the provided notifyServiceInboundPattern
|
||||
*
|
||||
* @param context the context
|
||||
* @param inboundPattern the notifyServiceInboundPattern
|
||||
* @throws SQLException if database error
|
||||
*/
|
||||
public void delete(Context context, NotifyServiceInboundPattern inboundPattern) throws SQLException;
|
||||
}
|
@@ -0,0 +1,404 @@
|
||||
/**
|
||||
* 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.ldn.service.impl;
|
||||
|
||||
import java.net.InetAddress;
|
||||
import java.net.UnknownHostException;
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.google.gson.JsonSyntaxException;
|
||||
import org.apache.commons.lang.time.DateUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.dspace.app.ldn.LDNMessageEntity;
|
||||
import org.dspace.app.ldn.LDNRouter;
|
||||
import org.dspace.app.ldn.NotifyServiceEntity;
|
||||
import org.dspace.app.ldn.dao.LDNMessageDao;
|
||||
import org.dspace.app.ldn.dao.NotifyServiceDao;
|
||||
import org.dspace.app.ldn.model.Notification;
|
||||
import org.dspace.app.ldn.model.NotifyRequestStatus;
|
||||
import org.dspace.app.ldn.model.NotifyRequestStatusEnum;
|
||||
import org.dspace.app.ldn.model.RequestStatus;
|
||||
import org.dspace.app.ldn.model.Service;
|
||||
import org.dspace.app.ldn.processor.LDNProcessor;
|
||||
import org.dspace.app.ldn.service.LDNMessageService;
|
||||
import org.dspace.app.ldn.utility.LDNUtils;
|
||||
import org.dspace.content.DSpaceObject;
|
||||
import org.dspace.content.Item;
|
||||
import org.dspace.content.service.ItemService;
|
||||
import org.dspace.core.Constants;
|
||||
import org.dspace.core.Context;
|
||||
import org.dspace.discovery.indexobject.IndexableLDNNotification;
|
||||
import org.dspace.event.Event;
|
||||
import org.dspace.handle.service.HandleService;
|
||||
import org.dspace.services.ConfigurationService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
|
||||
/**
|
||||
* Implementation of {@link LDNMessageService}
|
||||
*
|
||||
* @author Mohamed Eskander (mohamed.eskander at 4science dot it)
|
||||
*/
|
||||
public class LDNMessageServiceImpl implements LDNMessageService {
|
||||
|
||||
@Autowired(required = true)
|
||||
private LDNMessageDao ldnMessageDao;
|
||||
@Autowired(required = true)
|
||||
private NotifyServiceDao notifyServiceDao;
|
||||
@Autowired(required = true)
|
||||
private ConfigurationService configurationService;
|
||||
@Autowired(required = true)
|
||||
private HandleService handleService;
|
||||
@Autowired(required = true)
|
||||
private ItemService itemService;
|
||||
@Autowired(required = true)
|
||||
private LDNRouter ldnRouter;
|
||||
|
||||
private static final Logger log = org.apache.logging.log4j.LogManager.getLogger(LDNMessageServiceImpl.class);
|
||||
private static final String LDN_ID_PREFIX = "urn:uuid:";
|
||||
|
||||
protected LDNMessageServiceImpl() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public LDNMessageEntity find(Context context, String id) throws SQLException {
|
||||
|
||||
if (id == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
id = id.startsWith(LDN_ID_PREFIX) ? id : LDN_ID_PREFIX + id;
|
||||
return ldnMessageDao.findByID(context, LDNMessageEntity.class, id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<LDNMessageEntity> findAll(Context context) throws SQLException {
|
||||
return ldnMessageDao.findAll(context, LDNMessageEntity.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public LDNMessageEntity create(Context context, String id) throws SQLException {
|
||||
LDNMessageEntity result = ldnMessageDao.findByID(context, LDNMessageEntity.class, id);
|
||||
if (result != null) {
|
||||
throw new SQLException("Duplicate LDN Message ID [" + id + "] detected. This message is rejected.");
|
||||
}
|
||||
return ldnMessageDao.create(context, new LDNMessageEntity(id));
|
||||
}
|
||||
|
||||
@Override
|
||||
public LDNMessageEntity create(Context context, Notification notification, String sourceIp) throws SQLException {
|
||||
LDNMessageEntity ldnMessage = create(context, notification.getId());
|
||||
DSpaceObject obj = findDspaceObjectByUrl(context, notification.getObject().getId());
|
||||
if (obj == null) {
|
||||
if (isTargetCurrent(notification)) {
|
||||
// this means we're sending the notification
|
||||
obj = findDspaceObjectByUrl(context, notification.getObject().getAsObject());
|
||||
// use as:object for sender
|
||||
} else {
|
||||
// this means we're receiving the notification
|
||||
obj = findDspaceObjectByUrl(context, notification.getObject().getAsSubject());
|
||||
// use as:subject for receiver
|
||||
}
|
||||
}
|
||||
ldnMessage.setObject(obj);
|
||||
if (null != notification.getContext()) {
|
||||
ldnMessage.setContext(findDspaceObjectByUrl(context, notification.getContext().getId()));
|
||||
}
|
||||
ldnMessage.setOrigin(findNotifyService(context, notification.getOrigin()));
|
||||
ldnMessage.setInReplyTo(find(context, notification.getInReplyTo()));
|
||||
ObjectMapper mapper = new ObjectMapper();
|
||||
String message = null;
|
||||
try {
|
||||
message = mapper.writeValueAsString(notification);
|
||||
ldnMessage.setMessage(message);
|
||||
} catch (JsonProcessingException e) {
|
||||
log.error("Notification json can't be correctly processed " +
|
||||
"and stored inside the LDN Message Entity" + ldnMessage);
|
||||
log.error(e);
|
||||
}
|
||||
ldnMessage.setType(StringUtils.joinWith(",", notification.getType()));
|
||||
Set<String> notificationType = notification.getType();
|
||||
if (notificationType == null) {
|
||||
log.error("Notification has no notificationType attribute! " + notification);
|
||||
return null;
|
||||
}
|
||||
ArrayList<String> notificationTypeArrayList = new ArrayList<String>(notificationType);
|
||||
// sorting the list
|
||||
Collections.sort(notificationTypeArrayList);
|
||||
ldnMessage.setActivityStreamType(notificationTypeArrayList.get(0));
|
||||
if (notificationTypeArrayList.size() > 1) {
|
||||
ldnMessage.setCoarNotifyType(notificationTypeArrayList.get(1));
|
||||
}
|
||||
ldnMessage.setQueueStatus(LDNMessageEntity.QUEUE_STATUS_QUEUED);
|
||||
ldnMessage.setSourceIp(sourceIp);
|
||||
if (ldnMessage.getOrigin() == null && !"Offer".equalsIgnoreCase(ldnMessage.getActivityStreamType())) {
|
||||
ldnMessage.setQueueStatus(LDNMessageEntity.QUEUE_STATUS_UNTRUSTED);
|
||||
} else {
|
||||
|
||||
boolean ipCheckRangeEnabled = configurationService.getBooleanProperty("ldn.ip-range.enabled", true);
|
||||
if (ipCheckRangeEnabled && !isValidIp(ldnMessage.getOrigin(), sourceIp)) {
|
||||
ldnMessage.setQueueStatus(LDNMessageEntity.QUEUE_STATUS_UNTRUSTED_IP);
|
||||
}
|
||||
}
|
||||
ldnMessage.setQueueTimeout(new Date());
|
||||
|
||||
update(context, ldnMessage);
|
||||
return ldnMessage;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValidIp(NotifyServiceEntity origin, String sourceIp) {
|
||||
|
||||
String lowerIp = origin.getLowerIp();
|
||||
String upperIp = origin.getUpperIp();
|
||||
|
||||
try {
|
||||
InetAddress ip = InetAddress.getByName(sourceIp);
|
||||
InetAddress lowerBoundAddress = InetAddress.getByName(lowerIp);
|
||||
InetAddress upperBoundAddress = InetAddress.getByName(upperIp);
|
||||
|
||||
long ipLong = ipToLong(ip);
|
||||
long lowerBoundLong = ipToLong(lowerBoundAddress);
|
||||
long upperBoundLong = ipToLong(upperBoundAddress);
|
||||
|
||||
return ipLong >= lowerBoundLong && ipLong <= upperBoundLong;
|
||||
} catch (UnknownHostException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private long ipToLong(InetAddress ip) {
|
||||
byte[] octets = ip.getAddress();
|
||||
long result = 0;
|
||||
for (byte octet : octets) {
|
||||
result <<= 8;
|
||||
result |= octet & 0xff;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(Context context, LDNMessageEntity ldnMessage) throws SQLException {
|
||||
// move the queue_status from UNTRUSTED to QUEUED if origin is a known NotifyService
|
||||
if (ldnMessage.getOrigin() != null &&
|
||||
LDNMessageEntity.QUEUE_STATUS_UNTRUSTED.compareTo(ldnMessage.getQueueStatus()) == 0) {
|
||||
ldnMessage.setQueueStatus(LDNMessageEntity.QUEUE_STATUS_QUEUED);
|
||||
}
|
||||
ldnMessageDao.save(context, ldnMessage);
|
||||
UUID notificationUUID = UUID.fromString(ldnMessage.getID().replace(LDN_ID_PREFIX, ""));
|
||||
ArrayList<String> identifiersList = new ArrayList<String>();
|
||||
identifiersList.add(ldnMessage.getID());
|
||||
context.addEvent(
|
||||
new Event(Event.MODIFY, Constants.LDN_MESSAGE,
|
||||
notificationUUID,
|
||||
IndexableLDNNotification.TYPE, identifiersList));
|
||||
}
|
||||
|
||||
private DSpaceObject findDspaceObjectByUrl(Context context, String url) throws SQLException {
|
||||
String dspaceUrl = configurationService.getProperty("dspace.ui.url") + "/handle/";
|
||||
|
||||
if (StringUtils.startsWith(url, dspaceUrl)) {
|
||||
return handleService.resolveToObject(context, url.substring(dspaceUrl.length()));
|
||||
}
|
||||
|
||||
String handleResolver = configurationService.getProperty("handle.canonical.prefix", "https://hdl.handle.net/");
|
||||
if (StringUtils.startsWith(url, handleResolver)) {
|
||||
return handleService.resolveToObject(context, url.substring(handleResolver.length()));
|
||||
}
|
||||
|
||||
dspaceUrl = configurationService.getProperty("dspace.ui.url") + "/items/";
|
||||
if (StringUtils.startsWith(url, dspaceUrl)) {
|
||||
return itemService.find(context, UUID.fromString(url.substring(dspaceUrl.length())));
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public NotifyServiceEntity findNotifyService(Context context, Service service) throws SQLException {
|
||||
return notifyServiceDao.findByLdnUrl(context, service.getInbox());
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<LDNMessageEntity> findOldestMessagesToProcess(Context context) throws SQLException {
|
||||
List<LDNMessageEntity> result = null;
|
||||
int max_attempts = configurationService.getIntProperty("ldn.processor.max.attempts");
|
||||
result = ldnMessageDao.findOldestMessageToProcess(context, max_attempts);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<LDNMessageEntity> findMessagesToBeReprocessed(Context context) throws SQLException {
|
||||
List<LDNMessageEntity> result = null;
|
||||
result = ldnMessageDao.findMessagesToBeReprocessed(context);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<LDNMessageEntity> findProcessingTimedoutMessages(Context context) throws SQLException {
|
||||
List<LDNMessageEntity> result = null;
|
||||
int max_attempts = configurationService.getIntProperty("ldn.processor.max.attempts");
|
||||
result = ldnMessageDao.findProcessingTimedoutMessages(context, max_attempts);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int extractAndProcessMessageFromQueue(Context context) throws SQLException {
|
||||
int count = 0;
|
||||
int timeoutInMinutes = configurationService.getIntProperty("ldn.processor.queue.msg.timeout", 60);
|
||||
|
||||
List<LDNMessageEntity> messages = findOldestMessagesToProcess(context);
|
||||
messages.addAll(findMessagesToBeReprocessed(context));
|
||||
|
||||
Optional<LDNMessageEntity> msgOpt = getSingleMessageEntity(messages);
|
||||
|
||||
while (msgOpt.isPresent()) {
|
||||
LDNProcessor processor = null;
|
||||
LDNMessageEntity msg = msgOpt.get();
|
||||
processor = ldnRouter.route(msg);
|
||||
try {
|
||||
boolean isServiceDisabled = !isServiceEnabled(msg);
|
||||
if (processor == null || isServiceDisabled) {
|
||||
log.warn("No processor found for LDN message " + msg);
|
||||
Integer status = isServiceDisabled ? LDNMessageEntity.QUEUE_STATUS_UNTRUSTED
|
||||
: LDNMessageEntity.QUEUE_STATUS_UNMAPPED_ACTION;
|
||||
msg.setQueueStatus(status);
|
||||
msg.setQueueAttempts(msg.getQueueAttempts() + 1);
|
||||
update(context, msg);
|
||||
} else {
|
||||
msg.setQueueLastStartTime(new Date());
|
||||
msg.setQueueStatus(LDNMessageEntity.QUEUE_STATUS_PROCESSING);
|
||||
msg.setQueueTimeout(DateUtils.addMinutes(new Date(), timeoutInMinutes));
|
||||
update(context, msg);
|
||||
ObjectMapper mapper = new ObjectMapper();
|
||||
Notification notification = mapper.readValue(msg.getMessage(), Notification.class);
|
||||
processor.process(context, notification);
|
||||
msg.setQueueStatus(LDNMessageEntity.QUEUE_STATUS_PROCESSED);
|
||||
count++;
|
||||
}
|
||||
} catch (JsonSyntaxException jse) {
|
||||
log.error("Unable to read JSON notification from LdnMessage " + msg, jse);
|
||||
msg.setQueueStatus(LDNMessageEntity.QUEUE_STATUS_FAILED);
|
||||
} catch (Exception e) {
|
||||
log.error(e);
|
||||
msg.setQueueStatus(LDNMessageEntity.QUEUE_STATUS_FAILED);
|
||||
} finally {
|
||||
msg.setQueueAttempts(msg.getQueueAttempts() + 1);
|
||||
update(context, msg);
|
||||
}
|
||||
|
||||
messages = findOldestMessagesToProcess(context);
|
||||
messages.addAll(findMessagesToBeReprocessed(context));
|
||||
msgOpt = getSingleMessageEntity(messages);
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
private boolean isServiceEnabled(LDNMessageEntity msg) {
|
||||
String localInboxUrl = configurationService.getProperty("ldn.notify.inbox");
|
||||
if (msg.getTarget() == null || StringUtils.equals(msg.getTarget().getLdnUrl(), localInboxUrl)) {
|
||||
return msg.getOrigin().isEnabled();
|
||||
}
|
||||
return msg.getTarget().isEnabled();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int checkQueueMessageTimeout(Context context) throws SQLException {
|
||||
int count = 0;
|
||||
int maxAttempts = configurationService.getIntProperty("ldn.processor.max.attempts", 5);
|
||||
/*
|
||||
* put failed on processing messages with timed-out timeout and
|
||||
* attempts >= configured_max_attempts put queue on processing messages with
|
||||
* timed-out timeout and attempts < configured_max_attempts
|
||||
*/
|
||||
Optional<LDNMessageEntity> msgOpt = getSingleMessageEntity(findProcessingTimedoutMessages(context));
|
||||
|
||||
while (msgOpt.isPresent()) {
|
||||
LDNMessageEntity msg = msgOpt.get();
|
||||
try {
|
||||
if (msg.getQueueAttempts() >= maxAttempts) {
|
||||
msg.setQueueStatus(LDNMessageEntity.QUEUE_STATUS_FAILED);
|
||||
} else {
|
||||
msg.setQueueStatus(LDNMessageEntity.QUEUE_STATUS_QUEUED);
|
||||
}
|
||||
update(context, msg);
|
||||
count++;
|
||||
} catch (SQLException e) {
|
||||
log.error("Can't update LDN message " + msg, e);
|
||||
}
|
||||
msgOpt = getSingleMessageEntity(findProcessingTimedoutMessages(context));
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
public Optional<LDNMessageEntity> getSingleMessageEntity(Collection<LDNMessageEntity> messages) {
|
||||
return messages.stream().findFirst();
|
||||
}
|
||||
|
||||
@Override
|
||||
public NotifyRequestStatus findRequestsByItem(Context context, Item item) throws SQLException {
|
||||
NotifyRequestStatus result = new NotifyRequestStatus();
|
||||
result.setItemUuid(item.getID());
|
||||
List<LDNMessageEntity> msgs = ldnMessageDao.findAllMessagesByItem(
|
||||
context, item, "Offer");
|
||||
if (msgs != null && !msgs.isEmpty()) {
|
||||
for (LDNMessageEntity msg : msgs) {
|
||||
RequestStatus offer = new RequestStatus();
|
||||
NotifyServiceEntity nse = msg.getOrigin();
|
||||
if (nse == null) {
|
||||
nse = msg.getTarget();
|
||||
}
|
||||
offer.setServiceName(nse == null ? "Unknown Service" : nse.getName());
|
||||
offer.setServiceUrl(nse == null ? "" : nse.getUrl());
|
||||
offer.setOfferType(LDNUtils.getNotifyType(msg.getCoarNotifyType()));
|
||||
List<LDNMessageEntity> acks = ldnMessageDao.findAllRelatedMessagesByItem(
|
||||
context, msg, item, "Accept", "TentativeReject", "TentativeAccept", "Announce");
|
||||
if (acks == null || acks.isEmpty()) {
|
||||
offer.setStatus(NotifyRequestStatusEnum.REQUESTED);
|
||||
} else if (acks.stream()
|
||||
.filter(c -> (c.getActivityStreamType().equalsIgnoreCase("TentativeAccept") ||
|
||||
c.getActivityStreamType().equalsIgnoreCase("Accept")))
|
||||
.findAny().isPresent()) {
|
||||
offer.setStatus(NotifyRequestStatusEnum.ACCEPTED);
|
||||
} else if (acks.stream()
|
||||
.filter(c -> c.getActivityStreamType().equalsIgnoreCase("TentativeReject"))
|
||||
.findAny().isPresent()) {
|
||||
offer.setStatus(NotifyRequestStatusEnum.REJECTED);
|
||||
}
|
||||
if (acks.stream().filter(
|
||||
c -> c.getActivityStreamType().equalsIgnoreCase("Announce"))
|
||||
.findAny().isEmpty()) {
|
||||
result.addRequestStatus(offer);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public void delete(Context context, LDNMessageEntity ldnMessage) throws SQLException {
|
||||
ldnMessageDao.delete(context, ldnMessage);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isTargetCurrent(Notification notification) {
|
||||
String localInboxUrl = configurationService.getProperty("ldn.notify.inbox");
|
||||
return StringUtils.equals(notification.getTarget().getInbox(), localInboxUrl);
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,62 @@
|
||||
/**
|
||||
* The contents of this file are subject to the license and copyright
|
||||
* detailed in the LICENSE and NOTICE files at the root of the source
|
||||
* tree and available online at
|
||||
*
|
||||
* http://www.dspace.org/license/
|
||||
*/
|
||||
package org.dspace.app.ldn.service.impl;
|
||||
|
||||
import java.sql.SQLException;
|
||||
import java.util.List;
|
||||
|
||||
import org.dspace.app.ldn.NotifyPatternToTrigger;
|
||||
import org.dspace.app.ldn.dao.NotifyPatternToTriggerDao;
|
||||
import org.dspace.app.ldn.service.NotifyPatternToTriggerService;
|
||||
import org.dspace.content.Item;
|
||||
import org.dspace.core.Context;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
/**
|
||||
* Implementation of {@link NotifyPatternToTriggerService}.
|
||||
*
|
||||
* @author Mohamed Eskander (mohamed.eskander at 4science.com)
|
||||
*/
|
||||
public class NotifyPatternToTriggerImpl implements NotifyPatternToTriggerService {
|
||||
|
||||
@Autowired(required = true)
|
||||
private NotifyPatternToTriggerDao notifyPatternToTriggerDao;
|
||||
|
||||
@Override
|
||||
public List<NotifyPatternToTrigger> findAll(Context context) throws SQLException {
|
||||
return notifyPatternToTriggerDao.findAll(context, NotifyPatternToTrigger.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<NotifyPatternToTrigger> findByItem(Context context, Item item) throws SQLException {
|
||||
return notifyPatternToTriggerDao.findByItem(context, item);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<NotifyPatternToTrigger> findByItemAndPattern(Context context, Item item, String pattern)
|
||||
throws SQLException {
|
||||
return notifyPatternToTriggerDao.findByItemAndPattern(context, item, pattern);
|
||||
}
|
||||
|
||||
@Override
|
||||
public NotifyPatternToTrigger create(Context context) throws SQLException {
|
||||
NotifyPatternToTrigger notifyPatternToTrigger = new NotifyPatternToTrigger();
|
||||
return notifyPatternToTriggerDao.create(context, notifyPatternToTrigger);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(Context context, NotifyPatternToTrigger notifyPatternToTrigger) throws SQLException {
|
||||
notifyPatternToTriggerDao.save(context, notifyPatternToTrigger);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delete(Context context, NotifyPatternToTrigger notifyPatternToTrigger) throws SQLException {
|
||||
notifyPatternToTriggerDao.delete(context, notifyPatternToTrigger);
|
||||
}
|
||||
|
||||
}
|
@@ -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.ldn.service.impl;
|
||||
|
||||
import java.sql.SQLException;
|
||||
import java.util.List;
|
||||
|
||||
import org.dspace.app.ldn.NotifyServiceEntity;
|
||||
import org.dspace.app.ldn.dao.NotifyServiceDao;
|
||||
import org.dspace.app.ldn.service.NotifyService;
|
||||
import org.dspace.core.Context;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
/**
|
||||
* Implementation of {@link NotifyService}.
|
||||
*
|
||||
* @author Mohamed Eskander (mohamed.eskander at 4science.com)
|
||||
*/
|
||||
public class NotifyServiceImpl implements NotifyService {
|
||||
|
||||
@Autowired(required = true)
|
||||
private NotifyServiceDao notifyServiceDao;
|
||||
|
||||
@Override
|
||||
public List<NotifyServiceEntity> findAll(Context context) throws SQLException {
|
||||
return notifyServiceDao.findAll(context, NotifyServiceEntity.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public NotifyServiceEntity find(Context context, Integer id) throws SQLException {
|
||||
return notifyServiceDao.findByID(context, NotifyServiceEntity.class, id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public NotifyServiceEntity create(Context context, String name) throws SQLException {
|
||||
NotifyServiceEntity notifyServiceEntity = new NotifyServiceEntity();
|
||||
notifyServiceEntity.setName(name);
|
||||
return notifyServiceDao.create(context, notifyServiceEntity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(Context context, NotifyServiceEntity notifyServiceEntity) throws SQLException {
|
||||
notifyServiceDao.save(context, notifyServiceEntity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delete(Context context, NotifyServiceEntity notifyServiceEntity) throws SQLException {
|
||||
notifyServiceDao.delete(context, notifyServiceEntity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public NotifyServiceEntity findByLdnUrl(Context context, String ldnUrl) throws SQLException {
|
||||
return notifyServiceDao.findByLdnUrl(context, ldnUrl);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<NotifyServiceEntity> findManualServicesByInboundPattern(Context context, String pattern)
|
||||
throws SQLException {
|
||||
return notifyServiceDao.findManualServicesByInboundPattern(context, pattern);
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,59 @@
|
||||
/**
|
||||
* The contents of this file are subject to the license and copyright
|
||||
* detailed in the LICENSE and NOTICE files at the root of the source
|
||||
* tree and available online at
|
||||
*
|
||||
* http://www.dspace.org/license/
|
||||
*/
|
||||
package org.dspace.app.ldn.service.impl;
|
||||
|
||||
import java.sql.SQLException;
|
||||
import java.util.List;
|
||||
|
||||
import org.dspace.app.ldn.NotifyServiceEntity;
|
||||
import org.dspace.app.ldn.NotifyServiceInboundPattern;
|
||||
import org.dspace.app.ldn.dao.NotifyServiceInboundPatternDao;
|
||||
import org.dspace.app.ldn.service.NotifyServiceInboundPatternService;
|
||||
import org.dspace.core.Context;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
/**
|
||||
* Implementation Service class for the {@link NotifyServiceInboundPatternService}.
|
||||
*
|
||||
* @author Mohamed Eskander (mohamed.eskander at 4science.com)
|
||||
*/
|
||||
public class NotifyServiceInboundPatternServiceImpl implements NotifyServiceInboundPatternService {
|
||||
|
||||
@Autowired
|
||||
private NotifyServiceInboundPatternDao inboundPatternDao;
|
||||
|
||||
@Override
|
||||
public NotifyServiceInboundPattern findByServiceAndPattern(Context context,
|
||||
NotifyServiceEntity notifyServiceEntity,
|
||||
String pattern) throws SQLException {
|
||||
return inboundPatternDao.findByServiceAndPattern(context, notifyServiceEntity, pattern);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<NotifyServiceInboundPattern> findAutomaticPatterns(Context context) throws SQLException {
|
||||
return inboundPatternDao.findAutomaticPatterns(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public NotifyServiceInboundPattern create(Context context, NotifyServiceEntity notifyServiceEntity)
|
||||
throws SQLException {
|
||||
NotifyServiceInboundPattern inboundPattern = new NotifyServiceInboundPattern();
|
||||
inboundPattern.setNotifyService(notifyServiceEntity);
|
||||
return inboundPatternDao.create(context, inboundPattern);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(Context context, NotifyServiceInboundPattern inboundPattern) throws SQLException {
|
||||
inboundPatternDao.save(context, inboundPattern);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delete(Context context, NotifyServiceInboundPattern inboundPattern) throws SQLException {
|
||||
inboundPatternDao.delete(context, inboundPattern);
|
||||
}
|
||||
}
|
@@ -0,0 +1,96 @@
|
||||
/**
|
||||
* 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.ldn.utility;
|
||||
|
||||
import static org.apache.commons.lang3.StringUtils.EMPTY;
|
||||
|
||||
import java.util.UUID;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* Some linked data notification utilities.
|
||||
*/
|
||||
public class LDNUtils {
|
||||
|
||||
private final static Pattern UUID_REGEX_PATTERN = Pattern.compile(
|
||||
"\\p{XDigit}{8}-\\p{XDigit}{4}-\\p{XDigit}{4}-\\p{XDigit}{4}-\\p{XDigit}{12}");
|
||||
|
||||
private final static String SIMPLE_PROTOCOL_REGEX = "^(http[s]?://www\\.|http[s]?://|www\\.)";
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private LDNUtils() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the URL contains an UUID. Used to determine item id from item URL.
|
||||
*
|
||||
* @param url item URL
|
||||
* @return boolean true if URL has UUID, false otherwise
|
||||
*/
|
||||
public static boolean hasUUIDInURL(String url) {
|
||||
Matcher matcher = UUID_REGEX_PATTERN.matcher(url);
|
||||
|
||||
return matcher.find();
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract UUID from URL.
|
||||
*
|
||||
* @param url item URL
|
||||
* @return UUID item id
|
||||
*/
|
||||
public static UUID getUUIDFromURL(String url) {
|
||||
Matcher matcher = UUID_REGEX_PATTERN.matcher(url);
|
||||
StringBuilder handle = new StringBuilder();
|
||||
if (matcher.find()) {
|
||||
handle.append(matcher.group(0));
|
||||
}
|
||||
return UUID.fromString(handle.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove http or https protocol from URL.
|
||||
*
|
||||
* @param url URL
|
||||
* @return String URL without protocol
|
||||
*/
|
||||
public static String removedProtocol(String url) {
|
||||
return url.replaceFirst(SIMPLE_PROTOCOL_REGEX, EMPTY);
|
||||
}
|
||||
|
||||
/**
|
||||
* Custom context resolver processing. Currently converting DOI URL to DOI id.
|
||||
*
|
||||
* @param value context ietf:cite-as
|
||||
* @return String ietf:cite-as identifier
|
||||
*/
|
||||
public static String processContextResolverId(String value) {
|
||||
String resolverId = value;
|
||||
resolverId = resolverId.replace("https://doi.org/", "doi:");
|
||||
|
||||
return resolverId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear the coarNotifyType from the source code.
|
||||
*
|
||||
* @param coarNotifyType coar Notify Type to sanitize
|
||||
* @return String just the notify type
|
||||
*/
|
||||
public static String getNotifyType(String coarNotifyType) {
|
||||
String justNotifyType = coarNotifyType;
|
||||
justNotifyType = justNotifyType.substring(justNotifyType.lastIndexOf(":") + 1);
|
||||
justNotifyType = justNotifyType.replace("Action", "");
|
||||
return justNotifyType;
|
||||
}
|
||||
|
||||
}
|
@@ -148,7 +148,7 @@ public abstract class ImageMagickThumbnailFilter extends MediaFilter {
|
||||
// the thumbnail because the CropBox is generally used to define the
|
||||
// area displayed when a user opens the PDF on a screen, whereas the
|
||||
// MediaBox is used for print. Not all PDFs set these correctly, so
|
||||
// we can use ImageMagick's default behavior unless we see an explit
|
||||
// we can use ImageMagick's default behavior unless we see an explicit
|
||||
// CropBox. Note: we don't need to do anything special to detect if
|
||||
// the CropBox is missing or empty because pdfbox will set it to the
|
||||
// same size as the MediaBox if it doesn't exist. Also note that we
|
||||
|
@@ -7,6 +7,7 @@
|
||||
*/
|
||||
package org.dspace.app.mediafilter;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
@@ -37,8 +38,9 @@ import org.dspace.utils.DSpace;
|
||||
* MFM: -v verbose outputs all extracted text to STDOUT; -f force forces all
|
||||
* bitstreams to be processed, even if they have been before; -n noindex does not
|
||||
* recreate index after processing bitstreams; -i [identifier] limits processing
|
||||
* scope to a community, collection or item; and -m [max] limits processing to a
|
||||
* maximum number of items.
|
||||
* scope to a community, collection or item; -m [max] limits processing to a
|
||||
* maximum number of items; -fd [fromdate] takes only items starting from this date,
|
||||
* filtering by last_modified in the item table.
|
||||
*/
|
||||
public class MediaFilterScript extends DSpaceRunnable<MediaFilterScriptConfiguration> {
|
||||
|
||||
@@ -60,6 +62,7 @@ public class MediaFilterScript extends DSpaceRunnable<MediaFilterScriptConfigura
|
||||
private String[] filterNames;
|
||||
private String[] skipIds = null;
|
||||
private Map<String, List<String>> filterFormats = new HashMap<>();
|
||||
private LocalDate fromDate = null;
|
||||
|
||||
public MediaFilterScriptConfiguration getScriptConfiguration() {
|
||||
return new DSpace().getServiceManager()
|
||||
@@ -112,6 +115,10 @@ public class MediaFilterScript extends DSpaceRunnable<MediaFilterScriptConfigura
|
||||
skipIds = commandLine.getOptionValues('s');
|
||||
}
|
||||
|
||||
if (commandLine.hasOption('d')) {
|
||||
fromDate = LocalDate.parse(commandLine.getOptionValue('d'));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -215,6 +222,10 @@ public class MediaFilterScript extends DSpaceRunnable<MediaFilterScriptConfigura
|
||||
mediaFilterService.setSkipList(Arrays.asList(skipIds));
|
||||
}
|
||||
|
||||
if (fromDate != null) {
|
||||
mediaFilterService.setFromDate(fromDate);
|
||||
}
|
||||
|
||||
Context c = null;
|
||||
|
||||
try {
|
||||
|
@@ -52,6 +52,8 @@ public class MediaFilterScriptConfiguration<T extends MediaFilterScript> extends
|
||||
.build();
|
||||
options.addOption(pluginOption);
|
||||
|
||||
options.addOption("d", "fromdate", true, "Process only item from specified last modified date");
|
||||
|
||||
Option skipOption = Option.builder("s")
|
||||
.longOpt("skip")
|
||||
.hasArg()
|
||||
|
@@ -9,8 +9,11 @@ package org.dspace.app.mediafilter;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.sql.SQLException;
|
||||
import java.time.LocalDate;
|
||||
import java.time.ZoneId;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
@@ -93,6 +96,7 @@ public class MediaFilterServiceImpl implements MediaFilterService, InitializingB
|
||||
protected boolean isVerbose = false;
|
||||
protected boolean isQuiet = false;
|
||||
protected boolean isForce = false; // default to not forced
|
||||
protected LocalDate fromDate = null;
|
||||
|
||||
protected MediaFilterServiceImpl() {
|
||||
|
||||
@@ -120,6 +124,15 @@ public class MediaFilterServiceImpl implements MediaFilterService, InitializingB
|
||||
for (Community topLevelCommunity : topLevelCommunities) {
|
||||
applyFiltersCommunity(context, topLevelCommunity);
|
||||
}
|
||||
} else if (fromDate != null) {
|
||||
Iterator<Item> itemIterator =
|
||||
itemService.findByLastModifiedSince(
|
||||
context,
|
||||
Date.from(fromDate.atStartOfDay(ZoneId.systemDefault()).toInstant())
|
||||
);
|
||||
while (itemIterator.hasNext() && processed < max2Process) {
|
||||
applyFiltersItem(context, itemIterator.next());
|
||||
}
|
||||
} else {
|
||||
//otherwise, just find every item and process
|
||||
Iterator<Item> itemIterator = itemService.findAll(context);
|
||||
@@ -588,4 +601,9 @@ public class MediaFilterServiceImpl implements MediaFilterService, InitializingB
|
||||
public void setLogHandler(DSpaceRunnableHandler handler) {
|
||||
this.handler = handler;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setFromDate(LocalDate fromDate) {
|
||||
this.fromDate = fromDate;
|
||||
}
|
||||
}
|
||||
|
@@ -8,6 +8,7 @@
|
||||
package org.dspace.app.mediafilter.service;
|
||||
|
||||
import java.sql.SQLException;
|
||||
import java.time.LocalDate;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@@ -149,4 +150,6 @@ public interface MediaFilterService {
|
||||
* @param handler
|
||||
*/
|
||||
public void setLogHandler(DSpaceRunnableHandler handler);
|
||||
|
||||
public void setFromDate(LocalDate fromDate);
|
||||
}
|
||||
|
@@ -8,19 +8,19 @@
|
||||
package org.dspace.app.requestitem;
|
||||
|
||||
import java.util.Date;
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.FetchType;
|
||||
import javax.persistence.GeneratedValue;
|
||||
import javax.persistence.GenerationType;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.JoinColumn;
|
||||
import javax.persistence.ManyToOne;
|
||||
import javax.persistence.SequenceGenerator;
|
||||
import javax.persistence.Table;
|
||||
import javax.persistence.Temporal;
|
||||
import javax.persistence.TemporalType;
|
||||
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.FetchType;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.GenerationType;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.JoinColumn;
|
||||
import jakarta.persistence.ManyToOne;
|
||||
import jakarta.persistence.SequenceGenerator;
|
||||
import jakarta.persistence.Table;
|
||||
import jakarta.persistence.Temporal;
|
||||
import jakarta.persistence.TemporalType;
|
||||
import org.dspace.content.Bitstream;
|
||||
import org.dspace.content.Item;
|
||||
import org.dspace.core.Context;
|
||||
|
@@ -11,11 +11,11 @@ package org.dspace.app.requestitem;
|
||||
import java.io.IOException;
|
||||
import java.sql.SQLException;
|
||||
import java.util.List;
|
||||
import javax.annotation.ManagedBean;
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
import javax.mail.MessagingException;
|
||||
|
||||
import jakarta.annotation.ManagedBean;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.inject.Singleton;
|
||||
import jakarta.mail.MessagingException;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.dspace.app.requestitem.service.RequestItemService;
|
||||
|
@@ -9,11 +9,11 @@ package org.dspace.app.requestitem.dao.impl;
|
||||
|
||||
import java.sql.SQLException;
|
||||
import java.util.Iterator;
|
||||
import javax.persistence.Query;
|
||||
import javax.persistence.criteria.CriteriaBuilder;
|
||||
import javax.persistence.criteria.CriteriaQuery;
|
||||
import javax.persistence.criteria.Root;
|
||||
|
||||
import jakarta.persistence.Query;
|
||||
import jakarta.persistence.criteria.CriteriaBuilder;
|
||||
import jakarta.persistence.criteria.CriteriaQuery;
|
||||
import jakarta.persistence.criteria.Root;
|
||||
import org.dspace.app.requestitem.RequestItem;
|
||||
import org.dspace.app.requestitem.RequestItem_;
|
||||
import org.dspace.app.requestitem.dao.RequestItemDAO;
|
||||
@@ -44,8 +44,12 @@ public class RequestItemDAOImpl extends AbstractHibernateDAO<RequestItem> implem
|
||||
}
|
||||
@Override
|
||||
public Iterator<RequestItem> findByItem(Context context, Item item) throws SQLException {
|
||||
Query query = createQuery(context, "FROM RequestItem WHERE item_id= :uuid");
|
||||
query.setParameter("uuid", item.getID());
|
||||
CriteriaBuilder criteriaBuilder = getCriteriaBuilder(context);
|
||||
CriteriaQuery criteriaQuery = getCriteriaQuery(criteriaBuilder, RequestItem.class);
|
||||
Root<RequestItem> requestItemRoot = criteriaQuery.from(RequestItem.class);
|
||||
criteriaQuery.select(requestItemRoot);
|
||||
criteriaQuery.where(criteriaBuilder.equal(requestItemRoot.get(RequestItem_.item), item));
|
||||
Query query = createQuery(context, criteriaQuery);
|
||||
return iterate(query);
|
||||
}
|
||||
}
|
||||
|
@@ -12,8 +12,8 @@ import java.io.InputStream;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import javax.annotation.PostConstruct;
|
||||
|
||||
import jakarta.annotation.PostConstruct;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.http.HttpEntity;
|
||||
@@ -78,7 +78,7 @@ public class SHERPAService {
|
||||
@SuppressWarnings("unused")
|
||||
@PostConstruct
|
||||
private void init() {
|
||||
// Get endoint and API key from configuration
|
||||
// Get endpoint and API key from configuration
|
||||
endpoint = configurationService.getProperty("sherpa.romeo.url",
|
||||
"https://v2.sherpa.ac.uk/cgi/retrieve");
|
||||
apiKey = configurationService.getProperty("sherpa.romeo.apikey");
|
||||
@@ -93,7 +93,7 @@ public class SHERPAService {
|
||||
* @param query ISSN string to pass in an "issn equals" API query
|
||||
* @return SHERPAResponse containing an error or journal policies
|
||||
*/
|
||||
@Cacheable(key = "#query", cacheNames = "sherpa.searchByJournalISSN")
|
||||
@Cacheable(key = "#query", condition = "#query != null", cacheNames = "sherpa.searchByJournalISSN")
|
||||
public SHERPAResponse searchByJournalISSN(String query) {
|
||||
return performRequest("publication", "issn", "equals", query, 0, 1);
|
||||
}
|
||||
@@ -156,7 +156,7 @@ public class SHERPAService {
|
||||
|
||||
// If the response body is valid, pass to SHERPAResponse for parsing as JSON
|
||||
if (null != responseBody) {
|
||||
log.debug("Non-null SHERPA resonse received for query of " + value);
|
||||
log.debug("Non-null SHERPA response received for query of " + value);
|
||||
InputStream content = null;
|
||||
try {
|
||||
content = responseBody.getContent();
|
||||
@@ -259,7 +259,7 @@ public class SHERPAService {
|
||||
|
||||
// If the response body is valid, pass to SHERPAResponse for parsing as JSON
|
||||
if (null != responseBody) {
|
||||
log.debug("Non-null SHERPA resonse received for query of " + value);
|
||||
log.debug("Non-null SHERPA response received for query of " + value);
|
||||
InputStream content = null;
|
||||
try {
|
||||
content = responseBody.getContent();
|
||||
|
@@ -13,7 +13,7 @@ import org.ehcache.event.CacheEvent;
|
||||
import org.ehcache.event.CacheEventListener;
|
||||
|
||||
/**
|
||||
* This is a EHCache listner responsible for logging sherpa cache events. It is
|
||||
* This is a EHCache listener responsible for logging sherpa cache events. It is
|
||||
* bound to the sherpa cache via the dspace/config/ehcache.xml file. We need a
|
||||
* dedicated Logger for each cache as the CacheEvent doesn't include details
|
||||
* about where the event occur
|
||||
|
@@ -47,7 +47,7 @@ public class SHERPASubmitService {
|
||||
}
|
||||
|
||||
/**
|
||||
* Setter for SHERPA service, reponsible for actual HTTP API calls
|
||||
* Setter for SHERPA service, responsible for actual HTTP API calls
|
||||
* @see "dspace-dspace-addon-sherpa-configuration-services.xml"
|
||||
* @param sherpaService
|
||||
*/
|
||||
|
@@ -141,7 +141,7 @@ public class GenerateSitemaps {
|
||||
public static void deleteSitemaps() throws IOException {
|
||||
File outputDir = new File(configurationService.getProperty("sitemap.dir"));
|
||||
if (!outputDir.exists() && !outputDir.isDirectory()) {
|
||||
log.error("Unable to delete sitemaps directory, doesn't exist or isn't a directort");
|
||||
log.error("Unable to delete sitemaps directory, doesn't exist or isn't a directory");
|
||||
} else {
|
||||
FileUtils.deleteDirectory(outputDir);
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user