diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 1749c28fe29..3b9311abeca 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -53,11 +53,11 @@ jobs:
steps:
# https://github.com/actions/checkout
- name: Checkout codebase
- uses: actions/checkout@v4
+ uses: actions/checkout@v6
# https://github.com/actions/setup-java
- name: Install JDK ${{ matrix.java }}
- uses: actions/setup-java@v4
+ uses: actions/setup-java@v5
with:
java-version: ${{ matrix.java }}
distribution: 'temurin'
@@ -73,14 +73,14 @@ jobs:
# (This artifact is downloadable at the bottom of any job's summary page)
- name: Upload Results of ${{ matrix.type }} to Artifact
if: ${{ failure() }}
- uses: actions/upload-artifact@v4
+ uses: actions/upload-artifact@v7
with:
name: ${{ matrix.type }} results
path: ${{ matrix.resultsdir }}
# Upload code coverage report to artifact, so that it can be shared with the 'codecov' job (see below)
- name: Upload code coverage report to Artifact
- uses: actions/upload-artifact@v4
+ uses: actions/upload-artifact@v7
with:
name: ${{ matrix.type }} coverage report
path: 'dspace/target/site/jacoco-aggregate/jacoco.xml'
diff --git a/.github/workflows/codescan.yml b/.github/workflows/codescan.yml
index 720a0cadf1a..a1c3833dd40 100644
--- a/.github/workflows/codescan.yml
+++ b/.github/workflows/codescan.yml
@@ -31,11 +31,11 @@ jobs:
steps:
# https://github.com/actions/checkout
- name: Checkout repository
- uses: actions/checkout@v4
+ uses: actions/checkout@v6
# https://github.com/actions/setup-java
- name: Install JDK
- uses: actions/setup-java@v4
+ uses: actions/setup-java@v5
with:
java-version: 11
distribution: 'temurin'
@@ -43,7 +43,7 @@ jobs:
# Initializes the CodeQL tools for scanning.
# https://github.com/github/codeql-action
- name: Initialize CodeQL
- uses: github/codeql-action/init@v2
+ uses: github/codeql-action/init@v4
with:
# Codescan Javascript as well since a few JS files exist in REST API's interface
languages: java, javascript
@@ -52,8 +52,8 @@ jobs:
# NOTE: Based on testing, this autobuild process works well for DSpace. A custom
# DSpace build w/caching (like in build.yml) was about the same speed as autobuild.
- name: Autobuild
- uses: github/codeql-action/autobuild@v2
+ uses: github/codeql-action/autobuild@v4
# Perform GitHub Code Scanning.
- name: Perform CodeQL Analysis
- uses: github/codeql-action/analyze@v2
+ uses: github/codeql-action/analyze@v4
diff --git a/.github/workflows/port_merged_pull_request.yml b/.github/workflows/port_merged_pull_request.yml
index 857f22755e4..676ad45ba26 100644
--- a/.github/workflows/port_merged_pull_request.yml
+++ b/.github/workflows/port_merged_pull_request.yml
@@ -23,11 +23,11 @@ jobs:
if: github.event.pull_request.merged
steps:
# Checkout code
- - uses: actions/checkout@v4
+ - uses: actions/checkout@v6
# Port PR to other branch (ONLY if labeled with "port to")
# See https://github.com/korthout/backport-action
- name: Create backport pull requests
- uses: korthout/backport-action@v2
+ uses: korthout/backport-action@v4
with:
# Trigger based on a "port to [branch]" label on PR
# (This label must specify the branch name to port to)
diff --git a/.github/workflows/pull_request_opened.yml b/.github/workflows/pull_request_opened.yml
index bbac52af243..e2b6e8ba9c2 100644
--- a/.github/workflows/pull_request_opened.yml
+++ b/.github/workflows/pull_request_opened.yml
@@ -21,4 +21,4 @@ jobs:
# Assign the PR to whomever created it. This is useful for visualizing assignments on project boards
# See https://github.com/toshimaru/auto-author-assign
- name: Assign PR to creator
- uses: toshimaru/auto-author-assign@v2.1.0
+ uses: toshimaru/auto-author-assign@v3.0.1
diff --git a/.github/workflows/reusable-docker-build.yml b/.github/workflows/reusable-docker-build.yml
index 2e37477bee6..a45c973b977 100644
--- a/.github/workflows/reusable-docker-build.yml
+++ b/.github/workflows/reusable-docker-build.yml
@@ -105,7 +105,7 @@ jobs:
steps:
# https://github.com/actions/checkout
- name: Checkout codebase
- uses: actions/checkout@v4
+ uses: actions/checkout@v6
- name: Add version
if: ${{ inputs.run_python_version_script }}
@@ -113,7 +113,7 @@ jobs:
# https://github.com/docker/setup-buildx-action
- name: Setup Docker Buildx
- uses: docker/setup-buildx-action@v3
+ uses: docker/setup-buildx-action@v4
# https://github.com/docker/setup-qemu-action
- name: Set up QEMU emulation to build for multiple architectures
@@ -132,7 +132,7 @@ jobs:
# Get Metadata for docker_build_deps step below
- name: Sync metadata (tags, labels) from GitHub to Docker for image
id: meta_build
- uses: docker/metadata-action@v5
+ uses: docker/metadata-action@v6
with:
images: ${{ env.IMAGE_NAME }}
tags: ${{ env.IMAGE_TAGS }}
@@ -141,7 +141,7 @@ jobs:
# https://github.com/docker/build-push-action
- name: Build and push image
id: docker_build
- uses: docker/build-push-action@v5
+ uses: docker/build-push-action@v7
with:
build-contexts: |
${{ inputs.dockerfile_additional_contexts }}
@@ -154,6 +154,10 @@ jobs:
# Use tags / labels provided by 'docker/metadata-action' above
tags: ${{ steps.meta_build.outputs.tags }}
labels: ${{ steps.meta_build.outputs.labels }}
+ # Use GitHub cache to load cached Docker images and cache the results of this build
+ # This decreases the number of images we need to fetch from DockerHub
+ cache-from: type=gha,scope=${{ inputs.build_id }}
+ cache-to: type=gha,scope=${{ inputs.build_id }},mode=min
# Export the digest of Docker build locally (for non PRs only)
# - name: Export Docker build digest
@@ -236,4 +240,4 @@ jobs:
# - name: Inspect image
# run: |
- # docker buildx imagetools inspect ${{ env.IMAGE_NAME }}:${{ steps.meta.outputs.version }}
\ No newline at end of file
+ # docker buildx imagetools inspect ${{ env.IMAGE_NAME }}:${{ steps.meta.outputs.version }}
diff --git a/Dockerfile b/Dockerfile
index c4a453c42c1..3da85193fe8 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -38,16 +38,11 @@ ARG TARGET_DIR=dspace-installer
# COPY the /install directory from 'build' container to /dspace-src in this container
COPY --from=build /install /dspace-src
WORKDIR /dspace-src
-# Create the initial install deployment using ANT
-ENV ANT_VERSION=1.10.13
-ENV ANT_HOME=/tmp/ant-$ANT_VERSION
-ENV PATH=$ANT_HOME/bin:$PATH
-# Download and install 'ant'
-RUN mkdir $ANT_HOME && \
- curl --silent --show-error --location --fail --retry 5 --output /tmp/apache-ant.tar.gz \
- https://archive.apache.org/dist/ant/binaries/apache-ant-${ANT_VERSION}-bin.tar.gz && \
- tar -zx --strip-components=1 -f /tmp/apache-ant.tar.gz -C $ANT_HOME && \
- rm /tmp/apache-ant.tar.gz
+# Install Apache Ant
+RUN apt-get update \
+ && apt-get install -y --no-install-recommends ant \
+ && apt-get purge -y --auto-remove \
+ && rm -rf /var/lib/apt/lists/*
# Run necessary 'ant' deploy scripts
RUN ant init_installation update_configs update_code update_webapps
diff --git a/Dockerfile.cli b/Dockerfile.cli
index 4c55629f0b5..a765aa43351 100644
--- a/Dockerfile.cli
+++ b/Dockerfile.cli
@@ -34,16 +34,11 @@ ARG TARGET_DIR=dspace-installer
# COPY the /install directory from 'build' container to /dspace-src in this container
COPY --from=build /install /dspace-src
WORKDIR /dspace-src
-# Create the initial install deployment using ANT
-ENV ANT_VERSION=1.10.13
-ENV ANT_HOME=/tmp/ant-$ANT_VERSION
-ENV PATH=$ANT_HOME/bin:$PATH
-# Download and install 'ant'
-RUN mkdir $ANT_HOME && \
- curl --silent --show-error --location --fail --retry 5 --output /tmp/apache-ant.tar.gz \
- https://archive.apache.org/dist/ant/binaries/apache-ant-${ANT_VERSION}-bin.tar.gz && \
- tar -zx --strip-components=1 -f /tmp/apache-ant.tar.gz -C $ANT_HOME && \
- rm /tmp/apache-ant.tar.gz
+# Install Apache Ant
+RUN apt-get update \
+ && apt-get install -y --no-install-recommends ant \
+ && apt-get purge -y --auto-remove \
+ && rm -rf /var/lib/apt/lists/*
# Run necessary 'ant' deploy scripts
RUN ant init_installation update_configs update_code
diff --git a/Dockerfile.test b/Dockerfile.test
index 65e9cb956a4..8d14834a9bc 100644
--- a/Dockerfile.test
+++ b/Dockerfile.test
@@ -37,16 +37,11 @@ ARG TARGET_DIR=dspace-installer
# COPY the /install directory from 'build' container to /dspace-src in this container
COPY --from=build /install /dspace-src
WORKDIR /dspace-src
-# Create the initial install deployment using ANT
-ENV ANT_VERSION=1.10.12
-ENV ANT_HOME=/tmp/ant-$ANT_VERSION
-ENV PATH=$ANT_HOME/bin:$PATH
-# Download and install 'ant'
-RUN mkdir $ANT_HOME && \
- curl --silent --show-error --location --fail --retry 5 --output /tmp/apache-ant.tar.gz \
- https://archive.apache.org/dist/ant/binaries/apache-ant-${ANT_VERSION}-bin.tar.gz && \
- tar -zx --strip-components=1 -f /tmp/apache-ant.tar.gz -C $ANT_HOME && \
- rm /tmp/apache-ant.tar.gz
+# Install Apache Ant
+RUN apt-get update \
+ && apt-get install -y --no-install-recommends ant \
+ && apt-get purge -y --auto-remove \
+ && rm -rf /var/lib/apt/lists/*
# Run necessary 'ant' deploy scripts
RUN ant init_installation update_configs update_code update_webapps
diff --git a/LICENSES_THIRD_PARTY b/LICENSES_THIRD_PARTY
index 92868467c98..fdb56efc360 100644
--- a/LICENSES_THIRD_PARTY
+++ b/LICENSES_THIRD_PARTY
@@ -21,19 +21,14 @@ https://wiki.lyrasis.org/display/DSPACE/Code+Contribution+Guidelines
Apache Software License, Version 2.0:
* Ant-Contrib Tasks (ant-contrib:ant-contrib:1.0b3 - http://ant-contrib.sourceforge.net)
- * AWS SDK for Java - Core (com.amazonaws:aws-java-sdk-core:1.12.785 - https://aws.amazon.com/sdkforjava)
- * AWS Java SDK for AWS KMS (com.amazonaws:aws-java-sdk-kms:1.12.785 - https://aws.amazon.com/sdkforjava)
- * AWS Java SDK for Amazon S3 (com.amazonaws:aws-java-sdk-s3:1.12.785 - https://aws.amazon.com/sdkforjava)
- * JMES Path Query library (com.amazonaws:jmespath-java:1.12.785 - https://aws.amazon.com/sdkforjava)
* HPPC Collections (com.carrotsearch:hppc:0.8.1 - http://labs.carrotsearch.com/hppc.html/hppc)
* com.drewnoakes:metadata-extractor (com.drewnoakes:metadata-extractor:2.19.0 - https://drewnoakes.com/code/exif/)
* parso (com.epam:parso:2.0.14 - https://github.com/epam/parso)
* Internet Time Utility (com.ethlo.time:itu:1.7.0 - https://github.com/ethlo/itu)
- * ClassMate (com.fasterxml:classmate:1.7.0 - https://github.com/FasterXML/java-classmate)
- * Jackson-annotations (com.fasterxml.jackson.core:jackson-annotations:2.19.1 - https://github.com/FasterXML/jackson)
- * Jackson-core (com.fasterxml.jackson.core:jackson-core:2.19.1 - https://github.com/FasterXML/jackson-core)
- * jackson-databind (com.fasterxml.jackson.core:jackson-databind:2.19.1 - https://github.com/FasterXML/jackson)
- * Jackson dataformat: CBOR (com.fasterxml.jackson.dataformat:jackson-dataformat-cbor:2.17.2 - https://github.com/FasterXML/jackson-dataformats-binary)
+ * ClassMate (com.fasterxml:classmate:1.7.3 - https://github.com/FasterXML/java-classmate)
+ * Jackson-annotations (com.fasterxml.jackson.core:jackson-annotations:2.21 - https://github.com/FasterXML/jackson)
+ * Jackson-core (com.fasterxml.jackson.core:jackson-core:2.21.2 - https://github.com/FasterXML/jackson-core)
+ * jackson-databind (com.fasterxml.jackson.core:jackson-databind:2.21.2 - https://github.com/FasterXML/jackson)
* Jackson dataformat: Smile (com.fasterxml.jackson.dataformat:jackson-dataformat-smile:2.15.2 - https://github.com/FasterXML/jackson-dataformats-binary)
* Jackson-dataformat-YAML (com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:2.14.0 - https://github.com/FasterXML/jackson-dataformats-text)
* Jackson datatype: jdk8 (com.fasterxml.jackson.datatype:jackson-datatype-jdk8:2.13.5 - https://github.com/FasterXML/jackson-modules-java8/jackson-datatype-jdk8)
@@ -45,6 +40,9 @@ https://wiki.lyrasis.org/display/DSPACE/Code+Contribution+Guidelines
* zjsonpatch (com.flipkart.zjsonpatch:zjsonpatch:0.4.16 - https://github.com/flipkart-incubator/zjsonpatch/)
* Caffeine cache (com.github.ben-manes.caffeine:caffeine:2.9.3 - https://github.com/ben-manes/caffeine)
* JSON.simple (com.github.cliftonlabs:json-simple:3.0.2 - https://cliftonlabs.github.io/json-simple/)
+ * docker-java-api (com.github.docker-java:docker-java-api:3.7.1 - https://github.com/docker-java/docker-java)
+ * docker-java-transport (com.github.docker-java:docker-java-transport:3.7.1 - https://github.com/docker-java/docker-java)
+ * docker-java-transport-zerodep (com.github.docker-java:docker-java-transport-zerodep:3.7.1 - https://github.com/docker-java/docker-java)
* btf (com.github.java-json-tools:btf:1.3 - https://github.com/java-json-tools/btf)
* jackson-coreutils (com.github.java-json-tools:jackson-coreutils:2.0 - https://github.com/java-json-tools/jackson-coreutils)
* jackson-coreutils-equivalence (com.github.java-json-tools:jackson-coreutils-equivalence:1.0 - https://github.com/java-json-tools/jackson-coreutils)
@@ -58,30 +56,31 @@ https://wiki.lyrasis.org/display/DSPACE/Code+Contribution+Guidelines
* Google Analytics API v3-rev145-1.23.0 (com.google.apis:google-api-services-analytics:v3-rev145-1.23.0 - http://nexus.sonatype.org/oss-repository-hosting.html/google-api-services-analytics)
* FindBugs-jsr305 (com.google.code.findbugs:jsr305:3.0.2 - http://findbugs.sourceforge.net/)
* Gson (com.google.code.gson:gson:2.11.0 - https://github.com/google/gson)
- * error-prone annotations (com.google.errorprone:error_prone_annotations:2.21.1 - https://errorprone.info/error_prone_annotations)
+ * error-prone annotations (com.google.errorprone:error_prone_annotations:2.47.0 - https://errorprone.info/error_prone_annotations)
* Guava InternalFutureFailureAccess and InternalFutures (com.google.guava:failureaccess:1.0.1 - https://github.com/google/guava/failureaccess)
- * Guava: Google Core Libraries for Java (com.google.guava:guava:32.1.3-jre - https://github.com/google/guava)
+ * Guava InternalFutureFailureAccess and InternalFutures (com.google.guava:failureaccess:1.0.3 - https://github.com/google/guava/failureaccess)
+ * Guava: Google Core Libraries for Java (com.google.guava:guava:33.6.0-jre - https://github.com/google/guava)
* Guava ListenableFuture only (com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava - https://github.com/google/guava/listenablefuture)
- * Google HTTP Client Library for Java (com.google.http-client:google-http-client:1.47.0 - https://github.com/googleapis/google-http-java-client/google-http-client)
+ * Google HTTP Client Library for Java (com.google.http-client:google-http-client:1.47.1 - https://github.com/googleapis/google-http-java-client/google-http-client)
* Apache HTTP transport v2 for the Google HTTP Client Library for Java. (com.google.http-client:google-http-client-apache-v2:1.42.0 - https://github.com/googleapis/google-http-java-client/google-http-client-apache-v2)
- * GSON extensions to the Google HTTP Client Library for Java. (com.google.http-client:google-http-client-gson:1.47.0 - https://github.com/googleapis/google-http-java-client/google-http-client-gson)
- * Jackson 2 extensions to the Google HTTP Client Library for Java. (com.google.http-client:google-http-client-jackson2:1.47.0 - https://github.com/googleapis/google-http-java-client/google-http-client-jackson2)
+ * GSON extensions to the Google HTTP Client Library for Java. (com.google.http-client:google-http-client-gson:1.47.1 - https://github.com/googleapis/google-http-java-client/google-http-client-gson)
+ * Jackson 2 extensions to the Google HTTP Client Library for Java. (com.google.http-client:google-http-client-jackson2:1.47.1 - https://github.com/googleapis/google-http-java-client/google-http-client-jackson2)
* J2ObjC Annotations (com.google.j2objc:j2objc-annotations:1.3 - https://github.com/google/j2objc/)
- * J2ObjC Annotations (com.google.j2objc:j2objc-annotations:2.8 - https://github.com/google/j2objc/)
+ * J2ObjC Annotations (com.google.j2objc:j2objc-annotations:3.1 - https://github.com/google/j2objc/)
* Google OAuth Client Library for Java (com.google.oauth-client:google-oauth-client:1.39.0 - https://github.com/googleapis/google-oauth-java-client/google-oauth-client)
* ConcurrentLinkedHashMap (com.googlecode.concurrentlinkedhashmap:concurrentlinkedhashmap-lru:1.4.2 - http://code.google.com/p/concurrentlinkedhashmap)
* libphonenumber (com.googlecode.libphonenumber:libphonenumber:8.11.1 - https://github.com/google/libphonenumber/)
- * Jackcess (com.healthmarketscience.jackcess:jackcess:4.0.8 - https://jackcess.sourceforge.io)
+ * Jackcess (com.healthmarketscience.jackcess:jackcess:4.0.10 - https://jackcess.sourceforge.io)
* Jackcess Encrypt (com.healthmarketscience.jackcess:jackcess-encrypt:4.0.3 - http://jackcessencrypt.sf.net)
- * json-path (com.jayway.jsonpath:json-path:2.9.0 - https://github.com/jayway/JsonPath)
- * json-path-assert (com.jayway.jsonpath:json-path-assert:2.9.0 - https://github.com/jayway/JsonPath)
+ * json-path (com.jayway.jsonpath:json-path:2.10.0 - https://github.com/jayway/JsonPath)
+ * json-path-assert (com.jayway.jsonpath:json-path-assert:2.10.0 - https://github.com/jayway/JsonPath)
* Disruptor Framework (com.lmax:disruptor:3.4.2 - http://lmax-exchange.github.com/disruptor)
* MaxMind DB Reader (com.maxmind.db:maxmind-db:2.1.0 - http://dev.maxmind.com/)
* MaxMind GeoIP2 API (com.maxmind.geoip2:geoip2:2.17.0 - https://dev.maxmind.com/geoip?lang=en)
* JsonSchemaValidator (com.networknt:json-schema-validator:1.0.76 - https://github.com/networknt/json-schema-validator)
* Nimbus JOSE+JWT (com.nimbusds:nimbus-jose-jwt:7.9 - https://bitbucket.org/connect2id/nimbus-jose-jwt)
* Nimbus JOSE+JWT (com.nimbusds:nimbus-jose-jwt:9.28 - https://bitbucket.org/connect2id/nimbus-jose-jwt)
- * opencsv (com.opencsv:opencsv:5.11.1 - http://opencsv.sf.net)
+ * opencsv (com.opencsv:opencsv:5.12.0 - http://opencsv.sf.net)
* java-libpst (com.pff:java-libpst:0.9.3 - https://github.com/rjohnsondev/java-libpst)
* rome (com.rometools:rome:1.19.0 - http://rometools.com/rome)
* rome-modules (com.rometools:rome-modules:1.19.0 - http://rometools.com/rome-modules)
@@ -91,27 +90,18 @@ https://wiki.lyrasis.org/display/DSPACE/Code+Contribution+Guidelines
* okio (com.squareup.okio:okio:3.6.0 - https://github.com/square/okio/)
* okio (com.squareup.okio:okio-jvm:3.6.0 - https://github.com/square/okio/)
* T-Digest (com.tdunning:t-digest:3.1 - https://github.com/tdunning/t-digest)
- * config (com.typesafe:config:1.3.3 - https://github.com/lightbend/config)
- * ssl-config-core (com.typesafe:ssl-config-core_2.13:0.3.8 - https://github.com/lightbend/ssl-config)
- * akka-actor (com.typesafe.akka:akka-actor_2.13:2.5.31 - https://akka.io/)
- * akka-http-core (com.typesafe.akka:akka-http-core_2.13:10.1.12 - https://akka.io)
- * akka-http (com.typesafe.akka:akka-http_2.13:10.1.12 - https://akka.io)
- * akka-parsing (com.typesafe.akka:akka-parsing_2.13:10.1.12 - https://akka.io)
- * akka-protobuf (com.typesafe.akka:akka-protobuf_2.13:2.5.31 - https://akka.io/)
- * akka-stream (com.typesafe.akka:akka-stream_2.13:2.5.31 - https://akka.io/)
- * scala-logging (com.typesafe.scala-logging:scala-logging_2.13:3.9.2 - https://github.com/lightbend/scala-logging)
* JSON library from Android SDK (com.vaadin.external.google:android-json:0.0.20131108.vaadin1 - http://developer.android.com/sdk)
* SparseBitSet (com.zaxxer:SparseBitSet:1.3 - https://github.com/brettwooldridge/SparseBitSet)
* Apache Commons BeanUtils (commons-beanutils:commons-beanutils:1.11.0 - https://commons.apache.org/proper/commons-beanutils)
- * Apache Commons CLI (commons-cli:commons-cli:1.9.0 - https://commons.apache.org/proper/commons-cli/)
- * Apache Commons Codec (commons-codec:commons-codec:1.18.0 - https://commons.apache.org/proper/commons-codec/)
+ * Apache Commons CLI (commons-cli:commons-cli:1.11.0 - https://commons.apache.org/proper/commons-cli/)
+ * Apache Commons Codec (commons-codec:commons-codec:1.22.0 - https://commons.apache.org/proper/commons-codec/)
* Apache Commons Collections (commons-collections:commons-collections:3.2.2 - http://commons.apache.org/collections/)
* Commons Digester (commons-digester:commons-digester:2.1 - http://commons.apache.org/digester/)
* Commons FileUpload (commons-fileupload:commons-fileupload:1.2.1 - http://commons.apache.org/fileupload/)
- * Apache Commons IO (commons-io:commons-io:2.19.0 - https://commons.apache.org/proper/commons-io/)
+ * Apache Commons IO (commons-io:commons-io:2.22.0 - https://commons.apache.org/proper/commons-io/)
* Commons Lang (commons-lang:commons-lang:2.6 - http://commons.apache.org/lang/)
- * Apache Commons Logging (commons-logging:commons-logging:1.3.5 - https://commons.apache.org/proper/commons-logging/)
- * Apache Commons Validator (commons-validator:commons-validator:1.9.0 - http://commons.apache.org/proper/commons-validator/)
+ * Apache Commons Logging (commons-logging:commons-logging:1.3.6 - https://commons.apache.org/proper/commons-logging/)
+ * Apache Commons Validator (commons-validator:commons-validator:1.10.1 - https://commons.apache.org/proper/commons-validator/)
* GeoJson POJOs for Jackson (de.grundid.opendatalab:geojson-jackson:1.14 - https://github.com/opendatalab-de/geojson-jackson)
* OpenAIRE Funders Model (eu.openaire:funders-model:2.0.0 - https://api.openaire.eu)
* Metrics Core (io.dropwizard.metrics:metrics-core:4.1.5 - https://metrics.dropwizard.io/metrics-core)
@@ -119,34 +109,34 @@ https://wiki.lyrasis.org/display/DSPACE/Code+Contribution+Guidelines
* Metrics Integration for Jetty 9.3 and higher (io.dropwizard.metrics:metrics-jetty9:4.1.5 - https://metrics.dropwizard.io/metrics-jetty9)
* Metrics Integration with JMX (io.dropwizard.metrics:metrics-jmx:4.1.5 - https://metrics.dropwizard.io/metrics-jmx)
* JVM Integration for Metrics (io.dropwizard.metrics:metrics-jvm:4.1.5 - https://metrics.dropwizard.io/metrics-jvm)
- * io.grpc:grpc-api (io.grpc:grpc-api:1.73.0 - https://github.com/grpc/grpc-java)
- * io.grpc:grpc-context (io.grpc:grpc-context:1.73.0 - https://github.com/grpc/grpc-java)
+ * io.grpc:grpc-api (io.grpc:grpc-api:1.80.0 - https://github.com/grpc/grpc-java)
+ * io.grpc:grpc-context (io.grpc:grpc-context:1.80.0 - https://github.com/grpc/grpc-java)
* micrometer-core (io.micrometer:micrometer-core:1.9.17 - https://github.com/micrometer-metrics/micrometer)
* Netty/Buffer (io.netty:netty-buffer:4.1.99.Final - https://netty.io/netty-buffer/)
- * Netty/Buffer (io.netty:netty-buffer:4.2.2.Final - https://netty.io/netty-buffer/)
+ * Netty/Buffer (io.netty:netty-buffer:4.2.12.Final - https://netty.io/netty-buffer/)
* Netty/Codec (io.netty:netty-codec:4.1.99.Final - https://netty.io/netty-codec/)
- * Netty/Codec (io.netty:netty-codec:4.2.2.Final - https://netty.io/netty-codec/)
- * Netty/Codec/Base (io.netty:netty-codec-base:4.2.2.Final - https://netty.io/netty-codec-base/)
- * Netty/Codec/Compression (io.netty:netty-codec-compression:4.2.2.Final - https://netty.io/netty-codec-compression/)
+ * Netty/Codec (io.netty:netty-codec:4.2.12.Final - https://netty.io/netty-codec/)
+ * Netty/Codec/Base (io.netty:netty-codec-base:4.2.12.Final - https://netty.io/netty-codec-base/)
+ * Netty/Codec/Compression (io.netty:netty-codec-compression:4.2.12.Final - https://netty.io/netty-codec-compression/)
* Netty/Codec/HTTP (io.netty:netty-codec-http:4.1.86.Final - https://netty.io/netty-codec-http/)
* Netty/Codec/HTTP2 (io.netty:netty-codec-http2:4.1.86.Final - https://netty.io/netty-codec-http2/)
- * Netty/Codec/Marshalling (io.netty:netty-codec-marshalling:4.2.2.Final - https://netty.io/netty-codec-marshalling/)
- * Netty/Codec/Protobuf (io.netty:netty-codec-protobuf:4.2.2.Final - https://netty.io/netty-codec-protobuf/)
+ * Netty/Codec/Marshalling (io.netty:netty-codec-marshalling:4.2.12.Final - https://netty.io/netty-codec-marshalling/)
+ * Netty/Codec/Protobuf (io.netty:netty-codec-protobuf:4.2.12.Final - https://netty.io/netty-codec-protobuf/)
* Netty/Codec/Socks (io.netty:netty-codec-socks:4.1.86.Final - https://netty.io/netty-codec-socks/)
* Netty/Common (io.netty:netty-common:4.1.99.Final - https://netty.io/netty-common/)
- * Netty/Common (io.netty:netty-common:4.2.2.Final - https://netty.io/netty-common/)
+ * Netty/Common (io.netty:netty-common:4.2.12.Final - https://netty.io/netty-common/)
* Netty/Handler (io.netty:netty-handler:4.1.99.Final - https://netty.io/netty-handler/)
- * Netty/Handler (io.netty:netty-handler:4.2.2.Final - https://netty.io/netty-handler/)
+ * Netty/Handler (io.netty:netty-handler:4.2.12.Final - https://netty.io/netty-handler/)
* Netty/Handler/Proxy (io.netty:netty-handler-proxy:4.1.86.Final - https://netty.io/netty-handler-proxy/)
* Netty/Resolver (io.netty:netty-resolver:4.1.99.Final - https://netty.io/netty-resolver/)
* Netty/TomcatNative [BoringSSL - Static] (io.netty:netty-tcnative-boringssl-static:2.0.56.Final - https://github.com/netty/netty-tcnative/netty-tcnative-boringssl-static/)
* Netty/TomcatNative [OpenSSL - Classes] (io.netty:netty-tcnative-classes:2.0.56.Final - https://github.com/netty/netty-tcnative/netty-tcnative-classes/)
* Netty/Transport (io.netty:netty-transport:4.1.99.Final - https://netty.io/netty-transport/)
- * Netty/Transport (io.netty:netty-transport:4.2.2.Final - https://netty.io/netty-transport/)
+ * Netty/Transport (io.netty:netty-transport:4.2.12.Final - https://netty.io/netty-transport/)
* Netty/Transport/Classes/Epoll (io.netty:netty-transport-classes-epoll:4.1.99.Final - https://netty.io/netty-transport-classes-epoll/)
* Netty/Transport/Native/Epoll (io.netty:netty-transport-native-epoll:4.1.99.Final - https://netty.io/netty-transport-native-epoll/)
* Netty/Transport/Native/Unix/Common (io.netty:netty-transport-native-unix-common:4.1.99.Final - https://netty.io/netty-transport-native-unix-common/)
- * Netty/Transport/Native/Unix/Common (io.netty:netty-transport-native-unix-common:4.2.2.Final - https://netty.io/netty-transport-native-unix-common/)
+ * Netty/Transport/Native/Unix/Common (io.netty:netty-transport-native-unix-common:4.2.12.Final - https://netty.io/netty-transport-native-unix-common/)
* OpenCensus (io.opencensus:opencensus-api:0.31.1 - https://github.com/census-instrumentation/opencensus-java)
* OpenCensus (io.opencensus:opencensus-contrib-http-util:0.31.1 - https://github.com/census-instrumentation/opencensus-java)
* OpenTracing API (io.opentracing:opentracing-api:0.33.0 - https://github.com/opentracing/opentracing-java/opentracing-api)
@@ -175,38 +165,37 @@ https://wiki.lyrasis.org/display/DSPACE/Code+Contribution+Guidelines
* JSR107 API and SPI (javax.cache:cache-api:1.1.1 - https://github.com/jsr107/jsr107spec)
* javax.inject (javax.inject:javax.inject:1 - http://code.google.com/p/atinject/)
* jdbm (jdbm:jdbm:1.0 - no url defined)
- * Joda-Time (joda-time:joda-time:2.14.0 - https://www.joda.org/joda-time/)
+ * Joda-Time (joda-time:joda-time:2.14.1 - https://www.joda.org/joda-time/)
* Byte Buddy (without dependencies) (net.bytebuddy:byte-buddy:1.11.13 - https://bytebuddy.net/byte-buddy)
* Byte Buddy (without dependencies) (net.bytebuddy:byte-buddy:1.12.18 - https://bytebuddy.net/byte-buddy)
* Byte Buddy agent (net.bytebuddy:byte-buddy-agent:1.11.13 - https://bytebuddy.net/byte-buddy-agent)
* eigenbase-properties (net.hydromatic:eigenbase-properties:1.1.5 - http://github.com/julianhyde/eigenbase-properties)
+ * Java Native Access (net.java.dev.jna:jna:5.18.1 - https://github.com/java-native-access/jna)
* json-unit-core (net.javacrumbs.json-unit:json-unit-core:2.36.0 - https://github.com/lukas-krecan/JsonUnit/json-unit-core)
* "Java Concurrency in Practice" book annotations (net.jcip:jcip-annotations:1.0 - http://jcip.net/)
- * ASM based accessors helper used by json-smart (net.minidev:accessors-smart:2.5.0 - https://urielch.github.io/)
- * ASM based accessors helper used by json-smart (net.minidev:accessors-smart:2.5.2 - https://urielch.github.io/)
- * JSON Small and Fast Parser (net.minidev:json-smart:2.5.0 - https://urielch.github.io/)
- * JSON Small and Fast Parser (net.minidev:json-smart:2.5.2 - https://urielch.github.io/)
+ * ASM based accessors helper used by json-smart (net.minidev:accessors-smart:2.6.0 - https://urielch.github.io/)
+ * JSON Small and Fast Parser (net.minidev:json-smart:2.6.0 - https://urielch.github.io/)
* Abdera Core (org.apache.abdera:abdera-core:1.1.3 - http://abdera.apache.org/abdera-core)
* I18N Libraries (org.apache.abdera:abdera-i18n:1.1.3 - http://abdera.apache.org)
- * Apache Ant Core (org.apache.ant:ant:1.10.15 - https://ant.apache.org/)
- * Apache Ant Launcher (org.apache.ant:ant-launcher:1.10.15 - https://ant.apache.org/)
- * Apache Commons BCEL (org.apache.bcel:bcel:6.10.0 - https://commons.apache.org/proper/commons-bcel)
+ * Apache Ant Core (org.apache.ant:ant:1.10.17 - https://ant.apache.org/)
+ * Apache Ant Launcher (org.apache.ant:ant-launcher:1.10.17 - https://ant.apache.org/)
+ * Apache Commons BCEL (org.apache.bcel:bcel:6.12.0 - https://commons.apache.org/proper/commons-bcel)
* Calcite Core (org.apache.calcite:calcite-core:1.35.0 - https://calcite.apache.org)
* Calcite Linq4j (org.apache.calcite:calcite-linq4j:1.35.0 - https://calcite.apache.org)
* Apache Calcite Avatica (org.apache.calcite.avatica:avatica-core:1.23.0 - https://calcite.apache.org/avatica)
* Apache Calcite Avatica Metrics (org.apache.calcite.avatica:avatica-metrics:1.23.0 - https://calcite.apache.org/avatica)
* Apache Commons Collections (org.apache.commons:commons-collections4:4.5.0 - https://commons.apache.org/proper/commons-collections/)
- * Apache Commons Compress (org.apache.commons:commons-compress:1.27.1 - https://commons.apache.org/proper/commons-compress/)
- * Apache Commons Configuration (org.apache.commons:commons-configuration2:2.12.0 - https://commons.apache.org/proper/commons-configuration/)
- * Apache Commons CSV (org.apache.commons:commons-csv:1.14.0 - https://commons.apache.org/proper/commons-csv/)
- * Apache Commons DBCP (org.apache.commons:commons-dbcp2:2.13.0 - https://commons.apache.org/proper/commons-dbcp/)
+ * Apache Commons Compress (org.apache.commons:commons-compress:1.28.0 - https://commons.apache.org/proper/commons-compress/)
+ * Apache Commons Configuration (org.apache.commons:commons-configuration2:2.15.0 - https://commons.apache.org/proper/commons-configuration/)
+ * Apache Commons CSV (org.apache.commons:commons-csv:1.14.1 - https://commons.apache.org/proper/commons-csv/)
+ * Apache Commons DBCP (org.apache.commons:commons-dbcp2:2.14.0 - https://commons.apache.org/proper/commons-dbcp/)
* Apache Commons Digester (org.apache.commons:commons-digester3:3.2 - http://commons.apache.org/digester/)
* Apache Commons Exec (org.apache.commons:commons-exec:1.3 - http://commons.apache.org/proper/commons-exec/)
- * Apache Commons Exec (org.apache.commons:commons-exec:1.4.0 - https://commons.apache.org/proper/commons-exec/)
- * Apache Commons Lang (org.apache.commons:commons-lang3:3.17.0 - https://commons.apache.org/proper/commons-lang/)
+ * Apache Commons Exec (org.apache.commons:commons-exec:1.6.0 - https://commons.apache.org/proper/commons-exec/)
+ * Apache Commons Lang (org.apache.commons:commons-lang3:3.20.0 - https://commons.apache.org/proper/commons-lang/)
* Apache Commons Math (org.apache.commons:commons-math3:3.6.1 - http://commons.apache.org/proper/commons-math/)
- * Apache Commons Pool (org.apache.commons:commons-pool2:2.12.1 - https://commons.apache.org/proper/commons-pool/)
- * Apache Commons Text (org.apache.commons:commons-text:1.13.1 - https://commons.apache.org/proper/commons-text)
+ * Apache Commons Pool (org.apache.commons:commons-pool2:2.13.1 - https://commons.apache.org/proper/commons-pool/)
+ * Apache Commons Text (org.apache.commons:commons-text:1.15.0 - https://commons.apache.org/proper/commons-text)
* Curator Client (org.apache.curator:curator-client:2.13.0 - http://curator.apache.org/curator-client)
* Curator Framework (org.apache.curator:curator-framework:2.13.0 - http://curator.apache.org/curator-framework)
* Curator Recipes (org.apache.curator:curator-recipes:2.13.0 - http://curator.apache.org/curator-recipes)
@@ -222,8 +211,8 @@ https://wiki.lyrasis.org/display/DSPACE/Code+Contribution+Guidelines
* Apache HttpClient (org.apache.httpcomponents.client5:httpclient5:5.1.3 - https://hc.apache.org/httpcomponents-client-5.0.x/5.1.3/httpclient5/)
* Apache HttpComponents Core HTTP/1.1 (org.apache.httpcomponents.core5:httpcore5:5.1.3 - https://hc.apache.org/httpcomponents-core-5.1.x/5.1.3/httpcore5/)
* Apache HttpComponents Core HTTP/2 (org.apache.httpcomponents.core5:httpcore5-h2:5.1.3 - https://hc.apache.org/httpcomponents-core-5.1.x/5.1.3/httpcore5-h2/)
- * Apache James :: Mime4j :: Core (org.apache.james:apache-mime4j-core:0.8.12 - http://james.apache.org/mime4j/apache-mime4j-core)
- * Apache James :: Mime4j :: DOM (org.apache.james:apache-mime4j-dom:0.8.12 - http://james.apache.org/mime4j/apache-mime4j-dom)
+ * Apache James :: Mime4j :: Core (org.apache.james:apache-mime4j-core:0.8.14 - http://james.apache.org/mime4j/apache-mime4j-core)
+ * Apache James :: Mime4j :: DOM (org.apache.james:apache-mime4j-dom:0.8.13 - http://james.apache.org/mime4j/apache-mime4j-dom)
* Apache Jena - Libraries POM (org.apache.jena:apache-jena-libs:2.13.0 - http://jena.apache.org/apache-jena-libs/)
* Apache Jena - ARQ (SPARQL 1.1 Query Engine) (org.apache.jena:jena-arq:2.13.0 - http://jena.apache.org/jena-arq/)
* Apache Jena - Core (org.apache.jena:jena-core:2.13.0 - http://jena.apache.org/jena-core/)
@@ -233,9 +222,9 @@ https://wiki.lyrasis.org/display/DSPACE/Code+Contribution+Guidelines
* Kerby-kerb Util (org.apache.kerby:kerb-util:1.0.1 - http://directory.apache.org/kerby/kerby-kerb/kerb-util)
* Kerby ASN1 Project (org.apache.kerby:kerby-asn1:1.0.1 - http://directory.apache.org/kerby/kerby-common/kerby-asn1)
* Kerby PKIX Project (org.apache.kerby:kerby-pkix:1.0.1 - http://directory.apache.org/kerby/kerby-pkix)
- * Apache Log4j 1.x Compatibility API (org.apache.logging.log4j:log4j-1.2-api:2.25.1 - https://logging.apache.org/log4j/2.x/)
- * Apache Log4j API (org.apache.logging.log4j:log4j-api:2.25.1 - https://logging.apache.org/log4j/2.x/)
- * Apache Log4j Core (org.apache.logging.log4j:log4j-core:2.25.1 - https://logging.apache.org/log4j/2.x/)
+ * Apache Log4j 1.x Compatibility API (org.apache.logging.log4j:log4j-1.2-api:2.25.4 - https://logging.apache.org/log4j/2.x/)
+ * Apache Log4j API (org.apache.logging.log4j:log4j-api:2.25.4 - https://logging.apache.org/log4j/2.x/)
+ * Apache Log4j Core (org.apache.logging.log4j:log4j-core:2.25.4 - https://logging.apache.org/log4j/2.x/)
* Apache Log4j JUL Adapter (org.apache.logging.log4j:log4j-jul:2.17.2 - https://logging.apache.org/log4j/2.x/log4j-jul/)
* Apache Log4j Layout for JSON template (org.apache.logging.log4j:log4j-layout-template-json:2.17.2 - https://logging.apache.org/log4j/2.x/log4j-layout-template-json/)
* Apache Log4j SLF4J Binding (org.apache.logging.log4j:log4j-slf4j-impl:2.17.2 - https://logging.apache.org/log4j/2.x/log4j-slf4j-impl/)
@@ -263,45 +252,46 @@ https://wiki.lyrasis.org/display/DSPACE/Code+Contribution+Guidelines
* Lucene Spatial Extras (org.apache.lucene:lucene-spatial-extras:8.11.4 - https://lucene.apache.org/lucene-parent/lucene-spatial-extras)
* Lucene Spatial 3D (org.apache.lucene:lucene-spatial3d:8.11.4 - https://lucene.apache.org/lucene-parent/lucene-spatial3d)
* Lucene Suggest (org.apache.lucene:lucene-suggest:8.11.4 - https://lucene.apache.org/lucene-parent/lucene-suggest)
- * Apache FontBox (org.apache.pdfbox:fontbox:2.0.34 - http://pdfbox.apache.org/)
+ * Apache FontBox (org.apache.pdfbox:fontbox:3.0.7 - http://pdfbox.apache.org/)
* PDFBox JBIG2 ImageIO plugin (org.apache.pdfbox:jbig2-imageio:3.0.4 - https://www.apache.org/jbig2-imageio/)
* Apache JempBox (org.apache.pdfbox:jempbox:1.8.17 - http://www.apache.org/pdfbox-parent/jempbox/)
- * Apache PDFBox (org.apache.pdfbox:pdfbox:2.0.34 - https://www.apache.org/pdfbox-parent/pdfbox/)
- * Apache PDFBox tools (org.apache.pdfbox:pdfbox-tools:2.0.34 - https://www.apache.org/pdfbox-parent/pdfbox-tools/)
- * Apache XmpBox (org.apache.pdfbox:xmpbox:2.0.34 - https://www.apache.org/pdfbox-parent/xmpbox/)
- * Apache POI - Common (org.apache.poi:poi:5.4.1 - https://poi.apache.org/)
- * Apache POI - API based on OPC and OOXML schemas (org.apache.poi:poi-ooxml:5.4.1 - https://poi.apache.org/)
- * Apache POI (org.apache.poi:poi-ooxml-lite:5.4.1 - https://poi.apache.org/)
- * Apache POI (org.apache.poi:poi-scratchpad:5.4.1 - https://poi.apache.org/)
+ * Apache PDFBox (org.apache.pdfbox:pdfbox:3.0.7 - https://www.apache.org/pdfbox-parent/pdfbox/)
+ * Apache PDFBox io (org.apache.pdfbox:pdfbox-io:3.0.7 - https://www.apache.org/pdfbox-parent/pdfbox-io/)
+ * Apache PDFBox tools (org.apache.pdfbox:pdfbox-tools:3.0.7 - https://www.apache.org/pdfbox-parent/pdfbox-tools/)
+ * Apache XmpBox (org.apache.pdfbox:xmpbox:3.0.7 - https://www.apache.org/pdfbox-parent/xmpbox/)
+ * Apache POI - Common (org.apache.poi:poi:5.5.1 - https://poi.apache.org/)
+ * Apache POI - API based on OPC and OOXML schemas (org.apache.poi:poi-ooxml:5.5.1 - https://poi.apache.org/)
+ * Apache POI - OOXML schemas (full) (org.apache.poi:poi-ooxml-full:5.5.1 - https://poi.apache.org/)
+ * Apache POI (org.apache.poi:poi-scratchpad:5.5.1 - https://poi.apache.org/)
* Apache Solr Core (org.apache.solr:solr-core:8.11.4 - https://lucene.apache.org/solr-parent/solr-core)
* Apache Solr Solrj (org.apache.solr:solr-solrj:8.11.4 - https://lucene.apache.org/solr-parent/solr-solrj)
* Apache Standard Taglib Implementation (org.apache.taglibs:taglibs-standard-impl:1.2.5 - http://tomcat.apache.org/taglibs/standard-1.2.5/taglibs-standard-impl)
* Apache Standard Taglib Specification API (org.apache.taglibs:taglibs-standard-spec:1.2.5 - http://tomcat.apache.org/taglibs/standard-1.2.5/taglibs-standard-spec)
* Apache Thrift (org.apache.thrift:libthrift:0.9.2 - http://thrift.apache.org)
- * Apache Tika core (org.apache.tika:tika-core:2.9.4 - https://tika.apache.org/)
- * Apache Tika Apple parser module (org.apache.tika:tika-parser-apple-module:2.9.4 - https://tika.apache.org/tika-parser-apple-module/)
- * Apache Tika audiovideo parser module (org.apache.tika:tika-parser-audiovideo-module:2.9.4 - https://tika.apache.org/tika-parser-audiovideo-module/)
- * Apache Tika cad parser module (org.apache.tika:tika-parser-cad-module:2.9.4 - https://tika.apache.org/tika-parser-cad-module/)
- * Apache Tika code parser module (org.apache.tika:tika-parser-code-module:2.9.4 - https://tika.apache.org/tika-parser-code-module/)
- * Apache Tika crypto parser module (org.apache.tika:tika-parser-crypto-module:2.9.4 - https://tika.apache.org/tika-parser-crypto-module/)
- * Apache Tika digest commons (org.apache.tika:tika-parser-digest-commons:2.9.4 - https://tika.apache.org/tika-parser-digest-commons/)
- * Apache Tika font parser module (org.apache.tika:tika-parser-font-module:2.9.4 - https://tika.apache.org/tika-parser-font-module/)
- * Apache Tika html parser module (org.apache.tika:tika-parser-html-module:2.9.4 - https://tika.apache.org/tika-parser-html-module/)
- * Apache Tika image parser module (org.apache.tika:tika-parser-image-module:2.9.4 - https://tika.apache.org/tika-parser-image-module/)
- * Apache Tika mail commons (org.apache.tika:tika-parser-mail-commons:2.9.4 - https://tika.apache.org/tika-parser-mail-commons/)
- * Apache Tika mail parser module (org.apache.tika:tika-parser-mail-module:2.9.4 - https://tika.apache.org/tika-parser-mail-module/)
- * Apache Tika Microsoft parser module (org.apache.tika:tika-parser-microsoft-module:2.9.4 - https://tika.apache.org/tika-parser-microsoft-module/)
- * Apache Tika miscellaneous office format parser module (org.apache.tika:tika-parser-miscoffice-module:2.9.4 - https://tika.apache.org/tika-parser-miscoffice-module/)
- * Apache Tika news parser module (org.apache.tika:tika-parser-news-module:2.9.4 - https://tika.apache.org/tika-parser-news-module/)
- * Apache Tika OCR parser module (org.apache.tika:tika-parser-ocr-module:2.9.4 - https://tika.apache.org/tika-parser-ocr-module/)
- * Apache Tika PDF parser module (org.apache.tika:tika-parser-pdf-module:2.9.4 - https://tika.apache.org/tika-parser-pdf-module/)
- * Apache Tika package parser module (org.apache.tika:tika-parser-pkg-module:2.9.4 - https://tika.apache.org/tika-parser-pkg-module/)
- * Apache Tika text parser module (org.apache.tika:tika-parser-text-module:2.9.4 - https://tika.apache.org/tika-parser-text-module/)
- * Apache Tika WARC parser module (org.apache.tika:tika-parser-webarchive-module:2.9.4 - https://tika.apache.org/tika-parser-webarchive-module/)
- * Apache Tika XML parser module (org.apache.tika:tika-parser-xml-module:2.9.4 - https://tika.apache.org/tika-parser-xml-module/)
- * Apache Tika XMP commons (org.apache.tika:tika-parser-xmp-commons:2.9.4 - https://tika.apache.org/tika-parser-xmp-commons/)
- * Apache Tika ZIP commons (org.apache.tika:tika-parser-zip-commons:2.9.4 - https://tika.apache.org/tika-parser-zip-commons/)
- * Apache Tika standard parser package (org.apache.tika:tika-parsers-standard-package:2.9.4 - https://tika.apache.org/tika-parsers/tika-parsers-standard/tika-parsers-standard-package/)
+ * Apache Tika core (org.apache.tika:tika-core:3.3.0 - https://tika.apache.org/)
+ * Apache Tika Apple parser module (org.apache.tika:tika-parser-apple-module:3.3.0 - https://tika.apache.org/tika-parser-apple-module/)
+ * Apache Tika audiovideo parser module (org.apache.tika:tika-parser-audiovideo-module:3.3.0 - https://tika.apache.org/tika-parser-audiovideo-module/)
+ * Apache Tika cad parser module (org.apache.tika:tika-parser-cad-module:3.3.0 - https://tika.apache.org/tika-parser-cad-module/)
+ * Apache Tika code parser module (org.apache.tika:tika-parser-code-module:3.3.0 - https://tika.apache.org/tika-parser-code-module/)
+ * Apache Tika crypto parser module (org.apache.tika:tika-parser-crypto-module:3.3.0 - https://tika.apache.org/tika-parser-crypto-module/)
+ * Apache Tika digest commons (org.apache.tika:tika-parser-digest-commons:3.3.0 - https://tika.apache.org/tika-parser-digest-commons/)
+ * Apache Tika font parser module (org.apache.tika:tika-parser-font-module:3.3.0 - https://tika.apache.org/tika-parser-font-module/)
+ * Apache Tika html parser module (org.apache.tika:tika-parser-html-module:3.3.0 - https://tika.apache.org/tika-parser-html-module/)
+ * Apache Tika image parser module (org.apache.tika:tika-parser-image-module:3.3.0 - https://tika.apache.org/tika-parser-image-module/)
+ * Apache Tika mail commons (org.apache.tika:tika-parser-mail-commons:3.3.0 - https://tika.apache.org/tika-parser-mail-commons/)
+ * Apache Tika mail parser module (org.apache.tika:tika-parser-mail-module:3.3.0 - https://tika.apache.org/tika-parser-mail-module/)
+ * Apache Tika Microsoft parser module (org.apache.tika:tika-parser-microsoft-module:3.3.0 - https://tika.apache.org/tika-parser-microsoft-module/)
+ * Apache Tika miscellaneous office format parser module (org.apache.tika:tika-parser-miscoffice-module:3.3.0 - https://tika.apache.org/tika-parser-miscoffice-module/)
+ * Apache Tika news parser module (org.apache.tika:tika-parser-news-module:3.3.0 - https://tika.apache.org/tika-parser-news-module/)
+ * Apache Tika OCR parser module (org.apache.tika:tika-parser-ocr-module:3.3.0 - https://tika.apache.org/tika-parser-ocr-module/)
+ * Apache Tika PDF parser module (org.apache.tika:tika-parser-pdf-module:3.3.0 - https://tika.apache.org/tika-parser-pdf-module/)
+ * Apache Tika package parser module (org.apache.tika:tika-parser-pkg-module:3.3.0 - https://tika.apache.org/tika-parser-pkg-module/)
+ * Apache Tika text parser module (org.apache.tika:tika-parser-text-module:3.3.0 - https://tika.apache.org/tika-parser-text-module/)
+ * Apache Tika WARC parser module (org.apache.tika:tika-parser-webarchive-module:3.3.0 - https://tika.apache.org/tika-parser-webarchive-module/)
+ * Apache Tika XML parser module (org.apache.tika:tika-parser-xml-module:3.3.0 - https://tika.apache.org/tika-parser-xml-module/)
+ * Apache Tika XMP commons (org.apache.tika:tika-parser-xmp-commons:3.3.0 - https://tika.apache.org/tika-parser-xmp-commons/)
+ * Apache Tika ZIP commons (org.apache.tika:tika-parser-zip-commons:3.3.0 - https://tika.apache.org/tika-parser-zip-commons/)
+ * Apache Tika standard parser package (org.apache.tika:tika-parsers-standard-package:3.3.0 - https://tika.apache.org/tika-parsers/tika-parsers-standard/tika-parsers-standard-package/)
* tomcat-embed-core (org.apache.tomcat.embed:tomcat-embed-core:9.0.83 - https://tomcat.apache.org/)
* tomcat-embed-el (org.apache.tomcat.embed:tomcat-embed-el:9.0.83 - https://tomcat.apache.org/)
* tomcat-embed-websocket (org.apache.tomcat.embed:tomcat-embed-websocket:9.0.83 - https://tomcat.apache.org/)
@@ -317,63 +307,61 @@ https://wiki.lyrasis.org/display/DSPACE/Code+Contribution+Guidelines
* AssertJ fluent assertions (org.assertj:assertj-core:3.22.0 - https://assertj.github.io/doc/assertj-core/)
* Evo Inflector (org.atteo:evo-inflector:1.3 - http://atteo.org/static/evo-inflector)
* jose4j (org.bitbucket.b_c:jose4j:0.6.5 - https://bitbucket.org/b_c/jose4j/)
- * TagSoup (org.ccil.cowan.tagsoup:tagsoup:1.2.1 - http://home.ccil.org/~cowan/XML/tagsoup/)
* jems (org.dmfs:jems:1.18 - https://github.com/dmfs/jems)
* rfc3986-uri (org.dmfs:rfc3986-uri:0.8.1 - https://github.com/dmfs/uri-toolkit)
* Jetty :: Apache JSP Implementation (org.eclipse.jetty:apache-jsp:9.4.15.v20190215 - http://www.eclipse.org/jetty)
* Apache :: JSTL module (org.eclipse.jetty:apache-jstl:9.4.15.v20190215 - http://tomcat.apache.org/taglibs/standard/)
* Jetty :: ALPN :: Client (org.eclipse.jetty:jetty-alpn-client:9.4.53.v20231009 - https://eclipse.org/jetty/jetty-alpn-parent/jetty-alpn-client)
* Jetty :: ALPN :: JDK9 Client Implementation (org.eclipse.jetty:jetty-alpn-java-client:9.4.53.v20231009 - https://eclipse.org/jetty/jetty-alpn-parent/jetty-alpn-java-client)
- * Jetty :: ALPN :: JDK9 Server Implementation (org.eclipse.jetty:jetty-alpn-java-server:9.4.57.v20241219 - https://jetty.org/jetty-alpn-parent/jetty-alpn-java-server/)
+ * Jetty :: ALPN :: JDK9 Server Implementation (org.eclipse.jetty:jetty-alpn-java-server:9.4.58.v20250814 - https://jetty.org/jetty-alpn-parent/jetty-alpn-java-server/)
* Jetty :: ALPN :: Server (org.eclipse.jetty:jetty-alpn-server:9.4.53.v20231009 - https://eclipse.org/jetty/jetty-alpn-parent/jetty-alpn-server)
- * Jetty :: ALPN :: Server (org.eclipse.jetty:jetty-alpn-server:9.4.57.v20241219 - https://jetty.org/jetty-alpn-parent/jetty-alpn-server/)
+ * Jetty :: ALPN :: Server (org.eclipse.jetty:jetty-alpn-server:9.4.58.v20250814 - https://jetty.org/jetty-alpn-parent/jetty-alpn-server/)
* Jetty :: Servlet Annotations (org.eclipse.jetty:jetty-annotations:9.4.15.v20190215 - http://www.eclipse.org/jetty)
* Jetty :: Asynchronous HTTP Client (org.eclipse.jetty:jetty-client:9.4.53.v20231009 - https://eclipse.org/jetty/jetty-client)
* Jetty :: Continuation (org.eclipse.jetty:jetty-continuation:9.4.53.v20231009 - https://eclipse.org/jetty/jetty-continuation)
- * Jetty :: Continuation (org.eclipse.jetty:jetty-continuation:9.4.57.v20241219 - https://jetty.org/jetty-continuation/)
- * Jetty :: Deployers (org.eclipse.jetty:jetty-deploy:9.4.57.v20241219 - https://jetty.org/jetty-deploy/)
- * Jetty :: Http Utility (org.eclipse.jetty:jetty-http:9.4.57.v20241219 - https://jetty.org/jetty-http/)
- * Jetty :: IO Utility (org.eclipse.jetty:jetty-io:9.4.57.v20241219 - https://jetty.org/jetty-io/)
+ * Jetty :: Continuation (org.eclipse.jetty:jetty-continuation:9.4.58.v20250814 - https://jetty.org/jetty-continuation/)
+ * Jetty :: Deployers (org.eclipse.jetty:jetty-deploy:9.4.58.v20250814 - https://jetty.org/jetty-deploy/)
+ * Jetty :: Http Utility (org.eclipse.jetty:jetty-http:9.4.58.v20250814 - https://jetty.org/jetty-http/)
+ * Jetty :: IO Utility (org.eclipse.jetty:jetty-io:9.4.58.v20250814 - https://jetty.org/jetty-io/)
* Jetty :: JMX Management (org.eclipse.jetty:jetty-jmx:9.4.53.v20231009 - https://eclipse.org/jetty/jetty-jmx)
* Jetty :: JNDI Naming (org.eclipse.jetty:jetty-jndi:9.4.15.v20190215 - http://www.eclipse.org/jetty)
* Jetty :: Plus (org.eclipse.jetty:jetty-plus:9.4.15.v20190215 - http://www.eclipse.org/jetty)
* Jetty :: Rewrite Handler (org.eclipse.jetty:jetty-rewrite:9.4.53.v20231009 - https://eclipse.org/jetty/jetty-rewrite)
* Jetty :: Security (org.eclipse.jetty:jetty-security:9.4.53.v20231009 - https://eclipse.org/jetty/jetty-security)
- * Jetty :: Security (org.eclipse.jetty:jetty-security:9.4.57.v20241219 - https://jetty.org/jetty-security/)
- * Jetty :: Server Core (org.eclipse.jetty:jetty-server:9.4.57.v20241219 - https://jetty.org/jetty-server/)
- * Jetty :: Servlet Handling (org.eclipse.jetty:jetty-servlet:9.4.57.v20241219 - https://jetty.org/jetty-servlet/)
- * Jetty :: Utility Servlets and Filters (org.eclipse.jetty:jetty-servlets:9.4.57.v20241219 - https://jetty.org/jetty-servlets/)
- * Jetty :: Utilities (org.eclipse.jetty:jetty-util:9.4.57.v20241219 - https://jetty.org/jetty-util/)
- * Jetty :: Utilities :: Ajax(JSON) (org.eclipse.jetty:jetty-util-ajax:9.4.57.v20241219 - https://jetty.org/jetty-util-ajax/)
- * Jetty :: Webapp Application Support (org.eclipse.jetty:jetty-webapp:9.4.57.v20241219 - https://jetty.org/jetty-webapp/)
- * Jetty :: XML utilities (org.eclipse.jetty:jetty-xml:9.4.57.v20241219 - https://jetty.org/jetty-xml/)
+ * Jetty :: Security (org.eclipse.jetty:jetty-security:9.4.58.v20250814 - https://jetty.org/jetty-security/)
+ * Jetty :: Server Core (org.eclipse.jetty:jetty-server:9.4.58.v20250814 - https://jetty.org/jetty-server/)
+ * Jetty :: Servlet Handling (org.eclipse.jetty:jetty-servlet:9.4.58.v20250814 - https://jetty.org/jetty-servlet/)
+ * Jetty :: Utility Servlets and Filters (org.eclipse.jetty:jetty-servlets:9.4.58.v20250814 - https://jetty.org/jetty-servlets/)
+ * Jetty :: Utilities (org.eclipse.jetty:jetty-util:9.4.58.v20250814 - https://jetty.org/jetty-util/)
+ * Jetty :: Utilities :: Ajax(JSON) (org.eclipse.jetty:jetty-util-ajax:9.4.58.v20250814 - https://jetty.org/jetty-util-ajax/)
+ * Jetty :: Webapp Application Support (org.eclipse.jetty:jetty-webapp:9.4.58.v20250814 - https://jetty.org/jetty-webapp/)
+ * Jetty :: XML utilities (org.eclipse.jetty:jetty-xml:9.4.58.v20250814 - https://jetty.org/jetty-xml/)
* Jetty :: HTTP2 :: Client (org.eclipse.jetty.http2:http2-client:9.4.53.v20231009 - https://eclipse.org/jetty/http2-parent/http2-client)
- * Jetty :: HTTP2 :: Common (org.eclipse.jetty.http2:http2-common:9.4.57.v20241219 - https://jetty.org/http2-parent/http2-common/)
+ * Jetty :: HTTP2 :: Common (org.eclipse.jetty.http2:http2-common:9.4.58.v20250814 - https://jetty.org/http2-parent/http2-common/)
* Jetty :: HTTP2 :: HPACK (org.eclipse.jetty.http2:http2-hpack:9.4.53.v20231009 - https://eclipse.org/jetty/http2-parent/http2-hpack)
* Jetty :: HTTP2 :: HTTP Client Transport (org.eclipse.jetty.http2:http2-http-client-transport:9.4.53.v20231009 - https://eclipse.org/jetty/http2-parent/http2-http-client-transport)
- * Jetty :: HTTP2 :: Server (org.eclipse.jetty.http2:http2-server:9.4.57.v20241219 - https://jetty.org/http2-parent/http2-server/)
+ * Jetty :: HTTP2 :: Server (org.eclipse.jetty.http2:http2-server:9.4.58.v20250814 - https://jetty.org/http2-parent/http2-server/)
* Jetty :: Schemas (org.eclipse.jetty.toolchain:jetty-schemas:3.1.2 - https://eclipse.org/jetty/jetty-schemas)
- * Ehcache (org.ehcache:ehcache:3.10.8 - http://ehcache.org)
+ * Ehcache (org.ehcache:ehcache:3.11.1 - http://ehcache.org)
* flyway-core (org.flywaydb:flyway-core:8.5.13 - https://flywaydb.org/flyway-core)
* Ogg and Vorbis for Java, Core (org.gagravarr:vorbis-java-core:0.8 - https://github.com/Gagravarr/VorbisJava)
* Apache Tika plugin for Ogg, Vorbis and FLAC (org.gagravarr:vorbis-java-tika:0.8 - https://github.com/Gagravarr/VorbisJava)
- * jersey-core-client (org.glassfish.jersey.core:jersey-client:2.47 - https://projects.eclipse.org/projects/ee4j.jersey/jersey-client)
- * jersey-core-common (org.glassfish.jersey.core:jersey-common:2.47 - https://projects.eclipse.org/projects/ee4j.jersey/jersey-common)
- * jersey-inject-hk2 (org.glassfish.jersey.inject:jersey-hk2:2.47 - https://projects.eclipse.org/projects/ee4j.jersey/project/jersey-hk2)
+ * jersey-core-client (org.glassfish.jersey.core:jersey-client:2.48 - https://projects.eclipse.org/projects/ee4j.jersey/jersey-client)
+ * jersey-core-common (org.glassfish.jersey.core:jersey-common:2.48 - https://projects.eclipse.org/projects/ee4j.jersey/jersey-common)
+ * jersey-inject-hk2 (org.glassfish.jersey.inject:jersey-hk2:2.48 - https://projects.eclipse.org/projects/ee4j.jersey/project/jersey-hk2)
* Hibernate Validator Engine (org.hibernate.validator:hibernate-validator:6.2.5.Final - http://hibernate.org/validator/hibernate-validator)
* Hibernate Validator Portable Extension (org.hibernate.validator:hibernate-validator-cdi:6.2.5.Final - http://hibernate.org/validator/hibernate-validator-cdi)
* org.immutables.value-annotations (org.immutables:value-annotations:2.9.2 - http://immutables.org/value-annotations)
- * leveldb (org.iq80.leveldb:leveldb:0.12 - http://github.com/dain/leveldb/leveldb)
- * leveldb-api (org.iq80.leveldb:leveldb-api:0.12 - http://github.com/dain/leveldb/leveldb-api)
* Javassist (org.javassist:javassist:3.30.2-GA - https://www.javassist.org/)
* Java Annotation Indexer (org.jboss:jandex:2.4.2.Final - http://www.jboss.org/jandex)
- * JBoss Logging 3 (org.jboss.logging:jboss-logging:3.6.1.Final - http://www.jboss.org)
+ * JBoss Logging 3 (org.jboss.logging:jboss-logging:3.4.3.Final - http://www.jboss.org)
* JDOM (org.jdom:jdom2:2.0.6.1 - http://www.jdom.org)
- * IntelliJ IDEA Annotations (org.jetbrains:annotations:13.0 - http://www.jetbrains.org)
+ * JetBrains Java Annotations (org.jetbrains:annotations:17.0.0 - https://github.com/JetBrains/java-annotations)
* Kotlin Stdlib (org.jetbrains.kotlin:kotlin-stdlib:1.8.21 - https://kotlinlang.org/)
* Kotlin Stdlib Common (org.jetbrains.kotlin:kotlin-stdlib-common:1.8.21 - https://kotlinlang.org/)
* Kotlin Stdlib Jdk7 (org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.8.21 - https://kotlinlang.org/)
* Kotlin Stdlib Jdk8 (org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.8.21 - https://kotlinlang.org/)
+ * JSpecify annotations (org.jspecify:jspecify:1.0.0 - http://jspecify.org/)
* jtwig-core (org.jtwig:jtwig-core:5.87.0.RELEASE - http://jtwig.org)
* jtwig-reflection (org.jtwig:jtwig-reflection:5.87.0.RELEASE - http://jtwig.org)
* jtwig-spring (org.jtwig:jtwig-spring:5.87.0.RELEASE - http://jtwig.org)
@@ -391,18 +379,11 @@ https://wiki.lyrasis.org/display/DSPACE/Code+Contribution+Guidelines
* Jetty Servlet Tester (org.mortbay.jetty:jetty-servlet-tester:6.1.26 - http://www.eclipse.org/jetty/jetty-parent/project/jetty-servlet-tester)
* Jetty Utilities (org.mortbay.jetty:jetty-util:6.1.26 - http://www.eclipse.org/jetty/jetty-parent/project/jetty-util)
* Servlet Specification API (org.mortbay.jetty:servlet-api:2.5-20081211 - http://jetty.mortbay.org/servlet-api)
- * jwarc (org.netpreserve:jwarc:0.31.1 - https://github.com/iipc/jwarc)
+ * jwarc (org.netpreserve:jwarc:0.35.0 - https://github.com/iipc/jwarc)
* Objenesis (org.objenesis:objenesis:3.2 - http://objenesis.org/objenesis)
- * org.opentest4j:opentest4j (org.opentest4j:opentest4j:1.3.0 - https://github.com/ota4j-team/opentest4j)
* parboiled-core (org.parboiled:parboiled-core:1.1.7 - http://parboiled.org)
* parboiled-java (org.parboiled:parboiled-java:1.1.7 - http://parboiled.org)
* RRD4J (org.rrd4j:rrd4j:3.5 - https://github.com/rrd4j/rrd4j/)
- * Scala Library (org.scala-lang:scala-library:2.13.16 - https://www.scala-lang.org/)
- * Scala Compiler (org.scala-lang:scala-reflect:2.13.0 - https://www.scala-lang.org/)
- * scala-collection-compat (org.scala-lang.modules:scala-collection-compat_2.13:2.1.6 - http://www.scala-lang.org/)
- * scala-java8-compat (org.scala-lang.modules:scala-java8-compat_2.13:0.9.0 - http://www.scala-lang.org/)
- * scala-parser-combinators (org.scala-lang.modules:scala-parser-combinators_2.13:1.1.2 - http://www.scala-lang.org/)
- * scala-xml (org.scala-lang.modules:scala-xml_2.13:1.3.0 - http://www.scala-lang.org/)
* JSONassert (org.skyscreamer:jsonassert:1.5.1 - https://github.com/skyscreamer/JSONassert)
* JCL 1.2 implemented over SLF4J (org.slf4j:jcl-over-slf4j:1.7.36 - http://www.slf4j.org)
* Spring AOP (org.springframework:spring-aop:5.3.39 - https://github.com/spring-projects/spring-framework)
@@ -453,13 +434,47 @@ https://wiki.lyrasis.org/display/DSPACE/Code+Contribution+Guidelines
* SWORD v2 :: Common Server Library (org.swordapp:sword2-server:1.0 - http://www.swordapp.org/)
* snappy-java (org.xerial.snappy:snappy-java:1.1.10.1 - https://github.com/xerial/snappy-java)
* xml-matchers (org.xmlmatchers:xml-matchers:0.10 - http://code.google.com/p/xml-matchers/)
- * org.xmlunit:xmlunit-core (org.xmlunit:xmlunit-core:2.10.2 - https://www.xmlunit.org/)
+ * org.xmlunit:xmlunit-core (org.xmlunit:xmlunit-core:2.11.0 - https://www.xmlunit.org/)
* org.xmlunit:xmlunit-core (org.xmlunit:xmlunit-core:2.9.1 - https://www.xmlunit.org/)
* org.xmlunit:xmlunit-placeholders (org.xmlunit:xmlunit-placeholders:2.9.1 - https://www.xmlunit.org/xmlunit-placeholders/)
* SnakeYAML (org.yaml:snakeyaml:1.30 - https://bitbucket.org/snakeyaml/snakeyaml)
+ * AWS Java SDK :: Annotations (software.amazon.awssdk:annotations:2.42.40 - https://aws.amazon.com/sdkforjava/core/annotations)
+ * AWS Java SDK :: Arns (software.amazon.awssdk:arns:2.42.40 - https://aws.amazon.com/sdkforjava)
+ * AWS Java SDK :: Auth (software.amazon.awssdk:auth:2.42.40 - https://aws.amazon.com/sdkforjava)
+ * AWS Java SDK :: AWS Core (software.amazon.awssdk:aws-core:2.42.40 - https://aws.amazon.com/sdkforjava)
+ * AWS Java SDK :: Core :: Protocols :: AWS Query Protocol (software.amazon.awssdk:aws-query-protocol:2.42.40 - https://aws.amazon.com/sdkforjava)
+ * AWS Java SDK :: Core :: Protocols :: AWS Xml Protocol (software.amazon.awssdk:aws-xml-protocol:2.42.40 - https://aws.amazon.com/sdkforjava)
+ * AWS Java SDK :: Checksums (software.amazon.awssdk:checksums:2.42.40 - https://aws.amazon.com/sdkforjava)
+ * AWS Java SDK :: Checksums SPI (software.amazon.awssdk:checksums-spi:2.42.40 - https://aws.amazon.com/sdkforjava)
+ * AWS Java SDK :: AWS CRT Core (software.amazon.awssdk:crt-core:2.42.40 - https://aws.amazon.com/sdkforjava)
+ * AWS Java SDK :: Endpoints SPI (software.amazon.awssdk:endpoints-spi:2.42.40 - https://aws.amazon.com/sdkforjava/core/endpoints-spi)
+ * AWS Java SDK :: HTTP Auth (software.amazon.awssdk:http-auth:2.42.40 - https://aws.amazon.com/sdkforjava)
+ * AWS Java SDK :: HTTP Auth AWS (software.amazon.awssdk:http-auth-aws:2.42.40 - https://aws.amazon.com/sdkforjava)
+ * AWS Java SDK :: HTTP Auth Event Stream (software.amazon.awssdk:http-auth-aws-eventstream:2.42.40 - https://aws.amazon.com/sdkforjava)
+ * AWS Java SDK :: HTTP Auth SPI (software.amazon.awssdk:http-auth-spi:2.42.40 - https://aws.amazon.com/sdkforjava)
+ * AWS Java SDK :: HTTP Client Interface (software.amazon.awssdk:http-client-spi:2.42.40 - https://aws.amazon.com/sdkforjava/http-client-spi)
+ * AWS Java SDK :: Identity SPI (software.amazon.awssdk:identity-spi:2.42.40 - https://aws.amazon.com/sdkforjava)
+ * AWS Java SDK :: Core :: Protocols :: Json Utils (software.amazon.awssdk:json-utils:2.42.40 - https://aws.amazon.com/sdkforjava)
+ * AWS Java SDK :: Metrics SPI (software.amazon.awssdk:metrics-spi:2.42.40 - https://aws.amazon.com/sdkforjava/core/metrics-spi)
+ * AWS Java SDK :: Profiles (software.amazon.awssdk:profiles:2.42.40 - https://aws.amazon.com/sdkforjava)
+ * AWS Java SDK :: Core :: Protocols :: Protocol Core (software.amazon.awssdk:protocol-core:2.42.40 - https://aws.amazon.com/sdkforjava)
+ * AWS Java SDK :: Regions (software.amazon.awssdk:regions:2.42.40 - https://aws.amazon.com/sdkforjava/core/regions)
+ * AWS Java SDK :: Retries (software.amazon.awssdk:retries:2.42.40 - https://aws.amazon.com/sdkforjava/core/retries)
+ * AWS Java SDK :: Retries API (software.amazon.awssdk:retries-spi:2.42.40 - https://aws.amazon.com/sdkforjava/core/retries-spi)
+ * AWS Java SDK :: Services :: Amazon S3 (software.amazon.awssdk:s3:2.42.40 - https://aws.amazon.com/sdkforjava)
+ * AWS Java SDK :: SDK Core (software.amazon.awssdk:sdk-core:2.42.40 - https://aws.amazon.com/sdkforjava)
+ * AWS Java SDK :: Third Party :: Jackson-core (software.amazon.awssdk:third-party-jackson-core:2.42.40 - https://aws.amazon.com/sdkforjava)
+ * AWS Java SDK :: Utilities (software.amazon.awssdk:utils:2.42.40 - https://aws.amazon.com/sdkforjava/utils)
+ * AWS Java SDK :: Utils Lite (software.amazon.awssdk:utils-lite:2.42.40 - https://aws.amazon.com/sdkforjava)
+ * software.amazon.awssdk.crt:aws-crt (software.amazon.awssdk.crt:aws-crt:0.45.1 - https://github.com/awslabs/aws-crt-java)
+ * AWS Event Stream (software.amazon.eventstream:eventstream:1.0.1 - https://github.com/awslabs/aws-eventstream-java)
* Xerces2-j (xerces:xercesImpl:2.12.2 - https://xerces.apache.org/xerces2-j/)
* XML Commons External Components XML APIs (xml-apis:xml-apis:1.4.01 - http://xml.apache.org/commons/components/external/)
+ BSD 2-Clause License:
+
+ * zstd-jni (com.github.luben:zstd-jni:1.5.7-4 - https://github.com/luben/zstd-jni)
+
BSD License:
* AntLR Parser Generator (antlr:antlr:2.7.7 - http://www.antlr.org/)
@@ -471,15 +486,15 @@ https://wiki.lyrasis.org/display/DSPACE/Code+Contribution+Guidelines
* Protocol Buffers [Core] (com.google.protobuf:protobuf-java:3.15.0 - https://developers.google.com/protocol-buffers/protobuf-java/)
* JZlib (com.jcraft:jzlib:1.1.3 - http://www.jcraft.com/jzlib/)
* jmustache (com.samskivert:jmustache:1.15 - http://github.com/samskivert/jmustache)
- * dnsjava (dnsjava:dnsjava:3.6.3 - https://github.com/dnsjava/dnsjava)
- * jaxen (jaxen:jaxen:2.0.0 - http://www.cafeconleche.org/jaxen/jaxen)
+ * dnsjava (dnsjava:dnsjava:3.6.4 - https://github.com/dnsjava/dnsjava)
+ * jaxen (jaxen:jaxen:2.0.1 - https://jaxen-xpath.github.io/jaxen/jaxen/)
* ANTLR 4 Runtime (org.antlr:antlr4-runtime:4.5.1-1 - http://www.antlr.org/antlr4-runtime)
* commons-compiler (org.codehaus.janino:commons-compiler:3.1.8 - http://janino-compiler.github.io/commons-compiler/)
* janino (org.codehaus.janino:janino:3.1.8 - http://janino-compiler.github.io/janino/)
* Stax2 API (org.codehaus.woodstox:stax2-api:4.2.1 - http://github.com/FasterXML/stax2-api)
* Hamcrest Date (org.exparity:hamcrest-date:2.0.8 - https://github.com/exparity/hamcrest-date)
- * jersey-core-client (org.glassfish.jersey.core:jersey-client:2.47 - https://projects.eclipse.org/projects/ee4j.jersey/jersey-client)
- * jersey-inject-hk2 (org.glassfish.jersey.inject:jersey-hk2:2.47 - https://projects.eclipse.org/projects/ee4j.jersey/project/jersey-hk2)
+ * jersey-core-client (org.glassfish.jersey.core:jersey-client:2.48 - https://projects.eclipse.org/projects/ee4j.jersey/jersey-client)
+ * jersey-inject-hk2 (org.glassfish.jersey.inject:jersey-hk2:2.48 - https://projects.eclipse.org/projects/ee4j.jersey/project/jersey-hk2)
* Hamcrest (org.hamcrest:hamcrest:2.2 - http://hamcrest.org/JavaHamcrest/)
* Hamcrest Core (org.hamcrest:hamcrest-core:2.2 - http://hamcrest.org/JavaHamcrest/)
* HdrHistogram (org.hdrhistogram:HdrHistogram:2.1.12 - http://hdrhistogram.github.io/HdrHistogram/)
@@ -489,16 +504,12 @@ https://wiki.lyrasis.org/display/DSPACE/Code+Contribution+Guidelines
* asm-commons (org.ow2.asm:asm-commons:9.3 - http://asm.ow2.io/)
* ASM Tree (org.ow2.asm:asm-tree:5.0.3 - http://asm.objectweb.org/asm-tree/)
* ASM Util (org.ow2.asm:asm-util:5.0.3 - http://asm.objectweb.org/asm-util/)
- * PostgreSQL JDBC Driver (org.postgresql:postgresql:42.7.7 - https://jdbc.postgresql.org)
+ * PostgreSQL JDBC Driver (org.postgresql:postgresql:42.7.11 - https://jdbc.postgresql.org)
* Reflections (org.reflections:reflections:0.9.12 - http://github.com/ronmamo/reflections)
* JMatIO (org.tallison:jmatio:1.5 - https://github.com/tballison/jmatio)
- * XZ for Java (org.tukaani:xz:1.10 - https://tukaani.org/xz/java.html)
+ * XZ for Java (org.tukaani:xz:1.12 - https://tukaani.org/xz/java.html)
* XMLUnit for Java (xmlunit:xmlunit:1.3 - http://xmlunit.sourceforge.net/)
- CC0:
-
- * reactive-streams (org.reactivestreams:reactive-streams:1.0.2 - http://www.reactive-streams.org/)
-
Common Development and Distribution License (CDDL):
* JavaMail API (com.sun.mail:javax.mail:1.6.2 - http://javaee.github.io/javamail/javax.mail)
@@ -514,15 +525,15 @@ https://wiki.lyrasis.org/display/DSPACE/Code+Contribution+Guidelines
* Java Servlet API (javax.servlet:javax.servlet-api:3.1.0 - http://servlet-spec.java.net)
* javax.transaction API (javax.transaction:javax.transaction-api:1.3 - http://jta-spec.java.net)
* jaxb-api (javax.xml.bind:jaxb-api:2.3.1 - https://github.com/javaee/jaxb-spec/jaxb-api)
- * JHighlight (org.codelibs:jhighlight:1.1.0 - https://github.com/codelibs/jhighlight)
+ * JHighlight (org.codelibs:jhighlight:1.1.1 - https://github.com/codelibs/jhighlight)
* HK2 API module (org.glassfish.hk2:hk2-api:2.6.1 - https://github.com/eclipse-ee4j/glassfish-hk2/hk2-api)
* ServiceLocator Default Implementation (org.glassfish.hk2:hk2-locator:2.6.1 - https://github.com/eclipse-ee4j/glassfish-hk2/hk2-locator)
* HK2 Implementation Utilities (org.glassfish.hk2:hk2-utils:2.6.1 - https://github.com/eclipse-ee4j/glassfish-hk2/hk2-utils)
* OSGi resource locator (org.glassfish.hk2:osgi-resource-locator:1.0.3 - https://projects.eclipse.org/projects/ee4j/osgi-resource-locator)
* aopalliance version 1.0 repackaged as a module (org.glassfish.hk2.external:aopalliance-repackaged:2.6.1 - https://github.com/eclipse-ee4j/glassfish-hk2/external/aopalliance-repackaged)
* javax.inject:1 as OSGi bundle (org.glassfish.hk2.external:jakarta.inject:2.6.1 - https://github.com/eclipse-ee4j/glassfish-hk2/external/jakarta.inject)
- * jersey-core-client (org.glassfish.jersey.core:jersey-client:2.47 - https://projects.eclipse.org/projects/ee4j.jersey/jersey-client)
- * jersey-inject-hk2 (org.glassfish.jersey.inject:jersey-hk2:2.47 - https://projects.eclipse.org/projects/ee4j.jersey/project/jersey-hk2)
+ * jersey-core-client (org.glassfish.jersey.core:jersey-client:2.48 - https://projects.eclipse.org/projects/ee4j.jersey/jersey-client)
+ * jersey-inject-hk2 (org.glassfish.jersey.inject:jersey-hk2:2.48 - https://projects.eclipse.org/projects/ee4j.jersey/project/jersey-hk2)
* Java Transaction API (org.jboss.spec.javax.transaction:jboss-transaction-api_1.2_spec:1.1.1.Final - http://www.jboss.org/jboss-transaction-api_1.2_spec)
Cordra (Version 2) License Agreement:
@@ -543,8 +554,8 @@ https://wiki.lyrasis.org/display/DSPACE/Code+Contribution+Guidelines
* javax.persistence-api (javax.persistence:javax.persistence-api:2.2 - https://github.com/javaee/jpa-spec)
* JAXB Runtime (org.glassfish.jaxb:jaxb-runtime:2.3.9 - https://eclipse-ee4j.github.io/jaxb-ri/)
* TXW2 Runtime (org.glassfish.jaxb:txw2:2.3.9 - https://eclipse-ee4j.github.io/jaxb-ri/)
- * jersey-core-client (org.glassfish.jersey.core:jersey-client:2.47 - https://projects.eclipse.org/projects/ee4j.jersey/jersey-client)
- * jersey-inject-hk2 (org.glassfish.jersey.inject:jersey-hk2:2.47 - https://projects.eclipse.org/projects/ee4j.jersey/project/jersey-hk2)
+ * jersey-core-client (org.glassfish.jersey.core:jersey-client:2.48 - https://projects.eclipse.org/projects/ee4j.jersey/jersey-client)
+ * jersey-inject-hk2 (org.glassfish.jersey.inject:jersey-hk2:2.48 - https://projects.eclipse.org/projects/ee4j.jersey/project/jersey-hk2)
* Java Persistence API, Version 2.1 (org.hibernate.javax.persistence:hibernate-jpa-2.1-api:1.0.2.Final - http://hibernate.org)
* org.locationtech.jts:jts-core (org.locationtech.jts:jts-core:1.19.0 - https://www.locationtech.org/projects/technology.jts/jts-modules/jts-core)
* org.locationtech.jts.io:jts-io-common (org.locationtech.jts.io:jts-io-common:1.19.0 - https://www.locationtech.org/projects/technology.jts/jts-modules/jts-io/jts-io-common)
@@ -552,7 +563,7 @@ https://wiki.lyrasis.org/display/DSPACE/Code+Contribution+Guidelines
Eclipse Public License:
* System Rules (com.github.stefanbirkner:system-rules:1.19.0 - http://stefanbirkner.github.io/system-rules/)
- * H2 Database Engine (com.h2database:h2:2.3.232 - https://h2database.com)
+ * H2 Database Engine (com.h2database:h2:2.4.240 - https://h2database.com)
* Jakarta Annotations API (jakarta.annotation:jakarta.annotation-api:1.3.5 - https://projects.eclipse.org/projects/ee4j.ca)
* javax.transaction API (jakarta.transaction:jakarta.transaction-api:1.3.3 - https://projects.eclipse.org/projects/ee4j.jta)
* jakarta.ws.rs-api (jakarta.ws.rs:jakarta.ws.rs-api:2.1.6 - https://github.com/eclipse-ee4j/jaxrs-api)
@@ -564,34 +575,34 @@ https://wiki.lyrasis.org/display/DSPACE/Code+Contribution+Guidelines
* Apache :: JSTL module (org.eclipse.jetty:apache-jstl:9.4.15.v20190215 - http://tomcat.apache.org/taglibs/standard/)
* Jetty :: ALPN :: Client (org.eclipse.jetty:jetty-alpn-client:9.4.53.v20231009 - https://eclipse.org/jetty/jetty-alpn-parent/jetty-alpn-client)
* Jetty :: ALPN :: JDK9 Client Implementation (org.eclipse.jetty:jetty-alpn-java-client:9.4.53.v20231009 - https://eclipse.org/jetty/jetty-alpn-parent/jetty-alpn-java-client)
- * Jetty :: ALPN :: JDK9 Server Implementation (org.eclipse.jetty:jetty-alpn-java-server:9.4.57.v20241219 - https://jetty.org/jetty-alpn-parent/jetty-alpn-java-server/)
+ * Jetty :: ALPN :: JDK9 Server Implementation (org.eclipse.jetty:jetty-alpn-java-server:9.4.58.v20250814 - https://jetty.org/jetty-alpn-parent/jetty-alpn-java-server/)
* Jetty :: ALPN :: Server (org.eclipse.jetty:jetty-alpn-server:9.4.53.v20231009 - https://eclipse.org/jetty/jetty-alpn-parent/jetty-alpn-server)
- * Jetty :: ALPN :: Server (org.eclipse.jetty:jetty-alpn-server:9.4.57.v20241219 - https://jetty.org/jetty-alpn-parent/jetty-alpn-server/)
+ * Jetty :: ALPN :: Server (org.eclipse.jetty:jetty-alpn-server:9.4.58.v20250814 - https://jetty.org/jetty-alpn-parent/jetty-alpn-server/)
* Jetty :: Servlet Annotations (org.eclipse.jetty:jetty-annotations:9.4.15.v20190215 - http://www.eclipse.org/jetty)
* Jetty :: Asynchronous HTTP Client (org.eclipse.jetty:jetty-client:9.4.53.v20231009 - https://eclipse.org/jetty/jetty-client)
* Jetty :: Continuation (org.eclipse.jetty:jetty-continuation:9.4.53.v20231009 - https://eclipse.org/jetty/jetty-continuation)
- * Jetty :: Continuation (org.eclipse.jetty:jetty-continuation:9.4.57.v20241219 - https://jetty.org/jetty-continuation/)
- * Jetty :: Deployers (org.eclipse.jetty:jetty-deploy:9.4.57.v20241219 - https://jetty.org/jetty-deploy/)
- * Jetty :: Http Utility (org.eclipse.jetty:jetty-http:9.4.57.v20241219 - https://jetty.org/jetty-http/)
- * Jetty :: IO Utility (org.eclipse.jetty:jetty-io:9.4.57.v20241219 - https://jetty.org/jetty-io/)
+ * Jetty :: Continuation (org.eclipse.jetty:jetty-continuation:9.4.58.v20250814 - https://jetty.org/jetty-continuation/)
+ * Jetty :: Deployers (org.eclipse.jetty:jetty-deploy:9.4.58.v20250814 - https://jetty.org/jetty-deploy/)
+ * Jetty :: Http Utility (org.eclipse.jetty:jetty-http:9.4.58.v20250814 - https://jetty.org/jetty-http/)
+ * Jetty :: IO Utility (org.eclipse.jetty:jetty-io:9.4.58.v20250814 - https://jetty.org/jetty-io/)
* Jetty :: JMX Management (org.eclipse.jetty:jetty-jmx:9.4.53.v20231009 - https://eclipse.org/jetty/jetty-jmx)
* Jetty :: JNDI Naming (org.eclipse.jetty:jetty-jndi:9.4.15.v20190215 - http://www.eclipse.org/jetty)
* Jetty :: Plus (org.eclipse.jetty:jetty-plus:9.4.15.v20190215 - http://www.eclipse.org/jetty)
* Jetty :: Rewrite Handler (org.eclipse.jetty:jetty-rewrite:9.4.53.v20231009 - https://eclipse.org/jetty/jetty-rewrite)
* Jetty :: Security (org.eclipse.jetty:jetty-security:9.4.53.v20231009 - https://eclipse.org/jetty/jetty-security)
- * Jetty :: Security (org.eclipse.jetty:jetty-security:9.4.57.v20241219 - https://jetty.org/jetty-security/)
- * Jetty :: Server Core (org.eclipse.jetty:jetty-server:9.4.57.v20241219 - https://jetty.org/jetty-server/)
- * Jetty :: Servlet Handling (org.eclipse.jetty:jetty-servlet:9.4.57.v20241219 - https://jetty.org/jetty-servlet/)
- * Jetty :: Utility Servlets and Filters (org.eclipse.jetty:jetty-servlets:9.4.57.v20241219 - https://jetty.org/jetty-servlets/)
- * Jetty :: Utilities (org.eclipse.jetty:jetty-util:9.4.57.v20241219 - https://jetty.org/jetty-util/)
- * Jetty :: Utilities :: Ajax(JSON) (org.eclipse.jetty:jetty-util-ajax:9.4.57.v20241219 - https://jetty.org/jetty-util-ajax/)
- * Jetty :: Webapp Application Support (org.eclipse.jetty:jetty-webapp:9.4.57.v20241219 - https://jetty.org/jetty-webapp/)
- * Jetty :: XML utilities (org.eclipse.jetty:jetty-xml:9.4.57.v20241219 - https://jetty.org/jetty-xml/)
+ * Jetty :: Security (org.eclipse.jetty:jetty-security:9.4.58.v20250814 - https://jetty.org/jetty-security/)
+ * Jetty :: Server Core (org.eclipse.jetty:jetty-server:9.4.58.v20250814 - https://jetty.org/jetty-server/)
+ * Jetty :: Servlet Handling (org.eclipse.jetty:jetty-servlet:9.4.58.v20250814 - https://jetty.org/jetty-servlet/)
+ * Jetty :: Utility Servlets and Filters (org.eclipse.jetty:jetty-servlets:9.4.58.v20250814 - https://jetty.org/jetty-servlets/)
+ * Jetty :: Utilities (org.eclipse.jetty:jetty-util:9.4.58.v20250814 - https://jetty.org/jetty-util/)
+ * Jetty :: Utilities :: Ajax(JSON) (org.eclipse.jetty:jetty-util-ajax:9.4.58.v20250814 - https://jetty.org/jetty-util-ajax/)
+ * Jetty :: Webapp Application Support (org.eclipse.jetty:jetty-webapp:9.4.58.v20250814 - https://jetty.org/jetty-webapp/)
+ * Jetty :: XML utilities (org.eclipse.jetty:jetty-xml:9.4.58.v20250814 - https://jetty.org/jetty-xml/)
* Jetty :: HTTP2 :: Client (org.eclipse.jetty.http2:http2-client:9.4.53.v20231009 - https://eclipse.org/jetty/http2-parent/http2-client)
- * Jetty :: HTTP2 :: Common (org.eclipse.jetty.http2:http2-common:9.4.57.v20241219 - https://jetty.org/http2-parent/http2-common/)
+ * Jetty :: HTTP2 :: Common (org.eclipse.jetty.http2:http2-common:9.4.58.v20250814 - https://jetty.org/http2-parent/http2-common/)
* Jetty :: HTTP2 :: HPACK (org.eclipse.jetty.http2:http2-hpack:9.4.53.v20231009 - https://eclipse.org/jetty/http2-parent/http2-hpack)
* Jetty :: HTTP2 :: HTTP Client Transport (org.eclipse.jetty.http2:http2-http-client-transport:9.4.53.v20231009 - https://eclipse.org/jetty/http2-parent/http2-http-client-transport)
- * Jetty :: HTTP2 :: Server (org.eclipse.jetty.http2:http2-server:9.4.57.v20241219 - https://jetty.org/http2-parent/http2-server/)
+ * Jetty :: HTTP2 :: Server (org.eclipse.jetty.http2:http2-server:9.4.58.v20250814 - https://jetty.org/http2-parent/http2-server/)
* Jetty :: Schemas (org.eclipse.jetty.toolchain:jetty-schemas:3.1.2 - https://eclipse.org/jetty/jetty-schemas)
* HK2 API module (org.glassfish.hk2:hk2-api:2.6.1 - https://github.com/eclipse-ee4j/glassfish-hk2/hk2-api)
* ServiceLocator Default Implementation (org.glassfish.hk2:hk2-locator:2.6.1 - https://github.com/eclipse-ee4j/glassfish-hk2/hk2-locator)
@@ -599,13 +610,10 @@ https://wiki.lyrasis.org/display/DSPACE/Code+Contribution+Guidelines
* OSGi resource locator (org.glassfish.hk2:osgi-resource-locator:1.0.3 - https://projects.eclipse.org/projects/ee4j/osgi-resource-locator)
* aopalliance version 1.0 repackaged as a module (org.glassfish.hk2.external:aopalliance-repackaged:2.6.1 - https://github.com/eclipse-ee4j/glassfish-hk2/external/aopalliance-repackaged)
* javax.inject:1 as OSGi bundle (org.glassfish.hk2.external:jakarta.inject:2.6.1 - https://github.com/eclipse-ee4j/glassfish-hk2/external/jakarta.inject)
- * jersey-core-client (org.glassfish.jersey.core:jersey-client:2.47 - https://projects.eclipse.org/projects/ee4j.jersey/jersey-client)
- * jersey-core-common (org.glassfish.jersey.core:jersey-common:2.47 - https://projects.eclipse.org/projects/ee4j.jersey/jersey-common)
- * jersey-inject-hk2 (org.glassfish.jersey.inject:jersey-hk2:2.47 - https://projects.eclipse.org/projects/ee4j.jersey/project/jersey-hk2)
+ * jersey-core-client (org.glassfish.jersey.core:jersey-client:2.48 - https://projects.eclipse.org/projects/ee4j.jersey/jersey-client)
+ * jersey-core-common (org.glassfish.jersey.core:jersey-common:2.48 - https://projects.eclipse.org/projects/ee4j.jersey/jersey-common)
+ * jersey-inject-hk2 (org.glassfish.jersey.inject:jersey-hk2:2.48 - https://projects.eclipse.org/projects/ee4j.jersey/project/jersey-hk2)
* Java Persistence API, Version 2.1 (org.hibernate.javax.persistence:hibernate-jpa-2.1-api:1.0.2.Final - http://hibernate.org)
- * JUnit Platform Commons (org.junit.platform:junit-platform-commons:1.11.4 - https://junit.org/junit5/)
- * JUnit Platform Engine API (org.junit.platform:junit-platform-engine:1.11.4 - https://junit.org/junit5/)
- * JUnit Vintage Engine (org.junit.vintage:junit-vintage-engine:5.11.4 - https://junit.org/junit5/)
* org.locationtech.jts:jts-core (org.locationtech.jts:jts-core:1.19.0 - https://www.locationtech.org/projects/technology.jts/jts-modules/jts-core)
* org.locationtech.jts.io:jts-io-common (org.locationtech.jts.io:jts-io-common:1.19.0 - https://www.locationtech.org/projects/technology.jts/jts-modules/jts-io/jts-io-common)
* Jetty Server (org.mortbay.jetty:jetty:6.1.26 - http://www.eclipse.org/jetty/jetty-parent/project/modules/jetty)
@@ -631,14 +639,14 @@ https://wiki.lyrasis.org/display/DSPACE/Code+Contribution+Guidelines
* msg-simple (com.github.java-json-tools:msg-simple:1.2 - https://github.com/java-json-tools/msg-simple)
* uri-template (com.github.java-json-tools:uri-template:0.10 - https://github.com/java-json-tools/uri-template)
* FindBugs-Annotations (com.google.code.findbugs:annotations:3.0.1u2 - http://findbugs.sourceforge.net/)
- * JHighlight (org.codelibs:jhighlight:1.1.0 - https://github.com/codelibs/jhighlight)
+ * JHighlight (org.codelibs:jhighlight:1.1.1 - https://github.com/codelibs/jhighlight)
* Hibernate ORM - hibernate-core (org.hibernate:hibernate-core:5.6.15.Final - https://hibernate.org/orm)
* Hibernate ORM - hibernate-jcache (org.hibernate:hibernate-jcache:5.6.15.Final - https://hibernate.org/orm)
* Hibernate ORM - hibernate-jpamodelgen (org.hibernate:hibernate-jpamodelgen:5.6.15.Final - https://hibernate.org/orm)
* Hibernate Commons Annotations (org.hibernate.common:hibernate-commons-annotations:5.1.2.Final - http://hibernate.org)
* im4java (org.im4java:im4java:1.4.0 - http://sourceforge.net/projects/im4java/)
* Javassist (org.javassist:javassist:3.30.2-GA - https://www.javassist.org/)
- * XOM (xom:xom:1.3.9 - https://xom.nu)
+ * XOM (xom:xom:1.4.0 - https://xom.nu)
Go License:
@@ -652,29 +660,34 @@ https://wiki.lyrasis.org/display/DSPACE/Code+Contribution+Guidelines
* Simple Magic (com.j256.simplemagic:simplemagic:1.17 - https://256stuff.com/sources/simplemagic/)
+ LGPL-2.1-or-later:
+
+ * Java Native Access (net.java.dev.jna:jna:5.18.1 - https://github.com/java-native-access/jna)
+
MIT License:
- * better-files (com.github.pathikrit:better-files_2.13:3.9.1 - https://github.com/pathikrit/better-files)
* Java SemVer (com.github.zafarkhaja:java-semver:0.9.0 - https://github.com/zafarkhaja/jsemver)
- * dd-plist (com.googlecode.plist:dd-plist:1.28 - http://www.github.com/3breadt/dd-plist)
+ * dd-plist (com.googlecode.plist:dd-plist:1.29 - http://www.github.com/3breadt/dd-plist)
* DigitalCollections: IIIF API Library (de.digitalcollections.iiif:iiif-apis:0.3.11 - https://github.com/dbmdz/iiif-apis)
- * s3mock (io.findify:s3mock_2.13:0.2.6 - https://github.com/findify/s3mock)
* ClassGraph (io.github.classgraph:classgraph:4.8.154 - https://github.com/classgraph/classgraph)
* JOpt Simple (net.sf.jopt-simple:jopt-simple:5.0.4 - http://jopt-simple.github.io/jopt-simple)
- * Bouncy Castle JavaMail S/MIME APIs (org.bouncycastle:bcmail-jdk18on:1.80 - https://www.bouncycastle.org/download/bouncy-castle-java/)
+ * Bouncy Castle JavaMail Jakarta S/MIME APIs (org.bouncycastle:bcjmail-jdk18on:1.83 - https://www.bouncycastle.org/download/bouncy-castle-java/)
* Bouncy Castle PKIX, CMS, EAC, TSP, PKCS, OCSP, CMP, and CRMF APIs (org.bouncycastle:bcpkix-jdk18on:1.81 - https://www.bouncycastle.org/download/bouncy-castle-java/)
* Bouncy Castle Provider (org.bouncycastle:bcprov-jdk18on:1.81 - https://www.bouncycastle.org/download/bouncy-castle-java/)
* Bouncy Castle ASN.1 Extension and Utility APIs (org.bouncycastle:bcutil-jdk18on:1.81 - https://www.bouncycastle.org/download/bouncy-castle-java/)
* org.brotli:dec (org.brotli:dec:0.1.2 - http://brotli.org/dec)
* Checker Qual (org.checkerframework:checker-qual:3.23.0 - https://checkerframework.org)
- * Checker Qual (org.checkerframework:checker-qual:3.49.3 - https://checkerframework.org/)
- * jersey-core-client (org.glassfish.jersey.core:jersey-client:2.47 - https://projects.eclipse.org/projects/ee4j.jersey/jersey-client)
- * jersey-inject-hk2 (org.glassfish.jersey.inject:jersey-hk2:2.47 - https://projects.eclipse.org/projects/ee4j.jersey/project/jersey-hk2)
+ * jersey-core-client (org.glassfish.jersey.core:jersey-client:2.48 - https://projects.eclipse.org/projects/ee4j.jersey/jersey-client)
+ * jersey-inject-hk2 (org.glassfish.jersey.inject:jersey-hk2:2.48 - https://projects.eclipse.org/projects/ee4j.jersey/project/jersey-hk2)
+ * jsoup Java HTML Parser (org.jsoup:jsoup:1.22.1 - https://jsoup.org/)
* mockito-core (org.mockito:mockito-core:3.12.4 - https://github.com/mockito/mockito)
* mockito-inline (org.mockito:mockito-inline:3.12.4 - https://github.com/mockito/mockito)
* ORCID - Model (org.orcid:orcid-model:3.0.2 - http://github.com/ORCID/orcid-model)
+ * Duct Tape (org.rnorth.duct-tape:duct-tape:1.0.8 - https://github.com/rnorth/duct-tape)
* JUL to SLF4J bridge (org.slf4j:jul-to-slf4j:1.7.36 - http://www.slf4j.org)
* SLF4J API Module (org.slf4j:slf4j-api:1.7.36 - http://www.slf4j.org)
+ * Testcontainers Core (org.testcontainers:testcontainers:2.0.5 - https://java.testcontainers.org)
+ * Testcontainers :: Localstack (org.testcontainers:testcontainers-localstack:2.0.5 - https://java.testcontainers.org)
* HAL Browser (org.webjars:hal-browser:ad9b865 - http://webjars.org)
* toastr (org.webjars.bowergithub.codeseven:toastr:2.1.4 - http://webjars.org)
* backbone (org.webjars.bowergithub.jashkenas:backbone:1.4.1 - https://www.webjars.org)
@@ -682,30 +695,38 @@ https://wiki.lyrasis.org/display/DSPACE/Code+Contribution+Guidelines
* jquery (org.webjars.bowergithub.jquery:jquery-dist:3.7.1 - https://www.webjars.org)
* urijs (org.webjars.bowergithub.medialize:uri.js:1.19.11 - https://www.webjars.org)
* bootstrap (org.webjars.bowergithub.twbs:bootstrap:4.6.2 - https://www.webjars.org)
- * core-js (org.webjars.npm:core-js:3.42.0 - https://www.webjars.org)
+ * core-js (org.webjars.npm:core-js:3.49.0 - https://www.webjars.org)
* @json-editor/json-editor (org.webjars.npm:json-editor__json-editor:2.15.2 - https://www.webjars.org)
+ MIT-0:
+
+ * reactive-streams (org.reactivestreams:reactive-streams:1.0.4 - http://www.reactive-streams.org/)
+
Mozilla Public License:
* juniversalchardet (com.github.albfernandez:juniversalchardet:2.5.0 - https://github.com/albfernandez/juniversalchardet)
- * H2 Database Engine (com.h2database:h2:2.3.232 - https://h2database.com)
+ * H2 Database Engine (com.h2database:h2:2.4.240 - https://h2database.com)
* Saxon-HE (net.sf.saxon:Saxon-HE:9.9.1-8 - http://www.saxonica.com/)
* Javassist (org.javassist:javassist:3.30.2-GA - https://www.javassist.org/)
* Mozilla Rhino (org.mozilla:rhino:1.7.7.2 - https://developer.mozilla.org/en/Rhino)
Public Domain:
- * jersey-core-client (org.glassfish.jersey.core:jersey-client:2.47 - https://projects.eclipse.org/projects/ee4j.jersey/jersey-client)
- * jersey-core-common (org.glassfish.jersey.core:jersey-common:2.47 - https://projects.eclipse.org/projects/ee4j.jersey/jersey-common)
- * jersey-inject-hk2 (org.glassfish.jersey.inject:jersey-hk2:2.47 - https://projects.eclipse.org/projects/ee4j.jersey/project/jersey-hk2)
+ * jersey-core-client (org.glassfish.jersey.core:jersey-client:2.48 - https://projects.eclipse.org/projects/ee4j.jersey/jersey-client)
+ * jersey-core-common (org.glassfish.jersey.core:jersey-common:2.48 - https://projects.eclipse.org/projects/ee4j.jersey/jersey-common)
+ * jersey-inject-hk2 (org.glassfish.jersey.inject:jersey-hk2:2.48 - https://projects.eclipse.org/projects/ee4j.jersey/project/jersey-hk2)
* HdrHistogram (org.hdrhistogram:HdrHistogram:2.1.12 - http://hdrhistogram.github.io/HdrHistogram/)
* JSON in Java (org.json:json:20231013 - https://github.com/douglascrockford/JSON-java)
* LatencyUtils (org.latencyutils:LatencyUtils:2.0.3 - http://latencyutils.github.io/LatencyUtils/)
* Reflections (org.reflections:reflections:0.9.12 - http://github.com/ronmamo/reflections)
+ The Apache Software License, version 2.0:
+
+ * picocli (info.picocli:picocli:4.7.7 - https://picocli.info)
+
UnRar License:
- * Java Unrar (com.github.junrar:junrar:7.5.5 - https://github.com/junrar/junrar)
+ * Java Unrar (com.github.junrar:junrar:7.5.8 - https://github.com/junrar/junrar)
Unicode/ICU License:
@@ -713,10 +734,10 @@ https://wiki.lyrasis.org/display/DSPACE/Code+Contribution+Guidelines
W3C license:
- * jersey-core-client (org.glassfish.jersey.core:jersey-client:2.47 - https://projects.eclipse.org/projects/ee4j.jersey/jersey-client)
- * jersey-inject-hk2 (org.glassfish.jersey.inject:jersey-hk2:2.47 - https://projects.eclipse.org/projects/ee4j.jersey/project/jersey-hk2)
+ * jersey-core-client (org.glassfish.jersey.core:jersey-client:2.48 - https://projects.eclipse.org/projects/ee4j.jersey/jersey-client)
+ * jersey-inject-hk2 (org.glassfish.jersey.inject:jersey-hk2:2.48 - https://projects.eclipse.org/projects/ee4j.jersey/project/jersey-hk2)
jQuery license:
- * jersey-core-client (org.glassfish.jersey.core:jersey-client:2.47 - https://projects.eclipse.org/projects/ee4j.jersey/jersey-client)
- * jersey-inject-hk2 (org.glassfish.jersey.inject:jersey-hk2:2.47 - https://projects.eclipse.org/projects/ee4j.jersey/project/jersey-hk2)
+ * jersey-core-client (org.glassfish.jersey.core:jersey-client:2.48 - https://projects.eclipse.org/projects/ee4j.jersey/jersey-client)
+ * jersey-inject-hk2 (org.glassfish.jersey.inject:jersey-hk2:2.48 - https://projects.eclipse.org/projects/ee4j.jersey/project/jersey-hk2)
diff --git a/docker-compose-cli.yml b/docker-compose-cli.yml
index d6a194617e0..df97629d0c5 100644
--- a/docker-compose-cli.yml
+++ b/docker-compose-cli.yml
@@ -19,9 +19,9 @@ services:
# dspace.dir: Must match with Dockerfile's DSPACE_INSTALL directory.
dspace__P__dir: /dspace
# db.url: Ensure we are using the 'dspacedb' image for our database
- db__P__url: 'jdbc:postgresql://dspacedb:5432/dspace'
+ db__P__url: ${db__P__url:-jdbc:postgresql://dspacedb:5432/dspace}
# solr.server: Ensure we are using the 'dspacesolr' image for Solr
- solr__P__server: http://dspacesolr:8983/solr
+ solr__P__server: ${solr__P__server:-http://dspacesolr:8983/solr}
volumes:
# Keep DSpace assetstore directory between reboots
- assetstore:/dspace/assetstore
@@ -30,8 +30,6 @@ services:
- ./dspace/config:/dspace/config
entrypoint: /dspace/bin/dspace
command: help
- tty: true
- stdin_open: true
volumes:
assetstore:
diff --git a/docker-compose.yml b/docker-compose.yml
index a7894135c6a..b94b31b15fb 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -6,6 +6,8 @@ networks:
# Define a custom subnet for our DSpace network, so that we can easily trust requests from host to container.
# If you customize this value, be sure to customize the 'proxies.trusted.ipranges' env variable below.
- subnet: 172.23.0.0/16
+ # Explicitly set external=false because this script creates the network.
+ external: false
services:
# DSpace (backend) webapp container
dspace:
@@ -20,15 +22,18 @@ services:
# Uncomment to set a non-default value for dspace.server.url or dspace.ui.url
# dspace__P__server__P__url: http://localhost:8080/server
# dspace__P__ui__P__url: http://localhost:4000
- dspace__P__name: 'DSpace Started with Docker Compose'
+ # Set SSR URL to the Docker container name so that UI can contact container directly in Production mode.
+ # (This is necessary for docker-compose-angular.yml as it uses production mode by default)
+ dspace__P__server__P__ssr__P__url: ${dspace__P__server__P__ssr__P__url:-http://dspace:8080/server}
+ dspace__P__name: ${dspace__P__name:-DSpace Started with Docker Compose}
# db.url: Ensure we are using the 'dspacedb' image for our database
- db__P__url: 'jdbc:postgresql://dspacedb:5432/dspace'
+ db__P__url: ${db__P__url:-jdbc:postgresql://dspacedb:5432/dspace}
# solr.server: Ensure we are using the 'dspacesolr' image for Solr
- solr__P__server: http://dspacesolr:8983/solr
+ solr__P__server: ${solr__P__server:-http://dspacesolr:8983/solr}
# proxies.trusted.ipranges: This setting is required for a REST API running in Docker to trust requests
# 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
+ proxies__P__trusted__P__ipranges: ${proxies__P__trusted__P__ipranges:-172.23.0}
+ LOGGING_CONFIG: ${LOGGING_CONFIG:-/dspace/config/log4j2-container.xml}
image: "${DOCKER_OWNER:-dspace}/dspace:${DSPACE_VER:-dspace-7_x-test}"
build:
context: .
@@ -44,8 +49,6 @@ services:
target: 8009
- published: 8000
target: 8000
- stdin_open: true
- tty: true
volumes:
# Keep DSpace assetstore directory between reboots
- assetstore:/dspace/assetstore
@@ -79,8 +82,6 @@ services:
ports:
- published: 5432
target: 5432
- stdin_open: true
- tty: true
volumes:
# Keep Postgres data directory between reboots
- pgdata:/pgdata
@@ -100,8 +101,6 @@ services:
ports:
- published: 8983
target: 8983
- stdin_open: true
- tty: true
working_dir: /var/solr/data
volumes:
# Keep Solr data directory between reboots
diff --git a/dspace-api/pom.xml b/dspace-api/pom.xml
index e2f66f0b81b..fbe0c946bbd 100644
--- a/dspace-api/pom.xml
+++ b/dspace-api/pom.xml
@@ -12,7 +12,7 @@
org.dspacedspace-parent
- 7.6.5
+ 7.6.7..
@@ -99,24 +99,10 @@
-
- org.codehaus.mojo
- build-helper-maven-plugin
- 3.6.1
-
-
- validate
-
- maven-version
-
-
-
-
-
org.codehaus.mojobuildnumber-maven-plugin
- 3.2.1
+ 3.3.0UNKNOWN_REVISION
@@ -647,7 +633,7 @@
dnsjavadnsjava
- 3.6.3
+ 3.6.4
@@ -656,6 +642,7 @@
1.1.1
+
com.google.guavaguava
@@ -755,9 +742,25 @@
- com.amazonaws
- aws-java-sdk-s3
- 1.12.785
+ software.amazon.awssdk
+ s3
+ 2.42.40
+
+
+ software.amazon.awssdk
+ netty-nio-client
+
+
+ software.amazon.awssdk
+ apache-client
+
+
+
+
+
+ software.amazon.awssdk.crt
+ aws-crt
+ 0.45.1
@@ -806,7 +809,7 @@
com.opencsvopencsv
- 5.11.1
+ 5.12.0
@@ -824,7 +827,7 @@
org.apache.bcelbcel
- 6.10.0
+ 6.12.0test
@@ -872,22 +875,13 @@
-
+
+
- io.findify
- s3mock_2.13
- 0.2.6
- test
-
-
- com.amazonawsl
- aws-java-sdk-s3
-
-
- com.amazonaws
- aws-java-sdk-s3
-
-
+ org.testcontainers
+ testcontainers-localstack
+ 2.0.5
+ test
@@ -930,32 +924,32 @@
io.nettynetty-buffer
- 4.2.2.Final
+ 4.2.12.Finalio.nettynetty-transport
- 4.2.2.Final
+ 4.2.12.Finalio.nettynetty-transport-native-unix-common
- 4.2.2.Final
+ 4.2.12.Finalio.nettynetty-common
- 4.2.2.Final
+ 4.2.12.Finalio.nettynetty-handler
- 4.2.2.Final
+ 4.2.12.Finalio.nettynetty-codec
- 4.2.2.Final
+ 4.2.12.Finalorg.apache.velocity
@@ -965,7 +959,7 @@
org.xmlunitxmlunit-core
- 2.10.2
+ 2.11.0test
@@ -986,7 +980,7 @@
org.scala-langscala-library
- 2.13.16
+ 2.13.18test
diff --git a/dspace-api/src/main/java/org/dspace/app/bulkaccesscontrol/BulkAccessControl.java b/dspace-api/src/main/java/org/dspace/app/bulkaccesscontrol/BulkAccessControl.java
index 333aa995e9a..afeb3980377 100644
--- a/dspace-api/src/main/java/org/dspace/app/bulkaccesscontrol/BulkAccessControl.java
+++ b/dspace-api/src/main/java/org/dspace/app/bulkaccesscontrol/BulkAccessControl.java
@@ -18,6 +18,7 @@
import java.sql.SQLException;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
+import java.time.ZoneOffset;
import java.util.Arrays;
import java.util.Date;
import java.util.Iterator;
@@ -157,7 +158,7 @@ public void internalRun() throws Exception {
}
ObjectMapper mapper = new ObjectMapper();
- mapper.setTimeZone(TimeZone.getTimeZone("UTC"));
+ mapper.setTimeZone(TimeZone.getTimeZone(ZoneOffset.UTC));
BulkAccessControlInput accessControl;
context = new Context(Context.Mode.BATCH_EDIT);
setEPerson(context);
diff --git a/dspace-api/src/main/java/org/dspace/app/bulkedit/DSpaceCSV.java b/dspace-api/src/main/java/org/dspace/app/bulkedit/DSpaceCSV.java
index 3533a2397b3..89caa6c1528 100644
--- a/dspace-api/src/main/java/org/dspace/app/bulkedit/DSpaceCSV.java
+++ b/dspace-api/src/main/java/org/dspace/app/bulkedit/DSpaceCSV.java
@@ -25,6 +25,7 @@
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
+import org.dspace.app.util.MetadataExposureServiceImpl;
import org.dspace.authority.AuthorityValue;
import org.dspace.authority.factory.AuthorityServiceFactory;
import org.dspace.authority.service.AuthorityValueService;
@@ -321,20 +322,7 @@ protected void init() {
// Set the metadata fields to ignore
ignore = new HashMap<>();
- // Specify default values
- String[] defaultValues =
- new String[] {
- "dc.date.accessioned", "dc.date.available", "dc.date.updated", "dc.description.provenance"
- };
- String[] toIgnoreArray =
- DSpaceServicesFactory.getInstance()
- .getConfigurationService()
- .getArrayProperty("bulkedit.ignore-on-export", defaultValues);
- for (String toIgnoreString : toIgnoreArray) {
- if (!"".equals(toIgnoreString.trim())) {
- ignore.put(toIgnoreString.trim(), toIgnoreString.trim());
- }
- }
+ getConfiguredIgnoreFields();
}
/**
@@ -352,6 +340,40 @@ public boolean hasActions() {
return false;
}
+ /**
+ * Sets the ignored fields with 'bulkedit.ignore-on-export'
+ *
+ * Also adds 'metadata.hide.*' fields to ignored if 'bulkedit.ignore-on-export.include-metadata-hide' is true
+ */
+ private void getConfiguredIgnoreFields() {
+ // Specify default values
+ String[] defaultValues =
+ new String[] {
+ "dc.date.accessioned", "dc.date.available", "dc.date.updated", "dc.description.provenance"
+ };
+ String[] toIgnoreArray =
+ DSpaceServicesFactory.getInstance()
+ .getConfigurationService()
+ .getArrayProperty("bulkedit.ignore-on-export", defaultValues);
+
+ boolean ignoreHiddenMetadata = DSpaceServicesFactory.getInstance().getConfigurationService()
+ .getBooleanProperty("bulkedit.ignore-on-export.include-metadata-hide", true);
+ if (ignoreHiddenMetadata) {
+ List hiddenMetadata = DSpaceServicesFactory.getInstance().getConfigurationService()
+ .getPropertyKeys(MetadataExposureServiceImpl.CONFIG_PREFIX);
+ for (String hiddenMetadataKey : hiddenMetadata) {
+ String key = hiddenMetadataKey.split(MetadataExposureServiceImpl.CONFIG_PREFIX)[1];
+ ignore.put(key.trim(), key.trim());
+ }
+ }
+
+ for (String toIgnoreString : toIgnoreArray) {
+ if (!"".equals(toIgnoreString.trim())) {
+ ignore.put(toIgnoreString.trim(), toIgnoreString.trim());
+ }
+ }
+ }
+
/**
* Set the value separator for multiple values stored in one csv value.
*
diff --git a/dspace-api/src/main/java/org/dspace/app/bulkedit/MetadataExportSearch.java b/dspace-api/src/main/java/org/dspace/app/bulkedit/MetadataExportSearch.java
index e4bbe335d63..689df4701a9 100644
--- a/dspace-api/src/main/java/org/dspace/app/bulkedit/MetadataExportSearch.java
+++ b/dspace-api/src/main/java/org/dspace/app/bulkedit/MetadataExportSearch.java
@@ -14,6 +14,8 @@
import java.util.List;
import java.util.UUID;
+import org.apache.commons.cli.DefaultParser;
+import org.apache.commons.cli.DefaultParser.Builder;
import org.apache.commons.cli.ParseException;
import org.dspace.content.Item;
import org.dspace.content.MetadataDSpaceCsvExportServiceImpl;
@@ -167,4 +169,14 @@ public IndexableObject resolveScope(Context context, String id) throws SQLExcept
}
return scopeObj;
}
+
+ @Override
+ protected StepResult parse(String[] args) throws ParseException {
+ commandLine = new DefaultParser().parse(getScriptConfiguration().getOptions(), args);
+ Builder builder = new DefaultParser().builder();
+ builder.setStripLeadingAndTrailingQuotes(false);
+ commandLine = builder.build().parse(getScriptConfiguration().getOptions(), args);
+ setup();
+ return StepResult.Continue;
+ }
}
diff --git a/dspace-api/src/main/java/org/dspace/app/bulkedit/MetadataImport.java b/dspace-api/src/main/java/org/dspace/app/bulkedit/MetadataImport.java
index ad46cb95c35..fb5657a9e3c 100644
--- a/dspace-api/src/main/java/org/dspace/app/bulkedit/MetadataImport.java
+++ b/dspace-api/src/main/java/org/dspace/app/bulkedit/MetadataImport.java
@@ -358,6 +358,16 @@ public List runImport(Context c, boolean change,
// Process each change
rowCount = 1;
+
+ int maxItems = configurationService.getIntProperty("bulkedit.import.max.items", 1000);
+ int numItems = toImport.size();
+ if (numItems > maxItems && maxItems > 0) {
+ throw new MetadataImportException(
+ "Import contains " + numItems + " items, which exceeds the configured "
+ + "maximum of " + maxItems + ". You can change this limit by setting "
+ + "'bulkedit.import.max.items' in your local configuration.");
+ }
+
for (DSpaceCSVLine line : toImport) {
// Resolve target references to other items
populateRefAndRowMap(line, line.getID());
@@ -494,7 +504,7 @@ public List runImport(Context c, boolean change,
// Check it has an owning collection
List collections = line.get("collection");
- if (collections == null) {
+ if (collections == null || collections.isEmpty()) {
throw new MetadataImportException(
"New items must have a 'collection' assigned in the form of a handle");
}
diff --git a/dspace-api/src/main/java/org/dspace/app/checker/ChecksumChecker.java b/dspace-api/src/main/java/org/dspace/app/checker/ChecksumChecker.java
index ec024c34526..160d23e3220 100644
--- a/dspace-api/src/main/java/org/dspace/app/checker/ChecksumChecker.java
+++ b/dspace-api/src/main/java/org/dspace/app/checker/ChecksumChecker.java
@@ -98,7 +98,7 @@ public static void main(String[] args) throws SQLException {
options.addOption("h", "help", false, "Help");
options.addOption("d", "duration", true, "Checking duration");
options.addOption("c", "count", true, "Check count");
- options.addOption("a", "handle", true, "Specify a handle to check");
+ options.addOption("i", "handle", true, "Specify a handle to check");
options.addOption("v", "verbose", false, "Report all processing");
Option option;
@@ -106,7 +106,7 @@ public static void main(String[] args) throws SQLException {
option = Option.builder("b")
.longOpt("bitstream-ids")
.hasArgs()
- .desc("Space separated list of bitstream ids")
+ .desc("Space separated list of bitstream UUIDs")
.build();
options.addOption(option);
@@ -132,6 +132,17 @@ public static void main(String[] args) throws SQLException {
try {
context = new Context();
+ int mutuallyExclusiveOpts = 0;
+ for (char c : new char[]{'l', 'L', 'd', 'b', 'i','c'}) {
+ if (line.hasOption(c)) {
+ mutuallyExclusiveOpts++;
+ }
+ }
+ if (mutuallyExclusiveOpts > 1) {
+ System.err.println("Please use only one option of -l, -L, -d, -b, -i, or -c");
+ LOG.error("Please use only one option of -l, -L, -d, -b, -i, or -c");
+ System.exit(1);
+ }
// Prune stage
if (line.hasOption('p')) {
@@ -169,13 +180,13 @@ public static void main(String[] args) throws SQLException {
bitstreams.add(bitstreamService.find(context, UUID.fromString(ids[i])));
} catch (NumberFormatException nfe) {
System.err.println("The following argument: " + ids[i]
- + " is not an integer");
+ + " is not an UUID");
System.exit(0);
}
}
dispatcher = new IteratorDispatcher(bitstreams.iterator());
- } else if (line.hasOption('a')) {
- dispatcher = new HandleDispatcher(context, line.getOptionValue('a'));
+ } else if (line.hasOption('i')) {
+ dispatcher = new HandleDispatcher(context, line.getOptionValue('i'));
} else if (line.hasOption('d')) {
// run checker process for specified duration
try {
@@ -185,6 +196,8 @@ public static void main(String[] args) throws SQLException {
+ Utils.parseDuration(line
.getOptionValue('d'))));
} catch (Exception e) {
+ System.err.println("Couldn't parse " + line.getOptionValue('d')
+ + " as a duration");
LOG.fatal("Couldn't parse " + line.getOptionValue('d')
+ " as a duration: ", e);
System.exit(0);
@@ -228,18 +241,24 @@ public static void main(String[] args) throws SQLException {
private static void printHelp(Options options) {
HelpFormatter myhelp = new HelpFormatter();
- myhelp.printHelp("Checksum Checker\n", options);
- System.out.println("\nSpecify a duration for checker process, using s(seconds),"
- + "m(minutes), or h(hours): ChecksumChecker -d 30s"
- + " OR ChecksumChecker -d 30m"
- + " OR ChecksumChecker -d 2h");
- System.out.println("\nSpecify bitstream IDs: ChecksumChecker -b 13 15 17 20");
- System.out.println("\nLoop once through all bitstreams: "
- + "ChecksumChecker -l");
- System.out.println("\nLoop continuously through all bitstreams: ChecksumChecker -L");
- System.out.println("\nCheck a defined number of bitstreams: ChecksumChecker -c 10");
- System.out.println("\nReport all processing (verbose)(default reports only errors): ChecksumChecker -v");
- System.out.println("\nDefault (no arguments) is equivalent to '-c 1'");
+ myhelp.printHelp("checker\n", options);
+ System.out.println("\nChecksum Checker usage examples:");
+ System.out.println("\nThe following options are mutually exclusive:");
+ System.out.println(" - Specify a duration for checker process, using s(seconds),"
+ + "m(minutes), or h(hours): checker -d 30s"
+ + " OR checker -d 30m"
+ + " OR checker -d 2h");
+ System.out.println(" - Specify bitstream UUIDs: checker -b 550e8400-e29b-41d4-a716-446655440000"
+ + " f3f2e850-b5d4-11ef-ac7e-96584d5248b2");
+ System.out.println(" - Specify handle: checker -i 12345/100");
+ System.out.println(" - Loop once through all bitstreams: "
+ + "checker -l");
+ System.out.println(" - Loop continuously through all bitstreams: checker -L");
+ System.out.println(" - Check a defined number of bitstreams: checker -c 10");
+ System.out.println("\nThe following options can be used in combination with others above:");
+ System.out.println(" - Report all processing to checker.log (by default logs only errors): checker -v");
+ System.out.println(" - Prune old results from the database: checker -p");
+ System.out.println("\nDefault (no arguments) is equivalent to 'checker -c 1'\n");
System.exit(0);
}
diff --git a/dspace-api/src/main/java/org/dspace/app/itemexport/ItemExportServiceImpl.java b/dspace-api/src/main/java/org/dspace/app/itemexport/ItemExportServiceImpl.java
index 7c80e1ea7dc..e5ca8fe688c 100644
--- a/dspace-api/src/main/java/org/dspace/app/itemexport/ItemExportServiceImpl.java
+++ b/dspace-api/src/main/java/org/dspace/app/itemexport/ItemExportServiceImpl.java
@@ -352,7 +352,7 @@ protected void writeHandle(Context c, Item i, File destDir)
/**
* Create the 'collections' file. List handles of all Collections which
- * contain this Item. The "owning" Collection is listed first.
+ * contain this Item. The "owning" Collection is listed first.
*
* @param item list collections holding this Item.
* @param destDir write the file here.
@@ -363,12 +363,14 @@ protected void writeCollections(Item item, File destDir)
File outFile = new File(destDir, "collections");
if (outFile.createNewFile()) {
try (PrintWriter out = new PrintWriter(new FileWriter(outFile))) {
- String ownerHandle = item.getOwningCollection().getHandle();
- out.println(ownerHandle);
+ Collection owningCollection = item.getOwningCollection();
+ // The owning collection is null for workspace and workflow items
+ if (owningCollection != null) {
+ out.println(owningCollection.getHandle());
+ }
for (Collection collection : item.getCollections()) {
- String collectionHandle = collection.getHandle();
- if (!collectionHandle.equals(ownerHandle)) {
- out.println(collectionHandle);
+ if (!collection.equals(owningCollection)) {
+ out.println(collection.getHandle());
}
}
}
diff --git a/dspace-api/src/main/java/org/dspace/app/itemimport/ItemImport.java b/dspace-api/src/main/java/org/dspace/app/itemimport/ItemImport.java
index b32de11f7a7..33487bc8e35 100644
--- a/dspace-api/src/main/java/org/dspace/app/itemimport/ItemImport.java
+++ b/dspace-api/src/main/java/org/dspace/app/itemimport/ItemImport.java
@@ -22,6 +22,7 @@
import org.apache.commons.cli.ParseException;
import org.apache.commons.io.FileUtils;
+import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.tika.Tika;
import org.dspace.app.itemimport.factory.ItemImportServiceFactory;
@@ -333,33 +334,38 @@ protected void process(Context context, ItemImportService itemImportService,
protected void readZip(Context context, ItemImportService itemImportService) throws Exception {
Optional optionalFileStream = Optional.empty();
Optional validationFileStream = Optional.empty();
- if (!remoteUrl) {
- // manage zip via upload
- optionalFileStream = handler.getFileStream(context, zipfilename);
- validationFileStream = handler.getFileStream(context, zipfilename);
- } else {
- // manage zip via remote url
- optionalFileStream = Optional.ofNullable(new URL(zipfilename).openStream());
- validationFileStream = Optional.ofNullable(new URL(zipfilename).openStream());
- }
+ try {
+ if (!remoteUrl) {
+ // manage zip via upload
+ optionalFileStream = handler.getFileStream(context, zipfilename);
+ validationFileStream = handler.getFileStream(context, zipfilename);
+ } else {
+ // manage zip via remote url
+ optionalFileStream = Optional.ofNullable(new URL(zipfilename).openStream());
+ validationFileStream = Optional.ofNullable(new URL(zipfilename).openStream());
+ }
- if (validationFileStream.isPresent()) {
- // validate zip file
if (validationFileStream.isPresent()) {
- validateZip(validationFileStream.get());
+ // validate zip file
+ if (validationFileStream.isPresent()) {
+ validateZip(validationFileStream.get());
+ }
+
+ workFile = new File(itemImportService.getTempWorkDir() + File.separator
+ + zipfilename + "-" + context.getCurrentUser().getID());
+ FileUtils.copyInputStreamToFile(optionalFileStream.get(), workFile);
+ } else {
+ throw new IllegalArgumentException(
+ "Error reading file, the file couldn't be found for filename: " + zipfilename);
}
- workFile = new File(itemImportService.getTempWorkDir() + File.separator
- + zipfilename + "-" + context.getCurrentUser().getID());
- FileUtils.copyInputStreamToFile(optionalFileStream.get(), workFile);
- } else {
- throw new IllegalArgumentException(
- "Error reading file, the file couldn't be found for filename: " + zipfilename);
+ workDir = new File(itemImportService.getTempWorkDir() + File.separator + TEMP_DIR
+ + File.separator + context.getCurrentUser().getID());
+ sourcedir = itemImportService.unzip(workFile, workDir.getAbsolutePath());
+ } finally {
+ optionalFileStream.ifPresent(IOUtils::closeQuietly);
+ validationFileStream.ifPresent(IOUtils::closeQuietly);
}
-
- workDir = new File(itemImportService.getTempWorkDir() + File.separator + TEMP_DIR
- + File.separator + context.getCurrentUser().getID());
- sourcedir = itemImportService.unzip(workFile, workDir.getAbsolutePath());
}
/**
diff --git a/dspace-api/src/main/java/org/dspace/app/itemimport/ItemImportCLI.java b/dspace-api/src/main/java/org/dspace/app/itemimport/ItemImportCLI.java
index 98d2469b715..bd29aa97fe4 100644
--- a/dspace-api/src/main/java/org/dspace/app/itemimport/ItemImportCLI.java
+++ b/dspace-api/src/main/java/org/dspace/app/itemimport/ItemImportCLI.java
@@ -17,6 +17,7 @@
import java.util.UUID;
import org.apache.commons.io.FileUtils;
+import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.dspace.app.itemimport.service.ItemImportService;
import org.dspace.content.Collection;
@@ -111,7 +112,11 @@ protected void readZip(Context context, ItemImportService itemImportService) thr
// validate zip file
InputStream validationFileStream = new FileInputStream(myZipFile);
- validateZip(validationFileStream);
+ try {
+ validateZip(validationFileStream);
+ } finally {
+ IOUtils.closeQuietly(validationFileStream);
+ }
workDir = new File(itemImportService.getTempWorkDir() + File.separator + TEMP_DIR
+ File.separator + context.getCurrentUser().getID());
@@ -120,22 +125,28 @@ protected void readZip(Context context, ItemImportService itemImportService) thr
} else {
// manage zip via remote url
Optional optionalFileStream = Optional.ofNullable(new URL(zipfilename).openStream());
- if (optionalFileStream.isPresent()) {
- // validate zip file via url
- Optional validationFileStream = Optional.ofNullable(new URL(zipfilename).openStream());
- if (validationFileStream.isPresent()) {
- validateZip(validationFileStream.get());
+ Optional validationFileStream = Optional.ofNullable(new URL(zipfilename).openStream());
+ try {
+ if (optionalFileStream.isPresent()) {
+ // validate zip file via url
+
+ if (validationFileStream.isPresent()) {
+ validateZip(validationFileStream.get());
+ }
+
+ workFile = new File(itemImportService.getTempWorkDir() + File.separator
+ + zipfilename + "-" + context.getCurrentUser().getID());
+ FileUtils.copyInputStreamToFile(optionalFileStream.get(), workFile);
+ workDir = new File(itemImportService.getTempWorkDir() + File.separator + TEMP_DIR
+ + File.separator + context.getCurrentUser().getID());
+ sourcedir = itemImportService.unzip(workFile, workDir.getAbsolutePath());
+ } else {
+ throw new IllegalArgumentException(
+ "Error reading file, the file couldn't be found for filename: " + zipfilename);
}
-
- workFile = new File(itemImportService.getTempWorkDir() + File.separator
- + zipfilename + "-" + context.getCurrentUser().getID());
- FileUtils.copyInputStreamToFile(optionalFileStream.get(), workFile);
- workDir = new File(itemImportService.getTempWorkDir() + File.separator + TEMP_DIR
- + File.separator + context.getCurrentUser().getID());
- sourcedir = itemImportService.unzip(workFile, workDir.getAbsolutePath());
- } else {
- throw new IllegalArgumentException(
- "Error reading file, the file couldn't be found for filename: " + zipfilename);
+ } finally {
+ optionalFileStream.ifPresent(IOUtils::closeQuietly);
+ validationFileStream.ifPresent(IOUtils::closeQuietly);
}
}
}
diff --git a/dspace-api/src/main/java/org/dspace/app/mediafilter/BrandedPreviewJPEGFilter.java b/dspace-api/src/main/java/org/dspace/app/mediafilter/BrandedPreviewJPEGFilter.java
index 7b082c6c21a..483e4f5f6ea 100644
--- a/dspace-api/src/main/java/org/dspace/app/mediafilter/BrandedPreviewJPEGFilter.java
+++ b/dspace-api/src/main/java/org/dspace/app/mediafilter/BrandedPreviewJPEGFilter.java
@@ -7,9 +7,7 @@
*/
package org.dspace.app.mediafilter;
-import java.awt.image.BufferedImage;
import java.io.InputStream;
-import javax.imageio.ImageIO;
import org.dspace.content.Item;
import org.dspace.services.ConfigurationService;
@@ -63,27 +61,20 @@ public String getDescription() {
@Override
public InputStream getDestinationStream(Item currentItem, InputStream source, boolean verbose)
throws Exception {
- // read in bitstream's image
- BufferedImage buf = ImageIO.read(source);
-
// get config params
ConfigurationService configurationService
= DSpaceServicesFactory.getInstance().getConfigurationService();
- float xmax = (float) configurationService
- .getIntProperty("webui.preview.maxwidth");
- float ymax = (float) configurationService
- .getIntProperty("webui.preview.maxheight");
- boolean blurring = (boolean) configurationService
- .getBooleanProperty("webui.preview.blurring");
- boolean hqscaling = (boolean) configurationService
- .getBooleanProperty("webui.preview.hqscaling");
+ int xmax = configurationService.getIntProperty("webui.preview.maxwidth");
+ int ymax = configurationService.getIntProperty("webui.preview.maxheight");
+ boolean blurring = configurationService.getBooleanProperty("webui.preview.blurring");
+ boolean hqscaling = configurationService.getBooleanProperty("webui.preview.hqscaling");
int brandHeight = configurationService.getIntProperty("webui.preview.brand.height");
String brandFont = configurationService.getProperty("webui.preview.brand.font");
int brandFontPoint = configurationService.getIntProperty("webui.preview.brand.fontpoint");
JPEGFilter jpegFilter = new JPEGFilter();
- return jpegFilter
- .getThumbDim(currentItem, buf, verbose, xmax, ymax, blurring, hqscaling, brandHeight, brandFontPoint,
- brandFont);
+ return jpegFilter.getThumb(
+ currentItem, source, verbose, xmax, ymax, blurring, hqscaling, brandHeight, brandFontPoint, brandFont
+ );
}
}
diff --git a/dspace-api/src/main/java/org/dspace/app/mediafilter/ImageMagickThumbnailFilter.java b/dspace-api/src/main/java/org/dspace/app/mediafilter/ImageMagickThumbnailFilter.java
index 7543410a796..28bfc72dc11 100644
--- a/dspace-api/src/main/java/org/dspace/app/mediafilter/ImageMagickThumbnailFilter.java
+++ b/dspace-api/src/main/java/org/dspace/app/mediafilter/ImageMagickThumbnailFilter.java
@@ -105,7 +105,7 @@ public File getThumbnailFile(File f, boolean verbose)
ConvertCmd cmd = new ConvertCmd();
IMOperation op = new IMOperation();
op.autoOrient();
- op.addImage(f.getAbsolutePath());
+ op.addImage(f.getAbsolutePath() + "[0]");
op.thumbnail(configurationService.getIntProperty("thumbnail.maxwidth", DEFAULT_WIDTH),
configurationService.getIntProperty("thumbnail.maxheight", DEFAULT_HEIGHT));
op.addImage(f2.getAbsolutePath());
diff --git a/dspace-api/src/main/java/org/dspace/app/mediafilter/JPEGFilter.java b/dspace-api/src/main/java/org/dspace/app/mediafilter/JPEGFilter.java
index 502f71eb5ca..2ccc2afbb2d 100644
--- a/dspace-api/src/main/java/org/dspace/app/mediafilter/JPEGFilter.java
+++ b/dspace-api/src/main/java/org/dspace/app/mediafilter/JPEGFilter.java
@@ -8,19 +8,32 @@
package org.dspace.app.mediafilter;
import java.awt.Color;
+import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.Transparency;
+import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.awt.image.BufferedImageOp;
import java.awt.image.ConvolveOp;
import java.awt.image.Kernel;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
import java.io.InputStream;
import javax.imageio.ImageIO;
+import com.drew.imaging.ImageMetadataReader;
+import com.drew.imaging.ImageProcessingException;
+import com.drew.metadata.Metadata;
+import com.drew.metadata.MetadataException;
+import com.drew.metadata.exif.ExifIFD0Directory;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
import org.dspace.content.Item;
import org.dspace.services.ConfigurationService;
import org.dspace.services.factory.DSpaceServicesFactory;
@@ -33,6 +46,8 @@
* @author Jason Sherman jsherman@usao.edu
*/
public class JPEGFilter extends MediaFilter implements SelfRegisterInputFormats {
+ private static final Logger log = LogManager.getLogger(JPEGFilter.class);
+
@Override
public String getFilteredName(String oldFilename) {
return oldFilename + ".jpg";
@@ -62,6 +77,115 @@ public String getDescription() {
return "Generated Thumbnail";
}
+ /**
+ * Gets the rotation angle from image's metadata using ImageReader.
+ * This method consumes the InputStream, so you need to be careful to don't reuse the same InputStream after
+ * computing the rotation angle.
+ *
+ * @param buf InputStream of the image file
+ * @return Rotation angle in degrees (0, 90, 180, or 270)
+ */
+ public static int getImageRotationUsingImageReader(InputStream buf) {
+ try {
+ Metadata metadata = ImageMetadataReader.readMetadata(buf);
+ ExifIFD0Directory directory = metadata.getFirstDirectoryOfType(ExifIFD0Directory.class);
+ if (directory != null && directory.containsTag(ExifIFD0Directory.TAG_ORIENTATION)) {
+ return convertRotationToDegrees(directory.getInt(ExifIFD0Directory.TAG_ORIENTATION));
+ }
+ } catch (MetadataException | ImageProcessingException | IOException e) {
+ log.error("Error reading image metadata", e);
+ }
+ return 0;
+ }
+
+ public static int convertRotationToDegrees(int valueNode) {
+ // Common orientation values:
+ // 1 = Normal (0°)
+ // 6 = Rotated 90° CW
+ // 3 = Rotated 180°
+ // 8 = Rotated 270° CW
+ switch (valueNode) {
+ case 6:
+ return 90;
+ case 3:
+ return 180;
+ case 8:
+ return 270;
+ default:
+ return 0;
+ }
+ }
+
+ /**
+ * Rotates an image by the specified angle
+ *
+ * @param image The original image
+ * @param angle The rotation angle in degrees
+ * @return Rotated image
+ */
+ public static BufferedImage rotateImage(BufferedImage image, int angle) {
+ if (angle == 0) {
+ return image;
+ }
+
+ double radians = Math.toRadians(angle);
+ double sin = Math.abs(Math.sin(radians));
+ double cos = Math.abs(Math.cos(radians));
+
+ int newWidth = (int) Math.round(image.getWidth() * cos + image.getHeight() * sin);
+ int newHeight = (int) Math.round(image.getWidth() * sin + image.getHeight() * cos);
+
+ BufferedImage rotated = new BufferedImage(newWidth, newHeight, image.getType());
+ Graphics2D g2d = rotated.createGraphics();
+ AffineTransform at = new AffineTransform();
+
+ at.translate(newWidth / 2, newHeight / 2);
+ at.rotate(radians);
+ at.translate(-image.getWidth() / 2, -image.getHeight() / 2);
+
+ g2d.setTransform(at);
+ g2d.drawImage(image, 0, 0, null);
+ g2d.dispose();
+
+ return rotated;
+ }
+
+ /**
+ * Calculates scaled dimension while maintaining aspect ratio
+ *
+ * @param imgSize Original image dimensions
+ * @param boundary Maximum allowed dimensions
+ * @return New dimensions that fit within boundary while preserving aspect ratio
+ */
+ private Dimension getScaledDimension(Dimension imgSize, Dimension boundary) {
+
+ int originalWidth = imgSize.width;
+ int originalHeight = imgSize.height;
+ int boundWidth = boundary.width;
+ int boundHeight = boundary.height;
+ int newWidth = originalWidth;
+ int newHeight = originalHeight;
+
+
+ // First check if we need to scale width
+ if (originalWidth > boundWidth) {
+ // Scale width to fit
+ newWidth = boundWidth;
+ // Scale height to maintain aspect ratio
+ newHeight = (newWidth * originalHeight) / originalWidth;
+ }
+
+ // Then check if we need to scale even with the new height
+ if (newHeight > boundHeight) {
+ // Scale height to fit instead
+ newHeight = boundHeight;
+ newWidth = (newHeight * originalWidth) / originalHeight;
+ }
+
+ return new Dimension(newWidth, newHeight);
+ }
+
+
/**
* @param currentItem item
* @param source source input stream
@@ -72,10 +196,65 @@ public String getDescription() {
@Override
public InputStream getDestinationStream(Item currentItem, InputStream source, boolean verbose)
throws Exception {
- // read in bitstream's image
- BufferedImage buf = ImageIO.read(source);
+ return getThumb(currentItem, source, verbose);
+ }
- return getThumb(currentItem, buf, verbose);
+ public InputStream getThumb(Item currentItem, InputStream source, boolean verbose)
+ throws Exception {
+ // get config params
+ final ConfigurationService configurationService
+ = DSpaceServicesFactory.getInstance().getConfigurationService();
+ int xmax = configurationService
+ .getIntProperty("thumbnail.maxwidth");
+ int ymax = configurationService
+ .getIntProperty("thumbnail.maxheight");
+ boolean blurring = (boolean) configurationService
+ .getBooleanProperty("thumbnail.blurring");
+ boolean hqscaling = (boolean) configurationService
+ .getBooleanProperty("thumbnail.hqscaling");
+
+ return getThumb(currentItem, source, verbose, xmax, ymax, blurring, hqscaling, 0, 0, null);
+ }
+
+ protected InputStream getThumb(
+ Item currentItem,
+ InputStream source,
+ boolean verbose,
+ int xmax,
+ int ymax,
+ boolean blurring,
+ boolean hqscaling,
+ int brandHeight,
+ int brandFontPoint,
+ String brandFont
+ ) throws Exception {
+
+ File tempFile = File.createTempFile("temp", ".tmp");
+ tempFile.deleteOnExit();
+
+ // Write to temp file
+ try (FileOutputStream fos = new FileOutputStream(tempFile)) {
+ byte[] buffer = new byte[4096];
+ int len;
+ while ((len = source.read(buffer)) != -1) {
+ fos.write(buffer, 0, len);
+ }
+ }
+
+ int rotation = 0;
+ try (FileInputStream fis = new FileInputStream(tempFile)) {
+ rotation = getImageRotationUsingImageReader(fis);
+ }
+
+ try (FileInputStream fis = new FileInputStream(tempFile)) {
+ // read in bitstream's image
+ BufferedImage buf = ImageIO.read(fis);
+
+ return getThumbDim(
+ currentItem, buf, verbose, xmax, ymax, blurring, hqscaling, brandHeight, brandFontPoint, rotation,
+ brandFont
+ );
+ }
}
public InputStream getThumb(Item currentItem, BufferedImage buf, boolean verbose)
@@ -83,25 +262,28 @@ public InputStream getThumb(Item currentItem, BufferedImage buf, boolean verbose
// get config params
final ConfigurationService configurationService
= DSpaceServicesFactory.getInstance().getConfigurationService();
- float xmax = (float) configurationService
+ int xmax = configurationService
.getIntProperty("thumbnail.maxwidth");
- float ymax = (float) configurationService
+ int ymax = configurationService
.getIntProperty("thumbnail.maxheight");
boolean blurring = (boolean) configurationService
.getBooleanProperty("thumbnail.blurring");
boolean hqscaling = (boolean) configurationService
.getBooleanProperty("thumbnail.hqscaling");
- return getThumbDim(currentItem, buf, verbose, xmax, ymax, blurring, hqscaling, 0, 0, null);
+ return getThumbDim(currentItem, buf, verbose, xmax, ymax, blurring, hqscaling, 0, 0, 0, null);
}
- public InputStream getThumbDim(Item currentItem, BufferedImage buf, boolean verbose, float xmax, float ymax,
+ public InputStream getThumbDim(Item currentItem, BufferedImage buf, boolean verbose, int xmax, int ymax,
boolean blurring, boolean hqscaling, int brandHeight, int brandFontPoint,
- String brandFont)
+ int rotation, String brandFont)
throws Exception {
- // now get the image dimensions
- float xsize = (float) buf.getWidth(null);
- float ysize = (float) buf.getHeight(null);
+
+ // Rotate the image if needed
+ BufferedImage correctedImage = rotateImage(buf, rotation);
+
+ int xsize = correctedImage.getWidth();
+ int ysize = correctedImage.getHeight();
// if verbose flag is set, print out dimensions
// to STDOUT
@@ -109,86 +291,63 @@ public InputStream getThumbDim(Item currentItem, BufferedImage buf, boolean verb
System.out.println("original size: " + xsize + "," + ysize);
}
- // scale by x first if needed
- if (xsize > xmax) {
- // calculate scaling factor so that xsize * scale = new size (max)
- float scale_factor = xmax / xsize;
+ // Calculate new dimensions while maintaining aspect ratio
+ Dimension newDimension = getScaledDimension(
+ new Dimension(xsize, ysize),
+ new Dimension(xmax, ymax)
+ );
- // if verbose flag is set, print out extracted text
- // to STDOUT
- if (verbose) {
- System.out.println("x scale factor: " + scale_factor);
- }
-
- // now reduce x size
- // and y size
- xsize = xsize * scale_factor;
- ysize = ysize * scale_factor;
-
- // if verbose flag is set, print out extracted text
- // to STDOUT
- if (verbose) {
- System.out.println("size after fitting to maximum width: " + xsize + "," + ysize);
- }
- }
-
- // scale by y if needed
- if (ysize > ymax) {
- float scale_factor = ymax / ysize;
-
- // now reduce x size
- // and y size
- xsize = xsize * scale_factor;
- ysize = ysize * scale_factor;
- }
// if verbose flag is set, print details to STDOUT
if (verbose) {
- System.out.println("size after fitting to maximum height: " + xsize + ", "
- + ysize);
+ System.out.println("size after fitting to maximum height: " + newDimension.width + ", "
+ + newDimension.height);
}
+ xsize = newDimension.width;
+ ysize = newDimension.height;
+
// create an image buffer for the thumbnail with the new xsize, ysize
- BufferedImage thumbnail = new BufferedImage((int) xsize, (int) ysize,
- BufferedImage.TYPE_INT_RGB);
+ BufferedImage thumbnail = new BufferedImage(xsize, ysize, BufferedImage.TYPE_INT_RGB);
// Use blurring if selected in config.
// a little blur before scaling does wonders for keeping moire in check.
if (blurring) {
// send the buffered image off to get blurred.
- buf = getBlurredInstance((BufferedImage) buf);
+ correctedImage = getBlurredInstance(correctedImage);
}
// Use high quality scaling method if selected in config.
// this has a definite performance penalty.
if (hqscaling) {
// send the buffered image off to get an HQ downscale.
- buf = getScaledInstance((BufferedImage) buf, (int) xsize, (int) ysize,
- (Object) RenderingHints.VALUE_INTERPOLATION_BICUBIC, (boolean) true);
+ correctedImage = getScaledInstance(correctedImage, xsize, ysize,
+ RenderingHints.VALUE_INTERPOLATION_BICUBIC, true);
}
// now render the image into the thumbnail buffer
Graphics2D g2d = thumbnail.createGraphics();
- g2d.drawImage(buf, 0, 0, (int) xsize, (int) ysize, null);
+ g2d.drawImage(correctedImage, 0, 0, xsize, ysize, null);
if (brandHeight != 0) {
ConfigurationService configurationService
= DSpaceServicesFactory.getInstance().getConfigurationService();
- Brand brand = new Brand((int) xsize, brandHeight, new Font(brandFont, Font.PLAIN, brandFontPoint), 5);
+ Brand brand = new Brand(xsize, brandHeight, new Font(brandFont, Font.PLAIN, brandFontPoint), 5);
BufferedImage brandImage = brand.create(configurationService.getProperty("webui.preview.brand"),
configurationService.getProperty("webui.preview.brand.abbrev"),
currentItem == null ? "" : "hdl:" + currentItem.getHandle());
- g2d.drawImage(brandImage, (int) 0, (int) ysize, (int) xsize, (int) 20, null);
+ g2d.drawImage(brandImage, 0, ysize, xsize, 20, null);
}
- // now create an input stream for the thumbnail buffer and return it
- ByteArrayOutputStream baos = new ByteArrayOutputStream();
-
- ImageIO.write(thumbnail, "jpeg", baos);
- // now get the array
- ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
+ ByteArrayInputStream bais;
+ // now create an input stream for the thumbnail buffer and return it
+ try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
+ ImageIO.write(thumbnail, "jpeg", baos);
+ // now get the array
+ bais = new ByteArrayInputStream(baos.toByteArray());
+ }
return bais; // hope this gets written out before its garbage collected!
}
diff --git a/dspace-api/src/main/java/org/dspace/app/mediafilter/PDFBoxThumbnail.java b/dspace-api/src/main/java/org/dspace/app/mediafilter/PDFBoxThumbnail.java
index 94c463b2808..eb23e9daa08 100644
--- a/dspace-api/src/main/java/org/dspace/app/mediafilter/PDFBoxThumbnail.java
+++ b/dspace-api/src/main/java/org/dspace/app/mediafilter/PDFBoxThumbnail.java
@@ -83,6 +83,7 @@ public InputStream getDestinationStream(Item currentItem, InputStream source, bo
// Generate thumbnail derivative and return as IO stream.
JPEGFilter jpegFilter = new JPEGFilter();
+
return jpegFilter.getThumb(currentItem, buf, verbose);
}
}
diff --git a/dspace-api/src/main/java/org/dspace/app/requestitem/RequestItemServiceImpl.java b/dspace-api/src/main/java/org/dspace/app/requestitem/RequestItemServiceImpl.java
index b915cfedd34..d6d0225655e 100644
--- a/dspace-api/src/main/java/org/dspace/app/requestitem/RequestItemServiceImpl.java
+++ b/dspace-api/src/main/java/org/dspace/app/requestitem/RequestItemServiceImpl.java
@@ -11,6 +11,7 @@
import java.util.Date;
import java.util.Iterator;
import java.util.List;
+import java.util.UUID;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
@@ -96,6 +97,11 @@ public Iterator findByItem(Context context, Item item) throws SQLEx
return requestItemDAO.findByItem(context, item);
}
+ @Override
+ public Iterator findByBitstreamId(Context context, UUID bitstreamId) throws SQLException {
+ return requestItemDAO.findByBitstreamId(context, bitstreamId);
+ }
+
@Override
public void update(Context context, RequestItem requestItem) {
try {
diff --git a/dspace-api/src/main/java/org/dspace/app/requestitem/dao/RequestItemDAO.java b/dspace-api/src/main/java/org/dspace/app/requestitem/dao/RequestItemDAO.java
index b36ae58e0ca..9c6954fe6be 100644
--- a/dspace-api/src/main/java/org/dspace/app/requestitem/dao/RequestItemDAO.java
+++ b/dspace-api/src/main/java/org/dspace/app/requestitem/dao/RequestItemDAO.java
@@ -9,6 +9,7 @@
import java.sql.SQLException;
import java.util.Iterator;
+import java.util.UUID;
import org.dspace.app.requestitem.RequestItem;
import org.dspace.content.Item;
@@ -26,7 +27,7 @@
*/
public interface RequestItemDAO extends GenericDAO {
/**
- * Fetch a request named by its unique token (passed in emails).
+ * Fetch a request named by its unique approval token (passed in emails).
*
* @param context the current DSpace context.
* @param token uniquely identifies the request.
@@ -36,4 +37,17 @@ public interface RequestItemDAO extends GenericDAO {
public RequestItem findByToken(Context context, String token) throws SQLException;
public Iterator findByItem(Context context, Item item) throws SQLException;
+
+ /**
+ * Retrieve all requests (as iterator) for a given bitstream UUID
+ * A UUID parameter is used here rather than Bitstream object, to make it usable
+ * in situations even when a bitstream object no longer exists, but orphaned
+ * entries need to be found by their (previous) bitstream UUID.
+ *
+ * @param context current DSpace context
+ * @param bitstreamId the bitstream UUID to search for
+ * @return the matching requests (or empty iterator)
+ */
+ public Iterator findByBitstreamId(Context context, UUID bitstreamId) throws SQLException;
+
}
diff --git a/dspace-api/src/main/java/org/dspace/app/requestitem/dao/impl/RequestItemDAOImpl.java b/dspace-api/src/main/java/org/dspace/app/requestitem/dao/impl/RequestItemDAOImpl.java
index 008174ded88..b1ef405f5e1 100644
--- a/dspace-api/src/main/java/org/dspace/app/requestitem/dao/impl/RequestItemDAOImpl.java
+++ b/dspace-api/src/main/java/org/dspace/app/requestitem/dao/impl/RequestItemDAOImpl.java
@@ -9,6 +9,7 @@
import java.sql.SQLException;
import java.util.Iterator;
+import java.util.UUID;
import javax.persistence.Query;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
@@ -42,10 +43,22 @@ public RequestItem findByToken(Context context, String token) throws SQLExceptio
criteriaQuery.where(criteriaBuilder.equal(requestItemRoot.get(RequestItem_.token), token));
return uniqueResult(context, criteriaQuery, false, RequestItem.class);
}
+
@Override
public Iterator 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 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);
+ }
+
+ @Override
+ public Iterator findByBitstreamId(Context context, UUID bitstreamId) throws SQLException {
+ Query query = createQuery(context, "FROM RequestItem WHERE bitstream.id = :bitstreamId");
+ query.setParameter("bitstreamId", bitstreamId);
return iterate(query);
}
}
diff --git a/dspace-api/src/main/java/org/dspace/app/requestitem/service/RequestItemService.java b/dspace-api/src/main/java/org/dspace/app/requestitem/service/RequestItemService.java
index efac3b18bc7..5c27ba9acb5 100644
--- a/dspace-api/src/main/java/org/dspace/app/requestitem/service/RequestItemService.java
+++ b/dspace-api/src/main/java/org/dspace/app/requestitem/service/RequestItemService.java
@@ -10,6 +10,7 @@
import java.sql.SQLException;
import java.util.Iterator;
import java.util.List;
+import java.util.UUID;
import org.dspace.app.requestitem.RequestItem;
import org.dspace.content.Bitstream;
@@ -40,7 +41,7 @@ public interface RequestItemService {
* @return the token of the request item
* @throws SQLException if database error
*/
- public String createRequest(Context context, Bitstream bitstream, Item item,
+ String createRequest(Context context, Bitstream bitstream, Item item,
boolean allFiles, String reqEmail, String reqName, String reqMessage)
throws SQLException;
@@ -49,35 +50,51 @@ public String createRequest(Context context, Bitstream bitstream, Item item,
*
* @param context current DSpace session.
* @return all item requests.
- * @throws java.sql.SQLException passed through.
+ * @throws SQLException passed through.
*/
- public List findAll(Context context)
+ List findAll(Context context)
throws SQLException;
/**
- * Retrieve a request by its token.
+ * Retrieve a request by its approver token.
*
* @param context current DSpace session.
- * @param token the token identifying the request.
+ * @param token the token identifying the request to be approved.
* @return the matching request, or null if not found.
*/
- public RequestItem findByToken(Context context, String token);
+ RequestItem findByToken(Context context, String token);
/**
- * Retrieve a request based on the item.
+ * Retrieve all requests (as iterator) for a given item
* @param context current DSpace session.
* @param item the item to find requests for.
- * @return the matching requests, or null if not found.
+ * @return the matching requests (or empty iterator)
*/
- public Iterator findByItem(Context context, Item item) throws SQLException;
+ Iterator findByItem(Context context, Item item) throws SQLException;
+
/**
- * Save updates to the record. Only accept_request, and decision_date are set-able.
+ * Retrieve all requests (as iterator) for a given bitstream UUID
+ * A UUID parameter is used here rather than Bitstream object, to make it usable
+ * in situations even when a bitstream object no longer exists, but orphaned
+ * entries need to be found by their (previous) bitstream UUID.
+ *
+ * @param context current DSpace context
+ * @param bitstreamId the bitstream UUID to search for
+ * @return the matching requests (or empty iterator)
+ */
+ Iterator findByBitstreamId(Context context, UUID bitstreamId) throws SQLException;
+
+ /**
+ * Save updates to the record. Only accept_request, decision_date, access_period are settable.
+ *
+ * Note: the "is settable" rules mentioned here are enforced in RequestItemRest with annotations meaning that
+ * these JSON properties are considered READ-ONLY by the core DSpaceRestRepository methods
*
* @param context The relevant DSpace Context.
* @param requestItem requested item
*/
- public void update(Context context, RequestItem requestItem);
+ void update(Context context, RequestItem requestItem);
/**
* Remove the record from the database.
@@ -85,7 +102,7 @@ public List findAll(Context context)
* @param context current DSpace context.
* @param request record to be removed.
*/
- public void delete(Context context, RequestItem request);
+ void delete(Context context, RequestItem request);
/**
* Is there at least one valid READ resource policy for this object?
@@ -94,6 +111,6 @@ public List findAll(Context context)
* @return true if a READ policy applies.
* @throws SQLException passed through.
*/
- public boolean isRestricted(Context context, DSpaceObject o)
+ boolean isRestricted(Context context, DSpaceObject o)
throws SQLException;
}
diff --git a/dspace-api/src/main/java/org/dspace/app/util/DSpaceObjectUtilsImpl.java b/dspace-api/src/main/java/org/dspace/app/util/DSpaceObjectUtilsImpl.java
index e3f2b0ea5fa..33621abd529 100644
--- a/dspace-api/src/main/java/org/dspace/app/util/DSpaceObjectUtilsImpl.java
+++ b/dspace-api/src/main/java/org/dspace/app/util/DSpaceObjectUtilsImpl.java
@@ -15,12 +15,15 @@
import org.dspace.content.factory.ContentServiceFactory;
import org.dspace.content.service.DSpaceObjectService;
import org.dspace.core.Context;
+import org.dspace.handle.service.HandleService;
import org.springframework.beans.factory.annotation.Autowired;
public class DSpaceObjectUtilsImpl implements DSpaceObjectUtils {
@Autowired
private ContentServiceFactory contentServiceFactory;
+ @Autowired
+ private HandleService handleService;
/**
* Retrieve a DSpaceObject from its uuid. As this method need to iterate over all the different services that
@@ -44,4 +47,32 @@ public DSpaceObject findDSpaceObject(Context context, UUID uuid) throws SQLExcep
}
return null;
}
+
+ /**
+ * Retrieve a DSpaceObject from its uuid or handle. As this method need to iterate over all the different services
+ * that support concrete class of DSpaceObject it has poor performance. Please consider the use of the direct
+ * service (ItemService, CommunityService, etc.) if you know in advance the type of DSpaceObject that you are
+ * looking for
+ *
+ * @param context DSpace context
+ * @param id the uuid or handle to lookup
+ * @return the DSpaceObject if any with the supplied uuid or handle
+ * @throws SQLException
+ */
+ public DSpaceObject findDSpaceObject(Context context, String id) throws SQLException {
+ DSpaceObject dso = handleService.resolveToObject(context, id);
+ // if the id did not resolve to a handle, check if it is a uuid
+ if (dso == null) {
+ UUID uuid = null;
+ try {
+ uuid = UUID.fromString(id);
+ } catch (IllegalArgumentException iae) {
+ // nothing to do here. We check later fo empty uuids anyway
+ }
+ if (uuid != null) {
+ dso = findDSpaceObject(context, uuid);
+ }
+ }
+ return dso;
+ }
}
diff --git a/dspace-api/src/main/java/org/dspace/app/util/MetadataExposureServiceImpl.java b/dspace-api/src/main/java/org/dspace/app/util/MetadataExposureServiceImpl.java
index 55deff2853d..386b6e99886 100644
--- a/dspace-api/src/main/java/org/dspace/app/util/MetadataExposureServiceImpl.java
+++ b/dspace-api/src/main/java/org/dspace/app/util/MetadataExposureServiceImpl.java
@@ -67,7 +67,7 @@ public class MetadataExposureServiceImpl implements MetadataExposureService {
protected Map> hiddenElementSets = null;
protected Map>> hiddenElementMaps = null;
- protected final String CONFIG_PREFIX = "metadata.hide.";
+ public static final String CONFIG_PREFIX = "metadata.hide.";
/**
* You can define hidden metadata could be seen by the submitter.
diff --git a/dspace-api/src/main/java/org/dspace/app/util/service/DSpaceObjectUtils.java b/dspace-api/src/main/java/org/dspace/app/util/service/DSpaceObjectUtils.java
index e6a97004ef6..8088a4ca4de 100644
--- a/dspace-api/src/main/java/org/dspace/app/util/service/DSpaceObjectUtils.java
+++ b/dspace-api/src/main/java/org/dspace/app/util/service/DSpaceObjectUtils.java
@@ -30,4 +30,17 @@ public interface DSpaceObjectUtils {
* @throws SQLException
*/
public DSpaceObject findDSpaceObject(Context context, UUID uuid) throws SQLException;
+
+ /**
+ * Retrieve a DSpaceObject from its uuid or handle. As this method need to iterate over all the different services
+ * that support concrete class of DSpaceObject it has poor performance. Please consider the use of the direct
+ * service (ItemService, CommunityService, etc.) if you know in advance the type of DSpaceObject that you are
+ * looking for
+ *
+ * @param context DSpace context
+ * @param id the uuid or handle to lookup
+ * @return the DSpaceObject if any with the supplied uuid or handle
+ * @throws SQLException
+ */
+ public DSpaceObject findDSpaceObject(Context context, String id) throws SQLException;
}
diff --git a/dspace-api/src/main/java/org/dspace/authenticate/AuthenticationMethod.java b/dspace-api/src/main/java/org/dspace/authenticate/AuthenticationMethod.java
index 500ee04a979..7c8793a6c21 100644
--- a/dspace-api/src/main/java/org/dspace/authenticate/AuthenticationMethod.java
+++ b/dspace-api/src/main/java/org/dspace/authenticate/AuthenticationMethod.java
@@ -54,7 +54,7 @@ public interface AuthenticationMethod {
public static final int BAD_CREDENTIALS = 2;
/**
- * Not allowed to login this way without X.509 certificate.
+ * Not allowed to login this way without a certificate.
*/
public static final int CERT_REQUIRED = 3;
@@ -124,8 +124,8 @@ public boolean allowSetPassword(Context context,
* Predicate, is this an implicit authentication method.
* An implicit method gets credentials from the environment (such as
* an HTTP request or even Java system properties) rather than the
- * explicit username and password. For example, a method that reads
- * the X.509 certificates in an HTTPS request is implicit.
+ * explicit username and password. For example, a method that provides
+ * IP-based authentication is implicit.
*
* @return true if this method uses implicit authentication.
*/
@@ -166,7 +166,7 @@ public List getSpecialGroups(Context context, HttpServletRequest request)
* otherwise
*/
public default boolean areSpecialGroupsApplicable(Context context, HttpServletRequest request) {
- return getName().equals(context.getAuthenticationMethod());
+ return getName().equals(context.getAuthenticationMethod()) || isUsed(context, request);
}
/**
@@ -188,7 +188,7 @@ public default boolean areSpecialGroupsApplicable(Context context, HttpServletRe
*
Meaning:
* SUCCESS - authenticated OK.
* BAD_CREDENTIALS - user exists, but credentials (e.g. passwd) don't match
- * CERT_REQUIRED - not allowed to login this way without X.509 cert.
+ * CERT_REQUIRED - not allowed to login this way without a cert.
* NO_SUCH_USER - user not found using this method.
* BAD_ARGS - user/pw not appropriate for this method
* @throws SQLException if database error
diff --git a/dspace-api/src/main/java/org/dspace/authenticate/AuthenticationServiceImpl.java b/dspace-api/src/main/java/org/dspace/authenticate/AuthenticationServiceImpl.java
index 1d67da37ecb..4ad7fbd8c34 100644
--- a/dspace-api/src/main/java/org/dspace/authenticate/AuthenticationServiceImpl.java
+++ b/dspace-api/src/main/java/org/dspace/authenticate/AuthenticationServiceImpl.java
@@ -38,11 +38,11 @@
* Configuration
* The stack of authentication methods is defined by one property in the DSpace configuration:
*
- * plugin.sequence.org.dspace.eperson.AuthenticationMethod = a list of method class names
+ * plugin.sequence.org.dspace.authenticate.AuthenticationMethod = a list of method class names
* e.g.
- * plugin.sequence.org.dspace.eperson.AuthenticationMethod = \
- * org.dspace.eperson.X509Authentication, \
- * org.dspace.eperson.PasswordAuthentication
+ * plugin.sequence.org.dspace.authenticate.AuthenticationMethod = \
+ * org.dspace.authenticate.IPAuthentication, \
+ * org.dspace.authenticate.PasswordAuthentication
*
*
* The "stack" is always traversed in order, with the methods
@@ -111,6 +111,7 @@ protected int authenticateInternal(Context context,
}
if (ret == AuthenticationMethod.SUCCESS) {
updateLastActiveDate(context);
+ context.setAuthenticationMethod(aMethodStack.getName());
return ret;
}
if (ret < bestRet) {
diff --git a/dspace-api/src/main/java/org/dspace/authenticate/LDAPAuthentication.java b/dspace-api/src/main/java/org/dspace/authenticate/LDAPAuthentication.java
index da6a7092481..888ae2530e5 100644
--- a/dspace-api/src/main/java/org/dspace/authenticate/LDAPAuthentication.java
+++ b/dspace-api/src/main/java/org/dspace/authenticate/LDAPAuthentication.java
@@ -203,7 +203,7 @@ public List getSpecialGroups(Context context, HttpServletRequest request)
*
Meaning:
* SUCCESS - authenticated OK.
* BAD_CREDENTIALS - user exists, but credentials (e.g. passwd) don't match
- * CERT_REQUIRED - not allowed to login this way without X.509 cert.
+ * CERT_REQUIRED - not allowed to login this way without a cert.
* NO_SUCH_USER - user not found using this method.
* BAD_ARGS - user/pw not appropriate for this method
*/
@@ -322,7 +322,7 @@ public int authenticate(Context context,
log.info(LogHelper.getHeader(context,
"type=ldap-login", "type=ldap_but_already_email"));
context.turnOffAuthorisationSystem();
- setEpersonAttributes(context, eperson, ldap, Optional.of(netid));
+ setEpersonAttributes(context, eperson, ldap, Optional.of(netid), email);
ePersonService.update(context, eperson);
context.dispatchEvents();
context.restoreAuthSystemState();
@@ -339,7 +339,7 @@ public int authenticate(Context context,
try {
context.turnOffAuthorisationSystem();
eperson = ePersonService.create(context);
- setEpersonAttributes(context, eperson, ldap, Optional.of(netid));
+ setEpersonAttributes(context, eperson, ldap, Optional.of(netid), email);
eperson.setCanLogIn(true);
authenticationService.initEPerson(context, request, eperson);
ePersonService.update(context, eperson);
@@ -381,11 +381,24 @@ public int authenticate(Context context,
* Update eperson's attributes
*/
private void setEpersonAttributes(Context context, EPerson eperson, SpeakerToLDAP ldap, Optional netid)
- throws SQLException {
+ throws SQLException {
+ setEpersonAttributes(context, eperson, ldap, netid, null);
+ }
+ /**
+ * Update eperson's attributes
+ */
+ private void setEpersonAttributes(Context context, EPerson eperson, SpeakerToLDAP ldap, Optional netid,
+ String email)
+ throws SQLException {
+
+ // Set email address: first try LDAP email, then fallback to provided email parameter
if (StringUtils.isNotEmpty(ldap.ldapEmail)) {
eperson.setEmail(ldap.ldapEmail);
+ } else if (StringUtils.isNotEmpty(email)) {
+ eperson.setEmail(email);
}
+
if (StringUtils.isNotEmpty(ldap.ldapGivenName)) {
eperson.setFirstName(context, ldap.ldapGivenName);
}
diff --git a/dspace-api/src/main/java/org/dspace/authenticate/OrcidAuthenticationBean.java b/dspace-api/src/main/java/org/dspace/authenticate/OrcidAuthenticationBean.java
index a11bbfc867b..b5c238f3344 100644
--- a/dspace-api/src/main/java/org/dspace/authenticate/OrcidAuthenticationBean.java
+++ b/dspace-api/src/main/java/org/dspace/authenticate/OrcidAuthenticationBean.java
@@ -120,7 +120,7 @@ public String loginPageURL(Context context, HttpServletRequest request, HttpServ
@Override
public boolean isUsed(Context context, HttpServletRequest request) {
- return request.getAttribute(ORCID_AUTH_ATTRIBUTE) != null;
+ return request != null && request.getAttribute(ORCID_AUTH_ATTRIBUTE) != null;
}
@Override
diff --git a/dspace-api/src/main/java/org/dspace/authenticate/PasswordAuthentication.java b/dspace-api/src/main/java/org/dspace/authenticate/PasswordAuthentication.java
index 6d1ca862d30..69f0bd55f87 100644
--- a/dspace-api/src/main/java/org/dspace/authenticate/PasswordAuthentication.java
+++ b/dspace-api/src/main/java/org/dspace/authenticate/PasswordAuthentication.java
@@ -188,7 +188,7 @@ public List getSpecialGroups(Context context, HttpServletRequest request)
*
Meaning:
* SUCCESS - authenticated OK.
* BAD_CREDENTIALS - user exists, but password doesn't match
- * CERT_REQUIRED - not allowed to login this way without X.509 cert.
+ * CERT_REQUIRED - not allowed to login this way without a cert.
* NO_SUCH_USER - no EPerson with matching email address.
* BAD_ARGS - missing username, or user matched but cannot login.
* @throws SQLException if database error
@@ -213,7 +213,7 @@ public int authenticate(Context context,
// cannot login this way
return BAD_ARGS;
} else if (eperson.getRequireCertificate()) {
- // this user can only login with x.509 certificate
+ // this user can only login with a certificate
log.warn(LogHelper.getHeader(context, "authenticate",
"rejecting PasswordAuthentication because " + username + " requires " +
"certificate."));
diff --git a/dspace-api/src/main/java/org/dspace/authenticate/ShibAuthentication.java b/dspace-api/src/main/java/org/dspace/authenticate/ShibAuthentication.java
index d9d5338877e..7911993437e 100644
--- a/dspace-api/src/main/java/org/dspace/authenticate/ShibAuthentication.java
+++ b/dspace-api/src/main/java/org/dspace/authenticate/ShibAuthentication.java
@@ -166,7 +166,7 @@ public class ShibAuthentication implements AuthenticationMethod {
* SUCCESS - authenticated OK.
* BAD_CREDENTIALS - user exists, but credentials (e.g. passwd)
* don't match
- * CERT_REQUIRED - not allowed to login this way without X.509 cert.
+ * CERT_REQUIRED - not allowed to login this way without a cert.
*
* NO_SUCH_USER - user not found using this method.
* BAD_ARGS - user/pw not appropriate for this method
@@ -422,8 +422,7 @@ public boolean allowSetPassword(Context context,
* Predicate, is this an implicit authentication method. An implicit method
* gets credentials from the environment (such as an HTTP request or even
* Java system properties) rather than the explicit username and password.
- * For example, a method that reads the X.509 certificates in an HTTPS
- * request is implicit.
+ * For example, a method that provides IP-based authentication is implicit.
*
* @return true if this method uses implicit authentication.
*/
@@ -899,7 +898,7 @@ protected void updateEPerson(Context context, HttpServletRequest request, EPerso
String[] nameParts = MetadataFieldName.parse(field);
ePersonService.setMetadataSingleValue(context, eperson,
- nameParts[0], nameParts[1], nameParts[2], value, null);
+ nameParts[0], nameParts[1], nameParts[2], null, value);
log.debug("Updated the eperson's '{}' metadata using header: '{}' = '{}'.",
field, header, value);
}
@@ -945,7 +944,7 @@ protected int swordCompatibility(Context context, String username, String passwo
" is not allowed to login.");
return BAD_ARGS;
} else if (eperson.getRequireCertificate()) {
- // this user can only login with x.509 certificate
+ // this user can only login with a certificate
log.error(
"Shibboleth-based password authentication failed for user " + username + " because the eperson object" +
" requires a certificate to authenticate..");
diff --git a/dspace-api/src/main/java/org/dspace/authenticate/X509Authentication.java b/dspace-api/src/main/java/org/dspace/authenticate/X509Authentication.java
deleted file mode 100644
index 12dc5feda58..00000000000
--- a/dspace-api/src/main/java/org/dspace/authenticate/X509Authentication.java
+++ /dev/null
@@ -1,616 +0,0 @@
-/**
- * The contents of this file are subject to the license and copyright
- * detailed in the LICENSE and NOTICE files at the root of the source
- * tree and available online at
- *
- * http://www.dspace.org/license/
- */
-package org.dspace.authenticate;
-
-import java.io.BufferedInputStream;
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.security.GeneralSecurityException;
-import java.security.KeyStore;
-import java.security.Principal;
-import java.security.PublicKey;
-import java.security.cert.Certificate;
-import java.security.cert.CertificateException;
-import java.security.cert.CertificateFactory;
-import java.security.cert.X509Certificate;
-import java.sql.SQLException;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Enumeration;
-import java.util.List;
-import java.util.StringTokenizer;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-import javax.servlet.http.HttpSession;
-
-import org.apache.commons.lang3.ArrayUtils;
-import org.apache.logging.log4j.Logger;
-import org.dspace.authenticate.factory.AuthenticateServiceFactory;
-import org.dspace.authenticate.service.AuthenticationService;
-import org.dspace.authorize.AuthorizeException;
-import org.dspace.core.Context;
-import org.dspace.core.LogHelper;
-import org.dspace.eperson.EPerson;
-import org.dspace.eperson.Group;
-import org.dspace.eperson.factory.EPersonServiceFactory;
-import org.dspace.eperson.service.EPersonService;
-import org.dspace.eperson.service.GroupService;
-import org.dspace.services.ConfigurationService;
-import org.dspace.services.factory.DSpaceServicesFactory;
-
-/**
- * Implicit authentication method that gets credentials from the X.509 client
- * certificate supplied by the HTTPS client when connecting to this server. The
- * email address in that certificate is taken as the authenticated user name
- * with no further checking, so be sure your HTTP server (e.g. Tomcat) is
- * configured correctly to accept only client certificates it can validate.
- *
- * See the AuthenticationMethod interface for more details.
- *
- * Configuration:
- *
- *
- * x509.keystore.path =
- *
- * path to Java keystore file
- *
- * keystore.password =
- *
- * password to access the keystore
- *
- * ca.cert =
- *
- * path to certificate file for CA whose client certs to accept.
- *
- * autoregister =
- *
- * "true" if E-Person is created automatically for unknown new users.
- *
- * groups =
- *
- * comma-delimited list of special groups to add user to if authenticated.
- *
- * emaildomain =
- *
- * email address domain (after the 'at' symbol) to match before allowing
- * membership in special groups.
- *
- *
- *
- * Only one of the "keystore.path" or "ca.cert"
- * options is required. If you supply a keystore, then all of the "trusted"
- * certificates in the keystore represent CAs whose client certificates will be
- * accepted. The ca.cert option only allows a single CA to be
- * named.
- *
- * You can configure both a keystore and a CA cert, and both will be
- * used.
- *
- * The autoregister configuration parameter determines what the
- * canSelfRegister() method returns. It also allows an EPerson
- * record to be created automatically when the presented certificate is
- * acceptable but there is no corresponding EPerson.
- *
- * @author Larry Stone
- * @version $Revision$
- */
-public class X509Authentication implements AuthenticationMethod {
-
- /**
- * log4j category
- */
- private static Logger log = org.apache.logging.log4j.LogManager.getLogger(X509Authentication.class);
-
- /**
- * public key of CA to check client certs against.
- */
- private static PublicKey caPublicKey = null;
-
- /**
- * key store for CA certs if we use that
- */
- private static KeyStore caCertKeyStore = null;
-
- private static String loginPageTitle = null;
-
- private static String loginPageURL = null;
-
- protected AuthenticationService authenticationService = AuthenticateServiceFactory.getInstance()
- .getAuthenticationService();
- protected EPersonService ePersonService = EPersonServiceFactory.getInstance().getEPersonService();
- protected GroupService groupService = EPersonServiceFactory.getInstance().getGroupService();
- protected ConfigurationService configurationService =
- DSpaceServicesFactory.getInstance().getConfigurationService();
-
- private static final String X509_AUTHENTICATED = "x509.authenticated";
-
-
- /**
- * Initialization: Set caPublicKey and/or keystore. This loads the
- * information needed to check if a client cert presented is valid and
- * acceptable.
- */
- static {
- ConfigurationService configurationService =
- DSpaceServicesFactory.getInstance().getConfigurationService();
- /*
- * allow identification of alternative entry points for certificate
- * authentication when selected by the user rather than implicitly.
- */
- loginPageTitle = configurationService
- .getProperty("authentication-x509.chooser.title.key");
- loginPageURL = configurationService
- .getProperty("authentication-x509.chooser.uri");
-
- String keystorePath = configurationService
- .getProperty("authentication-x509.keystore.path");
- String keystorePassword = configurationService
- .getProperty("authentication-x509.keystore.password");
- String caCertPath = configurationService
- .getProperty("authentication-x509.ca.cert");
-
- // First look for keystore full of trusted certs.
- if (keystorePath != null) {
- FileInputStream fis = null;
- if (keystorePassword == null) {
- keystorePassword = "";
- }
- try {
- KeyStore ks = KeyStore.getInstance("JKS");
- fis = new FileInputStream(keystorePath);
- ks.load(fis, keystorePassword.toCharArray());
- caCertKeyStore = ks;
- } catch (IOException e) {
- log
- .error("X509Authentication: Failed to load CA keystore, file="
- + keystorePath + ", error=" + e.toString());
- } catch (GeneralSecurityException e) {
- log
- .error("X509Authentication: Failed to extract CA keystore, file="
- + keystorePath + ", error=" + e.toString());
- } finally {
- if (fis != null) {
- try {
- fis.close();
- } catch (IOException ioe) {
- // ignore
- }
- }
- }
- }
-
- // Second, try getting public key out of CA cert, if that's configured.
- if (caCertPath != null) {
- InputStream is = null;
- FileInputStream fis = null;
- try {
- fis = new FileInputStream(caCertPath);
- is = new BufferedInputStream(fis);
- X509Certificate cert = (X509Certificate) CertificateFactory
- .getInstance("X.509").generateCertificate(is);
- if (cert != null) {
- caPublicKey = cert.getPublicKey();
- }
- } catch (IOException e) {
- log.error("X509Authentication: Failed to load CA cert, file="
- + caCertPath + ", error=" + e.toString());
- } catch (CertificateException e) {
- log
- .error("X509Authentication: Failed to extract CA cert, file="
- + caCertPath + ", error=" + e.toString());
- } finally {
- if (is != null) {
- try {
- is.close();
- } catch (IOException ioe) {
- // ignore
- }
- }
-
- if (fis != null) {
- try {
- fis.close();
- } catch (IOException ioe) {
- // ignore
- }
- }
- }
- }
- }
-
- /**
- * Return the email address from certificate, or null if an
- * email address cannot be found in the certificate.
- *
- * Note that the certificate parsing has only been tested with certificates
- * granted by the MIT Certification Authority, and may not work elsewhere.
- *
- * @param certificate -
- * An X509 certificate object
- * @return - The email address found in certificate, or null if an email
- * address cannot be found in the certificate.
- */
- private static String getEmail(X509Certificate certificate)
- throws SQLException {
- Principal principal = certificate.getSubjectDN();
-
- if (principal == null) {
- return null;
- }
-
- String dn = principal.getName();
- if (dn == null) {
- return null;
- }
-
- StringTokenizer tokenizer = new StringTokenizer(dn, ",");
- String token = null;
- while (tokenizer.hasMoreTokens()) {
- int len = "emailaddress=".length();
-
- token = (String) tokenizer.nextToken();
-
- if (token.toLowerCase().startsWith("emailaddress=")) {
- // Make sure the token actually contains something
- if (token.length() <= len) {
- return null;
- }
-
- return token.substring(len).toLowerCase();
- }
- }
-
- return null;
- }
-
- /**
- * Verify CERTIFICATE against KEY. Return true if and only if CERTIFICATE is
- * valid and can be verified against KEY.
- *
- * @param context The current DSpace context
- * @param certificate -
- * An X509 certificate object
- * @return - True if CERTIFICATE is valid and can be verified against KEY,
- * false otherwise.
- */
- private static boolean isValid(Context context, X509Certificate certificate) {
- if (certificate == null) {
- return false;
- }
-
- // This checks that current time is within cert's validity window:
- try {
- certificate.checkValidity();
- } catch (CertificateException e) {
- log.info(LogHelper.getHeader(context, "authentication",
- "X.509 Certificate is EXPIRED or PREMATURE: "
- + e.toString()));
- return false;
- }
-
- // Try CA public key, if available.
- if (caPublicKey != null) {
- try {
- certificate.verify(caPublicKey);
- return true;
- } catch (GeneralSecurityException e) {
- log.info(LogHelper.getHeader(context, "authentication",
- "X.509 Certificate FAILED SIGNATURE check: "
- + e.toString()));
- }
- }
-
- // Try it with keystore, if available.
- if (caCertKeyStore != null) {
- try {
- Enumeration ke = caCertKeyStore.aliases();
-
- while (ke.hasMoreElements()) {
- String alias = (String) ke.nextElement();
- if (caCertKeyStore.isCertificateEntry(alias)) {
- Certificate ca = caCertKeyStore.getCertificate(alias);
- try {
- certificate.verify(ca.getPublicKey());
- return true;
- } catch (CertificateException ce) {
- // ignore
- }
- }
- }
- log
- .info(LogHelper
- .getHeader(context, "authentication",
- "Keystore method FAILED SIGNATURE check on client cert."));
- } catch (GeneralSecurityException e) {
- log.info(LogHelper.getHeader(context, "authentication",
- "X.509 Certificate FAILED SIGNATURE check: "
- + e.toString()));
- }
-
- }
- return false;
- }
-
- /**
- * Predicate, can new user automatically create EPerson. Checks
- * configuration value. You'll probably want this to be true to take
- * advantage of a Web certificate infrastructure with many more users than
- * are already known by DSpace.
- *
- * @throws SQLException if database error
- */
- @Override
- public boolean canSelfRegister(Context context, HttpServletRequest request,
- String username) throws SQLException {
- return configurationService
- .getBooleanProperty("authentication-x509.autoregister");
- }
-
- /**
- * Nothing extra to initialize.
- *
- * @throws SQLException if database error
- */
- @Override
- public void initEPerson(Context context, HttpServletRequest request,
- EPerson eperson) throws SQLException {
- }
-
- /**
- * We don't use EPerson password so there is no reason to change it.
- *
- * @throws SQLException if database error
- */
- @Override
- public boolean allowSetPassword(Context context,
- HttpServletRequest request, String username) throws SQLException {
- return false;
- }
-
- /**
- * Returns true, this is an implicit method.
- */
- @Override
- public boolean isImplicit() {
- return true;
- }
-
- /**
- * Returns a list of group names that the user should be added to upon
- * successful authentication, configured in dspace.cfg.
- *
- * @return List of special groups configured for this authenticator
- */
- private List getX509Groups() {
- List groupNames = new ArrayList();
-
- String[] groups = configurationService
- .getArrayProperty("authentication-x509.groups");
-
- if (ArrayUtils.isNotEmpty(groups)) {
- for (String group : groups) {
- groupNames.add(group.trim());
- }
- }
-
- return groupNames;
- }
-
- /**
- * Checks for configured email domain required to grant special groups
- * membership. If no email domain is configured to verify, special group
- * membership is simply granted.
- *
- * @param request -
- * The current request object
- * @param email -
- * The email address from the x509 certificate
- */
- private void setSpecialGroupsFlag(HttpServletRequest request, String email) {
- String emailDomain = null;
- emailDomain = (String) request
- .getAttribute("authentication.x509.emaildomain");
-
- HttpSession session = request.getSession(true);
-
- if (null != emailDomain && !"".equals(emailDomain)) {
- if (email.substring(email.length() - emailDomain.length()).equals(
- emailDomain)) {
- session.setAttribute("x509Auth", Boolean.TRUE);
- }
- } else {
- // No configured email domain to verify. Just flag
- // as authenticated so special groups are granted.
- session.setAttribute("x509Auth", Boolean.TRUE);
- }
- }
-
- /**
- * Return special groups configured in dspace.cfg for X509 certificate
- * authentication.
- *
- * @param context context
- * @param request object potentially containing the cert
- * @return An int array of group IDs
- * @throws SQLException if database error
- */
- @Override
- public List getSpecialGroups(Context context, HttpServletRequest request)
- throws SQLException {
- if (request == null) {
- return Collections.EMPTY_LIST;
- }
-
- Boolean authenticated = false;
- HttpSession session = request.getSession(false);
- authenticated = (Boolean) session.getAttribute("x509Auth");
- authenticated = (null == authenticated) ? false : authenticated;
-
- if (authenticated) {
- List groupNames = getX509Groups();
- List groups = new ArrayList<>();
-
- if (groupNames != null) {
- for (String groupName : groupNames) {
- if (groupName != null) {
- Group group = groupService.findByName(context, groupName);
- if (group != null) {
- groups.add(group);
- } else {
- log.warn(LogHelper.getHeader(context,
- "configuration_error", "unknown_group="
- + groupName));
- }
- }
- }
- }
-
- return groups;
- }
-
- return Collections.EMPTY_LIST;
- }
-
- /**
- * X509 certificate authentication. The client certificate is obtained from
- * the ServletRequest object.
- *
- *
If the certificate is valid, and corresponds to an existing EPerson,
- * and the user is allowed to login, return success.
- *
If the user is matched but is not allowed to login, it fails.
- *
If the certificate is valid, but there is no corresponding EPerson,
- * the "authentication.x509.autoregister" configuration
- * parameter is checked (via canSelfRegister())
- *
- *
If it's true, a new EPerson record is created for the certificate,
- * and the result is success.
- *
If it's false, return that the user was unknown.
- *
- *
- *
- *
- * @return One of: SUCCESS, BAD_CREDENTIALS, NO_SUCH_USER, BAD_ARGS
- * @throws SQLException if database error
- */
- @Override
- public int authenticate(Context context, String username, String password,
- String realm, HttpServletRequest request) throws SQLException {
- // Obtain the certificate from the request, if any
- X509Certificate[] certs = null;
- if (request != null) {
- certs = (X509Certificate[]) request
- .getAttribute("javax.servlet.request.X509Certificate");
- }
-
- if ((certs == null) || (certs.length == 0)) {
- return BAD_ARGS;
- } else {
- // We have a cert -- check it and get username from it.
- try {
- if (!isValid(context, certs[0])) {
- log
- .warn(LogHelper
- .getHeader(context, "authenticate",
- "type=x509certificate, status=BAD_CREDENTIALS (not valid)"));
- return BAD_CREDENTIALS;
- }
-
- // And it's valid - try and get an e-person
- String email = getEmail(certs[0]);
- EPerson eperson = null;
- if (email != null) {
- eperson = ePersonService.findByEmail(context, email);
- }
- if (eperson == null) {
- // Cert is valid, but no record.
- if (email != null
- && canSelfRegister(context, request, null)) {
- // Register the new user automatically
- log.info(LogHelper.getHeader(context, "autoregister",
- "from=x.509, email=" + email));
-
- // TEMPORARILY turn off authorisation
- context.turnOffAuthorisationSystem();
- eperson = ePersonService.create(context);
- eperson.setEmail(email);
- eperson.setCanLogIn(true);
- authenticationService.initEPerson(context, request,
- eperson);
- ePersonService.update(context, eperson);
- context.dispatchEvents();
- context.restoreAuthSystemState();
- context.setCurrentUser(eperson);
- request.setAttribute(X509_AUTHENTICATED, true);
- setSpecialGroupsFlag(request, email);
- return SUCCESS;
- } else {
- // No auto-registration for valid certs
- log
- .warn(LogHelper
- .getHeader(context, "authenticate",
- "type=cert_but_no_record, cannot auto-register"));
- return NO_SUCH_USER;
- }
- } else if (!eperson.canLogIn()) { // make sure this is a login account
- log.warn(LogHelper.getHeader(context, "authenticate",
- "type=x509certificate, email=" + email
- + ", canLogIn=false, rejecting."));
- return BAD_ARGS;
- } else {
- log.info(LogHelper.getHeader(context, "login",
- "type=x509certificate"));
- context.setCurrentUser(eperson);
- request.setAttribute(X509_AUTHENTICATED, true);
- setSpecialGroupsFlag(request, email);
- return SUCCESS;
- }
- } catch (AuthorizeException ce) {
- log.warn(LogHelper.getHeader(context, "authorize_exception",
- ""), ce);
- }
-
- return BAD_ARGS;
- }
- }
-
- /**
- * Returns URL of password-login servlet.
- *
- * @param context DSpace context, will be modified (EPerson set) upon success.
- * @param request The HTTP request that started this operation, or null if not
- * applicable.
- * @param response The HTTP response from the servlet method.
- * @return fully-qualified URL
- */
- @Override
- public String loginPageURL(Context context, HttpServletRequest request,
- HttpServletResponse response) {
- return loginPageURL;
- }
-
- @Override
- public String getName() {
- return "x509";
- }
-
- @Override
- public boolean isUsed(final Context context, final HttpServletRequest request) {
- if (request != null &&
- context.getCurrentUser() != null &&
- request.getAttribute(X509_AUTHENTICATED) != null) {
- return true;
- }
- return false;
- }
-
- @Override
- public boolean canChangePassword(Context context, EPerson ePerson, String currentPassword) {
- return false;
- }
-}
diff --git a/dspace-api/src/main/java/org/dspace/authenticate/service/AuthenticationService.java b/dspace-api/src/main/java/org/dspace/authenticate/service/AuthenticationService.java
index e955302ec3d..105e30d9bdf 100644
--- a/dspace-api/src/main/java/org/dspace/authenticate/service/AuthenticationService.java
+++ b/dspace-api/src/main/java/org/dspace/authenticate/service/AuthenticationService.java
@@ -29,11 +29,11 @@
* Configuration
* The stack of authentication methods is defined by one property in the DSpace configuration:
*
- * plugin.sequence.org.dspace.eperson.AuthenticationMethod = a list of method class names
+ * plugin.sequence.org.dspace.authenticate.AuthenticationMethod = a list of method class names
* e.g.
- * plugin.sequence.org.dspace.eperson.AuthenticationMethod = \
- * org.dspace.eperson.X509Authentication, \
- * org.dspace.eperson.PasswordAuthentication
+ * plugin.sequence.org.dspace.authenticate.AuthenticationMethod = \
+ * org.dspace.authenticate.IPAuthentication, \
+ * org.dspace.authenticate.PasswordAuthentication
*
*
* The "stack" is always traversed in order, with the methods
@@ -64,7 +64,7 @@ public interface AuthenticationService {
*
Meaning:
* SUCCESS - authenticated OK.
* BAD_CREDENTIALS - user exists, but credentials (e.g. password) don't match
- * CERT_REQUIRED - not allowed to login this way without X.509 cert.
+ * CERT_REQUIRED - not allowed to login this way without a cert.
* NO_SUCH_USER - user not found using this method.
* BAD_ARGS - user/password not appropriate for this method
*/
@@ -91,7 +91,7 @@ public int authenticate(Context context,
*
Meaning:
* SUCCESS - authenticated OK.
* BAD_CREDENTIALS - user exists, but credentials (e.g. password) don't match
- * CERT_REQUIRED - not allowed to login this way without X.509 cert.
+ * CERT_REQUIRED - not allowed to login this way without a cert.
* NO_SUCH_USER - user not found using this method.
* BAD_ARGS - user/password not appropriate for this method
*/
diff --git a/dspace-api/src/main/java/org/dspace/authority/orcid/Orcidv3SolrAuthorityImpl.java b/dspace-api/src/main/java/org/dspace/authority/orcid/Orcidv3SolrAuthorityImpl.java
index 494daa97734..312a00c146a 100644
--- a/dspace-api/src/main/java/org/dspace/authority/orcid/Orcidv3SolrAuthorityImpl.java
+++ b/dspace-api/src/main/java/org/dspace/authority/orcid/Orcidv3SolrAuthorityImpl.java
@@ -10,6 +10,7 @@
import java.io.IOException;
import java.io.InputStream;
import java.net.URLEncoder;
+import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedList;
@@ -20,6 +21,7 @@
import org.apache.logging.log4j.Logger;
import org.dspace.authority.AuthorityValue;
import org.dspace.authority.SolrAuthorityInterface;
+import org.dspace.external.OrcidConnectionException;
import org.dspace.external.OrcidRestConnector;
import org.dspace.external.provider.orcid.xml.XMLtoBio;
import org.dspace.orcid.model.factory.OrcidFactoryUtils;
@@ -142,9 +144,15 @@ public Person getBio(String id) {
return null;
}
initializeAccessToken();
- InputStream bioDocument = orcidRestConnector.get(id + ((id.endsWith("/person")) ? "" : "/person"), accessToken);
- XMLtoBio converter = new XMLtoBio();
- return converter.convertSinglePerson(bioDocument);
+ try {
+ InputStream bioDocument = orcidRestConnector.get(id + ((id.endsWith("/person")) ? "" : "/person"),
+ accessToken);
+ XMLtoBio converter = new XMLtoBio();
+ return converter.convertSinglePerson(bioDocument);
+ } catch (OrcidConnectionException e) {
+ log.error("Error retrieving ORCID bio for ID=" + id, e);
+ return null;
+ }
}
@@ -167,29 +175,35 @@ public List queryBio(String text, int start, int rows) {
// Check / init access token
initializeAccessToken();
- String searchPath = "search?q=" + URLEncoder.encode(text) + "&start=" + start + "&rows=" + rows;
+ String searchPath = "search?q=" + URLEncoder.encode(text, StandardCharsets.UTF_8) + "&start=" + start +
+ "&rows=" + rows;
log.debug("queryBio searchPath=" + searchPath + " accessToken=" + accessToken);
- InputStream bioDocument = orcidRestConnector.get(searchPath, accessToken);
- XMLtoBio converter = new XMLtoBio();
- List results = converter.convert(bioDocument);
- List bios = new LinkedList<>();
- for (Result result : results) {
- OrcidIdentifier orcidIdentifier = result.getOrcidIdentifier();
- if (orcidIdentifier != null) {
- log.debug("Found OrcidId=" + orcidIdentifier.toString());
- String orcid = orcidIdentifier.getPath();
- Person bio = getBio(orcid);
- if (bio != null) {
- bios.add(bio);
+ try {
+ InputStream bioDocument = orcidRestConnector.get(searchPath, accessToken);
+ XMLtoBio converter = new XMLtoBio();
+ List results = converter.convert(bioDocument);
+ List bios = new LinkedList<>();
+ for (Result result : results) {
+ OrcidIdentifier orcidIdentifier = result.getOrcidIdentifier();
+ if (orcidIdentifier != null) {
+ log.debug("Found OrcidId=" + orcidIdentifier);
+ String orcid = orcidIdentifier.getPath();
+ Person bio = getBio(orcid);
+ if (bio != null) {
+ bios.add(bio);
+ }
}
}
+ try {
+ bioDocument.close();
+ } catch (IOException e) {
+ log.error(e.getMessage(), e);
+ }
+ return bios;
+ } catch (OrcidConnectionException e) {
+ log.error("Error searching ORCID for query=" + text, e);
+ return Collections.emptyList();
}
- try {
- bioDocument.close();
- } catch (IOException e) {
- log.error(e.getMessage(), e);
- }
- return bios;
}
/**
diff --git a/dspace-api/src/main/java/org/dspace/authorize/AuthorizeServiceImpl.java b/dspace-api/src/main/java/org/dspace/authorize/AuthorizeServiceImpl.java
index ec418163acd..189dbe3d0d0 100644
--- a/dspace-api/src/main/java/org/dspace/authorize/AuthorizeServiceImpl.java
+++ b/dspace-api/src/main/java/org/dspace/authorize/AuthorizeServiceImpl.java
@@ -741,15 +741,15 @@ public List getPoliciesActionFilterExceptRpType(Context c, DSpac
/**
* Checks that the context's current user is a community admin in the site by querying the solr database.
+ * This query doesn't use authorization inheritance because direct community admin is enough to perform this check.
*
* @param context context with the current user
* @return true if the current user is a community admin in the site
* false when this is not the case, or an exception occurred
- * @throws java.sql.SQLException passed through.
*/
@Override
- public boolean isCommunityAdmin(Context context) throws SQLException {
- return performCheck(context, RESOURCE_TYPE_FIELD + ":" + IndexableCommunity.TYPE);
+ public boolean isCommunityAdmin(Context context) {
+ return performCheck(context, RESOURCE_TYPE_FIELD + ":" + IndexableCommunity.TYPE, false);
}
/**
@@ -758,11 +758,10 @@ public boolean isCommunityAdmin(Context context) throws SQLException {
* @param context context with the current user
* @return true if the current user is a collection admin in the site
* false when this is not the case, or an exception occurred
- * @throws java.sql.SQLException passed through.
*/
@Override
- public boolean isCollectionAdmin(Context context) throws SQLException {
- return performCheck(context, RESOURCE_TYPE_FIELD + ":" + IndexableCollection.TYPE);
+ public boolean isCollectionAdmin(Context context) {
+ return performCheck(context, RESOURCE_TYPE_FIELD + ":" + IndexableCollection.TYPE, true);
}
/**
@@ -771,26 +770,26 @@ public boolean isCollectionAdmin(Context context) throws SQLException {
* @param context context with the current user
* @return true if the current user is an item admin in the site
* false when this is not the case, or an exception occurred
- * @throws java.sql.SQLException passed through.
*/
@Override
- public boolean isItemAdmin(Context context) throws SQLException {
- return performCheck(context, RESOURCE_TYPE_FIELD + ":" + IndexableItem.TYPE);
+ public boolean isItemAdmin(Context context) {
+ return performCheck(context, RESOURCE_TYPE_FIELD + ":" + IndexableItem.TYPE, true);
}
/**
* Checks that the context's current user is a community or collection admin in the site.
+ * This query doesn't use authorization inheritance because direct community/collection admin is enough to
+ * perform this check.
*
* @param context context with the current user
* @return true if the current user is a community or collection admin in the site
* false when this is not the case, or an exception occurred
- * @throws java.sql.SQLException passed through.
*/
@Override
- public boolean isComColAdmin(Context context) throws SQLException {
+ public boolean isComColAdmin(Context context) {
return performCheck(context,
"(" + RESOURCE_TYPE_FIELD + ":" + IndexableCommunity.TYPE + " OR " +
- RESOURCE_TYPE_FIELD + ":" + IndexableCollection.TYPE + ")");
+ RESOURCE_TYPE_FIELD + ":" + IndexableCollection.TYPE + ")", false);
}
/**
@@ -805,11 +804,30 @@ public boolean isComColAdmin(Context context) throws SQLException {
*/
@Override
public List findAdminAuthorizedCommunity(Context context, String query, int offset, int limit)
- throws SearchServiceException, SQLException {
+ throws SearchServiceException {
+ return findAuthorizedCommunityByAction(context, query, Constants.ADMIN, offset, limit);
+ }
+
+ /**
+ * Finds communities for which the logged in user has the rights specified by the action parameter.
+ *
+ * @param context the context whose user is checked against
+ * @param query the optional extra query
+ * @param action the action to check for
+ * @param offset the offset for pagination
+ * @param limit the amount of dso's to return
+ * @return a list of communities for which the logged in user has the rights specified by the action
+ * @throws SearchServiceException
+ */
+ @Override
+ public List findAuthorizedCommunityByAction(Context context, String query, int action, int offset,
+ int limit)
+ throws SearchServiceException {
List communities = new ArrayList<>();
+ query = searchService.formatAutoCompleteQuery(query, "dc.title_sort");
query = formatCustomQuery(query);
DiscoverResult discoverResult = getDiscoverResult(context, query + RESOURCE_TYPE_FIELD + ":" +
- IndexableCommunity.TYPE,
+ IndexableCommunity.TYPE, action, true,
offset, limit, null, null);
for (IndexableObject solrCollections : discoverResult.getIndexableObjects()) {
Community community = ((IndexableCommunity) solrCollections).getIndexedObject();
@@ -828,10 +846,26 @@ public List findAdminAuthorizedCommunity(Context context, String quer
*/
@Override
public long countAdminAuthorizedCommunity(Context context, String query)
- throws SearchServiceException, SQLException {
+ throws SearchServiceException {
+ return countAuthorizedCommunityByAction(context, query, Constants.ADMIN);
+ }
+
+ /**
+ * Counts communities for which the current user has the rights specified by the action parameter.
+ *
+ * @param context context with the current user
+ * @param query the query for which to filter the results more
+ * @param action the action to check for
+ * @return the matching communities
+ * @throws SearchServiceException
+ */
+ @Override
+ public long countAuthorizedCommunityByAction(Context context, String query, int action)
+ throws SearchServiceException {
+ query = searchService.formatAutoCompleteQuery(query, "dc.title_sort");
query = formatCustomQuery(query);
DiscoverResult discoverResult = getDiscoverResult(context, query + RESOURCE_TYPE_FIELD + ":" +
- IndexableCommunity.TYPE,
+ IndexableCommunity.TYPE, action, true,
null, 0, null, null);
return discoverResult.getTotalSearchResults();
}
@@ -848,15 +882,34 @@ public long countAdminAuthorizedCommunity(Context context, String query)
*/
@Override
public List findAdminAuthorizedCollection(Context context, String query, int offset, int limit)
- throws SearchServiceException, SQLException {
+ throws SearchServiceException {
+ return findAuthorizedCollectionByAction(context, query, Constants.ADMIN, offset, limit);
+ }
+
+ /**
+ * Finds collections for which the logged in user has the rights specified by the action parameter.
+ *
+ * @param context the context whose user is checked against
+ * @param query the optional extra query
+ * @param action the action to check for
+ * @param offset the offset for pagination
+ * @param limit the amount of dso's to return
+ * @return a list of collections for which the logged in user has the rights specified by the action
+ * @throws SearchServiceException
+ */
+ @Override
+ public List findAuthorizedCollectionByAction(Context context, String query,
+ int action, int offset, int limit)
+ throws SearchServiceException {
List collections = new ArrayList<>();
if (context.getCurrentUser() == null) {
return collections;
}
+ query = searchService.formatAutoCompleteQuery(query, "dc.title_sort");
query = formatCustomQuery(query);
DiscoverResult discoverResult = getDiscoverResult(context, query + RESOURCE_TYPE_FIELD + ":" +
- IndexableCollection.TYPE,
+ IndexableCollection.TYPE, action, true,
offset, limit, CollectionService.SOLR_SORT_FIELD, SORT_ORDER.asc);
for (IndexableObject solrCollections : discoverResult.getIndexableObjects()) {
Collection collection = ((IndexableCollection) solrCollections).getIndexedObject();
@@ -875,31 +928,44 @@ public List findAdminAuthorizedCollection(Context context, String qu
*/
@Override
public long countAdminAuthorizedCollection(Context context, String query)
- throws SearchServiceException, SQLException {
+ throws SearchServiceException {
+ return countAuthorizedCollectionByAction(context, query, Constants.ADMIN);
+ }
+
+ /**
+ * Counts collections for which the current user has the rights specified by the action parameter.
+ *
+ * @param context context with the current user
+ * @param query the query for which to filter the results more
+ * @param action the action to check for
+ * @return the matching collections
+ * @throws SearchServiceException
+ */
+ @Override
+ public long countAuthorizedCollectionByAction(Context context, String query, int action)
+ throws SearchServiceException {
+ query = searchService.formatAutoCompleteQuery(query, "dc.title_sort");
query = formatCustomQuery(query);
DiscoverResult discoverResult = getDiscoverResult(context, query + RESOURCE_TYPE_FIELD + ":" +
- IndexableCollection.TYPE,
- null, 0, null, null);
+ IndexableCollection.TYPE, action,
+ true, null, 0, null, null);
return discoverResult.getTotalSearchResults();
}
@Override
public boolean isAccountManager(Context context) {
- try {
- return (canCommunityAdminManageAccounts() && isCommunityAdmin(context)
- || canCollectionAdminManageAccounts() && isCollectionAdmin(context));
- } catch (SQLException e) {
- throw new RuntimeException(e);
- }
+ return (canCommunityAdminManageAccounts() && isCommunityAdmin(context)
+ || canCollectionAdminManageAccounts() && isCollectionAdmin(context));
}
- private boolean performCheck(Context context, String query) throws SQLException {
+ private boolean performCheck(Context context, String query, boolean inheritAuthorizations) {
if (context.getCurrentUser() == null) {
return false;
}
try {
- DiscoverResult discoverResult = getDiscoverResult(context, query, null, null, null, null);
+ DiscoverResult discoverResult = getDiscoverResult(context, query, Constants.ADMIN, inheritAuthorizations,
+ null, 0, null, null);
if (discoverResult.getTotalSearchResults() > 0) {
return true;
}
@@ -911,16 +977,11 @@ private boolean performCheck(Context context, String query) throws SQLException
return false;
}
- private DiscoverResult getDiscoverResult(Context context, String query, Integer offset, Integer limit,
- String sortField, SORT_ORDER sortOrder)
- throws SearchServiceException, SQLException {
- String groupQuery = getGroupToQuery(groupService.allMemberGroups(context, context.getCurrentUser()));
+ private DiscoverResult getDiscoverResult(Context context, String query, int action, boolean inheritAuthorizations,
+ Integer offset, Integer limit, String sortField, SORT_ORDER sortOrder)
+ throws SearchServiceException {
DiscoverQuery discoverQuery = new DiscoverQuery();
- if (!this.isAdmin(context)) {
- query = query + " AND (" +
- "admin:e" + context.getCurrentUser().getID() + groupQuery + ")";
- }
discoverQuery.setQuery(query);
if (offset != null) {
discoverQuery.setStart(offset);
@@ -931,23 +992,12 @@ private DiscoverResult getDiscoverResult(Context context, String query, Integer
if (sortField != null && sortOrder != null) {
discoverQuery.setSortField(sortField, sortOrder);
}
+ discoverQuery.addRequiredAuthorization(action);
+ discoverQuery.setInheritAuthorizations(inheritAuthorizations);
return searchService.search(context, discoverQuery);
}
- private String getGroupToQuery(List groups) {
- StringBuilder groupQuery = new StringBuilder();
-
- if (groups != null) {
- for (Group group: groups) {
- groupQuery.append(" OR admin:g");
- groupQuery.append(group.getID());
- }
- }
-
- return groupQuery.toString();
- }
-
private String formatCustomQuery(String query) {
if (StringUtils.isBlank(query)) {
return "";
diff --git a/dspace-api/src/main/java/org/dspace/authorize/dao/impl/ResourcePolicyDAOImpl.java b/dspace-api/src/main/java/org/dspace/authorize/dao/impl/ResourcePolicyDAOImpl.java
index e4f3a0c057b..75d0e701b4a 100644
--- a/dspace-api/src/main/java/org/dspace/authorize/dao/impl/ResourcePolicyDAOImpl.java
+++ b/dspace-api/src/main/java/org/dspace/authorize/dao/impl/ResourcePolicyDAOImpl.java
@@ -165,7 +165,7 @@ public List findByEPersonGroupTypeIdAction(Context context, EPer
(resourcePolicyRoot.get(ResourcePolicy_.epersonGroup).in(groups)))
)
);
- return list(context, criteriaQuery, false, ResourcePolicy.class, 1, -1);
+ return list(context, criteriaQuery, false, ResourcePolicy.class, -1, -1);
}
@Override
diff --git a/dspace-api/src/main/java/org/dspace/authorize/service/AuthorizeService.java b/dspace-api/src/main/java/org/dspace/authorize/service/AuthorizeService.java
index e0a94833d76..12fae93c3e5 100644
--- a/dspace-api/src/main/java/org/dspace/authorize/service/AuthorizeService.java
+++ b/dspace-api/src/main/java/org/dspace/authorize/service/AuthorizeService.java
@@ -546,6 +546,20 @@ void switchPoliciesAction(Context context, DSpaceObject dso, int fromAction, int
List findAdminAuthorizedCommunity(Context context, String query, int offset, int limit)
throws SearchServiceException, SQLException;
+ /**
+ * Finds communities for which the logged in user has the rights specified by the action parameter.
+ *
+ * @param context the context whose user is checked against
+ * @param query the optional extra query
+ * @param action the action to check for
+ * @param offset the offset for pagination
+ * @param limit the amount of dso's to return
+ * @return a list of communities for which the logged in user has the rights specified by the action
+ * @throws SearchServiceException
+ */
+ List findAuthorizedCommunityByAction(Context context, String query, int action, int offset, int limit)
+ throws SearchServiceException, SQLException;
+
/**
* Counts communities for which the current user is admin, AND which match the query.
*
@@ -558,6 +572,18 @@ List findAdminAuthorizedCommunity(Context context, String query, int
long countAdminAuthorizedCommunity(Context context, String query)
throws SearchServiceException, SQLException;
+ /**
+ * Counts communities for which the current user has the rights specified by the action parameter.
+ *
+ * @param context context with the current user
+ * @param query the query for which to filter the results more
+ * @param action the action to check for
+ * @return the matching communities
+ * @throws SearchServiceException
+ */
+ long countAuthorizedCommunityByAction(Context context, String query, int action)
+ throws SearchServiceException;
+
/**
* Finds collections for which the current user is admin, AND which match the query.
*
@@ -567,10 +593,24 @@ long countAdminAuthorizedCommunity(Context context, String query)
* @param limit used for pagination of the results
* @return the matching collections
* @throws SearchServiceException
- * @throws SQLException
*/
List findAdminAuthorizedCollection(Context context, String query, int offset, int limit)
- throws SearchServiceException, SQLException;
+ throws SearchServiceException;
+
+ /**
+ * Finds collections for which the current user has the rights specified by the action parameter.
+ *
+ * @param context context with the current user
+ * @param query the query for which to filter the results more
+ * @param action the action to check for
+ * @param offset used for pagination of the results
+ * @param limit used for pagination of the results
+ * @return the matching collections
+ * @throws SearchServiceException
+ */
+ List findAuthorizedCollectionByAction(Context context, String query, int action, int offset,
+ int limit)
+ throws SearchServiceException;
/**
* Counts collections for which the current user is admin, AND which match the query.
@@ -582,7 +622,19 @@ List findAdminAuthorizedCollection(Context context, String query, in
* @throws SQLException
*/
long countAdminAuthorizedCollection(Context context, String query)
- throws SearchServiceException, SQLException;
+ throws SearchServiceException;
+
+ /**
+ * Counts collections for which the current user has the rights specified by the action parameter.
+ *
+ * @param context context with the current user
+ * @param query the query for which to filter the results more
+ * @param action the action to check for
+ * @return the number of matching collections
+ * @throws SearchServiceException
+ */
+ long countAuthorizedCollectionByAction(Context context, String query, int action)
+ throws SearchServiceException;
/**
* Returns true if the current user can manage accounts.
diff --git a/dspace-api/src/main/java/org/dspace/browse/BrowseDAO.java b/dspace-api/src/main/java/org/dspace/browse/BrowseDAO.java
index 4a00922cc5c..26983303a15 100644
--- a/dspace-api/src/main/java/org/dspace/browse/BrowseDAO.java
+++ b/dspace-api/src/main/java/org/dspace/browse/BrowseDAO.java
@@ -396,4 +396,6 @@ public interface BrowseDAO {
public void setStartsWith(String startsWith);
public String getStartsWith();
-}
\ No newline at end of file
+
+ public void setDateStartsWith(String dateStartsWith);
+}
diff --git a/dspace-api/src/main/java/org/dspace/browse/BrowseEngine.java b/dspace-api/src/main/java/org/dspace/browse/BrowseEngine.java
index be7a34086a4..f7f5f2ff7df 100644
--- a/dspace-api/src/main/java/org/dspace/browse/BrowseEngine.java
+++ b/dspace-api/src/main/java/org/dspace/browse/BrowseEngine.java
@@ -203,12 +203,8 @@ private BrowseInfo browseByItem(BrowserScope bs)
// get the table name that we are going to be getting our data from
dao.setTable(browseIndex.getTableName());
- if (scope.getBrowseIndex() != null && OrderFormat.TITLE.equals(scope.getBrowseIndex().getDataType())) {
- // For browsing by title, apply the same normalization applied to indexed titles
- dao.setStartsWith(normalizeJumpToValue(scope.getStartsWith()));
- } else {
- dao.setStartsWith(StringUtils.lowerCase(scope.getStartsWith()));
- }
+ // Set startsWith or dateStartsWith params on SolrBrowseDAO
+ addStartsWithParams(bs);
// tell the browse query whether we are ascending or descending on the value
dao.setAscending(scope.isAscending());
@@ -367,6 +363,30 @@ private BrowseInfo browseByItem(BrowserScope bs)
}
}
+ private void addStartsWithParams(BrowserScope bs) throws BrowseException {
+ if (StringUtils.isNotBlank(scope.getStartsWith())) {
+ boolean isDateBrowse = bs.getSortOption().getType().equals("date");
+ if (!isDateBrowse) {
+ if (scope.getBrowseIndex() != null
+ && OrderFormat.TITLE.equals(scope.getBrowseIndex().getDataType())) {
+ // For browsing by title, apply the same normalization applied to indexed titles
+ dao.setStartsWith(normalizeJumpToValue(scope.getStartsWith()));
+ } else {
+ dao.setStartsWith(StringUtils.lowerCase(scope.getStartsWith()));
+ }
+ // clear the old date starts with
+ dao.setDateStartsWith(null);
+ } else {
+ // For "date" sort browses ({@code webui.itemlist.sort-option.*} config):
+ // sets a date specific filter where the startsWith query is the start date,
+ // eg `fq=bi_sort_*_sort:+["1940-02" TO + ]`
+ dao.setDateStartsWith(scope.getStartsWith().trim());
+ // clear the old non date starts with
+ dao.setStartsWith(null);
+ }
+ }
+ }
+
/**
* Browse the archive by single values (such as the name of an author). This
* produces a BrowseInfo object that contains Strings as the results of
diff --git a/dspace-api/src/main/java/org/dspace/browse/ItemCountDAOSolr.java b/dspace-api/src/main/java/org/dspace/browse/ItemCountDAOSolr.java
index e4d0079fe20..723b49d63ae 100644
--- a/dspace-api/src/main/java/org/dspace/browse/ItemCountDAOSolr.java
+++ b/dspace-api/src/main/java/org/dspace/browse/ItemCountDAOSolr.java
@@ -7,121 +7,62 @@
*/
package org.dspace.browse;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
+import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
+import org.apache.solr.client.solrj.SolrClient;
+import org.apache.solr.client.solrj.SolrQuery;
+import org.apache.solr.client.solrj.response.QueryResponse;
import org.dspace.content.Collection;
import org.dspace.content.Community;
import org.dspace.content.DSpaceObject;
import org.dspace.core.Context;
-import org.dspace.discovery.DiscoverFacetField;
-import org.dspace.discovery.DiscoverQuery;
-import org.dspace.discovery.DiscoverResult;
-import org.dspace.discovery.DiscoverResult.FacetResult;
-import org.dspace.discovery.SearchService;
-import org.dspace.discovery.SearchServiceException;
-import org.dspace.discovery.configuration.DiscoveryConfigurationParameters;
+import org.dspace.discovery.SolrSearchCore;
import org.dspace.discovery.indexobject.IndexableItem;
import org.springframework.beans.factory.annotation.Autowired;
/**
* Discovery (Solr) driver implementing ItemCountDAO interface to look up item
- * count information in communities and collections. Caching operations are
- * intentionally not implemented because Solr already is our cache.
+ * count information in communities and collections.
+ *
+ * Counts are computed by querying Solr for archived, non-withdrawn, discoverable
+ * items using {@code location.comm} / {@code location.coll} filters.
+ * The query returns only {@code numFound} (rows=0), making it very fast.
*/
public class ItemCountDAOSolr implements ItemCountDAO {
- /**
- * Log4j logger
- */
- private static Logger log = org.apache.logging.log4j.LogManager.getLogger(ItemCountDAOSolr.class);
-
- /**
- * Hold the communities item count obtained from SOLR after the first query. This only works
- * well if the ItemCountDAO lifecycle is bound to the request lifecycle as
- * it is now. If we switch to a Spring-based instantiation we should mark
- * this bean as prototype
- **/
- private Map communitiesCount = null;
-
- /**
- * Hold the collection item count obtained from SOLR after the first query
- **/
- private Map collectionsCount = null;
+ private static final Logger log = LogManager.getLogger(ItemCountDAOSolr.class);
- /**
- * Solr search service
- */
@Autowired
- protected SearchService searchService;
+ private SolrSearchCore solrSearchCore;
- /**
- * Get the count of the items in the given container.
- *
- * @param context DSpace context
- * @param dso DspaceObject
- * @return count
- */
@Override
public int getCount(Context context, DSpaceObject dso) {
- loadCount(context);
- Integer val = null;
+ String locationFilter;
if (dso instanceof Collection) {
- val = collectionsCount.get(dso.getID().toString());
+ locationFilter = "location.coll:" + dso.getID().toString();
} else if (dso instanceof Community) {
- val = communitiesCount.get(dso.getID().toString());
- }
-
- if (val != null) {
- return val;
+ locationFilter = "location.comm:" + dso.getID().toString();
} else {
return 0;
}
- }
- /**
- * make sure that the counts are actually fetched from Solr (if haven't been
- * cached in a Map yet)
- *
- * @param context DSpace Context
- */
- private void loadCount(Context context) {
- if (communitiesCount != null || collectionsCount != null) {
- return;
- }
-
- communitiesCount = new HashMap<>();
- collectionsCount = new HashMap<>();
-
- DiscoverQuery query = new DiscoverQuery();
- query.setFacetMinCount(1);
- query.addFacetField(new DiscoverFacetField("location.comm",
- DiscoveryConfigurationParameters.TYPE_STANDARD, -1,
- DiscoveryConfigurationParameters.SORT.COUNT));
- query.addFacetField(new DiscoverFacetField("location.coll",
- DiscoveryConfigurationParameters.TYPE_STANDARD, -1,
- DiscoveryConfigurationParameters.SORT.COUNT));
- query.addFilterQueries("search.resourcetype:" + IndexableItem.TYPE); // count only items
- query.addFilterQueries("NOT(discoverable:false)"); // only discoverable
- query.addFilterQueries("withdrawn:false"); // only not withdrawn
- query.addFilterQueries("archived:true"); // only archived
- query.setMaxResults(0);
-
- DiscoverResult sResponse;
try {
- sResponse = searchService.search(context, query);
- List commCount = sResponse.getFacetResult("location.comm");
- List collCount = sResponse.getFacetResult("location.coll");
- for (FacetResult c : commCount) {
- communitiesCount.put(c.getAsFilterQuery(), (int) c.getCount());
- }
- for (FacetResult c : collCount) {
- collectionsCount.put(c.getAsFilterQuery(), (int) c.getCount());
+ SolrClient solr = solrSearchCore.getSolr();
+ if (solr == null) {
+ return 0;
}
- } catch (SearchServiceException e) {
- log.error("Could not initialize Community/Collection Item Counts from Solr: ", e);
+ SolrQuery query = new SolrQuery("*:*");
+ query.addFilterQuery(locationFilter);
+ query.addFilterQuery("search.resourcetype:" + IndexableItem.TYPE);
+ query.addFilterQuery("NOT(discoverable:false)");
+ query.addFilterQuery("withdrawn:false");
+ query.addFilterQuery("archived:true");
+ query.setRows(0);
+ QueryResponse response = solr.query(query, solrSearchCore.REQUEST_METHOD);
+ return (int) response.getResults().getNumFound();
+ } catch (Exception e) {
+ log.error("Error counting items in Solr for {}: ", dso.getID(), e);
}
+ return 0;
}
}
diff --git a/dspace-api/src/main/java/org/dspace/browse/SolrBrowseDAO.java b/dspace-api/src/main/java/org/dspace/browse/SolrBrowseDAO.java
index a0a7725fa13..32841e6c4b3 100644
--- a/dspace-api/src/main/java/org/dspace/browse/SolrBrowseDAO.java
+++ b/dspace-api/src/main/java/org/dspace/browse/SolrBrowseDAO.java
@@ -11,6 +11,7 @@
import static org.dspace.discovery.SearchUtils.RESOURCE_TYPE_FIELD;
import java.io.Serializable;
+import java.time.YearMonth;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
@@ -99,6 +100,8 @@ public int compare(Object o1, Object o2) {
private String startsWith = null;
+ private String dateStartsWith = null;
+
/**
* field to look for value in
*/
@@ -221,10 +224,33 @@ private DiscoverResult getSolrResponse() throws BrowseException {
} else if (valuePartial) {
query.addFilterQueries("{!field f=" + facetField + "_partial}" + value);
}
+
if (StringUtils.isNotBlank(startsWith) && orderField != null) {
query.addFilterQueries(
"bi_" + orderField + "_sort:" + ClientUtils.escapeQueryChars(startsWith) + "*");
}
+ if (StringUtils.isNotBlank(dateStartsWith)) {
+ if (!ascending) {
+ String raw = dateStartsWith.trim();
+ String upperBound;
+ if (raw.length() == 4) { // YYYY
+ upperBound = raw + "-12-31";
+ } else if (raw.length() == 7) { // YYYY-MM
+ YearMonth ym = YearMonth.parse(raw);
+ upperBound = ym.atEndOfMonth().toString();
+ } else { // YYYY-MM-DD
+ upperBound = raw;
+ }
+ query.addFilterQueries("bi_" + orderField + "_sort" + ": [* TO \"" + upperBound + "\"]");
+ } else {
+ query.addFilterQueries("bi_" + orderField + "_sort" + ": [\"" + dateStartsWith + "\" TO *]");
+ }
+ }
+ if (StringUtils.isNotBlank(startsWith) && StringUtils.isNotBlank(dateStartsWith)) {
+ log.warn(String.format("dateStartsWith %s and startsWith %s both given, only one should " +
+ "be given since different type of sort filterquery applied depending on which is not blank",
+ dateStartsWith, startsWith));
+ }
// filter on item to be sure to don't include any other object
// indexed in the Discovery Search core
query.addFilterQueries("search.resourcetype:" + IndexableItem.TYPE);
@@ -466,6 +492,11 @@ public int getLimit() {
return limit;
}
+ @Override
+ public void setDateStartsWith(String dateStartsWith) {
+ this.dateStartsWith = dateStartsWith;
+ }
+
/*
* (non-Javadoc)
*
diff --git a/dspace-api/src/main/java/org/dspace/checker/CheckerCommand.java b/dspace-api/src/main/java/org/dspace/checker/CheckerCommand.java
index ba503d83eb4..90e833c9f71 100644
--- a/dspace-api/src/main/java/org/dspace/checker/CheckerCommand.java
+++ b/dspace-api/src/main/java/org/dspace/checker/CheckerCommand.java
@@ -134,7 +134,7 @@ public void process() throws SQLException {
collector.collect(context, info);
}
- context.uncacheEntity(bitstream);
+ context.commit();
bitstream = dispatcher.next();
}
}
diff --git a/dspace-api/src/main/java/org/dspace/checker/DailyReportEmailer.java b/dspace-api/src/main/java/org/dspace/checker/DailyReportEmailer.java
index b291232e8b5..82c01bf0b9c 100644
--- a/dspace-api/src/main/java/org/dspace/checker/DailyReportEmailer.java
+++ b/dspace-api/src/main/java/org/dspace/checker/DailyReportEmailer.java
@@ -75,6 +75,7 @@ public void sendReport(File attachment, int numberOfBitstreams)
email.setContent("Checker Report", "report is attached ...");
email.addAttachment(attachment, "checksum_checker_report.txt");
email.addRecipient(configurationService.getProperty("mail.admin"));
+ log.info("Sending checker report email to " + configurationService.getProperty("mail.admin"));
email.send();
}
}
@@ -109,18 +110,19 @@ public static void main(String[] args) {
Options options = new Options();
options.addOption("h", "help", false, "Help");
- options.addOption("d", "Deleted", false,
- "Send E-mail report for all bitstreams set as deleted for today");
- options.addOption("m", "Missing", false,
- "Send E-mail report for all bitstreams not found in assetstore for today");
- options.addOption("c", "Changed", false,
- "Send E-mail report for all bitstreams where checksum has been changed for today");
- options.addOption("a", "All", false,
- "Send all E-mail reports");
- options.addOption("u", "Unchecked", false,
- "Send the Unchecked bitstream report");
- options.addOption("n", "Not Processed", false,
- "Send E-mail report for all bitstreams set to longer be processed for today");
+ options.addOption("d", "deleted", false,
+ "Send email report for all bitstreams set as deleted for today");
+ options.addOption("m", "missing", false,
+ "Send email report for all bitstreams not found in assetstore for today");
+ options.addOption("c", "changed", false,
+ "Send email report for all bitstreams where checksum has been changed for today");
+ options.addOption("a", "all", false,
+ "Send all email reports (used by default)");
+ options.addOption("u", "unchecked", false,
+ "Send the unchecked (i.e. recently added) bitstream email report");
+ options.addOption("n", "not-processed", false,
+ "Send email report for all bitstreams set to no longer be processed for today (includes"
+ + " bitstreams marked as deleted or not found)");
try {
line = parser.parse(options, args);
@@ -133,13 +135,15 @@ public static void main(String[] args) {
if (line.hasOption('h')) {
HelpFormatter myhelp = new HelpFormatter();
- myhelp.printHelp("Checksum Reporter\n", options);
- System.out.println("\nSend Deleted bitstream email report: DailyReportEmailer -d");
- System.out.println("\nSend Missing bitstreams email report: DailyReportEmailer -m");
- System.out.println("\nSend Checksum Changed email report: DailyReportEmailer -c");
- System.out.println("\nSend bitstream not to be processed email report: DailyReportEmailer -n");
- System.out.println("\nSend Un-checked bitstream report: DailyReportEmailer -u");
- System.out.println("\nSend All email reports: DailyReportEmailer");
+ myhelp.printHelp("checker-emailer\n", options);
+ System.out.println("\nChecksum Checker Reporter usage examples:\n");
+ System.out.println(" - Send all email reports: checker-emailer -a");
+ System.out.println(" - Send deleted bitstream email report: checker-emailer -d");
+ System.out.println(" - Send missing bitstreams email report: checker-emailer -m");
+ System.out.println(" - Send checksum changed email report: checker-emailer -c");
+ System.out.println(" - Send bitstream not to be processed email report: checker-emailer -n");
+ System.out.println(" - Send unchecked bitstream email report: checker-emailer -u");
+ System.out.println("\nDefault (no arguments) is equivalent to 'checker-emailer -a'\n");
System.exit(0);
}
@@ -191,7 +195,9 @@ public static void main(String[] args) {
writer.write("\n--------------------------------- Report Spacer ---------------------------\n\n");
numBitstreams += reporter.getBitstreamNotFoundReport(context, yesterday, tomorrow, writer);
writer.write("\n--------------------------------- Report Spacer ---------------------------\n\n");
- numBitstreams += reporter.getNotToBeProcessedReport(context, yesterday, tomorrow, writer);
+ // not to be processed report includes deleted and not found bitstreams so it is not necessary to
+ // include the sum in the counter
+ reporter.getNotToBeProcessedReport(context, yesterday, tomorrow, writer);
writer.write("\n--------------------------------- Report Spacer ---------------------------\n\n");
numBitstreams += reporter.getUncheckedBitstreamsReport(context, writer);
writer.write("\n--------------------------------- End Report ---------------------------\n\n");
diff --git a/dspace-api/src/main/java/org/dspace/checker/MostRecentChecksumServiceImpl.java b/dspace-api/src/main/java/org/dspace/checker/MostRecentChecksumServiceImpl.java
index d267171aa0d..9ee777a3e15 100644
--- a/dspace-api/src/main/java/org/dspace/checker/MostRecentChecksumServiceImpl.java
+++ b/dspace-api/src/main/java/org/dspace/checker/MostRecentChecksumServiceImpl.java
@@ -90,61 +90,16 @@ public List findBitstreamResultTypeReport(Context context, D
/**
* Queries the bitstream table for bitstream IDs that are not yet in the
* most_recent_checksum table, and inserts them into the
- * most_recent_checksum and checksum_history tables.
- *
+ * most_recent_checksum table.
* @param context Context
* @throws SQLException if database error
*/
@Override
public void updateMissingBitstreams(Context context) throws SQLException {
-// "insert into most_recent_checksum ( "
-// + "bitstream_id, to_be_processed, expected_checksum, current_checksum, "
-// + "last_process_start_date, last_process_end_date, "
-// + "checksum_algorithm, matched_prev_checksum, result ) "
-// + "select bitstream.bitstream_id, "
-// + "CASE WHEN bitstream.deleted = false THEN true ELSE false END, "
-// + "CASE WHEN bitstream.checksum IS NULL THEN '' ELSE bitstream.checksum END, "
-// + "CASE WHEN bitstream.checksum IS NULL THEN '' ELSE bitstream.checksum END, "
-// + "?, ?, CASE WHEN bitstream.checksum_algorithm IS NULL "
-// + "THEN 'MD5' ELSE bitstream.checksum_algorithm END, true, "
-// + "CASE WHEN bitstream.deleted = true THEN 'BITSTREAM_MARKED_DELETED' else 'CHECKSUM_MATCH' END "
-// + "from bitstream where not exists( "
-// + "select 'x' from most_recent_checksum "
-// + "where most_recent_checksum.bitstream_id = bitstream.bitstream_id )";
-
- List unknownBitstreams = bitstreamService.findBitstreamsWithNoRecentChecksum(context);
- for (Bitstream bitstream : unknownBitstreams) {
- log.info(bitstream + " " + bitstream.getID().toString() + " " + bitstream.getName());
-
- MostRecentChecksum mostRecentChecksum = new MostRecentChecksum();
- mostRecentChecksum.setBitstream(bitstream);
- //Only process if our bitstream isn't deleted
- mostRecentChecksum.setToBeProcessed(!bitstream.isDeleted());
- if (bitstream.getChecksum() == null) {
- mostRecentChecksum.setCurrentChecksum("");
- mostRecentChecksum.setExpectedChecksum("");
- } else {
- mostRecentChecksum.setCurrentChecksum(bitstream.getChecksum());
- mostRecentChecksum.setExpectedChecksum(bitstream.getChecksum());
- }
- mostRecentChecksum.setProcessStartDate(new Date());
- mostRecentChecksum.setProcessEndDate(new Date());
- if (bitstream.getChecksumAlgorithm() == null) {
- mostRecentChecksum.setChecksumAlgorithm("MD5");
- } else {
- mostRecentChecksum.setChecksumAlgorithm(bitstream.getChecksumAlgorithm());
- }
- mostRecentChecksum.setMatchedPrevChecksum(true);
- ChecksumResult checksumResult;
- if (bitstream.isDeleted()) {
- checksumResult = checksumResultService.findByCode(context, ChecksumResultCode.BITSTREAM_MARKED_DELETED);
- } else {
- checksumResult = checksumResultService.findByCode(context, ChecksumResultCode.CHECKSUM_MATCH);
- }
- mostRecentChecksum.setChecksumResult(checksumResult);
- mostRecentChecksumDAO.create(context, mostRecentChecksum);
- mostRecentChecksumDAO.save(context, mostRecentChecksum);
- }
+ log.info("Retrieving missing bitsreams (bitstream IDs that are not yet in most_recent_checksum table)...");
+ int updated = mostRecentChecksumDAO.updateMissingBitstreams(context);
+ log.info("Updated most_recent_checksum for " + updated + " bitstreams.");
+ log.info("Missing bitsreams processing done.");
}
@Override
diff --git a/dspace-api/src/main/java/org/dspace/checker/SimpleReporterServiceImpl.java b/dspace-api/src/main/java/org/dspace/checker/SimpleReporterServiceImpl.java
index ddefb28e1b5..6c69764fdc7 100644
--- a/dspace-api/src/main/java/org/dspace/checker/SimpleReporterServiceImpl.java
+++ b/dspace-api/src/main/java/org/dspace/checker/SimpleReporterServiceImpl.java
@@ -70,6 +70,7 @@ public int getDeletedBitstreamReport(Context context, Date startDate, Date endDa
osw.write("\n");
osw.write(msg("deleted-bitstream-intro"));
+ osw.write(" ");
osw.write(applyDateFormatShort(startDate));
osw.write(" ");
osw.write(msg("date-range-to"));
@@ -111,7 +112,6 @@ public int getChangedChecksumReport(Context context, Date startDate, Date endDat
osw.write("\n");
osw.write(msg("checksum-did-not-match"));
osw.write(" ");
- osw.write("\n");
osw.write(applyDateFormatShort(startDate));
osw.write(" ");
osw.write(msg("date-range-to"));
diff --git a/dspace-api/src/main/java/org/dspace/checker/dao/MostRecentChecksumDAO.java b/dspace-api/src/main/java/org/dspace/checker/dao/MostRecentChecksumDAO.java
index 56485c9b4b4..73a81e4d9b4 100644
--- a/dspace-api/src/main/java/org/dspace/checker/dao/MostRecentChecksumDAO.java
+++ b/dspace-api/src/main/java/org/dspace/checker/dao/MostRecentChecksumDAO.java
@@ -33,6 +33,8 @@ public List findByNotProcessedInDateRange(Context context, D
public List findByResultTypeInDateRange(Context context, Date startDate, Date endDate,
ChecksumResultCode resultCode) throws SQLException;
+ public int updateMissingBitstreams(Context context) throws SQLException;
+
public void deleteByBitstream(Context context, Bitstream bitstream) throws SQLException;
public MostRecentChecksum getOldestRecord(Context context) throws SQLException;
diff --git a/dspace-api/src/main/java/org/dspace/checker/dao/impl/MostRecentChecksumDAOImpl.java b/dspace-api/src/main/java/org/dspace/checker/dao/impl/MostRecentChecksumDAOImpl.java
index a31e02cbab4..6ecd94f67f9 100644
--- a/dspace-api/src/main/java/org/dspace/checker/dao/impl/MostRecentChecksumDAOImpl.java
+++ b/dspace-api/src/main/java/org/dspace/checker/dao/impl/MostRecentChecksumDAOImpl.java
@@ -56,8 +56,8 @@ public List findByNotProcessedInDateRange(Context context, D
criteriaQuery.where(criteriaBuilder.and(
criteriaBuilder.equal(mostRecentChecksumRoot.get(MostRecentChecksum_.toBeProcessed), false),
criteriaBuilder
- .lessThanOrEqualTo(mostRecentChecksumRoot.get(MostRecentChecksum_.processStartDate), startDate),
- criteriaBuilder.greaterThan(mostRecentChecksumRoot.get(MostRecentChecksum_.processStartDate), endDate)
+ .lessThanOrEqualTo(mostRecentChecksumRoot.get(MostRecentChecksum_.processStartDate), endDate),
+ criteriaBuilder.greaterThan(mostRecentChecksumRoot.get(MostRecentChecksum_.processStartDate), startDate)
)
);
List orderList = new LinkedList<>();
@@ -66,6 +66,24 @@ public List findByNotProcessedInDateRange(Context context, D
return list(context, criteriaQuery, false, MostRecentChecksum.class, -1, -1);
}
+ @Override
+ public int updateMissingBitstreams(Context context) throws SQLException {
+ String hql = "INSERT INTO MostRecentChecksum(bitstream, toBeProcessed, expectedChecksum, currentChecksum, " +
+ "processStartDate, processEndDate, checksumAlgorithm, matchedPrevChecksum, checksumResult) " +
+ "SELECT b, " +
+ "CASE WHEN deleted = false THEN true ELSE false END, " +
+ "CASE WHEN checksum IS NULL THEN '' ELSE checksum END, " +
+ "CASE WHEN checksum IS NULL THEN '' ELSE checksum END, " +
+ "current_timestamp(), current_timestamp(), " +
+ "CASE WHEN checksumAlgorithm IS NULL THEN 'MD5' ELSE checksumAlgorithm END, " +
+ "CAST(1 AS boolean), " +
+ "(SELECT cr FROM ChecksumResult AS cr WHERE " +
+ "(resultCode = 'BITSTREAM_MARKED_DELETED' AND b.deleted = true) " +
+ "OR (resultCode = 'CHECKSUM_MATCH' AND b.deleted = false)) " +
+ "FROM Bitstream AS b WHERE NOT EXISTS(SELECT 'x' FROM MostRecentChecksum AS c WHERE c.bitstream = b)";
+ Query query = createQuery(context, hql);
+ return query.executeUpdate();
+ }
@Override
public MostRecentChecksum findByBitstream(Context context, Bitstream bitstream) throws SQLException {
diff --git a/dspace-api/src/main/java/org/dspace/checker/service/SimpleReporterService.java b/dspace-api/src/main/java/org/dspace/checker/service/SimpleReporterService.java
index 1dc56c20a3d..f3e0b43d889 100644
--- a/dspace-api/src/main/java/org/dspace/checker/service/SimpleReporterService.java
+++ b/dspace-api/src/main/java/org/dspace/checker/service/SimpleReporterService.java
@@ -72,7 +72,8 @@ public int getBitstreamNotFoundReport(Context context, Date startDate, Date endD
/**
* The bitstreams that were set to not be processed report for the specified
- * date range.
+ * date range. This includes bitstreams that are marked as deleted and bitstreams
+ * that are not found from the assetstore.
*
* @param context context
* @param startDate the start date range.
diff --git a/dspace-api/src/main/java/org/dspace/content/BitstreamServiceImpl.java b/dspace-api/src/main/java/org/dspace/content/BitstreamServiceImpl.java
index a29b6285374..6b5ee61a98f 100644
--- a/dspace-api/src/main/java/org/dspace/content/BitstreamServiceImpl.java
+++ b/dspace-api/src/main/java/org/dspace/content/BitstreamServiceImpl.java
@@ -20,6 +20,8 @@
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.Logger;
+import org.dspace.app.requestitem.RequestItem;
+import org.dspace.app.requestitem.service.RequestItemService;
import org.dspace.authorize.AuthorizeException;
import org.dspace.authorize.service.AuthorizeService;
import org.dspace.content.dao.BitstreamDAO;
@@ -70,6 +72,8 @@ public class BitstreamServiceImpl extends DSpaceObjectServiceImpl imp
protected ClarinLicenseResourceMappingService clarinLicenseResourceMappingService;
@Autowired(required = true)
protected ClarinItemService clarinItemService;
+ @Autowired(required = true)
+ protected RequestItemService requestItemService;
protected BitstreamServiceImpl() {
super();
@@ -295,6 +299,13 @@ public void delete(Context context, Bitstream bitstream) throws SQLException, Au
// Remove all bundles from the bitstream object, clearing the connection in 2 ways
bundles.clear();
+ // Remove any RequestItem entities associated with this bitstream ensuring there are no requests referencing
+ // a deleted bitstream
+ Iterator requestItems = requestItemService.findByBitstreamId(context, bitstream.getID());
+ while (requestItems.hasNext()) {
+ requestItemService.delete(context, requestItems.next());
+ }
+
// Remove policies only after the bitstream has been updated (otherwise the current user has not WRITE rights)
authorizeService.removeAllPolicies(context, bitstream);
diff --git a/dspace-api/src/main/java/org/dspace/content/BundleServiceImpl.java b/dspace-api/src/main/java/org/dspace/content/BundleServiceImpl.java
index 38a322baeff..42e0a2cef72 100644
--- a/dspace-api/src/main/java/org/dspace/content/BundleServiceImpl.java
+++ b/dspace-api/src/main/java/org/dspace/content/BundleServiceImpl.java
@@ -597,4 +597,8 @@ public Bundle findByLegacyId(Context context, int id) throws SQLException {
public int countTotal(Context context) throws SQLException {
return bundleDAO.countRows(context);
}
+
+ public int countBitstreams(Context context, Bundle bundle) throws SQLException {
+ return bundleDAO.countBitstreams(context, bundle);
+ }
}
diff --git a/dspace-api/src/main/java/org/dspace/content/CollectionServiceImpl.java b/dspace-api/src/main/java/org/dspace/content/CollectionServiceImpl.java
index 32d7b2f8bc2..4f45570bf7c 100644
--- a/dspace-api/src/main/java/org/dspace/content/CollectionServiceImpl.java
+++ b/dspace-api/src/main/java/org/dspace/content/CollectionServiceImpl.java
@@ -13,18 +13,21 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
+import java.util.HashSet;
import java.util.Iterator;
+import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.MissingResourceException;
import java.util.Objects;
+import java.util.Queue;
import java.util.Set;
import java.util.UUID;
+import java.util.stream.Collectors;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.Logger;
-import org.apache.solr.client.solrj.util.ClientUtils;
import org.dspace.app.util.AuthorizeUtil;
import org.dspace.authorize.AuthorizeConfiguration;
import org.dspace.authorize.AuthorizeException;
@@ -846,6 +849,86 @@ public List findAuthorized(Context context, Community community, int
return myResults;
}
+ @Override
+ public List findAuthorized(Context context, Community community, List actions)
+ throws SQLException {
+
+ List myCollections = new ArrayList<>();
+ EPerson eperson = context.getCurrentUser();
+
+ //If eperson is Administrator return all colls or if a community is not null only the community's collections
+ if (authorizeService.isAdmin(context, eperson)) {
+ if (community != null) {
+ return community.getCollections();
+ }
+ myCollections = this.findAll(context);
+ return myCollections;
+ }
+
+ //Get the collections of the eperson where is is admin of a community
+ List directGroups = new ArrayList<>(eperson.getGroups()); // direct membership
+ Queue queue = new LinkedList<>(directGroups);
+ while (!queue.isEmpty()) {
+ Group current = queue.poll();
+ List parents = current.getParentGroups();
+
+ for (Group parent : parents) {
+ if (directGroups.add(parent)) {
+ queue.add(parent);
+ }
+ }
+ }
+
+ List resourcePolicies = resourcePolicyService
+ .find(context, eperson, directGroups, Constants.ADMIN, Constants.COMMUNITY);
+ List uuids = resourcePolicies.stream()
+ .map(policy -> policy.getdSpaceObject().getID())
+ .collect(Collectors.toList());
+
+ List communities = uuids.stream()
+ .map(uuid -> {
+ try {
+ return communityService.find(context, uuid);
+ } catch (SQLException e) {
+ return null; //ignore that uuid
+ }
+ })
+ .filter(Objects::nonNull)
+ .collect(Collectors.toList());
+
+ Set allCommunities = new HashSet<>(communities);
+ Set allCommAdminCollections = communities.stream()
+ .flatMap(cm -> cm.getCollections().stream())
+ .collect(Collectors.toSet());
+ Queue queueComm = new LinkedList<>(communities);
+
+ while (!queueComm.isEmpty()) {
+ Community com = queueComm.poll();
+ List childrenComms = com.getSubcommunities();
+ for (Community childComm : childrenComms) {
+ if (allCommunities.add(childComm)) {
+ queueComm.add(childComm);
+ allCommAdminCollections.addAll(childComm.getCollections());
+ }
+ }
+ }
+
+ //Now get the collection when the eperson can deposit or is admin or is in a group with those privileges
+ myCollections = collectionDAO.findAuthorizedByEPerson(context, eperson, actions);
+ Set allCollections = new HashSet<>(myCollections);
+ //Join EPerson Community Admin Collections with Collection Admins
+ allCollections.addAll(allCommAdminCollections);
+
+ List collsAllowed = new ArrayList<>(allCollections);
+
+ //A community is passed, only the community's collections will be used and existing in eperson Authorizations
+ if (community != null) {
+ collsAllowed.retainAll(community.getCollections());
+ }
+
+ return collsAllowed;
+ }
+
@Override
public Collection findByGroup(Context context, Group group) throws SQLException {
return collectionDAO.findByGroup(context, group);
@@ -957,7 +1040,7 @@ public String getDefaultReadGroupName(Collection collection, String typeOfGroupS
@Override
public List findCollectionsWithSubmit(String q, Context context, Community community,
- int offset, int limit) throws SQLException, SearchServiceException {
+ int offset, int limit) throws SearchServiceException {
List collections = new ArrayList<>();
DiscoverQuery discoverQuery = new DiscoverQuery();
@@ -974,8 +1057,8 @@ public List findCollectionsWithSubmit(String q, Context context, Com
}
@Override
- public int countCollectionsWithSubmit(String q, Context context, Community community)
- throws SQLException, SearchServiceException {
+ public int countCollectionsWithSubmit(Context context, String q, Community community)
+ throws SearchServiceException {
DiscoverQuery discoverQuery = new DiscoverQuery();
discoverQuery.setMaxResults(0);
@@ -997,29 +1080,12 @@ public int countCollectionsWithSubmit(String q, Context context, Community commu
* terms. The terms are used to make also a prefix query on SOLR
* so it can be used to implement an autosuggest feature over the collection name
* @return discovery search result objects
- * @throws SQLException if something goes wrong
* @throws SearchServiceException if search error
*/
private DiscoverResult retrieveCollectionsWithSubmit(Context context, DiscoverQuery discoverQuery,
String entityType, Community community, String q)
- throws SQLException, SearchServiceException {
-
- StringBuilder query = new StringBuilder();
- EPerson currentUser = context.getCurrentUser();
- if (!authorizeService.isAdmin(context)) {
- String userId = "";
- if (currentUser != null) {
- userId = currentUser.getID().toString();
- }
- query.append("submit:(e").append(userId);
+ throws SearchServiceException {
- Set groups = groupService.allMemberGroupsSet(context, currentUser);
- for (Group group : groups) {
- query.append(" OR g").append(group.getID());
- }
- query.append(")");
- discoverQuery.addFilterQueries(query.toString());
- }
if (Objects.nonNull(community)) {
discoverQuery.addFilterQueries("location.comm:" + community.getID().toString());
}
@@ -1027,19 +1093,17 @@ private DiscoverResult retrieveCollectionsWithSubmit(Context context, DiscoverQu
discoverQuery.addFilterQueries("search.entitytype:" + entityType);
}
if (StringUtils.isNotBlank(q)) {
- StringBuilder buildQuery = new StringBuilder();
- String escapedQuery = ClientUtils.escapeQueryChars(q);
- buildQuery.append("(").append(escapedQuery).append(" OR dc.title_sort:*")
- .append(escapedQuery).append("*").append(")");
- discoverQuery.setQuery(buildQuery.toString());
+ q = searchService.formatAutoCompleteQuery(q, "dc.title_sort");
+ discoverQuery.setQuery(q);
}
+ discoverQuery.addRequiredAuthorization(Constants.ADD);
DiscoverResult resp = searchService.search(context, discoverQuery);
return resp;
}
@Override
- public List findCollectionsWithSubmit(String q, Context context, Community community, String entityType,
- int offset, int limit) throws SQLException, SearchServiceException {
+ public List findCollectionsWithSubmit(Context context, String q, Community community, String entityType,
+ int offset, int limit) throws SearchServiceException {
List collections = new ArrayList<>();
DiscoverQuery discoverQuery = new DiscoverQuery();
discoverQuery.setDSpaceObjectFilter(IndexableCollection.TYPE);
@@ -1056,8 +1120,8 @@ public List findCollectionsWithSubmit(String q, Context context, Com
}
@Override
- public int countCollectionsWithSubmit(String q, Context context, Community community, String entityType)
- throws SQLException, SearchServiceException {
+ public int countCollectionsWithSubmit(Context context, String q, Community community, String entityType)
+ throws SearchServiceException {
DiscoverQuery discoverQuery = new DiscoverQuery();
discoverQuery.setMaxResults(0);
discoverQuery.setDSpaceObjectFilter(IndexableCollection.TYPE);
diff --git a/dspace-api/src/main/java/org/dspace/content/EntityType.java b/dspace-api/src/main/java/org/dspace/content/EntityType.java
index 20ab758a0b7..1c041e776e7 100644
--- a/dspace-api/src/main/java/org/dspace/content/EntityType.java
+++ b/dspace-api/src/main/java/org/dspace/content/EntityType.java
@@ -8,6 +8,7 @@
package org.dspace.content;
import java.util.Objects;
+import javax.persistence.Cacheable;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
@@ -19,6 +20,8 @@
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.builder.HashCodeBuilder;
import org.dspace.core.ReloadableEntity;
+import org.hibernate.annotations.Cache;
+import org.hibernate.annotations.CacheConcurrencyStrategy;
/**
* Class representing an EntityType
@@ -26,6 +29,8 @@
* This also has a label that will be used to identify what kind of EntityType this object is
*/
@Entity
+@Cacheable
+@Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
@Table(name = "entity_type")
public class EntityType implements ReloadableEntity {
diff --git a/dspace-api/src/main/java/org/dspace/content/EntityTypeServiceImpl.java b/dspace-api/src/main/java/org/dspace/content/EntityTypeServiceImpl.java
index 7df892cd56f..92a7b62e720 100644
--- a/dspace-api/src/main/java/org/dspace/content/EntityTypeServiceImpl.java
+++ b/dspace-api/src/main/java/org/dspace/content/EntityTypeServiceImpl.java
@@ -16,6 +16,7 @@
import java.util.Set;
import org.apache.commons.collections.CollectionUtils;
+import org.apache.commons.lang3.StringUtils;
import org.apache.solr.client.solrj.SolrQuery;
import org.apache.solr.client.solrj.SolrServerException;
import org.apache.solr.client.solrj.response.FacetField;
@@ -28,6 +29,7 @@
import org.dspace.content.service.EntityTypeService;
import org.dspace.core.Constants;
import org.dspace.core.Context;
+import org.dspace.discovery.SearchService;
import org.dspace.discovery.SolrSearchCore;
import org.dspace.discovery.indexobject.IndexableCollection;
import org.dspace.eperson.EPerson;
@@ -49,6 +51,9 @@ public class EntityTypeServiceImpl implements EntityTypeService {
@Autowired
protected SolrSearchCore solrSearchCore;
+ @Autowired
+ protected SearchService searchService;
+
@Override
public EntityType findByEntityType(Context context, String entityType) throws SQLException {
return entityTypeDAO.findByEntityType(context, entityType);
@@ -126,26 +131,34 @@ public List getSubmitAuthorizedTypes(Context context)
throws SQLException, SolrServerException, IOException {
List types = new ArrayList<>();
StringBuilder query = null;
- EPerson currentUser = context.getCurrentUser();
if (!authorizeService.isAdmin(context)) {
- String userId = "";
+ EPerson currentUser = context.getCurrentUser();
+ StringBuilder epersonAndGroupClause = new StringBuilder();
if (currentUser != null) {
- userId = currentUser.getID().toString();
- query = new StringBuilder();
- query.append("submit:(e").append(userId);
+ epersonAndGroupClause.append("e").append(currentUser.getID());
}
-
+ //Retrieve all the groups the current user is a member of
Set groups = groupService.allMemberGroupsSet(context, currentUser);
for (Group group : groups) {
- if (query == null) {
- query = new StringBuilder();
- query.append("submit:(g");
+ if (epersonAndGroupClause.length() > 0) {
+ epersonAndGroupClause.append(" OR g").append(group.getID());
} else {
- query.append(" OR g");
+ epersonAndGroupClause.append("g").append(group.getID());
}
- query.append(group.getID());
}
- query.append(")");
+
+ if (epersonAndGroupClause.length() == 0) {
+ // No user or groups, no authorized types
+ return new ArrayList<>();
+ }
+ query = new StringBuilder();
+ query.append("submit:(").append(epersonAndGroupClause).append(")");
+ query.append(" OR ").append("admin:(").append(epersonAndGroupClause).append(")");
+ String locations = searchService.createLocationQueryForAdministrableDSOs(epersonAndGroupClause.toString());
+ if (StringUtils.isNotBlank(locations)) {
+ query.append(" OR ");
+ query.append(locations);
+ }
}
SolrQuery sQuery = new SolrQuery("*:*");
diff --git a/dspace-api/src/main/java/org/dspace/content/ItemServiceImpl.java b/dspace-api/src/main/java/org/dspace/content/ItemServiceImpl.java
index 9c853613a94..d3898a4fe06 100644
--- a/dspace-api/src/main/java/org/dspace/content/ItemServiceImpl.java
+++ b/dspace-api/src/main/java/org/dspace/content/ItemServiceImpl.java
@@ -22,7 +22,6 @@
import java.util.UUID;
import java.util.function.Supplier;
import java.util.stream.Collectors;
-import java.util.stream.Stream;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
@@ -1145,11 +1144,6 @@ public void adjustItemPolicies(Context context, Item item, Collection collection
public void move(Context context, Item item, Collection from, Collection to)
throws SQLException, AuthorizeException, IOException {
- // If the two collections are the same, do nothing.
- if (from.equals(to)) {
- return;
- }
-
// Use the normal move method, and default to not inherit permissions
this.move(context, item, from, to, false);
}
@@ -1164,6 +1158,11 @@ public void move(Context context, Item item, Collection from, Collection to, boo
authorizeService.authorizeAction(context, item, Constants.WRITE);
}
+ // If the two collections are the same, do nothing.
+ if (from.equals(to)) {
+ return;
+ }
+
// Move the Item from one Collection to the other
collectionService.addItem(context, to, item);
collectionService.removeItem(context, from, item);
@@ -1266,43 +1265,41 @@ public boolean canEdit(Context context, Item item) throws SQLException {
*
* @param context DSpace context
* @param discoverQuery
+ * @param q query string
* @return discovery search result objects
- * @throws SQLException if something goes wrong
* @throws SearchServiceException if search error
*/
- private DiscoverResult retrieveItemsWithEdit(Context context, DiscoverQuery discoverQuery)
- throws SQLException, SearchServiceException {
- EPerson currentUser = context.getCurrentUser();
- if (!authorizeService.isAdmin(context)) {
- String userId = currentUser != null ? "e" + currentUser.getID().toString() : "e";
- Stream groupIds = groupService.allMemberGroupsSet(context, currentUser).stream()
- .map(group -> "g" + group.getID());
- String query = Stream.concat(Stream.of(userId), groupIds)
- .collect(Collectors.joining(" OR ", "edit:(", ")"));
- discoverQuery.addFilterQueries(query);
- }
+ private DiscoverResult retrieveItemsWithEdit(Context context, DiscoverQuery discoverQuery, String q)
+ throws SearchServiceException {
+ if (StringUtils.isNotBlank(q)) {
+ // Although not all items will have a metadata dc.title, we use it for autocomplete because it is the
+ // most common. Ideally, we should use a field that all indexed items have
+ q = searchService.formatAutoCompleteQuery(q, "dc.title_sort");
+ discoverQuery.setQuery(q);
+ }
+ discoverQuery.addRequiredAuthorization(Constants.WRITE);
return searchService.search(context, discoverQuery);
}
@Override
- public List findItemsWithEdit(Context context, int offset, int limit)
- throws SQLException, SearchServiceException {
+ public List findItemsWithEdit(Context context, String q, int offset, int limit)
+ throws SearchServiceException {
DiscoverQuery discoverQuery = new DiscoverQuery();
discoverQuery.setDSpaceObjectFilter(IndexableItem.TYPE);
discoverQuery.setStart(offset);
discoverQuery.setMaxResults(limit);
- DiscoverResult resp = retrieveItemsWithEdit(context, discoverQuery);
+ DiscoverResult resp = retrieveItemsWithEdit(context, discoverQuery, q);
return resp.getIndexableObjects().stream()
.map(solrItems -> ((IndexableItem) solrItems).getIndexedObject())
.collect(Collectors.toList());
}
@Override
- public int countItemsWithEdit(Context context) throws SQLException, SearchServiceException {
+ public int countItemsWithEdit(Context context, String q) throws SearchServiceException {
DiscoverQuery discoverQuery = new DiscoverQuery();
discoverQuery.setMaxResults(0);
discoverQuery.setDSpaceObjectFilter(IndexableItem.TYPE);
- DiscoverResult resp = retrieveItemsWithEdit(context, discoverQuery);
+ DiscoverResult resp = retrieveItemsWithEdit(context, discoverQuery, q);
return (int) resp.getTotalSearchResults();
}
diff --git a/dspace-api/src/main/java/org/dspace/content/RelationshipType.java b/dspace-api/src/main/java/org/dspace/content/RelationshipType.java
index 5e6941052b8..5b89911e1f7 100644
--- a/dspace-api/src/main/java/org/dspace/content/RelationshipType.java
+++ b/dspace-api/src/main/java/org/dspace/content/RelationshipType.java
@@ -7,6 +7,7 @@
*/
package org.dspace.content;
+import javax.persistence.Cacheable;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
@@ -21,6 +22,8 @@
import org.dspace.core.Context;
import org.dspace.core.ReloadableEntity;
+import org.hibernate.annotations.Cache;
+import org.hibernate.annotations.CacheConcurrencyStrategy;
/**
* Class representing a RelationshipType
@@ -31,6 +34,8 @@
* The cardinality properties describe how many of each relations this relationshipType can support
*/
@Entity
+@Cacheable
+@Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
@Table(name = "relationship_type")
public class RelationshipType implements ReloadableEntity {
diff --git a/dspace-api/src/main/java/org/dspace/content/authority/DSpaceControlledVocabulary.java b/dspace-api/src/main/java/org/dspace/content/authority/DSpaceControlledVocabulary.java
index 444332df97d..746e0579dcf 100644
--- a/dspace-api/src/main/java/org/dspace/content/authority/DSpaceControlledVocabulary.java
+++ b/dspace-api/src/main/java/org/dspace/content/authority/DSpaceControlledVocabulary.java
@@ -34,35 +34,48 @@
* from {@code ${dspace.dir}/config/controlled-vocabularies/*.xml} and turns
* them into autocompleting authorities.
*
- * Configuration: This MUST be configured as a self-named plugin, e.g.: {@code
- * plugin.selfnamed.org.dspace.content.authority.ChoiceAuthority = \
+ *
Configuration: This MUST be configured as a self-named plugin, e.g.: {@code
+ * plugin.selfnamed.org.dspace.content.authority.ChoiceAuthority =
* org.dspace.content.authority.DSpaceControlledVocabulary
* }
*
- * It AUTOMATICALLY configures a plugin instance for each XML file in the
+ *
It AUTOMATICALLY configures a plugin instance for each XML file in the
* controlled vocabularies directory. The name of the plugin is the basename of
* the file; e.g., {@code ${dspace.dir}/config/controlled-vocabularies/nsi.xml}
* would generate a plugin called "nsi".
*
- * Each configured plugin comes with three configuration options: {@code
- * vocabulary.plugin._plugin_.hierarchy.store =
- * # Store entire hierarchy along with selected value. Default: TRUE
- * vocabulary.plugin._plugin_.hierarchy.suggest =
- * # Display entire hierarchy in the suggestion list. Default: TRUE
- * vocabulary.plugin._plugin_.delimiter = ""
- * # Delimiter to use when building hierarchy strings. Default: "::"
- * }
+ *
Each configured plugin comes with three configuration options:
+ *
+ *
{@code vocabulary.plugin._plugin_.hierarchy.store = }
+ * # Store entire hierarchy along with selected value. Default: TRUE
+ *
{@code vocabulary.plugin._plugin_.hierarchy.suggest =
+ * # Display entire hierarchy in the suggestion list. Default: TRUE}
+ *
{@code vocabulary.plugin._plugin_.delimiter = ""
+ * # Delimiter to use when building hierarchy strings. Default: "::"}
+ *
*
* @author Michael B. Klein
*/
public class DSpaceControlledVocabulary extends SelfNamedPlugin implements HierarchicalAuthority {
- private static Logger log = org.apache.logging.log4j.LogManager.getLogger(DSpaceControlledVocabulary.class);
- protected static String xpathTemplate = "//node[contains(translate(@label,'ABCDEFGHIJKLMNOPQRSTUVWXYZ'," +
- "'abcdefghijklmnopqrstuvwxyz'),'%s')]";
- protected static String idTemplate = "//node[@id = '%s']";
- protected static String labelTemplate = "//node[@label = '%s']";
+ private static final Logger log = org.apache.logging.log4j.LogManager.getLogger();
+ protected static final String xpathTemplate;
+ static {
+ StringBuilder upper = new StringBuilder();
+ StringBuilder lower = new StringBuilder();
+ for (int cp = 'A'; cp <= Character.MAX_CODE_POINT; cp++) {
+ if (Character.isLetter(cp) && Character.isUpperCase(cp)) {
+ int lcp = Character.toLowerCase(cp);
+ upper.appendCodePoint(cp);
+ lower.appendCodePoint(lcp);
+ }
+ }
+ xpathTemplate = "//node[contains(translate(@label,'" + upper + "','" + lower + "'),%s)]";
+ }
+ protected static String idTemplate = "//node[@id = %s]";
+ protected static String idTemplateQuoted = "//node[@id = '%s']";
+ protected static String labelTemplate = "//node[@label = %s]";
protected static String idParentTemplate = "//node[@id = '%s']/parent::isComposedBy/parent::node";
protected static String rootTemplate = "/node";
protected static String pluginNames[] = null;
@@ -106,7 +119,7 @@ public boolean accept(File dir, String name) {
File.separator + "config" +
File.separator + "controlled-vocabularies";
String[] xmlFiles = (new File(vocabulariesPath)).list(new xmlFilter());
- List names = new ArrayList();
+ List names = new ArrayList<>();
for (String filename : xmlFiles) {
names.add((new File(filename)).getName().replace(".xml", ""));
}
@@ -162,14 +175,23 @@ protected String buildString(Node node) {
public Choices getMatches(String text, int start, int limit, String locale) {
init();
log.debug("Getting matches for '" + text + "'");
- String xpathExpression = "";
String[] textHierarchy = text.split(hierarchyDelimiter, -1);
+ StringBuilder xpathExpressionBuilder = new StringBuilder();
for (int i = 0; i < textHierarchy.length; i++) {
- xpathExpression += String.format(xpathTemplate, textHierarchy[i].replaceAll("'", "'").toLowerCase());
+ xpathExpressionBuilder.append(String.format(xpathTemplate, "$var" + i));
}
+ String xpathExpression = xpathExpressionBuilder.toString();
XPath xpath = XPathFactory.newInstance().newXPath();
- int total = 0;
- List choices = new ArrayList();
+ xpath.setXPathVariableResolver(variableName -> {
+ String varName = variableName.getLocalPart();
+ if (varName.startsWith("var")) {
+ int index = Integer.parseInt(varName.substring(3));
+ return textHierarchy[index].toLowerCase();
+ }
+ throw new IllegalArgumentException("Unexpected variable: " + varName);
+ });
+ int total;
+ List choices;
try {
NodeList results = (NodeList) xpath.evaluate(xpathExpression, vocabulary, XPathConstants.NODESET);
total = results.getLength();
@@ -185,14 +207,23 @@ public Choices getMatches(String text, int start, int limit, String locale) {
@Override
public Choices getBestMatch(String text, String locale) {
init();
- log.debug("Getting best matches for '" + text + "'");
- String xpathExpression = "";
+ log.debug("Getting best matches for {}'", text);
String[] textHierarchy = text.split(hierarchyDelimiter, -1);
+ StringBuilder xpathExpressionBuilder = new StringBuilder();
for (int i = 0; i < textHierarchy.length; i++) {
- xpathExpression += String.format(labelTemplate, textHierarchy[i].replaceAll("'", "'"));
+ xpathExpressionBuilder.append(String.format(labelTemplate, "$var" + i));
}
+ String xpathExpression = xpathExpressionBuilder.toString();
XPath xpath = XPathFactory.newInstance().newXPath();
- List choices = new ArrayList();
+ xpath.setXPathVariableResolver(variableName -> {
+ String varName = variableName.getLocalPart();
+ if (varName.startsWith("var")) {
+ int index = Integer.parseInt(varName.substring(3));
+ return textHierarchy[index];
+ }
+ throw new IllegalArgumentException("Unexpected variable: " + varName);
+ });
+ List choices;
try {
NodeList results = (NodeList) xpath.evaluate(xpathExpression, vocabulary, XPathConstants.NODESET);
choices = getChoicesFromNodeList(results, 0, 1);
@@ -240,7 +271,7 @@ public Choices getTopChoices(String authorityName, int start, int limit, String
@Override
public Choices getChoicesByParent(String authorityName, String parentId, int start, int limit, String locale) {
init();
- String xpathExpression = String.format(idTemplate, parentId);
+ String xpathExpression = String.format(idTemplateQuoted, parentId);
return getChoicesByXpath(xpathExpression, start, limit);
}
@@ -264,15 +295,12 @@ public Integer getPreloadLevel() {
}
private boolean isRootElement(Node node) {
- if (node != null && node.getOwnerDocument().getDocumentElement().equals(node)) {
- return true;
- }
- return false;
+ return node != null && node.getOwnerDocument().getDocumentElement().equals(node);
}
private Node getNode(String key) throws XPathExpressionException {
init();
- String xpathExpression = String.format(idTemplate, key);
+ String xpathExpression = String.format(idTemplateQuoted, key);
Node node = getNodeFromXPath(xpathExpression);
return node;
}
@@ -284,7 +312,7 @@ private Node getNodeFromXPath(String xpathExpression) throws XPathExpressionExce
}
private List getChoicesFromNodeList(NodeList results, int start, int limit) {
- List choices = new ArrayList();
+ List choices = new ArrayList<>();
for (int i = 0; i < results.getLength(); i++) {
if (i < start) {
continue;
@@ -303,14 +331,14 @@ private List getChoicesFromNodeList(NodeList results, int start, int lim
private Map addOtherInformation(String parentCurr, String noteCurr,
List childrenCurr, String authorityCurr) {
- Map extras = new HashMap();
+ Map extras = new HashMap<>();
if (StringUtils.isNotBlank(parentCurr)) {
extras.put("parent", parentCurr);
}
if (StringUtils.isNotBlank(noteCurr)) {
extras.put("note", noteCurr);
}
- if (childrenCurr.size() > 0) {
+ if (!childrenCurr.isEmpty()) {
extras.put("hasChildren", "true");
} else {
extras.put("hasChildren", "false");
@@ -368,7 +396,7 @@ private String getNote(Node node) {
}
private List getChildren(Node node) {
- List children = new ArrayList();
+ List children = new ArrayList<>();
NodeList childNodes = node.getChildNodes();
for (int ci = 0; ci < childNodes.getLength(); ci++) {
Node firstChild = childNodes.item(ci);
@@ -391,7 +419,7 @@ private List getChildren(Node node) {
private boolean isSelectable(Node node) {
Node selectableAttr = node.getAttributes().getNamedItem("selectable");
if (null != selectableAttr) {
- return Boolean.valueOf(selectableAttr.getNodeValue());
+ return Boolean.parseBoolean(selectableAttr.getNodeValue());
} else { // Default is true
return true;
}
@@ -418,7 +446,7 @@ private String getAuthority(Node node) {
}
private Choices getChoicesByXpath(String xpathExpression, int start, int limit) {
- List choices = new ArrayList();
+ List choices = new ArrayList<>();
XPath xpath = XPathFactory.newInstance().newXPath();
try {
Node parentNode = (Node) xpath.evaluate(xpathExpression, vocabulary, XPathConstants.NODE);
diff --git a/dspace-api/src/main/java/org/dspace/content/crosswalk/CrosswalkMetadataValidator.java b/dspace-api/src/main/java/org/dspace/content/crosswalk/CrosswalkMetadataValidator.java
index b1be458a255..1bcc1c9ba9f 100644
--- a/dspace-api/src/main/java/org/dspace/content/crosswalk/CrosswalkMetadataValidator.java
+++ b/dspace-api/src/main/java/org/dspace/content/crosswalk/CrosswalkMetadataValidator.java
@@ -107,9 +107,12 @@ public MetadataField checkMetadata(Context context, String schema, String elemen
e.printStackTrace();
}
} else if (!fieldChoice.equals("ignore")) {
- throw new CrosswalkException(
- "The '" + element + "." + qualifier + "' element has not been defined in this DSpace " +
- "instance. ");
+ throw new CrosswalkException(String.format(
+ "The '%s.%s%s' element has not been defined in this DSpace instance.",
+ mdSchema.getName(),
+ element,
+ qualifier == null ? "" : ("." + qualifier)
+ ));
}
}
}
diff --git a/dspace-api/src/main/java/org/dspace/content/crosswalk/LicenseStreamDisseminationCrosswalk.java b/dspace-api/src/main/java/org/dspace/content/crosswalk/LicenseStreamDisseminationCrosswalk.java
index 46858747870..b1854bfd85a 100644
--- a/dspace-api/src/main/java/org/dspace/content/crosswalk/LicenseStreamDisseminationCrosswalk.java
+++ b/dspace-api/src/main/java/org/dspace/content/crosswalk/LicenseStreamDisseminationCrosswalk.java
@@ -8,6 +8,7 @@
package org.dspace.content.crosswalk;
import java.io.IOException;
+import java.io.InputStream;
import java.io.OutputStream;
import java.sql.SQLException;
@@ -56,7 +57,12 @@ public void disseminate(Context context, DSpaceObject dso, OutputStream out)
Bitstream licenseBs = PackageUtils.findDepositLicense(context, (Item) dso);
if (licenseBs != null) {
- Utils.copy(bitstreamService.retrieve(context, licenseBs), out);
+ try (final InputStream bitInputStream = bitstreamService.retrieve(context, licenseBs)) {
+ Utils.copy(bitInputStream, out);
+ } catch (Exception e) {
+ log.warn("Could not retrieve license file for Item with UUID={}. " +
+ "Leaving it out of generated package. Error{}", dso.getID(), e.getMessage());
+ }
}
}
}
diff --git a/dspace-api/src/main/java/org/dspace/content/crosswalk/PREMISCrosswalk.java b/dspace-api/src/main/java/org/dspace/content/crosswalk/PREMISCrosswalk.java
index 39b6c8f29c8..6b6c0fd7c5a 100644
--- a/dspace-api/src/main/java/org/dspace/content/crosswalk/PREMISCrosswalk.java
+++ b/dspace-api/src/main/java/org/dspace/content/crosswalk/PREMISCrosswalk.java
@@ -20,9 +20,7 @@
import org.dspace.authorize.AuthorizeException;
import org.dspace.content.Bitstream;
import org.dspace.content.BitstreamFormat;
-import org.dspace.content.Bundle;
import org.dspace.content.DSpaceObject;
-import org.dspace.content.Item;
import org.dspace.content.factory.ContentServiceFactory;
import org.dspace.content.service.BitstreamFormatService;
import org.dspace.content.service.BitstreamService;
@@ -224,29 +222,17 @@ public Element disseminateElement(Context context, DSpaceObject dso)
// c. made-up name based on sequence ID and extension.
String sid = String.valueOf(bitstream.getSequenceID());
String baseUrl = configurationService.getProperty("dspace.ui.url");
- String handle = null;
- // get handle of parent Item of this bitstream, if there is one:
- List bn = bitstream.getBundles();
- if (bn.size() > 0) {
- List bi = bn.get(0).getItems();
- if (bi.size() > 0) {
- handle = bi.get(0).getHandle();
- }
- }
// get or make up name for bitstream:
String bsName = bitstream.getName();
if (bsName == null) {
List ext = bitstream.getFormat(context).getExtensions();
bsName = "bitstream_" + sid + (ext.size() > 0 ? ext.get(0) : "");
}
- if (handle != null && baseUrl != null) {
+ if (baseUrl != null) {
oiv.setText(baseUrl
- + "/bitstream/"
- + URLEncoder.encode(handle, "UTF-8")
- + "/"
- + sid
- + "/"
- + URLEncoder.encode(bsName, "UTF-8"));
+ + "/bitstreams/"
+ + bitstream.getID()
+ + "/download");
} else {
oiv.setText(URLEncoder.encode(bsName, "UTF-8"));
}
diff --git a/dspace-api/src/main/java/org/dspace/content/dao/BundleDAO.java b/dspace-api/src/main/java/org/dspace/content/dao/BundleDAO.java
index da7435d4664..99abd84eabc 100644
--- a/dspace-api/src/main/java/org/dspace/content/dao/BundleDAO.java
+++ b/dspace-api/src/main/java/org/dspace/content/dao/BundleDAO.java
@@ -22,4 +22,6 @@
*/
public interface BundleDAO extends DSpaceObjectLegacySupportDAO {
int countRows(Context context) throws SQLException;
+
+ int countBitstreams(Context context, Bundle bundle) throws SQLException;
}
diff --git a/dspace-api/src/main/java/org/dspace/content/dao/CollectionDAO.java b/dspace-api/src/main/java/org/dspace/content/dao/CollectionDAO.java
index 6bb65bbb46d..13bcf5f52c0 100644
--- a/dspace-api/src/main/java/org/dspace/content/dao/CollectionDAO.java
+++ b/dspace-api/src/main/java/org/dspace/content/dao/CollectionDAO.java
@@ -48,6 +48,18 @@ public List findAll(Context context, MetadataField order, Integer li
List findAuthorizedByGroup(Context context, EPerson ePerson, List actions) throws SQLException;
+ /**
+ * Get all authorized collections of the current EPerson
+ *
+ * @param context DSpace context object
+ * @param ePerson the current EPerson
+ * @param actions list of actionsID ADD, READ, etc.
+ * @return the collections the eperson is defined
+ * @throws SQLException if database error
+ */
+ List findAuthorizedByEPerson(Context context, EPerson ePerson, List actions)
+ throws SQLException;
+
List findCollectionsWithSubscribers(Context context) throws SQLException;
int countRows(Context context) throws SQLException;
diff --git a/dspace-api/src/main/java/org/dspace/content/dao/impl/BundleDAOImpl.java b/dspace-api/src/main/java/org/dspace/content/dao/impl/BundleDAOImpl.java
index 99163610849..08d6342e267 100644
--- a/dspace-api/src/main/java/org/dspace/content/dao/impl/BundleDAOImpl.java
+++ b/dspace-api/src/main/java/org/dspace/content/dao/impl/BundleDAOImpl.java
@@ -8,6 +8,7 @@
package org.dspace.content.dao.impl;
import java.sql.SQLException;
+import javax.persistence.Query;
import org.dspace.content.Bundle;
import org.dspace.content.dao.BundleDAO;
@@ -31,4 +32,13 @@ protected BundleDAOImpl() {
public int countRows(Context context) throws SQLException {
return count(createQuery(context, "SELECT count(*) from Bundle"));
}
+
+ @Override
+ public int countBitstreams(Context context, Bundle bundle) throws SQLException {
+ Query query = createQuery(
+ context, "SELECT count(bi.id) from Bundle bu join bu.bitstreams bi where bu.id = :bundleID"
+ );
+ query.setParameter("bundleID", bundle.getID());
+ return count(query);
+ }
}
diff --git a/dspace-api/src/main/java/org/dspace/content/dao/impl/CollectionDAOImpl.java b/dspace-api/src/main/java/org/dspace/content/dao/impl/CollectionDAOImpl.java
index befa1397a8e..e54542eda2d 100644
--- a/dspace-api/src/main/java/org/dspace/content/dao/impl/CollectionDAOImpl.java
+++ b/dspace-api/src/main/java/org/dspace/content/dao/impl/CollectionDAOImpl.java
@@ -10,8 +10,13 @@
import java.sql.SQLException;
import java.util.AbstractMap;
import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.LinkedList;
import java.util.List;
import java.util.Map;
+import java.util.Queue;
+import java.util.Set;
+import java.util.UUID;
import javax.persistence.Query;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
@@ -19,6 +24,7 @@
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
+import org.apache.logging.log4j.Logger;
import org.dspace.authorize.ResourcePolicy;
import org.dspace.authorize.ResourcePolicy_;
import org.dspace.content.Collection;
@@ -40,6 +46,11 @@
* @author kevinvandevelde at atmire.com
*/
public class CollectionDAOImpl extends AbstractHibernateDSODAO implements CollectionDAO {
+ /**
+ * log4j logger
+ */
+ private static Logger log = org.apache.logging.log4j.LogManager.getLogger(CollectionDAOImpl.class);
+
protected CollectionDAOImpl() {
super();
}
@@ -157,9 +168,103 @@ public List findAuthorizedByGroup(Context context, EPerson ePerson,
}
+ /**
+ * Get all authorized collections of the current EPerson
+ *
+ * @param context DSpace context object
+ * @param ePerson the current EPerson
+ * @param actions list of actionsID ADD, READ, etc.
+ * @return the collections the eperson is defined
+ * @throws SQLException if database error
+ */
+ @Override
+ public List findAuthorizedByEPerson(Context context, EPerson ePerson, List actions)
+ throws SQLException {
+
+ //NOTE steps 1) and 2) removes the need of WITH RECURSIVE and a NativeQuery
+
+ // 1) Get all groups a eperson belongs
+ /*ArrayList<>(ePerson.getGroups()) - This ensures you have a concrete copy and can modify it safely.
+ instead if List directGroups = ePerson.getGroups();
+ Also - Can be done using this query:
+ List directGroups = createQuery(context, """
+ SELECT g
+ FROM Group g
+ JOIN g.epeople e
+ WHERE e.id = :epersonId
+ """)
+ .setParameter("epersonId", ePerson.getID())
+ .getResultList();
+ */
+ List directGroups = new ArrayList<>(ePerson.getGroups()); // direct membership
+
+ // 2) Expand hierarquy of groups in memory (recursively)
+ Set allGroups = new HashSet<>(directGroups);
+ Queue queue = new LinkedList<>(directGroups);
+
+ /*
+ * Using the query avoids the change of the getParentGroups visibility in Group
+ * The List parents = current.getParentGroups() could be achieved using:
+ * List parents = createQuery(context,"""
+ SELECT g
+ FROM Group g
+ JOIN g.groups child
+ WHERE child = :child
+ """)
+ */
+ // //current.getMemberGroups()- Making public getParentGroups in Group Class (why it isn't already public?)
+ while (!queue.isEmpty()) {
+ Group current = queue.poll();
+ List parents = current.getParentGroups();
+
+ for (Group parent : parents) {
+ if (allGroups.add(parent)) {
+ queue.add(parent);
+ }
+ }
+ }
+
+ CriteriaBuilder cb = getCriteriaBuilder(context);
+ CriteriaQuery cq = getCriteriaQuery(cb, Collection.class);
+ Root collectionRoot = cq.from(Collection.class);
+
+ // Join to ResourcePolicy using metamodel
+ Join rpJoin = collectionRoot.join("resourcePolicies");
+ // Use metamodel for typesafe access
+ cq.select(collectionRoot).distinct(true);
+
+ List predicates = new ArrayList<>(actions.size());
+ // WHERE rp.resourceTypeId = :resourceType
+ predicates.add(cb.equal(rpJoin.get(ResourcePolicy_.resourceTypeId), Constants.COLLECTION));
+ // AND (:hasActions = false OR rp.actionId IN :actionIds)
+ if (actions != null && !actions.isEmpty()) {
+ predicates.add(rpJoin.get(ResourcePolicy_.actionId).in(actions));
+ }
+
+ // AND (rp.eperson.id = :epersonId OR (:hasGroups = true AND rp.epersonGroup.id IN :groupIds))
+ Predicate epersonPredicate = cb.equal(
+ rpJoin.get(ResourcePolicy_.eperson), ePerson
+ );
+ // Using only groups instead of groupsIDs
+ Predicate groupPredicate = cb.disjunction(); // false by default
+ if (allGroups != null && !allGroups.isEmpty()) {
+ groupPredicate = rpJoin.get(ResourcePolicy_.epersonGroup).in(allGroups);
+ }
+
+ // Combine access condition
+ Predicate accessPredicate = cb.or(epersonPredicate, groupPredicate);
+ predicates.add(accessPredicate);
+
+ // Apply WHERE clause
+ cq.where(cb.and(predicates.toArray(new Predicate[0])));
+
+ // Execute
+ return list(context, cq, true, Collection.class, -1, -1);
+ }
+
@Override
public List findCollectionsWithSubscribers(Context context) throws SQLException {
- return list(createQuery(context, "SELECT DISTINCT c FROM Collection c JOIN Subscription s ON c.id = " +
+ return list(createQuery(context, "SELECT DISTINCT c FROM Collection c JOIN Subscription s ON c = " +
"s.dSpaceObject"));
}
@@ -172,15 +277,26 @@ public int countRows(Context context) throws SQLException {
@SuppressWarnings("unchecked")
public List> getCollectionsWithBitstreamSizesTotal(Context context)
throws SQLException {
- String q = "select col as collection, sum(bit.sizeBytes) as totalBytes from Item i join i.collections col " +
- "join i.bundles bun join bun.bitstreams bit group by col";
+ String q = "select col.id, sum(bit.sizeBytes) as totalBytes from Item i join i.collections col " +
+ "join i.bundles bun join bun.bitstreams bit group by col.id";
Query query = createQuery(context, q);
+ CriteriaBuilder criteriaBuilder = getCriteriaBuilder(context);
+
List