From 2cb8204d3c474ce9a7cb6946c43a0beab0ba92a2 Mon Sep 17 00:00:00 2001 From: nwoodward Date: Mon, 8 Jul 2024 12:33:07 -0500 Subject: [PATCH 001/979] updated creative commons licenses version from 3.0 to 4.0 --- .../rest/submit/factory/impl/CCLicenseAddPatchOperation.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/factory/impl/CCLicenseAddPatchOperation.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/factory/impl/CCLicenseAddPatchOperation.java index 602ae5e9690b..80624257e6ce 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/factory/impl/CCLicenseAddPatchOperation.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/factory/impl/CCLicenseAddPatchOperation.java @@ -26,7 +26,7 @@ * Example: * curl -X PATCH http://${dspace.server.url}/api/submission/workspaceitems/31599 -H "Content-Type: * application/json" -d '[{ "op": "add", "path": "/sections/cclicense/uri", - * "value":"https://creativecommons.org/licenses/by-nc-sa/3.0/us/"}]' + * "value":"https://creativecommons.org/licenses/by-nc-sa/4.0/us/"}]' * */ public class CCLicenseAddPatchOperation extends AddPatchOperation { From 86b28bf927d1614aa3d5b162c736fb834d25495d Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Tue, 9 Jul 2024 14:10:05 -0500 Subject: [PATCH 002/979] [maven-release-plugin] prepare for next development iteration --- dspace-api/pom.xml | 2 +- dspace-iiif/pom.xml | 2 +- dspace-oai/pom.xml | 2 +- dspace-rdf/pom.xml | 2 +- dspace-rest/pom.xml | 4 ++-- dspace-server-webapp/pom.xml | 2 +- dspace-services/pom.xml | 2 +- dspace-sword/pom.xml | 2 +- dspace-swordv2/pom.xml | 2 +- dspace/modules/additions/pom.xml | 2 +- dspace/modules/pom.xml | 2 +- dspace/modules/rest/pom.xml | 2 +- dspace/modules/server/pom.xml | 2 +- dspace/pom.xml | 2 +- pom.xml | 32 ++++++++++++++++---------------- 15 files changed, 31 insertions(+), 31 deletions(-) diff --git a/dspace-api/pom.xml b/dspace-api/pom.xml index a15a21e6bc07..16b151b092e5 100644 --- a/dspace-api/pom.xml +++ b/dspace-api/pom.xml @@ -12,7 +12,7 @@ org.dspace dspace-parent - 7.6.2 + 7.6.3-SNAPSHOT .. diff --git a/dspace-iiif/pom.xml b/dspace-iiif/pom.xml index 58ea58687e05..64dd5106f0af 100644 --- a/dspace-iiif/pom.xml +++ b/dspace-iiif/pom.xml @@ -15,7 +15,7 @@ org.dspace dspace-parent - 7.6.2 + 7.6.3-SNAPSHOT .. diff --git a/dspace-oai/pom.xml b/dspace-oai/pom.xml index a76a878454e8..28b610e996c1 100644 --- a/dspace-oai/pom.xml +++ b/dspace-oai/pom.xml @@ -8,7 +8,7 @@ dspace-parent org.dspace - 7.6.2 + 7.6.3-SNAPSHOT .. diff --git a/dspace-rdf/pom.xml b/dspace-rdf/pom.xml index 43e637587947..e656a920a1e6 100644 --- a/dspace-rdf/pom.xml +++ b/dspace-rdf/pom.xml @@ -9,7 +9,7 @@ org.dspace dspace-parent - 7.6.2 + 7.6.3-SNAPSHOT .. diff --git a/dspace-rest/pom.xml b/dspace-rest/pom.xml index 1192f9ed7cdd..d1f3b95aafb0 100644 --- a/dspace-rest/pom.xml +++ b/dspace-rest/pom.xml @@ -3,7 +3,7 @@ org.dspace dspace-rest war - 7.6.2 + 7.6.3-SNAPSHOT DSpace (Deprecated) REST Webapp DSpace RESTful Web Services API. NOTE: this REST API is DEPRECATED. Please consider using the REST API in the dspace-server-webapp instead! @@ -12,7 +12,7 @@ org.dspace dspace-parent - 7.6.2 + 7.6.3-SNAPSHOT .. diff --git a/dspace-server-webapp/pom.xml b/dspace-server-webapp/pom.xml index feb8c2af6e4b..e0ec7ef5ed76 100644 --- a/dspace-server-webapp/pom.xml +++ b/dspace-server-webapp/pom.xml @@ -15,7 +15,7 @@ org.dspace dspace-parent - 7.6.2 + 7.6.3-SNAPSHOT .. diff --git a/dspace-services/pom.xml b/dspace-services/pom.xml index 20110a814459..51c6dea62413 100644 --- a/dspace-services/pom.xml +++ b/dspace-services/pom.xml @@ -9,7 +9,7 @@ org.dspace dspace-parent - 7.6.2 + 7.6.3-SNAPSHOT diff --git a/dspace-sword/pom.xml b/dspace-sword/pom.xml index d9fd8843a542..1403c779e720 100644 --- a/dspace-sword/pom.xml +++ b/dspace-sword/pom.xml @@ -15,7 +15,7 @@ org.dspace dspace-parent - 7.6.2 + 7.6.3-SNAPSHOT .. diff --git a/dspace-swordv2/pom.xml b/dspace-swordv2/pom.xml index 2b6c75901e56..470388883126 100644 --- a/dspace-swordv2/pom.xml +++ b/dspace-swordv2/pom.xml @@ -13,7 +13,7 @@ org.dspace dspace-parent - 7.6.2 + 7.6.3-SNAPSHOT .. diff --git a/dspace/modules/additions/pom.xml b/dspace/modules/additions/pom.xml index 0a4f43d84bf9..db9343804013 100644 --- a/dspace/modules/additions/pom.xml +++ b/dspace/modules/additions/pom.xml @@ -17,7 +17,7 @@ org.dspace modules - 7.6.2 + 7.6.3-SNAPSHOT .. diff --git a/dspace/modules/pom.xml b/dspace/modules/pom.xml index d4f47af66a00..3f15bdd01f4f 100644 --- a/dspace/modules/pom.xml +++ b/dspace/modules/pom.xml @@ -11,7 +11,7 @@ org.dspace dspace-parent - 7.6.2 + 7.6.3-SNAPSHOT ../../pom.xml diff --git a/dspace/modules/rest/pom.xml b/dspace/modules/rest/pom.xml index 3f4e99b482aa..e2e0041cb298 100644 --- a/dspace/modules/rest/pom.xml +++ b/dspace/modules/rest/pom.xml @@ -13,7 +13,7 @@ org.dspace modules - 7.6.2 + 7.6.3-SNAPSHOT .. diff --git a/dspace/modules/server/pom.xml b/dspace/modules/server/pom.xml index d5882c9270c1..5134ffb94733 100644 --- a/dspace/modules/server/pom.xml +++ b/dspace/modules/server/pom.xml @@ -13,7 +13,7 @@ just adding new jar in the classloader modules org.dspace - 7.6.2 + 7.6.3-SNAPSHOT .. diff --git a/dspace/pom.xml b/dspace/pom.xml index cc2968fab99f..b3b474bfbcbc 100644 --- a/dspace/pom.xml +++ b/dspace/pom.xml @@ -16,7 +16,7 @@ org.dspace dspace-parent - 7.6.2 + 7.6.3-SNAPSHOT ../pom.xml diff --git a/pom.xml b/pom.xml index 0670e2cf4832..b662e333225d 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ org.dspace dspace-parent pom - 7.6.2 + 7.6.3-SNAPSHOT DSpace Parent Project DSpace open source software is a turnkey institutional repository application. @@ -874,14 +874,14 @@ org.dspace dspace-rest - 7.6.2 + 7.6.3-SNAPSHOT jar classes org.dspace dspace-rest - 7.6.2 + 7.6.3-SNAPSHOT war @@ -1032,69 +1032,69 @@ org.dspace dspace-api - 7.6.2 + 7.6.3-SNAPSHOT org.dspace dspace-api test-jar - 7.6.2 + 7.6.3-SNAPSHOT test org.dspace.modules additions - 7.6.2 + 7.6.3-SNAPSHOT org.dspace dspace-sword - 7.6.2 + 7.6.3-SNAPSHOT org.dspace dspace-swordv2 - 7.6.2 + 7.6.3-SNAPSHOT org.dspace dspace-oai - 7.6.2 + 7.6.3-SNAPSHOT org.dspace dspace-services - 7.6.2 + 7.6.3-SNAPSHOT org.dspace dspace-server-webapp test-jar - 7.6.2 + 7.6.3-SNAPSHOT test org.dspace dspace-rdf - 7.6.2 + 7.6.3-SNAPSHOT org.dspace dspace-iiif - 7.6.2 + 7.6.3-SNAPSHOT org.dspace dspace-server-webapp - 7.6.2 + 7.6.3-SNAPSHOT jar classes org.dspace dspace-server-webapp - 7.6.2 + 7.6.3-SNAPSHOT war @@ -1940,7 +1940,7 @@ scm:git:git@github.com:DSpace/DSpace.git scm:git:git@github.com:DSpace/DSpace.git git@github.com:DSpace/DSpace.git - dspace-7.6.2 + dspace-7_x From 17a46a2fb6c2cb63bedc475caf9bf3953d9f76ce Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Wed, 10 Jul 2024 12:24:25 -0500 Subject: [PATCH 003/979] Remove unused services & unnecessary cache cleanup. This can result in random failures if these services are not yet loaded by another test. --- .../controller/LinksetRestControllerIT.java | 33 ------------------- 1 file changed, 33 deletions(-) diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/signposting/controller/LinksetRestControllerIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/signposting/controller/LinksetRestControllerIT.java index 6d1d242cad7f..a65357f97bfe 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/signposting/controller/LinksetRestControllerIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/signposting/controller/LinksetRestControllerIT.java @@ -40,15 +40,12 @@ import org.dspace.content.RelationshipType; import org.dspace.content.WorkspaceItem; import org.dspace.content.authority.Choices; -import org.dspace.content.authority.service.ChoiceAuthorityService; -import org.dspace.content.authority.service.MetadataAuthorityService; import org.dspace.content.service.BitstreamService; import org.dspace.content.service.ItemService; import org.dspace.content.service.RelationshipTypeService; import org.dspace.core.Constants; import org.dspace.eperson.Group; import org.dspace.services.ConfigurationService; -import org.dspace.services.factory.DSpaceServicesFactory; import org.dspace.util.SimpleMapConverter; import org.hamcrest.Matchers; import org.junit.Before; @@ -67,12 +64,6 @@ public class LinksetRestControllerIT extends AbstractControllerIntegrationTest { @Autowired private ConfigurationService configurationService; - @Autowired - private MetadataAuthorityService metadataAuthorityService; - - @Autowired - private ChoiceAuthorityService choiceAuthorityService; - @Autowired private ItemService itemService; @@ -735,10 +726,6 @@ public void findTypedLinkForBitstream() throws Exception { .andExpect(jsonPath("$[?(@.href == '" + uiUrl + "/signposting/linksets/" + item.getID() + "/json" + "' && @.rel == 'linkset' " + "&& @.type == 'application/linkset+json')]").exists()); - - DSpaceServicesFactory.getInstance().getConfigurationService().reloadConfig(); - metadataAuthorityService.clearCache(); - choiceAuthorityService.clearCache(); } @Test @@ -780,10 +767,6 @@ public void findTypedLinkForBitstreamWithType() throws Exception { "&& @.type == 'application/linkset+json')]").exists()) .andExpect(jsonPath("$[?(@.href == 'https://schema.org/ScholarlyArticle' " + "&& @.rel == 'type')]").exists()); - - DSpaceServicesFactory.getInstance().getConfigurationService().reloadConfig(); - metadataAuthorityService.clearCache(); - choiceAuthorityService.clearCache(); } @Test @@ -813,10 +796,6 @@ public void findTypedLinkForRestrictedBitstream() throws Exception { getClient().perform(get("/signposting/links/" + bitstream.getID())) .andExpect(status().isUnauthorized()); - - DSpaceServicesFactory.getInstance().getConfigurationService().reloadConfig(); - metadataAuthorityService.clearCache(); - choiceAuthorityService.clearCache(); } @Test @@ -844,10 +823,6 @@ public void findTypedLinkForBitstreamUnderEmbargo() throws Exception { getClient().perform(get("/signposting/links/" + bitstream.getID())) .andExpect(status().isUnauthorized()); - - DSpaceServicesFactory.getInstance().getConfigurationService().reloadConfig(); - metadataAuthorityService.clearCache(); - choiceAuthorityService.clearCache(); } @Test @@ -874,10 +849,6 @@ public void findTypedLinkForBitstreamOfWorkspaceItem() throws Exception { getClient().perform(get("/signposting/links/" + bitstream.getID())) .andExpect(status().isUnauthorized()); - - DSpaceServicesFactory.getInstance().getConfigurationService().reloadConfig(); - metadataAuthorityService.clearCache(); - choiceAuthorityService.clearCache(); } @Test @@ -890,10 +861,6 @@ public void findTypedLinkForUnDiscoverableItem() throws Exception { getClient().perform(get("/signposting/links/" + item.getID())) .andExpect(status().isUnauthorized()); - - DSpaceServicesFactory.getInstance().getConfigurationService().reloadConfig(); - metadataAuthorityService.clearCache(); - choiceAuthorityService.clearCache(); } @Test From 3bb93be79310fc7931359d0cc82bbfa87f9cec85 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Wed, 10 Jul 2024 14:21:59 -0500 Subject: [PATCH 004/979] Fix random pagination failures in ManageGroupsFeatureIT by using the "feature" param to filter for the feature we are looking for. If this feature appeared on page 2, then the tests would fail. --- .../authorization/ManageGroupsFeatureIT.java | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/authorization/ManageGroupsFeatureIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/authorization/ManageGroupsFeatureIT.java index a42f219909aa..1997a6147f3a 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/authorization/ManageGroupsFeatureIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/authorization/ManageGroupsFeatureIT.java @@ -176,7 +176,7 @@ public void testSubGroupOfAdminGroup() throws Exception { // Verify an ePerson in a subgroup of the site administrators has this feature getClient(token).perform(get("/api/authz/authorizations/search/object?embed=feature&uri=" - + "http://localhost/api/core/site/" + siteService.findSite(context).getID())) + + "http://localhost/api/core/site/" + siteService.findSite(context).getID() + "&feature=canManageGroups")) .andExpect(status().isOk()) .andExpect( jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='canManageGroups')]") @@ -285,7 +285,7 @@ public void testSubSubGroupOfAdminGroup() throws Exception { // Verify an ePerson in a sub-subgroup of the site administrators has this feature getClient(token).perform(get("/api/authz/authorizations/search/object?embed=feature&uri=" - + "http://localhost/api/core/site/" + siteService.findSite(context).getID())) + + "http://localhost/api/core/site/" + siteService.findSite(context).getID() + "&feature=canManageGroups")) .andExpect(status().isOk()) .andExpect( jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='canManageGroups')]") @@ -502,7 +502,7 @@ public void testSubGroupOfAdminGroupNoCommunityGroupPermission() throws Exceptio // Verify an ePerson in a subgroup of the site administrators has this feature getClient(token).perform(get("/api/authz/authorizations/search/object?embed=feature&uri=" - + "http://localhost/api/core/site/" + siteService.findSite(context).getID())) + + "http://localhost/api/core/site/" + siteService.findSite(context).getID() + "&feature=canManageGroups")) .andExpect(status().isOk()) .andExpect( jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='canManageGroups')]") @@ -636,7 +636,7 @@ public void testSubSubGroupOfAdminGroupNoCommunityGroupPermission() throws Excep // Verify an ePerson in a sub-subgroup of the site administrators has this feature getClient(token).perform(get("/api/authz/authorizations/search/object?embed=feature&uri=" - + "http://localhost/api/core/site/" + siteService.findSite(context).getID())) + + "http://localhost/api/core/site/" + siteService.findSite(context).getID() + "&feature=canManageGroups")) .andExpect(status().isOk()) .andExpect( jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='canManageGroups')]") @@ -897,7 +897,7 @@ public void testSubGroupOfAdminGroupNoCollectionGroupPermission() throws Excepti // Verify an ePerson in a subgroup of the site administrators has this feature getClient(token).perform(get("/api/authz/authorizations/search/object?embed=feature&uri=" - + "http://localhost/api/core/site/" + siteService.findSite(context).getID())) + + "http://localhost/api/core/site/" + siteService.findSite(context).getID() + "&feature=canManageGroups")) .andExpect(status().isOk()) .andExpect( jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='canManageGroups')]") @@ -1051,7 +1051,7 @@ public void testSubSubGroupOfAdminGroupNoCollectionGroupPermission() throws Exce // Verify an ePerson in a sub-subgroup of the site administrators has this feature getClient(token).perform(get("/api/authz/authorizations/search/object?embed=feature&uri=" - + "http://localhost/api/core/site/" + siteService.findSite(context).getID())) + + "http://localhost/api/core/site/" + siteService.findSite(context).getID() + "&feature=canManageGroups")) .andExpect(status().isOk()) .andExpect( jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='canManageGroups')]") @@ -1352,7 +1352,7 @@ public void testSubGroupOfAdminGroupNoComColGroupPermission() throws Exception { // Verify an ePerson in a subgroup of the site administrators has this feature getClient(token).perform(get("/api/authz/authorizations/search/object?embed=feature&uri=" - + "http://localhost/api/core/site/" + siteService.findSite(context).getID())) + + "http://localhost/api/core/site/" + siteService.findSite(context).getID() + "&feature=canManageGroups")) .andExpect(status().isOk()) .andExpect( jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='canManageGroups')]") @@ -1526,7 +1526,7 @@ public void testSubSubGroupOfAdminGroupNoComColGroupPermission() throws Exceptio // Verify an ePerson in a sub-subgroup of the site administrators has this feature getClient(token).perform(get("/api/authz/authorizations/search/object?embed=feature&uri=" - + "http://localhost/api/core/site/" + siteService.findSite(context).getID())) + + "http://localhost/api/core/site/" + siteService.findSite(context).getID() + "&feature=canManageGroups")) .andExpect(status().isOk()) .andExpect( jsonPath("$._embedded.authorizations[?(@._embedded.feature.id=='canManageGroups')]") From 22f40943563c404bbf43e8a857ab30c8c4268e88 Mon Sep 17 00:00:00 2001 From: Sascha Szott Date: Wed, 17 Jul 2024 15:33:38 +0200 Subject: [PATCH 005/979] fix FromAsCasing warning (cherry picked from commit 51635d5ff1449c4956f7ad1750a8053de201949b) --- Dockerfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index 964b76a2565d..75817980379c 100644 --- a/Dockerfile +++ b/Dockerfile @@ -9,7 +9,7 @@ ARG JDK_VERSION=17 ARG DSPACE_VERSION=latest # Step 1 - Run Maven Build -FROM dspace/dspace-dependencies:${DSPACE_VERSION} as build +FROM dspace/dspace-dependencies:${DSPACE_VERSION} AS build ARG TARGET_DIR=dspace-installer WORKDIR /app # The dspace-installer directory will be written to /install @@ -31,7 +31,7 @@ RUN mvn --no-transfer-progress package ${MAVEN_FLAGS} && \ RUN rm -rf /install/webapps/server/ # Step 2 - Run Ant Deploy -FROM eclipse-temurin:${JDK_VERSION} as ant_build +FROM eclipse-temurin:${JDK_VERSION} AS ant_build ARG TARGET_DIR=dspace-installer # COPY the /install directory from 'build' container to /dspace-src in this container COPY --from=build /install /dspace-src From 6512513c4f3102bdd3b5b52826c1938f14da7d11 Mon Sep 17 00:00:00 2001 From: Sascha Szott Date: Wed, 17 Jul 2024 15:34:52 +0200 Subject: [PATCH 006/979] fix FromAsCasing warning (cherry picked from commit f40e0aaf4eb13fdef1070bd35867fae08a2dfd61) --- Dockerfile.cli | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Dockerfile.cli b/Dockerfile.cli index 7dd35c9651d9..5254d1eb4d69 100644 --- a/Dockerfile.cli +++ b/Dockerfile.cli @@ -9,7 +9,7 @@ ARG JDK_VERSION=17 ARG DSPACE_VERSION=latest # Step 1 - Run Maven Build -FROM dspace/dspace-dependencies:${DSPACE_VERSION} as build +FROM dspace/dspace-dependencies:${DSPACE_VERSION} AS build ARG TARGET_DIR=dspace-installer WORKDIR /app # The dspace-installer directory will be written to /install @@ -25,7 +25,7 @@ RUN mvn --no-transfer-progress package && \ mvn clean # Step 2 - Run Ant Deploy -FROM eclipse-temurin:${JDK_VERSION} as ant_build +FROM eclipse-temurin:${JDK_VERSION} AS ant_build ARG TARGET_DIR=dspace-installer # COPY the /install directory from 'build' container to /dspace-src in this container COPY --from=build /install /dspace-src From 4d014778823ef5c835f7a66df0c9d1a8c688d073 Mon Sep 17 00:00:00 2001 From: Sascha Szott Date: Wed, 17 Jul 2024 15:35:21 +0200 Subject: [PATCH 007/979] fix FromAsCasing warning (cherry picked from commit ac43ec48edc408d3a08ea47bb0c9572bf23e39d4) --- Dockerfile.test | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Dockerfile.test b/Dockerfile.test index cdfd5e83af5f..f3acef00e825 100644 --- a/Dockerfile.test +++ b/Dockerfile.test @@ -11,7 +11,7 @@ ARG JDK_VERSION=17 ARG DSPACE_VERSION=latest # Step 1 - Run Maven Build -FROM dspace/dspace-dependencies:${DSPACE_VERSION} as build +FROM dspace/dspace-dependencies:${DSPACE_VERSION} AS build ARG TARGET_DIR=dspace-installer WORKDIR /app # The dspace-installer directory will be written to /install @@ -30,7 +30,7 @@ RUN mvn --no-transfer-progress package && \ RUN rm -rf /install/webapps/server/ # Step 2 - Run Ant Deploy -FROM eclipse-temurin:${JDK_VERSION} as ant_build +FROM eclipse-temurin:${JDK_VERSION} AS ant_build ARG TARGET_DIR=dspace-installer # COPY the /install directory from 'build' container to /dspace-src in this container COPY --from=build /install /dspace-src From ff280bef24ce7cca88dcbb0d426b35763a5bd680 Mon Sep 17 00:00:00 2001 From: Sascha Szott Date: Wed, 17 Jul 2024 15:35:54 +0200 Subject: [PATCH 008/979] fix FromAsCasing warning (cherry picked from commit bed23969199549444d65b43f21315a33f9dd4cab) --- Dockerfile.dependencies | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile.dependencies b/Dockerfile.dependencies index 2ca4d3040e98..f3bf1f833205 100644 --- a/Dockerfile.dependencies +++ b/Dockerfile.dependencies @@ -7,7 +7,7 @@ ARG JDK_VERSION=17 # Step 1 - Run Maven Build -FROM maven:3-eclipse-temurin-${JDK_VERSION} as build +FROM maven:3-eclipse-temurin-${JDK_VERSION} AS build ARG TARGET_DIR=dspace-installer WORKDIR /app # Create the 'dspace' user account & home directory From 83f3ea4d052859994f66b881e4bd692dd552e576 Mon Sep 17 00:00:00 2001 From: Sascha Szott Date: Wed, 17 Jul 2024 15:33:38 +0200 Subject: [PATCH 009/979] fix FromAsCasing warning --- Dockerfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index ee48dec5083c..9d89710fe11c 100644 --- a/Dockerfile +++ b/Dockerfile @@ -8,7 +8,7 @@ ARG JDK_VERSION=11 # Step 1 - Run Maven Build -FROM dspace/dspace-dependencies:dspace-7_x as build +FROM dspace/dspace-dependencies:dspace-7_x AS build ARG TARGET_DIR=dspace-installer WORKDIR /app # The dspace-installer directory will be written to /install @@ -28,7 +28,7 @@ RUN mvn --no-transfer-progress package ${MAVEN_FLAGS} && \ mvn clean # Step 2 - Run Ant Deploy -FROM eclipse-temurin:${JDK_VERSION} as ant_build +FROM eclipse-temurin:${JDK_VERSION} AS ant_build ARG TARGET_DIR=dspace-installer # COPY the /install directory from 'build' container to /dspace-src in this container COPY --from=build /install /dspace-src From f340dfadbec9143009fbb670296c500e05ee00e9 Mon Sep 17 00:00:00 2001 From: Sascha Szott Date: Wed, 17 Jul 2024 15:34:52 +0200 Subject: [PATCH 010/979] fix FromAsCasing warning --- Dockerfile.cli | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Dockerfile.cli b/Dockerfile.cli index 53040a2fad89..8a69b32e2dc6 100644 --- a/Dockerfile.cli +++ b/Dockerfile.cli @@ -8,7 +8,7 @@ ARG JDK_VERSION=11 # Step 1 - Run Maven Build -FROM dspace/dspace-dependencies:dspace-7_x as build +FROM dspace/dspace-dependencies:dspace-7_x AS build ARG TARGET_DIR=dspace-installer WORKDIR /app # The dspace-installer directory will be written to /install @@ -24,7 +24,7 @@ RUN mvn --no-transfer-progress package && \ mvn clean # Step 2 - Run Ant Deploy -FROM eclipse-temurin:${JDK_VERSION} as ant_build +FROM eclipse-temurin:${JDK_VERSION} AS ant_build ARG TARGET_DIR=dspace-installer # COPY the /install directory from 'build' container to /dspace-src in this container COPY --from=build /install /dspace-src From 9b6b7ded97d542f034f95159ce402ecb527a2e7a Mon Sep 17 00:00:00 2001 From: Sascha Szott Date: Wed, 17 Jul 2024 15:35:21 +0200 Subject: [PATCH 011/979] fix FromAsCasing warning --- Dockerfile.test | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Dockerfile.test b/Dockerfile.test index f6f8c1a290f9..031394ad256c 100644 --- a/Dockerfile.test +++ b/Dockerfile.test @@ -10,7 +10,7 @@ ARG JDK_VERSION=11 # Step 1 - Run Maven Build -FROM dspace/dspace-dependencies:dspace-7_x as build +FROM dspace/dspace-dependencies:dspace-7_x AS build ARG TARGET_DIR=dspace-installer WORKDIR /app # The dspace-installer directory will be written to /install @@ -27,7 +27,7 @@ RUN mvn --no-transfer-progress package -Pdspace-rest && \ mvn clean # Step 2 - Run Ant Deploy -FROM eclipse-temurin:${JDK_VERSION} as ant_build +FROM eclipse-temurin:${JDK_VERSION} AS ant_build ARG TARGET_DIR=dspace-installer # COPY the /install directory from 'build' container to /dspace-src in this container COPY --from=build /install /dspace-src From e4209d5d0b6ac0a301ccf080136e05005e68af11 Mon Sep 17 00:00:00 2001 From: Sascha Szott Date: Wed, 17 Jul 2024 15:35:54 +0200 Subject: [PATCH 012/979] fix FromAsCasing warning --- Dockerfile.dependencies | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile.dependencies b/Dockerfile.dependencies index 1400b356d418..123206ea5887 100644 --- a/Dockerfile.dependencies +++ b/Dockerfile.dependencies @@ -7,7 +7,7 @@ ARG JDK_VERSION=11 # Step 1 - Run Maven Build -FROM maven:3-eclipse-temurin-${JDK_VERSION} as build +FROM maven:3-eclipse-temurin-${JDK_VERSION} AS build ARG TARGET_DIR=dspace-installer WORKDIR /app # Create the 'dspace' user account & home directory From 03cff072c30888979e89a8c2f8adc4900f269686 Mon Sep 17 00:00:00 2001 From: Jens Vannerum Date: Mon, 22 Jul 2024 15:08:59 +0200 Subject: [PATCH 013/979] 116542: fix issues with CSV importing and the Any language being set on metadata values --- .../org/dspace/administer/StructBuilder.java | 2 +- .../org/dspace/app/bulkedit/DSpaceCSV.java | 18 ++++++ .../java/org/dspace/content/Collection.java | 2 +- .../org/dspace/content/MetadataValue.java | 4 ++ .../dspace/app/bulkedit/MetadataImportIT.java | 27 +++++++- .../builder/AbstractDSpaceObjectBuilder.java | 3 +- .../dspace/content/ItemComparatorTest.java | 64 +++++++++---------- .../java/org/dspace/content/ItemTest.java | 38 +++++------ .../content/dao/RelationshipDAOImplTest.java | 4 +- .../dao/RelationshipTypeDAOImplTest.java | 4 +- .../org/dspace/app/rest/PatchMetadataIT.java | 2 +- .../rest/RelationshipRestRepositoryIT.java | 26 ++++---- 12 files changed, 120 insertions(+), 74 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/administer/StructBuilder.java b/dspace-api/src/main/java/org/dspace/administer/StructBuilder.java index 13a1b3b5bbf8..8bbcfe0ff753 100644 --- a/dspace-api/src/main/java/org/dspace/administer/StructBuilder.java +++ b/dspace-api/src/main/java/org/dspace/administer/StructBuilder.java @@ -802,7 +802,7 @@ private static Element[] handleCollections(Context context, // default the short description to the empty string collectionService.setMetadataSingleValue(context, collection, - MD_SHORT_DESCRIPTION, Item.ANY, " "); + MD_SHORT_DESCRIPTION, null, " "); // import the rest of the metadata for (Map.Entry entry : collectionMap.entrySet()) { 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 cbc052b5573f..3533a2397b3d 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 @@ -188,6 +188,15 @@ public DSpaceCSV(InputStream inputStream, Context c) throws Exception { // Verify that the heading is valid in the metadata registry String[] clean = element.split("\\["); String[] parts = clean[0].split("\\."); + // Check language if present, if it's ANY then throw an exception + if (clean.length > 1 && clean[1].equals(Item.ANY + "]")) { + throw new MetadataImportInvalidHeadingException("Language ANY (*) was found in the heading " + + "of the metadata value to import, " + + "this should never be the case", + MetadataImportInvalidHeadingException.ENTRY, + columnCounter); + + } if (parts.length < 2) { throw new MetadataImportInvalidHeadingException(element, @@ -223,6 +232,15 @@ public DSpaceCSV(InputStream inputStream, Context c) throws Exception { } } + // Verify there isn’t already a header that is the same; if it already exists, + // throw MetadataImportInvalidHeadingException + String header = authorityPrefix + element; + if (headings.contains(header)) { + throw new MetadataImportInvalidHeadingException("Duplicate heading found: " + header, + MetadataImportInvalidHeadingException.ENTRY, + columnCounter); + } + // Store the heading headings.add(authorityPrefix + element); } diff --git a/dspace-api/src/main/java/org/dspace/content/Collection.java b/dspace-api/src/main/java/org/dspace/content/Collection.java index ffec3b45cc87..0036230a8b9d 100644 --- a/dspace-api/src/main/java/org/dspace/content/Collection.java +++ b/dspace-api/src/main/java/org/dspace/content/Collection.java @@ -230,7 +230,7 @@ public String getLicenseCollection() { * @throws SQLException if database error */ public void setLicense(Context context, String license) throws SQLException { - getCollectionService().setMetadataSingleValue(context, this, MD_LICENSE, Item.ANY, license); + getCollectionService().setMetadataSingleValue(context, this, MD_LICENSE, null, license); } /** diff --git a/dspace-api/src/main/java/org/dspace/content/MetadataValue.java b/dspace-api/src/main/java/org/dspace/content/MetadataValue.java index 9ff3cb9ec2af..56ed81ecbac8 100644 --- a/dspace-api/src/main/java/org/dspace/content/MetadataValue.java +++ b/dspace-api/src/main/java/org/dspace/content/MetadataValue.java @@ -20,6 +20,7 @@ import javax.persistence.SequenceGenerator; import javax.persistence.Table; +import org.apache.commons.lang3.StringUtils; import org.dspace.core.Context; import org.dspace.core.ReloadableEntity; import org.hibernate.annotations.Type; @@ -142,6 +143,9 @@ public String getLanguage() { * @param language new language */ public void setLanguage(String language) { + if (StringUtils.equals(language, Item.ANY)) { + language = null; + } this.language = language; } diff --git a/dspace-api/src/test/java/org/dspace/app/bulkedit/MetadataImportIT.java b/dspace-api/src/test/java/org/dspace/app/bulkedit/MetadataImportIT.java index ac5e1e6ae6b9..481aa09e531f 100644 --- a/dspace-api/src/test/java/org/dspace/app/bulkedit/MetadataImportIT.java +++ b/dspace-api/src/test/java/org/dspace/app/bulkedit/MetadataImportIT.java @@ -75,6 +75,31 @@ public void setUp() throws Exception { context.restoreAuthSystemState(); } + @Test + public void metadataImportTestWithDuplicateHeader() { + String[] csv = {"id,collection,dc.title,dc.title,dc.contributor.author", + "+," + collection.getHandle() + ",\"Test Import 1\",\"Test Import 2\"," + "\"Donald, SmithImported\"," + + "+," + collection.getHandle() + ",\"Test Import 3\",\"Test Import 4\"," + "\"Donald, SmithImported\""}; + // Should throw an exception because of duplicate header + try { + performImportScript(csv); + } catch (Exception e) { + assertTrue(e instanceof MetadataImportInvalidHeadingException); + } + } + + @Test + public void metadataImportTestWithAnyLanguage() { + String[] csv = {"id,collection,dc.title[*],dc.contributor.author", + "+," + collection.getHandle() + ",\"Test Import 1\"," + "\"Donald, SmithImported\""}; + // Should throw an exception because of invalid ANY language (*) in metadata field + try { + performImportScript(csv); + } catch (Exception e) { + assertTrue(e instanceof MetadataImportInvalidHeadingException); + } + } + @Test public void metadataImportTest() throws Exception { String[] csv = {"id,collection,dc.title,dc.contributor.author", @@ -228,7 +253,7 @@ public void metadataImportRemovingValueTest() throws Exception { itemService.getMetadata(item, "dc", "contributor", "author", Item.ANY).get(0).getValue(), "TestAuthorToRemove")); - String[] csv = {"id,collection,dc.title,dc.contributor.author[*]", + String[] csv = {"id,collection,dc.title,dc.contributor.author", item.getID().toString() + "," + personCollection.getHandle() + "," + item.getName() + ","}; performImportScript(csv); item = findItemByName("title"); diff --git a/dspace-api/src/test/java/org/dspace/builder/AbstractDSpaceObjectBuilder.java b/dspace-api/src/test/java/org/dspace/builder/AbstractDSpaceObjectBuilder.java index ff1083d318d9..c6106c3a75e3 100644 --- a/dspace-api/src/test/java/org/dspace/builder/AbstractDSpaceObjectBuilder.java +++ b/dspace-api/src/test/java/org/dspace/builder/AbstractDSpaceObjectBuilder.java @@ -14,7 +14,6 @@ import org.dspace.authorize.AuthorizeException; import org.dspace.authorize.ResourcePolicy; import org.dspace.content.DSpaceObject; -import org.dspace.content.Item; import org.dspace.content.service.DSpaceObjectService; import org.dspace.core.Constants; import org.dspace.core.Context; @@ -103,7 +102,7 @@ protected > B setMetadataSingleValue(fi final String qualifier, final String value) { try { - getService().setMetadataSingleValue(context, dso, schema, element, qualifier, Item.ANY, value); + getService().setMetadataSingleValue(context, dso, schema, element, qualifier, null, value); } catch (Exception e) { return handleException(e); } diff --git a/dspace-api/src/test/java/org/dspace/content/ItemComparatorTest.java b/dspace-api/src/test/java/org/dspace/content/ItemComparatorTest.java index 54ff9ce02624..be670d9b5097 100644 --- a/dspace-api/src/test/java/org/dspace/content/ItemComparatorTest.java +++ b/dspace-api/src/test/java/org/dspace/content/ItemComparatorTest.java @@ -141,37 +141,37 @@ public void testCompare() throws SQLException { assertTrue("testCompare 0", result == 0); ic = new ItemComparator("test", "one", Item.ANY, true); - itemService.addMetadata(context, one, "dc", "test", "one", Item.ANY, "1"); + itemService.addMetadata(context, one, "dc", "test", "one", null, "1"); result = ic.compare(one, two); assertTrue("testCompare 1", result >= 1); itemService.clearMetadata(context, one, "dc", "test", "one", Item.ANY); ic = new ItemComparator("test", "one", Item.ANY, true); - itemService.addMetadata(context, two, "dc", "test", "one", Item.ANY, "1"); + itemService.addMetadata(context, two, "dc", "test", "one", null, "1"); result = ic.compare(one, two); assertTrue("testCompare 2", result <= -1); itemService.clearMetadata(context, two, "dc", "test", "one", Item.ANY); //value in both items ic = new ItemComparator("test", "one", Item.ANY, true); - itemService.addMetadata(context, one, "dc", "test", "one", Item.ANY, "1"); - itemService.addMetadata(context, two, "dc", "test", "one", Item.ANY, "2"); + itemService.addMetadata(context, one, "dc", "test", "one", null, "1"); + itemService.addMetadata(context, two, "dc", "test", "one", null, "2"); result = ic.compare(one, two); assertTrue("testCompare 3", result <= -1); itemService.clearMetadata(context, one, "dc", "test", "one", Item.ANY); itemService.clearMetadata(context, two, "dc", "test", "one", Item.ANY); ic = new ItemComparator("test", "one", Item.ANY, true); - itemService.addMetadata(context, one, "dc", "test", "one", Item.ANY, "1"); - itemService.addMetadata(context, two, "dc", "test", "one", Item.ANY, "1"); + itemService.addMetadata(context, one, "dc", "test", "one", null, "1"); + itemService.addMetadata(context, two, "dc", "test", "one", null, "1"); result = ic.compare(one, two); assertTrue("testCompare 4", result == 0); itemService.clearMetadata(context, one, "dc", "test", "one", Item.ANY); itemService.clearMetadata(context, two, "dc", "test", "one", Item.ANY); ic = new ItemComparator("test", "one", Item.ANY, true); - itemService.addMetadata(context, one, "dc", "test", "one", Item.ANY, "2"); - itemService.addMetadata(context, two, "dc", "test", "one", Item.ANY, "1"); + itemService.addMetadata(context, one, "dc", "test", "one", null, "2"); + itemService.addMetadata(context, two, "dc", "test", "one", null, "1"); result = ic.compare(one, two); assertTrue("testCompare 5", result >= 1); itemService.clearMetadata(context, one, "dc", "test", "one", Item.ANY); @@ -179,60 +179,60 @@ public void testCompare() throws SQLException { //multiple values (min, max) ic = new ItemComparator("test", "one", Item.ANY, true); - itemService.addMetadata(context, one, "dc", "test", "one", Item.ANY, "0"); - itemService.addMetadata(context, one, "dc", "test", "one", Item.ANY, "1"); - itemService.addMetadata(context, two, "dc", "test", "one", Item.ANY, "2"); - itemService.addMetadata(context, two, "dc", "test", "one", Item.ANY, "3"); + itemService.addMetadata(context, one, "dc", "test", "one", null, "0"); + itemService.addMetadata(context, one, "dc", "test", "one", null, "1"); + itemService.addMetadata(context, two, "dc", "test", "one", null, "2"); + itemService.addMetadata(context, two, "dc", "test", "one", null, "3"); result = ic.compare(one, two); assertTrue("testCompare 3", result <= -1); itemService.clearMetadata(context, one, "dc", "test", "one", Item.ANY); itemService.clearMetadata(context, two, "dc", "test", "one", Item.ANY); ic = new ItemComparator("test", "one", Item.ANY, true); - itemService.addMetadata(context, one, "dc", "test", "one", Item.ANY, "0"); - itemService.addMetadata(context, one, "dc", "test", "one", Item.ANY, "1"); - itemService.addMetadata(context, two, "dc", "test", "one", Item.ANY, "-1"); - itemService.addMetadata(context, two, "dc", "test", "one", Item.ANY, "1"); + itemService.addMetadata(context, one, "dc", "test", "one", null, "0"); + itemService.addMetadata(context, one, "dc", "test", "one", null, "1"); + itemService.addMetadata(context, two, "dc", "test", "one", null, "-1"); + itemService.addMetadata(context, two, "dc", "test", "one", null, "1"); result = ic.compare(one, two); assertTrue("testCompare 4", result == 0); itemService.clearMetadata(context, one, "dc", "test", "one", Item.ANY); itemService.clearMetadata(context, two, "dc", "test", "one", Item.ANY); ic = new ItemComparator("test", "one", Item.ANY, true); - itemService.addMetadata(context, one, "dc", "test", "one", Item.ANY, "1"); - itemService.addMetadata(context, one, "dc", "test", "one", Item.ANY, "2"); - itemService.addMetadata(context, two, "dc", "test", "one", Item.ANY, "1"); - itemService.addMetadata(context, two, "dc", "test", "one", Item.ANY, "-1"); + itemService.addMetadata(context, one, "dc", "test", "one", null, "1"); + itemService.addMetadata(context, one, "dc", "test", "one", null, "2"); + itemService.addMetadata(context, two, "dc", "test", "one", null, "1"); + itemService.addMetadata(context, two, "dc", "test", "one", null, "-1"); result = ic.compare(one, two); assertTrue("testCompare 5", result >= 1); itemService.clearMetadata(context, one, "dc", "test", "one", Item.ANY); itemService.clearMetadata(context, two, "dc", "test", "one", Item.ANY); ic = new ItemComparator("test", "one", Item.ANY, false); - itemService.addMetadata(context, one, "dc", "test", "one", Item.ANY, "1"); - itemService.addMetadata(context, one, "dc", "test", "one", Item.ANY, "2"); - itemService.addMetadata(context, two, "dc", "test", "one", Item.ANY, "2"); - itemService.addMetadata(context, two, "dc", "test", "one", Item.ANY, "3"); + itemService.addMetadata(context, one, "dc", "test", "one", null, "1"); + itemService.addMetadata(context, one, "dc", "test", "one", null, "2"); + itemService.addMetadata(context, two, "dc", "test", "one", null, "2"); + itemService.addMetadata(context, two, "dc", "test", "one", null, "3"); result = ic.compare(one, two); assertTrue("testCompare 3", result <= -1); itemService.clearMetadata(context, one, "dc", "test", "one", Item.ANY); itemService.clearMetadata(context, two, "dc", "test", "one", Item.ANY); ic = new ItemComparator("test", "one", Item.ANY, false); - itemService.addMetadata(context, one, "dc", "test", "one", Item.ANY, "1"); - itemService.addMetadata(context, one, "dc", "test", "one", Item.ANY, "2"); - itemService.addMetadata(context, two, "dc", "test", "one", Item.ANY, "1"); - itemService.addMetadata(context, two, "dc", "test", "one", Item.ANY, "5"); + itemService.addMetadata(context, one, "dc", "test", "one", null, "1"); + itemService.addMetadata(context, one, "dc", "test", "one", null, "2"); + itemService.addMetadata(context, two, "dc", "test", "one", null, "1"); + itemService.addMetadata(context, two, "dc", "test", "one", null, "5"); result = ic.compare(one, two); assertTrue("testCompare 4", result == 0); itemService.clearMetadata(context, one, "dc", "test", "one", Item.ANY); itemService.clearMetadata(context, two, "dc", "test", "one", Item.ANY); ic = new ItemComparator("test", "one", Item.ANY, false); - itemService.addMetadata(context, one, "dc", "test", "one", Item.ANY, "2"); - itemService.addMetadata(context, one, "dc", "test", "one", Item.ANY, "3"); - itemService.addMetadata(context, two, "dc", "test", "one", Item.ANY, "1"); - itemService.addMetadata(context, two, "dc", "test", "one", Item.ANY, "4"); + itemService.addMetadata(context, one, "dc", "test", "one", null, "2"); + itemService.addMetadata(context, one, "dc", "test", "one", null, "3"); + itemService.addMetadata(context, two, "dc", "test", "one", null, "1"); + itemService.addMetadata(context, two, "dc", "test", "one", null, "4"); result = ic.compare(one, two); assertTrue("testCompare 5", result >= 1); itemService.clearMetadata(context, one, "dc", "test", "one", Item.ANY); diff --git a/dspace-api/src/test/java/org/dspace/content/ItemTest.java b/dspace-api/src/test/java/org/dspace/content/ItemTest.java index 15e425e23a2a..24d6e515bbdf 100644 --- a/dspace-api/src/test/java/org/dspace/content/ItemTest.java +++ b/dspace-api/src/test/java/org/dspace/content/ItemTest.java @@ -516,11 +516,11 @@ public void testAddMetadata_5args_1() throws SQLException { String schema = "dc"; String element = "contributor"; String qualifier = "author"; - String lang = Item.ANY; + String lang = null; String[] values = {"value0", "value1"}; itemService.addMetadata(context, it, schema, element, qualifier, lang, Arrays.asList(values)); - List dc = itemService.getMetadata(it, schema, element, qualifier, lang); + List dc = itemService.getMetadata(it, schema, element, qualifier, Item.ANY); assertThat("testAddMetadata_5args_1 0", dc, notNullValue()); assertTrue("testAddMetadata_5args_1 1", dc.size() == 2); assertThat("testAddMetadata_5args_1 2", dc.get(0).getMetadataField().getMetadataSchema().getName(), @@ -550,13 +550,13 @@ public void testAddMetadata_7args_1_authority() String schema = "dc"; String element = "language"; String qualifier = "iso"; - String lang = Item.ANY; + String lang = null; List values = Arrays.asList("en_US", "en"); List authorities = Arrays.asList("accepted", "uncertain"); List confidences = Arrays.asList(0, 0); itemService.addMetadata(context, it, schema, element, qualifier, lang, values, authorities, confidences); - List dc = itemService.getMetadata(it, schema, element, qualifier, lang); + List dc = itemService.getMetadata(it, schema, element, qualifier, Item.ANY); assertThat("testAddMetadata_7args_1 0", dc, notNullValue()); assertTrue("testAddMetadata_7args_1 1", dc.size() == 2); assertThat("testAddMetadata_7args_1 2", dc.get(0).getMetadataField().getMetadataSchema().getName(), @@ -587,13 +587,13 @@ public void testAddMetadata_7args_1_noauthority() throws SQLException { String schema = "dc"; String element = "contributor"; String qualifier = "author"; - String lang = Item.ANY; + String lang = null; List values = Arrays.asList("value0", "value1"); List authorities = Arrays.asList("auth0", "auth2"); List confidences = Arrays.asList(0, 0); itemService.addMetadata(context, it, schema, element, qualifier, lang, values, authorities, confidences); - List dc = itemService.getMetadata(it, schema, element, qualifier, lang); + List dc = itemService.getMetadata(it, schema, element, qualifier, Item.ANY); assertThat("testAddMetadata_7args_1 0", dc, notNullValue()); assertTrue("testAddMetadata_7args_1 1", dc.size() == 2); assertThat("testAddMetadata_7args_1 2", dc.get(0).getMetadataField().getMetadataSchema().getName(), @@ -622,11 +622,11 @@ public void testAddMetadata_5args_2() throws SQLException { String schema = "dc"; String element = "contributor"; String qualifier = "author"; - String lang = Item.ANY; + String lang = null; List values = Arrays.asList("value0", "value1"); itemService.addMetadata(context, it, schema, element, qualifier, lang, values); - List dc = itemService.getMetadata(it, schema, element, qualifier, lang); + List dc = itemService.getMetadata(it, schema, element, qualifier, Item.ANY); assertThat("testAddMetadata_5args_2 0", dc, notNullValue()); assertTrue("testAddMetadata_5args_2 1", dc.size() == 2); assertThat("testAddMetadata_5args_2 2", dc.get(0).getMetadataField().getMetadataSchema().getName(), @@ -654,13 +654,13 @@ public void testAddMetadata_7args_2_authority() throws SQLException { String schema = "dc"; String element = "language"; String qualifier = "iso"; - String lang = Item.ANY; + String lang = null; String values = "en"; String authorities = "accepted"; int confidences = 0; itemService.addMetadata(context, it, schema, element, qualifier, lang, values, authorities, confidences); - List dc = itemService.getMetadata(it, schema, element, qualifier, lang); + List dc = itemService.getMetadata(it, schema, element, qualifier, Item.ANY); assertThat("testAddMetadata_7args_2 0", dc, notNullValue()); assertTrue("testAddMetadata_7args_2 1", dc.size() == 1); assertThat("testAddMetadata_7args_2 2", dc.get(0).getMetadataField().getMetadataSchema().getName(), @@ -683,13 +683,13 @@ public void testAddMetadata_7args_2_noauthority() throws SQLException { String schema = "dc"; String element = "contributor"; String qualifier = "editor"; - String lang = Item.ANY; + String lang = null; String values = "value0"; String authorities = "auth0"; int confidences = 0; itemService.addMetadata(context, it, schema, element, qualifier, lang, values, authorities, confidences); - List dc = itemService.getMetadata(it, schema, element, qualifier, lang); + List dc = itemService.getMetadata(it, schema, element, qualifier, Item.ANY); assertThat("testAddMetadata_7args_2 0", dc, notNullValue()); assertTrue("testAddMetadata_7args_2 1", dc.size() == 1); assertThat("testAddMetadata_7args_2 2", dc.get(0).getMetadataField().getMetadataSchema().getName(), @@ -710,13 +710,13 @@ public void testClearMetadata() throws SQLException { String schema = "dc"; String element = "contributor"; String qualifier = "author"; - String lang = Item.ANY; + String lang = null; String values = "value0"; itemService.addMetadata(context, it, schema, element, qualifier, lang, values); - itemService.clearMetadata(context, it, schema, element, qualifier, lang); + itemService.clearMetadata(context, it, schema, element, qualifier, Item.ANY); - List dc = itemService.getMetadata(it, schema, element, qualifier, lang); + List dc = itemService.getMetadata(it, schema, element, qualifier, Item.ANY); assertThat("testClearMetadata 0", dc, notNullValue()); assertTrue("testClearMetadata 1", dc.size() == 0); } @@ -758,11 +758,11 @@ public void testGetCollections() throws Exception { context.turnOffAuthorisationSystem(); Collection collection = collectionService.create(context, owningCommunity); collectionService.setMetadataSingleValue(context, collection, MetadataSchemaEnum.DC.getName(), - "title", null, Item.ANY, "collection B"); + "title", null, null, "collection B"); it.addCollection(collection); collection = collectionService.create(context, owningCommunity); collectionService.setMetadataSingleValue(context, collection, MetadataSchemaEnum.DC.getName(), - "title", null, Item.ANY, "collection A"); + "title", null, null, "collection A"); it.addCollection(collection); context.restoreAuthSystemState(); assertThat("testGetCollections 0", it.getCollections(), notNullValue()); @@ -1602,7 +1602,7 @@ public void testFindByMetadataField() throws Exception { // add new metadata to item context.turnOffAuthorisationSystem(); - itemService.addMetadata(context, it, schema, element, qualifier, Item.ANY, value); + itemService.addMetadata(context, it, schema, element, qualifier, null, value); itemService.update(context, it); context.restoreAuthSystemState(); @@ -1667,7 +1667,7 @@ public void testFindByAuthorityValue() throws Exception { // add new metadata (with authority) to item context.turnOffAuthorisationSystem(); - itemService.addMetadata(context, it, schema, element, qualifier, Item.ANY, value, authority, confidence); + itemService.addMetadata(context, it, schema, element, qualifier, null, value, authority, confidence); itemService.update(context, it); context.restoreAuthSystemState(); diff --git a/dspace-api/src/test/java/org/dspace/content/dao/RelationshipDAOImplTest.java b/dspace-api/src/test/java/org/dspace/content/dao/RelationshipDAOImplTest.java index b6f5da6be065..05e5ba0e1162 100644 --- a/dspace-api/src/test/java/org/dspace/content/dao/RelationshipDAOImplTest.java +++ b/dspace-api/src/test/java/org/dspace/content/dao/RelationshipDAOImplTest.java @@ -87,8 +87,8 @@ public void init() { WorkspaceItem workspaceItemTwo = workspaceItemService.create(context, collection, false); itemOne = installItemService.installItem(context, workspaceItem); itemTwo = installItemService.installItem(context, workspaceItemTwo); - itemService.addMetadata(context, itemOne, "dspace", "entity", "type", Item.ANY, "Publication"); - itemService.addMetadata(context, itemTwo, "dspace", "entity", "type", Item.ANY, "Person"); + itemService.addMetadata(context, itemOne, "dspace", "entity", "type", null, "Publication"); + itemService.addMetadata(context, itemTwo, "dspace", "entity", "type", null, "Person"); itemService.update(context, itemOne); itemService.update(context, itemTwo); entityTypeOne = entityTypeService.create(context, "Person"); diff --git a/dspace-api/src/test/java/org/dspace/content/dao/RelationshipTypeDAOImplTest.java b/dspace-api/src/test/java/org/dspace/content/dao/RelationshipTypeDAOImplTest.java index 3fff6fec4762..4d405c6a27e7 100644 --- a/dspace-api/src/test/java/org/dspace/content/dao/RelationshipTypeDAOImplTest.java +++ b/dspace-api/src/test/java/org/dspace/content/dao/RelationshipTypeDAOImplTest.java @@ -82,8 +82,8 @@ public void init() { WorkspaceItem workspaceItemTwo = workspaceItemService.create(context, collection, false); itemOne = installItemService.installItem(context, workspaceItem); itemTwo = installItemService.installItem(context, workspaceItemTwo); - itemService.addMetadata(context, itemOne, "dspace", "entity", "type", Item.ANY, "Publication"); - itemService.addMetadata(context, itemTwo, "dspace", "entity", "type", Item.ANY, "Person"); + itemService.addMetadata(context, itemOne, "dspace", "entity", "type", null, "Publication"); + itemService.addMetadata(context, itemTwo, "dspace", "entity", "type", null, "Person"); itemService.update(context, itemOne); itemService.update(context, itemTwo); entityTypeOne = entityTypeService.create(context, "Person"); diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/PatchMetadataIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/PatchMetadataIT.java index b78436f1fb38..f3ccfe9fc10e 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/PatchMetadataIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/PatchMetadataIT.java @@ -247,7 +247,7 @@ private void initSimplePublicationItem() throws Exception { for (String author : authorsOriginalOrder) { itemService.addMetadata( - context, publicationItem, "dc", "contributor", "author", Item.ANY, author + context, publicationItem, "dc", "contributor", "author", null, author ); } diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/RelationshipRestRepositoryIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/RelationshipRestRepositoryIT.java index d8e53c770c70..264fb0d246ad 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/RelationshipRestRepositoryIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/RelationshipRestRepositoryIT.java @@ -819,7 +819,7 @@ public void addRelationshipsAndMetadataToValidatePlaceTest() throws Exception { // Make sure we grab the latest instance of the Item from the database publication1 = itemService.find(context, publication1.getID()); // Add a plain text dc.contributor.author value - itemService.addMetadata(context, publication1, "dc", "contributor", "author", Item.ANY, "plain text"); + itemService.addMetadata(context, publication1, "dc", "contributor", "author", null, "plain text"); itemService.update(context, publication1); List list = itemService.getMetadata(publication1, "dc", "contributor", "author", Item.ANY); @@ -885,7 +885,7 @@ public void addRelationshipsAndMetadataToValidatePlaceTest() throws Exception { // Ensure we have the latest instance of the Item from the database publication1 = itemService.find(context, publication1.getID()); // Add a fourth dc.contributor.author mdv - itemService.addMetadata(context, publication1, "dc", "contributor", "author", Item.ANY, "plain text two"); + itemService.addMetadata(context, publication1, "dc", "contributor", "author", null, "plain text two"); itemService.update(context, publication1); context.restoreAuthSystemState(); @@ -954,7 +954,7 @@ public void addRelationshipsAndMetadataToValidatePlaceTest() throws Exception { context.turnOffAuthorisationSystem(); // The following additions of Metadata will perform the same sequence of logic and tests as described above publication1 = itemService.find(context, publication1.getID()); - itemService.addMetadata(context, publication1, "dc", "contributor", "author", Item.ANY, "plain text three"); + itemService.addMetadata(context, publication1, "dc", "contributor", "author", null, "plain text three"); itemService.update(context, publication1); context.restoreAuthSystemState(); @@ -984,10 +984,10 @@ public void addRelationshipsAndMetadataToValidatePlaceTest() throws Exception { context.turnOffAuthorisationSystem(); publication1 = itemService.find(context, publication1.getID()); - itemService.addMetadata(context, publication1, "dc", "contributor", "author", Item.ANY, "plain text four"); - itemService.addMetadata(context, publication1, "dc", "contributor", "author", Item.ANY, "plain text five"); - itemService.addMetadata(context, publication1, "dc", "contributor", "author", Item.ANY, "plain text six"); - itemService.addMetadata(context, publication1, "dc", "contributor", "author", Item.ANY, "plain text seven"); + itemService.addMetadata(context, publication1, "dc", "contributor", "author", null, "plain text four"); + itemService.addMetadata(context, publication1, "dc", "contributor", "author", null, "plain text five"); + itemService.addMetadata(context, publication1, "dc", "contributor", "author", null, "plain text six"); + itemService.addMetadata(context, publication1, "dc", "contributor", "author", null, "plain text seven"); itemService.update(context, publication1); context.restoreAuthSystemState(); @@ -1115,7 +1115,7 @@ public void deleteMetadataValueAndValidatePlace() throws Exception { publication1 = itemService.find(context, publication1.getID()); // Add a plain text metadatavalue to the publication // After this addition, the list of authors should like like "Donald Smith", "plain text" - itemService.addMetadata(context, publication1, "dc", "contributor", "author", Item.ANY, "plain text"); + itemService.addMetadata(context, publication1, "dc", "contributor", "author", null, "plain text"); itemService.update(context, publication1); context.restoreAuthSystemState(); List list = itemService.getMetadata(publication1, "dc", "contributor", "author", Item.ANY); @@ -1158,7 +1158,7 @@ public void deleteMetadataValueAndValidatePlace() throws Exception { // Get the publication from the DB again to ensure that we have the latest object publication1 = itemService.find(context, publication1.getID()); // Add a fourth metadata value to the publication - itemService.addMetadata(context, publication1, "dc", "contributor", "author", Item.ANY, "plain text two"); + itemService.addMetadata(context, publication1, "dc", "contributor", "author", null, "plain text two"); itemService.update(context, publication1); context.restoreAuthSystemState(); list = itemService.getMetadata(publication1, "dc", "contributor", "author", Item.ANY); @@ -1196,7 +1196,7 @@ public void deleteMetadataValueAndValidatePlace() throws Exception { context.turnOffAuthorisationSystem(); publication1 = itemService.find(context, publication1.getID()); // Create another plain text metadata value on the publication - itemService.addMetadata(context, publication1, "dc", "contributor", "author", Item.ANY, "plain text three"); + itemService.addMetadata(context, publication1, "dc", "contributor", "author", null, "plain text three"); itemService.update(context, publication1); context.restoreAuthSystemState(); list = itemService.getMetadata(publication1, "dc", "contributor", "author", Item.ANY); @@ -1324,7 +1324,7 @@ public void deleteRelationshipsAndValidatePlace() throws Exception { publication1 = itemService.find(context, publication1.getID()); // Add a plain text metadatavalue to the publication // After this addition, the list of authors should like like "Donald Smith", "plain text" - itemService.addMetadata(context, publication1, "dc", "contributor", "author", Item.ANY, "plain text"); + itemService.addMetadata(context, publication1, "dc", "contributor", "author", null, "plain text"); itemService.update(context, publication1); context.restoreAuthSystemState(); List list = itemService.getMetadata(publication1, "dc", "contributor", "author", Item.ANY); @@ -1368,7 +1368,7 @@ public void deleteRelationshipsAndValidatePlace() throws Exception { // Get the publication from the DB again to ensure that we have the latest object publication1 = itemService.find(context, publication1.getID()); // Add a fourth metadata value to the publication - itemService.addMetadata(context, publication1, "dc", "contributor", "author", Item.ANY, "plain text two"); + itemService.addMetadata(context, publication1, "dc", "contributor", "author", null, "plain text two"); itemService.update(context, publication1); context.restoreAuthSystemState(); list = itemService.getMetadata(publication1, "dc", "contributor", "author", Item.ANY); @@ -1406,7 +1406,7 @@ public void deleteRelationshipsAndValidatePlace() throws Exception { context.turnOffAuthorisationSystem(); publication1 = itemService.find(context, publication1.getID()); // Create another plain text metadata value on the publication - itemService.addMetadata(context, publication1, "dc", "contributor", "author", Item.ANY, "plain text three"); + itemService.addMetadata(context, publication1, "dc", "contributor", "author", null, "plain text three"); itemService.update(context, publication1); context.restoreAuthSystemState(); list = itemService.getMetadata(publication1, "dc", "contributor", "author", Item.ANY); From 7082e3259595dbcbd31f339c53a15c17cd47110b Mon Sep 17 00:00:00 2001 From: Jens Vannerum Date: Mon, 22 Jul 2024 15:18:44 +0200 Subject: [PATCH 014/979] 116542: resolve issues after merge with latest 7-x branch --- .../test/java/org/dspace/content/ItemTest.java | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/dspace-api/src/test/java/org/dspace/content/ItemTest.java b/dspace-api/src/test/java/org/dspace/content/ItemTest.java index 87f42cf42935..00dbf2994d98 100644 --- a/dspace-api/src/test/java/org/dspace/content/ItemTest.java +++ b/dspace-api/src/test/java/org/dspace/content/ItemTest.java @@ -544,7 +544,7 @@ public void testAddMetadata_5args_no_values() throws Exception { String schema = "dc"; String element = "contributor"; String qualifier = "author"; - String lang = Item.ANY; + String lang = null; String[] values = {}; itemService.addMetadata(context, it, schema, element, qualifier, lang, Arrays.asList(values)); fail("IllegalArgumentException expected"); @@ -632,7 +632,7 @@ public void testAddMetadata_7args_no_values() throws Exception { String schema = "dc"; String element = "contributor"; String qualifier = "author"; - String lang = Item.ANY; + String lang = null; List values = new ArrayList(); List authorities = new ArrayList(); List confidences = new ArrayList(); @@ -645,7 +645,7 @@ public void testAddMetadata_list_with_virtual_metadata() throws Exception { String schema = "dc"; String element = "contributor"; String qualifier = "author"; - String lang = Item.ANY; + String lang = null; // Create two fake virtual metadata ("virtual::[relationship-id]") values List values = new ArrayList<>(Arrays.asList("uuid-1", "uuid-2")); List authorities = new ArrayList<>(Arrays.asList(Constants.VIRTUAL_AUTHORITY_PREFIX + "relationship-1", @@ -674,7 +674,7 @@ public void testAddMetadata_list_with_virtual_metadata() throws Exception { assertEquals(1, valuesAdded.size()); // Get metadata and ensure new value is the ONLY ONE for this metadata field - List dc = itemService.getMetadata(it, schema, element, qualifier, lang); + List dc = itemService.getMetadata(it, schema, element, qualifier, Item.ANY); assertNotNull(dc); assertEquals(1, dc.size()); assertEquals(schema, dc.get(0).getMetadataField().getMetadataSchema().getName()); @@ -693,7 +693,7 @@ public void testAddMetadata_5args_2() throws SQLException { String schema = "dc"; String element = "contributor"; String qualifier = "author"; - String lang = Item.ANY; + String lang = null; String value = "value0"; itemService.addMetadata(context, it, schema, element, qualifier, lang, value); @@ -772,7 +772,7 @@ public void testAddMetadata_single_virtual_metadata() throws Exception { String schema = "dc"; String element = "contributor"; String qualifier = "author"; - String lang = Item.ANY; + String lang = null; // Create a single fake virtual metadata ("virtual::[relationship-id]") value String value = "uuid-1"; String authority = Constants.VIRTUAL_AUTHORITY_PREFIX + "relationship-1"; @@ -786,7 +786,7 @@ public void testAddMetadata_single_virtual_metadata() throws Exception { assertNull(valuesAdded); // Verify this metadata field does NOT exist on the item - List mv = itemService.getMetadata(it, schema, element, qualifier, lang); + List mv = itemService.getMetadata(it, schema, element, qualifier, Item.ANY); assertNotNull(mv); assertTrue(mv.isEmpty()); @@ -797,7 +797,7 @@ public void testAddMetadata_single_virtual_metadata() throws Exception { assertNull(valuesAdded); // Verify this metadata field does NOT exist on the item - mv = itemService.getMetadata(it, schema, element, qualifier, lang); + mv = itemService.getMetadata(it, schema, element, qualifier, Item.ANY); assertNotNull(mv); assertTrue(mv.isEmpty()); } From fff8cc3cb4f05ace42b035b3eb2c3a9b46f3b5f7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 22 Jul 2024 14:34:41 +0000 Subject: [PATCH 015/979] Bump dnsjava:dnsjava from 2.1.9 to 3.6.0 in /dspace-api Bumps [dnsjava:dnsjava](https://github.com/dnsjava/dnsjava) from 2.1.9 to 3.6.0. - [Release notes](https://github.com/dnsjava/dnsjava/releases) - [Changelog](https://github.com/dnsjava/dnsjava/blob/master/Changelog) - [Commits](https://github.com/dnsjava/dnsjava/compare/v2.1.9...v3.6.0) --- updated-dependencies: - dependency-name: dnsjava:dnsjava dependency-type: direct:production ... Signed-off-by: dependabot[bot] (cherry picked from commit 1775c88919cacf063788e4a6a0b67cdf1ef23fec) --- dspace-api/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dspace-api/pom.xml b/dspace-api/pom.xml index 16b151b092e5..bdf035179bee 100644 --- a/dspace-api/pom.xml +++ b/dspace-api/pom.xml @@ -629,7 +629,7 @@ dnsjava dnsjava - 2.1.9 + 3.6.0 From f41113e198f49091ceeaa09c2d9369771b6f15bf Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 22 Jul 2024 14:34:41 +0000 Subject: [PATCH 016/979] Bump dnsjava:dnsjava from 2.1.9 to 3.6.0 in /dspace-api Bumps [dnsjava:dnsjava](https://github.com/dnsjava/dnsjava) from 2.1.9 to 3.6.0. - [Release notes](https://github.com/dnsjava/dnsjava/releases) - [Changelog](https://github.com/dnsjava/dnsjava/blob/master/Changelog) - [Commits](https://github.com/dnsjava/dnsjava/compare/v2.1.9...v3.6.0) --- updated-dependencies: - dependency-name: dnsjava:dnsjava dependency-type: direct:production ... Signed-off-by: dependabot[bot] (cherry picked from commit 1775c88919cacf063788e4a6a0b67cdf1ef23fec) --- dspace-api/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dspace-api/pom.xml b/dspace-api/pom.xml index d2d2d123c350..dfb3f030ac0e 100644 --- a/dspace-api/pom.xml +++ b/dspace-api/pom.xml @@ -623,7 +623,7 @@ dnsjava dnsjava - 2.1.9 + 3.6.0 From 30b6eddeb66c0e89c951090d8c2b2d6d0f3a31ab Mon Sep 17 00:00:00 2001 From: Kim Shepherd Date: Tue, 23 Jul 2024 12:17:07 +0200 Subject: [PATCH 017/979] Update spider list URLs to satisfy cloudflare redirects Update spider list URLs to satisfy cloudflare redirects Update spider list URLs to satisfy cloudflare redirects (cherry picked from commit d22ea117ca6970fd52b296ac18d3f71878c1a857) --- dspace/config/modules/solr-statistics.cfg | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/dspace/config/modules/solr-statistics.cfg b/dspace/config/modules/solr-statistics.cfg index 073850ca232e..632ff5078b48 100644 --- a/dspace/config/modules/solr-statistics.cfg +++ b/dspace/config/modules/solr-statistics.cfg @@ -32,10 +32,10 @@ solr-statistics.configset = statistics solr-statistics.autoCommit = true # URLs to download IP addresses of search engine spiders from -solr-statistics.spiderips.urls = http://iplists.com/google.txt, \ - http://iplists.com/inktomi.txt, \ - http://iplists.com/lycos.txt, \ - http://iplists.com/infoseek.txt, \ - http://iplists.com/altavista.txt, \ - http://iplists.com/excite.txt, \ - http://iplists.com/misc.txt +solr-statistics.spiderips.urls = https://www.iplists.com/google.txt, \ + https://www.iplists.com/inktomi.txt, \ + https://www.iplists.com/lycos.txt, \ + https://www.iplists.com/infoseek.txt, \ + https://www.iplists.com/altavista.txt, \ + https://www.iplists.com/excite.txt, \ + https://www.iplists.com/misc.txt From 0d3f80e5d7ee150c4d840f344eade206334a5f6c Mon Sep 17 00:00:00 2001 From: Kim Shepherd Date: Tue, 23 Jul 2024 12:17:07 +0200 Subject: [PATCH 018/979] Update spider list URLs to satisfy cloudflare redirects Update spider list URLs to satisfy cloudflare redirects Update spider list URLs to satisfy cloudflare redirects (cherry picked from commit d22ea117ca6970fd52b296ac18d3f71878c1a857) --- dspace/config/modules/solr-statistics.cfg | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/dspace/config/modules/solr-statistics.cfg b/dspace/config/modules/solr-statistics.cfg index 28273809de70..57c061dac75d 100644 --- a/dspace/config/modules/solr-statistics.cfg +++ b/dspace/config/modules/solr-statistics.cfg @@ -28,10 +28,10 @@ solr-statistics.configset = statistics solr-statistics.autoCommit = true # URLs to download IP addresses of search engine spiders from -solr-statistics.spiderips.urls = http://iplists.com/google.txt, \ - http://iplists.com/inktomi.txt, \ - http://iplists.com/lycos.txt, \ - http://iplists.com/infoseek.txt, \ - http://iplists.com/altavista.txt, \ - http://iplists.com/excite.txt, \ - http://iplists.com/misc.txt +solr-statistics.spiderips.urls = https://www.iplists.com/google.txt, \ + https://www.iplists.com/inktomi.txt, \ + https://www.iplists.com/lycos.txt, \ + https://www.iplists.com/infoseek.txt, \ + https://www.iplists.com/altavista.txt, \ + https://www.iplists.com/excite.txt, \ + https://www.iplists.com/misc.txt From affa4b00ff59cffa69d47de6a3a7d4137008dd40 Mon Sep 17 00:00:00 2001 From: Vincenzo Mecca Date: Wed, 24 Jul 2024 19:07:44 +0200 Subject: [PATCH 019/979] [CST-14905] Orcid revoke token feature --- .../org/dspace/orcid/client/OrcidClient.java | 8 + .../dspace/orcid/client/OrcidClientImpl.java | 37 +++++ .../orcid/client/OrcidConfiguration.java | 9 ++ .../impl/OrcidSynchronizationServiceImpl.java | 14 ++ .../ResearcherProfileRestRepositoryIT.java | 146 ++++++++++++++++-- dspace/config/modules/orcid.cfg | 1 + dspace/config/spring/api/orcid-services.xml | 1 + 7 files changed, 200 insertions(+), 16 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/orcid/client/OrcidClient.java b/dspace-api/src/main/java/org/dspace/orcid/client/OrcidClient.java index 99d1920aa53a..d21f61a922f5 100644 --- a/dspace-api/src/main/java/org/dspace/orcid/client/OrcidClient.java +++ b/dspace-api/src/main/java/org/dspace/orcid/client/OrcidClient.java @@ -10,6 +10,7 @@ import java.util.List; import java.util.Optional; +import org.dspace.orcid.OrcidToken; import org.dspace.orcid.exception.OrcidClientException; import org.dspace.orcid.model.OrcidTokenResponseDTO; import org.orcid.jaxb.model.v3.release.record.Person; @@ -161,4 +162,11 @@ public interface OrcidClient { */ OrcidResponse deleteByPutCode(String accessToken, String orcid, String putCode, String path); + /** + * Revokes the given {@param accessToken} with a POST method. + * @param orcidToken the access token to revoke + * @throws OrcidClientException if some error occurs during the search + */ + void revokeToken(OrcidToken orcidToken); + } diff --git a/dspace-api/src/main/java/org/dspace/orcid/client/OrcidClientImpl.java b/dspace-api/src/main/java/org/dspace/orcid/client/OrcidClientImpl.java index 3e7ca7b21029..1532abe63412 100644 --- a/dspace-api/src/main/java/org/dspace/orcid/client/OrcidClientImpl.java +++ b/dspace-api/src/main/java/org/dspace/orcid/client/OrcidClientImpl.java @@ -42,6 +42,7 @@ import org.apache.http.entity.StringEntity; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.message.BasicNameValuePair; +import org.dspace.orcid.OrcidToken; import org.dspace.orcid.exception.OrcidClientException; import org.dspace.orcid.model.OrcidEntityType; import org.dspace.orcid.model.OrcidProfileSectionType; @@ -178,6 +179,16 @@ public OrcidResponse deleteByPutCode(String accessToken, String orcid, String pu return execute(buildDeleteUriRequest(accessToken, "/" + orcid + path + "/" + putCode), true); } + @Override + public void revokeToken(OrcidToken orcidToken) { + List params = new ArrayList<>(); + params.add(new BasicNameValuePair("client_id", orcidConfiguration.getClientId())); + params.add(new BasicNameValuePair("client_secret", orcidConfiguration.getClientSecret())); + params.add(new BasicNameValuePair("token", orcidToken.getAccessToken())); + + executeSuccessful(buildPostForRevokeToken(new UrlEncodedFormEntity(params, Charset.defaultCharset()))); + } + @Override public OrcidTokenResponseDTO getReadPublicAccessToken() { return getClientCredentialsAccessToken("/read-public"); @@ -220,6 +231,14 @@ private HttpUriRequest buildPostUriRequest(String accessToken, String relativePa .build(); } + private HttpUriRequest buildPostForRevokeToken(HttpEntity entity) { + return post(orcidConfiguration.getRevokeUrl()) + .addHeader("Accept", "application/json") + .addHeader("Content-Type", "application/x-www-form-urlencoded") + .setEntity(entity) + .build(); + } + private HttpUriRequest buildPutUriRequest(String accessToken, String relativePath, Object object) { return put(orcidConfiguration.getApiUrl() + relativePath.trim()) .addHeader("Content-Type", "application/vnd.orcid+xml") @@ -234,6 +253,24 @@ private HttpUriRequest buildDeleteUriRequest(String accessToken, String relative .build(); } + private void executeSuccessful(HttpUriRequest httpUriRequest) { + try { + HttpClient client = HttpClientBuilder.create().build(); + HttpResponse response = client.execute(httpUriRequest); + + if (isNotSuccessfull(response)) { + throw new OrcidClientException( + getStatusCode(response), + "Operation " + httpUriRequest.getMethod() + " for the resource " + httpUriRequest.getURI() + + " was not successful: " + new String(response.getEntity().getContent().readAllBytes(), + StandardCharsets.UTF_8) + ); + } + } catch (IOException e) { + throw new RuntimeException(e); + } + } + private T executeAndParseJson(HttpUriRequest httpUriRequest, Class clazz) { HttpClient client = HttpClientBuilder.create().build(); diff --git a/dspace-api/src/main/java/org/dspace/orcid/client/OrcidConfiguration.java b/dspace-api/src/main/java/org/dspace/orcid/client/OrcidConfiguration.java index 550b0215c435..dfa90fcae03a 100644 --- a/dspace-api/src/main/java/org/dspace/orcid/client/OrcidConfiguration.java +++ b/dspace-api/src/main/java/org/dspace/orcid/client/OrcidConfiguration.java @@ -35,6 +35,8 @@ public final class OrcidConfiguration { private String scopes; + private String revokeUrl; + public String getApiUrl() { return apiUrl; } @@ -111,4 +113,11 @@ public boolean isApiConfigured() { return !StringUtils.isAnyBlank(clientId, clientSecret); } + public String getRevokeUrl() { + return revokeUrl; + } + + public void setRevokeUrl(String revokeUrl) { + this.revokeUrl = revokeUrl; + } } diff --git a/dspace-api/src/main/java/org/dspace/orcid/service/impl/OrcidSynchronizationServiceImpl.java b/dspace-api/src/main/java/org/dspace/orcid/service/impl/OrcidSynchronizationServiceImpl.java index 97d832d3de82..a302c58f2c5e 100644 --- a/dspace-api/src/main/java/org/dspace/orcid/service/impl/OrcidSynchronizationServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/orcid/service/impl/OrcidSynchronizationServiceImpl.java @@ -37,6 +37,7 @@ import org.dspace.eperson.EPerson; import org.dspace.eperson.service.EPersonService; import org.dspace.orcid.OrcidToken; +import org.dspace.orcid.client.OrcidClient; import org.dspace.orcid.model.OrcidEntityType; import org.dspace.orcid.model.OrcidTokenResponseDTO; import org.dspace.orcid.service.OrcidSynchronizationService; @@ -47,6 +48,8 @@ import org.dspace.profile.OrcidSynchronizationMode; import org.dspace.profile.service.ResearcherProfileService; import org.dspace.services.ConfigurationService; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; /** @@ -57,6 +60,7 @@ */ public class OrcidSynchronizationServiceImpl implements OrcidSynchronizationService { + private static final Logger log = LoggerFactory.getLogger(OrcidSynchronizationServiceImpl.class); @Autowired private ItemService itemService; @@ -75,6 +79,9 @@ public class OrcidSynchronizationServiceImpl implements OrcidSynchronizationServ @Autowired private ResearcherProfileService researcherProfileService; + @Autowired + private OrcidClient orcidClient; + @Override public void linkProfile(Context context, Item profile, OrcidTokenResponseDTO token) throws SQLException { @@ -118,7 +125,14 @@ public void unlinkProfile(Context context, Item profile) throws SQLException { itemService.clearMetadata(context, profile, "dspace", "orcid", "scope", Item.ANY); itemService.clearMetadata(context, profile, "dspace", "orcid", "authenticated", Item.ANY); + OrcidToken profileToken = orcidTokenService.findByProfileItem(context, profile); + if (profileToken == null) { + log.warn("Cannot find any token related to the user profile: {}", profile.getID()); + return; + } + orcidTokenService.deleteByProfileItem(context, profile); + orcidClient.revokeToken(profileToken); updateItem(context, profile); diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/ResearcherProfileRestRepositoryIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/ResearcherProfileRestRepositoryIT.java index 70009d049fc5..0d46f4268cae 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/ResearcherProfileRestRepositoryIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/ResearcherProfileRestRepositoryIT.java @@ -30,7 +30,10 @@ import static org.hamcrest.Matchers.notNullValue; import static org.hamcrest.Matchers.nullValue; import static org.junit.Assert.assertEquals; -import static org.mockito.Mockito.mock; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.argThat; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.when; @@ -44,6 +47,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import java.io.UnsupportedEncodingException; +import java.lang.reflect.Field; import java.sql.SQLException; import java.util.List; import java.util.UUID; @@ -79,11 +83,13 @@ import org.dspace.orcid.exception.OrcidClientException; import org.dspace.orcid.model.OrcidTokenResponseDTO; import org.dspace.orcid.service.OrcidQueueService; +import org.dspace.orcid.service.OrcidSynchronizationService; import org.dspace.orcid.service.OrcidTokenService; import org.dspace.services.ConfigurationService; import org.dspace.util.UUIDUtils; import org.junit.After; import org.junit.Test; +import org.mockito.Mock; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.MediaType; import org.springframework.test.web.servlet.MvcResult; @@ -114,7 +120,11 @@ public class ResearcherProfileRestRepositoryIT extends AbstractControllerIntegra @Autowired private OrcidClient orcidClient; - private OrcidClient orcidClientMock = mock(OrcidClient.class); + @Mock + private OrcidClient orcidClientMock; + + @Autowired + private OrcidSynchronizationService orcidSynchronizationService; private EPerson user; @@ -158,16 +168,36 @@ public void setUp() throws Exception { context.restoreAuthSystemState(); - researcherProfileAddOrcidOperation.setOrcidClient(orcidClientMock); - + useInstanceForBean(orcidSynchronizationService, orcidClientMock); + useInstanceForBean(researcherProfileAddOrcidOperation, orcidClientMock); } @After public void after() { orcidTokenService.deleteAll(context); - researcherProfileAddOrcidOperation.setOrcidClient(orcidClient); + useInstanceForBean(orcidSynchronizationService, orcidClient); + useInstanceForBean(researcherProfileAddOrcidOperation, orcidClient); + } + + private void useInstanceForBean(B bean, I instance) { + Field[] fields = bean.getClass().getDeclaredFields(); + + for (Field field : fields) { + if (field.getType().isAssignableFrom(instance.getClass())) { + boolean accessible = field.isAccessible(); + try { + field.setAccessible(true); + field.set(bean, instance); + } catch (IllegalAccessException e) { + throw new RuntimeException(e); + } finally { + field.setAccessible(accessible); + } + } + } } + /** * Verify that the findById endpoint returns the own profile. * @@ -608,30 +638,38 @@ public void testDeleteWithProfileLinkedWithOrcid() throws Exception { .withOrcidAuthenticated("authenticated") .build(); + context.restoreAuthSystemState(); + String id = user.getID().toString(); String authToken = getAuthToken(user.getEmail(), password); + OrcidToken orcidToken = orcidTokenService.findByProfileItem(context, profileItem); - context.restoreAuthSystemState(); - - getClient(authToken).perform(get("/api/eperson/profiles/{id}", id)) + getClient(authToken) + .perform(get("/api/eperson/profiles/{id}", id)) .andExpect(status().isOk()); assertThat(profileItem.getMetadata(), hasItem(with("person.identifier.orcid", "0000-1111-2222-3333"))); assertThat(profileItem.getMetadata(), hasItem(with("dspace.orcid.authenticated", "authenticated"))); - assertThat(getOrcidAccessToken(profileItem), notNullValue()); + assertThat(orcidToken.getAccessToken(), notNullValue()); getClient(authToken).perform(get("/api/eperson/profiles/{id}/item", id)) .andExpect(status().isOk()) .andExpect(jsonPath("$", hasJsonPath("$.metadata", matchMetadataNotEmpty("dspace.object.owner")))); + getClient(authToken).perform(delete("/api/eperson/profiles/{id}", id)) .andExpect(status().isNoContent()); + verify(orcidClientMock, times(1)).revokeToken(matchesToken(orcidToken)); + verifyNoMoreInteractions(orcidClientMock); + profileItem = context.reloadEntity(profileItem); + orcidToken = orcidTokenService.findByProfileItem(context, profileItem); + assertThat(profileItem.getMetadata(), not(hasItem(with("person.identifier.orcid", "0000-1111-2222-3333")))); assertThat(profileItem.getMetadata(), not(hasItem(with("dspace.orcid.authenticated", "authenticated")))); - assertThat(getOrcidAccessToken(profileItem), nullValue()); + assertThat(orcidToken, nullValue()); } @@ -1850,7 +1888,8 @@ public void testOwnerPatchToDisconnectProfileFromOrcidWithOnlyOwnerConfiguration .withNameInMetadata("Test", "User") .build(); - OrcidTokenBuilder.create(context, ePerson, "3de2e370-8aa9-4bbe-8d7e-f5b1577bdad4").build(); + OrcidToken orcidToken = + OrcidTokenBuilder.create(context, ePerson, "3de2e370-8aa9-4bbe-8d7e-f5b1577bdad4").build(); Item profile = createProfile(ePerson); @@ -1872,6 +1911,9 @@ public void testOwnerPatchToDisconnectProfileFromOrcidWithOnlyOwnerConfiguration .andExpect(jsonPath("$.orcid").doesNotExist()) .andExpect(jsonPath("$.orcidSynchronization").doesNotExist()); + verify(orcidClientMock, times(1)).revokeToken(matchesToken(orcidToken)); + verifyNoMoreInteractions(orcidClientMock); + profile = context.reloadEntity(profile); assertThat(getMetadataValues(profile, "person.identifier.orcid"), empty()); @@ -1880,6 +1922,54 @@ public void testOwnerPatchToDisconnectProfileFromOrcidWithOnlyOwnerConfiguration assertThat(getOrcidAccessToken(profile), nullValue()); } + @Test + public void testPatchToDisconnectProfileFromOrcidDoesntRevokeOrcidToken() throws Exception { + + configurationService.setProperty("orcid.disconnection.allowed-users", "admin_and_owner"); + + context.turnOffAuthorisationSystem(); + + EPerson ePerson = EPersonBuilder.createEPerson(context) + .withCanLogin(true) + .withOrcid("0000-1111-2222-3333") + .withOrcidScope("/read") + .withOrcidScope("/write") + .withEmail("test@email.it") + .withPassword(password) + .withNameInMetadata("Test", "User") + .build(); + + OrcidToken orcidToken = + OrcidTokenBuilder.create(context, ePerson, "3de2e370-8aa9-4bbe-8d7e-f5b1577bdad4").build(); + + Item profile = createProfile(ePerson); + + assertThat(getMetadataValues(profile, "person.identifier.orcid"), not(empty())); + assertThat(getMetadataValues(profile, "dspace.orcid.scope"), not(empty())); + assertThat(getMetadataValues(profile, "dspace.orcid.authenticated"), not(empty())); + assertThat(getOrcidAccessToken(profile), is("3de2e370-8aa9-4bbe-8d7e-f5b1577bdad4")); + + context.restoreAuthSystemState(); + + doThrow(new OrcidClientException(403, "")).when(orcidClientMock).revokeToken(any(OrcidToken.class)); + + getClient(getAuthToken(ePerson.getEmail(), password)) + .perform(patch("/api/eperson/profiles/{id}", ePerson.getID().toString()) + .content(getPatchContent(asList(new RemoveOperation("/orcid")))) + .contentType(MediaType.APPLICATION_JSON_VALUE)) + .andExpect(status().isInternalServerError()); + + verify(orcidClientMock, times(1)).revokeToken(matchesToken(orcidToken)); + verifyNoMoreInteractions(orcidClientMock); + + profile = context.reloadEntity(profile); + + assertThat(getMetadataValues(profile, "person.identifier.orcid"), not(empty())); + assertThat(getMetadataValues(profile, "dspace.orcid.scope"), not(empty())); + assertThat(getMetadataValues(profile, "dspace.orcid.authenticated"), not(empty())); + assertThat(getOrcidAccessToken(profile), is("3de2e370-8aa9-4bbe-8d7e-f5b1577bdad4")); + } + @Test public void testAdminPatchToDisconnectProfileFromOrcidWithOnlyOwnerConfiguration() throws Exception { @@ -2023,7 +2113,8 @@ public void testAdminPatchToDisconnectProfileFromOrcidWithOnlyAdminConfiguration .withNameInMetadata("Test", "User") .build(); - OrcidTokenBuilder.create(context, ePerson, "3de2e370-8aa9-4bbe-8d7e-f5b1577bdad4").build(); + OrcidToken orcidToken = + OrcidTokenBuilder.create(context, ePerson, "3de2e370-8aa9-4bbe-8d7e-f5b1577bdad4").build(); Item profile = createProfile(ePerson); @@ -2034,6 +2125,7 @@ public void testAdminPatchToDisconnectProfileFromOrcidWithOnlyAdminConfiguration context.restoreAuthSystemState(); + getClient(getAuthToken(admin.getEmail(), password)) .perform(patch("/api/eperson/profiles/{id}", ePerson.getID().toString()) .content(getPatchContent(asList(new RemoveOperation("/orcid")))) @@ -2045,6 +2137,9 @@ public void testAdminPatchToDisconnectProfileFromOrcidWithOnlyAdminConfiguration .andExpect(jsonPath("$.orcid").doesNotExist()) .andExpect(jsonPath("$.orcidSynchronization").doesNotExist()); + verify(orcidClientMock, times(1)).revokeToken(matchesToken(orcidToken)); + verifyNoMoreInteractions(orcidClientMock); + profile = context.reloadEntity(profile); assertThat(getMetadataValues(profile, "person.identifier.orcid"), empty()); @@ -2111,17 +2206,18 @@ public void testOwnerPatchToDisconnectProfileFromOrcidWithAdminAndOwnerConfigura .withPassword(password) .withNameInMetadata("Test", "User") .build(); - - OrcidTokenBuilder.create(context, ePerson, "3de2e370-8aa9-4bbe-8d7e-f5b1577bdad4").build(); + OrcidToken orcidToken = + OrcidTokenBuilder.create(context, ePerson, "3de2e370-8aa9-4bbe-8d7e-f5b1577bdad4").build(); Item profile = createProfile(ePerson); + context.restoreAuthSystemState(); + assertThat(getMetadataValues(profile, "person.identifier.orcid"), not(empty())); assertThat(getMetadataValues(profile, "dspace.orcid.scope"), not(empty())); assertThat(getMetadataValues(profile, "dspace.orcid.authenticated"), not(empty())); assertThat(getOrcidAccessToken(profile), is("3de2e370-8aa9-4bbe-8d7e-f5b1577bdad4")); - context.restoreAuthSystemState(); getClient(getAuthToken(ePerson.getEmail(), password)) .perform(patch("/api/eperson/profiles/{id}", ePerson.getID().toString()) @@ -2134,6 +2230,9 @@ public void testOwnerPatchToDisconnectProfileFromOrcidWithAdminAndOwnerConfigura .andExpect(jsonPath("$.orcid").doesNotExist()) .andExpect(jsonPath("$.orcidSynchronization").doesNotExist()); + verify(orcidClientMock, times(1)).revokeToken(matchesToken(orcidToken)); + verifyNoMoreInteractions(orcidClientMock); + profile = context.reloadEntity(profile); assertThat(getMetadataValues(profile, "person.identifier.orcid"), empty()); @@ -2159,7 +2258,8 @@ public void testAdminPatchToDisconnectProfileFromOrcidWithAdminAndOwnerConfigura .withNameInMetadata("Test", "User") .build(); - OrcidTokenBuilder.create(context, ePerson, "3de2e370-8aa9-4bbe-8d7e-f5b1577bdad4").build(); + OrcidToken orcidToken = + OrcidTokenBuilder.create(context, ePerson, "3de2e370-8aa9-4bbe-8d7e-f5b1577bdad4").build(); Item profile = createProfile(ePerson); @@ -2170,6 +2270,7 @@ public void testAdminPatchToDisconnectProfileFromOrcidWithAdminAndOwnerConfigura context.restoreAuthSystemState(); + getClient(getAuthToken(admin.getEmail(), password)) .perform(patch("/api/eperson/profiles/{id}", ePerson.getID().toString()) .content(getPatchContent(asList(new RemoveOperation("/orcid")))) @@ -2181,6 +2282,10 @@ public void testAdminPatchToDisconnectProfileFromOrcidWithAdminAndOwnerConfigura .andExpect(jsonPath("$.orcid").doesNotExist()) .andExpect(jsonPath("$.orcidSynchronization").doesNotExist()); + + verify(orcidClientMock, times(1)).revokeToken(matchesToken(orcidToken)); + verifyNoMoreInteractions(orcidClientMock); + profile = context.reloadEntity(profile); assertThat(getMetadataValues(profile, "person.identifier.orcid"), empty()); @@ -2627,4 +2732,13 @@ private OrcidTokenResponseDTO buildOrcidTokenResponse(String orcid, String acces return token; } + private static OrcidToken matchesToken(OrcidToken orcidToken) { + return argThat( + token -> + token != null && + orcidToken.getAccessToken().equals(token.getAccessToken()) && + orcidToken.getID().equals(token.getID()) + ); + } + } diff --git a/dspace/config/modules/orcid.cfg b/dspace/config/modules/orcid.cfg index cde819677447..93da0a5f4cb9 100644 --- a/dspace/config/modules/orcid.cfg +++ b/dspace/config/modules/orcid.cfg @@ -14,6 +14,7 @@ orcid.disconnection.allowed-users = admin_and_owner # ORCID API (https://github.com/ORCID/ORCID-Source/tree/master/orcid-api-web#endpoints) orcid.domain-url= https://sandbox.orcid.org orcid.authorize-url = ${orcid.domain-url}/oauth/authorize +orcid.revoke-url = ${orcid.domain-url}/oauth/revoke orcid.token-url = ${orcid.domain-url}/oauth/token orcid.api-url = https://api.sandbox.orcid.org/v3.0 orcid.public-url = https://pub.sandbox.orcid.org/v3.0 diff --git a/dspace/config/spring/api/orcid-services.xml b/dspace/config/spring/api/orcid-services.xml index e5fb002c314f..eb31acb29c4d 100644 --- a/dspace/config/spring/api/orcid-services.xml +++ b/dspace/config/spring/api/orcid-services.xml @@ -30,6 +30,7 @@ + From be2434bce12c2a059488716983b9fc9dec8da15e Mon Sep 17 00:00:00 2001 From: Vincenzo Mecca Date: Wed, 24 Jul 2024 19:07:44 +0200 Subject: [PATCH 020/979] [CST-14905] Orcid revoke token feature --- .../org/dspace/orcid/client/OrcidClient.java | 8 + .../dspace/orcid/client/OrcidClientImpl.java | 37 +++ .../orcid/client/OrcidConfiguration.java | 9 + .../impl/OrcidSynchronizationServiceImpl.java | 39 +++- .../ResearcherProfileRestRepositoryIT.java | 216 ++++++++++++++++-- dspace/config/modules/orcid.cfg | 1 + dspace/config/spring/api/orcid-services.xml | 1 + 7 files changed, 284 insertions(+), 27 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/orcid/client/OrcidClient.java b/dspace-api/src/main/java/org/dspace/orcid/client/OrcidClient.java index 99d1920aa53a..d21f61a922f5 100644 --- a/dspace-api/src/main/java/org/dspace/orcid/client/OrcidClient.java +++ b/dspace-api/src/main/java/org/dspace/orcid/client/OrcidClient.java @@ -10,6 +10,7 @@ import java.util.List; import java.util.Optional; +import org.dspace.orcid.OrcidToken; import org.dspace.orcid.exception.OrcidClientException; import org.dspace.orcid.model.OrcidTokenResponseDTO; import org.orcid.jaxb.model.v3.release.record.Person; @@ -161,4 +162,11 @@ public interface OrcidClient { */ OrcidResponse deleteByPutCode(String accessToken, String orcid, String putCode, String path); + /** + * Revokes the given {@param accessToken} with a POST method. + * @param orcidToken the access token to revoke + * @throws OrcidClientException if some error occurs during the search + */ + void revokeToken(OrcidToken orcidToken); + } diff --git a/dspace-api/src/main/java/org/dspace/orcid/client/OrcidClientImpl.java b/dspace-api/src/main/java/org/dspace/orcid/client/OrcidClientImpl.java index 8356167692e3..6c2a1512980c 100644 --- a/dspace-api/src/main/java/org/dspace/orcid/client/OrcidClientImpl.java +++ b/dspace-api/src/main/java/org/dspace/orcid/client/OrcidClientImpl.java @@ -42,6 +42,7 @@ import org.apache.http.entity.StringEntity; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.message.BasicNameValuePair; +import org.dspace.orcid.OrcidToken; import org.dspace.orcid.exception.OrcidClientException; import org.dspace.orcid.model.OrcidEntityType; import org.dspace.orcid.model.OrcidProfileSectionType; @@ -178,6 +179,16 @@ public OrcidResponse deleteByPutCode(String accessToken, String orcid, String pu return execute(buildDeleteUriRequest(accessToken, "/" + orcid + path + "/" + putCode), true); } + @Override + public void revokeToken(OrcidToken orcidToken) { + List params = new ArrayList<>(); + params.add(new BasicNameValuePair("client_id", orcidConfiguration.getClientId())); + params.add(new BasicNameValuePair("client_secret", orcidConfiguration.getClientSecret())); + params.add(new BasicNameValuePair("token", orcidToken.getAccessToken())); + + executeSuccessful(buildPostForRevokeToken(new UrlEncodedFormEntity(params, Charset.defaultCharset()))); + } + @Override public OrcidTokenResponseDTO getReadPublicAccessToken() { return getClientCredentialsAccessToken("/read-public"); @@ -220,6 +231,14 @@ private HttpUriRequest buildPostUriRequest(String accessToken, String relativePa .build(); } + private HttpUriRequest buildPostForRevokeToken(HttpEntity entity) { + return post(orcidConfiguration.getRevokeUrl()) + .addHeader("Accept", "application/json") + .addHeader("Content-Type", "application/x-www-form-urlencoded") + .setEntity(entity) + .build(); + } + private HttpUriRequest buildPutUriRequest(String accessToken, String relativePath, Object object) { return put(orcidConfiguration.getApiUrl() + relativePath.trim()) .addHeader("Content-Type", "application/vnd.orcid+xml") @@ -234,6 +253,24 @@ private HttpUriRequest buildDeleteUriRequest(String accessToken, String relative .build(); } + private void executeSuccessful(HttpUriRequest httpUriRequest) { + try { + HttpClient client = HttpClientBuilder.create().build(); + HttpResponse response = client.execute(httpUriRequest); + + if (isNotSuccessfull(response)) { + throw new OrcidClientException( + getStatusCode(response), + "Operation " + httpUriRequest.getMethod() + " for the resource " + httpUriRequest.getURI() + + " was not successful: " + new String(response.getEntity().getContent().readAllBytes(), + StandardCharsets.UTF_8) + ); + } + } catch (IOException e) { + throw new RuntimeException(e); + } + } + private T executeAndParseJson(HttpUriRequest httpUriRequest, Class clazz) { HttpClient client = HttpClientBuilder.create().build(); diff --git a/dspace-api/src/main/java/org/dspace/orcid/client/OrcidConfiguration.java b/dspace-api/src/main/java/org/dspace/orcid/client/OrcidConfiguration.java index 550b0215c435..dfa90fcae03a 100644 --- a/dspace-api/src/main/java/org/dspace/orcid/client/OrcidConfiguration.java +++ b/dspace-api/src/main/java/org/dspace/orcid/client/OrcidConfiguration.java @@ -35,6 +35,8 @@ public final class OrcidConfiguration { private String scopes; + private String revokeUrl; + public String getApiUrl() { return apiUrl; } @@ -111,4 +113,11 @@ public boolean isApiConfigured() { return !StringUtils.isAnyBlank(clientId, clientSecret); } + public String getRevokeUrl() { + return revokeUrl; + } + + public void setRevokeUrl(String revokeUrl) { + this.revokeUrl = revokeUrl; + } } diff --git a/dspace-api/src/main/java/org/dspace/orcid/service/impl/OrcidSynchronizationServiceImpl.java b/dspace-api/src/main/java/org/dspace/orcid/service/impl/OrcidSynchronizationServiceImpl.java index 59e4dea64145..f976864d07f9 100644 --- a/dspace-api/src/main/java/org/dspace/orcid/service/impl/OrcidSynchronizationServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/orcid/service/impl/OrcidSynchronizationServiceImpl.java @@ -37,6 +37,7 @@ import org.dspace.eperson.EPerson; import org.dspace.eperson.service.EPersonService; import org.dspace.orcid.OrcidToken; +import org.dspace.orcid.client.OrcidClient; import org.dspace.orcid.model.OrcidEntityType; import org.dspace.orcid.model.OrcidTokenResponseDTO; import org.dspace.orcid.service.OrcidSynchronizationService; @@ -47,6 +48,8 @@ import org.dspace.profile.OrcidSynchronizationMode; import org.dspace.profile.service.ResearcherProfileService; import org.dspace.services.ConfigurationService; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; /** @@ -57,6 +60,7 @@ */ public class OrcidSynchronizationServiceImpl implements OrcidSynchronizationService { + private static final Logger log = LoggerFactory.getLogger(OrcidSynchronizationServiceImpl.class); @Autowired private ItemService itemService; @@ -75,6 +79,9 @@ public class OrcidSynchronizationServiceImpl implements OrcidSynchronizationServ @Autowired private ResearcherProfileService researcherProfileService; + @Autowired + private OrcidClient orcidClient; + @Override public void linkProfile(Context context, Item profile, OrcidTokenResponseDTO token) throws SQLException { @@ -114,20 +121,33 @@ public void linkProfile(Context context, Item profile, OrcidTokenResponseDTO tok @Override public void unlinkProfile(Context context, Item profile) throws SQLException { - itemService.clearMetadata(context, profile, "person", "identifier", "orcid", Item.ANY); - itemService.clearMetadata(context, profile, "dspace", "orcid", "scope", Item.ANY); - itemService.clearMetadata(context, profile, "dspace", "orcid", "authenticated", Item.ANY); + clearOrcidProfileMetadata(context, profile); - if (!configurationService.getBooleanProperty("orcid.disconnection.remain-sync", false)) { - clearSynchronizationSettings(context, profile); - } + clearSynchronizationSettings(context, profile); - orcidTokenService.deleteByProfileItem(context, profile); + clearOrcidToken(context, profile); updateItem(context, profile); } + private void clearOrcidToken(Context context, Item profile) { + OrcidToken profileToken = orcidTokenService.findByProfileItem(context, profile); + if (profileToken == null) { + log.warn("Cannot find any token related to the user profile: {}", profile.getID()); + return; + } + + orcidTokenService.deleteByProfileItem(context, profile); + orcidClient.revokeToken(profileToken); + } + + private void clearOrcidProfileMetadata(Context context, Item profile) throws SQLException { + itemService.clearMetadata(context, profile, "person", "identifier", "orcid", Item.ANY); + itemService.clearMetadata(context, profile, "dspace", "orcid", "scope", Item.ANY); + itemService.clearMetadata(context, profile, "dspace", "orcid", "authenticated", Item.ANY); + } + @Override public boolean setEntityPreference(Context context, Item profile, OrcidEntityType type, OrcidEntitySyncPreference value) throws SQLException { @@ -273,6 +293,11 @@ private boolean updatePreferenceForSynchronizingWithOrcid(Context context, Item private void clearSynchronizationSettings(Context context, Item profile) throws SQLException { + + if (configurationService.getBooleanProperty("orcid.disconnection.remain-sync", false)) { + return; + } + itemService.clearMetadata(context, profile, "dspace", "orcid", "sync-mode", Item.ANY); itemService.clearMetadata(context, profile, "dspace", "orcid", "sync-profile", Item.ANY); diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/ResearcherProfileRestRepositoryIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/ResearcherProfileRestRepositoryIT.java index b3c70f8128d4..493b3bf94628 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/ResearcherProfileRestRepositoryIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/ResearcherProfileRestRepositoryIT.java @@ -31,7 +31,10 @@ import static org.hamcrest.Matchers.notNullValue; import static org.hamcrest.Matchers.nullValue; import static org.junit.Assert.assertEquals; -import static org.mockito.Mockito.mock; +import static org.mockito.ArgumentMatchers.argThat; +import static org.mockito.Mockito.any; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.when; @@ -45,6 +48,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import java.io.UnsupportedEncodingException; +import java.lang.reflect.Field; import java.sql.SQLException; import java.util.List; import java.util.UUID; @@ -80,11 +84,13 @@ import org.dspace.orcid.exception.OrcidClientException; import org.dspace.orcid.model.OrcidTokenResponseDTO; import org.dspace.orcid.service.OrcidQueueService; +import org.dspace.orcid.service.OrcidSynchronizationService; import org.dspace.orcid.service.OrcidTokenService; import org.dspace.services.ConfigurationService; import org.dspace.util.UUIDUtils; import org.junit.After; import org.junit.Test; +import org.mockito.Mock; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.MediaType; import org.springframework.test.web.servlet.MvcResult; @@ -115,7 +121,11 @@ public class ResearcherProfileRestRepositoryIT extends AbstractControllerIntegra @Autowired private OrcidClient orcidClient; - private OrcidClient orcidClientMock = mock(OrcidClient.class); + @Mock + private OrcidClient orcidClientMock; + + @Autowired + private OrcidSynchronizationService orcidSynchronizationService; private EPerson user; @@ -159,16 +169,36 @@ public void setUp() throws Exception { context.restoreAuthSystemState(); - researcherProfileAddOrcidOperation.setOrcidClient(orcidClientMock); - + useInstanceForBean(orcidSynchronizationService, orcidClientMock); + useInstanceForBean(researcherProfileAddOrcidOperation, orcidClientMock); } @After public void after() { orcidTokenService.deleteAll(context); - researcherProfileAddOrcidOperation.setOrcidClient(orcidClient); + useInstanceForBean(orcidSynchronizationService, orcidClient); + useInstanceForBean(researcherProfileAddOrcidOperation, orcidClient); } + private void useInstanceForBean(B bean, I instance) { + Field[] fields = bean.getClass().getDeclaredFields(); + + for (Field field : fields) { + if (field.getType().isAssignableFrom(instance.getClass())) { + boolean accessible = field.isAccessible(); + try { + field.setAccessible(true); + field.set(bean, instance); + } catch (IllegalAccessException e) { + throw new RuntimeException(e); + } finally { + field.setAccessible(accessible); + } + } + } + } + + /** * Verify that the findById endpoint returns the own profile. * @@ -609,30 +639,38 @@ public void testDeleteWithProfileLinkedWithOrcid() throws Exception { .withOrcidAuthenticated("authenticated") .build(); + context.restoreAuthSystemState(); + String id = user.getID().toString(); String authToken = getAuthToken(user.getEmail(), password); + OrcidToken orcidToken = orcidTokenService.findByProfileItem(context, profileItem); - context.restoreAuthSystemState(); - - getClient(authToken).perform(get("/api/eperson/profiles/{id}", id)) + getClient(authToken) + .perform(get("/api/eperson/profiles/{id}", id)) .andExpect(status().isOk()); assertThat(profileItem.getMetadata(), hasItem(with("person.identifier.orcid", "0000-1111-2222-3333"))); assertThat(profileItem.getMetadata(), hasItem(with("dspace.orcid.authenticated", "authenticated"))); - assertThat(getOrcidAccessToken(profileItem), notNullValue()); + assertThat(orcidToken.getAccessToken(), notNullValue()); getClient(authToken).perform(get("/api/eperson/profiles/{id}/item", id)) .andExpect(status().isOk()) .andExpect(jsonPath("$", hasJsonPath("$.metadata", matchMetadataNotEmpty("dspace.object.owner")))); + getClient(authToken).perform(delete("/api/eperson/profiles/{id}", id)) .andExpect(status().isNoContent()); + verify(orcidClientMock, times(1)).revokeToken(matchesToken(orcidToken)); + verifyNoMoreInteractions(orcidClientMock); + profileItem = context.reloadEntity(profileItem); + orcidToken = orcidTokenService.findByProfileItem(context, profileItem); + assertThat(profileItem.getMetadata(), not(hasItem(with("person.identifier.orcid", "0000-1111-2222-3333")))); assertThat(profileItem.getMetadata(), not(hasItem(with("dspace.orcid.authenticated", "authenticated")))); - assertThat(getOrcidAccessToken(profileItem), nullValue()); + assertThat(orcidToken, nullValue()); } @@ -1851,7 +1889,60 @@ public void testOwnerPatchToDisconnectProfileFromOrcidWithOnlyOwnerConfiguration .withNameInMetadata("Test", "User") .build(); - OrcidTokenBuilder.create(context, ePerson, "3de2e370-8aa9-4bbe-8d7e-f5b1577bdad4").build(); + OrcidToken orcidToken = + OrcidTokenBuilder.create(context, ePerson, "3de2e370-8aa9-4bbe-8d7e-f5b1577bdad4").build(); + + Item profile = createProfile(ePerson); + + context.restoreAuthSystemState(); + + assertThat(getMetadataValues(profile, "person.identifier.orcid"), not(empty())); + assertThat(getMetadataValues(profile, "dspace.orcid.scope"), not(empty())); + assertThat(getMetadataValues(profile, "dspace.orcid.authenticated"), not(empty())); + assertThat(getOrcidAccessToken(profile), is("3de2e370-8aa9-4bbe-8d7e-f5b1577bdad4")); + + getClient(getAuthToken(ePerson.getEmail(), password)) + .perform(patch("/api/eperson/profiles/{id}", ePerson.getID().toString()) + .content(getPatchContent(asList(new RemoveOperation("/orcid")))) + .contentType(MediaType.APPLICATION_JSON_VALUE)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.id", is(ePerson.getID().toString()))) + .andExpect(jsonPath("$.visible", is(false))) + .andExpect(jsonPath("$.type", is("profile"))) + .andExpect(jsonPath("$.orcid").doesNotExist()) + .andExpect(jsonPath("$.orcidSynchronization").doesNotExist()); + + verify(orcidClientMock, times(1)) + .revokeToken(matchesToken(orcidToken)); + verifyNoMoreInteractions(orcidClientMock); + + profile = context.reloadEntity(profile); + + assertThat(getMetadataValues(profile, "person.identifier.orcid"), empty()); + assertThat(getMetadataValues(profile, "dspace.orcid.scope"), empty()); + assertThat(getMetadataValues(profile, "dspace.orcid.authenticated"), empty()); + assertThat(getOrcidAccessToken(profile), nullValue()); + } + + @Test + public void testPatchToDisconnectProfileFromOrcidRevokesOrcidToken() throws Exception { + + configurationService.setProperty("orcid.disconnection.allowed-users", "admin_and_owner"); + + context.turnOffAuthorisationSystem(); + + EPerson ePerson = EPersonBuilder.createEPerson(context) + .withCanLogin(true) + .withOrcid("0000-1111-2222-3333") + .withOrcidScope("/read") + .withOrcidScope("/write") + .withEmail("test@email.it") + .withPassword(password) + .withNameInMetadata("Test", "User") + .build(); + + OrcidToken orcidToken = + OrcidTokenBuilder.create(context, ePerson, "3de2e370-8aa9-4bbe-8d7e-f5b1577bdad4").build(); Item profile = createProfile(ePerson); @@ -1873,6 +1964,9 @@ public void testOwnerPatchToDisconnectProfileFromOrcidWithOnlyOwnerConfiguration .andExpect(jsonPath("$.orcid").doesNotExist()) .andExpect(jsonPath("$.orcidSynchronization").doesNotExist()); + verify(orcidClientMock, times(1)).revokeToken(matchesToken(orcidToken)); + verifyNoMoreInteractions(orcidClientMock); + profile = context.reloadEntity(profile); assertThat(getMetadataValues(profile, "person.identifier.orcid"), empty()); @@ -1881,6 +1975,54 @@ public void testOwnerPatchToDisconnectProfileFromOrcidWithOnlyOwnerConfiguration assertThat(getOrcidAccessToken(profile), nullValue()); } + @Test + public void testPatchToDisconnectProfileFromOrcidDoesntRevokeOrcidToken() throws Exception { + + configurationService.setProperty("orcid.disconnection.allowed-users", "admin_and_owner"); + + context.turnOffAuthorisationSystem(); + + EPerson ePerson = EPersonBuilder.createEPerson(context) + .withCanLogin(true) + .withOrcid("0000-1111-2222-3333") + .withOrcidScope("/read") + .withOrcidScope("/write") + .withEmail("test@email.it") + .withPassword(password) + .withNameInMetadata("Test", "User") + .build(); + + OrcidToken orcidToken = + OrcidTokenBuilder.create(context, ePerson, "3de2e370-8aa9-4bbe-8d7e-f5b1577bdad4").build(); + + Item profile = createProfile(ePerson); + + assertThat(getMetadataValues(profile, "person.identifier.orcid"), not(empty())); + assertThat(getMetadataValues(profile, "dspace.orcid.scope"), not(empty())); + assertThat(getMetadataValues(profile, "dspace.orcid.authenticated"), not(empty())); + assertThat(getOrcidAccessToken(profile), is("3de2e370-8aa9-4bbe-8d7e-f5b1577bdad4")); + + context.restoreAuthSystemState(); + + doThrow(new OrcidClientException(403, "")).when(orcidClientMock).revokeToken(any(OrcidToken.class)); + + getClient(getAuthToken(ePerson.getEmail(), password)) + .perform(patch("/api/eperson/profiles/{id}", ePerson.getID().toString()) + .content(getPatchContent(asList(new RemoveOperation("/orcid")))) + .contentType(MediaType.APPLICATION_JSON_VALUE)) + .andExpect(status().isInternalServerError()); + + verify(orcidClientMock, times(1)).revokeToken(matchesToken(orcidToken)); + verifyNoMoreInteractions(orcidClientMock); + + profile = context.reloadEntity(profile); + + assertThat(getMetadataValues(profile, "person.identifier.orcid"), not(empty())); + assertThat(getMetadataValues(profile, "dspace.orcid.scope"), not(empty())); + assertThat(getMetadataValues(profile, "dspace.orcid.authenticated"), not(empty())); + assertThat(getOrcidAccessToken(profile), is("3de2e370-8aa9-4bbe-8d7e-f5b1577bdad4")); + } + @Test public void testAdminPatchToDisconnectProfileFromOrcidWithOnlyOwnerConfiguration() throws Exception { @@ -2024,7 +2166,8 @@ public void testAdminPatchToDisconnectProfileFromOrcidWithOnlyAdminConfiguration .withNameInMetadata("Test", "User") .build(); - OrcidTokenBuilder.create(context, ePerson, "3de2e370-8aa9-4bbe-8d7e-f5b1577bdad4").build(); + OrcidToken orcidToken = + OrcidTokenBuilder.create(context, ePerson, "3de2e370-8aa9-4bbe-8d7e-f5b1577bdad4").build(); Item profile = createProfile(ePerson); @@ -2035,6 +2178,7 @@ public void testAdminPatchToDisconnectProfileFromOrcidWithOnlyAdminConfiguration context.restoreAuthSystemState(); + getClient(getAuthToken(admin.getEmail(), password)) .perform(patch("/api/eperson/profiles/{id}", ePerson.getID().toString()) .content(getPatchContent(asList(new RemoveOperation("/orcid")))) @@ -2046,6 +2190,9 @@ public void testAdminPatchToDisconnectProfileFromOrcidWithOnlyAdminConfiguration .andExpect(jsonPath("$.orcid").doesNotExist()) .andExpect(jsonPath("$.orcidSynchronization").doesNotExist()); + verify(orcidClientMock, times(1)).revokeToken(matchesToken(orcidToken)); + verifyNoMoreInteractions(orcidClientMock); + profile = context.reloadEntity(profile); assertThat(getMetadataValues(profile, "person.identifier.orcid"), empty()); @@ -2112,17 +2259,18 @@ public void testOwnerPatchToDisconnectProfileFromOrcidWithAdminAndOwnerConfigura .withPassword(password) .withNameInMetadata("Test", "User") .build(); - - OrcidTokenBuilder.create(context, ePerson, "3de2e370-8aa9-4bbe-8d7e-f5b1577bdad4").build(); + OrcidToken orcidToken = + OrcidTokenBuilder.create(context, ePerson, "3de2e370-8aa9-4bbe-8d7e-f5b1577bdad4").build(); Item profile = createProfile(ePerson); + context.restoreAuthSystemState(); + assertThat(getMetadataValues(profile, "person.identifier.orcid"), not(empty())); assertThat(getMetadataValues(profile, "dspace.orcid.scope"), not(empty())); assertThat(getMetadataValues(profile, "dspace.orcid.authenticated"), not(empty())); assertThat(getOrcidAccessToken(profile), is("3de2e370-8aa9-4bbe-8d7e-f5b1577bdad4")); - context.restoreAuthSystemState(); getClient(getAuthToken(ePerson.getEmail(), password)) .perform(patch("/api/eperson/profiles/{id}", ePerson.getID().toString()) @@ -2135,6 +2283,9 @@ public void testOwnerPatchToDisconnectProfileFromOrcidWithAdminAndOwnerConfigura .andExpect(jsonPath("$.orcid").doesNotExist()) .andExpect(jsonPath("$.orcidSynchronization").doesNotExist()); + verify(orcidClientMock, times(1)).revokeToken(matchesToken(orcidToken)); + verifyNoMoreInteractions(orcidClientMock); + profile = context.reloadEntity(profile); assertThat(getMetadataValues(profile, "person.identifier.orcid"), empty()); @@ -2160,7 +2311,8 @@ public void testAdminPatchToDisconnectProfileFromOrcidWithAdminAndOwnerConfigura .withNameInMetadata("Test", "User") .build(); - OrcidTokenBuilder.create(context, ePerson, "3de2e370-8aa9-4bbe-8d7e-f5b1577bdad4").build(); + OrcidToken orcidToken = + OrcidTokenBuilder.create(context, ePerson, "3de2e370-8aa9-4bbe-8d7e-f5b1577bdad4").build(); Item profile = createProfile(ePerson); @@ -2171,6 +2323,7 @@ public void testAdminPatchToDisconnectProfileFromOrcidWithAdminAndOwnerConfigura context.restoreAuthSystemState(); + getClient(getAuthToken(admin.getEmail(), password)) .perform(patch("/api/eperson/profiles/{id}", ePerson.getID().toString()) .content(getPatchContent(asList(new RemoveOperation("/orcid")))) @@ -2182,6 +2335,10 @@ public void testAdminPatchToDisconnectProfileFromOrcidWithAdminAndOwnerConfigura .andExpect(jsonPath("$.orcid").doesNotExist()) .andExpect(jsonPath("$.orcidSynchronization").doesNotExist()); + + verify(orcidClientMock, times(1)).revokeToken(matchesToken(orcidToken)); + verifyNoMoreInteractions(orcidClientMock); + profile = context.reloadEntity(profile); assertThat(getMetadataValues(profile, "person.identifier.orcid"), empty()); @@ -2250,7 +2407,8 @@ public void testOwnerPatchToDisconnectProfileFromOrcidWithSyncSettingsRemoved() .withNameInMetadata("Test", "User") .build(); - OrcidTokenBuilder.create(context, ePerson, "3de2e370-8aa9-4bbe-8d7e-f5b1577bdad4").build(); + OrcidToken orcidToken = + OrcidTokenBuilder.create(context, ePerson, "3de2e370-8aa9-4bbe-8d7e-f5b1577bdad4").build(); Item profile = createProfile(ePerson); @@ -2285,6 +2443,7 @@ public void testOwnerPatchToDisconnectProfileFromOrcidWithSyncSettingsRemoved() assertThat(metadata, hasItem(with("dspace.orcid.sync-fundings",ALL.name()))); assertThat(getMetadataValues(profile, "dspace.orcid.sync-profile"), hasSize(2)); + getClient(authToken).perform(patch("/api/eperson/profiles/{id}", ePersonId) .content(getPatchContent(asList(new RemoveOperation("/orcid")))) .contentType(MediaType.APPLICATION_JSON_VALUE)) @@ -2295,6 +2454,9 @@ public void testOwnerPatchToDisconnectProfileFromOrcidWithSyncSettingsRemoved() .andExpect(jsonPath("$.orcid").doesNotExist()) .andExpect(jsonPath("$.orcidSynchronization").doesNotExist()); + verify(orcidClientMock, times(1)).revokeToken(matchesToken(orcidToken)); + verifyNoMoreInteractions(orcidClientMock); + profile = context.reloadEntity(profile); assertThat(getMetadataValues(profile, "person.identifier.orcid"), empty()); @@ -2324,17 +2486,18 @@ public void testOwnerPatchToDisconnectProfileFromOrcidWithSyncSettingsRemain() t .withNameInMetadata("Test", "User") .build(); - OrcidTokenBuilder.create(context, ePerson, "3de2e370-8aa9-4bbe-8d7e-f5b1577bdad4").build(); + OrcidToken orcidToken = + OrcidTokenBuilder.create(context, ePerson, "3de2e370-8aa9-4bbe-8d7e-f5b1577bdad4").build(); Item profile = createProfile(ePerson); + context.restoreAuthSystemState(); + assertThat(getMetadataValues(profile, "person.identifier.orcid"), not(empty())); assertThat(getMetadataValues(profile, "dspace.orcid.scope"), not(empty())); assertThat(getMetadataValues(profile, "dspace.orcid.authenticated"), not(empty())); assertThat(getOrcidAccessToken(profile), is("3de2e370-8aa9-4bbe-8d7e-f5b1577bdad4")); - context.restoreAuthSystemState(); - String authToken = getAuthToken(ePerson.getEmail(), password); String ePersonId = ePerson.getID().toString(); List operations = asList(new ReplaceOperation("/orcid/mode", "BATCH"), @@ -2359,6 +2522,7 @@ public void testOwnerPatchToDisconnectProfileFromOrcidWithSyncSettingsRemain() t assertThat(metadata, hasItem(with("dspace.orcid.sync-fundings",ALL.name()))); assertThat(getMetadataValues(profile, "dspace.orcid.sync-profile"), hasSize(2)); + getClient(authToken).perform(patch("/api/eperson/profiles/{id}", ePersonId) .content(getPatchContent(asList(new RemoveOperation("/orcid")))) .contentType(MediaType.APPLICATION_JSON_VALUE)) @@ -2369,6 +2533,9 @@ public void testOwnerPatchToDisconnectProfileFromOrcidWithSyncSettingsRemain() t .andExpect(jsonPath("$.orcid").doesNotExist()) .andExpect(jsonPath("$.orcidSynchronization").doesNotExist()); + verify(orcidClientMock, times(1)).revokeToken(matchesToken(orcidToken)); + verifyNoMoreInteractions(orcidClientMock); + profile = context.reloadEntity(profile); assertThat(getMetadataValues(profile, "person.identifier.orcid"), empty()); @@ -2778,4 +2945,13 @@ private OrcidTokenResponseDTO buildOrcidTokenResponse(String orcid, String acces return token; } + private static OrcidToken matchesToken(OrcidToken orcidToken) { + return argThat( + token -> + token != null && + orcidToken.getAccessToken().equals(token.getAccessToken()) && + orcidToken.getID().equals(token.getID()) + ); + } + } diff --git a/dspace/config/modules/orcid.cfg b/dspace/config/modules/orcid.cfg index 28e8df60f83d..ad31371cb890 100644 --- a/dspace/config/modules/orcid.cfg +++ b/dspace/config/modules/orcid.cfg @@ -17,6 +17,7 @@ orcid.disconnection.remain-sync = true # ORCID API (https://github.com/ORCID/ORCID-Source/tree/master/orcid-api-web#endpoints) orcid.domain-url= https://sandbox.orcid.org orcid.authorize-url = ${orcid.domain-url}/oauth/authorize +orcid.revoke-url = ${orcid.domain-url}/oauth/revoke orcid.token-url = ${orcid.domain-url}/oauth/token orcid.api-url = https://api.sandbox.orcid.org/v3.0 orcid.public-url = https://pub.sandbox.orcid.org/v3.0 diff --git a/dspace/config/spring/api/orcid-services.xml b/dspace/config/spring/api/orcid-services.xml index 8286a16a88fb..6ec9be9fdf5d 100644 --- a/dspace/config/spring/api/orcid-services.xml +++ b/dspace/config/spring/api/orcid-services.xml @@ -30,6 +30,7 @@ + From a9400a7f48c7afa321310911e6c870197237394a Mon Sep 17 00:00:00 2001 From: Vincenzo Mecca Date: Tue, 21 Nov 2023 11:51:25 +0100 Subject: [PATCH 021/979] [CST-14901][DSC-1357][#8662] Handles versioning for ORCID publications. feat: - ORCID publications waiting to be published are removed whenever a new version is created - ORCID publications already published will be updated with the ref to the last item version - ORCID consumer will process only latest item versions, ignoring all the other ones --- .../org/dspace/content/ItemServiceImpl.java | 42 ++++ .../dspace/content/service/ItemService.java | 10 + .../orcid/consumer/OrcidQueueConsumer.java | 49 +++-- .../org/dspace/orcid/dao/OrcidQueueDAO.java | 10 + .../orcid/dao/impl/OrcidQueueDAOImpl.java | 7 + .../orcid/service/OrcidQueueService.java | 10 + .../service/impl/OrcidQueueServiceImpl.java | 5 + .../dspace/versioning/VersioningConsumer.java | 35 +++- .../dspace/orcid/OrcidQueueConsumerIT.java | 185 ++++++++++++++++++ 9 files changed, 336 insertions(+), 17 deletions(-) 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 70bdf4b7d950..d300f22d5637 100644 --- a/dspace-api/src/main/java/org/dspace/content/ItemServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/content/ItemServiceImpl.java @@ -81,6 +81,9 @@ import org.dspace.profile.service.ResearcherProfileService; import org.dspace.qaevent.dao.QAEventsDAO; import org.dspace.services.ConfigurationService; +import org.dspace.versioning.Version; +import org.dspace.versioning.VersionHistory; +import org.dspace.versioning.service.VersionHistoryService; import org.dspace.versioning.service.VersioningService; import org.dspace.workflow.WorkflowItemService; import org.dspace.workflow.factory.WorkflowServiceFactory; @@ -176,6 +179,9 @@ public class ItemServiceImpl extends DSpaceObjectServiceImpl implements It @Autowired private QAEventsDAO qaEventsDao; + @Autowired + private VersionHistoryService versionHistoryService; + protected ItemServiceImpl() { } @@ -1931,4 +1937,40 @@ private void deleteOrcidQueueRecords(Context context, Item item) throws SQLExcep } } + @Override + public boolean isLatestVersion(Context context, Item item) throws SQLException { + + VersionHistory history = versionHistoryService.findByItem(context, item); + if (history == null) { + // not all items have a version history + // if an item does not have a version history, it is by definition the latest + // version + return true; + } + + // start with the very latest version of the given item (may still be in + // workspace) + Version latestVersion = versionHistoryService.getLatestVersion(context, history); + + // find the latest version of the given item that is archived + while (latestVersion != null && !latestVersion.getItem().isArchived()) { + latestVersion = versionHistoryService.getPrevious(context, history, latestVersion); + } + + // could not find an archived version of the given item + if (latestVersion == null) { + // this scenario should never happen, but let's err on the side of showing too + // many items vs. to little + // (see discovery.xml, a lot of discovery configs filter out all items that are + // not the latest version) + return true; + } + + // sanity check + assert latestVersion.getItem().isArchived(); + + return item.equals(latestVersion.getItem()); + + } + } diff --git a/dspace-api/src/main/java/org/dspace/content/service/ItemService.java b/dspace-api/src/main/java/org/dspace/content/service/ItemService.java index 47d2d5bdaa88..3fea75665bcb 100644 --- a/dspace-api/src/main/java/org/dspace/content/service/ItemService.java +++ b/dspace-api/src/main/java/org/dspace/content/service/ItemService.java @@ -1009,4 +1009,14 @@ List getMetadata(Item item, String schema, String element, String */ EntityType getEntityType(Context context, Item item) throws SQLException; + + /** + * Check whether the given item is the latest version. If the latest item cannot + * be determined, because either the version history or the latest version is + * not present, assume the item is latest. + * @param context the DSpace context. + * @param item the item that should be checked. + * @return true if the item is the latest version, false otherwise. + */ + public boolean isLatestVersion(Context context, Item item) throws SQLException; } diff --git a/dspace-api/src/main/java/org/dspace/orcid/consumer/OrcidQueueConsumer.java b/dspace-api/src/main/java/org/dspace/orcid/consumer/OrcidQueueConsumer.java index 97da341fb811..6b174e96957f 100644 --- a/dspace-api/src/main/java/org/dspace/orcid/consumer/OrcidQueueConsumer.java +++ b/dspace-api/src/main/java/org/dspace/orcid/consumer/OrcidQueueConsumer.java @@ -14,9 +14,10 @@ import static org.apache.commons.collections.CollectionUtils.isNotEmpty; import java.sql.SQLException; -import java.util.ArrayList; +import java.util.HashSet; import java.util.List; import java.util.Optional; +import java.util.Set; import java.util.UUID; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -82,7 +83,7 @@ public class OrcidQueueConsumer implements Consumer { private RelationshipService relationshipService; - private final List alreadyConsumedItems = new ArrayList<>(); + private final Set itemsToConsume = new HashSet<>(); @Override public void initialize() throws Exception { @@ -117,17 +118,26 @@ public void consume(Context context, Event event) throws Exception { return; } - if (alreadyConsumedItems.contains(item.getID())) { - return; - } + itemsToConsume.add(item.getID()); + } + + @Override + public void end(Context context) throws Exception { + + for (UUID itemId : itemsToConsume) { + + Item item = itemService.find(context, itemId); + + context.turnOffAuthorisationSystem(); + try { + consumeItem(context, item); + } finally { + context.restoreAuthSystemState(); + } - context.turnOffAuthorisationSystem(); - try { - consumeItem(context, item); - } finally { - context.restoreAuthSystemState(); } + itemsToConsume.clear(); } /** @@ -146,7 +156,7 @@ private void consumeItem(Context context, Item item) throws SQLException { consumeProfile(context, item); } - alreadyConsumedItems.add(item.getID()); + itemsToConsume.add(item.getID()); } @@ -169,6 +179,10 @@ private void consumeEntity(Context context, Item entity) throws SQLException { continue; } + if (isNotLatestVersion(context, entity)) { + continue; + } + orcidQueueService.create(context, relatedItem, entity); } @@ -329,6 +343,14 @@ private boolean isNotProfileItem(Item profileItemItem) { return !getProfileType().equals(itemService.getEntityTypeLabel(profileItemItem)); } + private boolean isNotLatestVersion(Context context, Item entity) { + try { + return !itemService.isLatestVersion(context, entity); + } catch (SQLException e) { + throw new RuntimeException(e); + } + } + private String getMetadataValue(Item item, String metadataField) { return itemService.getMetadataFirstValue(item, new MetadataFieldName(metadataField), Item.ANY); } @@ -345,11 +367,6 @@ private boolean isOrcidSynchronizationDisabled() { return !configurationService.getBooleanProperty("orcid.synchronization-enabled", true); } - @Override - public void end(Context context) throws Exception { - alreadyConsumedItems.clear(); - } - @Override public void finish(Context context) throws Exception { // nothing to do diff --git a/dspace-api/src/main/java/org/dspace/orcid/dao/OrcidQueueDAO.java b/dspace-api/src/main/java/org/dspace/orcid/dao/OrcidQueueDAO.java index 235443b15033..b7e0b1ed2a85 100644 --- a/dspace-api/src/main/java/org/dspace/orcid/dao/OrcidQueueDAO.java +++ b/dspace-api/src/main/java/org/dspace/orcid/dao/OrcidQueueDAO.java @@ -74,6 +74,16 @@ public List findByProfileItemAndEntity(Context context, Item profile */ public List findByProfileItemOrEntity(Context context, Item item) throws SQLException; + /** + * Get the OrcidQueue records where the given item is the entity. + * + * @param context DSpace context object + * @param item the item to search for + * @return the found OrcidQueue entities + * @throws SQLException if database error + */ + public List findByEntity(Context context, Item item) throws SQLException; + /** * Find all the OrcidQueue records with the given entity and record type. * diff --git a/dspace-api/src/main/java/org/dspace/orcid/dao/impl/OrcidQueueDAOImpl.java b/dspace-api/src/main/java/org/dspace/orcid/dao/impl/OrcidQueueDAOImpl.java index c8e48e3f17d6..091e59750517 100644 --- a/dspace-api/src/main/java/org/dspace/orcid/dao/impl/OrcidQueueDAOImpl.java +++ b/dspace-api/src/main/java/org/dspace/orcid/dao/impl/OrcidQueueDAOImpl.java @@ -63,6 +63,13 @@ public List findByProfileItemOrEntity(Context context, Item item) th return query.getResultList(); } + @Override + public List findByEntity(Context context, Item item) throws SQLException { + Query query = createQuery(context, "FROM OrcidQueue WHERE entity.id = :itemId"); + query.setParameter("itemId", item.getID()); + return query.getResultList(); + } + @Override public List findByEntityAndRecordType(Context context, Item entity, String type) throws SQLException { Query query = createQuery(context, "FROM OrcidQueue WHERE entity = :entity AND recordType = :type"); diff --git a/dspace-api/src/main/java/org/dspace/orcid/service/OrcidQueueService.java b/dspace-api/src/main/java/org/dspace/orcid/service/OrcidQueueService.java index 8de25e9caf1e..b667088eabb4 100644 --- a/dspace-api/src/main/java/org/dspace/orcid/service/OrcidQueueService.java +++ b/dspace-api/src/main/java/org/dspace/orcid/service/OrcidQueueService.java @@ -164,6 +164,16 @@ public List findByProfileItemAndEntity(Context context, Item profile */ public List findByProfileItemOrEntity(Context context, Item item) throws SQLException; + /** + * Get the OrcidQueue records where the given item is the entity. + * + * @param context DSpace context object + * @param item the item to search for + * @return the found OrcidQueue records + * @throws SQLException if database error + */ + public List findByEntity(Context context, Item item) throws SQLException; + /** * Get all the OrcidQueue records with attempts less than the given attempts. * diff --git a/dspace-api/src/main/java/org/dspace/orcid/service/impl/OrcidQueueServiceImpl.java b/dspace-api/src/main/java/org/dspace/orcid/service/impl/OrcidQueueServiceImpl.java index d3300fea6606..261f8ef9a9f7 100644 --- a/dspace-api/src/main/java/org/dspace/orcid/service/impl/OrcidQueueServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/orcid/service/impl/OrcidQueueServiceImpl.java @@ -70,6 +70,11 @@ public List findByProfileItemOrEntity(Context context, Item item) th return orcidQueueDAO.findByProfileItemOrEntity(context, item); } + @Override + public List findByEntity(Context context, Item item) throws SQLException { + return orcidQueueDAO.findByEntity(context, item); + } + @Override public long countByProfileItemId(Context context, UUID profileItemId) throws SQLException { return orcidQueueDAO.countByProfileItemId(context, profileItemId); diff --git a/dspace-api/src/main/java/org/dspace/versioning/VersioningConsumer.java b/dspace-api/src/main/java/org/dspace/versioning/VersioningConsumer.java index 63b5391d0a28..27a81a157917 100644 --- a/dspace-api/src/main/java/org/dspace/versioning/VersioningConsumer.java +++ b/dspace-api/src/main/java/org/dspace/versioning/VersioningConsumer.java @@ -33,6 +33,11 @@ import org.dspace.discovery.IndexEventConsumer; import org.dspace.event.Consumer; import org.dspace.event.Event; +import org.dspace.orcid.OrcidHistory; +import org.dspace.orcid.OrcidQueue; +import org.dspace.orcid.factory.OrcidServiceFactory; +import org.dspace.orcid.service.OrcidHistoryService; +import org.dspace.orcid.service.OrcidQueueService; import org.dspace.versioning.factory.VersionServiceFactory; import org.dspace.versioning.service.VersionHistoryService; import org.dspace.versioning.utils.RelationshipVersioningUtils; @@ -58,6 +63,8 @@ public class VersioningConsumer implements Consumer { private RelationshipTypeService relationshipTypeService; private RelationshipService relationshipService; private RelationshipVersioningUtils relationshipVersioningUtils; + private OrcidQueueService orcidQueueService; + private OrcidHistoryService orcidHistoryService; @Override public void initialize() throws Exception { @@ -67,6 +74,8 @@ public void initialize() throws Exception { relationshipTypeService = ContentServiceFactory.getInstance().getRelationshipTypeService(); relationshipService = ContentServiceFactory.getInstance().getRelationshipService(); relationshipVersioningUtils = VersionServiceFactory.getInstance().getRelationshipVersioningUtils(); + this.orcidQueueService = OrcidServiceFactory.getInstance().getOrcidQueueService(); + this.orcidHistoryService = OrcidServiceFactory.getInstance().getOrcidHistoryService(); } @Override @@ -132,7 +141,8 @@ public void consume(Context ctx, Event event) throws Exception { // unarchive previous item unarchiveItem(ctx, previousItem); - + // handles versions for ORCID publications waiting to be shipped, or already published (history-queue). + handleOrcidSynchronization(ctx, previousItem, latestItem); // update relationships updateRelationships(ctx, latestItem, previousItem); } @@ -148,6 +158,29 @@ protected void unarchiveItem(Context ctx, Item item) { )); } + private void handleOrcidSynchronization(Context ctx, Item previousItem, Item latestItem) { + try { + replaceOrcidHistoryEntities(ctx, previousItem, latestItem); + removeOrcidQueueEntries(ctx, previousItem); + } catch (SQLException e) { + throw new RuntimeException(e); + } + } + + private void removeOrcidQueueEntries(Context ctx, Item previousItem) throws SQLException { + List queueEntries = orcidQueueService.findByEntity(ctx, previousItem); + for (OrcidQueue queueEntry : queueEntries) { + orcidQueueService.delete(ctx, queueEntry); + } + } + + private void replaceOrcidHistoryEntities(Context ctx, Item previousItem, Item latestItem) throws SQLException { + List entries = orcidHistoryService.findByEntity(ctx, previousItem); + for (OrcidHistory entry : entries) { + entry.setEntity(latestItem); + } + } + /** * Update {@link Relationship#latestVersionStatus} of the relationships of both the old version and the new version * of the item. diff --git a/dspace-api/src/test/java/org/dspace/orcid/OrcidQueueConsumerIT.java b/dspace-api/src/test/java/org/dspace/orcid/OrcidQueueConsumerIT.java index f2e528d78cd6..e17fd0072efa 100644 --- a/dspace-api/src/test/java/org/dspace/orcid/OrcidQueueConsumerIT.java +++ b/dspace-api/src/test/java/org/dspace/orcid/OrcidQueueConsumerIT.java @@ -23,6 +23,7 @@ import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.hasItem; import static org.hamcrest.Matchers.hasSize; +import static org.hamcrest.Matchers.is; import java.sql.SQLException; import java.time.Instant; @@ -41,13 +42,19 @@ import org.dspace.content.Item; import org.dspace.content.MetadataValue; import org.dspace.content.RelationshipType; +import org.dspace.content.WorkspaceItem; import org.dspace.content.factory.ContentServiceFactory; +import org.dspace.content.service.InstallItemService; import org.dspace.content.service.ItemService; +import org.dspace.content.service.WorkspaceItemService; import org.dspace.orcid.consumer.OrcidQueueConsumer; import org.dspace.orcid.factory.OrcidServiceFactory; import org.dspace.orcid.service.OrcidQueueService; import org.dspace.services.ConfigurationService; import org.dspace.services.factory.DSpaceServicesFactory; +import org.dspace.utils.DSpace; +import org.dspace.versioning.Version; +import org.dspace.versioning.service.VersioningService; import org.junit.After; import org.junit.Before; import org.junit.Test; @@ -64,8 +71,15 @@ public class OrcidQueueConsumerIT extends AbstractIntegrationTestWithDatabase { private ItemService itemService = ContentServiceFactory.getInstance().getItemService(); + private WorkspaceItemService workspaceItemService = ContentServiceFactory.getInstance().getWorkspaceItemService(); + + private InstallItemService installItemService = ContentServiceFactory.getInstance().getInstallItemService(); + private ConfigurationService configurationService = DSpaceServicesFactory.getInstance().getConfigurationService(); + private VersioningService versioningService = new DSpace().getServiceManager() + .getServicesByType(VersioningService.class).get(0); + private Collection profileCollection; @Before @@ -763,6 +777,177 @@ public void testWithManyInsertionAndDeletionOfSameMetadataValue() throws Excepti } + @Test + public void testOrcidQueueRecordCreationForPublicationWithNotFoundAuthority() throws Exception { + + context.turnOffAuthorisationSystem(); + + Item profile = ItemBuilder.createItem(context, profileCollection) + .withTitle("Test User") + .withOrcidIdentifier("0000-1111-2222-3333") + .withOrcidAccessToken("ab4d18a0-8d9a-40f1-b601-a417255c8d20", eperson) + .withOrcidSynchronizationPublicationsPreference(ALL) + .build(); + + Collection publicationCollection = createCollection("Publications", "Publication"); + + Item publication = ItemBuilder.createItem(context, publicationCollection) + .withTitle("Test publication") + .withAuthor("First User") + .withAuthor("Test User") + .build(); + + EntityType publicationType = EntityTypeBuilder.createEntityTypeBuilder(context, "Publication").build(); + EntityType personType = EntityTypeBuilder.createEntityTypeBuilder(context, "Person").build(); + + RelationshipType isAuthorOfPublication = createRelationshipTypeBuilder(context, personType, publicationType, + "isAuthorOfPublication", + "isPublicationOfAuthor", 0, null, 0, + null).build(); + + RelationshipBuilder.createRelationshipBuilder(context, profile, publication, isAuthorOfPublication).build(); + + context.restoreAuthSystemState(); + context.commit(); + + List orcidQueueRecords = orcidQueueService.findAll(context); + assertThat(orcidQueueRecords, hasSize(1)); + assertThat(orcidQueueRecords.get(0), matches(profile, publication, "Publication", INSERT)); + } + + @Test + public void testOrcidQueueWithItemVersioning() throws Exception { + + context.turnOffAuthorisationSystem(); + + Item profile = ItemBuilder.createItem(context, profileCollection) + .withTitle("Test User") + .withOrcidIdentifier("0000-1111-2222-3333") + .withOrcidAccessToken("ab4d18a0-8d9a-40f1-b601-a417255c8d20", eperson) + .withOrcidSynchronizationPublicationsPreference(ALL) + .build(); + + Collection publicationCollection = createCollection("Publications", "Publication"); + + Item publication = ItemBuilder.createItem(context, publicationCollection) + .withTitle("Test publication") + .withAuthor("Test User") + .build(); + + EntityType publicationType = EntityTypeBuilder.createEntityTypeBuilder(context, "Publication").build(); + EntityType personType = EntityTypeBuilder.createEntityTypeBuilder(context, "Person").build(); + + RelationshipType isAuthorOfPublication = createRelationshipTypeBuilder(context, personType, publicationType, + "isAuthorOfPublication", + "isPublicationOfAuthor", 0, null, 0, + null).build(); + + RelationshipBuilder.createRelationshipBuilder(context, profile, publication, isAuthorOfPublication).build(); + + context.restoreAuthSystemState(); + context.commit(); + + List orcidQueueRecords = orcidQueueService.findAll(context); + assertThat(orcidQueueRecords, hasSize(1)); + assertThat(orcidQueueRecords.get(0), matches(profile, publication, "Publication", INSERT)); + + context.turnOffAuthorisationSystem(); + Version newVersion = versioningService.createNewVersion(context, publication); + context.restoreAuthSystemState(); + Item newPublication = newVersion.getItem(); + assertThat(newPublication.isArchived(), is(false)); + + context.commit(); + + orcidQueueRecords = orcidQueueService.findAll(context); + assertThat(orcidQueueRecords, hasSize(1)); + assertThat(orcidQueueRecords.get(0), matches(profile, publication, "Publication", INSERT)); + + WorkspaceItem workspaceItem = workspaceItemService.findByItem(context, newVersion.getItem()); + context.turnOffAuthorisationSystem(); + + installItemService.installItem(context, workspaceItem); + + context.restoreAuthSystemState(); + context.commit(); + + orcidQueueRecords = orcidQueueService.findAll(context); + assertThat(orcidQueueRecords, hasSize(1)); + assertThat(orcidQueueRecords.get(0), matches(profile, newPublication, "Publication", INSERT)); + } + + @Test + public void testOrcidQueueUpdateWithItemVersioning() throws Exception { + + context.turnOffAuthorisationSystem(); + + Item profile = ItemBuilder.createItem(context, profileCollection) + .withTitle("Test User") + .withOrcidIdentifier("0000-1111-2222-3333") + .withOrcidAccessToken("ab4d18a0-8d9a-40f1-b601-a417255c8d20", eperson) + .withOrcidSynchronizationPublicationsPreference(ALL) + .build(); + + Collection publicationCollection = createCollection("Publications", "Publication"); + + Item publication = ItemBuilder.createItem(context, publicationCollection) + .withTitle("Test publication") + .build(); + + OrcidHistory orcidHistory = OrcidHistoryBuilder.createOrcidHistory(context, profile, publication) + .withDescription("Test publication") + .withOperation(OrcidOperation.INSERT) + .withPutCode("12345") + .withStatus(201) + .build(); + + addMetadata(publication, "dc", "contributor", "author", "Test User", null); + + EntityType publicationType = EntityTypeBuilder.createEntityTypeBuilder(context, "Publication").build(); + EntityType personType = EntityTypeBuilder.createEntityTypeBuilder(context, "Person").build(); + + RelationshipType isAuthorOfPublication = + createRelationshipTypeBuilder( + context, personType, publicationType, + "isAuthorOfPublication", + "isPublicationOfAuthor", 0, null, 0, + null + ).build(); + + RelationshipBuilder.createRelationshipBuilder(context, profile, publication, isAuthorOfPublication).build(); + + context.commit(); + + List orcidQueueRecords = orcidQueueService.findAll(context); + assertThat(orcidQueueRecords, hasSize(1)); + assertThat(orcidQueueRecords.get(0), matches(profile, publication, "Publication", "12345", UPDATE)); + + Version newVersion = versioningService.createNewVersion(context, publication); + Item newPublication = newVersion.getItem(); + assertThat(newPublication.isArchived(), is(false)); + + context.commit(); + + orcidQueueRecords = orcidQueueService.findAll(context); + assertThat(orcidQueueRecords, hasSize(1)); + assertThat(orcidQueueRecords.get(0), matches(profile, publication, "Publication", "12345", UPDATE)); + + WorkspaceItem workspaceItem = workspaceItemService.findByItem(context, newVersion.getItem()); + installItemService.installItem(context, workspaceItem); + + context.commit(); + + context.restoreAuthSystemState(); + + orcidQueueRecords = orcidQueueService.findAll(context); + assertThat(orcidQueueRecords, hasSize(1)); + assertThat(orcidQueueRecords.get(0), matches(profile, newPublication, "Publication", "12345", UPDATE)); + + orcidHistory = context.reloadEntity(orcidHistory); + assertThat(orcidHistory.getEntity(), is(newPublication)); + + } + private void addMetadata(Item item, String schema, String element, String qualifier, String value, String authority) throws Exception { context.turnOffAuthorisationSystem(); From 26905b7d45f058f1bd8476f9e915af9a5be1547f Mon Sep 17 00:00:00 2001 From: Vincenzo Mecca Date: Tue, 21 Nov 2023 11:51:25 +0100 Subject: [PATCH 022/979] [CST-14901][DSC-1357][#8662] Handles versioning for ORCID publications. feat: - ORCID publications waiting to be published are removed whenever a new version is created - ORCID publications already published will be updated with the ref to the last item version - ORCID consumer will process only latest item versions, ignoring all the other ones --- .../org/dspace/content/ItemServiceImpl.java | 42 ++++ .../dspace/content/service/ItemService.java | 10 + .../orcid/consumer/OrcidQueueConsumer.java | 51 +++-- .../org/dspace/orcid/dao/OrcidQueueDAO.java | 10 + .../orcid/dao/impl/OrcidQueueDAOImpl.java | 7 + .../orcid/service/OrcidQueueService.java | 10 + .../service/impl/OrcidQueueServiceImpl.java | 5 + .../dspace/versioning/VersioningConsumer.java | 35 +++- .../dspace/orcid/OrcidQueueConsumerIT.java | 185 ++++++++++++++++++ 9 files changed, 337 insertions(+), 18 deletions(-) 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 ac6c0d43d710..ae7081f73a37 100644 --- a/dspace-api/src/main/java/org/dspace/content/ItemServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/content/ItemServiceImpl.java @@ -79,6 +79,9 @@ import org.dspace.orcid.service.OrcidTokenService; import org.dspace.profile.service.ResearcherProfileService; import org.dspace.services.ConfigurationService; +import org.dspace.versioning.Version; +import org.dspace.versioning.VersionHistory; +import org.dspace.versioning.service.VersionHistoryService; import org.dspace.versioning.service.VersioningService; import org.dspace.workflow.WorkflowItemService; import org.dspace.workflow.factory.WorkflowServiceFactory; @@ -171,6 +174,9 @@ public class ItemServiceImpl extends DSpaceObjectServiceImpl implements It @Autowired(required = true) protected SubscribeService subscribeService; + @Autowired + private VersionHistoryService versionHistoryService; + protected ItemServiceImpl() { super(); } @@ -1920,4 +1926,40 @@ private void deleteOrcidQueueRecords(Context context, Item item) throws SQLExcep } } + @Override + public boolean isLatestVersion(Context context, Item item) throws SQLException { + + VersionHistory history = versionHistoryService.findByItem(context, item); + if (history == null) { + // not all items have a version history + // if an item does not have a version history, it is by definition the latest + // version + return true; + } + + // start with the very latest version of the given item (may still be in + // workspace) + Version latestVersion = versionHistoryService.getLatestVersion(context, history); + + // find the latest version of the given item that is archived + while (latestVersion != null && !latestVersion.getItem().isArchived()) { + latestVersion = versionHistoryService.getPrevious(context, history, latestVersion); + } + + // could not find an archived version of the given item + if (latestVersion == null) { + // this scenario should never happen, but let's err on the side of showing too + // many items vs. to little + // (see discovery.xml, a lot of discovery configs filter out all items that are + // not the latest version) + return true; + } + + // sanity check + assert latestVersion.getItem().isArchived(); + + return item.equals(latestVersion.getItem()); + + } + } diff --git a/dspace-api/src/main/java/org/dspace/content/service/ItemService.java b/dspace-api/src/main/java/org/dspace/content/service/ItemService.java index 12867ad18c3f..a307e7ceffad 100644 --- a/dspace-api/src/main/java/org/dspace/content/service/ItemService.java +++ b/dspace-api/src/main/java/org/dspace/content/service/ItemService.java @@ -983,4 +983,14 @@ public List getMetadata(Item item, String schema, String element, */ public EntityType getEntityType(Context context, Item item) throws SQLException; + + /** + * Check whether the given item is the latest version. If the latest item cannot + * be determined, because either the version history or the latest version is + * not present, assume the item is latest. + * @param context the DSpace context. + * @param item the item that should be checked. + * @return true if the item is the latest version, false otherwise. + */ + public boolean isLatestVersion(Context context, Item item) throws SQLException; } diff --git a/dspace-api/src/main/java/org/dspace/orcid/consumer/OrcidQueueConsumer.java b/dspace-api/src/main/java/org/dspace/orcid/consumer/OrcidQueueConsumer.java index d177e61607f1..ae989e1dd8f8 100644 --- a/dspace-api/src/main/java/org/dspace/orcid/consumer/OrcidQueueConsumer.java +++ b/dspace-api/src/main/java/org/dspace/orcid/consumer/OrcidQueueConsumer.java @@ -14,9 +14,10 @@ import static org.apache.commons.collections.CollectionUtils.isNotEmpty; import java.sql.SQLException; -import java.util.ArrayList; +import java.util.HashSet; import java.util.List; import java.util.Optional; +import java.util.Set; import java.util.UUID; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -56,7 +57,7 @@ * be synchronized (based on the preferences set by the user) *
  • are publications/fundings related to profile items linked to orcid (based * on the preferences set by the user)
  • - * + * * * * @author Luca Giamminonni (luca.giamminonni at 4science.it) @@ -82,7 +83,7 @@ public class OrcidQueueConsumer implements Consumer { private RelationshipService relationshipService; - private List alreadyConsumedItems = new ArrayList<>(); + private final Set itemsToConsume = new HashSet<>(); @Override public void initialize() throws Exception { @@ -117,17 +118,26 @@ public void consume(Context context, Event event) throws Exception { return; } - if (alreadyConsumedItems.contains(item.getID())) { - return; - } + itemsToConsume.add(item.getID()); + } + + @Override + public void end(Context context) throws Exception { + + for (UUID itemId : itemsToConsume) { + + Item item = itemService.find(context, itemId); + + context.turnOffAuthorisationSystem(); + try { + consumeItem(context, item); + } finally { + context.restoreAuthSystemState(); + } - context.turnOffAuthorisationSystem(); - try { - consumeItem(context, item); - } finally { - context.restoreAuthSystemState(); } + itemsToConsume.clear(); } /** @@ -146,7 +156,7 @@ private void consumeItem(Context context, Item item) throws SQLException { consumeProfile(context, item); } - alreadyConsumedItems.add(item.getID()); + itemsToConsume.add(item.getID()); } @@ -169,6 +179,10 @@ private void consumeEntity(Context context, Item entity) throws SQLException { continue; } + if (isNotLatestVersion(context, entity)) { + continue; + } + orcidQueueService.create(context, relatedItem, entity); } @@ -329,6 +343,14 @@ private boolean isNotProfileItem(Item profileItemItem) { return !getProfileType().equals(itemService.getEntityTypeLabel(profileItemItem)); } + private boolean isNotLatestVersion(Context context, Item entity) { + try { + return !itemService.isLatestVersion(context, entity); + } catch (SQLException e) { + throw new RuntimeException(e); + } + } + private String getMetadataValue(Item item, String metadataField) { return itemService.getMetadataFirstValue(item, new MetadataFieldName(metadataField), Item.ANY); } @@ -345,11 +367,6 @@ private boolean isOrcidSynchronizationDisabled() { return !configurationService.getBooleanProperty("orcid.synchronization-enabled", true); } - @Override - public void end(Context context) throws Exception { - alreadyConsumedItems.clear(); - } - @Override public void finish(Context context) throws Exception { // nothing to do diff --git a/dspace-api/src/main/java/org/dspace/orcid/dao/OrcidQueueDAO.java b/dspace-api/src/main/java/org/dspace/orcid/dao/OrcidQueueDAO.java index 235443b15033..b7e0b1ed2a85 100644 --- a/dspace-api/src/main/java/org/dspace/orcid/dao/OrcidQueueDAO.java +++ b/dspace-api/src/main/java/org/dspace/orcid/dao/OrcidQueueDAO.java @@ -74,6 +74,16 @@ public List findByProfileItemAndEntity(Context context, Item profile */ public List findByProfileItemOrEntity(Context context, Item item) throws SQLException; + /** + * Get the OrcidQueue records where the given item is the entity. + * + * @param context DSpace context object + * @param item the item to search for + * @return the found OrcidQueue entities + * @throws SQLException if database error + */ + public List findByEntity(Context context, Item item) throws SQLException; + /** * Find all the OrcidQueue records with the given entity and record type. * diff --git a/dspace-api/src/main/java/org/dspace/orcid/dao/impl/OrcidQueueDAOImpl.java b/dspace-api/src/main/java/org/dspace/orcid/dao/impl/OrcidQueueDAOImpl.java index 2114b2535759..8e941b056535 100644 --- a/dspace-api/src/main/java/org/dspace/orcid/dao/impl/OrcidQueueDAOImpl.java +++ b/dspace-api/src/main/java/org/dspace/orcid/dao/impl/OrcidQueueDAOImpl.java @@ -63,6 +63,13 @@ public List findByProfileItemOrEntity(Context context, Item item) th return query.getResultList(); } + @Override + public List findByEntity(Context context, Item item) throws SQLException { + Query query = createQuery(context, "FROM OrcidQueue WHERE entity.id = :itemId"); + query.setParameter("itemId", item.getID()); + return query.getResultList(); + } + @Override public List findByEntityAndRecordType(Context context, Item entity, String type) throws SQLException { Query query = createQuery(context, "FROM OrcidQueue WHERE entity = :entity AND recordType = :type"); diff --git a/dspace-api/src/main/java/org/dspace/orcid/service/OrcidQueueService.java b/dspace-api/src/main/java/org/dspace/orcid/service/OrcidQueueService.java index 8de25e9caf1e..b667088eabb4 100644 --- a/dspace-api/src/main/java/org/dspace/orcid/service/OrcidQueueService.java +++ b/dspace-api/src/main/java/org/dspace/orcid/service/OrcidQueueService.java @@ -164,6 +164,16 @@ public List findByProfileItemAndEntity(Context context, Item profile */ public List findByProfileItemOrEntity(Context context, Item item) throws SQLException; + /** + * Get the OrcidQueue records where the given item is the entity. + * + * @param context DSpace context object + * @param item the item to search for + * @return the found OrcidQueue records + * @throws SQLException if database error + */ + public List findByEntity(Context context, Item item) throws SQLException; + /** * Get all the OrcidQueue records with attempts less than the given attempts. * diff --git a/dspace-api/src/main/java/org/dspace/orcid/service/impl/OrcidQueueServiceImpl.java b/dspace-api/src/main/java/org/dspace/orcid/service/impl/OrcidQueueServiceImpl.java index d3300fea6606..261f8ef9a9f7 100644 --- a/dspace-api/src/main/java/org/dspace/orcid/service/impl/OrcidQueueServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/orcid/service/impl/OrcidQueueServiceImpl.java @@ -70,6 +70,11 @@ public List findByProfileItemOrEntity(Context context, Item item) th return orcidQueueDAO.findByProfileItemOrEntity(context, item); } + @Override + public List findByEntity(Context context, Item item) throws SQLException { + return orcidQueueDAO.findByEntity(context, item); + } + @Override public long countByProfileItemId(Context context, UUID profileItemId) throws SQLException { return orcidQueueDAO.countByProfileItemId(context, profileItemId); diff --git a/dspace-api/src/main/java/org/dspace/versioning/VersioningConsumer.java b/dspace-api/src/main/java/org/dspace/versioning/VersioningConsumer.java index 63b5391d0a28..27a81a157917 100644 --- a/dspace-api/src/main/java/org/dspace/versioning/VersioningConsumer.java +++ b/dspace-api/src/main/java/org/dspace/versioning/VersioningConsumer.java @@ -33,6 +33,11 @@ import org.dspace.discovery.IndexEventConsumer; import org.dspace.event.Consumer; import org.dspace.event.Event; +import org.dspace.orcid.OrcidHistory; +import org.dspace.orcid.OrcidQueue; +import org.dspace.orcid.factory.OrcidServiceFactory; +import org.dspace.orcid.service.OrcidHistoryService; +import org.dspace.orcid.service.OrcidQueueService; import org.dspace.versioning.factory.VersionServiceFactory; import org.dspace.versioning.service.VersionHistoryService; import org.dspace.versioning.utils.RelationshipVersioningUtils; @@ -58,6 +63,8 @@ public class VersioningConsumer implements Consumer { private RelationshipTypeService relationshipTypeService; private RelationshipService relationshipService; private RelationshipVersioningUtils relationshipVersioningUtils; + private OrcidQueueService orcidQueueService; + private OrcidHistoryService orcidHistoryService; @Override public void initialize() throws Exception { @@ -67,6 +74,8 @@ public void initialize() throws Exception { relationshipTypeService = ContentServiceFactory.getInstance().getRelationshipTypeService(); relationshipService = ContentServiceFactory.getInstance().getRelationshipService(); relationshipVersioningUtils = VersionServiceFactory.getInstance().getRelationshipVersioningUtils(); + this.orcidQueueService = OrcidServiceFactory.getInstance().getOrcidQueueService(); + this.orcidHistoryService = OrcidServiceFactory.getInstance().getOrcidHistoryService(); } @Override @@ -132,7 +141,8 @@ public void consume(Context ctx, Event event) throws Exception { // unarchive previous item unarchiveItem(ctx, previousItem); - + // handles versions for ORCID publications waiting to be shipped, or already published (history-queue). + handleOrcidSynchronization(ctx, previousItem, latestItem); // update relationships updateRelationships(ctx, latestItem, previousItem); } @@ -148,6 +158,29 @@ protected void unarchiveItem(Context ctx, Item item) { )); } + private void handleOrcidSynchronization(Context ctx, Item previousItem, Item latestItem) { + try { + replaceOrcidHistoryEntities(ctx, previousItem, latestItem); + removeOrcidQueueEntries(ctx, previousItem); + } catch (SQLException e) { + throw new RuntimeException(e); + } + } + + private void removeOrcidQueueEntries(Context ctx, Item previousItem) throws SQLException { + List queueEntries = orcidQueueService.findByEntity(ctx, previousItem); + for (OrcidQueue queueEntry : queueEntries) { + orcidQueueService.delete(ctx, queueEntry); + } + } + + private void replaceOrcidHistoryEntities(Context ctx, Item previousItem, Item latestItem) throws SQLException { + List entries = orcidHistoryService.findByEntity(ctx, previousItem); + for (OrcidHistory entry : entries) { + entry.setEntity(latestItem); + } + } + /** * Update {@link Relationship#latestVersionStatus} of the relationships of both the old version and the new version * of the item. diff --git a/dspace-api/src/test/java/org/dspace/orcid/OrcidQueueConsumerIT.java b/dspace-api/src/test/java/org/dspace/orcid/OrcidQueueConsumerIT.java index f2e528d78cd6..e17fd0072efa 100644 --- a/dspace-api/src/test/java/org/dspace/orcid/OrcidQueueConsumerIT.java +++ b/dspace-api/src/test/java/org/dspace/orcid/OrcidQueueConsumerIT.java @@ -23,6 +23,7 @@ import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.hasItem; import static org.hamcrest.Matchers.hasSize; +import static org.hamcrest.Matchers.is; import java.sql.SQLException; import java.time.Instant; @@ -41,13 +42,19 @@ import org.dspace.content.Item; import org.dspace.content.MetadataValue; import org.dspace.content.RelationshipType; +import org.dspace.content.WorkspaceItem; import org.dspace.content.factory.ContentServiceFactory; +import org.dspace.content.service.InstallItemService; import org.dspace.content.service.ItemService; +import org.dspace.content.service.WorkspaceItemService; import org.dspace.orcid.consumer.OrcidQueueConsumer; import org.dspace.orcid.factory.OrcidServiceFactory; import org.dspace.orcid.service.OrcidQueueService; import org.dspace.services.ConfigurationService; import org.dspace.services.factory.DSpaceServicesFactory; +import org.dspace.utils.DSpace; +import org.dspace.versioning.Version; +import org.dspace.versioning.service.VersioningService; import org.junit.After; import org.junit.Before; import org.junit.Test; @@ -64,8 +71,15 @@ public class OrcidQueueConsumerIT extends AbstractIntegrationTestWithDatabase { private ItemService itemService = ContentServiceFactory.getInstance().getItemService(); + private WorkspaceItemService workspaceItemService = ContentServiceFactory.getInstance().getWorkspaceItemService(); + + private InstallItemService installItemService = ContentServiceFactory.getInstance().getInstallItemService(); + private ConfigurationService configurationService = DSpaceServicesFactory.getInstance().getConfigurationService(); + private VersioningService versioningService = new DSpace().getServiceManager() + .getServicesByType(VersioningService.class).get(0); + private Collection profileCollection; @Before @@ -763,6 +777,177 @@ public void testWithManyInsertionAndDeletionOfSameMetadataValue() throws Excepti } + @Test + public void testOrcidQueueRecordCreationForPublicationWithNotFoundAuthority() throws Exception { + + context.turnOffAuthorisationSystem(); + + Item profile = ItemBuilder.createItem(context, profileCollection) + .withTitle("Test User") + .withOrcidIdentifier("0000-1111-2222-3333") + .withOrcidAccessToken("ab4d18a0-8d9a-40f1-b601-a417255c8d20", eperson) + .withOrcidSynchronizationPublicationsPreference(ALL) + .build(); + + Collection publicationCollection = createCollection("Publications", "Publication"); + + Item publication = ItemBuilder.createItem(context, publicationCollection) + .withTitle("Test publication") + .withAuthor("First User") + .withAuthor("Test User") + .build(); + + EntityType publicationType = EntityTypeBuilder.createEntityTypeBuilder(context, "Publication").build(); + EntityType personType = EntityTypeBuilder.createEntityTypeBuilder(context, "Person").build(); + + RelationshipType isAuthorOfPublication = createRelationshipTypeBuilder(context, personType, publicationType, + "isAuthorOfPublication", + "isPublicationOfAuthor", 0, null, 0, + null).build(); + + RelationshipBuilder.createRelationshipBuilder(context, profile, publication, isAuthorOfPublication).build(); + + context.restoreAuthSystemState(); + context.commit(); + + List orcidQueueRecords = orcidQueueService.findAll(context); + assertThat(orcidQueueRecords, hasSize(1)); + assertThat(orcidQueueRecords.get(0), matches(profile, publication, "Publication", INSERT)); + } + + @Test + public void testOrcidQueueWithItemVersioning() throws Exception { + + context.turnOffAuthorisationSystem(); + + Item profile = ItemBuilder.createItem(context, profileCollection) + .withTitle("Test User") + .withOrcidIdentifier("0000-1111-2222-3333") + .withOrcidAccessToken("ab4d18a0-8d9a-40f1-b601-a417255c8d20", eperson) + .withOrcidSynchronizationPublicationsPreference(ALL) + .build(); + + Collection publicationCollection = createCollection("Publications", "Publication"); + + Item publication = ItemBuilder.createItem(context, publicationCollection) + .withTitle("Test publication") + .withAuthor("Test User") + .build(); + + EntityType publicationType = EntityTypeBuilder.createEntityTypeBuilder(context, "Publication").build(); + EntityType personType = EntityTypeBuilder.createEntityTypeBuilder(context, "Person").build(); + + RelationshipType isAuthorOfPublication = createRelationshipTypeBuilder(context, personType, publicationType, + "isAuthorOfPublication", + "isPublicationOfAuthor", 0, null, 0, + null).build(); + + RelationshipBuilder.createRelationshipBuilder(context, profile, publication, isAuthorOfPublication).build(); + + context.restoreAuthSystemState(); + context.commit(); + + List orcidQueueRecords = orcidQueueService.findAll(context); + assertThat(orcidQueueRecords, hasSize(1)); + assertThat(orcidQueueRecords.get(0), matches(profile, publication, "Publication", INSERT)); + + context.turnOffAuthorisationSystem(); + Version newVersion = versioningService.createNewVersion(context, publication); + context.restoreAuthSystemState(); + Item newPublication = newVersion.getItem(); + assertThat(newPublication.isArchived(), is(false)); + + context.commit(); + + orcidQueueRecords = orcidQueueService.findAll(context); + assertThat(orcidQueueRecords, hasSize(1)); + assertThat(orcidQueueRecords.get(0), matches(profile, publication, "Publication", INSERT)); + + WorkspaceItem workspaceItem = workspaceItemService.findByItem(context, newVersion.getItem()); + context.turnOffAuthorisationSystem(); + + installItemService.installItem(context, workspaceItem); + + context.restoreAuthSystemState(); + context.commit(); + + orcidQueueRecords = orcidQueueService.findAll(context); + assertThat(orcidQueueRecords, hasSize(1)); + assertThat(orcidQueueRecords.get(0), matches(profile, newPublication, "Publication", INSERT)); + } + + @Test + public void testOrcidQueueUpdateWithItemVersioning() throws Exception { + + context.turnOffAuthorisationSystem(); + + Item profile = ItemBuilder.createItem(context, profileCollection) + .withTitle("Test User") + .withOrcidIdentifier("0000-1111-2222-3333") + .withOrcidAccessToken("ab4d18a0-8d9a-40f1-b601-a417255c8d20", eperson) + .withOrcidSynchronizationPublicationsPreference(ALL) + .build(); + + Collection publicationCollection = createCollection("Publications", "Publication"); + + Item publication = ItemBuilder.createItem(context, publicationCollection) + .withTitle("Test publication") + .build(); + + OrcidHistory orcidHistory = OrcidHistoryBuilder.createOrcidHistory(context, profile, publication) + .withDescription("Test publication") + .withOperation(OrcidOperation.INSERT) + .withPutCode("12345") + .withStatus(201) + .build(); + + addMetadata(publication, "dc", "contributor", "author", "Test User", null); + + EntityType publicationType = EntityTypeBuilder.createEntityTypeBuilder(context, "Publication").build(); + EntityType personType = EntityTypeBuilder.createEntityTypeBuilder(context, "Person").build(); + + RelationshipType isAuthorOfPublication = + createRelationshipTypeBuilder( + context, personType, publicationType, + "isAuthorOfPublication", + "isPublicationOfAuthor", 0, null, 0, + null + ).build(); + + RelationshipBuilder.createRelationshipBuilder(context, profile, publication, isAuthorOfPublication).build(); + + context.commit(); + + List orcidQueueRecords = orcidQueueService.findAll(context); + assertThat(orcidQueueRecords, hasSize(1)); + assertThat(orcidQueueRecords.get(0), matches(profile, publication, "Publication", "12345", UPDATE)); + + Version newVersion = versioningService.createNewVersion(context, publication); + Item newPublication = newVersion.getItem(); + assertThat(newPublication.isArchived(), is(false)); + + context.commit(); + + orcidQueueRecords = orcidQueueService.findAll(context); + assertThat(orcidQueueRecords, hasSize(1)); + assertThat(orcidQueueRecords.get(0), matches(profile, publication, "Publication", "12345", UPDATE)); + + WorkspaceItem workspaceItem = workspaceItemService.findByItem(context, newVersion.getItem()); + installItemService.installItem(context, workspaceItem); + + context.commit(); + + context.restoreAuthSystemState(); + + orcidQueueRecords = orcidQueueService.findAll(context); + assertThat(orcidQueueRecords, hasSize(1)); + assertThat(orcidQueueRecords.get(0), matches(profile, newPublication, "Publication", "12345", UPDATE)); + + orcidHistory = context.reloadEntity(orcidHistory); + assertThat(orcidHistory.getEntity(), is(newPublication)); + + } + private void addMetadata(Item item, String schema, String element, String qualifier, String value, String authority) throws Exception { context.turnOffAuthorisationSystem(); From dc126f7e943e372c507348a349a302bda57c86c1 Mon Sep 17 00:00:00 2001 From: Jens Vannerum Date: Mon, 22 Jul 2024 15:08:59 +0200 Subject: [PATCH 023/979] 116542: fix issues with CSV importing and the Any language being set on metadata values (cherry picked from commit e03c402a9dfdc8df798ded03c96f23a2dfff0eea) --- .../org/dspace/administer/StructBuilder.java | 2 +- .../org/dspace/app/bulkedit/DSpaceCSV.java | 18 ++++++ .../java/org/dspace/content/Collection.java | 2 +- .../org/dspace/content/MetadataValue.java | 4 ++ .../dspace/app/bulkedit/MetadataImportIT.java | 27 +++++++- .../builder/AbstractDSpaceObjectBuilder.java | 3 +- .../dspace/content/ItemComparatorTest.java | 64 +++++++++---------- .../java/org/dspace/content/ItemTest.java | 34 +++++----- .../content/dao/RelationshipDAOImplIT.java | 4 +- .../dao/RelationshipTypeDAOImplIT.java | 4 +- .../org/dspace/app/rest/PatchMetadataIT.java | 2 +- .../rest/RelationshipRestRepositoryIT.java | 26 ++++---- 12 files changed, 118 insertions(+), 72 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/administer/StructBuilder.java b/dspace-api/src/main/java/org/dspace/administer/StructBuilder.java index 13a1b3b5bbf8..8bbcfe0ff753 100644 --- a/dspace-api/src/main/java/org/dspace/administer/StructBuilder.java +++ b/dspace-api/src/main/java/org/dspace/administer/StructBuilder.java @@ -802,7 +802,7 @@ private static Element[] handleCollections(Context context, // default the short description to the empty string collectionService.setMetadataSingleValue(context, collection, - MD_SHORT_DESCRIPTION, Item.ANY, " "); + MD_SHORT_DESCRIPTION, null, " "); // import the rest of the metadata for (Map.Entry entry : collectionMap.entrySet()) { 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 cbc052b5573f..3533a2397b3d 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 @@ -188,6 +188,15 @@ public DSpaceCSV(InputStream inputStream, Context c) throws Exception { // Verify that the heading is valid in the metadata registry String[] clean = element.split("\\["); String[] parts = clean[0].split("\\."); + // Check language if present, if it's ANY then throw an exception + if (clean.length > 1 && clean[1].equals(Item.ANY + "]")) { + throw new MetadataImportInvalidHeadingException("Language ANY (*) was found in the heading " + + "of the metadata value to import, " + + "this should never be the case", + MetadataImportInvalidHeadingException.ENTRY, + columnCounter); + + } if (parts.length < 2) { throw new MetadataImportInvalidHeadingException(element, @@ -223,6 +232,15 @@ public DSpaceCSV(InputStream inputStream, Context c) throws Exception { } } + // Verify there isn’t already a header that is the same; if it already exists, + // throw MetadataImportInvalidHeadingException + String header = authorityPrefix + element; + if (headings.contains(header)) { + throw new MetadataImportInvalidHeadingException("Duplicate heading found: " + header, + MetadataImportInvalidHeadingException.ENTRY, + columnCounter); + } + // Store the heading headings.add(authorityPrefix + element); } diff --git a/dspace-api/src/main/java/org/dspace/content/Collection.java b/dspace-api/src/main/java/org/dspace/content/Collection.java index 22293dd35ffc..33692d04b3d1 100644 --- a/dspace-api/src/main/java/org/dspace/content/Collection.java +++ b/dspace-api/src/main/java/org/dspace/content/Collection.java @@ -229,7 +229,7 @@ public String getLicenseCollection() { * @throws SQLException if database error */ public void setLicense(Context context, String license) throws SQLException { - getCollectionService().setMetadataSingleValue(context, this, MD_LICENSE, Item.ANY, license); + getCollectionService().setMetadataSingleValue(context, this, MD_LICENSE, null, license); } /** diff --git a/dspace-api/src/main/java/org/dspace/content/MetadataValue.java b/dspace-api/src/main/java/org/dspace/content/MetadataValue.java index 279bdd67c243..dc45579f4ef9 100644 --- a/dspace-api/src/main/java/org/dspace/content/MetadataValue.java +++ b/dspace-api/src/main/java/org/dspace/content/MetadataValue.java @@ -19,6 +19,7 @@ import jakarta.persistence.SequenceGenerator; import jakarta.persistence.Table; import jakarta.persistence.Transient; +import org.apache.commons.lang3.StringUtils; import org.dspace.core.Context; import org.dspace.core.HibernateProxyHelper; import org.dspace.core.ReloadableEntity; @@ -139,6 +140,9 @@ public String getLanguage() { * @param language new language */ public void setLanguage(String language) { + if (StringUtils.equals(language, Item.ANY)) { + language = null; + } this.language = language; } diff --git a/dspace-api/src/test/java/org/dspace/app/bulkedit/MetadataImportIT.java b/dspace-api/src/test/java/org/dspace/app/bulkedit/MetadataImportIT.java index ae079df560ed..de1dcc91c9a1 100644 --- a/dspace-api/src/test/java/org/dspace/app/bulkedit/MetadataImportIT.java +++ b/dspace-api/src/test/java/org/dspace/app/bulkedit/MetadataImportIT.java @@ -75,6 +75,31 @@ public void setUp() throws Exception { context.restoreAuthSystemState(); } + @Test + public void metadataImportTestWithDuplicateHeader() { + String[] csv = {"id,collection,dc.title,dc.title,dc.contributor.author", + "+," + collection.getHandle() + ",\"Test Import 1\",\"Test Import 2\"," + "\"Donald, SmithImported\"," + + "+," + collection.getHandle() + ",\"Test Import 3\",\"Test Import 4\"," + "\"Donald, SmithImported\""}; + // Should throw an exception because of duplicate header + try { + performImportScript(csv); + } catch (Exception e) { + assertTrue(e instanceof MetadataImportInvalidHeadingException); + } + } + + @Test + public void metadataImportTestWithAnyLanguage() { + String[] csv = {"id,collection,dc.title[*],dc.contributor.author", + "+," + collection.getHandle() + ",\"Test Import 1\"," + "\"Donald, SmithImported\""}; + // Should throw an exception because of invalid ANY language (*) in metadata field + try { + performImportScript(csv); + } catch (Exception e) { + assertTrue(e instanceof MetadataImportInvalidHeadingException); + } + } + @Test public void metadataImportTest() throws Exception { String[] csv = {"id,collection,dc.title,dc.contributor.author", @@ -230,7 +255,7 @@ public void metadataImportRemovingValueTest() throws Exception { itemService.getMetadata(item, "dc", "contributor", "author", Item.ANY).get(0).getValue(), "TestAuthorToRemove")); - String[] csv = {"id,collection,dc.title,dc.contributor.author[*]", + String[] csv = {"id,collection,dc.title,dc.contributor.author", item.getID().toString() + "," + personCollection.getHandle() + "," + item.getName() + ","}; performImportScript(csv); item = findItemByName(itemTitle); diff --git a/dspace-api/src/test/java/org/dspace/builder/AbstractDSpaceObjectBuilder.java b/dspace-api/src/test/java/org/dspace/builder/AbstractDSpaceObjectBuilder.java index e7ebd8768e7d..fa7306ad9955 100644 --- a/dspace-api/src/test/java/org/dspace/builder/AbstractDSpaceObjectBuilder.java +++ b/dspace-api/src/test/java/org/dspace/builder/AbstractDSpaceObjectBuilder.java @@ -18,7 +18,6 @@ import org.dspace.authorize.AuthorizeException; import org.dspace.authorize.ResourcePolicy; import org.dspace.content.DSpaceObject; -import org.dspace.content.Item; import org.dspace.content.service.DSpaceObjectService; import org.dspace.core.Constants; import org.dspace.core.Context; @@ -103,7 +102,7 @@ protected > B setMetadataSingleValue(fi final String qualifier, final String value) { try { - getService().setMetadataSingleValue(context, dso, schema, element, qualifier, Item.ANY, value); + getService().setMetadataSingleValue(context, dso, schema, element, qualifier, null, value); } catch (Exception e) { return handleException(e); } diff --git a/dspace-api/src/test/java/org/dspace/content/ItemComparatorTest.java b/dspace-api/src/test/java/org/dspace/content/ItemComparatorTest.java index 54ff9ce02624..be670d9b5097 100644 --- a/dspace-api/src/test/java/org/dspace/content/ItemComparatorTest.java +++ b/dspace-api/src/test/java/org/dspace/content/ItemComparatorTest.java @@ -141,37 +141,37 @@ public void testCompare() throws SQLException { assertTrue("testCompare 0", result == 0); ic = new ItemComparator("test", "one", Item.ANY, true); - itemService.addMetadata(context, one, "dc", "test", "one", Item.ANY, "1"); + itemService.addMetadata(context, one, "dc", "test", "one", null, "1"); result = ic.compare(one, two); assertTrue("testCompare 1", result >= 1); itemService.clearMetadata(context, one, "dc", "test", "one", Item.ANY); ic = new ItemComparator("test", "one", Item.ANY, true); - itemService.addMetadata(context, two, "dc", "test", "one", Item.ANY, "1"); + itemService.addMetadata(context, two, "dc", "test", "one", null, "1"); result = ic.compare(one, two); assertTrue("testCompare 2", result <= -1); itemService.clearMetadata(context, two, "dc", "test", "one", Item.ANY); //value in both items ic = new ItemComparator("test", "one", Item.ANY, true); - itemService.addMetadata(context, one, "dc", "test", "one", Item.ANY, "1"); - itemService.addMetadata(context, two, "dc", "test", "one", Item.ANY, "2"); + itemService.addMetadata(context, one, "dc", "test", "one", null, "1"); + itemService.addMetadata(context, two, "dc", "test", "one", null, "2"); result = ic.compare(one, two); assertTrue("testCompare 3", result <= -1); itemService.clearMetadata(context, one, "dc", "test", "one", Item.ANY); itemService.clearMetadata(context, two, "dc", "test", "one", Item.ANY); ic = new ItemComparator("test", "one", Item.ANY, true); - itemService.addMetadata(context, one, "dc", "test", "one", Item.ANY, "1"); - itemService.addMetadata(context, two, "dc", "test", "one", Item.ANY, "1"); + itemService.addMetadata(context, one, "dc", "test", "one", null, "1"); + itemService.addMetadata(context, two, "dc", "test", "one", null, "1"); result = ic.compare(one, two); assertTrue("testCompare 4", result == 0); itemService.clearMetadata(context, one, "dc", "test", "one", Item.ANY); itemService.clearMetadata(context, two, "dc", "test", "one", Item.ANY); ic = new ItemComparator("test", "one", Item.ANY, true); - itemService.addMetadata(context, one, "dc", "test", "one", Item.ANY, "2"); - itemService.addMetadata(context, two, "dc", "test", "one", Item.ANY, "1"); + itemService.addMetadata(context, one, "dc", "test", "one", null, "2"); + itemService.addMetadata(context, two, "dc", "test", "one", null, "1"); result = ic.compare(one, two); assertTrue("testCompare 5", result >= 1); itemService.clearMetadata(context, one, "dc", "test", "one", Item.ANY); @@ -179,60 +179,60 @@ public void testCompare() throws SQLException { //multiple values (min, max) ic = new ItemComparator("test", "one", Item.ANY, true); - itemService.addMetadata(context, one, "dc", "test", "one", Item.ANY, "0"); - itemService.addMetadata(context, one, "dc", "test", "one", Item.ANY, "1"); - itemService.addMetadata(context, two, "dc", "test", "one", Item.ANY, "2"); - itemService.addMetadata(context, two, "dc", "test", "one", Item.ANY, "3"); + itemService.addMetadata(context, one, "dc", "test", "one", null, "0"); + itemService.addMetadata(context, one, "dc", "test", "one", null, "1"); + itemService.addMetadata(context, two, "dc", "test", "one", null, "2"); + itemService.addMetadata(context, two, "dc", "test", "one", null, "3"); result = ic.compare(one, two); assertTrue("testCompare 3", result <= -1); itemService.clearMetadata(context, one, "dc", "test", "one", Item.ANY); itemService.clearMetadata(context, two, "dc", "test", "one", Item.ANY); ic = new ItemComparator("test", "one", Item.ANY, true); - itemService.addMetadata(context, one, "dc", "test", "one", Item.ANY, "0"); - itemService.addMetadata(context, one, "dc", "test", "one", Item.ANY, "1"); - itemService.addMetadata(context, two, "dc", "test", "one", Item.ANY, "-1"); - itemService.addMetadata(context, two, "dc", "test", "one", Item.ANY, "1"); + itemService.addMetadata(context, one, "dc", "test", "one", null, "0"); + itemService.addMetadata(context, one, "dc", "test", "one", null, "1"); + itemService.addMetadata(context, two, "dc", "test", "one", null, "-1"); + itemService.addMetadata(context, two, "dc", "test", "one", null, "1"); result = ic.compare(one, two); assertTrue("testCompare 4", result == 0); itemService.clearMetadata(context, one, "dc", "test", "one", Item.ANY); itemService.clearMetadata(context, two, "dc", "test", "one", Item.ANY); ic = new ItemComparator("test", "one", Item.ANY, true); - itemService.addMetadata(context, one, "dc", "test", "one", Item.ANY, "1"); - itemService.addMetadata(context, one, "dc", "test", "one", Item.ANY, "2"); - itemService.addMetadata(context, two, "dc", "test", "one", Item.ANY, "1"); - itemService.addMetadata(context, two, "dc", "test", "one", Item.ANY, "-1"); + itemService.addMetadata(context, one, "dc", "test", "one", null, "1"); + itemService.addMetadata(context, one, "dc", "test", "one", null, "2"); + itemService.addMetadata(context, two, "dc", "test", "one", null, "1"); + itemService.addMetadata(context, two, "dc", "test", "one", null, "-1"); result = ic.compare(one, two); assertTrue("testCompare 5", result >= 1); itemService.clearMetadata(context, one, "dc", "test", "one", Item.ANY); itemService.clearMetadata(context, two, "dc", "test", "one", Item.ANY); ic = new ItemComparator("test", "one", Item.ANY, false); - itemService.addMetadata(context, one, "dc", "test", "one", Item.ANY, "1"); - itemService.addMetadata(context, one, "dc", "test", "one", Item.ANY, "2"); - itemService.addMetadata(context, two, "dc", "test", "one", Item.ANY, "2"); - itemService.addMetadata(context, two, "dc", "test", "one", Item.ANY, "3"); + itemService.addMetadata(context, one, "dc", "test", "one", null, "1"); + itemService.addMetadata(context, one, "dc", "test", "one", null, "2"); + itemService.addMetadata(context, two, "dc", "test", "one", null, "2"); + itemService.addMetadata(context, two, "dc", "test", "one", null, "3"); result = ic.compare(one, two); assertTrue("testCompare 3", result <= -1); itemService.clearMetadata(context, one, "dc", "test", "one", Item.ANY); itemService.clearMetadata(context, two, "dc", "test", "one", Item.ANY); ic = new ItemComparator("test", "one", Item.ANY, false); - itemService.addMetadata(context, one, "dc", "test", "one", Item.ANY, "1"); - itemService.addMetadata(context, one, "dc", "test", "one", Item.ANY, "2"); - itemService.addMetadata(context, two, "dc", "test", "one", Item.ANY, "1"); - itemService.addMetadata(context, two, "dc", "test", "one", Item.ANY, "5"); + itemService.addMetadata(context, one, "dc", "test", "one", null, "1"); + itemService.addMetadata(context, one, "dc", "test", "one", null, "2"); + itemService.addMetadata(context, two, "dc", "test", "one", null, "1"); + itemService.addMetadata(context, two, "dc", "test", "one", null, "5"); result = ic.compare(one, two); assertTrue("testCompare 4", result == 0); itemService.clearMetadata(context, one, "dc", "test", "one", Item.ANY); itemService.clearMetadata(context, two, "dc", "test", "one", Item.ANY); ic = new ItemComparator("test", "one", Item.ANY, false); - itemService.addMetadata(context, one, "dc", "test", "one", Item.ANY, "2"); - itemService.addMetadata(context, one, "dc", "test", "one", Item.ANY, "3"); - itemService.addMetadata(context, two, "dc", "test", "one", Item.ANY, "1"); - itemService.addMetadata(context, two, "dc", "test", "one", Item.ANY, "4"); + itemService.addMetadata(context, one, "dc", "test", "one", null, "2"); + itemService.addMetadata(context, one, "dc", "test", "one", null, "3"); + itemService.addMetadata(context, two, "dc", "test", "one", null, "1"); + itemService.addMetadata(context, two, "dc", "test", "one", null, "4"); result = ic.compare(one, two); assertTrue("testCompare 5", result >= 1); itemService.clearMetadata(context, one, "dc", "test", "one", Item.ANY); diff --git a/dspace-api/src/test/java/org/dspace/content/ItemTest.java b/dspace-api/src/test/java/org/dspace/content/ItemTest.java index aaa28769dca6..0ba1136d8f0a 100644 --- a/dspace-api/src/test/java/org/dspace/content/ItemTest.java +++ b/dspace-api/src/test/java/org/dspace/content/ItemTest.java @@ -518,11 +518,11 @@ public void testAddMetadata_5args_1() throws SQLException { String schema = "dc"; String element = "contributor"; String qualifier = "author"; - String lang = Item.ANY; + String lang = null; String[] values = {"value0", "value1"}; itemService.addMetadata(context, it, schema, element, qualifier, lang, Arrays.asList(values)); - List dc = itemService.getMetadata(it, schema, element, qualifier, lang); + List dc = itemService.getMetadata(it, schema, element, qualifier, Item.ANY); assertThat("testAddMetadata_5args_1 0", dc, notNullValue()); assertTrue("testAddMetadata_5args_1 1", dc.size() == 2); assertThat("testAddMetadata_5args_1 2", dc.get(0).getMetadataField().getMetadataSchema().getName(), @@ -563,13 +563,13 @@ public void testAddMetadata_7args_1_authority() String schema = "dc"; String element = "language"; String qualifier = "iso"; - String lang = Item.ANY; + String lang = null; List values = Arrays.asList("en_US", "en"); List authorities = Arrays.asList("accepted", "uncertain"); List confidences = Arrays.asList(0, 0); itemService.addMetadata(context, it, schema, element, qualifier, lang, values, authorities, confidences); - List dc = itemService.getMetadata(it, schema, element, qualifier, lang); + List dc = itemService.getMetadata(it, schema, element, qualifier, Item.ANY); assertThat("testAddMetadata_7args_1 0", dc, notNullValue()); assertTrue("testAddMetadata_7args_1 1", dc.size() == 2); assertThat("testAddMetadata_7args_1 2", dc.get(0).getMetadataField().getMetadataSchema().getName(), @@ -600,13 +600,13 @@ public void testAddMetadata_7args_1_noauthority() throws SQLException { String schema = "dc"; String element = "contributor"; String qualifier = "author"; - String lang = Item.ANY; + String lang = null; List values = Arrays.asList("value0", "value1"); List authorities = Arrays.asList("auth0", "auth2"); List confidences = Arrays.asList(0, 0); itemService.addMetadata(context, it, schema, element, qualifier, lang, values, authorities, confidences); - List dc = itemService.getMetadata(it, schema, element, qualifier, lang); + List dc = itemService.getMetadata(it, schema, element, qualifier, Item.ANY); assertThat("testAddMetadata_7args_1 0", dc, notNullValue()); assertTrue("testAddMetadata_7args_1 1", dc.size() == 2); assertThat("testAddMetadata_7args_1 2", dc.get(0).getMetadataField().getMetadataSchema().getName(), @@ -719,13 +719,13 @@ public void testAddMetadata_7args_2_authority() throws SQLException { String schema = "dc"; String element = "language"; String qualifier = "iso"; - String lang = Item.ANY; + String lang = null; String values = "en"; String authorities = "accepted"; int confidences = 0; itemService.addMetadata(context, it, schema, element, qualifier, lang, values, authorities, confidences); - List dc = itemService.getMetadata(it, schema, element, qualifier, lang); + List dc = itemService.getMetadata(it, schema, element, qualifier, Item.ANY); assertThat("testAddMetadata_7args_2 0", dc, notNullValue()); assertTrue("testAddMetadata_7args_2 1", dc.size() == 1); assertThat("testAddMetadata_7args_2 2", dc.get(0).getMetadataField().getMetadataSchema().getName(), @@ -748,13 +748,13 @@ public void testAddMetadata_7args_2_noauthority() throws SQLException { String schema = "dc"; String element = "contributor"; String qualifier = "editor"; - String lang = Item.ANY; + String lang = null; String values = "value0"; String authorities = "auth0"; int confidences = 0; itemService.addMetadata(context, it, schema, element, qualifier, lang, values, authorities, confidences); - List dc = itemService.getMetadata(it, schema, element, qualifier, lang); + List dc = itemService.getMetadata(it, schema, element, qualifier, Item.ANY); assertThat("testAddMetadata_7args_2 0", dc, notNullValue()); assertTrue("testAddMetadata_7args_2 1", dc.size() == 1); assertThat("testAddMetadata_7args_2 2", dc.get(0).getMetadataField().getMetadataSchema().getName(), @@ -811,13 +811,13 @@ public void testClearMetadata() throws SQLException { String schema = "dc"; String element = "contributor"; String qualifier = "author"; - String lang = Item.ANY; + String lang = null; String values = "value0"; itemService.addMetadata(context, it, schema, element, qualifier, lang, values); - itemService.clearMetadata(context, it, schema, element, qualifier, lang); + itemService.clearMetadata(context, it, schema, element, qualifier, Item.ANY); - List dc = itemService.getMetadata(it, schema, element, qualifier, lang); + List dc = itemService.getMetadata(it, schema, element, qualifier, Item.ANY); assertThat("testClearMetadata 0", dc, notNullValue()); assertTrue("testClearMetadata 1", dc.size() == 0); } @@ -859,11 +859,11 @@ public void testGetCollections() throws Exception { context.turnOffAuthorisationSystem(); Collection collection = collectionService.create(context, owningCommunity); collectionService.setMetadataSingleValue(context, collection, MetadataSchemaEnum.DC.getName(), - "title", null, Item.ANY, "collection B"); + "title", null, null, "collection B"); it.addCollection(collection); collection = collectionService.create(context, owningCommunity); collectionService.setMetadataSingleValue(context, collection, MetadataSchemaEnum.DC.getName(), - "title", null, Item.ANY, "collection A"); + "title", null, null, "collection A"); it.addCollection(collection); context.restoreAuthSystemState(); assertThat("testGetCollections 0", it.getCollections(), notNullValue()); @@ -1772,7 +1772,7 @@ public void testFindByMetadataField() throws Exception { // add new metadata to item context.turnOffAuthorisationSystem(); - itemService.addMetadata(context, it, schema, element, qualifier, Item.ANY, value); + itemService.addMetadata(context, it, schema, element, qualifier, null, value); itemService.update(context, it); context.restoreAuthSystemState(); @@ -1837,7 +1837,7 @@ public void testFindByAuthorityValue() throws Exception { // add new metadata (with authority) to item context.turnOffAuthorisationSystem(); - itemService.addMetadata(context, it, schema, element, qualifier, Item.ANY, value, authority, confidence); + itemService.addMetadata(context, it, schema, element, qualifier, null, value, authority, confidence); itemService.update(context, it); context.restoreAuthSystemState(); diff --git a/dspace-api/src/test/java/org/dspace/content/dao/RelationshipDAOImplIT.java b/dspace-api/src/test/java/org/dspace/content/dao/RelationshipDAOImplIT.java index 2d08223b2e3e..20710ab5f25b 100644 --- a/dspace-api/src/test/java/org/dspace/content/dao/RelationshipDAOImplIT.java +++ b/dspace-api/src/test/java/org/dspace/content/dao/RelationshipDAOImplIT.java @@ -87,8 +87,8 @@ public void init() { WorkspaceItem workspaceItemTwo = workspaceItemService.create(context, collection, false); itemOne = installItemService.installItem(context, workspaceItem); itemTwo = installItemService.installItem(context, workspaceItemTwo); - itemService.addMetadata(context, itemOne, "dspace", "entity", "type", Item.ANY, "Publication"); - itemService.addMetadata(context, itemTwo, "dspace", "entity", "type", Item.ANY, "Person"); + itemService.addMetadata(context, itemOne, "dspace", "entity", "type", null, "Publication"); + itemService.addMetadata(context, itemTwo, "dspace", "entity", "type", null, "Person"); itemService.update(context, itemOne); itemService.update(context, itemTwo); entityTypeOne = entityTypeService.create(context, "Person"); diff --git a/dspace-api/src/test/java/org/dspace/content/dao/RelationshipTypeDAOImplIT.java b/dspace-api/src/test/java/org/dspace/content/dao/RelationshipTypeDAOImplIT.java index ff7d03b49f6d..d76e5faa804a 100644 --- a/dspace-api/src/test/java/org/dspace/content/dao/RelationshipTypeDAOImplIT.java +++ b/dspace-api/src/test/java/org/dspace/content/dao/RelationshipTypeDAOImplIT.java @@ -82,8 +82,8 @@ public void init() { WorkspaceItem workspaceItemTwo = workspaceItemService.create(context, collection, false); itemOne = installItemService.installItem(context, workspaceItem); itemTwo = installItemService.installItem(context, workspaceItemTwo); - itemService.addMetadata(context, itemOne, "dspace", "entity", "type", Item.ANY, "Publication"); - itemService.addMetadata(context, itemTwo, "dspace", "entity", "type", Item.ANY, "Person"); + itemService.addMetadata(context, itemOne, "dspace", "entity", "type", null, "Publication"); + itemService.addMetadata(context, itemTwo, "dspace", "entity", "type", null, "Person"); itemService.update(context, itemOne); itemService.update(context, itemTwo); entityTypeOne = entityTypeService.create(context, "Person"); diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/PatchMetadataIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/PatchMetadataIT.java index eec7ce95f8d4..9b9db41a7c92 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/PatchMetadataIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/PatchMetadataIT.java @@ -261,7 +261,7 @@ private void initSimplePublicationItem() throws Exception { for (String author : authorsOriginalOrder) { itemService.addMetadata( - context, publicationItem, "dc", "contributor", "author", Item.ANY, author + context, publicationItem, "dc", "contributor", "author", null, author ); } diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/RelationshipRestRepositoryIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/RelationshipRestRepositoryIT.java index 641f30149b73..1bf4e2f41202 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/RelationshipRestRepositoryIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/RelationshipRestRepositoryIT.java @@ -818,7 +818,7 @@ public void addRelationshipsAndMetadataToValidatePlaceTest() throws Exception { // Make sure we grab the latest instance of the Item from the database publication1 = itemService.find(context, publication1.getID()); // Add a plain text dc.contributor.author value - itemService.addMetadata(context, publication1, "dc", "contributor", "author", Item.ANY, "plain text"); + itemService.addMetadata(context, publication1, "dc", "contributor", "author", null, "plain text"); itemService.update(context, publication1); List list = itemService.getMetadata(publication1, "dc", "contributor", "author", Item.ANY); @@ -884,7 +884,7 @@ public void addRelationshipsAndMetadataToValidatePlaceTest() throws Exception { // Ensure we have the latest instance of the Item from the database publication1 = itemService.find(context, publication1.getID()); // Add a fourth dc.contributor.author mdv - itemService.addMetadata(context, publication1, "dc", "contributor", "author", Item.ANY, "plain text two"); + itemService.addMetadata(context, publication1, "dc", "contributor", "author", null, "plain text two"); itemService.update(context, publication1); context.restoreAuthSystemState(); @@ -953,7 +953,7 @@ public void addRelationshipsAndMetadataToValidatePlaceTest() throws Exception { context.turnOffAuthorisationSystem(); // The following additions of Metadata will perform the same sequence of logic and tests as described above publication1 = itemService.find(context, publication1.getID()); - itemService.addMetadata(context, publication1, "dc", "contributor", "author", Item.ANY, "plain text three"); + itemService.addMetadata(context, publication1, "dc", "contributor", "author", null, "plain text three"); itemService.update(context, publication1); context.restoreAuthSystemState(); @@ -983,10 +983,10 @@ public void addRelationshipsAndMetadataToValidatePlaceTest() throws Exception { context.turnOffAuthorisationSystem(); publication1 = itemService.find(context, publication1.getID()); - itemService.addMetadata(context, publication1, "dc", "contributor", "author", Item.ANY, "plain text four"); - itemService.addMetadata(context, publication1, "dc", "contributor", "author", Item.ANY, "plain text five"); - itemService.addMetadata(context, publication1, "dc", "contributor", "author", Item.ANY, "plain text six"); - itemService.addMetadata(context, publication1, "dc", "contributor", "author", Item.ANY, "plain text seven"); + itemService.addMetadata(context, publication1, "dc", "contributor", "author", null, "plain text four"); + itemService.addMetadata(context, publication1, "dc", "contributor", "author", null, "plain text five"); + itemService.addMetadata(context, publication1, "dc", "contributor", "author", null, "plain text six"); + itemService.addMetadata(context, publication1, "dc", "contributor", "author", null, "plain text seven"); itemService.update(context, publication1); context.restoreAuthSystemState(); @@ -1114,7 +1114,7 @@ public void deleteMetadataValueAndValidatePlace() throws Exception { publication1 = itemService.find(context, publication1.getID()); // Add a plain text metadatavalue to the publication // After this addition, the list of authors should like like "Donald Smith", "plain text" - itemService.addMetadata(context, publication1, "dc", "contributor", "author", Item.ANY, "plain text"); + itemService.addMetadata(context, publication1, "dc", "contributor", "author", null, "plain text"); itemService.update(context, publication1); context.restoreAuthSystemState(); List list = itemService.getMetadata(publication1, "dc", "contributor", "author", Item.ANY); @@ -1157,7 +1157,7 @@ public void deleteMetadataValueAndValidatePlace() throws Exception { // Get the publication from the DB again to ensure that we have the latest object publication1 = itemService.find(context, publication1.getID()); // Add a fourth metadata value to the publication - itemService.addMetadata(context, publication1, "dc", "contributor", "author", Item.ANY, "plain text two"); + itemService.addMetadata(context, publication1, "dc", "contributor", "author", null, "plain text two"); itemService.update(context, publication1); context.restoreAuthSystemState(); list = itemService.getMetadata(publication1, "dc", "contributor", "author", Item.ANY); @@ -1195,7 +1195,7 @@ public void deleteMetadataValueAndValidatePlace() throws Exception { context.turnOffAuthorisationSystem(); publication1 = itemService.find(context, publication1.getID()); // Create another plain text metadata value on the publication - itemService.addMetadata(context, publication1, "dc", "contributor", "author", Item.ANY, "plain text three"); + itemService.addMetadata(context, publication1, "dc", "contributor", "author", null, "plain text three"); itemService.update(context, publication1); context.restoreAuthSystemState(); list = itemService.getMetadata(publication1, "dc", "contributor", "author", Item.ANY); @@ -1323,7 +1323,7 @@ public void deleteRelationshipsAndValidatePlace() throws Exception { publication1 = itemService.find(context, publication1.getID()); // Add a plain text metadatavalue to the publication // After this addition, the list of authors should like like "Donald Smith", "plain text" - itemService.addMetadata(context, publication1, "dc", "contributor", "author", Item.ANY, "plain text"); + itemService.addMetadata(context, publication1, "dc", "contributor", "author", null, "plain text"); itemService.update(context, publication1); context.restoreAuthSystemState(); List list = itemService.getMetadata(publication1, "dc", "contributor", "author", Item.ANY); @@ -1367,7 +1367,7 @@ public void deleteRelationshipsAndValidatePlace() throws Exception { // Get the publication from the DB again to ensure that we have the latest object publication1 = itemService.find(context, publication1.getID()); // Add a fourth metadata value to the publication - itemService.addMetadata(context, publication1, "dc", "contributor", "author", Item.ANY, "plain text two"); + itemService.addMetadata(context, publication1, "dc", "contributor", "author", null, "plain text two"); itemService.update(context, publication1); context.restoreAuthSystemState(); list = itemService.getMetadata(publication1, "dc", "contributor", "author", Item.ANY); @@ -1405,7 +1405,7 @@ public void deleteRelationshipsAndValidatePlace() throws Exception { context.turnOffAuthorisationSystem(); publication1 = itemService.find(context, publication1.getID()); // Create another plain text metadata value on the publication - itemService.addMetadata(context, publication1, "dc", "contributor", "author", Item.ANY, "plain text three"); + itemService.addMetadata(context, publication1, "dc", "contributor", "author", null, "plain text three"); itemService.update(context, publication1); context.restoreAuthSystemState(); list = itemService.getMetadata(publication1, "dc", "contributor", "author", Item.ANY); From c7696649af8da714e7469c843fb9a2afb14cf063 Mon Sep 17 00:00:00 2001 From: Jens Vannerum Date: Tue, 23 Jul 2024 12:16:28 +0200 Subject: [PATCH 024/979] 116542: resolve issues after merge with latest main branch (cherry picked from commit be179bad6ab25a96d4b43d3ef73a7c065557e266) --- .../test/java/org/dspace/content/ItemTest.java | 18 +++++++++--------- .../dspace/content/service/ItemServiceIT.java | 2 +- .../app/rest/EPersonRestRepositoryIT.java | 3 +-- .../controller/LinksetRestControllerIT.java | 4 ++-- 4 files changed, 13 insertions(+), 14 deletions(-) diff --git a/dspace-api/src/test/java/org/dspace/content/ItemTest.java b/dspace-api/src/test/java/org/dspace/content/ItemTest.java index 0ba1136d8f0a..00dbf2994d98 100644 --- a/dspace-api/src/test/java/org/dspace/content/ItemTest.java +++ b/dspace-api/src/test/java/org/dspace/content/ItemTest.java @@ -544,7 +544,7 @@ public void testAddMetadata_5args_no_values() throws Exception { String schema = "dc"; String element = "contributor"; String qualifier = "author"; - String lang = Item.ANY; + String lang = null; String[] values = {}; itemService.addMetadata(context, it, schema, element, qualifier, lang, Arrays.asList(values)); fail("IllegalArgumentException expected"); @@ -632,7 +632,7 @@ public void testAddMetadata_7args_no_values() throws Exception { String schema = "dc"; String element = "contributor"; String qualifier = "author"; - String lang = Item.ANY; + String lang = null; List values = new ArrayList(); List authorities = new ArrayList(); List confidences = new ArrayList(); @@ -645,7 +645,7 @@ public void testAddMetadata_list_with_virtual_metadata() throws Exception { String schema = "dc"; String element = "contributor"; String qualifier = "author"; - String lang = Item.ANY; + String lang = null; // Create two fake virtual metadata ("virtual::[relationship-id]") values List values = new ArrayList<>(Arrays.asList("uuid-1", "uuid-2")); List authorities = new ArrayList<>(Arrays.asList(Constants.VIRTUAL_AUTHORITY_PREFIX + "relationship-1", @@ -674,7 +674,7 @@ public void testAddMetadata_list_with_virtual_metadata() throws Exception { assertEquals(1, valuesAdded.size()); // Get metadata and ensure new value is the ONLY ONE for this metadata field - List dc = itemService.getMetadata(it, schema, element, qualifier, lang); + List dc = itemService.getMetadata(it, schema, element, qualifier, Item.ANY); assertNotNull(dc); assertEquals(1, dc.size()); assertEquals(schema, dc.get(0).getMetadataField().getMetadataSchema().getName()); @@ -693,11 +693,11 @@ public void testAddMetadata_5args_2() throws SQLException { String schema = "dc"; String element = "contributor"; String qualifier = "author"; - String lang = Item.ANY; + String lang = null; String value = "value0"; itemService.addMetadata(context, it, schema, element, qualifier, lang, value); - List dc = itemService.getMetadata(it, schema, element, qualifier, lang); + List dc = itemService.getMetadata(it, schema, element, qualifier, Item.ANY); assertThat("testAddMetadata_5args_2 0", dc, notNullValue()); assertTrue("testAddMetadata_5args_2 1", dc.size() == 1); assertThat("testAddMetadata_5args_2 2", dc.get(0).getMetadataField().getMetadataSchema().getName(), @@ -772,7 +772,7 @@ public void testAddMetadata_single_virtual_metadata() throws Exception { String schema = "dc"; String element = "contributor"; String qualifier = "author"; - String lang = Item.ANY; + String lang = null; // Create a single fake virtual metadata ("virtual::[relationship-id]") value String value = "uuid-1"; String authority = Constants.VIRTUAL_AUTHORITY_PREFIX + "relationship-1"; @@ -786,7 +786,7 @@ public void testAddMetadata_single_virtual_metadata() throws Exception { assertNull(valuesAdded); // Verify this metadata field does NOT exist on the item - List mv = itemService.getMetadata(it, schema, element, qualifier, lang); + List mv = itemService.getMetadata(it, schema, element, qualifier, Item.ANY); assertNotNull(mv); assertTrue(mv.isEmpty()); @@ -797,7 +797,7 @@ public void testAddMetadata_single_virtual_metadata() throws Exception { assertNull(valuesAdded); // Verify this metadata field does NOT exist on the item - mv = itemService.getMetadata(it, schema, element, qualifier, lang); + mv = itemService.getMetadata(it, schema, element, qualifier, Item.ANY); assertNotNull(mv); assertTrue(mv.isEmpty()); } diff --git a/dspace-api/src/test/java/org/dspace/content/service/ItemServiceIT.java b/dspace-api/src/test/java/org/dspace/content/service/ItemServiceIT.java index 0704c2d98d1c..eee445b3334f 100644 --- a/dspace-api/src/test/java/org/dspace/content/service/ItemServiceIT.java +++ b/dspace-api/src/test/java/org/dspace/content/service/ItemServiceIT.java @@ -198,7 +198,7 @@ public void preserveMetadataOrder() throws Exception { // now just add one metadata to be the last itemService.addMetadata( - context, item, dcSchema, contributorElement, authorQualifier, Item.ANY, "test, latest", null, 0 + context, item, dcSchema, contributorElement, authorQualifier, null, "test, latest", null, 0 ); // now just remove first metadata itemService.removeMetadataValues(context, item, List.of(placeZero)); diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/EPersonRestRepositoryIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/EPersonRestRepositoryIT.java index dfca8b4328f8..beeef03aec47 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/EPersonRestRepositoryIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/EPersonRestRepositoryIT.java @@ -72,7 +72,6 @@ import org.dspace.builder.WorkflowItemBuilder; import org.dspace.content.Collection; import org.dspace.content.Community; -import org.dspace.content.Item; import org.dspace.core.I18nUtil; import org.dspace.eperson.EPerson; import org.dspace.eperson.Group; @@ -2120,7 +2119,7 @@ public void patchMultipleReplaceMetadataByAdmin() throws Exception { .build(); this.ePersonService - .addMetadata(context, ePerson, "eperson", "firstname", null, Item.ANY, List.of(first, second, third)); + .addMetadata(context, ePerson, "eperson", "firstname", null, null, List.of(first, second, third)); context.restoreAuthSystemState(); diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/signposting/controller/LinksetRestControllerIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/signposting/controller/LinksetRestControllerIT.java index c5873dd53f42..cf62d5ac0861 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/signposting/controller/LinksetRestControllerIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/signposting/controller/LinksetRestControllerIT.java @@ -748,7 +748,7 @@ public void findTypedLinkForBitstreamWithType() throws Exception { .withMimeType(bitstreamMimeType) .build(); } - bitstreamService.addMetadata(context, bitstream, "dc", "type", null, Item.ANY, "Article"); + bitstreamService.addMetadata(context, bitstream, "dc", "type", null, null, "Article"); context.restoreAuthSystemState(); @@ -836,7 +836,7 @@ public void findTypedLinkForBitstreamOfWorkspaceItem() throws Exception { .withTitle("Workspace Item") .build(); Item item = workspaceItem.getItem(); - itemService.addMetadata(context, item, "dc", "identifier", "doi", Item.ANY, doi); + itemService.addMetadata(context, item, "dc", "identifier", "doi", null, doi); Bitstream bitstream = null; try (InputStream is = IOUtils.toInputStream(bitstreamContent, CharEncoding.UTF_8)) { From 24318e01b84ec7ef3c2b7081bc5a85ab10295209 Mon Sep 17 00:00:00 2001 From: Sascha Szott Date: Fri, 2 Aug 2024 14:12:53 +0200 Subject: [PATCH 025/979] fix invalid usage of == operator (cherry picked from commit fa0fb14a185ba8a9c593a3653df302f0446a397c) --- .../src/main/java/org/dspace/content/ItemServiceImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 ac6c0d43d710..b2cc3e939d87 100644 --- a/dspace-api/src/main/java/org/dspace/content/ItemServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/content/ItemServiceImpl.java @@ -1788,7 +1788,7 @@ protected void moveSingleMetadataValue(Context context, Item dso, int place, Met //Retrieve the applicable relationship Relationship rs = relationshipService.find(context, ((RelationshipMetadataValue) rr).getRelationshipId()); - if (rs.getLeftItem() == dso) { + if (rs.getLeftItem().equals(dso)) { rs.setLeftPlace(place); } else { rs.setRightPlace(place); From 74d032732298cb372f6ad22cf26ac88488e49a74 Mon Sep 17 00:00:00 2001 From: Sascha Szott Date: Fri, 2 Aug 2024 14:12:53 +0200 Subject: [PATCH 026/979] fix invalid usage of == operator (cherry picked from commit fa0fb14a185ba8a9c593a3653df302f0446a397c) --- .../src/main/java/org/dspace/content/ItemServiceImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 70bdf4b7d950..cceb954ebe2f 100644 --- a/dspace-api/src/main/java/org/dspace/content/ItemServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/content/ItemServiceImpl.java @@ -1799,7 +1799,7 @@ protected void moveSingleMetadataValue(Context context, Item dso, int place, Met //Retrieve the applicable relationship Relationship rs = relationshipService.find(context, ((RelationshipMetadataValue) rr).getRelationshipId()); - if (rs.getLeftItem() == dso) { + if (rs.getLeftItem().equals(dso)) { rs.setLeftPlace(place); } else { rs.setRightPlace(place); From b8f638b3b6eff9c0138dcfe883a9083599a2ffa7 Mon Sep 17 00:00:00 2001 From: Sascha Szott Date: Fri, 2 Aug 2024 17:44:35 +0200 Subject: [PATCH 027/979] use equals instead of == (cherry picked from commit 80de8f6fb567ceb4497596fa714f2b357f1b8b26) --- .../src/main/java/org/dspace/content/EntityServiceImpl.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/content/EntityServiceImpl.java b/dspace-api/src/main/java/org/dspace/content/EntityServiceImpl.java index 2f34129f2e69..6049b7e6aae8 100644 --- a/dspace-api/src/main/java/org/dspace/content/EntityServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/content/EntityServiceImpl.java @@ -64,7 +64,7 @@ public List getLeftRelations(Context context, Entity entity) { List fullList = entity.getRelationships(); List listToReturn = new LinkedList<>(); for (Relationship relationship : fullList) { - if (relationship.getLeftItem().getID() == entity.getItem().getID()) { + if (relationship.getLeftItem().getID().equals(entity.getItem().getID())) { listToReturn.add(relationship); } } @@ -76,7 +76,7 @@ public List getRightRelations(Context context, Entity entity) { List fullList = entity.getRelationships(); List listToReturn = new LinkedList<>(); for (Relationship relationship : fullList) { - if (relationship.getRightItem().getID() == entity.getItem().getID()) { + if (relationship.getRightItem().getID().equals(entity.getItem().getID())) { listToReturn.add(relationship); } } From dc09d06ed6eedc89249fad5230f5346a830bbe41 Mon Sep 17 00:00:00 2001 From: Sascha Szott Date: Fri, 2 Aug 2024 17:47:07 +0200 Subject: [PATCH 028/979] use equals instead of == (cherry picked from commit 5e3552ee3885049df34c3fcaf49bfe3028c5dbd0) --- .../java/org/dspace/authorize/ResourcePolicyServiceImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dspace-api/src/main/java/org/dspace/authorize/ResourcePolicyServiceImpl.java b/dspace-api/src/main/java/org/dspace/authorize/ResourcePolicyServiceImpl.java index 7b93b912378e..86998a2196e7 100644 --- a/dspace-api/src/main/java/org/dspace/authorize/ResourcePolicyServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/authorize/ResourcePolicyServiceImpl.java @@ -417,7 +417,7 @@ public boolean isMyResourcePolicy(Context context, EPerson eperson, Integer id) ResourcePolicy resourcePolicy = resourcePolicyDAO.findOneById(context, id); Group group = resourcePolicy.getGroup(); - if (resourcePolicy.getEPerson() != null && resourcePolicy.getEPerson().getID() == eperson.getID()) { + if (resourcePolicy.getEPerson() != null && resourcePolicy.getEPerson().getID().equals(eperson.getID())) { isMy = true; } else if (group != null && groupService.isMember(context, eperson, group)) { isMy = true; From 6a5236f2f93a05999bfa391887ecb22f8bfae257 Mon Sep 17 00:00:00 2001 From: Sascha Szott Date: Fri, 2 Aug 2024 17:48:28 +0200 Subject: [PATCH 029/979] use equals instead of == (cherry picked from commit d2ef7b01ef1a5d769d764b708be393dbb481fb65) --- .../src/main/java/org/dspace/eperson/GroupServiceImpl.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/eperson/GroupServiceImpl.java b/dspace-api/src/main/java/org/dspace/eperson/GroupServiceImpl.java index 730053e42ce2..3fb20e2f1e6f 100644 --- a/dspace-api/src/main/java/org/dspace/eperson/GroupServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/eperson/GroupServiceImpl.java @@ -147,7 +147,7 @@ public void addMember(Context context, Group group, EPerson e) { public void addMember(Context context, Group groupParent, Group groupChild) throws SQLException { // don't add if it's already a member // and don't add itself - if (groupParent.contains(groupChild) || groupParent.getID() == groupChild.getID()) { + if (groupParent.contains(groupChild) || groupParent.getID().equals(groupChild.getID())) { return; } @@ -178,7 +178,7 @@ public void removeMember(Context context, Group group, EPerson ePerson) throws S Role role = stepByName.getRole(); for (CollectionRole collectionRole : collectionRoles) { if (StringUtils.equals(collectionRole.getRoleId(), role.getId()) - && claimedTask.getWorkflowItem().getCollection() == collectionRole.getCollection()) { + && claimedTask.getWorkflowItem().getCollection().equals(collectionRole.getCollection())) { // Count number of EPersons who are *direct* members of this group int totalDirectEPersons = ePersonService.countByGroups(context, Set.of(group)); // Count number of Groups which have this groupParent as a direct parent From 36ff5ca6cbc6a8641555f9267e06884f9112badc Mon Sep 17 00:00:00 2001 From: Sascha Szott Date: Fri, 2 Aug 2024 17:49:14 +0200 Subject: [PATCH 030/979] use equals instead of == (cherry picked from commit a13cc82d405c5aefe00c7bb86d89c7dc8073a39b) --- .../submit/factory/impl/ItemMetadataValueAddPatchOperation.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/factory/impl/ItemMetadataValueAddPatchOperation.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/factory/impl/ItemMetadataValueAddPatchOperation.java index e749c4e79328..a7cd5bfc1de3 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/factory/impl/ItemMetadataValueAddPatchOperation.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/factory/impl/ItemMetadataValueAddPatchOperation.java @@ -213,7 +213,7 @@ private Integer getRelId(String authority) { private void updateRelationshipPlace(Context context, Item dso, int place, Relationship rs) { try { - if (rs.getLeftItem() == dso) { + if (rs.getLeftItem().equals(dso)) { rs.setLeftPlace(place); } else { rs.setRightPlace(place); From 19daa72ff40cd172721776f795ff86b028b38c04 Mon Sep 17 00:00:00 2001 From: Sascha Szott Date: Fri, 2 Aug 2024 17:44:35 +0200 Subject: [PATCH 031/979] use equals instead of == (cherry picked from commit 80de8f6fb567ceb4497596fa714f2b357f1b8b26) --- .../src/main/java/org/dspace/content/EntityServiceImpl.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/content/EntityServiceImpl.java b/dspace-api/src/main/java/org/dspace/content/EntityServiceImpl.java index 9b28203827e0..e83178667840 100644 --- a/dspace-api/src/main/java/org/dspace/content/EntityServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/content/EntityServiceImpl.java @@ -60,7 +60,7 @@ public List getLeftRelations(Context context, Entity entity) { List fullList = entity.getRelationships(); List listToReturn = new LinkedList<>(); for (Relationship relationship : fullList) { - if (relationship.getLeftItem().getID() == entity.getItem().getID()) { + if (relationship.getLeftItem().getID().equals(entity.getItem().getID())) { listToReturn.add(relationship); } } @@ -72,7 +72,7 @@ public List getRightRelations(Context context, Entity entity) { List fullList = entity.getRelationships(); List listToReturn = new LinkedList<>(); for (Relationship relationship : fullList) { - if (relationship.getRightItem().getID() == entity.getItem().getID()) { + if (relationship.getRightItem().getID().equals(entity.getItem().getID())) { listToReturn.add(relationship); } } From 7b08fdfe9e4b7c3c572fcfba3a14695f1c40164f Mon Sep 17 00:00:00 2001 From: Sascha Szott Date: Fri, 2 Aug 2024 17:47:07 +0200 Subject: [PATCH 032/979] use equals instead of == (cherry picked from commit 5e3552ee3885049df34c3fcaf49bfe3028c5dbd0) --- .../java/org/dspace/authorize/ResourcePolicyServiceImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dspace-api/src/main/java/org/dspace/authorize/ResourcePolicyServiceImpl.java b/dspace-api/src/main/java/org/dspace/authorize/ResourcePolicyServiceImpl.java index 7b93b912378e..86998a2196e7 100644 --- a/dspace-api/src/main/java/org/dspace/authorize/ResourcePolicyServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/authorize/ResourcePolicyServiceImpl.java @@ -417,7 +417,7 @@ public boolean isMyResourcePolicy(Context context, EPerson eperson, Integer id) ResourcePolicy resourcePolicy = resourcePolicyDAO.findOneById(context, id); Group group = resourcePolicy.getGroup(); - if (resourcePolicy.getEPerson() != null && resourcePolicy.getEPerson().getID() == eperson.getID()) { + if (resourcePolicy.getEPerson() != null && resourcePolicy.getEPerson().getID().equals(eperson.getID())) { isMy = true; } else if (group != null && groupService.isMember(context, eperson, group)) { isMy = true; From f0ea00b5cd4c4c9120eb1e744ae11d0ad0977ec0 Mon Sep 17 00:00:00 2001 From: Sascha Szott Date: Fri, 2 Aug 2024 17:48:28 +0200 Subject: [PATCH 033/979] use equals instead of == (cherry picked from commit d2ef7b01ef1a5d769d764b708be393dbb481fb65) --- .../src/main/java/org/dspace/eperson/GroupServiceImpl.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/eperson/GroupServiceImpl.java b/dspace-api/src/main/java/org/dspace/eperson/GroupServiceImpl.java index 730053e42ce2..3fb20e2f1e6f 100644 --- a/dspace-api/src/main/java/org/dspace/eperson/GroupServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/eperson/GroupServiceImpl.java @@ -147,7 +147,7 @@ public void addMember(Context context, Group group, EPerson e) { public void addMember(Context context, Group groupParent, Group groupChild) throws SQLException { // don't add if it's already a member // and don't add itself - if (groupParent.contains(groupChild) || groupParent.getID() == groupChild.getID()) { + if (groupParent.contains(groupChild) || groupParent.getID().equals(groupChild.getID())) { return; } @@ -178,7 +178,7 @@ public void removeMember(Context context, Group group, EPerson ePerson) throws S Role role = stepByName.getRole(); for (CollectionRole collectionRole : collectionRoles) { if (StringUtils.equals(collectionRole.getRoleId(), role.getId()) - && claimedTask.getWorkflowItem().getCollection() == collectionRole.getCollection()) { + && claimedTask.getWorkflowItem().getCollection().equals(collectionRole.getCollection())) { // Count number of EPersons who are *direct* members of this group int totalDirectEPersons = ePersonService.countByGroups(context, Set.of(group)); // Count number of Groups which have this groupParent as a direct parent From ef5479d77a6f61039caf000b97c5bbceeaff30a0 Mon Sep 17 00:00:00 2001 From: Sascha Szott Date: Fri, 2 Aug 2024 17:49:14 +0200 Subject: [PATCH 034/979] use equals instead of == (cherry picked from commit a13cc82d405c5aefe00c7bb86d89c7dc8073a39b) --- .../submit/factory/impl/ItemMetadataValueAddPatchOperation.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/factory/impl/ItemMetadataValueAddPatchOperation.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/factory/impl/ItemMetadataValueAddPatchOperation.java index 54dfa6b02c04..8ab9bb451647 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/factory/impl/ItemMetadataValueAddPatchOperation.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/factory/impl/ItemMetadataValueAddPatchOperation.java @@ -214,7 +214,7 @@ private Integer getRelId(String authority) { private void updateRelationshipPlace(Context context, Item dso, int place, Relationship rs) { try { - if (rs.getLeftItem() == dso) { + if (rs.getLeftItem().equals(dso)) { rs.setLeftPlace(place); } else { rs.setRightPlace(place); From 408e8b608f3a5da9bd688c1bb128ecd93552b599 Mon Sep 17 00:00:00 2001 From: Christian Clauss Date: Sun, 4 Aug 2024 10:56:32 +0200 Subject: [PATCH 035/979] README.md: v8 is the current release, not v7 (cherry picked from commit 2b698eff609d510c487ad2331d4e11cd28f64e9a) --- README.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index af9158eff361..2305643fd8e1 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ ## Overview -DSpace open source software is a turnkey repository application used by more than +DSpace open-source software is a turnkey repository application used by more than 2,000 organizations and institutions worldwide to provide durable access to digital resources. For more information, visit http://www.dspace.org/ @@ -20,7 +20,7 @@ DSpace consists of both a Java-based backend and an Angular-based frontend. * The REST Contract is at https://github.com/DSpace/RestContract * Frontend (https://github.com/DSpace/dspace-angular/) is the User Interface built on the REST API -Prior versions of DSpace (v6.x and below) used two different UIs (XMLUI and JSPUI). Those UIs are no longer supported in v7 (and above). +Prior versions of DSpace (v6.x and below) used two different UIs (XMLUI and JSPUI). Those UIs are no longer supported in v7 and above. * A maintenance branch for older versions is still available, see `dspace-6_x` for 6.x maintenance. ## Downloads @@ -33,18 +33,18 @@ Prior versions of DSpace (v6.x and below) used two different UIs (XMLUI and JSPU Documentation for each release may be viewed online or downloaded via our [Documentation Wiki](https://wiki.lyrasis.org/display/DSDOC/). The latest DSpace Installation instructions are available at: -https://wiki.lyrasis.org/display/DSDOC7x/Installing+DSpace +https://wiki.lyrasis.org/display/DSDOC8x/Installing+DSpace Please be aware that, as a Java web application, DSpace requires a database (PostgreSQL) and a servlet container (usually Tomcat) in order to function. More information about these and all other prerequisites can be found in the Installation instructions above. -## Running DSpace 7 in Docker +## Running DSpace 8 in Docker NOTE: At this time, we do not have production-ready Docker images for DSpace. That said, we do have quick-start Docker Compose scripts for development or testing purposes. -See [Running DSpace 7 with Docker Compose](dspace/src/main/docker-compose/README.md) +See [Running DSpace 8 with Docker Compose](dspace/src/main/docker-compose/README.md) ## Contributing @@ -64,7 +64,7 @@ Great Q&A is also available under the [DSpace tag on Stackoverflow](http://stack Additional support options are at https://wiki.lyrasis.org/display/DSPACE/Support DSpace also has an active service provider network. If you'd rather hire a service provider to -install, upgrade, customize or host DSpace, then we recommend getting in touch with one of our +install, upgrade, customize, or host DSpace, then we recommend getting in touch with one of our [Registered Service Providers](http://www.dspace.org/service-providers). ## Issue Tracker @@ -112,7 +112,7 @@ run automatically by [GitHub Actions](https://github.com/DSpace/DSpace/actions?q ``` * How to run only tests of a specific DSpace module ``` - # Before you can run only one module's tests, other modules may need installing into your ~/.m2 + # Before you can run only one module's tests, other modules may need to be installed into your ~/.m2 cd [dspace-src] mvn clean install From 98dee8c2fcdc89263b95b1bacf56a947bdeb93c9 Mon Sep 17 00:00:00 2001 From: Christian Clauss Date: Sun, 4 Aug 2024 11:04:12 +0200 Subject: [PATCH 036/979] Update README.md (cherry picked from commit 671234b08f909810d798dd950f87d1818b098363) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 2305643fd8e1..1d93abe49948 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ ## Overview -DSpace open-source software is a turnkey repository application used by more than +DSpace open source software is a turnkey repository application used by more than 2,000 organizations and institutions worldwide to provide durable access to digital resources. For more information, visit http://www.dspace.org/ From f1dc25195f025919aa18cc390ad81459c3698169 Mon Sep 17 00:00:00 2001 From: Kristof De Langhe Date: Tue, 18 Jun 2024 13:52:38 +0200 Subject: [PATCH 037/979] 115693: data-cite xsl targetting dc.identifier.uri fixes doi registration error (cherry picked from commit c5d08f037cb56111b8ef047279f323c06b11a864) --- .../main/java/org/dspace/identifier/DOIIdentifierProvider.java | 1 + dspace/config/crosswalks/DIM2DataCite.xsl | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/dspace-api/src/main/java/org/dspace/identifier/DOIIdentifierProvider.java b/dspace-api/src/main/java/org/dspace/identifier/DOIIdentifierProvider.java index b70eda960d35..8d71ac92f9d1 100644 --- a/dspace-api/src/main/java/org/dspace/identifier/DOIIdentifierProvider.java +++ b/dspace-api/src/main/java/org/dspace/identifier/DOIIdentifierProvider.java @@ -70,6 +70,7 @@ public class DOIIdentifierProvider extends FilteredIdentifierProvider { static final char SLASH = '/'; // Metadata field name elements + // Warning: If this metadata field is changed for whatever reason, DIM2DataCite.xsl's template needs to reflect this // TODO: move these to MetadataSchema or some such? public static final String MD_SCHEMA = "dc"; public static final String DOI_ELEMENT = "identifier"; diff --git a/dspace/config/crosswalks/DIM2DataCite.xsl b/dspace/config/crosswalks/DIM2DataCite.xsl index a97d127694ef..d57996e6d8cf 100644 --- a/dspace/config/crosswalks/DIM2DataCite.xsl +++ b/dspace/config/crosswalks/DIM2DataCite.xsl @@ -333,7 +333,8 @@ company as well. We have to ensure to use URIs of our prefix as primary identifiers only. --> - + + From d240a16b051326b0357cd8476928f899207ddc4c Mon Sep 17 00:00:00 2001 From: Kristof De Langhe Date: Thu, 27 Jun 2024 10:59:36 +0200 Subject: [PATCH 038/979] 115693: DataCiteConnector fallback for blank metadata doi (cherry picked from commit 021e42434731e505245b3aa149eaf59a5fbccb94) --- .../main/java/org/dspace/identifier/doi/DataCiteConnector.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/dspace-api/src/main/java/org/dspace/identifier/doi/DataCiteConnector.java b/dspace-api/src/main/java/org/dspace/identifier/doi/DataCiteConnector.java index 57136d6143bb..fc4e2652ac2f 100644 --- a/dspace-api/src/main/java/org/dspace/identifier/doi/DataCiteConnector.java +++ b/dspace-api/src/main/java/org/dspace/identifier/doi/DataCiteConnector.java @@ -15,6 +15,7 @@ import java.util.Iterator; import java.util.Map; +import org.apache.commons.lang.StringUtils; import org.apache.http.HttpEntity; import org.apache.http.HttpResponse; import org.apache.http.StatusLine; @@ -412,7 +413,7 @@ public void reserveDOI(Context context, DSpaceObject dso, String doi) } String metadataDOI = extractDOI(root); - if (null == metadataDOI) { + if (StringUtils.isBlank(metadataDOI)) { // The DOI will be saved as metadata of dso after successful // registration. To register a doi it has to be part of the metadata // sent to DataCite. So we add it to the XML we'll send to DataCite From c1be5f8e4ed9577e22ce7b33bca3c171b84083cb Mon Sep 17 00:00:00 2001 From: Kristof De Langhe Date: Fri, 19 Jul 2024 13:42:09 +0200 Subject: [PATCH 039/979] 115693: Pass doi metadatafield with xsl parameters (cherry picked from commit 9e11e1f9ae69f19e506618d1d0b1fec0059ba165) --- .../identifier/DOIIdentifierProvider.java | 1 - .../identifier/doi/DataCiteConnector.java | 8 ++++++ dspace/config/crosswalks/DIM2DataCite.xsl | 25 +++++++++++-------- 3 files changed, 23 insertions(+), 11 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/identifier/DOIIdentifierProvider.java b/dspace-api/src/main/java/org/dspace/identifier/DOIIdentifierProvider.java index 8d71ac92f9d1..b70eda960d35 100644 --- a/dspace-api/src/main/java/org/dspace/identifier/DOIIdentifierProvider.java +++ b/dspace-api/src/main/java/org/dspace/identifier/DOIIdentifierProvider.java @@ -70,7 +70,6 @@ public class DOIIdentifierProvider extends FilteredIdentifierProvider { static final char SLASH = '/'; // Metadata field name elements - // Warning: If this metadata field is changed for whatever reason, DIM2DataCite.xsl's template needs to reflect this // TODO: move these to MetadataSchema or some such? public static final String MD_SCHEMA = "dc"; public static final String DOI_ELEMENT = "identifier"; diff --git a/dspace-api/src/main/java/org/dspace/identifier/doi/DataCiteConnector.java b/dspace-api/src/main/java/org/dspace/identifier/doi/DataCiteConnector.java index fc4e2652ac2f..a15e3f7fdbfe 100644 --- a/dspace-api/src/main/java/org/dspace/identifier/doi/DataCiteConnector.java +++ b/dspace-api/src/main/java/org/dspace/identifier/doi/DataCiteConnector.java @@ -7,6 +7,10 @@ */ package org.dspace.identifier.doi; +import static org.dspace.identifier.DOIIdentifierProvider.DOI_ELEMENT; +import static org.dspace.identifier.DOIIdentifierProvider.DOI_QUALIFIER; +import static org.dspace.identifier.DOIIdentifierProvider.MD_SCHEMA; + import java.io.ByteArrayInputStream; import java.io.IOException; import java.net.URISyntaxException; @@ -386,6 +390,10 @@ public void reserveDOI(Context context, DSpaceObject dso, String doi) parameters.put("hostinginstitution", configurationService.getProperty(CFG_HOSTINGINSTITUTION)); } + parameters.put("mdSchema", MD_SCHEMA); + parameters.put("mdElement", DOI_ELEMENT); + // Pass an empty string for qualifier if the metadata field doesn't have any + parameters.put("mdQualifier", DOI_QUALIFIER); Element root = null; try { diff --git a/dspace/config/crosswalks/DIM2DataCite.xsl b/dspace/config/crosswalks/DIM2DataCite.xsl index d57996e6d8cf..d4d8cbe6417a 100644 --- a/dspace/config/crosswalks/DIM2DataCite.xsl +++ b/dspace/config/crosswalks/DIM2DataCite.xsl @@ -36,6 +36,10 @@ + + dc + identifier + uri @@ -333,16 +337,17 @@ company as well. We have to ensure to use URIs of our prefix as primary identifiers only. --> - - - - - - - - - - + + + + + + + + + + + From d35946af0a9abbc5dd0bc17f783d376a7fa66f96 Mon Sep 17 00:00:00 2001 From: Kristof De Langhe Date: Tue, 18 Jun 2024 13:52:38 +0200 Subject: [PATCH 040/979] 115693: data-cite xsl targetting dc.identifier.uri fixes doi registration error (cherry picked from commit c5d08f037cb56111b8ef047279f323c06b11a864) --- .../main/java/org/dspace/identifier/DOIIdentifierProvider.java | 1 + dspace/config/crosswalks/DIM2DataCite.xsl | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/dspace-api/src/main/java/org/dspace/identifier/DOIIdentifierProvider.java b/dspace-api/src/main/java/org/dspace/identifier/DOIIdentifierProvider.java index ae31e54f7e96..aff4666fff4b 100644 --- a/dspace-api/src/main/java/org/dspace/identifier/DOIIdentifierProvider.java +++ b/dspace-api/src/main/java/org/dspace/identifier/DOIIdentifierProvider.java @@ -70,6 +70,7 @@ public class DOIIdentifierProvider extends FilteredIdentifierProvider { static final char SLASH = '/'; // Metadata field name elements + // Warning: If this metadata field is changed for whatever reason, DIM2DataCite.xsl's template needs to reflect this // TODO: move these to MetadataSchema or some such? public static final String MD_SCHEMA = "dc"; public static final String DOI_ELEMENT = "identifier"; diff --git a/dspace/config/crosswalks/DIM2DataCite.xsl b/dspace/config/crosswalks/DIM2DataCite.xsl index a97d127694ef..d57996e6d8cf 100644 --- a/dspace/config/crosswalks/DIM2DataCite.xsl +++ b/dspace/config/crosswalks/DIM2DataCite.xsl @@ -333,7 +333,8 @@ company as well. We have to ensure to use URIs of our prefix as primary identifiers only. --> - + + From acec968e940ca69c4eb28afe9512493a0690a4e3 Mon Sep 17 00:00:00 2001 From: Kristof De Langhe Date: Thu, 27 Jun 2024 10:59:36 +0200 Subject: [PATCH 041/979] 115693: DataCiteConnector fallback for blank metadata doi (cherry picked from commit 021e42434731e505245b3aa149eaf59a5fbccb94) --- .../main/java/org/dspace/identifier/doi/DataCiteConnector.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/dspace-api/src/main/java/org/dspace/identifier/doi/DataCiteConnector.java b/dspace-api/src/main/java/org/dspace/identifier/doi/DataCiteConnector.java index 931f1538583e..0fbac1a9f52b 100644 --- a/dspace-api/src/main/java/org/dspace/identifier/doi/DataCiteConnector.java +++ b/dspace-api/src/main/java/org/dspace/identifier/doi/DataCiteConnector.java @@ -15,6 +15,7 @@ import java.util.Iterator; import java.util.Map; +import org.apache.commons.lang.StringUtils; import org.apache.http.HttpEntity; import org.apache.http.HttpResponse; import org.apache.http.StatusLine; @@ -410,7 +411,7 @@ public void reserveDOI(Context context, DSpaceObject dso, String doi) } String metadataDOI = extractDOI(root); - if (null == metadataDOI) { + if (StringUtils.isBlank(metadataDOI)) { // The DOI will be saved as metadata of dso after successful // registration. To register a doi it has to be part of the metadata // sent to DataCite. So we add it to the XML we'll send to DataCite From cd8961bc0573edaf788fcc45aba8bcc2c38d91e2 Mon Sep 17 00:00:00 2001 From: Kristof De Langhe Date: Fri, 19 Jul 2024 13:42:09 +0200 Subject: [PATCH 042/979] 115693: Pass doi metadatafield with xsl parameters (cherry picked from commit 9e11e1f9ae69f19e506618d1d0b1fec0059ba165) --- .../identifier/DOIIdentifierProvider.java | 1 - .../identifier/doi/DataCiteConnector.java | 8 ++++++ dspace/config/crosswalks/DIM2DataCite.xsl | 25 +++++++++++-------- 3 files changed, 23 insertions(+), 11 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/identifier/DOIIdentifierProvider.java b/dspace-api/src/main/java/org/dspace/identifier/DOIIdentifierProvider.java index aff4666fff4b..ae31e54f7e96 100644 --- a/dspace-api/src/main/java/org/dspace/identifier/DOIIdentifierProvider.java +++ b/dspace-api/src/main/java/org/dspace/identifier/DOIIdentifierProvider.java @@ -70,7 +70,6 @@ public class DOIIdentifierProvider extends FilteredIdentifierProvider { static final char SLASH = '/'; // Metadata field name elements - // Warning: If this metadata field is changed for whatever reason, DIM2DataCite.xsl's template needs to reflect this // TODO: move these to MetadataSchema or some such? public static final String MD_SCHEMA = "dc"; public static final String DOI_ELEMENT = "identifier"; diff --git a/dspace-api/src/main/java/org/dspace/identifier/doi/DataCiteConnector.java b/dspace-api/src/main/java/org/dspace/identifier/doi/DataCiteConnector.java index 0fbac1a9f52b..b4cdac96303a 100644 --- a/dspace-api/src/main/java/org/dspace/identifier/doi/DataCiteConnector.java +++ b/dspace-api/src/main/java/org/dspace/identifier/doi/DataCiteConnector.java @@ -7,6 +7,10 @@ */ package org.dspace.identifier.doi; +import static org.dspace.identifier.DOIIdentifierProvider.DOI_ELEMENT; +import static org.dspace.identifier.DOIIdentifierProvider.DOI_QUALIFIER; +import static org.dspace.identifier.DOIIdentifierProvider.MD_SCHEMA; + import java.io.ByteArrayInputStream; import java.io.IOException; import java.net.URISyntaxException; @@ -384,6 +388,10 @@ public void reserveDOI(Context context, DSpaceObject dso, String doi) parameters.put("hostinginstitution", configurationService.getProperty(CFG_HOSTINGINSTITUTION)); } + parameters.put("mdSchema", MD_SCHEMA); + parameters.put("mdElement", DOI_ELEMENT); + // Pass an empty string for qualifier if the metadata field doesn't have any + parameters.put("mdQualifier", DOI_QUALIFIER); Element root = null; try { diff --git a/dspace/config/crosswalks/DIM2DataCite.xsl b/dspace/config/crosswalks/DIM2DataCite.xsl index d57996e6d8cf..d4d8cbe6417a 100644 --- a/dspace/config/crosswalks/DIM2DataCite.xsl +++ b/dspace/config/crosswalks/DIM2DataCite.xsl @@ -36,6 +36,10 @@ + + dc + identifier + uri @@ -333,16 +337,17 @@ company as well. We have to ensure to use URIs of our prefix as primary identifiers only. --> - - - - - - - - - - + + + + + + + + + + + From f61c45bdc6533f7328e84af9c59e908bc1b3e6e2 Mon Sep 17 00:00:00 2001 From: Sascha Szott Date: Mon, 8 Jul 2024 19:26:11 +0200 Subject: [PATCH 043/979] change order of name parts: familyName, givenName (cherry picked from commit 076f1f233ea0eef1a37ed4087f34008d1e92e40a) --- .../external/crossref/CrossRefAuthorMetadataProcessor.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/importer/external/crossref/CrossRefAuthorMetadataProcessor.java b/dspace-api/src/main/java/org/dspace/importer/external/crossref/CrossRefAuthorMetadataProcessor.java index abf84f52d058..b9b384f8ed77 100644 --- a/dspace-api/src/main/java/org/dspace/importer/external/crossref/CrossRefAuthorMetadataProcessor.java +++ b/dspace-api/src/main/java/org/dspace/importer/external/crossref/CrossRefAuthorMetadataProcessor.java @@ -42,8 +42,8 @@ public Collection processMetadata(String json) { JsonNode author = authors.next(); String givenName = author.at("/given").textValue(); String familyName = author.at("/family").textValue(); - if (StringUtils.isNoneBlank(givenName) && StringUtils.isNoneBlank(familyName)) { - values.add(givenName + " " + familyName); + if (StringUtils.isNotBlank(givenName) && StringUtils.isNotBlank(familyName)) { + values.add(familyName.trim() + ", " + givenName.trim()); } } return values; @@ -64,4 +64,4 @@ public void setPathToArray(String pathToArray) { this.pathToArray = pathToArray; } -} \ No newline at end of file +} From 7e87d57672640e72c432f432fd07ad8a6f6aedc8 Mon Sep 17 00:00:00 2001 From: Sascha Szott Date: Tue, 9 Jul 2024 10:53:23 +0200 Subject: [PATCH 044/979] fix broken unit tests (cherry picked from commit 1712b9f07875c67141b67da96fa93f0deaff4090) --- .../app/rest/CrossRefImportMetadataSourceServiceIT.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/CrossRefImportMetadataSourceServiceIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/CrossRefImportMetadataSourceServiceIT.java index 31c22692f008..f61a81140ddc 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/CrossRefImportMetadataSourceServiceIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/CrossRefImportMetadataSourceServiceIT.java @@ -162,7 +162,7 @@ private ArrayList getRecords() { MetadatumDTO title = createMetadatumDTO("dc", "title", null, "State of Awareness of Freshers’ Groups Chortkiv State" + " Medical College of Prevention of Iodine Deficiency Diseases"); - MetadatumDTO author = createMetadatumDTO("dc", "contributor", "author", "L.V. Senyuk"); + MetadatumDTO author = createMetadatumDTO("dc", "contributor", "author", "Senyuk, L.V."); MetadatumDTO type = createMetadatumDTO("dc", "type", null, "journal-article"); MetadatumDTO date = createMetadatumDTO("dc", "date", "issued", "2016-05-19"); MetadatumDTO ispartof = createMetadatumDTO("dc", "relation", "ispartof", @@ -191,7 +191,7 @@ private ArrayList getRecords() { List metadatums2 = new ArrayList(); MetadatumDTO title2 = createMetadatumDTO("dc", "title", null, "Ischemic Heart Disease and Role of Nurse of Cardiology Department"); - MetadatumDTO author2 = createMetadatumDTO("dc", "contributor", "author", "K. І. Kozak"); + MetadatumDTO author2 = createMetadatumDTO("dc", "contributor", "author", "Kozak, K. І."); MetadatumDTO type2 = createMetadatumDTO("dc", "type", null, "journal-article"); MetadatumDTO date2 = createMetadatumDTO("dc", "date", "issued", "2016-05-19"); MetadatumDTO ispartof2 = createMetadatumDTO("dc", "relation", "ispartof", From 65c452d3ef067cb1e28b68881ebcdf44a56e7ccf Mon Sep 17 00:00:00 2001 From: Sascha Szott Date: Mon, 8 Jul 2024 19:26:11 +0200 Subject: [PATCH 045/979] change order of name parts: familyName, givenName (cherry picked from commit 076f1f233ea0eef1a37ed4087f34008d1e92e40a) --- .../external/crossref/CrossRefAuthorMetadataProcessor.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/importer/external/crossref/CrossRefAuthorMetadataProcessor.java b/dspace-api/src/main/java/org/dspace/importer/external/crossref/CrossRefAuthorMetadataProcessor.java index abf84f52d058..b9b384f8ed77 100644 --- a/dspace-api/src/main/java/org/dspace/importer/external/crossref/CrossRefAuthorMetadataProcessor.java +++ b/dspace-api/src/main/java/org/dspace/importer/external/crossref/CrossRefAuthorMetadataProcessor.java @@ -42,8 +42,8 @@ public Collection processMetadata(String json) { JsonNode author = authors.next(); String givenName = author.at("/given").textValue(); String familyName = author.at("/family").textValue(); - if (StringUtils.isNoneBlank(givenName) && StringUtils.isNoneBlank(familyName)) { - values.add(givenName + " " + familyName); + if (StringUtils.isNotBlank(givenName) && StringUtils.isNotBlank(familyName)) { + values.add(familyName.trim() + ", " + givenName.trim()); } } return values; @@ -64,4 +64,4 @@ public void setPathToArray(String pathToArray) { this.pathToArray = pathToArray; } -} \ No newline at end of file +} From b6e1bcb30654d657958b766764319a4f1edfd1f7 Mon Sep 17 00:00:00 2001 From: Sascha Szott Date: Tue, 9 Jul 2024 10:53:23 +0200 Subject: [PATCH 046/979] fix broken unit tests (cherry picked from commit 1712b9f07875c67141b67da96fa93f0deaff4090) --- .../app/rest/CrossRefImportMetadataSourceServiceIT.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/CrossRefImportMetadataSourceServiceIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/CrossRefImportMetadataSourceServiceIT.java index 863fd1f753d1..37bd3a90eeda 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/CrossRefImportMetadataSourceServiceIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/CrossRefImportMetadataSourceServiceIT.java @@ -162,7 +162,7 @@ private ArrayList getRecords() { MetadatumDTO title = createMetadatumDTO("dc", "title", null, "State of Awareness of Freshers’ Groups Chortkiv State" + " Medical College of Prevention of Iodine Deficiency Diseases"); - MetadatumDTO author = createMetadatumDTO("dc", "contributor", "author", "L.V. Senyuk"); + MetadatumDTO author = createMetadatumDTO("dc", "contributor", "author", "Senyuk, L.V."); MetadatumDTO type = createMetadatumDTO("dc", "type", null, "journal-article"); MetadatumDTO date = createMetadatumDTO("dc", "date", "issued", "2016-05-19"); MetadatumDTO ispartof = createMetadatumDTO("dc", "relation", "ispartof", @@ -191,7 +191,7 @@ private ArrayList getRecords() { List metadatums2 = new ArrayList(); MetadatumDTO title2 = createMetadatumDTO("dc", "title", null, "Ischemic Heart Disease and Role of Nurse of Cardiology Department"); - MetadatumDTO author2 = createMetadatumDTO("dc", "contributor", "author", "K. І. Kozak"); + MetadatumDTO author2 = createMetadatumDTO("dc", "contributor", "author", "Kozak, K. І."); MetadatumDTO type2 = createMetadatumDTO("dc", "type", null, "journal-article"); MetadatumDTO date2 = createMetadatumDTO("dc", "date", "issued", "2016-05-19"); MetadatumDTO ispartof2 = createMetadatumDTO("dc", "relation", "ispartof", From ffc99d06a1fc301109df664c3b126137410e1e95 Mon Sep 17 00:00:00 2001 From: autavares-dev Date: Mon, 12 Aug 2024 15:08:26 -0300 Subject: [PATCH 047/979] Fix index-discovery process when using handle (cherry picked from commit 077aed38dc55172ed70d8bfb9c44fd369d966a6d) --- dspace-api/src/main/java/org/dspace/discovery/IndexClient.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/dspace-api/src/main/java/org/dspace/discovery/IndexClient.java b/dspace-api/src/main/java/org/dspace/discovery/IndexClient.java index 661c48d91cfc..867359a949c6 100644 --- a/dspace-api/src/main/java/org/dspace/discovery/IndexClient.java +++ b/dspace-api/src/main/java/org/dspace/discovery/IndexClient.java @@ -21,6 +21,7 @@ import org.dspace.content.Item; import org.dspace.content.factory.ContentServiceFactory; import org.dspace.content.service.ItemService; +import org.dspace.core.Constants; import org.dspace.core.Context; import org.dspace.discovery.indexobject.IndexableCollection; import org.dspace.discovery.indexobject.IndexableCommunity; @@ -92,7 +93,7 @@ public void internalRun() throws Exception { .getHandleService().resolveToObject(context, param); if (dso != null) { final IndexFactory indexableObjectService = IndexObjectFactoryFactory.getInstance(). - getIndexFactoryByType(String.valueOf(dso.getType())); + getIndexFactoryByType(Constants.typeText[dso.getType()]); indexableObject = indexableObjectService.findIndexableObject(context, dso.getID().toString()); } } From b98704ea4cc10d70ab34c6c2b557f0e85d696170 Mon Sep 17 00:00:00 2001 From: autavares-dev Date: Mon, 12 Aug 2024 15:08:26 -0300 Subject: [PATCH 048/979] Fix index-discovery process when using handle (cherry picked from commit 077aed38dc55172ed70d8bfb9c44fd369d966a6d) --- dspace-api/src/main/java/org/dspace/discovery/IndexClient.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/dspace-api/src/main/java/org/dspace/discovery/IndexClient.java b/dspace-api/src/main/java/org/dspace/discovery/IndexClient.java index b70e9162f7a1..3479c25bf367 100644 --- a/dspace-api/src/main/java/org/dspace/discovery/IndexClient.java +++ b/dspace-api/src/main/java/org/dspace/discovery/IndexClient.java @@ -27,6 +27,7 @@ import org.dspace.content.Item; import org.dspace.content.factory.ContentServiceFactory; import org.dspace.content.service.ItemService; +import org.dspace.core.Constants; import org.dspace.core.Context; import org.dspace.discovery.indexobject.IndexableCollection; import org.dspace.discovery.indexobject.IndexableCommunity; @@ -109,7 +110,7 @@ public void internalRun() throws Exception { .getHandleService().resolveToObject(context, param); if (dso != null) { final IndexFactory indexableObjectService = IndexObjectFactoryFactory.getInstance(). - getIndexFactoryByType(String.valueOf(dso.getType())); + getIndexFactoryByType(Constants.typeText[dso.getType()]); indexableObject = indexableObjectService.findIndexableObject(context, dso.getID().toString()); } } From d1f837b2ba3177c13271a311cfd6b117d9fb30d6 Mon Sep 17 00:00:00 2001 From: "Mark H. Wood" Date: Tue, 23 Apr 2024 13:33:01 -0400 Subject: [PATCH 049/979] Separate task-list building from execution. The old code would curate the object once for each task, meaning that all but one task would be executed N times up to the length of the list. (cherry picked from commit c43948bf3d190d68e2c2840c5beee9268764ad2a) --- .../curate/XmlWorkflowCuratorServiceImpl.java | 29 ++++++++++--------- .../java/org/dspace/curate/package-info.java | 12 ++++++++ 2 files changed, 27 insertions(+), 14 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/curate/XmlWorkflowCuratorServiceImpl.java b/dspace-api/src/main/java/org/dspace/curate/XmlWorkflowCuratorServiceImpl.java index 00e91ee1fb40..ec32ff92f9a2 100644 --- a/dspace-api/src/main/java/org/dspace/curate/XmlWorkflowCuratorServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/curate/XmlWorkflowCuratorServiceImpl.java @@ -140,13 +140,14 @@ public boolean curate(Curator curator, Context c, XmlWorkflowItem wfi) item.setOwningCollection(wfi.getCollection()); for (Task task : step.tasks) { curator.addTask(task.name); - // Check whether the task is configured to be queued rather than automatically run - if (StringUtils.isNotEmpty(step.queue)) { - // queue attribute has been set in the FlowStep configuration: add task to configured queue - curator.queue(c, item.getID().toString(), step.queue); - } else { - // Task is configured to be run automatically - curator.curate(c, item); + } + + if (StringUtils.isNotEmpty(step.queue)) { // Step's tasks are to be queued. + curator.queue(c, item.getID().toString(), step.queue); + } else { // Step's tasks are to be run now. + curator.curate(c, item); + + for (Task task : step.tasks) { int status = curator.getStatus(task.name); String result = curator.getResult(task.name); String action = "none"; @@ -183,14 +184,14 @@ public boolean curate(Curator curator, Context c, XmlWorkflowItem wfi) } } curator.clear(); - } - // Record any reporting done by the tasks. - if (reporter.length() > 0) { - LOG.info("Curation tasks over item {} for step {} report:%n{}", - () -> wfi.getItem().getID(), - () -> step.step, - () -> reporter.toString()); + // Record any reporting done by the tasks. + if (reporter.length() > 0) { + LOG.info("Curation tasks over item {} for step {} report:\n{}", + () -> wfi.getItem().getID(), + () -> step.step, + () -> reporter.toString()); + } } } return true; diff --git a/dspace-api/src/main/java/org/dspace/curate/package-info.java b/dspace-api/src/main/java/org/dspace/curate/package-info.java index 492642f60c57..1168bbd283d2 100644 --- a/dspace-api/src/main/java/org/dspace/curate/package-info.java +++ b/dspace-api/src/main/java/org/dspace/curate/package-info.java @@ -20,6 +20,8 @@ * * *

    Curation requests may be run immediately or queued for batch processing. + * See {@link TaskQueue} and its relatives, {@link Curation} and its relatives + * for more on queued curation. * *

    Tasks may also be attached to a workflow step, so that a set of tasks is * applied to each uninstalled Item which passes through that step. See @@ -27,5 +29,15 @@ * *

    A task may return to the Curator a status code, a final status message, * and an optional report character stream. + * + *

    The {@link Reporter} classes absorb strings of text and preserve it in + * various ways. A Reporter is a simple {@link Appendable} and makes no + * assumptions about e.g. whether a string represents a complete line. If you + * want your report formatted, insert appropriate newlines and other whitespace + * as needed. Your tasks can emit marked-up text if you wish, but the stock + * Reporter implementations make no attempt to render it. + * + *

    Tasks may be annotated to inform the Curator of special properties. See + * {@link Distributive}, {@link Mutative}, {@link Suspendable} etc. */ package org.dspace.curate; From 0d74bd9f185f2036ca68853576545f09e32d23fb Mon Sep 17 00:00:00 2001 From: "Mark H. Wood" Date: Tue, 23 Apr 2024 13:33:01 -0400 Subject: [PATCH 050/979] Separate task-list building from execution. The old code would curate the object once for each task, meaning that all but one task would be executed N times up to the length of the list. --- .../curate/XmlWorkflowCuratorServiceImpl.java | 28 +++++++++---------- .../java/org/dspace/curate/package-info.java | 12 ++++++++ 2 files changed, 26 insertions(+), 14 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/curate/XmlWorkflowCuratorServiceImpl.java b/dspace-api/src/main/java/org/dspace/curate/XmlWorkflowCuratorServiceImpl.java index 27a162d543c2..ec32ff92f9a2 100644 --- a/dspace-api/src/main/java/org/dspace/curate/XmlWorkflowCuratorServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/curate/XmlWorkflowCuratorServiceImpl.java @@ -140,14 +140,14 @@ public boolean curate(Curator curator, Context c, XmlWorkflowItem wfi) item.setOwningCollection(wfi.getCollection()); for (Task task : step.tasks) { curator.addTask(task.name); + } - // Check whether the task is configured to be queued rather than automatically run - if (StringUtils.isNotEmpty(step.queue)) { - // queue attribute has been set in the FlowStep configuration: add task to configured queue - curator.queue(c, item.getID().toString(), step.queue); - } else { - // Task is configured to be run automatically - curator.curate(c, item); + if (StringUtils.isNotEmpty(step.queue)) { // Step's tasks are to be queued. + curator.queue(c, item.getID().toString(), step.queue); + } else { // Step's tasks are to be run now. + curator.curate(c, item); + + for (Task task : step.tasks) { int status = curator.getStatus(task.name); String result = curator.getResult(task.name); String action = "none"; @@ -184,14 +184,14 @@ public boolean curate(Curator curator, Context c, XmlWorkflowItem wfi) } } curator.clear(); - } - // Record any reporting done by the tasks. - if (reporter.length() > 0) { - LOG.info("Curation tasks over item {} for step {} report:%n{}", - () -> wfi.getItem().getID(), - () -> step.step, - () -> reporter.toString()); + // Record any reporting done by the tasks. + if (reporter.length() > 0) { + LOG.info("Curation tasks over item {} for step {} report:\n{}", + () -> wfi.getItem().getID(), + () -> step.step, + () -> reporter.toString()); + } } } return true; diff --git a/dspace-api/src/main/java/org/dspace/curate/package-info.java b/dspace-api/src/main/java/org/dspace/curate/package-info.java index 492642f60c57..1168bbd283d2 100644 --- a/dspace-api/src/main/java/org/dspace/curate/package-info.java +++ b/dspace-api/src/main/java/org/dspace/curate/package-info.java @@ -20,6 +20,8 @@ * * *

    Curation requests may be run immediately or queued for batch processing. + * See {@link TaskQueue} and its relatives, {@link Curation} and its relatives + * for more on queued curation. * *

    Tasks may also be attached to a workflow step, so that a set of tasks is * applied to each uninstalled Item which passes through that step. See @@ -27,5 +29,15 @@ * *

    A task may return to the Curator a status code, a final status message, * and an optional report character stream. + * + *

    The {@link Reporter} classes absorb strings of text and preserve it in + * various ways. A Reporter is a simple {@link Appendable} and makes no + * assumptions about e.g. whether a string represents a complete line. If you + * want your report formatted, insert appropriate newlines and other whitespace + * as needed. Your tasks can emit marked-up text if you wish, but the stock + * Reporter implementations make no attempt to render it. + * + *

    Tasks may be annotated to inform the Curator of special properties. See + * {@link Distributive}, {@link Mutative}, {@link Suspendable} etc. */ package org.dspace.curate; From 41312eec6e4e7d86574428f03425c54117ea267c Mon Sep 17 00:00:00 2001 From: "Mark H. Wood" Date: Mon, 26 Aug 2024 15:02:31 -0400 Subject: [PATCH 051/979] Make statistics autocommit much more frequent. (cherry picked from commit 5c9af9764e2485b26398ec06446cb8f0edd9cab2) --- dspace/solr/statistics/conf/solrconfig.xml | 25 ++++++++++++++-------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/dspace/solr/statistics/conf/solrconfig.xml b/dspace/solr/statistics/conf/solrconfig.xml index 2b1cff45373d..c3f023ff2eee 100644 --- a/dspace/solr/statistics/conf/solrconfig.xml +++ b/dspace/solr/statistics/conf/solrconfig.xml @@ -32,14 +32,16 @@ - + 32 1000 ${solr.lock.type:native} - + false @@ -48,7 +50,7 @@ 10000 - ${solr.autoCommit.maxTime:900000} + ${solr.autoCommit.maxTime:10000} true @@ -62,14 +64,16 @@ ${solr.max.booleanClauses:1024} + unordered sets of *all* documents that match a + query. Caches results of 'fq' search param. --> - + 1000 - + - + uuid @@ -126,7 +132,8 @@ - + uid From da4cc4cb3137a620e00df8c586c138b60c1bed2b Mon Sep 17 00:00:00 2001 From: "Mark H. Wood" Date: Mon, 26 Aug 2024 15:02:31 -0400 Subject: [PATCH 052/979] Make statistics autocommit much more frequent. (cherry picked from commit 5c9af9764e2485b26398ec06446cb8f0edd9cab2) --- dspace/solr/statistics/conf/solrconfig.xml | 25 ++++++++++++++-------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/dspace/solr/statistics/conf/solrconfig.xml b/dspace/solr/statistics/conf/solrconfig.xml index 2b1cff45373d..c3f023ff2eee 100644 --- a/dspace/solr/statistics/conf/solrconfig.xml +++ b/dspace/solr/statistics/conf/solrconfig.xml @@ -32,14 +32,16 @@ - + 32 1000 ${solr.lock.type:native} - + false @@ -48,7 +50,7 @@ 10000 - ${solr.autoCommit.maxTime:900000} + ${solr.autoCommit.maxTime:10000} true @@ -62,14 +64,16 @@ ${solr.max.booleanClauses:1024} + unordered sets of *all* documents that match a + query. Caches results of 'fq' search param. --> - + 1000 - + - + uuid @@ -126,7 +132,8 @@ - + uid From e15f60abd3120b81b4b982cc9cb5344e3e2b2951 Mon Sep 17 00:00:00 2001 From: Kim Shepherd Date: Tue, 3 Sep 2024 17:13:01 +0200 Subject: [PATCH 053/979] Translate underscores to dashes in xml:lang attr for DIM2DataCite.xsl Modified the DataCite crosswalk to ensure that the xml:lang attribute translates any underscores in the value of @lang to dashes. This change aligns the attribute formatting with standard language code conventions. (cherry picked from commit a898afd5acbd6dc51a88b837cab2a1c56cc33967) --- dspace/config/crosswalks/DIM2DataCite.xsl | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/dspace/config/crosswalks/DIM2DataCite.xsl b/dspace/config/crosswalks/DIM2DataCite.xsl index d4d8cbe6417a..935b3dc4038a 100644 --- a/dspace/config/crosswalks/DIM2DataCite.xsl +++ b/dspace/config/crosswalks/DIM2DataCite.xsl @@ -25,6 +25,10 @@ to register DOIs anymore. Please follow and reuse the examples included in this file. For more information on the DataCite Schema, see https://schema.datacite.org. --> + 10.5072/dspace- @@ -362,20 +366,20 @@ - + - + AlternativeTitle - - + Subtitle - + TranslatedTitle @@ -394,7 +398,7 @@ --> - + @@ -626,7 +630,7 @@ --> - + Abstract From 175c3d54cd5b3a6b66950b374e0bb35caf52897c Mon Sep 17 00:00:00 2001 From: Kim Shepherd Date: Tue, 3 Sep 2024 17:13:01 +0200 Subject: [PATCH 054/979] Translate underscores to dashes in xml:lang attr for DIM2DataCite.xsl Modified the DataCite crosswalk to ensure that the xml:lang attribute translates any underscores in the value of @lang to dashes. This change aligns the attribute formatting with standard language code conventions. (cherry picked from commit a898afd5acbd6dc51a88b837cab2a1c56cc33967) --- dspace/config/crosswalks/DIM2DataCite.xsl | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/dspace/config/crosswalks/DIM2DataCite.xsl b/dspace/config/crosswalks/DIM2DataCite.xsl index d4d8cbe6417a..935b3dc4038a 100644 --- a/dspace/config/crosswalks/DIM2DataCite.xsl +++ b/dspace/config/crosswalks/DIM2DataCite.xsl @@ -25,6 +25,10 @@ to register DOIs anymore. Please follow and reuse the examples included in this file. For more information on the DataCite Schema, see https://schema.datacite.org. --> + 10.5072/dspace- @@ -362,20 +366,20 @@ - + - + AlternativeTitle - - + Subtitle - + TranslatedTitle @@ -394,7 +398,7 @@ --> - + @@ -626,7 +630,7 @@ --> - + Abstract From 5ca9fee2be64d4fc4512d1f121ecc47e84a5827f Mon Sep 17 00:00:00 2001 From: Kim Shepherd Date: Tue, 25 Jun 2024 13:45:31 +0200 Subject: [PATCH 055/979] Fix request a copy link token generation Ensure DSpace URLs with extra segments are included fully in the generated link (cherry picked from commit 52702a23df37f087058c7837a2dfcbbcf4306382) --- .../rest/repository/RequestItemRepository.java | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/RequestItemRepository.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/RequestItemRepository.java index 6bfae8ed3515..1bb3ddbc9aed 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/RequestItemRepository.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/RequestItemRepository.java @@ -21,6 +21,7 @@ import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import jakarta.servlet.http.HttpServletRequest; +import org.apache.commons.lang3.StringUtils; import org.apache.commons.validator.routines.EmailValidator; import org.apache.http.client.utils.URIBuilder; import org.apache.logging.log4j.LogManager; @@ -287,19 +288,17 @@ public Class getDomainClass() { * Generate a link back to DSpace, to act on a request. * * @param token identifies the request. - * @return URL to the item request API, with the token as request parameter - * "token". + * @return URL to the item request API, with /request-a-copy/{token} as the last URL segments * @throws URISyntaxException passed through. * @throws MalformedURLException passed through. */ private String getLinkTokenEmail(String token) throws URISyntaxException, MalformedURLException { final String base = configurationService.getProperty("dspace.ui.url"); - - URI link = new URIBuilder(base) - .setPathSegments("request-a-copy", token) - .build(); - - return link.toURL().toExternalForm(); + URIBuilder uriBuilder = new URIBuilder(base); + // Add request-a-copy/token to the existing path (without breaking /sub/dir dspace URLs) + URI uri = uriBuilder.setPath(StringUtils.stripEnd(uriBuilder.getPath(), "") + + "/request-a-copy/" + token).build(); + return uri.toURL().toExternalForm(); } } From 083f7b45e0b23a76a41fedd6e7c560f7278a3ef6 Mon Sep 17 00:00:00 2001 From: Kim Shepherd Date: Tue, 25 Jun 2024 14:36:17 +0200 Subject: [PATCH 056/979] Make RequestItemRepository#getLinkTokenEmail public, write test (cherry picked from commit 3646a54df3d540e7dc7603cd7013961df8d9a404) --- .../repository/RequestItemRepository.java | 2 +- .../app/rest/RequestItemRepositoryIT.java | 29 +++++++++++++++++++ 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/RequestItemRepository.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/RequestItemRepository.java index 1bb3ddbc9aed..0698567dcbc6 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/RequestItemRepository.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/RequestItemRepository.java @@ -292,7 +292,7 @@ public Class getDomainClass() { * @throws URISyntaxException passed through. * @throws MalformedURLException passed through. */ - private String getLinkTokenEmail(String token) + public String getLinkTokenEmail(String token) throws URISyntaxException, MalformedURLException { final String base = configurationService.getProperty("dspace.ui.url"); URIBuilder uriBuilder = new URIBuilder(base); diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/RequestItemRepositoryIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/RequestItemRepositoryIT.java index ea61db514504..a43e458207cf 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/RequestItemRepositoryIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/RequestItemRepositoryIT.java @@ -28,6 +28,8 @@ import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; +import java.net.MalformedURLException; +import java.net.URISyntaxException; import java.sql.SQLException; import java.time.temporal.ChronoUnit; import java.util.Date; @@ -55,10 +57,12 @@ import org.dspace.content.Bitstream; import org.dspace.content.Collection; import org.dspace.content.Item; +import org.dspace.services.ConfigurationService; import org.hamcrest.Matchers; import org.junit.Before; import org.junit.Test; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationContext; /** * @@ -81,6 +85,12 @@ public class RequestItemRepositoryIT @Autowired(required = true) RequestItemService requestItemService; + @Autowired + ApplicationContext applicationContext; + + @Autowired + private ConfigurationService configurationService; + private Collection collection; private Item item; @@ -594,4 +604,23 @@ public void testGetDomainClass() { Class instanceClass = instance.getDomainClass(); assertEquals("Wrong domain class", RequestItemRest.class, instanceClass); } + + /** + * Test that generated links include the correct base URL, even if it has extra URL segments + */ + @Test + public void testGetLinkTokenEmail() throws MalformedURLException, URISyntaxException { + RequestItemRepository instance = applicationContext.getBean( + RequestItemRest.CATEGORY + '.' + RequestItemRest.PLURAL_NAME, + RequestItemRepository.class); + String currentDspaceUrl = configurationService.getProperty("dspace.ui.url"); + String newDspaceUrl = currentDspaceUrl + "/subdir"; + // Add a /subdir to the url for this test + configurationService.setProperty("dspace.ui.url", newDspaceUrl); + String expectedUrl = newDspaceUrl + "/request-a-copy/token"; + String generatedLink = instance.getLinkTokenEmail("token"); + // The URLs should match + assertEquals(expectedUrl, generatedLink); + configurationService.reloadConfig(); + } } From 6f1bc3bb6aa7c4c8069435c3d219637269aabb46 Mon Sep 17 00:00:00 2001 From: Kim Shepherd Date: Wed, 10 Jul 2024 16:04:34 +0200 Subject: [PATCH 057/979] #9668: Ensure proper handling of non-subpath URLs in link tokens (cherry picked from commit 6eb3271fa320b9d7d32ef3c59f7b83467bd2b03a) --- .../repository/RequestItemRepository.java | 5 +++-- .../app/rest/RequestItemRepositoryIT.java | 20 +++++++++++++++++-- 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/RequestItemRepository.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/RequestItemRepository.java index 0698567dcbc6..35bd21489543 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/RequestItemRepository.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/RequestItemRepository.java @@ -297,8 +297,9 @@ public String getLinkTokenEmail(String token) final String base = configurationService.getProperty("dspace.ui.url"); URIBuilder uriBuilder = new URIBuilder(base); // Add request-a-copy/token to the existing path (without breaking /sub/dir dspace URLs) - URI uri = uriBuilder.setPath(StringUtils.stripEnd(uriBuilder.getPath(), "") - + "/request-a-copy/" + token).build(); + URI uri = uriBuilder.setPath( + (uriBuilder.getPath() == null ? "" : StringUtils.stripEnd(uriBuilder.getPath(), "")) + + "/request-a-copy/" + token).build(); return uri.toURL().toExternalForm(); } } diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/RequestItemRepositoryIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/RequestItemRepositoryIT.java index a43e458207cf..56409d18d738 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/RequestItemRepositoryIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/RequestItemRepositoryIT.java @@ -606,10 +606,10 @@ public void testGetDomainClass() { } /** - * Test that generated links include the correct base URL, even if it has extra URL segments + * Test that generated links include the correct base URL, where the UI URL has a subpath like /subdir */ @Test - public void testGetLinkTokenEmail() throws MalformedURLException, URISyntaxException { + public void testGetLinkTokenEmailWithSubPath() throws MalformedURLException, URISyntaxException { RequestItemRepository instance = applicationContext.getBean( RequestItemRest.CATEGORY + '.' + RequestItemRest.PLURAL_NAME, RequestItemRepository.class); @@ -623,4 +623,20 @@ public void testGetLinkTokenEmail() throws MalformedURLException, URISyntaxExcep assertEquals(expectedUrl, generatedLink); configurationService.reloadConfig(); } + + /** + * Test that generated links include the correct base URL, with NO subpath elements + */ + @Test + public void testGetLinkTokenEmailWithoutSubPath() throws MalformedURLException, URISyntaxException { + RequestItemRepository instance = applicationContext.getBean( + RequestItemRest.CATEGORY + '.' + RequestItemRest.PLURAL_NAME, + RequestItemRepository.class); + String currentDspaceUrl = configurationService.getProperty("dspace.ui.url"); + String expectedUrl = currentDspaceUrl + "/request-a-copy/token"; + String generatedLink = instance.getLinkTokenEmail("token"); + // The URLs should match + assertEquals(expectedUrl, generatedLink); + configurationService.reloadConfig(); + } } From 67873075a90f6effe63b5bc0429c9508eb076a1b Mon Sep 17 00:00:00 2001 From: Kim Shepherd Date: Sun, 1 Sep 2024 12:53:04 +0200 Subject: [PATCH 058/979] Improved URI build method as per review (cherry picked from commit a9f6d771121087f120c0a82710bde864b73b5e10) --- .../rest/repository/RequestItemRepository.java | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/RequestItemRepository.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/RequestItemRepository.java index 35bd21489543..3de3dbef9c5a 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/RequestItemRepository.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/RequestItemRepository.java @@ -15,7 +15,11 @@ import java.net.URI; import java.net.URISyntaxException; import java.sql.SQLException; +import java.util.ArrayList; +import java.util.Collections; import java.util.Date; +import java.util.LinkedList; +import java.util.List; import java.util.UUID; import com.fasterxml.jackson.databind.JsonNode; @@ -295,11 +299,16 @@ public Class getDomainClass() { public String getLinkTokenEmail(String token) throws URISyntaxException, MalformedURLException { final String base = configurationService.getProperty("dspace.ui.url"); + URIBuilder uriBuilder = new URIBuilder(base); - // Add request-a-copy/token to the existing path (without breaking /sub/dir dspace URLs) - URI uri = uriBuilder.setPath( - (uriBuilder.getPath() == null ? "" : StringUtils.stripEnd(uriBuilder.getPath(), "")) - + "/request-a-copy/" + token).build(); + List segments = new LinkedList<>(); + if (StringUtils.isNotBlank(uriBuilder.getPath())) { + segments.add(StringUtils.strip(uriBuilder.getPath(), "/")); + } + segments.add("request-a-copy"); + segments.add(token); + URI uri = uriBuilder.setPathSegments(segments).build(); + return uri.toURL().toExternalForm(); } } From 24199cf6f26dd30183f5d5f698eb939f97c780be Mon Sep 17 00:00:00 2001 From: Kim Shepherd Date: Wed, 4 Sep 2024 14:04:20 +0200 Subject: [PATCH 059/979] Tidy implementation of link token generation (cherry picked from commit 74a6dc218787b77ed03aa36900be6bd070be043c) --- .../dspace/app/rest/repository/RequestItemRepository.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/RequestItemRepository.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/RequestItemRepository.java index 3de3dbef9c5a..59645aaf1d57 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/RequestItemRepository.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/RequestItemRepository.java @@ -300,6 +300,7 @@ public String getLinkTokenEmail(String token) throws URISyntaxException, MalformedURLException { final String base = configurationService.getProperty("dspace.ui.url"); + // Construct the link, making sure to support sub-paths URIBuilder uriBuilder = new URIBuilder(base); List segments = new LinkedList<>(); if (StringUtils.isNotBlank(uriBuilder.getPath())) { @@ -307,8 +308,8 @@ public String getLinkTokenEmail(String token) } segments.add("request-a-copy"); segments.add(token); - URI uri = uriBuilder.setPathSegments(segments).build(); - return uri.toURL().toExternalForm(); + // Build and return the URL from segments (or throw exception) + return uriBuilder.setPathSegments(segments).build().toURL().toExternalForm(); } } From 8656c5051d8919df88ea0016a2d0a05316f767fb Mon Sep 17 00:00:00 2001 From: Kim Shepherd Date: Wed, 4 Sep 2024 15:19:34 +0200 Subject: [PATCH 060/979] lint fixes (RequestItemRepository) (cherry picked from commit 185a6fdf91c33eb5e100fd28b881f714b3a0a3be) --- .../org/dspace/app/rest/repository/RequestItemRepository.java | 3 --- 1 file changed, 3 deletions(-) diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/RequestItemRepository.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/RequestItemRepository.java index 59645aaf1d57..eaae877f283e 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/RequestItemRepository.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/RequestItemRepository.java @@ -12,11 +12,8 @@ import java.io.IOException; import java.net.MalformedURLException; -import java.net.URI; import java.net.URISyntaxException; import java.sql.SQLException; -import java.util.ArrayList; -import java.util.Collections; import java.util.Date; import java.util.LinkedList; import java.util.List; From 404039ade6796f9abfb5dbf420ab83f87057a033 Mon Sep 17 00:00:00 2001 From: Kim Shepherd Date: Fri, 6 Sep 2024 15:25:18 +0200 Subject: [PATCH 061/979] #9806: Add new create methods to group builder Now supports admin groups, default read, workflow role (cherry picked from commit cdb167e55aac9916618dcfee7222105f4456dbd8) --- .../java/org/dspace/builder/GroupBuilder.java | 78 +++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/dspace-api/src/test/java/org/dspace/builder/GroupBuilder.java b/dspace-api/src/test/java/org/dspace/builder/GroupBuilder.java index b3447dd8bd9a..c16fb696b0c3 100644 --- a/dspace-api/src/test/java/org/dspace/builder/GroupBuilder.java +++ b/dspace-api/src/test/java/org/dspace/builder/GroupBuilder.java @@ -12,6 +12,9 @@ import java.util.UUID; import org.dspace.authorize.AuthorizeException; +import org.dspace.content.Collection; +import org.dspace.content.Community; +import org.dspace.content.DSpaceObject; import org.dspace.content.service.DSpaceObjectService; import org.dspace.core.Context; import org.dspace.eperson.EPerson; @@ -51,6 +54,33 @@ public static GroupBuilder createGroup(final Context context) { return builder.create(context); } + public static GroupBuilder createCollectionAdminGroup(final Context context, Collection collection) { + GroupBuilder builder = new GroupBuilder(context); + return builder.createAdminGroup(context, collection); + } + + public static GroupBuilder createCollectionSubmitterGroup(final Context context, Collection collection) { + GroupBuilder builder = new GroupBuilder(context); + return builder.createSubmitterGroup(context, collection); + } + + public static GroupBuilder createCollectionDefaultReadGroup(final Context context, Collection collection, + String typeOfGroupString, int defaultRead) { + GroupBuilder builder = new GroupBuilder(context); + return builder.createDefaultReadGroup(context, collection, typeOfGroupString, defaultRead); + } + + public static GroupBuilder createCollectionWorkflowRoleGroup(final Context context, Collection collection, + String roleName) { + GroupBuilder builder = new GroupBuilder(context); + return builder.createWorkflowRoleGroup(context, collection, roleName); + } + + public static GroupBuilder createCommunityAdminGroup(final Context context, Community community) { + GroupBuilder builder = new GroupBuilder(context); + return builder.createAdminGroup(context, community); + } + private GroupBuilder create(final Context context) { this.context = context; try { @@ -61,6 +91,54 @@ private GroupBuilder create(final Context context) { return this; } + private GroupBuilder createAdminGroup(final Context context, DSpaceObject container) { + this.context = context; + try { + if (container instanceof Collection) { + group = collectionService.createAdministrators(context, (Collection) container); + } else if (container instanceof Community) { + group = communityService.createAdministrators(context, (Community) container); + } else { + handleException(new IllegalArgumentException("DSpaceObject must be collection or community. " + + "Type: " + container.getType())); + } + } catch (Exception e) { + return handleException(e); + } + return this; + } + + private GroupBuilder createSubmitterGroup(final Context context, Collection collection) { + this.context = context; + try { + group = collectionService.createSubmitters(context, collection); + } catch (Exception e) { + return handleException(e); + } + return this; + } + + private GroupBuilder createDefaultReadGroup(final Context context, Collection collection, + String typeOfGroupString, int defaultRead) { + this.context = context; + try { + group = collectionService.createDefaultReadGroup(context, collection, typeOfGroupString, defaultRead); + } catch (Exception e) { + return handleException(e); + } + return this; + } + + private GroupBuilder createWorkflowRoleGroup(final Context context, Collection collection, String roleName) { + this.context = context; + try { + group = workflowService.createWorkflowRoleGroup(context, collection, roleName); + } catch (Exception e) { + return handleException(e); + } + return this; + } + @Override protected DSpaceObjectService getService() { return groupService; From 2ff2297b1fdd90b6f7c595c34b1167df3f91c036 Mon Sep 17 00:00:00 2001 From: Kim Shepherd Date: Fri, 6 Sep 2024 15:28:27 +0200 Subject: [PATCH 062/979] #9806: Use builders for coll, comm, group creation in BitstreamRestRepositoryIT (cherry picked from commit b13abac7539944358711a381548fbaedd9dcbd92) --- .../app/rest/BitstreamRestRepositoryIT.java | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/BitstreamRestRepositoryIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/BitstreamRestRepositoryIT.java index bd79dc2f2b89..ecfe933fd921 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/BitstreamRestRepositoryIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/BitstreamRestRepositoryIT.java @@ -49,6 +49,7 @@ import org.dspace.builder.CollectionBuilder; import org.dspace.builder.CommunityBuilder; import org.dspace.builder.EPersonBuilder; +import org.dspace.builder.GroupBuilder; import org.dspace.builder.ItemBuilder; import org.dspace.builder.ResourcePolicyBuilder; import org.dspace.content.Bitstream; @@ -2767,10 +2768,12 @@ public void deleteBitstreamsInBulk_collectionAdmin() throws Exception { .withEmail("col2admin@test.com") .withPassword(password) .build(); - Group col1_AdminGroup = collectionService.createAdministrators(context, col1); - Group col2_AdminGroup = collectionService.createAdministrators(context, col2); - groupService.addMember(context, col1_AdminGroup, col1Admin); - groupService.addMember(context, col2_AdminGroup, col2Admin); + Group col1_AdminGroup = GroupBuilder.createCollectionAdminGroup(context, col1) + .addMember(col1Admin) + .build(); + Group col2_AdminGroup = GroupBuilder.createCollectionAdminGroup(context, col2) + .addMember(col2Admin) + .build(); Item publicItem1 = ItemBuilder.createItem(context, col1) .withTitle("Test item 1") .build(); @@ -2871,8 +2874,9 @@ public void deleteBitstreamsInBulk_communityAdmin() throws Exception { .withEmail("parentComAdmin@test.com") .withPassword(password) .build(); - Group parentComAdminGroup = communityService.createAdministrators(context, parentCommunity); - groupService.addMember(context, parentComAdminGroup, parentCommunityAdmin); + Group parentComAdminGroup = GroupBuilder.createCommunityAdminGroup(context, parentCommunity) + .addMember(parentCommunityAdmin) + .build(); Item publicItem1 = ItemBuilder.createItem(context, col1) .withTitle("Test item 1") .build(); From b9b135163f5f261065aa272b3af41b92ac17cea7 Mon Sep 17 00:00:00 2001 From: Kim Shepherd Date: Fri, 6 Sep 2024 15:29:48 +0200 Subject: [PATCH 063/979] #9806: Use builders for coll, comm, group creation in GroupRestRepositoryIT (cherry picked from commit 9205773802a8d22820b074e18409e245c63f5609) --- .../app/rest/GroupRestRepositoryIT.java | 202 ++++++++++-------- 1 file changed, 107 insertions(+), 95 deletions(-) diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/GroupRestRepositoryIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/GroupRestRepositoryIT.java index 2999b89d934f..92e53d922d25 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/GroupRestRepositoryIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/GroupRestRepositoryIT.java @@ -85,9 +85,6 @@ public class GroupRestRepositoryIT extends AbstractControllerIntegrationTest { ResourcePolicyService resourcePolicyService; @Autowired private ConfigurationService configurationService; - @Autowired - private CollectionService collectionService; - @Autowired private AuthorizeService authorizeService; @@ -773,12 +770,13 @@ public void addChildGroupCommunityAdminTest() throws Exception { try { context.turnOffAuthorisationSystem(); - community = communityService.create(null, context); - parentGroup = communityService.createAdministrators(context, community); - childGroup1 = groupService.create(context); - childGroup2 = groupService.create(context); + community = CommunityBuilder.createCommunity(context).build(); + parentGroup = GroupBuilder.createCommunityAdminGroup(context, community) + .addMember(eperson) + .build(); + childGroup1 = GroupBuilder.createGroup(context).build(); + childGroup2 = GroupBuilder.createGroup(context).build(); - groupService.addMember(context, parentGroup, eperson); groupService.update(context, parentGroup); context.commit(); @@ -810,6 +808,7 @@ public void addChildGroupCommunityAdminTest() throws Exception { ); } finally { + // TODO: Can we remove these lines now that we are creating them with the builder? if (community != null) { CommunityBuilder.deleteCommunity(community.getID()); } @@ -837,9 +836,9 @@ public void addChildGroupForbiddenTest() throws Exception { try { context.turnOffAuthorisationSystem(); - parentGroup = groupService.create(context); - childGroup1 = groupService.create(context); - childGroup2 = groupService.create(context); + parentGroup = GroupBuilder.createGroup(context).build(); + childGroup1 = GroupBuilder.createGroup(context).build(); + childGroup2 = GroupBuilder.createGroup(context).build(); context.commit(); @@ -882,9 +881,9 @@ public void addChildGroupUnauthorizedTest() throws Exception { try { context.turnOffAuthorisationSystem(); - parentGroup = groupService.create(context); - childGroup1 = groupService.create(context); - childGroup2 = groupService.create(context); + parentGroup = GroupBuilder.createGroup(context).build(); + childGroup1 = GroupBuilder.createGroup(context).build(); + childGroup2 = GroupBuilder.createGroup(context).build(); context.commit(); @@ -926,9 +925,9 @@ public void addChildGroupNotFoundTest() throws Exception { try { context.turnOffAuthorisationSystem(); - parentGroup = groupService.create(context); - childGroup1 = groupService.create(context); - childGroup2 = groupService.create(context); + parentGroup = GroupBuilder.createGroup(context).build(); + childGroup1 = GroupBuilder.createGroup(context).build(); + childGroup2 = GroupBuilder.createGroup(context).build(); context.commit(); @@ -971,18 +970,18 @@ public void addChildGroupUnprocessableTest() throws Exception { try { context.turnOffAuthorisationSystem(); - parentGroup = groupService.create(context); - childGroup1 = groupService.create(context); - childGroup2 = groupService.create(context); - - groupService.addMember(context, childGroup1, parentGroup); + parentGroup = GroupBuilder.createGroup(context).build(); + childGroup1 = GroupBuilder.createGroup(context) + .withParent(parentGroup) + .build(); + childGroup2 = GroupBuilder.createGroup(context).build(); groupService.update(context, childGroup1); - context.commit(); - - parentGroup = context.reloadEntity(parentGroup); - childGroup1 = context.reloadEntity(childGroup1); - childGroup2 = context.reloadEntity(childGroup2); +// context.commit(); +// +// parentGroup = context.reloadEntity(parentGroup); +// childGroup1 = context.reloadEntity(childGroup1); +// childGroup2 = context.reloadEntity(childGroup2); context.restoreAuthSystemState(); String authToken = getAuthToken(admin.getEmail(), password); @@ -995,13 +994,15 @@ public void addChildGroupUnprocessableTest() throws Exception { ) ).andExpect(status().isUnprocessableEntity()); + // TODO - confirm with reviewers that this is a mistake - it actually should be No Content + // (see AddMember test) but was incorrectly expecting 422? getClient(authToken).perform( post("/api/eperson/groups/" + parentGroup.getID() + "/subgroups") .contentType(parseMediaType(TEXT_URI_LIST_VALUE)) .content(REST_SERVER_URL + "eperson/groups/" + childGroup1.getID() + "/\n" + REST_SERVER_URL + "eperson/groups/" + childGroup2.getID() ) - ).andExpect(status().isUnprocessableEntity()); + ).andExpect(status().isNoContent()); } finally { if (parentGroup != null) { @@ -1093,13 +1094,12 @@ public void addMemberCommunityAdminTest() throws Exception { try { context.turnOffAuthorisationSystem(); - community = communityService.create(null, context); - parentGroup = communityService.createAdministrators(context, community); - member1 = ePersonService.create(context); - member2 = ePersonService.create(context); - - groupService.addMember(context, parentGroup, eperson); - groupService.update(context, parentGroup); + community = CommunityBuilder.createCommunity(context).build(); + parentGroup = GroupBuilder.createCommunityAdminGroup(context, community) + .addMember(eperson) + .build(); + member1 = EPersonBuilder.createEPerson(context).build(); + member2 = EPersonBuilder.createEPerson(context).build(); context.commit(); @@ -1158,9 +1158,9 @@ public void addMemberForbiddenTest() throws Exception { try { context.turnOffAuthorisationSystem(); - parentGroup = groupService.create(context); - member1 = ePersonService.create(context); - member2 = ePersonService.create(context); + parentGroup = GroupBuilder.createGroup(context).build(); + member1 = EPersonBuilder.createEPerson(context).build(); + member2 = EPersonBuilder.createEPerson(context).build(); context.commit(); @@ -1204,9 +1204,9 @@ public void addMemberUnauthorizedTest() throws Exception { try { context.turnOffAuthorisationSystem(); - parentGroup = groupService.create(context); - member1 = ePersonService.create(context); - member2 = ePersonService.create(context); + parentGroup = GroupBuilder.createGroup(context).build(); + member1 = EPersonBuilder.createEPerson(context).build(); + member2 = EPersonBuilder.createEPerson(context).build(); context.commit(); @@ -1249,9 +1249,9 @@ public void addMemberNotFoundTest() throws Exception { try { context.turnOffAuthorisationSystem(); - parentGroup = groupService.create(context); - member1 = ePersonService.create(context); - member2 = ePersonService.create(context); + parentGroup = GroupBuilder.createGroup(context).build(); + member1 = EPersonBuilder.createEPerson(context).build(); + member2 = EPersonBuilder.createEPerson(context).build(); context.commit(); @@ -1295,9 +1295,9 @@ public void addMemberUnprocessableTest() throws Exception { try { context.turnOffAuthorisationSystem(); - parentGroup = groupService.create(context); - member1 = ePersonService.create(context); - member2 = ePersonService.create(context); + parentGroup = GroupBuilder.createGroup(context).build(); + member1 = EPersonBuilder.createEPerson(context).build(); + member2 = EPersonBuilder.createEPerson(context).build(); context.commit(); @@ -1389,13 +1389,13 @@ public void removeChildGroupCommunityAdminTest() throws Exception { try { context.turnOffAuthorisationSystem(); - community = communityService.create(null, context); - parentGroup = communityService.createAdministrators(context, community); - childGroup = groupService.create(context); - - groupService.addMember(context, parentGroup, childGroup); - groupService.addMember(context, parentGroup, eperson); - groupService.update(context, parentGroup); + community = CommunityBuilder.createCommunity(context).build(); + parentGroup = GroupBuilder.createCommunityAdminGroup(context, community) + .addMember(eperson) + .build(); + childGroup = GroupBuilder.createGroup(context) + .withParent(parentGroup) + .build(); context.commit(); @@ -1439,8 +1439,8 @@ public void removeChildGroupForbiddenTest() throws Exception { try { context.turnOffAuthorisationSystem(); - parentGroup = groupService.create(context); - childGroup = groupService.create(context); + parentGroup = GroupBuilder.createGroup(context).build(); + childGroup = GroupBuilder.createGroup(context).build(); context.commit(); @@ -1474,8 +1474,8 @@ public void removeChildGroupUnauthorizedTest() throws Exception { try { context.turnOffAuthorisationSystem(); - parentGroup = groupService.create(context); - childGroup = groupService.create(context); + parentGroup = GroupBuilder.createGroup(context).build(); + childGroup = GroupBuilder.createGroup(context).build(); context.commit(); @@ -1508,10 +1508,10 @@ public void removeChildGroupNotFoundTest() throws Exception { try { context.turnOffAuthorisationSystem(); - parentGroup = groupService.create(context); - childGroup = groupService.create(context); - - groupService.addMember(context, childGroup, parentGroup); + parentGroup = GroupBuilder.createGroup(context).build(); + childGroup = GroupBuilder.createGroup(context) + .withParent(parentGroup) + .build(); context.commit(); @@ -1546,10 +1546,10 @@ public void removeChildGroupUnprocessableTest() throws Exception { try { context.turnOffAuthorisationSystem(); - parentGroup = groupService.create(context); - childGroup = groupService.create(context); - - groupService.addMember(context, childGroup, parentGroup); + parentGroup = GroupBuilder.createGroup(context).build(); + childGroup = GroupBuilder.createGroup(context) + .withParent(parentGroup) + .build(); context.commit(); @@ -1625,13 +1625,12 @@ public void removeMemberCommunityAdminTest() throws Exception { try { context.turnOffAuthorisationSystem(); - community = communityService.create(null, context); - parentGroup = communityService.createAdministrators(context, community); - member = ePersonService.create(context); - - groupService.addMember(context, parentGroup, member); - groupService.addMember(context, parentGroup, eperson); - groupService.update(context, parentGroup); + community = CommunityBuilder.createCommunity(context).build(); + member = EPersonBuilder.createEPerson(context).build(); + parentGroup = GroupBuilder.createCommunityAdminGroup(context, community) + .addMember(member) + .addMember(eperson) + .build(); assertTrue(groupService.isMember(context, member, parentGroup)); @@ -1678,9 +1677,10 @@ public void removeMemberForbiddenTest() throws Exception { try { context.turnOffAuthorisationSystem(); - parentGroup = groupService.create(context); - member = ePersonService.create(context); - groupService.addMember(context, parentGroup, member); + member = EPersonBuilder.createEPerson(context).build(); + parentGroup = GroupBuilder.createGroup(context) + .addMember(member) + .build(); context.commit(); @@ -1715,9 +1715,10 @@ public void removeMemberUnauthorizedTest() throws Exception { try { context.turnOffAuthorisationSystem(); - parentGroup = groupService.create(context); - member = ePersonService.create(context); - groupService.addMember(context, parentGroup, member); + member = EPersonBuilder.createEPerson(context).build(); + parentGroup = GroupBuilder.createGroup(context) + .addMember(member) + .build(); context.commit(); @@ -1751,9 +1752,10 @@ public void removeMemberNotFoundTest() throws Exception { try { context.turnOffAuthorisationSystem(); - parentGroup = groupService.create(context); - member = ePersonService.create(context); - groupService.addMember(context, parentGroup, member); + member = EPersonBuilder.createEPerson(context).build(); + parentGroup = GroupBuilder.createGroup(context) + .addMember(member) + .build(); context.commit(); @@ -1789,9 +1791,10 @@ public void removeMemberUnprocessableTest() throws Exception { try { context.turnOffAuthorisationSystem(); - parentGroup = groupService.create(context); - member = ePersonService.create(context); - groupService.addMember(context, parentGroup, member); + member = EPersonBuilder.createEPerson(context).build(); + parentGroup = GroupBuilder.createGroup(context) + .addMember(member) + .build(); context.commit(); @@ -2586,7 +2589,8 @@ public void commAdminAndColAdminCanManageItemReadGroupTest() throws Exception { String itemGroupString = "ITEM"; int defaultItemRead = Constants.DEFAULT_ITEM_READ; - Group itemReadGroup = collectionService.createDefaultReadGroup(context, col1, itemGroupString, defaultItemRead); + Group itemReadGroup = GroupBuilder.createCollectionDefaultReadGroup(context, + col1, itemGroupString, defaultItemRead).build(); context.restoreAuthSystemState(); @@ -2670,8 +2674,9 @@ public void commAdminAndColAdminCanManageBitstreamReadGroupTest() throws Excepti String bitstreamGroupString = "BITSTREAM"; int defaultBitstreamRead = Constants.DEFAULT_BITSTREAM_READ; - Group bitstreamReadGroup = collectionService.createDefaultReadGroup(context, col1, bitstreamGroupString, - defaultBitstreamRead); + Group bitstreamReadGroup = GroupBuilder.createCollectionDefaultReadGroup(context, col1, bitstreamGroupString, + defaultBitstreamRead) + .build(); context.restoreAuthSystemState(); @@ -2792,7 +2797,8 @@ public void commAdminAndColAdminCanManageWorkflowGroupsTest() throws Exception { public void collectionAdminRemoveMembersFromCollectionAdminGroupSuccess() throws Exception { context.turnOffAuthorisationSystem(); - Group adminGroup = collectionService.createAdministrators(context, collection); + Group adminGroup = GroupBuilder.createCollectionAdminGroup(context, collection) + .build(); authorizeService.addPolicy(context, collection, Constants.ADMIN, eperson); EPerson ePerson = EPersonBuilder.createEPerson(context).withEmail("testToAdd@test.com").build(); context.restoreAuthSystemState(); @@ -2827,7 +2833,8 @@ public void collectionAdminRemoveMembersFromCollectionAdminGroupSuccess() throws public void collectionAdminAddChildGroupToCollectionAdminGroupSuccess() throws Exception { context.turnOffAuthorisationSystem(); - Group adminGroup = collectionService.createAdministrators(context, collection); + Group adminGroup = GroupBuilder.createCollectionAdminGroup(context, collection) + .build(); authorizeService.addPolicy(context, collection, Constants.ADMIN, eperson); Group group = GroupBuilder.createGroup(context).withName("testGroup").build(); context.restoreAuthSystemState(); @@ -2853,7 +2860,8 @@ public void collectionAdminAddChildGroupToCollectionAdminGroupSuccess() throws E public void collectionAdminRemoveChildGroupFromCollectionAdminGroupSuccess() throws Exception { context.turnOffAuthorisationSystem(); - Group adminGroup = collectionService.createAdministrators(context, collection); + Group adminGroup = GroupBuilder.createCollectionAdminGroup(context, collection) + .build(); authorizeService.addPolicy(context, collection, Constants.ADMIN, eperson); Group group = GroupBuilder.createGroup(context).withName("testGroup").build(); context.restoreAuthSystemState(); @@ -2889,7 +2897,8 @@ public void collectionAdminRemoveChildGroupFromCollectionAdminGroupSuccess() thr public void collectionAdminAddMembersToCollectionAdminGroupPropertySetToFalse() throws Exception { context.turnOffAuthorisationSystem(); - Group adminGroup = collectionService.createAdministrators(context, collection); + Group adminGroup = GroupBuilder.createCollectionAdminGroup(context, collection) + .build(); authorizeService.addPolicy(context, collection, Constants.ADMIN, eperson); EPerson ePerson = EPersonBuilder.createEPerson(context).withEmail("testToAdd@test.com").build(); configurationService.setProperty("core.authorization.community-admin.collection.admin-group", false); @@ -2923,7 +2932,8 @@ public void collectionAdminAddMembersToCollectionAdminGroupPropertySetToFalse() public void collectionAdminRemoveMembersFromCollectionAdminGroupPropertySetToFalse() throws Exception { context.turnOffAuthorisationSystem(); - Group adminGroup = collectionService.createAdministrators(context, collection); + Group adminGroup = GroupBuilder.createCollectionAdminGroup(context, collection) + .build(); authorizeService.addPolicy(context, collection, Constants.ADMIN, eperson); EPerson ePerson = EPersonBuilder.createEPerson(context).withEmail("testToAdd@test.com").build(); context.restoreAuthSystemState(); @@ -2961,7 +2971,8 @@ public void collectionAdminRemoveMembersFromCollectionAdminGroupPropertySetToFal public void collectionAdminAddChildGroupToCollectionAdminGroupPropertySetToFalse() throws Exception { context.turnOffAuthorisationSystem(); - Group adminGroup = collectionService.createAdministrators(context, collection); + Group adminGroup = GroupBuilder.createCollectionAdminGroup(context, collection) + .build(); authorizeService.addPolicy(context, collection, Constants.ADMIN, eperson); Group group = GroupBuilder.createGroup(context).withName("testGroup").build(); configurationService.setProperty("core.authorization.community-admin.collection.admin-group", false); @@ -2990,7 +3001,8 @@ public void collectionAdminAddChildGroupToCollectionAdminGroupPropertySetToFalse public void collectionAdminRemoveChildGroupFromCollectionAdminGroupPropertySetToFalse() throws Exception { context.turnOffAuthorisationSystem(); - Group adminGroup = collectionService.createAdministrators(context, collection); + Group adminGroup = GroupBuilder.createCollectionAdminGroup(context, collection) + .build(); authorizeService.addPolicy(context, collection, Constants.ADMIN, eperson); Group group = GroupBuilder.createGroup(context).withName("testGroup").build(); context.restoreAuthSystemState(); From 690dfa0809dc0c1829898c5f2a4d8bdbc2de0095 Mon Sep 17 00:00:00 2001 From: Kim Shepherd Date: Fri, 6 Sep 2024 15:30:52 +0200 Subject: [PATCH 064/979] #9806: Tidy imports for GroupRestRepositoryIT (cherry picked from commit 80328eaca5f2ad36e006d32ea06aa3b89e2ada92) --- .../src/test/java/org/dspace/app/rest/GroupRestRepositoryIT.java | 1 - 1 file changed, 1 deletion(-) diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/GroupRestRepositoryIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/GroupRestRepositoryIT.java index 92e53d922d25..2d191b2fe7c7 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/GroupRestRepositoryIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/GroupRestRepositoryIT.java @@ -60,7 +60,6 @@ import org.dspace.content.Item; import org.dspace.content.MetadataSchemaEnum; import org.dspace.content.factory.ContentServiceFactory; -import org.dspace.content.service.CollectionService; import org.dspace.content.service.CommunityService; import org.dspace.core.Constants; import org.dspace.core.I18nUtil; From 67fa262b83cf89d3645959bd466b1efb5d8b3736 Mon Sep 17 00:00:00 2001 From: Kim Shepherd Date: Fri, 6 Sep 2024 15:31:31 +0200 Subject: [PATCH 065/979] #9806: Use builders for group, comm, coll creation in PackagerIT (cherry picked from commit 1f475aa731bbe42ad523f1056e6b56796ea8a2ee) --- .../src/test/java/org/dspace/app/packager/PackagerIT.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/dspace-api/src/test/java/org/dspace/app/packager/PackagerIT.java b/dspace-api/src/test/java/org/dspace/app/packager/PackagerIT.java index 2cddbb511f91..aeda97f818c2 100644 --- a/dspace-api/src/test/java/org/dspace/app/packager/PackagerIT.java +++ b/dspace-api/src/test/java/org/dspace/app/packager/PackagerIT.java @@ -24,6 +24,7 @@ import org.dspace.builder.CollectionBuilder; import org.dspace.builder.CommunityBuilder; import org.dspace.builder.ItemBuilder; +import org.dspace.builder.WorkspaceItemBuilder; import org.dspace.content.Collection; import org.dspace.content.Community; import org.dspace.content.Item; @@ -159,7 +160,7 @@ public void packagerUUIDAlreadyExistWithoutForceTest() throws Exception { performExportScript(article.getHandle(), tempFile); UUID id = article.getID(); itemService.delete(context, article); - WorkspaceItem workspaceItem = workspaceItemService.create(context, col1, id, false, false); + WorkspaceItem workspaceItem = WorkspaceItemBuilder.createWorkspaceItem(context, col1, id).build(); installItemService.installItem(context, workspaceItem, "123456789/0100"); performImportNoForceScript(tempFile); Iterator items = itemService.findByCollection(context, col1); From 8d576f83002d9a2f7df39717d2af61f744b9d78c Mon Sep 17 00:00:00 2001 From: Kim Shepherd Date: Fri, 6 Sep 2024 15:32:08 +0200 Subject: [PATCH 066/979] #9806: Use builders for group, comm, coll creation in StructBuilderIT (cherry picked from commit 2ef69045d15ae49a4d447227e8080860eecc61cd) --- .../dspace/administer/StructBuilderIT.java | 29 +++++++++---------- 1 file changed, 13 insertions(+), 16 deletions(-) diff --git a/dspace-api/src/test/java/org/dspace/administer/StructBuilderIT.java b/dspace-api/src/test/java/org/dspace/administer/StructBuilderIT.java index 63340698ac00..ead338bc8e70 100644 --- a/dspace-api/src/test/java/org/dspace/administer/StructBuilderIT.java +++ b/dspace-api/src/test/java/org/dspace/administer/StructBuilderIT.java @@ -23,11 +23,12 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import org.dspace.AbstractIntegrationTest; +import org.dspace.AbstractIntegrationTestWithDatabase; import org.dspace.authorize.AuthorizeException; +import org.dspace.builder.CollectionBuilder; +import org.dspace.builder.CommunityBuilder; import org.dspace.content.Collection; import org.dspace.content.Community; -import org.dspace.content.MetadataSchemaEnum; import org.dspace.content.factory.ContentServiceFactory; import org.dspace.content.service.CollectionService; import org.dspace.content.service.CommunityService; @@ -38,7 +39,6 @@ import org.junit.Test; import org.w3c.dom.Attr; import org.w3c.dom.Node; -import org.xml.sax.SAXException; import org.xmlunit.builder.DiffBuilder; import org.xmlunit.diff.Comparison; import org.xmlunit.diff.ComparisonFormatter; @@ -52,7 +52,7 @@ * @author Mark H. Wood */ public class StructBuilderIT - extends AbstractIntegrationTest { + extends AbstractIntegrationTestWithDatabase { private static final Logger log = LogManager.getLogger(); private static final CommunityService communityService @@ -79,7 +79,8 @@ public static void tearDownClass() { * @throws IOException passed through. */ @Before - public void setUp() throws SQLException, AuthorizeException, IOException { + public void setUp() throws Exception { + super.setUp(); // Clear out all communities and collections. context.turnOffAuthorisationSystem(); for (Community community : communityService.findAllTop(context)) { @@ -285,19 +286,15 @@ public void testImportStructureWithHandles() * @throws org.dspace.authorize.AuthorizeException passed through. */ @Test - public void testExportStructure() - throws ParserConfigurationException, SAXException, IOException, - SQLException, AuthorizeException { + public void testExportStructure() { // Create some structure to test. context.turnOffAuthorisationSystem(); - Community community0 = communityService.create(null, context); - communityService.setMetadataSingleValue(context, community0, - MetadataSchemaEnum.DC.getName(), "title", null, - null, "Top Community 0"); - Collection collection0_0 = collectionService.create(context, community0); - collectionService.setMetadataSingleValue(context, collection0_0, - MetadataSchemaEnum.DC.getName(), "title", null, - null, "Collection 0.0"); + // Top level community + Community community0 = CommunityBuilder.createCommunity(context) + .withName("Top Community 0").build(); + // Collection below top level community + Collection collection0_0 = CollectionBuilder.createCollection(context, community0) + .withName("Collection 0.0").build(); // Export the current structure. System.out.println("exportStructure"); From ffc807205f8faecf380c99c7cc43479a5d474803 Mon Sep 17 00:00:00 2001 From: Kim Shepherd Date: Fri, 6 Sep 2024 15:33:30 +0200 Subject: [PATCH 067/979] #9806: Refactor WorkspaceItemBuilder to support specific item uuid (cherry picked from commit b99b1eec29c5a0b6e92998b072282b03cfedc080) --- .../dspace/builder/WorkspaceItemBuilder.java | 24 ++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/dspace-api/src/test/java/org/dspace/builder/WorkspaceItemBuilder.java b/dspace-api/src/test/java/org/dspace/builder/WorkspaceItemBuilder.java index 9d786d4761f0..06bdb82bd76e 100644 --- a/dspace-api/src/test/java/org/dspace/builder/WorkspaceItemBuilder.java +++ b/dspace-api/src/test/java/org/dspace/builder/WorkspaceItemBuilder.java @@ -10,6 +10,7 @@ import java.io.IOException; import java.io.InputStream; import java.sql.SQLException; +import java.util.UUID; import org.dspace.authorize.AuthorizeException; import org.dspace.content.Bitstream; @@ -41,14 +42,31 @@ protected WorkspaceItemBuilder(Context context) { public static WorkspaceItemBuilder createWorkspaceItem(final Context context, final Collection col) { WorkspaceItemBuilder builder = new WorkspaceItemBuilder(context); - return builder.create(context, col); + return builder.create(context, col, null); } - private WorkspaceItemBuilder create(final Context context, final Collection col) { + public static WorkspaceItemBuilder createWorkspaceItem(final Context context, final Collection col, UUID uuid) { + WorkspaceItemBuilder builder = new WorkspaceItemBuilder(context); + return builder.create(context, col, uuid); + } + + /** + * Create with a specific UUID (e.g. restoring items with Packager import) + * + * @param context DSpace context + * @param col Parent collection + * @param uuid Item UUID + * @return WorkspaceItemBuilder + */ + private WorkspaceItemBuilder create(final Context context, final Collection col, UUID uuid) { this.context = context; try { - workspaceItem = workspaceItemService.create(context, col, false); + if (uuid == null) { + workspaceItem = workspaceItemService.create(context, col, false); + } else { + workspaceItem = workspaceItemService.create(context, col, uuid, false, false); + } item = workspaceItem.getItem(); } catch (Exception e) { return handleException(e); From b9278cbf33bf5fa9efd607bd1976fda33b03ce46 Mon Sep 17 00:00:00 2001 From: Kim Shepherd Date: Fri, 6 Sep 2024 15:34:05 +0200 Subject: [PATCH 068/979] #9806: Use builders for comm, coll, group creation in SupervisionOrderServiceIT (cherry picked from commit 6e9181e3f7cd502b345af7d5879fa25654176c25) --- .../SupervisionOrderServiceIT.java | 32 ++++++++++++------- 1 file changed, 21 insertions(+), 11 deletions(-) diff --git a/dspace-api/src/test/java/org/dspace/supervision/SupervisionOrderServiceIT.java b/dspace-api/src/test/java/org/dspace/supervision/SupervisionOrderServiceIT.java index 60407823485b..aa4cd8bd4e49 100644 --- a/dspace-api/src/test/java/org/dspace/supervision/SupervisionOrderServiceIT.java +++ b/dspace-api/src/test/java/org/dspace/supervision/SupervisionOrderServiceIT.java @@ -18,6 +18,7 @@ import org.dspace.builder.CommunityBuilder; import org.dspace.builder.EPersonBuilder; import org.dspace.builder.GroupBuilder; +import org.dspace.builder.SupervisionOrderBuilder; import org.dspace.builder.WorkspaceItemBuilder; import org.dspace.content.Collection; import org.dspace.content.Item; @@ -85,10 +86,10 @@ public void createSupervisionOrderTest() throws Exception { .build(); SupervisionOrder supervisionOrderOne = - supervisionOrderService.create(context, item, groupA); + SupervisionOrderBuilder.createSupervisionOrder(context, item, groupA).build(); SupervisionOrder supervisionOrderTwo = - supervisionOrderService.create(context, item, groupB); + SupervisionOrderBuilder.createSupervisionOrder(context, item, groupB).build(); context.restoreAuthSystemState(); @@ -136,7 +137,8 @@ public void findSupervisionOrderTest() throws Exception { .build(); SupervisionOrder supervisionOrderOne = - supervisionOrderService.create(context, workspaceItem.getItem(), groupA); + SupervisionOrderBuilder.createSupervisionOrder(context, workspaceItem.getItem(), groupA) + .build(); context.restoreAuthSystemState(); @@ -205,9 +207,12 @@ public void findAllSupervisionOrdersTest() throws Exception { .addMember(userB) .build(); - supervisionOrderService.create(context, workspaceItem.getItem(), groupA); - supervisionOrderService.create(context, workspaceItem.getItem(), groupB); - supervisionOrderService.create(context, workspaceItemTwo.getItem(), groupA); + SupervisionOrderBuilder.createSupervisionOrder(context, workspaceItem.getItem(), groupA) + .build(); + SupervisionOrderBuilder.createSupervisionOrder(context, workspaceItem.getItem(), groupB) + .build(); + SupervisionOrderBuilder.createSupervisionOrder(context, workspaceItemTwo.getItem(), groupA) + .build(); context.restoreAuthSystemState(); @@ -259,9 +264,12 @@ public void findSupervisionOrderByItemTest() throws Exception { .addMember(eperson) .build(); - supervisionOrderService.create(context, workspaceItem.getItem(), groupA); - supervisionOrderService.create(context, workspaceItem.getItem(), groupB); - supervisionOrderService.create(context, workspaceItemTwo.getItem(), groupA); + SupervisionOrderBuilder.createSupervisionOrder(context, workspaceItem.getItem(), groupA) + .build(); + SupervisionOrderBuilder.createSupervisionOrder(context, workspaceItem.getItem(), groupB) + .build(); + SupervisionOrderBuilder.createSupervisionOrder(context, workspaceItemTwo.getItem(), groupA) + .build(); context.restoreAuthSystemState(); @@ -310,7 +318,8 @@ public void findSupervisionOrderByItemAndGroupTest() throws Exception { .addMember(eperson) .build(); - supervisionOrderService.create(context, item, groupA); + SupervisionOrderBuilder.createSupervisionOrder(context, item, groupA) + .build(); context.restoreAuthSystemState(); @@ -370,7 +379,8 @@ public void isSupervisorTest() throws Exception { .addMember(userB) .build(); - supervisionOrderService.create(context, workspaceItem.getItem(), groupA); + SupervisionOrderBuilder.createSupervisionOrder(context, workspaceItem.getItem(), groupA) + .build(); context.restoreAuthSystemState(); From 968559bbafcafc1c6e67f03778e35094df648a21 Mon Sep 17 00:00:00 2001 From: Kim Shepherd Date: Fri, 6 Sep 2024 15:35:14 +0200 Subject: [PATCH 069/979] #9806: Use builders for comm, coll, group creation in CollectionGroupRestControllerIT (cherry picked from commit f4629d8351b70ca945ffeebdf928cebfb868c349) --- .../rest/CollectionGroupRestControllerIT.java | 151 +++++++++--------- 1 file changed, 77 insertions(+), 74 deletions(-) diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/CollectionGroupRestControllerIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/CollectionGroupRestControllerIT.java index f6ab10c087ad..8d490109220d 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/CollectionGroupRestControllerIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/CollectionGroupRestControllerIT.java @@ -28,9 +28,9 @@ import org.dspace.authorize.service.AuthorizeService; import org.dspace.builder.CollectionBuilder; import org.dspace.builder.CommunityBuilder; +import org.dspace.builder.GroupBuilder; import org.dspace.builder.WorkspaceItemBuilder; import org.dspace.content.Collection; -import org.dspace.content.service.CollectionService; import org.dspace.core.Constants; import org.dspace.eperson.Group; import org.dspace.eperson.service.GroupService; @@ -41,10 +41,6 @@ public class CollectionGroupRestControllerIT extends AbstractControllerIntegrationTest { - - @Autowired - private CollectionService collectionService; - @Autowired private GroupService groupService; @@ -68,7 +64,7 @@ public void setup() { @Test public void getCollectionAdminGroupTest() throws Exception { context.turnOffAuthorisationSystem(); - Group adminGroup = collectionService.createAdministrators(context, collection); + Group adminGroup = GroupBuilder.createCollectionAdminGroup(context, collection).build(); context.restoreAuthSystemState(); String token = getAuthToken(admin.getEmail(), password); @@ -81,7 +77,7 @@ public void getCollectionAdminGroupTest() throws Exception { @Test public void getCollectionAdminGroupTestParentCommunityAdmin() throws Exception { context.turnOffAuthorisationSystem(); - Group adminGroup = collectionService.createAdministrators(context, collection); + Group adminGroup = GroupBuilder.createCollectionAdminGroup(context, collection).build(); authorizeService.addPolicy(context, parentCommunity, Constants.ADMIN, eperson); context.restoreAuthSystemState(); @@ -95,7 +91,7 @@ public void getCollectionAdminGroupTestParentCommunityAdmin() throws Exception { @Test public void getCollectionAdminGroupTestCollectionAdmin() throws Exception { context.turnOffAuthorisationSystem(); - Group adminGroup = collectionService.createAdministrators(context, collection); + Group adminGroup = GroupBuilder.createCollectionAdminGroup(context, collection).build(); authorizeService.addPolicy(context, collection, Constants.ADMIN, eperson); context.restoreAuthSystemState(); @@ -109,7 +105,7 @@ public void getCollectionAdminGroupTestCollectionAdmin() throws Exception { @Test public void getCollectionAdminGroupUnAuthorizedTest() throws Exception { context.turnOffAuthorisationSystem(); - collectionService.createAdministrators(context, collection); + GroupBuilder.createCollectionAdminGroup(context, collection).build(); context.restoreAuthSystemState(); getClient().perform(get("/api/core/collections/" + collection.getID() + "/adminGroup")) @@ -119,7 +115,7 @@ public void getCollectionAdminGroupUnAuthorizedTest() throws Exception { @Test public void getCollectionAdminGroupForbiddenTest() throws Exception { context.turnOffAuthorisationSystem(); - collectionService.createAdministrators(context, collection); + GroupBuilder.createCollectionAdminGroup(context, collection).build(); context.restoreAuthSystemState(); String token = getAuthToken(eperson.getEmail(), password); @@ -413,7 +409,7 @@ public void postCollectionAdminGroupCreateAdminGroupUnProcessablePermanent() thr @Test public void deleteCollectionAdminGroupTest() throws Exception { context.turnOffAuthorisationSystem(); - Group adminGroup = collectionService.createAdministrators(context, collection); + GroupBuilder.createCollectionAdminGroup(context, collection).build(); context.restoreAuthSystemState(); String token = getAuthToken(admin.getEmail(), password); @@ -428,7 +424,7 @@ public void deleteCollectionAdminGroupTest() throws Exception { @Test public void deleteCollectionAdminGroupTestParentCommunityAdmin() throws Exception { context.turnOffAuthorisationSystem(); - Group adminGroup = collectionService.createAdministrators(context, collection); + GroupBuilder.createCollectionAdminGroup(context, collection).build(); authorizeService.addPolicy(context, parentCommunity, Constants.ADMIN, eperson); context.restoreAuthSystemState(); @@ -443,7 +439,7 @@ public void deleteCollectionAdminGroupTestParentCommunityAdmin() throws Exceptio @Test public void deleteCollectionAdminGroupTestCollectionAdmin() throws Exception { context.turnOffAuthorisationSystem(); - Group adminGroup = collectionService.createAdministrators(context, collection); + GroupBuilder.createCollectionAdminGroup(context, collection).build(); authorizeService.addPolicy(context, collection, Constants.ADMIN, eperson); context.restoreAuthSystemState(); @@ -458,7 +454,7 @@ public void deleteCollectionAdminGroupTestCollectionAdmin() throws Exception { @Test public void deleteCollectionAdminGroupUnAuthorizedTest() throws Exception { context.turnOffAuthorisationSystem(); - Group adminGroup = collectionService.createAdministrators(context, collection); + Group adminGroup = GroupBuilder.createCollectionAdminGroup(context, collection).build(); context.restoreAuthSystemState(); getClient().perform(delete("/api/core/collections/" + collection.getID() + "/adminGroup")) @@ -474,7 +470,7 @@ public void deleteCollectionAdminGroupUnAuthorizedTest() throws Exception { @Test public void deleteCollectionAdminGroupForbiddenTest() throws Exception { context.turnOffAuthorisationSystem(); - Group adminGroup = collectionService.createAdministrators(context, collection); + Group adminGroup = GroupBuilder.createCollectionAdminGroup(context, collection).build(); context.restoreAuthSystemState(); String token = getAuthToken(eperson.getEmail(), password); @@ -493,7 +489,7 @@ public void deleteCollectionAdminGroupForbiddenTest() throws Exception { @Test public void deleteCollectionAdminGroupNotFoundTest() throws Exception { context.turnOffAuthorisationSystem(); - Group adminGroup = collectionService.createAdministrators(context, collection); + Group adminGroup = GroupBuilder.createCollectionAdminGroup(context, collection).build(); context.restoreAuthSystemState(); String token = getAuthToken(eperson.getEmail(), password); @@ -512,7 +508,7 @@ public void deleteCollectionAdminGroupNotFoundTest() throws Exception { @Test public void getCollectionSubmittersGroupTest() throws Exception { context.turnOffAuthorisationSystem(); - Group submitters = collectionService.createSubmitters(context, collection); + Group submitters = GroupBuilder.createCollectionSubmitterGroup(context, collection).build(); context.restoreAuthSystemState(); String token = getAuthToken(admin.getEmail(), password); @@ -525,7 +521,7 @@ public void getCollectionSubmittersGroupTest() throws Exception { @Test public void getCollectionSubmittersGroupTestParentCommunityAdmin() throws Exception { context.turnOffAuthorisationSystem(); - Group submitters = collectionService.createSubmitters(context, collection); + Group submitters = GroupBuilder.createCollectionSubmitterGroup(context, collection).build(); authorizeService.addPolicy(context, parentCommunity, Constants.ADMIN, eperson); context.restoreAuthSystemState(); @@ -539,7 +535,7 @@ public void getCollectionSubmittersGroupTestParentCommunityAdmin() throws Except @Test public void getCollectionSubmittersGroupTestCollectionAdmin() throws Exception { context.turnOffAuthorisationSystem(); - Group submitters = collectionService.createSubmitters(context, collection); + Group submitters = GroupBuilder.createCollectionSubmitterGroup(context, collection).build(); authorizeService.addPolicy(context, collection, Constants.ADMIN, eperson); context.restoreAuthSystemState(); @@ -553,7 +549,7 @@ public void getCollectionSubmittersGroupTestCollectionAdmin() throws Exception { @Test public void getCollectionSubmittersGroupUnAuthorizedTest() throws Exception { context.turnOffAuthorisationSystem(); - collectionService.createSubmitters(context, collection); + GroupBuilder.createCollectionSubmitterGroup(context, collection).build(); context.restoreAuthSystemState(); getClient().perform(get("/api/core/collections/" + collection.getID() + "/submittersGroup")) @@ -563,7 +559,7 @@ public void getCollectionSubmittersGroupUnAuthorizedTest() throws Exception { @Test public void getCollectionSubmittersGroupForbiddenTest() throws Exception { context.turnOffAuthorisationSystem(); - collectionService.createSubmitters(context, collection); + GroupBuilder.createCollectionSubmitterGroup(context, collection).build(); context.restoreAuthSystemState(); String token = getAuthToken(eperson.getEmail(), password); @@ -860,7 +856,7 @@ public void postCollectionSubmittersGroupCreateSubmittersGroupUnProcessablePerma @Test public void deleteCollectionSubmitterGroupTest() throws Exception { context.turnOffAuthorisationSystem(); - Group submittersGroup = collectionService.createSubmitters(context, collection); + GroupBuilder.createCollectionSubmitterGroup(context, collection).build(); context.restoreAuthSystemState(); String token = getAuthToken(admin.getEmail(), password); @@ -875,7 +871,7 @@ public void deleteCollectionSubmitterGroupTest() throws Exception { @Test public void deleteCollectionSubmittersGroupTestParentCommunityAdmin() throws Exception { context.turnOffAuthorisationSystem(); - Group submittersGroup = collectionService.createSubmitters(context, collection); + GroupBuilder.createCollectionSubmitterGroup(context, collection).build(); authorizeService.addPolicy(context, parentCommunity, Constants.ADMIN, eperson); context.restoreAuthSystemState(); @@ -890,7 +886,7 @@ public void deleteCollectionSubmittersGroupTestParentCommunityAdmin() throws Exc @Test public void deleteCollectionSubmittersGroupTestCollectionAdmin() throws Exception { context.turnOffAuthorisationSystem(); - Group submittersGroup = collectionService.createSubmitters(context, collection); + GroupBuilder.createCollectionSubmitterGroup(context, collection).build(); authorizeService.addPolicy(context, collection, Constants.ADMIN, eperson); context.restoreAuthSystemState(); @@ -905,7 +901,7 @@ public void deleteCollectionSubmittersGroupTestCollectionAdmin() throws Exceptio @Test public void deleteCollectionSubmittersGroupUnAuthorizedTest() throws Exception { context.turnOffAuthorisationSystem(); - Group submittersGroup = collectionService.createSubmitters(context, collection); + Group submittersGroup = GroupBuilder.createCollectionSubmitterGroup(context, collection).build(); context.restoreAuthSystemState(); getClient().perform(delete("/api/core/collections/" + collection.getID() + "/submittersGroup")) @@ -924,7 +920,7 @@ public void deleteCollectionSubmittersGroupUnAuthorizedTest() throws Exception { @Test public void deleteCollectionSubmittersGroupForbiddenTest() throws Exception { context.turnOffAuthorisationSystem(); - Group submittersGroup = collectionService.createSubmitters(context, collection); + Group submittersGroup = GroupBuilder.createCollectionSubmitterGroup(context, collection).build(); context.restoreAuthSystemState(); String token = getAuthToken(eperson.getEmail(), password); @@ -945,7 +941,7 @@ public void deleteCollectionSubmittersGroupForbiddenTest() throws Exception { @Test public void deleteCollectionSubmittersGroupNotFoundTest() throws Exception { context.turnOffAuthorisationSystem(); - Group submittersGroup = collectionService.createSubmitters(context, collection); + GroupBuilder.createCollectionSubmitterGroup(context, collection).build(); context.restoreAuthSystemState(); String token = getAuthToken(eperson.getEmail(), password); @@ -961,7 +957,8 @@ public void getCollectionItemReadGroupTest() throws Exception { String itemGroupString = "ITEM"; int defaultItemRead = Constants.DEFAULT_ITEM_READ; - Group role = collectionService.createDefaultReadGroup(context, collection, itemGroupString, defaultItemRead); + Group role = GroupBuilder.createCollectionDefaultReadGroup(context, collection, + itemGroupString, defaultItemRead).build(); context.restoreAuthSystemState(); String token = getAuthToken(admin.getEmail(), password); @@ -977,7 +974,8 @@ public void getCollectionDefaultItemReadGroupTestParentCommunityAdmin() throws E String itemGroupString = "ITEM"; int defaultItemRead = Constants.DEFAULT_ITEM_READ; - Group role = collectionService.createDefaultReadGroup(context, collection, itemGroupString, defaultItemRead); + Group role = GroupBuilder.createCollectionDefaultReadGroup(context, collection, + itemGroupString, defaultItemRead).build(); authorizeService.addPolicy(context, parentCommunity, Constants.ADMIN, eperson); context.restoreAuthSystemState(); @@ -995,7 +993,8 @@ public void getCollectionDefaultItemReadGroupTestCollectionAdmin() throws Except String itemGroupString = "ITEM"; int defaultItemRead = Constants.DEFAULT_ITEM_READ; - Group role = collectionService.createDefaultReadGroup(context, collection, itemGroupString, defaultItemRead); + Group role = GroupBuilder.createCollectionDefaultReadGroup(context, collection, + itemGroupString, defaultItemRead).build(); authorizeService.addPolicy(context, collection, Constants.ADMIN, eperson); context.restoreAuthSystemState(); @@ -1012,7 +1011,8 @@ public void getCollectionDefaultItemReadGroupUnAuthorizedTest() throws Exception String itemGroupString = "ITEM"; int defaultItemRead = Constants.DEFAULT_ITEM_READ; - Group role = collectionService.createDefaultReadGroup(context, collection, itemGroupString, defaultItemRead); + GroupBuilder.createCollectionDefaultReadGroup(context, collection, + itemGroupString, defaultItemRead).build(); context.restoreAuthSystemState(); getClient().perform(get("/api/core/collections/" + collection.getID() + "/itemReadGroup")) @@ -1025,7 +1025,8 @@ public void getCollectionDefaultItemReadGroupForbiddenTest() throws Exception { String itemGroupString = "ITEM"; int defaultItemRead = Constants.DEFAULT_ITEM_READ; - Group role = collectionService.createDefaultReadGroup(context, collection, itemGroupString, defaultItemRead); + GroupBuilder.createCollectionDefaultReadGroup(context, collection, + itemGroupString, defaultItemRead).build(); context.restoreAuthSystemState(); String token = getAuthToken(eperson.getEmail(), password); @@ -1321,7 +1322,7 @@ public void deleteCollectionDefaultItemReadGroupTest() throws Exception { String itemGroupString = "ITEM"; int defaultItemRead = Constants.DEFAULT_ITEM_READ; - Group role = collectionService.createDefaultReadGroup(context, collection, itemGroupString, defaultItemRead); + GroupBuilder.createCollectionDefaultReadGroup(context, collection, itemGroupString, defaultItemRead).build(); context.restoreAuthSystemState(); String token = getAuthToken(admin.getEmail(), password); @@ -1345,7 +1346,7 @@ public void deleteCollectionDefaultItemReadGroupTestParentCommunityAdmin() throw String itemGroupString = "ITEM"; int defaultItemRead = Constants.DEFAULT_ITEM_READ; - Group role = collectionService.createDefaultReadGroup(context, collection, itemGroupString, defaultItemRead); + GroupBuilder.createCollectionDefaultReadGroup(context, collection, itemGroupString, defaultItemRead).build(); authorizeService.addPolicy(context, parentCommunity, Constants.ADMIN, eperson); context.restoreAuthSystemState(); @@ -1367,7 +1368,7 @@ public void deleteCollectionDefaultItemReadGroupTestCollectionAdmin() throws Exc String itemGroupString = "ITEM"; int defaultItemRead = Constants.DEFAULT_ITEM_READ; - Group role = collectionService.createDefaultReadGroup(context, collection, itemGroupString, defaultItemRead); + GroupBuilder.createCollectionDefaultReadGroup(context, collection, itemGroupString, defaultItemRead).build(); authorizeService.addPolicy(context, collection, Constants.ADMIN, eperson); context.restoreAuthSystemState(); @@ -1389,7 +1390,8 @@ public void deleteCollectionDefaultItemReadGroupUnAuthorizedTest() throws Except String itemGroupString = "ITEM"; int defaultItemRead = Constants.DEFAULT_ITEM_READ; - Group role = collectionService.createDefaultReadGroup(context, collection, itemGroupString, defaultItemRead); + Group role = GroupBuilder.createCollectionDefaultReadGroup(context, collection, + itemGroupString, defaultItemRead).build(); context.restoreAuthSystemState(); getClient().perform(delete("/api/core/collections/" + collection.getID() + "/itemReadGroup")) @@ -1408,7 +1410,8 @@ public void deleteCollectionDefaultItemReadGroupForbiddenTest() throws Exception String itemGroupString = "ITEM"; int defaultItemRead = Constants.DEFAULT_ITEM_READ; - Group role = collectionService.createDefaultReadGroup(context, collection, itemGroupString, defaultItemRead); + Group role = GroupBuilder.createCollectionDefaultReadGroup(context, collection, + itemGroupString, defaultItemRead).build(); context.restoreAuthSystemState(); String token = getAuthToken(eperson.getEmail(), password); @@ -1430,7 +1433,7 @@ public void deleteCollectionDefaultItemReadGroupNotFoundTest() throws Exception String itemGroupString = "ITEM"; int defaultItemRead = Constants.DEFAULT_ITEM_READ; - Group role = collectionService.createDefaultReadGroup(context, collection, itemGroupString, defaultItemRead); + GroupBuilder.createCollectionDefaultReadGroup(context, collection, itemGroupString, defaultItemRead).build(); context.restoreAuthSystemState(); String token = getAuthToken(eperson.getEmail(), password); @@ -1445,8 +1448,8 @@ public void getCollectionBitstreamReadGroupTest() throws Exception { String bitstreamGroupString = "BITSTREAM"; int defaultBitstreamRead = Constants.DEFAULT_BITSTREAM_READ; - Group role = collectionService.createDefaultReadGroup(context, collection, bitstreamGroupString, - defaultBitstreamRead); + Group role = GroupBuilder.createCollectionDefaultReadGroup(context, collection, + bitstreamGroupString, defaultBitstreamRead).build(); context.restoreAuthSystemState(); String token = getAuthToken(admin.getEmail(), password); @@ -1462,8 +1465,8 @@ public void getCollectionDefaultBitstreamReadGroupTestParentCommunityAdmin() thr String bitstreamGroupString = "BITSTREAM"; int defaultBitstreamRead = Constants.DEFAULT_BITSTREAM_READ; - Group role = collectionService.createDefaultReadGroup(context, collection, bitstreamGroupString, - defaultBitstreamRead); + Group role = GroupBuilder.createCollectionDefaultReadGroup(context, collection, + bitstreamGroupString, defaultBitstreamRead).build(); authorizeService.addPolicy(context, parentCommunity, Constants.ADMIN, eperson); context.restoreAuthSystemState(); @@ -1480,8 +1483,8 @@ public void getCollectionDefaultBitstreamReadGroupTestCollectionAdmin() throws E String bitstreamGroupString = "BITSTREAM"; int defaultBitstreamRead = Constants.DEFAULT_BITSTREAM_READ; - Group role = collectionService.createDefaultReadGroup(context, collection, bitstreamGroupString, - defaultBitstreamRead); + Group role = GroupBuilder.createCollectionDefaultReadGroup(context, collection, + bitstreamGroupString, defaultBitstreamRead).build(); authorizeService.addPolicy(context, collection, Constants.ADMIN, eperson); context.restoreAuthSystemState(); @@ -1498,8 +1501,8 @@ public void getCollectionDefaultBitstreamReadGroupUnAuthorizedTest() throws Exce String bitstreamGroupString = "BITSTREAM"; int defaultBitstreamRead = Constants.DEFAULT_BITSTREAM_READ; - Group role = collectionService.createDefaultReadGroup(context, collection, bitstreamGroupString, - defaultBitstreamRead); + GroupBuilder.createCollectionDefaultReadGroup(context, collection, + bitstreamGroupString, defaultBitstreamRead).build(); context.restoreAuthSystemState(); getClient().perform(get("/api/core/collections/" + collection.getID() + "/bitstreamReadGroup")) @@ -1512,8 +1515,8 @@ public void getCollectionDefaultBitstreamReadGroupForbiddenTest() throws Excepti String bitstreamGroupString = "BITSTREAM"; int defaultBitstreamRead = Constants.DEFAULT_BITSTREAM_READ; - Group role = collectionService.createDefaultReadGroup(context, collection, bitstreamGroupString, - defaultBitstreamRead); + GroupBuilder.createCollectionDefaultReadGroup(context, collection, + bitstreamGroupString, defaultBitstreamRead).build(); context.restoreAuthSystemState(); String token = getAuthToken(eperson.getEmail(), password); @@ -1811,8 +1814,8 @@ public void deleteCollectionDefaultBitstreamReadGroupTest() throws Exception { String bitstreamGroupString = "BITSTREAM"; int defaultBitstreamRead = Constants.DEFAULT_BITSTREAM_READ; - Group role = collectionService.createDefaultReadGroup(context, collection, bitstreamGroupString, - defaultBitstreamRead); + GroupBuilder.createCollectionDefaultReadGroup(context, collection, + bitstreamGroupString, defaultBitstreamRead).build(); context.restoreAuthSystemState(); String token = getAuthToken(admin.getEmail(), password); @@ -1835,8 +1838,8 @@ public void deleteCollectionDefaultBitstreamReadGroupTestParentCommunityAdmin() String bitstreamGroupString = "BITSTREAM"; int defaultBitstreamRead = Constants.DEFAULT_BITSTREAM_READ; - Group role = collectionService.createDefaultReadGroup(context, collection, bitstreamGroupString, - defaultBitstreamRead); + GroupBuilder.createCollectionDefaultReadGroup(context, collection, + bitstreamGroupString, defaultBitstreamRead).build(); authorizeService.addPolicy(context, parentCommunity, Constants.ADMIN, eperson); context.restoreAuthSystemState(); @@ -1859,8 +1862,8 @@ public void deleteCollectionDefaultBitstreamReadGroupTestCollectionAdmin() throw String bitstreamGroupString = "BITSTREAM"; int defaultBitstreamRead = Constants.DEFAULT_BITSTREAM_READ; - Group role = collectionService.createDefaultReadGroup(context, collection, bitstreamGroupString, - defaultBitstreamRead); + GroupBuilder.createCollectionDefaultReadGroup(context, collection, + bitstreamGroupString, defaultBitstreamRead).build(); authorizeService.addPolicy(context, collection, Constants.ADMIN, eperson); context.restoreAuthSystemState(); @@ -1882,8 +1885,8 @@ public void deleteCollectionDefaultBitstreamReadGroupUnAuthorizedTest() throws E String bitstreamGroupString = "BITSTREAM"; int defaultBitstreamRead = Constants.DEFAULT_BITSTREAM_READ; - Group role = collectionService.createDefaultReadGroup(context, collection, bitstreamGroupString, - defaultBitstreamRead); + Group role = GroupBuilder.createCollectionDefaultReadGroup(context, collection, + bitstreamGroupString, defaultBitstreamRead).build(); context.restoreAuthSystemState(); getClient().perform(delete("/api/core/collections/" + collection.getID() + "/bitstreamReadGroup")) @@ -1902,8 +1905,8 @@ public void deleteCollectionDefaultBitstreamReadGroupForbiddenTest() throws Exce String bitstreamGroupString = "BITSTREAM"; int defaultBitstreamRead = Constants.DEFAULT_BITSTREAM_READ; - Group role = collectionService.createDefaultReadGroup(context, collection, bitstreamGroupString, - defaultBitstreamRead); + GroupBuilder.createCollectionDefaultReadGroup(context, collection, + bitstreamGroupString, defaultBitstreamRead).build(); context.restoreAuthSystemState(); String token = getAuthToken(eperson.getEmail(), password); @@ -1918,8 +1921,8 @@ public void deleteCollectionDefaultBitstreamReadGroupNotFoundTest() throws Excep String bitstreamGroupString = "BITSTREAM"; int defaultBitstreamRead = Constants.DEFAULT_BITSTREAM_READ; - Group role = collectionService.createDefaultReadGroup(context, collection, bitstreamGroupString, - defaultBitstreamRead); + GroupBuilder.createCollectionDefaultReadGroup(context, collection, + bitstreamGroupString, defaultBitstreamRead).build(); context.restoreAuthSystemState(); String token = getAuthToken(eperson.getEmail(), password); @@ -1931,7 +1934,7 @@ public void deleteCollectionDefaultBitstreamReadGroupNotFoundTest() throws Excep public void getWorkflowGroupForCollectionAndRole() throws Exception { context.turnOffAuthorisationSystem(); - Group group = workflowService.createWorkflowRoleGroup(context, collection, "reviewer"); + Group group = GroupBuilder.createCollectionWorkflowRoleGroup(context, collection, "reviewer").build(); context.restoreAuthSystemState(); String token = getAuthToken(admin.getEmail(), password); @@ -1944,7 +1947,7 @@ public void getWorkflowGroupForCollectionAndRole() throws Exception { @Test public void getWorkflowGroupForCollectionAndRoleParentCommunityAdmin() throws Exception { context.turnOffAuthorisationSystem(); - Group group = workflowService.createWorkflowRoleGroup(context, collection, "reviewer"); + Group group = GroupBuilder.createCollectionWorkflowRoleGroup(context, collection, "reviewer").build(); authorizeService.addPolicy(context, parentCommunity, Constants.ADMIN, eperson); context.restoreAuthSystemState(); @@ -1958,7 +1961,7 @@ public void getWorkflowGroupForCollectionAndRoleParentCommunityAdmin() throws Ex @Test public void getWorkflowGroupForCollectionAndRoleWrongUUIDCollectionNotFound() throws Exception { context.turnOffAuthorisationSystem(); - Group group = workflowService.createWorkflowRoleGroup(context, collection, "reviewer"); + GroupBuilder.createCollectionWorkflowRoleGroup(context, collection, "reviewer").build(); context.restoreAuthSystemState(); String token = getAuthToken(eperson.getEmail(), password); @@ -1979,7 +1982,7 @@ public void getWorkflowGroupForCollectionAndRoleWrongRoleNotFound() throws Excep public void getWorkflowGroupCommunityAdmin() throws Exception { context.turnOffAuthorisationSystem(); - Group group = workflowService.createWorkflowRoleGroup(context, collection, "reviewer"); + Group group = GroupBuilder.createCollectionWorkflowRoleGroup(context, collection, "reviewer").build(); authorizeService.addPolicy(context, parentCommunity, Constants.ADMIN, eperson); context.restoreAuthSystemState(); @@ -1994,7 +1997,7 @@ public void getWorkflowGroupCommunityAdmin() throws Exception { @Test public void getWorkflowGroupCollectionAdmin() throws Exception { context.turnOffAuthorisationSystem(); - Group group = workflowService.createWorkflowRoleGroup(context, collection, "reviewer"); + Group group = GroupBuilder.createCollectionWorkflowRoleGroup(context, collection, "reviewer").build(); authorizeService.addPolicy(context, collection, Constants.ADMIN, eperson); context.restoreAuthSystemState(); @@ -2009,7 +2012,7 @@ public void getWorkflowGroupCollectionAdmin() throws Exception { @Test public void getWorkflowGroupUnAuthorized() throws Exception { context.turnOffAuthorisationSystem(); - Group group = workflowService.createWorkflowRoleGroup(context, collection, "reviewer"); + GroupBuilder.createCollectionWorkflowRoleGroup(context, collection, "reviewer").build(); context.restoreAuthSystemState(); getClient().perform(get("/api/core/collections/" + collection.getID() + "/workflowGroups/reviewer")) @@ -2019,7 +2022,7 @@ public void getWorkflowGroupUnAuthorized() throws Exception { @Test public void getWorkflowGroupForbidden() throws Exception { context.turnOffAuthorisationSystem(); - Group group = workflowService.createWorkflowRoleGroup(context, collection, "reviewer"); + GroupBuilder.createCollectionWorkflowRoleGroup(context, collection, "reviewer").build(); context.restoreAuthSystemState(); String token = getAuthToken(eperson.getEmail(), password); @@ -2324,7 +2327,7 @@ public void postCollectionWorkflowGroupCreateWorkflowGroupUnProcessablePermanent @Test public void deleteCollectionWorkflowGroupTest() throws Exception { context.turnOffAuthorisationSystem(); - Group group = workflowService.createWorkflowRoleGroup(context, collection, "reviewer"); + GroupBuilder.createCollectionWorkflowRoleGroup(context, collection, "reviewer").build(); context.restoreAuthSystemState(); String token = getAuthToken(admin.getEmail(), password); @@ -2339,7 +2342,7 @@ public void deleteCollectionWorkflowGroupTest() throws Exception { @Test public void deleteCollectionWorkflowGroupTestParentCommunityAdmin() throws Exception { context.turnOffAuthorisationSystem(); - Group group = workflowService.createWorkflowRoleGroup(context, collection, "reviewer"); + GroupBuilder.createCollectionWorkflowRoleGroup(context, collection, "reviewer").build(); authorizeService.addPolicy(context, parentCommunity, Constants.ADMIN, eperson); context.restoreAuthSystemState(); @@ -2355,7 +2358,7 @@ public void deleteCollectionWorkflowGroupTestParentCommunityAdmin() throws Excep @Test public void deleteCollectionWorkflowGroupTestCollectionAdmin() throws Exception { context.turnOffAuthorisationSystem(); - Group group = workflowService.createWorkflowRoleGroup(context, collection, "reviewer"); + GroupBuilder.createCollectionWorkflowRoleGroup(context, collection, "reviewer").build(); authorizeService.addPolicy(context, collection, Constants.ADMIN, eperson); context.restoreAuthSystemState(); @@ -2371,7 +2374,7 @@ public void deleteCollectionWorkflowGroupTestCollectionAdmin() throws Exception @Test public void deleteCollectionWorkflowGroupUnAuthorizedTest() throws Exception { context.turnOffAuthorisationSystem(); - Group group = workflowService.createWorkflowRoleGroup(context, collection, "reviewer"); + Group group = GroupBuilder.createCollectionWorkflowRoleGroup(context, collection, "reviewer").build(); context.restoreAuthSystemState(); getClient().perform(delete("/api/core/collections/" + collection.getID() + "/workflowGroups/reviewer")) @@ -2387,7 +2390,7 @@ public void deleteCollectionWorkflowGroupUnAuthorizedTest() throws Exception { @Test public void deleteCollectionWorkflowGroupForbiddenTest() throws Exception { context.turnOffAuthorisationSystem(); - Group group = workflowService.createWorkflowRoleGroup(context, collection, "reviewer"); + Group group = GroupBuilder.createCollectionWorkflowRoleGroup(context, collection, "reviewer").build(); context.restoreAuthSystemState(); String token = getAuthToken(eperson.getEmail(), password); @@ -2406,7 +2409,7 @@ public void deleteCollectionWorkflowGroupForbiddenTest() throws Exception { @Test public void deleteCollectionWorkflowGroupNotFoundTest() throws Exception { context.turnOffAuthorisationSystem(); - Group group = workflowService.createWorkflowRoleGroup(context, collection, "reviewer"); + GroupBuilder.createCollectionWorkflowRoleGroup(context, collection, "reviewer").build(); context.restoreAuthSystemState(); String token = getAuthToken(eperson.getEmail(), password); @@ -2418,7 +2421,7 @@ public void deleteCollectionWorkflowGroupNotFoundTest() throws Exception { @Test public void deleteCollectionWorkflowGroupWithPooledTaskTest() throws Exception { context.turnOffAuthorisationSystem(); - Group reviewer = workflowService.createWorkflowRoleGroup(context, collection, "reviewer"); + Group reviewer = GroupBuilder.createCollectionWorkflowRoleGroup(context, collection, "reviewer").build(); // Submit an Item into the workflow -> moves to the "reviewer" step's pool. // The role must have at least one EPerson, otherwise the WSI gets archived immediately From e6a0bb8943a1022f22e3fe2bc2aa6be51ed95a44 Mon Sep 17 00:00:00 2001 From: Kim Shepherd Date: Fri, 6 Sep 2024 15:35:43 +0200 Subject: [PATCH 070/979] #9806: Builders for comm, coll, group in CommunityAdminGroupRestControllerIT (cherry picked from commit 2d9988f77c52537f46888f27e35dbc0360299835) --- .../CommunityAdminGroupRestControllerIT.java | 52 +++++++++---------- 1 file changed, 24 insertions(+), 28 deletions(-) diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/CommunityAdminGroupRestControllerIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/CommunityAdminGroupRestControllerIT.java index 37548553b143..074a7e6a3557 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/CommunityAdminGroupRestControllerIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/CommunityAdminGroupRestControllerIT.java @@ -34,8 +34,6 @@ import org.dspace.builder.GroupBuilder; import org.dspace.content.Collection; import org.dspace.content.Community; -import org.dspace.content.service.CollectionService; -import org.dspace.content.service.CommunityService; import org.dspace.core.Constants; import org.dspace.eperson.EPerson; import org.dspace.eperson.Group; @@ -49,19 +47,12 @@ public class CommunityAdminGroupRestControllerIT extends AbstractControllerIntegrationTest { - - @Autowired - private CommunityService communityService; - @Autowired private GroupService groupService; @Autowired private AuthorizeService authorizeService; - @Autowired - private CollectionService collectionService; - @Autowired private ConfigurationService configurationService; @@ -78,7 +69,7 @@ public void setup() { @Test public void getCommunityAdminGroupTest() throws Exception { context.turnOffAuthorisationSystem(); - Group adminGroup = communityService.createAdministrators(context, parentCommunity); + Group adminGroup = GroupBuilder.createCommunityAdminGroup(context, parentCommunity).build(); context.restoreAuthSystemState(); String token = getAuthToken(admin.getEmail(), password); @@ -91,7 +82,8 @@ public void getCommunityAdminGroupTest() throws Exception { @Test public void getCommunityAdminGroupTestCommunityAdmin() throws Exception { context.turnOffAuthorisationSystem(); - Group adminGroup = communityService.createAdministrators(context, parentCommunity); + Group adminGroup = GroupBuilder.createCommunityAdminGroup(context, parentCommunity).build(); + // TODO: this should actually be "add member", not directly setting a policy, right? authorizeService.addPolicy(context, parentCommunity, Constants.ADMIN, eperson); context.restoreAuthSystemState(); @@ -106,7 +98,7 @@ public void getCommunityAdminGroupTestCommunityAdmin() throws Exception { @Test public void getCommunityAdminGroupUnAuthorizedTest() throws Exception { context.turnOffAuthorisationSystem(); - communityService.createAdministrators(context, parentCommunity); + GroupBuilder.createCommunityAdminGroup(context, parentCommunity).build(); context.restoreAuthSystemState(); getClient().perform(get("/api/core/communities/" + parentCommunity.getID() + "/adminGroup")) @@ -116,7 +108,7 @@ public void getCommunityAdminGroupUnAuthorizedTest() throws Exception { @Test public void getCommunityAdminGroupForbiddenTest() throws Exception { context.turnOffAuthorisationSystem(); - communityService.createAdministrators(context, parentCommunity); + GroupBuilder.createCommunityAdminGroup(context, parentCommunity).build(); context.restoreAuthSystemState(); String token = getAuthToken(eperson.getEmail(), password); getClient(token).perform(get("/api/core/communities/" + parentCommunity.getID() + "/adminGroup")) @@ -379,7 +371,7 @@ public void postCommunityAdminGroupCreateAdminGroupUnProcessablePermanent() thro @Test public void deleteCommunityAdminGroupTest() throws Exception { context.turnOffAuthorisationSystem(); - Group adminGroup = communityService.createAdministrators(context, parentCommunity); + GroupBuilder.createCommunityAdminGroup(context, parentCommunity).build(); context.restoreAuthSystemState(); String token = getAuthToken(admin.getEmail(), password); @@ -397,7 +389,7 @@ public void deleteCommunityAdminGroupTestCommunityAdmin() throws Exception { Community child1 = CommunityBuilder.createSubCommunity(context, parentCommunity) .withName("Sub Community") .build(); - Group adminGroup = communityService.createAdministrators(context, child1); + GroupBuilder.createCommunityAdminGroup(context, child1).build(); authorizeService.addPolicy(context, parentCommunity, Constants.ADMIN, eperson); context.restoreAuthSystemState(); @@ -412,7 +404,7 @@ public void deleteCommunityAdminGroupTestCommunityAdmin() throws Exception { @Test public void deleteCommunityAdminGroupUnAuthorizedTest() throws Exception { context.turnOffAuthorisationSystem(); - Group adminGroup = communityService.createAdministrators(context, parentCommunity); + Group adminGroup = GroupBuilder.createCommunityAdminGroup(context, parentCommunity).build(); context.restoreAuthSystemState(); getClient().perform(delete("/api/core/communities/" + parentCommunity.getID() + "/adminGroup")) @@ -429,7 +421,7 @@ public void deleteCommunityAdminGroupUnAuthorizedTest() throws Exception { @Test public void deleteCommunityAdminGroupForbiddenTest() throws Exception { context.turnOffAuthorisationSystem(); - Group adminGroup = communityService.createAdministrators(context, parentCommunity); + Group adminGroup = GroupBuilder.createCommunityAdminGroup(context, parentCommunity).build(); context.restoreAuthSystemState(); String token = getAuthToken(eperson.getEmail(), password); @@ -449,7 +441,7 @@ public void deleteCommunityAdminGroupForbiddenTest() throws Exception { @Test public void deleteCommunityAdminGroupNotFoundTest() throws Exception { context.turnOffAuthorisationSystem(); - Group adminGroup = communityService.createAdministrators(context, parentCommunity); + GroupBuilder.createCommunityAdminGroup(context, parentCommunity).build(); context.restoreAuthSystemState(); String token = getAuthToken(eperson.getEmail(), password); @@ -462,7 +454,7 @@ public void deleteCommunityAdminGroupNotFoundTest() throws Exception { public void communityAdminAddMembersToCommunityAdminGroupPropertySetToFalse() throws Exception { context.turnOffAuthorisationSystem(); - Group adminGroup = communityService.createAdministrators(context, parentCommunity); + Group adminGroup = GroupBuilder.createCommunityAdminGroup(context, parentCommunity).build(); authorizeService.addPolicy(context, parentCommunity, Constants.ADMIN, eperson); EPerson ePerson = EPersonBuilder.createEPerson(context).withEmail("testToAdd@test.com").build(); configurationService.setProperty("core.authorization.community-admin.admin-group", false); @@ -489,7 +481,7 @@ public void communityAdminAddMembersToCommunityAdminGroupPropertySetToFalse() th public void communityAdminRemoveMembersFromCommunityAdminGroupPropertySetToFalse() throws Exception { context.turnOffAuthorisationSystem(); - Group adminGroup = communityService.createAdministrators(context, parentCommunity); + Group adminGroup = GroupBuilder.createCommunityAdminGroup(context, parentCommunity).build(); authorizeService.addPolicy(context, parentCommunity, Constants.ADMIN, eperson); EPerson ePerson = EPersonBuilder.createEPerson(context).withEmail("testToAdd@test.com").build(); context.restoreAuthSystemState(); @@ -526,7 +518,7 @@ public void communityAdminRemoveMembersFromCommunityAdminGroupPropertySetToFalse public void communityAdminAddChildGroupToCommunityAdminGroupPropertySetToFalse() throws Exception { context.turnOffAuthorisationSystem(); - Group adminGroup = communityService.createAdministrators(context, parentCommunity); + Group adminGroup = GroupBuilder.createCommunityAdminGroup(context, parentCommunity).build(); authorizeService.addPolicy(context, parentCommunity, Constants.ADMIN, eperson); Group group = GroupBuilder.createGroup(context).withName("testGroup").build(); configurationService.setProperty("core.authorization.community-admin.admin-group", false); @@ -554,7 +546,7 @@ public void communityAdminAddChildGroupToCommunityAdminGroupPropertySetToFalse() public void communityAdminRemoveChildGroupFromCommunityAdminGroupPropertySetToFalse() throws Exception { context.turnOffAuthorisationSystem(); - Group adminGroup = communityService.createAdministrators(context, parentCommunity); + Group adminGroup = GroupBuilder.createCommunityAdminGroup(context, parentCommunity).build(); authorizeService.addPolicy(context, parentCommunity, Constants.ADMIN, eperson); Group group = GroupBuilder.createGroup(context).withName("testGroup").build(); context.restoreAuthSystemState(); @@ -591,7 +583,9 @@ public void communityAdminRemoveChildGroupFromCommunityAdminGroupPropertySetToFa public void communityAdminAddChildGroupToCollectionAdminGroupSuccess() throws Exception { context.turnOffAuthorisationSystem(); - Group adminGroup = collectionService.createAdministrators(context, collection); + // TODO: Why is this test in CommunityAdmin? it seems to purely be a collection group test? + // copy paste gone wrong and we should actually be testing for community admin group sub? + Group adminGroup = GroupBuilder.createCollectionAdminGroup(context, collection).build(); authorizeService.addPolicy(context, parentCommunity, Constants.ADMIN, eperson); Group group = GroupBuilder.createGroup(context).withName("testGroup").build(); context.restoreAuthSystemState(); @@ -617,7 +611,9 @@ public void communityAdminAddChildGroupToCollectionAdminGroupSuccess() throws Ex public void communityAdminRemoveChildGroupFromCollectionAdminGroupSuccess() throws Exception { context.turnOffAuthorisationSystem(); - Group adminGroup = collectionService.createAdministrators(context, collection); + // TODO: Why is this test in CommunityAdmin? it seems to purely be a collection group test? + // copy paste gone wrong and we should actually be testing for community admin group sub? + Group adminGroup = GroupBuilder.createCollectionAdminGroup(context, collection).build(); authorizeService.addPolicy(context, parentCommunity, Constants.ADMIN, eperson); Group group = GroupBuilder.createGroup(context).withName("testGroup").build(); context.restoreAuthSystemState(); @@ -653,7 +649,7 @@ public void communityAdminRemoveChildGroupFromCollectionAdminGroupSuccess() thro public void communityAdminAddMembersToCollectionAdminGroupPropertySetToFalse() throws Exception { context.turnOffAuthorisationSystem(); - Group adminGroup = collectionService.createAdministrators(context, collection); + Group adminGroup = GroupBuilder.createCollectionAdminGroup(context, collection).build(); authorizeService.addPolicy(context, parentCommunity, Constants.ADMIN, eperson); EPerson ePerson = EPersonBuilder.createEPerson(context).withEmail("testToAdd@test.com").build(); configurationService.setProperty("core.authorization.community-admin.collection.admin-group", false); @@ -681,7 +677,7 @@ public void communityAdminAddMembersToCollectionAdminGroupPropertySetToFalse() t public void communityAdminRemoveMembersFromCollectionAdminGroupPropertySetToFalse() throws Exception { context.turnOffAuthorisationSystem(); - Group adminGroup = collectionService.createAdministrators(context, collection); + Group adminGroup = GroupBuilder.createCollectionAdminGroup(context, collection).build(); authorizeService.addPolicy(context, parentCommunity, Constants.ADMIN, eperson); EPerson ePerson = EPersonBuilder.createEPerson(context).withEmail("testToAdd@test.com").build(); context.restoreAuthSystemState(); @@ -719,7 +715,7 @@ public void communityAdminRemoveMembersFromCollectionAdminGroupPropertySetToFals public void communityAdminAddChildGroupToCollectionAdminGroupPropertySetToFalse() throws Exception { context.turnOffAuthorisationSystem(); - Group adminGroup = collectionService.createAdministrators(context, collection); + Group adminGroup = GroupBuilder.createCollectionAdminGroup(context, collection).build(); authorizeService.addPolicy(context, parentCommunity, Constants.ADMIN, eperson); Group group = GroupBuilder.createGroup(context).withName("testGroup").build(); configurationService.setProperty("core.authorization.community-admin.collection.admin-group", false); @@ -748,7 +744,7 @@ public void communityAdminAddChildGroupToCollectionAdminGroupPropertySetToFalse( public void communityAdminRemoveChildGroupFromCollectionAdminGroupPropertySetToFalse() throws Exception { context.turnOffAuthorisationSystem(); - Group adminGroup = collectionService.createAdministrators(context, collection); + Group adminGroup = GroupBuilder.createCollectionAdminGroup(context, collection).build(); authorizeService.addPolicy(context, parentCommunity, Constants.ADMIN, eperson); Group group = GroupBuilder.createGroup(context).withName("testGroup").build(); context.restoreAuthSystemState(); From 5fd99d8b14f022c72508b34fe19957d1dd4896a4 Mon Sep 17 00:00:00 2001 From: Kim Shepherd Date: Fri, 6 Sep 2024 15:58:59 +0200 Subject: [PATCH 071/979] #9806: Update object cleanup in GroupRestRepositoryIT (cherry picked from commit 8cfb433c40ad01f348461a2c656665d1891de398) --- .../app/rest/GroupRestRepositoryIT.java | 952 +++++------------- 1 file changed, 239 insertions(+), 713 deletions(-) diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/GroupRestRepositoryIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/GroupRestRepositoryIT.java index 2d191b2fe7c7..a711c6c8be16 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/GroupRestRepositoryIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/GroupRestRepositoryIT.java @@ -59,14 +59,11 @@ import org.dspace.content.Community; import org.dspace.content.Item; import org.dspace.content.MetadataSchemaEnum; -import org.dspace.content.factory.ContentServiceFactory; -import org.dspace.content.service.CommunityService; import org.dspace.core.Constants; import org.dspace.core.I18nUtil; 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.hamcrest.Matchers; @@ -757,263 +754,136 @@ public void addChildGroupTest() throws Exception { @Test public void addChildGroupCommunityAdminTest() throws Exception { - CommunityService communityService = ContentServiceFactory.getInstance().getCommunityService(); GroupService groupService = EPersonServiceFactory.getInstance().getGroupService(); - Community community = null; - - Group parentGroup = null; - Group childGroup1 = null; - Group childGroup2 = null; - - try { - context.turnOffAuthorisationSystem(); - - community = CommunityBuilder.createCommunity(context).build(); - parentGroup = GroupBuilder.createCommunityAdminGroup(context, community) - .addMember(eperson) - .build(); - childGroup1 = GroupBuilder.createGroup(context).build(); - childGroup2 = GroupBuilder.createGroup(context).build(); - - groupService.update(context, parentGroup); - - context.commit(); - - parentGroup = context.reloadEntity(parentGroup); - childGroup1 = context.reloadEntity(childGroup1); - childGroup2 = context.reloadEntity(childGroup2); + context.turnOffAuthorisationSystem(); - context.restoreAuthSystemState(); - String authToken = getAuthToken(eperson.getEmail(), password); - getClient(authToken).perform( - post("/api/eperson/groups/" + parentGroup.getID() + "/subgroups") - .contentType(parseMediaType(TEXT_URI_LIST_VALUE)) - .content(REST_SERVER_URL + "eperson/groups/" + childGroup1.getID() + "/\n" - + REST_SERVER_URL + "eperson/groups/" + childGroup2.getID() - ) - ).andExpect(status().isNoContent()); + Community community = CommunityBuilder.createCommunity(context).build(); + Group parentGroup = GroupBuilder.createCommunityAdminGroup(context, community) + .addMember(eperson) + .build(); + Group childGroup1 = GroupBuilder.createGroup(context).build(); + Group childGroup2 = GroupBuilder.createGroup(context).build(); - parentGroup = context.reloadEntity(parentGroup); - childGroup1 = context.reloadEntity(childGroup1); - childGroup2 = context.reloadEntity(childGroup2); + context.restoreAuthSystemState(); + String authToken = getAuthToken(eperson.getEmail(), password); + getClient(authToken).perform( + post("/api/eperson/groups/" + parentGroup.getID() + "/subgroups") + .contentType(parseMediaType(TEXT_URI_LIST_VALUE)) + .content(REST_SERVER_URL + "eperson/groups/" + childGroup1.getID() + "/\n" + + REST_SERVER_URL + "eperson/groups/" + childGroup2.getID() + ) + ).andExpect(status().isNoContent()); - assertTrue( - groupService.isMember(parentGroup, childGroup1) - ); + parentGroup = context.reloadEntity(parentGroup); + childGroup1 = context.reloadEntity(childGroup1); + childGroup2 = context.reloadEntity(childGroup2); - assertTrue( - groupService.isMember(parentGroup, childGroup2) - ); + assertTrue( + groupService.isMember(parentGroup, childGroup1) + ); - } finally { - // TODO: Can we remove these lines now that we are creating them with the builder? - if (community != null) { - CommunityBuilder.deleteCommunity(community.getID()); - } - if (parentGroup != null) { - GroupBuilder.deleteGroup(parentGroup.getID()); - } - if (childGroup1 != null) { - GroupBuilder.deleteGroup(childGroup1.getID()); - } - if (childGroup2 != null) { - GroupBuilder.deleteGroup(childGroup2.getID()); - } - } + assertTrue( + groupService.isMember(parentGroup, childGroup2) + ); } @Test public void addChildGroupForbiddenTest() throws Exception { - GroupService groupService = EPersonServiceFactory.getInstance().getGroupService(); - - Group parentGroup = null; - Group childGroup1 = null; - Group childGroup2 = null; - - try { - context.turnOffAuthorisationSystem(); - - parentGroup = GroupBuilder.createGroup(context).build(); - childGroup1 = GroupBuilder.createGroup(context).build(); - childGroup2 = GroupBuilder.createGroup(context).build(); - - context.commit(); - - parentGroup = context.reloadEntity(parentGroup); - childGroup1 = context.reloadEntity(childGroup1); - childGroup2 = context.reloadEntity(childGroup2); + context.turnOffAuthorisationSystem(); - context.restoreAuthSystemState(); - String authToken = getAuthToken(eperson.getEmail(), password); - getClient(authToken).perform( - post("/api/eperson/groups/" + parentGroup.getID() + "/subgroups") - .contentType(parseMediaType(TEXT_URI_LIST_VALUE)) - .content(REST_SERVER_URL + "eperson/groups/" + childGroup1.getID() + "/\n" - + REST_SERVER_URL + "eperson/groups/" + childGroup2.getID() - ) - ).andExpect(status().isForbidden()); + Group parentGroup = GroupBuilder.createGroup(context).build(); + Group childGroup1 = GroupBuilder.createGroup(context).build(); + Group childGroup2 = GroupBuilder.createGroup(context).build(); - } finally { - if (parentGroup != null) { - GroupBuilder.deleteGroup(parentGroup.getID()); - } - if (childGroup1 != null) { - GroupBuilder.deleteGroup(childGroup1.getID()); - } - if (childGroup2 != null) { - GroupBuilder.deleteGroup(childGroup2.getID()); - } - } + context.restoreAuthSystemState(); + String authToken = getAuthToken(eperson.getEmail(), password); + getClient(authToken).perform( + post("/api/eperson/groups/" + parentGroup.getID() + "/subgroups") + .contentType(parseMediaType(TEXT_URI_LIST_VALUE)) + .content(REST_SERVER_URL + "eperson/groups/" + childGroup1.getID() + "/\n" + + REST_SERVER_URL + "eperson/groups/" + childGroup2.getID() + ) + ).andExpect(status().isForbidden()); } @Test public void addChildGroupUnauthorizedTest() throws Exception { - GroupService groupService = EPersonServiceFactory.getInstance().getGroupService(); - - Group parentGroup = null; - Group childGroup1 = null; - Group childGroup2 = null; - - try { - context.turnOffAuthorisationSystem(); - - parentGroup = GroupBuilder.createGroup(context).build(); - childGroup1 = GroupBuilder.createGroup(context).build(); - childGroup2 = GroupBuilder.createGroup(context).build(); + context.turnOffAuthorisationSystem(); - context.commit(); + Group parentGroup = GroupBuilder.createGroup(context).build(); + Group childGroup1 = GroupBuilder.createGroup(context).build(); + Group childGroup2 = GroupBuilder.createGroup(context).build(); - parentGroup = context.reloadEntity(parentGroup); - childGroup1 = context.reloadEntity(childGroup1); - childGroup2 = context.reloadEntity(childGroup2); + context.commit(); - context.restoreAuthSystemState(); - getClient().perform( - post("/api/eperson/groups/" + parentGroup.getID() + "/subgroups") - .contentType(parseMediaType(TEXT_URI_LIST_VALUE)) - .content(REST_SERVER_URL + "eperson/groups/" + childGroup1.getID() + "/\n" - + REST_SERVER_URL + "eperson/groups/" + childGroup2.getID() - ) - ).andExpect(status().isUnauthorized()); + parentGroup = context.reloadEntity(parentGroup); + childGroup1 = context.reloadEntity(childGroup1); + childGroup2 = context.reloadEntity(childGroup2); - } finally { - if (parentGroup != null) { - GroupBuilder.deleteGroup(parentGroup.getID()); - } - if (childGroup1 != null) { - GroupBuilder.deleteGroup(childGroup1.getID()); - } - if (childGroup2 != null) { - GroupBuilder.deleteGroup(childGroup2.getID()); - } - } + context.restoreAuthSystemState(); + getClient().perform( + post("/api/eperson/groups/" + parentGroup.getID() + "/subgroups") + .contentType(parseMediaType(TEXT_URI_LIST_VALUE)) + .content(REST_SERVER_URL + "eperson/groups/" + childGroup1.getID() + "/\n" + + REST_SERVER_URL + "eperson/groups/" + childGroup2.getID() + ) + ).andExpect(status().isUnauthorized()); } @Test public void addChildGroupNotFoundTest() throws Exception { - GroupService groupService = EPersonServiceFactory.getInstance().getGroupService(); - - Group parentGroup = null; - Group childGroup1 = null; - Group childGroup2 = null; - - try { - context.turnOffAuthorisationSystem(); - - parentGroup = GroupBuilder.createGroup(context).build(); - childGroup1 = GroupBuilder.createGroup(context).build(); - childGroup2 = GroupBuilder.createGroup(context).build(); - - context.commit(); - - parentGroup = context.reloadEntity(parentGroup); - childGroup1 = context.reloadEntity(childGroup1); - childGroup2 = context.reloadEntity(childGroup2); + context.turnOffAuthorisationSystem(); - context.restoreAuthSystemState(); - String authToken = getAuthToken(admin.getEmail(), password); - getClient(authToken).perform( - post("/api/eperson/groups/" + UUID.randomUUID() + "/subgroups") - .contentType(parseMediaType(TEXT_URI_LIST_VALUE)) - .content(REST_SERVER_URL + "eperson/groups/" + childGroup1.getID() + "/\n" - + REST_SERVER_URL + "eperson/groups/" + childGroup2.getID() - ) - ).andExpect(status().isNotFound()); + Group parentGroup = GroupBuilder.createGroup(context).build(); + Group childGroup1 = GroupBuilder.createGroup(context).build(); + Group childGroup2 = GroupBuilder.createGroup(context).build(); - } finally { - if (parentGroup != null) { - GroupBuilder.deleteGroup(parentGroup.getID()); - } - if (childGroup1 != null) { - GroupBuilder.deleteGroup(childGroup1.getID()); - } - if (childGroup2 != null) { - GroupBuilder.deleteGroup(childGroup2.getID()); - } - } + context.restoreAuthSystemState(); + String authToken = getAuthToken(admin.getEmail(), password); + getClient(authToken).perform( + post("/api/eperson/groups/" + UUID.randomUUID() + "/subgroups") + .contentType(parseMediaType(TEXT_URI_LIST_VALUE)) + .content(REST_SERVER_URL + "eperson/groups/" + childGroup1.getID() + "/\n" + + REST_SERVER_URL + "eperson/groups/" + childGroup2.getID() + ) + ).andExpect(status().isNotFound()); } @Test public void addChildGroupUnprocessableTest() throws Exception { - GroupService groupService = EPersonServiceFactory.getInstance().getGroupService(); - - Group parentGroup = null; - Group childGroup1 = null; - Group childGroup2 = null; - - try { - context.turnOffAuthorisationSystem(); - - parentGroup = GroupBuilder.createGroup(context).build(); - childGroup1 = GroupBuilder.createGroup(context) - .withParent(parentGroup) - .build(); - childGroup2 = GroupBuilder.createGroup(context).build(); - groupService.update(context, childGroup1); - -// context.commit(); -// -// parentGroup = context.reloadEntity(parentGroup); -// childGroup1 = context.reloadEntity(childGroup1); -// childGroup2 = context.reloadEntity(childGroup2); + context.turnOffAuthorisationSystem(); - context.restoreAuthSystemState(); - String authToken = getAuthToken(admin.getEmail(), password); + Group parentGroup = GroupBuilder.createGroup(context).build(); + Group childGroup1 = GroupBuilder.createGroup(context) + .withParent(parentGroup) + .build(); + Group childGroup2 = GroupBuilder.createGroup(context).build(); - getClient(authToken).perform( - post("/api/eperson/groups/" + parentGroup.getID() + "/subgroups") - .contentType(parseMediaType(TEXT_URI_LIST_VALUE)) - .content(REST_SERVER_URL + "eperson/groups/123456789\n" - + REST_SERVER_URL + "eperson/groups/" + childGroup2.getID() - ) - ).andExpect(status().isUnprocessableEntity()); + context.restoreAuthSystemState(); + String authToken = getAuthToken(admin.getEmail(), password); - // TODO - confirm with reviewers that this is a mistake - it actually should be No Content - // (see AddMember test) but was incorrectly expecting 422? - getClient(authToken).perform( - post("/api/eperson/groups/" + parentGroup.getID() + "/subgroups") - .contentType(parseMediaType(TEXT_URI_LIST_VALUE)) - .content(REST_SERVER_URL + "eperson/groups/" + childGroup1.getID() + "/\n" - + REST_SERVER_URL + "eperson/groups/" + childGroup2.getID() - ) - ).andExpect(status().isNoContent()); + getClient(authToken).perform( + post("/api/eperson/groups/" + parentGroup.getID() + "/subgroups") + .contentType(parseMediaType(TEXT_URI_LIST_VALUE)) + .content(REST_SERVER_URL + "eperson/groups/123456789\n" + + REST_SERVER_URL + "eperson/groups/" + childGroup2.getID() + ) + ).andExpect(status().isUnprocessableEntity()); - } finally { - if (parentGroup != null) { - GroupBuilder.deleteGroup(parentGroup.getID()); - } - if (childGroup1 != null) { - GroupBuilder.deleteGroup(childGroup1.getID()); - } - if (childGroup2 != null) { - GroupBuilder.deleteGroup(childGroup2.getID()); - } - } + // TODO - confirm with reviewers that this is a mistake - it actually should be No Content + // (see AddMember test) but was incorrectly expecting 422? + getClient(authToken).perform( + post("/api/eperson/groups/" + parentGroup.getID() + "/subgroups") + .contentType(parseMediaType(TEXT_URI_LIST_VALUE)) + .content(REST_SERVER_URL + "eperson/groups/" + childGroup1.getID() + "/\n" + + REST_SERVER_URL + "eperson/groups/" + childGroup2.getID() + ) + ).andExpect(status().isNoContent()); } @Test @@ -1081,251 +951,118 @@ public void addMemberTest() throws Exception { @Test public void addMemberCommunityAdminTest() throws Exception { - CommunityService communityService = ContentServiceFactory.getInstance().getCommunityService(); GroupService groupService = EPersonServiceFactory.getInstance().getGroupService(); - EPersonService ePersonService = EPersonServiceFactory.getInstance().getEPersonService(); - - Community community = null; - Group parentGroup = null; - EPerson member1 = null; - EPerson member2 = null; - - try { - context.turnOffAuthorisationSystem(); - - community = CommunityBuilder.createCommunity(context).build(); - parentGroup = GroupBuilder.createCommunityAdminGroup(context, community) - .addMember(eperson) - .build(); - member1 = EPersonBuilder.createEPerson(context).build(); - member2 = EPersonBuilder.createEPerson(context).build(); - context.commit(); - - parentGroup = context.reloadEntity(parentGroup); - member1 = context.reloadEntity(member1); - member2 = context.reloadEntity(member2); + context.turnOffAuthorisationSystem(); - context.restoreAuthSystemState(); - String authToken = getAuthToken(eperson.getEmail(), password); - getClient(authToken).perform( - post("/api/eperson/groups/" + parentGroup.getID() + "/epersons") - .contentType(parseMediaType(TEXT_URI_LIST_VALUE)) - .content(REST_SERVER_URL + "eperson/groups/" + member1.getID() + "/\n" - + REST_SERVER_URL + "eperson/groups/" + member2.getID() - ) - ).andExpect(status().isNoContent()); + Community community = CommunityBuilder.createCommunity(context).build(); + Group parentGroup = GroupBuilder.createCommunityAdminGroup(context, community) + .addMember(eperson) + .build(); + EPerson member1 = EPersonBuilder.createEPerson(context).build(); + EPerson member2 = EPersonBuilder.createEPerson(context).build(); - parentGroup = context.reloadEntity(parentGroup); - member1 = context.reloadEntity(member1); - member2 = context.reloadEntity(member2); + context.restoreAuthSystemState(); + String authToken = getAuthToken(eperson.getEmail(), password); + getClient(authToken).perform( + post("/api/eperson/groups/" + parentGroup.getID() + "/epersons") + .contentType(parseMediaType(TEXT_URI_LIST_VALUE)) + .content(REST_SERVER_URL + "eperson/groups/" + member1.getID() + "/\n" + + REST_SERVER_URL + "eperson/groups/" + member2.getID() + ) + ).andExpect(status().isNoContent()); - assertTrue( - groupService.isMember(context, member1, parentGroup) - ); + parentGroup = context.reloadEntity(parentGroup); + member1 = context.reloadEntity(member1); + member2 = context.reloadEntity(member2); - assertTrue( - groupService.isMember(context, member2, parentGroup) - ); + assertTrue( + groupService.isMember(context, member1, parentGroup) + ); - } finally { - if (community != null) { - CommunityBuilder.deleteCommunity(community.getID()); - } - if (parentGroup != null) { - GroupBuilder.deleteGroup(parentGroup.getID()); - } - if (member1 != null) { - EPersonBuilder.deleteEPerson(member1.getID()); - } - if (member2 != null) { - EPersonBuilder.deleteEPerson(member2.getID()); - } - } + assertTrue( + groupService.isMember(context, member2, parentGroup) + ); } @Test public void addMemberForbiddenTest() throws Exception { - GroupService groupService = EPersonServiceFactory.getInstance().getGroupService(); - EPersonService ePersonService = EPersonServiceFactory.getInstance().getEPersonService(); - - Group parentGroup = null; - EPerson member1 = null; - EPerson member2 = null; - - try { - context.turnOffAuthorisationSystem(); - - parentGroup = GroupBuilder.createGroup(context).build(); - member1 = EPersonBuilder.createEPerson(context).build(); - member2 = EPersonBuilder.createEPerson(context).build(); - - context.commit(); - - parentGroup = context.reloadEntity(parentGroup); - member1 = context.reloadEntity(member1); - member2 = context.reloadEntity(member2); + context.turnOffAuthorisationSystem(); - context.restoreAuthSystemState(); - String authToken = getAuthToken(eperson.getEmail(), password); - getClient(authToken).perform( - post("/api/eperson/groups/" + parentGroup.getID() + "/epersons") - .contentType(parseMediaType(TEXT_URI_LIST_VALUE)) - .content(REST_SERVER_URL + "eperson/groups/" + member1.getID() + "/\n" - + REST_SERVER_URL + "eperson/groups/" + member2.getID() - ) - ).andExpect(status().isForbidden()); + Group parentGroup = GroupBuilder.createGroup(context).build(); + EPerson member1 = EPersonBuilder.createEPerson(context).build(); + EPerson member2 = EPersonBuilder.createEPerson(context).build(); - } finally { - if (parentGroup != null) { - GroupBuilder.deleteGroup(parentGroup.getID()); - } - if (member1 != null) { - EPersonBuilder.deleteEPerson(member1.getID()); - } - if (member2 != null) { - EPersonBuilder.deleteEPerson(member2.getID()); - } - } + context.restoreAuthSystemState(); + String authToken = getAuthToken(eperson.getEmail(), password); + getClient(authToken).perform( + post("/api/eperson/groups/" + parentGroup.getID() + "/epersons") + .contentType(parseMediaType(TEXT_URI_LIST_VALUE)) + .content(REST_SERVER_URL + "eperson/groups/" + member1.getID() + "/\n" + + REST_SERVER_URL + "eperson/groups/" + member2.getID() + ) + ).andExpect(status().isForbidden()); } @Test public void addMemberUnauthorizedTest() throws Exception { - GroupService groupService = EPersonServiceFactory.getInstance().getGroupService(); - EPersonService ePersonService = EPersonServiceFactory.getInstance().getEPersonService(); - - Group parentGroup = null; - EPerson member1 = null; - EPerson member2 = null; - - try { - context.turnOffAuthorisationSystem(); - - parentGroup = GroupBuilder.createGroup(context).build(); - member1 = EPersonBuilder.createEPerson(context).build(); - member2 = EPersonBuilder.createEPerson(context).build(); - - context.commit(); - - parentGroup = context.reloadEntity(parentGroup); - member1 = context.reloadEntity(member1); - member2 = context.reloadEntity(member2); + context.turnOffAuthorisationSystem(); - context.restoreAuthSystemState(); - getClient().perform( - post("/api/eperson/groups/" + parentGroup.getID() + "/epersons") - .contentType(parseMediaType(TEXT_URI_LIST_VALUE)) - .content(REST_SERVER_URL + "eperson/groups/" + member1.getID() + "/\n" - + REST_SERVER_URL + "eperson/groups/" + member2.getID() - ) - ).andExpect(status().isUnauthorized()); + Group parentGroup = GroupBuilder.createGroup(context).build(); + EPerson member1 = EPersonBuilder.createEPerson(context).build(); + EPerson member2 = EPersonBuilder.createEPerson(context).build(); - } finally { - if (parentGroup != null) { - GroupBuilder.deleteGroup(parentGroup.getID()); - } - if (member1 != null) { - EPersonBuilder.deleteEPerson(member1.getID()); - } - if (member2 != null) { - EPersonBuilder.deleteEPerson(member2.getID()); - } - } + context.restoreAuthSystemState(); + getClient().perform( + post("/api/eperson/groups/" + parentGroup.getID() + "/epersons") + .contentType(parseMediaType(TEXT_URI_LIST_VALUE)) + .content(REST_SERVER_URL + "eperson/groups/" + member1.getID() + "/\n" + + REST_SERVER_URL + "eperson/groups/" + member2.getID() + ) + ).andExpect(status().isUnauthorized()); } @Test public void addMemberNotFoundTest() throws Exception { - GroupService groupService = EPersonServiceFactory.getInstance().getGroupService(); - EPersonService ePersonService = EPersonServiceFactory.getInstance().getEPersonService(); - - Group parentGroup = null; - EPerson member1 = null; - EPerson member2 = null; - - try { - context.turnOffAuthorisationSystem(); - - parentGroup = GroupBuilder.createGroup(context).build(); - member1 = EPersonBuilder.createEPerson(context).build(); - member2 = EPersonBuilder.createEPerson(context).build(); - - context.commit(); - - parentGroup = context.reloadEntity(parentGroup); - member1 = context.reloadEntity(member1); - member2 = context.reloadEntity(member2); + context.turnOffAuthorisationSystem(); - context.restoreAuthSystemState(); - String authToken = getAuthToken(admin.getEmail(), password); - getClient(authToken).perform( - post("/api/eperson/groups/" + UUID.randomUUID() + "/epersons") - .contentType(parseMediaType(TEXT_URI_LIST_VALUE)) - .content(REST_SERVER_URL + "eperson/groups/" + member1.getID() + "/\n" - + REST_SERVER_URL + "eperson/groups/" + member2.getID() - ) - ).andExpect(status().isNotFound()); + Group parentGroup = GroupBuilder.createGroup(context).build(); + EPerson member1 = EPersonBuilder.createEPerson(context).build(); + EPerson member2 = EPersonBuilder.createEPerson(context).build(); - } finally { - if (parentGroup != null) { - GroupBuilder.deleteGroup(parentGroup.getID()); - } - if (member1 != null) { - EPersonBuilder.deleteEPerson(member1.getID()); - } - if (member2 != null) { - EPersonBuilder.deleteEPerson(member2.getID()); - } - } + context.restoreAuthSystemState(); + String authToken = getAuthToken(admin.getEmail(), password); + getClient(authToken).perform( + post("/api/eperson/groups/" + UUID.randomUUID() + "/epersons") + .contentType(parseMediaType(TEXT_URI_LIST_VALUE)) + .content(REST_SERVER_URL + "eperson/groups/" + member1.getID() + "/\n" + + REST_SERVER_URL + "eperson/groups/" + member2.getID() + ) + ).andExpect(status().isNotFound()); } @Test public void addMemberUnprocessableTest() throws Exception { - GroupService groupService = EPersonServiceFactory.getInstance().getGroupService(); - EPersonService ePersonService = EPersonServiceFactory.getInstance().getEPersonService(); - - Group parentGroup = null; - EPerson member1 = null; - EPerson member2 = null; - - try { - context.turnOffAuthorisationSystem(); - - parentGroup = GroupBuilder.createGroup(context).build(); - member1 = EPersonBuilder.createEPerson(context).build(); - member2 = EPersonBuilder.createEPerson(context).build(); - - context.commit(); - - parentGroup = context.reloadEntity(parentGroup); - member1 = context.reloadEntity(member1); - member2 = context.reloadEntity(member2); + context.turnOffAuthorisationSystem(); - context.restoreAuthSystemState(); - String authToken = getAuthToken(admin.getEmail(), password); + Group parentGroup = GroupBuilder.createGroup(context).build(); + EPerson member1 = EPersonBuilder.createEPerson(context).build(); + EPerson member2 = EPersonBuilder.createEPerson(context).build(); - getClient(authToken).perform( - post("/api/eperson/groups/" + parentGroup.getID() + "/epersons") - .contentType(parseMediaType(TEXT_URI_LIST_VALUE)) - .content(REST_SERVER_URL + "eperson/groups/123456789\n" - + REST_SERVER_URL + "eperson/groups/" + member2.getID() - ) - ).andExpect(status().isUnprocessableEntity()); + context.restoreAuthSystemState(); + String authToken = getAuthToken(admin.getEmail(), password); - } finally { - if (parentGroup != null) { - GroupBuilder.deleteGroup(parentGroup.getID()); - } - if (member1 != null) { - EPersonBuilder.deleteEPerson(member1.getID()); - } - if (member2 != null) { - EPersonBuilder.deleteEPerson(member2.getID()); - } - } + getClient(authToken).perform( + post("/api/eperson/groups/" + parentGroup.getID() + "/epersons") + .contentType(parseMediaType(TEXT_URI_LIST_VALUE)) + .content(REST_SERVER_URL + "eperson/groups/123456789\n" + + REST_SERVER_URL + "eperson/groups/" + member2.getID() + ) + ).andExpect(status().isUnprocessableEntity()); } @Test @@ -1378,29 +1115,18 @@ public void removeChildGroupTest() throws Exception { @Test public void removeChildGroupCommunityAdminTest() throws Exception { - CommunityService communityService = ContentServiceFactory.getInstance().getCommunityService(); GroupService groupService = EPersonServiceFactory.getInstance().getGroupService(); - Community community = null; - Group parentGroup = null; - Group childGroup = null; - - try { context.turnOffAuthorisationSystem(); - community = CommunityBuilder.createCommunity(context).build(); - parentGroup = GroupBuilder.createCommunityAdminGroup(context, community) + Community community = CommunityBuilder.createCommunity(context).build(); + Group parentGroup = GroupBuilder.createCommunityAdminGroup(context, community) .addMember(eperson) .build(); - childGroup = GroupBuilder.createGroup(context) + Group childGroup = GroupBuilder.createGroup(context) .withParent(parentGroup) .build(); - context.commit(); - - parentGroup = context.reloadEntity(parentGroup); - childGroup = context.reloadEntity(childGroup); - context.restoreAuthSystemState(); String authToken = getAuthToken(eperson.getEmail(), password); getClient(authToken).perform( @@ -1413,163 +1139,71 @@ public void removeChildGroupCommunityAdminTest() throws Exception { assertFalse( groupService.isMember(parentGroup, childGroup) ); - - } finally { - if (community != null) { - CommunityBuilder.deleteCommunity(community.getID()); - } - if (parentGroup != null) { - GroupBuilder.deleteGroup(parentGroup.getID()); - } - if (childGroup != null) { - GroupBuilder.deleteGroup(childGroup.getID()); - } - } } @Test public void removeChildGroupForbiddenTest() throws Exception { - GroupService groupService = EPersonServiceFactory.getInstance().getGroupService(); - - Group parentGroup = null; - Group childGroup = null; - - try { - context.turnOffAuthorisationSystem(); - - parentGroup = GroupBuilder.createGroup(context).build(); - childGroup = GroupBuilder.createGroup(context).build(); - - context.commit(); - - parentGroup = context.reloadEntity(parentGroup); - childGroup = context.reloadEntity(childGroup); + context.turnOffAuthorisationSystem(); - context.restoreAuthSystemState(); - String authToken = getAuthToken(eperson.getEmail(), password); - getClient(authToken).perform( - delete("/api/eperson/groups/" + parentGroup.getID() + "/subgroups/" + childGroup.getID()) - ).andExpect(status().isForbidden()); + Group parentGroup = GroupBuilder.createGroup(context).build(); + Group childGroup = GroupBuilder.createGroup(context).build(); - } finally { - if (parentGroup != null) { - GroupBuilder.deleteGroup(parentGroup.getID()); - } - if (childGroup != null) { - GroupBuilder.deleteGroup(childGroup.getID()); - } - } + context.restoreAuthSystemState(); + String authToken = getAuthToken(eperson.getEmail(), password); + getClient(authToken).perform( + delete("/api/eperson/groups/" + parentGroup.getID() + "/subgroups/" + childGroup.getID()) + ).andExpect(status().isForbidden()); } @Test public void removeChildGroupUnauthorizedTest() throws Exception { - GroupService groupService = EPersonServiceFactory.getInstance().getGroupService(); - - Group parentGroup = null; - Group childGroup = null; - - try { - context.turnOffAuthorisationSystem(); - - parentGroup = GroupBuilder.createGroup(context).build(); - childGroup = GroupBuilder.createGroup(context).build(); - - context.commit(); - - parentGroup = context.reloadEntity(parentGroup); - childGroup = context.reloadEntity(childGroup); + context.turnOffAuthorisationSystem(); - context.restoreAuthSystemState(); - getClient().perform( - delete("/api/eperson/groups/" + parentGroup.getID() + "/subgroups/" + childGroup.getID()) - ).andExpect(status().isUnauthorized()); + Group parentGroup = GroupBuilder.createGroup(context).build(); + Group childGroup = GroupBuilder.createGroup(context).build(); - } finally { - if (parentGroup != null) { - GroupBuilder.deleteGroup(parentGroup.getID()); - } - if (childGroup != null) { - GroupBuilder.deleteGroup(childGroup.getID()); - } - } + context.restoreAuthSystemState(); + getClient().perform( + delete("/api/eperson/groups/" + parentGroup.getID() + "/subgroups/" + childGroup.getID()) + ).andExpect(status().isUnauthorized()); } @Test public void removeChildGroupNotFoundTest() throws Exception { - GroupService groupService = EPersonServiceFactory.getInstance().getGroupService(); - - Group parentGroup = null; - Group childGroup = null; - - try { - context.turnOffAuthorisationSystem(); - - parentGroup = GroupBuilder.createGroup(context).build(); - childGroup = GroupBuilder.createGroup(context) - .withParent(parentGroup) - .build(); - - context.commit(); - - parentGroup = context.reloadEntity(parentGroup); - childGroup = context.reloadEntity(childGroup); + context.turnOffAuthorisationSystem(); - context.restoreAuthSystemState(); - String authToken = getAuthToken(admin.getEmail(), password); + Group parentGroup = GroupBuilder.createGroup(context).build(); + Group childGroup = GroupBuilder.createGroup(context) + .withParent(parentGroup) + .build(); - getClient(authToken).perform( - delete("/api/eperson/groups/" + UUID.randomUUID() + "/subgroups/" + childGroup.getID()) - ).andExpect(status().isNotFound()); + context.restoreAuthSystemState(); + String authToken = getAuthToken(admin.getEmail(), password); - } finally { - if (parentGroup != null) { - GroupBuilder.deleteGroup(parentGroup.getID()); - } - if (childGroup != null) { - GroupBuilder.deleteGroup(childGroup.getID()); - } - } + getClient(authToken).perform( + delete("/api/eperson/groups/" + UUID.randomUUID() + "/subgroups/" + childGroup.getID()) + ).andExpect(status().isNotFound()); } @Test public void removeChildGroupUnprocessableTest() throws Exception { - GroupService groupService = EPersonServiceFactory.getInstance().getGroupService(); - - Group parentGroup = null; - Group childGroup = null; - - try { - context.turnOffAuthorisationSystem(); - - parentGroup = GroupBuilder.createGroup(context).build(); - childGroup = GroupBuilder.createGroup(context) - .withParent(parentGroup) - .build(); - - context.commit(); - - parentGroup = context.reloadEntity(parentGroup); - childGroup = context.reloadEntity(childGroup); + context.turnOffAuthorisationSystem(); - context.restoreAuthSystemState(); - String authToken = getAuthToken(admin.getEmail(), password); + Group parentGroup = GroupBuilder.createGroup(context).build(); + Group childGroup = GroupBuilder.createGroup(context) + .withParent(parentGroup) + .build(); - getClient(authToken).perform( - delete("/api/eperson/groups/" + parentGroup.getID() + "/subgroups/" + UUID.randomUUID()) - ).andExpect(status().isUnprocessableEntity()); + context.restoreAuthSystemState(); + String authToken = getAuthToken(admin.getEmail(), password); - } finally { - if (parentGroup != null) { - GroupBuilder.deleteGroup(parentGroup.getID()); - } - if (childGroup != null) { - GroupBuilder.deleteGroup(childGroup.getID()); - } - } + getClient(authToken).perform( + delete("/api/eperson/groups/" + parentGroup.getID() + "/subgroups/" + UUID.randomUUID()) + ).andExpect(status().isUnprocessableEntity()); } @Test @@ -1613,31 +1247,19 @@ public void removeMemberTest() throws Exception { @Test public void removeMemberCommunityAdminTest() throws Exception { - CommunityService communityService = ContentServiceFactory.getInstance().getCommunityService(); GroupService groupService = EPersonServiceFactory.getInstance().getGroupService(); - EPersonService ePersonService = EPersonServiceFactory.getInstance().getEPersonService(); - - Community community = null; - Group parentGroup = null; - EPerson member = null; - try { context.turnOffAuthorisationSystem(); - community = CommunityBuilder.createCommunity(context).build(); - member = EPersonBuilder.createEPerson(context).build(); - parentGroup = GroupBuilder.createCommunityAdminGroup(context, community) + Community community = CommunityBuilder.createCommunity(context).build(); + EPerson member = EPersonBuilder.createEPerson(context).build(); + Group parentGroup = GroupBuilder.createCommunityAdminGroup(context, community) .addMember(member) .addMember(eperson) .build(); assertTrue(groupService.isMember(context, member, parentGroup)); - context.commit(); - - parentGroup = context.reloadEntity(parentGroup); - member = context.reloadEntity(member); - context.restoreAuthSystemState(); String authToken = getAuthToken(eperson.getEmail(), password); getClient(authToken).perform( @@ -1650,171 +1272,75 @@ public void removeMemberCommunityAdminTest() throws Exception { assertFalse( groupService.isMember(context, member, parentGroup) ); - - } finally { - if (community != null) { - CommunityBuilder.deleteCommunity(community.getID()); - } - if (parentGroup != null) { - GroupBuilder.deleteGroup(parentGroup.getID()); - } - if (member != null) { - EPersonBuilder.deleteEPerson(member.getID()); - } - } } @Test public void removeMemberForbiddenTest() throws Exception { - GroupService groupService = EPersonServiceFactory.getInstance().getGroupService(); - EPersonService ePersonService = EPersonServiceFactory.getInstance().getEPersonService(); - - Group parentGroup = null; - EPerson member = null; - - try { - context.turnOffAuthorisationSystem(); - - member = EPersonBuilder.createEPerson(context).build(); - parentGroup = GroupBuilder.createGroup(context) - .addMember(member) - .build(); - - context.commit(); - - parentGroup = context.reloadEntity(parentGroup); - member = context.reloadEntity(member); + context.turnOffAuthorisationSystem(); - context.restoreAuthSystemState(); - String authToken = getAuthToken(eperson.getEmail(), password); - getClient(authToken).perform( - delete("/api/eperson/groups/" + parentGroup.getID() + "/epersons/" + member.getID()) - ).andExpect(status().isForbidden()); + EPerson member = EPersonBuilder.createEPerson(context).build(); + Group parentGroup = GroupBuilder.createGroup(context) + .addMember(member) + .build(); - } finally { - if (parentGroup != null) { - GroupBuilder.deleteGroup(parentGroup.getID()); - } - if (member != null) { - EPersonBuilder.deleteEPerson(member.getID()); - } - } + context.restoreAuthSystemState(); + String authToken = getAuthToken(eperson.getEmail(), password); + getClient(authToken).perform( + delete("/api/eperson/groups/" + parentGroup.getID() + "/epersons/" + member.getID()) + ).andExpect(status().isForbidden()); } @Test public void removeMemberUnauthorizedTest() throws Exception { - GroupService groupService = EPersonServiceFactory.getInstance().getGroupService(); - EPersonService ePersonService = EPersonServiceFactory.getInstance().getEPersonService(); - - Group parentGroup = null; - EPerson member = null; - - try { - context.turnOffAuthorisationSystem(); - - member = EPersonBuilder.createEPerson(context).build(); - parentGroup = GroupBuilder.createGroup(context) - .addMember(member) - .build(); - - context.commit(); - - parentGroup = context.reloadEntity(parentGroup); - member = context.reloadEntity(member); + context.turnOffAuthorisationSystem(); - context.restoreAuthSystemState(); - getClient().perform( - delete("/api/eperson/groups/" + parentGroup.getID() + "/epersons/" + member.getID()) - ).andExpect(status().isUnauthorized()); + EPerson member = EPersonBuilder.createEPerson(context).build(); + Group parentGroup = GroupBuilder.createGroup(context) + .addMember(member) + .build(); - } finally { - if (parentGroup != null) { - GroupBuilder.deleteGroup(parentGroup.getID()); - } - if (member != null) { - EPersonBuilder.deleteEPerson(member.getID()); - } - } + context.restoreAuthSystemState(); + getClient().perform( + delete("/api/eperson/groups/" + parentGroup.getID() + "/epersons/" + member.getID()) + ).andExpect(status().isUnauthorized()); } @Test public void removeMemberNotFoundTest() throws Exception { - GroupService groupService = EPersonServiceFactory.getInstance().getGroupService(); - EPersonService ePersonService = EPersonServiceFactory.getInstance().getEPersonService(); - - Group parentGroup = null; - EPerson member = null; - - try { - context.turnOffAuthorisationSystem(); - - member = EPersonBuilder.createEPerson(context).build(); - parentGroup = GroupBuilder.createGroup(context) - .addMember(member) - .build(); - - context.commit(); - - parentGroup = context.reloadEntity(parentGroup); - member = context.reloadEntity(member); + context.turnOffAuthorisationSystem(); - context.restoreAuthSystemState(); - String authToken = getAuthToken(admin.getEmail(), password); + EPerson member = EPersonBuilder.createEPerson(context).build(); + Group parentGroup = GroupBuilder.createGroup(context) + .addMember(member) + .build(); - getClient(authToken).perform( - delete("/api/eperson/groups/" + UUID.randomUUID() + "/epersons/" + member.getID()) - ).andExpect(status().isNotFound()); + context.restoreAuthSystemState(); + String authToken = getAuthToken(admin.getEmail(), password); - } finally { - if (parentGroup != null) { - GroupBuilder.deleteGroup(parentGroup.getID()); - } - if (member != null) { - EPersonBuilder.deleteEPerson(member.getID()); - } - } + getClient(authToken).perform( + delete("/api/eperson/groups/" + UUID.randomUUID() + "/epersons/" + member.getID()) + ).andExpect(status().isNotFound()); } @Test public void removeMemberUnprocessableTest() throws Exception { - GroupService groupService = EPersonServiceFactory.getInstance().getGroupService(); - EPersonService ePersonService = EPersonServiceFactory.getInstance().getEPersonService(); - - Group parentGroup = null; - EPerson member = null; - - try { context.turnOffAuthorisationSystem(); - member = EPersonBuilder.createEPerson(context).build(); - parentGroup = GroupBuilder.createGroup(context) + EPerson member = EPersonBuilder.createEPerson(context).build(); + Group parentGroup = GroupBuilder.createGroup(context) .addMember(member) .build(); - context.commit(); - - parentGroup = context.reloadEntity(parentGroup); - member = context.reloadEntity(member); - context.restoreAuthSystemState(); String authToken = getAuthToken(admin.getEmail(), password); getClient(authToken).perform( delete("/api/eperson/groups/" + parentGroup.getID() + "/epersons/" + UUID.randomUUID()) ).andExpect(status().isUnprocessableEntity()); - - } finally { - if (parentGroup != null) { - GroupBuilder.deleteGroup(parentGroup.getID()); - } - if (member != null) { - EPersonBuilder.deleteEPerson(member.getID()); - } - } } @Test From 282d4db36bebb3e159a09f222e2aa13160a99468 Mon Sep 17 00:00:00 2001 From: Kim Shepherd Date: Fri, 6 Sep 2024 16:43:36 +0200 Subject: [PATCH 072/979] #9806: Align provider reg in CreateMissingIdentifiersIT with other tests VersionedHandlerIdentifierProviderIT uses this registerProvider method which looks more reliable and doesn't do a refresh/reload of applicationContext after (which I suspected might have an odd interaction with VersioningWithRelationshipsIT and its createBean() calls?) (cherry picked from commit 90536e443b7fedef0481a34326cafc58fb334396) --- .../general/CreateMissingIdentifiersIT.java | 34 ++++++++++++++++--- 1 file changed, 29 insertions(+), 5 deletions(-) diff --git a/dspace-api/src/test/java/org/dspace/ctask/general/CreateMissingIdentifiersIT.java b/dspace-api/src/test/java/org/dspace/ctask/general/CreateMissingIdentifiersIT.java index 2a07799deee5..8038a7153325 100644 --- a/dspace-api/src/test/java/org/dspace/ctask/general/CreateMissingIdentifiersIT.java +++ b/dspace-api/src/test/java/org/dspace/ctask/general/CreateMissingIdentifiersIT.java @@ -10,6 +10,8 @@ import static org.junit.Assert.assertEquals; import java.io.IOException; +import java.util.ArrayList; +import java.util.List; import org.dspace.AbstractIntegrationTestWithDatabase; import org.dspace.builder.CollectionBuilder; @@ -19,7 +21,10 @@ import org.dspace.content.Item; import org.dspace.core.factory.CoreServiceFactory; import org.dspace.curate.Curator; +import org.dspace.identifier.IdentifierProvider; +import org.dspace.identifier.IdentifierServiceImpl; import org.dspace.identifier.VersionedHandleIdentifierProviderWithCanonicalHandles; +import org.dspace.kernel.ServiceManager; import org.dspace.services.ConfigurationService; import org.dspace.services.factory.DSpaceServicesFactory; import org.junit.After; @@ -32,10 +37,23 @@ */ public class CreateMissingIdentifiersIT extends AbstractIntegrationTestWithDatabase { + private ServiceManager serviceManager; + private IdentifierServiceImpl identifierService; private static final String P_TASK_DEF = "plugin.named.org.dspace.curate.CurationTask"; private static final String TASK_NAME = "test"; + @Override + public void setUp() throws Exception { + super.setUp(); + context.turnOffAuthorisationSystem(); + + serviceManager = DSpaceServicesFactory.getInstance().getServiceManager(); + identifierService = serviceManager.getServicesByType(IdentifierServiceImpl.class).get(0); + // Clean out providers to avoid any being used for creation of community and collection + identifierService.setProviders(new ArrayList<>()); + } + @Test public void testPerform() throws IOException { @@ -67,11 +85,7 @@ public void testPerform() /* * Now install an incompatible provider to make the task fail. */ - DSpaceServicesFactory.getInstance() - .getServiceManager() - .registerServiceClass( - VersionedHandleIdentifierProviderWithCanonicalHandles.class.getCanonicalName(), - VersionedHandleIdentifierProviderWithCanonicalHandles.class); + registerProvider(VersionedHandleIdentifierProviderWithCanonicalHandles.class); curator.curate(context, item); System.out.format("With incompatible provider, result is '%s'.\n", @@ -86,4 +100,14 @@ public void destroy() throws Exception { super.destroy(); DSpaceServicesFactory.getInstance().getServiceManager().getApplicationContext().refresh(); } + + private void registerProvider(Class type) { + // Register our new provider + serviceManager.registerServiceClass(type.getName(), type); + IdentifierProvider identifierProvider = + (IdentifierProvider) serviceManager.getServiceByName(type.getName(), type); + + // Overwrite the identifier-service's providers with the new one to ensure only this provider is used + identifierService.setProviders(List.of(identifierProvider)); + } } From 48c8848fc21ed739c99045b5e3e2b690297ba3da Mon Sep 17 00:00:00 2001 From: Kim Shepherd Date: Fri, 6 Sep 2024 16:45:05 +0200 Subject: [PATCH 073/979] #9806: Use builders for creation in VersioningWithRelationshipsIT I am a bit uncertain about the createBean() calls here, why do we not simply *get* the configured beans using the service manager instead, but will look at that in a separate change (cherry picked from commit 3521ab6d3598e716cc9fe8e938e883b302006580) --- .../VersioningWithRelationshipsIT.java | 33 +++++++++---------- 1 file changed, 15 insertions(+), 18 deletions(-) diff --git a/dspace-api/src/test/java/org/dspace/content/VersioningWithRelationshipsIT.java b/dspace-api/src/test/java/org/dspace/content/VersioningWithRelationshipsIT.java index 44653300e0de..accc52d0d830 100644 --- a/dspace-api/src/test/java/org/dspace/content/VersioningWithRelationshipsIT.java +++ b/dspace-api/src/test/java/org/dspace/content/VersioningWithRelationshipsIT.java @@ -49,6 +49,7 @@ import org.dspace.builder.ItemBuilder; import org.dspace.builder.RelationshipBuilder; import org.dspace.builder.RelationshipTypeBuilder; +import org.dspace.builder.VersionBuilder; import org.dspace.content.factory.ContentServiceFactory; import org.dspace.content.service.InstallItemService; import org.dspace.content.service.ItemService; @@ -62,8 +63,6 @@ import org.dspace.kernel.ServiceManager; import org.dspace.services.factory.DSpaceServicesFactory; import org.dspace.versioning.Version; -import org.dspace.versioning.factory.VersionServiceFactory; -import org.dspace.versioning.service.VersioningService; import org.hamcrest.Matcher; import org.junit.Assert; import org.junit.Before; @@ -74,8 +73,6 @@ public class VersioningWithRelationshipsIT extends AbstractIntegrationTestWithDa private final RelationshipService relationshipService = ContentServiceFactory.getInstance().getRelationshipService(); - private final VersioningService versioningService = - VersionServiceFactory.getInstance().getVersionService(); private final WorkspaceItemService workspaceItemService = ContentServiceFactory.getInstance().getWorkspaceItemService(); private final InstallItemService installItemService = @@ -291,7 +288,7 @@ public void test_createNewVersionOfItemOnLeftSideOfRelationships() throws Except // create a new version of the publication // ///////////////////////////////////////////// - Version newVersion = versioningService.createNewVersion(context, originalPublication); + Version newVersion = VersionBuilder.createVersion(context, originalPublication, "test").build(); Item newPublication = newVersion.getItem(); assertNotSame(originalPublication, newPublication); @@ -567,7 +564,7 @@ public void test_createNewVersionOfItemAndModifyRelationships() throws Exception // create a new version of the publication // ///////////////////////////////////////////// - Version newVersion = versioningService.createNewVersion(context, originalPublication); + Version newVersion = VersionBuilder.createVersion(context, originalPublication, "test").build(); Item newPublication = newVersion.getItem(); assertNotSame(originalPublication, newPublication); @@ -927,7 +924,7 @@ public void test_createNewVersionOfItemOnRightSideOfRelationships() throws Excep // create a new version of the person // //////////////////////////////////////// - Version newVersion = versioningService.createNewVersion(context, originalPerson); + Version newVersion = VersionBuilder.createVersion(context, originalPerson, "test").build(); Item newPerson = newVersion.getItem(); assertNotSame(originalPerson, newPerson); @@ -1300,7 +1297,7 @@ public void test_createNewVersionOfItemAndVerifyMetadataOrder() throws Exception // create new version of publication // /////////////////////////////////////// - Version newVersion = versioningService.createNewVersion(context, originalPublication); + Version newVersion = VersionBuilder.createVersion(context, originalPublication, "test").build(); Item newPublication = newVersion.getItem(); assertNotSame(originalPublication, newPublication); @@ -1463,7 +1460,7 @@ public void test_createNewVersionOfItemWithAddRemoveMove() throws Exception { // create a new version of the publication // ///////////////////////////////////////////// - Version newVersion = versioningService.createNewVersion(context, originalPublication); + Version newVersion = VersionBuilder.createVersion(context, originalPublication, "test").build(); Item newPublication = newVersion.getItem(); assertNotSame(originalPublication, newPublication); @@ -1782,7 +1779,7 @@ public void test_placeRecalculationAfterDelete() throws Exception { // create new version - volume 1.2 // ///////////////////////////////////// - Item v1_2 = versioningService.createNewVersion(context, v1_1).getItem(); + Item v1_2 = VersionBuilder.createVersion(context, v1_1, "test").build().getItem(); installItemService.installItem(context, workspaceItemService.findByItem(context, v1_2)); context.commit(); @@ -1790,7 +1787,7 @@ public void test_placeRecalculationAfterDelete() throws Exception { // create new version - issue 3.2 // //////////////////////////////////// - Item i3_2 = versioningService.createNewVersion(context, i3_1).getItem(); + Item i3_2 = VersionBuilder.createVersion(context, i3_1, "test").build().getItem(); installItemService.installItem(context, workspaceItemService.findByItem(context, i3_2)); context.commit(); @@ -2316,7 +2313,7 @@ public void test_placeRecalculationAfterDelete_complex() throws Exception { // create new version - person 3.2 // ///////////////////////////////////// - Item pe3_2 = versioningService.createNewVersion(context, pe3_1).getItem(); + Item pe3_2 = VersionBuilder.createVersion(context, pe3_1, "test").build().getItem(); installItemService.installItem(context, workspaceItemService.findByItem(context, pe3_2)); context.commit(); @@ -2324,7 +2321,7 @@ public void test_placeRecalculationAfterDelete_complex() throws Exception { // create new version - project 3.2 // ////////////////////////////////////// - Item pr3_2 = versioningService.createNewVersion(context, pr3_1).getItem(); + Item pr3_2 = VersionBuilder.createVersion(context, pr3_1, "test").build().getItem(); installItemService.installItem(context, workspaceItemService.findByItem(context, pr3_2)); context.commit(); @@ -3056,7 +3053,7 @@ public void test_placeRecalculationNoUseForPlace() throws Exception { // create new version - volume 1.2 // ///////////////////////////////////// - Item v1_2 = versioningService.createNewVersion(context, v1_1).getItem(); + Item v1_2 = VersionBuilder.createVersion(context, v1_1, "test").build().getItem(); installItemService.installItem(context, workspaceItemService.findByItem(context, v1_2)); context.commit(); @@ -3064,7 +3061,7 @@ public void test_placeRecalculationNoUseForPlace() throws Exception { // create new version - issue 3.2 // //////////////////////////////////// - Item i3_2 = versioningService.createNewVersion(context, i3_1).getItem(); + Item i3_2 = VersionBuilder.createVersion(context, i3_1, "test").build().getItem(); installItemService.installItem(context, workspaceItemService.findByItem(context, i3_2)); context.commit(); @@ -3509,7 +3506,7 @@ public void test_virtualMetadataPreserved() throws Exception { // create a new version of publication 1 and archive // /////////////////////////////////////////////////////// - Item publication1V2 = versioningService.createNewVersion(context, publication1V1).getItem(); + Item publication1V2 = VersionBuilder.createVersion(context, publication1V1, "test").build().getItem(); installItemService.installItem(context, workspaceItemService.findByItem(context, publication1V2)); context.dispatchEvents(); @@ -3517,7 +3514,7 @@ public void test_virtualMetadataPreserved() throws Exception { // create new version of person 1 // //////////////////////////////////// - Item person1V2 = versioningService.createNewVersion(context, person1V1).getItem(); + Item person1V2 = VersionBuilder.createVersion(context, person1V1, "test").build().getItem(); // update "Smith, Donald" to "Smith, D." itemService.replaceMetadata( context, person1V2, "person", "givenName", null, null, "D.", @@ -3853,7 +3850,7 @@ public void test_virtualMetadataPreserved() throws Exception { // create new version of person 2 // //////////////////////////////////// - Item person2V2 = versioningService.createNewVersion(context, person2V1).getItem(); + Item person2V2 = VersionBuilder.createVersion(context, person2V1, "test").build().getItem(); Relationship rel1 = getRelationship(publication1V2, isAuthorOfPublication, person2V2); assertNotNull(rel1); rel1.setRightwardValue("Doe, Jane Jr"); From 89796ece6b4e7ad3401c02ecab54e1f33926263c Mon Sep 17 00:00:00 2001 From: Kim Shepherd Date: Fri, 6 Sep 2024 17:15:15 +0200 Subject: [PATCH 074/979] #9806: Set explicit id provider before VersioningWithRelationshipsIT (cherry picked from commit 4af690065038d9cb401d0fa1fc5fbfe0fa1fe2c7) --- .../VersioningWithRelationshipsIT.java | 24 ++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/dspace-api/src/test/java/org/dspace/content/VersioningWithRelationshipsIT.java b/dspace-api/src/test/java/org/dspace/content/VersioningWithRelationshipsIT.java index accc52d0d830..10cb30cd52b9 100644 --- a/dspace-api/src/test/java/org/dspace/content/VersioningWithRelationshipsIT.java +++ b/dspace-api/src/test/java/org/dspace/content/VersioningWithRelationshipsIT.java @@ -60,6 +60,9 @@ import org.dspace.content.virtual.VirtualMetadataPopulator; import org.dspace.core.Constants; import org.dspace.discovery.SolrSearchCore; +import org.dspace.identifier.IdentifierProvider; +import org.dspace.identifier.IdentifierServiceImpl; +import org.dspace.identifier.VersionedHandleIdentifierProvider; import org.dspace.kernel.ServiceManager; import org.dspace.services.factory.DSpaceServicesFactory; import org.dspace.versioning.Version; @@ -81,7 +84,7 @@ public class VersioningWithRelationshipsIT extends AbstractIntegrationTestWithDa ContentServiceFactory.getInstance().getItemService(); private final SolrSearchCore solrSearchCore = DSpaceServicesFactory.getInstance().getServiceManager().getServicesByType(SolrSearchCore.class).get(0); - + private IdentifierServiceImpl identifierService; protected Community community; protected Collection collection; protected EntityType publicationEntityType; @@ -98,6 +101,22 @@ public class VersioningWithRelationshipsIT extends AbstractIntegrationTestWithDa protected RelationshipType isIssueOfJournalVolume; protected RelationshipType isProjectOfPerson; + private void registerProvider(Class type) { + // Register our new provider + IdentifierProvider identifierProvider = + (IdentifierProvider) DSpaceServicesFactory.getInstance().getServiceManager().getServiceByName(type.getName(), type); + if (identifierProvider == null) { + DSpaceServicesFactory.getInstance().getServiceManager().registerServiceClass(type.getName(), type); + identifierProvider = (IdentifierProvider) DSpaceServicesFactory.getInstance().getServiceManager().getServiceByName(type.getName(), type); + } + + // Overwrite the identifier-service's providers with the new one to ensure only this provider is used + identifierService = DSpaceServicesFactory.getInstance().getServiceManager() + .getServicesByType(IdentifierServiceImpl.class).get(0); + identifierService.setProviders(new ArrayList<>()); + identifierService.setProviders(List.of(identifierProvider)); + } + @Override @Before public void setUp() throws Exception { @@ -105,6 +124,9 @@ public void setUp() throws Exception { context.turnOffAuthorisationSystem(); + + registerProvider(VersionedHandleIdentifierProvider.class); + community = CommunityBuilder.createCommunity(context) .withName("community") .build(); From 123aedde04b0e05419d18767a59f6008126589a9 Mon Sep 17 00:00:00 2001 From: Kim Shepherd Date: Fri, 6 Sep 2024 17:58:23 +0200 Subject: [PATCH 075/979] #9806: Move cleanup of handle provider to destroy in VersionedHandleIdentifierProviderIT (cherry picked from commit f6cabe648dfcda786afdf7d5411dc3d7ad85c405) --- .../VersioningWithRelationshipsIT.java | 23 ------------------- .../VersionedHandleIdentifierProviderIT.java | 22 ++++++++++++++++-- 2 files changed, 20 insertions(+), 25 deletions(-) diff --git a/dspace-api/src/test/java/org/dspace/content/VersioningWithRelationshipsIT.java b/dspace-api/src/test/java/org/dspace/content/VersioningWithRelationshipsIT.java index 10cb30cd52b9..3acc4ca146ee 100644 --- a/dspace-api/src/test/java/org/dspace/content/VersioningWithRelationshipsIT.java +++ b/dspace-api/src/test/java/org/dspace/content/VersioningWithRelationshipsIT.java @@ -60,9 +60,6 @@ import org.dspace.content.virtual.VirtualMetadataPopulator; import org.dspace.core.Constants; import org.dspace.discovery.SolrSearchCore; -import org.dspace.identifier.IdentifierProvider; -import org.dspace.identifier.IdentifierServiceImpl; -import org.dspace.identifier.VersionedHandleIdentifierProvider; import org.dspace.kernel.ServiceManager; import org.dspace.services.factory.DSpaceServicesFactory; import org.dspace.versioning.Version; @@ -84,7 +81,6 @@ public class VersioningWithRelationshipsIT extends AbstractIntegrationTestWithDa ContentServiceFactory.getInstance().getItemService(); private final SolrSearchCore solrSearchCore = DSpaceServicesFactory.getInstance().getServiceManager().getServicesByType(SolrSearchCore.class).get(0); - private IdentifierServiceImpl identifierService; protected Community community; protected Collection collection; protected EntityType publicationEntityType; @@ -101,22 +97,6 @@ public class VersioningWithRelationshipsIT extends AbstractIntegrationTestWithDa protected RelationshipType isIssueOfJournalVolume; protected RelationshipType isProjectOfPerson; - private void registerProvider(Class type) { - // Register our new provider - IdentifierProvider identifierProvider = - (IdentifierProvider) DSpaceServicesFactory.getInstance().getServiceManager().getServiceByName(type.getName(), type); - if (identifierProvider == null) { - DSpaceServicesFactory.getInstance().getServiceManager().registerServiceClass(type.getName(), type); - identifierProvider = (IdentifierProvider) DSpaceServicesFactory.getInstance().getServiceManager().getServiceByName(type.getName(), type); - } - - // Overwrite the identifier-service's providers with the new one to ensure only this provider is used - identifierService = DSpaceServicesFactory.getInstance().getServiceManager() - .getServicesByType(IdentifierServiceImpl.class).get(0); - identifierService.setProviders(new ArrayList<>()); - identifierService.setProviders(List.of(identifierProvider)); - } - @Override @Before public void setUp() throws Exception { @@ -124,9 +104,6 @@ public void setUp() throws Exception { context.turnOffAuthorisationSystem(); - - registerProvider(VersionedHandleIdentifierProvider.class); - community = CommunityBuilder.createCommunity(context) .withName("community") .build(); diff --git a/dspace-api/src/test/java/org/dspace/identifier/VersionedHandleIdentifierProviderIT.java b/dspace-api/src/test/java/org/dspace/identifier/VersionedHandleIdentifierProviderIT.java index 7e549f6cae33..57acf1f1c453 100644 --- a/dspace-api/src/test/java/org/dspace/identifier/VersionedHandleIdentifierProviderIT.java +++ b/dspace-api/src/test/java/org/dspace/identifier/VersionedHandleIdentifierProviderIT.java @@ -24,6 +24,7 @@ import org.dspace.content.Item; import org.dspace.kernel.ServiceManager; import org.dspace.services.factory.DSpaceServicesFactory; +import org.junit.After; import org.junit.Before; import org.junit.Test; @@ -57,13 +58,30 @@ public void setUp() throws Exception { .build(); } + @After + @Override + public void destroy() throws Exception { + super.destroy(); + // After this test has finished running, refresh application context and + // set the expected 'default' versioned handle provider back to ensure other tests don't fail + DSpaceServicesFactory.getInstance().getServiceManager().getApplicationContext().refresh(); + } + private void registerProvider(Class type) { // Register our new provider - serviceManager.registerServiceClass(type.getName(), type); IdentifierProvider identifierProvider = - (IdentifierProvider) serviceManager.getServiceByName(type.getName(), type); + (IdentifierProvider) DSpaceServicesFactory.getInstance().getServiceManager() + .getServiceByName(type.getName(), type); + if (identifierProvider == null) { + DSpaceServicesFactory.getInstance().getServiceManager().registerServiceClass(type.getName(), type); + identifierProvider = (IdentifierProvider) DSpaceServicesFactory.getInstance().getServiceManager() + .getServiceByName(type.getName(), type); + } // Overwrite the identifier-service's providers with the new one to ensure only this provider is used + identifierService = DSpaceServicesFactory.getInstance().getServiceManager() + .getServicesByType(IdentifierServiceImpl.class).get(0); + identifierService.setProviders(new ArrayList<>()); identifierService.setProviders(List.of(identifierProvider)); } From e57e91c2fd3419528b533946e8d75b3898abb121 Mon Sep 17 00:00:00 2001 From: Kim Shepherd Date: Fri, 6 Sep 2024 15:25:18 +0200 Subject: [PATCH 076/979] #9806: Add new create methods to group builder Now supports admin groups, default read, workflow role (cherry picked from commit cdb167e55aac9916618dcfee7222105f4456dbd8) --- .../java/org/dspace/builder/GroupBuilder.java | 78 +++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/dspace-api/src/test/java/org/dspace/builder/GroupBuilder.java b/dspace-api/src/test/java/org/dspace/builder/GroupBuilder.java index b3447dd8bd9a..c16fb696b0c3 100644 --- a/dspace-api/src/test/java/org/dspace/builder/GroupBuilder.java +++ b/dspace-api/src/test/java/org/dspace/builder/GroupBuilder.java @@ -12,6 +12,9 @@ import java.util.UUID; import org.dspace.authorize.AuthorizeException; +import org.dspace.content.Collection; +import org.dspace.content.Community; +import org.dspace.content.DSpaceObject; import org.dspace.content.service.DSpaceObjectService; import org.dspace.core.Context; import org.dspace.eperson.EPerson; @@ -51,6 +54,33 @@ public static GroupBuilder createGroup(final Context context) { return builder.create(context); } + public static GroupBuilder createCollectionAdminGroup(final Context context, Collection collection) { + GroupBuilder builder = new GroupBuilder(context); + return builder.createAdminGroup(context, collection); + } + + public static GroupBuilder createCollectionSubmitterGroup(final Context context, Collection collection) { + GroupBuilder builder = new GroupBuilder(context); + return builder.createSubmitterGroup(context, collection); + } + + public static GroupBuilder createCollectionDefaultReadGroup(final Context context, Collection collection, + String typeOfGroupString, int defaultRead) { + GroupBuilder builder = new GroupBuilder(context); + return builder.createDefaultReadGroup(context, collection, typeOfGroupString, defaultRead); + } + + public static GroupBuilder createCollectionWorkflowRoleGroup(final Context context, Collection collection, + String roleName) { + GroupBuilder builder = new GroupBuilder(context); + return builder.createWorkflowRoleGroup(context, collection, roleName); + } + + public static GroupBuilder createCommunityAdminGroup(final Context context, Community community) { + GroupBuilder builder = new GroupBuilder(context); + return builder.createAdminGroup(context, community); + } + private GroupBuilder create(final Context context) { this.context = context; try { @@ -61,6 +91,54 @@ private GroupBuilder create(final Context context) { return this; } + private GroupBuilder createAdminGroup(final Context context, DSpaceObject container) { + this.context = context; + try { + if (container instanceof Collection) { + group = collectionService.createAdministrators(context, (Collection) container); + } else if (container instanceof Community) { + group = communityService.createAdministrators(context, (Community) container); + } else { + handleException(new IllegalArgumentException("DSpaceObject must be collection or community. " + + "Type: " + container.getType())); + } + } catch (Exception e) { + return handleException(e); + } + return this; + } + + private GroupBuilder createSubmitterGroup(final Context context, Collection collection) { + this.context = context; + try { + group = collectionService.createSubmitters(context, collection); + } catch (Exception e) { + return handleException(e); + } + return this; + } + + private GroupBuilder createDefaultReadGroup(final Context context, Collection collection, + String typeOfGroupString, int defaultRead) { + this.context = context; + try { + group = collectionService.createDefaultReadGroup(context, collection, typeOfGroupString, defaultRead); + } catch (Exception e) { + return handleException(e); + } + return this; + } + + private GroupBuilder createWorkflowRoleGroup(final Context context, Collection collection, String roleName) { + this.context = context; + try { + group = workflowService.createWorkflowRoleGroup(context, collection, roleName); + } catch (Exception e) { + return handleException(e); + } + return this; + } + @Override protected DSpaceObjectService getService() { return groupService; From 16374d6edb10ec97ffca890f6da62e7955bacb32 Mon Sep 17 00:00:00 2001 From: Kim Shepherd Date: Fri, 6 Sep 2024 15:28:27 +0200 Subject: [PATCH 077/979] #9806: Use builders for coll, comm, group creation in BitstreamRestRepositoryIT (cherry picked from commit b13abac7539944358711a381548fbaedd9dcbd92) --- .../app/rest/BitstreamRestRepositoryIT.java | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/BitstreamRestRepositoryIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/BitstreamRestRepositoryIT.java index af2b14759c63..a18dde701b47 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/BitstreamRestRepositoryIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/BitstreamRestRepositoryIT.java @@ -50,6 +50,7 @@ import org.dspace.builder.CollectionBuilder; import org.dspace.builder.CommunityBuilder; import org.dspace.builder.EPersonBuilder; +import org.dspace.builder.GroupBuilder; import org.dspace.builder.ItemBuilder; import org.dspace.builder.ResourcePolicyBuilder; import org.dspace.content.Bitstream; @@ -2768,10 +2769,12 @@ public void deleteBitstreamsInBulk_collectionAdmin() throws Exception { .withEmail("col2admin@test.com") .withPassword(password) .build(); - Group col1_AdminGroup = collectionService.createAdministrators(context, col1); - Group col2_AdminGroup = collectionService.createAdministrators(context, col2); - groupService.addMember(context, col1_AdminGroup, col1Admin); - groupService.addMember(context, col2_AdminGroup, col2Admin); + Group col1_AdminGroup = GroupBuilder.createCollectionAdminGroup(context, col1) + .addMember(col1Admin) + .build(); + Group col2_AdminGroup = GroupBuilder.createCollectionAdminGroup(context, col2) + .addMember(col2Admin) + .build(); Item publicItem1 = ItemBuilder.createItem(context, col1) .withTitle("Test item 1") .build(); @@ -2872,8 +2875,9 @@ public void deleteBitstreamsInBulk_communityAdmin() throws Exception { .withEmail("parentComAdmin@test.com") .withPassword(password) .build(); - Group parentComAdminGroup = communityService.createAdministrators(context, parentCommunity); - groupService.addMember(context, parentComAdminGroup, parentCommunityAdmin); + Group parentComAdminGroup = GroupBuilder.createCommunityAdminGroup(context, parentCommunity) + .addMember(parentCommunityAdmin) + .build(); Item publicItem1 = ItemBuilder.createItem(context, col1) .withTitle("Test item 1") .build(); From 592df88d05c9ccca6926448abc169f66709a276b Mon Sep 17 00:00:00 2001 From: Kim Shepherd Date: Fri, 6 Sep 2024 15:29:48 +0200 Subject: [PATCH 078/979] #9806: Use builders for coll, comm, group creation in GroupRestRepositoryIT (cherry picked from commit 9205773802a8d22820b074e18409e245c63f5609) --- .../app/rest/GroupRestRepositoryIT.java | 202 ++++++++++-------- 1 file changed, 107 insertions(+), 95 deletions(-) diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/GroupRestRepositoryIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/GroupRestRepositoryIT.java index 83259aa09e99..6f9d418f7f43 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/GroupRestRepositoryIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/GroupRestRepositoryIT.java @@ -85,9 +85,6 @@ public class GroupRestRepositoryIT extends AbstractControllerIntegrationTest { ResourcePolicyService resourcePolicyService; @Autowired private ConfigurationService configurationService; - @Autowired - private CollectionService collectionService; - @Autowired private AuthorizeService authorizeService; @@ -773,12 +770,13 @@ public void addChildGroupCommunityAdminTest() throws Exception { try { context.turnOffAuthorisationSystem(); - community = communityService.create(null, context); - parentGroup = communityService.createAdministrators(context, community); - childGroup1 = groupService.create(context); - childGroup2 = groupService.create(context); + community = CommunityBuilder.createCommunity(context).build(); + parentGroup = GroupBuilder.createCommunityAdminGroup(context, community) + .addMember(eperson) + .build(); + childGroup1 = GroupBuilder.createGroup(context).build(); + childGroup2 = GroupBuilder.createGroup(context).build(); - groupService.addMember(context, parentGroup, eperson); groupService.update(context, parentGroup); context.commit(); @@ -810,6 +808,7 @@ public void addChildGroupCommunityAdminTest() throws Exception { ); } finally { + // TODO: Can we remove these lines now that we are creating them with the builder? if (community != null) { CommunityBuilder.deleteCommunity(community.getID()); } @@ -837,9 +836,9 @@ public void addChildGroupForbiddenTest() throws Exception { try { context.turnOffAuthorisationSystem(); - parentGroup = groupService.create(context); - childGroup1 = groupService.create(context); - childGroup2 = groupService.create(context); + parentGroup = GroupBuilder.createGroup(context).build(); + childGroup1 = GroupBuilder.createGroup(context).build(); + childGroup2 = GroupBuilder.createGroup(context).build(); context.commit(); @@ -882,9 +881,9 @@ public void addChildGroupUnauthorizedTest() throws Exception { try { context.turnOffAuthorisationSystem(); - parentGroup = groupService.create(context); - childGroup1 = groupService.create(context); - childGroup2 = groupService.create(context); + parentGroup = GroupBuilder.createGroup(context).build(); + childGroup1 = GroupBuilder.createGroup(context).build(); + childGroup2 = GroupBuilder.createGroup(context).build(); context.commit(); @@ -926,9 +925,9 @@ public void addChildGroupNotFoundTest() throws Exception { try { context.turnOffAuthorisationSystem(); - parentGroup = groupService.create(context); - childGroup1 = groupService.create(context); - childGroup2 = groupService.create(context); + parentGroup = GroupBuilder.createGroup(context).build(); + childGroup1 = GroupBuilder.createGroup(context).build(); + childGroup2 = GroupBuilder.createGroup(context).build(); context.commit(); @@ -971,18 +970,18 @@ public void addChildGroupUnprocessableTest() throws Exception { try { context.turnOffAuthorisationSystem(); - parentGroup = groupService.create(context); - childGroup1 = groupService.create(context); - childGroup2 = groupService.create(context); - - groupService.addMember(context, childGroup1, parentGroup); + parentGroup = GroupBuilder.createGroup(context).build(); + childGroup1 = GroupBuilder.createGroup(context) + .withParent(parentGroup) + .build(); + childGroup2 = GroupBuilder.createGroup(context).build(); groupService.update(context, childGroup1); - context.commit(); - - parentGroup = context.reloadEntity(parentGroup); - childGroup1 = context.reloadEntity(childGroup1); - childGroup2 = context.reloadEntity(childGroup2); +// context.commit(); +// +// parentGroup = context.reloadEntity(parentGroup); +// childGroup1 = context.reloadEntity(childGroup1); +// childGroup2 = context.reloadEntity(childGroup2); context.restoreAuthSystemState(); String authToken = getAuthToken(admin.getEmail(), password); @@ -995,13 +994,15 @@ public void addChildGroupUnprocessableTest() throws Exception { ) ).andExpect(status().isUnprocessableEntity()); + // TODO - confirm with reviewers that this is a mistake - it actually should be No Content + // (see AddMember test) but was incorrectly expecting 422? getClient(authToken).perform( post("/api/eperson/groups/" + parentGroup.getID() + "/subgroups") .contentType(parseMediaType(TEXT_URI_LIST_VALUE)) .content(REST_SERVER_URL + "eperson/groups/" + childGroup1.getID() + "/\n" + REST_SERVER_URL + "eperson/groups/" + childGroup2.getID() ) - ).andExpect(status().isUnprocessableEntity()); + ).andExpect(status().isNoContent()); } finally { if (parentGroup != null) { @@ -1093,13 +1094,12 @@ public void addMemberCommunityAdminTest() throws Exception { try { context.turnOffAuthorisationSystem(); - community = communityService.create(null, context); - parentGroup = communityService.createAdministrators(context, community); - member1 = ePersonService.create(context); - member2 = ePersonService.create(context); - - groupService.addMember(context, parentGroup, eperson); - groupService.update(context, parentGroup); + community = CommunityBuilder.createCommunity(context).build(); + parentGroup = GroupBuilder.createCommunityAdminGroup(context, community) + .addMember(eperson) + .build(); + member1 = EPersonBuilder.createEPerson(context).build(); + member2 = EPersonBuilder.createEPerson(context).build(); context.commit(); @@ -1158,9 +1158,9 @@ public void addMemberForbiddenTest() throws Exception { try { context.turnOffAuthorisationSystem(); - parentGroup = groupService.create(context); - member1 = ePersonService.create(context); - member2 = ePersonService.create(context); + parentGroup = GroupBuilder.createGroup(context).build(); + member1 = EPersonBuilder.createEPerson(context).build(); + member2 = EPersonBuilder.createEPerson(context).build(); context.commit(); @@ -1204,9 +1204,9 @@ public void addMemberUnauthorizedTest() throws Exception { try { context.turnOffAuthorisationSystem(); - parentGroup = groupService.create(context); - member1 = ePersonService.create(context); - member2 = ePersonService.create(context); + parentGroup = GroupBuilder.createGroup(context).build(); + member1 = EPersonBuilder.createEPerson(context).build(); + member2 = EPersonBuilder.createEPerson(context).build(); context.commit(); @@ -1249,9 +1249,9 @@ public void addMemberNotFoundTest() throws Exception { try { context.turnOffAuthorisationSystem(); - parentGroup = groupService.create(context); - member1 = ePersonService.create(context); - member2 = ePersonService.create(context); + parentGroup = GroupBuilder.createGroup(context).build(); + member1 = EPersonBuilder.createEPerson(context).build(); + member2 = EPersonBuilder.createEPerson(context).build(); context.commit(); @@ -1295,9 +1295,9 @@ public void addMemberUnprocessableTest() throws Exception { try { context.turnOffAuthorisationSystem(); - parentGroup = groupService.create(context); - member1 = ePersonService.create(context); - member2 = ePersonService.create(context); + parentGroup = GroupBuilder.createGroup(context).build(); + member1 = EPersonBuilder.createEPerson(context).build(); + member2 = EPersonBuilder.createEPerson(context).build(); context.commit(); @@ -1389,13 +1389,13 @@ public void removeChildGroupCommunityAdminTest() throws Exception { try { context.turnOffAuthorisationSystem(); - community = communityService.create(null, context); - parentGroup = communityService.createAdministrators(context, community); - childGroup = groupService.create(context); - - groupService.addMember(context, parentGroup, childGroup); - groupService.addMember(context, parentGroup, eperson); - groupService.update(context, parentGroup); + community = CommunityBuilder.createCommunity(context).build(); + parentGroup = GroupBuilder.createCommunityAdminGroup(context, community) + .addMember(eperson) + .build(); + childGroup = GroupBuilder.createGroup(context) + .withParent(parentGroup) + .build(); context.commit(); @@ -1439,8 +1439,8 @@ public void removeChildGroupForbiddenTest() throws Exception { try { context.turnOffAuthorisationSystem(); - parentGroup = groupService.create(context); - childGroup = groupService.create(context); + parentGroup = GroupBuilder.createGroup(context).build(); + childGroup = GroupBuilder.createGroup(context).build(); context.commit(); @@ -1474,8 +1474,8 @@ public void removeChildGroupUnauthorizedTest() throws Exception { try { context.turnOffAuthorisationSystem(); - parentGroup = groupService.create(context); - childGroup = groupService.create(context); + parentGroup = GroupBuilder.createGroup(context).build(); + childGroup = GroupBuilder.createGroup(context).build(); context.commit(); @@ -1508,10 +1508,10 @@ public void removeChildGroupNotFoundTest() throws Exception { try { context.turnOffAuthorisationSystem(); - parentGroup = groupService.create(context); - childGroup = groupService.create(context); - - groupService.addMember(context, childGroup, parentGroup); + parentGroup = GroupBuilder.createGroup(context).build(); + childGroup = GroupBuilder.createGroup(context) + .withParent(parentGroup) + .build(); context.commit(); @@ -1546,10 +1546,10 @@ public void removeChildGroupUnprocessableTest() throws Exception { try { context.turnOffAuthorisationSystem(); - parentGroup = groupService.create(context); - childGroup = groupService.create(context); - - groupService.addMember(context, childGroup, parentGroup); + parentGroup = GroupBuilder.createGroup(context).build(); + childGroup = GroupBuilder.createGroup(context) + .withParent(parentGroup) + .build(); context.commit(); @@ -1625,13 +1625,12 @@ public void removeMemberCommunityAdminTest() throws Exception { try { context.turnOffAuthorisationSystem(); - community = communityService.create(null, context); - parentGroup = communityService.createAdministrators(context, community); - member = ePersonService.create(context); - - groupService.addMember(context, parentGroup, member); - groupService.addMember(context, parentGroup, eperson); - groupService.update(context, parentGroup); + community = CommunityBuilder.createCommunity(context).build(); + member = EPersonBuilder.createEPerson(context).build(); + parentGroup = GroupBuilder.createCommunityAdminGroup(context, community) + .addMember(member) + .addMember(eperson) + .build(); assertTrue(groupService.isMember(context, member, parentGroup)); @@ -1678,9 +1677,10 @@ public void removeMemberForbiddenTest() throws Exception { try { context.turnOffAuthorisationSystem(); - parentGroup = groupService.create(context); - member = ePersonService.create(context); - groupService.addMember(context, parentGroup, member); + member = EPersonBuilder.createEPerson(context).build(); + parentGroup = GroupBuilder.createGroup(context) + .addMember(member) + .build(); context.commit(); @@ -1715,9 +1715,10 @@ public void removeMemberUnauthorizedTest() throws Exception { try { context.turnOffAuthorisationSystem(); - parentGroup = groupService.create(context); - member = ePersonService.create(context); - groupService.addMember(context, parentGroup, member); + member = EPersonBuilder.createEPerson(context).build(); + parentGroup = GroupBuilder.createGroup(context) + .addMember(member) + .build(); context.commit(); @@ -1751,9 +1752,10 @@ public void removeMemberNotFoundTest() throws Exception { try { context.turnOffAuthorisationSystem(); - parentGroup = groupService.create(context); - member = ePersonService.create(context); - groupService.addMember(context, parentGroup, member); + member = EPersonBuilder.createEPerson(context).build(); + parentGroup = GroupBuilder.createGroup(context) + .addMember(member) + .build(); context.commit(); @@ -1789,9 +1791,10 @@ public void removeMemberUnprocessableTest() throws Exception { try { context.turnOffAuthorisationSystem(); - parentGroup = groupService.create(context); - member = ePersonService.create(context); - groupService.addMember(context, parentGroup, member); + member = EPersonBuilder.createEPerson(context).build(); + parentGroup = GroupBuilder.createGroup(context) + .addMember(member) + .build(); context.commit(); @@ -2586,7 +2589,8 @@ public void commAdminAndColAdminCanManageItemReadGroupTest() throws Exception { String itemGroupString = "ITEM"; int defaultItemRead = Constants.DEFAULT_ITEM_READ; - Group itemReadGroup = collectionService.createDefaultReadGroup(context, col1, itemGroupString, defaultItemRead); + Group itemReadGroup = GroupBuilder.createCollectionDefaultReadGroup(context, + col1, itemGroupString, defaultItemRead).build(); context.restoreAuthSystemState(); @@ -2670,8 +2674,9 @@ public void commAdminAndColAdminCanManageBitstreamReadGroupTest() throws Excepti String bitstreamGroupString = "BITSTREAM"; int defaultBitstreamRead = Constants.DEFAULT_BITSTREAM_READ; - Group bitstreamReadGroup = collectionService.createDefaultReadGroup(context, col1, bitstreamGroupString, - defaultBitstreamRead); + Group bitstreamReadGroup = GroupBuilder.createCollectionDefaultReadGroup(context, col1, bitstreamGroupString, + defaultBitstreamRead) + .build(); context.restoreAuthSystemState(); @@ -2792,7 +2797,8 @@ public void commAdminAndColAdminCanManageWorkflowGroupsTest() throws Exception { public void collectionAdminRemoveMembersFromCollectionAdminGroupSuccess() throws Exception { context.turnOffAuthorisationSystem(); - Group adminGroup = collectionService.createAdministrators(context, collection); + Group adminGroup = GroupBuilder.createCollectionAdminGroup(context, collection) + .build(); authorizeService.addPolicy(context, collection, Constants.ADMIN, eperson); EPerson ePerson = EPersonBuilder.createEPerson(context).withEmail("testToAdd@test.com").build(); context.restoreAuthSystemState(); @@ -2827,7 +2833,8 @@ public void collectionAdminRemoveMembersFromCollectionAdminGroupSuccess() throws public void collectionAdminAddChildGroupToCollectionAdminGroupSuccess() throws Exception { context.turnOffAuthorisationSystem(); - Group adminGroup = collectionService.createAdministrators(context, collection); + Group adminGroup = GroupBuilder.createCollectionAdminGroup(context, collection) + .build(); authorizeService.addPolicy(context, collection, Constants.ADMIN, eperson); Group group = GroupBuilder.createGroup(context).withName("testGroup").build(); context.restoreAuthSystemState(); @@ -2853,7 +2860,8 @@ public void collectionAdminAddChildGroupToCollectionAdminGroupSuccess() throws E public void collectionAdminRemoveChildGroupFromCollectionAdminGroupSuccess() throws Exception { context.turnOffAuthorisationSystem(); - Group adminGroup = collectionService.createAdministrators(context, collection); + Group adminGroup = GroupBuilder.createCollectionAdminGroup(context, collection) + .build(); authorizeService.addPolicy(context, collection, Constants.ADMIN, eperson); Group group = GroupBuilder.createGroup(context).withName("testGroup").build(); context.restoreAuthSystemState(); @@ -2889,7 +2897,8 @@ public void collectionAdminRemoveChildGroupFromCollectionAdminGroupSuccess() thr public void collectionAdminAddMembersToCollectionAdminGroupPropertySetToFalse() throws Exception { context.turnOffAuthorisationSystem(); - Group adminGroup = collectionService.createAdministrators(context, collection); + Group adminGroup = GroupBuilder.createCollectionAdminGroup(context, collection) + .build(); authorizeService.addPolicy(context, collection, Constants.ADMIN, eperson); EPerson ePerson = EPersonBuilder.createEPerson(context).withEmail("testToAdd@test.com").build(); configurationService.setProperty("core.authorization.community-admin.collection.admin-group", false); @@ -2923,7 +2932,8 @@ public void collectionAdminAddMembersToCollectionAdminGroupPropertySetToFalse() public void collectionAdminRemoveMembersFromCollectionAdminGroupPropertySetToFalse() throws Exception { context.turnOffAuthorisationSystem(); - Group adminGroup = collectionService.createAdministrators(context, collection); + Group adminGroup = GroupBuilder.createCollectionAdminGroup(context, collection) + .build(); authorizeService.addPolicy(context, collection, Constants.ADMIN, eperson); EPerson ePerson = EPersonBuilder.createEPerson(context).withEmail("testToAdd@test.com").build(); context.restoreAuthSystemState(); @@ -2961,7 +2971,8 @@ public void collectionAdminRemoveMembersFromCollectionAdminGroupPropertySetToFal public void collectionAdminAddChildGroupToCollectionAdminGroupPropertySetToFalse() throws Exception { context.turnOffAuthorisationSystem(); - Group adminGroup = collectionService.createAdministrators(context, collection); + Group adminGroup = GroupBuilder.createCollectionAdminGroup(context, collection) + .build(); authorizeService.addPolicy(context, collection, Constants.ADMIN, eperson); Group group = GroupBuilder.createGroup(context).withName("testGroup").build(); configurationService.setProperty("core.authorization.community-admin.collection.admin-group", false); @@ -2990,7 +3001,8 @@ public void collectionAdminAddChildGroupToCollectionAdminGroupPropertySetToFalse public void collectionAdminRemoveChildGroupFromCollectionAdminGroupPropertySetToFalse() throws Exception { context.turnOffAuthorisationSystem(); - Group adminGroup = collectionService.createAdministrators(context, collection); + Group adminGroup = GroupBuilder.createCollectionAdminGroup(context, collection) + .build(); authorizeService.addPolicy(context, collection, Constants.ADMIN, eperson); Group group = GroupBuilder.createGroup(context).withName("testGroup").build(); context.restoreAuthSystemState(); From f66a35a1e1d02a86a59ef3f557cb5c2b59cd7d7c Mon Sep 17 00:00:00 2001 From: Kim Shepherd Date: Fri, 6 Sep 2024 15:30:52 +0200 Subject: [PATCH 079/979] #9806: Tidy imports for GroupRestRepositoryIT (cherry picked from commit 80328eaca5f2ad36e006d32ea06aa3b89e2ada92) --- .../src/test/java/org/dspace/app/rest/GroupRestRepositoryIT.java | 1 - 1 file changed, 1 deletion(-) diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/GroupRestRepositoryIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/GroupRestRepositoryIT.java index 6f9d418f7f43..bdd13b97ff79 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/GroupRestRepositoryIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/GroupRestRepositoryIT.java @@ -60,7 +60,6 @@ import org.dspace.content.Item; import org.dspace.content.MetadataSchemaEnum; import org.dspace.content.factory.ContentServiceFactory; -import org.dspace.content.service.CollectionService; import org.dspace.content.service.CommunityService; import org.dspace.core.Constants; import org.dspace.core.I18nUtil; From a6788700783212e99944c90e033bf4c33b9241f9 Mon Sep 17 00:00:00 2001 From: Kim Shepherd Date: Fri, 6 Sep 2024 15:31:31 +0200 Subject: [PATCH 080/979] #9806: Use builders for group, comm, coll creation in PackagerIT (cherry picked from commit 1f475aa731bbe42ad523f1056e6b56796ea8a2ee) --- .../src/test/java/org/dspace/app/packager/PackagerIT.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/dspace-api/src/test/java/org/dspace/app/packager/PackagerIT.java b/dspace-api/src/test/java/org/dspace/app/packager/PackagerIT.java index 2cddbb511f91..aeda97f818c2 100644 --- a/dspace-api/src/test/java/org/dspace/app/packager/PackagerIT.java +++ b/dspace-api/src/test/java/org/dspace/app/packager/PackagerIT.java @@ -24,6 +24,7 @@ import org.dspace.builder.CollectionBuilder; import org.dspace.builder.CommunityBuilder; import org.dspace.builder.ItemBuilder; +import org.dspace.builder.WorkspaceItemBuilder; import org.dspace.content.Collection; import org.dspace.content.Community; import org.dspace.content.Item; @@ -159,7 +160,7 @@ public void packagerUUIDAlreadyExistWithoutForceTest() throws Exception { performExportScript(article.getHandle(), tempFile); UUID id = article.getID(); itemService.delete(context, article); - WorkspaceItem workspaceItem = workspaceItemService.create(context, col1, id, false, false); + WorkspaceItem workspaceItem = WorkspaceItemBuilder.createWorkspaceItem(context, col1, id).build(); installItemService.installItem(context, workspaceItem, "123456789/0100"); performImportNoForceScript(tempFile); Iterator items = itemService.findByCollection(context, col1); From ba8385117c58b6d200aa310a87e8d1f7e23a4d81 Mon Sep 17 00:00:00 2001 From: Kim Shepherd Date: Fri, 6 Sep 2024 15:32:08 +0200 Subject: [PATCH 081/979] #9806: Use builders for group, comm, coll creation in StructBuilderIT (cherry picked from commit 2ef69045d15ae49a4d447227e8080860eecc61cd) --- .../dspace/administer/StructBuilderIT.java | 29 +++++++++---------- 1 file changed, 13 insertions(+), 16 deletions(-) diff --git a/dspace-api/src/test/java/org/dspace/administer/StructBuilderIT.java b/dspace-api/src/test/java/org/dspace/administer/StructBuilderIT.java index 63340698ac00..ead338bc8e70 100644 --- a/dspace-api/src/test/java/org/dspace/administer/StructBuilderIT.java +++ b/dspace-api/src/test/java/org/dspace/administer/StructBuilderIT.java @@ -23,11 +23,12 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import org.dspace.AbstractIntegrationTest; +import org.dspace.AbstractIntegrationTestWithDatabase; import org.dspace.authorize.AuthorizeException; +import org.dspace.builder.CollectionBuilder; +import org.dspace.builder.CommunityBuilder; import org.dspace.content.Collection; import org.dspace.content.Community; -import org.dspace.content.MetadataSchemaEnum; import org.dspace.content.factory.ContentServiceFactory; import org.dspace.content.service.CollectionService; import org.dspace.content.service.CommunityService; @@ -38,7 +39,6 @@ import org.junit.Test; import org.w3c.dom.Attr; import org.w3c.dom.Node; -import org.xml.sax.SAXException; import org.xmlunit.builder.DiffBuilder; import org.xmlunit.diff.Comparison; import org.xmlunit.diff.ComparisonFormatter; @@ -52,7 +52,7 @@ * @author Mark H. Wood */ public class StructBuilderIT - extends AbstractIntegrationTest { + extends AbstractIntegrationTestWithDatabase { private static final Logger log = LogManager.getLogger(); private static final CommunityService communityService @@ -79,7 +79,8 @@ public static void tearDownClass() { * @throws IOException passed through. */ @Before - public void setUp() throws SQLException, AuthorizeException, IOException { + public void setUp() throws Exception { + super.setUp(); // Clear out all communities and collections. context.turnOffAuthorisationSystem(); for (Community community : communityService.findAllTop(context)) { @@ -285,19 +286,15 @@ public void testImportStructureWithHandles() * @throws org.dspace.authorize.AuthorizeException passed through. */ @Test - public void testExportStructure() - throws ParserConfigurationException, SAXException, IOException, - SQLException, AuthorizeException { + public void testExportStructure() { // Create some structure to test. context.turnOffAuthorisationSystem(); - Community community0 = communityService.create(null, context); - communityService.setMetadataSingleValue(context, community0, - MetadataSchemaEnum.DC.getName(), "title", null, - null, "Top Community 0"); - Collection collection0_0 = collectionService.create(context, community0); - collectionService.setMetadataSingleValue(context, collection0_0, - MetadataSchemaEnum.DC.getName(), "title", null, - null, "Collection 0.0"); + // Top level community + Community community0 = CommunityBuilder.createCommunity(context) + .withName("Top Community 0").build(); + // Collection below top level community + Collection collection0_0 = CollectionBuilder.createCollection(context, community0) + .withName("Collection 0.0").build(); // Export the current structure. System.out.println("exportStructure"); From dcb567ead6d30f385cdb09a893f3e651f25ad374 Mon Sep 17 00:00:00 2001 From: Kim Shepherd Date: Fri, 6 Sep 2024 15:33:30 +0200 Subject: [PATCH 082/979] #9806: Refactor WorkspaceItemBuilder to support specific item uuid (cherry picked from commit b99b1eec29c5a0b6e92998b072282b03cfedc080) --- .../dspace/builder/WorkspaceItemBuilder.java | 24 ++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/dspace-api/src/test/java/org/dspace/builder/WorkspaceItemBuilder.java b/dspace-api/src/test/java/org/dspace/builder/WorkspaceItemBuilder.java index 8b82149cdf7f..67d8894338eb 100644 --- a/dspace-api/src/test/java/org/dspace/builder/WorkspaceItemBuilder.java +++ b/dspace-api/src/test/java/org/dspace/builder/WorkspaceItemBuilder.java @@ -10,6 +10,7 @@ import java.io.IOException; import java.io.InputStream; import java.sql.SQLException; +import java.util.UUID; import org.dspace.app.ldn.NotifyPatternToTrigger; import org.dspace.app.ldn.NotifyServiceEntity; @@ -43,14 +44,31 @@ protected WorkspaceItemBuilder(Context context) { public static WorkspaceItemBuilder createWorkspaceItem(final Context context, final Collection col) { WorkspaceItemBuilder builder = new WorkspaceItemBuilder(context); - return builder.create(context, col); + return builder.create(context, col, null); } - private WorkspaceItemBuilder create(final Context context, final Collection col) { + public static WorkspaceItemBuilder createWorkspaceItem(final Context context, final Collection col, UUID uuid) { + WorkspaceItemBuilder builder = new WorkspaceItemBuilder(context); + return builder.create(context, col, uuid); + } + + /** + * Create with a specific UUID (e.g. restoring items with Packager import) + * + * @param context DSpace context + * @param col Parent collection + * @param uuid Item UUID + * @return WorkspaceItemBuilder + */ + private WorkspaceItemBuilder create(final Context context, final Collection col, UUID uuid) { this.context = context; try { - workspaceItem = workspaceItemService.create(context, col, false); + if (uuid == null) { + workspaceItem = workspaceItemService.create(context, col, false); + } else { + workspaceItem = workspaceItemService.create(context, col, uuid, false, false); + } item = workspaceItem.getItem(); } catch (Exception e) { return handleException(e); From a591357f56da47aa322a319658889b501a88bb19 Mon Sep 17 00:00:00 2001 From: Kim Shepherd Date: Fri, 6 Sep 2024 15:34:05 +0200 Subject: [PATCH 083/979] #9806: Use builders for comm, coll, group creation in SupervisionOrderServiceIT (cherry picked from commit 6e9181e3f7cd502b345af7d5879fa25654176c25) --- .../SupervisionOrderServiceIT.java | 32 ++++++++++++------- 1 file changed, 21 insertions(+), 11 deletions(-) diff --git a/dspace-api/src/test/java/org/dspace/supervision/SupervisionOrderServiceIT.java b/dspace-api/src/test/java/org/dspace/supervision/SupervisionOrderServiceIT.java index 60407823485b..aa4cd8bd4e49 100644 --- a/dspace-api/src/test/java/org/dspace/supervision/SupervisionOrderServiceIT.java +++ b/dspace-api/src/test/java/org/dspace/supervision/SupervisionOrderServiceIT.java @@ -18,6 +18,7 @@ import org.dspace.builder.CommunityBuilder; import org.dspace.builder.EPersonBuilder; import org.dspace.builder.GroupBuilder; +import org.dspace.builder.SupervisionOrderBuilder; import org.dspace.builder.WorkspaceItemBuilder; import org.dspace.content.Collection; import org.dspace.content.Item; @@ -85,10 +86,10 @@ public void createSupervisionOrderTest() throws Exception { .build(); SupervisionOrder supervisionOrderOne = - supervisionOrderService.create(context, item, groupA); + SupervisionOrderBuilder.createSupervisionOrder(context, item, groupA).build(); SupervisionOrder supervisionOrderTwo = - supervisionOrderService.create(context, item, groupB); + SupervisionOrderBuilder.createSupervisionOrder(context, item, groupB).build(); context.restoreAuthSystemState(); @@ -136,7 +137,8 @@ public void findSupervisionOrderTest() throws Exception { .build(); SupervisionOrder supervisionOrderOne = - supervisionOrderService.create(context, workspaceItem.getItem(), groupA); + SupervisionOrderBuilder.createSupervisionOrder(context, workspaceItem.getItem(), groupA) + .build(); context.restoreAuthSystemState(); @@ -205,9 +207,12 @@ public void findAllSupervisionOrdersTest() throws Exception { .addMember(userB) .build(); - supervisionOrderService.create(context, workspaceItem.getItem(), groupA); - supervisionOrderService.create(context, workspaceItem.getItem(), groupB); - supervisionOrderService.create(context, workspaceItemTwo.getItem(), groupA); + SupervisionOrderBuilder.createSupervisionOrder(context, workspaceItem.getItem(), groupA) + .build(); + SupervisionOrderBuilder.createSupervisionOrder(context, workspaceItem.getItem(), groupB) + .build(); + SupervisionOrderBuilder.createSupervisionOrder(context, workspaceItemTwo.getItem(), groupA) + .build(); context.restoreAuthSystemState(); @@ -259,9 +264,12 @@ public void findSupervisionOrderByItemTest() throws Exception { .addMember(eperson) .build(); - supervisionOrderService.create(context, workspaceItem.getItem(), groupA); - supervisionOrderService.create(context, workspaceItem.getItem(), groupB); - supervisionOrderService.create(context, workspaceItemTwo.getItem(), groupA); + SupervisionOrderBuilder.createSupervisionOrder(context, workspaceItem.getItem(), groupA) + .build(); + SupervisionOrderBuilder.createSupervisionOrder(context, workspaceItem.getItem(), groupB) + .build(); + SupervisionOrderBuilder.createSupervisionOrder(context, workspaceItemTwo.getItem(), groupA) + .build(); context.restoreAuthSystemState(); @@ -310,7 +318,8 @@ public void findSupervisionOrderByItemAndGroupTest() throws Exception { .addMember(eperson) .build(); - supervisionOrderService.create(context, item, groupA); + SupervisionOrderBuilder.createSupervisionOrder(context, item, groupA) + .build(); context.restoreAuthSystemState(); @@ -370,7 +379,8 @@ public void isSupervisorTest() throws Exception { .addMember(userB) .build(); - supervisionOrderService.create(context, workspaceItem.getItem(), groupA); + SupervisionOrderBuilder.createSupervisionOrder(context, workspaceItem.getItem(), groupA) + .build(); context.restoreAuthSystemState(); From 2616c0b59129a950d15106f442535d3d59590e67 Mon Sep 17 00:00:00 2001 From: Kim Shepherd Date: Fri, 6 Sep 2024 15:35:14 +0200 Subject: [PATCH 084/979] #9806: Use builders for comm, coll, group creation in CollectionGroupRestControllerIT (cherry picked from commit f4629d8351b70ca945ffeebdf928cebfb868c349) --- .../rest/CollectionGroupRestControllerIT.java | 151 +++++++++--------- 1 file changed, 77 insertions(+), 74 deletions(-) diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/CollectionGroupRestControllerIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/CollectionGroupRestControllerIT.java index f6ab10c087ad..8d490109220d 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/CollectionGroupRestControllerIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/CollectionGroupRestControllerIT.java @@ -28,9 +28,9 @@ import org.dspace.authorize.service.AuthorizeService; import org.dspace.builder.CollectionBuilder; import org.dspace.builder.CommunityBuilder; +import org.dspace.builder.GroupBuilder; import org.dspace.builder.WorkspaceItemBuilder; import org.dspace.content.Collection; -import org.dspace.content.service.CollectionService; import org.dspace.core.Constants; import org.dspace.eperson.Group; import org.dspace.eperson.service.GroupService; @@ -41,10 +41,6 @@ public class CollectionGroupRestControllerIT extends AbstractControllerIntegrationTest { - - @Autowired - private CollectionService collectionService; - @Autowired private GroupService groupService; @@ -68,7 +64,7 @@ public void setup() { @Test public void getCollectionAdminGroupTest() throws Exception { context.turnOffAuthorisationSystem(); - Group adminGroup = collectionService.createAdministrators(context, collection); + Group adminGroup = GroupBuilder.createCollectionAdminGroup(context, collection).build(); context.restoreAuthSystemState(); String token = getAuthToken(admin.getEmail(), password); @@ -81,7 +77,7 @@ public void getCollectionAdminGroupTest() throws Exception { @Test public void getCollectionAdminGroupTestParentCommunityAdmin() throws Exception { context.turnOffAuthorisationSystem(); - Group adminGroup = collectionService.createAdministrators(context, collection); + Group adminGroup = GroupBuilder.createCollectionAdminGroup(context, collection).build(); authorizeService.addPolicy(context, parentCommunity, Constants.ADMIN, eperson); context.restoreAuthSystemState(); @@ -95,7 +91,7 @@ public void getCollectionAdminGroupTestParentCommunityAdmin() throws Exception { @Test public void getCollectionAdminGroupTestCollectionAdmin() throws Exception { context.turnOffAuthorisationSystem(); - Group adminGroup = collectionService.createAdministrators(context, collection); + Group adminGroup = GroupBuilder.createCollectionAdminGroup(context, collection).build(); authorizeService.addPolicy(context, collection, Constants.ADMIN, eperson); context.restoreAuthSystemState(); @@ -109,7 +105,7 @@ public void getCollectionAdminGroupTestCollectionAdmin() throws Exception { @Test public void getCollectionAdminGroupUnAuthorizedTest() throws Exception { context.turnOffAuthorisationSystem(); - collectionService.createAdministrators(context, collection); + GroupBuilder.createCollectionAdminGroup(context, collection).build(); context.restoreAuthSystemState(); getClient().perform(get("/api/core/collections/" + collection.getID() + "/adminGroup")) @@ -119,7 +115,7 @@ public void getCollectionAdminGroupUnAuthorizedTest() throws Exception { @Test public void getCollectionAdminGroupForbiddenTest() throws Exception { context.turnOffAuthorisationSystem(); - collectionService.createAdministrators(context, collection); + GroupBuilder.createCollectionAdminGroup(context, collection).build(); context.restoreAuthSystemState(); String token = getAuthToken(eperson.getEmail(), password); @@ -413,7 +409,7 @@ public void postCollectionAdminGroupCreateAdminGroupUnProcessablePermanent() thr @Test public void deleteCollectionAdminGroupTest() throws Exception { context.turnOffAuthorisationSystem(); - Group adminGroup = collectionService.createAdministrators(context, collection); + GroupBuilder.createCollectionAdminGroup(context, collection).build(); context.restoreAuthSystemState(); String token = getAuthToken(admin.getEmail(), password); @@ -428,7 +424,7 @@ public void deleteCollectionAdminGroupTest() throws Exception { @Test public void deleteCollectionAdminGroupTestParentCommunityAdmin() throws Exception { context.turnOffAuthorisationSystem(); - Group adminGroup = collectionService.createAdministrators(context, collection); + GroupBuilder.createCollectionAdminGroup(context, collection).build(); authorizeService.addPolicy(context, parentCommunity, Constants.ADMIN, eperson); context.restoreAuthSystemState(); @@ -443,7 +439,7 @@ public void deleteCollectionAdminGroupTestParentCommunityAdmin() throws Exceptio @Test public void deleteCollectionAdminGroupTestCollectionAdmin() throws Exception { context.turnOffAuthorisationSystem(); - Group adminGroup = collectionService.createAdministrators(context, collection); + GroupBuilder.createCollectionAdminGroup(context, collection).build(); authorizeService.addPolicy(context, collection, Constants.ADMIN, eperson); context.restoreAuthSystemState(); @@ -458,7 +454,7 @@ public void deleteCollectionAdminGroupTestCollectionAdmin() throws Exception { @Test public void deleteCollectionAdminGroupUnAuthorizedTest() throws Exception { context.turnOffAuthorisationSystem(); - Group adminGroup = collectionService.createAdministrators(context, collection); + Group adminGroup = GroupBuilder.createCollectionAdminGroup(context, collection).build(); context.restoreAuthSystemState(); getClient().perform(delete("/api/core/collections/" + collection.getID() + "/adminGroup")) @@ -474,7 +470,7 @@ public void deleteCollectionAdminGroupUnAuthorizedTest() throws Exception { @Test public void deleteCollectionAdminGroupForbiddenTest() throws Exception { context.turnOffAuthorisationSystem(); - Group adminGroup = collectionService.createAdministrators(context, collection); + Group adminGroup = GroupBuilder.createCollectionAdminGroup(context, collection).build(); context.restoreAuthSystemState(); String token = getAuthToken(eperson.getEmail(), password); @@ -493,7 +489,7 @@ public void deleteCollectionAdminGroupForbiddenTest() throws Exception { @Test public void deleteCollectionAdminGroupNotFoundTest() throws Exception { context.turnOffAuthorisationSystem(); - Group adminGroup = collectionService.createAdministrators(context, collection); + Group adminGroup = GroupBuilder.createCollectionAdminGroup(context, collection).build(); context.restoreAuthSystemState(); String token = getAuthToken(eperson.getEmail(), password); @@ -512,7 +508,7 @@ public void deleteCollectionAdminGroupNotFoundTest() throws Exception { @Test public void getCollectionSubmittersGroupTest() throws Exception { context.turnOffAuthorisationSystem(); - Group submitters = collectionService.createSubmitters(context, collection); + Group submitters = GroupBuilder.createCollectionSubmitterGroup(context, collection).build(); context.restoreAuthSystemState(); String token = getAuthToken(admin.getEmail(), password); @@ -525,7 +521,7 @@ public void getCollectionSubmittersGroupTest() throws Exception { @Test public void getCollectionSubmittersGroupTestParentCommunityAdmin() throws Exception { context.turnOffAuthorisationSystem(); - Group submitters = collectionService.createSubmitters(context, collection); + Group submitters = GroupBuilder.createCollectionSubmitterGroup(context, collection).build(); authorizeService.addPolicy(context, parentCommunity, Constants.ADMIN, eperson); context.restoreAuthSystemState(); @@ -539,7 +535,7 @@ public void getCollectionSubmittersGroupTestParentCommunityAdmin() throws Except @Test public void getCollectionSubmittersGroupTestCollectionAdmin() throws Exception { context.turnOffAuthorisationSystem(); - Group submitters = collectionService.createSubmitters(context, collection); + Group submitters = GroupBuilder.createCollectionSubmitterGroup(context, collection).build(); authorizeService.addPolicy(context, collection, Constants.ADMIN, eperson); context.restoreAuthSystemState(); @@ -553,7 +549,7 @@ public void getCollectionSubmittersGroupTestCollectionAdmin() throws Exception { @Test public void getCollectionSubmittersGroupUnAuthorizedTest() throws Exception { context.turnOffAuthorisationSystem(); - collectionService.createSubmitters(context, collection); + GroupBuilder.createCollectionSubmitterGroup(context, collection).build(); context.restoreAuthSystemState(); getClient().perform(get("/api/core/collections/" + collection.getID() + "/submittersGroup")) @@ -563,7 +559,7 @@ public void getCollectionSubmittersGroupUnAuthorizedTest() throws Exception { @Test public void getCollectionSubmittersGroupForbiddenTest() throws Exception { context.turnOffAuthorisationSystem(); - collectionService.createSubmitters(context, collection); + GroupBuilder.createCollectionSubmitterGroup(context, collection).build(); context.restoreAuthSystemState(); String token = getAuthToken(eperson.getEmail(), password); @@ -860,7 +856,7 @@ public void postCollectionSubmittersGroupCreateSubmittersGroupUnProcessablePerma @Test public void deleteCollectionSubmitterGroupTest() throws Exception { context.turnOffAuthorisationSystem(); - Group submittersGroup = collectionService.createSubmitters(context, collection); + GroupBuilder.createCollectionSubmitterGroup(context, collection).build(); context.restoreAuthSystemState(); String token = getAuthToken(admin.getEmail(), password); @@ -875,7 +871,7 @@ public void deleteCollectionSubmitterGroupTest() throws Exception { @Test public void deleteCollectionSubmittersGroupTestParentCommunityAdmin() throws Exception { context.turnOffAuthorisationSystem(); - Group submittersGroup = collectionService.createSubmitters(context, collection); + GroupBuilder.createCollectionSubmitterGroup(context, collection).build(); authorizeService.addPolicy(context, parentCommunity, Constants.ADMIN, eperson); context.restoreAuthSystemState(); @@ -890,7 +886,7 @@ public void deleteCollectionSubmittersGroupTestParentCommunityAdmin() throws Exc @Test public void deleteCollectionSubmittersGroupTestCollectionAdmin() throws Exception { context.turnOffAuthorisationSystem(); - Group submittersGroup = collectionService.createSubmitters(context, collection); + GroupBuilder.createCollectionSubmitterGroup(context, collection).build(); authorizeService.addPolicy(context, collection, Constants.ADMIN, eperson); context.restoreAuthSystemState(); @@ -905,7 +901,7 @@ public void deleteCollectionSubmittersGroupTestCollectionAdmin() throws Exceptio @Test public void deleteCollectionSubmittersGroupUnAuthorizedTest() throws Exception { context.turnOffAuthorisationSystem(); - Group submittersGroup = collectionService.createSubmitters(context, collection); + Group submittersGroup = GroupBuilder.createCollectionSubmitterGroup(context, collection).build(); context.restoreAuthSystemState(); getClient().perform(delete("/api/core/collections/" + collection.getID() + "/submittersGroup")) @@ -924,7 +920,7 @@ public void deleteCollectionSubmittersGroupUnAuthorizedTest() throws Exception { @Test public void deleteCollectionSubmittersGroupForbiddenTest() throws Exception { context.turnOffAuthorisationSystem(); - Group submittersGroup = collectionService.createSubmitters(context, collection); + Group submittersGroup = GroupBuilder.createCollectionSubmitterGroup(context, collection).build(); context.restoreAuthSystemState(); String token = getAuthToken(eperson.getEmail(), password); @@ -945,7 +941,7 @@ public void deleteCollectionSubmittersGroupForbiddenTest() throws Exception { @Test public void deleteCollectionSubmittersGroupNotFoundTest() throws Exception { context.turnOffAuthorisationSystem(); - Group submittersGroup = collectionService.createSubmitters(context, collection); + GroupBuilder.createCollectionSubmitterGroup(context, collection).build(); context.restoreAuthSystemState(); String token = getAuthToken(eperson.getEmail(), password); @@ -961,7 +957,8 @@ public void getCollectionItemReadGroupTest() throws Exception { String itemGroupString = "ITEM"; int defaultItemRead = Constants.DEFAULT_ITEM_READ; - Group role = collectionService.createDefaultReadGroup(context, collection, itemGroupString, defaultItemRead); + Group role = GroupBuilder.createCollectionDefaultReadGroup(context, collection, + itemGroupString, defaultItemRead).build(); context.restoreAuthSystemState(); String token = getAuthToken(admin.getEmail(), password); @@ -977,7 +974,8 @@ public void getCollectionDefaultItemReadGroupTestParentCommunityAdmin() throws E String itemGroupString = "ITEM"; int defaultItemRead = Constants.DEFAULT_ITEM_READ; - Group role = collectionService.createDefaultReadGroup(context, collection, itemGroupString, defaultItemRead); + Group role = GroupBuilder.createCollectionDefaultReadGroup(context, collection, + itemGroupString, defaultItemRead).build(); authorizeService.addPolicy(context, parentCommunity, Constants.ADMIN, eperson); context.restoreAuthSystemState(); @@ -995,7 +993,8 @@ public void getCollectionDefaultItemReadGroupTestCollectionAdmin() throws Except String itemGroupString = "ITEM"; int defaultItemRead = Constants.DEFAULT_ITEM_READ; - Group role = collectionService.createDefaultReadGroup(context, collection, itemGroupString, defaultItemRead); + Group role = GroupBuilder.createCollectionDefaultReadGroup(context, collection, + itemGroupString, defaultItemRead).build(); authorizeService.addPolicy(context, collection, Constants.ADMIN, eperson); context.restoreAuthSystemState(); @@ -1012,7 +1011,8 @@ public void getCollectionDefaultItemReadGroupUnAuthorizedTest() throws Exception String itemGroupString = "ITEM"; int defaultItemRead = Constants.DEFAULT_ITEM_READ; - Group role = collectionService.createDefaultReadGroup(context, collection, itemGroupString, defaultItemRead); + GroupBuilder.createCollectionDefaultReadGroup(context, collection, + itemGroupString, defaultItemRead).build(); context.restoreAuthSystemState(); getClient().perform(get("/api/core/collections/" + collection.getID() + "/itemReadGroup")) @@ -1025,7 +1025,8 @@ public void getCollectionDefaultItemReadGroupForbiddenTest() throws Exception { String itemGroupString = "ITEM"; int defaultItemRead = Constants.DEFAULT_ITEM_READ; - Group role = collectionService.createDefaultReadGroup(context, collection, itemGroupString, defaultItemRead); + GroupBuilder.createCollectionDefaultReadGroup(context, collection, + itemGroupString, defaultItemRead).build(); context.restoreAuthSystemState(); String token = getAuthToken(eperson.getEmail(), password); @@ -1321,7 +1322,7 @@ public void deleteCollectionDefaultItemReadGroupTest() throws Exception { String itemGroupString = "ITEM"; int defaultItemRead = Constants.DEFAULT_ITEM_READ; - Group role = collectionService.createDefaultReadGroup(context, collection, itemGroupString, defaultItemRead); + GroupBuilder.createCollectionDefaultReadGroup(context, collection, itemGroupString, defaultItemRead).build(); context.restoreAuthSystemState(); String token = getAuthToken(admin.getEmail(), password); @@ -1345,7 +1346,7 @@ public void deleteCollectionDefaultItemReadGroupTestParentCommunityAdmin() throw String itemGroupString = "ITEM"; int defaultItemRead = Constants.DEFAULT_ITEM_READ; - Group role = collectionService.createDefaultReadGroup(context, collection, itemGroupString, defaultItemRead); + GroupBuilder.createCollectionDefaultReadGroup(context, collection, itemGroupString, defaultItemRead).build(); authorizeService.addPolicy(context, parentCommunity, Constants.ADMIN, eperson); context.restoreAuthSystemState(); @@ -1367,7 +1368,7 @@ public void deleteCollectionDefaultItemReadGroupTestCollectionAdmin() throws Exc String itemGroupString = "ITEM"; int defaultItemRead = Constants.DEFAULT_ITEM_READ; - Group role = collectionService.createDefaultReadGroup(context, collection, itemGroupString, defaultItemRead); + GroupBuilder.createCollectionDefaultReadGroup(context, collection, itemGroupString, defaultItemRead).build(); authorizeService.addPolicy(context, collection, Constants.ADMIN, eperson); context.restoreAuthSystemState(); @@ -1389,7 +1390,8 @@ public void deleteCollectionDefaultItemReadGroupUnAuthorizedTest() throws Except String itemGroupString = "ITEM"; int defaultItemRead = Constants.DEFAULT_ITEM_READ; - Group role = collectionService.createDefaultReadGroup(context, collection, itemGroupString, defaultItemRead); + Group role = GroupBuilder.createCollectionDefaultReadGroup(context, collection, + itemGroupString, defaultItemRead).build(); context.restoreAuthSystemState(); getClient().perform(delete("/api/core/collections/" + collection.getID() + "/itemReadGroup")) @@ -1408,7 +1410,8 @@ public void deleteCollectionDefaultItemReadGroupForbiddenTest() throws Exception String itemGroupString = "ITEM"; int defaultItemRead = Constants.DEFAULT_ITEM_READ; - Group role = collectionService.createDefaultReadGroup(context, collection, itemGroupString, defaultItemRead); + Group role = GroupBuilder.createCollectionDefaultReadGroup(context, collection, + itemGroupString, defaultItemRead).build(); context.restoreAuthSystemState(); String token = getAuthToken(eperson.getEmail(), password); @@ -1430,7 +1433,7 @@ public void deleteCollectionDefaultItemReadGroupNotFoundTest() throws Exception String itemGroupString = "ITEM"; int defaultItemRead = Constants.DEFAULT_ITEM_READ; - Group role = collectionService.createDefaultReadGroup(context, collection, itemGroupString, defaultItemRead); + GroupBuilder.createCollectionDefaultReadGroup(context, collection, itemGroupString, defaultItemRead).build(); context.restoreAuthSystemState(); String token = getAuthToken(eperson.getEmail(), password); @@ -1445,8 +1448,8 @@ public void getCollectionBitstreamReadGroupTest() throws Exception { String bitstreamGroupString = "BITSTREAM"; int defaultBitstreamRead = Constants.DEFAULT_BITSTREAM_READ; - Group role = collectionService.createDefaultReadGroup(context, collection, bitstreamGroupString, - defaultBitstreamRead); + Group role = GroupBuilder.createCollectionDefaultReadGroup(context, collection, + bitstreamGroupString, defaultBitstreamRead).build(); context.restoreAuthSystemState(); String token = getAuthToken(admin.getEmail(), password); @@ -1462,8 +1465,8 @@ public void getCollectionDefaultBitstreamReadGroupTestParentCommunityAdmin() thr String bitstreamGroupString = "BITSTREAM"; int defaultBitstreamRead = Constants.DEFAULT_BITSTREAM_READ; - Group role = collectionService.createDefaultReadGroup(context, collection, bitstreamGroupString, - defaultBitstreamRead); + Group role = GroupBuilder.createCollectionDefaultReadGroup(context, collection, + bitstreamGroupString, defaultBitstreamRead).build(); authorizeService.addPolicy(context, parentCommunity, Constants.ADMIN, eperson); context.restoreAuthSystemState(); @@ -1480,8 +1483,8 @@ public void getCollectionDefaultBitstreamReadGroupTestCollectionAdmin() throws E String bitstreamGroupString = "BITSTREAM"; int defaultBitstreamRead = Constants.DEFAULT_BITSTREAM_READ; - Group role = collectionService.createDefaultReadGroup(context, collection, bitstreamGroupString, - defaultBitstreamRead); + Group role = GroupBuilder.createCollectionDefaultReadGroup(context, collection, + bitstreamGroupString, defaultBitstreamRead).build(); authorizeService.addPolicy(context, collection, Constants.ADMIN, eperson); context.restoreAuthSystemState(); @@ -1498,8 +1501,8 @@ public void getCollectionDefaultBitstreamReadGroupUnAuthorizedTest() throws Exce String bitstreamGroupString = "BITSTREAM"; int defaultBitstreamRead = Constants.DEFAULT_BITSTREAM_READ; - Group role = collectionService.createDefaultReadGroup(context, collection, bitstreamGroupString, - defaultBitstreamRead); + GroupBuilder.createCollectionDefaultReadGroup(context, collection, + bitstreamGroupString, defaultBitstreamRead).build(); context.restoreAuthSystemState(); getClient().perform(get("/api/core/collections/" + collection.getID() + "/bitstreamReadGroup")) @@ -1512,8 +1515,8 @@ public void getCollectionDefaultBitstreamReadGroupForbiddenTest() throws Excepti String bitstreamGroupString = "BITSTREAM"; int defaultBitstreamRead = Constants.DEFAULT_BITSTREAM_READ; - Group role = collectionService.createDefaultReadGroup(context, collection, bitstreamGroupString, - defaultBitstreamRead); + GroupBuilder.createCollectionDefaultReadGroup(context, collection, + bitstreamGroupString, defaultBitstreamRead).build(); context.restoreAuthSystemState(); String token = getAuthToken(eperson.getEmail(), password); @@ -1811,8 +1814,8 @@ public void deleteCollectionDefaultBitstreamReadGroupTest() throws Exception { String bitstreamGroupString = "BITSTREAM"; int defaultBitstreamRead = Constants.DEFAULT_BITSTREAM_READ; - Group role = collectionService.createDefaultReadGroup(context, collection, bitstreamGroupString, - defaultBitstreamRead); + GroupBuilder.createCollectionDefaultReadGroup(context, collection, + bitstreamGroupString, defaultBitstreamRead).build(); context.restoreAuthSystemState(); String token = getAuthToken(admin.getEmail(), password); @@ -1835,8 +1838,8 @@ public void deleteCollectionDefaultBitstreamReadGroupTestParentCommunityAdmin() String bitstreamGroupString = "BITSTREAM"; int defaultBitstreamRead = Constants.DEFAULT_BITSTREAM_READ; - Group role = collectionService.createDefaultReadGroup(context, collection, bitstreamGroupString, - defaultBitstreamRead); + GroupBuilder.createCollectionDefaultReadGroup(context, collection, + bitstreamGroupString, defaultBitstreamRead).build(); authorizeService.addPolicy(context, parentCommunity, Constants.ADMIN, eperson); context.restoreAuthSystemState(); @@ -1859,8 +1862,8 @@ public void deleteCollectionDefaultBitstreamReadGroupTestCollectionAdmin() throw String bitstreamGroupString = "BITSTREAM"; int defaultBitstreamRead = Constants.DEFAULT_BITSTREAM_READ; - Group role = collectionService.createDefaultReadGroup(context, collection, bitstreamGroupString, - defaultBitstreamRead); + GroupBuilder.createCollectionDefaultReadGroup(context, collection, + bitstreamGroupString, defaultBitstreamRead).build(); authorizeService.addPolicy(context, collection, Constants.ADMIN, eperson); context.restoreAuthSystemState(); @@ -1882,8 +1885,8 @@ public void deleteCollectionDefaultBitstreamReadGroupUnAuthorizedTest() throws E String bitstreamGroupString = "BITSTREAM"; int defaultBitstreamRead = Constants.DEFAULT_BITSTREAM_READ; - Group role = collectionService.createDefaultReadGroup(context, collection, bitstreamGroupString, - defaultBitstreamRead); + Group role = GroupBuilder.createCollectionDefaultReadGroup(context, collection, + bitstreamGroupString, defaultBitstreamRead).build(); context.restoreAuthSystemState(); getClient().perform(delete("/api/core/collections/" + collection.getID() + "/bitstreamReadGroup")) @@ -1902,8 +1905,8 @@ public void deleteCollectionDefaultBitstreamReadGroupForbiddenTest() throws Exce String bitstreamGroupString = "BITSTREAM"; int defaultBitstreamRead = Constants.DEFAULT_BITSTREAM_READ; - Group role = collectionService.createDefaultReadGroup(context, collection, bitstreamGroupString, - defaultBitstreamRead); + GroupBuilder.createCollectionDefaultReadGroup(context, collection, + bitstreamGroupString, defaultBitstreamRead).build(); context.restoreAuthSystemState(); String token = getAuthToken(eperson.getEmail(), password); @@ -1918,8 +1921,8 @@ public void deleteCollectionDefaultBitstreamReadGroupNotFoundTest() throws Excep String bitstreamGroupString = "BITSTREAM"; int defaultBitstreamRead = Constants.DEFAULT_BITSTREAM_READ; - Group role = collectionService.createDefaultReadGroup(context, collection, bitstreamGroupString, - defaultBitstreamRead); + GroupBuilder.createCollectionDefaultReadGroup(context, collection, + bitstreamGroupString, defaultBitstreamRead).build(); context.restoreAuthSystemState(); String token = getAuthToken(eperson.getEmail(), password); @@ -1931,7 +1934,7 @@ public void deleteCollectionDefaultBitstreamReadGroupNotFoundTest() throws Excep public void getWorkflowGroupForCollectionAndRole() throws Exception { context.turnOffAuthorisationSystem(); - Group group = workflowService.createWorkflowRoleGroup(context, collection, "reviewer"); + Group group = GroupBuilder.createCollectionWorkflowRoleGroup(context, collection, "reviewer").build(); context.restoreAuthSystemState(); String token = getAuthToken(admin.getEmail(), password); @@ -1944,7 +1947,7 @@ public void getWorkflowGroupForCollectionAndRole() throws Exception { @Test public void getWorkflowGroupForCollectionAndRoleParentCommunityAdmin() throws Exception { context.turnOffAuthorisationSystem(); - Group group = workflowService.createWorkflowRoleGroup(context, collection, "reviewer"); + Group group = GroupBuilder.createCollectionWorkflowRoleGroup(context, collection, "reviewer").build(); authorizeService.addPolicy(context, parentCommunity, Constants.ADMIN, eperson); context.restoreAuthSystemState(); @@ -1958,7 +1961,7 @@ public void getWorkflowGroupForCollectionAndRoleParentCommunityAdmin() throws Ex @Test public void getWorkflowGroupForCollectionAndRoleWrongUUIDCollectionNotFound() throws Exception { context.turnOffAuthorisationSystem(); - Group group = workflowService.createWorkflowRoleGroup(context, collection, "reviewer"); + GroupBuilder.createCollectionWorkflowRoleGroup(context, collection, "reviewer").build(); context.restoreAuthSystemState(); String token = getAuthToken(eperson.getEmail(), password); @@ -1979,7 +1982,7 @@ public void getWorkflowGroupForCollectionAndRoleWrongRoleNotFound() throws Excep public void getWorkflowGroupCommunityAdmin() throws Exception { context.turnOffAuthorisationSystem(); - Group group = workflowService.createWorkflowRoleGroup(context, collection, "reviewer"); + Group group = GroupBuilder.createCollectionWorkflowRoleGroup(context, collection, "reviewer").build(); authorizeService.addPolicy(context, parentCommunity, Constants.ADMIN, eperson); context.restoreAuthSystemState(); @@ -1994,7 +1997,7 @@ public void getWorkflowGroupCommunityAdmin() throws Exception { @Test public void getWorkflowGroupCollectionAdmin() throws Exception { context.turnOffAuthorisationSystem(); - Group group = workflowService.createWorkflowRoleGroup(context, collection, "reviewer"); + Group group = GroupBuilder.createCollectionWorkflowRoleGroup(context, collection, "reviewer").build(); authorizeService.addPolicy(context, collection, Constants.ADMIN, eperson); context.restoreAuthSystemState(); @@ -2009,7 +2012,7 @@ public void getWorkflowGroupCollectionAdmin() throws Exception { @Test public void getWorkflowGroupUnAuthorized() throws Exception { context.turnOffAuthorisationSystem(); - Group group = workflowService.createWorkflowRoleGroup(context, collection, "reviewer"); + GroupBuilder.createCollectionWorkflowRoleGroup(context, collection, "reviewer").build(); context.restoreAuthSystemState(); getClient().perform(get("/api/core/collections/" + collection.getID() + "/workflowGroups/reviewer")) @@ -2019,7 +2022,7 @@ public void getWorkflowGroupUnAuthorized() throws Exception { @Test public void getWorkflowGroupForbidden() throws Exception { context.turnOffAuthorisationSystem(); - Group group = workflowService.createWorkflowRoleGroup(context, collection, "reviewer"); + GroupBuilder.createCollectionWorkflowRoleGroup(context, collection, "reviewer").build(); context.restoreAuthSystemState(); String token = getAuthToken(eperson.getEmail(), password); @@ -2324,7 +2327,7 @@ public void postCollectionWorkflowGroupCreateWorkflowGroupUnProcessablePermanent @Test public void deleteCollectionWorkflowGroupTest() throws Exception { context.turnOffAuthorisationSystem(); - Group group = workflowService.createWorkflowRoleGroup(context, collection, "reviewer"); + GroupBuilder.createCollectionWorkflowRoleGroup(context, collection, "reviewer").build(); context.restoreAuthSystemState(); String token = getAuthToken(admin.getEmail(), password); @@ -2339,7 +2342,7 @@ public void deleteCollectionWorkflowGroupTest() throws Exception { @Test public void deleteCollectionWorkflowGroupTestParentCommunityAdmin() throws Exception { context.turnOffAuthorisationSystem(); - Group group = workflowService.createWorkflowRoleGroup(context, collection, "reviewer"); + GroupBuilder.createCollectionWorkflowRoleGroup(context, collection, "reviewer").build(); authorizeService.addPolicy(context, parentCommunity, Constants.ADMIN, eperson); context.restoreAuthSystemState(); @@ -2355,7 +2358,7 @@ public void deleteCollectionWorkflowGroupTestParentCommunityAdmin() throws Excep @Test public void deleteCollectionWorkflowGroupTestCollectionAdmin() throws Exception { context.turnOffAuthorisationSystem(); - Group group = workflowService.createWorkflowRoleGroup(context, collection, "reviewer"); + GroupBuilder.createCollectionWorkflowRoleGroup(context, collection, "reviewer").build(); authorizeService.addPolicy(context, collection, Constants.ADMIN, eperson); context.restoreAuthSystemState(); @@ -2371,7 +2374,7 @@ public void deleteCollectionWorkflowGroupTestCollectionAdmin() throws Exception @Test public void deleteCollectionWorkflowGroupUnAuthorizedTest() throws Exception { context.turnOffAuthorisationSystem(); - Group group = workflowService.createWorkflowRoleGroup(context, collection, "reviewer"); + Group group = GroupBuilder.createCollectionWorkflowRoleGroup(context, collection, "reviewer").build(); context.restoreAuthSystemState(); getClient().perform(delete("/api/core/collections/" + collection.getID() + "/workflowGroups/reviewer")) @@ -2387,7 +2390,7 @@ public void deleteCollectionWorkflowGroupUnAuthorizedTest() throws Exception { @Test public void deleteCollectionWorkflowGroupForbiddenTest() throws Exception { context.turnOffAuthorisationSystem(); - Group group = workflowService.createWorkflowRoleGroup(context, collection, "reviewer"); + Group group = GroupBuilder.createCollectionWorkflowRoleGroup(context, collection, "reviewer").build(); context.restoreAuthSystemState(); String token = getAuthToken(eperson.getEmail(), password); @@ -2406,7 +2409,7 @@ public void deleteCollectionWorkflowGroupForbiddenTest() throws Exception { @Test public void deleteCollectionWorkflowGroupNotFoundTest() throws Exception { context.turnOffAuthorisationSystem(); - Group group = workflowService.createWorkflowRoleGroup(context, collection, "reviewer"); + GroupBuilder.createCollectionWorkflowRoleGroup(context, collection, "reviewer").build(); context.restoreAuthSystemState(); String token = getAuthToken(eperson.getEmail(), password); @@ -2418,7 +2421,7 @@ public void deleteCollectionWorkflowGroupNotFoundTest() throws Exception { @Test public void deleteCollectionWorkflowGroupWithPooledTaskTest() throws Exception { context.turnOffAuthorisationSystem(); - Group reviewer = workflowService.createWorkflowRoleGroup(context, collection, "reviewer"); + Group reviewer = GroupBuilder.createCollectionWorkflowRoleGroup(context, collection, "reviewer").build(); // Submit an Item into the workflow -> moves to the "reviewer" step's pool. // The role must have at least one EPerson, otherwise the WSI gets archived immediately From 365987456164b58ed508e6aaed9816b0dda27efb Mon Sep 17 00:00:00 2001 From: Kim Shepherd Date: Fri, 6 Sep 2024 15:35:43 +0200 Subject: [PATCH 085/979] #9806: Builders for comm, coll, group in CommunityAdminGroupRestControllerIT (cherry picked from commit 2d9988f77c52537f46888f27e35dbc0360299835) --- .../CommunityAdminGroupRestControllerIT.java | 52 +++++++++---------- 1 file changed, 24 insertions(+), 28 deletions(-) diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/CommunityAdminGroupRestControllerIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/CommunityAdminGroupRestControllerIT.java index 37548553b143..074a7e6a3557 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/CommunityAdminGroupRestControllerIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/CommunityAdminGroupRestControllerIT.java @@ -34,8 +34,6 @@ import org.dspace.builder.GroupBuilder; import org.dspace.content.Collection; import org.dspace.content.Community; -import org.dspace.content.service.CollectionService; -import org.dspace.content.service.CommunityService; import org.dspace.core.Constants; import org.dspace.eperson.EPerson; import org.dspace.eperson.Group; @@ -49,19 +47,12 @@ public class CommunityAdminGroupRestControllerIT extends AbstractControllerIntegrationTest { - - @Autowired - private CommunityService communityService; - @Autowired private GroupService groupService; @Autowired private AuthorizeService authorizeService; - @Autowired - private CollectionService collectionService; - @Autowired private ConfigurationService configurationService; @@ -78,7 +69,7 @@ public void setup() { @Test public void getCommunityAdminGroupTest() throws Exception { context.turnOffAuthorisationSystem(); - Group adminGroup = communityService.createAdministrators(context, parentCommunity); + Group adminGroup = GroupBuilder.createCommunityAdminGroup(context, parentCommunity).build(); context.restoreAuthSystemState(); String token = getAuthToken(admin.getEmail(), password); @@ -91,7 +82,8 @@ public void getCommunityAdminGroupTest() throws Exception { @Test public void getCommunityAdminGroupTestCommunityAdmin() throws Exception { context.turnOffAuthorisationSystem(); - Group adminGroup = communityService.createAdministrators(context, parentCommunity); + Group adminGroup = GroupBuilder.createCommunityAdminGroup(context, parentCommunity).build(); + // TODO: this should actually be "add member", not directly setting a policy, right? authorizeService.addPolicy(context, parentCommunity, Constants.ADMIN, eperson); context.restoreAuthSystemState(); @@ -106,7 +98,7 @@ public void getCommunityAdminGroupTestCommunityAdmin() throws Exception { @Test public void getCommunityAdminGroupUnAuthorizedTest() throws Exception { context.turnOffAuthorisationSystem(); - communityService.createAdministrators(context, parentCommunity); + GroupBuilder.createCommunityAdminGroup(context, parentCommunity).build(); context.restoreAuthSystemState(); getClient().perform(get("/api/core/communities/" + parentCommunity.getID() + "/adminGroup")) @@ -116,7 +108,7 @@ public void getCommunityAdminGroupUnAuthorizedTest() throws Exception { @Test public void getCommunityAdminGroupForbiddenTest() throws Exception { context.turnOffAuthorisationSystem(); - communityService.createAdministrators(context, parentCommunity); + GroupBuilder.createCommunityAdminGroup(context, parentCommunity).build(); context.restoreAuthSystemState(); String token = getAuthToken(eperson.getEmail(), password); getClient(token).perform(get("/api/core/communities/" + parentCommunity.getID() + "/adminGroup")) @@ -379,7 +371,7 @@ public void postCommunityAdminGroupCreateAdminGroupUnProcessablePermanent() thro @Test public void deleteCommunityAdminGroupTest() throws Exception { context.turnOffAuthorisationSystem(); - Group adminGroup = communityService.createAdministrators(context, parentCommunity); + GroupBuilder.createCommunityAdminGroup(context, parentCommunity).build(); context.restoreAuthSystemState(); String token = getAuthToken(admin.getEmail(), password); @@ -397,7 +389,7 @@ public void deleteCommunityAdminGroupTestCommunityAdmin() throws Exception { Community child1 = CommunityBuilder.createSubCommunity(context, parentCommunity) .withName("Sub Community") .build(); - Group adminGroup = communityService.createAdministrators(context, child1); + GroupBuilder.createCommunityAdminGroup(context, child1).build(); authorizeService.addPolicy(context, parentCommunity, Constants.ADMIN, eperson); context.restoreAuthSystemState(); @@ -412,7 +404,7 @@ public void deleteCommunityAdminGroupTestCommunityAdmin() throws Exception { @Test public void deleteCommunityAdminGroupUnAuthorizedTest() throws Exception { context.turnOffAuthorisationSystem(); - Group adminGroup = communityService.createAdministrators(context, parentCommunity); + Group adminGroup = GroupBuilder.createCommunityAdminGroup(context, parentCommunity).build(); context.restoreAuthSystemState(); getClient().perform(delete("/api/core/communities/" + parentCommunity.getID() + "/adminGroup")) @@ -429,7 +421,7 @@ public void deleteCommunityAdminGroupUnAuthorizedTest() throws Exception { @Test public void deleteCommunityAdminGroupForbiddenTest() throws Exception { context.turnOffAuthorisationSystem(); - Group adminGroup = communityService.createAdministrators(context, parentCommunity); + Group adminGroup = GroupBuilder.createCommunityAdminGroup(context, parentCommunity).build(); context.restoreAuthSystemState(); String token = getAuthToken(eperson.getEmail(), password); @@ -449,7 +441,7 @@ public void deleteCommunityAdminGroupForbiddenTest() throws Exception { @Test public void deleteCommunityAdminGroupNotFoundTest() throws Exception { context.turnOffAuthorisationSystem(); - Group adminGroup = communityService.createAdministrators(context, parentCommunity); + GroupBuilder.createCommunityAdminGroup(context, parentCommunity).build(); context.restoreAuthSystemState(); String token = getAuthToken(eperson.getEmail(), password); @@ -462,7 +454,7 @@ public void deleteCommunityAdminGroupNotFoundTest() throws Exception { public void communityAdminAddMembersToCommunityAdminGroupPropertySetToFalse() throws Exception { context.turnOffAuthorisationSystem(); - Group adminGroup = communityService.createAdministrators(context, parentCommunity); + Group adminGroup = GroupBuilder.createCommunityAdminGroup(context, parentCommunity).build(); authorizeService.addPolicy(context, parentCommunity, Constants.ADMIN, eperson); EPerson ePerson = EPersonBuilder.createEPerson(context).withEmail("testToAdd@test.com").build(); configurationService.setProperty("core.authorization.community-admin.admin-group", false); @@ -489,7 +481,7 @@ public void communityAdminAddMembersToCommunityAdminGroupPropertySetToFalse() th public void communityAdminRemoveMembersFromCommunityAdminGroupPropertySetToFalse() throws Exception { context.turnOffAuthorisationSystem(); - Group adminGroup = communityService.createAdministrators(context, parentCommunity); + Group adminGroup = GroupBuilder.createCommunityAdminGroup(context, parentCommunity).build(); authorizeService.addPolicy(context, parentCommunity, Constants.ADMIN, eperson); EPerson ePerson = EPersonBuilder.createEPerson(context).withEmail("testToAdd@test.com").build(); context.restoreAuthSystemState(); @@ -526,7 +518,7 @@ public void communityAdminRemoveMembersFromCommunityAdminGroupPropertySetToFalse public void communityAdminAddChildGroupToCommunityAdminGroupPropertySetToFalse() throws Exception { context.turnOffAuthorisationSystem(); - Group adminGroup = communityService.createAdministrators(context, parentCommunity); + Group adminGroup = GroupBuilder.createCommunityAdminGroup(context, parentCommunity).build(); authorizeService.addPolicy(context, parentCommunity, Constants.ADMIN, eperson); Group group = GroupBuilder.createGroup(context).withName("testGroup").build(); configurationService.setProperty("core.authorization.community-admin.admin-group", false); @@ -554,7 +546,7 @@ public void communityAdminAddChildGroupToCommunityAdminGroupPropertySetToFalse() public void communityAdminRemoveChildGroupFromCommunityAdminGroupPropertySetToFalse() throws Exception { context.turnOffAuthorisationSystem(); - Group adminGroup = communityService.createAdministrators(context, parentCommunity); + Group adminGroup = GroupBuilder.createCommunityAdminGroup(context, parentCommunity).build(); authorizeService.addPolicy(context, parentCommunity, Constants.ADMIN, eperson); Group group = GroupBuilder.createGroup(context).withName("testGroup").build(); context.restoreAuthSystemState(); @@ -591,7 +583,9 @@ public void communityAdminRemoveChildGroupFromCommunityAdminGroupPropertySetToFa public void communityAdminAddChildGroupToCollectionAdminGroupSuccess() throws Exception { context.turnOffAuthorisationSystem(); - Group adminGroup = collectionService.createAdministrators(context, collection); + // TODO: Why is this test in CommunityAdmin? it seems to purely be a collection group test? + // copy paste gone wrong and we should actually be testing for community admin group sub? + Group adminGroup = GroupBuilder.createCollectionAdminGroup(context, collection).build(); authorizeService.addPolicy(context, parentCommunity, Constants.ADMIN, eperson); Group group = GroupBuilder.createGroup(context).withName("testGroup").build(); context.restoreAuthSystemState(); @@ -617,7 +611,9 @@ public void communityAdminAddChildGroupToCollectionAdminGroupSuccess() throws Ex public void communityAdminRemoveChildGroupFromCollectionAdminGroupSuccess() throws Exception { context.turnOffAuthorisationSystem(); - Group adminGroup = collectionService.createAdministrators(context, collection); + // TODO: Why is this test in CommunityAdmin? it seems to purely be a collection group test? + // copy paste gone wrong and we should actually be testing for community admin group sub? + Group adminGroup = GroupBuilder.createCollectionAdminGroup(context, collection).build(); authorizeService.addPolicy(context, parentCommunity, Constants.ADMIN, eperson); Group group = GroupBuilder.createGroup(context).withName("testGroup").build(); context.restoreAuthSystemState(); @@ -653,7 +649,7 @@ public void communityAdminRemoveChildGroupFromCollectionAdminGroupSuccess() thro public void communityAdminAddMembersToCollectionAdminGroupPropertySetToFalse() throws Exception { context.turnOffAuthorisationSystem(); - Group adminGroup = collectionService.createAdministrators(context, collection); + Group adminGroup = GroupBuilder.createCollectionAdminGroup(context, collection).build(); authorizeService.addPolicy(context, parentCommunity, Constants.ADMIN, eperson); EPerson ePerson = EPersonBuilder.createEPerson(context).withEmail("testToAdd@test.com").build(); configurationService.setProperty("core.authorization.community-admin.collection.admin-group", false); @@ -681,7 +677,7 @@ public void communityAdminAddMembersToCollectionAdminGroupPropertySetToFalse() t public void communityAdminRemoveMembersFromCollectionAdminGroupPropertySetToFalse() throws Exception { context.turnOffAuthorisationSystem(); - Group adminGroup = collectionService.createAdministrators(context, collection); + Group adminGroup = GroupBuilder.createCollectionAdminGroup(context, collection).build(); authorizeService.addPolicy(context, parentCommunity, Constants.ADMIN, eperson); EPerson ePerson = EPersonBuilder.createEPerson(context).withEmail("testToAdd@test.com").build(); context.restoreAuthSystemState(); @@ -719,7 +715,7 @@ public void communityAdminRemoveMembersFromCollectionAdminGroupPropertySetToFals public void communityAdminAddChildGroupToCollectionAdminGroupPropertySetToFalse() throws Exception { context.turnOffAuthorisationSystem(); - Group adminGroup = collectionService.createAdministrators(context, collection); + Group adminGroup = GroupBuilder.createCollectionAdminGroup(context, collection).build(); authorizeService.addPolicy(context, parentCommunity, Constants.ADMIN, eperson); Group group = GroupBuilder.createGroup(context).withName("testGroup").build(); configurationService.setProperty("core.authorization.community-admin.collection.admin-group", false); @@ -748,7 +744,7 @@ public void communityAdminAddChildGroupToCollectionAdminGroupPropertySetToFalse( public void communityAdminRemoveChildGroupFromCollectionAdminGroupPropertySetToFalse() throws Exception { context.turnOffAuthorisationSystem(); - Group adminGroup = collectionService.createAdministrators(context, collection); + Group adminGroup = GroupBuilder.createCollectionAdminGroup(context, collection).build(); authorizeService.addPolicy(context, parentCommunity, Constants.ADMIN, eperson); Group group = GroupBuilder.createGroup(context).withName("testGroup").build(); context.restoreAuthSystemState(); From 76719f73f2e8e01aba9ad750fa6f1b5a2f34c4d3 Mon Sep 17 00:00:00 2001 From: Kim Shepherd Date: Fri, 6 Sep 2024 15:58:59 +0200 Subject: [PATCH 086/979] #9806: Update object cleanup in GroupRestRepositoryIT (cherry picked from commit 8cfb433c40ad01f348461a2c656665d1891de398) --- .../app/rest/GroupRestRepositoryIT.java | 952 +++++------------- 1 file changed, 239 insertions(+), 713 deletions(-) diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/GroupRestRepositoryIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/GroupRestRepositoryIT.java index bdd13b97ff79..ff8aea493aef 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/GroupRestRepositoryIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/GroupRestRepositoryIT.java @@ -59,14 +59,11 @@ import org.dspace.content.Community; import org.dspace.content.Item; import org.dspace.content.MetadataSchemaEnum; -import org.dspace.content.factory.ContentServiceFactory; -import org.dspace.content.service.CommunityService; import org.dspace.core.Constants; import org.dspace.core.I18nUtil; 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.hamcrest.Matchers; @@ -757,263 +754,136 @@ public void addChildGroupTest() throws Exception { @Test public void addChildGroupCommunityAdminTest() throws Exception { - CommunityService communityService = ContentServiceFactory.getInstance().getCommunityService(); GroupService groupService = EPersonServiceFactory.getInstance().getGroupService(); - Community community = null; - - Group parentGroup = null; - Group childGroup1 = null; - Group childGroup2 = null; - - try { - context.turnOffAuthorisationSystem(); - - community = CommunityBuilder.createCommunity(context).build(); - parentGroup = GroupBuilder.createCommunityAdminGroup(context, community) - .addMember(eperson) - .build(); - childGroup1 = GroupBuilder.createGroup(context).build(); - childGroup2 = GroupBuilder.createGroup(context).build(); - - groupService.update(context, parentGroup); - - context.commit(); - - parentGroup = context.reloadEntity(parentGroup); - childGroup1 = context.reloadEntity(childGroup1); - childGroup2 = context.reloadEntity(childGroup2); + context.turnOffAuthorisationSystem(); - context.restoreAuthSystemState(); - String authToken = getAuthToken(eperson.getEmail(), password); - getClient(authToken).perform( - post("/api/eperson/groups/" + parentGroup.getID() + "/subgroups") - .contentType(parseMediaType(TEXT_URI_LIST_VALUE)) - .content(REST_SERVER_URL + "eperson/groups/" + childGroup1.getID() + "/\n" - + REST_SERVER_URL + "eperson/groups/" + childGroup2.getID() - ) - ).andExpect(status().isNoContent()); + Community community = CommunityBuilder.createCommunity(context).build(); + Group parentGroup = GroupBuilder.createCommunityAdminGroup(context, community) + .addMember(eperson) + .build(); + Group childGroup1 = GroupBuilder.createGroup(context).build(); + Group childGroup2 = GroupBuilder.createGroup(context).build(); - parentGroup = context.reloadEntity(parentGroup); - childGroup1 = context.reloadEntity(childGroup1); - childGroup2 = context.reloadEntity(childGroup2); + context.restoreAuthSystemState(); + String authToken = getAuthToken(eperson.getEmail(), password); + getClient(authToken).perform( + post("/api/eperson/groups/" + parentGroup.getID() + "/subgroups") + .contentType(parseMediaType(TEXT_URI_LIST_VALUE)) + .content(REST_SERVER_URL + "eperson/groups/" + childGroup1.getID() + "/\n" + + REST_SERVER_URL + "eperson/groups/" + childGroup2.getID() + ) + ).andExpect(status().isNoContent()); - assertTrue( - groupService.isMember(parentGroup, childGroup1) - ); + parentGroup = context.reloadEntity(parentGroup); + childGroup1 = context.reloadEntity(childGroup1); + childGroup2 = context.reloadEntity(childGroup2); - assertTrue( - groupService.isMember(parentGroup, childGroup2) - ); + assertTrue( + groupService.isMember(parentGroup, childGroup1) + ); - } finally { - // TODO: Can we remove these lines now that we are creating them with the builder? - if (community != null) { - CommunityBuilder.deleteCommunity(community.getID()); - } - if (parentGroup != null) { - GroupBuilder.deleteGroup(parentGroup.getID()); - } - if (childGroup1 != null) { - GroupBuilder.deleteGroup(childGroup1.getID()); - } - if (childGroup2 != null) { - GroupBuilder.deleteGroup(childGroup2.getID()); - } - } + assertTrue( + groupService.isMember(parentGroup, childGroup2) + ); } @Test public void addChildGroupForbiddenTest() throws Exception { - GroupService groupService = EPersonServiceFactory.getInstance().getGroupService(); - - Group parentGroup = null; - Group childGroup1 = null; - Group childGroup2 = null; - - try { - context.turnOffAuthorisationSystem(); - - parentGroup = GroupBuilder.createGroup(context).build(); - childGroup1 = GroupBuilder.createGroup(context).build(); - childGroup2 = GroupBuilder.createGroup(context).build(); - - context.commit(); - - parentGroup = context.reloadEntity(parentGroup); - childGroup1 = context.reloadEntity(childGroup1); - childGroup2 = context.reloadEntity(childGroup2); + context.turnOffAuthorisationSystem(); - context.restoreAuthSystemState(); - String authToken = getAuthToken(eperson.getEmail(), password); - getClient(authToken).perform( - post("/api/eperson/groups/" + parentGroup.getID() + "/subgroups") - .contentType(parseMediaType(TEXT_URI_LIST_VALUE)) - .content(REST_SERVER_URL + "eperson/groups/" + childGroup1.getID() + "/\n" - + REST_SERVER_URL + "eperson/groups/" + childGroup2.getID() - ) - ).andExpect(status().isForbidden()); + Group parentGroup = GroupBuilder.createGroup(context).build(); + Group childGroup1 = GroupBuilder.createGroup(context).build(); + Group childGroup2 = GroupBuilder.createGroup(context).build(); - } finally { - if (parentGroup != null) { - GroupBuilder.deleteGroup(parentGroup.getID()); - } - if (childGroup1 != null) { - GroupBuilder.deleteGroup(childGroup1.getID()); - } - if (childGroup2 != null) { - GroupBuilder.deleteGroup(childGroup2.getID()); - } - } + context.restoreAuthSystemState(); + String authToken = getAuthToken(eperson.getEmail(), password); + getClient(authToken).perform( + post("/api/eperson/groups/" + parentGroup.getID() + "/subgroups") + .contentType(parseMediaType(TEXT_URI_LIST_VALUE)) + .content(REST_SERVER_URL + "eperson/groups/" + childGroup1.getID() + "/\n" + + REST_SERVER_URL + "eperson/groups/" + childGroup2.getID() + ) + ).andExpect(status().isForbidden()); } @Test public void addChildGroupUnauthorizedTest() throws Exception { - GroupService groupService = EPersonServiceFactory.getInstance().getGroupService(); - - Group parentGroup = null; - Group childGroup1 = null; - Group childGroup2 = null; - - try { - context.turnOffAuthorisationSystem(); - - parentGroup = GroupBuilder.createGroup(context).build(); - childGroup1 = GroupBuilder.createGroup(context).build(); - childGroup2 = GroupBuilder.createGroup(context).build(); + context.turnOffAuthorisationSystem(); - context.commit(); + Group parentGroup = GroupBuilder.createGroup(context).build(); + Group childGroup1 = GroupBuilder.createGroup(context).build(); + Group childGroup2 = GroupBuilder.createGroup(context).build(); - parentGroup = context.reloadEntity(parentGroup); - childGroup1 = context.reloadEntity(childGroup1); - childGroup2 = context.reloadEntity(childGroup2); + context.commit(); - context.restoreAuthSystemState(); - getClient().perform( - post("/api/eperson/groups/" + parentGroup.getID() + "/subgroups") - .contentType(parseMediaType(TEXT_URI_LIST_VALUE)) - .content(REST_SERVER_URL + "eperson/groups/" + childGroup1.getID() + "/\n" - + REST_SERVER_URL + "eperson/groups/" + childGroup2.getID() - ) - ).andExpect(status().isUnauthorized()); + parentGroup = context.reloadEntity(parentGroup); + childGroup1 = context.reloadEntity(childGroup1); + childGroup2 = context.reloadEntity(childGroup2); - } finally { - if (parentGroup != null) { - GroupBuilder.deleteGroup(parentGroup.getID()); - } - if (childGroup1 != null) { - GroupBuilder.deleteGroup(childGroup1.getID()); - } - if (childGroup2 != null) { - GroupBuilder.deleteGroup(childGroup2.getID()); - } - } + context.restoreAuthSystemState(); + getClient().perform( + post("/api/eperson/groups/" + parentGroup.getID() + "/subgroups") + .contentType(parseMediaType(TEXT_URI_LIST_VALUE)) + .content(REST_SERVER_URL + "eperson/groups/" + childGroup1.getID() + "/\n" + + REST_SERVER_URL + "eperson/groups/" + childGroup2.getID() + ) + ).andExpect(status().isUnauthorized()); } @Test public void addChildGroupNotFoundTest() throws Exception { - GroupService groupService = EPersonServiceFactory.getInstance().getGroupService(); - - Group parentGroup = null; - Group childGroup1 = null; - Group childGroup2 = null; - - try { - context.turnOffAuthorisationSystem(); - - parentGroup = GroupBuilder.createGroup(context).build(); - childGroup1 = GroupBuilder.createGroup(context).build(); - childGroup2 = GroupBuilder.createGroup(context).build(); - - context.commit(); - - parentGroup = context.reloadEntity(parentGroup); - childGroup1 = context.reloadEntity(childGroup1); - childGroup2 = context.reloadEntity(childGroup2); + context.turnOffAuthorisationSystem(); - context.restoreAuthSystemState(); - String authToken = getAuthToken(admin.getEmail(), password); - getClient(authToken).perform( - post("/api/eperson/groups/" + UUID.randomUUID() + "/subgroups") - .contentType(parseMediaType(TEXT_URI_LIST_VALUE)) - .content(REST_SERVER_URL + "eperson/groups/" + childGroup1.getID() + "/\n" - + REST_SERVER_URL + "eperson/groups/" + childGroup2.getID() - ) - ).andExpect(status().isNotFound()); + Group parentGroup = GroupBuilder.createGroup(context).build(); + Group childGroup1 = GroupBuilder.createGroup(context).build(); + Group childGroup2 = GroupBuilder.createGroup(context).build(); - } finally { - if (parentGroup != null) { - GroupBuilder.deleteGroup(parentGroup.getID()); - } - if (childGroup1 != null) { - GroupBuilder.deleteGroup(childGroup1.getID()); - } - if (childGroup2 != null) { - GroupBuilder.deleteGroup(childGroup2.getID()); - } - } + context.restoreAuthSystemState(); + String authToken = getAuthToken(admin.getEmail(), password); + getClient(authToken).perform( + post("/api/eperson/groups/" + UUID.randomUUID() + "/subgroups") + .contentType(parseMediaType(TEXT_URI_LIST_VALUE)) + .content(REST_SERVER_URL + "eperson/groups/" + childGroup1.getID() + "/\n" + + REST_SERVER_URL + "eperson/groups/" + childGroup2.getID() + ) + ).andExpect(status().isNotFound()); } @Test public void addChildGroupUnprocessableTest() throws Exception { - GroupService groupService = EPersonServiceFactory.getInstance().getGroupService(); - - Group parentGroup = null; - Group childGroup1 = null; - Group childGroup2 = null; - - try { - context.turnOffAuthorisationSystem(); - - parentGroup = GroupBuilder.createGroup(context).build(); - childGroup1 = GroupBuilder.createGroup(context) - .withParent(parentGroup) - .build(); - childGroup2 = GroupBuilder.createGroup(context).build(); - groupService.update(context, childGroup1); - -// context.commit(); -// -// parentGroup = context.reloadEntity(parentGroup); -// childGroup1 = context.reloadEntity(childGroup1); -// childGroup2 = context.reloadEntity(childGroup2); + context.turnOffAuthorisationSystem(); - context.restoreAuthSystemState(); - String authToken = getAuthToken(admin.getEmail(), password); + Group parentGroup = GroupBuilder.createGroup(context).build(); + Group childGroup1 = GroupBuilder.createGroup(context) + .withParent(parentGroup) + .build(); + Group childGroup2 = GroupBuilder.createGroup(context).build(); - getClient(authToken).perform( - post("/api/eperson/groups/" + parentGroup.getID() + "/subgroups") - .contentType(parseMediaType(TEXT_URI_LIST_VALUE)) - .content(REST_SERVER_URL + "eperson/groups/123456789\n" - + REST_SERVER_URL + "eperson/groups/" + childGroup2.getID() - ) - ).andExpect(status().isUnprocessableEntity()); + context.restoreAuthSystemState(); + String authToken = getAuthToken(admin.getEmail(), password); - // TODO - confirm with reviewers that this is a mistake - it actually should be No Content - // (see AddMember test) but was incorrectly expecting 422? - getClient(authToken).perform( - post("/api/eperson/groups/" + parentGroup.getID() + "/subgroups") - .contentType(parseMediaType(TEXT_URI_LIST_VALUE)) - .content(REST_SERVER_URL + "eperson/groups/" + childGroup1.getID() + "/\n" - + REST_SERVER_URL + "eperson/groups/" + childGroup2.getID() - ) - ).andExpect(status().isNoContent()); + getClient(authToken).perform( + post("/api/eperson/groups/" + parentGroup.getID() + "/subgroups") + .contentType(parseMediaType(TEXT_URI_LIST_VALUE)) + .content(REST_SERVER_URL + "eperson/groups/123456789\n" + + REST_SERVER_URL + "eperson/groups/" + childGroup2.getID() + ) + ).andExpect(status().isUnprocessableEntity()); - } finally { - if (parentGroup != null) { - GroupBuilder.deleteGroup(parentGroup.getID()); - } - if (childGroup1 != null) { - GroupBuilder.deleteGroup(childGroup1.getID()); - } - if (childGroup2 != null) { - GroupBuilder.deleteGroup(childGroup2.getID()); - } - } + // TODO - confirm with reviewers that this is a mistake - it actually should be No Content + // (see AddMember test) but was incorrectly expecting 422? + getClient(authToken).perform( + post("/api/eperson/groups/" + parentGroup.getID() + "/subgroups") + .contentType(parseMediaType(TEXT_URI_LIST_VALUE)) + .content(REST_SERVER_URL + "eperson/groups/" + childGroup1.getID() + "/\n" + + REST_SERVER_URL + "eperson/groups/" + childGroup2.getID() + ) + ).andExpect(status().isNoContent()); } @Test @@ -1081,251 +951,118 @@ public void addMemberTest() throws Exception { @Test public void addMemberCommunityAdminTest() throws Exception { - CommunityService communityService = ContentServiceFactory.getInstance().getCommunityService(); GroupService groupService = EPersonServiceFactory.getInstance().getGroupService(); - EPersonService ePersonService = EPersonServiceFactory.getInstance().getEPersonService(); - - Community community = null; - Group parentGroup = null; - EPerson member1 = null; - EPerson member2 = null; - - try { - context.turnOffAuthorisationSystem(); - - community = CommunityBuilder.createCommunity(context).build(); - parentGroup = GroupBuilder.createCommunityAdminGroup(context, community) - .addMember(eperson) - .build(); - member1 = EPersonBuilder.createEPerson(context).build(); - member2 = EPersonBuilder.createEPerson(context).build(); - context.commit(); - - parentGroup = context.reloadEntity(parentGroup); - member1 = context.reloadEntity(member1); - member2 = context.reloadEntity(member2); + context.turnOffAuthorisationSystem(); - context.restoreAuthSystemState(); - String authToken = getAuthToken(eperson.getEmail(), password); - getClient(authToken).perform( - post("/api/eperson/groups/" + parentGroup.getID() + "/epersons") - .contentType(parseMediaType(TEXT_URI_LIST_VALUE)) - .content(REST_SERVER_URL + "eperson/groups/" + member1.getID() + "/\n" - + REST_SERVER_URL + "eperson/groups/" + member2.getID() - ) - ).andExpect(status().isNoContent()); + Community community = CommunityBuilder.createCommunity(context).build(); + Group parentGroup = GroupBuilder.createCommunityAdminGroup(context, community) + .addMember(eperson) + .build(); + EPerson member1 = EPersonBuilder.createEPerson(context).build(); + EPerson member2 = EPersonBuilder.createEPerson(context).build(); - parentGroup = context.reloadEntity(parentGroup); - member1 = context.reloadEntity(member1); - member2 = context.reloadEntity(member2); + context.restoreAuthSystemState(); + String authToken = getAuthToken(eperson.getEmail(), password); + getClient(authToken).perform( + post("/api/eperson/groups/" + parentGroup.getID() + "/epersons") + .contentType(parseMediaType(TEXT_URI_LIST_VALUE)) + .content(REST_SERVER_URL + "eperson/groups/" + member1.getID() + "/\n" + + REST_SERVER_URL + "eperson/groups/" + member2.getID() + ) + ).andExpect(status().isNoContent()); - assertTrue( - groupService.isMember(context, member1, parentGroup) - ); + parentGroup = context.reloadEntity(parentGroup); + member1 = context.reloadEntity(member1); + member2 = context.reloadEntity(member2); - assertTrue( - groupService.isMember(context, member2, parentGroup) - ); + assertTrue( + groupService.isMember(context, member1, parentGroup) + ); - } finally { - if (community != null) { - CommunityBuilder.deleteCommunity(community.getID()); - } - if (parentGroup != null) { - GroupBuilder.deleteGroup(parentGroup.getID()); - } - if (member1 != null) { - EPersonBuilder.deleteEPerson(member1.getID()); - } - if (member2 != null) { - EPersonBuilder.deleteEPerson(member2.getID()); - } - } + assertTrue( + groupService.isMember(context, member2, parentGroup) + ); } @Test public void addMemberForbiddenTest() throws Exception { - GroupService groupService = EPersonServiceFactory.getInstance().getGroupService(); - EPersonService ePersonService = EPersonServiceFactory.getInstance().getEPersonService(); - - Group parentGroup = null; - EPerson member1 = null; - EPerson member2 = null; - - try { - context.turnOffAuthorisationSystem(); - - parentGroup = GroupBuilder.createGroup(context).build(); - member1 = EPersonBuilder.createEPerson(context).build(); - member2 = EPersonBuilder.createEPerson(context).build(); - - context.commit(); - - parentGroup = context.reloadEntity(parentGroup); - member1 = context.reloadEntity(member1); - member2 = context.reloadEntity(member2); + context.turnOffAuthorisationSystem(); - context.restoreAuthSystemState(); - String authToken = getAuthToken(eperson.getEmail(), password); - getClient(authToken).perform( - post("/api/eperson/groups/" + parentGroup.getID() + "/epersons") - .contentType(parseMediaType(TEXT_URI_LIST_VALUE)) - .content(REST_SERVER_URL + "eperson/groups/" + member1.getID() + "/\n" - + REST_SERVER_URL + "eperson/groups/" + member2.getID() - ) - ).andExpect(status().isForbidden()); + Group parentGroup = GroupBuilder.createGroup(context).build(); + EPerson member1 = EPersonBuilder.createEPerson(context).build(); + EPerson member2 = EPersonBuilder.createEPerson(context).build(); - } finally { - if (parentGroup != null) { - GroupBuilder.deleteGroup(parentGroup.getID()); - } - if (member1 != null) { - EPersonBuilder.deleteEPerson(member1.getID()); - } - if (member2 != null) { - EPersonBuilder.deleteEPerson(member2.getID()); - } - } + context.restoreAuthSystemState(); + String authToken = getAuthToken(eperson.getEmail(), password); + getClient(authToken).perform( + post("/api/eperson/groups/" + parentGroup.getID() + "/epersons") + .contentType(parseMediaType(TEXT_URI_LIST_VALUE)) + .content(REST_SERVER_URL + "eperson/groups/" + member1.getID() + "/\n" + + REST_SERVER_URL + "eperson/groups/" + member2.getID() + ) + ).andExpect(status().isForbidden()); } @Test public void addMemberUnauthorizedTest() throws Exception { - GroupService groupService = EPersonServiceFactory.getInstance().getGroupService(); - EPersonService ePersonService = EPersonServiceFactory.getInstance().getEPersonService(); - - Group parentGroup = null; - EPerson member1 = null; - EPerson member2 = null; - - try { - context.turnOffAuthorisationSystem(); - - parentGroup = GroupBuilder.createGroup(context).build(); - member1 = EPersonBuilder.createEPerson(context).build(); - member2 = EPersonBuilder.createEPerson(context).build(); - - context.commit(); - - parentGroup = context.reloadEntity(parentGroup); - member1 = context.reloadEntity(member1); - member2 = context.reloadEntity(member2); + context.turnOffAuthorisationSystem(); - context.restoreAuthSystemState(); - getClient().perform( - post("/api/eperson/groups/" + parentGroup.getID() + "/epersons") - .contentType(parseMediaType(TEXT_URI_LIST_VALUE)) - .content(REST_SERVER_URL + "eperson/groups/" + member1.getID() + "/\n" - + REST_SERVER_URL + "eperson/groups/" + member2.getID() - ) - ).andExpect(status().isUnauthorized()); + Group parentGroup = GroupBuilder.createGroup(context).build(); + EPerson member1 = EPersonBuilder.createEPerson(context).build(); + EPerson member2 = EPersonBuilder.createEPerson(context).build(); - } finally { - if (parentGroup != null) { - GroupBuilder.deleteGroup(parentGroup.getID()); - } - if (member1 != null) { - EPersonBuilder.deleteEPerson(member1.getID()); - } - if (member2 != null) { - EPersonBuilder.deleteEPerson(member2.getID()); - } - } + context.restoreAuthSystemState(); + getClient().perform( + post("/api/eperson/groups/" + parentGroup.getID() + "/epersons") + .contentType(parseMediaType(TEXT_URI_LIST_VALUE)) + .content(REST_SERVER_URL + "eperson/groups/" + member1.getID() + "/\n" + + REST_SERVER_URL + "eperson/groups/" + member2.getID() + ) + ).andExpect(status().isUnauthorized()); } @Test public void addMemberNotFoundTest() throws Exception { - GroupService groupService = EPersonServiceFactory.getInstance().getGroupService(); - EPersonService ePersonService = EPersonServiceFactory.getInstance().getEPersonService(); - - Group parentGroup = null; - EPerson member1 = null; - EPerson member2 = null; - - try { - context.turnOffAuthorisationSystem(); - - parentGroup = GroupBuilder.createGroup(context).build(); - member1 = EPersonBuilder.createEPerson(context).build(); - member2 = EPersonBuilder.createEPerson(context).build(); - - context.commit(); - - parentGroup = context.reloadEntity(parentGroup); - member1 = context.reloadEntity(member1); - member2 = context.reloadEntity(member2); + context.turnOffAuthorisationSystem(); - context.restoreAuthSystemState(); - String authToken = getAuthToken(admin.getEmail(), password); - getClient(authToken).perform( - post("/api/eperson/groups/" + UUID.randomUUID() + "/epersons") - .contentType(parseMediaType(TEXT_URI_LIST_VALUE)) - .content(REST_SERVER_URL + "eperson/groups/" + member1.getID() + "/\n" - + REST_SERVER_URL + "eperson/groups/" + member2.getID() - ) - ).andExpect(status().isNotFound()); + Group parentGroup = GroupBuilder.createGroup(context).build(); + EPerson member1 = EPersonBuilder.createEPerson(context).build(); + EPerson member2 = EPersonBuilder.createEPerson(context).build(); - } finally { - if (parentGroup != null) { - GroupBuilder.deleteGroup(parentGroup.getID()); - } - if (member1 != null) { - EPersonBuilder.deleteEPerson(member1.getID()); - } - if (member2 != null) { - EPersonBuilder.deleteEPerson(member2.getID()); - } - } + context.restoreAuthSystemState(); + String authToken = getAuthToken(admin.getEmail(), password); + getClient(authToken).perform( + post("/api/eperson/groups/" + UUID.randomUUID() + "/epersons") + .contentType(parseMediaType(TEXT_URI_LIST_VALUE)) + .content(REST_SERVER_URL + "eperson/groups/" + member1.getID() + "/\n" + + REST_SERVER_URL + "eperson/groups/" + member2.getID() + ) + ).andExpect(status().isNotFound()); } @Test public void addMemberUnprocessableTest() throws Exception { - GroupService groupService = EPersonServiceFactory.getInstance().getGroupService(); - EPersonService ePersonService = EPersonServiceFactory.getInstance().getEPersonService(); - - Group parentGroup = null; - EPerson member1 = null; - EPerson member2 = null; - - try { - context.turnOffAuthorisationSystem(); - - parentGroup = GroupBuilder.createGroup(context).build(); - member1 = EPersonBuilder.createEPerson(context).build(); - member2 = EPersonBuilder.createEPerson(context).build(); - - context.commit(); - - parentGroup = context.reloadEntity(parentGroup); - member1 = context.reloadEntity(member1); - member2 = context.reloadEntity(member2); + context.turnOffAuthorisationSystem(); - context.restoreAuthSystemState(); - String authToken = getAuthToken(admin.getEmail(), password); + Group parentGroup = GroupBuilder.createGroup(context).build(); + EPerson member1 = EPersonBuilder.createEPerson(context).build(); + EPerson member2 = EPersonBuilder.createEPerson(context).build(); - getClient(authToken).perform( - post("/api/eperson/groups/" + parentGroup.getID() + "/epersons") - .contentType(parseMediaType(TEXT_URI_LIST_VALUE)) - .content(REST_SERVER_URL + "eperson/groups/123456789\n" - + REST_SERVER_URL + "eperson/groups/" + member2.getID() - ) - ).andExpect(status().isUnprocessableEntity()); + context.restoreAuthSystemState(); + String authToken = getAuthToken(admin.getEmail(), password); - } finally { - if (parentGroup != null) { - GroupBuilder.deleteGroup(parentGroup.getID()); - } - if (member1 != null) { - EPersonBuilder.deleteEPerson(member1.getID()); - } - if (member2 != null) { - EPersonBuilder.deleteEPerson(member2.getID()); - } - } + getClient(authToken).perform( + post("/api/eperson/groups/" + parentGroup.getID() + "/epersons") + .contentType(parseMediaType(TEXT_URI_LIST_VALUE)) + .content(REST_SERVER_URL + "eperson/groups/123456789\n" + + REST_SERVER_URL + "eperson/groups/" + member2.getID() + ) + ).andExpect(status().isUnprocessableEntity()); } @Test @@ -1378,29 +1115,18 @@ public void removeChildGroupTest() throws Exception { @Test public void removeChildGroupCommunityAdminTest() throws Exception { - CommunityService communityService = ContentServiceFactory.getInstance().getCommunityService(); GroupService groupService = EPersonServiceFactory.getInstance().getGroupService(); - Community community = null; - Group parentGroup = null; - Group childGroup = null; - - try { context.turnOffAuthorisationSystem(); - community = CommunityBuilder.createCommunity(context).build(); - parentGroup = GroupBuilder.createCommunityAdminGroup(context, community) + Community community = CommunityBuilder.createCommunity(context).build(); + Group parentGroup = GroupBuilder.createCommunityAdminGroup(context, community) .addMember(eperson) .build(); - childGroup = GroupBuilder.createGroup(context) + Group childGroup = GroupBuilder.createGroup(context) .withParent(parentGroup) .build(); - context.commit(); - - parentGroup = context.reloadEntity(parentGroup); - childGroup = context.reloadEntity(childGroup); - context.restoreAuthSystemState(); String authToken = getAuthToken(eperson.getEmail(), password); getClient(authToken).perform( @@ -1413,163 +1139,71 @@ public void removeChildGroupCommunityAdminTest() throws Exception { assertFalse( groupService.isMember(parentGroup, childGroup) ); - - } finally { - if (community != null) { - CommunityBuilder.deleteCommunity(community.getID()); - } - if (parentGroup != null) { - GroupBuilder.deleteGroup(parentGroup.getID()); - } - if (childGroup != null) { - GroupBuilder.deleteGroup(childGroup.getID()); - } - } } @Test public void removeChildGroupForbiddenTest() throws Exception { - GroupService groupService = EPersonServiceFactory.getInstance().getGroupService(); - - Group parentGroup = null; - Group childGroup = null; - - try { - context.turnOffAuthorisationSystem(); - - parentGroup = GroupBuilder.createGroup(context).build(); - childGroup = GroupBuilder.createGroup(context).build(); - - context.commit(); - - parentGroup = context.reloadEntity(parentGroup); - childGroup = context.reloadEntity(childGroup); + context.turnOffAuthorisationSystem(); - context.restoreAuthSystemState(); - String authToken = getAuthToken(eperson.getEmail(), password); - getClient(authToken).perform( - delete("/api/eperson/groups/" + parentGroup.getID() + "/subgroups/" + childGroup.getID()) - ).andExpect(status().isForbidden()); + Group parentGroup = GroupBuilder.createGroup(context).build(); + Group childGroup = GroupBuilder.createGroup(context).build(); - } finally { - if (parentGroup != null) { - GroupBuilder.deleteGroup(parentGroup.getID()); - } - if (childGroup != null) { - GroupBuilder.deleteGroup(childGroup.getID()); - } - } + context.restoreAuthSystemState(); + String authToken = getAuthToken(eperson.getEmail(), password); + getClient(authToken).perform( + delete("/api/eperson/groups/" + parentGroup.getID() + "/subgroups/" + childGroup.getID()) + ).andExpect(status().isForbidden()); } @Test public void removeChildGroupUnauthorizedTest() throws Exception { - GroupService groupService = EPersonServiceFactory.getInstance().getGroupService(); - - Group parentGroup = null; - Group childGroup = null; - - try { - context.turnOffAuthorisationSystem(); - - parentGroup = GroupBuilder.createGroup(context).build(); - childGroup = GroupBuilder.createGroup(context).build(); - - context.commit(); - - parentGroup = context.reloadEntity(parentGroup); - childGroup = context.reloadEntity(childGroup); + context.turnOffAuthorisationSystem(); - context.restoreAuthSystemState(); - getClient().perform( - delete("/api/eperson/groups/" + parentGroup.getID() + "/subgroups/" + childGroup.getID()) - ).andExpect(status().isUnauthorized()); + Group parentGroup = GroupBuilder.createGroup(context).build(); + Group childGroup = GroupBuilder.createGroup(context).build(); - } finally { - if (parentGroup != null) { - GroupBuilder.deleteGroup(parentGroup.getID()); - } - if (childGroup != null) { - GroupBuilder.deleteGroup(childGroup.getID()); - } - } + context.restoreAuthSystemState(); + getClient().perform( + delete("/api/eperson/groups/" + parentGroup.getID() + "/subgroups/" + childGroup.getID()) + ).andExpect(status().isUnauthorized()); } @Test public void removeChildGroupNotFoundTest() throws Exception { - GroupService groupService = EPersonServiceFactory.getInstance().getGroupService(); - - Group parentGroup = null; - Group childGroup = null; - - try { - context.turnOffAuthorisationSystem(); - - parentGroup = GroupBuilder.createGroup(context).build(); - childGroup = GroupBuilder.createGroup(context) - .withParent(parentGroup) - .build(); - - context.commit(); - - parentGroup = context.reloadEntity(parentGroup); - childGroup = context.reloadEntity(childGroup); + context.turnOffAuthorisationSystem(); - context.restoreAuthSystemState(); - String authToken = getAuthToken(admin.getEmail(), password); + Group parentGroup = GroupBuilder.createGroup(context).build(); + Group childGroup = GroupBuilder.createGroup(context) + .withParent(parentGroup) + .build(); - getClient(authToken).perform( - delete("/api/eperson/groups/" + UUID.randomUUID() + "/subgroups/" + childGroup.getID()) - ).andExpect(status().isNotFound()); + context.restoreAuthSystemState(); + String authToken = getAuthToken(admin.getEmail(), password); - } finally { - if (parentGroup != null) { - GroupBuilder.deleteGroup(parentGroup.getID()); - } - if (childGroup != null) { - GroupBuilder.deleteGroup(childGroup.getID()); - } - } + getClient(authToken).perform( + delete("/api/eperson/groups/" + UUID.randomUUID() + "/subgroups/" + childGroup.getID()) + ).andExpect(status().isNotFound()); } @Test public void removeChildGroupUnprocessableTest() throws Exception { - GroupService groupService = EPersonServiceFactory.getInstance().getGroupService(); - - Group parentGroup = null; - Group childGroup = null; - - try { - context.turnOffAuthorisationSystem(); - - parentGroup = GroupBuilder.createGroup(context).build(); - childGroup = GroupBuilder.createGroup(context) - .withParent(parentGroup) - .build(); - - context.commit(); - - parentGroup = context.reloadEntity(parentGroup); - childGroup = context.reloadEntity(childGroup); + context.turnOffAuthorisationSystem(); - context.restoreAuthSystemState(); - String authToken = getAuthToken(admin.getEmail(), password); + Group parentGroup = GroupBuilder.createGroup(context).build(); + Group childGroup = GroupBuilder.createGroup(context) + .withParent(parentGroup) + .build(); - getClient(authToken).perform( - delete("/api/eperson/groups/" + parentGroup.getID() + "/subgroups/" + UUID.randomUUID()) - ).andExpect(status().isUnprocessableEntity()); + context.restoreAuthSystemState(); + String authToken = getAuthToken(admin.getEmail(), password); - } finally { - if (parentGroup != null) { - GroupBuilder.deleteGroup(parentGroup.getID()); - } - if (childGroup != null) { - GroupBuilder.deleteGroup(childGroup.getID()); - } - } + getClient(authToken).perform( + delete("/api/eperson/groups/" + parentGroup.getID() + "/subgroups/" + UUID.randomUUID()) + ).andExpect(status().isUnprocessableEntity()); } @Test @@ -1613,31 +1247,19 @@ public void removeMemberTest() throws Exception { @Test public void removeMemberCommunityAdminTest() throws Exception { - CommunityService communityService = ContentServiceFactory.getInstance().getCommunityService(); GroupService groupService = EPersonServiceFactory.getInstance().getGroupService(); - EPersonService ePersonService = EPersonServiceFactory.getInstance().getEPersonService(); - - Community community = null; - Group parentGroup = null; - EPerson member = null; - try { context.turnOffAuthorisationSystem(); - community = CommunityBuilder.createCommunity(context).build(); - member = EPersonBuilder.createEPerson(context).build(); - parentGroup = GroupBuilder.createCommunityAdminGroup(context, community) + Community community = CommunityBuilder.createCommunity(context).build(); + EPerson member = EPersonBuilder.createEPerson(context).build(); + Group parentGroup = GroupBuilder.createCommunityAdminGroup(context, community) .addMember(member) .addMember(eperson) .build(); assertTrue(groupService.isMember(context, member, parentGroup)); - context.commit(); - - parentGroup = context.reloadEntity(parentGroup); - member = context.reloadEntity(member); - context.restoreAuthSystemState(); String authToken = getAuthToken(eperson.getEmail(), password); getClient(authToken).perform( @@ -1650,171 +1272,75 @@ public void removeMemberCommunityAdminTest() throws Exception { assertFalse( groupService.isMember(context, member, parentGroup) ); - - } finally { - if (community != null) { - CommunityBuilder.deleteCommunity(community.getID()); - } - if (parentGroup != null) { - GroupBuilder.deleteGroup(parentGroup.getID()); - } - if (member != null) { - EPersonBuilder.deleteEPerson(member.getID()); - } - } } @Test public void removeMemberForbiddenTest() throws Exception { - GroupService groupService = EPersonServiceFactory.getInstance().getGroupService(); - EPersonService ePersonService = EPersonServiceFactory.getInstance().getEPersonService(); - - Group parentGroup = null; - EPerson member = null; - - try { - context.turnOffAuthorisationSystem(); - - member = EPersonBuilder.createEPerson(context).build(); - parentGroup = GroupBuilder.createGroup(context) - .addMember(member) - .build(); - - context.commit(); - - parentGroup = context.reloadEntity(parentGroup); - member = context.reloadEntity(member); + context.turnOffAuthorisationSystem(); - context.restoreAuthSystemState(); - String authToken = getAuthToken(eperson.getEmail(), password); - getClient(authToken).perform( - delete("/api/eperson/groups/" + parentGroup.getID() + "/epersons/" + member.getID()) - ).andExpect(status().isForbidden()); + EPerson member = EPersonBuilder.createEPerson(context).build(); + Group parentGroup = GroupBuilder.createGroup(context) + .addMember(member) + .build(); - } finally { - if (parentGroup != null) { - GroupBuilder.deleteGroup(parentGroup.getID()); - } - if (member != null) { - EPersonBuilder.deleteEPerson(member.getID()); - } - } + context.restoreAuthSystemState(); + String authToken = getAuthToken(eperson.getEmail(), password); + getClient(authToken).perform( + delete("/api/eperson/groups/" + parentGroup.getID() + "/epersons/" + member.getID()) + ).andExpect(status().isForbidden()); } @Test public void removeMemberUnauthorizedTest() throws Exception { - GroupService groupService = EPersonServiceFactory.getInstance().getGroupService(); - EPersonService ePersonService = EPersonServiceFactory.getInstance().getEPersonService(); - - Group parentGroup = null; - EPerson member = null; - - try { - context.turnOffAuthorisationSystem(); - - member = EPersonBuilder.createEPerson(context).build(); - parentGroup = GroupBuilder.createGroup(context) - .addMember(member) - .build(); - - context.commit(); - - parentGroup = context.reloadEntity(parentGroup); - member = context.reloadEntity(member); + context.turnOffAuthorisationSystem(); - context.restoreAuthSystemState(); - getClient().perform( - delete("/api/eperson/groups/" + parentGroup.getID() + "/epersons/" + member.getID()) - ).andExpect(status().isUnauthorized()); + EPerson member = EPersonBuilder.createEPerson(context).build(); + Group parentGroup = GroupBuilder.createGroup(context) + .addMember(member) + .build(); - } finally { - if (parentGroup != null) { - GroupBuilder.deleteGroup(parentGroup.getID()); - } - if (member != null) { - EPersonBuilder.deleteEPerson(member.getID()); - } - } + context.restoreAuthSystemState(); + getClient().perform( + delete("/api/eperson/groups/" + parentGroup.getID() + "/epersons/" + member.getID()) + ).andExpect(status().isUnauthorized()); } @Test public void removeMemberNotFoundTest() throws Exception { - GroupService groupService = EPersonServiceFactory.getInstance().getGroupService(); - EPersonService ePersonService = EPersonServiceFactory.getInstance().getEPersonService(); - - Group parentGroup = null; - EPerson member = null; - - try { - context.turnOffAuthorisationSystem(); - - member = EPersonBuilder.createEPerson(context).build(); - parentGroup = GroupBuilder.createGroup(context) - .addMember(member) - .build(); - - context.commit(); - - parentGroup = context.reloadEntity(parentGroup); - member = context.reloadEntity(member); + context.turnOffAuthorisationSystem(); - context.restoreAuthSystemState(); - String authToken = getAuthToken(admin.getEmail(), password); + EPerson member = EPersonBuilder.createEPerson(context).build(); + Group parentGroup = GroupBuilder.createGroup(context) + .addMember(member) + .build(); - getClient(authToken).perform( - delete("/api/eperson/groups/" + UUID.randomUUID() + "/epersons/" + member.getID()) - ).andExpect(status().isNotFound()); + context.restoreAuthSystemState(); + String authToken = getAuthToken(admin.getEmail(), password); - } finally { - if (parentGroup != null) { - GroupBuilder.deleteGroup(parentGroup.getID()); - } - if (member != null) { - EPersonBuilder.deleteEPerson(member.getID()); - } - } + getClient(authToken).perform( + delete("/api/eperson/groups/" + UUID.randomUUID() + "/epersons/" + member.getID()) + ).andExpect(status().isNotFound()); } @Test public void removeMemberUnprocessableTest() throws Exception { - GroupService groupService = EPersonServiceFactory.getInstance().getGroupService(); - EPersonService ePersonService = EPersonServiceFactory.getInstance().getEPersonService(); - - Group parentGroup = null; - EPerson member = null; - - try { context.turnOffAuthorisationSystem(); - member = EPersonBuilder.createEPerson(context).build(); - parentGroup = GroupBuilder.createGroup(context) + EPerson member = EPersonBuilder.createEPerson(context).build(); + Group parentGroup = GroupBuilder.createGroup(context) .addMember(member) .build(); - context.commit(); - - parentGroup = context.reloadEntity(parentGroup); - member = context.reloadEntity(member); - context.restoreAuthSystemState(); String authToken = getAuthToken(admin.getEmail(), password); getClient(authToken).perform( delete("/api/eperson/groups/" + parentGroup.getID() + "/epersons/" + UUID.randomUUID()) ).andExpect(status().isUnprocessableEntity()); - - } finally { - if (parentGroup != null) { - GroupBuilder.deleteGroup(parentGroup.getID()); - } - if (member != null) { - EPersonBuilder.deleteEPerson(member.getID()); - } - } } @Test From 7d7edcb4c6ab8de23b9d3d8a868fd706294b9523 Mon Sep 17 00:00:00 2001 From: Kim Shepherd Date: Fri, 6 Sep 2024 16:43:36 +0200 Subject: [PATCH 087/979] #9806: Align provider reg in CreateMissingIdentifiersIT with other tests VersionedHandlerIdentifierProviderIT uses this registerProvider method which looks more reliable and doesn't do a refresh/reload of applicationContext after (which I suspected might have an odd interaction with VersioningWithRelationshipsIT and its createBean() calls?) (cherry picked from commit 90536e443b7fedef0481a34326cafc58fb334396) --- .../general/CreateMissingIdentifiersIT.java | 34 ++++++++++++++++--- 1 file changed, 29 insertions(+), 5 deletions(-) diff --git a/dspace-api/src/test/java/org/dspace/ctask/general/CreateMissingIdentifiersIT.java b/dspace-api/src/test/java/org/dspace/ctask/general/CreateMissingIdentifiersIT.java index 2a07799deee5..8038a7153325 100644 --- a/dspace-api/src/test/java/org/dspace/ctask/general/CreateMissingIdentifiersIT.java +++ b/dspace-api/src/test/java/org/dspace/ctask/general/CreateMissingIdentifiersIT.java @@ -10,6 +10,8 @@ import static org.junit.Assert.assertEquals; import java.io.IOException; +import java.util.ArrayList; +import java.util.List; import org.dspace.AbstractIntegrationTestWithDatabase; import org.dspace.builder.CollectionBuilder; @@ -19,7 +21,10 @@ import org.dspace.content.Item; import org.dspace.core.factory.CoreServiceFactory; import org.dspace.curate.Curator; +import org.dspace.identifier.IdentifierProvider; +import org.dspace.identifier.IdentifierServiceImpl; import org.dspace.identifier.VersionedHandleIdentifierProviderWithCanonicalHandles; +import org.dspace.kernel.ServiceManager; import org.dspace.services.ConfigurationService; import org.dspace.services.factory.DSpaceServicesFactory; import org.junit.After; @@ -32,10 +37,23 @@ */ public class CreateMissingIdentifiersIT extends AbstractIntegrationTestWithDatabase { + private ServiceManager serviceManager; + private IdentifierServiceImpl identifierService; private static final String P_TASK_DEF = "plugin.named.org.dspace.curate.CurationTask"; private static final String TASK_NAME = "test"; + @Override + public void setUp() throws Exception { + super.setUp(); + context.turnOffAuthorisationSystem(); + + serviceManager = DSpaceServicesFactory.getInstance().getServiceManager(); + identifierService = serviceManager.getServicesByType(IdentifierServiceImpl.class).get(0); + // Clean out providers to avoid any being used for creation of community and collection + identifierService.setProviders(new ArrayList<>()); + } + @Test public void testPerform() throws IOException { @@ -67,11 +85,7 @@ public void testPerform() /* * Now install an incompatible provider to make the task fail. */ - DSpaceServicesFactory.getInstance() - .getServiceManager() - .registerServiceClass( - VersionedHandleIdentifierProviderWithCanonicalHandles.class.getCanonicalName(), - VersionedHandleIdentifierProviderWithCanonicalHandles.class); + registerProvider(VersionedHandleIdentifierProviderWithCanonicalHandles.class); curator.curate(context, item); System.out.format("With incompatible provider, result is '%s'.\n", @@ -86,4 +100,14 @@ public void destroy() throws Exception { super.destroy(); DSpaceServicesFactory.getInstance().getServiceManager().getApplicationContext().refresh(); } + + private void registerProvider(Class type) { + // Register our new provider + serviceManager.registerServiceClass(type.getName(), type); + IdentifierProvider identifierProvider = + (IdentifierProvider) serviceManager.getServiceByName(type.getName(), type); + + // Overwrite the identifier-service's providers with the new one to ensure only this provider is used + identifierService.setProviders(List.of(identifierProvider)); + } } From 59ecfb8360b761980dfc14d3ca7ab0b9bd6bfc7d Mon Sep 17 00:00:00 2001 From: Kim Shepherd Date: Fri, 6 Sep 2024 16:45:05 +0200 Subject: [PATCH 088/979] #9806: Use builders for creation in VersioningWithRelationshipsIT I am a bit uncertain about the createBean() calls here, why do we not simply *get* the configured beans using the service manager instead, but will look at that in a separate change (cherry picked from commit 3521ab6d3598e716cc9fe8e938e883b302006580) --- .../VersioningWithRelationshipsIT.java | 33 +++++++++---------- 1 file changed, 15 insertions(+), 18 deletions(-) diff --git a/dspace-api/src/test/java/org/dspace/content/VersioningWithRelationshipsIT.java b/dspace-api/src/test/java/org/dspace/content/VersioningWithRelationshipsIT.java index 44653300e0de..accc52d0d830 100644 --- a/dspace-api/src/test/java/org/dspace/content/VersioningWithRelationshipsIT.java +++ b/dspace-api/src/test/java/org/dspace/content/VersioningWithRelationshipsIT.java @@ -49,6 +49,7 @@ import org.dspace.builder.ItemBuilder; import org.dspace.builder.RelationshipBuilder; import org.dspace.builder.RelationshipTypeBuilder; +import org.dspace.builder.VersionBuilder; import org.dspace.content.factory.ContentServiceFactory; import org.dspace.content.service.InstallItemService; import org.dspace.content.service.ItemService; @@ -62,8 +63,6 @@ import org.dspace.kernel.ServiceManager; import org.dspace.services.factory.DSpaceServicesFactory; import org.dspace.versioning.Version; -import org.dspace.versioning.factory.VersionServiceFactory; -import org.dspace.versioning.service.VersioningService; import org.hamcrest.Matcher; import org.junit.Assert; import org.junit.Before; @@ -74,8 +73,6 @@ public class VersioningWithRelationshipsIT extends AbstractIntegrationTestWithDa private final RelationshipService relationshipService = ContentServiceFactory.getInstance().getRelationshipService(); - private final VersioningService versioningService = - VersionServiceFactory.getInstance().getVersionService(); private final WorkspaceItemService workspaceItemService = ContentServiceFactory.getInstance().getWorkspaceItemService(); private final InstallItemService installItemService = @@ -291,7 +288,7 @@ public void test_createNewVersionOfItemOnLeftSideOfRelationships() throws Except // create a new version of the publication // ///////////////////////////////////////////// - Version newVersion = versioningService.createNewVersion(context, originalPublication); + Version newVersion = VersionBuilder.createVersion(context, originalPublication, "test").build(); Item newPublication = newVersion.getItem(); assertNotSame(originalPublication, newPublication); @@ -567,7 +564,7 @@ public void test_createNewVersionOfItemAndModifyRelationships() throws Exception // create a new version of the publication // ///////////////////////////////////////////// - Version newVersion = versioningService.createNewVersion(context, originalPublication); + Version newVersion = VersionBuilder.createVersion(context, originalPublication, "test").build(); Item newPublication = newVersion.getItem(); assertNotSame(originalPublication, newPublication); @@ -927,7 +924,7 @@ public void test_createNewVersionOfItemOnRightSideOfRelationships() throws Excep // create a new version of the person // //////////////////////////////////////// - Version newVersion = versioningService.createNewVersion(context, originalPerson); + Version newVersion = VersionBuilder.createVersion(context, originalPerson, "test").build(); Item newPerson = newVersion.getItem(); assertNotSame(originalPerson, newPerson); @@ -1300,7 +1297,7 @@ public void test_createNewVersionOfItemAndVerifyMetadataOrder() throws Exception // create new version of publication // /////////////////////////////////////// - Version newVersion = versioningService.createNewVersion(context, originalPublication); + Version newVersion = VersionBuilder.createVersion(context, originalPublication, "test").build(); Item newPublication = newVersion.getItem(); assertNotSame(originalPublication, newPublication); @@ -1463,7 +1460,7 @@ public void test_createNewVersionOfItemWithAddRemoveMove() throws Exception { // create a new version of the publication // ///////////////////////////////////////////// - Version newVersion = versioningService.createNewVersion(context, originalPublication); + Version newVersion = VersionBuilder.createVersion(context, originalPublication, "test").build(); Item newPublication = newVersion.getItem(); assertNotSame(originalPublication, newPublication); @@ -1782,7 +1779,7 @@ public void test_placeRecalculationAfterDelete() throws Exception { // create new version - volume 1.2 // ///////////////////////////////////// - Item v1_2 = versioningService.createNewVersion(context, v1_1).getItem(); + Item v1_2 = VersionBuilder.createVersion(context, v1_1, "test").build().getItem(); installItemService.installItem(context, workspaceItemService.findByItem(context, v1_2)); context.commit(); @@ -1790,7 +1787,7 @@ public void test_placeRecalculationAfterDelete() throws Exception { // create new version - issue 3.2 // //////////////////////////////////// - Item i3_2 = versioningService.createNewVersion(context, i3_1).getItem(); + Item i3_2 = VersionBuilder.createVersion(context, i3_1, "test").build().getItem(); installItemService.installItem(context, workspaceItemService.findByItem(context, i3_2)); context.commit(); @@ -2316,7 +2313,7 @@ public void test_placeRecalculationAfterDelete_complex() throws Exception { // create new version - person 3.2 // ///////////////////////////////////// - Item pe3_2 = versioningService.createNewVersion(context, pe3_1).getItem(); + Item pe3_2 = VersionBuilder.createVersion(context, pe3_1, "test").build().getItem(); installItemService.installItem(context, workspaceItemService.findByItem(context, pe3_2)); context.commit(); @@ -2324,7 +2321,7 @@ public void test_placeRecalculationAfterDelete_complex() throws Exception { // create new version - project 3.2 // ////////////////////////////////////// - Item pr3_2 = versioningService.createNewVersion(context, pr3_1).getItem(); + Item pr3_2 = VersionBuilder.createVersion(context, pr3_1, "test").build().getItem(); installItemService.installItem(context, workspaceItemService.findByItem(context, pr3_2)); context.commit(); @@ -3056,7 +3053,7 @@ public void test_placeRecalculationNoUseForPlace() throws Exception { // create new version - volume 1.2 // ///////////////////////////////////// - Item v1_2 = versioningService.createNewVersion(context, v1_1).getItem(); + Item v1_2 = VersionBuilder.createVersion(context, v1_1, "test").build().getItem(); installItemService.installItem(context, workspaceItemService.findByItem(context, v1_2)); context.commit(); @@ -3064,7 +3061,7 @@ public void test_placeRecalculationNoUseForPlace() throws Exception { // create new version - issue 3.2 // //////////////////////////////////// - Item i3_2 = versioningService.createNewVersion(context, i3_1).getItem(); + Item i3_2 = VersionBuilder.createVersion(context, i3_1, "test").build().getItem(); installItemService.installItem(context, workspaceItemService.findByItem(context, i3_2)); context.commit(); @@ -3509,7 +3506,7 @@ public void test_virtualMetadataPreserved() throws Exception { // create a new version of publication 1 and archive // /////////////////////////////////////////////////////// - Item publication1V2 = versioningService.createNewVersion(context, publication1V1).getItem(); + Item publication1V2 = VersionBuilder.createVersion(context, publication1V1, "test").build().getItem(); installItemService.installItem(context, workspaceItemService.findByItem(context, publication1V2)); context.dispatchEvents(); @@ -3517,7 +3514,7 @@ public void test_virtualMetadataPreserved() throws Exception { // create new version of person 1 // //////////////////////////////////// - Item person1V2 = versioningService.createNewVersion(context, person1V1).getItem(); + Item person1V2 = VersionBuilder.createVersion(context, person1V1, "test").build().getItem(); // update "Smith, Donald" to "Smith, D." itemService.replaceMetadata( context, person1V2, "person", "givenName", null, null, "D.", @@ -3853,7 +3850,7 @@ public void test_virtualMetadataPreserved() throws Exception { // create new version of person 2 // //////////////////////////////////// - Item person2V2 = versioningService.createNewVersion(context, person2V1).getItem(); + Item person2V2 = VersionBuilder.createVersion(context, person2V1, "test").build().getItem(); Relationship rel1 = getRelationship(publication1V2, isAuthorOfPublication, person2V2); assertNotNull(rel1); rel1.setRightwardValue("Doe, Jane Jr"); From 8f8680179695c4dad637ac798eff0bc6fb47b1c8 Mon Sep 17 00:00:00 2001 From: Kim Shepherd Date: Fri, 6 Sep 2024 17:15:15 +0200 Subject: [PATCH 089/979] #9806: Set explicit id provider before VersioningWithRelationshipsIT (cherry picked from commit 4af690065038d9cb401d0fa1fc5fbfe0fa1fe2c7) --- .../VersioningWithRelationshipsIT.java | 24 ++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/dspace-api/src/test/java/org/dspace/content/VersioningWithRelationshipsIT.java b/dspace-api/src/test/java/org/dspace/content/VersioningWithRelationshipsIT.java index accc52d0d830..10cb30cd52b9 100644 --- a/dspace-api/src/test/java/org/dspace/content/VersioningWithRelationshipsIT.java +++ b/dspace-api/src/test/java/org/dspace/content/VersioningWithRelationshipsIT.java @@ -60,6 +60,9 @@ import org.dspace.content.virtual.VirtualMetadataPopulator; import org.dspace.core.Constants; import org.dspace.discovery.SolrSearchCore; +import org.dspace.identifier.IdentifierProvider; +import org.dspace.identifier.IdentifierServiceImpl; +import org.dspace.identifier.VersionedHandleIdentifierProvider; import org.dspace.kernel.ServiceManager; import org.dspace.services.factory.DSpaceServicesFactory; import org.dspace.versioning.Version; @@ -81,7 +84,7 @@ public class VersioningWithRelationshipsIT extends AbstractIntegrationTestWithDa ContentServiceFactory.getInstance().getItemService(); private final SolrSearchCore solrSearchCore = DSpaceServicesFactory.getInstance().getServiceManager().getServicesByType(SolrSearchCore.class).get(0); - + private IdentifierServiceImpl identifierService; protected Community community; protected Collection collection; protected EntityType publicationEntityType; @@ -98,6 +101,22 @@ public class VersioningWithRelationshipsIT extends AbstractIntegrationTestWithDa protected RelationshipType isIssueOfJournalVolume; protected RelationshipType isProjectOfPerson; + private void registerProvider(Class type) { + // Register our new provider + IdentifierProvider identifierProvider = + (IdentifierProvider) DSpaceServicesFactory.getInstance().getServiceManager().getServiceByName(type.getName(), type); + if (identifierProvider == null) { + DSpaceServicesFactory.getInstance().getServiceManager().registerServiceClass(type.getName(), type); + identifierProvider = (IdentifierProvider) DSpaceServicesFactory.getInstance().getServiceManager().getServiceByName(type.getName(), type); + } + + // Overwrite the identifier-service's providers with the new one to ensure only this provider is used + identifierService = DSpaceServicesFactory.getInstance().getServiceManager() + .getServicesByType(IdentifierServiceImpl.class).get(0); + identifierService.setProviders(new ArrayList<>()); + identifierService.setProviders(List.of(identifierProvider)); + } + @Override @Before public void setUp() throws Exception { @@ -105,6 +124,9 @@ public void setUp() throws Exception { context.turnOffAuthorisationSystem(); + + registerProvider(VersionedHandleIdentifierProvider.class); + community = CommunityBuilder.createCommunity(context) .withName("community") .build(); From ad7499f245be07aa9a5715b9eaa91bf164b79515 Mon Sep 17 00:00:00 2001 From: Kim Shepherd Date: Fri, 6 Sep 2024 17:58:23 +0200 Subject: [PATCH 090/979] #9806: Move cleanup of handle provider to destroy in VersionedHandleIdentifierProviderIT (cherry picked from commit f6cabe648dfcda786afdf7d5411dc3d7ad85c405) --- .../VersioningWithRelationshipsIT.java | 23 ------------------- .../VersionedHandleIdentifierProviderIT.java | 22 ++++++++++++++++-- 2 files changed, 20 insertions(+), 25 deletions(-) diff --git a/dspace-api/src/test/java/org/dspace/content/VersioningWithRelationshipsIT.java b/dspace-api/src/test/java/org/dspace/content/VersioningWithRelationshipsIT.java index 10cb30cd52b9..3acc4ca146ee 100644 --- a/dspace-api/src/test/java/org/dspace/content/VersioningWithRelationshipsIT.java +++ b/dspace-api/src/test/java/org/dspace/content/VersioningWithRelationshipsIT.java @@ -60,9 +60,6 @@ import org.dspace.content.virtual.VirtualMetadataPopulator; import org.dspace.core.Constants; import org.dspace.discovery.SolrSearchCore; -import org.dspace.identifier.IdentifierProvider; -import org.dspace.identifier.IdentifierServiceImpl; -import org.dspace.identifier.VersionedHandleIdentifierProvider; import org.dspace.kernel.ServiceManager; import org.dspace.services.factory.DSpaceServicesFactory; import org.dspace.versioning.Version; @@ -84,7 +81,6 @@ public class VersioningWithRelationshipsIT extends AbstractIntegrationTestWithDa ContentServiceFactory.getInstance().getItemService(); private final SolrSearchCore solrSearchCore = DSpaceServicesFactory.getInstance().getServiceManager().getServicesByType(SolrSearchCore.class).get(0); - private IdentifierServiceImpl identifierService; protected Community community; protected Collection collection; protected EntityType publicationEntityType; @@ -101,22 +97,6 @@ public class VersioningWithRelationshipsIT extends AbstractIntegrationTestWithDa protected RelationshipType isIssueOfJournalVolume; protected RelationshipType isProjectOfPerson; - private void registerProvider(Class type) { - // Register our new provider - IdentifierProvider identifierProvider = - (IdentifierProvider) DSpaceServicesFactory.getInstance().getServiceManager().getServiceByName(type.getName(), type); - if (identifierProvider == null) { - DSpaceServicesFactory.getInstance().getServiceManager().registerServiceClass(type.getName(), type); - identifierProvider = (IdentifierProvider) DSpaceServicesFactory.getInstance().getServiceManager().getServiceByName(type.getName(), type); - } - - // Overwrite the identifier-service's providers with the new one to ensure only this provider is used - identifierService = DSpaceServicesFactory.getInstance().getServiceManager() - .getServicesByType(IdentifierServiceImpl.class).get(0); - identifierService.setProviders(new ArrayList<>()); - identifierService.setProviders(List.of(identifierProvider)); - } - @Override @Before public void setUp() throws Exception { @@ -124,9 +104,6 @@ public void setUp() throws Exception { context.turnOffAuthorisationSystem(); - - registerProvider(VersionedHandleIdentifierProvider.class); - community = CommunityBuilder.createCommunity(context) .withName("community") .build(); diff --git a/dspace-api/src/test/java/org/dspace/identifier/VersionedHandleIdentifierProviderIT.java b/dspace-api/src/test/java/org/dspace/identifier/VersionedHandleIdentifierProviderIT.java index 7e549f6cae33..57acf1f1c453 100644 --- a/dspace-api/src/test/java/org/dspace/identifier/VersionedHandleIdentifierProviderIT.java +++ b/dspace-api/src/test/java/org/dspace/identifier/VersionedHandleIdentifierProviderIT.java @@ -24,6 +24,7 @@ import org.dspace.content.Item; import org.dspace.kernel.ServiceManager; import org.dspace.services.factory.DSpaceServicesFactory; +import org.junit.After; import org.junit.Before; import org.junit.Test; @@ -57,13 +58,30 @@ public void setUp() throws Exception { .build(); } + @After + @Override + public void destroy() throws Exception { + super.destroy(); + // After this test has finished running, refresh application context and + // set the expected 'default' versioned handle provider back to ensure other tests don't fail + DSpaceServicesFactory.getInstance().getServiceManager().getApplicationContext().refresh(); + } + private void registerProvider(Class type) { // Register our new provider - serviceManager.registerServiceClass(type.getName(), type); IdentifierProvider identifierProvider = - (IdentifierProvider) serviceManager.getServiceByName(type.getName(), type); + (IdentifierProvider) DSpaceServicesFactory.getInstance().getServiceManager() + .getServiceByName(type.getName(), type); + if (identifierProvider == null) { + DSpaceServicesFactory.getInstance().getServiceManager().registerServiceClass(type.getName(), type); + identifierProvider = (IdentifierProvider) DSpaceServicesFactory.getInstance().getServiceManager() + .getServiceByName(type.getName(), type); + } // Overwrite the identifier-service's providers with the new one to ensure only this provider is used + identifierService = DSpaceServicesFactory.getInstance().getServiceManager() + .getServicesByType(IdentifierServiceImpl.class).get(0); + identifierService.setProviders(new ArrayList<>()); identifierService.setProviders(List.of(identifierProvider)); } From 724f821bee86479663671f0eda94341c0e34ec85 Mon Sep 17 00:00:00 2001 From: Kim Shepherd Date: Tue, 25 Jun 2024 13:45:31 +0200 Subject: [PATCH 091/979] Fix request a copy link token generation Ensure DSpace URLs with extra segments are included fully in the generated link --- .../rest/repository/RequestItemRepository.java | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/RequestItemRepository.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/RequestItemRepository.java index 5945d516600a..a666b673530f 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/RequestItemRepository.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/RequestItemRepository.java @@ -21,6 +21,7 @@ import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; +import org.apache.commons.lang3.StringUtils; import org.apache.commons.validator.routines.EmailValidator; import org.apache.http.client.utils.URIBuilder; import org.apache.logging.log4j.LogManager; @@ -287,19 +288,17 @@ public Class getDomainClass() { * Generate a link back to DSpace, to act on a request. * * @param token identifies the request. - * @return URL to the item request API, with the token as request parameter - * "token". + * @return URL to the item request API, with /request-a-copy/{token} as the last URL segments * @throws URISyntaxException passed through. * @throws MalformedURLException passed through. */ private String getLinkTokenEmail(String token) throws URISyntaxException, MalformedURLException { final String base = configurationService.getProperty("dspace.ui.url"); - - URI link = new URIBuilder(base) - .setPathSegments("request-a-copy", token) - .build(); - - return link.toURL().toExternalForm(); + URIBuilder uriBuilder = new URIBuilder(base); + // Add request-a-copy/token to the existing path (without breaking /sub/dir dspace URLs) + URI uri = uriBuilder.setPath(StringUtils.stripEnd(uriBuilder.getPath(), "") + + "/request-a-copy/" + token).build(); + return uri.toURL().toExternalForm(); } } From cdb255fecaf2fb06944137d359cc188a36e5ee66 Mon Sep 17 00:00:00 2001 From: Kim Shepherd Date: Tue, 25 Jun 2024 14:36:17 +0200 Subject: [PATCH 092/979] Make RequestItemRepository#getLinkTokenEmail public, write test --- .../repository/RequestItemRepository.java | 2 +- .../app/rest/RequestItemRepositoryIT.java | 29 +++++++++++++++++++ 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/RequestItemRepository.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/RequestItemRepository.java index a666b673530f..3a3444615344 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/RequestItemRepository.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/RequestItemRepository.java @@ -292,7 +292,7 @@ public Class getDomainClass() { * @throws URISyntaxException passed through. * @throws MalformedURLException passed through. */ - private String getLinkTokenEmail(String token) + public String getLinkTokenEmail(String token) throws URISyntaxException, MalformedURLException { final String base = configurationService.getProperty("dspace.ui.url"); URIBuilder uriBuilder = new URIBuilder(base); diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/RequestItemRepositoryIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/RequestItemRepositoryIT.java index 2fb7dbbc969d..065660f2c879 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/RequestItemRepositoryIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/RequestItemRepositoryIT.java @@ -29,6 +29,8 @@ import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; +import java.net.MalformedURLException; +import java.net.URISyntaxException; import java.sql.SQLException; import java.time.temporal.ChronoUnit; import java.util.Date; @@ -56,10 +58,12 @@ import org.dspace.content.Bitstream; import org.dspace.content.Collection; import org.dspace.content.Item; +import org.dspace.services.ConfigurationService; import org.hamcrest.Matchers; import org.junit.Before; import org.junit.Test; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationContext; /** * @@ -82,6 +86,12 @@ public class RequestItemRepositoryIT @Autowired(required = true) RequestItemService requestItemService; + @Autowired + ApplicationContext applicationContext; + + @Autowired + private ConfigurationService configurationService; + private Collection collection; private Item item; @@ -610,4 +620,23 @@ public void testGetDomainClass() { Class instanceClass = instance.getDomainClass(); assertEquals("Wrong domain class", RequestItemRest.class, instanceClass); } + + /** + * Test that generated links include the correct base URL, even if it has extra URL segments + */ + @Test + public void testGetLinkTokenEmail() throws MalformedURLException, URISyntaxException { + RequestItemRepository instance = applicationContext.getBean( + RequestItemRest.CATEGORY + '.' + RequestItemRest.PLURAL_NAME, + RequestItemRepository.class); + String currentDspaceUrl = configurationService.getProperty("dspace.ui.url"); + String newDspaceUrl = currentDspaceUrl + "/subdir"; + // Add a /subdir to the url for this test + configurationService.setProperty("dspace.ui.url", newDspaceUrl); + String expectedUrl = newDspaceUrl + "/request-a-copy/token"; + String generatedLink = instance.getLinkTokenEmail("token"); + // The URLs should match + assertEquals(expectedUrl, generatedLink); + configurationService.reloadConfig(); + } } From 64255ffb05788898ee37baef7d42e1e89f98764a Mon Sep 17 00:00:00 2001 From: Kim Shepherd Date: Wed, 10 Jul 2024 16:04:34 +0200 Subject: [PATCH 093/979] #9668: Ensure proper handling of non-subpath URLs in link tokens --- .../repository/RequestItemRepository.java | 5 +++-- .../app/rest/RequestItemRepositoryIT.java | 20 +++++++++++++++++-- 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/RequestItemRepository.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/RequestItemRepository.java index 3a3444615344..71c46b4dc843 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/RequestItemRepository.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/RequestItemRepository.java @@ -297,8 +297,9 @@ public String getLinkTokenEmail(String token) final String base = configurationService.getProperty("dspace.ui.url"); URIBuilder uriBuilder = new URIBuilder(base); // Add request-a-copy/token to the existing path (without breaking /sub/dir dspace URLs) - URI uri = uriBuilder.setPath(StringUtils.stripEnd(uriBuilder.getPath(), "") - + "/request-a-copy/" + token).build(); + URI uri = uriBuilder.setPath( + (uriBuilder.getPath() == null ? "" : StringUtils.stripEnd(uriBuilder.getPath(), "")) + + "/request-a-copy/" + token).build(); return uri.toURL().toExternalForm(); } } diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/RequestItemRepositoryIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/RequestItemRepositoryIT.java index 065660f2c879..f8732d0826e3 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/RequestItemRepositoryIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/RequestItemRepositoryIT.java @@ -622,10 +622,10 @@ public void testGetDomainClass() { } /** - * Test that generated links include the correct base URL, even if it has extra URL segments + * Test that generated links include the correct base URL, where the UI URL has a subpath like /subdir */ @Test - public void testGetLinkTokenEmail() throws MalformedURLException, URISyntaxException { + public void testGetLinkTokenEmailWithSubPath() throws MalformedURLException, URISyntaxException { RequestItemRepository instance = applicationContext.getBean( RequestItemRest.CATEGORY + '.' + RequestItemRest.PLURAL_NAME, RequestItemRepository.class); @@ -639,4 +639,20 @@ public void testGetLinkTokenEmail() throws MalformedURLException, URISyntaxExcep assertEquals(expectedUrl, generatedLink); configurationService.reloadConfig(); } + + /** + * Test that generated links include the correct base URL, with NO subpath elements + */ + @Test + public void testGetLinkTokenEmailWithoutSubPath() throws MalformedURLException, URISyntaxException { + RequestItemRepository instance = applicationContext.getBean( + RequestItemRest.CATEGORY + '.' + RequestItemRest.PLURAL_NAME, + RequestItemRepository.class); + String currentDspaceUrl = configurationService.getProperty("dspace.ui.url"); + String expectedUrl = currentDspaceUrl + "/request-a-copy/token"; + String generatedLink = instance.getLinkTokenEmail("token"); + // The URLs should match + assertEquals(expectedUrl, generatedLink); + configurationService.reloadConfig(); + } } From 1914aac18b244c3b4053a56905c78d359b5e1910 Mon Sep 17 00:00:00 2001 From: Kim Shepherd Date: Sun, 1 Sep 2024 12:53:04 +0200 Subject: [PATCH 094/979] Improved URI build method as per review --- .../rest/repository/RequestItemRepository.java | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/RequestItemRepository.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/RequestItemRepository.java index 71c46b4dc843..8be7971c84dd 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/RequestItemRepository.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/RequestItemRepository.java @@ -15,7 +15,11 @@ import java.net.URI; import java.net.URISyntaxException; import java.sql.SQLException; +import java.util.ArrayList; +import java.util.Collections; import java.util.Date; +import java.util.LinkedList; +import java.util.List; import java.util.UUID; import javax.servlet.http.HttpServletRequest; @@ -295,11 +299,16 @@ public Class getDomainClass() { public String getLinkTokenEmail(String token) throws URISyntaxException, MalformedURLException { final String base = configurationService.getProperty("dspace.ui.url"); + URIBuilder uriBuilder = new URIBuilder(base); - // Add request-a-copy/token to the existing path (without breaking /sub/dir dspace URLs) - URI uri = uriBuilder.setPath( - (uriBuilder.getPath() == null ? "" : StringUtils.stripEnd(uriBuilder.getPath(), "")) - + "/request-a-copy/" + token).build(); + List segments = new LinkedList<>(); + if (StringUtils.isNotBlank(uriBuilder.getPath())) { + segments.add(StringUtils.strip(uriBuilder.getPath(), "/")); + } + segments.add("request-a-copy"); + segments.add(token); + URI uri = uriBuilder.setPathSegments(segments).build(); + return uri.toURL().toExternalForm(); } } From ab43b8606615ce6a77cb88333469611dc8411fb6 Mon Sep 17 00:00:00 2001 From: Kim Shepherd Date: Wed, 4 Sep 2024 14:04:20 +0200 Subject: [PATCH 095/979] Tidy implementation of link token generation --- .../dspace/app/rest/repository/RequestItemRepository.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/RequestItemRepository.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/RequestItemRepository.java index 8be7971c84dd..f7c986ff9a02 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/RequestItemRepository.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/RequestItemRepository.java @@ -300,6 +300,7 @@ public String getLinkTokenEmail(String token) throws URISyntaxException, MalformedURLException { final String base = configurationService.getProperty("dspace.ui.url"); + // Construct the link, making sure to support sub-paths URIBuilder uriBuilder = new URIBuilder(base); List segments = new LinkedList<>(); if (StringUtils.isNotBlank(uriBuilder.getPath())) { @@ -307,8 +308,8 @@ public String getLinkTokenEmail(String token) } segments.add("request-a-copy"); segments.add(token); - URI uri = uriBuilder.setPathSegments(segments).build(); - return uri.toURL().toExternalForm(); + // Build and return the URL from segments (or throw exception) + return uriBuilder.setPathSegments(segments).build().toURL().toExternalForm(); } } From 7ec746d0052a01f5c5ed9f0351c988fe13f6aafa Mon Sep 17 00:00:00 2001 From: Kim Shepherd Date: Wed, 4 Sep 2024 15:19:34 +0200 Subject: [PATCH 096/979] lint fixes (RequestItemRepository) --- .../org/dspace/app/rest/repository/RequestItemRepository.java | 3 --- 1 file changed, 3 deletions(-) diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/RequestItemRepository.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/RequestItemRepository.java index f7c986ff9a02..10208b27fe7a 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/RequestItemRepository.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/RequestItemRepository.java @@ -12,11 +12,8 @@ import java.io.IOException; import java.net.MalformedURLException; -import java.net.URI; import java.net.URISyntaxException; import java.sql.SQLException; -import java.util.ArrayList; -import java.util.Collections; import java.util.Date; import java.util.LinkedList; import java.util.List; From d40a5e2a4b1e90b3ca7e2ba7e75bb5cbe7bb72fa Mon Sep 17 00:00:00 2001 From: Andrew Date: Mon, 17 Jun 2024 13:02:33 +0200 Subject: [PATCH 097/979] feat: new parameter "fromdate" to evaluate items only from certain date (cherry picked from commit a9e120f3d434ece061dcaf7371822b9201eac655) --- .../app/mediafilter/MediaFilterScript.java | 15 +++++++++++++-- .../MediaFilterScriptConfiguration.java | 2 ++ .../mediafilter/MediaFilterServiceImpl.java | 18 ++++++++++++++++++ .../service/MediaFilterService.java | 3 +++ 4 files changed, 36 insertions(+), 2 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/app/mediafilter/MediaFilterScript.java b/dspace-api/src/main/java/org/dspace/app/mediafilter/MediaFilterScript.java index 5fbbebbb28cc..71c0d6e14ddb 100644 --- a/dspace-api/src/main/java/org/dspace/app/mediafilter/MediaFilterScript.java +++ b/dspace-api/src/main/java/org/dspace/app/mediafilter/MediaFilterScript.java @@ -7,6 +7,7 @@ */ package org.dspace.app.mediafilter; +import java.time.LocalDate; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; @@ -37,8 +38,9 @@ * MFM: -v verbose outputs all extracted text to STDOUT; -f force forces all * bitstreams to be processed, even if they have been before; -n noindex does not * recreate index after processing bitstreams; -i [identifier] limits processing - * scope to a community, collection or item; and -m [max] limits processing to a - * maximum number of items. + * scope to a community, collection or item; -m [max] limits processing to a + * maximum number of items; -fd [fromdate] takes only items starting from this date, + * filtering by last_modified in the item table. */ public class MediaFilterScript extends DSpaceRunnable { @@ -60,6 +62,7 @@ public class MediaFilterScript extends DSpaceRunnable> filterFormats = new HashMap<>(); + private LocalDate fromDate = null; public MediaFilterScriptConfiguration getScriptConfiguration() { return new DSpace().getServiceManager() @@ -112,6 +115,10 @@ public void setup() throws ParseException { skipIds = commandLine.getOptionValues('s'); } + if (commandLine.hasOption('f')) { + fromDate = LocalDate.parse(commandLine.getOptionValue('f')); + } + } @@ -215,6 +222,10 @@ public void internalRun() throws Exception { mediaFilterService.setSkipList(Arrays.asList(skipIds)); } + if (fromDate != null) { + mediaFilterService.setFromDate(fromDate); + } + Context c = null; try { diff --git a/dspace-api/src/main/java/org/dspace/app/mediafilter/MediaFilterScriptConfiguration.java b/dspace-api/src/main/java/org/dspace/app/mediafilter/MediaFilterScriptConfiguration.java index 7465fa6e1279..9f62f19e8367 100644 --- a/dspace-api/src/main/java/org/dspace/app/mediafilter/MediaFilterScriptConfiguration.java +++ b/dspace-api/src/main/java/org/dspace/app/mediafilter/MediaFilterScriptConfiguration.java @@ -52,6 +52,8 @@ public Options getOptions() { .build(); options.addOption(pluginOption); + options.addOption("fd", "fromdate", true, "Process only item from specified last modified date"); + Option skipOption = Option.builder("s") .longOpt("skip") .hasArg() diff --git a/dspace-api/src/main/java/org/dspace/app/mediafilter/MediaFilterServiceImpl.java b/dspace-api/src/main/java/org/dspace/app/mediafilter/MediaFilterServiceImpl.java index a6ba9fde88d9..512b8f803b9b 100644 --- a/dspace-api/src/main/java/org/dspace/app/mediafilter/MediaFilterServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/app/mediafilter/MediaFilterServiceImpl.java @@ -9,8 +9,11 @@ import java.io.InputStream; import java.sql.SQLException; +import java.time.LocalDate; +import java.time.ZoneId; import java.util.ArrayList; import java.util.Collections; +import java.util.Date; import java.util.HashMap; import java.util.Iterator; import java.util.List; @@ -93,6 +96,7 @@ public class MediaFilterServiceImpl implements MediaFilterService, InitializingB protected boolean isVerbose = false; protected boolean isQuiet = false; protected boolean isForce = false; // default to not forced + protected LocalDate fromDate = null; protected MediaFilterServiceImpl() { @@ -120,6 +124,15 @@ public void applyFiltersAllItems(Context context) throws Exception { for (Community topLevelCommunity : topLevelCommunities) { applyFiltersCommunity(context, topLevelCommunity); } + } else if (fromDate != null) { + Iterator itemIterator = + itemService.findByLastModifiedSince( + context, + Date.from(fromDate.atStartOfDay(ZoneId.systemDefault()).toInstant()) + ); + while (itemIterator.hasNext() && processed < max2Process) { + applyFiltersItem(context, itemIterator.next()); + } } else { //otherwise, just find every item and process Iterator itemIterator = itemService.findAll(context); @@ -588,4 +601,9 @@ public void setFilterFormats(Map> filterFormats) { public void setLogHandler(DSpaceRunnableHandler handler) { this.handler = handler; } + + @Override + public void setFromDate(LocalDate fromDate) { + this.fromDate = fromDate; + } } diff --git a/dspace-api/src/main/java/org/dspace/app/mediafilter/service/MediaFilterService.java b/dspace-api/src/main/java/org/dspace/app/mediafilter/service/MediaFilterService.java index bc92ff521098..30e6dba42f08 100644 --- a/dspace-api/src/main/java/org/dspace/app/mediafilter/service/MediaFilterService.java +++ b/dspace-api/src/main/java/org/dspace/app/mediafilter/service/MediaFilterService.java @@ -8,6 +8,7 @@ package org.dspace.app.mediafilter.service; import java.sql.SQLException; +import java.time.LocalDate; import java.util.List; import java.util.Map; @@ -149,4 +150,6 @@ public void updatePoliciesOfDerivativeBitstreams(Context context, Item item, Bit * @param handler */ public void setLogHandler(DSpaceRunnableHandler handler); + + public void setFromDate(LocalDate fromDate); } From cc76ebee108074c538090230cb397ce44f01e9d3 Mon Sep 17 00:00:00 2001 From: Andrew Date: Mon, 17 Jun 2024 13:57:39 +0200 Subject: [PATCH 098/979] refactor: changed short parameter fd to d (cherry picked from commit 2f6b7f3ee4b0c40d215730dd2e86f4492a455e77) --- .../main/java/org/dspace/app/mediafilter/MediaFilterScript.java | 2 +- .../dspace/app/mediafilter/MediaFilterScriptConfiguration.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/app/mediafilter/MediaFilterScript.java b/dspace-api/src/main/java/org/dspace/app/mediafilter/MediaFilterScript.java index 71c0d6e14ddb..e76feada0339 100644 --- a/dspace-api/src/main/java/org/dspace/app/mediafilter/MediaFilterScript.java +++ b/dspace-api/src/main/java/org/dspace/app/mediafilter/MediaFilterScript.java @@ -115,7 +115,7 @@ public void setup() throws ParseException { skipIds = commandLine.getOptionValues('s'); } - if (commandLine.hasOption('f')) { + if (commandLine.hasOption('d')) { fromDate = LocalDate.parse(commandLine.getOptionValue('f')); } diff --git a/dspace-api/src/main/java/org/dspace/app/mediafilter/MediaFilterScriptConfiguration.java b/dspace-api/src/main/java/org/dspace/app/mediafilter/MediaFilterScriptConfiguration.java index 9f62f19e8367..c9f61292d617 100644 --- a/dspace-api/src/main/java/org/dspace/app/mediafilter/MediaFilterScriptConfiguration.java +++ b/dspace-api/src/main/java/org/dspace/app/mediafilter/MediaFilterScriptConfiguration.java @@ -52,7 +52,7 @@ public Options getOptions() { .build(); options.addOption(pluginOption); - options.addOption("fd", "fromdate", true, "Process only item from specified last modified date"); + options.addOption("d", "fromdate", true, "Process only item from specified last modified date"); Option skipOption = Option.builder("s") .longOpt("skip") From f652b2c47ac2aab95c8dd84b4f045b715d2ea9f8 Mon Sep 17 00:00:00 2001 From: Andrew Date: Mon, 17 Jun 2024 16:39:02 +0200 Subject: [PATCH 099/979] refactor: changed short parameter fd to d (cherry picked from commit 3fd88b867eb9bbd8b3621904ecac5b845fc7346e) --- .../main/java/org/dspace/app/mediafilter/MediaFilterScript.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dspace-api/src/main/java/org/dspace/app/mediafilter/MediaFilterScript.java b/dspace-api/src/main/java/org/dspace/app/mediafilter/MediaFilterScript.java index e76feada0339..7f022f38b318 100644 --- a/dspace-api/src/main/java/org/dspace/app/mediafilter/MediaFilterScript.java +++ b/dspace-api/src/main/java/org/dspace/app/mediafilter/MediaFilterScript.java @@ -116,7 +116,7 @@ public void setup() throws ParseException { } if (commandLine.hasOption('d')) { - fromDate = LocalDate.parse(commandLine.getOptionValue('f')); + fromDate = LocalDate.parse(commandLine.getOptionValue('d')); } From 83f61ce12caf582ff1a59df699bffe7b09d3b95e Mon Sep 17 00:00:00 2001 From: Andrew Date: Wed, 4 Sep 2024 15:31:28 +0200 Subject: [PATCH 100/979] fix: changed parameter of HQL query (cherry picked from commit 9c7b20ff5721e2886acd7186ba4df310c9efcf07) --- .../src/main/java/org/dspace/content/dao/impl/ItemDAOImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dspace-api/src/main/java/org/dspace/content/dao/impl/ItemDAOImpl.java b/dspace-api/src/main/java/org/dspace/content/dao/impl/ItemDAOImpl.java index 3be39f1788fb..bd042648384b 100644 --- a/dspace-api/src/main/java/org/dspace/content/dao/impl/ItemDAOImpl.java +++ b/dspace-api/src/main/java/org/dspace/content/dao/impl/ItemDAOImpl.java @@ -441,7 +441,7 @@ public int countItems(Context context, List collections, boolean inc public Iterator findByLastModifiedSince(Context context, Date since) throws SQLException { Query query = createQuery(context, - "SELECT i.id FROM Item i WHERE last_modified > :last_modified ORDER BY id"); + "SELECT i.id FROM Item i WHERE lastModified > :last_modified ORDER BY id"); query.setParameter("last_modified", since, TemporalType.TIMESTAMP); @SuppressWarnings("unchecked") List uuids = query.getResultList(); From 4ac4c911cf47ecf3540e05a9067550bba3faab25 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Fri, 6 Sep 2024 14:38:15 -0500 Subject: [PATCH 101/979] Singular name needed in 7.x --- .../java/org/dspace/app/rest/RequestItemRepositoryIT.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/RequestItemRepositoryIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/RequestItemRepositoryIT.java index f8732d0826e3..fbbd179fd287 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/RequestItemRepositoryIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/RequestItemRepositoryIT.java @@ -627,7 +627,7 @@ public void testGetDomainClass() { @Test public void testGetLinkTokenEmailWithSubPath() throws MalformedURLException, URISyntaxException { RequestItemRepository instance = applicationContext.getBean( - RequestItemRest.CATEGORY + '.' + RequestItemRest.PLURAL_NAME, + RequestItemRest.CATEGORY + '.' + RequestItemRest.NAME, RequestItemRepository.class); String currentDspaceUrl = configurationService.getProperty("dspace.ui.url"); String newDspaceUrl = currentDspaceUrl + "/subdir"; @@ -646,7 +646,7 @@ public void testGetLinkTokenEmailWithSubPath() throws MalformedURLException, URI @Test public void testGetLinkTokenEmailWithoutSubPath() throws MalformedURLException, URISyntaxException { RequestItemRepository instance = applicationContext.getBean( - RequestItemRest.CATEGORY + '.' + RequestItemRest.PLURAL_NAME, + RequestItemRest.CATEGORY + '.' + RequestItemRest.NAME, RequestItemRepository.class); String currentDspaceUrl = configurationService.getProperty("dspace.ui.url"); String expectedUrl = currentDspaceUrl + "/request-a-copy/token"; From 9031322e7d4eccc73f057a5b3cf11c3c8cfc5361 Mon Sep 17 00:00:00 2001 From: Mikhail Schastlivtsev Date: Wed, 15 May 2024 10:08:46 +0300 Subject: [PATCH 102/979] add missing wosPublisherContrib key-ref in wos-integration.xml (#9579) (cherry picked from commit 6a8c76bbe1e7149785c17c7a7249e319199c3e0a) --- dspace/config/spring/api/wos-integration.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/dspace/config/spring/api/wos-integration.xml b/dspace/config/spring/api/wos-integration.xml index 17bd53c04a4d..ba90363a2e60 100644 --- a/dspace/config/spring/api/wos-integration.xml +++ b/dspace/config/spring/api/wos-integration.xml @@ -35,6 +35,7 @@ + From a49ee624508f71c3e928d032b8448d1ec1316d6d Mon Sep 17 00:00:00 2001 From: Mikhail Schastlivtsev Date: Mon, 17 Jun 2024 14:06:53 +0300 Subject: [PATCH 103/979] add missing publisher metadatum in test (#9579) (cherry picked from commit cde892c8c7e1df8fff70a4cbfc693f378c2065d2) --- .../org/dspace/app/rest/WOSImportMetadataSourceServiceIT.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/WOSImportMetadataSourceServiceIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/WOSImportMetadataSourceServiceIT.java index f1d3f5303ec0..9f68d79c2036 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/WOSImportMetadataSourceServiceIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/WOSImportMetadataSourceServiceIT.java @@ -136,6 +136,7 @@ private ArrayList getRecords() { MetadatumDTO subject15 = createMetadatumDTO("dc", "subject", null, "Coding concepts"); MetadatumDTO subject16 = createMetadatumDTO("dc", "subject", null, "Lesson design"); MetadatumDTO subject17 = createMetadatumDTO("dc", "subject", null, "Social Sciences"); + MetadatumDTO publisher = createMetadatumDTO("dc", "publisher", null, "SPRINGER"); MetadatumDTO other = createMetadatumDTO("dc", "identifier", "other", "WOS:000805105200003"); metadatums.add(edition); metadatums.add(date); @@ -166,6 +167,7 @@ private ArrayList getRecords() { metadatums.add(subject15); metadatums.add(subject16); metadatums.add(subject17); + metadatums.add(publisher); metadatums.add(other); ImportRecord firstrRecord = new ImportRecord(metadatums); @@ -205,6 +207,7 @@ private ArrayList getRecords() { MetadatumDTO subject26 = createMetadatumDTO("dc", "subject", null, "Social Sciences"); MetadatumDTO subject27 = createMetadatumDTO("dc", "subject", null, "Science & Technology"); MetadatumDTO subject28 = createMetadatumDTO("dc", "subject", null, "Life Sciences & Biomedicine"); + MetadatumDTO publisher2 = createMetadatumDTO("dc", "publisher", null, "NATURE PORTFOLIO"); MetadatumDTO other2 = createMetadatumDTO("dc", "identifier", "other", "WOS:000805100600001"); MetadatumDTO rid = createMetadatumDTO("person", "identifier", "rid", "C-6334-2011"); MetadatumDTO rid2 = createMetadatumDTO("person", "identifier", "rid", "B-1251-2008"); @@ -236,6 +239,7 @@ private ArrayList getRecords() { metadatums2.add(subject26); metadatums2.add(subject27); metadatums2.add(subject28); + metadatums2.add(publisher2); metadatums2.add(other2); metadatums2.add(rid); metadatums2.add(rid2); From 4f5b9bb916b084f258fedb56ffbbc3abeda72895 Mon Sep 17 00:00:00 2001 From: Mikhail Schastlivtsev Date: Mon, 17 Jun 2024 14:06:53 +0300 Subject: [PATCH 104/979] add missing publisher metadatum in test (#9579) --- .../org/dspace/app/rest/WOSImportMetadataSourceServiceIT.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/WOSImportMetadataSourceServiceIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/WOSImportMetadataSourceServiceIT.java index f1d3f5303ec0..9f68d79c2036 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/WOSImportMetadataSourceServiceIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/WOSImportMetadataSourceServiceIT.java @@ -136,6 +136,7 @@ private ArrayList getRecords() { MetadatumDTO subject15 = createMetadatumDTO("dc", "subject", null, "Coding concepts"); MetadatumDTO subject16 = createMetadatumDTO("dc", "subject", null, "Lesson design"); MetadatumDTO subject17 = createMetadatumDTO("dc", "subject", null, "Social Sciences"); + MetadatumDTO publisher = createMetadatumDTO("dc", "publisher", null, "SPRINGER"); MetadatumDTO other = createMetadatumDTO("dc", "identifier", "other", "WOS:000805105200003"); metadatums.add(edition); metadatums.add(date); @@ -166,6 +167,7 @@ private ArrayList getRecords() { metadatums.add(subject15); metadatums.add(subject16); metadatums.add(subject17); + metadatums.add(publisher); metadatums.add(other); ImportRecord firstrRecord = new ImportRecord(metadatums); @@ -205,6 +207,7 @@ private ArrayList getRecords() { MetadatumDTO subject26 = createMetadatumDTO("dc", "subject", null, "Social Sciences"); MetadatumDTO subject27 = createMetadatumDTO("dc", "subject", null, "Science & Technology"); MetadatumDTO subject28 = createMetadatumDTO("dc", "subject", null, "Life Sciences & Biomedicine"); + MetadatumDTO publisher2 = createMetadatumDTO("dc", "publisher", null, "NATURE PORTFOLIO"); MetadatumDTO other2 = createMetadatumDTO("dc", "identifier", "other", "WOS:000805100600001"); MetadatumDTO rid = createMetadatumDTO("person", "identifier", "rid", "C-6334-2011"); MetadatumDTO rid2 = createMetadatumDTO("person", "identifier", "rid", "B-1251-2008"); @@ -236,6 +239,7 @@ private ArrayList getRecords() { metadatums2.add(subject26); metadatums2.add(subject27); metadatums2.add(subject28); + metadatums2.add(publisher2); metadatums2.add(other2); metadatums2.add(rid); metadatums2.add(rid2); From 5263b08eafe4393b6abc2af50132590795cdda81 Mon Sep 17 00:00:00 2001 From: Mikhail Schastlivtsev Date: Wed, 15 May 2024 10:08:46 +0300 Subject: [PATCH 105/979] add missing wosPublisherContrib key-ref in wos-integration.xml (#9579) --- dspace/config/spring/api/wos-integration.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/dspace/config/spring/api/wos-integration.xml b/dspace/config/spring/api/wos-integration.xml index 17bd53c04a4d..ba90363a2e60 100644 --- a/dspace/config/spring/api/wos-integration.xml +++ b/dspace/config/spring/api/wos-integration.xml @@ -35,6 +35,7 @@ + From 405397b8b061205253768db73924aeffa77c846d Mon Sep 17 00:00:00 2001 From: Sascha Szott Date: Wed, 3 Jul 2024 13:23:32 +0200 Subject: [PATCH 106/979] update eperson's attributes right after successful login (cherry picked from commit 428489ca5258ea7ac6942a722621e22c3371bfcf) --- .../authenticate/LDAPAuthentication.java | 69 +++++++++++-------- 1 file changed, 42 insertions(+), 27 deletions(-) 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 585eaf9cd8b1..2203937e75d6 100644 --- a/dspace-api/src/main/java/org/dspace/authenticate/LDAPAuthentication.java +++ b/dspace-api/src/main/java/org/dspace/authenticate/LDAPAuthentication.java @@ -68,12 +68,8 @@ * @author Ivan Masár * @author Michael Plate */ -public class LDAPAuthentication - implements AuthenticationMethod { +public class LDAPAuthentication implements AuthenticationMethod { - /** - * log4j category - */ private static final Logger log = org.apache.logging.log4j.LogManager.getLogger(LDAPAuthentication.class); @@ -130,7 +126,7 @@ public boolean allowSetPassword(Context context, return false; } - /* + /** * This is an explicit method. */ @Override @@ -138,7 +134,7 @@ public boolean isImplicit() { return false; } - /* + /** * Add authenticated users to the group defined in dspace.cfg by * the login.specialgroup key. */ @@ -177,7 +173,7 @@ public List getSpecialGroups(Context context, HttpServletRequest request) return Collections.EMPTY_LIST; } - /* + /** * Authenticate the given credentials. * This is the heart of the authentication method: test the * credentials for authenticity, and if accepted, attempt to match @@ -250,7 +246,7 @@ public int authenticate(Context context, } // Check a DN was found - if ((dn == null) || (dn.trim().equals(""))) { + if (StringUtils.isBlank(dn)) { log.info(LogHelper .getHeader(context, "failed_login", "no DN found for user " + netid)); return BAD_CREDENTIALS; @@ -269,6 +265,18 @@ public int authenticate(Context context, context.setCurrentUser(eperson); request.setAttribute(LDAP_AUTHENTICATED, true); + // update eperson's attributes + context.turnOffAuthorisationSystem(); + setEpersonAttributes(context, eperson, ldap, Optional.empty()); + try { + ePersonService.update(context, eperson); + context.dispatchEvents(); + } catch (AuthorizeException e) { + log.warn("update of eperson " + eperson.getID() + " failed", e); + } finally { + context.restoreAuthSystemState(); + } + // assign user to groups based on ldap dn assignGroups(dn, ldap.ldapGroup, context); @@ -313,14 +321,13 @@ public int authenticate(Context context, log.info(LogHelper.getHeader(context, "type=ldap-login", "type=ldap_but_already_email")); context.turnOffAuthorisationSystem(); - eperson.setNetid(netid.toLowerCase()); + setEpersonAttributes(context, eperson, ldap, Optional.of(netid)); ePersonService.update(context, eperson); context.dispatchEvents(); context.restoreAuthSystemState(); context.setCurrentUser(eperson); request.setAttribute(LDAP_AUTHENTICATED, true); - // assign user to groups based on ldap dn assignGroups(dn, ldap.ldapGroup, context); @@ -331,20 +338,7 @@ public int authenticate(Context context, try { context.turnOffAuthorisationSystem(); eperson = ePersonService.create(context); - if (StringUtils.isNotEmpty(email)) { - eperson.setEmail(email); - } - if (StringUtils.isNotEmpty(ldap.ldapGivenName)) { - eperson.setFirstName(context, ldap.ldapGivenName); - } - if (StringUtils.isNotEmpty(ldap.ldapSurname)) { - eperson.setLastName(context, ldap.ldapSurname); - } - if (StringUtils.isNotEmpty(ldap.ldapPhone)) { - ePersonService.setMetadataSingleValue(context, eperson, - MD_PHONE, ldap.ldapPhone, null); - } - eperson.setNetid(netid.toLowerCase()); + setEpersonAttributes(context, eperson, ldap, Optional.of(netid)); eperson.setCanLogIn(true); authenticationService.initEPerson(context, request, eperson); ePersonService.update(context, eperson); @@ -382,6 +376,27 @@ public int authenticate(Context context, return BAD_ARGS; } + /** + * Update eperson's attributes + */ + private void setEpersonAttributes(Context context, EPerson eperson, SpeakerToLDAP ldap, Optional netid) throws SQLException { + if (StringUtils.isNotEmpty(ldap.ldapEmail)) { + eperson.setEmail(ldap.ldapEmail); + } + if (StringUtils.isNotEmpty(ldap.ldapGivenName)) { + eperson.setFirstName(context, ldap.ldapGivenName); + } + if (StringUtils.isNotEmpty(ldap.ldapSurname)) { + eperson.setLastName(context, ldap.ldapSurname); + } + if (StringUtils.isNotEmpty(ldap.ldapPhone)) { + ePersonService.setMetadataSingleValue(context, eperson, MD_PHONE, ldap.ldapPhone, null); + } + if (netid.isPresent()) { + eperson.setNetid(netid.get().toLowerCase()); + } + } + /** * Internal class to manage LDAP query and results, mainly * because there are multiple values to return. @@ -671,7 +686,7 @@ protected boolean ldapAuthenticate(String netid, String password, } } - /* + /** * Returns the URL of an external login page which is not applicable for this authn method. * * Note: Prior to DSpace 7, this method return the page of login servlet. @@ -699,7 +714,7 @@ public String getName() { return "ldap"; } - /* + /** * Add authenticated users to the group defined in dspace.cfg by * the authentication-ldap.login.groupmap.* key. * From b0370a064b843db6d77efb58c5d54788e3872253 Mon Sep 17 00:00:00 2001 From: Sascha Szott Date: Wed, 3 Jul 2024 13:36:45 +0200 Subject: [PATCH 107/979] add missing import (cherry picked from commit c5ad32a9b3ece7e64043f9f39d22b594e913737f) --- .../main/java/org/dspace/authenticate/LDAPAuthentication.java | 1 + 1 file changed, 1 insertion(+) 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 2203937e75d6..06bd6ae603d9 100644 --- a/dspace-api/src/main/java/org/dspace/authenticate/LDAPAuthentication.java +++ b/dspace-api/src/main/java/org/dspace/authenticate/LDAPAuthentication.java @@ -17,6 +17,7 @@ import java.util.Hashtable; import java.util.Iterator; import java.util.List; +import java.util.Optional; import javax.naming.NamingEnumeration; import javax.naming.NamingException; import javax.naming.directory.Attribute; From 31af49ea725b5658f408e519cc577805b8f8c72d Mon Sep 17 00:00:00 2001 From: Sascha Szott Date: Wed, 3 Jul 2024 13:54:53 +0200 Subject: [PATCH 108/979] fix Checkstyle violations (cherry picked from commit aaa74b88c99af8ece67d43fa412a39a7406fe10c) --- .../org/dspace/authenticate/LDAPAuthentication.java | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) 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 06bd6ae603d9..93f0feb384c0 100644 --- a/dspace-api/src/main/java/org/dspace/authenticate/LDAPAuthentication.java +++ b/dspace-api/src/main/java/org/dspace/authenticate/LDAPAuthentication.java @@ -184,7 +184,7 @@ public List getSpecialGroups(Context context, HttpServletRequest request) * @param context * DSpace context, will be modified (ePerson set) upon success. * - * @param username + * @param netid * Username (or email address) when method is explicit. Use null for * implicit 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)); ePersonService.update(context, eperson); context.dispatchEvents(); context.restoreAuthSystemState(); @@ -380,7 +380,9 @@ public int authenticate(Context context, /** * Update eperson's attributes */ - private void setEpersonAttributes(Context context, EPerson eperson, SpeakerToLDAP ldap, Optional netid) throws SQLException { + private void setEpersonAttributes(Context context, EPerson eperson, SpeakerToLDAP ldap, Optional netid) + throws SQLException { + if (StringUtils.isNotEmpty(ldap.ldapEmail)) { eperson.setEmail(ldap.ldapEmail); } @@ -389,7 +391,7 @@ private void setEpersonAttributes(Context context, EPerson eperson, SpeakerToLDA } if (StringUtils.isNotEmpty(ldap.ldapSurname)) { eperson.setLastName(context, ldap.ldapSurname); - } + } if (StringUtils.isNotEmpty(ldap.ldapPhone)) { ePersonService.setMetadataSingleValue(context, eperson, MD_PHONE, ldap.ldapPhone, null); } From da6de795338f95e8b6658c4765d5837e6325554c Mon Sep 17 00:00:00 2001 From: Sascha Szott Date: Wed, 3 Jul 2024 13:23:32 +0200 Subject: [PATCH 109/979] update eperson's attributes right after successful login (cherry picked from commit 428489ca5258ea7ac6942a722621e22c3371bfcf) --- .../authenticate/LDAPAuthentication.java | 69 +++++++++++-------- 1 file changed, 42 insertions(+), 27 deletions(-) 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 b791df15b5c0..c6df4b30faad 100644 --- a/dspace-api/src/main/java/org/dspace/authenticate/LDAPAuthentication.java +++ b/dspace-api/src/main/java/org/dspace/authenticate/LDAPAuthentication.java @@ -68,12 +68,8 @@ * @author Ivan Masár * @author Michael Plate */ -public class LDAPAuthentication - implements AuthenticationMethod { +public class LDAPAuthentication implements AuthenticationMethod { - /** - * log4j category - */ private static final Logger log = org.apache.logging.log4j.LogManager.getLogger(LDAPAuthentication.class); @@ -130,7 +126,7 @@ public boolean allowSetPassword(Context context, return false; } - /* + /** * This is an explicit method. */ @Override @@ -138,7 +134,7 @@ public boolean isImplicit() { return false; } - /* + /** * Add authenticated users to the group defined in dspace.cfg by * the login.specialgroup key. */ @@ -177,7 +173,7 @@ public List getSpecialGroups(Context context, HttpServletRequest request) return Collections.EMPTY_LIST; } - /* + /** * Authenticate the given credentials. * This is the heart of the authentication method: test the * credentials for authenticity, and if accepted, attempt to match @@ -250,7 +246,7 @@ public int authenticate(Context context, } // Check a DN was found - if ((dn == null) || (dn.trim().equals(""))) { + if (StringUtils.isBlank(dn)) { log.info(LogHelper .getHeader(context, "failed_login", "no DN found for user " + netid)); return BAD_CREDENTIALS; @@ -269,6 +265,18 @@ public int authenticate(Context context, context.setCurrentUser(eperson); request.setAttribute(LDAP_AUTHENTICATED, true); + // update eperson's attributes + context.turnOffAuthorisationSystem(); + setEpersonAttributes(context, eperson, ldap, Optional.empty()); + try { + ePersonService.update(context, eperson); + context.dispatchEvents(); + } catch (AuthorizeException e) { + log.warn("update of eperson " + eperson.getID() + " failed", e); + } finally { + context.restoreAuthSystemState(); + } + // assign user to groups based on ldap dn assignGroups(dn, ldap.ldapGroup, context); @@ -313,14 +321,13 @@ public int authenticate(Context context, log.info(LogHelper.getHeader(context, "type=ldap-login", "type=ldap_but_already_email")); context.turnOffAuthorisationSystem(); - eperson.setNetid(netid.toLowerCase()); + setEpersonAttributes(context, eperson, ldap, Optional.of(netid)); ePersonService.update(context, eperson); context.dispatchEvents(); context.restoreAuthSystemState(); context.setCurrentUser(eperson); request.setAttribute(LDAP_AUTHENTICATED, true); - // assign user to groups based on ldap dn assignGroups(dn, ldap.ldapGroup, context); @@ -331,20 +338,7 @@ public int authenticate(Context context, try { context.turnOffAuthorisationSystem(); eperson = ePersonService.create(context); - if (StringUtils.isNotEmpty(email)) { - eperson.setEmail(email); - } - if (StringUtils.isNotEmpty(ldap.ldapGivenName)) { - eperson.setFirstName(context, ldap.ldapGivenName); - } - if (StringUtils.isNotEmpty(ldap.ldapSurname)) { - eperson.setLastName(context, ldap.ldapSurname); - } - if (StringUtils.isNotEmpty(ldap.ldapPhone)) { - ePersonService.setMetadataSingleValue(context, eperson, - MD_PHONE, ldap.ldapPhone, null); - } - eperson.setNetid(netid.toLowerCase()); + setEpersonAttributes(context, eperson, ldap, Optional.of(netid)); eperson.setCanLogIn(true); authenticationService.initEPerson(context, request, eperson); ePersonService.update(context, eperson); @@ -382,6 +376,27 @@ public int authenticate(Context context, return BAD_ARGS; } + /** + * Update eperson's attributes + */ + private void setEpersonAttributes(Context context, EPerson eperson, SpeakerToLDAP ldap, Optional netid) throws SQLException { + if (StringUtils.isNotEmpty(ldap.ldapEmail)) { + eperson.setEmail(ldap.ldapEmail); + } + if (StringUtils.isNotEmpty(ldap.ldapGivenName)) { + eperson.setFirstName(context, ldap.ldapGivenName); + } + if (StringUtils.isNotEmpty(ldap.ldapSurname)) { + eperson.setLastName(context, ldap.ldapSurname); + } + if (StringUtils.isNotEmpty(ldap.ldapPhone)) { + ePersonService.setMetadataSingleValue(context, eperson, MD_PHONE, ldap.ldapPhone, null); + } + if (netid.isPresent()) { + eperson.setNetid(netid.get().toLowerCase()); + } + } + /** * Internal class to manage LDAP query and results, mainly * because there are multiple values to return. @@ -671,7 +686,7 @@ protected boolean ldapAuthenticate(String netid, String password, } } - /* + /** * Returns the URL of an external login page which is not applicable for this authn method. * * Note: Prior to DSpace 7, this method return the page of login servlet. @@ -699,7 +714,7 @@ public String getName() { return "ldap"; } - /* + /** * Add authenticated users to the group defined in dspace.cfg by * the authentication-ldap.login.groupmap.* key. * From c6d95c69583342e3d18bb90b81da1b7e40c838e0 Mon Sep 17 00:00:00 2001 From: Sascha Szott Date: Wed, 3 Jul 2024 13:36:45 +0200 Subject: [PATCH 110/979] add missing import (cherry picked from commit c5ad32a9b3ece7e64043f9f39d22b594e913737f) --- .../main/java/org/dspace/authenticate/LDAPAuthentication.java | 1 + 1 file changed, 1 insertion(+) 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 c6df4b30faad..9ca5245c54ba 100644 --- a/dspace-api/src/main/java/org/dspace/authenticate/LDAPAuthentication.java +++ b/dspace-api/src/main/java/org/dspace/authenticate/LDAPAuthentication.java @@ -17,6 +17,7 @@ import java.util.Hashtable; import java.util.Iterator; import java.util.List; +import java.util.Optional; import javax.naming.NamingEnumeration; import javax.naming.NamingException; import javax.naming.directory.Attribute; From 8a3596d0db8b2f0abdca932312a960e487028ddf Mon Sep 17 00:00:00 2001 From: Sascha Szott Date: Wed, 3 Jul 2024 13:54:53 +0200 Subject: [PATCH 111/979] fix Checkstyle violations (cherry picked from commit aaa74b88c99af8ece67d43fa412a39a7406fe10c) --- .../org/dspace/authenticate/LDAPAuthentication.java | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) 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 9ca5245c54ba..4f2c5cc3d02c 100644 --- a/dspace-api/src/main/java/org/dspace/authenticate/LDAPAuthentication.java +++ b/dspace-api/src/main/java/org/dspace/authenticate/LDAPAuthentication.java @@ -184,7 +184,7 @@ public List getSpecialGroups(Context context, HttpServletRequest request) * @param context * DSpace context, will be modified (ePerson set) upon success. * - * @param username + * @param netid * Username (or email address) when method is explicit. Use null for * implicit 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)); ePersonService.update(context, eperson); context.dispatchEvents(); context.restoreAuthSystemState(); @@ -380,7 +380,9 @@ public int authenticate(Context context, /** * Update eperson's attributes */ - private void setEpersonAttributes(Context context, EPerson eperson, SpeakerToLDAP ldap, Optional netid) throws SQLException { + private void setEpersonAttributes(Context context, EPerson eperson, SpeakerToLDAP ldap, Optional netid) + throws SQLException { + if (StringUtils.isNotEmpty(ldap.ldapEmail)) { eperson.setEmail(ldap.ldapEmail); } @@ -389,7 +391,7 @@ private void setEpersonAttributes(Context context, EPerson eperson, SpeakerToLDA } if (StringUtils.isNotEmpty(ldap.ldapSurname)) { eperson.setLastName(context, ldap.ldapSurname); - } + } if (StringUtils.isNotEmpty(ldap.ldapPhone)) { ePersonService.setMetadataSingleValue(context, eperson, MD_PHONE, ldap.ldapPhone, null); } From 702ff9cf4c0e9e2afeb1bab820087e6fb7584154 Mon Sep 17 00:00:00 2001 From: Sascha Szott Date: Fri, 27 Sep 2024 12:07:52 +0200 Subject: [PATCH 112/979] minor fix in parameter description (cherry picked from commit 5758d9e90302d4da33ec869b6ec656e5c25b865a) --- dspace/config/emails/subscriptions_content | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dspace/config/emails/subscriptions_content b/dspace/config/emails/subscriptions_content index 9b8c91e559df..a1b107e8fc50 100644 --- a/dspace/config/emails/subscriptions_content +++ b/dspace/config/emails/subscriptions_content @@ -1,7 +1,7 @@ ## E-mail sent to designated address about updates on subscribed items ## -## Parameters: {0} Collections updates -## {1} Communities updates +## Parameters: {0} Communities updates +## {1} Collections updates #set($subject = "${config.get('dspace.name')} Subscriptions") This email is sent from ${config.get('dspace.name')} based on the chosen subscription preferences. From c08c62fccf450c98225827e3ecc126e494b16c5e Mon Sep 17 00:00:00 2001 From: Sascha Szott Date: Fri, 27 Sep 2024 12:07:52 +0200 Subject: [PATCH 113/979] minor fix in parameter description (cherry picked from commit 5758d9e90302d4da33ec869b6ec656e5c25b865a) --- dspace/config/emails/subscriptions_content | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dspace/config/emails/subscriptions_content b/dspace/config/emails/subscriptions_content index 9b8c91e559df..a1b107e8fc50 100644 --- a/dspace/config/emails/subscriptions_content +++ b/dspace/config/emails/subscriptions_content @@ -1,7 +1,7 @@ ## E-mail sent to designated address about updates on subscribed items ## -## Parameters: {0} Collections updates -## {1} Communities updates +## Parameters: {0} Communities updates +## {1} Collections updates #set($subject = "${config.get('dspace.name')} Subscriptions") This email is sent from ${config.get('dspace.name')} based on the chosen subscription preferences. From a721f8c41e92762d6d1af831b337b111a5ea9991 Mon Sep 17 00:00:00 2001 From: Sascha Szott Date: Tue, 27 Aug 2024 10:33:01 +0200 Subject: [PATCH 114/979] fix failed first login attempt in HAL browser (cherry picked from commit 002e637d4fc69b31c2a2321230d96534e49ee33e) --- .../src/main/webapp/login.html | 128 ++++++++++-------- 1 file changed, 68 insertions(+), 60 deletions(-) diff --git a/dspace-server-webapp/src/main/webapp/login.html b/dspace-server-webapp/src/main/webapp/login.html index 50d95b80472f..0597a66bfe96 100644 --- a/dspace-server-webapp/src/main/webapp/login.html +++ b/dspace-server-webapp/src/main/webapp/login.html @@ -32,7 +32,7 @@ border-radius: 5px; box-shadow: 0 1px 2px rgba(0, 0, 0, .05); } - .form-signin .form-signin-heading, .form-signin .checkbox { + .form-signin .form-signin-heading, .form-signin { margin-bottom: 10px; } .form-signin input[type="text"], .form-signin input[type="password"] { @@ -94,63 +94,71 @@

    Other login methods:

    "onclick" : function() { toastr.remove(); } } + // retrieves a valid CSRF token (please note that this method works both in DS 7 and DS 8) + // HTTP response code 403 is expected at this point (the response contains the DSPACE-XSRF-TOKEN header) + $.ajax({ + url : window.location.href.replace("login.html", "") + 'api/authn/login', + type : 'POST', + error : function(xhr) { + // Check for an update to the CSRF Token & save to a MyHalBrowserCsrfToken cookie (if found) + checkForUpdatedCSRFTokenInResponse(xhr); + } + }); + // When the login page loads, we do *two* AJAX requests. - // (1) Call GET /api/authn/status. This call has two purposes. First, it checks to see if you are logged in, - // (if not, WWW-Authenticate will return login options). Second, it retrieves the CSRF token, if a - // new one has been assigned (as a valid CSRF token is required for the POST call). + // (1) Call GET /api/authn/status. This call checks to see if you are logged in + // (if not, WWW-Authenticate will return login options). // (2) If that /api/authn/status call finds authentication data, call POST /api/authn/login. - // This scenario occurs when you login via an external authentication system (e.g. Shibboleth)... + // This scenario occurs when you log in via an external authentication system (e.g. Shibboleth) // in which case the main role of /api/authn/login is to simply ensure the "Authorization" header // is sent back to the client (based on your authentication data). $.ajax({ - url : window.location.href.replace("login.html", "") + 'api/authn/status', - type : 'GET', - success : function(result, status, xhr) { - // Check for an update to the CSRF Token & save to a MyHalBrowserCsrfToken cookie (if found) - checkForUpdatedCSRFTokenInResponse(xhr); + url : window.location.href.replace("login.html", "") + 'api/authn/status', + type : 'GET', + success : function(result, status, xhr) { - // Check for WWW-Authenticate header. If found, this means we are not yet authenticated, and - // therefore we need to display available authentication options. - var authenticate = xhr.getResponseHeader("WWW-Authenticate"); - if (authenticate !== null) { - var element = $('div.other-login-methods'); - var realms = authenticate.match(/(\w+ (\w+=((".*?")|[^,]*)(, )?)*)/g); - if (realms.length == 1){ - var loc = /location="([^,]*)"/.exec(authenticate); - if (loc !== null && loc.length === 2) { - document.location = loc[1]; - } - } else if (realms.length > 1){ - for (var i = 0; i < realms.length; i++){ - addLocationButton(realms[i], element); + // Check for WWW-Authenticate header. If found, this means we are not yet authenticated, and + // therefore we need to display available authentication options. + var authenticate = xhr.getResponseHeader("WWW-Authenticate"); + if (authenticate !== null && authenticate.contains('location=')) { + var element = $('div.other-login-methods'); + var realms = authenticate.match(/(\w+ (\w+=((".*?")|[^,]*)(, )?)*)/g); + if (realms.length === 1){ + var loc = /location="([^,]*)"/.exec(authenticate); + if (loc !== null && loc.length === 2) { + document.location = loc[1]; + } + } else if (realms.length > 1){ + for (var i = 0; i < realms.length; i++){ + addLocationButton(realms[i], element); + } } + } else { + // If Authentication data was found, do a POST /api/authn/login to ensure that data's JWT + // is sent back in the "Authorization" header. This simply completes an external authentication + // process (e.g. Shibboleth) + $.ajax({ + url : window.location.href.replace("login.html", "") + 'api/authn/login', + type : 'POST', + beforeSend: function (xhr) { + // If CSRF token found in cookie, send it back as X-XSRF-Token header + var csrfToken = getCSRFToken(); + if (csrfToken != null) { + xhr.setRequestHeader('X-XSRF-Token', csrfToken); + } + }, + success : successHandler, + error : function(xhr) { + // Check for an update to the CSRF Token & save to a MyHalBrowserCsrfToken cookie (if found) + checkForUpdatedCSRFTokenInResponse(xhr); + toastr.error('Failed to logged in. Please check for errors in Javascript console.', 'Login Failed'); + } + }); } - } else { - // If Authentication data was found, do a POST /api/authn/login to ensure that data's JWT - // is sent back in the "Authorization" header. This simply completes an external authentication - // process (e.g. Shibboleth) - $.ajax({ - url : window.location.href.replace("login.html", "") + 'api/authn/login', - type : 'POST', - beforeSend: function (xhr, settings) { - // If CSRF token found in cookie, send it back as X-XSRF-Token header - var csrfToken = getCSRFToken(); - if (csrfToken != null) { - xhr.setRequestHeader('X-XSRF-Token', csrfToken); - } - }, - success : successHandler, - error : function(xhr, textStatus, errorThrown) { - // Check for an update to the CSRF Token & save to a MyHalBrowserCsrfToken cookie (if found) - checkForUpdatedCSRFTokenInResponse(xhr); - toastr.error('Failed to logged in. Please check for errors in Javascript console.', 'Login Failed'); - } - }); + }, + error : function() { + toastr.error('Failed to connect with backend. Please check for errors in Javascript console.', 'Could Not Load'); } - }, - error : function(xhr, textStatus, errorThrown) { - toastr.error('Failed to connect with backend. Please check for errors in Javascript console.', 'Could Not Load'); - } }); function addLocationButton(realm, element){ @@ -166,22 +174,22 @@

    Other login methods:

    return string.charAt(0).toUpperCase() + string.slice(1); } - /** - * Check current response headers to see if the CSRF Token has changed. If a new value is found in headers, - * save the new value into our "MyHalBrowserCsrfToken" cookie. - **/ + /** + * Check current response headers to see if the CSRF Token has changed. If a new value is found in headers, + * save the new value into our "MyHalBrowserCsrfToken" cookie. + **/ function checkForUpdatedCSRFTokenInResponse(jqxhr) { // look for DSpace-XSRF-TOKEN header & save to our MyHalBrowserCsrfToken cookie (if found) var updatedCsrfToken = jqxhr.getResponseHeader('DSPACE-XSRF-TOKEN'); if (updatedCsrfToken != null) { - document.cookie = "MyHalBrowserCsrfToken=" + updatedCsrfToken; + document.cookie = "MyHalBrowserCsrfToken=" + updatedCsrfToken; } } - /** - * Get CSRF Token by parsing it out of the "MyHalBrowserCsrfToken" cookie. - * This cookie is set in login.html after a successful login occurs. - **/ + /** + * Get CSRF Token by parsing it out of the "MyHalBrowserCsrfToken" cookie. + * This cookie is set in login.html after a successful login occurs. + **/ function getCSRFToken() { var cookie = document.cookie.match('(^|;)\\s*' + 'MyHalBrowserCsrfToken' + '\\s*=\\s*([^;]+)'); if (cookie != null) { @@ -204,11 +212,11 @@

    Other login methods:

    user: $("#username").val(), password: $("#password").val() }, - beforeSend: function (xhr, settings) { + beforeSend: function (xhr) { // If CSRF token found in cookie, send it back as X-XSRF-Token header var csrfToken = getCSRFToken(); if (csrfToken != null) { - xhr.setRequestHeader('X-XSRF-Token', csrfToken); + xhr.setRequestHeader('X-XSRF-Token', csrfToken); } }, success : successHandler, From 4669d8f7605a980a3f038c6f10835f89b7d81bee Mon Sep 17 00:00:00 2001 From: Sascha Szott Date: Thu, 29 Aug 2024 16:24:38 +0200 Subject: [PATCH 115/979] applied change suggested by reviewer: use String.prototype.includes (cherry picked from commit 546afb189e1df8bdfbc9948e790004baa81ec894) --- dspace-server-webapp/src/main/webapp/login.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dspace-server-webapp/src/main/webapp/login.html b/dspace-server-webapp/src/main/webapp/login.html index 0597a66bfe96..959190020a13 100644 --- a/dspace-server-webapp/src/main/webapp/login.html +++ b/dspace-server-webapp/src/main/webapp/login.html @@ -120,7 +120,7 @@

    Other login methods:

    // Check for WWW-Authenticate header. If found, this means we are not yet authenticated, and // therefore we need to display available authentication options. var authenticate = xhr.getResponseHeader("WWW-Authenticate"); - if (authenticate !== null && authenticate.contains('location=')) { + if (authenticate !== null && authenticate.includes('location=')) { var element = $('div.other-login-methods'); var realms = authenticate.match(/(\w+ (\w+=((".*?")|[^,]*)(, )?)*)/g); if (realms.length === 1){ From 058f878d9275873bc9849c75777543bce686d989 Mon Sep 17 00:00:00 2001 From: Sascha Szott Date: Tue, 27 Aug 2024 10:33:01 +0200 Subject: [PATCH 116/979] fix failed first login attempt in HAL browser (cherry picked from commit 002e637d4fc69b31c2a2321230d96534e49ee33e) --- .../src/main/resources/static/login.html | 128 ++++++++++-------- 1 file changed, 68 insertions(+), 60 deletions(-) diff --git a/dspace-server-webapp/src/main/resources/static/login.html b/dspace-server-webapp/src/main/resources/static/login.html index 50d95b80472f..0597a66bfe96 100644 --- a/dspace-server-webapp/src/main/resources/static/login.html +++ b/dspace-server-webapp/src/main/resources/static/login.html @@ -32,7 +32,7 @@ border-radius: 5px; box-shadow: 0 1px 2px rgba(0, 0, 0, .05); } - .form-signin .form-signin-heading, .form-signin .checkbox { + .form-signin .form-signin-heading, .form-signin { margin-bottom: 10px; } .form-signin input[type="text"], .form-signin input[type="password"] { @@ -94,63 +94,71 @@

    Other login methods:

    "onclick" : function() { toastr.remove(); } } + // retrieves a valid CSRF token (please note that this method works both in DS 7 and DS 8) + // HTTP response code 403 is expected at this point (the response contains the DSPACE-XSRF-TOKEN header) + $.ajax({ + url : window.location.href.replace("login.html", "") + 'api/authn/login', + type : 'POST', + error : function(xhr) { + // Check for an update to the CSRF Token & save to a MyHalBrowserCsrfToken cookie (if found) + checkForUpdatedCSRFTokenInResponse(xhr); + } + }); + // When the login page loads, we do *two* AJAX requests. - // (1) Call GET /api/authn/status. This call has two purposes. First, it checks to see if you are logged in, - // (if not, WWW-Authenticate will return login options). Second, it retrieves the CSRF token, if a - // new one has been assigned (as a valid CSRF token is required for the POST call). + // (1) Call GET /api/authn/status. This call checks to see if you are logged in + // (if not, WWW-Authenticate will return login options). // (2) If that /api/authn/status call finds authentication data, call POST /api/authn/login. - // This scenario occurs when you login via an external authentication system (e.g. Shibboleth)... + // This scenario occurs when you log in via an external authentication system (e.g. Shibboleth) // in which case the main role of /api/authn/login is to simply ensure the "Authorization" header // is sent back to the client (based on your authentication data). $.ajax({ - url : window.location.href.replace("login.html", "") + 'api/authn/status', - type : 'GET', - success : function(result, status, xhr) { - // Check for an update to the CSRF Token & save to a MyHalBrowserCsrfToken cookie (if found) - checkForUpdatedCSRFTokenInResponse(xhr); + url : window.location.href.replace("login.html", "") + 'api/authn/status', + type : 'GET', + success : function(result, status, xhr) { - // Check for WWW-Authenticate header. If found, this means we are not yet authenticated, and - // therefore we need to display available authentication options. - var authenticate = xhr.getResponseHeader("WWW-Authenticate"); - if (authenticate !== null) { - var element = $('div.other-login-methods'); - var realms = authenticate.match(/(\w+ (\w+=((".*?")|[^,]*)(, )?)*)/g); - if (realms.length == 1){ - var loc = /location="([^,]*)"/.exec(authenticate); - if (loc !== null && loc.length === 2) { - document.location = loc[1]; - } - } else if (realms.length > 1){ - for (var i = 0; i < realms.length; i++){ - addLocationButton(realms[i], element); + // Check for WWW-Authenticate header. If found, this means we are not yet authenticated, and + // therefore we need to display available authentication options. + var authenticate = xhr.getResponseHeader("WWW-Authenticate"); + if (authenticate !== null && authenticate.contains('location=')) { + var element = $('div.other-login-methods'); + var realms = authenticate.match(/(\w+ (\w+=((".*?")|[^,]*)(, )?)*)/g); + if (realms.length === 1){ + var loc = /location="([^,]*)"/.exec(authenticate); + if (loc !== null && loc.length === 2) { + document.location = loc[1]; + } + } else if (realms.length > 1){ + for (var i = 0; i < realms.length; i++){ + addLocationButton(realms[i], element); + } } + } else { + // If Authentication data was found, do a POST /api/authn/login to ensure that data's JWT + // is sent back in the "Authorization" header. This simply completes an external authentication + // process (e.g. Shibboleth) + $.ajax({ + url : window.location.href.replace("login.html", "") + 'api/authn/login', + type : 'POST', + beforeSend: function (xhr) { + // If CSRF token found in cookie, send it back as X-XSRF-Token header + var csrfToken = getCSRFToken(); + if (csrfToken != null) { + xhr.setRequestHeader('X-XSRF-Token', csrfToken); + } + }, + success : successHandler, + error : function(xhr) { + // Check for an update to the CSRF Token & save to a MyHalBrowserCsrfToken cookie (if found) + checkForUpdatedCSRFTokenInResponse(xhr); + toastr.error('Failed to logged in. Please check for errors in Javascript console.', 'Login Failed'); + } + }); } - } else { - // If Authentication data was found, do a POST /api/authn/login to ensure that data's JWT - // is sent back in the "Authorization" header. This simply completes an external authentication - // process (e.g. Shibboleth) - $.ajax({ - url : window.location.href.replace("login.html", "") + 'api/authn/login', - type : 'POST', - beforeSend: function (xhr, settings) { - // If CSRF token found in cookie, send it back as X-XSRF-Token header - var csrfToken = getCSRFToken(); - if (csrfToken != null) { - xhr.setRequestHeader('X-XSRF-Token', csrfToken); - } - }, - success : successHandler, - error : function(xhr, textStatus, errorThrown) { - // Check for an update to the CSRF Token & save to a MyHalBrowserCsrfToken cookie (if found) - checkForUpdatedCSRFTokenInResponse(xhr); - toastr.error('Failed to logged in. Please check for errors in Javascript console.', 'Login Failed'); - } - }); + }, + error : function() { + toastr.error('Failed to connect with backend. Please check for errors in Javascript console.', 'Could Not Load'); } - }, - error : function(xhr, textStatus, errorThrown) { - toastr.error('Failed to connect with backend. Please check for errors in Javascript console.', 'Could Not Load'); - } }); function addLocationButton(realm, element){ @@ -166,22 +174,22 @@

    Other login methods:

    return string.charAt(0).toUpperCase() + string.slice(1); } - /** - * Check current response headers to see if the CSRF Token has changed. If a new value is found in headers, - * save the new value into our "MyHalBrowserCsrfToken" cookie. - **/ + /** + * Check current response headers to see if the CSRF Token has changed. If a new value is found in headers, + * save the new value into our "MyHalBrowserCsrfToken" cookie. + **/ function checkForUpdatedCSRFTokenInResponse(jqxhr) { // look for DSpace-XSRF-TOKEN header & save to our MyHalBrowserCsrfToken cookie (if found) var updatedCsrfToken = jqxhr.getResponseHeader('DSPACE-XSRF-TOKEN'); if (updatedCsrfToken != null) { - document.cookie = "MyHalBrowserCsrfToken=" + updatedCsrfToken; + document.cookie = "MyHalBrowserCsrfToken=" + updatedCsrfToken; } } - /** - * Get CSRF Token by parsing it out of the "MyHalBrowserCsrfToken" cookie. - * This cookie is set in login.html after a successful login occurs. - **/ + /** + * Get CSRF Token by parsing it out of the "MyHalBrowserCsrfToken" cookie. + * This cookie is set in login.html after a successful login occurs. + **/ function getCSRFToken() { var cookie = document.cookie.match('(^|;)\\s*' + 'MyHalBrowserCsrfToken' + '\\s*=\\s*([^;]+)'); if (cookie != null) { @@ -204,11 +212,11 @@

    Other login methods:

    user: $("#username").val(), password: $("#password").val() }, - beforeSend: function (xhr, settings) { + beforeSend: function (xhr) { // If CSRF token found in cookie, send it back as X-XSRF-Token header var csrfToken = getCSRFToken(); if (csrfToken != null) { - xhr.setRequestHeader('X-XSRF-Token', csrfToken); + xhr.setRequestHeader('X-XSRF-Token', csrfToken); } }, success : successHandler, From c468e48f2f1177fc5477f003e68319503bf2b265 Mon Sep 17 00:00:00 2001 From: Sascha Szott Date: Thu, 29 Aug 2024 16:24:38 +0200 Subject: [PATCH 117/979] applied change suggested by reviewer: use String.prototype.includes (cherry picked from commit 546afb189e1df8bdfbc9948e790004baa81ec894) --- dspace-server-webapp/src/main/resources/static/login.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dspace-server-webapp/src/main/resources/static/login.html b/dspace-server-webapp/src/main/resources/static/login.html index 0597a66bfe96..959190020a13 100644 --- a/dspace-server-webapp/src/main/resources/static/login.html +++ b/dspace-server-webapp/src/main/resources/static/login.html @@ -120,7 +120,7 @@

    Other login methods:

    // Check for WWW-Authenticate header. If found, this means we are not yet authenticated, and // therefore we need to display available authentication options. var authenticate = xhr.getResponseHeader("WWW-Authenticate"); - if (authenticate !== null && authenticate.contains('location=')) { + if (authenticate !== null && authenticate.includes('location=')) { var element = $('div.other-login-methods'); var realms = authenticate.match(/(\w+ (\w+=((".*?")|[^,]*)(, )?)*)/g); if (realms.length === 1){ From 6e22495bffae2bb6a414e094cc6ed5b77886422a Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Thu, 10 Oct 2024 16:02:53 -0500 Subject: [PATCH 118/979] Bump to Spring 6.1.13, Spring Boot 3.3.4 and Spring Security 6.3.3 (cherry picked from commit 46dfd902f113b9ea13ce53b496aff6979024d67e) --- dspace-server-webapp/pom.xml | 14 +++++++++++++- pom.xml | 6 +++--- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/dspace-server-webapp/pom.xml b/dspace-server-webapp/pom.xml index 563019983402..759ad724b9b6 100644 --- a/dspace-server-webapp/pom.xml +++ b/dspace-server-webapp/pom.xml @@ -343,6 +343,18 @@ org.springframework.boot spring-boot-starter-actuator ${spring-boot.version} + + + + io.micrometer + micrometer-observation + + + + io.micrometer + micrometer-commons + +
    @@ -437,7 +449,7 @@ spring-boot-starter-security ${spring-boot.version} - + io.micrometer micrometer-observation diff --git a/pom.xml b/pom.xml index 54e56556f275..e92469dff3c4 100644 --- a/pom.xml +++ b/pom.xml @@ -19,9 +19,9 @@ 17 - 6.1.8 - 3.2.6 - 6.2.4 + 6.1.13 + 3.3.4 + 6.3.3 6.4.8.Final 8.0.1.Final 42.7.3 From 1e47fa61d92c9c87aeabbb4d1c074cc360c721af Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Fri, 11 Oct 2024 14:38:47 -0500 Subject: [PATCH 119/979] Bump to Spring 5.3.39 and Spring Security 5.8.14 --- dspace-server-webapp/pom.xml | 21 +++++++++++++++++++++ pom.xml | 4 ++-- 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/dspace-server-webapp/pom.xml b/dspace-server-webapp/pom.xml index e0ec7ef5ed76..97bdaa241a4f 100644 --- a/dspace-server-webapp/pom.xml +++ b/dspace-server-webapp/pom.xml @@ -362,6 +362,27 @@ org.springframework.boot spring-boot-starter-security ${spring-boot.version} + + + + org.springframework.security + spring-security-core + + + org.springframework.security + spring-security-web + + + + + org.springframework.security + spring-security-core + ${spring-security.version} + + + org.springframework.security + spring-security-web + ${spring-security.version} diff --git a/pom.xml b/pom.xml index b662e333225d..6433b6f227de 100644 --- a/pom.xml +++ b/pom.xml @@ -19,9 +19,9 @@ 11 - 5.3.34 + 5.3.39 2.7.18 - 5.7.11 + 5.8.14 5.6.15.Final 6.2.5.Final 42.7.3 From 70dd8477599abf1e0b4c8933b45c80ddd49e7561 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Fri, 11 Oct 2024 16:41:40 -0500 Subject: [PATCH 120/979] Revert to Spring Security 5.7.12. Spring Security 5.8.x changes behavior of CSRF tokens --- dspace-server-webapp/pom.xml | 9 +++++++++ pom.xml | 2 +- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/dspace-server-webapp/pom.xml b/dspace-server-webapp/pom.xml index 97bdaa241a4f..5a4e1b32123d 100644 --- a/dspace-server-webapp/pom.xml +++ b/dspace-server-webapp/pom.xml @@ -364,6 +364,10 @@ ${spring-boot.version} + + org.springframework.security + spring-security-config + org.springframework.security spring-security-core @@ -374,6 +378,11 @@
    + + org.springframework.security + spring-security-config + ${spring-security.version} + org.springframework.security spring-security-core diff --git a/pom.xml b/pom.xml index 6433b6f227de..02579c03294a 100644 --- a/pom.xml +++ b/pom.xml @@ -21,7 +21,7 @@ 11 5.3.39 2.7.18 - 5.8.14 + 5.7.12 5.6.15.Final 6.2.5.Final 42.7.3 From 4a4a8bcb22796e5b22c9bbb1796e3458b48f1c07 Mon Sep 17 00:00:00 2001 From: Brian Keese Date: Tue, 15 Oct 2024 11:38:54 -0500 Subject: [PATCH 121/979] Fix full-text indexing for files over the character limit The error handler for files over the limit logged the correct message, but never actually added the full text to the index doc. --- .../indexobject/IndexFactoryImpl.java | 31 +++++++++---------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/discovery/indexobject/IndexFactoryImpl.java b/dspace-api/src/main/java/org/dspace/discovery/indexobject/IndexFactoryImpl.java index f1ae137b9163..c9a865ec85b2 100644 --- a/dspace-api/src/main/java/org/dspace/discovery/indexobject/IndexFactoryImpl.java +++ b/dspace-api/src/main/java/org/dspace/discovery/indexobject/IndexFactoryImpl.java @@ -118,20 +118,10 @@ protected void writeDocument(SolrInputDocument doc, FullTextContentStreams strea ParseContext tikaContext = new ParseContext(); // Use Apache Tika to parse the full text stream(s) + boolean extractionSucceeded = false; try (InputStream fullTextStreams = streams.getStream()) { tikaParser.parse(fullTextStreams, tikaHandler, tikaMetadata, tikaContext); - - // Write Tika metadata to "tika_meta_*" fields. - // This metadata is not very useful right now, - // but we'll keep it just in case it becomes more useful. - for (String name : tikaMetadata.names()) { - for (String value : tikaMetadata.getValues(name)) { - doc.addField("tika_meta_" + name, value); - } - } - - // Save (parsed) full text to "fulltext" field - doc.addField("fulltext", tikaHandler.toString()); + extractionSucceeded = true; } catch (SAXException saxe) { // Check if this SAXException is just a notice that this file was longer than the character limit. // Unfortunately there is not a unique, public exception type to catch here. This error is thrown @@ -141,6 +131,7 @@ protected void writeDocument(SolrInputDocument doc, FullTextContentStreams strea // log that we only indexed up to that configured limit log.info("Full text is larger than the configured limit (discovery.solr.fulltext.charLimit)." + " Only the first {} characters were indexed.", charLimit); + extractionSucceeded = true; } else { log.error("Tika parsing error. Could not index full text.", saxe); throw new IOException("Tika parsing error. Could not index full text.", saxe); @@ -148,11 +139,19 @@ protected void writeDocument(SolrInputDocument doc, FullTextContentStreams strea } catch (TikaException | IOException ex) { log.error("Tika parsing error. Could not index full text.", ex); throw new IOException("Tika parsing error. Could not index full text.", ex); - } finally { - // Add document to index - solr.add(doc); } - return; + if (extractionSucceeded) { + // Write Tika metadata to "tika_meta_*" fields. + // This metadata is not very useful right now, + // but we'll keep it just in case it becomes more useful. + for (String name : tikaMetadata.names()) { + for (String value : tikaMetadata.getValues(name)) { + doc.addField("tika_meta_" + name, value); + } + } + // Save (parsed) full text to "fulltext" field + doc.addField("fulltext", tikaHandler.toString()); + } } // Add document to index solr.add(doc); From 31d36e7abf89f188fffba004520e4cbe2c4890a0 Mon Sep 17 00:00:00 2001 From: Miika Nurminen Date: Tue, 27 Aug 2024 08:46:15 +0300 Subject: [PATCH 122/979] Bugfix: BitstreamRestController etag/content-length calculation does not consider cover page Ported Alphonse Bendt's PR #9666 to DSpace 7 branch (squashed 5 commits). This PR fixes a bug where the etag/content-length calculation did not respect the potential existence of a coverpage. The controller now will use the post processed pdf if coverpages are enabled. --- .../app/rest/BitstreamRestController.java | 14 +- .../app/rest/utils/BitstreamResource.java | 119 ++++++++++------ .../app/rest/BitstreamRestControllerIT.java | 129 +++++++++++++++++- 3 files changed, 209 insertions(+), 53 deletions(-) diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/BitstreamRestController.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/BitstreamRestController.java index 22b18724b90b..242b231654c6 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/BitstreamRestController.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/BitstreamRestController.java @@ -135,11 +135,16 @@ public ResponseEntity retrieve(@PathVariable UUID uuid, HttpServletResponse resp long filesize = bit.getSizeBytes(); Boolean citationEnabledForBitstream = citationDocumentService.isCitationEnabledForBitstream(bit, context); + var bitstreamResource = + new org.dspace.app.rest.utils.BitstreamResource(name, uuid, + currentUser != null ? currentUser.getID() : null, + context.getSpecialGroupUuids(), citationEnabledForBitstream); + HttpHeadersInitializer httpHeadersInitializer = new HttpHeadersInitializer() .withBufferSize(BUFFER_SIZE) .withFileName(name) - .withChecksum(bit.getChecksum()) - .withLength(bit.getSizeBytes()) + .withChecksum(bitstreamResource.getChecksum()) + .withLength(bitstreamResource.contentLength()) .withMimetype(mimetype) .with(request) .with(response); @@ -157,11 +162,6 @@ public ResponseEntity retrieve(@PathVariable UUID uuid, HttpServletResponse resp httpHeadersInitializer.withDisposition(HttpHeadersInitializer.CONTENT_DISPOSITION_ATTACHMENT); } - org.dspace.app.rest.utils.BitstreamResource bitstreamResource = - new org.dspace.app.rest.utils.BitstreamResource(name, uuid, - currentUser != null ? currentUser.getID() : null, - context.getSpecialGroupUuids(), citationEnabledForBitstream); - //We have all the data we need, close the connection to the database so that it doesn't stay open during //download/streaming context.complete(); diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/utils/BitstreamResource.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/utils/BitstreamResource.java index 4e5545fabc7f..2f47dacc7ccc 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/utils/BitstreamResource.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/utils/BitstreamResource.java @@ -15,7 +15,8 @@ import java.util.UUID; import org.apache.commons.io.IOUtils; -import org.apache.commons.lang3.tuple.Pair; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.dspace.authorize.AuthorizeException; import org.dspace.content.Bitstream; import org.dspace.content.factory.ContentServiceFactory; @@ -27,6 +28,7 @@ import org.dspace.eperson.service.EPersonService; import org.dspace.utils.DSpace; import org.springframework.core.io.AbstractResource; +import org.springframework.util.DigestUtils; /** * This class acts as a {@link AbstractResource} used by Spring's framework to send the data in a proper and @@ -36,18 +38,23 @@ */ public class BitstreamResource extends AbstractResource { - private String name; - private UUID uuid; - private UUID currentUserUUID; - private boolean shouldGenerateCoverPage; - private byte[] file; - private Set currentSpecialGroups; + private static final Logger LOG = LogManager.getLogger(BitstreamResource.class); - private BitstreamService bitstreamService = ContentServiceFactory.getInstance().getBitstreamService(); - private EPersonService ePersonService = EPersonServiceFactory.getInstance().getEPersonService(); - private CitationDocumentService citationDocumentService = + private final String name; + private final UUID uuid; + private final UUID currentUserUUID; + private final boolean shouldGenerateCoverPage; + private final Set currentSpecialGroups; + + private final BitstreamService bitstreamService = ContentServiceFactory.getInstance().getBitstreamService(); + private final EPersonService ePersonService = EPersonServiceFactory.getInstance().getEPersonService(); + private final CitationDocumentService citationDocumentService = new DSpace().getServiceManager() - .getServicesByType(CitationDocumentService.class).get(0); + .getServicesByType(CitationDocumentService.class).get(0); + + private String documentEtag; + private long documentLength; + private InputStream documentInputStream = null; public BitstreamResource(String name, UUID uuid, UUID currentUserUUID, Set currentSpecialGroups, boolean shouldGenerateCoverPage) { @@ -68,16 +75,14 @@ public BitstreamResource(String name, UUID uuid, UUID currentUserUUID, Set */ private byte[] getCoverpageByteArray(Context context, Bitstream bitstream) throws IOException, SQLException, AuthorizeException { - if (file == null) { - try { - Pair citedDocument = citationDocumentService.makeCitedDocument(context, bitstream); - this.file = citedDocument.getLeft(); - } catch (Exception e) { - // Return the original bitstream without the cover page - this.file = IOUtils.toByteArray(bitstreamService.retrieve(context, bitstream)); - } + try { + var citedDocument = citationDocumentService.makeCitedDocument(context, bitstream); + return citedDocument.getLeft(); + } catch (Exception e) { + LOG.warn("Could not generate cover page. Will fallback to original document", e); + // Return the original bitstream without the cover page + return IOUtils.toByteArray(bitstreamService.retrieve(context, bitstream)); } - return file; } @Override @@ -87,22 +92,9 @@ public String getDescription() { @Override public InputStream getInputStream() throws IOException { - try (Context context = initializeContext()) { - - Bitstream bitstream = bitstreamService.find(context, uuid); - InputStream out; - - if (shouldGenerateCoverPage) { - out = new ByteArrayInputStream(getCoverpageByteArray(context, bitstream)); - } else { - out = bitstreamService.retrieve(context, bitstream); - } + fetchDocument(); - this.file = null; - return out; - } catch (SQLException | AuthorizeException e) { - throw new IOException(e); - } + return this.documentInputStream; } @Override @@ -111,17 +103,63 @@ public String getFilename() { } @Override - public long contentLength() throws IOException { + public long contentLength() { + fetchDocument(); + + return this.documentLength; + } + + public String getChecksum() { + fetchDocument(); + + return this.documentEtag; + } + + private void fetchDocument() { + if (this.documentInputStream != null) { + return; + } + try (Context context = initializeContext()) { Bitstream bitstream = bitstreamService.find(context, uuid); if (shouldGenerateCoverPage) { - return getCoverpageByteArray(context, bitstream).length; + var coverPage = getCoverpageByteArray(context, bitstream); + + this.documentEtag = etag(bitstream); + this.documentLength = coverPage.length; + this.documentInputStream = new ByteArrayInputStream(coverPage); + } else { - return bitstream.getSizeBytes(); + + this.documentEtag = bitstream.getChecksum(); + this.documentLength = bitstream.getSizeBytes(); + this.documentInputStream = bitstreamService.retrieve(context, bitstream); + } - } catch (SQLException | AuthorizeException e) { - throw new IOException(e); + } catch (SQLException | AuthorizeException | IOException e) { + throw new RuntimeException(e); } + + LOG.debug("fetched document {} {} {}", shouldGenerateCoverPage, this.documentEtag, this.documentLength); + } + + private String etag(Bitstream bitstream) { + + /* Ideally we would calculate the md5 checksum based on the document with coverpage. + However it looks like the coverpage generation is not stable (e.g. if invoked twice it will return + different results). This means we cannot use it for etag calculation/comparison! + + Instead we will create the MD5 based off the original checksum plus fixed prefix. This ensures + that checksums will differ when coverpage is on/off. + However the checksum will _not_ change if the coverpage content changes. + */ + + var content = "coverpage:" + bitstream.getChecksum(); + + StringBuilder builder = new StringBuilder(37); + DigestUtils.appendMd5DigestAsHex(content.getBytes(), builder); + + return builder.toString(); } private Context initializeContext() throws SQLException { @@ -131,4 +169,5 @@ private Context initializeContext() throws SQLException { currentSpecialGroups.forEach(context::setSpecialGroup); return context; } + } diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/BitstreamRestControllerIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/BitstreamRestControllerIT.java index 855fedbaa2ab..45f8565fbaf3 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/BitstreamRestControllerIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/BitstreamRestControllerIT.java @@ -23,6 +23,7 @@ import static org.hamcrest.CoreMatchers.not; import static org.hamcrest.CoreMatchers.nullValue; import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.greaterThan; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertThat; @@ -49,8 +50,10 @@ import java.io.InputStream; import java.io.StringWriter; import java.io.Writer; +import java.nio.file.Files; import java.util.UUID; +import org.apache.commons.codec.digest.DigestUtils; import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.CharEncoding; import org.apache.commons.lang3.StringUtils; @@ -90,6 +93,7 @@ import org.mockito.Mockito; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.util.ReflectionTestUtils; +import org.springframework.test.web.servlet.result.MockMvcResultHandlers; /** * Integration test to test the /api/core/bitstreams/[id]/* endpoints @@ -948,12 +952,11 @@ public void retrieveCitationCoverpageOfBitstream() throws Exception { //** THEN ** .andExpect(status().isOk()) - //The Content Length must match the full length + // exact content-length and etag values are verified in s separate test .andExpect(header().string("Content-Length", not(nullValue()))) + .andExpect(header().string("ETag", not(nullValue()))) //The server should indicate we support Range requests .andExpect(header().string("Accept-Ranges", "bytes")) - //The ETag has to be based on the checksum - .andExpect(header().string("ETag", "\"" + bitstream.getChecksum() + "\"")) //We expect the content type to match the bitstream mime type .andExpect(content().contentType("application/pdf;charset=UTF-8")) //THe bytes of the content must match the original content @@ -962,20 +965,22 @@ public void retrieveCitationCoverpageOfBitstream() throws Exception { // The citation cover page contains the item title. // We will now verify that the pdf text contains this title. String pdfText = extractPDFText(content); - System.out.println(pdfText); assertTrue(StringUtils.contains(pdfText,"Public item citation cover page test 1")); // The dspace-api/src/test/data/dspaceFolder/assetstore/ConstitutionofIreland.pdf file contains 64 pages, // manually counted + 1 citation cover page assertEquals(65,getNumberOfPdfPages(content)); + var etagHeader = getClient().perform(get("/api/core/bitstreams/" + bitstream.getID() + "/content")) + .andReturn().getResponse().getHeader("ETag"); + //A If-None-Match HEAD request on the ETag must tell is the bitstream is not modified getClient().perform(head("/api/core/bitstreams/" + bitstream.getID() + "/content") - .header("If-None-Match", bitstream.getChecksum())) + .header("If-None-Match", etagHeader)) .andExpect(status().isNotModified()); //The download and head request should also be logged as a statistics record - checkNumberOfStatsRecords(bitstream, 2); + checkNumberOfStatsRecords(bitstream, 3); } private String extractPDFText(byte[] content) throws IOException { @@ -1383,4 +1388,116 @@ private void verifyBitstreamDownload(Bitstream file, String contentType, boolean header.contains("attachment")); } } + + @Test + public void contentLengthAndEtagUsesOriginalBitstream() throws Exception { + givenPdf(false, originalPdf -> { + var originalMd5 = md5Checksum(originalPdf); + long originalLength = Files.size(originalPdf.toPath()); + + assertThat(originalLength, greaterThan(0L)); + + getClient().perform(get("/api/core/bitstreams/" + bitstream.getID() + "/content")) + .andDo(MockMvcResultHandlers.print()) + .andExpect(status().isOk()) + .andExpect(header().longValue("Content-Length", originalLength)) + .andExpect(header().string("ETag", "\"" + originalMd5 + "\"")); + }); + } + + private static String md5Checksum(File file) throws IOException { + final String md5; + try (InputStream is = new FileInputStream(file)) { + md5 = DigestUtils.md5Hex(is); + } + return md5; + } + + @Test + public void withCoverPageContentLengthAndEtagChanges() throws Exception { + givenPdf(true, originalPdf -> { + var originalMd5 = md5Checksum(originalPdf); + long originalLength = Files.size(originalPdf.toPath()); + + getClient().perform(get("/api/core/bitstreams/" + bitstream.getID() + "/content")) + .andDo(MockMvcResultHandlers.print()) + .andExpect(status().isOk()) + .andExpect(header().string("Content-Length", not(Long.toString(originalLength)))) + .andExpect(header().string("ETag", not("\"" + originalMd5 + "\""))); + }); + } + + @Test + public void etagAndContentLengthIsStable() { + givenPdf(false, ignored -> { + var etag1 = getClient().perform(get("/api/core/bitstreams/" + bitstream.getID() + "/content")) + .andReturn().getResponse().getHeader("Etag"); + + var etag2 = getClient().perform(get("/api/core/bitstreams/" + bitstream.getID() + "/content")) + .andReturn().getResponse().getHeader("Etag"); + + assertThat(etag1, equalTo(etag2)); + }); + } + + @Test + public void withCoverPageEtagAndContentLengthIsStable() { + givenPdf(true, ignored -> { + var etag1 = getClient().perform(get("/api/core/bitstreams/" + bitstream.getID() + "/content")) + .andReturn().getResponse().getHeader("Etag"); + + var etag2 = getClient().perform(get("/api/core/bitstreams/" + bitstream.getID() + "/content")) + .andReturn().getResponse().getHeader("Etag"); + + assertThat(etag1, equalTo(etag2)); + }); + } + + @FunctionalInterface + interface ThrowingConsumer { + void accept(T t) throws Exception; + } + + private void givenPdf(boolean coverPageEnabled, ThrowingConsumer block) { + configurationService.setProperty("citation-page.enable_globally", coverPageEnabled); + + try { + citationDocumentService.afterPropertiesSet(); + context.turnOffAuthorisationSystem(); + + //** GIVEN ** + //1. A community-collection structure with one parent community and one collections. + parentCommunity = CommunityBuilder.createCommunity(context) + .withName("Parent Community") + .build(); + + Collection col1 = + CollectionBuilder.createCollection(context, parentCommunity).withName("Collection 1").build(); + + //2. A public item with a bitstream + File originalPdf = new File(testProps.getProperty("test.bitstream")); + + try (InputStream is = new FileInputStream(originalPdf)) { + Item publicItem1 = ItemBuilder.createItem(context, col1) + .withTitle("Public item citation cover page test 1") + .withIssueDate("2017-10-17") + .withAuthor("Smith, Donald").withAuthor("Doe, John") + .build(); + + bitstream = BitstreamBuilder + .createBitstream(context, publicItem1, is) + .withName("Test bitstream") + .withDescription("This is a bitstream to test the citation cover page.") + .withMimeType("application/pdf") + .build(); + } + context.restoreAuthSystemState(); + + block.accept(originalPdf); + + } catch (Exception e) { + throw new RuntimeException(e); + } + } + } From 6018f926d7a599b081664bf862531ededb645839 Mon Sep 17 00:00:00 2001 From: Kim Shepherd Date: Tue, 15 Oct 2024 23:23:24 +0200 Subject: [PATCH 123/979] Add Eclipse JDT .factorypath to .gitignore (cherry picked from commit 9ce645e08b50cd752e48a640e340b55466f019be) --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 2fcb46b9932c..529351edc5c2 100644 --- a/.gitignore +++ b/.gitignore @@ -10,6 +10,7 @@ tags .project .classpath .checkstyle +.factorypath ## Ignore project files created by IntelliJ IDEA *.iml From f6ec314ef762ce83037ad94940cfc352246e300d Mon Sep 17 00:00:00 2001 From: Kim Shepherd Date: Tue, 15 Oct 2024 23:23:24 +0200 Subject: [PATCH 124/979] Add Eclipse JDT .factorypath to .gitignore (cherry picked from commit 9ce645e08b50cd752e48a640e340b55466f019be) --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 2fcb46b9932c..529351edc5c2 100644 --- a/.gitignore +++ b/.gitignore @@ -10,6 +10,7 @@ tags .project .classpath .checkstyle +.factorypath ## Ignore project files created by IntelliJ IDEA *.iml From 42be64cf941083c7f517e81c70651f78fcedd788 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 18 Oct 2024 20:21:19 +0000 Subject: [PATCH 125/979] Bump the maven group with 3 updates Bumps the maven group with 3 updates: [org.springframework:spring-context](https://github.com/spring-projects/spring-framework), org.eclipse.jetty:jetty-server and org.eclipse.jetty:jetty-http. Updates `org.springframework:spring-context` from 6.1.13 to 6.1.14 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.1.13...v6.1.14) Updates `org.eclipse.jetty:jetty-server` from 9.4.54.v20240208 to 9.4.55.v20240627 Updates `org.eclipse.jetty:jetty-http` from 9.4.54.v20240208 to 9.4.55.v20240627 --- updated-dependencies: - dependency-name: org.springframework:spring-context dependency-type: direct:production dependency-group: maven - dependency-name: org.eclipse.jetty:jetty-server dependency-type: direct:production dependency-group: maven - dependency-name: org.eclipse.jetty:jetty-http dependency-type: direct:production dependency-group: maven ... Signed-off-by: dependabot[bot] (cherry picked from commit e96dbfefeb6924c4b17c66535215878576190fb5) --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index e92469dff3c4..3eec65d4a9fd 100644 --- a/pom.xml +++ b/pom.xml @@ -19,7 +19,7 @@ 17 - 6.1.13 + 6.1.14 3.3.4 6.3.3 6.4.8.Final @@ -38,7 +38,7 @@ 4.0.5 1.1.1 - 9.4.54.v20240208 + 9.4.55.v20240627 2.23.1 2.0.31 1.19.0 From e6d4ea07f6ab49b7baa63fe1dfd45ea1ee09d126 Mon Sep 17 00:00:00 2001 From: Sascha Szott Date: Fri, 1 Nov 2024 14:12:25 +0100 Subject: [PATCH 126/979] fix typo in endpoint path (cherry picked from commit 6a7b0fc06b48ad145cc654d38675dcdbe0fafa70) --- .../app/rest/ItemOwningCollectionUpdateRestController.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/ItemOwningCollectionUpdateRestController.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/ItemOwningCollectionUpdateRestController.java index eec5b15825ac..db238e1a5c83 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/ItemOwningCollectionUpdateRestController.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/ItemOwningCollectionUpdateRestController.java @@ -43,7 +43,7 @@ import org.springframework.web.bind.annotation.RestController; /** - * This controller will handle all the incoming calls on the api/code/items/{uuid}/owningCollection endpoint + * This controller will handle all the incoming calls on the api/core/items/{uuid}/owningCollection endpoint * where the uuid corresponds to the item of which you want to edit the owning collection. */ @RestController From 10a95ae92aa78fbd1998d439015eb91a7bc9b75c Mon Sep 17 00:00:00 2001 From: Andreas Awouters Date: Wed, 23 Oct 2024 08:53:01 +0200 Subject: [PATCH 127/979] 119602: Add accessibility settings metadata field --- dspace/config/registries/dspace-types.xml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/dspace/config/registries/dspace-types.xml b/dspace/config/registries/dspace-types.xml index fcdbc9b2af9b..ebe021dad267 100644 --- a/dspace/config/registries/dspace-types.xml +++ b/dspace/config/registries/dspace-types.xml @@ -93,4 +93,12 @@ Stores the timestamp related to the user authentication on ORCID + + dspace + accessibility + settings + Metadata field storing the user-configured accessibility settings values for the EPerson. + + + From 68266cd3c19d27dbed4f181bc12017d46fc6f64c Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Wed, 6 Nov 2024 13:36:17 -0600 Subject: [PATCH 128/979] Move logging of test methods to Abstract*Test classes in dspace-api. That way they work for **both** dspace-server-webapp and dspace-api tests. (cherry picked from commit bd20c9262b1992ff15033701fa01738329864b1b) --- .../dspace/AbstractDSpaceIntegrationTest.java | 24 ++++++++++ .../java/org/dspace/AbstractDSpaceTest.java | 23 ++++++++++ .../AbstractControllerIntegrationTest.java | 4 -- .../AbstractWebClientIntegrationTest.java | 4 -- .../test/LoggingTestExecutionListener.java | 44 ------------------- 5 files changed, 47 insertions(+), 52 deletions(-) delete mode 100644 dspace-server-webapp/src/test/java/org/dspace/app/rest/test/LoggingTestExecutionListener.java diff --git a/dspace-api/src/test/java/org/dspace/AbstractDSpaceIntegrationTest.java b/dspace-api/src/test/java/org/dspace/AbstractDSpaceIntegrationTest.java index 5a5ce8bf6d4c..791fdbc66abc 100644 --- a/dspace-api/src/test/java/org/dspace/AbstractDSpaceIntegrationTest.java +++ b/dspace-api/src/test/java/org/dspace/AbstractDSpaceIntegrationTest.java @@ -21,8 +21,12 @@ import org.dspace.discovery.SearchUtils; import org.dspace.servicemanager.DSpaceKernelImpl; import org.dspace.servicemanager.DSpaceKernelInit; +import org.junit.After; import org.junit.AfterClass; +import org.junit.Before; import org.junit.BeforeClass; +import org.junit.Rule; +import org.junit.rules.TestName; /** * Abstract Test class copied from DSpace API @@ -46,6 +50,12 @@ public class AbstractDSpaceIntegrationTest { */ protected static DSpaceKernelImpl kernelImpl; + /** + * Obtain the TestName from JUnit, so that we can print it out in the test logs (see below) + */ + @Rule + public TestName testName = new TestName(); + /** * Default constructor */ @@ -90,6 +100,20 @@ public static void initTestEnvironment() { } } + @Before + public void printTestMethodBefore() { + // Log the test method being executed. Put lines around it to make it stand out. + log.info("---"); + log.info("Starting execution of test method: {}()", testName.getMethodName()); + log.info("---"); + } + + @After + public void printTestMethodAfter() { + // Log the test method just completed. + log.info("Finished execution of test method: {}()", testName.getMethodName()); + } + /** * This method will be run after all tests finish as per @AfterClass. It * will clean resources initialized by the @BeforeClass methods. diff --git a/dspace-api/src/test/java/org/dspace/AbstractDSpaceTest.java b/dspace-api/src/test/java/org/dspace/AbstractDSpaceTest.java index 36477556d3de..136af83f076f 100644 --- a/dspace-api/src/test/java/org/dspace/AbstractDSpaceTest.java +++ b/dspace-api/src/test/java/org/dspace/AbstractDSpaceTest.java @@ -18,9 +18,13 @@ import org.apache.logging.log4j.Logger; import org.dspace.servicemanager.DSpaceKernelImpl; import org.dspace.servicemanager.DSpaceKernelInit; +import org.junit.After; import org.junit.AfterClass; +import org.junit.Before; import org.junit.BeforeClass; import org.junit.Ignore; +import org.junit.Rule; +import org.junit.rules.TestName; import org.junit.runner.RunWith; import org.mockito.junit.MockitoJUnitRunner; @@ -62,6 +66,12 @@ protected AbstractDSpaceTest() { } */ protected static DSpaceKernelImpl kernelImpl; + /** + * Obtain the TestName from JUnit, so that we can print it out in the test logs (see below) + */ + @Rule + public TestName testName = new TestName(); + /** * This method will be run before the first test as per @BeforeClass. It will * initialize shared resources required for all tests of this class. @@ -94,6 +104,19 @@ public static void initKernel() { } } + @Before + public void printTestMethodBefore() { + // Log the test method being executed. Put lines around it to make it stand out. + log.info("---"); + log.info("Starting execution of test method: {}()", testName.getMethodName()); + log.info("---"); + } + + @After + public void printTestMethodAfter() { + // Log the test method just completed. + log.info("Finished execution of test method: {}()", testName.getMethodName()); + } /** * This method will be run after all tests finish as per @AfterClass. It diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/test/AbstractControllerIntegrationTest.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/test/AbstractControllerIntegrationTest.java index 00339ba2e482..28c5560edf31 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/test/AbstractControllerIntegrationTest.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/test/AbstractControllerIntegrationTest.java @@ -39,7 +39,6 @@ import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; import org.springframework.mock.web.MockHttpServletResponse; import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.context.TestExecutionListeners; import org.springframework.test.context.TestPropertySource; import org.springframework.test.context.junit4.SpringRunner; import org.springframework.test.context.web.WebAppConfiguration; @@ -75,9 +74,6 @@ @WebAppConfiguration // Load our src/test/resources/application-test.properties to override some settings in default application.properties @TestPropertySource(locations = "classpath:application-test.properties") -// Enable our custom Logging listener to log when each test method starts/stops -@TestExecutionListeners(listeners = {LoggingTestExecutionListener.class}, - mergeMode = TestExecutionListeners.MergeMode.MERGE_WITH_DEFAULTS) public class AbstractControllerIntegrationTest extends AbstractIntegrationTestWithDatabase { protected static final String AUTHORIZATION_HEADER = "Authorization"; diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/test/AbstractWebClientIntegrationTest.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/test/AbstractWebClientIntegrationTest.java index 75b0143e3e65..df6afcb16e32 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/test/AbstractWebClientIntegrationTest.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/test/AbstractWebClientIntegrationTest.java @@ -22,7 +22,6 @@ import org.springframework.http.RequestEntity; import org.springframework.http.ResponseEntity; import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.context.TestExecutionListeners; import org.springframework.test.context.TestPropertySource; import org.springframework.test.context.junit4.SpringRunner; @@ -52,9 +51,6 @@ @ContextConfiguration(initializers = { DSpaceKernelInitializer.class, DSpaceConfigurationInitializer.class }) // Load our src/test/resources/application-test.properties to override some settings in default application.properties @TestPropertySource(locations = "classpath:application-test.properties") -// Enable our custom Logging listener to log when each test method starts/stops -@TestExecutionListeners(listeners = {LoggingTestExecutionListener.class}, - mergeMode = TestExecutionListeners.MergeMode.MERGE_WITH_DEFAULTS) public class AbstractWebClientIntegrationTest extends AbstractIntegrationTestWithDatabase { // (Random) port chosen for test web server @LocalServerPort diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/test/LoggingTestExecutionListener.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/test/LoggingTestExecutionListener.java deleted file mode 100644 index 2a04bab2041c..000000000000 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/test/LoggingTestExecutionListener.java +++ /dev/null @@ -1,44 +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.app.rest.test; - -import org.apache.logging.log4j.Logger; -import org.springframework.test.context.TestContext; -import org.springframework.test.context.support.AbstractTestExecutionListener; - -/** - * Custom DSpace TestExecutionListener which logs messages whenever a specific Test Case (i.e. test method) has - * started or ended execution. This makes Test environment logs easier to read/understand as you know which method has - * caused errors, etc. - */ -public class LoggingTestExecutionListener extends AbstractTestExecutionListener { - - private static final Logger log = org.apache.logging.log4j.LogManager.getLogger(LoggingTestExecutionListener.class); - - /** - * Before each test method is run - * @param testContext - */ - @Override - public void beforeTestMethod(TestContext testContext) { - // Log the test method being executed. Put lines around it to make it stand out. - log.info("---"); - log.info("Starting execution of test method: {}()", testContext.getTestMethod().getName()); - log.info("---"); - } - - /** - * After each test method is run - * @param testContext - */ - @Override - public void afterTestMethod(TestContext testContext) { - // Log the test method just completed. - log.info("Finished execution of test method: {}()", testContext.getTestMethod().getName()); - } -} From 2bf5baf69779cfde398f368cb756a0576d82794b Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Wed, 6 Nov 2024 13:36:17 -0600 Subject: [PATCH 129/979] Move logging of test methods to Abstract*Test classes in dspace-api. That way they work for **both** dspace-server-webapp and dspace-api tests. (cherry picked from commit bd20c9262b1992ff15033701fa01738329864b1b) --- .../dspace/AbstractDSpaceIntegrationTest.java | 24 ++++++++++ .../java/org/dspace/AbstractDSpaceTest.java | 23 ++++++++++ .../AbstractControllerIntegrationTest.java | 4 -- .../AbstractWebClientIntegrationTest.java | 4 -- .../test/LoggingTestExecutionListener.java | 44 ------------------- 5 files changed, 47 insertions(+), 52 deletions(-) delete mode 100644 dspace-server-webapp/src/test/java/org/dspace/app/rest/test/LoggingTestExecutionListener.java diff --git a/dspace-api/src/test/java/org/dspace/AbstractDSpaceIntegrationTest.java b/dspace-api/src/test/java/org/dspace/AbstractDSpaceIntegrationTest.java index 5a5ce8bf6d4c..791fdbc66abc 100644 --- a/dspace-api/src/test/java/org/dspace/AbstractDSpaceIntegrationTest.java +++ b/dspace-api/src/test/java/org/dspace/AbstractDSpaceIntegrationTest.java @@ -21,8 +21,12 @@ import org.dspace.discovery.SearchUtils; import org.dspace.servicemanager.DSpaceKernelImpl; import org.dspace.servicemanager.DSpaceKernelInit; +import org.junit.After; import org.junit.AfterClass; +import org.junit.Before; import org.junit.BeforeClass; +import org.junit.Rule; +import org.junit.rules.TestName; /** * Abstract Test class copied from DSpace API @@ -46,6 +50,12 @@ public class AbstractDSpaceIntegrationTest { */ protected static DSpaceKernelImpl kernelImpl; + /** + * Obtain the TestName from JUnit, so that we can print it out in the test logs (see below) + */ + @Rule + public TestName testName = new TestName(); + /** * Default constructor */ @@ -90,6 +100,20 @@ public static void initTestEnvironment() { } } + @Before + public void printTestMethodBefore() { + // Log the test method being executed. Put lines around it to make it stand out. + log.info("---"); + log.info("Starting execution of test method: {}()", testName.getMethodName()); + log.info("---"); + } + + @After + public void printTestMethodAfter() { + // Log the test method just completed. + log.info("Finished execution of test method: {}()", testName.getMethodName()); + } + /** * This method will be run after all tests finish as per @AfterClass. It * will clean resources initialized by the @BeforeClass methods. diff --git a/dspace-api/src/test/java/org/dspace/AbstractDSpaceTest.java b/dspace-api/src/test/java/org/dspace/AbstractDSpaceTest.java index 36477556d3de..136af83f076f 100644 --- a/dspace-api/src/test/java/org/dspace/AbstractDSpaceTest.java +++ b/dspace-api/src/test/java/org/dspace/AbstractDSpaceTest.java @@ -18,9 +18,13 @@ import org.apache.logging.log4j.Logger; import org.dspace.servicemanager.DSpaceKernelImpl; import org.dspace.servicemanager.DSpaceKernelInit; +import org.junit.After; import org.junit.AfterClass; +import org.junit.Before; import org.junit.BeforeClass; import org.junit.Ignore; +import org.junit.Rule; +import org.junit.rules.TestName; import org.junit.runner.RunWith; import org.mockito.junit.MockitoJUnitRunner; @@ -62,6 +66,12 @@ protected AbstractDSpaceTest() { } */ protected static DSpaceKernelImpl kernelImpl; + /** + * Obtain the TestName from JUnit, so that we can print it out in the test logs (see below) + */ + @Rule + public TestName testName = new TestName(); + /** * This method will be run before the first test as per @BeforeClass. It will * initialize shared resources required for all tests of this class. @@ -94,6 +104,19 @@ public static void initKernel() { } } + @Before + public void printTestMethodBefore() { + // Log the test method being executed. Put lines around it to make it stand out. + log.info("---"); + log.info("Starting execution of test method: {}()", testName.getMethodName()); + log.info("---"); + } + + @After + public void printTestMethodAfter() { + // Log the test method just completed. + log.info("Finished execution of test method: {}()", testName.getMethodName()); + } /** * This method will be run after all tests finish as per @AfterClass. It diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/test/AbstractControllerIntegrationTest.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/test/AbstractControllerIntegrationTest.java index c4ab14250bca..26dac5facdec 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/test/AbstractControllerIntegrationTest.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/test/AbstractControllerIntegrationTest.java @@ -42,7 +42,6 @@ import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; import org.springframework.mock.web.MockHttpServletResponse; import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.context.TestExecutionListeners; import org.springframework.test.context.TestPropertySource; import org.springframework.test.context.junit4.SpringRunner; import org.springframework.test.context.web.WebAppConfiguration; @@ -80,9 +79,6 @@ @WebAppConfiguration // Load our src/test/resources/application-test.properties to override some settings in default application.properties @TestPropertySource(locations = "classpath:application-test.properties") -// Enable our custom Logging listener to log when each test method starts/stops -@TestExecutionListeners(listeners = {LoggingTestExecutionListener.class}, - mergeMode = TestExecutionListeners.MergeMode.MERGE_WITH_DEFAULTS) public class AbstractControllerIntegrationTest extends AbstractIntegrationTestWithDatabase { private static final Logger log = org.apache.logging.log4j.LogManager.getLogger(); diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/test/AbstractWebClientIntegrationTest.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/test/AbstractWebClientIntegrationTest.java index 7745fba96c3f..e4ef1c741f72 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/test/AbstractWebClientIntegrationTest.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/test/AbstractWebClientIntegrationTest.java @@ -22,7 +22,6 @@ import org.springframework.http.RequestEntity; import org.springframework.http.ResponseEntity; import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.context.TestExecutionListeners; import org.springframework.test.context.TestPropertySource; import org.springframework.test.context.junit4.SpringRunner; @@ -52,9 +51,6 @@ @ContextConfiguration(initializers = { DSpaceKernelInitializer.class, DSpaceConfigurationInitializer.class }) // Load our src/test/resources/application-test.properties to override some settings in default application.properties @TestPropertySource(locations = "classpath:application-test.properties") -// Enable our custom Logging listener to log when each test method starts/stops -@TestExecutionListeners(listeners = {LoggingTestExecutionListener.class}, - mergeMode = TestExecutionListeners.MergeMode.MERGE_WITH_DEFAULTS) public class AbstractWebClientIntegrationTest extends AbstractIntegrationTestWithDatabase { // (Random) port chosen for test web server @LocalServerPort diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/test/LoggingTestExecutionListener.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/test/LoggingTestExecutionListener.java deleted file mode 100644 index 2a04bab2041c..000000000000 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/test/LoggingTestExecutionListener.java +++ /dev/null @@ -1,44 +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.app.rest.test; - -import org.apache.logging.log4j.Logger; -import org.springframework.test.context.TestContext; -import org.springframework.test.context.support.AbstractTestExecutionListener; - -/** - * Custom DSpace TestExecutionListener which logs messages whenever a specific Test Case (i.e. test method) has - * started or ended execution. This makes Test environment logs easier to read/understand as you know which method has - * caused errors, etc. - */ -public class LoggingTestExecutionListener extends AbstractTestExecutionListener { - - private static final Logger log = org.apache.logging.log4j.LogManager.getLogger(LoggingTestExecutionListener.class); - - /** - * Before each test method is run - * @param testContext - */ - @Override - public void beforeTestMethod(TestContext testContext) { - // Log the test method being executed. Put lines around it to make it stand out. - log.info("---"); - log.info("Starting execution of test method: {}()", testContext.getTestMethod().getName()); - log.info("---"); - } - - /** - * After each test method is run - * @param testContext - */ - @Override - public void afterTestMethod(TestContext testContext) { - // Log the test method just completed. - log.info("Finished execution of test method: {}()", testContext.getTestMethod().getName()); - } -} From 831db33018cdcfa132dc0b1da0f427aca0d03585 Mon Sep 17 00:00:00 2001 From: Jens Vannerum Date: Mon, 14 Oct 2024 17:03:15 +0200 Subject: [PATCH 130/979] 118774: status of doi should be set to TO_BE_DELETED when related item is removed permanently (cherry picked from commit 352f4c21523237cefa5bf67c99f7d7b26a51d72b) --- .../src/main/java/org/dspace/content/ItemServiceImpl.java | 2 ++ 1 file changed, 2 insertions(+) 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 b2cc3e939d87..ad4156328aeb 100644 --- a/dspace-api/src/main/java/org/dspace/content/ItemServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/content/ItemServiceImpl.java @@ -66,6 +66,7 @@ import org.dspace.harvest.HarvestedItem; import org.dspace.harvest.service.HarvestedItemService; import org.dspace.identifier.DOI; +import org.dspace.identifier.DOIIdentifierProvider; import org.dspace.identifier.IdentifierException; import org.dspace.identifier.service.DOIService; import org.dspace.identifier.service.IdentifierService; @@ -848,6 +849,7 @@ protected void rawDelete(Context context, Item item) throws AuthorizeException, DOI doi = doiService.findDOIByDSpaceObject(context, item); if (doi != null) { doi.setDSpaceObject(null); + doi.setStatus(DOIIdentifierProvider.TO_BE_DELETED); } // remove version attached to the item From 578726c25174d97710b4d5e12d2ed14c874735d3 Mon Sep 17 00:00:00 2001 From: Jens Vannerum Date: Mon, 14 Oct 2024 17:03:15 +0200 Subject: [PATCH 131/979] 118774: status of doi should be set to TO_BE_DELETED when related item is removed permanently (cherry picked from commit 352f4c21523237cefa5bf67c99f7d7b26a51d72b) --- .../src/main/java/org/dspace/content/ItemServiceImpl.java | 2 ++ 1 file changed, 2 insertions(+) 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 cceb954ebe2f..cb6cb15054cb 100644 --- a/dspace-api/src/main/java/org/dspace/content/ItemServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/content/ItemServiceImpl.java @@ -67,6 +67,7 @@ import org.dspace.harvest.HarvestedItem; import org.dspace.harvest.service.HarvestedItemService; import org.dspace.identifier.DOI; +import org.dspace.identifier.DOIIdentifierProvider; import org.dspace.identifier.IdentifierException; import org.dspace.identifier.service.DOIService; import org.dspace.identifier.service.IdentifierService; @@ -851,6 +852,7 @@ protected void rawDelete(Context context, Item item) throws AuthorizeException, DOI doi = doiService.findDOIByDSpaceObject(context, item); if (doi != null) { doi.setDSpaceObject(null); + doi.setStatus(DOIIdentifierProvider.TO_BE_DELETED); } // remove version attached to the item From fde825265db6b44ca0bd852ec6df56ca3f3eacfe Mon Sep 17 00:00:00 2001 From: Andrew Date: Fri, 27 Sep 2024 12:52:03 +0200 Subject: [PATCH 132/979] fix: performance of claiming workflow task (cherry picked from commit 27dd5a2ec58970256964f15f0879834c436aa542) --- .../storedcomponents/PoolTaskServiceImpl.java | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/xmlworkflow/storedcomponents/PoolTaskServiceImpl.java b/dspace-api/src/main/java/org/dspace/xmlworkflow/storedcomponents/PoolTaskServiceImpl.java index fb673725e181..d3c8f6334d8f 100644 --- a/dspace-api/src/main/java/org/dspace/xmlworkflow/storedcomponents/PoolTaskServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/xmlworkflow/storedcomponents/PoolTaskServiceImpl.java @@ -13,6 +13,7 @@ import java.util.Collections; import java.util.Iterator; import java.util.List; +import java.util.Optional; import java.util.Set; import org.apache.commons.collections4.CollectionUtils; @@ -100,12 +101,17 @@ public PoolTask findByWorkflowIdAndEPerson(Context context, XmlWorkflowItem work //If the user does not have a claimedtask yet, see whether one of the groups of the user has pooltasks //for this workflow item Set groups = groupService.allMemberGroupsSet(context, ePerson); - for (Group group : groups) { - poolTask = poolTaskDAO.findByWorkflowItemAndGroup(context, group, workflowItem); - if (poolTask != null) { - return poolTask; - } + List generalTasks = poolTaskDAO.findByWorkflowItem(context, workflowItem); + Optional firstClaimedTask = groups.stream() + .flatMap(group -> generalTasks.stream() + .filter(f -> f.getGroup().getID().equals(group.getID())) + .findFirst() + .stream()) + .findFirst(); + + if (firstClaimedTask.isPresent()) { + return firstClaimedTask.get(); } } } From b8dc7683814de4fe72b2e26218c99b8ea989a333 Mon Sep 17 00:00:00 2001 From: Andrew Date: Fri, 27 Sep 2024 12:52:03 +0200 Subject: [PATCH 133/979] fix: performance of claiming workflow task (cherry picked from commit 27dd5a2ec58970256964f15f0879834c436aa542) --- .../storedcomponents/PoolTaskServiceImpl.java | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/xmlworkflow/storedcomponents/PoolTaskServiceImpl.java b/dspace-api/src/main/java/org/dspace/xmlworkflow/storedcomponents/PoolTaskServiceImpl.java index fb673725e181..d3c8f6334d8f 100644 --- a/dspace-api/src/main/java/org/dspace/xmlworkflow/storedcomponents/PoolTaskServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/xmlworkflow/storedcomponents/PoolTaskServiceImpl.java @@ -13,6 +13,7 @@ import java.util.Collections; import java.util.Iterator; import java.util.List; +import java.util.Optional; import java.util.Set; import org.apache.commons.collections4.CollectionUtils; @@ -100,12 +101,17 @@ public PoolTask findByWorkflowIdAndEPerson(Context context, XmlWorkflowItem work //If the user does not have a claimedtask yet, see whether one of the groups of the user has pooltasks //for this workflow item Set groups = groupService.allMemberGroupsSet(context, ePerson); - for (Group group : groups) { - poolTask = poolTaskDAO.findByWorkflowItemAndGroup(context, group, workflowItem); - if (poolTask != null) { - return poolTask; - } + List generalTasks = poolTaskDAO.findByWorkflowItem(context, workflowItem); + Optional firstClaimedTask = groups.stream() + .flatMap(group -> generalTasks.stream() + .filter(f -> f.getGroup().getID().equals(group.getID())) + .findFirst() + .stream()) + .findFirst(); + + if (firstClaimedTask.isPresent()) { + return firstClaimedTask.get(); } } } From 6283c6e072edea6d867551e94d35da9aec1c123c Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Mon, 11 Nov 2024 10:26:29 -0600 Subject: [PATCH 134/979] Add a job to test Docker deployment with the built images (cherry picked from commit f1d12ef4560bdf7dffea29c5545d8d51d596cd17) --- .github/workflows/docker.yml | 43 +++++++++++++++++++++++++++++++++++- 1 file changed, 42 insertions(+), 1 deletion(-) diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index a9ff8760e763..12d49689b319 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -147,4 +147,45 @@ jobs: tags_flavor: suffix=-loadsql secrets: DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }} - DOCKER_ACCESS_TOKEN: ${{ secrets.DOCKER_ACCESS_TOKEN }} \ No newline at end of file + DOCKER_ACCESS_TOKEN: ${{ secrets.DOCKER_ACCESS_TOKEN }} + + ######################################################################## + # Test Deployment via Docker to ensure images are working properly + ######################################################################## + docker-deploy: + # Ensure this job never runs on forked repos. It's only executed for 'dspace/dspace' + if: github.repository == 'dspace/dspace' + runs-on: ubuntu-latest + # Must run after all major images are built + needs: [dspace-test, dspace-cli, dspace-postgres-pgcrypto, dspace-solr] + steps: + - name: Checkout codebase + uses: actions/checkout@v4 + # Start backend using our compose script in the codebase. + - name: Start backend in Docker + env: + # Override defaults dspace.server.url because backend starts at http://127.0.0.1:8080 + dspace__P__server__P__url: http://127.0.0.1:8080/server + run: | + docker compose -f docker-compose.yml up -d + sleep 10 + docker container ls + # Create a test admin account. Load test data from a simple set of AIPs as defined in cli.ingest.yml + - name: Load test data into Backend + run: | + docker compose -f docker-compose-cli.yml run --rm dspace-cli create-administrator -e test@test.edu -f admin -l user -p admin -c en + docker compose -f docker-compose-cli.yml -f dspace/src/main/docker-compose/cli.ingest.yml run --rm dspace-cli + # Verify backend started successfully. + # 1. Make sure root endpoint is responding (check for dspace.name defined in docker-compose.yml) + # 2. Also check /collections endpoint to ensure the test data loaded properly (check for a collection name in AIPs) + - name: Verify backend is responding properly + run: | + result=$(wget -O- -q http://127.0.0.1:8080/server/api) + echo "$result" + echo "$result" | grep -oE "\"DSpace Started with Docker Compose\"," + result=$(wget -O- -q http://127.0.0.1:8080/server/api/core/collections) + echo "$result" + echo "$result" | grep -oE "\"Dog in Yard\"," + - name: Shutdown Docker containers + run: | + docker compose -f docker-compose.yml down From 0c3aee117cef30e8ebe9664eb634e8fe27b8c7f3 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Mon, 11 Nov 2024 14:10:44 -0600 Subject: [PATCH 135/979] Add a check that the Handle Server can be started & works properly (cherry picked from commit c96b5316d5b4af9e479c814fd0048dac002cbffa) --- .github/workflows/docker.yml | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 12d49689b319..660c9eb817a6 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -186,6 +186,25 @@ jobs: result=$(wget -O- -q http://127.0.0.1:8080/server/api/core/collections) echo "$result" echo "$result" | grep -oE "\"Dog in Yard\"," + # Verify Handle Server can be stared and is working properly + # 1. First generate the "[dspace]/handle-server" folder with the sitebndl.zip + # 2. Start the Handle Server (and wait 20 seconds to let it start up) + # 3. Verify logs do NOT include "Exception" in the text (as that means an error occurred) + # 4. Check that Handle Proxy HTML page is responding on default port (8000) + - name: Verify Handle Server is working properly + run: | + docker exec -i dspace /dspace/bin/make-handle-config + docker exec -i dspace /dspace/bin/start-handle-server + sleep 20 + echo "Checking for errors in handle-server.log..." + result=$(docker exec -i dspace cat /dspace/log/handle-server.log) + echo "$result" + echo "$result" | grep -vqz "Exception" + echo "Checking to see if Handle Proxy webpage is available..." + result=$(wget -O- -q http://127.0.0.1:8000/) + echo "$result" + echo "$result" | grep -oE "Handle Proxy" + # Shutdown our containers - name: Shutdown Docker containers run: | docker compose -f docker-compose.yml down From 77b76b32f1b981fee485fdb517144d35f06243bc Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Tue, 12 Nov 2024 13:50:57 -0600 Subject: [PATCH 136/979] Ensure Docker images built from PRs are stored as artifacts. This allows us to use those new images when testing deployment (in docker-deploy) (cherry picked from commit eb766c7cdf1d92898cc8c2a1ba067ba8dac2abad) --- .github/workflows/docker.yml | 34 +++++++++--- .github/workflows/reusable-docker-build.yml | 58 +++++++++++++++++++-- 2 files changed, 81 insertions(+), 11 deletions(-) diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 660c9eb817a6..eed8de5a8722 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -157,27 +157,47 @@ jobs: if: github.repository == 'dspace/dspace' runs-on: ubuntu-latest # Must run after all major images are built - needs: [dspace-test, dspace-cli, dspace-postgres-pgcrypto, dspace-solr] + needs: [dspace, dspace-test, dspace-cli, dspace-postgres-pgcrypto, dspace-solr] steps: + # Checkout our codebase (to get access to Docker Compose scripts) - name: Checkout codebase uses: actions/checkout@v4 - # Start backend using our compose script in the codebase. + # For PRs, download Docker image artifacts (built by reusable-docker-build.yml for all PRs) + - name: Download Docker image artifacts (for PRs) + if: github.event_name == 'pull_request' + uses: actions/download-artifact@v4 + with: + # Download all Docker images (TAR files) into the /tmp/docker directory + pattern: docker-image-* + path: /tmp/docker + merge-multiple: true + # For PRs, load each of the images into Docker by calling "docker image load" for each. + # This ensures we are using the images built from this PR & not the prior versions on DockerHub + - name: Load all downloaded Docker images (for PRs) + if: github.event_name == 'pull_request' + run: | + find /tmp/docker -type f -name "*.tar" -exec docker image load --input "{}" \; + docker image ls -a + # Start backend using our compose script in the codebase. - name: Start backend in Docker env: # Override defaults dspace.server.url because backend starts at http://127.0.0.1:8080 dspace__P__server__P__url: http://127.0.0.1:8080/server + # Force using "pr-testing" version of Docker images. The "pr-testing" tag is a temporary tag that we assign to + # all PR-built docker images in reusabe-docker-build.yml + DSPACE_VER: pr-testing run: | docker compose -f docker-compose.yml up -d sleep 10 docker container ls - # Create a test admin account. Load test data from a simple set of AIPs as defined in cli.ingest.yml + # Create a test admin account. Load test data from a simple set of AIPs as defined in cli.ingest.yml - name: Load test data into Backend run: | docker compose -f docker-compose-cli.yml run --rm dspace-cli create-administrator -e test@test.edu -f admin -l user -p admin -c en docker compose -f docker-compose-cli.yml -f dspace/src/main/docker-compose/cli.ingest.yml run --rm dspace-cli - # Verify backend started successfully. - # 1. Make sure root endpoint is responding (check for dspace.name defined in docker-compose.yml) - # 2. Also check /collections endpoint to ensure the test data loaded properly (check for a collection name in AIPs) + # Verify backend started successfully. + # 1. Make sure root endpoint is responding (check for dspace.name defined in docker-compose.yml) + # 2. Also check /collections endpoint to ensure the test data loaded properly (check for a collection name in AIPs) - name: Verify backend is responding properly run: | result=$(wget -O- -q http://127.0.0.1:8080/server/api) @@ -204,7 +224,7 @@ jobs: result=$(wget -O- -q http://127.0.0.1:8000/) echo "$result" echo "$result" | grep -oE "Handle Proxy" - # Shutdown our containers + # Shutdown our containers - name: Shutdown Docker containers run: | docker compose -f docker-compose.yml down diff --git a/.github/workflows/reusable-docker-build.yml b/.github/workflows/reusable-docker-build.yml index 7a8de661fa68..91aa93c54ab2 100644 --- a/.github/workflows/reusable-docker-build.yml +++ b/.github/workflows/reusable-docker-build.yml @@ -113,6 +113,12 @@ jobs: - name: Set up QEMU emulation to build for multiple architectures uses: docker/setup-qemu-action@v3 + #------------------------------------------------------------ + # Build & deploy steps for new commits to a branch (non-PRs) + # + # These steps build the images, push to DockerHub, and + # (if necessary) redeploy demo/sandbox sites. + #------------------------------------------------------------ # https://github.com/docker/login-action - name: Login to DockerHub # Only login if not a PR, as PRs only trigger a Docker build and not a push @@ -125,6 +131,7 @@ jobs: # https://github.com/docker/metadata-action # Get Metadata for docker_build_deps step below - name: Sync metadata (tags, labels) from GitHub to Docker for image + if: ${{ ! matrix.isPr }} id: meta_build uses: docker/metadata-action@v5 with: @@ -133,7 +140,9 @@ jobs: flavor: ${{ env.TAGS_FLAVOR }} # https://github.com/docker/build-push-action - - name: Build and push image + - name: Build and push image to DockerHub + # Only build & push if not a PR + if: ${{ ! matrix.isPr }} id: docker_build uses: docker/build-push-action@v5 with: @@ -142,9 +151,7 @@ jobs: context: ${{ inputs.dockerfile_context }} file: ${{ inputs.dockerfile_path }} platforms: ${{ matrix.arch }} - # For pull requests, we run the Docker build (to ensure no PR changes break the build), - # but we ONLY do an image push to DockerHub if it's NOT a PR - push: ${{ ! matrix.isPr }} + push: true # Use tags / labels provided by 'docker/metadata-action' above tags: ${{ steps.meta_build.outputs.tags }} labels: ${{ steps.meta_build.outputs.labels }} @@ -189,11 +196,54 @@ jobs: run: | curl -X POST $REDEPLOY_DEMO_URL + #------------------------------------------------------------- + # Build steps for PRs only + # + # These steps build the images and store as a build artifact. + # These artifacts can then be used by later jobs to run the + # brand-new images for automated testing. + #-------------------------------------------------------------- + # Get Metadata for docker_build_deps step below + - name: Create metadata (tags, labels) for local Docker image + if: matrix.isPr + id: meta_build_pr + uses: docker/metadata-action@v5 + with: + images: ${{ env.IMAGE_NAME }} + # Hardcode to use custom "pr-testing" tag because that will allow us to spin up this PR + # for testing in docker.yml + tags: pr-testing + flavor: ${{ env.TAGS_FLAVOR }} + # Build local image and stores in a TAR file in /tmp directory + - name: Build and push image to local image + if: matrix.isPr + uses: docker/build-push-action@v5 + with: + build-contexts: | + ${{ inputs.dockerfile_additional_contexts }} + context: ${{ inputs.dockerfile_context }} + file: ${{ inputs.dockerfile_path }} + platforms: ${{ matrix.arch }} + tags: ${{ steps.meta_build_pr.outputs.tags }} + labels: ${{ steps.meta_build_pr.outputs.labels }} + # Export image to a local TAR file + outputs: type=docker,dest=/tmp/${{ inputs.build_id }}.tar + # Upload the local docker image (in TAR file) to a build Artifact + - name: Upload local image to artifact + if: matrix.isPr + uses: actions/upload-artifact@v4 + with: + name: docker-image-${{ inputs.build_id }} + path: /tmp/${{ inputs.build_id }}.tar + if-no-files-found: error + retention-days: 1 + # Merge Docker digests (from various architectures) into a manifest. # This runs after all Docker builds complete above, and it tells hub.docker.com # that these builds should be all included in the manifest for this tag. # (e.g. AMD64 and ARM64 should be listed as options under the same tagged Docker image) docker-build_manifest: + # Only run if this is NOT a PR if: ${{ github.event_name != 'pull_request' }} runs-on: ubuntu-latest needs: From cf3da4585551862cf6c74ad1e7906a6a08c162c6 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Mon, 11 Nov 2024 16:45:59 -0600 Subject: [PATCH 137/979] Fix error in running Handle Server in GitHub Actions. Must exclude "spring-jcl" from dependencies as it conflicts with "commons-logging" (used by more of our dependencies) (cherry picked from commit 31312b800a6bed795d5ca31bf2dfb445fc709c4d) --- dspace-api/pom.xml | 7 +++++++ dspace-iiif/pom.xml | 5 +++++ dspace-oai/pom.xml | 5 +++++ dspace-rdf/pom.xml | 5 +++++ dspace-server-webapp/pom.xml | 5 +++++ dspace-services/pom.xml | 7 +++++++ dspace-sword/pom.xml | 5 +++++ dspace-swordv2/pom.xml | 5 +++++ pom.xml | 7 +++++++ 9 files changed, 51 insertions(+) diff --git a/dspace-api/pom.xml b/dspace-api/pom.xml index dfb3f030ac0e..c51685fdb425 100644 --- a/dspace-api/pom.xml +++ b/dspace-api/pom.xml @@ -388,6 +388,13 @@ org.springframework spring-orm + + + + org.springframework + spring-jcl + + diff --git a/dspace-iiif/pom.xml b/dspace-iiif/pom.xml index cd2b69288d2e..030ac31b3843 100644 --- a/dspace-iiif/pom.xml +++ b/dspace-iiif/pom.xml @@ -44,6 +44,11 @@ org.springframework.boot spring-boot-starter-logging + + + org.springframework + spring-jcl + diff --git a/dspace-oai/pom.xml b/dspace-oai/pom.xml index 4158ec74f34d..29644392d540 100644 --- a/dspace-oai/pom.xml +++ b/dspace-oai/pom.xml @@ -80,6 +80,11 @@ org.springframework.boot spring-boot-starter-logging + + + org.springframework + spring-jcl + diff --git a/dspace-rdf/pom.xml b/dspace-rdf/pom.xml index d7db7fe76e8a..3d11794d86a2 100644 --- a/dspace-rdf/pom.xml +++ b/dspace-rdf/pom.xml @@ -67,6 +67,11 @@ org.springframework.boot spring-boot-starter-logging + + + org.springframework + spring-jcl + diff --git a/dspace-server-webapp/pom.xml b/dspace-server-webapp/pom.xml index 759ad724b9b6..290f8afb0dc6 100644 --- a/dspace-server-webapp/pom.xml +++ b/dspace-server-webapp/pom.xml @@ -468,6 +468,11 @@ org.springframework.boot spring-boot-starter-logging + + + org.springframework + spring-jcl + diff --git a/dspace-services/pom.xml b/dspace-services/pom.xml index d99132309100..6b2bf191acd5 100644 --- a/dspace-services/pom.xml +++ b/dspace-services/pom.xml @@ -90,6 +90,13 @@ spring-context-support ${spring.version} compile + + + + org.springframework + spring-jcl + + org.apache.commons diff --git a/dspace-sword/pom.xml b/dspace-sword/pom.xml index a808d0bbfeb9..ac84a674c455 100644 --- a/dspace-sword/pom.xml +++ b/dspace-sword/pom.xml @@ -55,6 +55,11 @@ org.springframework.boot spring-boot-starter-logging + + + org.springframework + spring-jcl + diff --git a/dspace-swordv2/pom.xml b/dspace-swordv2/pom.xml index c0549abcc88c..f22acf9dc9e0 100644 --- a/dspace-swordv2/pom.xml +++ b/dspace-swordv2/pom.xml @@ -78,6 +78,11 @@ org.springframework.boot spring-boot-starter-logging + + + org.springframework + spring-jcl + diff --git a/pom.xml b/pom.xml index 3eec65d4a9fd..5be05ca44e36 100644 --- a/pom.xml +++ b/pom.xml @@ -1155,6 +1155,13 @@ org.springframework spring-orm ${spring.version} + + + + org.springframework + spring-jcl + + org.swordapp From 48cb5e2082fb0e778c703e2b218a1ab361358af0 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Wed, 13 Nov 2024 09:17:30 -0600 Subject: [PATCH 138/979] Ensure "host" command is installed in images, so "bin/make-handle-config" will work. (cherry picked from commit a2172b37c3bb57a55e5a82ac2003ef20918c8984) --- Dockerfile | 5 +++++ Dockerfile.test | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/Dockerfile b/Dockerfile index 75817980379c..fdf2808c1c9a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -58,6 +58,11 @@ ENV DSPACE_INSTALL=/dspace # Copy the /dspace directory from 'ant_build' container to /dspace in this container COPY --from=ant_build /dspace $DSPACE_INSTALL WORKDIR $DSPACE_INSTALL +# Need host command for "[dspace]/bin/make-handle-config" +RUN apt-get update \ + && apt-get install -y --no-install-recommends host \ + && apt-get purge -y --auto-remove \ + && rm -rf /var/lib/apt/lists/* # Expose Tomcat port EXPOSE 8080 # Give java extra memory (2GB) diff --git a/Dockerfile.test b/Dockerfile.test index f3acef00e825..218126b17aab 100644 --- a/Dockerfile.test +++ b/Dockerfile.test @@ -57,6 +57,11 @@ ENV DSPACE_INSTALL=/dspace # Copy the /dspace directory from 'ant_build' container to /dspace in this container COPY --from=ant_build /dspace $DSPACE_INSTALL WORKDIR $DSPACE_INSTALL +# Need host command for "[dspace]/bin/make-handle-config" +RUN apt-get update \ + && apt-get install -y --no-install-recommends host \ + && apt-get purge -y --auto-remove \ + && rm -rf /var/lib/apt/lists/* # Expose Tomcat port and debugging port EXPOSE 8080 8000 # Give java extra memory (2GB) From 2253d79c1906149c2417d9f1b42fba243fc8c78b Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Wed, 13 Nov 2024 10:37:53 -0600 Subject: [PATCH 139/979] Bug fixes. Ensure all steps of docker-deploy use the same environment variables. Ensure Handle Server HTTP port is open (cherry picked from commit daa4abba62d500bb4a9f26dd9f62c156780ee22a) --- .github/workflows/docker.yml | 13 +++++++------ Dockerfile | 4 ++-- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index eed8de5a8722..5197ed3bfe39 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -158,6 +158,12 @@ jobs: runs-on: ubuntu-latest # Must run after all major images are built needs: [dspace, dspace-test, dspace-cli, dspace-postgres-pgcrypto, dspace-solr] + env: + # Override defaults dspace.server.url because backend starts at http://127.0.0.1:8080 + dspace__P__server__P__url: http://127.0.0.1:8080/server + # Force using "pr-testing" version of all Docker images. The "pr-testing" tag is a temporary tag that we + # assign to all PR-built docker images in reusabe-docker-build.yml + DSPACE_VER: pr-testing steps: # Checkout our codebase (to get access to Docker Compose scripts) - name: Checkout codebase @@ -180,12 +186,6 @@ jobs: docker image ls -a # Start backend using our compose script in the codebase. - name: Start backend in Docker - env: - # Override defaults dspace.server.url because backend starts at http://127.0.0.1:8080 - dspace__P__server__P__url: http://127.0.0.1:8080/server - # Force using "pr-testing" version of Docker images. The "pr-testing" tag is a temporary tag that we assign to - # all PR-built docker images in reusabe-docker-build.yml - DSPACE_VER: pr-testing run: | docker compose -f docker-compose.yml up -d sleep 10 @@ -214,6 +214,7 @@ jobs: - name: Verify Handle Server is working properly run: | docker exec -i dspace /dspace/bin/make-handle-config + echo "Starting Handle Server..." docker exec -i dspace /dspace/bin/start-handle-server sleep 20 echo "Checking for errors in handle-server.log..." diff --git a/Dockerfile b/Dockerfile index fdf2808c1c9a..d3f85a5bd641 100644 --- a/Dockerfile +++ b/Dockerfile @@ -63,8 +63,8 @@ RUN apt-get update \ && apt-get install -y --no-install-recommends host \ && apt-get purge -y --auto-remove \ && rm -rf /var/lib/apt/lists/* -# Expose Tomcat port -EXPOSE 8080 +# Expose Tomcat port (8080) & Handle Server HTTP port (8000) +EXPOSE 8080 8000 # Give java extra memory (2GB) ENV JAVA_OPTS=-Xmx2000m # On startup, run DSpace Runnable JAR From 663b871356c4835dc82161e9bd92ba30f79aa953 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Wed, 13 Nov 2024 11:45:03 -0600 Subject: [PATCH 140/979] Add check for Handle Server error.log (cherry picked from commit 53d24606431fcd3bd9a7c1a54298190842836e1c) --- .github/workflows/docker.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 5197ed3bfe39..1e839e89fceb 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -217,6 +217,10 @@ jobs: echo "Starting Handle Server..." docker exec -i dspace /dspace/bin/start-handle-server sleep 20 + echo "Checking for errors in error.log" + result=$(docker exec -i dspace sh -c "cat /dspace/handle-server/logs/error.log* || echo ''") + echo "$result" + echo "$result" | grep -vqz "Exception" echo "Checking for errors in handle-server.log..." result=$(docker exec -i dspace cat /dspace/log/handle-server.log) echo "$result" From c2e6f6f5d2843a5dd003a3f48ac63171a38a62f1 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Wed, 13 Nov 2024 14:27:28 -0600 Subject: [PATCH 141/979] Fix error in Handle Server startup caused by having multiple versions of BouncyCastle in our classpath. Exclude the old version brought in by cnri-servlet-container (cherry picked from commit 6076afec5f9a2bf53ace35abe384dc67dc5c1aef) --- dspace-api/pom.xml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/dspace-api/pom.xml b/dspace-api/pom.xml index c51685fdb425..acac810d6cda 100644 --- a/dspace-api/pom.xml +++ b/dspace-api/pom.xml @@ -413,6 +413,16 @@ org.mortbay.jasper apache-jsp + + + org.bouncycastle + bcpkix-jdk15on + + + org.bouncycastle + bcprov-jdk15on + From c66dde2aa85540dfbfa36f18f581599a6457889e Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Mon, 11 Nov 2024 10:26:29 -0600 Subject: [PATCH 142/979] Add a job to test Docker deployment with the built images --- .github/workflows/docker.yml | 43 +++++++++++++++++++++++++++++++++++- 1 file changed, 42 insertions(+), 1 deletion(-) diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index a9ff8760e763..12d49689b319 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -147,4 +147,45 @@ jobs: tags_flavor: suffix=-loadsql secrets: DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }} - DOCKER_ACCESS_TOKEN: ${{ secrets.DOCKER_ACCESS_TOKEN }} \ No newline at end of file + DOCKER_ACCESS_TOKEN: ${{ secrets.DOCKER_ACCESS_TOKEN }} + + ######################################################################## + # Test Deployment via Docker to ensure images are working properly + ######################################################################## + docker-deploy: + # Ensure this job never runs on forked repos. It's only executed for 'dspace/dspace' + if: github.repository == 'dspace/dspace' + runs-on: ubuntu-latest + # Must run after all major images are built + needs: [dspace-test, dspace-cli, dspace-postgres-pgcrypto, dspace-solr] + steps: + - name: Checkout codebase + uses: actions/checkout@v4 + # Start backend using our compose script in the codebase. + - name: Start backend in Docker + env: + # Override defaults dspace.server.url because backend starts at http://127.0.0.1:8080 + dspace__P__server__P__url: http://127.0.0.1:8080/server + run: | + docker compose -f docker-compose.yml up -d + sleep 10 + docker container ls + # Create a test admin account. Load test data from a simple set of AIPs as defined in cli.ingest.yml + - name: Load test data into Backend + run: | + docker compose -f docker-compose-cli.yml run --rm dspace-cli create-administrator -e test@test.edu -f admin -l user -p admin -c en + docker compose -f docker-compose-cli.yml -f dspace/src/main/docker-compose/cli.ingest.yml run --rm dspace-cli + # Verify backend started successfully. + # 1. Make sure root endpoint is responding (check for dspace.name defined in docker-compose.yml) + # 2. Also check /collections endpoint to ensure the test data loaded properly (check for a collection name in AIPs) + - name: Verify backend is responding properly + run: | + result=$(wget -O- -q http://127.0.0.1:8080/server/api) + echo "$result" + echo "$result" | grep -oE "\"DSpace Started with Docker Compose\"," + result=$(wget -O- -q http://127.0.0.1:8080/server/api/core/collections) + echo "$result" + echo "$result" | grep -oE "\"Dog in Yard\"," + - name: Shutdown Docker containers + run: | + docker compose -f docker-compose.yml down From 3ce85a2df3c548bf7842227cf86d8aa8bd2191b0 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Mon, 11 Nov 2024 14:10:44 -0600 Subject: [PATCH 143/979] Add a check that the Handle Server can be started & works properly --- .github/workflows/docker.yml | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 12d49689b319..660c9eb817a6 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -186,6 +186,25 @@ jobs: result=$(wget -O- -q http://127.0.0.1:8080/server/api/core/collections) echo "$result" echo "$result" | grep -oE "\"Dog in Yard\"," + # Verify Handle Server can be stared and is working properly + # 1. First generate the "[dspace]/handle-server" folder with the sitebndl.zip + # 2. Start the Handle Server (and wait 20 seconds to let it start up) + # 3. Verify logs do NOT include "Exception" in the text (as that means an error occurred) + # 4. Check that Handle Proxy HTML page is responding on default port (8000) + - name: Verify Handle Server is working properly + run: | + docker exec -i dspace /dspace/bin/make-handle-config + docker exec -i dspace /dspace/bin/start-handle-server + sleep 20 + echo "Checking for errors in handle-server.log..." + result=$(docker exec -i dspace cat /dspace/log/handle-server.log) + echo "$result" + echo "$result" | grep -vqz "Exception" + echo "Checking to see if Handle Proxy webpage is available..." + result=$(wget -O- -q http://127.0.0.1:8000/) + echo "$result" + echo "$result" | grep -oE "Handle Proxy" + # Shutdown our containers - name: Shutdown Docker containers run: | docker compose -f docker-compose.yml down From c1f168245b9756b6109bf6b53bc87317a66aa55c Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Tue, 12 Nov 2024 13:50:57 -0600 Subject: [PATCH 144/979] Ensure Docker images built from PRs are stored as artifacts. This allows us to use those new images when testing deployment (in docker-deploy) --- .github/workflows/docker.yml | 34 +++++++++--- .github/workflows/reusable-docker-build.yml | 58 +++++++++++++++++++-- 2 files changed, 81 insertions(+), 11 deletions(-) diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 660c9eb817a6..eed8de5a8722 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -157,27 +157,47 @@ jobs: if: github.repository == 'dspace/dspace' runs-on: ubuntu-latest # Must run after all major images are built - needs: [dspace-test, dspace-cli, dspace-postgres-pgcrypto, dspace-solr] + needs: [dspace, dspace-test, dspace-cli, dspace-postgres-pgcrypto, dspace-solr] steps: + # Checkout our codebase (to get access to Docker Compose scripts) - name: Checkout codebase uses: actions/checkout@v4 - # Start backend using our compose script in the codebase. + # For PRs, download Docker image artifacts (built by reusable-docker-build.yml for all PRs) + - name: Download Docker image artifacts (for PRs) + if: github.event_name == 'pull_request' + uses: actions/download-artifact@v4 + with: + # Download all Docker images (TAR files) into the /tmp/docker directory + pattern: docker-image-* + path: /tmp/docker + merge-multiple: true + # For PRs, load each of the images into Docker by calling "docker image load" for each. + # This ensures we are using the images built from this PR & not the prior versions on DockerHub + - name: Load all downloaded Docker images (for PRs) + if: github.event_name == 'pull_request' + run: | + find /tmp/docker -type f -name "*.tar" -exec docker image load --input "{}" \; + docker image ls -a + # Start backend using our compose script in the codebase. - name: Start backend in Docker env: # Override defaults dspace.server.url because backend starts at http://127.0.0.1:8080 dspace__P__server__P__url: http://127.0.0.1:8080/server + # Force using "pr-testing" version of Docker images. The "pr-testing" tag is a temporary tag that we assign to + # all PR-built docker images in reusabe-docker-build.yml + DSPACE_VER: pr-testing run: | docker compose -f docker-compose.yml up -d sleep 10 docker container ls - # Create a test admin account. Load test data from a simple set of AIPs as defined in cli.ingest.yml + # Create a test admin account. Load test data from a simple set of AIPs as defined in cli.ingest.yml - name: Load test data into Backend run: | docker compose -f docker-compose-cli.yml run --rm dspace-cli create-administrator -e test@test.edu -f admin -l user -p admin -c en docker compose -f docker-compose-cli.yml -f dspace/src/main/docker-compose/cli.ingest.yml run --rm dspace-cli - # Verify backend started successfully. - # 1. Make sure root endpoint is responding (check for dspace.name defined in docker-compose.yml) - # 2. Also check /collections endpoint to ensure the test data loaded properly (check for a collection name in AIPs) + # Verify backend started successfully. + # 1. Make sure root endpoint is responding (check for dspace.name defined in docker-compose.yml) + # 2. Also check /collections endpoint to ensure the test data loaded properly (check for a collection name in AIPs) - name: Verify backend is responding properly run: | result=$(wget -O- -q http://127.0.0.1:8080/server/api) @@ -204,7 +224,7 @@ jobs: result=$(wget -O- -q http://127.0.0.1:8000/) echo "$result" echo "$result" | grep -oE "Handle Proxy" - # Shutdown our containers + # Shutdown our containers - name: Shutdown Docker containers run: | docker compose -f docker-compose.yml down diff --git a/.github/workflows/reusable-docker-build.yml b/.github/workflows/reusable-docker-build.yml index 12aa0cfe2864..83dfd74b9a1a 100644 --- a/.github/workflows/reusable-docker-build.yml +++ b/.github/workflows/reusable-docker-build.yml @@ -113,6 +113,12 @@ jobs: - name: Set up QEMU emulation to build for multiple architectures uses: docker/setup-qemu-action@v3 + #------------------------------------------------------------ + # Build & deploy steps for new commits to a branch (non-PRs) + # + # These steps build the images, push to DockerHub, and + # (if necessary) redeploy demo/sandbox sites. + #------------------------------------------------------------ # https://github.com/docker/login-action - name: Login to DockerHub # Only login if not a PR, as PRs only trigger a Docker build and not a push @@ -125,6 +131,7 @@ jobs: # https://github.com/docker/metadata-action # Get Metadata for docker_build_deps step below - name: Sync metadata (tags, labels) from GitHub to Docker for image + if: ${{ ! matrix.isPr }} id: meta_build uses: docker/metadata-action@v5 with: @@ -133,7 +140,9 @@ jobs: flavor: ${{ env.TAGS_FLAVOR }} # https://github.com/docker/build-push-action - - name: Build and push image + - name: Build and push image to DockerHub + # Only build & push if not a PR + if: ${{ ! matrix.isPr }} id: docker_build uses: docker/build-push-action@v5 with: @@ -142,9 +151,7 @@ jobs: context: ${{ inputs.dockerfile_context }} file: ${{ inputs.dockerfile_path }} platforms: ${{ matrix.arch }} - # For pull requests, we run the Docker build (to ensure no PR changes break the build), - # but we ONLY do an image push to DockerHub if it's NOT a PR - push: ${{ ! matrix.isPr }} + push: true # Use tags / labels provided by 'docker/metadata-action' above tags: ${{ steps.meta_build.outputs.tags }} labels: ${{ steps.meta_build.outputs.labels }} @@ -189,11 +196,54 @@ jobs: run: | curl -X POST $REDEPLOY_DEMO_URL + #------------------------------------------------------------- + # Build steps for PRs only + # + # These steps build the images and store as a build artifact. + # These artifacts can then be used by later jobs to run the + # brand-new images for automated testing. + #-------------------------------------------------------------- + # Get Metadata for docker_build_deps step below + - name: Create metadata (tags, labels) for local Docker image + if: matrix.isPr + id: meta_build_pr + uses: docker/metadata-action@v5 + with: + images: ${{ env.IMAGE_NAME }} + # Hardcode to use custom "pr-testing" tag because that will allow us to spin up this PR + # for testing in docker.yml + tags: pr-testing + flavor: ${{ env.TAGS_FLAVOR }} + # Build local image and stores in a TAR file in /tmp directory + - name: Build and push image to local image + if: matrix.isPr + uses: docker/build-push-action@v5 + with: + build-contexts: | + ${{ inputs.dockerfile_additional_contexts }} + context: ${{ inputs.dockerfile_context }} + file: ${{ inputs.dockerfile_path }} + platforms: ${{ matrix.arch }} + tags: ${{ steps.meta_build_pr.outputs.tags }} + labels: ${{ steps.meta_build_pr.outputs.labels }} + # Export image to a local TAR file + outputs: type=docker,dest=/tmp/${{ inputs.build_id }}.tar + # Upload the local docker image (in TAR file) to a build Artifact + - name: Upload local image to artifact + if: matrix.isPr + uses: actions/upload-artifact@v4 + with: + name: docker-image-${{ inputs.build_id }} + path: /tmp/${{ inputs.build_id }}.tar + if-no-files-found: error + retention-days: 1 + # Merge Docker digests (from various architectures) into a manifest. # This runs after all Docker builds complete above, and it tells hub.docker.com # that these builds should be all included in the manifest for this tag. # (e.g. AMD64 and ARM64 should be listed as options under the same tagged Docker image) docker-build_manifest: + # Only run if this is NOT a PR if: ${{ github.event_name != 'pull_request' }} runs-on: ubuntu-latest needs: From 9853aa5bb45ceef6ba6e01a08515f0cdea68f92c Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Mon, 11 Nov 2024 16:45:59 -0600 Subject: [PATCH 145/979] Fix error in running Handle Server in GitHub Actions. Must exclude "spring-jcl" from dependencies as it conflicts with "commons-logging" (used by more of our dependencies) --- dspace-api/pom.xml | 7 +++++++ dspace-iiif/pom.xml | 5 +++++ dspace-oai/pom.xml | 5 +++++ dspace-rdf/pom.xml | 5 +++++ dspace-server-webapp/pom.xml | 5 +++++ dspace-services/pom.xml | 7 +++++++ dspace-sword/pom.xml | 5 +++++ dspace-swordv2/pom.xml | 5 +++++ pom.xml | 7 +++++++ 9 files changed, 51 insertions(+) diff --git a/dspace-api/pom.xml b/dspace-api/pom.xml index bdf035179bee..c3ef2c86044d 100644 --- a/dspace-api/pom.xml +++ b/dspace-api/pom.xml @@ -396,6 +396,13 @@ org.springframework spring-orm + + + + org.springframework + spring-jcl + + diff --git a/dspace-iiif/pom.xml b/dspace-iiif/pom.xml index 64dd5106f0af..49ee02b56500 100644 --- a/dspace-iiif/pom.xml +++ b/dspace-iiif/pom.xml @@ -38,6 +38,11 @@ org.springframework.boot spring-boot-starter-logging + + + org.springframework + spring-jcl + diff --git a/dspace-oai/pom.xml b/dspace-oai/pom.xml index 28b610e996c1..bfa2c8a27381 100644 --- a/dspace-oai/pom.xml +++ b/dspace-oai/pom.xml @@ -80,6 +80,11 @@ org.springframework.boot spring-boot-starter-logging + + + org.springframework + spring-jcl + diff --git a/dspace-rdf/pom.xml b/dspace-rdf/pom.xml index e656a920a1e6..075f2d7038e8 100644 --- a/dspace-rdf/pom.xml +++ b/dspace-rdf/pom.xml @@ -67,6 +67,11 @@ org.springframework.boot spring-boot-starter-logging + + + org.springframework + spring-jcl + diff --git a/dspace-server-webapp/pom.xml b/dspace-server-webapp/pom.xml index 5a4e1b32123d..35fa473fc170 100644 --- a/dspace-server-webapp/pom.xml +++ b/dspace-server-webapp/pom.xml @@ -405,6 +405,11 @@ org.springframework.boot spring-boot-starter-logging + + + org.springframework + spring-jcl + diff --git a/dspace-services/pom.xml b/dspace-services/pom.xml index 51c6dea62413..7c85ff7f0f0f 100644 --- a/dspace-services/pom.xml +++ b/dspace-services/pom.xml @@ -85,6 +85,13 @@ spring-context-support ${spring.version} compile + + + + org.springframework + spring-jcl + + org.apache.commons diff --git a/dspace-sword/pom.xml b/dspace-sword/pom.xml index 1403c779e720..01aa68dfbfe6 100644 --- a/dspace-sword/pom.xml +++ b/dspace-sword/pom.xml @@ -60,6 +60,11 @@ org.springframework.boot spring-boot-starter-logging + + + org.springframework + spring-jcl + diff --git a/dspace-swordv2/pom.xml b/dspace-swordv2/pom.xml index 470388883126..f575898c122a 100644 --- a/dspace-swordv2/pom.xml +++ b/dspace-swordv2/pom.xml @@ -80,6 +80,11 @@ org.springframework.boot spring-boot-starter-logging + + + org.springframework + spring-jcl + diff --git a/pom.xml b/pom.xml index 02579c03294a..b0d9d1819d3f 100644 --- a/pom.xml +++ b/pom.xml @@ -1171,6 +1171,13 @@ org.springframework spring-orm ${spring.version} + + + + org.springframework + spring-jcl + + org.swordapp From faca14ad4059de0cb66c6d6ca0baf4161ac3aadd Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Wed, 13 Nov 2024 09:17:30 -0600 Subject: [PATCH 146/979] Ensure "host" command is installed in images, so "bin/make-handle-config" will work. --- Dockerfile | 5 +++++ Dockerfile.test | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/Dockerfile b/Dockerfile index 9d89710fe11c..ffce09239d1b 100644 --- a/Dockerfile +++ b/Dockerfile @@ -55,6 +55,11 @@ FROM tomcat:9-jdk${JDK_VERSION} ENV DSPACE_INSTALL=/dspace # Copy the /dspace directory from 'ant_build' container to /dspace in this container COPY --from=ant_build /dspace $DSPACE_INSTALL +# Need host command for "[dspace]/bin/make-handle-config" +RUN apt-get update \ + && apt-get install -y --no-install-recommends host \ + && apt-get purge -y --auto-remove \ + && rm -rf /var/lib/apt/lists/* # Expose Tomcat port and AJP port EXPOSE 8080 8009 # Give java extra memory (2GB) diff --git a/Dockerfile.test b/Dockerfile.test index 031394ad256c..d88699ca52ab 100644 --- a/Dockerfile.test +++ b/Dockerfile.test @@ -54,6 +54,11 @@ ENV DSPACE_INSTALL=/dspace ENV TOMCAT_INSTALL=/usr/local/tomcat # Copy the /dspace directory from 'ant_build' containger to /dspace in this container COPY --from=ant_build /dspace $DSPACE_INSTALL +# Need host command for "[dspace]/bin/make-handle-config" +RUN apt-get update \ + && apt-get install -y --no-install-recommends host \ + && apt-get purge -y --auto-remove \ + && rm -rf /var/lib/apt/lists/* # Enable the AJP connector in Tomcat's server.xml # NOTE: secretRequired="false" should only be used when AJP is NOT accessible from an external network. But, secretRequired="true" isn't supported by mod_proxy_ajp until Apache 2.5 RUN sed -i '/Service name="Catalina".*/a \\n ' $TOMCAT_INSTALL/conf/server.xml From a0ed4a40eabab6296a577b3d7c8aa5a5fe024888 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Wed, 13 Nov 2024 10:37:53 -0600 Subject: [PATCH 147/979] Bug fixes. Ensure all steps of docker-deploy use the same environment variables. Ensure Handle Server HTTP port is open --- .github/workflows/docker.yml | 13 +++++++------ Dockerfile | 4 ++-- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index eed8de5a8722..5197ed3bfe39 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -158,6 +158,12 @@ jobs: runs-on: ubuntu-latest # Must run after all major images are built needs: [dspace, dspace-test, dspace-cli, dspace-postgres-pgcrypto, dspace-solr] + env: + # Override defaults dspace.server.url because backend starts at http://127.0.0.1:8080 + dspace__P__server__P__url: http://127.0.0.1:8080/server + # Force using "pr-testing" version of all Docker images. The "pr-testing" tag is a temporary tag that we + # assign to all PR-built docker images in reusabe-docker-build.yml + DSPACE_VER: pr-testing steps: # Checkout our codebase (to get access to Docker Compose scripts) - name: Checkout codebase @@ -180,12 +186,6 @@ jobs: docker image ls -a # Start backend using our compose script in the codebase. - name: Start backend in Docker - env: - # Override defaults dspace.server.url because backend starts at http://127.0.0.1:8080 - dspace__P__server__P__url: http://127.0.0.1:8080/server - # Force using "pr-testing" version of Docker images. The "pr-testing" tag is a temporary tag that we assign to - # all PR-built docker images in reusabe-docker-build.yml - DSPACE_VER: pr-testing run: | docker compose -f docker-compose.yml up -d sleep 10 @@ -214,6 +214,7 @@ jobs: - name: Verify Handle Server is working properly run: | docker exec -i dspace /dspace/bin/make-handle-config + echo "Starting Handle Server..." docker exec -i dspace /dspace/bin/start-handle-server sleep 20 echo "Checking for errors in handle-server.log..." diff --git a/Dockerfile b/Dockerfile index ffce09239d1b..adc5d6125f0a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -60,8 +60,8 @@ RUN apt-get update \ && apt-get install -y --no-install-recommends host \ && apt-get purge -y --auto-remove \ && rm -rf /var/lib/apt/lists/* -# Expose Tomcat port and AJP port -EXPOSE 8080 8009 +# Expose Tomcat port (8080) and AJP port (8009) and Handle Server HTTP port (8000) +EXPOSE 8080 8009 8000 # Give java extra memory (2GB) ENV JAVA_OPTS=-Xmx2000m From d6d78298b31252155db7d00d7ae8eee8f2c0ac93 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Wed, 13 Nov 2024 11:45:03 -0600 Subject: [PATCH 148/979] Add check for Handle Server error.log --- .github/workflows/docker.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 5197ed3bfe39..1e839e89fceb 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -217,6 +217,10 @@ jobs: echo "Starting Handle Server..." docker exec -i dspace /dspace/bin/start-handle-server sleep 20 + echo "Checking for errors in error.log" + result=$(docker exec -i dspace sh -c "cat /dspace/handle-server/logs/error.log* || echo ''") + echo "$result" + echo "$result" | grep -vqz "Exception" echo "Checking for errors in handle-server.log..." result=$(docker exec -i dspace cat /dspace/log/handle-server.log) echo "$result" From 5bb65c6b5647f74161244868c0864e9adb12d12a Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Wed, 13 Nov 2024 14:27:28 -0600 Subject: [PATCH 149/979] Fix error in Handle Server startup caused by having multiple versions of BouncyCastle in our classpath. Exclude the old version brought in by cnri-servlet-container --- dspace-api/pom.xml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/dspace-api/pom.xml b/dspace-api/pom.xml index c3ef2c86044d..c42895ab4c49 100644 --- a/dspace-api/pom.xml +++ b/dspace-api/pom.xml @@ -427,6 +427,16 @@ org.bouncycastle bcprov-jdk15on + + + org.bouncycastle + bcpkix-jdk15on + + + org.bouncycastle + bcprov-jdk15on + From aa71e4840be6fa9c6368e061d3e169e08a89c66a Mon Sep 17 00:00:00 2001 From: Agustina Martinez Date: Mon, 5 Aug 2024 11:06:06 +0200 Subject: [PATCH 150/979] Fix 9734: Check configured workflow.reviewer.file-edit to show item edit functionality in workflow UI (cherry picked from commit e8ec0c1b1d20dd5e812f38593a24718fff1d8c6e) --- .../processingaction/ScoreReviewAction.java | 15 ++++++++++++++- .../processingaction/SingleUserReviewAction.java | 8 ++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/dspace-api/src/main/java/org/dspace/xmlworkflow/state/actions/processingaction/ScoreReviewAction.java b/dspace-api/src/main/java/org/dspace/xmlworkflow/state/actions/processingaction/ScoreReviewAction.java index 43a3decacc7e..419bb1236402 100644 --- a/dspace-api/src/main/java/org/dspace/xmlworkflow/state/actions/processingaction/ScoreReviewAction.java +++ b/dspace-api/src/main/java/org/dspace/xmlworkflow/state/actions/processingaction/ScoreReviewAction.java @@ -8,6 +8,7 @@ package org.dspace.xmlworkflow.state.actions.processingaction; import java.sql.SQLException; +import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; @@ -20,6 +21,8 @@ import org.dspace.authorize.AuthorizeException; import org.dspace.content.MetadataFieldName; import org.dspace.core.Context; +import org.dspace.services.ConfigurationService; +import org.dspace.services.factory.DSpaceServicesFactory; import org.dspace.xmlworkflow.service.WorkflowRequirementsService; import org.dspace.xmlworkflow.state.Step; import org.dspace.xmlworkflow.state.actions.ActionAdvancedInfo; @@ -34,6 +37,9 @@ public class ScoreReviewAction extends ProcessingAction { private static final Logger log = LogManager.getLogger(ScoreReviewAction.class); + private final ConfigurationService configurationService + = DSpaceServicesFactory.getInstance().getConfigurationService(); + // Option(s) public static final String SUBMIT_SCORE = "submit_score"; @@ -114,7 +120,14 @@ private boolean checkRequestValid(int score, String review) { @Override public List getOptions() { - return List.of(SUBMIT_SCORE, RETURN_TO_POOL); + List options = new ArrayList<>(); + options.add(SUBMIT_SCORE); + if (configurationService.getBooleanProperty("workflow.reviewer.file-edit", false)) { + options.add(SUBMIT_EDIT_METADATA); + } + options.add(RETURN_TO_POOL); + + return options; } @Override diff --git a/dspace-api/src/main/java/org/dspace/xmlworkflow/state/actions/processingaction/SingleUserReviewAction.java b/dspace-api/src/main/java/org/dspace/xmlworkflow/state/actions/processingaction/SingleUserReviewAction.java index b3fe896ace24..0b8f2d13648c 100644 --- a/dspace-api/src/main/java/org/dspace/xmlworkflow/state/actions/processingaction/SingleUserReviewAction.java +++ b/dspace-api/src/main/java/org/dspace/xmlworkflow/state/actions/processingaction/SingleUserReviewAction.java @@ -21,6 +21,8 @@ import org.dspace.content.factory.ContentServiceFactory; import org.dspace.core.Context; import org.dspace.eperson.EPerson; +import org.dspace.services.ConfigurationService; +import org.dspace.services.factory.DSpaceServicesFactory; import org.dspace.workflow.WorkflowException; import org.dspace.xmlworkflow.factory.XmlWorkflowServiceFactory; import org.dspace.xmlworkflow.state.Step; @@ -40,6 +42,9 @@ public class SingleUserReviewAction extends ProcessingAction { private static final Logger log = LogManager.getLogger(SingleUserReviewAction.class); + private final ConfigurationService configurationService + = DSpaceServicesFactory.getInstance().getConfigurationService(); + public static final int OUTCOME_REJECT = 1; protected static final String SUBMIT_DECLINE_TASK = "submit_decline_task"; @@ -95,6 +100,9 @@ public ActionResult processAccept(Context c, XmlWorkflowItem wfi) throws SQLExce public List getOptions() { List options = new ArrayList<>(); options.add(SUBMIT_APPROVE); + if (configurationService.getBooleanProperty("workflow.reviewer.file-edit", false)) { + options.add(SUBMIT_EDIT_METADATA); + } options.add(SUBMIT_REJECT); options.add(SUBMIT_DECLINE_TASK); return options; From 60004c32ab1d73a1d0e85d9b68973c1daf31d5b9 Mon Sep 17 00:00:00 2001 From: Agustina Martinez Date: Mon, 5 Aug 2024 11:06:06 +0200 Subject: [PATCH 151/979] Fix 9734: Check configured workflow.reviewer.file-edit to show item edit functionality in workflow UI (cherry picked from commit e8ec0c1b1d20dd5e812f38593a24718fff1d8c6e) --- .../processingaction/ScoreReviewAction.java | 15 ++++++++++++++- .../processingaction/SingleUserReviewAction.java | 8 ++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/dspace-api/src/main/java/org/dspace/xmlworkflow/state/actions/processingaction/ScoreReviewAction.java b/dspace-api/src/main/java/org/dspace/xmlworkflow/state/actions/processingaction/ScoreReviewAction.java index a8ed4fd3dae9..50f338499282 100644 --- a/dspace-api/src/main/java/org/dspace/xmlworkflow/state/actions/processingaction/ScoreReviewAction.java +++ b/dspace-api/src/main/java/org/dspace/xmlworkflow/state/actions/processingaction/ScoreReviewAction.java @@ -8,6 +8,7 @@ package org.dspace.xmlworkflow.state.actions.processingaction; import java.sql.SQLException; +import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; @@ -20,6 +21,8 @@ import org.dspace.authorize.AuthorizeException; import org.dspace.content.MetadataFieldName; import org.dspace.core.Context; +import org.dspace.services.ConfigurationService; +import org.dspace.services.factory.DSpaceServicesFactory; import org.dspace.xmlworkflow.service.WorkflowRequirementsService; import org.dspace.xmlworkflow.state.Step; import org.dspace.xmlworkflow.state.actions.ActionAdvancedInfo; @@ -34,6 +37,9 @@ public class ScoreReviewAction extends ProcessingAction { private static final Logger log = LogManager.getLogger(ScoreReviewAction.class); + private final ConfigurationService configurationService + = DSpaceServicesFactory.getInstance().getConfigurationService(); + // Option(s) public static final String SUBMIT_SCORE = "submit_score"; @@ -114,7 +120,14 @@ private boolean checkRequestValid(int score, String review) { @Override public List getOptions() { - return List.of(SUBMIT_SCORE, RETURN_TO_POOL); + List options = new ArrayList<>(); + options.add(SUBMIT_SCORE); + if (configurationService.getBooleanProperty("workflow.reviewer.file-edit", false)) { + options.add(SUBMIT_EDIT_METADATA); + } + options.add(RETURN_TO_POOL); + + return options; } @Override diff --git a/dspace-api/src/main/java/org/dspace/xmlworkflow/state/actions/processingaction/SingleUserReviewAction.java b/dspace-api/src/main/java/org/dspace/xmlworkflow/state/actions/processingaction/SingleUserReviewAction.java index 64e0957b65b7..c46fa851e4f1 100644 --- a/dspace-api/src/main/java/org/dspace/xmlworkflow/state/actions/processingaction/SingleUserReviewAction.java +++ b/dspace-api/src/main/java/org/dspace/xmlworkflow/state/actions/processingaction/SingleUserReviewAction.java @@ -21,6 +21,8 @@ import org.dspace.content.factory.ContentServiceFactory; import org.dspace.core.Context; import org.dspace.eperson.EPerson; +import org.dspace.services.ConfigurationService; +import org.dspace.services.factory.DSpaceServicesFactory; import org.dspace.workflow.WorkflowException; import org.dspace.xmlworkflow.factory.XmlWorkflowServiceFactory; import org.dspace.xmlworkflow.state.Step; @@ -40,6 +42,9 @@ public class SingleUserReviewAction extends ProcessingAction { private static final Logger log = LogManager.getLogger(SingleUserReviewAction.class); + private final ConfigurationService configurationService + = DSpaceServicesFactory.getInstance().getConfigurationService(); + public static final int OUTCOME_REJECT = 1; protected static final String SUBMIT_DECLINE_TASK = "submit_decline_task"; @@ -95,6 +100,9 @@ public ActionResult processAccept(Context c, XmlWorkflowItem wfi) throws SQLExce public List getOptions() { List options = new ArrayList<>(); options.add(SUBMIT_APPROVE); + if (configurationService.getBooleanProperty("workflow.reviewer.file-edit", false)) { + options.add(SUBMIT_EDIT_METADATA); + } options.add(SUBMIT_REJECT); options.add(SUBMIT_DECLINE_TASK); return options; From b76d152444f464e26ec6c99b86d59c7e46a540e2 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Thu, 14 Nov 2024 14:46:31 -0600 Subject: [PATCH 152/979] Remove unnecessary dependencyManagement section from dspace-api. No longer needed for dependency convergence --- dspace-api/pom.xml | 73 ++-------------------------------------------- 1 file changed, 2 insertions(+), 71 deletions(-) diff --git a/dspace-api/pom.xml b/dspace-api/pom.xml index acac810d6cda..c953abf0702b 100644 --- a/dspace-api/pom.xml +++ b/dspace-api/pom.xml @@ -793,11 +793,13 @@ org.apache.velocity velocity-engine-core + 2.3 org.xmlunit xmlunit-core + 2.10.0 test @@ -873,75 +875,4 @@ - - - - - - - io.netty - netty-buffer - 4.1.106.Final - - - io.netty - netty-transport - 4.1.106.Final - - - io.netty - netty-transport-native-unix-common - 4.1.106.Final - - - io.netty - netty-common - 4.1.106.Final - - - io.netty - netty-handler - 4.1.106.Final - - - io.netty - netty-codec - 4.1.106.Final - - - org.apache.velocity - velocity-engine-core - 2.3 - - - org.xmlunit - xmlunit-core - 2.10.0 - test - - - com.github.java-json-tools - json-schema-validator - 2.2.14 - - - jakarta.validation - jakarta.validation-api - 3.0.2 - - - io.swagger - swagger-core - 1.6.2 - - - org.scala-lang - scala-library - 2.13.11 - test - - - - From b4f19158b072edcd70ac25163562619ecec7856e Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Thu, 14 Nov 2024 14:58:49 -0600 Subject: [PATCH 153/979] Fix multiple declarations of maven-dependency-plugin. Combine two tasks into one declaration --- dspace/modules/server/pom.xml | 36 +++++++++++++++-------------------- 1 file changed, 15 insertions(+), 21 deletions(-) diff --git a/dspace/modules/server/pom.xml b/dspace/modules/server/pom.xml index ca4eea26a07c..c1ce045fe896 100644 --- a/dspace/modules/server/pom.xml +++ b/dspace/modules/server/pom.xml @@ -27,7 +27,7 @@ maven-dependency-plugin - unpack + unpack-additions prepare-package unpack-dependencies @@ -42,6 +42,20 @@ META-INF/** + + unpack-server + prepare-package + + unpack-dependencies + + + runtime + org.dspace + dspace-server-webapp + **/static/**,**/*.properties + ${project.build.directory}/additions + + @@ -68,26 +82,6 @@ - - org.apache.maven.plugins - maven-dependency-plugin - - - unpack - prepare-package - - unpack-dependencies - - - runtime - org.dspace - dspace-server-webapp - **/static/**,**/*.properties - ${project.build.directory}/additions - - - - diff --git a/pom.xml b/pom.xml index 5be05ca44e36..63985484215c 100644 --- a/pom.xml +++ b/pom.xml @@ -1746,36 +1746,6 @@ google-api-services-analytics v3-rev145-1.23.0 - - com.google.api-client - google-api-client - 1.23.0 - - - com.google.http-client - google-http-client - 1.23.0 - - - com.google.http-client - google-http-client-jackson2 - 1.23.0 - - - jackson-core - com.fasterxml.jackson.core - - - jackson-databind - com.fasterxml.jackson.core - - - - - com.google.oauth-client - google-oauth-client - 1.33.3 - From f0c92ac96b6f6f22445020b9e9690886bb83830c Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Fri, 15 Nov 2024 16:06:10 -0600 Subject: [PATCH 158/979] Ensure only main branch uses "latest". Other branches should use the tag corresponding to the branch name (cherry picked from commit e0b7241acb167496ebc8c80c73ae69b9f6611a1c) --- .github/workflows/docker.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index a0aee14bea38..143b69a2f9fe 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -161,10 +161,10 @@ jobs: env: # Override defaults dspace.server.url because backend starts at http://127.0.0.1:8080 dspace__P__server__P__url: http://127.0.0.1:8080/server - # If this is a PR, force using "pr-testing" version of all Docker images. Otherwise, for branch commits, use the - # "latest" tag. NOTE: the "pr-testing" tag is a temporary tag that we assign to all PR-built docker images in - # reusabe-docker-build.yml - DSPACE_VER: ${{ github.event_name == 'pull_request' && 'pr-testing' || 'latest' }} + # If this is a PR, force using "pr-testing" version of all Docker images. Otherwise, if on main branch, use the + # "latest" tag. Otherwise, use the branch name. NOTE: the "pr-testing" tag is a temporary tag that we assign to + # all PR-built docker images in reusabe-docker-build.yml + DSPACE_VER: ${{ (github.event_name == 'pull_request' && 'pr-testing') || (github.ref_name == github.event.repository.default_branch && 'latest') || github.ref_name }} steps: # Checkout our codebase (to get access to Docker Compose scripts) - name: Checkout codebase From 8c9cdc6c9c0c03df1d4a9342552fe42553cba639 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Fri, 15 Nov 2024 16:06:10 -0600 Subject: [PATCH 159/979] Ensure only main branch uses "latest". Other branches should use the tag corresponding to the branch name (cherry picked from commit e0b7241acb167496ebc8c80c73ae69b9f6611a1c) --- .github/workflows/docker.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index a0aee14bea38..143b69a2f9fe 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -161,10 +161,10 @@ jobs: env: # Override defaults dspace.server.url because backend starts at http://127.0.0.1:8080 dspace__P__server__P__url: http://127.0.0.1:8080/server - # If this is a PR, force using "pr-testing" version of all Docker images. Otherwise, for branch commits, use the - # "latest" tag. NOTE: the "pr-testing" tag is a temporary tag that we assign to all PR-built docker images in - # reusabe-docker-build.yml - DSPACE_VER: ${{ github.event_name == 'pull_request' && 'pr-testing' || 'latest' }} + # If this is a PR, force using "pr-testing" version of all Docker images. Otherwise, if on main branch, use the + # "latest" tag. Otherwise, use the branch name. NOTE: the "pr-testing" tag is a temporary tag that we assign to + # all PR-built docker images in reusabe-docker-build.yml + DSPACE_VER: ${{ (github.event_name == 'pull_request' && 'pr-testing') || (github.ref_name == github.event.repository.default_branch && 'latest') || github.ref_name }} steps: # Checkout our codebase (to get access to Docker Compose scripts) - name: Checkout codebase From 56029149c1b7a366ea73269c1f55760b5251cd0e Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Fri, 15 Nov 2024 11:30:14 -0600 Subject: [PATCH 160/979] Remove parboiled-java and minor cleanup of unused OAI dependencies --- dspace-oai/pom.xml | 23 +------------------ .../xoai/app/CCElementItemCompilePlugin.java | 2 +- .../main/java/org/dspace/xoai/app/XOAI.java | 2 +- .../tests/integration/xoai/PipelineTest.java | 9 ++++---- 4 files changed, 8 insertions(+), 28 deletions(-) diff --git a/dspace-oai/pom.xml b/dspace-oai/pom.xml index 29644392d540..35f12b02a5ce 100644 --- a/dspace-oai/pom.xml +++ b/dspace-oai/pom.xml @@ -99,22 +99,9 @@ org.springframework.boot spring-boot-starter-web - - - org.parboiled - parboiled-java - - - - org.parboiled - parboiled-java - 1.3.1 - - org.dspace @@ -137,15 +124,7 @@ org.apache.logging.log4j log4j-core - - org.apache.logging.log4j - log4j-web - - - org.apache.logging.log4j - log4j-slf4j-impl - runtime - + org.apache.logging.log4j log4j-1.2-api diff --git a/dspace-oai/src/main/java/org/dspace/xoai/app/CCElementItemCompilePlugin.java b/dspace-oai/src/main/java/org/dspace/xoai/app/CCElementItemCompilePlugin.java index 225d56a4c982..370543029d8b 100644 --- a/dspace-oai/src/main/java/org/dspace/xoai/app/CCElementItemCompilePlugin.java +++ b/dspace-oai/src/main/java/org/dspace/xoai/app/CCElementItemCompilePlugin.java @@ -11,7 +11,7 @@ import com.lyncode.xoai.dataprovider.xml.xoai.Element; import com.lyncode.xoai.dataprovider.xml.xoai.Metadata; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; import org.dspace.content.Item; import org.dspace.core.Context; import org.dspace.license.factory.LicenseServiceFactory; diff --git a/dspace-oai/src/main/java/org/dspace/xoai/app/XOAI.java b/dspace-oai/src/main/java/org/dspace/xoai/app/XOAI.java index 25cc1ee3655f..ad138ca9f2ad 100644 --- a/dspace-oai/src/main/java/org/dspace/xoai/app/XOAI.java +++ b/dspace-oai/src/main/java/org/dspace/xoai/app/XOAI.java @@ -9,7 +9,7 @@ import static com.lyncode.xoai.dataprovider.core.Granularity.Second; import static java.util.Objects.nonNull; -import static org.apache.commons.lang.StringUtils.EMPTY; +import static org.apache.commons.lang3.StringUtils.EMPTY; import static org.apache.solr.common.params.CursorMarkParams.CURSOR_MARK_PARAM; import static org.apache.solr.common.params.CursorMarkParams.CURSOR_MARK_START; import static org.dspace.xoai.util.ItemUtils.retrieveMetadata; diff --git a/dspace-oai/src/test/java/org/dspace/xoai/tests/integration/xoai/PipelineTest.java b/dspace-oai/src/test/java/org/dspace/xoai/tests/integration/xoai/PipelineTest.java index 0f48824159c2..0f7ffde0bd00 100644 --- a/dspace-oai/src/test/java/org/dspace/xoai/tests/integration/xoai/PipelineTest.java +++ b/dspace-oai/src/test/java/org/dspace/xoai/tests/integration/xoai/PipelineTest.java @@ -13,13 +13,14 @@ import static org.hamcrest.MatcherAssert.assertThat; import java.io.InputStream; +import java.nio.charset.Charset; import javax.xml.transform.TransformerFactory; import javax.xml.transform.stream.StreamSource; import com.lyncode.xoai.util.XSLPipeline; +import org.apache.commons.io.IOUtils; import org.dspace.xoai.tests.support.XmlMatcherBuilder; import org.junit.Test; -import org.parboiled.common.FileUtils; public class PipelineTest { private static TransformerFactory factory = TransformerFactory.newInstance(); @@ -28,9 +29,9 @@ public class PipelineTest { public void pipelineTest() throws Exception { InputStream input = PipelineTest.class.getClassLoader().getResourceAsStream("item.xml"); InputStream xslt = PipelineTest.class.getClassLoader().getResourceAsStream("oai_dc.xsl"); - String output = FileUtils.readAllText(new XSLPipeline(input, true) - .apply(factory.newTemplates(new StreamSource(xslt))) - .getTransformed()); + String output = IOUtils.toString(new XSLPipeline(input, true) + .apply(factory.newTemplates(new StreamSource(xslt))) + .getTransformed(), Charset.defaultCharset()); assertThat(output, oai_dc().withXPath("/oai_dc:dc/dc:title", equalTo("Teste"))); From e53905131049c3a231274ab4cb6732e168609353 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Fri, 15 Nov 2024 11:31:28 -0600 Subject: [PATCH 161/979] Fix OAI using incorrect Java Injection API. --- dspace-api/pom.xml | 1 - dspace-oai/pom.xml | 5 ++--- pom.xml | 6 ++++++ 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/dspace-api/pom.xml b/dspace-api/pom.xml index acac810d6cda..845692686f30 100644 --- a/dspace-api/pom.xml +++ b/dspace-api/pom.xml @@ -719,7 +719,6 @@ jakarta.inject jakarta.inject-api - 2.0.1 diff --git a/dspace-oai/pom.xml b/dspace-oai/pom.xml index 35f12b02a5ce..72c243f55c21 100644 --- a/dspace-oai/pom.xml +++ b/dspace-oai/pom.xml @@ -65,9 +65,8 @@ - javax.inject - javax.inject - 1 + jakarta.inject + jakarta.inject-api diff --git a/pom.xml b/pom.xml index 5be05ca44e36..7bad6fbc2b9d 100644 --- a/pom.xml +++ b/pom.xml @@ -1553,6 +1553,12 @@ jakarta.activation-api 2.1.3 + + + jakarta.inject + jakarta.inject-api + 2.0.1 + jakarta.mail From f248b6395d12e55d4cc68ee1fb4db7b934fe8b42 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Fri, 15 Nov 2024 12:02:20 -0600 Subject: [PATCH 162/979] Log4j cleanup. Remove last traces of log4j v1 (and remove log4j1 bridge to avoid them coming back). Create log4j2 settings for Handle Plugin. --- .../app/bulkedit/MetadataExportSearchIT.java | 5 +- .../dspace/builder/OrcidHistoryBuilder.java | 5 +- dspace-oai/pom.xml | 5 -- .../xoai/filter/ItemsWithBitstreamFilter.java | 4 +- dspace-rdf/pom.xml | 4 -- .../rest/converter/SearchEventConverter.java | 5 +- .../bitstream/BitstreamLinksetProcessor.java | 5 +- .../BitstreamParentItemProcessor.java | 5 +- .../bitstream/BitstreamTypeProcessor.java | 5 +- .../processor/item/ItemAuthorProcessor.java | 5 +- .../item/ItemContentBitstreamsProcessor.java | 5 +- .../item/ItemDescribedbyProcessor.java | 5 +- .../processor/item/ItemLicenseProcessor.java | 5 +- .../processor/item/ItemLinksetProcessor.java | 5 +- .../processor/item/ItemTypeProcessor.java | 5 +- .../service/impl/LinksetServiceImpl.java | 5 +- dspace-sword/pom.xml | 4 -- dspace-swordv2/pom.xml | 4 -- dspace/bin/start-handle-server | 2 +- dspace/bin/start-handle-server.bat | 2 +- dspace/config/log4j-handle-plugin.properties | 34 ------------- dspace/config/log4j2-handle-plugin.xml | 48 +++++++++++++++++++ pom.xml | 25 ---------- 23 files changed, 91 insertions(+), 106 deletions(-) delete mode 100644 dspace/config/log4j-handle-plugin.properties create mode 100644 dspace/config/log4j2-handle-plugin.xml diff --git a/dspace-api/src/test/java/org/dspace/app/bulkedit/MetadataExportSearchIT.java b/dspace-api/src/test/java/org/dspace/app/bulkedit/MetadataExportSearchIT.java index 3a972692efeb..63a87a48f554 100644 --- a/dspace-api/src/test/java/org/dspace/app/bulkedit/MetadataExportSearchIT.java +++ b/dspace-api/src/test/java/org/dspace/app/bulkedit/MetadataExportSearchIT.java @@ -23,7 +23,8 @@ import com.google.common.io.Files; import com.opencsv.CSVReader; import com.opencsv.exceptions.CsvException; -import org.apache.log4j.Logger; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.dspace.AbstractIntegrationTestWithDatabase; import org.dspace.app.launcher.ScriptLauncher; import org.dspace.app.scripts.handler.impl.TestDSpaceRunnableHandler; @@ -51,7 +52,7 @@ public class MetadataExportSearchIT extends AbstractIntegrationTestWithDatabase private Item[] itemsSubject2 = new Item[numberItemsSubject2]; private String filename; private Collection collection; - private Logger logger = Logger.getLogger(MetadataExportSearchIT.class); + private Logger logger = LogManager.getLogger(MetadataExportSearchIT.class); private ConfigurationService configurationService = DSpaceServicesFactory.getInstance().getConfigurationService(); private SearchService searchService; diff --git a/dspace-api/src/test/java/org/dspace/builder/OrcidHistoryBuilder.java b/dspace-api/src/test/java/org/dspace/builder/OrcidHistoryBuilder.java index 199f412f8506..d811d03f5358 100644 --- a/dspace-api/src/test/java/org/dspace/builder/OrcidHistoryBuilder.java +++ b/dspace-api/src/test/java/org/dspace/builder/OrcidHistoryBuilder.java @@ -11,7 +11,8 @@ import java.sql.SQLException; import java.util.Date; -import org.apache.log4j.Logger; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.dspace.content.Item; import org.dspace.core.Context; import org.dspace.orcid.OrcidHistory; @@ -24,7 +25,7 @@ */ public class OrcidHistoryBuilder extends AbstractBuilder { - private static final Logger log = Logger.getLogger(OrcidHistoryBuilder.class); + private static final Logger log = LogManager.getLogger(OrcidHistoryBuilder.class); private OrcidHistory orcidHistory; diff --git a/dspace-oai/pom.xml b/dspace-oai/pom.xml index 72c243f55c21..f6b220baed3d 100644 --- a/dspace-oai/pom.xml +++ b/dspace-oai/pom.xml @@ -123,11 +123,6 @@ org.apache.logging.log4j log4j-core - - - org.apache.logging.log4j - log4j-1.2-api - diff --git a/dspace-oai/src/main/java/org/dspace/xoai/filter/ItemsWithBitstreamFilter.java b/dspace-oai/src/main/java/org/dspace/xoai/filter/ItemsWithBitstreamFilter.java index 3599c5b9e168..9bf1c65dc9d9 100644 --- a/dspace-oai/src/main/java/org/dspace/xoai/filter/ItemsWithBitstreamFilter.java +++ b/dspace-oai/src/main/java/org/dspace/xoai/filter/ItemsWithBitstreamFilter.java @@ -9,8 +9,8 @@ import java.sql.SQLException; -import org.apache.log4j.LogManager; -import org.apache.log4j.Logger; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.dspace.content.Bundle; import org.dspace.content.Item; import org.dspace.handle.factory.HandleServiceFactory; diff --git a/dspace-rdf/pom.xml b/dspace-rdf/pom.xml index 3d11794d86a2..3b3cb17119a1 100644 --- a/dspace-rdf/pom.xml +++ b/dspace-rdf/pom.xml @@ -89,10 +89,6 @@ org.apache.logging.log4j log4j-core - - org.apache.logging.log4j - log4j-web - org.apache.commons diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/SearchEventConverter.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/SearchEventConverter.java index e9786962e0f5..d781d255df11 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/SearchEventConverter.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/SearchEventConverter.java @@ -13,7 +13,8 @@ import java.util.List; import jakarta.servlet.http.HttpServletRequest; -import org.apache.log4j.Logger; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.dspace.app.rest.model.PageRest; import org.dspace.app.rest.model.SearchEventRest; import org.dspace.app.rest.model.SearchResultsRest; @@ -31,7 +32,7 @@ @Component public class SearchEventConverter { /* Log4j logger */ - private static final Logger log = Logger.getLogger(SearchEventConverter.class); + private static final Logger log = LogManager.getLogger(SearchEventConverter.class); @Autowired private ScopeResolver scopeResolver; diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/signposting/processor/bitstream/BitstreamLinksetProcessor.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/signposting/processor/bitstream/BitstreamLinksetProcessor.java index 97eb9f2a546d..349dde7b6dac 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/signposting/processor/bitstream/BitstreamLinksetProcessor.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/signposting/processor/bitstream/BitstreamLinksetProcessor.java @@ -10,7 +10,8 @@ import java.util.List; import jakarta.servlet.http.HttpServletRequest; -import org.apache.log4j.Logger; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.dspace.app.rest.signposting.model.LinksetNode; import org.dspace.app.rest.signposting.model.LinksetRelationType; import org.dspace.content.Bitstream; @@ -25,7 +26,7 @@ */ public class BitstreamLinksetProcessor extends BitstreamSignpostingProcessor { - private static final Logger log = Logger.getLogger(BitstreamLinksetProcessor.class); + private static final Logger log = LogManager.getLogger(BitstreamLinksetProcessor.class); private final BitstreamService bitstreamService; diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/signposting/processor/bitstream/BitstreamParentItemProcessor.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/signposting/processor/bitstream/BitstreamParentItemProcessor.java index 32928dfa8892..c855f06784f7 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/signposting/processor/bitstream/BitstreamParentItemProcessor.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/signposting/processor/bitstream/BitstreamParentItemProcessor.java @@ -10,7 +10,8 @@ import java.util.List; import jakarta.servlet.http.HttpServletRequest; -import org.apache.log4j.Logger; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.dspace.app.rest.signposting.model.LinksetNode; import org.dspace.app.rest.signposting.model.LinksetRelationType; import org.dspace.content.Bitstream; @@ -28,7 +29,7 @@ */ public class BitstreamParentItemProcessor extends BitstreamSignpostingProcessor { - private static final Logger log = Logger.getLogger(BitstreamParentItemProcessor.class); + private static final Logger log = LogManager.getLogger(BitstreamParentItemProcessor.class); private final BitstreamService bitstreamService; diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/signposting/processor/bitstream/BitstreamTypeProcessor.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/signposting/processor/bitstream/BitstreamTypeProcessor.java index 8889a415d327..d0f170b4c55a 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/signposting/processor/bitstream/BitstreamTypeProcessor.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/signposting/processor/bitstream/BitstreamTypeProcessor.java @@ -11,7 +11,8 @@ import jakarta.servlet.http.HttpServletRequest; import org.apache.commons.lang3.StringUtils; -import org.apache.log4j.Logger; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.dspace.app.rest.signposting.model.LinksetNode; import org.dspace.app.rest.signposting.model.LinksetRelationType; import org.dspace.content.Bitstream; @@ -28,7 +29,7 @@ */ public class BitstreamTypeProcessor extends BitstreamSignpostingProcessor { - private static final Logger log = Logger.getLogger(BitstreamTypeProcessor.class); + private static final Logger log = LogManager.getLogger(BitstreamTypeProcessor.class); @Autowired private SimpleMapConverter mapConverterDSpaceToSchemaOrgUri; diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/signposting/processor/item/ItemAuthorProcessor.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/signposting/processor/item/ItemAuthorProcessor.java index f3a9e35198a7..ebc7c46f4d23 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/signposting/processor/item/ItemAuthorProcessor.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/signposting/processor/item/ItemAuthorProcessor.java @@ -16,7 +16,8 @@ import java.util.List; import jakarta.servlet.http.HttpServletRequest; -import org.apache.log4j.Logger; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.dspace.app.rest.signposting.model.LinksetNode; import org.dspace.app.rest.signposting.model.LinksetRelationType; import org.dspace.content.Item; @@ -37,7 +38,7 @@ public class ItemAuthorProcessor extends ItemSignpostingProcessor { /** * log4j category */ - private static final Logger log = Logger.getLogger(ItemAuthorProcessor.class); + private static final Logger log = LogManager.getLogger(ItemAuthorProcessor.class); private final ItemService itemService; diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/signposting/processor/item/ItemContentBitstreamsProcessor.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/signposting/processor/item/ItemContentBitstreamsProcessor.java index 0b91e57f7b3f..65a29c2b6c98 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/signposting/processor/item/ItemContentBitstreamsProcessor.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/signposting/processor/item/ItemContentBitstreamsProcessor.java @@ -11,7 +11,8 @@ import java.util.List; import jakarta.servlet.http.HttpServletRequest; -import org.apache.log4j.Logger; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.dspace.app.rest.signposting.model.LinksetNode; import org.dspace.app.rest.signposting.model.LinksetRelationType; import org.dspace.content.Bitstream; @@ -33,7 +34,7 @@ public class ItemContentBitstreamsProcessor extends ItemSignpostingProcessor { /** * log4j category */ - private static final Logger log = Logger.getLogger(ItemContentBitstreamsProcessor.class); + private static final Logger log = LogManager.getLogger(ItemContentBitstreamsProcessor.class); public ItemContentBitstreamsProcessor(FrontendUrlService frontendUrlService) { super(frontendUrlService); diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/signposting/processor/item/ItemDescribedbyProcessor.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/signposting/processor/item/ItemDescribedbyProcessor.java index 20091e6d0992..a86b98f2e5d0 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/signposting/processor/item/ItemDescribedbyProcessor.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/signposting/processor/item/ItemDescribedbyProcessor.java @@ -10,7 +10,8 @@ import java.util.List; import jakarta.servlet.http.HttpServletRequest; -import org.apache.log4j.Logger; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.dspace.app.rest.signposting.model.LinksetNode; import org.dspace.app.rest.signposting.model.LinksetRelationType; import org.dspace.content.Item; @@ -23,7 +24,7 @@ */ public class ItemDescribedbyProcessor extends ItemSignpostingProcessor { - private static final Logger log = Logger.getLogger(ItemDescribedbyProcessor.class); + private static final Logger log = LogManager.getLogger(ItemDescribedbyProcessor.class); private final ConfigurationService configurationService; diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/signposting/processor/item/ItemLicenseProcessor.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/signposting/processor/item/ItemLicenseProcessor.java index b60ee35d7fe4..6e26d8f1b225 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/signposting/processor/item/ItemLicenseProcessor.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/signposting/processor/item/ItemLicenseProcessor.java @@ -11,7 +11,8 @@ import jakarta.servlet.http.HttpServletRequest; import org.apache.commons.lang3.StringUtils; -import org.apache.log4j.Logger; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.dspace.app.rest.signposting.model.LinksetNode; import org.dspace.app.rest.signposting.model.LinksetRelationType; import org.dspace.content.Item; @@ -25,7 +26,7 @@ */ public class ItemLicenseProcessor extends ItemSignpostingProcessor { - private static final Logger log = Logger.getLogger(ItemLicenseProcessor.class); + private static final Logger log = LogManager.getLogger(ItemLicenseProcessor.class); private final CreativeCommonsService creativeCommonsService = LicenseServiceFactory.getInstance().getCreativeCommonsService(); diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/signposting/processor/item/ItemLinksetProcessor.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/signposting/processor/item/ItemLinksetProcessor.java index 2d09e5616171..4e48caf9594f 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/signposting/processor/item/ItemLinksetProcessor.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/signposting/processor/item/ItemLinksetProcessor.java @@ -10,7 +10,8 @@ import java.util.List; import jakarta.servlet.http.HttpServletRequest; -import org.apache.log4j.Logger; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.dspace.app.rest.signposting.model.LinksetNode; import org.dspace.app.rest.signposting.model.LinksetRelationType; import org.dspace.content.Item; @@ -23,7 +24,7 @@ */ public class ItemLinksetProcessor extends ItemSignpostingProcessor { - private static final Logger log = Logger.getLogger(ItemLinksetProcessor.class); + private static final Logger log = LogManager.getLogger(ItemLinksetProcessor.class); private final ConfigurationService configurationService; diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/signposting/processor/item/ItemTypeProcessor.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/signposting/processor/item/ItemTypeProcessor.java index 49b3612cd92c..f2533a5a9564 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/signposting/processor/item/ItemTypeProcessor.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/signposting/processor/item/ItemTypeProcessor.java @@ -11,7 +11,8 @@ import jakarta.servlet.http.HttpServletRequest; import org.apache.commons.lang3.StringUtils; -import org.apache.log4j.Logger; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.dspace.app.rest.signposting.model.LinksetNode; import org.dspace.app.rest.signposting.model.LinksetRelationType; import org.dspace.content.Item; @@ -27,7 +28,7 @@ */ public class ItemTypeProcessor extends ItemSignpostingProcessor { - private static final Logger log = Logger.getLogger(ItemTypeProcessor.class); + private static final Logger log = LogManager.getLogger(ItemTypeProcessor.class); private static final String ABOUT_PAGE_URI = "https://schema.org/AboutPage"; @Autowired diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/signposting/service/impl/LinksetServiceImpl.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/signposting/service/impl/LinksetServiceImpl.java index 5b28817c9438..42b1c8184957 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/signposting/service/impl/LinksetServiceImpl.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/signposting/service/impl/LinksetServiceImpl.java @@ -13,7 +13,8 @@ import java.util.List; import jakarta.servlet.http.HttpServletRequest; -import org.apache.log4j.Logger; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.dspace.app.rest.security.BitstreamMetadataReadPermissionEvaluatorPlugin; import org.dspace.app.rest.signposting.model.LinksetNode; import org.dspace.app.rest.signposting.processor.bitstream.BitstreamSignpostingProcessor; @@ -37,7 +38,7 @@ @Service public class LinksetServiceImpl implements LinksetService { - private static final Logger log = Logger.getLogger(LinksetServiceImpl.class); + private static final Logger log = LogManager.getLogger(LinksetServiceImpl.class); @Autowired protected ItemService itemService; diff --git a/dspace-sword/pom.xml b/dspace-sword/pom.xml index ac84a674c455..283e61c86afc 100644 --- a/dspace-sword/pom.xml +++ b/dspace-sword/pom.xml @@ -91,10 +91,6 @@ org.apache.logging.log4j log4j-core - - org.apache.logging.log4j - log4j-web - xom diff --git a/dspace-swordv2/pom.xml b/dspace-swordv2/pom.xml index f22acf9dc9e0..af516d566036 100644 --- a/dspace-swordv2/pom.xml +++ b/dspace-swordv2/pom.xml @@ -95,10 +95,6 @@ org.apache.logging.log4j log4j-core - - org.apache.logging.log4j - log4j-web - diff --git a/dspace/bin/start-handle-server b/dspace/bin/start-handle-server index 6d5c9a4b4406..b2af9cf759b8 100755 --- a/dspace/bin/start-handle-server +++ b/dspace/bin/start-handle-server @@ -36,6 +36,6 @@ rm -f $HANDLEDIR/txns/lock # does not support more than one JVM writing to the same rolling log. nohup java $JAVA_OPTS -classpath `$BINDIR/dspace classpath` \ -Ddspace.log.init.disable=true \ - -Dlog4j.configuration=log4j-handle-plugin.properties \ + -Dlog4j2.configurationFile=log4j2-handle-plugin.xml \ net.handle.server.Main $HANDLEDIR \ > $LOGDIR/handle-server.log 2>&1 & diff --git a/dspace/bin/start-handle-server.bat b/dspace/bin/start-handle-server.bat index caf6ff3eef12..8280e2b442f1 100644 --- a/dspace/bin/start-handle-server.bat +++ b/dspace/bin/start-handle-server.bat @@ -51,7 +51,7 @@ echo. echo NOTE: If you want to run the Handle Server as a backend process, re-execute this script echo using the Windows "start" command. For example, "start /B start-handle-server.bat" echo. -java %JAVA_OPTS% -cp "%DSPACE_CLASSPATH%" -Ddspace.log.init.disable=true -Dlog4j.configuration=log4j-handle-plugin.properties net.handle.server.Main %HANDLEDIR% >> "%LOGDIR%/handle-server.log" +java %JAVA_OPTS% -cp "%DSPACE_CLASSPATH%" -Ddspace.log.init.disable=true -Dlog4j2.configurationFile=log4j2-handle-plugin.xml net.handle.server.Main %HANDLEDIR% >> "%LOGDIR%/handle-server.log" REM Clean up DSPACE_CLASSPATH variable set DSPACE_CLASSPATH= diff --git a/dspace/config/log4j-handle-plugin.properties b/dspace/config/log4j-handle-plugin.properties deleted file mode 100644 index 44d39fb1bdf0..000000000000 --- a/dspace/config/log4j-handle-plugin.properties +++ /dev/null @@ -1,34 +0,0 @@ -########################################################################### -# log4j-handle-plugin.properties -# -# This is the log4j configuration file for the embedded DSpace Handle server, -# writing daily rolling logs. We cannot simply write to the same logs, since -# log4j does not support more than one JVM writing to the same rolling log. -########################################################################### - -# VARIABLES: -# The following variables can be used to easily tweak the default log4j settings. -# These variables are used by the log4j config / appenders later in this file. - -# log.dir -# Default log file directory for DSpace. Defaults to the 'log' subdirectory -# under [dspace.dir]. NOTE: The value of 'dspace.dir' will be replaced by -# its value in your configuration when DSpace is deployed (via Ant). -log.dir=${dspace.dir}/log - -# Set root category priority to INFO and its only appender to A1. -log4j.rootCategory=INFO, A1 - -# A1 is set to be a DailyRollingFileAppender. -log4j.appender.A1=org.apache.log4j.DailyRollingFileAppender -log4j.appender.A1.File=${log.dir}/handle-plugin.log -log4j.appender.A1.DatePattern='.'yyyy-MM-dd - -# A1 uses PatternLayout. -log4j.appender.A1.layout=org.apache.log4j.PatternLayout -log4j.appender.A1.layout.ConversionPattern=%d %-5p %c @ %m%n - - -# block passwords from being exposed in Axis logs. -# (DEBUG exposes passwords in Basic Auth) -log4j.logger.org.apache.axis.handlers.http.HTTPAuthHandler=INFO \ No newline at end of file diff --git a/dspace/config/log4j2-handle-plugin.xml b/dspace/config/log4j2-handle-plugin.xml new file mode 100644 index 000000000000..5f72e05c7303 --- /dev/null +++ b/dspace/config/log4j2-handle-plugin.xml @@ -0,0 +1,48 @@ + + + + + + + ${log4j:configParentLocation}/../log + + + + + + + + + yyyy-MM-dd + + + + + + + + + + + + + + diff --git a/pom.xml b/pom.xml index 7bad6fbc2b9d..d84c4d50f28f 100644 --- a/pom.xml +++ b/pom.xml @@ -1600,36 +1600,11 @@ log4j-api ${log4j.version} - - org.apache.logging.log4j - log4j-1.2-api - ${log4j.version} - org.apache.logging.log4j log4j-core ${log4j.version} - - org.apache.logging.log4j - log4j-web - ${log4j.version} - - - org.apache.logging.log4j - log4j-slf4j-impl - ${log4j.version} - - - org.apache.logging.log4j - log4j-jul - ${log4j.version} - - - org.slf4j - jul-to-slf4j - ${slf4j.version} - org.apache.pdfbox From d952fea6a2f6b5dc8a54b4e6ba3deecca2d7a4fe Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Fri, 15 Nov 2024 11:30:14 -0600 Subject: [PATCH 163/979] Remove parboiled-java and minor cleanup of unused OAI dependencies --- dspace-oai/pom.xml | 23 +------------------ .../xoai/app/CCElementItemCompilePlugin.java | 2 +- .../main/java/org/dspace/xoai/app/XOAI.java | 2 +- .../tests/integration/xoai/PipelineTest.java | 9 ++++---- 4 files changed, 8 insertions(+), 28 deletions(-) diff --git a/dspace-oai/pom.xml b/dspace-oai/pom.xml index bfa2c8a27381..2f8dae45b236 100644 --- a/dspace-oai/pom.xml +++ b/dspace-oai/pom.xml @@ -99,22 +99,9 @@ org.springframework.boot spring-boot-starter-web - - - org.parboiled - parboiled-java - - - - org.parboiled - parboiled-java - 1.3.1 - - org.dspace @@ -137,15 +124,7 @@ org.apache.logging.log4j log4j-core - - org.apache.logging.log4j - log4j-web - - - org.apache.logging.log4j - log4j-slf4j-impl - runtime - + org.apache.logging.log4j log4j-1.2-api diff --git a/dspace-oai/src/main/java/org/dspace/xoai/app/CCElementItemCompilePlugin.java b/dspace-oai/src/main/java/org/dspace/xoai/app/CCElementItemCompilePlugin.java index 225d56a4c982..370543029d8b 100644 --- a/dspace-oai/src/main/java/org/dspace/xoai/app/CCElementItemCompilePlugin.java +++ b/dspace-oai/src/main/java/org/dspace/xoai/app/CCElementItemCompilePlugin.java @@ -11,7 +11,7 @@ import com.lyncode.xoai.dataprovider.xml.xoai.Element; import com.lyncode.xoai.dataprovider.xml.xoai.Metadata; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; import org.dspace.content.Item; import org.dspace.core.Context; import org.dspace.license.factory.LicenseServiceFactory; diff --git a/dspace-oai/src/main/java/org/dspace/xoai/app/XOAI.java b/dspace-oai/src/main/java/org/dspace/xoai/app/XOAI.java index 4f842b8e944c..c6aaaa34b539 100644 --- a/dspace-oai/src/main/java/org/dspace/xoai/app/XOAI.java +++ b/dspace-oai/src/main/java/org/dspace/xoai/app/XOAI.java @@ -9,7 +9,7 @@ import static com.lyncode.xoai.dataprovider.core.Granularity.Second; import static java.util.Objects.nonNull; -import static org.apache.commons.lang.StringUtils.EMPTY; +import static org.apache.commons.lang3.StringUtils.EMPTY; import static org.apache.solr.common.params.CursorMarkParams.CURSOR_MARK_PARAM; import static org.apache.solr.common.params.CursorMarkParams.CURSOR_MARK_START; import static org.dspace.xoai.util.ItemUtils.retrieveMetadata; diff --git a/dspace-oai/src/test/java/org/dspace/xoai/tests/integration/xoai/PipelineTest.java b/dspace-oai/src/test/java/org/dspace/xoai/tests/integration/xoai/PipelineTest.java index 0f48824159c2..0f7ffde0bd00 100644 --- a/dspace-oai/src/test/java/org/dspace/xoai/tests/integration/xoai/PipelineTest.java +++ b/dspace-oai/src/test/java/org/dspace/xoai/tests/integration/xoai/PipelineTest.java @@ -13,13 +13,14 @@ import static org.hamcrest.MatcherAssert.assertThat; import java.io.InputStream; +import java.nio.charset.Charset; import javax.xml.transform.TransformerFactory; import javax.xml.transform.stream.StreamSource; import com.lyncode.xoai.util.XSLPipeline; +import org.apache.commons.io.IOUtils; import org.dspace.xoai.tests.support.XmlMatcherBuilder; import org.junit.Test; -import org.parboiled.common.FileUtils; public class PipelineTest { private static TransformerFactory factory = TransformerFactory.newInstance(); @@ -28,9 +29,9 @@ public class PipelineTest { public void pipelineTest() throws Exception { InputStream input = PipelineTest.class.getClassLoader().getResourceAsStream("item.xml"); InputStream xslt = PipelineTest.class.getClassLoader().getResourceAsStream("oai_dc.xsl"); - String output = FileUtils.readAllText(new XSLPipeline(input, true) - .apply(factory.newTemplates(new StreamSource(xslt))) - .getTransformed()); + String output = IOUtils.toString(new XSLPipeline(input, true) + .apply(factory.newTemplates(new StreamSource(xslt))) + .getTransformed(), Charset.defaultCharset()); assertThat(output, oai_dc().withXPath("/oai_dc:dc/dc:title", equalTo("Teste"))); From f3d15e5c04227ceb5425e671189ec4eb5ea42656 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Fri, 15 Nov 2024 12:02:20 -0600 Subject: [PATCH 164/979] Log4j cleanup. Remove last traces of log4j v1 (and remove log4j1 bridge to avoid them coming back). Create log4j2 settings for Handle Plugin. --- .../app/bulkedit/MetadataExportSearchIT.java | 5 +- .../dspace/builder/OrcidHistoryBuilder.java | 5 +- dspace-oai/pom.xml | 5 -- dspace-rdf/pom.xml | 4 -- dspace-rest/pom.xml | 4 -- .../rest/converter/SearchEventConverter.java | 5 +- .../bitstream/BitstreamLinksetProcessor.java | 5 +- .../BitstreamParentItemProcessor.java | 5 +- .../bitstream/BitstreamTypeProcessor.java | 5 +- .../processor/item/ItemAuthorProcessor.java | 5 +- .../item/ItemContentBitstreamsProcessor.java | 5 +- .../item/ItemDescribedbyProcessor.java | 5 +- .../processor/item/ItemLicenseProcessor.java | 5 +- .../processor/item/ItemLinksetProcessor.java | 5 +- .../processor/item/ItemTypeProcessor.java | 5 +- .../service/impl/LinksetServiceImpl.java | 5 +- dspace-sword/pom.xml | 4 -- dspace-swordv2/pom.xml | 4 -- dspace/bin/start-handle-server | 2 +- dspace/bin/start-handle-server.bat | 2 +- dspace/config/log4j-handle-plugin.properties | 34 ------------- dspace/config/log4j2-handle-plugin.xml | 48 +++++++++++++++++++ pom.xml | 25 ---------- 23 files changed, 89 insertions(+), 108 deletions(-) delete mode 100644 dspace/config/log4j-handle-plugin.properties create mode 100644 dspace/config/log4j2-handle-plugin.xml diff --git a/dspace-api/src/test/java/org/dspace/app/bulkedit/MetadataExportSearchIT.java b/dspace-api/src/test/java/org/dspace/app/bulkedit/MetadataExportSearchIT.java index 3a972692efeb..63a87a48f554 100644 --- a/dspace-api/src/test/java/org/dspace/app/bulkedit/MetadataExportSearchIT.java +++ b/dspace-api/src/test/java/org/dspace/app/bulkedit/MetadataExportSearchIT.java @@ -23,7 +23,8 @@ import com.google.common.io.Files; import com.opencsv.CSVReader; import com.opencsv.exceptions.CsvException; -import org.apache.log4j.Logger; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.dspace.AbstractIntegrationTestWithDatabase; import org.dspace.app.launcher.ScriptLauncher; import org.dspace.app.scripts.handler.impl.TestDSpaceRunnableHandler; @@ -51,7 +52,7 @@ public class MetadataExportSearchIT extends AbstractIntegrationTestWithDatabase private Item[] itemsSubject2 = new Item[numberItemsSubject2]; private String filename; private Collection collection; - private Logger logger = Logger.getLogger(MetadataExportSearchIT.class); + private Logger logger = LogManager.getLogger(MetadataExportSearchIT.class); private ConfigurationService configurationService = DSpaceServicesFactory.getInstance().getConfigurationService(); private SearchService searchService; diff --git a/dspace-api/src/test/java/org/dspace/builder/OrcidHistoryBuilder.java b/dspace-api/src/test/java/org/dspace/builder/OrcidHistoryBuilder.java index 199f412f8506..d811d03f5358 100644 --- a/dspace-api/src/test/java/org/dspace/builder/OrcidHistoryBuilder.java +++ b/dspace-api/src/test/java/org/dspace/builder/OrcidHistoryBuilder.java @@ -11,7 +11,8 @@ import java.sql.SQLException; import java.util.Date; -import org.apache.log4j.Logger; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.dspace.content.Item; import org.dspace.core.Context; import org.dspace.orcid.OrcidHistory; @@ -24,7 +25,7 @@ */ public class OrcidHistoryBuilder extends AbstractBuilder { - private static final Logger log = Logger.getLogger(OrcidHistoryBuilder.class); + private static final Logger log = LogManager.getLogger(OrcidHistoryBuilder.class); private OrcidHistory orcidHistory; diff --git a/dspace-oai/pom.xml b/dspace-oai/pom.xml index 2f8dae45b236..f7acdae99292 100644 --- a/dspace-oai/pom.xml +++ b/dspace-oai/pom.xml @@ -124,11 +124,6 @@ org.apache.logging.log4j log4j-core - - - org.apache.logging.log4j - log4j-1.2-api - diff --git a/dspace-rdf/pom.xml b/dspace-rdf/pom.xml index 075f2d7038e8..64d88b63c780 100644 --- a/dspace-rdf/pom.xml +++ b/dspace-rdf/pom.xml @@ -89,10 +89,6 @@ org.apache.logging.log4j log4j-core - - org.apache.logging.log4j - log4j-web - org.apache.commons diff --git a/dspace-rest/pom.xml b/dspace-rest/pom.xml index d1f3b95aafb0..3f4bdaf1d0d8 100644 --- a/dspace-rest/pom.xml +++ b/dspace-rest/pom.xml @@ -185,10 +185,6 @@ org.apache.logging.log4j log4j-core - - org.apache.logging.log4j - log4j-web - org.dspace dspace-services diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/SearchEventConverter.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/SearchEventConverter.java index 126d37ba1ace..978ae2ca9230 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/SearchEventConverter.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/SearchEventConverter.java @@ -13,7 +13,8 @@ import java.util.List; import javax.servlet.http.HttpServletRequest; -import org.apache.log4j.Logger; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.dspace.app.rest.model.PageRest; import org.dspace.app.rest.model.SearchEventRest; import org.dspace.app.rest.model.SearchResultsRest; @@ -31,7 +32,7 @@ @Component public class SearchEventConverter { /* Log4j logger */ - private static final Logger log = Logger.getLogger(SearchEventConverter.class); + private static final Logger log = LogManager.getLogger(SearchEventConverter.class); @Autowired private ScopeResolver scopeResolver; diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/signposting/processor/bitstream/BitstreamLinksetProcessor.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/signposting/processor/bitstream/BitstreamLinksetProcessor.java index c65191cb0749..c9ee193b3536 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/signposting/processor/bitstream/BitstreamLinksetProcessor.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/signposting/processor/bitstream/BitstreamLinksetProcessor.java @@ -10,7 +10,8 @@ import java.util.List; import javax.servlet.http.HttpServletRequest; -import org.apache.log4j.Logger; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.dspace.app.rest.signposting.model.LinksetNode; import org.dspace.app.rest.signposting.model.LinksetRelationType; import org.dspace.content.Bitstream; @@ -25,7 +26,7 @@ */ public class BitstreamLinksetProcessor extends BitstreamSignpostingProcessor { - private static final Logger log = Logger.getLogger(BitstreamLinksetProcessor.class); + private static final Logger log = LogManager.getLogger(BitstreamLinksetProcessor.class); private final BitstreamService bitstreamService; diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/signposting/processor/bitstream/BitstreamParentItemProcessor.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/signposting/processor/bitstream/BitstreamParentItemProcessor.java index 815d7817d4cf..beb318a73481 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/signposting/processor/bitstream/BitstreamParentItemProcessor.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/signposting/processor/bitstream/BitstreamParentItemProcessor.java @@ -10,7 +10,8 @@ import java.util.List; import javax.servlet.http.HttpServletRequest; -import org.apache.log4j.Logger; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.dspace.app.rest.signposting.model.LinksetNode; import org.dspace.app.rest.signposting.model.LinksetRelationType; import org.dspace.content.Bitstream; @@ -28,7 +29,7 @@ */ public class BitstreamParentItemProcessor extends BitstreamSignpostingProcessor { - private static final Logger log = Logger.getLogger(BitstreamParentItemProcessor.class); + private static final Logger log = LogManager.getLogger(BitstreamParentItemProcessor.class); private final BitstreamService bitstreamService; diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/signposting/processor/bitstream/BitstreamTypeProcessor.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/signposting/processor/bitstream/BitstreamTypeProcessor.java index 005a8009836d..1dabf398da9b 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/signposting/processor/bitstream/BitstreamTypeProcessor.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/signposting/processor/bitstream/BitstreamTypeProcessor.java @@ -11,7 +11,8 @@ import javax.servlet.http.HttpServletRequest; import org.apache.commons.lang3.StringUtils; -import org.apache.log4j.Logger; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.dspace.app.rest.signposting.model.LinksetNode; import org.dspace.app.rest.signposting.model.LinksetRelationType; import org.dspace.content.Bitstream; @@ -28,7 +29,7 @@ */ public class BitstreamTypeProcessor extends BitstreamSignpostingProcessor { - private static final Logger log = Logger.getLogger(BitstreamTypeProcessor.class); + private static final Logger log = LogManager.getLogger(BitstreamTypeProcessor.class); @Autowired private SimpleMapConverter mapConverterDSpaceToSchemaOrgUri; diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/signposting/processor/item/ItemAuthorProcessor.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/signposting/processor/item/ItemAuthorProcessor.java index 1bb215c46864..b96a41b00b2b 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/signposting/processor/item/ItemAuthorProcessor.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/signposting/processor/item/ItemAuthorProcessor.java @@ -16,7 +16,8 @@ import java.util.List; import javax.servlet.http.HttpServletRequest; -import org.apache.log4j.Logger; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.dspace.app.rest.signposting.model.LinksetNode; import org.dspace.app.rest.signposting.model.LinksetRelationType; import org.dspace.content.Item; @@ -37,7 +38,7 @@ public class ItemAuthorProcessor extends ItemSignpostingProcessor { /** * log4j category */ - private static final Logger log = Logger.getLogger(ItemAuthorProcessor.class); + private static final Logger log = LogManager.getLogger(ItemAuthorProcessor.class); private final ItemService itemService; diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/signposting/processor/item/ItemContentBitstreamsProcessor.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/signposting/processor/item/ItemContentBitstreamsProcessor.java index 61bf371adbdf..40c3d96cf680 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/signposting/processor/item/ItemContentBitstreamsProcessor.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/signposting/processor/item/ItemContentBitstreamsProcessor.java @@ -11,7 +11,8 @@ import java.util.List; import javax.servlet.http.HttpServletRequest; -import org.apache.log4j.Logger; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.dspace.app.rest.signposting.model.LinksetNode; import org.dspace.app.rest.signposting.model.LinksetRelationType; import org.dspace.content.Bitstream; @@ -33,7 +34,7 @@ public class ItemContentBitstreamsProcessor extends ItemSignpostingProcessor { /** * log4j category */ - private static final Logger log = Logger.getLogger(ItemContentBitstreamsProcessor.class); + private static final Logger log = LogManager.getLogger(ItemContentBitstreamsProcessor.class); public ItemContentBitstreamsProcessor(FrontendUrlService frontendUrlService) { super(frontendUrlService); diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/signposting/processor/item/ItemDescribedbyProcessor.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/signposting/processor/item/ItemDescribedbyProcessor.java index a16770c4d103..62374d7ee830 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/signposting/processor/item/ItemDescribedbyProcessor.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/signposting/processor/item/ItemDescribedbyProcessor.java @@ -10,7 +10,8 @@ import java.util.List; import javax.servlet.http.HttpServletRequest; -import org.apache.log4j.Logger; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.dspace.app.rest.signposting.model.LinksetNode; import org.dspace.app.rest.signposting.model.LinksetRelationType; import org.dspace.content.Item; @@ -23,7 +24,7 @@ */ public class ItemDescribedbyProcessor extends ItemSignpostingProcessor { - private static final Logger log = Logger.getLogger(ItemDescribedbyProcessor.class); + private static final Logger log = LogManager.getLogger(ItemDescribedbyProcessor.class); private final ConfigurationService configurationService; diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/signposting/processor/item/ItemLicenseProcessor.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/signposting/processor/item/ItemLicenseProcessor.java index 1a26fa7695b1..a88e9eba37d2 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/signposting/processor/item/ItemLicenseProcessor.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/signposting/processor/item/ItemLicenseProcessor.java @@ -11,7 +11,8 @@ import javax.servlet.http.HttpServletRequest; import org.apache.commons.lang3.StringUtils; -import org.apache.log4j.Logger; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.dspace.app.rest.signposting.model.LinksetNode; import org.dspace.app.rest.signposting.model.LinksetRelationType; import org.dspace.content.Item; @@ -25,7 +26,7 @@ */ public class ItemLicenseProcessor extends ItemSignpostingProcessor { - private static final Logger log = Logger.getLogger(ItemLicenseProcessor.class); + private static final Logger log = LogManager.getLogger(ItemLicenseProcessor.class); private final CreativeCommonsService creativeCommonsService = LicenseServiceFactory.getInstance().getCreativeCommonsService(); diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/signposting/processor/item/ItemLinksetProcessor.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/signposting/processor/item/ItemLinksetProcessor.java index 9008a28e29a6..1c765047a62d 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/signposting/processor/item/ItemLinksetProcessor.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/signposting/processor/item/ItemLinksetProcessor.java @@ -10,7 +10,8 @@ import java.util.List; import javax.servlet.http.HttpServletRequest; -import org.apache.log4j.Logger; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.dspace.app.rest.signposting.model.LinksetNode; import org.dspace.app.rest.signposting.model.LinksetRelationType; import org.dspace.content.Item; @@ -23,7 +24,7 @@ */ public class ItemLinksetProcessor extends ItemSignpostingProcessor { - private static final Logger log = Logger.getLogger(ItemLinksetProcessor.class); + private static final Logger log = LogManager.getLogger(ItemLinksetProcessor.class); private final ConfigurationService configurationService; diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/signposting/processor/item/ItemTypeProcessor.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/signposting/processor/item/ItemTypeProcessor.java index ddd2da12d59a..6945c33619f7 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/signposting/processor/item/ItemTypeProcessor.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/signposting/processor/item/ItemTypeProcessor.java @@ -11,7 +11,8 @@ import javax.servlet.http.HttpServletRequest; import org.apache.commons.lang3.StringUtils; -import org.apache.log4j.Logger; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.dspace.app.rest.signposting.model.LinksetNode; import org.dspace.app.rest.signposting.model.LinksetRelationType; import org.dspace.content.Item; @@ -27,7 +28,7 @@ */ public class ItemTypeProcessor extends ItemSignpostingProcessor { - private static final Logger log = Logger.getLogger(ItemTypeProcessor.class); + private static final Logger log = LogManager.getLogger(ItemTypeProcessor.class); private static final String ABOUT_PAGE_URI = "https://schema.org/AboutPage"; @Autowired diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/signposting/service/impl/LinksetServiceImpl.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/signposting/service/impl/LinksetServiceImpl.java index 399b7bd1e6b0..de5556173557 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/signposting/service/impl/LinksetServiceImpl.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/signposting/service/impl/LinksetServiceImpl.java @@ -13,7 +13,8 @@ import java.util.List; import javax.servlet.http.HttpServletRequest; -import org.apache.log4j.Logger; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.dspace.app.rest.security.BitstreamMetadataReadPermissionEvaluatorPlugin; import org.dspace.app.rest.signposting.model.LinksetNode; import org.dspace.app.rest.signposting.processor.bitstream.BitstreamSignpostingProcessor; @@ -37,7 +38,7 @@ @Service public class LinksetServiceImpl implements LinksetService { - private static final Logger log = Logger.getLogger(LinksetServiceImpl.class); + private static final Logger log = LogManager.getLogger(LinksetServiceImpl.class); @Autowired protected ItemService itemService; diff --git a/dspace-sword/pom.xml b/dspace-sword/pom.xml index 01aa68dfbfe6..b72bee24db57 100644 --- a/dspace-sword/pom.xml +++ b/dspace-sword/pom.xml @@ -101,10 +101,6 @@ org.apache.logging.log4j log4j-core - - org.apache.logging.log4j - log4j-web - xom diff --git a/dspace-swordv2/pom.xml b/dspace-swordv2/pom.xml index f575898c122a..be9430b4d123 100644 --- a/dspace-swordv2/pom.xml +++ b/dspace-swordv2/pom.xml @@ -97,10 +97,6 @@ org.apache.logging.log4j log4j-core - - org.apache.logging.log4j - log4j-web - + + + + + ${log4j:configParentLocation}/../log + + + + + + + + + yyyy-MM-dd + + + + + + + + + + + + + + diff --git a/pom.xml b/pom.xml index b0d9d1819d3f..9b4dcef37dcf 100644 --- a/pom.xml +++ b/pom.xml @@ -1577,36 +1577,11 @@ log4j-api ${log4j.version} - - org.apache.logging.log4j - log4j-1.2-api - ${log4j.version} - org.apache.logging.log4j log4j-core ${log4j.version} - - org.apache.logging.log4j - log4j-web - ${log4j.version} - - - org.apache.logging.log4j - log4j-slf4j-impl - ${log4j.version} - - - org.apache.logging.log4j - log4j-jul - ${log4j.version} - - - org.slf4j - jul-to-slf4j - ${slf4j.version} - org.apache.pdfbox From 29eefe70b4e5de9da8808ed09bb3cb4c96ffb856 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Wed, 20 Nov 2024 12:45:05 -0600 Subject: [PATCH 165/979] Remove unused dependencies from several modules (cherry picked from commit e27ceb57c1324b7b15931d1ed48414c0316c1d2d) --- dspace-services/pom.xml | 23 ----------------------- dspace-sword/pom.xml | 18 ------------------ dspace/modules/server-boot/pom.xml | 4 ---- dspace/modules/server/pom.xml | 5 ----- pom.xml | 14 +------------- 5 files changed, 1 insertion(+), 63 deletions(-) diff --git a/dspace-services/pom.xml b/dspace-services/pom.xml index 6b2bf191acd5..05b490df7610 100644 --- a/dspace-services/pom.xml +++ b/dspace-services/pom.xml @@ -128,23 +128,6 @@ junit provided - - - org.mortbay.jetty - jetty - 6.1.26 - test - - - org.mortbay.jetty - jetty-servlet-tester - 6.1.26 - test - - - org.apache.commons - commons-collections4 - org.apache.commons commons-configuration2 @@ -164,11 +147,5 @@ jakarta.annotation-api - - org.springframework.boot - spring-boot-starter-log4j2 - ${spring-boot.version} - - diff --git a/dspace-sword/pom.xml b/dspace-sword/pom.xml index 283e61c86afc..ac629cb504e5 100644 --- a/dspace-sword/pom.xml +++ b/dspace-sword/pom.xml @@ -25,15 +25,6 @@
    - - - - org.dspace dspace-api @@ -63,11 +54,6 @@ - - jaxen - jaxen - - org.apache.httpcomponents httpclient @@ -97,10 +83,6 @@ xom 1.3.9 - - commons-io - commons-io - diff --git a/dspace/modules/server-boot/pom.xml b/dspace/modules/server-boot/pom.xml index f156fbeadb06..87bb2d634e69 100644 --- a/dspace/modules/server-boot/pom.xml +++ b/dspace/modules/server-boot/pom.xml @@ -30,10 +30,6 @@ org.dspace dspace-server-webapp - - org.apache.solr - solr-solrj - diff --git a/dspace/modules/server/pom.xml b/dspace/modules/server/pom.xml index c1ce045fe896..f235fcc0279d 100644 --- a/dspace/modules/server/pom.xml +++ b/dspace/modules/server/pom.xml @@ -271,11 +271,6 @@ provided ${spring-boot.version} - - org.apache.solr - solr-solrj - ${solr.client.version} - diff --git a/pom.xml b/pom.xml index d0e720ea3808..f7923b97dedb 100644 --- a/pom.xml +++ b/pom.xml @@ -1163,11 +1163,7 @@ - - org.swordapp - sword-common - 1.1 - + spring-core @@ -1476,14 +1472,6 @@ commons-collections4 4.4 - - - commons-collections - commons-collections - 3.2.2 - org.apache.commons commons-configuration2 From 9f8240987b71f1f37d4f791854b85bcbe870b032 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Wed, 20 Nov 2024 12:45:05 -0600 Subject: [PATCH 166/979] Remove unused dependencies from several modules --- dspace-services/pom.xml | 5 ----- dspace-sword/pom.xml | 18 ------------------ dspace/modules/server/pom.xml | 5 ----- pom.xml | 14 +------------- 4 files changed, 1 insertion(+), 41 deletions(-) diff --git a/dspace-services/pom.xml b/dspace-services/pom.xml index 7c85ff7f0f0f..6504ae2e4b2a 100644 --- a/dspace-services/pom.xml +++ b/dspace-services/pom.xml @@ -131,10 +131,6 @@ 6.1.26 test - - org.apache.commons - commons-collections4 - org.apache.commons commons-configuration2 @@ -159,6 +155,5 @@ spring-boot-starter-log4j2 ${spring-boot.version} - diff --git a/dspace-sword/pom.xml b/dspace-sword/pom.xml index b72bee24db57..12325533c5c5 100644 --- a/dspace-sword/pom.xml +++ b/dspace-sword/pom.xml @@ -25,15 +25,6 @@ - - - - org.dspace dspace-api @@ -68,11 +59,6 @@ - - jaxen - jaxen - - commons-fileupload @@ -107,10 +93,6 @@ xom 1.3.9 - - commons-io - commons-io - diff --git a/dspace/modules/server/pom.xml b/dspace/modules/server/pom.xml index 5134ffb94733..cdbec2f60c8b 100644 --- a/dspace/modules/server/pom.xml +++ b/dspace/modules/server/pom.xml @@ -262,11 +262,6 @@ just adding new jar in the classloader dspace-server-webapp war - - org.apache.solr - solr-solrj - ${solr.client.version} - diff --git a/pom.xml b/pom.xml index 9b4dcef37dcf..c0127724c1d5 100644 --- a/pom.xml +++ b/pom.xml @@ -1179,11 +1179,7 @@ - - org.swordapp - sword-common - 1.1 - + spring-core @@ -1484,14 +1480,6 @@ commons-collections4 4.4 - - - commons-collections - commons-collections - 3.2.2 - org.apache.commons commons-configuration2 From 3ec8862af8888f226c56d177627fa7ac5281bf1c Mon Sep 17 00:00:00 2001 From: Drew Heles Date: Thu, 21 Nov 2024 13:05:19 -0500 Subject: [PATCH 167/979] Update docker files for the 8_x branch --- Dockerfile | 4 ++-- Dockerfile.cli | 4 ++-- Dockerfile.test | 4 ++-- docker-compose-cli.yml | 2 +- docker-compose.yml | 2 +- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Dockerfile b/Dockerfile index d3f85a5bd641..fb9f8ac512c6 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,12 +1,12 @@ # This image will be published as dspace/dspace # See https://github.com/DSpace/DSpace/tree/main/dspace/src/main/docker for usage details # -# - note: default tag for branch: dspace/dspace: dspace/dspace:latest +# - note: default tag for branch: dspace/dspace: dspace/dspace:dspace-8_x # This Dockerfile uses JDK17 by default. # To build with other versions, use "--build-arg JDK_VERSION=[value]" ARG JDK_VERSION=17 -ARG DSPACE_VERSION=latest +ARG DSPACE_VERSION=dspace-8_x # Step 1 - Run Maven Build FROM dspace/dspace-dependencies:${DSPACE_VERSION} AS build diff --git a/Dockerfile.cli b/Dockerfile.cli index 5254d1eb4d69..7de72511e849 100644 --- a/Dockerfile.cli +++ b/Dockerfile.cli @@ -1,12 +1,12 @@ # This image will be published as dspace/dspace-cli # See https://github.com/DSpace/DSpace/tree/main/dspace/src/main/docker for usage details # -# - note: default tag for branch: dspace/dspace-cli: dspace/dspace-cli:latest +# - note: default tag for branch: dspace/dspace-cli: dspace/dspace-cli:dspace-8_x # This Dockerfile uses JDK17 by default. # To build with other versions, use "--build-arg JDK_VERSION=[value]" ARG JDK_VERSION=17 -ARG DSPACE_VERSION=latest +ARG DSPACE_VERSION=dspace-8_x # Step 1 - Run Maven Build FROM dspace/dspace-dependencies:${DSPACE_VERSION} AS build diff --git a/Dockerfile.test b/Dockerfile.test index 218126b17aab..921c57e69919 100644 --- a/Dockerfile.test +++ b/Dockerfile.test @@ -1,14 +1,14 @@ # This image will be published as dspace/dspace # See https://github.com/DSpace/DSpace/tree/main/dspace/src/main/docker for usage details # -# - note: default tag for branch: dspace/dspace: dspace/dspace:latest-test +# - note: default tag for branch: dspace/dspace: dspace/dspace:8_x-test # # This image is meant for TESTING/DEVELOPMENT ONLY as it deploys the old v6 REST API under HTTP (not HTTPS) # This Dockerfile uses JDK17 by default. # To build with other versions, use "--build-arg JDK_VERSION=[value]" ARG JDK_VERSION=17 -ARG DSPACE_VERSION=latest +ARG DSPACE_VERSION=dspace-8_x # Step 1 - Run Maven Build FROM dspace/dspace-dependencies:${DSPACE_VERSION} AS build diff --git a/docker-compose-cli.yml b/docker-compose-cli.yml index 91f89916d208..dd77bdf108ca 100644 --- a/docker-compose-cli.yml +++ b/docker-compose-cli.yml @@ -6,7 +6,7 @@ networks: external: true services: dspace-cli: - image: "${DOCKER_OWNER:-dspace}/dspace-cli:${DSPACE_VER:-latest}" + image: "${DOCKER_OWNER:-dspace}/dspace-cli:${DSPACE_VER:-dspace-8_x}" container_name: dspace-cli build: context: . diff --git a/docker-compose.yml b/docker-compose.yml index 6a930a8d31ec..4322c7173457 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -28,7 +28,7 @@ services: # from the host machine. This IP range MUST correspond to the 'dspacenet' subnet defined above. proxies__P__trusted__P__ipranges: '172.23.0' LOGGING_CONFIG: /dspace/config/log4j2-container.xml - image: "${DOCKER_OWNER:-dspace}/dspace:${DSPACE_VER:-latest-test}" + image: "${DOCKER_OWNER:-dspace}/dspace:${DSPACE_VER:-dspace-8_x-test}" build: context: . dockerfile: Dockerfile.test From b28cc63cbb42985e1a34d1e685c9640fa06a1cce Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Thu, 21 Nov 2024 16:21:22 -0600 Subject: [PATCH 168/979] Ensure log4j-slf4j2-impl bridge exists to forward slf4J logs to log4j. Move that and log4j-core to dspace-api so it is inherited everywhere else. --- dspace-api/pom.xml | 8 ++++++++ dspace-oai/pom.xml | 4 ---- dspace-rdf/pom.xml | 4 ---- dspace-sword/pom.xml | 4 ---- dspace-swordv2/pom.xml | 4 ---- pom.xml | 6 ++++++ 6 files changed, 14 insertions(+), 16 deletions(-) diff --git a/dspace-api/pom.xml b/dspace-api/pom.xml index ada822c1fb12..58bd8106e9af 100644 --- a/dspace-api/pom.xml +++ b/dspace-api/pom.xml @@ -341,6 +341,14 @@ org.apache.logging.log4j log4j-api + + org.apache.logging.log4j + log4j-core + + + org.apache.logging.log4j + log4j-slf4j2-impl + org.hibernate.orm hibernate-core diff --git a/dspace-oai/pom.xml b/dspace-oai/pom.xml index f6b220baed3d..1447fc6e19a0 100644 --- a/dspace-oai/pom.xml +++ b/dspace-oai/pom.xml @@ -119,10 +119,6 @@ org.apache.logging.log4j log4j-api - - org.apache.logging.log4j - log4j-core - diff --git a/dspace-rdf/pom.xml b/dspace-rdf/pom.xml index 3b3cb17119a1..f8e1bb9e36c4 100644 --- a/dspace-rdf/pom.xml +++ b/dspace-rdf/pom.xml @@ -85,10 +85,6 @@ org.apache.logging.log4j log4j-api - - org.apache.logging.log4j - log4j-core - org.apache.commons diff --git a/dspace-sword/pom.xml b/dspace-sword/pom.xml index ac629cb504e5..48a6392d3dba 100644 --- a/dspace-sword/pom.xml +++ b/dspace-sword/pom.xml @@ -73,10 +73,6 @@ org.apache.logging.log4j log4j-api - - org.apache.logging.log4j - log4j-core - xom diff --git a/dspace-swordv2/pom.xml b/dspace-swordv2/pom.xml index af516d566036..d218406a16cc 100644 --- a/dspace-swordv2/pom.xml +++ b/dspace-swordv2/pom.xml @@ -91,10 +91,6 @@ org.apache.logging.log4j log4j-api - - org.apache.logging.log4j - log4j-core - diff --git a/pom.xml b/pom.xml index f7923b97dedb..4bbd60ca9091 100644 --- a/pom.xml +++ b/pom.xml @@ -1593,6 +1593,12 @@ log4j-core ${log4j.version} + + + org.apache.logging.log4j + log4j-slf4j2-impl + ${log4j.version} + org.apache.pdfbox From 03890e6ef3b2ab4d5965f7ba2b7d5cf651b292d9 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Thu, 21 Nov 2024 16:59:30 -0600 Subject: [PATCH 169/979] Remove unused slf4j dependencies --- pom.xml | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/pom.xml b/pom.xml index 4bbd60ca9091..fa37ca9ed4f0 100644 --- a/pom.xml +++ b/pom.xml @@ -1660,16 +1660,6 @@ slf4j-api ${slf4j.version} - - org.slf4j - slf4j-jdk14 - ${slf4j.version} - - - org.slf4j - log4j-over-slf4j - ${slf4j.version} - com.google.errorprone From 9a597891bb319ef5d9ab80cb526d5f0e586c5553 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Fri, 22 Nov 2024 11:04:11 -0600 Subject: [PATCH 170/979] Enable all optional modules/controllers to test their deployment in Spring Boot (cherry picked from commit 98768d6f4fcc5181501d02d77ef5056641ac8cd3) --- .github/workflows/docker.yml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 143b69a2f9fe..816dcd228ca5 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -161,6 +161,15 @@ jobs: env: # Override defaults dspace.server.url because backend starts at http://127.0.0.1:8080 dspace__P__server__P__url: http://127.0.0.1:8080/server + # Enable all optional modules / controllers for this test deployment. + # This helps check for errors in deploying these modules via Spring Boot + iiif__P__enabled: true + ldn__P__enabled: true + oai__P__enabled: true + rdf__P__enabled: true + signposting__P__enabled: true + sword-server__P__enabled: true + swordv2-server__P__enabled: true # If this is a PR, force using "pr-testing" version of all Docker images. Otherwise, if on main branch, use the # "latest" tag. Otherwise, use the branch name. NOTE: the "pr-testing" tag is a temporary tag that we assign to # all PR-built docker images in reusabe-docker-build.yml From 71e17a275817a68a838575c5e92297c58f8a2bb8 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Fri, 22 Nov 2024 13:50:16 -0600 Subject: [PATCH 171/979] Fix syntax error in #10040. Env variables cannot have dashes or periods --- .github/workflows/docker.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 816dcd228ca5..7e7cbc1b07ca 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -168,8 +168,8 @@ jobs: oai__P__enabled: true rdf__P__enabled: true signposting__P__enabled: true - sword-server__P__enabled: true - swordv2-server__P__enabled: true + sword__D__server__P__enabled: true + swordv2__D__server__P__enabled: true # If this is a PR, force using "pr-testing" version of all Docker images. Otherwise, if on main branch, use the # "latest" tag. Otherwise, use the branch name. NOTE: the "pr-testing" tag is a temporary tag that we assign to # all PR-built docker images in reusabe-docker-build.yml From f44dba60cba8cacaa785e9bc5329b569f77a0c1e Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Fri, 22 Nov 2024 11:04:11 -0600 Subject: [PATCH 172/979] Enable all optional modules/controllers to test their deployment in Spring Boot (cherry picked from commit 98768d6f4fcc5181501d02d77ef5056641ac8cd3) --- .github/workflows/docker.yml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 143b69a2f9fe..7013f1e71a09 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -161,6 +161,14 @@ jobs: env: # Override defaults dspace.server.url because backend starts at http://127.0.0.1:8080 dspace__P__server__P__url: http://127.0.0.1:8080/server + # Enable all optional modules / controllers for this test deployment. + # This helps check for errors in deploying these modules via Spring Boot + iiif__P__enabled: true + oai__P__enabled: true + rdf__P__enabled: true + signposting__P__enabled: true + sword-server__P__enabled: true + swordv2-server__P__enabled: true # If this is a PR, force using "pr-testing" version of all Docker images. Otherwise, if on main branch, use the # "latest" tag. Otherwise, use the branch name. NOTE: the "pr-testing" tag is a temporary tag that we assign to # all PR-built docker images in reusabe-docker-build.yml From 66a9782eeefc4e9211a4efdd1a10b1303640415e Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Fri, 22 Nov 2024 13:50:16 -0600 Subject: [PATCH 173/979] Fix syntax error in #10040. Env variables cannot have dashes or periods --- .github/workflows/docker.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 7013f1e71a09..daa940f215a1 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -167,8 +167,8 @@ jobs: oai__P__enabled: true rdf__P__enabled: true signposting__P__enabled: true - sword-server__P__enabled: true - swordv2-server__P__enabled: true + sword__D__server__P__enabled: true + swordv2__D__server__P__enabled: true # If this is a PR, force using "pr-testing" version of all Docker images. Otherwise, if on main branch, use the # "latest" tag. Otherwise, use the branch name. NOTE: the "pr-testing" tag is a temporary tag that we assign to # all PR-built docker images in reusabe-docker-build.yml From cf99694a84714221ed506fa9afd54f262c11c1ce Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Fri, 22 Nov 2024 14:58:08 -0600 Subject: [PATCH 174/979] Fix startup errors for SWORDv2. Requires the log4jv1->v2 bridge to be installed. --- dspace-swordv2/pom.xml | 6 ++++++ pom.xml | 7 +++++++ 2 files changed, 13 insertions(+) diff --git a/dspace-swordv2/pom.xml b/dspace-swordv2/pom.xml index be9430b4d123..7cae05672470 100644 --- a/dspace-swordv2/pom.xml +++ b/dspace-swordv2/pom.xml @@ -97,6 +97,12 @@ org.apache.logging.log4j log4j-core + + + org.apache.logging.log4j + log4j-1.2-api + + + org.apache.logging.log4j + log4j-1.2-api + ${log4j.version} + org.apache.pdfbox From 54a1c75cbc652bbb60e39157450043087c2d822b Mon Sep 17 00:00:00 2001 From: Marie Verdonck Date: Thu, 28 Nov 2024 13:06:23 +0100 Subject: [PATCH 175/979] 109807: ArXiv mapping fix - author/name to dc.contributor.author https://info.arxiv.org/help/api/basics.html#using --- dspace/config/spring/api/arxiv-integration.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dspace/config/spring/api/arxiv-integration.xml b/dspace/config/spring/api/arxiv-integration.xml index e963e73a2055..3f7f408dff40 100644 --- a/dspace/config/spring/api/arxiv-integration.xml +++ b/dspace/config/spring/api/arxiv-integration.xml @@ -94,7 +94,7 @@ - + From 4d1f65c47f7c50bb349c0f13510d57d1d603477c Mon Sep 17 00:00:00 2001 From: "David P. Steelman" Date: Mon, 25 Nov 2024 08:57:37 -0500 Subject: [PATCH 176/979] Adjustments to POM files so that the changes in "modules/server" are incorporated into the JAR generated by "server-boot". This commit adds an "attachClasses" parameter to the "maven-war-plugin" in the "dspace/modules/server/pom.xml", which generates a JAR file that can be referenced in the "dspace/modules/server-boot/pom.xml" (see ) via ``` org.dspace.modules server classes ``` The dependency must be placed *before* the "dspace-server-webapp" dependency, to ensure that it overrides the classes in the "dspace-server-webapp" module. In the "server-boot.jar", the CLASSPATH is determined by the order of JARs in the "BOOT-INF/classpath.idx", which is generated based on the order of dependencies in the POM (see https://stackoverflow.com/a/67997782). The root "pom.xml" file was modified to provide the version for "modules/server" JAR file, in keeping with how the versions of other JAR files are specified. (cherry picked from commit 5bf1f26ebaacfc211d8dac3766ae9e4d7a31d4cd) --- dspace/modules/server-boot/pom.xml | 5 +++++ dspace/modules/server/pom.xml | 1 + pom.xml | 6 ++++++ 3 files changed, 12 insertions(+) diff --git a/dspace/modules/server-boot/pom.xml b/dspace/modules/server-boot/pom.xml index 87bb2d634e69..f2f79bb73869 100644 --- a/dspace/modules/server-boot/pom.xml +++ b/dspace/modules/server-boot/pom.xml @@ -26,6 +26,11 @@ org.dspace.modules additions + + org.dspace.modules + server + classes + org.dspace dspace-server-webapp diff --git a/dspace/modules/server/pom.xml b/dspace/modules/server/pom.xml index f235fcc0279d..916689516a1c 100644 --- a/dspace/modules/server/pom.xml +++ b/dspace/modules/server/pom.xml @@ -63,6 +63,7 @@ maven-war-plugin false + true true + + @@ -1050,24 +1052,6 @@ Available - - Collected - - - Copyrighted - - - Created - - - Submitted - - - Updated - - - Valid - From 04953b94d919bf5e0135aa3311b70767cba20160 Mon Sep 17 00:00:00 2001 From: Nona Luypaert Date: Wed, 11 Dec 2024 21:39:08 +0100 Subject: [PATCH 214/979] 121971: #9715 - Only dc.date.issued should have date types Accepted and Issued --- dspace/config/crosswalks/oai/metadataFormats/oai_openaire.xsl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dspace/config/crosswalks/oai/metadataFormats/oai_openaire.xsl b/dspace/config/crosswalks/oai/metadataFormats/oai_openaire.xsl index b6abd9daa056..f92367646d1f 100644 --- a/dspace/config/crosswalks/oai/metadataFormats/oai_openaire.xsl +++ b/dspace/config/crosswalks/oai/metadataFormats/oai_openaire.xsl @@ -731,7 +731,7 @@ From 31c79500ceec1c8e36228195b802a289ac9c30c9 Mon Sep 17 00:00:00 2001 From: Nona Luypaert Date: Wed, 11 Dec 2024 21:49:37 +0100 Subject: [PATCH 215/979] 121971: #9716 - Only dc.date.embargo should have date type Available --- dspace/config/crosswalks/oai/metadataFormats/oai_openaire.xsl | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/dspace/config/crosswalks/oai/metadataFormats/oai_openaire.xsl b/dspace/config/crosswalks/oai/metadataFormats/oai_openaire.xsl index f92367646d1f..7b141e48b3a0 100644 --- a/dspace/config/crosswalks/oai/metadataFormats/oai_openaire.xsl +++ b/dspace/config/crosswalks/oai/metadataFormats/oai_openaire.xsl @@ -1049,7 +1049,9 @@ - + + + Available From 52e5b35c0653afc17ebd47ce1272549ddf205469 Mon Sep 17 00:00:00 2001 From: Nona Luypaert Date: Thu, 12 Dec 2024 09:16:21 +0100 Subject: [PATCH 216/979] 121971: #9664 - Make cclicense step required in openaire submission form --- dspace/config/item-submission.xml | 4 ++++ dspace/config/submission-forms.xml | 23 ----------------------- 2 files changed, 4 insertions(+), 23 deletions(-) diff --git a/dspace/config/item-submission.xml b/dspace/config/item-submission.xml index a6cd49bdf1e8..866426afd61d 100644 --- a/dspace/config/item-submission.xml +++ b/dspace/config/item-submission.xml @@ -379,6 +379,10 @@ + + + + diff --git a/dspace/config/submission-forms.xml b/dspace/config/submission-forms.xml index 39a4778356c0..13c8fc208f6e 100644 --- a/dspace/config/submission-forms.xml +++ b/dspace/config/submission-forms.xml @@ -1133,29 +1133,6 @@ - - - dc - rights - false - - dropdown - Select the access type of the resource. - - - - - - oaire - license - condition - false - - dropdown - Select the license of the resource. - - - dc From e5401685944b6135795462748c791ed7ba7bab7d Mon Sep 17 00:00:00 2001 From: Nona Luypaert Date: Thu, 12 Dec 2024 09:18:40 +0100 Subject: [PATCH 217/979] 121971: #9867 - Remove objectType attribute from openaire crosswalk --- .../crosswalks/oai/metadataFormats/oai_openaire.xsl | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/dspace/config/crosswalks/oai/metadataFormats/oai_openaire.xsl b/dspace/config/crosswalks/oai/metadataFormats/oai_openaire.xsl index 7b141e48b3a0..6c6c369071a6 100644 --- a/dspace/config/crosswalks/oai/metadataFormats/oai_openaire.xsl +++ b/dspace/config/crosswalks/oai/metadataFormats/oai_openaire.xsl @@ -888,13 +888,13 @@ - + + fulltext - + other - + --> From 8ec4ae45b887645ab499daf738193bbabe3014cb Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Wed, 4 Dec 2024 13:44:44 -0600 Subject: [PATCH 218/979] Add Docker registry to all scripts. Allow it to be configurable for DSpace images (only). Other minor Dockerfile cleanup --- Dockerfile | 16 ++++++++++------ Dockerfile.cli | 16 ++++++++++------ Dockerfile.dependencies | 2 +- Dockerfile.test | 16 ++++++++++------ docker-compose-cli.yml | 2 +- docker-compose.yml | 6 +++--- .../dspace-postgres-pgcrypto-curl/Dockerfile | 2 +- .../docker/dspace-postgres-pgcrypto/Dockerfile | 2 +- .../src/main/docker/dspace-shibboleth/Dockerfile | 2 +- dspace/src/main/docker/dspace-solr/Dockerfile | 2 +- 10 files changed, 39 insertions(+), 27 deletions(-) diff --git a/Dockerfile b/Dockerfile index fb9f8ac512c6..0b844a1b26c2 100644 --- a/Dockerfile +++ b/Dockerfile @@ -6,10 +6,14 @@ # This Dockerfile uses JDK17 by default. # To build with other versions, use "--build-arg JDK_VERSION=[value]" ARG JDK_VERSION=17 +# The Docker version tag to build from ARG DSPACE_VERSION=dspace-8_x +# The Docker registry to use for DSpace images. Defaults to "docker.io" +# NOTE: non-DSpace images are hardcoded to use "docker.io" and are not impacted by this build argument +ARG DOCKER_REGISTRY=docker.io # Step 1 - Run Maven Build -FROM dspace/dspace-dependencies:${DSPACE_VERSION} AS build +FROM ${DOCKER_REGISTRY}/dspace/dspace-dependencies:${DSPACE_VERSION} AS build ARG TARGET_DIR=dspace-installer WORKDIR /app # The dspace-installer directory will be written to /install @@ -31,15 +35,15 @@ RUN mvn --no-transfer-progress package ${MAVEN_FLAGS} && \ RUN rm -rf /install/webapps/server/ # Step 2 - Run Ant Deploy -FROM eclipse-temurin:${JDK_VERSION} AS ant_build +FROM docker.io/eclipse-temurin:${JDK_VERSION} AS ant_build ARG TARGET_DIR=dspace-installer # COPY the /install directory from 'build' container to /dspace-src in this container COPY --from=build /install /dspace-src 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 +ENV ANT_VERSION=1.10.13 +ENV ANT_HOME=/tmp/ant-$ANT_VERSION +ENV PATH=$ANT_HOME/bin:$PATH # Need wget to install ant RUN apt-get update \ && apt-get install -y --no-install-recommends wget \ @@ -52,7 +56,7 @@ RUN mkdir $ANT_HOME && \ RUN ant init_installation update_configs update_code update_webapps # Step 3 - Start up DSpace via Runnable JAR -FROM eclipse-temurin:${JDK_VERSION} +FROM docker.io/eclipse-temurin:${JDK_VERSION} # NOTE: DSPACE_INSTALL must align with the "dspace.dir" default configuration. ENV DSPACE_INSTALL=/dspace # Copy the /dspace directory from 'ant_build' container to /dspace in this container diff --git a/Dockerfile.cli b/Dockerfile.cli index 7de72511e849..b0b056dfc99e 100644 --- a/Dockerfile.cli +++ b/Dockerfile.cli @@ -6,10 +6,14 @@ # This Dockerfile uses JDK17 by default. # To build with other versions, use "--build-arg JDK_VERSION=[value]" ARG JDK_VERSION=17 +# The Docker version tag to build from ARG DSPACE_VERSION=dspace-8_x +# The Docker registry to use for DSpace images. Defaults to "docker.io" +# NOTE: non-DSpace images are hardcoded to use "docker.io" and are not impacted by this build argument +ARG DOCKER_REGISTRY=docker.io # Step 1 - Run Maven Build -FROM dspace/dspace-dependencies:${DSPACE_VERSION} AS build +FROM ${DOCKER_REGISTRY}/dspace/dspace-dependencies:${DSPACE_VERSION} AS build ARG TARGET_DIR=dspace-installer WORKDIR /app # The dspace-installer directory will be written to /install @@ -25,15 +29,15 @@ RUN mvn --no-transfer-progress package && \ mvn clean # Step 2 - Run Ant Deploy -FROM eclipse-temurin:${JDK_VERSION} AS ant_build +FROM docker.io/eclipse-temurin:${JDK_VERSION} AS ant_build ARG TARGET_DIR=dspace-installer # COPY the /install directory from 'build' container to /dspace-src in this container COPY --from=build /install /dspace-src 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 +ENV ANT_VERSION=1.10.13 +ENV ANT_HOME=/tmp/ant-$ANT_VERSION +ENV PATH=$ANT_HOME/bin:$PATH # Need wget to install ant RUN apt-get update \ && apt-get install -y --no-install-recommends wget \ @@ -46,7 +50,7 @@ RUN mkdir $ANT_HOME && \ RUN ant init_installation update_configs update_code # Step 3 - Run jdk -FROM eclipse-temurin:${JDK_VERSION} +FROM docker.io/eclipse-temurin:${JDK_VERSION} # NOTE: DSPACE_INSTALL must align with the "dspace.dir" default configuration. ENV DSPACE_INSTALL=/dspace # Copy the /dspace directory from 'ant_build' container to /dspace in this container diff --git a/Dockerfile.dependencies b/Dockerfile.dependencies index f3bf1f833205..34bfbc3b8c78 100644 --- a/Dockerfile.dependencies +++ b/Dockerfile.dependencies @@ -7,7 +7,7 @@ ARG JDK_VERSION=17 # Step 1 - Run Maven Build -FROM maven:3-eclipse-temurin-${JDK_VERSION} AS build +FROM docker.io/maven:3-eclipse-temurin-${JDK_VERSION} AS build ARG TARGET_DIR=dspace-installer WORKDIR /app # Create the 'dspace' user account & home directory diff --git a/Dockerfile.test b/Dockerfile.test index 921c57e69919..828d61e74708 100644 --- a/Dockerfile.test +++ b/Dockerfile.test @@ -8,10 +8,14 @@ # This Dockerfile uses JDK17 by default. # To build with other versions, use "--build-arg JDK_VERSION=[value]" ARG JDK_VERSION=17 +# The Docker version tag to build from ARG DSPACE_VERSION=dspace-8_x +# The Docker registry to use for DSpace images. Defaults to "docker.io" +# NOTE: non-DSpace images are hardcoded to use "docker.io" and are not impacted by this build argument +ARG DOCKER_REGISTRY=docker.io # Step 1 - Run Maven Build -FROM dspace/dspace-dependencies:${DSPACE_VERSION} AS build +FROM ${DOCKER_REGISTRY}/dspace/dspace-dependencies:${DSPACE_VERSION} AS build ARG TARGET_DIR=dspace-installer WORKDIR /app # The dspace-installer directory will be written to /install @@ -30,15 +34,15 @@ RUN mvn --no-transfer-progress package && \ RUN rm -rf /install/webapps/server/ # Step 2 - Run Ant Deploy -FROM eclipse-temurin:${JDK_VERSION} AS ant_build +FROM docker.io/eclipse-temurin:${JDK_VERSION} AS ant_build ARG TARGET_DIR=dspace-installer # COPY the /install directory from 'build' container to /dspace-src in this container COPY --from=build /install /dspace-src 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 +ENV ANT_VERSION=1.10.12 +ENV ANT_HOME=/tmp/ant-$ANT_VERSION +ENV PATH=$ANT_HOME/bin:$PATH # Need wget to install ant RUN apt-get update \ && apt-get install -y --no-install-recommends wget \ @@ -51,7 +55,7 @@ RUN mkdir $ANT_HOME && \ RUN ant init_installation update_configs update_code update_webapps # Step 3 - Start up DSpace via Runnable JAR -FROM eclipse-temurin:${JDK_VERSION} +FROM docker.io/eclipse-temurin:${JDK_VERSION} # NOTE: DSPACE_INSTALL must align with the "dspace.dir" default configuration. ENV DSPACE_INSTALL=/dspace # Copy the /dspace directory from 'ant_build' container to /dspace in this container diff --git a/docker-compose-cli.yml b/docker-compose-cli.yml index dd77bdf108ca..5d15845fa8df 100644 --- a/docker-compose-cli.yml +++ b/docker-compose-cli.yml @@ -6,7 +6,7 @@ networks: external: true services: dspace-cli: - image: "${DOCKER_OWNER:-dspace}/dspace-cli:${DSPACE_VER:-dspace-8_x}" + image: "${DOCKER_REGISTRY:-docker.io}/${DOCKER_OWNER:-dspace}/dspace-cli:${DSPACE_VER:-dspace-8_x}" container_name: dspace-cli build: context: . diff --git a/docker-compose.yml b/docker-compose.yml index 8886443824dd..9177ff4bd977 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -28,7 +28,7 @@ services: # from the host machine. This IP range MUST correspond to the 'dspacenet' subnet defined above. proxies__P__trusted__P__ipranges: '172.23.0' LOGGING_CONFIG: /dspace/config/log4j2-container.xml - image: "${DOCKER_OWNER:-dspace}/dspace:${DSPACE_VER:-dspace-8_x-test}" + image: "${DOCKER_REGISTRY:-docker.io}/${DOCKER_OWNER:-dspace}/dspace:${DSPACE_VER:-dspace-8_x-test}" build: context: . dockerfile: Dockerfile.test @@ -64,7 +64,7 @@ services: dspacedb: container_name: dspacedb # Uses a custom Postgres image with pgcrypto installed - image: "${DOCKER_OWNER:-dspace}/dspace-postgres-pgcrypto:${DSPACE_VER:-dspace-8_x}" + image: "${DOCKER_REGISTRY:-docker.io}/${DOCKER_OWNER:-dspace}/dspace-postgres-pgcrypto:${DSPACE_VER:-dspace-8_x}" build: # Must build out of subdirectory to have access to install script for pgcrypto context: ./dspace/src/main/docker/dspace-postgres-pgcrypto/ @@ -84,7 +84,7 @@ services: # DSpace Solr container dspacesolr: container_name: dspacesolr - image: "${DOCKER_OWNER:-dspace}/dspace-solr:${DSPACE_VER:-dspace-8_x}" + image: "${DOCKER_REGISTRY:-docker.io}/${DOCKER_OWNER:-dspace}/dspace-solr:${DSPACE_VER:-dspace-8_x}" build: context: ./dspace/src/main/docker/dspace-solr/ # Provide path to Solr configs necessary to build Docker image diff --git a/dspace/src/main/docker/dspace-postgres-pgcrypto-curl/Dockerfile b/dspace/src/main/docker/dspace-postgres-pgcrypto-curl/Dockerfile index aabf87df3eda..791242f5bdc7 100644 --- a/dspace/src/main/docker/dspace-postgres-pgcrypto-curl/Dockerfile +++ b/dspace/src/main/docker/dspace-postgres-pgcrypto-curl/Dockerfile @@ -13,7 +13,7 @@ ARG POSTGRES_VERSION=15 ARG POSTGRES_PASSWORD=dspace -FROM postgres:${POSTGRES_VERSION} +FROM docker.io/postgres:${POSTGRES_VERSION} ENV POSTGRES_DB dspace ENV POSTGRES_USER dspace diff --git a/dspace/src/main/docker/dspace-postgres-pgcrypto/Dockerfile b/dspace/src/main/docker/dspace-postgres-pgcrypto/Dockerfile index 2298cd4e76ea..116d2dbcda31 100644 --- a/dspace/src/main/docker/dspace-postgres-pgcrypto/Dockerfile +++ b/dspace/src/main/docker/dspace-postgres-pgcrypto/Dockerfile @@ -13,7 +13,7 @@ ARG POSTGRES_VERSION=15 ARG POSTGRES_PASSWORD=dspace -FROM postgres:${POSTGRES_VERSION} +FROM docker.io/postgres:${POSTGRES_VERSION} ENV POSTGRES_DB dspace ENV POSTGRES_USER dspace diff --git a/dspace/src/main/docker/dspace-shibboleth/Dockerfile b/dspace/src/main/docker/dspace-shibboleth/Dockerfile index 79f2921bd7d6..25fb510e3d39 100644 --- a/dspace/src/main/docker/dspace-shibboleth/Dockerfile +++ b/dspace/src/main/docker/dspace-shibboleth/Dockerfile @@ -10,7 +10,7 @@ # Build from Ubuntu as it has easy Apache tooling (e.g. a2enmod script is debian only). # Apache & mod_shib are required for DSpace to act as an SP # See also https://wiki.lyrasis.org/display/DSDOC7x/Authentication+Plugins#AuthenticationPlugins-ShibbolethAuthentication -FROM ubuntu:20.04 +FROM docker.io/ubuntu:20.04 # Apache ENVs (default values) ENV APACHE_RUN_USER www-data diff --git a/dspace/src/main/docker/dspace-solr/Dockerfile b/dspace/src/main/docker/dspace-solr/Dockerfile index 289949922cc1..241a4345b305 100644 --- a/dspace/src/main/docker/dspace-solr/Dockerfile +++ b/dspace/src/main/docker/dspace-solr/Dockerfile @@ -12,7 +12,7 @@ ARG SOLR_VERSION=8.11 -FROM solr:${SOLR_VERSION}-slim +FROM docker.io/solr:${SOLR_VERSION}-slim ENV AUTHORITY_CONFIGSET_PATH=/opt/solr/server/solr/configsets/authority/conf \ OAI_CONFIGSET_PATH=/opt/solr/server/solr/configsets/oai/conf \ From dee2fae46c419cb86a32753410cd0daecffa0946 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Wed, 4 Dec 2024 13:45:33 -0600 Subject: [PATCH 219/979] Minor Dockerfile cleanup. Use new syntax for ENV variables --- .../docker/dspace-postgres-pgcrypto-curl/Dockerfile | 6 +++--- .../main/docker/dspace-postgres-pgcrypto/Dockerfile | 6 +++--- dspace/src/main/docker/dspace-shibboleth/Dockerfile | 12 ++++++------ 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/dspace/src/main/docker/dspace-postgres-pgcrypto-curl/Dockerfile b/dspace/src/main/docker/dspace-postgres-pgcrypto-curl/Dockerfile index 791242f5bdc7..f314fdb79e80 100644 --- a/dspace/src/main/docker/dspace-postgres-pgcrypto-curl/Dockerfile +++ b/dspace/src/main/docker/dspace-postgres-pgcrypto-curl/Dockerfile @@ -15,9 +15,9 @@ ARG POSTGRES_PASSWORD=dspace FROM docker.io/postgres:${POSTGRES_VERSION} -ENV POSTGRES_DB dspace -ENV POSTGRES_USER dspace -ENV POSTGRES_PASSWORD ${POSTGRES_PASSWORD} +ENV POSTGRES_DB=dspace +ENV POSTGRES_USER=dspace +ENV POSTGRES_PASSWORD=${POSTGRES_PASSWORD} # Install curl which is necessary to load SQL file RUN apt-get update && apt-get install -y curl && rm -rf /var/lib/apt/lists/* diff --git a/dspace/src/main/docker/dspace-postgres-pgcrypto/Dockerfile b/dspace/src/main/docker/dspace-postgres-pgcrypto/Dockerfile index 116d2dbcda31..4f639f361024 100644 --- a/dspace/src/main/docker/dspace-postgres-pgcrypto/Dockerfile +++ b/dspace/src/main/docker/dspace-postgres-pgcrypto/Dockerfile @@ -15,9 +15,9 @@ ARG POSTGRES_PASSWORD=dspace FROM docker.io/postgres:${POSTGRES_VERSION} -ENV POSTGRES_DB dspace -ENV POSTGRES_USER dspace -ENV POSTGRES_PASSWORD ${POSTGRES_PASSWORD} +ENV POSTGRES_DB=dspace +ENV POSTGRES_USER=dspace +ENV POSTGRES_PASSWORD=${POSTGRES_PASSWORD} # Copy over script which will initialize database and install pgcrypto extension COPY install-pgcrypto.sh /docker-entrypoint-initdb.d/ diff --git a/dspace/src/main/docker/dspace-shibboleth/Dockerfile b/dspace/src/main/docker/dspace-shibboleth/Dockerfile index 25fb510e3d39..15a436d7b6f7 100644 --- a/dspace/src/main/docker/dspace-shibboleth/Dockerfile +++ b/dspace/src/main/docker/dspace-shibboleth/Dockerfile @@ -13,12 +13,12 @@ FROM docker.io/ubuntu:20.04 # Apache ENVs (default values) -ENV APACHE_RUN_USER www-data -ENV APACHE_RUN_GROUP www-data -ENV APACHE_LOCK_DIR /var/lock/apache2 -ENV APACHE_LOG_DIR /var/log/apache2 -ENV APACHE_PID_FILE /var/run/apache2/apache2.pid -ENV APACHE_SERVER_NAME localhost +ENV APACHE_RUN_USER=www-data +ENV APACHE_RUN_GROUP=www-data +ENV APACHE_LOCK_DIR=/var/lock/apache2 +ENV APACHE_LOG_DIR=/var/log/apache2 +ENV APACHE_PID_FILE=/var/run/apache2/apache2.pid +ENV APACHE_SERVER_NAME=localhost # Ensure Apache2, mod_shib & shibboleth daemon are installed. # Also install ssl-cert to provide a local SSL cert for use with Apache From a6d2c4897b46062cca59add6946eb7a86c648585 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Wed, 4 Dec 2024 13:44:44 -0600 Subject: [PATCH 220/979] Add Docker registry to all scripts. Allow it to be configurable for DSpace images (only). Other minor Dockerfile cleanup --- Dockerfile | 17 +++++++++++------ Dockerfile.cli | 17 +++++++++++------ Dockerfile.dependencies | 2 +- Dockerfile.test | 17 +++++++++++------ docker-compose-cli.yml | 2 +- docker-compose.yml | 6 +++--- .../dspace-postgres-pgcrypto-curl/Dockerfile | 2 +- .../docker/dspace-postgres-pgcrypto/Dockerfile | 2 +- .../main/docker/dspace-shibboleth/Dockerfile | 2 +- dspace/src/main/docker/dspace-solr/Dockerfile | 2 +- 10 files changed, 42 insertions(+), 27 deletions(-) diff --git a/Dockerfile b/Dockerfile index adc5d6125f0a..ff35f2a2e3a7 100644 --- a/Dockerfile +++ b/Dockerfile @@ -6,9 +6,14 @@ # This Dockerfile uses JDK11 by default, but has also been tested with JDK17. # To build with JDK17, use "--build-arg JDK_VERSION=17" ARG JDK_VERSION=11 +# The Docker version tag to build from +ARG DSPACE_VERSION=dspace-7_x +# The Docker registry to use for DSpace images. Defaults to "docker.io" +# NOTE: non-DSpace images are hardcoded to use "docker.io" and are not impacted by this build argument +ARG DOCKER_REGISTRY=docker.io # Step 1 - Run Maven Build -FROM dspace/dspace-dependencies:dspace-7_x AS build +FROM ${DOCKER_REGISTRY}/dspace/dspace-dependencies:${DSPACE_VERSION} AS build ARG TARGET_DIR=dspace-installer WORKDIR /app # The dspace-installer directory will be written to /install @@ -28,15 +33,15 @@ RUN mvn --no-transfer-progress package ${MAVEN_FLAGS} && \ mvn clean # Step 2 - Run Ant Deploy -FROM eclipse-temurin:${JDK_VERSION} AS ant_build +FROM docker.io/eclipse-temurin:${JDK_VERSION} AS ant_build ARG TARGET_DIR=dspace-installer # COPY the /install directory from 'build' container to /dspace-src in this container COPY --from=build /install /dspace-src 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 +ENV ANT_VERSION=1.10.13 +ENV ANT_HOME=/tmp/ant-$ANT_VERSION +ENV PATH=$ANT_HOME/bin:$PATH # Need wget to install ant RUN apt-get update \ && apt-get install -y --no-install-recommends wget \ @@ -50,7 +55,7 @@ RUN ant init_installation update_configs update_code update_webapps # Step 3 - Run tomcat # Create a new tomcat image that does not retain the the build directory contents -FROM tomcat:9-jdk${JDK_VERSION} +FROM docker.io/tomcat:9-jdk${JDK_VERSION} # NOTE: DSPACE_INSTALL must align with the "dspace.dir" default configuration. ENV DSPACE_INSTALL=/dspace # Copy the /dspace directory from 'ant_build' container to /dspace in this container diff --git a/Dockerfile.cli b/Dockerfile.cli index 8a69b32e2dc6..be03e8922b2c 100644 --- a/Dockerfile.cli +++ b/Dockerfile.cli @@ -6,9 +6,14 @@ # This Dockerfile uses JDK11 by default, but has also been tested with JDK17. # To build with JDK17, use "--build-arg JDK_VERSION=17" ARG JDK_VERSION=11 +# The Docker version tag to build from +ARG DSPACE_VERSION=dspace-7_x +# The Docker registry to use for DSpace images. Defaults to "docker.io" +# NOTE: non-DSpace images are hardcoded to use "docker.io" and are not impacted by this build argument +ARG DOCKER_REGISTRY=docker.io # Step 1 - Run Maven Build -FROM dspace/dspace-dependencies:dspace-7_x AS build +FROM ${DOCKER_REGISTRY}/dspace/dspace-dependencies:${DSPACE_VERSION} AS build ARG TARGET_DIR=dspace-installer WORKDIR /app # The dspace-installer directory will be written to /install @@ -24,15 +29,15 @@ RUN mvn --no-transfer-progress package && \ mvn clean # Step 2 - Run Ant Deploy -FROM eclipse-temurin:${JDK_VERSION} AS ant_build +FROM docker.io/eclipse-temurin:${JDK_VERSION} AS ant_build ARG TARGET_DIR=dspace-installer # COPY the /install directory from 'build' container to /dspace-src in this container COPY --from=build /install /dspace-src 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 +ENV ANT_VERSION=1.10.13 +ENV ANT_HOME=/tmp/ant-$ANT_VERSION +ENV PATH=$ANT_HOME/bin:$PATH # Need wget to install ant RUN apt-get update \ && apt-get install -y --no-install-recommends wget \ @@ -45,7 +50,7 @@ RUN mkdir $ANT_HOME && \ RUN ant init_installation update_configs update_code # Step 3 - Run jdk -FROM eclipse-temurin:${JDK_VERSION} +FROM docker.io/eclipse-temurin:${JDK_VERSION} # NOTE: DSPACE_INSTALL must align with the "dspace.dir" default configuration. ENV DSPACE_INSTALL=/dspace # Copy the /dspace directory from 'ant_build' container to /dspace in this container diff --git a/Dockerfile.dependencies b/Dockerfile.dependencies index 123206ea5887..794dfa9a66a8 100644 --- a/Dockerfile.dependencies +++ b/Dockerfile.dependencies @@ -7,7 +7,7 @@ ARG JDK_VERSION=11 # Step 1 - Run Maven Build -FROM maven:3-eclipse-temurin-${JDK_VERSION} AS build +FROM docker.io/maven:3-eclipse-temurin-${JDK_VERSION} AS build ARG TARGET_DIR=dspace-installer WORKDIR /app # Create the 'dspace' user account & home directory diff --git a/Dockerfile.test b/Dockerfile.test index d88699ca52ab..08b6b3018b80 100644 --- a/Dockerfile.test +++ b/Dockerfile.test @@ -8,9 +8,14 @@ # This Dockerfile uses JDK11 by default, but has also been tested with JDK17. # To build with JDK17, use "--build-arg JDK_VERSION=17" ARG JDK_VERSION=11 +# The Docker version tag to build from +ARG DSPACE_VERSION=dspace-7_x +# The Docker registry to use for DSpace images. Defaults to "docker.io" +# NOTE: non-DSpace images are hardcoded to use "docker.io" and are not impacted by this build argument +ARG DOCKER_REGISTRY=docker.io # Step 1 - Run Maven Build -FROM dspace/dspace-dependencies:dspace-7_x AS build +FROM ${DOCKER_REGISTRY}/dspace/dspace-dependencies:${DSPACE_VERSION} AS build ARG TARGET_DIR=dspace-installer WORKDIR /app # The dspace-installer directory will be written to /install @@ -27,15 +32,15 @@ RUN mvn --no-transfer-progress package -Pdspace-rest && \ mvn clean # Step 2 - Run Ant Deploy -FROM eclipse-temurin:${JDK_VERSION} AS ant_build +FROM docker.io/eclipse-temurin:${JDK_VERSION} AS ant_build ARG TARGET_DIR=dspace-installer # COPY the /install directory from 'build' container to /dspace-src in this container COPY --from=build /install /dspace-src 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 +ENV ANT_VERSION=1.10.12 +ENV ANT_HOME=/tmp/ant-$ANT_VERSION +ENV PATH=$ANT_HOME/bin:$PATH # Need wget to install ant RUN apt-get update \ && apt-get install -y --no-install-recommends wget \ @@ -49,7 +54,7 @@ RUN ant init_installation update_configs update_code update_webapps # Step 3 - Run tomcat # Create a new tomcat image that does not retain the the build directory contents -FROM tomcat:9-jdk${JDK_VERSION} +FROM docker.io/tomcat:9-jdk${JDK_VERSION} ENV DSPACE_INSTALL=/dspace ENV TOMCAT_INSTALL=/usr/local/tomcat # Copy the /dspace directory from 'ant_build' containger to /dspace in this container diff --git a/docker-compose-cli.yml b/docker-compose-cli.yml index d6a194617e02..ce7676bdc6c3 100644 --- a/docker-compose-cli.yml +++ b/docker-compose-cli.yml @@ -6,7 +6,7 @@ networks: external: true services: dspace-cli: - image: "${DOCKER_OWNER:-dspace}/dspace-cli:${DSPACE_VER:-dspace-7_x}" + image: "${DOCKER_REGISTRY:-docker.io}/${DOCKER_OWNER:-dspace}/dspace-cli:${DSPACE_VER:-dspace-7_x}" container_name: dspace-cli build: context: . diff --git a/docker-compose.yml b/docker-compose.yml index 23fce37db245..a16c3677503c 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -28,7 +28,7 @@ services: # from the host machine. This IP range MUST correspond to the 'dspacenet' subnet defined above. proxies__P__trusted__P__ipranges: '172.23.0' LOGGING_CONFIG: /dspace/config/log4j2-container.xml - image: "${DOCKER_OWNER:-dspace}/dspace:${DSPACE_VER:-dspace-7_x-test}" + image: "${DOCKER_REGISTRY:-docker.io}/${DOCKER_OWNER:-dspace}/dspace:${DSPACE_VER:-dspace-7_x-test}" build: context: . dockerfile: Dockerfile.test @@ -66,7 +66,7 @@ services: dspacedb: container_name: dspacedb # Uses a custom Postgres image with pgcrypto installed - image: "${DOCKER_OWNER:-dspace}/dspace-postgres-pgcrypto:${DSPACE_VER:-dspace-7_x}" + image: "${DOCKER_REGISTRY:-docker.io}/${DOCKER_OWNER:-dspace}/dspace-postgres-pgcrypto:${DSPACE_VER:-dspace-7_x}" build: # Must build out of subdirectory to have access to install script for pgcrypto context: ./dspace/src/main/docker/dspace-postgres-pgcrypto/ @@ -86,7 +86,7 @@ services: # DSpace Solr container dspacesolr: container_name: dspacesolr - image: "${DOCKER_OWNER:-dspace}/dspace-solr:${DSPACE_VER:-dspace-7_x}" + image: "${DOCKER_REGISTRY:-docker.io}/${DOCKER_OWNER:-dspace}/dspace-solr:${DSPACE_VER:-dspace-7_x}" build: context: ./dspace/src/main/docker/dspace-solr/ # Provide path to Solr configs necessary to build Docker image diff --git a/dspace/src/main/docker/dspace-postgres-pgcrypto-curl/Dockerfile b/dspace/src/main/docker/dspace-postgres-pgcrypto-curl/Dockerfile index aabf87df3eda..791242f5bdc7 100644 --- a/dspace/src/main/docker/dspace-postgres-pgcrypto-curl/Dockerfile +++ b/dspace/src/main/docker/dspace-postgres-pgcrypto-curl/Dockerfile @@ -13,7 +13,7 @@ ARG POSTGRES_VERSION=15 ARG POSTGRES_PASSWORD=dspace -FROM postgres:${POSTGRES_VERSION} +FROM docker.io/postgres:${POSTGRES_VERSION} ENV POSTGRES_DB dspace ENV POSTGRES_USER dspace diff --git a/dspace/src/main/docker/dspace-postgres-pgcrypto/Dockerfile b/dspace/src/main/docker/dspace-postgres-pgcrypto/Dockerfile index 2298cd4e76ea..116d2dbcda31 100644 --- a/dspace/src/main/docker/dspace-postgres-pgcrypto/Dockerfile +++ b/dspace/src/main/docker/dspace-postgres-pgcrypto/Dockerfile @@ -13,7 +13,7 @@ ARG POSTGRES_VERSION=15 ARG POSTGRES_PASSWORD=dspace -FROM postgres:${POSTGRES_VERSION} +FROM docker.io/postgres:${POSTGRES_VERSION} ENV POSTGRES_DB dspace ENV POSTGRES_USER dspace diff --git a/dspace/src/main/docker/dspace-shibboleth/Dockerfile b/dspace/src/main/docker/dspace-shibboleth/Dockerfile index 79f2921bd7d6..25fb510e3d39 100644 --- a/dspace/src/main/docker/dspace-shibboleth/Dockerfile +++ b/dspace/src/main/docker/dspace-shibboleth/Dockerfile @@ -10,7 +10,7 @@ # Build from Ubuntu as it has easy Apache tooling (e.g. a2enmod script is debian only). # Apache & mod_shib are required for DSpace to act as an SP # See also https://wiki.lyrasis.org/display/DSDOC7x/Authentication+Plugins#AuthenticationPlugins-ShibbolethAuthentication -FROM ubuntu:20.04 +FROM docker.io/ubuntu:20.04 # Apache ENVs (default values) ENV APACHE_RUN_USER www-data diff --git a/dspace/src/main/docker/dspace-solr/Dockerfile b/dspace/src/main/docker/dspace-solr/Dockerfile index eb8e93493fa8..0c011d43105f 100644 --- a/dspace/src/main/docker/dspace-solr/Dockerfile +++ b/dspace/src/main/docker/dspace-solr/Dockerfile @@ -12,7 +12,7 @@ ARG SOLR_VERSION=8.11 -FROM solr:${SOLR_VERSION}-slim +FROM docker.io/solr:${SOLR_VERSION}-slim ENV AUTHORITY_CONFIGSET_PATH=/opt/solr/server/solr/configsets/authority/conf \ OAI_CONFIGSET_PATH=/opt/solr/server/solr/configsets/oai/conf \ From 2ee328ff18aa973dd2ed82abd062d8a923cbddf5 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Wed, 4 Dec 2024 13:45:33 -0600 Subject: [PATCH 221/979] Minor Dockerfile cleanup. Use new syntax for ENV variables --- .../docker/dspace-postgres-pgcrypto-curl/Dockerfile | 6 +++--- .../main/docker/dspace-postgres-pgcrypto/Dockerfile | 6 +++--- dspace/src/main/docker/dspace-shibboleth/Dockerfile | 12 ++++++------ 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/dspace/src/main/docker/dspace-postgres-pgcrypto-curl/Dockerfile b/dspace/src/main/docker/dspace-postgres-pgcrypto-curl/Dockerfile index 791242f5bdc7..f314fdb79e80 100644 --- a/dspace/src/main/docker/dspace-postgres-pgcrypto-curl/Dockerfile +++ b/dspace/src/main/docker/dspace-postgres-pgcrypto-curl/Dockerfile @@ -15,9 +15,9 @@ ARG POSTGRES_PASSWORD=dspace FROM docker.io/postgres:${POSTGRES_VERSION} -ENV POSTGRES_DB dspace -ENV POSTGRES_USER dspace -ENV POSTGRES_PASSWORD ${POSTGRES_PASSWORD} +ENV POSTGRES_DB=dspace +ENV POSTGRES_USER=dspace +ENV POSTGRES_PASSWORD=${POSTGRES_PASSWORD} # Install curl which is necessary to load SQL file RUN apt-get update && apt-get install -y curl && rm -rf /var/lib/apt/lists/* diff --git a/dspace/src/main/docker/dspace-postgres-pgcrypto/Dockerfile b/dspace/src/main/docker/dspace-postgres-pgcrypto/Dockerfile index 116d2dbcda31..4f639f361024 100644 --- a/dspace/src/main/docker/dspace-postgres-pgcrypto/Dockerfile +++ b/dspace/src/main/docker/dspace-postgres-pgcrypto/Dockerfile @@ -15,9 +15,9 @@ ARG POSTGRES_PASSWORD=dspace FROM docker.io/postgres:${POSTGRES_VERSION} -ENV POSTGRES_DB dspace -ENV POSTGRES_USER dspace -ENV POSTGRES_PASSWORD ${POSTGRES_PASSWORD} +ENV POSTGRES_DB=dspace +ENV POSTGRES_USER=dspace +ENV POSTGRES_PASSWORD=${POSTGRES_PASSWORD} # Copy over script which will initialize database and install pgcrypto extension COPY install-pgcrypto.sh /docker-entrypoint-initdb.d/ diff --git a/dspace/src/main/docker/dspace-shibboleth/Dockerfile b/dspace/src/main/docker/dspace-shibboleth/Dockerfile index 25fb510e3d39..15a436d7b6f7 100644 --- a/dspace/src/main/docker/dspace-shibboleth/Dockerfile +++ b/dspace/src/main/docker/dspace-shibboleth/Dockerfile @@ -13,12 +13,12 @@ FROM docker.io/ubuntu:20.04 # Apache ENVs (default values) -ENV APACHE_RUN_USER www-data -ENV APACHE_RUN_GROUP www-data -ENV APACHE_LOCK_DIR /var/lock/apache2 -ENV APACHE_LOG_DIR /var/log/apache2 -ENV APACHE_PID_FILE /var/run/apache2/apache2.pid -ENV APACHE_SERVER_NAME localhost +ENV APACHE_RUN_USER=www-data +ENV APACHE_RUN_GROUP=www-data +ENV APACHE_LOCK_DIR=/var/lock/apache2 +ENV APACHE_LOG_DIR=/var/log/apache2 +ENV APACHE_PID_FILE=/var/run/apache2/apache2.pid +ENV APACHE_SERVER_NAME=localhost # Ensure Apache2, mod_shib & shibboleth daemon are installed. # Also install ssl-cert to provide a local SSL cert for use with Apache From f5dcb445514fea275d6986ab40bd66671c2f53e9 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Thu, 12 Dec 2024 16:50:02 -0600 Subject: [PATCH 222/979] Refactor Docker build process to use ghcr.io for build, and then copy to docker.io once build completes (cherry picked from commit e6eb00366c4d6ee14366f2061f62682c1b5ed05f) --- .github/workflows/docker.yml | 1 + .github/workflows/reusable-docker-build.yml | 188 ++++++++++++++------ 2 files changed, 130 insertions(+), 59 deletions(-) diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 0a5618fbe354..d279fca009f4 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -15,6 +15,7 @@ on: permissions: contents: read # to fetch code (actions/checkout) + packages: write # to write images to GitHub Container Registry (GHCR) jobs: #################################################### diff --git a/.github/workflows/reusable-docker-build.yml b/.github/workflows/reusable-docker-build.yml index a6e326846006..085880db266b 100644 --- a/.github/workflows/reusable-docker-build.yml +++ b/.github/workflows/reusable-docker-build.yml @@ -75,6 +75,9 @@ env: DEPLOY_DEMO_BRANCH: 'dspace-8_x' DEPLOY_SANDBOX_BRANCH: 'main' DEPLOY_ARCH: 'linux/amd64' + # Registry used during building of Docker images. (All images are later copied to docker.io registry) + # We use GitHub's Container Registry to avoid aggressive rate limits at DockerHub. + DOCKER_BUILD_REGISTRY: ghcr.io jobs: docker-build: @@ -99,6 +102,7 @@ jobs: # This step converts the slashes in the "arch" matrix values above into dashes & saves to env.ARCH_NAME # E.g. "linux/amd64" becomes "linux-amd64" # This is necessary because all upload artifacts CANNOT have special chars (like slashes) + # NOTE: The regex-like syntax below is Bash Parameter Substitution - name: Prepare run: | platform=${{ matrix.arch }} @@ -109,13 +113,14 @@ jobs: uses: actions/checkout@v4 # https://github.com/docker/login-action - - name: Login to DockerHub - # Only login if not a PR, as PRs only trigger a Docker build and not a push + - name: Login to ${{ env.DOCKER_BUILD_REGISTRY }} + # Only login if not a PR, as PRs only trigger a Docker build and not a push if: ${{ ! matrix.isPr }} uses: docker/login-action@v3 with: - username: ${{ secrets.DOCKER_USERNAME }} - password: ${{ secrets.DOCKER_ACCESS_TOKEN }} + registry: ${{ env.DOCKER_BUILD_REGISTRY }} + username: ${{ github.repository_owner }} + password: ${{ secrets.GITHUB_TOKEN }} # https://github.com/docker/setup-qemu-action - name: Set up QEMU emulation to build for multiple architectures @@ -131,19 +136,20 @@ jobs: id: meta_build uses: docker/metadata-action@v5 with: - images: ${{ env.IMAGE_NAME }} + images: ${{ env.DOCKER_BUILD_REGISTRY }}/${{ env.IMAGE_NAME }} tags: ${{ env.IMAGE_TAGS }} flavor: ${{ env.TAGS_FLAVOR }} - #------------------------------------------------------------ - # Build & deploy steps for new commits to a branch (non-PRs) + #-------------------------------------------------------------------- + # First, for all branch commits (non-PRs) we build the image & upload + # to GitHub Container Registry (GHCR). After uploading the image + # to GHCR, we store the image digest in an artifact, so we can + # create a merged manifest later (see 'docker-build_manifest' job). # - # These steps build the images, push to DockerHub, and - # (if necessary) redeploy demo/sandbox sites. - #------------------------------------------------------------ + # NOTE: We use GHCR in order to avoid aggressive rate limits at DockerHub. + #-------------------------------------------------------------------- # https://github.com/docker/build-push-action - - name: Build and push image to DockerHub - # Only build & push if not a PR + - name: Build and push image to ${{ env.DOCKER_BUILD_REGISTRY }} if: ${{ ! matrix.isPr }} id: docker_build uses: docker/build-push-action@v5 @@ -152,6 +158,9 @@ jobs: ${{ inputs.dockerfile_additional_contexts }} context: ${{ inputs.dockerfile_context }} file: ${{ inputs.dockerfile_path }} + # Tell DSpace's Docker files to use the build registry instead of DockerHub + build-args: + DOCKER_REGISTRY=${{ env.DOCKER_BUILD_REGISTRY }} platforms: ${{ matrix.arch }} push: true # Use tags / labels provided by 'docker/metadata-action' above @@ -162,7 +171,7 @@ jobs: cache-from: type=gha,scope=${{ inputs.build_id }} cache-to: type=gha,scope=${{ inputs.build_id }},mode=max - # Export the digest of Docker build locally (for non PRs only) + # Export the digest of Docker build locally - name: Export Docker build digest if: ${{ ! matrix.isPr }} run: | @@ -170,7 +179,8 @@ jobs: digest="${{ steps.docker_build.outputs.digest }}" touch "/tmp/digests/${digest#sha256:}" - # Upload digest to an artifact, so that it can be used in manifest below + # Upload digest to an artifact, so that it can be used in combined manifest below + # (The purpose of the combined manifest is to list both amd64 and arm64 builds under same tag) - name: Upload Docker build digest to artifact if: ${{ ! matrix.isPr }} uses: actions/upload-artifact@v4 @@ -180,48 +190,31 @@ jobs: if-no-files-found: error retention-days: 1 - # If this build is NOT a PR and passed in a REDEPLOY_SANDBOX_URL secret, - # Then redeploy https://sandbox.dspace.org if this build is for our deployment architecture and 'main' branch. - - name: Redeploy sandbox.dspace.org (based on main branch) - if: | - !matrix.isPR && - env.REDEPLOY_SANDBOX_URL != '' && - matrix.arch == env.DEPLOY_ARCH && - github.ref_name == env.DEPLOY_SANDBOX_BRANCH - run: | - curl -X POST $REDEPLOY_SANDBOX_URL - - # If this build is NOT a PR and passed in a REDEPLOY_DEMO_URL secret, - # Then redeploy https://demo.dspace.org if this build is for our deployment architecture and demo branch. - - name: Redeploy demo.dspace.org (based on maintenance branch) - if: | - !matrix.isPR && - env.REDEPLOY_DEMO_URL != '' && - matrix.arch == env.DEPLOY_ARCH && - github.ref_name == env.DEPLOY_DEMO_BRANCH - run: | - curl -X POST $REDEPLOY_DEMO_URL - - #------------------------------------------------------------- - # Shared Build steps. - # These are used for PRs as well as new commits to a branch (non-PRs) + #------------------------------------------------------------------------------ + # Second, we build the image again in order to store it in a local TAR file. + # This TAR of the image is cached/saved as an artifact, so that it can be used + # by later jobs to install the brand-new images for automated testing. + # This TAR build is performed BOTH for PRs and for branch commits (non-PRs). # - # These steps build the images and cache/store as a build artifact. - # These artifacts can then be used by later jobs to install the - # brand-new images for automated testing. For non-PRs, this cache is - # also used to avoid pulling the images we just built from DockerHub. - #-------------------------------------------------------------- - + # (This approach has the advantage of avoiding having to download the newly built + # image from DockerHub or GHCR during automated testing.) + # + # See the 'docker-deploy' job in docker.yml as an example of where this TAR is used. + #------------------------------------------------------------------------------- # Build local image (again) and store in a TAR file in /tmp directory - # NOTE: This build is run for both PRs and non-PRs as it's used to "cache" our built images as artifacts. - # NOTE #2: This cannot be combined with push to DockerHub registry above as it's a different type of output. + # This step is only done for AMD64, as that's the only image we use in our automated testing (at this time). + # NOTE: This step cannot be combined with the build above as it's a different type of output. - name: Build and push image to local TAR file + if: ${{ matrix.arch == 'linux/amd64'}} uses: docker/build-push-action@v5 with: build-contexts: | ${{ inputs.dockerfile_additional_contexts }} context: ${{ inputs.dockerfile_context }} file: ${{ inputs.dockerfile_path }} + # Tell DSpace's Docker files to use the build registry instead of DockerHub + build-args: + DOCKER_REGISTRY=${{ env.DOCKER_BUILD_REGISTRY }} platforms: ${{ matrix.arch }} tags: ${{ steps.meta_build.outputs.tags }} labels: ${{ steps.meta_build.outputs.labels }} @@ -233,7 +226,9 @@ jobs: outputs: type=docker,dest=/tmp/${{ inputs.build_id }}.tar # Upload the local docker image (in TAR file) to a build Artifact + # This step is only done for AMD64, as that's the only image we use in our automated testing (at this time). - name: Upload local image TAR to artifact + if: ${{ matrix.arch == 'linux/amd64'}} uses: actions/upload-artifact@v4 with: name: docker-image-${{ inputs.build_id }}-${{ env.ARCH_NAME }} @@ -241,10 +236,12 @@ jobs: if-no-files-found: error retention-days: 1 - # Merge Docker digests (from various architectures) into a manifest. - # This runs after all Docker builds complete above, and it tells hub.docker.com - # that these builds should be all included in the manifest for this tag. - # (e.g. AMD64 and ARM64 should be listed as options under the same tagged Docker image) + ########################################################################################## + # Merge Docker digests (from various architectures) into a single manifest. + # This runs after all Docker builds complete above. The purpose is to include all builds + # under a single manifest for this tag. + # (e.g. both linux/amd64 and linux/arm64 should be listed under the same tagged Docker image) + ########################################################################################## docker-build_manifest: # Only run if this is NOT a PR if: ${{ github.event_name != 'pull_request' }} @@ -260,11 +257,12 @@ jobs: pattern: digests-${{ inputs.build_id }}-* merge-multiple: true - - name: Login to Docker Hub + - name: Login to ${{ env.DOCKER_BUILD_REGISTRY }} uses: docker/login-action@v3 with: - username: ${{ secrets.DOCKER_USERNAME }} - password: ${{ secrets.DOCKER_ACCESS_TOKEN }} + registry: ${{ env.DOCKER_BUILD_REGISTRY }} + username: ${{ github.repository_owner }} + password: ${{ secrets.GITHUB_TOKEN }} - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 @@ -273,16 +271,88 @@ jobs: id: meta uses: docker/metadata-action@v5 with: - images: ${{ env.IMAGE_NAME }} + images: ${{ env.DOCKER_BUILD_REGISTRY }}/${{ env.IMAGE_NAME }} tags: ${{ env.IMAGE_TAGS }} flavor: ${{ env.TAGS_FLAVOR }} - - name: Create manifest list from digests and push + - name: Create manifest list from digests and push to ${{ env.DOCKER_BUILD_REGISTRY }} working-directory: /tmp/digests run: | docker buildx imagetools create $(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \ - $(printf '${{ env.IMAGE_NAME }}@sha256:%s ' *) + $(printf '${{ env.DOCKER_BUILD_REGISTRY }}/${{ env.IMAGE_NAME }}@sha256:%s ' *) + + - name: Inspect manifest in ${{ env.DOCKER_BUILD_REGISTRY }} + run: | + docker buildx imagetools inspect ${{ env.DOCKER_BUILD_REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.meta.outputs.version }} + + ########################################################################################## + # Copy images / manifest to DockerHub. + # This MUST run after *both* images (AMD64 and ARM64) are built and uploaded to GitHub + # Container Registry (GHCR). Attempting to run this in parallel to GHCR builds can result + # in a race condition...i.e. the copy to DockerHub may fail if GHCR image has been updated + # at the moment when the copy occurs. + ########################################################################################## + docker-copy_to_dockerhub: + # Only run if this is NOT a PR + if: ${{ github.event_name != 'pull_request' }} + runs-on: ubuntu-latest + needs: + - docker-build_manifest + + steps: + # 'regctl' is used to more easily copy the image to DockerHub and obtain the digest from DockerHub + # See https://github.com/regclient/regclient/blob/main/docs/regctl.md + - name: Install regctl for Docker registry tools + uses: regclient/actions/regctl-installer@main + with: + release: 'v0.8.0' + + # This recreates Docker tags for DockerHub + - name: Add Docker metadata for image + id: meta_dockerhub + uses: docker/metadata-action@v5 + with: + images: ${{ env.IMAGE_NAME }} + tags: ${{ env.IMAGE_TAGS }} + flavor: ${{ env.TAGS_FLAVOR }} + + # Login to source registry first, as this is where we are copying *from* + - name: Login to ${{ env.DOCKER_BUILD_REGISTRY }} + uses: docker/login-action@v3 + with: + registry: ${{ env.DOCKER_BUILD_REGISTRY }} + username: ${{ github.repository_owner }} + password: ${{ secrets.GITHUB_TOKEN }} + + # Login to DockerHub, since this is where we are copying *to* + - name: Login to DockerHub + uses: docker/login-action@v3 + with: + username: ${{ secrets.DOCKER_USERNAME }} + password: ${{ secrets.DOCKER_ACCESS_TOKEN }} + + # Copy the image from source to DockerHub + - name: Copy image from ${{ env.DOCKER_BUILD_REGISTRY }} to docker.io + run: | + regctl image copy ${{ env.DOCKER_BUILD_REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.meta_dockerhub.outputs.version }} docker.io/${{ env.IMAGE_NAME }}:${{ steps.meta_dockerhub.outputs.version }} - - name: Inspect image + #-------------------------------------------------------------------- + # Finally, check whether demo.dspace.org or sandbox.dspace.org need + # to be redeployed based on these new DockerHub images. + #-------------------------------------------------------------------- + # If this build is for the branch that Sandbox uses and passed in a REDEPLOY_SANDBOX_URL secret, + # Then redeploy https://sandbox.dspace.org + - name: Redeploy sandbox.dspace.org (based on main branch) + if: | + env.REDEPLOY_SANDBOX_URL != '' && + github.ref_name == env.DEPLOY_SANDBOX_BRANCH + run: | + curl -X POST $REDEPLOY_SANDBOX_URL + # If this build is for the branch that Demo uses and passed in a REDEPLOY_DEMO_URL secret, + # Then redeploy https://demo.dspace.org + - name: Redeploy demo.dspace.org (based on maintenance branch) + if: | + env.REDEPLOY_DEMO_URL != '' && + github.ref_name == env.DEPLOY_DEMO_BRANCH run: | - docker buildx imagetools inspect ${{ env.IMAGE_NAME }}:${{ steps.meta.outputs.version }} + curl -X POST $REDEPLOY_DEMO_URL \ No newline at end of file From dc4c4ebc8179fbc661d62735782acb2f6024bc98 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Mon, 16 Dec 2024 12:05:41 -0600 Subject: [PATCH 223/979] PRs must also login to ghcr.io in order to read private images for the build process (cherry picked from commit 04d891241b36bad94a1c8a5ae02d99e50f06bf35) --- .github/workflows/reusable-docker-build.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/reusable-docker-build.yml b/.github/workflows/reusable-docker-build.yml index 085880db266b..7a8abda3e106 100644 --- a/.github/workflows/reusable-docker-build.yml +++ b/.github/workflows/reusable-docker-build.yml @@ -113,9 +113,9 @@ jobs: uses: actions/checkout@v4 # https://github.com/docker/login-action + # NOTE: This login occurs for BOTH non-PRs or PRs. PRs *must* also login to access private images from GHCR + # during the build process - name: Login to ${{ env.DOCKER_BUILD_REGISTRY }} - # Only login if not a PR, as PRs only trigger a Docker build and not a push - if: ${{ ! matrix.isPr }} uses: docker/login-action@v3 with: registry: ${{ env.DOCKER_BUILD_REGISTRY }} From 0328d076be5bb056a191078c9f0a01e0954e448d Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Mon, 16 Dec 2024 12:28:16 -0600 Subject: [PATCH 224/979] Ensure "docker-deploy" job also uses ghcr.io by default. (cherry picked from commit 5f314c9a75e1cf6ddaf1f91eeaa267add9ff3439) --- .github/workflows/docker.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index d279fca009f4..9d32cb119d41 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -177,6 +177,9 @@ jobs: # Else, just use the branch name. # NOTE: DSPACE_VER is used because our docker compose scripts default to using the "-test" image. DSPACE_VER: ${{ (github.event_name == 'pull_request' && github.event.pull_request.base.ref == github.event.repository.default_branch && 'latest') || (github.event_name == 'pull_request' && github.event.pull_request.base.ref) || (github.ref_name == github.event.repository.default_branch && 'latest') || github.ref_name }} + # Docker Registry to use for Docker compose scripts below. + # We use GitHub's Container Registry to avoid aggressive rate limits at DockerHub. + DOCKER_REGISTRY: ghcr.io steps: # Checkout our codebase (to get access to Docker Compose scripts) - name: Checkout codebase From 2a6c60e1392cf643b4ae4c8512ce92c7aba4b2eb Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Thu, 12 Dec 2024 16:50:02 -0600 Subject: [PATCH 225/979] Refactor Docker build process to use ghcr.io for build, and then copy to docker.io once build completes --- .github/workflows/docker.yml | 1 + .github/workflows/reusable-docker-build.yml | 189 ++++++++++++++------ 2 files changed, 131 insertions(+), 59 deletions(-) diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index ebbd8cc50984..fceea6403d2a 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -15,6 +15,7 @@ on: permissions: contents: read # to fetch code (actions/checkout) + packages: write # to write images to GitHub Container Registry (GHCR) jobs: #################################################### diff --git a/.github/workflows/reusable-docker-build.yml b/.github/workflows/reusable-docker-build.yml index d9f4c5342b40..36546af3a5da 100644 --- a/.github/workflows/reusable-docker-build.yml +++ b/.github/workflows/reusable-docker-build.yml @@ -74,7 +74,11 @@ env: # Current DSpace maintenance branch (and architecture) which is deployed to demo.dspace.org / sandbox.dspace.org # (NOTE: No deployment branch specified for sandbox.dspace.org as it uses the default_branch) DEPLOY_DEMO_BRANCH: 'dspace-8_x' + DEPLOY_SANDBOX_BRANCH: 'main' DEPLOY_ARCH: 'linux/amd64' + # Registry used during building of Docker images. (All images are later copied to docker.io registry) + # We use GitHub's Container Registry to avoid aggressive rate limits at DockerHub. + DOCKER_BUILD_REGISTRY: ghcr.io jobs: docker-build: @@ -99,6 +103,7 @@ jobs: # This step converts the slashes in the "arch" matrix values above into dashes & saves to env.ARCH_NAME # E.g. "linux/amd64" becomes "linux-amd64" # This is necessary because all upload artifacts CANNOT have special chars (like slashes) + # NOTE: The regex-like syntax below is Bash Parameter Substitution - name: Prepare run: | platform=${{ matrix.arch }} @@ -109,13 +114,14 @@ jobs: uses: actions/checkout@v4 # https://github.com/docker/login-action - - name: Login to DockerHub - # Only login if not a PR, as PRs only trigger a Docker build and not a push + - name: Login to ${{ env.DOCKER_BUILD_REGISTRY }} + # Only login if not a PR, as PRs only trigger a Docker build and not a push if: ${{ ! matrix.isPr }} uses: docker/login-action@v3 with: - username: ${{ secrets.DOCKER_USERNAME }} - password: ${{ secrets.DOCKER_ACCESS_TOKEN }} + registry: ${{ env.DOCKER_BUILD_REGISTRY }} + username: ${{ github.repository_owner }} + password: ${{ secrets.GITHUB_TOKEN }} # https://github.com/docker/setup-qemu-action - name: Set up QEMU emulation to build for multiple architectures @@ -131,19 +137,20 @@ jobs: id: meta_build uses: docker/metadata-action@v5 with: - images: ${{ env.IMAGE_NAME }} + images: ${{ env.DOCKER_BUILD_REGISTRY }}/${{ env.IMAGE_NAME }} tags: ${{ env.IMAGE_TAGS }} flavor: ${{ env.TAGS_FLAVOR }} - #------------------------------------------------------------ - # Build & deploy steps for new commits to a branch (non-PRs) + #-------------------------------------------------------------------- + # First, for all branch commits (non-PRs) we build the image & upload + # to GitHub Container Registry (GHCR). After uploading the image + # to GHCR, we store the image digest in an artifact, so we can + # create a merged manifest later (see 'docker-build_manifest' job). # - # These steps build the images, push to DockerHub, and - # (if necessary) redeploy demo/sandbox sites. - #------------------------------------------------------------ + # NOTE: We use GHCR in order to avoid aggressive rate limits at DockerHub. + #-------------------------------------------------------------------- # https://github.com/docker/build-push-action - - name: Build and push image to DockerHub - # Only build & push if not a PR + - name: Build and push image to ${{ env.DOCKER_BUILD_REGISTRY }} if: ${{ ! matrix.isPr }} id: docker_build uses: docker/build-push-action@v5 @@ -152,6 +159,9 @@ jobs: ${{ inputs.dockerfile_additional_contexts }} context: ${{ inputs.dockerfile_context }} file: ${{ inputs.dockerfile_path }} + # Tell DSpace's Docker files to use the build registry instead of DockerHub + build-args: + DOCKER_REGISTRY=${{ env.DOCKER_BUILD_REGISTRY }} platforms: ${{ matrix.arch }} push: true # Use tags / labels provided by 'docker/metadata-action' above @@ -162,7 +172,7 @@ jobs: cache-from: type=gha,scope=${{ inputs.build_id }} cache-to: type=gha,scope=${{ inputs.build_id }},mode=max - # Export the digest of Docker build locally (for non PRs only) + # Export the digest of Docker build locally - name: Export Docker build digest if: ${{ ! matrix.isPr }} run: | @@ -170,7 +180,8 @@ jobs: digest="${{ steps.docker_build.outputs.digest }}" touch "/tmp/digests/${digest#sha256:}" - # Upload digest to an artifact, so that it can be used in manifest below + # Upload digest to an artifact, so that it can be used in combined manifest below + # (The purpose of the combined manifest is to list both amd64 and arm64 builds under same tag) - name: Upload Docker build digest to artifact if: ${{ ! matrix.isPr }} uses: actions/upload-artifact@v4 @@ -180,48 +191,31 @@ jobs: if-no-files-found: error retention-days: 1 - # If this build is NOT a PR and passed in a REDEPLOY_SANDBOX_URL secret, - # Then redeploy https://sandbox.dspace.org if this build is for our deployment architecture and 'main' branch. - - name: Redeploy sandbox.dspace.org (based on main branch) - if: | - !matrix.isPR && - env.REDEPLOY_SANDBOX_URL != '' && - matrix.arch == env.DEPLOY_ARCH && - github.ref_name == github.event.repository.default_branch - run: | - curl -X POST $REDEPLOY_SANDBOX_URL - - # If this build is NOT a PR and passed in a REDEPLOY_DEMO_URL secret, - # Then redeploy https://demo.dspace.org if this build is for our deployment architecture and demo branch. - - name: Redeploy demo.dspace.org (based on maintenance branch) - if: | - !matrix.isPR && - env.REDEPLOY_DEMO_URL != '' && - matrix.arch == env.DEPLOY_ARCH && - github.ref_name == env.DEPLOY_DEMO_BRANCH - run: | - curl -X POST $REDEPLOY_DEMO_URL - - #------------------------------------------------------------- - # Shared Build steps. - # These are used for PRs as well as new commits to a branch (non-PRs) + #------------------------------------------------------------------------------ + # Second, we build the image again in order to store it in a local TAR file. + # This TAR of the image is cached/saved as an artifact, so that it can be used + # by later jobs to install the brand-new images for automated testing. + # This TAR build is performed BOTH for PRs and for branch commits (non-PRs). # - # These steps build the images and cache/store as a build artifact. - # These artifacts can then be used by later jobs to install the - # brand-new images for automated testing. For non-PRs, this cache is - # also used to avoid pulling the images we just built from DockerHub. - #-------------------------------------------------------------- - + # (This approach has the advantage of avoiding having to download the newly built + # image from DockerHub or GHCR during automated testing.) + # + # See the 'docker-deploy' job in docker.yml as an example of where this TAR is used. + #------------------------------------------------------------------------------- # Build local image (again) and store in a TAR file in /tmp directory - # NOTE: This build is run for both PRs and non-PRs as it's used to "cache" our built images as artifacts. - # NOTE #2: This cannot be combined with push to DockerHub registry above as it's a different type of output. + # This step is only done for AMD64, as that's the only image we use in our automated testing (at this time). + # NOTE: This step cannot be combined with the build above as it's a different type of output. - name: Build and push image to local TAR file + if: ${{ matrix.arch == 'linux/amd64'}} uses: docker/build-push-action@v5 with: build-contexts: | ${{ inputs.dockerfile_additional_contexts }} context: ${{ inputs.dockerfile_context }} file: ${{ inputs.dockerfile_path }} + # Tell DSpace's Docker files to use the build registry instead of DockerHub + build-args: + DOCKER_REGISTRY=${{ env.DOCKER_BUILD_REGISTRY }} platforms: ${{ matrix.arch }} tags: ${{ steps.meta_build.outputs.tags }} labels: ${{ steps.meta_build.outputs.labels }} @@ -233,7 +227,9 @@ jobs: outputs: type=docker,dest=/tmp/${{ inputs.build_id }}.tar # Upload the local docker image (in TAR file) to a build Artifact + # This step is only done for AMD64, as that's the only image we use in our automated testing (at this time). - name: Upload local image TAR to artifact + if: ${{ matrix.arch == 'linux/amd64'}} uses: actions/upload-artifact@v4 with: name: docker-image-${{ inputs.build_id }}-${{ env.ARCH_NAME }} @@ -241,10 +237,12 @@ jobs: if-no-files-found: error retention-days: 1 - # Merge Docker digests (from various architectures) into a manifest. - # This runs after all Docker builds complete above, and it tells hub.docker.com - # that these builds should be all included in the manifest for this tag. - # (e.g. AMD64 and ARM64 should be listed as options under the same tagged Docker image) + ########################################################################################## + # Merge Docker digests (from various architectures) into a single manifest. + # This runs after all Docker builds complete above. The purpose is to include all builds + # under a single manifest for this tag. + # (e.g. both linux/amd64 and linux/arm64 should be listed under the same tagged Docker image) + ########################################################################################## docker-build_manifest: # Only run if this is NOT a PR if: ${{ github.event_name != 'pull_request' }} @@ -260,11 +258,12 @@ jobs: pattern: digests-${{ inputs.build_id }}-* merge-multiple: true - - name: Login to Docker Hub + - name: Login to ${{ env.DOCKER_BUILD_REGISTRY }} uses: docker/login-action@v3 with: - username: ${{ secrets.DOCKER_USERNAME }} - password: ${{ secrets.DOCKER_ACCESS_TOKEN }} + registry: ${{ env.DOCKER_BUILD_REGISTRY }} + username: ${{ github.repository_owner }} + password: ${{ secrets.GITHUB_TOKEN }} - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 @@ -273,16 +272,88 @@ jobs: id: meta uses: docker/metadata-action@v5 with: - images: ${{ env.IMAGE_NAME }} + images: ${{ env.DOCKER_BUILD_REGISTRY }}/${{ env.IMAGE_NAME }} tags: ${{ env.IMAGE_TAGS }} flavor: ${{ env.TAGS_FLAVOR }} - - name: Create manifest list from digests and push + - name: Create manifest list from digests and push to ${{ env.DOCKER_BUILD_REGISTRY }} working-directory: /tmp/digests run: | docker buildx imagetools create $(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \ - $(printf '${{ env.IMAGE_NAME }}@sha256:%s ' *) + $(printf '${{ env.DOCKER_BUILD_REGISTRY }}/${{ env.IMAGE_NAME }}@sha256:%s ' *) + + - name: Inspect manifest in ${{ env.DOCKER_BUILD_REGISTRY }} + run: | + docker buildx imagetools inspect ${{ env.DOCKER_BUILD_REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.meta.outputs.version }} + + ########################################################################################## + # Copy images / manifest to DockerHub. + # This MUST run after *both* images (AMD64 and ARM64) are built and uploaded to GitHub + # Container Registry (GHCR). Attempting to run this in parallel to GHCR builds can result + # in a race condition...i.e. the copy to DockerHub may fail if GHCR image has been updated + # at the moment when the copy occurs. + ########################################################################################## + docker-copy_to_dockerhub: + # Only run if this is NOT a PR + if: ${{ github.event_name != 'pull_request' }} + runs-on: ubuntu-latest + needs: + - docker-build_manifest + + steps: + # 'regctl' is used to more easily copy the image to DockerHub and obtain the digest from DockerHub + # See https://github.com/regclient/regclient/blob/main/docs/regctl.md + - name: Install regctl for Docker registry tools + uses: regclient/actions/regctl-installer@main + with: + release: 'v0.8.0' - - name: Inspect image + # This recreates Docker tags for DockerHub + - name: Add Docker metadata for image + id: meta_dockerhub + uses: docker/metadata-action@v5 + with: + images: ${{ env.IMAGE_NAME }} + tags: ${{ env.IMAGE_TAGS }} + flavor: ${{ env.TAGS_FLAVOR }} + + # Login to source registry first, as this is where we are copying *from* + - name: Login to ${{ env.DOCKER_BUILD_REGISTRY }} + uses: docker/login-action@v3 + with: + registry: ${{ env.DOCKER_BUILD_REGISTRY }} + username: ${{ github.repository_owner }} + password: ${{ secrets.GITHUB_TOKEN }} + + # Login to DockerHub, since this is where we are copying *to* + - name: Login to DockerHub + uses: docker/login-action@v3 + with: + username: ${{ secrets.DOCKER_USERNAME }} + password: ${{ secrets.DOCKER_ACCESS_TOKEN }} + + # Copy the image from source to DockerHub + - name: Copy image from ${{ env.DOCKER_BUILD_REGISTRY }} to docker.io + run: | + regctl image copy ${{ env.DOCKER_BUILD_REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.meta_dockerhub.outputs.version }} docker.io/${{ env.IMAGE_NAME }}:${{ steps.meta_dockerhub.outputs.version }} + + #-------------------------------------------------------------------- + # Finally, check whether demo.dspace.org or sandbox.dspace.org need + # to be redeployed based on these new DockerHub images. + #-------------------------------------------------------------------- + # If this build is for the branch that Sandbox uses and passed in a REDEPLOY_SANDBOX_URL secret, + # Then redeploy https://sandbox.dspace.org + - name: Redeploy sandbox.dspace.org (based on main branch) + if: | + env.REDEPLOY_SANDBOX_URL != '' && + github.ref_name == env.DEPLOY_SANDBOX_BRANCH + run: | + curl -X POST $REDEPLOY_SANDBOX_URL + # If this build is for the branch that Demo uses and passed in a REDEPLOY_DEMO_URL secret, + # Then redeploy https://demo.dspace.org + - name: Redeploy demo.dspace.org (based on maintenance branch) + if: | + env.REDEPLOY_DEMO_URL != '' && + github.ref_name == env.DEPLOY_DEMO_BRANCH run: | - docker buildx imagetools inspect ${{ env.IMAGE_NAME }}:${{ steps.meta.outputs.version }} + curl -X POST $REDEPLOY_DEMO_URL \ No newline at end of file From 296c9a12f489d779c15bf07fd8c38fac6bc6d233 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Mon, 16 Dec 2024 12:05:41 -0600 Subject: [PATCH 226/979] PRs must also login to ghcr.io in order to read private images for the build process --- .github/workflows/reusable-docker-build.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/reusable-docker-build.yml b/.github/workflows/reusable-docker-build.yml index 36546af3a5da..3b74f250b539 100644 --- a/.github/workflows/reusable-docker-build.yml +++ b/.github/workflows/reusable-docker-build.yml @@ -114,9 +114,9 @@ jobs: uses: actions/checkout@v4 # https://github.com/docker/login-action + # NOTE: This login occurs for BOTH non-PRs or PRs. PRs *must* also login to access private images from GHCR + # during the build process - name: Login to ${{ env.DOCKER_BUILD_REGISTRY }} - # Only login if not a PR, as PRs only trigger a Docker build and not a push - if: ${{ ! matrix.isPr }} uses: docker/login-action@v3 with: registry: ${{ env.DOCKER_BUILD_REGISTRY }} From a27f1ed1757b193cf6f831a0fd27585b655bacc2 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Mon, 16 Dec 2024 12:28:16 -0600 Subject: [PATCH 227/979] Ensure "docker-deploy" job also uses ghcr.io by default. --- .github/workflows/docker.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index fceea6403d2a..815aec5cf6b4 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -176,6 +176,9 @@ jobs: # Else, just use the branch name. # NOTE: DSPACE_VER is used because our docker compose scripts default to using the "-test" image. DSPACE_VER: ${{ (github.event_name == 'pull_request' && github.event.pull_request.base.ref == github.event.repository.default_branch && 'latest') || (github.event_name == 'pull_request' && github.event.pull_request.base.ref) || (github.ref_name == github.event.repository.default_branch && 'latest') || github.ref_name }} + # Docker Registry to use for Docker compose scripts below. + # We use GitHub's Container Registry to avoid aggressive rate limits at DockerHub. + DOCKER_REGISTRY: ghcr.io steps: # Checkout our codebase (to get access to Docker Compose scripts) - name: Checkout codebase From c6ecdaf29376409bcea60abb4b634de7d0294c45 Mon Sep 17 00:00:00 2001 From: autavares-dev Date: Wed, 7 Aug 2024 16:34:38 -0300 Subject: [PATCH 228/979] Changes Group2GroupCache computation (cherry picked from commit 2bcea0f860db9a3dc915119a5e111d057d2b2936) --- .../org/dspace/eperson/Group2GroupCache.java | 3 +- .../org/dspace/eperson/GroupServiceImpl.java | 72 +++++++++--------- .../eperson/dao/Group2GroupCacheDAO.java | 74 +++++++++++++++++-- .../dao/impl/Group2GroupCacheDAOImpl.java | 34 +++++++++ 4 files changed, 143 insertions(+), 40 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/eperson/Group2GroupCache.java b/dspace-api/src/main/java/org/dspace/eperson/Group2GroupCache.java index a1c12371f5ff..0c6ea58b1977 100644 --- a/dspace-api/src/main/java/org/dspace/eperson/Group2GroupCache.java +++ b/dspace-api/src/main/java/org/dspace/eperson/Group2GroupCache.java @@ -15,6 +15,7 @@ import jakarta.persistence.JoinColumn; import jakarta.persistence.ManyToOne; import jakarta.persistence.Table; +import jakarta.persistence.UniqueConstraint; import org.dspace.core.HibernateProxyHelper; /** @@ -23,7 +24,7 @@ * @author kevinvandevelde at atmire.com */ @Entity -@Table(name = "group2groupcache") +@Table(name = "group2groupcache", uniqueConstraints = { @UniqueConstraint(columnNames = {"parent_id", "child_id"}) }) public class Group2GroupCache implements Serializable { @Id diff --git a/dspace-api/src/main/java/org/dspace/eperson/GroupServiceImpl.java b/dspace-api/src/main/java/org/dspace/eperson/GroupServiceImpl.java index 3fb20e2f1e6f..b52f17a5c692 100644 --- a/dspace-api/src/main/java/org/dspace/eperson/GroupServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/eperson/GroupServiceImpl.java @@ -20,6 +20,7 @@ import java.util.UUID; import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.collections4.SetUtils; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.tuple.Pair; import org.apache.logging.log4j.LogManager; @@ -673,15 +674,14 @@ protected boolean isEPersonInGroup(Context context, Group group, EPerson ePerson /** - * Regenerate the group cache AKA the group2groupcache table in the database - - * meant to be called when a group is added or removed from another group + * Returns a set with pairs of parent and child group UUIDs, representing the new cache table rows. * - * @param context The relevant DSpace Context. - * @param flushQueries flushQueries Flush all pending queries + * @param context The relevant DSpace Context. + * @param flushQueries flushQueries Flush all pending queries + * @return Pairs of parent and child group UUID of the new cache. * @throws SQLException An exception that provides information on a database access error or other errors. */ - protected void rethinkGroupCache(Context context, boolean flushQueries) throws SQLException { - + private Set> computeNewCache(Context context, boolean flushQueries) throws SQLException { Map> parents = new HashMap<>(); List> group2groupResults = groupDAO.getGroup2GroupResults(context, flushQueries); @@ -689,19 +689,8 @@ protected void rethinkGroupCache(Context context, boolean flushQueries) throws S UUID parent = group2groupResult.getLeft(); UUID child = group2groupResult.getRight(); - // if parent doesn't have an entry, create one - if (!parents.containsKey(parent)) { - Set children = new HashSet<>(); - - // add child id to the list - children.add(child); - parents.put(parent, children); - } else { - // parent has an entry, now add the child to the parent's record - // of children - Set children = parents.get(parent); - children.add(child); - } + parents.putIfAbsent(parent, new HashSet<>()); + parents.get(parent).add(child); } // now parents is a hash of all of the IDs of groups that are parents @@ -714,27 +703,42 @@ protected void rethinkGroupCache(Context context, boolean flushQueries) throws S parent.getValue().addAll(myChildren); } - // empty out group2groupcache table - group2GroupCacheDAO.deleteAll(context); - - // write out new one + // write out new cache IN MEMORY ONLY and returns it + Set> newCache = new HashSet<>(); for (Map.Entry> parent : parents.entrySet()) { UUID key = parent.getKey(); - for (UUID child : parent.getValue()) { + newCache.add(Pair.of(key, child)); + } + } + return newCache; + } - Group parentGroup = find(context, key); - Group childGroup = find(context, child); + /** + * Regenerate the group cache AKA the group2groupcache table in the database - + * meant to be called when a group is added or removed from another group + * + * @param context The relevant DSpace Context. + * @param flushQueries flushQueries Flush all pending queries + * @throws SQLException An exception that provides information on a database access error or other errors. + */ + protected void rethinkGroupCache(Context context, boolean flushQueries) throws SQLException { + // current cache in the database + var oldCache = group2GroupCacheDAO.getCache(context); - if (parentGroup != null && childGroup != null && group2GroupCacheDAO - .find(context, parentGroup, childGroup) == null) { - Group2GroupCache group2GroupCache = group2GroupCacheDAO.create(context, new Group2GroupCache()); - group2GroupCache.setParent(parentGroup); - group2GroupCache.setChild(childGroup); - group2GroupCacheDAO.save(context, group2GroupCache); - } - } + // correct cache, computed from the Group table + var newCache = computeNewCache(context, flushQueries); + + var toDelete = SetUtils.difference(oldCache, newCache); + var toCreate = SetUtils.difference(newCache, oldCache); + + for (var pair : toDelete ) { + group2GroupCacheDAO.deleteFromCache(context, pair.getLeft(), pair.getRight()); + } + + for (var pair : toCreate ) { + group2GroupCacheDAO.addToCache(context, pair.getLeft(), pair.getRight()); } } diff --git a/dspace-api/src/main/java/org/dspace/eperson/dao/Group2GroupCacheDAO.java b/dspace-api/src/main/java/org/dspace/eperson/dao/Group2GroupCacheDAO.java index 7db569a59e2b..d41d52c7e618 100644 --- a/dspace-api/src/main/java/org/dspace/eperson/dao/Group2GroupCacheDAO.java +++ b/dspace-api/src/main/java/org/dspace/eperson/dao/Group2GroupCacheDAO.java @@ -9,7 +9,10 @@ import java.sql.SQLException; import java.util.List; +import java.util.Set; +import java.util.UUID; +import org.apache.commons.lang3.tuple.Pair; import org.dspace.core.Context; import org.dspace.core.GenericDAO; import org.dspace.eperson.Group; @@ -25,13 +28,74 @@ */ public interface Group2GroupCacheDAO extends GenericDAO { - public List findByParent(Context context, Group group) throws SQLException; + /** + * Returns the current cache table as a set of UUID pairs. + * @param context The relevant DSpace Context. + * @return Set of UUID pairs, where the first element is the parent UUID and the second one is the child UUID. + * @throws SQLException An exception that provides information on a database access error or other errors. + */ + Set> getCache(Context context) throws SQLException; - public List findByChildren(Context context, Iterable groups) throws SQLException; + /** + * Returns all cache entities that are children of a given parent Group entity. + * @param context The relevant DSpace Context. + * @param group Parent group to perform the search. + * @return List of cached groups that are children of the parent group. + * @throws SQLException An exception that provides information on a database access error or other errors. + */ + List findByParent(Context context, Group group) throws SQLException; - public Group2GroupCache findByParentAndChild(Context context, Group parent, Group child) throws SQLException; + /** + * Returns all cache entities that are parents of at least one group from a children groups list. + * @param context The relevant DSpace Context. + * @param groups Children groups to perform the search. + * @return List of cached groups that are parents of at least one group from the children groups list. + * @throws SQLException An exception that provides information on a database access error or other errors. + */ + List findByChildren(Context context, Iterable groups) throws SQLException; - public Group2GroupCache find(Context context, Group parent, Group child) throws SQLException; + /** + * Returns the cache entity given specific parent and child groups. + * @param context The relevant DSpace Context. + * @param parent Parent group. + * @param child Child gruoup. + * @return Cached group. + * @throws SQLException An exception that provides information on a database access error or other errors. + */ + Group2GroupCache findByParentAndChild(Context context, Group parent, Group child) throws SQLException; - public void deleteAll(Context context) throws SQLException; + /** + * Returns the cache entity given specific parent and child groups. + * @param context The relevant DSpace Context. + * @param parent Parent group. + * @param child Child gruoup. + * @return Cached group. + * @throws SQLException An exception that provides information on a database access error or other errors. + */ + Group2GroupCache find(Context context, Group parent, Group child) throws SQLException; + + /** + * Completely deletes the current cache table. + * @param context The relevant DSpace Context. + * @throws SQLException An exception that provides information on a database access error or other errors. + */ + void deleteAll(Context context) throws SQLException; + + /** + * Deletes a specific cache row given parent and child groups UUIDs. + * @param context The relevant DSpace Context. + * @param parent Parent group UUID. + * @param child Child group UUID. + * @throws SQLException An exception that provides information on a database access error or other errors. + */ + void deleteFromCache(Context context, UUID parent, UUID child) throws SQLException; + + /** + * Adds a single row to the cache table given parent and child groups UUIDs. + * @param context The relevant DSpace Context. + * @param parent Parent group UUID. + * @param child Child group UUID. + * @throws SQLException An exception that provides information on a database access error or other errors. + */ + void addToCache(Context context, UUID parent, UUID child) throws SQLException; } diff --git a/dspace-api/src/main/java/org/dspace/eperson/dao/impl/Group2GroupCacheDAOImpl.java b/dspace-api/src/main/java/org/dspace/eperson/dao/impl/Group2GroupCacheDAOImpl.java index 1cd359188ca3..adbd776ffab6 100644 --- a/dspace-api/src/main/java/org/dspace/eperson/dao/impl/Group2GroupCacheDAOImpl.java +++ b/dspace-api/src/main/java/org/dspace/eperson/dao/impl/Group2GroupCacheDAOImpl.java @@ -8,14 +8,18 @@ package org.dspace.eperson.dao.impl; import java.sql.SQLException; +import java.util.HashSet; import java.util.LinkedList; import java.util.List; +import java.util.Set; +import java.util.UUID; import jakarta.persistence.Query; import jakarta.persistence.criteria.CriteriaBuilder; import jakarta.persistence.criteria.CriteriaQuery; import jakarta.persistence.criteria.Predicate; import jakarta.persistence.criteria.Root; +import org.apache.commons.lang3.tuple.Pair; import org.dspace.core.AbstractHibernateDAO; import org.dspace.core.Context; import org.dspace.eperson.Group; @@ -35,6 +39,16 @@ protected Group2GroupCacheDAOImpl() { super(); } + @Override + public Set> getCache(Context context) throws SQLException { + Query query = createQuery( + context, + "SELECT new org.apache.commons.lang3.tuple.ImmutablePair(g.parent.id, g.child.id) FROM Group2GroupCache g" + ); + List> results = query.getResultList(); + return new HashSet>(results); + } + @Override public List findByParent(Context context, Group group) throws SQLException { CriteriaBuilder criteriaBuilder = getCriteriaBuilder(context); @@ -90,4 +104,24 @@ public Group2GroupCache find(Context context, Group parent, Group child) throws public void deleteAll(Context context) throws SQLException { createQuery(context, "delete from Group2GroupCache").executeUpdate(); } + + @Override + public void deleteFromCache(Context context, UUID parent, UUID child) throws SQLException { + Query query = getHibernateSession(context).createNativeQuery( + "delete from group2groupcache g WHERE g.parent_id = :parent AND g.child_id = :child" + ); + query.setParameter("parent", parent); + query.setParameter("child", child); + query.executeUpdate(); + } + + @Override + public void addToCache(Context context, UUID parent, UUID child) throws SQLException { + Query query = getHibernateSession(context).createNativeQuery( + "insert into group2groupcache (parent_id, child_id) VALUES (:parent, :child)" + ); + query.setParameter("parent", parent); + query.setParameter("child", child); + query.executeUpdate(); + } } From 5b8f7077b1004e550435f40ff690fdc82eb85921 Mon Sep 17 00:00:00 2001 From: autavares-dev Date: Thu, 8 Aug 2024 15:52:03 -0300 Subject: [PATCH 229/979] Refactor 'var' variables to explicit types (cherry picked from commit 743b7049cfeeabfd414c38526bcdbf2e52426739) --- .../java/org/dspace/eperson/GroupServiceImpl.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/eperson/GroupServiceImpl.java b/dspace-api/src/main/java/org/dspace/eperson/GroupServiceImpl.java index b52f17a5c692..4cec4c9c0d93 100644 --- a/dspace-api/src/main/java/org/dspace/eperson/GroupServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/eperson/GroupServiceImpl.java @@ -725,19 +725,19 @@ private Set> computeNewCache(Context context, boolean flushQuer */ protected void rethinkGroupCache(Context context, boolean flushQueries) throws SQLException { // current cache in the database - var oldCache = group2GroupCacheDAO.getCache(context); + Set> oldCache = group2GroupCacheDAO.getCache(context); // correct cache, computed from the Group table - var newCache = computeNewCache(context, flushQueries); + Set> newCache = computeNewCache(context, flushQueries); - var toDelete = SetUtils.difference(oldCache, newCache); - var toCreate = SetUtils.difference(newCache, oldCache); + SetUtils.SetView> toDelete = SetUtils.difference(oldCache, newCache); + SetUtils.SetView> toCreate = SetUtils.difference(newCache, oldCache); - for (var pair : toDelete ) { + for (Pair pair : toDelete ) { group2GroupCacheDAO.deleteFromCache(context, pair.getLeft(), pair.getRight()); } - for (var pair : toCreate ) { + for (Pair pair : toCreate ) { group2GroupCacheDAO.addToCache(context, pair.getLeft(), pair.getRight()); } } From 64cb3bda0034d8e3051c31290a3eeea8a560d939 Mon Sep 17 00:00:00 2001 From: autavares-dev Date: Wed, 7 Aug 2024 16:34:38 -0300 Subject: [PATCH 230/979] Changes Group2GroupCache computation --- .../org/dspace/eperson/Group2GroupCache.java | 3 +- .../org/dspace/eperson/GroupServiceImpl.java | 72 +++++++++--------- .../eperson/dao/Group2GroupCacheDAO.java | 74 +++++++++++++++++-- .../dao/impl/Group2GroupCacheDAOImpl.java | 34 +++++++++ 4 files changed, 143 insertions(+), 40 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/eperson/Group2GroupCache.java b/dspace-api/src/main/java/org/dspace/eperson/Group2GroupCache.java index 09bdf34d4cad..58781cd402fa 100644 --- a/dspace-api/src/main/java/org/dspace/eperson/Group2GroupCache.java +++ b/dspace-api/src/main/java/org/dspace/eperson/Group2GroupCache.java @@ -14,6 +14,7 @@ import javax.persistence.JoinColumn; import javax.persistence.ManyToOne; import javax.persistence.Table; +import javax.persistence.UniqueConstraint; import org.hibernate.proxy.HibernateProxyHelper; @@ -23,7 +24,7 @@ * @author kevinvandevelde at atmire.com */ @Entity -@Table(name = "group2groupcache") +@Table(name = "group2groupcache", uniqueConstraints = { @UniqueConstraint(columnNames = {"parent_id", "child_id"}) }) public class Group2GroupCache implements Serializable { @Id diff --git a/dspace-api/src/main/java/org/dspace/eperson/GroupServiceImpl.java b/dspace-api/src/main/java/org/dspace/eperson/GroupServiceImpl.java index 3fb20e2f1e6f..b52f17a5c692 100644 --- a/dspace-api/src/main/java/org/dspace/eperson/GroupServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/eperson/GroupServiceImpl.java @@ -20,6 +20,7 @@ import java.util.UUID; import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.collections4.SetUtils; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.tuple.Pair; import org.apache.logging.log4j.LogManager; @@ -673,15 +674,14 @@ protected boolean isEPersonInGroup(Context context, Group group, EPerson ePerson /** - * Regenerate the group cache AKA the group2groupcache table in the database - - * meant to be called when a group is added or removed from another group + * Returns a set with pairs of parent and child group UUIDs, representing the new cache table rows. * - * @param context The relevant DSpace Context. - * @param flushQueries flushQueries Flush all pending queries + * @param context The relevant DSpace Context. + * @param flushQueries flushQueries Flush all pending queries + * @return Pairs of parent and child group UUID of the new cache. * @throws SQLException An exception that provides information on a database access error or other errors. */ - protected void rethinkGroupCache(Context context, boolean flushQueries) throws SQLException { - + private Set> computeNewCache(Context context, boolean flushQueries) throws SQLException { Map> parents = new HashMap<>(); List> group2groupResults = groupDAO.getGroup2GroupResults(context, flushQueries); @@ -689,19 +689,8 @@ protected void rethinkGroupCache(Context context, boolean flushQueries) throws S UUID parent = group2groupResult.getLeft(); UUID child = group2groupResult.getRight(); - // if parent doesn't have an entry, create one - if (!parents.containsKey(parent)) { - Set children = new HashSet<>(); - - // add child id to the list - children.add(child); - parents.put(parent, children); - } else { - // parent has an entry, now add the child to the parent's record - // of children - Set children = parents.get(parent); - children.add(child); - } + parents.putIfAbsent(parent, new HashSet<>()); + parents.get(parent).add(child); } // now parents is a hash of all of the IDs of groups that are parents @@ -714,27 +703,42 @@ protected void rethinkGroupCache(Context context, boolean flushQueries) throws S parent.getValue().addAll(myChildren); } - // empty out group2groupcache table - group2GroupCacheDAO.deleteAll(context); - - // write out new one + // write out new cache IN MEMORY ONLY and returns it + Set> newCache = new HashSet<>(); for (Map.Entry> parent : parents.entrySet()) { UUID key = parent.getKey(); - for (UUID child : parent.getValue()) { + newCache.add(Pair.of(key, child)); + } + } + return newCache; + } - Group parentGroup = find(context, key); - Group childGroup = find(context, child); + /** + * Regenerate the group cache AKA the group2groupcache table in the database - + * meant to be called when a group is added or removed from another group + * + * @param context The relevant DSpace Context. + * @param flushQueries flushQueries Flush all pending queries + * @throws SQLException An exception that provides information on a database access error or other errors. + */ + protected void rethinkGroupCache(Context context, boolean flushQueries) throws SQLException { + // current cache in the database + var oldCache = group2GroupCacheDAO.getCache(context); - if (parentGroup != null && childGroup != null && group2GroupCacheDAO - .find(context, parentGroup, childGroup) == null) { - Group2GroupCache group2GroupCache = group2GroupCacheDAO.create(context, new Group2GroupCache()); - group2GroupCache.setParent(parentGroup); - group2GroupCache.setChild(childGroup); - group2GroupCacheDAO.save(context, group2GroupCache); - } - } + // correct cache, computed from the Group table + var newCache = computeNewCache(context, flushQueries); + + var toDelete = SetUtils.difference(oldCache, newCache); + var toCreate = SetUtils.difference(newCache, oldCache); + + for (var pair : toDelete ) { + group2GroupCacheDAO.deleteFromCache(context, pair.getLeft(), pair.getRight()); + } + + for (var pair : toCreate ) { + group2GroupCacheDAO.addToCache(context, pair.getLeft(), pair.getRight()); } } diff --git a/dspace-api/src/main/java/org/dspace/eperson/dao/Group2GroupCacheDAO.java b/dspace-api/src/main/java/org/dspace/eperson/dao/Group2GroupCacheDAO.java index 7db569a59e2b..d41d52c7e618 100644 --- a/dspace-api/src/main/java/org/dspace/eperson/dao/Group2GroupCacheDAO.java +++ b/dspace-api/src/main/java/org/dspace/eperson/dao/Group2GroupCacheDAO.java @@ -9,7 +9,10 @@ import java.sql.SQLException; import java.util.List; +import java.util.Set; +import java.util.UUID; +import org.apache.commons.lang3.tuple.Pair; import org.dspace.core.Context; import org.dspace.core.GenericDAO; import org.dspace.eperson.Group; @@ -25,13 +28,74 @@ */ public interface Group2GroupCacheDAO extends GenericDAO { - public List findByParent(Context context, Group group) throws SQLException; + /** + * Returns the current cache table as a set of UUID pairs. + * @param context The relevant DSpace Context. + * @return Set of UUID pairs, where the first element is the parent UUID and the second one is the child UUID. + * @throws SQLException An exception that provides information on a database access error or other errors. + */ + Set> getCache(Context context) throws SQLException; - public List findByChildren(Context context, Iterable groups) throws SQLException; + /** + * Returns all cache entities that are children of a given parent Group entity. + * @param context The relevant DSpace Context. + * @param group Parent group to perform the search. + * @return List of cached groups that are children of the parent group. + * @throws SQLException An exception that provides information on a database access error or other errors. + */ + List findByParent(Context context, Group group) throws SQLException; - public Group2GroupCache findByParentAndChild(Context context, Group parent, Group child) throws SQLException; + /** + * Returns all cache entities that are parents of at least one group from a children groups list. + * @param context The relevant DSpace Context. + * @param groups Children groups to perform the search. + * @return List of cached groups that are parents of at least one group from the children groups list. + * @throws SQLException An exception that provides information on a database access error or other errors. + */ + List findByChildren(Context context, Iterable groups) throws SQLException; - public Group2GroupCache find(Context context, Group parent, Group child) throws SQLException; + /** + * Returns the cache entity given specific parent and child groups. + * @param context The relevant DSpace Context. + * @param parent Parent group. + * @param child Child gruoup. + * @return Cached group. + * @throws SQLException An exception that provides information on a database access error or other errors. + */ + Group2GroupCache findByParentAndChild(Context context, Group parent, Group child) throws SQLException; - public void deleteAll(Context context) throws SQLException; + /** + * Returns the cache entity given specific parent and child groups. + * @param context The relevant DSpace Context. + * @param parent Parent group. + * @param child Child gruoup. + * @return Cached group. + * @throws SQLException An exception that provides information on a database access error or other errors. + */ + Group2GroupCache find(Context context, Group parent, Group child) throws SQLException; + + /** + * Completely deletes the current cache table. + * @param context The relevant DSpace Context. + * @throws SQLException An exception that provides information on a database access error or other errors. + */ + void deleteAll(Context context) throws SQLException; + + /** + * Deletes a specific cache row given parent and child groups UUIDs. + * @param context The relevant DSpace Context. + * @param parent Parent group UUID. + * @param child Child group UUID. + * @throws SQLException An exception that provides information on a database access error or other errors. + */ + void deleteFromCache(Context context, UUID parent, UUID child) throws SQLException; + + /** + * Adds a single row to the cache table given parent and child groups UUIDs. + * @param context The relevant DSpace Context. + * @param parent Parent group UUID. + * @param child Child group UUID. + * @throws SQLException An exception that provides information on a database access error or other errors. + */ + void addToCache(Context context, UUID parent, UUID child) throws SQLException; } diff --git a/dspace-api/src/main/java/org/dspace/eperson/dao/impl/Group2GroupCacheDAOImpl.java b/dspace-api/src/main/java/org/dspace/eperson/dao/impl/Group2GroupCacheDAOImpl.java index 83fb48aaf03d..42ca2bb5d48c 100644 --- a/dspace-api/src/main/java/org/dspace/eperson/dao/impl/Group2GroupCacheDAOImpl.java +++ b/dspace-api/src/main/java/org/dspace/eperson/dao/impl/Group2GroupCacheDAOImpl.java @@ -8,14 +8,18 @@ package org.dspace.eperson.dao.impl; import java.sql.SQLException; +import java.util.HashSet; import java.util.LinkedList; import java.util.List; +import java.util.Set; +import java.util.UUID; import javax.persistence.Query; import javax.persistence.criteria.CriteriaBuilder; import javax.persistence.criteria.CriteriaQuery; import javax.persistence.criteria.Predicate; import javax.persistence.criteria.Root; +import org.apache.commons.lang3.tuple.Pair; import org.dspace.core.AbstractHibernateDAO; import org.dspace.core.Context; import org.dspace.eperson.Group; @@ -35,6 +39,16 @@ protected Group2GroupCacheDAOImpl() { super(); } + @Override + public Set> getCache(Context context) throws SQLException { + Query query = createQuery( + context, + "SELECT new org.apache.commons.lang3.tuple.ImmutablePair(g.parent.id, g.child.id) FROM Group2GroupCache g" + ); + List> results = query.getResultList(); + return new HashSet>(results); + } + @Override public List findByParent(Context context, Group group) throws SQLException { CriteriaBuilder criteriaBuilder = getCriteriaBuilder(context); @@ -90,4 +104,24 @@ public Group2GroupCache find(Context context, Group parent, Group child) throws public void deleteAll(Context context) throws SQLException { createQuery(context, "delete from Group2GroupCache").executeUpdate(); } + + @Override + public void deleteFromCache(Context context, UUID parent, UUID child) throws SQLException { + Query query = getHibernateSession(context).createNativeQuery( + "delete from group2groupcache g WHERE g.parent_id = :parent AND g.child_id = :child" + ); + query.setParameter("parent", parent); + query.setParameter("child", child); + query.executeUpdate(); + } + + @Override + public void addToCache(Context context, UUID parent, UUID child) throws SQLException { + Query query = getHibernateSession(context).createNativeQuery( + "insert into group2groupcache (parent_id, child_id) VALUES (:parent, :child)" + ); + query.setParameter("parent", parent); + query.setParameter("child", child); + query.executeUpdate(); + } } From 05bac14716e1b011ab56abc7b9d662e9d689e546 Mon Sep 17 00:00:00 2001 From: autavares-dev Date: Thu, 8 Aug 2024 15:52:03 -0300 Subject: [PATCH 231/979] Refactor 'var' variables to explicit types --- .../java/org/dspace/eperson/GroupServiceImpl.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/eperson/GroupServiceImpl.java b/dspace-api/src/main/java/org/dspace/eperson/GroupServiceImpl.java index b52f17a5c692..4cec4c9c0d93 100644 --- a/dspace-api/src/main/java/org/dspace/eperson/GroupServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/eperson/GroupServiceImpl.java @@ -725,19 +725,19 @@ private Set> computeNewCache(Context context, boolean flushQuer */ protected void rethinkGroupCache(Context context, boolean flushQueries) throws SQLException { // current cache in the database - var oldCache = group2GroupCacheDAO.getCache(context); + Set> oldCache = group2GroupCacheDAO.getCache(context); // correct cache, computed from the Group table - var newCache = computeNewCache(context, flushQueries); + Set> newCache = computeNewCache(context, flushQueries); - var toDelete = SetUtils.difference(oldCache, newCache); - var toCreate = SetUtils.difference(newCache, oldCache); + SetUtils.SetView> toDelete = SetUtils.difference(oldCache, newCache); + SetUtils.SetView> toCreate = SetUtils.difference(newCache, oldCache); - for (var pair : toDelete ) { + for (Pair pair : toDelete ) { group2GroupCacheDAO.deleteFromCache(context, pair.getLeft(), pair.getRight()); } - for (var pair : toCreate ) { + for (Pair pair : toCreate ) { group2GroupCacheDAO.addToCache(context, pair.getLeft(), pair.getRight()); } } From aa027aefae2e39558273fcdbd511c288f8b0def3 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Tue, 17 Dec 2024 13:11:43 -0600 Subject: [PATCH 232/979] Improve Apache Ant download process. Switch to using curl so that we can retry the request if it initially fails. (cherry picked from commit e236634a4c758a6c0d81e929b2f36d49e81b0835) --- Dockerfile | 10 ++++------ Dockerfile.cli | 10 ++++------ Dockerfile.test | 10 ++++------ 3 files changed, 12 insertions(+), 18 deletions(-) diff --git a/Dockerfile b/Dockerfile index ff35f2a2e3a7..102f00abe1f7 100644 --- a/Dockerfile +++ b/Dockerfile @@ -42,14 +42,12 @@ WORKDIR /dspace-src ENV ANT_VERSION=1.10.13 ENV ANT_HOME=/tmp/ant-$ANT_VERSION ENV PATH=$ANT_HOME/bin:$PATH -# Need wget to install ant -RUN apt-get update \ - && apt-get install -y --no-install-recommends wget \ - && apt-get purge -y --auto-remove \ - && rm -rf /var/lib/apt/lists/* # Download and install 'ant' RUN mkdir $ANT_HOME && \ - wget -qO- "https://archive.apache.org/dist/ant/binaries/apache-ant-$ANT_VERSION-bin.tar.gz" | tar -zx --strip-components=1 -C $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 # Run necessary 'ant' deploy scripts RUN ant init_installation update_configs update_code update_webapps diff --git a/Dockerfile.cli b/Dockerfile.cli index be03e8922b2c..e208e3d92145 100644 --- a/Dockerfile.cli +++ b/Dockerfile.cli @@ -38,14 +38,12 @@ WORKDIR /dspace-src ENV ANT_VERSION=1.10.13 ENV ANT_HOME=/tmp/ant-$ANT_VERSION ENV PATH=$ANT_HOME/bin:$PATH -# Need wget to install ant -RUN apt-get update \ - && apt-get install -y --no-install-recommends wget \ - && apt-get purge -y --auto-remove \ - && rm -rf /var/lib/apt/lists/* # Download and install 'ant' RUN mkdir $ANT_HOME && \ - wget -qO- "https://archive.apache.org/dist/ant/binaries/apache-ant-$ANT_VERSION-bin.tar.gz" | tar -zx --strip-components=1 -C $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 # Run necessary 'ant' deploy scripts RUN ant init_installation update_configs update_code diff --git a/Dockerfile.test b/Dockerfile.test index 08b6b3018b80..cc73c655b637 100644 --- a/Dockerfile.test +++ b/Dockerfile.test @@ -41,14 +41,12 @@ WORKDIR /dspace-src ENV ANT_VERSION=1.10.12 ENV ANT_HOME=/tmp/ant-$ANT_VERSION ENV PATH=$ANT_HOME/bin:$PATH -# Need wget to install ant -RUN apt-get update \ - && apt-get install -y --no-install-recommends wget \ - && apt-get purge -y --auto-remove \ - && rm -rf /var/lib/apt/lists/* # Download and install 'ant' RUN mkdir $ANT_HOME && \ - wget -qO- "https://archive.apache.org/dist/ant/binaries/apache-ant-$ANT_VERSION-bin.tar.gz" | tar -zx --strip-components=1 -C $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 # Run necessary 'ant' deploy scripts RUN ant init_installation update_configs update_code update_webapps From 58af9fd224ba37a8f3a9c8b354c04d772d6f3ee2 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Tue, 17 Dec 2024 14:13:21 -0600 Subject: [PATCH 233/979] Significantly speed up build of dspace-dependencies by only copying over POM files (cherry picked from commit 6d7a3fcb725bbb5e42790ba5c57292477f2f3f01) --- Dockerfile.dependencies | 58 ++++++++++++++++++++++++++++++++++++----- 1 file changed, 51 insertions(+), 7 deletions(-) diff --git a/Dockerfile.dependencies b/Dockerfile.dependencies index 794dfa9a66a8..d3ca9d5e3c87 100644 --- a/Dockerfile.dependencies +++ b/Dockerfile.dependencies @@ -6,7 +6,7 @@ # To build with JDK17, use "--build-arg JDK_VERSION=17" ARG JDK_VERSION=11 -# Step 1 - Run Maven Build +# Step 1 - Download all Dependencies FROM docker.io/maven:3-eclipse-temurin-${JDK_VERSION} AS build ARG TARGET_DIR=dspace-installer WORKDIR /app @@ -19,16 +19,60 @@ RUN chown -Rv dspace: /app # Switch to dspace user & run below commands as that user USER dspace -# Copy the DSpace source code (from local machine) into the workdir (excluding .dockerignore contents) -ADD --chown=dspace . /app/ +# This next part may look odd, but it speeds up the build of this image *significantly*. +# Copy ONLY the POMs to this image (from local machine). This will allow us to download all dependencies *without* +# performing any code compilation steps. + +# Parent POM +ADD --chown=dspace pom.xml /app/ +RUN mkdir -p /app/dspace + +# 'dspace' module POM. Includes 'additions' ONLY, as it's the only submodule that is required to exist. +ADD --chown=dspace dspace/pom.xml /app/dspace/ +RUN mkdir -p /app/dspace/modules/ +ADD --chown=dspace dspace/modules/pom.xml /app/dspace/modules/ +RUN mkdir -p /app/dspace/modules/additions +ADD --chown=dspace dspace/modules/additions/pom.xml /app/dspace/modules/additions/ + +# 'dspace-api' module POM +RUN mkdir -p /app/dspace-api +ADD --chown=dspace dspace-api/pom.xml /app/dspace-api/ + +# 'dspace-iiif' module POM +RUN mkdir -p /app/dspace-iiif +ADD --chown=dspace dspace-iiif/pom.xml /app/dspace-iiif/ + +# 'dspace-oai' module POM +RUN mkdir -p /app/dspace-oai +ADD --chown=dspace dspace-oai/pom.xml /app/dspace-oai/ + +# 'dspace-rdf' module POM +RUN mkdir -p /app/dspace-rdf +ADD --chown=dspace dspace-rdf/pom.xml /app/dspace-rdf/ + +# 'dspace-server-webapp' module POM +RUN mkdir -p /app/dspace-server-webapp +ADD --chown=dspace dspace-server-webapp/pom.xml /app/dspace-server-webapp/ + +# 'dspace-services' module POM +RUN mkdir -p /app/dspace-services +ADD --chown=dspace dspace-services/pom.xml /app/dspace-services/ + +# 'dspace-sword' module POM +RUN mkdir -p /app/dspace-sword +ADD --chown=dspace dspace-sword/pom.xml /app/dspace-sword/ + +# 'dspace-swordv2' module POM +RUN mkdir -p /app/dspace-swordv2 +ADD --chown=dspace dspace-swordv2/pom.xml /app/dspace-swordv2/ # Trigger the installation of all maven dependencies (hide download progress messages) # Maven flags here ensure that we skip final assembly, skip building test environment and skip all code verification checks. -# These flags speed up this installation as much as reasonably possible. -ENV MAVEN_FLAGS="-P-assembly -P-test-environment -Denforcer.skip=true -Dcheckstyle.skip=true -Dlicense.skip=true -Dxml.skip=true" -RUN mvn --no-transfer-progress install ${MAVEN_FLAGS} +# These flags speed up this installation and skip tasks we cannot perform as we don't have the full source code. +ENV MAVEN_FLAGS="-P-assembly -P-test-environment -Denforcer.skip=true -Dcheckstyle.skip=true -Dlicense.skip=true -Dxjc.skip=true -Dxml.skip=true" +RUN mvn --no-transfer-progress verify ${MAVEN_FLAGS} -# Clear the contents of the /app directory (including all maven builds), so no artifacts remain. +# Clear the contents of the /app directory (including all maven target folders), so no artifacts remain. # This ensures when dspace:dspace is built, it will use the Maven local cache (~/.m2) for dependencies USER root RUN rm -rf /app/* From 3c4fd4274aebf882cf1233a8d5f2eb8a7b1e8e24 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Tue, 17 Dec 2024 13:11:43 -0600 Subject: [PATCH 234/979] Improve Apache Ant download process. Switch to using curl so that we can retry the request if it initially fails. (cherry picked from commit e236634a4c758a6c0d81e929b2f36d49e81b0835) --- Dockerfile | 10 ++++------ Dockerfile.cli | 10 ++++------ Dockerfile.test | 10 ++++------ 3 files changed, 12 insertions(+), 18 deletions(-) diff --git a/Dockerfile b/Dockerfile index 0b844a1b26c2..eb90299ccd7d 100644 --- a/Dockerfile +++ b/Dockerfile @@ -44,14 +44,12 @@ WORKDIR /dspace-src ENV ANT_VERSION=1.10.13 ENV ANT_HOME=/tmp/ant-$ANT_VERSION ENV PATH=$ANT_HOME/bin:$PATH -# Need wget to install ant -RUN apt-get update \ - && apt-get install -y --no-install-recommends wget \ - && apt-get purge -y --auto-remove \ - && rm -rf /var/lib/apt/lists/* # Download and install 'ant' RUN mkdir $ANT_HOME && \ - wget -qO- "https://archive.apache.org/dist/ant/binaries/apache-ant-$ANT_VERSION-bin.tar.gz" | tar -zx --strip-components=1 -C $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 # Run necessary 'ant' deploy scripts RUN ant init_installation update_configs update_code update_webapps diff --git a/Dockerfile.cli b/Dockerfile.cli index b0b056dfc99e..1f08357b3bba 100644 --- a/Dockerfile.cli +++ b/Dockerfile.cli @@ -38,14 +38,12 @@ WORKDIR /dspace-src ENV ANT_VERSION=1.10.13 ENV ANT_HOME=/tmp/ant-$ANT_VERSION ENV PATH=$ANT_HOME/bin:$PATH -# Need wget to install ant -RUN apt-get update \ - && apt-get install -y --no-install-recommends wget \ - && apt-get purge -y --auto-remove \ - && rm -rf /var/lib/apt/lists/* # Download and install 'ant' RUN mkdir $ANT_HOME && \ - wget -qO- "https://archive.apache.org/dist/ant/binaries/apache-ant-$ANT_VERSION-bin.tar.gz" | tar -zx --strip-components=1 -C $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 # Run necessary 'ant' deploy scripts RUN ant init_installation update_configs update_code diff --git a/Dockerfile.test b/Dockerfile.test index 828d61e74708..1c36b31b66bb 100644 --- a/Dockerfile.test +++ b/Dockerfile.test @@ -43,14 +43,12 @@ WORKDIR /dspace-src ENV ANT_VERSION=1.10.12 ENV ANT_HOME=/tmp/ant-$ANT_VERSION ENV PATH=$ANT_HOME/bin:$PATH -# Need wget to install ant -RUN apt-get update \ - && apt-get install -y --no-install-recommends wget \ - && apt-get purge -y --auto-remove \ - && rm -rf /var/lib/apt/lists/* # Download and install 'ant' RUN mkdir $ANT_HOME && \ - wget -qO- "https://archive.apache.org/dist/ant/binaries/apache-ant-$ANT_VERSION-bin.tar.gz" | tar -zx --strip-components=1 -C $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 # Run necessary 'ant' deploy scripts RUN ant init_installation update_configs update_code update_webapps From 6792b8274de485c27801afb382ae9840f68081ec Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Tue, 17 Dec 2024 14:13:21 -0600 Subject: [PATCH 235/979] Significantly speed up build of dspace-dependencies by only copying over POM files (cherry picked from commit 6d7a3fcb725bbb5e42790ba5c57292477f2f3f01) --- Dockerfile.dependencies | 58 ++++++++++++++++++++++++++++++++++++----- 1 file changed, 51 insertions(+), 7 deletions(-) diff --git a/Dockerfile.dependencies b/Dockerfile.dependencies index 34bfbc3b8c78..04233cd415fa 100644 --- a/Dockerfile.dependencies +++ b/Dockerfile.dependencies @@ -6,7 +6,7 @@ # To build with other versions, use "--build-arg JDK_VERSION=[value]" ARG JDK_VERSION=17 -# Step 1 - Run Maven Build +# Step 1 - Download all Dependencies FROM docker.io/maven:3-eclipse-temurin-${JDK_VERSION} AS build ARG TARGET_DIR=dspace-installer WORKDIR /app @@ -19,16 +19,60 @@ RUN chown -Rv dspace: /app # Switch to dspace user & run below commands as that user USER dspace -# Copy the DSpace source code (from local machine) into the workdir (excluding .dockerignore contents) -ADD --chown=dspace . /app/ +# This next part may look odd, but it speeds up the build of this image *significantly*. +# Copy ONLY the POMs to this image (from local machine). This will allow us to download all dependencies *without* +# performing any code compilation steps. + +# Parent POM +ADD --chown=dspace pom.xml /app/ +RUN mkdir -p /app/dspace + +# 'dspace' module POM. Includes 'additions' ONLY, as it's the only submodule that is required to exist. +ADD --chown=dspace dspace/pom.xml /app/dspace/ +RUN mkdir -p /app/dspace/modules/ +ADD --chown=dspace dspace/modules/pom.xml /app/dspace/modules/ +RUN mkdir -p /app/dspace/modules/additions +ADD --chown=dspace dspace/modules/additions/pom.xml /app/dspace/modules/additions/ + +# 'dspace-api' module POM +RUN mkdir -p /app/dspace-api +ADD --chown=dspace dspace-api/pom.xml /app/dspace-api/ + +# 'dspace-iiif' module POM +RUN mkdir -p /app/dspace-iiif +ADD --chown=dspace dspace-iiif/pom.xml /app/dspace-iiif/ + +# 'dspace-oai' module POM +RUN mkdir -p /app/dspace-oai +ADD --chown=dspace dspace-oai/pom.xml /app/dspace-oai/ + +# 'dspace-rdf' module POM +RUN mkdir -p /app/dspace-rdf +ADD --chown=dspace dspace-rdf/pom.xml /app/dspace-rdf/ + +# 'dspace-server-webapp' module POM +RUN mkdir -p /app/dspace-server-webapp +ADD --chown=dspace dspace-server-webapp/pom.xml /app/dspace-server-webapp/ + +# 'dspace-services' module POM +RUN mkdir -p /app/dspace-services +ADD --chown=dspace dspace-services/pom.xml /app/dspace-services/ + +# 'dspace-sword' module POM +RUN mkdir -p /app/dspace-sword +ADD --chown=dspace dspace-sword/pom.xml /app/dspace-sword/ + +# 'dspace-swordv2' module POM +RUN mkdir -p /app/dspace-swordv2 +ADD --chown=dspace dspace-swordv2/pom.xml /app/dspace-swordv2/ # Trigger the installation of all maven dependencies (hide download progress messages) # Maven flags here ensure that we skip final assembly, skip building test environment and skip all code verification checks. -# These flags speed up this installation as much as reasonably possible. -ENV MAVEN_FLAGS="-P-assembly -P-test-environment -Denforcer.skip=true -Dcheckstyle.skip=true -Dlicense.skip=true -Dxml.skip=true" -RUN mvn --no-transfer-progress install ${MAVEN_FLAGS} +# These flags speed up this installation and skip tasks we cannot perform as we don't have the full source code. +ENV MAVEN_FLAGS="-P-assembly -P-test-environment -Denforcer.skip=true -Dcheckstyle.skip=true -Dlicense.skip=true -Dxjc.skip=true -Dxml.skip=true" +RUN mvn --no-transfer-progress verify ${MAVEN_FLAGS} -# Clear the contents of the /app directory (including all maven builds), so no artifacts remain. +# Clear the contents of the /app directory (including all maven target folders), so no artifacts remain. # This ensures when dspace:dspace is built, it will use the Maven local cache (~/.m2) for dependencies USER root RUN rm -rf /app/* From e5568157c87370531e6b86cbf582e160504858fb Mon Sep 17 00:00:00 2001 From: "Mark H. Wood" Date: Tue, 17 Sep 2024 08:47:54 -0400 Subject: [PATCH 236/979] More information about failed DOI registrations. (cherry picked from commit b8f4ab0eb3e90b080eaae057fb5ee673d3477dc2) --- .../dspace/identifier/doi/DOIOrganiser.java | 3 ++- .../identifier/doi/DataCiteConnector.java | 22 ++++++++++++++++++- 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/identifier/doi/DOIOrganiser.java b/dspace-api/src/main/java/org/dspace/identifier/doi/DOIOrganiser.java index 088e2b1cbc87..12f6d7c4fca4 100644 --- a/dspace-api/src/main/java/org/dspace/identifier/doi/DOIOrganiser.java +++ b/dspace-api/src/main/java/org/dspace/identifier/doi/DOIOrganiser.java @@ -577,7 +577,8 @@ public void update(DOI doiRow) { } } catch (IdentifierException ex) { if (!(ex instanceof DOIIdentifierException)) { - LOG.error("It wasn't possible to register the identifier online. ", ex); + LOG.error("Registering DOI {} for object {}: the registrar returned an error.", + doiRow.getDoi(), dso.getID(), ex); } DOIIdentifierException doiIdentifierException = (DOIIdentifierException) ex; diff --git a/dspace-api/src/main/java/org/dspace/identifier/doi/DataCiteConnector.java b/dspace-api/src/main/java/org/dspace/identifier/doi/DataCiteConnector.java index a15e3f7fdbfe..5bb37add2d9a 100644 --- a/dspace-api/src/main/java/org/dspace/identifier/doi/DataCiteConnector.java +++ b/dspace-api/src/main/java/org/dspace/identifier/doi/DataCiteConnector.java @@ -464,6 +464,10 @@ public void reserveDOI(Context context, DSpaceObject dso, String doi) log.warn("While reserving the DOI {}, we got a http status code " + "{} and the message \"{}\".", doi, Integer.toString(resp.statusCode), resp.getContent()); + Format format = Format.getCompactFormat(); + format.setEncoding("UTF-8"); + XMLOutputter xout = new XMLOutputter(format); + log.info("We send the following XML:\n{}", xout.outputString(root)); throw new DOIIdentifierException("Unable to parse an answer from " + "DataCite API. Please have a look into DSpace logs.", DOIIdentifierException.BAD_ANSWER); @@ -635,6 +639,14 @@ protected DataCiteResponse sendGetRequest(String doi, String path) return sendHttpRequest(httpget, doi); } + /** + * Send a DataCite metadata document to the registrar. + * + * @param doi identify the object. + * @param metadataRoot describe the object. The root element of the document. + * @return the registrar's response. + * @throws DOIIdentifierException passed through. + */ protected DataCiteResponse sendMetadataPostRequest(String doi, Element metadataRoot) throws DOIIdentifierException { Format format = Format.getCompactFormat(); @@ -643,6 +655,14 @@ protected DataCiteResponse sendMetadataPostRequest(String doi, Element metadataR return sendMetadataPostRequest(doi, xout.outputString(new Document(metadataRoot))); } + /** + * Send a DataCite metadata document to the registrar. + * + * @param doi identify the object. + * @param metadata describe the object. + * @return the registrar's response. + * @throws DOIIdentifierException passed through. + */ protected DataCiteResponse sendMetadataPostRequest(String doi, String metadata) throws DOIIdentifierException { // post mds/metadata/ @@ -690,7 +710,7 @@ protected DataCiteResponse sendMetadataPostRequest(String doi, String metadata) * properties such as request URI and method type. * @param doi DOI string to operate on * @return response from DataCite - * @throws DOIIdentifierException if DOI error + * @throws DOIIdentifierException if registrar returns an error. */ protected DataCiteResponse sendHttpRequest(HttpUriRequest req, String doi) throws DOIIdentifierException { From 8b468a171635bfdfeaf9530ba59cc7ed541070f3 Mon Sep 17 00:00:00 2001 From: "Mark H. Wood" Date: Tue, 17 Sep 2024 08:47:54 -0400 Subject: [PATCH 237/979] More information about failed DOI registrations. (cherry picked from commit b8f4ab0eb3e90b080eaae057fb5ee673d3477dc2) --- .../dspace/identifier/doi/DOIOrganiser.java | 3 ++- .../identifier/doi/DataCiteConnector.java | 22 ++++++++++++++++++- 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/identifier/doi/DOIOrganiser.java b/dspace-api/src/main/java/org/dspace/identifier/doi/DOIOrganiser.java index 78507a0edf13..05769e94bce5 100644 --- a/dspace-api/src/main/java/org/dspace/identifier/doi/DOIOrganiser.java +++ b/dspace-api/src/main/java/org/dspace/identifier/doi/DOIOrganiser.java @@ -577,7 +577,8 @@ public void update(DOI doiRow) { } } catch (IdentifierException ex) { if (!(ex instanceof DOIIdentifierException)) { - LOG.error("It wasn't possible to register the identifier online. ", ex); + LOG.error("Registering DOI {} for object {}: the registrar returned an error.", + doiRow.getDoi(), dso.getID(), ex); } DOIIdentifierException doiIdentifierException = (DOIIdentifierException) ex; diff --git a/dspace-api/src/main/java/org/dspace/identifier/doi/DataCiteConnector.java b/dspace-api/src/main/java/org/dspace/identifier/doi/DataCiteConnector.java index b4cdac96303a..3fc55ec146c7 100644 --- a/dspace-api/src/main/java/org/dspace/identifier/doi/DataCiteConnector.java +++ b/dspace-api/src/main/java/org/dspace/identifier/doi/DataCiteConnector.java @@ -461,6 +461,10 @@ public void reserveDOI(Context context, DSpaceObject dso, String doi) log.warn("While reserving the DOI {}, we got a http status code " + "{} and the message \"{}\".", doi, Integer.toString(resp.statusCode), resp.getContent()); + Format format = Format.getCompactFormat(); + format.setEncoding("UTF-8"); + XMLOutputter xout = new XMLOutputter(format); + log.info("We send the following XML:\n{}", xout.outputString(root)); throw new DOIIdentifierException("Unable to parse an answer from " + "DataCite API. Please have a look into DSpace logs.", DOIIdentifierException.BAD_ANSWER); @@ -632,6 +636,14 @@ protected DataCiteResponse sendGetRequest(String doi, String path) return sendHttpRequest(httpget, doi); } + /** + * Send a DataCite metadata document to the registrar. + * + * @param doi identify the object. + * @param metadataRoot describe the object. The root element of the document. + * @return the registrar's response. + * @throws DOIIdentifierException passed through. + */ protected DataCiteResponse sendMetadataPostRequest(String doi, Element metadataRoot) throws DOIIdentifierException { Format format = Format.getCompactFormat(); @@ -640,6 +652,14 @@ protected DataCiteResponse sendMetadataPostRequest(String doi, Element metadataR return sendMetadataPostRequest(doi, xout.outputString(new Document(metadataRoot))); } + /** + * Send a DataCite metadata document to the registrar. + * + * @param doi identify the object. + * @param metadata describe the object. + * @return the registrar's response. + * @throws DOIIdentifierException passed through. + */ protected DataCiteResponse sendMetadataPostRequest(String doi, String metadata) throws DOIIdentifierException { // post mds/metadata/ @@ -687,7 +707,7 @@ protected DataCiteResponse sendMetadataPostRequest(String doi, String metadata) * properties such as request URI and method type. * @param doi DOI string to operate on * @return response from DataCite - * @throws DOIIdentifierException if DOI error + * @throws DOIIdentifierException if registrar returns an error. */ protected DataCiteResponse sendHttpRequest(HttpUriRequest req, String doi) throws DOIIdentifierException { From fcc650e1a6fbc1311e905a349fc5ed3957dd1db7 Mon Sep 17 00:00:00 2001 From: Toni Prieto Date: Sat, 26 Oct 2024 23:33:13 +0200 Subject: [PATCH 238/979] Add limit, offset, and a new parameter to calculate the total entry count in the Solr query used for the metadata navigation index (cherry picked from commit e71de8a4d075d897f68c145233ac19ba0ec3718b) --- .../java/org/dspace/browse/BrowseEngine.java | 30 ++-------------- .../java/org/dspace/browse/SolrBrowseDAO.java | 32 ++++++++++++----- .../org/dspace/discovery/DiscoverResult.java | 11 ++++++ .../org/dspace/discovery/SolrServiceImpl.java | 36 ++++++++++++++++++- 4 files changed, 73 insertions(+), 36 deletions(-) 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 351c36248209..be7a34086a46 100644 --- a/dspace-api/src/main/java/org/dspace/browse/BrowseEngine.java +++ b/dspace-api/src/main/java/org/dspace/browse/BrowseEngine.java @@ -422,9 +422,6 @@ private BrowseInfo browseByValue(BrowserScope bs) } } - // this is the total number of results in answer to the query - int total = getTotalResults(true); - // set the ordering field (there is only one option) dao.setOrderField("sort_value"); @@ -444,6 +441,9 @@ private BrowseInfo browseByValue(BrowserScope bs) dao.setOffset(offset); dao.setLimit(scope.getResultsPerPage()); + // this is the total number of results in answer to the query + int total = getTotalResults(true); + // Holder for the results List results = null; @@ -680,33 +680,9 @@ private int getTotalResults(boolean distinct) // tell the browse query whether we are distinct dao.setDistinct(distinct); - // ensure that the select is set to "*" - String[] select = {"*"}; - dao.setCountValues(select); - - // FIXME: it would be nice to have a good way of doing this in the DAO - // now reset all of the fields that we don't want to have constraining - // our count, storing them locally to reinstate later - String focusField = dao.getJumpToField(); - String focusValue = dao.getJumpToValue(); - int limit = dao.getLimit(); - int offset = dao.getOffset(); - - dao.setJumpToField(null); - dao.setJumpToValue(null); - dao.setLimit(-1); - dao.setOffset(-1); - // perform the query and get the result int count = dao.doCountQuery(); - // now put back the values we removed for this method - dao.setJumpToField(focusField); - dao.setJumpToValue(focusValue); - dao.setLimit(limit); - dao.setOffset(offset); - dao.setCountValues(null); - log.debug(LogHelper.getHeader(context, "get_total_results_return", "return=" + count)); return count; 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 f99aab852bf5..1917dec423ec 100644 --- a/dspace-api/src/main/java/org/dspace/browse/SolrBrowseDAO.java +++ b/dspace-api/src/main/java/org/dspace/browse/SolrBrowseDAO.java @@ -13,6 +13,8 @@ import java.util.Comparator; import java.util.List; +import com.fasterxml.jackson.databind.node.JsonNodeFactory; +import com.fasterxml.jackson.databind.node.ObjectNode; import org.apache.commons.lang3.StringUtils; import org.apache.logging.log4j.Logger; import org.apache.solr.client.solrj.util.ClientUtils; @@ -180,18 +182,33 @@ private DiscoverResult getSolrResponse() throws BrowseException { addDefaultFilterQueries(query); if (distinct) { DiscoverFacetField dff; + + // To get the number of distinct values we use the next "json.facet" query param + // {"entries_count": {"type":"terms","field": "_filter", "limit":0, "numBuckets":true}}" + ObjectNode jsonFacet = JsonNodeFactory.instance.objectNode(); + ObjectNode entriesCount = JsonNodeFactory.instance.objectNode(); + entriesCount.put("type", "terms"); + entriesCount.put("field", facetField + "_filter"); + entriesCount.put("limit", 0); + entriesCount.put("numBuckets", true); + jsonFacet.set("entries_count", entriesCount); + if (StringUtils.isNotBlank(startsWith)) { dff = new DiscoverFacetField(facetField, - DiscoveryConfigurationParameters.TYPE_TEXT, -1, - DiscoveryConfigurationParameters.SORT.VALUE, startsWith); + DiscoveryConfigurationParameters.TYPE_TEXT, limit, + DiscoveryConfigurationParameters.SORT.VALUE, startsWith, offset); + + // Add the prefix to the json facet query + entriesCount.put("prefix", startsWith); } else { dff = new DiscoverFacetField(facetField, - DiscoveryConfigurationParameters.TYPE_TEXT, -1, - DiscoveryConfigurationParameters.SORT.VALUE); + DiscoveryConfigurationParameters.TYPE_TEXT, limit, + DiscoveryConfigurationParameters.SORT.VALUE, offset); } query.addFacetField(dff); query.setFacetMinCount(1); query.setMaxResults(0); + query.addProperty("json.facet", jsonFacet.toString()); } else { query.setMaxResults(limit/* > 0 ? limit : 20*/); if (offset > 0) { @@ -248,8 +265,7 @@ public int doCountQuery() throws BrowseException { DiscoverResult resp = getSolrResponse(); int count = 0; if (distinct) { - List facetResults = resp.getFacetResult(facetField); - count = facetResults.size(); + count = (int) resp.getTotalEntries(); } else { // we need to cast to int to respect the BrowseDAO contract... count = (int) resp.getTotalSearchResults(); @@ -266,8 +282,8 @@ public List doValueQuery() throws BrowseException { DiscoverResult resp = getSolrResponse(); List facet = resp.getFacetResult(facetField); int count = doCountQuery(); - int start = offset > 0 ? offset : 0; - int max = limit > 0 ? limit : count; //if negative, return everything + int start = 0; + int max = facet.size(); List result = new ArrayList<>(); if (ascending) { for (int i = start; i < (start + max) && i < count; i++) { diff --git a/dspace-api/src/main/java/org/dspace/discovery/DiscoverResult.java b/dspace-api/src/main/java/org/dspace/discovery/DiscoverResult.java index 00236d2bfe32..a56804e3e7ea 100644 --- a/dspace-api/src/main/java/org/dspace/discovery/DiscoverResult.java +++ b/dspace-api/src/main/java/org/dspace/discovery/DiscoverResult.java @@ -32,6 +32,9 @@ public class DiscoverResult { private List indexableObjects; private Map> facetResults; + // Total count of facet entries calculated for a metadata browsing query + private long totalEntries; + /** * A map that contains all the documents sougth after, the key is a string representation of the Indexable Object */ @@ -64,6 +67,14 @@ public void setTotalSearchResults(long totalSearchResults) { this.totalSearchResults = totalSearchResults; } + public long getTotalEntries() { + return totalEntries; + } + + public void setTotalEntries(long totalEntries) { + this.totalEntries = totalEntries; + } + public int getStart() { return start; } diff --git a/dspace-api/src/main/java/org/dspace/discovery/SolrServiceImpl.java b/dspace-api/src/main/java/org/dspace/discovery/SolrServiceImpl.java index cd3797e3e34e..9339b574b578 100644 --- a/dspace-api/src/main/java/org/dspace/discovery/SolrServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/discovery/SolrServiceImpl.java @@ -1055,6 +1055,8 @@ protected DiscoverResult retrieveResult(Context context, DiscoverQuery query) } //Resolve our facet field values resolveFacetFields(context, query, result, skipLoadingResponse, solrQueryResponse); + //Add total entries count for metadata browsing + resolveEntriesCount(result, solrQueryResponse); } // If any stale entries are found in the current page of results, // we remove those stale entries and rerun the same query again. @@ -1080,7 +1082,39 @@ protected DiscoverResult retrieveResult(Context context, DiscoverQuery query) return result; } - + /** + * Stores the total count of entries for metadata index browsing. The count is calculated by the + * json.facet parameter with the following value: + * + *
    
    +     * {
    +     *     "entries_count": {
    +     *         "type": "terms",
    +     *         "field": "facetNameField_filter",
    +     *         "limit": 0,
    +     *         "prefix": "prefix_value",
    +     *         "numBuckets": true
    +     *     }
    +     * }
    +     * 
    + * + * This value is returned in the facets field of the Solr response. + * + * @param result DiscoverResult object where the total entries count will be stored + * @param solrQueryResponse QueryResponse object containing the solr response + */ + private void resolveEntriesCount(DiscoverResult result, QueryResponse solrQueryResponse) { + + Object facetsObj = solrQueryResponse.getResponse().get("facets"); + if (facetsObj instanceof NamedList) { + NamedList facets = (NamedList) facetsObj; + Object bucketsInfoObj = facets.get("entries_count"); + if (bucketsInfoObj instanceof NamedList) { + NamedList bucketsInfo = (NamedList) bucketsInfoObj; + result.setTotalEntries((int) bucketsInfo.get("numBuckets")); + } + } + } private void resolveFacetFields(Context context, DiscoverQuery query, DiscoverResult result, boolean skipLoadingResponse, QueryResponse solrQueryResponse) throws SQLException { From 75647de90e4664a297cac2672fabe1d33306d195 Mon Sep 17 00:00:00 2001 From: Toni Prieto Date: Sat, 26 Oct 2024 23:33:13 +0200 Subject: [PATCH 239/979] Add limit, offset, and a new parameter to calculate the total entry count in the Solr query used for the metadata navigation index (cherry picked from commit e71de8a4d075d897f68c145233ac19ba0ec3718b) --- .../java/org/dspace/browse/BrowseEngine.java | 30 ++-------------- .../java/org/dspace/browse/SolrBrowseDAO.java | 32 ++++++++++++----- .../org/dspace/discovery/DiscoverResult.java | 11 ++++++ .../org/dspace/discovery/SolrServiceImpl.java | 36 ++++++++++++++++++- 4 files changed, 73 insertions(+), 36 deletions(-) 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 351c36248209..be7a34086a46 100644 --- a/dspace-api/src/main/java/org/dspace/browse/BrowseEngine.java +++ b/dspace-api/src/main/java/org/dspace/browse/BrowseEngine.java @@ -422,9 +422,6 @@ private BrowseInfo browseByValue(BrowserScope bs) } } - // this is the total number of results in answer to the query - int total = getTotalResults(true); - // set the ordering field (there is only one option) dao.setOrderField("sort_value"); @@ -444,6 +441,9 @@ private BrowseInfo browseByValue(BrowserScope bs) dao.setOffset(offset); dao.setLimit(scope.getResultsPerPage()); + // this is the total number of results in answer to the query + int total = getTotalResults(true); + // Holder for the results List results = null; @@ -680,33 +680,9 @@ private int getTotalResults(boolean distinct) // tell the browse query whether we are distinct dao.setDistinct(distinct); - // ensure that the select is set to "*" - String[] select = {"*"}; - dao.setCountValues(select); - - // FIXME: it would be nice to have a good way of doing this in the DAO - // now reset all of the fields that we don't want to have constraining - // our count, storing them locally to reinstate later - String focusField = dao.getJumpToField(); - String focusValue = dao.getJumpToValue(); - int limit = dao.getLimit(); - int offset = dao.getOffset(); - - dao.setJumpToField(null); - dao.setJumpToValue(null); - dao.setLimit(-1); - dao.setOffset(-1); - // perform the query and get the result int count = dao.doCountQuery(); - // now put back the values we removed for this method - dao.setJumpToField(focusField); - dao.setJumpToValue(focusValue); - dao.setLimit(limit); - dao.setOffset(offset); - dao.setCountValues(null); - log.debug(LogHelper.getHeader(context, "get_total_results_return", "return=" + count)); return count; 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 f99aab852bf5..1917dec423ec 100644 --- a/dspace-api/src/main/java/org/dspace/browse/SolrBrowseDAO.java +++ b/dspace-api/src/main/java/org/dspace/browse/SolrBrowseDAO.java @@ -13,6 +13,8 @@ import java.util.Comparator; import java.util.List; +import com.fasterxml.jackson.databind.node.JsonNodeFactory; +import com.fasterxml.jackson.databind.node.ObjectNode; import org.apache.commons.lang3.StringUtils; import org.apache.logging.log4j.Logger; import org.apache.solr.client.solrj.util.ClientUtils; @@ -180,18 +182,33 @@ private DiscoverResult getSolrResponse() throws BrowseException { addDefaultFilterQueries(query); if (distinct) { DiscoverFacetField dff; + + // To get the number of distinct values we use the next "json.facet" query param + // {"entries_count": {"type":"terms","field": "_filter", "limit":0, "numBuckets":true}}" + ObjectNode jsonFacet = JsonNodeFactory.instance.objectNode(); + ObjectNode entriesCount = JsonNodeFactory.instance.objectNode(); + entriesCount.put("type", "terms"); + entriesCount.put("field", facetField + "_filter"); + entriesCount.put("limit", 0); + entriesCount.put("numBuckets", true); + jsonFacet.set("entries_count", entriesCount); + if (StringUtils.isNotBlank(startsWith)) { dff = new DiscoverFacetField(facetField, - DiscoveryConfigurationParameters.TYPE_TEXT, -1, - DiscoveryConfigurationParameters.SORT.VALUE, startsWith); + DiscoveryConfigurationParameters.TYPE_TEXT, limit, + DiscoveryConfigurationParameters.SORT.VALUE, startsWith, offset); + + // Add the prefix to the json facet query + entriesCount.put("prefix", startsWith); } else { dff = new DiscoverFacetField(facetField, - DiscoveryConfigurationParameters.TYPE_TEXT, -1, - DiscoveryConfigurationParameters.SORT.VALUE); + DiscoveryConfigurationParameters.TYPE_TEXT, limit, + DiscoveryConfigurationParameters.SORT.VALUE, offset); } query.addFacetField(dff); query.setFacetMinCount(1); query.setMaxResults(0); + query.addProperty("json.facet", jsonFacet.toString()); } else { query.setMaxResults(limit/* > 0 ? limit : 20*/); if (offset > 0) { @@ -248,8 +265,7 @@ public int doCountQuery() throws BrowseException { DiscoverResult resp = getSolrResponse(); int count = 0; if (distinct) { - List facetResults = resp.getFacetResult(facetField); - count = facetResults.size(); + count = (int) resp.getTotalEntries(); } else { // we need to cast to int to respect the BrowseDAO contract... count = (int) resp.getTotalSearchResults(); @@ -266,8 +282,8 @@ public List doValueQuery() throws BrowseException { DiscoverResult resp = getSolrResponse(); List facet = resp.getFacetResult(facetField); int count = doCountQuery(); - int start = offset > 0 ? offset : 0; - int max = limit > 0 ? limit : count; //if negative, return everything + int start = 0; + int max = facet.size(); List result = new ArrayList<>(); if (ascending) { for (int i = start; i < (start + max) && i < count; i++) { diff --git a/dspace-api/src/main/java/org/dspace/discovery/DiscoverResult.java b/dspace-api/src/main/java/org/dspace/discovery/DiscoverResult.java index 00236d2bfe32..a56804e3e7ea 100644 --- a/dspace-api/src/main/java/org/dspace/discovery/DiscoverResult.java +++ b/dspace-api/src/main/java/org/dspace/discovery/DiscoverResult.java @@ -32,6 +32,9 @@ public class DiscoverResult { private List indexableObjects; private Map> facetResults; + // Total count of facet entries calculated for a metadata browsing query + private long totalEntries; + /** * A map that contains all the documents sougth after, the key is a string representation of the Indexable Object */ @@ -64,6 +67,14 @@ public void setTotalSearchResults(long totalSearchResults) { this.totalSearchResults = totalSearchResults; } + public long getTotalEntries() { + return totalEntries; + } + + public void setTotalEntries(long totalEntries) { + this.totalEntries = totalEntries; + } + public int getStart() { return start; } diff --git a/dspace-api/src/main/java/org/dspace/discovery/SolrServiceImpl.java b/dspace-api/src/main/java/org/dspace/discovery/SolrServiceImpl.java index a0c1188d7132..0769310b8175 100644 --- a/dspace-api/src/main/java/org/dspace/discovery/SolrServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/discovery/SolrServiceImpl.java @@ -1055,6 +1055,8 @@ protected DiscoverResult retrieveResult(Context context, DiscoverQuery query) } //Resolve our facet field values resolveFacetFields(context, query, result, skipLoadingResponse, solrQueryResponse); + //Add total entries count for metadata browsing + resolveEntriesCount(result, solrQueryResponse); } // If any stale entries are found in the current page of results, // we remove those stale entries and rerun the same query again. @@ -1080,7 +1082,39 @@ protected DiscoverResult retrieveResult(Context context, DiscoverQuery query) return result; } - + /** + * Stores the total count of entries for metadata index browsing. The count is calculated by the + * json.facet parameter with the following value: + * + *
    
    +     * {
    +     *     "entries_count": {
    +     *         "type": "terms",
    +     *         "field": "facetNameField_filter",
    +     *         "limit": 0,
    +     *         "prefix": "prefix_value",
    +     *         "numBuckets": true
    +     *     }
    +     * }
    +     * 
    + * + * This value is returned in the facets field of the Solr response. + * + * @param result DiscoverResult object where the total entries count will be stored + * @param solrQueryResponse QueryResponse object containing the solr response + */ + private void resolveEntriesCount(DiscoverResult result, QueryResponse solrQueryResponse) { + + Object facetsObj = solrQueryResponse.getResponse().get("facets"); + if (facetsObj instanceof NamedList) { + NamedList facets = (NamedList) facetsObj; + Object bucketsInfoObj = facets.get("entries_count"); + if (bucketsInfoObj instanceof NamedList) { + NamedList bucketsInfo = (NamedList) bucketsInfoObj; + result.setTotalEntries((int) bucketsInfo.get("numBuckets")); + } + } + } private void resolveFacetFields(Context context, DiscoverQuery query, DiscoverResult result, boolean skipLoadingResponse, QueryResponse solrQueryResponse) throws SQLException { From 95742fe4f38842fc0747188ec941b05364ec7797 Mon Sep 17 00:00:00 2001 From: Jukka Lipka <3710455+jlipka@users.noreply.github.com> Date: Mon, 21 Oct 2024 18:11:08 +0200 Subject: [PATCH 240/979] fix(submission): Submission scope naming fixed According to the documentation, the value for the property is 'submission' in the 'submission-forms.xml'. Without this change, an empty input field will never be marked as an error, even if the field is marked as 'required'. (cherry picked from commit 02f52c7d5c245eaae09dbd636fc8c7935d7cc242) --- dspace-api/src/main/java/org/dspace/app/util/DCInput.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dspace-api/src/main/java/org/dspace/app/util/DCInput.java b/dspace-api/src/main/java/org/dspace/app/util/DCInput.java index 11f9aadd869b..a3d1ba208b56 100644 --- a/dspace-api/src/main/java/org/dspace/app/util/DCInput.java +++ b/dspace-api/src/main/java/org/dspace/app/util/DCInput.java @@ -163,7 +163,7 @@ public class DCInput { * The scope of the input sets, this restricts hidden metadata fields from * view by the end user during submission. */ - public static final String SUBMISSION_SCOPE = "submit"; + public static final String SUBMISSION_SCOPE = "submission"; /** * Class constructor for creating a DCInput object based on the contents of From 31faf809d15d968a021a64720b620faf5f47bf0e Mon Sep 17 00:00:00 2001 From: Jukka Lipka <3710455+jlipka@users.noreply.github.com> Date: Mon, 9 Dec 2024 10:50:59 +0100 Subject: [PATCH 241/979] fix(submission): Submission scope naming fixed Corrected wording in related code comment (cherry picked from commit ec2187ea6532d7db5f5ffa38e20e971a49b90871) --- dspace-api/src/main/java/org/dspace/app/util/DCInput.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dspace-api/src/main/java/org/dspace/app/util/DCInput.java b/dspace-api/src/main/java/org/dspace/app/util/DCInput.java index a3d1ba208b56..0a1e77ee7292 100644 --- a/dspace-api/src/main/java/org/dspace/app/util/DCInput.java +++ b/dspace-api/src/main/java/org/dspace/app/util/DCInput.java @@ -262,7 +262,7 @@ protected void initRegex(String regex) { /** * Is this DCInput for display in the given scope? The scope should be - * either "workflow" or "submit", as per the input forms definition. If the + * either "workflow" or "submission", as per the input forms definition. If the * internal visibility is set to "null" then this will always return true. * * @param scope String identifying the scope that this input's visibility From 4dcf17314c3d694d42d7442315bcac6d2010f85f Mon Sep 17 00:00:00 2001 From: Jukka Lipka <3710455+jlipka@users.noreply.github.com> Date: Mon, 21 Oct 2024 18:11:08 +0200 Subject: [PATCH 242/979] fix(submission): Submission scope naming fixed According to the documentation, the value for the property is 'submission' in the 'submission-forms.xml'. Without this change, an empty input field will never be marked as an error, even if the field is marked as 'required'. (cherry picked from commit 02f52c7d5c245eaae09dbd636fc8c7935d7cc242) --- dspace-api/src/main/java/org/dspace/app/util/DCInput.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dspace-api/src/main/java/org/dspace/app/util/DCInput.java b/dspace-api/src/main/java/org/dspace/app/util/DCInput.java index dd88390cb856..846a74cad5d5 100644 --- a/dspace-api/src/main/java/org/dspace/app/util/DCInput.java +++ b/dspace-api/src/main/java/org/dspace/app/util/DCInput.java @@ -163,7 +163,7 @@ public class DCInput { * The scope of the input sets, this restricts hidden metadata fields from * view by the end user during submission. */ - public static final String SUBMISSION_SCOPE = "submit"; + public static final String SUBMISSION_SCOPE = "submission"; /** * Class constructor for creating a DCInput object based on the contents of From d34b55996479de15fbc449ebce8180c00c221972 Mon Sep 17 00:00:00 2001 From: Jukka Lipka <3710455+jlipka@users.noreply.github.com> Date: Mon, 9 Dec 2024 10:50:59 +0100 Subject: [PATCH 243/979] fix(submission): Submission scope naming fixed Corrected wording in related code comment (cherry picked from commit ec2187ea6532d7db5f5ffa38e20e971a49b90871) --- dspace-api/src/main/java/org/dspace/app/util/DCInput.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dspace-api/src/main/java/org/dspace/app/util/DCInput.java b/dspace-api/src/main/java/org/dspace/app/util/DCInput.java index 846a74cad5d5..c96be33d0132 100644 --- a/dspace-api/src/main/java/org/dspace/app/util/DCInput.java +++ b/dspace-api/src/main/java/org/dspace/app/util/DCInput.java @@ -262,7 +262,7 @@ protected void initRegex(String regex) { /** * Is this DCInput for display in the given scope? The scope should be - * either "workflow" or "submit", as per the input forms definition. If the + * either "workflow" or "submission", as per the input forms definition. If the * internal visibility is set to "null" then this will always return true. * * @param scope String identifying the scope that this input's visibility From 973beee2ce5f8dc993ef5eb91711b595c7786789 Mon Sep 17 00:00:00 2001 From: igorbaptist4 Date: Tue, 10 Sep 2024 18:44:15 -0300 Subject: [PATCH 244/979] fix: properly type in field id, adjust use of getProperty and add error handling when dbPath is null (when property usage-statistics.dbfile is commented (cherry picked from commit 412d5751f29f31bbdd872820a62b74970333f066) --- .../org/dspace/statistics/util/StatisticsImporter.java | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/statistics/util/StatisticsImporter.java b/dspace-api/src/main/java/org/dspace/statistics/util/StatisticsImporter.java index 95736a8bd6d9..1bd97a6f5ec9 100644 --- a/dspace-api/src/main/java/org/dspace/statistics/util/StatisticsImporter.java +++ b/dspace-api/src/main/java/org/dspace/statistics/util/StatisticsImporter.java @@ -357,7 +357,7 @@ protected void load(String filename, Context context, boolean verbose) { SolrInputDocument sid = new SolrInputDocument(); sid.addField("ip", ip); sid.addField("type", dso.getType()); - sid.addField("id", dso.getID()); + sid.addField("id", dso.getID().toString()); sid.addField("time", DateFormatUtils.format(date, SolrLoggerServiceImpl.DATE_FORMAT_8601)); sid.addField("continent", continent); sid.addField("country", country); @@ -471,13 +471,13 @@ public static void main(String[] args) throws Exception { boolean verbose = line.hasOption('v'); // Find our solr server - String sserver = configurationService.getProperty("solr-statistics", "server"); + String sserver = configurationService.getProperty("solr-statistics.server"); if (verbose) { System.out.println("Writing to solr server at: " + sserver); } solr = new HttpSolrClient.Builder(sserver).build(); - String dbPath = configurationService.getProperty("usage-statistics", "dbfile"); + String dbPath = configurationService.getProperty("usage-statistics.dbfile"); try { File dbFile = new File(dbPath); geoipLookup = new DatabaseReader.Builder(dbFile).build(); @@ -492,6 +492,10 @@ public static void main(String[] args) throws Exception { "Unable to load GeoLite Database file (" + dbPath + ")! You may need to reinstall it. See the DSpace " + "installation instructions for more details.", e); + } catch (NullPointerException e) { + log.error( + "The value of the property usage-statistics.dbfile is null. You may need to install the GeoLite Database file and/or uncomment the property in the config file!", + e); } From 3a223e3fefec9172538e7defa34ce982b0d72762 Mon Sep 17 00:00:00 2001 From: igorbaptist4 Date: Tue, 10 Sep 2024 20:16:47 -0300 Subject: [PATCH 245/979] fix line length checkstyle (cherry picked from commit 338f3b1d3efaa0f7f3852654121cf1f93adeae7e) --- .../java/org/dspace/statistics/util/StatisticsImporter.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/dspace-api/src/main/java/org/dspace/statistics/util/StatisticsImporter.java b/dspace-api/src/main/java/org/dspace/statistics/util/StatisticsImporter.java index 1bd97a6f5ec9..354c803fe2ae 100644 --- a/dspace-api/src/main/java/org/dspace/statistics/util/StatisticsImporter.java +++ b/dspace-api/src/main/java/org/dspace/statistics/util/StatisticsImporter.java @@ -494,7 +494,8 @@ public static void main(String[] args) throws Exception { e); } catch (NullPointerException e) { log.error( - "The value of the property usage-statistics.dbfile is null. You may need to install the GeoLite Database file and/or uncomment the property in the config file!", + "The value of the property usage-statistics.dbfile is null. You may need to install the GeoLite " + + "Database file and/or uncomment the property in the config file!", e); } From f0a514ae73fa8e469531572c2fa6931b491b0533 Mon Sep 17 00:00:00 2001 From: igorbaptist4 Date: Tue, 10 Sep 2024 18:44:15 -0300 Subject: [PATCH 246/979] fix: properly type in field id, adjust use of getProperty and add error handling when dbPath is null (when property usage-statistics.dbfile is commented (cherry picked from commit 412d5751f29f31bbdd872820a62b74970333f066) --- .../org/dspace/statistics/util/StatisticsImporter.java | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/statistics/util/StatisticsImporter.java b/dspace-api/src/main/java/org/dspace/statistics/util/StatisticsImporter.java index 95736a8bd6d9..1bd97a6f5ec9 100644 --- a/dspace-api/src/main/java/org/dspace/statistics/util/StatisticsImporter.java +++ b/dspace-api/src/main/java/org/dspace/statistics/util/StatisticsImporter.java @@ -357,7 +357,7 @@ protected void load(String filename, Context context, boolean verbose) { SolrInputDocument sid = new SolrInputDocument(); sid.addField("ip", ip); sid.addField("type", dso.getType()); - sid.addField("id", dso.getID()); + sid.addField("id", dso.getID().toString()); sid.addField("time", DateFormatUtils.format(date, SolrLoggerServiceImpl.DATE_FORMAT_8601)); sid.addField("continent", continent); sid.addField("country", country); @@ -471,13 +471,13 @@ public static void main(String[] args) throws Exception { boolean verbose = line.hasOption('v'); // Find our solr server - String sserver = configurationService.getProperty("solr-statistics", "server"); + String sserver = configurationService.getProperty("solr-statistics.server"); if (verbose) { System.out.println("Writing to solr server at: " + sserver); } solr = new HttpSolrClient.Builder(sserver).build(); - String dbPath = configurationService.getProperty("usage-statistics", "dbfile"); + String dbPath = configurationService.getProperty("usage-statistics.dbfile"); try { File dbFile = new File(dbPath); geoipLookup = new DatabaseReader.Builder(dbFile).build(); @@ -492,6 +492,10 @@ public static void main(String[] args) throws Exception { "Unable to load GeoLite Database file (" + dbPath + ")! You may need to reinstall it. See the DSpace " + "installation instructions for more details.", e); + } catch (NullPointerException e) { + log.error( + "The value of the property usage-statistics.dbfile is null. You may need to install the GeoLite Database file and/or uncomment the property in the config file!", + e); } From de3e8baebed0e25ed60c613ec75b1f21fe488f01 Mon Sep 17 00:00:00 2001 From: igorbaptist4 Date: Tue, 10 Sep 2024 20:16:47 -0300 Subject: [PATCH 247/979] fix line length checkstyle (cherry picked from commit 338f3b1d3efaa0f7f3852654121cf1f93adeae7e) --- .../java/org/dspace/statistics/util/StatisticsImporter.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/dspace-api/src/main/java/org/dspace/statistics/util/StatisticsImporter.java b/dspace-api/src/main/java/org/dspace/statistics/util/StatisticsImporter.java index 1bd97a6f5ec9..354c803fe2ae 100644 --- a/dspace-api/src/main/java/org/dspace/statistics/util/StatisticsImporter.java +++ b/dspace-api/src/main/java/org/dspace/statistics/util/StatisticsImporter.java @@ -494,7 +494,8 @@ public static void main(String[] args) throws Exception { e); } catch (NullPointerException e) { log.error( - "The value of the property usage-statistics.dbfile is null. You may need to install the GeoLite Database file and/or uncomment the property in the config file!", + "The value of the property usage-statistics.dbfile is null. You may need to install the GeoLite " + + "Database file and/or uncomment the property in the config file!", e); } From c86d082d5f6fe4a76eae018bbe6890cc0b72ff33 Mon Sep 17 00:00:00 2001 From: igorbaptist4 Date: Tue, 10 Sep 2024 13:10:14 -0300 Subject: [PATCH 248/979] fix: set default configFile (cherry picked from commit a5e8d7aa15bc56c268ba67f19657f19edaa45253) --- .../java/org/dspace/app/statistics/LogAnalyser.java | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/app/statistics/LogAnalyser.java b/dspace-api/src/main/java/org/dspace/app/statistics/LogAnalyser.java index 2e4ed69b268e..c787261419f8 100644 --- a/dspace-api/src/main/java/org/dspace/app/statistics/LogAnalyser.java +++ b/dspace-api/src/main/java/org/dspace/app/statistics/LogAnalyser.java @@ -281,10 +281,14 @@ public class LogAnalyser { */ private static String fileTemplate = "dspace\\.log.*"; + private static final ConfigurationService configurationService = + DSpaceServicesFactory.getInstance().getConfigurationService(); + /** * the configuration file from which to configure the analyser */ - private static String configFile; + private static String configFile = configurationService.getProperty("dspace.dir") + + File.separator + "config" + File.separator + "dstat.cfg"; /** * the output file to which to write aggregation data @@ -616,8 +620,6 @@ public static String processLogs(Context context, String myLogDir, } // now do the host name and url lookup - ConfigurationService configurationService - = DSpaceServicesFactory.getInstance().getConfigurationService(); hostName = Utils.getHostName(configurationService.getProperty("dspace.ui.url")); name = configurationService.getProperty("dspace.name").trim(); url = configurationService.getProperty("dspace.ui.url").trim(); @@ -658,8 +660,6 @@ public static void setParameters(String myLogDir, String myFileTemplate, String myConfigFile, String myOutFile, Date myStartDate, Date myEndDate, boolean myLookUp) { - ConfigurationService configurationService - = DSpaceServicesFactory.getInstance().getConfigurationService(); if (myLogDir != null) { logDir = myLogDir; @@ -673,9 +673,6 @@ public static void setParameters(String myLogDir, String myFileTemplate, if (myConfigFile != null) { configFile = myConfigFile; - } else { - configFile = configurationService.getProperty("dspace.dir") - + File.separator + "config" + File.separator + "dstat.cfg"; } if (myStartDate != null) { From d2ab46b0ae34a00de6bc99d1b5e0a2240cde74bb Mon Sep 17 00:00:00 2001 From: igorbaptist4 Date: Tue, 10 Sep 2024 13:10:14 -0300 Subject: [PATCH 249/979] fix: set default configFile (cherry picked from commit a5e8d7aa15bc56c268ba67f19657f19edaa45253) --- .../java/org/dspace/app/statistics/LogAnalyser.java | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/app/statistics/LogAnalyser.java b/dspace-api/src/main/java/org/dspace/app/statistics/LogAnalyser.java index 2e4ed69b268e..c787261419f8 100644 --- a/dspace-api/src/main/java/org/dspace/app/statistics/LogAnalyser.java +++ b/dspace-api/src/main/java/org/dspace/app/statistics/LogAnalyser.java @@ -281,10 +281,14 @@ public class LogAnalyser { */ private static String fileTemplate = "dspace\\.log.*"; + private static final ConfigurationService configurationService = + DSpaceServicesFactory.getInstance().getConfigurationService(); + /** * the configuration file from which to configure the analyser */ - private static String configFile; + private static String configFile = configurationService.getProperty("dspace.dir") + + File.separator + "config" + File.separator + "dstat.cfg"; /** * the output file to which to write aggregation data @@ -616,8 +620,6 @@ public static String processLogs(Context context, String myLogDir, } // now do the host name and url lookup - ConfigurationService configurationService - = DSpaceServicesFactory.getInstance().getConfigurationService(); hostName = Utils.getHostName(configurationService.getProperty("dspace.ui.url")); name = configurationService.getProperty("dspace.name").trim(); url = configurationService.getProperty("dspace.ui.url").trim(); @@ -658,8 +660,6 @@ public static void setParameters(String myLogDir, String myFileTemplate, String myConfigFile, String myOutFile, Date myStartDate, Date myEndDate, boolean myLookUp) { - ConfigurationService configurationService - = DSpaceServicesFactory.getInstance().getConfigurationService(); if (myLogDir != null) { logDir = myLogDir; @@ -673,9 +673,6 @@ public static void setParameters(String myLogDir, String myFileTemplate, if (myConfigFile != null) { configFile = myConfigFile; - } else { - configFile = configurationService.getProperty("dspace.dir") - + File.separator + "config" + File.separator + "dstat.cfg"; } if (myStartDate != null) { From 2d57d7f626dbe433d4581d3ef8114ab775cd8088 Mon Sep 17 00:00:00 2001 From: DSpace Bot <68393067+dspace-bot@users.noreply.github.com> Date: Wed, 18 Dec 2024 15:57:16 -0600 Subject: [PATCH 250/979] [Port dspace-8_x] Fix issue with submission sections visibility (#10141) * README.md: v8 is the current release, not v7 (cherry picked from commit 2b698eff609d510c487ad2331d4e11cd28f64e9a) (cherry picked from commit 83460afb3700a2ddaabce16069b2094e4edbe8fa) * Update README.md (cherry picked from commit 671234b08f909810d798dd950f87d1818b098363) (cherry picked from commit 7a6785b1c353cbc45c4dfaad89f5252064ef1e4e) * [DURACOM-291] Expose section scope attribute (cherry picked from commit 4107f937fda1b49da2b6a81dc91026ebf4b1cd37) * README.md: v8 is the current release, not v7 (cherry picked from commit 2b698eff609d510c487ad2331d4e11cd28f64e9a) (cherry picked from commit d98499a39416d026c0b45199e90ddb9bbfc1cd9c) * Update README.md (cherry picked from commit 671234b08f909810d798dd950f87d1818b098363) (cherry picked from commit 6a707548ffa6929e8a36775063ce05e2dc02a586) --------- Co-authored-by: Christian Clauss Co-authored-by: Giuseppe Digilio --- .../dspace/app/rest/converter/SubmissionSectionConverter.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/SubmissionSectionConverter.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/SubmissionSectionConverter.java index 0391cbce7a2d..3cd263493b5d 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/SubmissionSectionConverter.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/SubmissionSectionConverter.java @@ -10,6 +10,7 @@ import java.sql.SQLException; import org.apache.logging.log4j.Logger; +import org.dspace.app.rest.model.ScopeEnum; import org.dspace.app.rest.model.SubmissionSectionRest; import org.dspace.app.rest.model.SubmissionVisibilityRest; import org.dspace.app.rest.model.VisibilityEnum; @@ -41,6 +42,7 @@ public SubmissionSectionRest convert(SubmissionStepConfig step, Projection proje sp.setHeader(step.getHeading()); sp.setSectionType(step.getType()); sp.setId(step.getId()); + sp.setScope(ScopeEnum.fromString(step.getScope())); sp.setVisibility(new SubmissionVisibilityRest(VisibilityEnum.fromString(step.getVisibility()), VisibilityEnum.fromString(step.getVisibilityOutside()))); return sp; From 1ce4e08333559bffadde8d266a1009c1c875df33 Mon Sep 17 00:00:00 2001 From: DSpace Bot <68393067+dspace-bot@users.noreply.github.com> Date: Wed, 18 Dec 2024 16:45:16 -0600 Subject: [PATCH 251/979] [Port dspace-7_x] Fix issue with submission sections visibility (#10140) * README.md: v8 is the current release, not v7 (cherry picked from commit 2b698eff609d510c487ad2331d4e11cd28f64e9a) (cherry picked from commit 83460afb3700a2ddaabce16069b2094e4edbe8fa) * Update README.md (cherry picked from commit 671234b08f909810d798dd950f87d1818b098363) (cherry picked from commit 7a6785b1c353cbc45c4dfaad89f5252064ef1e4e) * [DURACOM-291] Expose section scope attribute (cherry picked from commit 4107f937fda1b49da2b6a81dc91026ebf4b1cd37) * README.md: v8 is the current release, not v7 (cherry picked from commit 2b698eff609d510c487ad2331d4e11cd28f64e9a) (cherry picked from commit d98499a39416d026c0b45199e90ddb9bbfc1cd9c) * Update README.md (cherry picked from commit 671234b08f909810d798dd950f87d1818b098363) (cherry picked from commit 6a707548ffa6929e8a36775063ce05e2dc02a586) --------- Co-authored-by: Christian Clauss Co-authored-by: Giuseppe Digilio --- .../dspace/app/rest/converter/SubmissionSectionConverter.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/SubmissionSectionConverter.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/SubmissionSectionConverter.java index 0391cbce7a2d..3cd263493b5d 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/SubmissionSectionConverter.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/SubmissionSectionConverter.java @@ -10,6 +10,7 @@ import java.sql.SQLException; import org.apache.logging.log4j.Logger; +import org.dspace.app.rest.model.ScopeEnum; import org.dspace.app.rest.model.SubmissionSectionRest; import org.dspace.app.rest.model.SubmissionVisibilityRest; import org.dspace.app.rest.model.VisibilityEnum; @@ -41,6 +42,7 @@ public SubmissionSectionRest convert(SubmissionStepConfig step, Projection proje sp.setHeader(step.getHeading()); sp.setSectionType(step.getType()); sp.setId(step.getId()); + sp.setScope(ScopeEnum.fromString(step.getScope())); sp.setVisibility(new SubmissionVisibilityRest(VisibilityEnum.fromString(step.getVisibility()), VisibilityEnum.fromString(step.getVisibilityOutside()))); return sp; From 51df5c1fe67b7660dafa85d57f688347ca7f024d Mon Sep 17 00:00:00 2001 From: Kristof De Langhe Date: Mon, 2 Dec 2024 15:35:10 +0100 Subject: [PATCH 252/979] 119664: Search event scope fix (cherry picked from commit 48956d90b7619bdb336632b454ac62ca62967a39) --- .../org/dspace/app/rest/converter/SearchEventConverter.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/SearchEventConverter.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/SearchEventConverter.java index 978ae2ca9230..f2fb12f2bda9 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/SearchEventConverter.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/SearchEventConverter.java @@ -67,8 +67,8 @@ public UsageSearchEvent convert(Context context, HttpServletRequest request, Sea if (searchEventRest.getScope() != null) { IndexableObject scopeObject = scopeResolver.resolveScope(context, String.valueOf(searchEventRest.getScope())); - if (scopeObject instanceof DSpaceObject) { - usageSearchEvent.setScope((DSpaceObject) scopeObject); + if (scopeObject != null && scopeObject.getIndexedObject() instanceof DSpaceObject) { + usageSearchEvent.setScope((DSpaceObject) scopeObject.getIndexedObject()); } } usageSearchEvent.setConfiguration(searchEventRest.getConfiguration()); From fa209881b15b5abfac522b9df8e302ad02a44c9c Mon Sep 17 00:00:00 2001 From: Kristof De Langhe Date: Mon, 2 Dec 2024 15:35:10 +0100 Subject: [PATCH 253/979] 119664: Search event scope fix (cherry picked from commit 48956d90b7619bdb336632b454ac62ca62967a39) --- .../org/dspace/app/rest/converter/SearchEventConverter.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/SearchEventConverter.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/SearchEventConverter.java index d781d255df11..dcf42f099821 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/SearchEventConverter.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/SearchEventConverter.java @@ -67,8 +67,8 @@ public UsageSearchEvent convert(Context context, HttpServletRequest request, Sea if (searchEventRest.getScope() != null) { IndexableObject scopeObject = scopeResolver.resolveScope(context, String.valueOf(searchEventRest.getScope())); - if (scopeObject instanceof DSpaceObject) { - usageSearchEvent.setScope((DSpaceObject) scopeObject); + if (scopeObject != null && scopeObject.getIndexedObject() instanceof DSpaceObject) { + usageSearchEvent.setScope((DSpaceObject) scopeObject.getIndexedObject()); } } usageSearchEvent.setConfiguration(searchEventRest.getConfiguration()); From 9cca67de0e95b272523bbbac9f982ce9594b20d9 Mon Sep 17 00:00:00 2001 From: Brian Keese Date: Tue, 15 Oct 2024 11:38:54 -0500 Subject: [PATCH 254/979] Fix full-text indexing for files over the character limit The error handler for files over the limit logged the correct message, but never actually added the full text to the index doc. (cherry picked from commit 4a4a8bcb22796e5b22c9bbb1796e3458b48f1c07) --- .../indexobject/IndexFactoryImpl.java | 31 +++++++++---------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/discovery/indexobject/IndexFactoryImpl.java b/dspace-api/src/main/java/org/dspace/discovery/indexobject/IndexFactoryImpl.java index f1ae137b9163..c9a865ec85b2 100644 --- a/dspace-api/src/main/java/org/dspace/discovery/indexobject/IndexFactoryImpl.java +++ b/dspace-api/src/main/java/org/dspace/discovery/indexobject/IndexFactoryImpl.java @@ -118,20 +118,10 @@ protected void writeDocument(SolrInputDocument doc, FullTextContentStreams strea ParseContext tikaContext = new ParseContext(); // Use Apache Tika to parse the full text stream(s) + boolean extractionSucceeded = false; try (InputStream fullTextStreams = streams.getStream()) { tikaParser.parse(fullTextStreams, tikaHandler, tikaMetadata, tikaContext); - - // Write Tika metadata to "tika_meta_*" fields. - // This metadata is not very useful right now, - // but we'll keep it just in case it becomes more useful. - for (String name : tikaMetadata.names()) { - for (String value : tikaMetadata.getValues(name)) { - doc.addField("tika_meta_" + name, value); - } - } - - // Save (parsed) full text to "fulltext" field - doc.addField("fulltext", tikaHandler.toString()); + extractionSucceeded = true; } catch (SAXException saxe) { // Check if this SAXException is just a notice that this file was longer than the character limit. // Unfortunately there is not a unique, public exception type to catch here. This error is thrown @@ -141,6 +131,7 @@ protected void writeDocument(SolrInputDocument doc, FullTextContentStreams strea // log that we only indexed up to that configured limit log.info("Full text is larger than the configured limit (discovery.solr.fulltext.charLimit)." + " Only the first {} characters were indexed.", charLimit); + extractionSucceeded = true; } else { log.error("Tika parsing error. Could not index full text.", saxe); throw new IOException("Tika parsing error. Could not index full text.", saxe); @@ -148,11 +139,19 @@ protected void writeDocument(SolrInputDocument doc, FullTextContentStreams strea } catch (TikaException | IOException ex) { log.error("Tika parsing error. Could not index full text.", ex); throw new IOException("Tika parsing error. Could not index full text.", ex); - } finally { - // Add document to index - solr.add(doc); } - return; + if (extractionSucceeded) { + // Write Tika metadata to "tika_meta_*" fields. + // This metadata is not very useful right now, + // but we'll keep it just in case it becomes more useful. + for (String name : tikaMetadata.names()) { + for (String value : tikaMetadata.getValues(name)) { + doc.addField("tika_meta_" + name, value); + } + } + // Save (parsed) full text to "fulltext" field + doc.addField("fulltext", tikaHandler.toString()); + } } // Add document to index solr.add(doc); From 8849212895d987abaca92865178c6b4d2f99452d Mon Sep 17 00:00:00 2001 From: Chris Wilper Date: Wed, 28 Apr 2021 09:29:36 -0400 Subject: [PATCH 255/979] Add Context method to uncache all entities (cherry picked from commit 8ea664adb2161b04ab8b6171bbb426d8875fe27f) --- .../main/java/org/dspace/core/Context.java | 14 +++++++- .../java/org/dspace/core/DBConnection.java | 32 ++++++++++++------- .../dspace/core/HibernateDBConnection.java | 5 +++ .../java/org/dspace/core/ContextTest.java | 25 +++++++++++++++ .../core/HibernateDBConnectionTest.java | 22 +++++++++++++ 5 files changed, 86 insertions(+), 12 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/core/Context.java b/dspace-api/src/main/java/org/dspace/core/Context.java index 02a3fee09f8a..dab6ab7fbd66 100644 --- a/dspace-api/src/main/java/org/dspace/core/Context.java +++ b/dspace-api/src/main/java/org/dspace/core/Context.java @@ -883,7 +883,19 @@ public E reloadEntity(E entity) throws SQLException } /** - * Remove an entity from the cache. This is necessary when batch processing a large number of items. + * Remove all entities from the cache and reload the current user entity. This is useful when batch processing + * a large number of entities when the calling code requires the cache to be completely cleared before continuing. + * + * @throws SQLException if a database error occurs. + */ + public void uncacheEntities() throws SQLException { + dbConnection.uncacheEntities(); + reloadContextBoundEntities(); + } + + /** + * Remove an entity from the cache. This is useful when batch processing a large number of entities + * when the calling code needs to retain some items in the cache while removing others. * * @param entity The entity to reload * @param The class of the entity. The entity must implement the {@link ReloadableEntity} interface. diff --git a/dspace-api/src/main/java/org/dspace/core/DBConnection.java b/dspace-api/src/main/java/org/dspace/core/DBConnection.java index 66e4a65dbfe1..c9c4ce0953e4 100644 --- a/dspace-api/src/main/java/org/dspace/core/DBConnection.java +++ b/dspace-api/src/main/java/org/dspace/core/DBConnection.java @@ -124,28 +124,38 @@ public interface DBConnection { public long getCacheSize() throws SQLException; /** - * Reload a DSpace object from the database. This will make sure the object + * Reload an entity from the database. This will make sure the object * is valid and stored in the cache. The returned object should be used * henceforth instead of the passed object. * - * @param type of {@link entity} - * @param entity The DSpace object to reload + * @param type of entity. + * @param entity The entity to reload. * @return the reloaded entity. - * @throws java.sql.SQLException passed through. + * @throws SQLException passed through. */ public E reloadEntity(E entity) throws SQLException; /** - * Remove a DSpace object from the session cache when batch processing a - * large number of objects. + * Remove all entities from the session cache. * - *

    Objects removed from cache are not saved in any way. Therefore, if you - * have modified an object, you should be sure to {@link commit()} changes + *

    Entities removed from cache are not saved in any way. Therefore, if you + * have modified any entities, you should be sure to {@link #commit()} changes * before calling this method. * - * @param Type of {@link entity} - * @param entity The DSpace object to decache. - * @throws java.sql.SQLException passed through. + * @throws SQLException passed through. + */ + public void uncacheEntities() throws SQLException; + + /** + * Remove an entity from the session cache. + * + *

    Entities removed from cache are not saved in any way. Therefore, if you + * have modified the entity, you should be sure to {@link #commit()} changes + * before calling this method. + * + * @param Type of entity. + * @param entity The entity to decache. + * @throws SQLException passed through. */ public void uncacheEntity(E entity) throws SQLException; diff --git a/dspace-api/src/main/java/org/dspace/core/HibernateDBConnection.java b/dspace-api/src/main/java/org/dspace/core/HibernateDBConnection.java index b371af80eede..bd00b844ba9a 100644 --- a/dspace-api/src/main/java/org/dspace/core/HibernateDBConnection.java +++ b/dspace-api/src/main/java/org/dspace/core/HibernateDBConnection.java @@ -243,6 +243,11 @@ private void configureDatabaseMode() throws SQLException { } } + @Override + public void uncacheEntities() throws SQLException { + getSession().clear(); + } + /** * Evict an entity from the hibernate cache. *

    diff --git a/dspace-api/src/test/java/org/dspace/core/ContextTest.java b/dspace-api/src/test/java/org/dspace/core/ContextTest.java index c6cd849d2110..ccc1d2f732cc 100644 --- a/dspace-api/src/test/java/org/dspace/core/ContextTest.java +++ b/dspace-api/src/test/java/org/dspace/core/ContextTest.java @@ -558,4 +558,29 @@ protected void init() { cleanupContext(instance); } + @Test + public void testUncacheEntities() throws Throwable { + // To set up the test, ensure the cache contains more than the current user entity + groupService.findByName(context, Group.ANONYMOUS); + assertTrue("Cache size should be greater than one", context.getDBConnection().getCacheSize() > 1); + + context.uncacheEntities(); + + assertThat("Cache size should be one (current user)", context.getDBConnection().getCacheSize(), equalTo(1L)); + context.reloadEntity(context.getCurrentUser()); + assertThat("Cache should only contain the current user", context.getDBConnection().getCacheSize(), equalTo(1L)); + } + + @Test + public void testUncacheEntity() throws Throwable { + // Remember the cache size after loading an entity + Group group = groupService.findByName(context, Group.ANONYMOUS); + long oldCacheSize = context.getDBConnection().getCacheSize(); + + // Uncache the entity + context.uncacheEntity(group); + + long newCacheSize = context.getDBConnection().getCacheSize(); + assertThat("Cache size should be reduced by one", newCacheSize, equalTo(oldCacheSize - 1)); + } } diff --git a/dspace-api/src/test/java/org/dspace/core/HibernateDBConnectionTest.java b/dspace-api/src/test/java/org/dspace/core/HibernateDBConnectionTest.java index 093f693d567f..302844ce62ac 100644 --- a/dspace-api/src/test/java/org/dspace/core/HibernateDBConnectionTest.java +++ b/dspace-api/src/test/java/org/dspace/core/HibernateDBConnectionTest.java @@ -205,6 +205,28 @@ public void testReloadEntityAfterCommit() throws SQLException { .contains(person)); } + /** + * Test of uncacheEntities method + */ + @Test + public void testUncacheEntities() throws SQLException { + // Get DBConnection associated with DSpace Context + HibernateDBConnection dbConnection = (HibernateDBConnection) context.getDBConnection(); + EPerson person = context.getCurrentUser(); + + assertTrue("Current user should be cached in session", dbConnection.getSession() + .contains(person)); + + dbConnection.uncacheEntities(); + assertFalse("Current user should be gone from cache", dbConnection.getSession() + .contains(person)); + + // Test ability to reload an uncached entity + person = dbConnection.reloadEntity(person); + assertTrue("Current user should be cached back in session", dbConnection.getSession() + .contains(person)); + } + /** * Test of uncacheEntity method */ From 9d01b87c4f3b8bf925d5526e7292f113975c9499 Mon Sep 17 00:00:00 2001 From: Chris Wilper Date: Wed, 28 Apr 2021 09:29:36 -0400 Subject: [PATCH 256/979] Add Context method to uncache all entities (cherry picked from commit 8ea664adb2161b04ab8b6171bbb426d8875fe27f) --- .../main/java/org/dspace/core/Context.java | 14 +++++++- .../java/org/dspace/core/DBConnection.java | 32 ++++++++++++------- .../dspace/core/HibernateDBConnection.java | 5 +++ .../java/org/dspace/core/ContextTest.java | 25 +++++++++++++++ .../core/HibernateDBConnectionTest.java | 22 +++++++++++++ 5 files changed, 86 insertions(+), 12 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/core/Context.java b/dspace-api/src/main/java/org/dspace/core/Context.java index 02a3fee09f8a..dab6ab7fbd66 100644 --- a/dspace-api/src/main/java/org/dspace/core/Context.java +++ b/dspace-api/src/main/java/org/dspace/core/Context.java @@ -883,7 +883,19 @@ public E reloadEntity(E entity) throws SQLException } /** - * Remove an entity from the cache. This is necessary when batch processing a large number of items. + * Remove all entities from the cache and reload the current user entity. This is useful when batch processing + * a large number of entities when the calling code requires the cache to be completely cleared before continuing. + * + * @throws SQLException if a database error occurs. + */ + public void uncacheEntities() throws SQLException { + dbConnection.uncacheEntities(); + reloadContextBoundEntities(); + } + + /** + * Remove an entity from the cache. This is useful when batch processing a large number of entities + * when the calling code needs to retain some items in the cache while removing others. * * @param entity The entity to reload * @param The class of the entity. The entity must implement the {@link ReloadableEntity} interface. diff --git a/dspace-api/src/main/java/org/dspace/core/DBConnection.java b/dspace-api/src/main/java/org/dspace/core/DBConnection.java index 66e4a65dbfe1..c9c4ce0953e4 100644 --- a/dspace-api/src/main/java/org/dspace/core/DBConnection.java +++ b/dspace-api/src/main/java/org/dspace/core/DBConnection.java @@ -124,28 +124,38 @@ public interface DBConnection { public long getCacheSize() throws SQLException; /** - * Reload a DSpace object from the database. This will make sure the object + * Reload an entity from the database. This will make sure the object * is valid and stored in the cache. The returned object should be used * henceforth instead of the passed object. * - * @param type of {@link entity} - * @param entity The DSpace object to reload + * @param type of entity. + * @param entity The entity to reload. * @return the reloaded entity. - * @throws java.sql.SQLException passed through. + * @throws SQLException passed through. */ public E reloadEntity(E entity) throws SQLException; /** - * Remove a DSpace object from the session cache when batch processing a - * large number of objects. + * Remove all entities from the session cache. * - *

    Objects removed from cache are not saved in any way. Therefore, if you - * have modified an object, you should be sure to {@link commit()} changes + *

    Entities removed from cache are not saved in any way. Therefore, if you + * have modified any entities, you should be sure to {@link #commit()} changes * before calling this method. * - * @param Type of {@link entity} - * @param entity The DSpace object to decache. - * @throws java.sql.SQLException passed through. + * @throws SQLException passed through. + */ + public void uncacheEntities() throws SQLException; + + /** + * Remove an entity from the session cache. + * + *

    Entities removed from cache are not saved in any way. Therefore, if you + * have modified the entity, you should be sure to {@link #commit()} changes + * before calling this method. + * + * @param Type of entity. + * @param entity The entity to decache. + * @throws SQLException passed through. */ public void uncacheEntity(E entity) throws SQLException; diff --git a/dspace-api/src/main/java/org/dspace/core/HibernateDBConnection.java b/dspace-api/src/main/java/org/dspace/core/HibernateDBConnection.java index a867849077a3..806930d0364a 100644 --- a/dspace-api/src/main/java/org/dspace/core/HibernateDBConnection.java +++ b/dspace-api/src/main/java/org/dspace/core/HibernateDBConnection.java @@ -242,6 +242,11 @@ private void configureDatabaseMode() throws SQLException { } } + @Override + public void uncacheEntities() throws SQLException { + getSession().clear(); + } + /** * Evict an entity from the hibernate cache. *

    diff --git a/dspace-api/src/test/java/org/dspace/core/ContextTest.java b/dspace-api/src/test/java/org/dspace/core/ContextTest.java index c6cd849d2110..ccc1d2f732cc 100644 --- a/dspace-api/src/test/java/org/dspace/core/ContextTest.java +++ b/dspace-api/src/test/java/org/dspace/core/ContextTest.java @@ -558,4 +558,29 @@ protected void init() { cleanupContext(instance); } + @Test + public void testUncacheEntities() throws Throwable { + // To set up the test, ensure the cache contains more than the current user entity + groupService.findByName(context, Group.ANONYMOUS); + assertTrue("Cache size should be greater than one", context.getDBConnection().getCacheSize() > 1); + + context.uncacheEntities(); + + assertThat("Cache size should be one (current user)", context.getDBConnection().getCacheSize(), equalTo(1L)); + context.reloadEntity(context.getCurrentUser()); + assertThat("Cache should only contain the current user", context.getDBConnection().getCacheSize(), equalTo(1L)); + } + + @Test + public void testUncacheEntity() throws Throwable { + // Remember the cache size after loading an entity + Group group = groupService.findByName(context, Group.ANONYMOUS); + long oldCacheSize = context.getDBConnection().getCacheSize(); + + // Uncache the entity + context.uncacheEntity(group); + + long newCacheSize = context.getDBConnection().getCacheSize(); + assertThat("Cache size should be reduced by one", newCacheSize, equalTo(oldCacheSize - 1)); + } } diff --git a/dspace-api/src/test/java/org/dspace/core/HibernateDBConnectionTest.java b/dspace-api/src/test/java/org/dspace/core/HibernateDBConnectionTest.java index 093f693d567f..302844ce62ac 100644 --- a/dspace-api/src/test/java/org/dspace/core/HibernateDBConnectionTest.java +++ b/dspace-api/src/test/java/org/dspace/core/HibernateDBConnectionTest.java @@ -205,6 +205,28 @@ public void testReloadEntityAfterCommit() throws SQLException { .contains(person)); } + /** + * Test of uncacheEntities method + */ + @Test + public void testUncacheEntities() throws SQLException { + // Get DBConnection associated with DSpace Context + HibernateDBConnection dbConnection = (HibernateDBConnection) context.getDBConnection(); + EPerson person = context.getCurrentUser(); + + assertTrue("Current user should be cached in session", dbConnection.getSession() + .contains(person)); + + dbConnection.uncacheEntities(); + assertFalse("Current user should be gone from cache", dbConnection.getSession() + .contains(person)); + + // Test ability to reload an uncached entity + person = dbConnection.reloadEntity(person); + assertTrue("Current user should be cached back in session", dbConnection.getSession() + .contains(person)); + } + /** * Test of uncacheEntity method */ From e856ae3291831feac74f01133f7e235d8513cba0 Mon Sep 17 00:00:00 2001 From: Toni Prieto Date: Sun, 27 Oct 2024 08:56:10 +0100 Subject: [PATCH 257/979] Uncache all entities during OAI indexing to reduce memory usage (cherry picked from commit 9af2e2e17cf289a2a6921d27a21d90db6ca8b9f9) --- dspace-oai/src/main/java/org/dspace/xoai/app/XOAI.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/dspace-oai/src/main/java/org/dspace/xoai/app/XOAI.java b/dspace-oai/src/main/java/org/dspace/xoai/app/XOAI.java index c6aaaa34b539..2d2679577802 100644 --- a/dspace-oai/src/main/java/org/dspace/xoai/app/XOAI.java +++ b/dspace-oai/src/main/java/org/dspace/xoai/app/XOAI.java @@ -334,6 +334,11 @@ private int index(Iterator iterator) throws DSpaceSolrIndexerException { server.add(list); server.commit(); list.clear(); + try { + context.uncacheEntities(); + } catch (SQLException ex) { + log.error("Error uncaching entities", ex); + } } } System.out.println("Total: " + i + " items"); From 79ef831c87d0b73c3e891d447f35f73af09a86f4 Mon Sep 17 00:00:00 2001 From: Toni Prieto Date: Sun, 27 Oct 2024 08:56:10 +0100 Subject: [PATCH 258/979] Uncache all entities during OAI indexing to reduce memory usage (cherry picked from commit 9af2e2e17cf289a2a6921d27a21d90db6ca8b9f9) --- dspace-oai/src/main/java/org/dspace/xoai/app/XOAI.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/dspace-oai/src/main/java/org/dspace/xoai/app/XOAI.java b/dspace-oai/src/main/java/org/dspace/xoai/app/XOAI.java index ad138ca9f2ad..5c29b259deab 100644 --- a/dspace-oai/src/main/java/org/dspace/xoai/app/XOAI.java +++ b/dspace-oai/src/main/java/org/dspace/xoai/app/XOAI.java @@ -334,6 +334,11 @@ private int index(Iterator iterator) throws DSpaceSolrIndexerException { server.add(list); server.commit(); list.clear(); + try { + context.uncacheEntities(); + } catch (SQLException ex) { + log.error("Error uncaching entities", ex); + } } } System.out.println("Total: " + i + " items"); From 964e4bf476b9cb49ce07c5d05242c63e9ae638d4 Mon Sep 17 00:00:00 2001 From: Sascha Szott Date: Mon, 23 Dec 2024 12:26:58 +0100 Subject: [PATCH 259/979] remove usage of deprecated constructor call (cherry picked from commit 45cdb4d9d47d3151a4672e99152a0b3bedb6cd18) --- dspace-api/src/main/java/org/dspace/curate/Curation.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dspace-api/src/main/java/org/dspace/curate/Curation.java b/dspace-api/src/main/java/org/dspace/curate/Curation.java index 4d70286e79e0..ea47d817c07d 100644 --- a/dspace-api/src/main/java/org/dspace/curate/Curation.java +++ b/dspace-api/src/main/java/org/dspace/curate/Curation.java @@ -185,7 +185,7 @@ private Curator initCurator() throws FileNotFoundException { Curator curator = new Curator(handler); OutputStream reporterStream; if (null == this.reporter) { - reporterStream = new NullOutputStream(); + reporterStream = NullOutputStream.NULL_OUTPUT_STREAM; } else if ("-".equals(this.reporter)) { reporterStream = System.out; } else { From 7f26447a8dc98aca168b9da4f250d2f123a8a73a Mon Sep 17 00:00:00 2001 From: Sascha Szott Date: Mon, 23 Dec 2024 12:26:58 +0100 Subject: [PATCH 260/979] remove usage of deprecated constructor call (cherry picked from commit 45cdb4d9d47d3151a4672e99152a0b3bedb6cd18) --- dspace-api/src/main/java/org/dspace/curate/Curation.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dspace-api/src/main/java/org/dspace/curate/Curation.java b/dspace-api/src/main/java/org/dspace/curate/Curation.java index 4d70286e79e0..ea47d817c07d 100644 --- a/dspace-api/src/main/java/org/dspace/curate/Curation.java +++ b/dspace-api/src/main/java/org/dspace/curate/Curation.java @@ -185,7 +185,7 @@ private Curator initCurator() throws FileNotFoundException { Curator curator = new Curator(handler); OutputStream reporterStream; if (null == this.reporter) { - reporterStream = new NullOutputStream(); + reporterStream = NullOutputStream.NULL_OUTPUT_STREAM; } else if ("-".equals(this.reporter)) { reporterStream = System.out; } else { From 214c669e8063f93eca2f878610647cc3b87346bd Mon Sep 17 00:00:00 2001 From: Sascha Szott Date: Wed, 11 Dec 2024 15:35:26 +0100 Subject: [PATCH 261/979] fix missing +1 offset (cherry picked from commit ab00de05b46fe98306e0047af7d6d98bba9d0077) --- dspace-oai/src/main/resources/static/style.xsl | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/dspace-oai/src/main/resources/static/style.xsl b/dspace-oai/src/main/resources/static/style.xsl index 17eb865e8f1f..67aeb975b26b 100644 --- a/dspace-oai/src/main/resources/static/style.xsl +++ b/dspace-oai/src/main/resources/static/style.xsl @@ -522,15 +522,14 @@ - + - - + - - + From 27e557662369e02b545580f48c616974510c4c74 Mon Sep 17 00:00:00 2001 From: Sascha Szott Date: Wed, 11 Dec 2024 15:35:26 +0100 Subject: [PATCH 262/979] fix missing +1 offset (cherry picked from commit ab00de05b46fe98306e0047af7d6d98bba9d0077) --- dspace-oai/src/main/resources/static/style.xsl | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/dspace-oai/src/main/resources/static/style.xsl b/dspace-oai/src/main/resources/static/style.xsl index 17eb865e8f1f..67aeb975b26b 100644 --- a/dspace-oai/src/main/resources/static/style.xsl +++ b/dspace-oai/src/main/resources/static/style.xsl @@ -522,15 +522,14 @@ - + - - + - - + From 7cfc0aa106568f3f9bb143fe3330008bbaf6ac0e Mon Sep 17 00:00:00 2001 From: Koen Pauwels Date: Tue, 7 Jan 2025 15:51:28 +0100 Subject: [PATCH 263/979] Bugfix: Enforce unique item id in workspace table (#9340) * 106798 Enforce values in item_id column of workspaceitem table to be unique, both at database level and at WorkspaceItemService level * 106798 Removed Oracle SQL migration * 106798 workspaceitem table migration: delete duplicate rows before introducing uniqueness constraint * 106798: update migration for H2 --------- Co-authored-by: Koen Pauwels Co-authored-by: wout --- .../content/WorkspaceItemServiceImpl.java | 8 +++ ...paceitem_add_item_id_unique_constraint.sql | 21 +++++++ ...paceitem_add_item_id_unique_constraint.sql | 21 +++++++ .../org/dspace/content/WorkspaceItemTest.java | 12 ++++ .../org/dspace/workflow/MockWorkflowItem.java | 62 +++++++++++++++++++ 5 files changed, 124 insertions(+) create mode 100644 dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/h2/V7.6_2024.12.17__workspaceitem_add_item_id_unique_constraint.sql create mode 100644 dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/postgres/V7.6_2024.12.17__workspaceitem_add_item_id_unique_constraint.sql create mode 100644 dspace-api/src/test/java/org/dspace/workflow/MockWorkflowItem.java diff --git a/dspace-api/src/main/java/org/dspace/content/WorkspaceItemServiceImpl.java b/dspace-api/src/main/java/org/dspace/content/WorkspaceItemServiceImpl.java index 1da9e6e44a6a..543f5a55efe2 100644 --- a/dspace-api/src/main/java/org/dspace/content/WorkspaceItemServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/content/WorkspaceItemServiceImpl.java @@ -178,6 +178,14 @@ public WorkspaceItem create(Context context, Collection collection, UUID uuid, b @Override public WorkspaceItem create(Context c, WorkflowItem workflowItem) throws SQLException, AuthorizeException { + WorkspaceItem potentialDuplicate = findByItem(c, workflowItem.getItem()); + if (potentialDuplicate != null) { + throw new IllegalArgumentException(String.format( + "A workspace item referring to item %s already exists (%d)", + workflowItem.getItem().getID(), + potentialDuplicate.getID() + )); + } WorkspaceItem workspaceItem = workspaceItemDAO.create(c, new WorkspaceItem()); workspaceItem.setItem(workflowItem.getItem()); workspaceItem.setCollection(workflowItem.getCollection()); diff --git a/dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/h2/V7.6_2024.12.17__workspaceitem_add_item_id_unique_constraint.sql b/dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/h2/V7.6_2024.12.17__workspaceitem_add_item_id_unique_constraint.sql new file mode 100644 index 000000000000..38389bf2d19b --- /dev/null +++ b/dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/h2/V7.6_2024.12.17__workspaceitem_add_item_id_unique_constraint.sql @@ -0,0 +1,21 @@ +-- +-- The contents of this file are subject to the license and copyright +-- detailed in the LICENSE and NOTICE files at the root of the source +-- tree and available online at +-- +-- http://www.dspace.org/license/ +-- + +-- In the workspaceitem table, if there are multiple rows referring to the same item ID, keep only the first of them. +DELETE FROM workspaceitem WHERE EXISTS ( + SELECT item_id + FROM workspaceitem + GROUP BY item_id + HAVING COUNT(workspace_item_id) > 1 +) AND workspaceitem.workspace_item_id NOT IN ( + SELECT MIN(workspace_item_id) AS workspace_item_id + FROM workspaceitem + GROUP BY item_id +); +-- Identify which rows have duplicates, and compute their replacements. +ALTER TABLE workspaceitem ADD CONSTRAINT unique_item_id UNIQUE(item_id); diff --git a/dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/postgres/V7.6_2024.12.17__workspaceitem_add_item_id_unique_constraint.sql b/dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/postgres/V7.6_2024.12.17__workspaceitem_add_item_id_unique_constraint.sql new file mode 100644 index 000000000000..20eb0f9119d3 --- /dev/null +++ b/dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/postgres/V7.6_2024.12.17__workspaceitem_add_item_id_unique_constraint.sql @@ -0,0 +1,21 @@ +-- +-- The contents of this file are subject to the license and copyright +-- detailed in the LICENSE and NOTICE files at the root of the source +-- tree and available online at +-- +-- http://www.dspace.org/license/ +-- + +-- In the workspaceitem table, if there are multiple rows referring to the same item ID, keep only the first of them. +WITH dedup AS ( + SELECT item_id, MIN(workspace_item_id) AS workspace_item_id + FROM workspaceitem + GROUP BY item_id + HAVING COUNT(workspace_item_id) > 1 +) +DELETE FROM workspaceitem +USING dedup +WHERE workspaceitem.item_id = dedup.item_id AND workspaceitem.workspace_item_id <> dedup.workspace_item_id; + +-- Enforce uniqueness of item_id in workspaceitem table. +ALTER TABLE workspaceitem ADD CONSTRAINT unique_item_id UNIQUE(item_id); diff --git a/dspace-api/src/test/java/org/dspace/content/WorkspaceItemTest.java b/dspace-api/src/test/java/org/dspace/content/WorkspaceItemTest.java index d018a15f9765..15d4720c9378 100644 --- a/dspace-api/src/test/java/org/dspace/content/WorkspaceItemTest.java +++ b/dspace-api/src/test/java/org/dspace/content/WorkspaceItemTest.java @@ -12,6 +12,7 @@ import static org.hamcrest.CoreMatchers.nullValue; import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertThrows; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import static org.mockito.ArgumentMatchers.any; @@ -39,6 +40,7 @@ import org.dspace.eperson.EPerson; import org.dspace.eperson.factory.EPersonServiceFactory; import org.dspace.eperson.service.EPersonService; +import org.dspace.workflow.MockWorkflowItem; import org.junit.After; import org.junit.Before; import org.junit.Test; @@ -468,4 +470,14 @@ public void testSetPublishedBefore() { assertTrue("testSetPublishedBefore 0", wi.isPublishedBefore()); } + @Test + public void testDuplicateItemID() throws Exception { + context.turnOffAuthorisationSystem(); + Item item = wi.getItem(); + MockWorkflowItem wfItem = new MockWorkflowItem(); + wfItem.item = item; + wfItem.collection = collection; + assertThrows(IllegalArgumentException.class, () -> workspaceItemService.create(context, wfItem)); + context.restoreAuthSystemState(); + } } diff --git a/dspace-api/src/test/java/org/dspace/workflow/MockWorkflowItem.java b/dspace-api/src/test/java/org/dspace/workflow/MockWorkflowItem.java new file mode 100644 index 000000000000..d36ecf7331b8 --- /dev/null +++ b/dspace-api/src/test/java/org/dspace/workflow/MockWorkflowItem.java @@ -0,0 +1,62 @@ +/** + * The contents of this file are subject to the license and copyright + * detailed in the LICENSE and NOTICE files at the root of the source + * tree and available online at + * + * http://www.dspace.org/license/ + */ +package org.dspace.workflow; + +import org.dspace.content.Collection; +import org.dspace.content.Item; +import org.dspace.eperson.EPerson; + +public class MockWorkflowItem implements WorkflowItem { + public Integer id; + public Item item; + public Collection collection; + public EPerson submitter; + boolean hasMultipleFiles; + boolean hasMultipleTitles; + boolean isPublishedBefore; + + public Integer getID() { + return id; + } + + public Item getItem() { + return item; + } + + public Collection getCollection() { + return collection; + } + + public EPerson getSubmitter() { + return submitter; + } + + public boolean hasMultipleFiles() { + return hasMultipleFiles; + } + + public void setMultipleFiles(boolean b) { + hasMultipleFiles = b; + } + + public boolean hasMultipleTitles() { + return hasMultipleTitles; + } + + public void setMultipleTitles(boolean b) { + hasMultipleTitles = b; + } + + public boolean isPublishedBefore() { + return isPublishedBefore; + } + + public void setPublishedBefore(boolean b) { + isPublishedBefore = b; + } +} From 6c82e2dba40e50e72ab8459995802a5d7f526a53 Mon Sep 17 00:00:00 2001 From: Koen Pauwels Date: Tue, 7 Jan 2025 15:51:28 +0100 Subject: [PATCH 264/979] Bugfix: Enforce unique item id in workspace table (#9340) * 106798 Enforce values in item_id column of workspaceitem table to be unique, both at database level and at WorkspaceItemService level * 106798 Removed Oracle SQL migration * 106798 workspaceitem table migration: delete duplicate rows before introducing uniqueness constraint * 106798: update migration for H2 --------- Co-authored-by: Koen Pauwels Co-authored-by: wout --- .../content/WorkspaceItemServiceImpl.java | 8 +++ ...paceitem_add_item_id_unique_constraint.sql | 21 +++++++ ...paceitem_add_item_id_unique_constraint.sql | 21 +++++++ .../org/dspace/content/WorkspaceItemTest.java | 12 ++++ .../org/dspace/workflow/MockWorkflowItem.java | 62 +++++++++++++++++++ 5 files changed, 124 insertions(+) create mode 100644 dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/h2/V7.6_2024.12.17__workspaceitem_add_item_id_unique_constraint.sql create mode 100644 dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/postgres/V7.6_2024.12.17__workspaceitem_add_item_id_unique_constraint.sql create mode 100644 dspace-api/src/test/java/org/dspace/workflow/MockWorkflowItem.java diff --git a/dspace-api/src/main/java/org/dspace/content/WorkspaceItemServiceImpl.java b/dspace-api/src/main/java/org/dspace/content/WorkspaceItemServiceImpl.java index 1da9e6e44a6a..543f5a55efe2 100644 --- a/dspace-api/src/main/java/org/dspace/content/WorkspaceItemServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/content/WorkspaceItemServiceImpl.java @@ -178,6 +178,14 @@ public WorkspaceItem create(Context context, Collection collection, UUID uuid, b @Override public WorkspaceItem create(Context c, WorkflowItem workflowItem) throws SQLException, AuthorizeException { + WorkspaceItem potentialDuplicate = findByItem(c, workflowItem.getItem()); + if (potentialDuplicate != null) { + throw new IllegalArgumentException(String.format( + "A workspace item referring to item %s already exists (%d)", + workflowItem.getItem().getID(), + potentialDuplicate.getID() + )); + } WorkspaceItem workspaceItem = workspaceItemDAO.create(c, new WorkspaceItem()); workspaceItem.setItem(workflowItem.getItem()); workspaceItem.setCollection(workflowItem.getCollection()); diff --git a/dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/h2/V7.6_2024.12.17__workspaceitem_add_item_id_unique_constraint.sql b/dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/h2/V7.6_2024.12.17__workspaceitem_add_item_id_unique_constraint.sql new file mode 100644 index 000000000000..38389bf2d19b --- /dev/null +++ b/dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/h2/V7.6_2024.12.17__workspaceitem_add_item_id_unique_constraint.sql @@ -0,0 +1,21 @@ +-- +-- The contents of this file are subject to the license and copyright +-- detailed in the LICENSE and NOTICE files at the root of the source +-- tree and available online at +-- +-- http://www.dspace.org/license/ +-- + +-- In the workspaceitem table, if there are multiple rows referring to the same item ID, keep only the first of them. +DELETE FROM workspaceitem WHERE EXISTS ( + SELECT item_id + FROM workspaceitem + GROUP BY item_id + HAVING COUNT(workspace_item_id) > 1 +) AND workspaceitem.workspace_item_id NOT IN ( + SELECT MIN(workspace_item_id) AS workspace_item_id + FROM workspaceitem + GROUP BY item_id +); +-- Identify which rows have duplicates, and compute their replacements. +ALTER TABLE workspaceitem ADD CONSTRAINT unique_item_id UNIQUE(item_id); diff --git a/dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/postgres/V7.6_2024.12.17__workspaceitem_add_item_id_unique_constraint.sql b/dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/postgres/V7.6_2024.12.17__workspaceitem_add_item_id_unique_constraint.sql new file mode 100644 index 000000000000..20eb0f9119d3 --- /dev/null +++ b/dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/postgres/V7.6_2024.12.17__workspaceitem_add_item_id_unique_constraint.sql @@ -0,0 +1,21 @@ +-- +-- The contents of this file are subject to the license and copyright +-- detailed in the LICENSE and NOTICE files at the root of the source +-- tree and available online at +-- +-- http://www.dspace.org/license/ +-- + +-- In the workspaceitem table, if there are multiple rows referring to the same item ID, keep only the first of them. +WITH dedup AS ( + SELECT item_id, MIN(workspace_item_id) AS workspace_item_id + FROM workspaceitem + GROUP BY item_id + HAVING COUNT(workspace_item_id) > 1 +) +DELETE FROM workspaceitem +USING dedup +WHERE workspaceitem.item_id = dedup.item_id AND workspaceitem.workspace_item_id <> dedup.workspace_item_id; + +-- Enforce uniqueness of item_id in workspaceitem table. +ALTER TABLE workspaceitem ADD CONSTRAINT unique_item_id UNIQUE(item_id); diff --git a/dspace-api/src/test/java/org/dspace/content/WorkspaceItemTest.java b/dspace-api/src/test/java/org/dspace/content/WorkspaceItemTest.java index d018a15f9765..15d4720c9378 100644 --- a/dspace-api/src/test/java/org/dspace/content/WorkspaceItemTest.java +++ b/dspace-api/src/test/java/org/dspace/content/WorkspaceItemTest.java @@ -12,6 +12,7 @@ import static org.hamcrest.CoreMatchers.nullValue; import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertThrows; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import static org.mockito.ArgumentMatchers.any; @@ -39,6 +40,7 @@ import org.dspace.eperson.EPerson; import org.dspace.eperson.factory.EPersonServiceFactory; import org.dspace.eperson.service.EPersonService; +import org.dspace.workflow.MockWorkflowItem; import org.junit.After; import org.junit.Before; import org.junit.Test; @@ -468,4 +470,14 @@ public void testSetPublishedBefore() { assertTrue("testSetPublishedBefore 0", wi.isPublishedBefore()); } + @Test + public void testDuplicateItemID() throws Exception { + context.turnOffAuthorisationSystem(); + Item item = wi.getItem(); + MockWorkflowItem wfItem = new MockWorkflowItem(); + wfItem.item = item; + wfItem.collection = collection; + assertThrows(IllegalArgumentException.class, () -> workspaceItemService.create(context, wfItem)); + context.restoreAuthSystemState(); + } } diff --git a/dspace-api/src/test/java/org/dspace/workflow/MockWorkflowItem.java b/dspace-api/src/test/java/org/dspace/workflow/MockWorkflowItem.java new file mode 100644 index 000000000000..d36ecf7331b8 --- /dev/null +++ b/dspace-api/src/test/java/org/dspace/workflow/MockWorkflowItem.java @@ -0,0 +1,62 @@ +/** + * The contents of this file are subject to the license and copyright + * detailed in the LICENSE and NOTICE files at the root of the source + * tree and available online at + * + * http://www.dspace.org/license/ + */ +package org.dspace.workflow; + +import org.dspace.content.Collection; +import org.dspace.content.Item; +import org.dspace.eperson.EPerson; + +public class MockWorkflowItem implements WorkflowItem { + public Integer id; + public Item item; + public Collection collection; + public EPerson submitter; + boolean hasMultipleFiles; + boolean hasMultipleTitles; + boolean isPublishedBefore; + + public Integer getID() { + return id; + } + + public Item getItem() { + return item; + } + + public Collection getCollection() { + return collection; + } + + public EPerson getSubmitter() { + return submitter; + } + + public boolean hasMultipleFiles() { + return hasMultipleFiles; + } + + public void setMultipleFiles(boolean b) { + hasMultipleFiles = b; + } + + public boolean hasMultipleTitles() { + return hasMultipleTitles; + } + + public void setMultipleTitles(boolean b) { + hasMultipleTitles = b; + } + + public boolean isPublishedBefore() { + return isPublishedBefore; + } + + public void setPublishedBefore(boolean b) { + isPublishedBefore = b; + } +} From 22511a17b67c8034b039634e7bf98d9e9a58ae5d Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Wed, 6 Nov 2024 12:03:31 -0600 Subject: [PATCH 265/979] Refactor identifier ITs to ensure they unregister all utilized IdentifierProviders which are non-default. Cannot use "getApplicationContext().refresh()" as that seems to result in empty test database in Hibernate 6.6. (cherry picked from commit cfca2adbb1d597761b3169a5a46170de8cabfa4f) --- .../general/CreateMissingIdentifiersIT.java | 48 +++---------- .../AbstractIdentifierProviderIT.java | 68 +++++++++++++++++++ .../VersionedHandleIdentifierProviderIT.java | 49 ++----------- 3 files changed, 84 insertions(+), 81 deletions(-) create mode 100644 dspace-api/src/test/java/org/dspace/identifier/AbstractIdentifierProviderIT.java diff --git a/dspace-api/src/test/java/org/dspace/ctask/general/CreateMissingIdentifiersIT.java b/dspace-api/src/test/java/org/dspace/ctask/general/CreateMissingIdentifiersIT.java index 8038a7153325..4934404c3b3e 100644 --- a/dspace-api/src/test/java/org/dspace/ctask/general/CreateMissingIdentifiersIT.java +++ b/dspace-api/src/test/java/org/dspace/ctask/general/CreateMissingIdentifiersIT.java @@ -10,10 +10,7 @@ import static org.junit.Assert.assertEquals; import java.io.IOException; -import java.util.ArrayList; -import java.util.List; -import org.dspace.AbstractIntegrationTestWithDatabase; import org.dspace.builder.CollectionBuilder; import org.dspace.builder.CommunityBuilder; import org.dspace.builder.ItemBuilder; @@ -21,13 +18,11 @@ import org.dspace.content.Item; import org.dspace.core.factory.CoreServiceFactory; import org.dspace.curate.Curator; -import org.dspace.identifier.IdentifierProvider; -import org.dspace.identifier.IdentifierServiceImpl; +import org.dspace.identifier.AbstractIdentifierProviderIT; +import org.dspace.identifier.VersionedHandleIdentifierProvider; import org.dspace.identifier.VersionedHandleIdentifierProviderWithCanonicalHandles; -import org.dspace.kernel.ServiceManager; import org.dspace.services.ConfigurationService; import org.dspace.services.factory.DSpaceServicesFactory; -import org.junit.After; import org.junit.Test; /** @@ -36,30 +31,19 @@ * @author mwood */ public class CreateMissingIdentifiersIT - extends AbstractIntegrationTestWithDatabase { - private ServiceManager serviceManager; - private IdentifierServiceImpl identifierService; + extends AbstractIdentifierProviderIT { + private static final String P_TASK_DEF = "plugin.named.org.dspace.curate.CurationTask"; private static final String TASK_NAME = "test"; - @Override - public void setUp() throws Exception { - super.setUp(); - context.turnOffAuthorisationSystem(); - - serviceManager = DSpaceServicesFactory.getInstance().getServiceManager(); - identifierService = serviceManager.getServicesByType(IdentifierServiceImpl.class).get(0); - // Clean out providers to avoid any being used for creation of community and collection - identifierService.setProviders(new ArrayList<>()); - } + private ConfigurationService configurationService = DSpaceServicesFactory.getInstance().getConfigurationService(); @Test public void testPerform() throws IOException { // Must remove any cached named plugins before creating a new one CoreServiceFactory.getInstance().getPluginService().clearNamedPluginClasses(); - ConfigurationService configurationService = kernelImpl.getConfigurationService(); // Define a new task dynamically configurationService.setProperty(P_TASK_DEF, CreateMissingIdentifiers.class.getCanonicalName() + " = " + TASK_NAME); @@ -76,7 +60,7 @@ public void testPerform() .build(); /* - * Curate with regular test configuration -- should succeed. + * Curate with default Handle Provider */ curator.curate(context, item); int status = curator.getStatus(TASK_NAME); @@ -92,22 +76,10 @@ public void testPerform() curator.getResult(TASK_NAME)); assertEquals("Curation should fail", Curator.CURATE_ERROR, curator.getStatus(TASK_NAME)); - } - - @Override - @After - public void destroy() throws Exception { - super.destroy(); - DSpaceServicesFactory.getInstance().getServiceManager().getApplicationContext().refresh(); - } - - private void registerProvider(Class type) { - // Register our new provider - serviceManager.registerServiceClass(type.getName(), type); - IdentifierProvider identifierProvider = - (IdentifierProvider) serviceManager.getServiceByName(type.getName(), type); - // Overwrite the identifier-service's providers with the new one to ensure only this provider is used - identifierService.setProviders(List.of(identifierProvider)); + // Unregister this non-default provider + unregisterProvider(VersionedHandleIdentifierProviderWithCanonicalHandles.class); + // Re-register the default provider (for later tests which may depend on it) + registerProvider(VersionedHandleIdentifierProvider.class); } } diff --git a/dspace-api/src/test/java/org/dspace/identifier/AbstractIdentifierProviderIT.java b/dspace-api/src/test/java/org/dspace/identifier/AbstractIdentifierProviderIT.java new file mode 100644 index 000000000000..6ec9efddf5e2 --- /dev/null +++ b/dspace-api/src/test/java/org/dspace/identifier/AbstractIdentifierProviderIT.java @@ -0,0 +1,68 @@ +/** + * 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.identifier; + +import java.util.ArrayList; +import java.util.List; + +import org.dspace.AbstractIntegrationTestWithDatabase; +import org.dspace.kernel.ServiceManager; +import org.dspace.services.factory.DSpaceServicesFactory; + +/** + * AbstractIdentifierProviderIT which contains a few useful utility methods for IdentifierProvider Integration Tests + */ +public class AbstractIdentifierProviderIT extends AbstractIntegrationTestWithDatabase { + + protected final ServiceManager serviceManager = DSpaceServicesFactory.getInstance().getServiceManager(); + protected final IdentifierServiceImpl identifierService = + serviceManager.getServicesByType(IdentifierServiceImpl.class).get(0); + + /** + * Register a specific IdentifierProvider into the current IdentifierService (replacing any existing providers). + * This method will also ensure the IdentifierProvider service is registered in the DSpace Service Manager. + * @param type IdentifierProvider Class + */ + protected void registerProvider(Class type) { + // Register our new provider + IdentifierProvider identifierProvider = + (IdentifierProvider) DSpaceServicesFactory.getInstance().getServiceManager() + .getServiceByName(type.getName(), type); + if (identifierProvider == null) { + DSpaceServicesFactory.getInstance().getServiceManager().registerServiceClass(type.getName(), type); + identifierProvider = (IdentifierProvider) DSpaceServicesFactory.getInstance().getServiceManager() + .getServiceByName(type.getName(), type); + } + + identifierService.setProviders(List.of(identifierProvider)); + } + + /** + * Unregister a specific IdentifierProvider from the current IdentifierService (removing all existing providers). + * This method will also ensure the IdentifierProvider service is unregistered in the DSpace Service Manager, + * which ensures it does not conflict with other IdentifierProvider services. + * @param type IdentifierProvider Class + */ + protected void unregisterProvider(Class type) { + // Find the provider service + IdentifierProvider identifierProvider = + (IdentifierProvider) DSpaceServicesFactory.getInstance().getServiceManager() + .getServiceByName(type.getName(), type); + // If found, unregister it + if (identifierProvider == null) { + DSpaceServicesFactory.getInstance().getServiceManager().unregisterService(type.getName()); + } + + // Overwrite the identifier-service's providers with an empty list + identifierService.setProviders(new ArrayList<>()); + } + +} + + + diff --git a/dspace-api/src/test/java/org/dspace/identifier/VersionedHandleIdentifierProviderIT.java b/dspace-api/src/test/java/org/dspace/identifier/VersionedHandleIdentifierProviderIT.java index 57acf1f1c453..8c0f7e5b5e10 100644 --- a/dspace-api/src/test/java/org/dspace/identifier/VersionedHandleIdentifierProviderIT.java +++ b/dspace-api/src/test/java/org/dspace/identifier/VersionedHandleIdentifierProviderIT.java @@ -11,10 +11,7 @@ import static org.junit.Assert.assertTrue; import java.sql.SQLException; -import java.util.ArrayList; -import java.util.List; -import org.dspace.AbstractIntegrationTestWithDatabase; import org.dspace.authorize.AuthorizeException; import org.dspace.builder.CollectionBuilder; import org.dspace.builder.CommunityBuilder; @@ -22,15 +19,10 @@ import org.dspace.builder.VersionBuilder; import org.dspace.content.Collection; import org.dspace.content.Item; -import org.dspace.kernel.ServiceManager; -import org.dspace.services.factory.DSpaceServicesFactory; -import org.junit.After; import org.junit.Before; import org.junit.Test; -public class VersionedHandleIdentifierProviderIT extends AbstractIntegrationTestWithDatabase { - private ServiceManager serviceManager; - private IdentifierServiceImpl identifierService; +public class VersionedHandleIdentifierProviderIT extends AbstractIdentifierProviderIT { private String firstHandle; @@ -44,12 +36,6 @@ public class VersionedHandleIdentifierProviderIT extends AbstractIntegrationTest public void setUp() throws Exception { super.setUp(); context.turnOffAuthorisationSystem(); - - serviceManager = DSpaceServicesFactory.getInstance().getServiceManager(); - identifierService = serviceManager.getServicesByType(IdentifierServiceImpl.class).get(0); - // Clean out providers to avoid any being used for creation of community and collection - identifierService.setProviders(new ArrayList<>()); - parentCommunity = CommunityBuilder.createCommunity(context) .withName("Parent Community") .build(); @@ -58,33 +44,6 @@ public void setUp() throws Exception { .build(); } - @After - @Override - public void destroy() throws Exception { - super.destroy(); - // After this test has finished running, refresh application context and - // set the expected 'default' versioned handle provider back to ensure other tests don't fail - DSpaceServicesFactory.getInstance().getServiceManager().getApplicationContext().refresh(); - } - - private void registerProvider(Class type) { - // Register our new provider - IdentifierProvider identifierProvider = - (IdentifierProvider) DSpaceServicesFactory.getInstance().getServiceManager() - .getServiceByName(type.getName(), type); - if (identifierProvider == null) { - DSpaceServicesFactory.getInstance().getServiceManager().registerServiceClass(type.getName(), type); - identifierProvider = (IdentifierProvider) DSpaceServicesFactory.getInstance().getServiceManager() - .getServiceByName(type.getName(), type); - } - - // Overwrite the identifier-service's providers with the new one to ensure only this provider is used - identifierService = DSpaceServicesFactory.getInstance().getServiceManager() - .getServicesByType(IdentifierServiceImpl.class).get(0); - identifierService.setProviders(new ArrayList<>()); - identifierService.setProviders(List.of(identifierProvider)); - } - private void createVersions() throws SQLException, AuthorizeException { itemV1 = ItemBuilder.createItem(context, collection) .withTitle("First version") @@ -96,7 +55,6 @@ private void createVersions() throws SQLException, AuthorizeException { @Test public void testDefaultVersionedHandleProvider() throws Exception { - registerProvider(VersionedHandleIdentifierProvider.class); createVersions(); // Confirm the original item only has its original handle @@ -125,6 +83,11 @@ public void testCanonicalVersionedHandleProvider() throws Exception { assertEquals(firstHandle, itemV3.getHandle()); assertEquals(2, itemV3.getHandles().size()); containsHandle(itemV3, firstHandle + ".3"); + + // Unregister this non-default provider + unregisterProvider(VersionedHandleIdentifierProviderWithCanonicalHandles.class); + // Re-register the default provider (for later tests) + registerProvider(VersionedHandleIdentifierProvider.class); } private void containsHandle(Item item, String handle) { From 41207d5dee23c78d9f8a8ef93be89ed331607b13 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Fri, 8 Nov 2024 10:17:31 -0600 Subject: [PATCH 266/979] Update CreateMissingIdentifiers to better identify when CanonicalHandles provider is enabled. Update CreateMissingIdentifiersIT to verify that we are accurately resetting to our default IdentifierProvider (cherry picked from commit 2385c13f2d48048306a1280d88da5d8c022cc24f) --- .../general/CreateMissingIdentifiers.java | 28 +++++++++---------- .../identifier/IdentifierServiceImpl.java | 5 ++++ .../identifier/service/IdentifierService.java | 6 ++++ .../general/CreateMissingIdentifiersIT.java | 17 +++++------ 4 files changed, 33 insertions(+), 23 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/ctask/general/CreateMissingIdentifiers.java b/dspace-api/src/main/java/org/dspace/ctask/general/CreateMissingIdentifiers.java index 9639461426ef..0734d60946bc 100644 --- a/dspace-api/src/main/java/org/dspace/ctask/general/CreateMissingIdentifiers.java +++ b/dspace-api/src/main/java/org/dspace/ctask/general/CreateMissingIdentifiers.java @@ -10,6 +10,7 @@ import java.io.IOException; import java.sql.SQLException; +import java.util.List; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -25,7 +26,6 @@ import org.dspace.identifier.VersionedHandleIdentifierProviderWithCanonicalHandles; import org.dspace.identifier.factory.IdentifierServiceFactory; import org.dspace.identifier.service.IdentifierService; -import org.dspace.services.factory.DSpaceServicesFactory; /** * Ensure that an object has all of the identifiers that it should, minting them @@ -45,20 +45,6 @@ public int perform(DSpaceObject dso) return Curator.CURATE_SKIP; } - // XXX Temporary escape when an incompatible provider is configured. - // XXX Remove this when the provider is fixed. - boolean compatible = DSpaceServicesFactory - .getInstance() - .getServiceManager() - .getServiceByName( - VersionedHandleIdentifierProviderWithCanonicalHandles.class.getCanonicalName(), - IdentifierProvider.class) == null; - if (!compatible) { - setResult("This task is not compatible with VersionedHandleIdentifierProviderWithCanonicalHandles"); - return Curator.CURATE_ERROR; - } - // XXX End of escape - String typeText = Constants.typeText[dso.getType()]; // Get a Context @@ -75,6 +61,18 @@ public int perform(DSpaceObject dso) .getInstance() .getIdentifierService(); + // XXX Temporary escape when an incompatible provider is configured. + // XXX Remove this when the provider is fixed. + List providerList = identifierService.getProviders(); + boolean compatible = + providerList.stream().noneMatch(p -> p instanceof VersionedHandleIdentifierProviderWithCanonicalHandles); + + if (!compatible) { + setResult("This task is not compatible with VersionedHandleIdentifierProviderWithCanonicalHandles"); + return Curator.CURATE_ERROR; + } + // XXX End of escape + // Register any missing identifiers. try { identifierService.register(context, dso); diff --git a/dspace-api/src/main/java/org/dspace/identifier/IdentifierServiceImpl.java b/dspace-api/src/main/java/org/dspace/identifier/IdentifierServiceImpl.java index b98aea24fa08..e6dcfcdda693 100644 --- a/dspace-api/src/main/java/org/dspace/identifier/IdentifierServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/identifier/IdentifierServiceImpl.java @@ -57,6 +57,11 @@ public void setProviders(List providers) { } } + @Override + public List getProviders() { + return this.providers; + } + /** * Reserves identifiers for the item * diff --git a/dspace-api/src/main/java/org/dspace/identifier/service/IdentifierService.java b/dspace-api/src/main/java/org/dspace/identifier/service/IdentifierService.java index 23005b657508..45bf3c6dea8a 100644 --- a/dspace-api/src/main/java/org/dspace/identifier/service/IdentifierService.java +++ b/dspace-api/src/main/java/org/dspace/identifier/service/IdentifierService.java @@ -19,6 +19,7 @@ import org.dspace.identifier.IdentifierException; import org.dspace.identifier.IdentifierNotFoundException; import org.dspace.identifier.IdentifierNotResolvableException; +import org.dspace.identifier.IdentifierProvider; /** * @author Fabio Bolognesi (fabio at atmire dot com) @@ -194,4 +195,9 @@ void register(Context context, DSpaceObject dso, String identifier) void delete(Context context, DSpaceObject dso, String identifier) throws AuthorizeException, SQLException, IdentifierException; + /** + * Get List of currently enabled IdentifierProviders + * @return List of enabled IdentifierProvider objects. + */ + List getProviders(); } diff --git a/dspace-api/src/test/java/org/dspace/ctask/general/CreateMissingIdentifiersIT.java b/dspace-api/src/test/java/org/dspace/ctask/general/CreateMissingIdentifiersIT.java index 4934404c3b3e..3b50258a5a23 100644 --- a/dspace-api/src/test/java/org/dspace/ctask/general/CreateMissingIdentifiersIT.java +++ b/dspace-api/src/test/java/org/dspace/ctask/general/CreateMissingIdentifiersIT.java @@ -60,14 +60,7 @@ public void testPerform() .build(); /* - * Curate with default Handle Provider - */ - curator.curate(context, item); - int status = curator.getStatus(TASK_NAME); - assertEquals("Curation should succeed", Curator.CURATE_SUCCESS, status); - - /* - * Now install an incompatible provider to make the task fail. + * First, install an incompatible provider to make the task fail. */ registerProvider(VersionedHandleIdentifierProviderWithCanonicalHandles.class); @@ -81,5 +74,13 @@ public void testPerform() unregisterProvider(VersionedHandleIdentifierProviderWithCanonicalHandles.class); // Re-register the default provider (for later tests which may depend on it) registerProvider(VersionedHandleIdentifierProvider.class); + + /* + * Now, verify curate with default Handle Provider works + * (and that our re-registration of the default provider above was successful) + */ + curator.curate(context, item); + int status = curator.getStatus(TASK_NAME); + assertEquals("Curation should succeed", Curator.CURATE_SUCCESS, status); } } From 7ee4ba1a281c379d4fceb205428948b6aea14955 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Wed, 6 Nov 2024 12:03:31 -0600 Subject: [PATCH 267/979] Refactor identifier ITs to ensure they unregister all utilized IdentifierProviders which are non-default. Cannot use "getApplicationContext().refresh()" as that seems to result in empty test database in Hibernate 6.6. (cherry picked from commit cfca2adbb1d597761b3169a5a46170de8cabfa4f) --- .../general/CreateMissingIdentifiersIT.java | 48 +++---------- .../AbstractIdentifierProviderIT.java | 68 +++++++++++++++++++ .../VersionedHandleIdentifierProviderIT.java | 49 ++----------- 3 files changed, 84 insertions(+), 81 deletions(-) create mode 100644 dspace-api/src/test/java/org/dspace/identifier/AbstractIdentifierProviderIT.java diff --git a/dspace-api/src/test/java/org/dspace/ctask/general/CreateMissingIdentifiersIT.java b/dspace-api/src/test/java/org/dspace/ctask/general/CreateMissingIdentifiersIT.java index 8038a7153325..4934404c3b3e 100644 --- a/dspace-api/src/test/java/org/dspace/ctask/general/CreateMissingIdentifiersIT.java +++ b/dspace-api/src/test/java/org/dspace/ctask/general/CreateMissingIdentifiersIT.java @@ -10,10 +10,7 @@ import static org.junit.Assert.assertEquals; import java.io.IOException; -import java.util.ArrayList; -import java.util.List; -import org.dspace.AbstractIntegrationTestWithDatabase; import org.dspace.builder.CollectionBuilder; import org.dspace.builder.CommunityBuilder; import org.dspace.builder.ItemBuilder; @@ -21,13 +18,11 @@ import org.dspace.content.Item; import org.dspace.core.factory.CoreServiceFactory; import org.dspace.curate.Curator; -import org.dspace.identifier.IdentifierProvider; -import org.dspace.identifier.IdentifierServiceImpl; +import org.dspace.identifier.AbstractIdentifierProviderIT; +import org.dspace.identifier.VersionedHandleIdentifierProvider; import org.dspace.identifier.VersionedHandleIdentifierProviderWithCanonicalHandles; -import org.dspace.kernel.ServiceManager; import org.dspace.services.ConfigurationService; import org.dspace.services.factory.DSpaceServicesFactory; -import org.junit.After; import org.junit.Test; /** @@ -36,30 +31,19 @@ * @author mwood */ public class CreateMissingIdentifiersIT - extends AbstractIntegrationTestWithDatabase { - private ServiceManager serviceManager; - private IdentifierServiceImpl identifierService; + extends AbstractIdentifierProviderIT { + private static final String P_TASK_DEF = "plugin.named.org.dspace.curate.CurationTask"; private static final String TASK_NAME = "test"; - @Override - public void setUp() throws Exception { - super.setUp(); - context.turnOffAuthorisationSystem(); - - serviceManager = DSpaceServicesFactory.getInstance().getServiceManager(); - identifierService = serviceManager.getServicesByType(IdentifierServiceImpl.class).get(0); - // Clean out providers to avoid any being used for creation of community and collection - identifierService.setProviders(new ArrayList<>()); - } + private ConfigurationService configurationService = DSpaceServicesFactory.getInstance().getConfigurationService(); @Test public void testPerform() throws IOException { // Must remove any cached named plugins before creating a new one CoreServiceFactory.getInstance().getPluginService().clearNamedPluginClasses(); - ConfigurationService configurationService = kernelImpl.getConfigurationService(); // Define a new task dynamically configurationService.setProperty(P_TASK_DEF, CreateMissingIdentifiers.class.getCanonicalName() + " = " + TASK_NAME); @@ -76,7 +60,7 @@ public void testPerform() .build(); /* - * Curate with regular test configuration -- should succeed. + * Curate with default Handle Provider */ curator.curate(context, item); int status = curator.getStatus(TASK_NAME); @@ -92,22 +76,10 @@ public void testPerform() curator.getResult(TASK_NAME)); assertEquals("Curation should fail", Curator.CURATE_ERROR, curator.getStatus(TASK_NAME)); - } - - @Override - @After - public void destroy() throws Exception { - super.destroy(); - DSpaceServicesFactory.getInstance().getServiceManager().getApplicationContext().refresh(); - } - - private void registerProvider(Class type) { - // Register our new provider - serviceManager.registerServiceClass(type.getName(), type); - IdentifierProvider identifierProvider = - (IdentifierProvider) serviceManager.getServiceByName(type.getName(), type); - // Overwrite the identifier-service's providers with the new one to ensure only this provider is used - identifierService.setProviders(List.of(identifierProvider)); + // Unregister this non-default provider + unregisterProvider(VersionedHandleIdentifierProviderWithCanonicalHandles.class); + // Re-register the default provider (for later tests which may depend on it) + registerProvider(VersionedHandleIdentifierProvider.class); } } diff --git a/dspace-api/src/test/java/org/dspace/identifier/AbstractIdentifierProviderIT.java b/dspace-api/src/test/java/org/dspace/identifier/AbstractIdentifierProviderIT.java new file mode 100644 index 000000000000..6ec9efddf5e2 --- /dev/null +++ b/dspace-api/src/test/java/org/dspace/identifier/AbstractIdentifierProviderIT.java @@ -0,0 +1,68 @@ +/** + * 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.identifier; + +import java.util.ArrayList; +import java.util.List; + +import org.dspace.AbstractIntegrationTestWithDatabase; +import org.dspace.kernel.ServiceManager; +import org.dspace.services.factory.DSpaceServicesFactory; + +/** + * AbstractIdentifierProviderIT which contains a few useful utility methods for IdentifierProvider Integration Tests + */ +public class AbstractIdentifierProviderIT extends AbstractIntegrationTestWithDatabase { + + protected final ServiceManager serviceManager = DSpaceServicesFactory.getInstance().getServiceManager(); + protected final IdentifierServiceImpl identifierService = + serviceManager.getServicesByType(IdentifierServiceImpl.class).get(0); + + /** + * Register a specific IdentifierProvider into the current IdentifierService (replacing any existing providers). + * This method will also ensure the IdentifierProvider service is registered in the DSpace Service Manager. + * @param type IdentifierProvider Class + */ + protected void registerProvider(Class type) { + // Register our new provider + IdentifierProvider identifierProvider = + (IdentifierProvider) DSpaceServicesFactory.getInstance().getServiceManager() + .getServiceByName(type.getName(), type); + if (identifierProvider == null) { + DSpaceServicesFactory.getInstance().getServiceManager().registerServiceClass(type.getName(), type); + identifierProvider = (IdentifierProvider) DSpaceServicesFactory.getInstance().getServiceManager() + .getServiceByName(type.getName(), type); + } + + identifierService.setProviders(List.of(identifierProvider)); + } + + /** + * Unregister a specific IdentifierProvider from the current IdentifierService (removing all existing providers). + * This method will also ensure the IdentifierProvider service is unregistered in the DSpace Service Manager, + * which ensures it does not conflict with other IdentifierProvider services. + * @param type IdentifierProvider Class + */ + protected void unregisterProvider(Class type) { + // Find the provider service + IdentifierProvider identifierProvider = + (IdentifierProvider) DSpaceServicesFactory.getInstance().getServiceManager() + .getServiceByName(type.getName(), type); + // If found, unregister it + if (identifierProvider == null) { + DSpaceServicesFactory.getInstance().getServiceManager().unregisterService(type.getName()); + } + + // Overwrite the identifier-service's providers with an empty list + identifierService.setProviders(new ArrayList<>()); + } + +} + + + diff --git a/dspace-api/src/test/java/org/dspace/identifier/VersionedHandleIdentifierProviderIT.java b/dspace-api/src/test/java/org/dspace/identifier/VersionedHandleIdentifierProviderIT.java index 57acf1f1c453..8c0f7e5b5e10 100644 --- a/dspace-api/src/test/java/org/dspace/identifier/VersionedHandleIdentifierProviderIT.java +++ b/dspace-api/src/test/java/org/dspace/identifier/VersionedHandleIdentifierProviderIT.java @@ -11,10 +11,7 @@ import static org.junit.Assert.assertTrue; import java.sql.SQLException; -import java.util.ArrayList; -import java.util.List; -import org.dspace.AbstractIntegrationTestWithDatabase; import org.dspace.authorize.AuthorizeException; import org.dspace.builder.CollectionBuilder; import org.dspace.builder.CommunityBuilder; @@ -22,15 +19,10 @@ import org.dspace.builder.VersionBuilder; import org.dspace.content.Collection; import org.dspace.content.Item; -import org.dspace.kernel.ServiceManager; -import org.dspace.services.factory.DSpaceServicesFactory; -import org.junit.After; import org.junit.Before; import org.junit.Test; -public class VersionedHandleIdentifierProviderIT extends AbstractIntegrationTestWithDatabase { - private ServiceManager serviceManager; - private IdentifierServiceImpl identifierService; +public class VersionedHandleIdentifierProviderIT extends AbstractIdentifierProviderIT { private String firstHandle; @@ -44,12 +36,6 @@ public class VersionedHandleIdentifierProviderIT extends AbstractIntegrationTest public void setUp() throws Exception { super.setUp(); context.turnOffAuthorisationSystem(); - - serviceManager = DSpaceServicesFactory.getInstance().getServiceManager(); - identifierService = serviceManager.getServicesByType(IdentifierServiceImpl.class).get(0); - // Clean out providers to avoid any being used for creation of community and collection - identifierService.setProviders(new ArrayList<>()); - parentCommunity = CommunityBuilder.createCommunity(context) .withName("Parent Community") .build(); @@ -58,33 +44,6 @@ public void setUp() throws Exception { .build(); } - @After - @Override - public void destroy() throws Exception { - super.destroy(); - // After this test has finished running, refresh application context and - // set the expected 'default' versioned handle provider back to ensure other tests don't fail - DSpaceServicesFactory.getInstance().getServiceManager().getApplicationContext().refresh(); - } - - private void registerProvider(Class type) { - // Register our new provider - IdentifierProvider identifierProvider = - (IdentifierProvider) DSpaceServicesFactory.getInstance().getServiceManager() - .getServiceByName(type.getName(), type); - if (identifierProvider == null) { - DSpaceServicesFactory.getInstance().getServiceManager().registerServiceClass(type.getName(), type); - identifierProvider = (IdentifierProvider) DSpaceServicesFactory.getInstance().getServiceManager() - .getServiceByName(type.getName(), type); - } - - // Overwrite the identifier-service's providers with the new one to ensure only this provider is used - identifierService = DSpaceServicesFactory.getInstance().getServiceManager() - .getServicesByType(IdentifierServiceImpl.class).get(0); - identifierService.setProviders(new ArrayList<>()); - identifierService.setProviders(List.of(identifierProvider)); - } - private void createVersions() throws SQLException, AuthorizeException { itemV1 = ItemBuilder.createItem(context, collection) .withTitle("First version") @@ -96,7 +55,6 @@ private void createVersions() throws SQLException, AuthorizeException { @Test public void testDefaultVersionedHandleProvider() throws Exception { - registerProvider(VersionedHandleIdentifierProvider.class); createVersions(); // Confirm the original item only has its original handle @@ -125,6 +83,11 @@ public void testCanonicalVersionedHandleProvider() throws Exception { assertEquals(firstHandle, itemV3.getHandle()); assertEquals(2, itemV3.getHandles().size()); containsHandle(itemV3, firstHandle + ".3"); + + // Unregister this non-default provider + unregisterProvider(VersionedHandleIdentifierProviderWithCanonicalHandles.class); + // Re-register the default provider (for later tests) + registerProvider(VersionedHandleIdentifierProvider.class); } private void containsHandle(Item item, String handle) { From ad6d2eb014ec0ae2a3e9ee81970c11e8809da210 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Fri, 8 Nov 2024 10:17:31 -0600 Subject: [PATCH 268/979] Update CreateMissingIdentifiers to better identify when CanonicalHandles provider is enabled. Update CreateMissingIdentifiersIT to verify that we are accurately resetting to our default IdentifierProvider (cherry picked from commit 2385c13f2d48048306a1280d88da5d8c022cc24f) --- .../general/CreateMissingIdentifiers.java | 28 +++++++++---------- .../identifier/IdentifierServiceImpl.java | 5 ++++ .../identifier/service/IdentifierService.java | 6 ++++ .../general/CreateMissingIdentifiersIT.java | 17 +++++------ 4 files changed, 33 insertions(+), 23 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/ctask/general/CreateMissingIdentifiers.java b/dspace-api/src/main/java/org/dspace/ctask/general/CreateMissingIdentifiers.java index 9639461426ef..0734d60946bc 100644 --- a/dspace-api/src/main/java/org/dspace/ctask/general/CreateMissingIdentifiers.java +++ b/dspace-api/src/main/java/org/dspace/ctask/general/CreateMissingIdentifiers.java @@ -10,6 +10,7 @@ import java.io.IOException; import java.sql.SQLException; +import java.util.List; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -25,7 +26,6 @@ import org.dspace.identifier.VersionedHandleIdentifierProviderWithCanonicalHandles; import org.dspace.identifier.factory.IdentifierServiceFactory; import org.dspace.identifier.service.IdentifierService; -import org.dspace.services.factory.DSpaceServicesFactory; /** * Ensure that an object has all of the identifiers that it should, minting them @@ -45,20 +45,6 @@ public int perform(DSpaceObject dso) return Curator.CURATE_SKIP; } - // XXX Temporary escape when an incompatible provider is configured. - // XXX Remove this when the provider is fixed. - boolean compatible = DSpaceServicesFactory - .getInstance() - .getServiceManager() - .getServiceByName( - VersionedHandleIdentifierProviderWithCanonicalHandles.class.getCanonicalName(), - IdentifierProvider.class) == null; - if (!compatible) { - setResult("This task is not compatible with VersionedHandleIdentifierProviderWithCanonicalHandles"); - return Curator.CURATE_ERROR; - } - // XXX End of escape - String typeText = Constants.typeText[dso.getType()]; // Get a Context @@ -75,6 +61,18 @@ public int perform(DSpaceObject dso) .getInstance() .getIdentifierService(); + // XXX Temporary escape when an incompatible provider is configured. + // XXX Remove this when the provider is fixed. + List providerList = identifierService.getProviders(); + boolean compatible = + providerList.stream().noneMatch(p -> p instanceof VersionedHandleIdentifierProviderWithCanonicalHandles); + + if (!compatible) { + setResult("This task is not compatible with VersionedHandleIdentifierProviderWithCanonicalHandles"); + return Curator.CURATE_ERROR; + } + // XXX End of escape + // Register any missing identifiers. try { identifierService.register(context, dso); diff --git a/dspace-api/src/main/java/org/dspace/identifier/IdentifierServiceImpl.java b/dspace-api/src/main/java/org/dspace/identifier/IdentifierServiceImpl.java index b98aea24fa08..e6dcfcdda693 100644 --- a/dspace-api/src/main/java/org/dspace/identifier/IdentifierServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/identifier/IdentifierServiceImpl.java @@ -57,6 +57,11 @@ public void setProviders(List providers) { } } + @Override + public List getProviders() { + return this.providers; + } + /** * Reserves identifiers for the item * diff --git a/dspace-api/src/main/java/org/dspace/identifier/service/IdentifierService.java b/dspace-api/src/main/java/org/dspace/identifier/service/IdentifierService.java index 23005b657508..45bf3c6dea8a 100644 --- a/dspace-api/src/main/java/org/dspace/identifier/service/IdentifierService.java +++ b/dspace-api/src/main/java/org/dspace/identifier/service/IdentifierService.java @@ -19,6 +19,7 @@ import org.dspace.identifier.IdentifierException; import org.dspace.identifier.IdentifierNotFoundException; import org.dspace.identifier.IdentifierNotResolvableException; +import org.dspace.identifier.IdentifierProvider; /** * @author Fabio Bolognesi (fabio at atmire dot com) @@ -194,4 +195,9 @@ void register(Context context, DSpaceObject dso, String identifier) void delete(Context context, DSpaceObject dso, String identifier) throws AuthorizeException, SQLException, IdentifierException; + /** + * Get List of currently enabled IdentifierProviders + * @return List of enabled IdentifierProvider objects. + */ + List getProviders(); } diff --git a/dspace-api/src/test/java/org/dspace/ctask/general/CreateMissingIdentifiersIT.java b/dspace-api/src/test/java/org/dspace/ctask/general/CreateMissingIdentifiersIT.java index 4934404c3b3e..3b50258a5a23 100644 --- a/dspace-api/src/test/java/org/dspace/ctask/general/CreateMissingIdentifiersIT.java +++ b/dspace-api/src/test/java/org/dspace/ctask/general/CreateMissingIdentifiersIT.java @@ -60,14 +60,7 @@ public void testPerform() .build(); /* - * Curate with default Handle Provider - */ - curator.curate(context, item); - int status = curator.getStatus(TASK_NAME); - assertEquals("Curation should succeed", Curator.CURATE_SUCCESS, status); - - /* - * Now install an incompatible provider to make the task fail. + * First, install an incompatible provider to make the task fail. */ registerProvider(VersionedHandleIdentifierProviderWithCanonicalHandles.class); @@ -81,5 +74,13 @@ public void testPerform() unregisterProvider(VersionedHandleIdentifierProviderWithCanonicalHandles.class); // Re-register the default provider (for later tests which may depend on it) registerProvider(VersionedHandleIdentifierProvider.class); + + /* + * Now, verify curate with default Handle Provider works + * (and that our re-registration of the default provider above was successful) + */ + curator.curate(context, item); + int status = curator.getStatus(TASK_NAME); + assertEquals("Curation should succeed", Curator.CURATE_SUCCESS, status); } } From 97a5439e3aa23bfde4f9741282d48c10a873b387 Mon Sep 17 00:00:00 2001 From: "Gantner, Florian Klaus" Date: Thu, 9 Jan 2025 14:09:24 +0100 Subject: [PATCH 269/979] switch IT search core to MockSolrSearchCore https://github.com/DSpace/DSpace/issues/10188 (cherry picked from commit 6d781e8f83a912863af8d998d982e030db64a2ce) --- .../org/dspace/content/VersioningWithRelationshipsIT.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/dspace-api/src/test/java/org/dspace/content/VersioningWithRelationshipsIT.java b/dspace-api/src/test/java/org/dspace/content/VersioningWithRelationshipsIT.java index 3acc4ca146ee..9ff452f7895f 100644 --- a/dspace-api/src/test/java/org/dspace/content/VersioningWithRelationshipsIT.java +++ b/dspace-api/src/test/java/org/dspace/content/VersioningWithRelationshipsIT.java @@ -59,7 +59,7 @@ import org.dspace.content.virtual.VirtualMetadataConfiguration; import org.dspace.content.virtual.VirtualMetadataPopulator; import org.dspace.core.Constants; -import org.dspace.discovery.SolrSearchCore; +import org.dspace.discovery.MockSolrSearchCore; import org.dspace.kernel.ServiceManager; import org.dspace.services.factory.DSpaceServicesFactory; import org.dspace.versioning.Version; @@ -79,8 +79,9 @@ public class VersioningWithRelationshipsIT extends AbstractIntegrationTestWithDa ContentServiceFactory.getInstance().getInstallItemService(); private final ItemService itemService = ContentServiceFactory.getInstance().getItemService(); - private final SolrSearchCore solrSearchCore = - DSpaceServicesFactory.getInstance().getServiceManager().getServicesByType(SolrSearchCore.class).get(0); + private final MockSolrSearchCore solrSearchCore = + DSpaceServicesFactory.getInstance().getServiceManager().getServiceByName(null, MockSolrSearchCore.class); + protected Community community; protected Collection collection; protected EntityType publicationEntityType; From fb47b27a2b55f59b3cf3c3cf77401ec9ef0ea594 Mon Sep 17 00:00:00 2001 From: "Gantner, Florian Klaus" Date: Thu, 9 Jan 2025 14:09:24 +0100 Subject: [PATCH 270/979] switch IT search core to MockSolrSearchCore https://github.com/DSpace/DSpace/issues/10188 (cherry picked from commit 6d781e8f83a912863af8d998d982e030db64a2ce) --- .../org/dspace/content/VersioningWithRelationshipsIT.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/dspace-api/src/test/java/org/dspace/content/VersioningWithRelationshipsIT.java b/dspace-api/src/test/java/org/dspace/content/VersioningWithRelationshipsIT.java index 3acc4ca146ee..9ff452f7895f 100644 --- a/dspace-api/src/test/java/org/dspace/content/VersioningWithRelationshipsIT.java +++ b/dspace-api/src/test/java/org/dspace/content/VersioningWithRelationshipsIT.java @@ -59,7 +59,7 @@ import org.dspace.content.virtual.VirtualMetadataConfiguration; import org.dspace.content.virtual.VirtualMetadataPopulator; import org.dspace.core.Constants; -import org.dspace.discovery.SolrSearchCore; +import org.dspace.discovery.MockSolrSearchCore; import org.dspace.kernel.ServiceManager; import org.dspace.services.factory.DSpaceServicesFactory; import org.dspace.versioning.Version; @@ -79,8 +79,9 @@ public class VersioningWithRelationshipsIT extends AbstractIntegrationTestWithDa ContentServiceFactory.getInstance().getInstallItemService(); private final ItemService itemService = ContentServiceFactory.getInstance().getItemService(); - private final SolrSearchCore solrSearchCore = - DSpaceServicesFactory.getInstance().getServiceManager().getServicesByType(SolrSearchCore.class).get(0); + private final MockSolrSearchCore solrSearchCore = + DSpaceServicesFactory.getInstance().getServiceManager().getServiceByName(null, MockSolrSearchCore.class); + protected Community community; protected Collection collection; protected EntityType publicationEntityType; From 49b3deef77055a18daba21c0bf2812bb2daa3265 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Thu, 9 Jan 2025 15:42:25 -0600 Subject: [PATCH 271/979] Refactor AbstractIntegrationTestWithDatabase to use Builders to create test EPersons. (cherry picked from commit 0b8b7be22b88a411898bcb975e238e7cd96f40ab) --- .../AbstractIntegrationTestWithDatabase.java | 48 ++++++++----------- .../AbstractWebClientIntegrationTest.java | 16 +++++++ 2 files changed, 37 insertions(+), 27 deletions(-) diff --git a/dspace-api/src/test/java/org/dspace/AbstractIntegrationTestWithDatabase.java b/dspace-api/src/test/java/org/dspace/AbstractIntegrationTestWithDatabase.java index e27fb19a68eb..d5bb6ab8a4c8 100644 --- a/dspace-api/src/test/java/org/dspace/AbstractIntegrationTestWithDatabase.java +++ b/dspace-api/src/test/java/org/dspace/AbstractIntegrationTestWithDatabase.java @@ -17,8 +17,8 @@ import org.dspace.app.scripts.handler.impl.TestDSpaceRunnableHandler; import org.dspace.authority.AuthoritySearchService; import org.dspace.authority.MockAuthoritySolrServiceImpl; -import org.dspace.authorize.AuthorizeException; import org.dspace.builder.AbstractBuilder; +import org.dspace.builder.EPersonBuilder; import org.dspace.content.Community; import org.dspace.core.Context; import org.dspace.core.I18nUtil; @@ -114,19 +114,16 @@ public void setUp() throws Exception { EPersonService ePersonService = EPersonServiceFactory.getInstance().getEPersonService(); eperson = ePersonService.findByEmail(context, "test@email.com"); if (eperson == null) { - // This EPerson creation should only happen once (i.e. for first test run) - log.info("Creating initial EPerson (email=test@email.com) for Unit Tests"); - eperson = ePersonService.create(context); - eperson.setFirstName(context, "first"); - eperson.setLastName(context, "last"); - eperson.setEmail("test@email.com"); - eperson.setCanLogIn(true); - eperson.setLanguage(context, I18nUtil.getDefaultLocale().getLanguage()); - ePersonService.setPassword(eperson, password); - // actually save the eperson to unit testing DB - ePersonService.update(context, eperson); + // Create test EPerson for usage in all tests + log.info("Creating Test EPerson (email=test@email.com) for Integration Tests"); + eperson = EPersonBuilder.createEPerson(context) + .withNameInMetadata("first", "last") + .withEmail("test@email.com") + .withCanLogin(true) + .withLanguage(I18nUtil.getDefaultLocale().getLanguage()) + .withPassword(password) + .build(); } - // Set our global test EPerson as the current user in DSpace context.setCurrentUser(eperson); @@ -135,26 +132,23 @@ public void setUp() throws Exception { admin = ePersonService.findByEmail(context, "admin@email.com"); if (admin == null) { - // This EPerson creation should only happen once (i.e. for first test run) - log.info("Creating initial EPerson (email=admin@email.com) for Unit Tests"); - admin = ePersonService.create(context); - admin.setFirstName(context, "first (admin)"); - admin.setLastName(context, "last (admin)"); - admin.setEmail("admin@email.com"); - admin.setCanLogIn(true); - admin.setLanguage(context, I18nUtil.getDefaultLocale().getLanguage()); - ePersonService.setPassword(admin, password); - // actually save the eperson to unit testing DB - ePersonService.update(context, admin); + // Create test Administrator for usage in all tests + log.info("Creating Test Admin EPerson (email=admin@email.com) for Integration Tests"); + admin = EPersonBuilder.createEPerson(context) + .withNameInMetadata("first (admin)", "last (admin)") + .withEmail("admin@email.com") + .withCanLogin(true) + .withLanguage(I18nUtil.getDefaultLocale().getLanguage()) + .withPassword(password) + .build(); + + // Add Test Administrator to the ADMIN group in test database GroupService groupService = EPersonServiceFactory.getInstance().getGroupService(); Group adminGroup = groupService.findByName(context, Group.ADMIN); groupService.addMember(context, adminGroup, admin); } context.restoreAuthSystemState(); - } catch (AuthorizeException ex) { - log.error("Error creating initial eperson or default groups", ex); - fail("Error creating initial eperson or default groups in AbstractUnitTest init()"); } catch (SQLException ex) { log.error(ex.getMessage(), ex); fail("SQL Error on AbstractUnitTest init()"); diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/test/AbstractWebClientIntegrationTest.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/test/AbstractWebClientIntegrationTest.java index df6afcb16e32..fadebff7de5b 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/test/AbstractWebClientIntegrationTest.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/test/AbstractWebClientIntegrationTest.java @@ -12,6 +12,7 @@ import org.dspace.app.rest.Application; import org.dspace.app.rest.utils.DSpaceConfigurationInitializer; import org.dspace.app.rest.utils.DSpaceKernelInitializer; +import org.junit.Before; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; @@ -64,6 +65,21 @@ public class AbstractWebClientIntegrationTest extends AbstractIntegrationTestWit @Autowired protected ApplicationContext applicationContext; + @Override + @Before + public void setUp() throws Exception { + super.setUp(); + + // Because AbstractWebClientIntegrationTest starts a new webserver, we need to ensure *everything* created by + // the "super.setUp()" script is committed to the test database. Otherwise, the new webserver may not see the + // created test data in the database. + // NOTE: This commit() does NOT occur in AbstractIntegrationTestDatabase because it will remove newly created + // Hibernate entities from the current Hibernate session. For most ITs we don't want that as it may result + // in "could not initialize proxy - no Session" errors when using those entities in other tests (or other tests + // would need to reload each test entity back into the Hibernate session) + context.commit(); + } + /** * Get client TestRestTemplate for making HTTP requests to test webserver * @return TestRestTemplate From 391ba5d7a774e4aea8d1351229a83b58220afe51 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Thu, 9 Jan 2025 15:42:25 -0600 Subject: [PATCH 272/979] Refactor AbstractIntegrationTestWithDatabase to use Builders to create test EPersons. (cherry picked from commit 0b8b7be22b88a411898bcb975e238e7cd96f40ab) --- .../AbstractIntegrationTestWithDatabase.java | 48 ++++++++----------- .../AbstractWebClientIntegrationTest.java | 16 +++++++ 2 files changed, 37 insertions(+), 27 deletions(-) diff --git a/dspace-api/src/test/java/org/dspace/AbstractIntegrationTestWithDatabase.java b/dspace-api/src/test/java/org/dspace/AbstractIntegrationTestWithDatabase.java index 9bacbb97eec4..76b3fe131be0 100644 --- a/dspace-api/src/test/java/org/dspace/AbstractIntegrationTestWithDatabase.java +++ b/dspace-api/src/test/java/org/dspace/AbstractIntegrationTestWithDatabase.java @@ -20,8 +20,8 @@ import org.dspace.app.scripts.handler.impl.TestDSpaceRunnableHandler; import org.dspace.authority.AuthoritySearchService; import org.dspace.authority.MockAuthoritySolrServiceImpl; -import org.dspace.authorize.AuthorizeException; import org.dspace.builder.AbstractBuilder; +import org.dspace.builder.EPersonBuilder; import org.dspace.content.Community; import org.dspace.core.Context; import org.dspace.core.I18nUtil; @@ -127,19 +127,16 @@ public void setUp() throws Exception { EPersonService ePersonService = EPersonServiceFactory.getInstance().getEPersonService(); eperson = ePersonService.findByEmail(context, "test@email.com"); if (eperson == null) { - // This EPerson creation should only happen once (i.e. for first test run) - log.info("Creating initial EPerson (email=test@email.com) for Unit Tests"); - eperson = ePersonService.create(context); - eperson.setFirstName(context, "first"); - eperson.setLastName(context, "last"); - eperson.setEmail("test@email.com"); - eperson.setCanLogIn(true); - eperson.setLanguage(context, I18nUtil.getDefaultLocale().getLanguage()); - ePersonService.setPassword(eperson, password); - // actually save the eperson to unit testing DB - ePersonService.update(context, eperson); + // Create test EPerson for usage in all tests + log.info("Creating Test EPerson (email=test@email.com) for Integration Tests"); + eperson = EPersonBuilder.createEPerson(context) + .withNameInMetadata("first", "last") + .withEmail("test@email.com") + .withCanLogin(true) + .withLanguage(I18nUtil.getDefaultLocale().getLanguage()) + .withPassword(password) + .build(); } - // Set our global test EPerson as the current user in DSpace context.setCurrentUser(eperson); @@ -148,26 +145,23 @@ public void setUp() throws Exception { admin = ePersonService.findByEmail(context, "admin@email.com"); if (admin == null) { - // This EPerson creation should only happen once (i.e. for first test run) - log.info("Creating initial EPerson (email=admin@email.com) for Unit Tests"); - admin = ePersonService.create(context); - admin.setFirstName(context, "first (admin)"); - admin.setLastName(context, "last (admin)"); - admin.setEmail("admin@email.com"); - admin.setCanLogIn(true); - admin.setLanguage(context, I18nUtil.getDefaultLocale().getLanguage()); - ePersonService.setPassword(admin, password); - // actually save the eperson to unit testing DB - ePersonService.update(context, admin); + // Create test Administrator for usage in all tests + log.info("Creating Test Admin EPerson (email=admin@email.com) for Integration Tests"); + admin = EPersonBuilder.createEPerson(context) + .withNameInMetadata("first (admin)", "last (admin)") + .withEmail("admin@email.com") + .withCanLogin(true) + .withLanguage(I18nUtil.getDefaultLocale().getLanguage()) + .withPassword(password) + .build(); + + // Add Test Administrator to the ADMIN group in test database GroupService groupService = EPersonServiceFactory.getInstance().getGroupService(); Group adminGroup = groupService.findByName(context, Group.ADMIN); groupService.addMember(context, adminGroup, admin); } context.restoreAuthSystemState(); - } catch (AuthorizeException ex) { - log.error("Error creating initial eperson or default groups", ex); - fail("Error creating initial eperson or default groups in AbstractUnitTest init()"); } catch (SQLException ex) { log.error(ex.getMessage(), ex); fail("SQL Error on AbstractUnitTest init()"); diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/test/AbstractWebClientIntegrationTest.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/test/AbstractWebClientIntegrationTest.java index e4ef1c741f72..f7ec1e941d0e 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/test/AbstractWebClientIntegrationTest.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/test/AbstractWebClientIntegrationTest.java @@ -12,6 +12,7 @@ import org.dspace.app.TestApplication; import org.dspace.app.rest.utils.DSpaceConfigurationInitializer; import org.dspace.app.rest.utils.DSpaceKernelInitializer; +import org.junit.Before; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; @@ -64,6 +65,21 @@ public class AbstractWebClientIntegrationTest extends AbstractIntegrationTestWit @Autowired protected ApplicationContext applicationContext; + @Override + @Before + public void setUp() throws Exception { + super.setUp(); + + // Because AbstractWebClientIntegrationTest starts a new webserver, we need to ensure *everything* created by + // the "super.setUp()" script is committed to the test database. Otherwise, the new webserver may not see the + // created test data in the database. + // NOTE: This commit() does NOT occur in AbstractIntegrationTestDatabase because it will remove newly created + // Hibernate entities from the current Hibernate session. For most ITs we don't want that as it may result + // in "could not initialize proxy - no Session" errors when using those entities in other tests (or other tests + // would need to reload each test entity back into the Hibernate session) + context.commit(); + } + /** * Get client TestRestTemplate for making HTTP requests to test webserver * @return TestRestTemplate From 5973c67eff7180496ee761a32fa1f9636b02c93e Mon Sep 17 00:00:00 2001 From: Sascha Szott Date: Tue, 9 Jul 2024 16:58:00 +0200 Subject: [PATCH 273/979] put DOIs in dc.identifier.doi (cherry picked from commit 3d1bef9d0e3fe820da9705427c129b5cb49d66fb) --- dspace/config/spring/api/scopus-integration.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dspace/config/spring/api/scopus-integration.xml b/dspace/config/spring/api/scopus-integration.xml index 47c5a4fbb900..6f7556574d09 100644 --- a/dspace/config/spring/api/scopus-integration.xml +++ b/dspace/config/spring/api/scopus-integration.xml @@ -128,7 +128,7 @@ - + @@ -264,4 +264,4 @@ - \ No newline at end of file + From 5d0e9871e9ec4cbacda577f9f2ae8cefcd961e27 Mon Sep 17 00:00:00 2001 From: Sascha Szott Date: Tue, 9 Jul 2024 17:37:07 +0200 Subject: [PATCH 274/979] fix broken test (cherry picked from commit 2eff833fab361d4ed3c7b602b681823911f989af) --- .../dspace/app/rest/ScopusImportMetadataSourceServiceIT.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/ScopusImportMetadataSourceServiceIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/ScopusImportMetadataSourceServiceIT.java index 7f6cb53ce400..e81f662ad5a9 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/ScopusImportMetadataSourceServiceIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/ScopusImportMetadataSourceServiceIT.java @@ -124,7 +124,7 @@ private ArrayList getRecords() { ArrayList records = new ArrayList<>(); //define first record List metadatums = new ArrayList(); - MetadatumDTO doi = createMetadatumDTO("dc", "identifier", null, "10.3934/mine.2023004"); + MetadatumDTO doi = createMetadatumDTO("dc", "identifier", "doi", "10.3934/mine.2023004"); MetadatumDTO title = createMetadatumDTO("dc","title", null, "Hardy potential versus lower order terms in Dirichlet problems: regularizing effects"); MetadatumDTO type = createMetadatumDTO("dc", "type", null, "Journal"); @@ -171,7 +171,7 @@ private ArrayList getRecords() { //define second record List metadatums2 = new ArrayList(); - MetadatumDTO doi2 = createMetadatumDTO("dc", "identifier", null, "10.3934/mine.2023001"); + MetadatumDTO doi2 = createMetadatumDTO("dc", "identifier", "doi", "10.3934/mine.2023001"); MetadatumDTO title2 = createMetadatumDTO("dc","title", null, "Large deviations for a binary collision model: energy evaporation"); MetadatumDTO date2 = createMetadatumDTO("dc", "date", "issued", "2023-01-01"); From 09a722a029ebc6e50835c360af8977d97cb1c033 Mon Sep 17 00:00:00 2001 From: Sascha Szott Date: Thu, 17 Oct 2024 20:17:06 +0200 Subject: [PATCH 275/979] use dc.relation.hasversion for externally generated DOIs (cherry picked from commit 29067b6572b538650e4686e983edafcc355d87c5) --- dspace/config/spring/api/scopus-integration.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dspace/config/spring/api/scopus-integration.xml b/dspace/config/spring/api/scopus-integration.xml index 6f7556574d09..d3e8150c74b1 100644 --- a/dspace/config/spring/api/scopus-integration.xml +++ b/dspace/config/spring/api/scopus-integration.xml @@ -128,7 +128,7 @@ - + From d109eac2a0ad170b656e36028d84fc294ae6eb35 Mon Sep 17 00:00:00 2001 From: Sascha Szott Date: Thu, 17 Oct 2024 20:19:04 +0200 Subject: [PATCH 276/979] use dc.relation.hasversion instead of dc.identifier.doi (cherry picked from commit d61dc8d911a4d226362d9a554226da5c304ad722) --- .../dspace/app/rest/ScopusImportMetadataSourceServiceIT.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/ScopusImportMetadataSourceServiceIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/ScopusImportMetadataSourceServiceIT.java index e81f662ad5a9..f9164ddc5994 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/ScopusImportMetadataSourceServiceIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/ScopusImportMetadataSourceServiceIT.java @@ -124,7 +124,7 @@ private ArrayList getRecords() { ArrayList records = new ArrayList<>(); //define first record List metadatums = new ArrayList(); - MetadatumDTO doi = createMetadatumDTO("dc", "identifier", "doi", "10.3934/mine.2023004"); + MetadatumDTO doi = createMetadatumDTO("dc", "relation", "hasversion", "10.3934/mine.2023004"); MetadatumDTO title = createMetadatumDTO("dc","title", null, "Hardy potential versus lower order terms in Dirichlet problems: regularizing effects"); MetadatumDTO type = createMetadatumDTO("dc", "type", null, "Journal"); @@ -171,7 +171,7 @@ private ArrayList getRecords() { //define second record List metadatums2 = new ArrayList(); - MetadatumDTO doi2 = createMetadatumDTO("dc", "identifier", "doi", "10.3934/mine.2023001"); + MetadatumDTO doi2 = createMetadatumDTO("dc", "relation", "hasversion", "10.3934/mine.2023001"); MetadatumDTO title2 = createMetadatumDTO("dc","title", null, "Large deviations for a binary collision model: energy evaporation"); MetadatumDTO date2 = createMetadatumDTO("dc", "date", "issued", "2023-01-01"); From 07a478b31add75b1653c0e1f78971935b9353e81 Mon Sep 17 00:00:00 2001 From: Sascha Szott Date: Tue, 9 Jul 2024 16:58:00 +0200 Subject: [PATCH 277/979] put DOIs in dc.identifier.doi (cherry picked from commit 3d1bef9d0e3fe820da9705427c129b5cb49d66fb) --- dspace/config/spring/api/scopus-integration.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dspace/config/spring/api/scopus-integration.xml b/dspace/config/spring/api/scopus-integration.xml index 47c5a4fbb900..6f7556574d09 100644 --- a/dspace/config/spring/api/scopus-integration.xml +++ b/dspace/config/spring/api/scopus-integration.xml @@ -128,7 +128,7 @@ - + @@ -264,4 +264,4 @@ - \ No newline at end of file + From 7a24605c0ac0426af6b3042d23bb28ab0e755727 Mon Sep 17 00:00:00 2001 From: Sascha Szott Date: Tue, 9 Jul 2024 17:37:07 +0200 Subject: [PATCH 278/979] fix broken test (cherry picked from commit 2eff833fab361d4ed3c7b602b681823911f989af) --- .../dspace/app/rest/ScopusImportMetadataSourceServiceIT.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/ScopusImportMetadataSourceServiceIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/ScopusImportMetadataSourceServiceIT.java index 7f6cb53ce400..e81f662ad5a9 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/ScopusImportMetadataSourceServiceIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/ScopusImportMetadataSourceServiceIT.java @@ -124,7 +124,7 @@ private ArrayList getRecords() { ArrayList records = new ArrayList<>(); //define first record List metadatums = new ArrayList(); - MetadatumDTO doi = createMetadatumDTO("dc", "identifier", null, "10.3934/mine.2023004"); + MetadatumDTO doi = createMetadatumDTO("dc", "identifier", "doi", "10.3934/mine.2023004"); MetadatumDTO title = createMetadatumDTO("dc","title", null, "Hardy potential versus lower order terms in Dirichlet problems: regularizing effects"); MetadatumDTO type = createMetadatumDTO("dc", "type", null, "Journal"); @@ -171,7 +171,7 @@ private ArrayList getRecords() { //define second record List metadatums2 = new ArrayList(); - MetadatumDTO doi2 = createMetadatumDTO("dc", "identifier", null, "10.3934/mine.2023001"); + MetadatumDTO doi2 = createMetadatumDTO("dc", "identifier", "doi", "10.3934/mine.2023001"); MetadatumDTO title2 = createMetadatumDTO("dc","title", null, "Large deviations for a binary collision model: energy evaporation"); MetadatumDTO date2 = createMetadatumDTO("dc", "date", "issued", "2023-01-01"); From 0416be3802e522638eb3012c52655f820303f76e Mon Sep 17 00:00:00 2001 From: Sascha Szott Date: Thu, 17 Oct 2024 20:17:06 +0200 Subject: [PATCH 279/979] use dc.relation.hasversion for externally generated DOIs (cherry picked from commit 29067b6572b538650e4686e983edafcc355d87c5) --- dspace/config/spring/api/scopus-integration.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dspace/config/spring/api/scopus-integration.xml b/dspace/config/spring/api/scopus-integration.xml index 6f7556574d09..d3e8150c74b1 100644 --- a/dspace/config/spring/api/scopus-integration.xml +++ b/dspace/config/spring/api/scopus-integration.xml @@ -128,7 +128,7 @@ - + From 8be99e47fa0acb8950ee53a9f2dad2bf39b5c8be Mon Sep 17 00:00:00 2001 From: Sascha Szott Date: Thu, 17 Oct 2024 20:19:04 +0200 Subject: [PATCH 280/979] use dc.relation.hasversion instead of dc.identifier.doi (cherry picked from commit d61dc8d911a4d226362d9a554226da5c304ad722) --- .../dspace/app/rest/ScopusImportMetadataSourceServiceIT.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/ScopusImportMetadataSourceServiceIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/ScopusImportMetadataSourceServiceIT.java index e81f662ad5a9..f9164ddc5994 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/ScopusImportMetadataSourceServiceIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/ScopusImportMetadataSourceServiceIT.java @@ -124,7 +124,7 @@ private ArrayList getRecords() { ArrayList records = new ArrayList<>(); //define first record List metadatums = new ArrayList(); - MetadatumDTO doi = createMetadatumDTO("dc", "identifier", "doi", "10.3934/mine.2023004"); + MetadatumDTO doi = createMetadatumDTO("dc", "relation", "hasversion", "10.3934/mine.2023004"); MetadatumDTO title = createMetadatumDTO("dc","title", null, "Hardy potential versus lower order terms in Dirichlet problems: regularizing effects"); MetadatumDTO type = createMetadatumDTO("dc", "type", null, "Journal"); @@ -171,7 +171,7 @@ private ArrayList getRecords() { //define second record List metadatums2 = new ArrayList(); - MetadatumDTO doi2 = createMetadatumDTO("dc", "identifier", "doi", "10.3934/mine.2023001"); + MetadatumDTO doi2 = createMetadatumDTO("dc", "relation", "hasversion", "10.3934/mine.2023001"); MetadatumDTO title2 = createMetadatumDTO("dc","title", null, "Large deviations for a binary collision model: energy evaporation"); MetadatumDTO date2 = createMetadatumDTO("dc", "date", "issued", "2023-01-01"); From 7ed3d326a8cb682c8b39406966348d9eeee5ce05 Mon Sep 17 00:00:00 2001 From: "Gantner, Florian Klaus" Date: Tue, 8 Oct 2024 13:42:07 +0200 Subject: [PATCH 281/979] option to exclude the submitter being indexed to solr in archived items https://github.com/DSpace/DSpace/issues/9660 (cherry picked from commit 8ed2cdcff7bbefb14e6e48fbc2731f2c9da67e98) --- .../indexobject/ItemIndexFactoryImpl.java | 8 +- .../org/dspace/discovery/DiscoveryIT.java | 109 ++++++++++++++++++ dspace/config/modules/discovery.cfg | 7 +- 3 files changed, 120 insertions(+), 4 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/discovery/indexobject/ItemIndexFactoryImpl.java b/dspace-api/src/main/java/org/dspace/discovery/indexobject/ItemIndexFactoryImpl.java index 7cdb8b93d80e..5fb2f779783c 100644 --- a/dspace-api/src/main/java/org/dspace/discovery/indexobject/ItemIndexFactoryImpl.java +++ b/dspace-api/src/main/java/org/dspace/discovery/indexobject/ItemIndexFactoryImpl.java @@ -154,9 +154,11 @@ public SolrInputDocument buildDocument(Context context, IndexableItem indexableI doc.addField("latestVersion", isLatestVersion(context, item)); EPerson submitter = item.getSubmitter(); - if (submitter != null) { - addFacetIndex(doc, "submitter", submitter.getID().toString(), - submitter.getFullName()); + if (submitter != null && !(DSpaceServicesFactory.getInstance().getConfigurationService().getBooleanProperty( + "discovery.index.item.submitter.enabled", false))) { + doc.addField("submitter_authority", submitter.getID().toString()); + } else if (submitter != null) { + addFacetIndex(doc, "submitter", submitter.getID().toString(), submitter.getFullName()); } // Add the item metadata diff --git a/dspace-api/src/test/java/org/dspace/discovery/DiscoveryIT.java b/dspace-api/src/test/java/org/dspace/discovery/DiscoveryIT.java index 6bc79cad558b..63ff93b6f387 100644 --- a/dspace-api/src/test/java/org/dspace/discovery/DiscoveryIT.java +++ b/dspace-api/src/test/java/org/dspace/discovery/DiscoveryIT.java @@ -8,6 +8,10 @@ package org.dspace.discovery; import static org.dspace.discovery.SolrServiceWorkspaceWorkflowRestrictionPlugin.DISCOVER_WORKSPACE_CONFIGURATION_NAME; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.hasItem; +import static org.hamcrest.Matchers.hasItems; +import static org.hamcrest.Matchers.not; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; @@ -21,6 +25,10 @@ import java.util.stream.Collectors; import jakarta.servlet.http.HttpServletRequest; +import org.apache.solr.client.solrj.SolrQuery; +import org.apache.solr.client.solrj.SolrServerException; +import org.apache.solr.client.solrj.response.QueryResponse; +import org.apache.solr.common.SolrDocument; import org.dspace.AbstractIntegrationTestWithDatabase; import org.dspace.app.launcher.ScriptLauncher; import org.dspace.app.scripts.handler.impl.TestDSpaceRunnableHandler; @@ -99,6 +107,9 @@ public class DiscoveryIT extends AbstractIntegrationTestWithDatabase { MetadataAuthorityService metadataAuthorityService = ContentAuthorityServiceFactory.getInstance() .getMetadataAuthorityService(); + MockSolrSearchCore solrSearchCore = DSpaceServicesFactory.getInstance().getServiceManager() + .getServiceByName(null, MockSolrSearchCore.class); + @Override @Before public void setUp() throws Exception { @@ -796,6 +807,104 @@ public void searchWithDefaultSortServiceTest() throws SearchServiceException { } } + /** + * Test designed to check if the submitter is not indexed in all in solr documents for items + * and the submitter authority is still indexed + * @throws SearchServiceException + */ + @Test + public void searchWithNoSubmitterTest() throws SearchServiceException { + + configurationService.setProperty("discovery.index.item.submitter.enabled", false); + DiscoveryConfiguration defaultConf = SearchUtils.getDiscoveryConfiguration(context, "default", null); + + // Populate the testing objects: create items in eperson's workspace and perform search in it + int numberItems = 10; + context.turnOffAuthorisationSystem(); + EPerson submitter = null; + try { + submitter = EPersonBuilder.createEPerson(context).withEmail("submitter@example.org") + .withNameInMetadata("Peter", "Funny").build(); + } catch (SQLException e) { + throw new RuntimeException(e); + } + context.setCurrentUser(submitter); + Community community = CommunityBuilder.createCommunity(context).build(); + Collection collection = CollectionBuilder.createCollection(context, community).build(); + for (int i = 0; i < numberItems; i++) { + ItemBuilder.createItem(context, collection) + .withTitle("item " + i) + .build(); + } + context.restoreAuthSystemState(); + + // Build query with default parameters (except for workspaceConf) + QueryResponse result = null; + try { + result = solrSearchCore.getSolr().query(new SolrQuery(String.format( + "search.resourcetype:\"Item\""))); + } catch (SolrServerException e) { + throw new RuntimeException(e); + } catch (IOException e) { + throw new RuntimeException(e); + } + assertEquals(result.getResults().size(), numberItems); + for (SolrDocument doc : result.getResults()) { + assertThat(doc.getFieldNames(), + not(hasItems("submitter_keyword", "submitter_ac", "submitter_acid", "submitter_filter"))); + assertThat(doc.getFieldNames(), hasItem("submitter_authority")); + } + } + + /** + * Test designed to check if the submitter is indexed in all in solr documents for items + * @throws SearchServiceException + */ + @Test + public void searchWithSubmitterTest() throws SearchServiceException { + + configurationService.setProperty("discovery.index.item.submitter.enabled", true); + DiscoveryConfiguration defaultConf = SearchUtils.getDiscoveryConfiguration(context, "default", null); + + // Populate the testing objects: create items in eperson's workspace and perform search in it + int numberItems = 10; + context.turnOffAuthorisationSystem(); + EPerson submitter = null; + try { + submitter = EPersonBuilder.createEPerson(context).withEmail("submitter@example.org") + .withNameInMetadata("Peter", "Funny").build(); + } catch (SQLException e) { + throw new RuntimeException(e); + } + context.setCurrentUser(submitter); + Community community = CommunityBuilder.createCommunity(context).build(); + Collection collection = CollectionBuilder.createCollection(context, community).build(); + for (int i = 0; i < numberItems; i++) { + ItemBuilder.createItem(context, collection) + .withTitle("item " + i) + .build(); + } + context.restoreAuthSystemState(); + + // Build query with default parameters (except for workspaceConf) + QueryResponse result = null; + try { + result = solrSearchCore.getSolr().query(new SolrQuery(String.format( + "search.resourcetype:\"Item\""))); + } catch (SolrServerException e) { + throw new RuntimeException(e); + } catch (IOException e) { + throw new RuntimeException(e); + } + assertEquals(result.getResults().size(), numberItems); + for (SolrDocument doc : result.getResults()) { + for (String fieldname : doc.getFieldNames()) { + assertThat(doc.getFieldNames(), hasItems("submitter_keyword","submitter_ac", "submitter_filter", + "submitter_authority")); + } + } + } + private void assertSearchQuery(String resourceType, int size) throws SearchServiceException { assertSearchQuery(resourceType, size, size, 0, -1); } diff --git a/dspace/config/modules/discovery.cfg b/dspace/config/modules/discovery.cfg index 72088ddc49fa..cd8e8636c2e3 100644 --- a/dspace/config/modules/discovery.cfg +++ b/dspace/config/modules/discovery.cfg @@ -24,6 +24,11 @@ discovery.search.server = ${solr.server}/${solr.multicorePrefix}search # discovery.index.ignore-authority = false discovery.index.projection=dc.title,dc.contributor.*,dc.date.issued +# Restricts the indexing of the submitter for archived items +# By default the submitter information from the corresponding eperson is not indexed. +# If you set this value to true, than the submitter information is indexed and you will need to reindex search core +# discovery.index.item.submitter.enabled = false + # Allow auto-reindexing. # If any database migrations are applied to your database (via Flyway), then a # reindex flag is always written to '[dspace]/solr/search/conf/reindex.flag'. @@ -48,4 +53,4 @@ discovery.facet.namedtype.workflow.pooled = 004workflow\n|||\nWaiting for Contro # Set the number of retry of a query when stale objects are found. # Set to -1 if stale objects should be ignored. Set to 0 if you want to avoid extra query but take the chance to cleanup # the index each time that stale objects are found. Default 3 -discovery.removestale.attempts = 3 \ No newline at end of file +discovery.removestale.attempts = 3 From 62596ad86313053f56fa316fba62ff1156cbeb37 Mon Sep 17 00:00:00 2001 From: "Gantner, Florian Klaus" Date: Tue, 8 Oct 2024 13:42:07 +0200 Subject: [PATCH 282/979] option to exclude the submitter being indexed to solr in archived items https://github.com/DSpace/DSpace/issues/9660 --- .../indexobject/ItemIndexFactoryImpl.java | 8 +- .../org/dspace/discovery/DiscoveryIT.java | 109 ++++++++++++++++++ dspace/config/modules/discovery.cfg | 7 +- 3 files changed, 120 insertions(+), 4 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/discovery/indexobject/ItemIndexFactoryImpl.java b/dspace-api/src/main/java/org/dspace/discovery/indexobject/ItemIndexFactoryImpl.java index 7cdb8b93d80e..5fb2f779783c 100644 --- a/dspace-api/src/main/java/org/dspace/discovery/indexobject/ItemIndexFactoryImpl.java +++ b/dspace-api/src/main/java/org/dspace/discovery/indexobject/ItemIndexFactoryImpl.java @@ -154,9 +154,11 @@ public SolrInputDocument buildDocument(Context context, IndexableItem indexableI doc.addField("latestVersion", isLatestVersion(context, item)); EPerson submitter = item.getSubmitter(); - if (submitter != null) { - addFacetIndex(doc, "submitter", submitter.getID().toString(), - submitter.getFullName()); + if (submitter != null && !(DSpaceServicesFactory.getInstance().getConfigurationService().getBooleanProperty( + "discovery.index.item.submitter.enabled", false))) { + doc.addField("submitter_authority", submitter.getID().toString()); + } else if (submitter != null) { + addFacetIndex(doc, "submitter", submitter.getID().toString(), submitter.getFullName()); } // Add the item metadata diff --git a/dspace-api/src/test/java/org/dspace/discovery/DiscoveryIT.java b/dspace-api/src/test/java/org/dspace/discovery/DiscoveryIT.java index 55be531418ae..159f7a0ac5ae 100644 --- a/dspace-api/src/test/java/org/dspace/discovery/DiscoveryIT.java +++ b/dspace-api/src/test/java/org/dspace/discovery/DiscoveryIT.java @@ -8,6 +8,10 @@ package org.dspace.discovery; import static org.dspace.discovery.SolrServiceWorkspaceWorkflowRestrictionPlugin.DISCOVER_WORKSPACE_CONFIGURATION_NAME; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.hasItem; +import static org.hamcrest.Matchers.hasItems; +import static org.hamcrest.Matchers.not; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; @@ -21,6 +25,10 @@ import java.util.stream.Collectors; import javax.servlet.http.HttpServletRequest; +import org.apache.solr.client.solrj.SolrQuery; +import org.apache.solr.client.solrj.SolrServerException; +import org.apache.solr.client.solrj.response.QueryResponse; +import org.apache.solr.common.SolrDocument; import org.dspace.AbstractIntegrationTestWithDatabase; import org.dspace.app.launcher.ScriptLauncher; import org.dspace.app.scripts.handler.impl.TestDSpaceRunnableHandler; @@ -99,6 +107,9 @@ public class DiscoveryIT extends AbstractIntegrationTestWithDatabase { MetadataAuthorityService metadataAuthorityService = ContentAuthorityServiceFactory.getInstance() .getMetadataAuthorityService(); + MockSolrSearchCore solrSearchCore = DSpaceServicesFactory.getInstance().getServiceManager() + .getServiceByName(null, MockSolrSearchCore.class); + @Override @Before public void setUp() throws Exception { @@ -796,6 +807,104 @@ public void searchWithDefaultSortServiceTest() throws SearchServiceException { } } + /** + * Test designed to check if the submitter is not indexed in all in solr documents for items + * and the submitter authority is still indexed + * @throws SearchServiceException + */ + @Test + public void searchWithNoSubmitterTest() throws SearchServiceException { + + configurationService.setProperty("discovery.index.item.submitter.enabled", false); + DiscoveryConfiguration defaultConf = SearchUtils.getDiscoveryConfiguration(context, "default", null); + + // Populate the testing objects: create items in eperson's workspace and perform search in it + int numberItems = 10; + context.turnOffAuthorisationSystem(); + EPerson submitter = null; + try { + submitter = EPersonBuilder.createEPerson(context).withEmail("submitter@example.org") + .withNameInMetadata("Peter", "Funny").build(); + } catch (SQLException e) { + throw new RuntimeException(e); + } + context.setCurrentUser(submitter); + Community community = CommunityBuilder.createCommunity(context).build(); + Collection collection = CollectionBuilder.createCollection(context, community).build(); + for (int i = 0; i < numberItems; i++) { + ItemBuilder.createItem(context, collection) + .withTitle("item " + i) + .build(); + } + context.restoreAuthSystemState(); + + // Build query with default parameters (except for workspaceConf) + QueryResponse result = null; + try { + result = solrSearchCore.getSolr().query(new SolrQuery(String.format( + "search.resourcetype:\"Item\""))); + } catch (SolrServerException e) { + throw new RuntimeException(e); + } catch (IOException e) { + throw new RuntimeException(e); + } + assertEquals(result.getResults().size(), numberItems); + for (SolrDocument doc : result.getResults()) { + assertThat(doc.getFieldNames(), + not(hasItems("submitter_keyword", "submitter_ac", "submitter_acid", "submitter_filter"))); + assertThat(doc.getFieldNames(), hasItem("submitter_authority")); + } + } + + /** + * Test designed to check if the submitter is indexed in all in solr documents for items + * @throws SearchServiceException + */ + @Test + public void searchWithSubmitterTest() throws SearchServiceException { + + configurationService.setProperty("discovery.index.item.submitter.enabled", true); + DiscoveryConfiguration defaultConf = SearchUtils.getDiscoveryConfiguration(context, "default", null); + + // Populate the testing objects: create items in eperson's workspace and perform search in it + int numberItems = 10; + context.turnOffAuthorisationSystem(); + EPerson submitter = null; + try { + submitter = EPersonBuilder.createEPerson(context).withEmail("submitter@example.org") + .withNameInMetadata("Peter", "Funny").build(); + } catch (SQLException e) { + throw new RuntimeException(e); + } + context.setCurrentUser(submitter); + Community community = CommunityBuilder.createCommunity(context).build(); + Collection collection = CollectionBuilder.createCollection(context, community).build(); + for (int i = 0; i < numberItems; i++) { + ItemBuilder.createItem(context, collection) + .withTitle("item " + i) + .build(); + } + context.restoreAuthSystemState(); + + // Build query with default parameters (except for workspaceConf) + QueryResponse result = null; + try { + result = solrSearchCore.getSolr().query(new SolrQuery(String.format( + "search.resourcetype:\"Item\""))); + } catch (SolrServerException e) { + throw new RuntimeException(e); + } catch (IOException e) { + throw new RuntimeException(e); + } + assertEquals(result.getResults().size(), numberItems); + for (SolrDocument doc : result.getResults()) { + for (String fieldname : doc.getFieldNames()) { + assertThat(doc.getFieldNames(), hasItems("submitter_keyword","submitter_ac", "submitter_filter", + "submitter_authority")); + } + } + } + private void assertSearchQuery(String resourceType, int size) throws SearchServiceException { assertSearchQuery(resourceType, size, size, 0, -1); } diff --git a/dspace/config/modules/discovery.cfg b/dspace/config/modules/discovery.cfg index 72088ddc49fa..cd8e8636c2e3 100644 --- a/dspace/config/modules/discovery.cfg +++ b/dspace/config/modules/discovery.cfg @@ -24,6 +24,11 @@ discovery.search.server = ${solr.server}/${solr.multicorePrefix}search # discovery.index.ignore-authority = false discovery.index.projection=dc.title,dc.contributor.*,dc.date.issued +# Restricts the indexing of the submitter for archived items +# By default the submitter information from the corresponding eperson is not indexed. +# If you set this value to true, than the submitter information is indexed and you will need to reindex search core +# discovery.index.item.submitter.enabled = false + # Allow auto-reindexing. # If any database migrations are applied to your database (via Flyway), then a # reindex flag is always written to '[dspace]/solr/search/conf/reindex.flag'. @@ -48,4 +53,4 @@ discovery.facet.namedtype.workflow.pooled = 004workflow\n|||\nWaiting for Contro # Set the number of retry of a query when stale objects are found. # Set to -1 if stale objects should be ignored. Set to 0 if you want to avoid extra query but take the chance to cleanup # the index each time that stale objects are found. Default 3 -discovery.removestale.attempts = 3 \ No newline at end of file +discovery.removestale.attempts = 3 From 59ea5cd0eea61f87a668a29d7233b45a33908586 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Mon, 28 Oct 2024 14:55:30 -0500 Subject: [PATCH 283/979] Create dependabot.yml --- .github/dependabot.yml | 115 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 115 insertions(+) create mode 100644 .github/dependabot.yml diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 000000000000..6f4f3ae601a0 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,115 @@ +#------------------- +# DSpace's dependabot rules. Enables maven updates for all dependencies on a weekly basis +# for main and any maintenance branches. Security updates only apply to main. +#------------------- +version: 2 +updates: + - package-ecosystem: "maven" + directory: "/" + schedule: + interval: "weekly" + # Allow up to 10 open PRs for dependencies + open-pull-requests-limit: 10 + # Group together some upgrades in a single PR + groups: + # Group together all Build Tools in a single PR + build-tools: + applies-to: version-updates + patterns: + - "org.apache.maven.plugins:*" + - "*:*-maven-plugin" + - "*:maven-*-plugin" + - "com.github.spotbugs:spotbugs" + - "com.google.code.findbugs:*" + - "com.google.errorprone:*" + - "com.puppycrawl.tools:checkstyle" + - "org.sonatype.plugins:*" + update-types: + - "minor" + - "patch" + test-tools: + applies-to: version-updates + patterns: + - "junit:*" + - "com.github.stefanbirker:system-rules" + - "com.h2database:*" + - "io.findify:s3mock*" + - "io.netty:*" + - "org.hamcrest:*" + - "org.mock-server:*" + - "org.mockito:*" + update-types: + - "minor" + - "patch" + # Group together all Apache Commons deps in a single PR + apache-commons: + applies-to: version-updates + patterns: + - "org.apache.commons:*" + - "commons-*:commons-*" + update-types: + - "minor" + - "patch" + # Group together all fasterxml deps in a single PR + fasterxml: + applies-to: version-updates + patterns: + - "com.fasterxml:*" + - "com.fasterxml.*:*" + update-types: + - "minor" + - "patch" + # Group together all Hibernate deps in a single PR + hibernate: + applies-to: version-updates + patterns: + - "org.hibernate.*:*" + update-types: + - "minor" + - "patch" + # Group together all Jakarta deps in a single PR + jakarta: + applies-to: version-updates + patterns: + - "jakarta.*:*" + - "org.eclipse.angus:jakarta.mail" + - "org.glassfish.jaxb:jaxb-runtime" + update-types: + - "minor" + - "patch" + # Group together all Google deps in a single PR + google-apis: + applies-to: version-updates + patterns: + - "com.google.apis:*" + - "com.google.api-client:*" + - "com.google.http-client:*" + - "com.google.oauth-client:*" + update-types: + - "minor" + - "patch" + # Group together all Spring deps in a single PR + spring: + applies-to: version-updates + patterns: + - "org.springframework:*" + - "org.springframework.*:*" + update-types: + - "minor" + - "patch" + # Group together all WebJARs deps in a single PR + webjars: + applies-to: version-updates + patterns: + - "org.webjars:*" + - "org.webjars.*:*" + update-types: + - "minor" + - "patch" + ignore: + # Don't try to auto-update any DSpace dependencies + - dependency-name: "org.dspace:*" + - dependency-name: "org.dspace.*:*" + # Ignore all major version updates for all dependencies. We'll only automate minor/patch updates. + - dependency-name: "*" + update-types: ["version-update:semver-major"] From 5b46c94bb4d398275abefc688e4a31dcda809b69 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Tue, 29 Oct 2024 15:01:41 -0500 Subject: [PATCH 284/979] Exclude spring from build-tools group in dependabot.yml --- .github/dependabot.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 6f4f3ae601a0..b6412b25b660 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -24,6 +24,9 @@ updates: - "com.google.errorprone:*" - "com.puppycrawl.tools:checkstyle" - "org.sonatype.plugins:*" + exclude-patterns: + # Exclude anything from Spring, as that is in a separate group + - "org.springframework.*:*" update-types: - "minor" - "patch" From e840ca27315ce8fb2772e8bcdf6c34c042a6cc99 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Mon, 28 Oct 2024 14:55:30 -0500 Subject: [PATCH 285/979] Create dependabot.yml --- .github/dependabot.yml | 115 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 115 insertions(+) create mode 100644 .github/dependabot.yml diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 000000000000..6f4f3ae601a0 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,115 @@ +#------------------- +# DSpace's dependabot rules. Enables maven updates for all dependencies on a weekly basis +# for main and any maintenance branches. Security updates only apply to main. +#------------------- +version: 2 +updates: + - package-ecosystem: "maven" + directory: "/" + schedule: + interval: "weekly" + # Allow up to 10 open PRs for dependencies + open-pull-requests-limit: 10 + # Group together some upgrades in a single PR + groups: + # Group together all Build Tools in a single PR + build-tools: + applies-to: version-updates + patterns: + - "org.apache.maven.plugins:*" + - "*:*-maven-plugin" + - "*:maven-*-plugin" + - "com.github.spotbugs:spotbugs" + - "com.google.code.findbugs:*" + - "com.google.errorprone:*" + - "com.puppycrawl.tools:checkstyle" + - "org.sonatype.plugins:*" + update-types: + - "minor" + - "patch" + test-tools: + applies-to: version-updates + patterns: + - "junit:*" + - "com.github.stefanbirker:system-rules" + - "com.h2database:*" + - "io.findify:s3mock*" + - "io.netty:*" + - "org.hamcrest:*" + - "org.mock-server:*" + - "org.mockito:*" + update-types: + - "minor" + - "patch" + # Group together all Apache Commons deps in a single PR + apache-commons: + applies-to: version-updates + patterns: + - "org.apache.commons:*" + - "commons-*:commons-*" + update-types: + - "minor" + - "patch" + # Group together all fasterxml deps in a single PR + fasterxml: + applies-to: version-updates + patterns: + - "com.fasterxml:*" + - "com.fasterxml.*:*" + update-types: + - "minor" + - "patch" + # Group together all Hibernate deps in a single PR + hibernate: + applies-to: version-updates + patterns: + - "org.hibernate.*:*" + update-types: + - "minor" + - "patch" + # Group together all Jakarta deps in a single PR + jakarta: + applies-to: version-updates + patterns: + - "jakarta.*:*" + - "org.eclipse.angus:jakarta.mail" + - "org.glassfish.jaxb:jaxb-runtime" + update-types: + - "minor" + - "patch" + # Group together all Google deps in a single PR + google-apis: + applies-to: version-updates + patterns: + - "com.google.apis:*" + - "com.google.api-client:*" + - "com.google.http-client:*" + - "com.google.oauth-client:*" + update-types: + - "minor" + - "patch" + # Group together all Spring deps in a single PR + spring: + applies-to: version-updates + patterns: + - "org.springframework:*" + - "org.springframework.*:*" + update-types: + - "minor" + - "patch" + # Group together all WebJARs deps in a single PR + webjars: + applies-to: version-updates + patterns: + - "org.webjars:*" + - "org.webjars.*:*" + update-types: + - "minor" + - "patch" + ignore: + # Don't try to auto-update any DSpace dependencies + - dependency-name: "org.dspace:*" + - dependency-name: "org.dspace.*:*" + # Ignore all major version updates for all dependencies. We'll only automate minor/patch updates. + - dependency-name: "*" + update-types: ["version-update:semver-major"] From fe28962e9672c01477059bcc2d2c8787a781dadf Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Tue, 29 Oct 2024 15:01:41 -0500 Subject: [PATCH 286/979] Exclude spring from build-tools group in dependabot.yml --- .github/dependabot.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 6f4f3ae601a0..b6412b25b660 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -24,6 +24,9 @@ updates: - "com.google.errorprone:*" - "com.puppycrawl.tools:checkstyle" - "org.sonatype.plugins:*" + exclude-patterns: + # Exclude anything from Spring, as that is in a separate group + - "org.springframework.*:*" update-types: - "minor" - "patch" From 0b46a0963c5cfe8189fe3b8764eb6450ec41664a Mon Sep 17 00:00:00 2001 From: Jens Vannerum Date: Tue, 2 Apr 2024 11:15:49 +0200 Subject: [PATCH 287/979] 113811: cli logs should be written to a different file (cherry picked from commit d30468a09fbbd288df6cf2556e1a630f040801d1) --- dspace/bin/dspace | 2 + dspace/config/log4j2-cli.xml | 105 +++++++++++++++++++++++++++++++++++ 2 files changed, 107 insertions(+) create mode 100644 dspace/config/log4j2-cli.xml diff --git a/dspace/bin/dspace b/dspace/bin/dspace index 7d5d678d0f6e..24644aae9112 100644 --- a/dspace/bin/dspace +++ b/dspace/bin/dspace @@ -39,6 +39,8 @@ if [ "$JAVA_OPTS" = "" ]; then JAVA_OPTS="-Xmx256m -Dfile.encoding=UTF-8" fi +export JAVA_OPTS="$JAVA_OPTS -Dlog4j2.configurationFile=$DSPACEDIR/config/log4j2-cli.xml" + # Now invoke Java java $JAVA_OPTS \ -classpath $FULLPATH \ diff --git a/dspace/config/log4j2-cli.xml b/dspace/config/log4j2-cli.xml new file mode 100644 index 000000000000..24d36fd12efb --- /dev/null +++ b/dspace/config/log4j2-cli.xml @@ -0,0 +1,105 @@ + + + + + + + ${log4j:configParentLocation}/../log + + + INFO + + + INFO + + + + + + + + + yyyy-MM-dd + + + + + + + + + yyyy-MM-dd + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 04ec6381cba0472f4fdbc0918588180393a89c5d Mon Sep 17 00:00:00 2001 From: Kevin Van de Velde Date: Fri, 6 Sep 2024 10:15:08 +0200 Subject: [PATCH 288/979] Modifying it so that the cli file content is stored in a file using the date (cherry picked from commit 529c3a77c15f401fe623b2c9d04a438754a1d72d) --- dspace/config/log4j2-cli.xml | 2 -- 1 file changed, 2 deletions(-) diff --git a/dspace/config/log4j2-cli.xml b/dspace/config/log4j2-cli.xml index 24d36fd12efb..73acab877f1f 100644 --- a/dspace/config/log4j2-cli.xml +++ b/dspace/config/log4j2-cli.xml @@ -25,8 +25,6 @@ From 1a6088388fa4bc0d01ecdea0b1fee7108a27bce5 Mon Sep 17 00:00:00 2001 From: Jens Vannerum Date: Wed, 4 Dec 2024 11:08:37 +0100 Subject: [PATCH 289/979] apply fix to windows env and remove duplicate logging for checksum checker (cherry picked from commit 9f39a3d6a53f9a815509d6a47abd176b07a6a86c) --- dspace/bin/dspace.bat | 3 +++ dspace/config/log4j2.xml | 27 --------------------------- 2 files changed, 3 insertions(+), 27 deletions(-) diff --git a/dspace/bin/dspace.bat b/dspace/bin/dspace.bat index 2288127c447a..768b1061762d 100644 --- a/dspace/bin/dspace.bat +++ b/dspace/bin/dspace.bat @@ -38,6 +38,9 @@ REM If JAVA_OPTS specified, use those options REM Otherwise, default Java to using 256MB of memory if "%JAVA_OPTS%"=="" set "JAVA_OPTS=-Xmx256m -Dfile.encoding=UTF-8" +REM Add log4j2 configuration to JAVA_OPTS +set "JAVA_OPTS=%JAVA_OPTS% -Dlog4j2.configurationFile=%cd%\config\log4j2-cli.xml" + REM Execute Java java %JAVA_OPTS% -classpath "%DSPACE_CLASSPATH%" org.dspace.app.launcher.ScriptLauncher %* diff --git a/dspace/config/log4j2.xml b/dspace/config/log4j2.xml index 6e9a43e4f0fe..4f212d128dc6 100644 --- a/dspace/config/log4j2.xml +++ b/dspace/config/log4j2.xml @@ -44,26 +44,6 @@ --> - - - - - - yyyy-MM-dd - - - @@ -75,13 +55,6 @@ - - - - - From 666e146125bf63b4cb48876f30b76c54afb0eca3 Mon Sep 17 00:00:00 2001 From: Jens Vannerum Date: Tue, 2 Apr 2024 11:15:49 +0200 Subject: [PATCH 290/979] 113811: cli logs should be written to a different file (cherry picked from commit d30468a09fbbd288df6cf2556e1a630f040801d1) --- dspace/bin/dspace | 2 + dspace/config/log4j2-cli.xml | 105 +++++++++++++++++++++++++++++++++++ 2 files changed, 107 insertions(+) create mode 100644 dspace/config/log4j2-cli.xml diff --git a/dspace/bin/dspace b/dspace/bin/dspace index 7d5d678d0f6e..24644aae9112 100644 --- a/dspace/bin/dspace +++ b/dspace/bin/dspace @@ -39,6 +39,8 @@ if [ "$JAVA_OPTS" = "" ]; then JAVA_OPTS="-Xmx256m -Dfile.encoding=UTF-8" fi +export JAVA_OPTS="$JAVA_OPTS -Dlog4j2.configurationFile=$DSPACEDIR/config/log4j2-cli.xml" + # Now invoke Java java $JAVA_OPTS \ -classpath $FULLPATH \ diff --git a/dspace/config/log4j2-cli.xml b/dspace/config/log4j2-cli.xml new file mode 100644 index 000000000000..24d36fd12efb --- /dev/null +++ b/dspace/config/log4j2-cli.xml @@ -0,0 +1,105 @@ + + + + + + + ${log4j:configParentLocation}/../log + + + INFO + + + INFO + + + + + + + + + yyyy-MM-dd + + + + + + + + + yyyy-MM-dd + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 5a2488ac48628ae906979aa064d5890be5e258f1 Mon Sep 17 00:00:00 2001 From: Kevin Van de Velde Date: Fri, 6 Sep 2024 10:15:08 +0200 Subject: [PATCH 291/979] Modifying it so that the cli file content is stored in a file using the date (cherry picked from commit 529c3a77c15f401fe623b2c9d04a438754a1d72d) --- dspace/config/log4j2-cli.xml | 2 -- 1 file changed, 2 deletions(-) diff --git a/dspace/config/log4j2-cli.xml b/dspace/config/log4j2-cli.xml index 24d36fd12efb..73acab877f1f 100644 --- a/dspace/config/log4j2-cli.xml +++ b/dspace/config/log4j2-cli.xml @@ -25,8 +25,6 @@ From 262be99d4e3b8cf5c1321eabc6072e40a4e3fdf7 Mon Sep 17 00:00:00 2001 From: Jens Vannerum Date: Wed, 4 Dec 2024 11:08:37 +0100 Subject: [PATCH 292/979] apply fix to windows env and remove duplicate logging for checksum checker (cherry picked from commit 9f39a3d6a53f9a815509d6a47abd176b07a6a86c) --- dspace/bin/dspace.bat | 3 +++ dspace/config/log4j2.xml | 27 --------------------------- 2 files changed, 3 insertions(+), 27 deletions(-) diff --git a/dspace/bin/dspace.bat b/dspace/bin/dspace.bat index 2288127c447a..768b1061762d 100644 --- a/dspace/bin/dspace.bat +++ b/dspace/bin/dspace.bat @@ -38,6 +38,9 @@ REM If JAVA_OPTS specified, use those options REM Otherwise, default Java to using 256MB of memory if "%JAVA_OPTS%"=="" set "JAVA_OPTS=-Xmx256m -Dfile.encoding=UTF-8" +REM Add log4j2 configuration to JAVA_OPTS +set "JAVA_OPTS=%JAVA_OPTS% -Dlog4j2.configurationFile=%cd%\config\log4j2-cli.xml" + REM Execute Java java %JAVA_OPTS% -classpath "%DSPACE_CLASSPATH%" org.dspace.app.launcher.ScriptLauncher %* diff --git a/dspace/config/log4j2.xml b/dspace/config/log4j2.xml index 6e9a43e4f0fe..4f212d128dc6 100644 --- a/dspace/config/log4j2.xml +++ b/dspace/config/log4j2.xml @@ -44,26 +44,6 @@ --> - - - - - - yyyy-MM-dd - - - @@ -75,13 +55,6 @@ - - - - - From f99755852b0b8c7a80fda12d5d85d58e47f92ce4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 15 Jan 2025 21:00:58 +0000 Subject: [PATCH 293/979] Bump the build-tools group with 24 updates Bumps the build-tools group with 24 updates: | Package | From | To | | --- | --- | --- | | [com.google.errorprone:error_prone_core](https://github.com/google/error-prone) | `2.10.0` | `2.36.0` | | [com.google.errorprone:error_prone_annotations](https://github.com/google/error-prone) | `2.10.0` | `2.36.0` | | [com.puppycrawl.tools:checkstyle](https://github.com/checkstyle/checkstyle) | `8.38` | `8.45.1` | | [com.github.spotbugs:spotbugs](https://github.com/spotbugs/spotbugs) | `4.8.2` | `4.8.6` | | [org.apache.maven.plugins:maven-enforcer-plugin](https://github.com/apache/maven-enforcer) | `3.4.1` | `3.5.0` | | [org.apache.maven.plugins:maven-jar-plugin](https://github.com/apache/maven-jar-plugin) | `3.3.0` | `3.4.2` | | [org.apache.maven.plugins:maven-surefire-plugin](https://github.com/apache/maven-surefire) | `3.2.5` | `3.5.2` | | [org.apache.maven.plugins:maven-failsafe-plugin](https://github.com/apache/maven-surefire) | `3.2.5` | `3.5.2` | | [org.apache.maven.plugins:maven-checkstyle-plugin](https://github.com/apache/maven-checkstyle-plugin) | `3.3.1` | `3.6.0` | | [com.github.spotbugs:spotbugs-maven-plugin](https://github.com/spotbugs/spotbugs-maven-plugin) | `4.8.2.0` | `4.8.6.6` | | [org.apache.maven.plugins:maven-clean-plugin](https://github.com/apache/maven-clean-plugin) | `3.3.2` | `3.4.0` | | [org.apache.maven.plugins:maven-dependency-plugin](https://github.com/apache/maven-dependency-plugin) | `3.6.1` | `3.8.1` | | org.sonatype.plugins:nexus-staging-maven-plugin | `1.6.13` | `1.7.0` | | [org.apache.maven.plugins:maven-javadoc-plugin](https://github.com/apache/maven-javadoc-plugin) | `3.6.3` | `3.11.2` | | [org.apache.maven.plugins:maven-source-plugin](https://github.com/apache/maven-source-plugin) | `3.3.0` | `3.3.1` | | [org.apache.maven.plugins:maven-gpg-plugin](https://github.com/apache/maven-gpg-plugin) | `3.2.1` | `3.2.7` | | [org.jacoco:jacoco-maven-plugin](https://github.com/jacoco/jacoco) | `0.8.11` | `0.8.12` | | [org.apache.maven.plugins:maven-release-plugin](https://github.com/apache/maven-release) | `3.0.0` | `3.1.1` | | [org.codehaus.mojo:xml-maven-plugin](https://github.com/mojohaus/xml-maven-plugin) | `1.0.2` | `1.1.0` | | [org.codehaus.mojo:license-maven-plugin](https://github.com/mojohaus/license-maven-plugin) | `2.0.0` | `2.5.0` | | [org.codehaus.mojo:build-helper-maven-plugin](https://github.com/mojohaus/build-helper-maven-plugin) | `3.4.0` | `3.6.0` | | [org.codehaus.mojo:buildnumber-maven-plugin](https://github.com/mojohaus/buildnumber-maven-plugin) | `3.2.0` | `3.2.1` | | [org.codehaus.mojo:jaxb2-maven-plugin](https://github.com/mojohaus/jaxb2-maven-plugin) | `3.1.0` | `3.2.0` | | [org.codehaus.mojo:properties-maven-plugin](https://github.com/mojohaus/properties-maven-plugin) | `1.1.0` | `1.2.1` | Updates `com.google.errorprone:error_prone_core` from 2.10.0 to 2.36.0 - [Release notes](https://github.com/google/error-prone/releases) - [Commits](https://github.com/google/error-prone/compare/v2.10.0...v2.36.0) Updates `com.google.errorprone:error_prone_annotations` from 2.10.0 to 2.36.0 - [Release notes](https://github.com/google/error-prone/releases) - [Commits](https://github.com/google/error-prone/compare/v2.10.0...v2.36.0) Updates `com.puppycrawl.tools:checkstyle` from 8.38 to 8.45.1 - [Release notes](https://github.com/checkstyle/checkstyle/releases) - [Commits](https://github.com/checkstyle/checkstyle/compare/checkstyle-8.38...checkstyle-8.45.1) Updates `com.github.spotbugs:spotbugs` from 4.8.2 to 4.8.6 - [Release notes](https://github.com/spotbugs/spotbugs/releases) - [Changelog](https://github.com/spotbugs/spotbugs/blob/master/CHANGELOG.md) - [Commits](https://github.com/spotbugs/spotbugs/compare/4.8.2...4.8.6) Updates `com.google.errorprone:error_prone_annotations` from 2.10.0 to 2.36.0 - [Release notes](https://github.com/google/error-prone/releases) - [Commits](https://github.com/google/error-prone/compare/v2.10.0...v2.36.0) Updates `org.apache.maven.plugins:maven-enforcer-plugin` from 3.4.1 to 3.5.0 - [Release notes](https://github.com/apache/maven-enforcer/releases) - [Commits](https://github.com/apache/maven-enforcer/compare/enforcer-3.4.1...enforcer-3.5.0) Updates `org.apache.maven.plugins:maven-jar-plugin` from 3.3.0 to 3.4.2 - [Release notes](https://github.com/apache/maven-jar-plugin/releases) - [Commits](https://github.com/apache/maven-jar-plugin/compare/maven-jar-plugin-3.3.0...maven-jar-plugin-3.4.2) Updates `org.apache.maven.plugins:maven-surefire-plugin` from 3.2.5 to 3.5.2 - [Release notes](https://github.com/apache/maven-surefire/releases) - [Commits](https://github.com/apache/maven-surefire/compare/surefire-3.2.5...surefire-3.5.2) Updates `org.apache.maven.plugins:maven-failsafe-plugin` from 3.2.5 to 3.5.2 - [Release notes](https://github.com/apache/maven-surefire/releases) - [Commits](https://github.com/apache/maven-surefire/compare/surefire-3.2.5...surefire-3.5.2) Updates `org.apache.maven.plugins:maven-checkstyle-plugin` from 3.3.1 to 3.6.0 - [Commits](https://github.com/apache/maven-checkstyle-plugin/compare/maven-checkstyle-plugin-3.3.1...maven-checkstyle-plugin-3.6.0) Updates `com.github.spotbugs:spotbugs-maven-plugin` from 4.8.2.0 to 4.8.6.6 - [Release notes](https://github.com/spotbugs/spotbugs-maven-plugin/releases) - [Commits](https://github.com/spotbugs/spotbugs-maven-plugin/compare/spotbugs-maven-plugin-4.8.2.0...spotbugs-maven-plugin-4.8.6.6) Updates `org.apache.maven.plugins:maven-clean-plugin` from 3.3.2 to 3.4.0 - [Release notes](https://github.com/apache/maven-clean-plugin/releases) - [Commits](https://github.com/apache/maven-clean-plugin/compare/maven-clean-plugin-3.3.2...maven-clean-plugin-3.4.0) Updates `org.apache.maven.plugins:maven-dependency-plugin` from 3.6.1 to 3.8.1 - [Release notes](https://github.com/apache/maven-dependency-plugin/releases) - [Commits](https://github.com/apache/maven-dependency-plugin/compare/maven-dependency-plugin-3.6.1...maven-dependency-plugin-3.8.1) Updates `org.sonatype.plugins:nexus-staging-maven-plugin` from 1.6.13 to 1.7.0 Updates `org.apache.maven.plugins:maven-javadoc-plugin` from 3.6.3 to 3.11.2 - [Release notes](https://github.com/apache/maven-javadoc-plugin/releases) - [Commits](https://github.com/apache/maven-javadoc-plugin/compare/maven-javadoc-plugin-3.6.3...maven-javadoc-plugin-3.11.2) Updates `org.apache.maven.plugins:maven-source-plugin` from 3.3.0 to 3.3.1 - [Release notes](https://github.com/apache/maven-source-plugin/releases) - [Commits](https://github.com/apache/maven-source-plugin/compare/maven-source-plugin-3.3.0...maven-source-plugin-3.3.1) Updates `org.apache.maven.plugins:maven-gpg-plugin` from 3.2.1 to 3.2.7 - [Release notes](https://github.com/apache/maven-gpg-plugin/releases) - [Commits](https://github.com/apache/maven-gpg-plugin/compare/maven-gpg-plugin-3.2.1...maven-gpg-plugin-3.2.7) Updates `org.jacoco:jacoco-maven-plugin` from 0.8.11 to 0.8.12 - [Release notes](https://github.com/jacoco/jacoco/releases) - [Commits](https://github.com/jacoco/jacoco/compare/v0.8.11...v0.8.12) Updates `org.apache.maven.plugins:maven-release-plugin` from 3.0.0 to 3.1.1 - [Release notes](https://github.com/apache/maven-release/releases) - [Commits](https://github.com/apache/maven-release/compare/maven-release-3.0.0...maven-release-3.1.1) Updates `org.codehaus.mojo:xml-maven-plugin` from 1.0.2 to 1.1.0 - [Release notes](https://github.com/mojohaus/xml-maven-plugin/releases) - [Commits](https://github.com/mojohaus/xml-maven-plugin/compare/xml-maven-plugin-1.0.2...1.1.0) Updates `org.codehaus.mojo:license-maven-plugin` from 2.0.0 to 2.5.0 - [Release notes](https://github.com/mojohaus/license-maven-plugin/releases) - [Commits](https://github.com/mojohaus/license-maven-plugin/compare/license-maven-plugin-2.0.0...2.5.0) Updates `org.codehaus.mojo:build-helper-maven-plugin` from 3.4.0 to 3.6.0 - [Release notes](https://github.com/mojohaus/build-helper-maven-plugin/releases) - [Commits](https://github.com/mojohaus/build-helper-maven-plugin/compare/3.4.0...3.6.0) Updates `org.codehaus.mojo:buildnumber-maven-plugin` from 3.2.0 to 3.2.1 - [Release notes](https://github.com/mojohaus/buildnumber-maven-plugin/releases) - [Commits](https://github.com/mojohaus/buildnumber-maven-plugin/compare/3.2.0...3.2.1) Updates `org.codehaus.mojo:jaxb2-maven-plugin` from 3.1.0 to 3.2.0 - [Release notes](https://github.com/mojohaus/jaxb2-maven-plugin/releases) - [Commits](https://github.com/mojohaus/jaxb2-maven-plugin/compare/jaxb2-maven-plugin-3.1.0...jaxb2-maven-plugin-3.2.0) Updates `org.codehaus.mojo:properties-maven-plugin` from 1.1.0 to 1.2.1 - [Release notes](https://github.com/mojohaus/properties-maven-plugin/releases) - [Commits](https://github.com/mojohaus/properties-maven-plugin/compare/properties-maven-plugin-1.1.0...1.2.1) --- updated-dependencies: - dependency-name: com.google.errorprone:error_prone_core dependency-type: direct:production update-type: version-update:semver-minor dependency-group: build-tools - dependency-name: com.google.errorprone:error_prone_annotations dependency-type: direct:production update-type: version-update:semver-minor dependency-group: build-tools - dependency-name: com.puppycrawl.tools:checkstyle dependency-type: direct:production update-type: version-update:semver-minor dependency-group: build-tools - dependency-name: com.github.spotbugs:spotbugs dependency-type: direct:production update-type: version-update:semver-patch dependency-group: build-tools - dependency-name: com.google.errorprone:error_prone_annotations dependency-type: direct:production update-type: version-update:semver-minor dependency-group: build-tools - dependency-name: org.apache.maven.plugins:maven-enforcer-plugin dependency-type: direct:production update-type: version-update:semver-minor dependency-group: build-tools - dependency-name: org.apache.maven.plugins:maven-jar-plugin dependency-type: direct:production update-type: version-update:semver-minor dependency-group: build-tools - dependency-name: org.apache.maven.plugins:maven-surefire-plugin dependency-type: direct:production update-type: version-update:semver-minor dependency-group: build-tools - dependency-name: org.apache.maven.plugins:maven-failsafe-plugin dependency-type: direct:production update-type: version-update:semver-minor dependency-group: build-tools - dependency-name: org.apache.maven.plugins:maven-checkstyle-plugin dependency-type: direct:production update-type: version-update:semver-minor dependency-group: build-tools - dependency-name: com.github.spotbugs:spotbugs-maven-plugin dependency-type: direct:production update-type: version-update:semver-patch dependency-group: build-tools - dependency-name: org.apache.maven.plugins:maven-clean-plugin dependency-type: direct:production update-type: version-update:semver-minor dependency-group: build-tools - dependency-name: org.apache.maven.plugins:maven-dependency-plugin dependency-type: direct:production update-type: version-update:semver-minor dependency-group: build-tools - dependency-name: org.sonatype.plugins:nexus-staging-maven-plugin dependency-type: direct:production update-type: version-update:semver-minor dependency-group: build-tools - dependency-name: org.apache.maven.plugins:maven-javadoc-plugin dependency-type: direct:production update-type: version-update:semver-minor dependency-group: build-tools - dependency-name: org.apache.maven.plugins:maven-source-plugin dependency-type: direct:production update-type: version-update:semver-patch dependency-group: build-tools - dependency-name: org.apache.maven.plugins:maven-gpg-plugin dependency-type: direct:production update-type: version-update:semver-patch dependency-group: build-tools - dependency-name: org.jacoco:jacoco-maven-plugin dependency-type: direct:production update-type: version-update:semver-patch dependency-group: build-tools - dependency-name: org.apache.maven.plugins:maven-release-plugin dependency-type: direct:production update-type: version-update:semver-minor dependency-group: build-tools - dependency-name: org.codehaus.mojo:xml-maven-plugin dependency-type: direct:production update-type: version-update:semver-minor dependency-group: build-tools - dependency-name: org.codehaus.mojo:license-maven-plugin dependency-type: direct:production update-type: version-update:semver-minor dependency-group: build-tools - dependency-name: org.codehaus.mojo:build-helper-maven-plugin dependency-type: direct:production update-type: version-update:semver-minor dependency-group: build-tools - dependency-name: org.codehaus.mojo:buildnumber-maven-plugin dependency-type: direct:production update-type: version-update:semver-patch dependency-group: build-tools - dependency-name: org.codehaus.mojo:jaxb2-maven-plugin dependency-type: direct:production update-type: version-update:semver-minor dependency-group: build-tools - dependency-name: org.codehaus.mojo:properties-maven-plugin dependency-type: direct:production update-type: version-update:semver-minor dependency-group: build-tools ... Signed-off-by: dependabot[bot] --- dspace-api/pom.xml | 6 +++--- dspace-server-webapp/pom.xml | 2 +- pom.xml | 38 ++++++++++++++++++------------------ 3 files changed, 23 insertions(+), 23 deletions(-) diff --git a/dspace-api/pom.xml b/dspace-api/pom.xml index 58bd8106e9af..d5e2f51b883b 100644 --- a/dspace-api/pom.xml +++ b/dspace-api/pom.xml @@ -102,7 +102,7 @@ org.codehaus.mojo build-helper-maven-plugin - 3.4.0 + 3.6.0 validate @@ -116,7 +116,7 @@ org.codehaus.mojo buildnumber-maven-plugin - 3.2.0 + 3.2.1 UNKNOWN_REVISION @@ -177,7 +177,7 @@ org.codehaus.mojo jaxb2-maven-plugin - 3.1.0 + 3.2.0 workflow-curation diff --git a/dspace-server-webapp/pom.xml b/dspace-server-webapp/pom.xml index 290f8afb0dc6..7adf63aa01ef 100644 --- a/dspace-server-webapp/pom.xml +++ b/dspace-server-webapp/pom.xml @@ -31,7 +31,7 @@ org.codehaus.mojo properties-maven-plugin - 1.1.0 + 1.2.1 initialize diff --git a/pom.xml b/pom.xml index bb093621ff2f..7a2f9792aa7a 100644 --- a/pom.xml +++ b/pom.xml @@ -29,7 +29,7 @@ 8.11.3 3.10.8 - 2.10.0 + 2.36.0 2.16.0 2.16.0 @@ -89,7 +89,7 @@ org.apache.maven.plugins maven-enforcer-plugin - 3.4.1 + 3.5.0 enforce-java @@ -176,7 +176,7 @@ org.apache.maven.plugins maven-jar-plugin - 3.3.0 + 3.4.2 @@ -207,7 +207,7 @@ org.apache.maven.plugins maven-surefire-plugin - 3.2.5 + 3.5.2 @@ -234,7 +234,7 @@ maven-failsafe-plugin - 3.2.5 + 3.5.2 @@ -266,7 +266,7 @@ org.apache.maven.plugins maven-checkstyle-plugin - 3.3.1 + 3.6.0 verify-style @@ -295,14 +295,14 @@ com.puppycrawl.tools checkstyle - 8.38 + 8.45.1 com.github.spotbugs spotbugs-maven-plugin - 4.8.2.0 + 4.8.6.6 Max Low @@ -312,7 +312,7 @@ com.github.spotbugs spotbugs - 4.8.2 + 4.8.6 @@ -328,7 +328,7 @@ maven-clean-plugin - 3.3.2 + 3.4.0 @@ -347,7 +347,7 @@ org.apache.maven.plugins maven-dependency-plugin - 3.6.1 + 3.8.1 org.apache.maven.plugins @@ -364,13 +364,13 @@ org.sonatype.plugins nexus-staging-maven-plugin - 1.6.13 + 1.7.0 org.apache.maven.plugins maven-javadoc-plugin - 3.6.3 + 3.11.2 false @@ -385,7 +385,7 @@ org.apache.maven.plugins maven-source-plugin - 3.3.0 + 3.3.1 @@ -398,13 +398,13 @@ org.apache.maven.plugins maven-gpg-plugin - 3.2.1 + 3.2.7 org.jacoco jacoco-maven-plugin - 0.8.11 + 0.8.12 @@ -416,7 +416,7 @@ org.apache.maven.plugins maven-release-plugin - 3.0.0 + 3.1.1 @@ -484,7 +484,7 @@ org.codehaus.mojo xml-maven-plugin - 1.0.2 + 1.1.0 validate-ALL-xml-and-xsl @@ -692,7 +692,7 @@ org.codehaus.mojo license-maven-plugin - 2.0.0 + 2.5.0 false From 3352a0a9c36c363f54505958a3e8ebf3d8be1949 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 15 Jan 2025 21:01:11 +0000 Subject: [PATCH 294/979] Bump the test-tools group with 2 updates Bumps the test-tools group with 2 updates: [com.h2database:h2](https://github.com/h2database/h2database) and [org.mock-server:mockserver-junit-rule](https://github.com/jamesdbloom/mockservice). Updates `com.h2database:h2` from 2.2.224 to 2.3.232 - [Release notes](https://github.com/h2database/h2database/releases) - [Commits](https://github.com/h2database/h2database/compare/version-2.2.224...version-2.3.232) Updates `org.mock-server:mockserver-junit-rule` from 5.11.2 to 5.15.0 - [Changelog](https://github.com/mock-server/mockserver/blob/master/changelog.md) - [Commits](https://github.com/jamesdbloom/mockservice/compare/mockserver-5.11.2...mockserver-5.15.0) --- updated-dependencies: - dependency-name: com.h2database:h2 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: test-tools - dependency-name: org.mock-server:mockserver-junit-rule dependency-type: direct:development update-type: version-update:semver-minor dependency-group: test-tools ... Signed-off-by: dependabot[bot] --- dspace-api/pom.xml | 2 +- pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/dspace-api/pom.xml b/dspace-api/pom.xml index 58bd8106e9af..16704f7bf294 100644 --- a/dspace-api/pom.xml +++ b/dspace-api/pom.xml @@ -824,7 +824,7 @@ org.mock-server mockserver-junit-rule - 5.11.2 + 5.15.0 test diff --git a/pom.xml b/pom.xml index bb093621ff2f..0d8539ba5209 100644 --- a/pom.xml +++ b/pom.xml @@ -1708,7 +1708,7 @@ com.h2database h2 - 2.2.224 + 2.3.232 test From 1492ed7f61b5685e0e8e0f4365460887465bef88 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 15 Jan 2025 21:01:16 +0000 Subject: [PATCH 295/979] Bump the build-tools group with 21 updates Bumps the build-tools group with 21 updates: | Package | From | To | | --- | --- | --- | | [com.google.errorprone:error_prone_core](https://github.com/google/error-prone) | `2.10.0` | `2.36.0` | | [com.puppycrawl.tools:checkstyle](https://github.com/checkstyle/checkstyle) | `8.38` | `8.45.1` | | [com.github.spotbugs:spotbugs](https://github.com/spotbugs/spotbugs) | `4.8.2` | `4.8.6` | | [org.apache.maven.plugins:maven-enforcer-plugin](https://github.com/apache/maven-enforcer) | `3.0.0-M3` | `3.5.0` | | [org.apache.maven.plugins:maven-compiler-plugin](https://github.com/apache/maven-compiler-plugin) | `3.8.1` | `3.13.0` | | [org.apache.maven.plugins:maven-jar-plugin](https://github.com/apache/maven-jar-plugin) | `3.2.0` | `3.4.2` | | [org.apache.maven.plugins:maven-war-plugin](https://github.com/apache/maven-war-plugin) | `3.2.3` | `3.4.0` | | [org.apache.maven.plugins:maven-checkstyle-plugin](https://github.com/apache/maven-checkstyle-plugin) | `3.3.1` | `3.6.0` | | [com.github.spotbugs:spotbugs-maven-plugin](https://github.com/spotbugs/spotbugs-maven-plugin) | `4.8.2.0` | `4.8.6.6` | | [org.apache.maven.plugins:maven-clean-plugin](https://github.com/apache/maven-clean-plugin) | `3.1.0` | `3.4.0` | | [org.apache.maven.plugins:maven-assembly-plugin](https://github.com/apache/maven-assembly-plugin) | `3.6.0` | `3.7.1` | | [org.apache.maven.plugins:maven-dependency-plugin](https://github.com/apache/maven-dependency-plugin) | `3.1.2` | `3.8.1` | | [org.apache.maven.plugins:maven-resources-plugin](https://github.com/apache/maven-resources-plugin) | `3.1.0` | `3.3.1` | | org.sonatype.plugins:nexus-staging-maven-plugin | `1.6.8` | `1.7.0` | | [org.apache.maven.plugins:maven-javadoc-plugin](https://github.com/apache/maven-javadoc-plugin) | `3.2.0` | `3.11.2` | | [org.apache.maven.plugins:maven-source-plugin](https://github.com/apache/maven-source-plugin) | `3.2.1` | `3.3.1` | | [org.jacoco:jacoco-maven-plugin](https://github.com/jacoco/jacoco) | `0.8.5` | `0.8.12` | | [org.codehaus.mojo:xml-maven-plugin](https://github.com/mojohaus/xml-maven-plugin) | `1.0.2` | `1.1.0` | | [org.codehaus.mojo:license-maven-plugin](https://github.com/mojohaus/license-maven-plugin) | `2.0.0` | `2.5.0` | | [org.codehaus.mojo:build-helper-maven-plugin](https://github.com/mojohaus/build-helper-maven-plugin) | `3.4.0` | `3.6.0` | | [org.codehaus.mojo:buildnumber-maven-plugin](https://github.com/mojohaus/buildnumber-maven-plugin) | `3.2.0` | `3.2.1` | Updates `com.google.errorprone:error_prone_core` from 2.10.0 to 2.36.0 - [Release notes](https://github.com/google/error-prone/releases) - [Commits](https://github.com/google/error-prone/compare/v2.10.0...v2.36.0) Updates `com.puppycrawl.tools:checkstyle` from 8.38 to 8.45.1 - [Release notes](https://github.com/checkstyle/checkstyle/releases) - [Commits](https://github.com/checkstyle/checkstyle/compare/checkstyle-8.38...checkstyle-8.45.1) Updates `com.github.spotbugs:spotbugs` from 4.8.2 to 4.8.6 - [Release notes](https://github.com/spotbugs/spotbugs/releases) - [Changelog](https://github.com/spotbugs/spotbugs/blob/master/CHANGELOG.md) - [Commits](https://github.com/spotbugs/spotbugs/compare/4.8.2...4.8.6) Updates `org.apache.maven.plugins:maven-enforcer-plugin` from 3.0.0-M3 to 3.5.0 - [Release notes](https://github.com/apache/maven-enforcer/releases) - [Commits](https://github.com/apache/maven-enforcer/compare/enforcer-3.0.0-M3...enforcer-3.5.0) Updates `org.apache.maven.plugins:maven-compiler-plugin` from 3.8.1 to 3.13.0 - [Release notes](https://github.com/apache/maven-compiler-plugin/releases) - [Commits](https://github.com/apache/maven-compiler-plugin/compare/maven-compiler-plugin-3.8.1...maven-compiler-plugin-3.13.0) Updates `org.apache.maven.plugins:maven-jar-plugin` from 3.2.0 to 3.4.2 - [Release notes](https://github.com/apache/maven-jar-plugin/releases) - [Commits](https://github.com/apache/maven-jar-plugin/compare/maven-jar-plugin-3.2.0...maven-jar-plugin-3.4.2) Updates `org.apache.maven.plugins:maven-war-plugin` from 3.2.3 to 3.4.0 - [Commits](https://github.com/apache/maven-war-plugin/compare/maven-war-plugin-3.2.3...maven-war-plugin-3.4.0) Updates `org.apache.maven.plugins:maven-checkstyle-plugin` from 3.3.1 to 3.6.0 - [Commits](https://github.com/apache/maven-checkstyle-plugin/compare/maven-checkstyle-plugin-3.3.1...maven-checkstyle-plugin-3.6.0) Updates `com.github.spotbugs:spotbugs-maven-plugin` from 4.8.2.0 to 4.8.6.6 - [Release notes](https://github.com/spotbugs/spotbugs-maven-plugin/releases) - [Commits](https://github.com/spotbugs/spotbugs-maven-plugin/compare/spotbugs-maven-plugin-4.8.2.0...spotbugs-maven-plugin-4.8.6.6) Updates `org.apache.maven.plugins:maven-clean-plugin` from 3.1.0 to 3.4.0 - [Release notes](https://github.com/apache/maven-clean-plugin/releases) - [Commits](https://github.com/apache/maven-clean-plugin/compare/maven-clean-plugin-3.1.0...maven-clean-plugin-3.4.0) Updates `org.apache.maven.plugins:maven-assembly-plugin` from 3.6.0 to 3.7.1 - [Release notes](https://github.com/apache/maven-assembly-plugin/releases) - [Commits](https://github.com/apache/maven-assembly-plugin/compare/maven-assembly-plugin-3.6.0...maven-assembly-plugin-3.7.1) Updates `org.apache.maven.plugins:maven-dependency-plugin` from 3.1.2 to 3.8.1 - [Release notes](https://github.com/apache/maven-dependency-plugin/releases) - [Commits](https://github.com/apache/maven-dependency-plugin/compare/maven-dependency-plugin-3.1.2...maven-dependency-plugin-3.8.1) Updates `org.apache.maven.plugins:maven-resources-plugin` from 3.1.0 to 3.3.1 - [Release notes](https://github.com/apache/maven-resources-plugin/releases) - [Commits](https://github.com/apache/maven-resources-plugin/compare/maven-resources-plugin-3.1.0...maven-resources-plugin-3.3.1) Updates `org.sonatype.plugins:nexus-staging-maven-plugin` from 1.6.8 to 1.7.0 Updates `org.apache.maven.plugins:maven-javadoc-plugin` from 3.2.0 to 3.11.2 - [Release notes](https://github.com/apache/maven-javadoc-plugin/releases) - [Commits](https://github.com/apache/maven-javadoc-plugin/compare/maven-javadoc-plugin-3.2.0...maven-javadoc-plugin-3.11.2) Updates `org.apache.maven.plugins:maven-source-plugin` from 3.2.1 to 3.3.1 - [Release notes](https://github.com/apache/maven-source-plugin/releases) - [Commits](https://github.com/apache/maven-source-plugin/compare/maven-source-plugin-3.2.1...maven-source-plugin-3.3.1) Updates `org.jacoco:jacoco-maven-plugin` from 0.8.5 to 0.8.12 - [Release notes](https://github.com/jacoco/jacoco/releases) - [Commits](https://github.com/jacoco/jacoco/compare/v0.8.5...v0.8.12) Updates `org.codehaus.mojo:xml-maven-plugin` from 1.0.2 to 1.1.0 - [Release notes](https://github.com/mojohaus/xml-maven-plugin/releases) - [Commits](https://github.com/mojohaus/xml-maven-plugin/compare/xml-maven-plugin-1.0.2...1.1.0) Updates `org.codehaus.mojo:license-maven-plugin` from 2.0.0 to 2.5.0 - [Release notes](https://github.com/mojohaus/license-maven-plugin/releases) - [Commits](https://github.com/mojohaus/license-maven-plugin/compare/license-maven-plugin-2.0.0...2.5.0) Updates `org.codehaus.mojo:build-helper-maven-plugin` from 3.4.0 to 3.6.0 - [Release notes](https://github.com/mojohaus/build-helper-maven-plugin/releases) - [Commits](https://github.com/mojohaus/build-helper-maven-plugin/compare/3.4.0...3.6.0) Updates `org.codehaus.mojo:buildnumber-maven-plugin` from 3.2.0 to 3.2.1 - [Release notes](https://github.com/mojohaus/buildnumber-maven-plugin/releases) - [Commits](https://github.com/mojohaus/buildnumber-maven-plugin/compare/3.2.0...3.2.1) --- updated-dependencies: - dependency-name: com.google.errorprone:error_prone_core dependency-type: direct:production update-type: version-update:semver-minor dependency-group: build-tools - dependency-name: com.puppycrawl.tools:checkstyle dependency-type: direct:production update-type: version-update:semver-minor dependency-group: build-tools - dependency-name: com.github.spotbugs:spotbugs dependency-type: direct:production update-type: version-update:semver-patch dependency-group: build-tools - dependency-name: org.apache.maven.plugins:maven-enforcer-plugin dependency-type: direct:production update-type: version-update:semver-minor dependency-group: build-tools - dependency-name: org.apache.maven.plugins:maven-compiler-plugin dependency-type: direct:production update-type: version-update:semver-minor dependency-group: build-tools - dependency-name: org.apache.maven.plugins:maven-jar-plugin dependency-type: direct:production update-type: version-update:semver-minor dependency-group: build-tools - dependency-name: org.apache.maven.plugins:maven-war-plugin dependency-type: direct:production update-type: version-update:semver-minor dependency-group: build-tools - dependency-name: org.apache.maven.plugins:maven-checkstyle-plugin dependency-type: direct:production update-type: version-update:semver-minor dependency-group: build-tools - dependency-name: com.github.spotbugs:spotbugs-maven-plugin dependency-type: direct:production update-type: version-update:semver-patch dependency-group: build-tools - dependency-name: org.apache.maven.plugins:maven-clean-plugin dependency-type: direct:production update-type: version-update:semver-minor dependency-group: build-tools - dependency-name: org.apache.maven.plugins:maven-assembly-plugin dependency-type: direct:production update-type: version-update:semver-minor dependency-group: build-tools - dependency-name: org.apache.maven.plugins:maven-dependency-plugin dependency-type: direct:production update-type: version-update:semver-minor dependency-group: build-tools - dependency-name: org.apache.maven.plugins:maven-resources-plugin dependency-type: direct:production update-type: version-update:semver-minor dependency-group: build-tools - dependency-name: org.sonatype.plugins:nexus-staging-maven-plugin dependency-type: direct:production update-type: version-update:semver-minor dependency-group: build-tools - dependency-name: org.apache.maven.plugins:maven-javadoc-plugin dependency-type: direct:production update-type: version-update:semver-minor dependency-group: build-tools - dependency-name: org.apache.maven.plugins:maven-source-plugin dependency-type: direct:production update-type: version-update:semver-minor dependency-group: build-tools - dependency-name: org.jacoco:jacoco-maven-plugin dependency-type: direct:production update-type: version-update:semver-patch dependency-group: build-tools - dependency-name: org.codehaus.mojo:xml-maven-plugin dependency-type: direct:production update-type: version-update:semver-minor dependency-group: build-tools - dependency-name: org.codehaus.mojo:license-maven-plugin dependency-type: direct:production update-type: version-update:semver-minor dependency-group: build-tools - dependency-name: org.codehaus.mojo:build-helper-maven-plugin dependency-type: direct:production update-type: version-update:semver-minor dependency-group: build-tools - dependency-name: org.codehaus.mojo:buildnumber-maven-plugin dependency-type: direct:production update-type: version-update:semver-patch dependency-group: build-tools ... Signed-off-by: dependabot[bot] --- dspace-api/pom.xml | 4 ++-- pom.xml | 38 +++++++++++++++++++------------------- 2 files changed, 21 insertions(+), 21 deletions(-) diff --git a/dspace-api/pom.xml b/dspace-api/pom.xml index c42895ab4c49..cf81408a2659 100644 --- a/dspace-api/pom.xml +++ b/dspace-api/pom.xml @@ -102,7 +102,7 @@ org.codehaus.mojo build-helper-maven-plugin - 3.4.0 + 3.6.0 validate @@ -116,7 +116,7 @@ org.codehaus.mojo buildnumber-maven-plugin - 3.2.0 + 3.2.1 UNKNOWN_REVISION diff --git a/pom.xml b/pom.xml index 50faf1f7372e..f496ed7e879d 100644 --- a/pom.xml +++ b/pom.xml @@ -28,7 +28,7 @@ 8.11.3 3.10.8 - 2.10.0 + 2.36.0 2.16.0 2.16.0 @@ -89,7 +89,7 @@ org.apache.maven.plugins maven-enforcer-plugin - 3.0.0-M3 + 3.5.0 enforce-java @@ -140,7 +140,7 @@ org.apache.maven.plugins maven-compiler-plugin - 3.8.1 + 3.13.0 11 @@ -173,7 +173,7 @@ org.apache.maven.plugins maven-jar-plugin - 3.2.0 + 3.4.2 @@ -187,7 +187,7 @@ org.apache.maven.plugins maven-war-plugin - 3.2.3 + 3.4.0 false @@ -257,7 +257,7 @@ org.apache.maven.plugins maven-checkstyle-plugin - 3.3.1 + 3.6.0 verify-style @@ -287,14 +287,14 @@ com.puppycrawl.tools checkstyle - 8.38 + 8.45.1 com.github.spotbugs spotbugs-maven-plugin - 4.8.2.0 + 4.8.6.6 Max Low @@ -304,7 +304,7 @@ com.github.spotbugs spotbugs - 4.8.2 + 4.8.6 @@ -320,7 +320,7 @@ maven-clean-plugin - 3.1.0 + 3.4.0 @@ -334,17 +334,17 @@ maven-assembly-plugin - 3.6.0 + 3.7.1 org.apache.maven.plugins maven-dependency-plugin - 3.1.2 + 3.8.1 org.apache.maven.plugins maven-resources-plugin - 3.1.0 + 3.3.1 @@ -356,13 +356,13 @@ org.sonatype.plugins nexus-staging-maven-plugin - 1.6.8 + 1.7.0 org.apache.maven.plugins maven-javadoc-plugin - 3.2.0 + 3.11.2 false @@ -372,7 +372,7 @@ org.apache.maven.plugins maven-source-plugin - 3.2.1 + 3.3.1 @@ -391,7 +391,7 @@ org.jacoco jacoco-maven-plugin - 0.8.5 + 0.8.12 @@ -471,7 +471,7 @@ org.codehaus.mojo xml-maven-plugin - 1.0.2 + 1.1.0 validate-ALL-xml-and-xsl @@ -679,7 +679,7 @@ org.codehaus.mojo license-maven-plugin - 2.0.0 + 2.5.0 false From f9fbc237bf30445ec679dff4c36b53a4f9e96de9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 15 Jan 2025 21:01:37 +0000 Subject: [PATCH 296/979] Bump the apache-commons group with 12 updates Bumps the apache-commons group with 12 updates: | Package | From | To | | --- | --- | --- | | commons-beanutils:commons-beanutils | `1.9.4` | `1.10.0` | | commons-cli:commons-cli | `1.6.0` | `1.9.0` | | [commons-codec:commons-codec](https://github.com/apache/commons-codec) | `1.16.0` | `1.17.2` | | org.apache.commons:commons-configuration2 | `2.10.1` | `2.11.0` | | org.apache.commons:commons-dbcp2 | `2.11.0` | `2.13.0` | | commons-io:commons-io | `2.15.1` | `2.18.0` | | org.apache.commons:commons-lang3 | `3.14.0` | `3.17.0` | | commons-logging:commons-logging | `1.3.0` | `1.3.4` | | org.apache.commons:commons-compress | `1.26.0` | `1.27.1` | | [org.apache.commons:commons-csv](https://github.com/apache/commons-csv) | `1.10.0` | `1.13.0` | | org.apache.commons:commons-text | `1.10.0` | `1.13.0` | | commons-validator:commons-validator | `1.7` | `1.9.0` | Updates `commons-beanutils:commons-beanutils` from 1.9.4 to 1.10.0 Updates `commons-cli:commons-cli` from 1.6.0 to 1.9.0 Updates `commons-codec:commons-codec` from 1.16.0 to 1.17.2 - [Changelog](https://github.com/apache/commons-codec/blob/master/RELEASE-NOTES.txt) - [Commits](https://github.com/apache/commons-codec/compare/rel/commons-codec-1.16.0...rel/commons-codec-1.17.2) Updates `org.apache.commons:commons-configuration2` from 2.10.1 to 2.11.0 Updates `org.apache.commons:commons-dbcp2` from 2.11.0 to 2.13.0 Updates `commons-io:commons-io` from 2.15.1 to 2.18.0 Updates `org.apache.commons:commons-lang3` from 3.14.0 to 3.17.0 Updates `commons-logging:commons-logging` from 1.3.0 to 1.3.4 Updates `org.apache.commons:commons-compress` from 1.26.0 to 1.27.1 Updates `org.apache.commons:commons-csv` from 1.10.0 to 1.13.0 - [Changelog](https://github.com/apache/commons-csv/blob/master/RELEASE-NOTES.txt) - [Commits](https://github.com/apache/commons-csv/compare/rel/commons-csv-1.10.0...rel/commons-csv-1.13.0) Updates `org.apache.commons:commons-text` from 1.10.0 to 1.13.0 Updates `commons-validator:commons-validator` from 1.7 to 1.9.0 --- updated-dependencies: - dependency-name: commons-beanutils:commons-beanutils dependency-type: direct:production update-type: version-update:semver-minor dependency-group: apache-commons - dependency-name: commons-cli:commons-cli dependency-type: direct:production update-type: version-update:semver-minor dependency-group: apache-commons - dependency-name: commons-codec:commons-codec dependency-type: direct:production update-type: version-update:semver-minor dependency-group: apache-commons - dependency-name: org.apache.commons:commons-configuration2 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: apache-commons - dependency-name: org.apache.commons:commons-dbcp2 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: apache-commons - dependency-name: commons-io:commons-io dependency-type: direct:production update-type: version-update:semver-minor dependency-group: apache-commons - dependency-name: org.apache.commons:commons-lang3 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: apache-commons - dependency-name: commons-logging:commons-logging dependency-type: direct:production update-type: version-update:semver-patch dependency-group: apache-commons - dependency-name: org.apache.commons:commons-compress dependency-type: direct:production update-type: version-update:semver-minor dependency-group: apache-commons - dependency-name: org.apache.commons:commons-csv dependency-type: direct:production update-type: version-update:semver-minor dependency-group: apache-commons - dependency-name: org.apache.commons:commons-text dependency-type: direct:production update-type: version-update:semver-minor dependency-group: apache-commons - dependency-name: commons-validator:commons-validator dependency-type: direct:production update-type: version-update:semver-minor dependency-group: apache-commons ... Signed-off-by: dependabot[bot] --- pom.xml | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/pom.xml b/pom.xml index bb093621ff2f..c24ed556bfcb 100644 --- a/pom.xml +++ b/pom.xml @@ -1461,17 +1461,17 @@ commons-beanutils commons-beanutils - 1.9.4 + 1.10.0 commons-cli commons-cli - 1.6.0 + 1.9.0 commons-codec commons-codec - 1.16.0 + 1.17.2 org.apache.commons @@ -1481,12 +1481,12 @@ org.apache.commons commons-configuration2 - 2.10.1 + 2.11.0 org.apache.commons commons-dbcp2 - 2.11.0 + 2.13.0 @@ -1498,29 +1498,29 @@ commons-io commons-io - 2.15.1 + 2.18.0 org.apache.commons commons-lang3 - 3.14.0 + 3.17.0 commons-logging commons-logging - 1.3.0 + 1.3.4 org.apache.commons commons-compress - 1.26.0 + 1.27.1 org.apache.commons commons-csv - 1.10.0 + 1.13.0 org.apache.commons @@ -1530,12 +1530,12 @@ org.apache.commons commons-text - 1.10.0 + 1.13.0 commons-validator commons-validator - 1.7 + 1.9.0 joda-time From b4d209368c6da68897092a8ae74e72c48de17ecf Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 15 Jan 2025 21:01:53 +0000 Subject: [PATCH 297/979] Bump the fasterxml group with 5 updates Bumps the fasterxml group with 5 updates: | Package | From | To | | --- | --- | --- | | [com.fasterxml:classmate](https://github.com/FasterXML/java-classmate) | `1.6.0` | `1.7.0` | | [com.fasterxml.jackson.core:jackson-annotations](https://github.com/FasterXML/jackson) | `2.16.0` | `2.18.2` | | [com.fasterxml.jackson.core:jackson-core](https://github.com/FasterXML/jackson-core) | `2.16.0` | `2.18.2` | | [com.fasterxml.jackson.core:jackson-databind](https://github.com/FasterXML/jackson) | `2.16.0` | `2.18.2` | | com.fasterxml.jackson.datatype:jackson-datatype-jsr310 | `2.16.0` | `2.18.2` | Updates `com.fasterxml:classmate` from 1.6.0 to 1.7.0 - [Commits](https://github.com/FasterXML/java-classmate/compare/classmate-1.6.0...classmate-1.7.0) Updates `com.fasterxml.jackson.core:jackson-annotations` from 2.16.0 to 2.18.2 - [Commits](https://github.com/FasterXML/jackson/commits) Updates `com.fasterxml.jackson.core:jackson-core` from 2.16.0 to 2.18.2 - [Commits](https://github.com/FasterXML/jackson-core/compare/jackson-core-2.16.0...jackson-core-2.18.2) Updates `com.fasterxml.jackson.core:jackson-core` from 2.16.0 to 2.18.2 - [Commits](https://github.com/FasterXML/jackson-core/compare/jackson-core-2.16.0...jackson-core-2.18.2) Updates `com.fasterxml.jackson.core:jackson-databind` from 2.16.0 to 2.18.2 - [Commits](https://github.com/FasterXML/jackson/commits) Updates `com.fasterxml.jackson.datatype:jackson-datatype-jsr310` from 2.16.0 to 2.18.2 Updates `com.fasterxml.jackson.datatype:jackson-datatype-jsr310` from 2.16.0 to 2.18.2 --- updated-dependencies: - dependency-name: com.fasterxml:classmate dependency-type: direct:production update-type: version-update:semver-minor dependency-group: fasterxml - dependency-name: com.fasterxml.jackson.core:jackson-annotations dependency-type: direct:production update-type: version-update:semver-minor dependency-group: fasterxml - dependency-name: com.fasterxml.jackson.core:jackson-core dependency-type: direct:production update-type: version-update:semver-minor dependency-group: fasterxml - dependency-name: com.fasterxml.jackson.core:jackson-core dependency-type: direct:production update-type: version-update:semver-minor dependency-group: fasterxml - dependency-name: com.fasterxml.jackson.core:jackson-databind dependency-type: direct:production update-type: version-update:semver-minor dependency-group: fasterxml - dependency-name: com.fasterxml.jackson.datatype:jackson-datatype-jsr310 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: fasterxml - dependency-name: com.fasterxml.jackson.datatype:jackson-datatype-jsr310 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: fasterxml ... Signed-off-by: dependabot[bot] --- pom.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pom.xml b/pom.xml index bb093621ff2f..9f34b01d1d6e 100644 --- a/pom.xml +++ b/pom.xml @@ -31,8 +31,8 @@ 3.10.8 2.10.0 - 2.16.0 - 2.16.0 + 2.18.2 + 2.18.2 2.1.1 4.0.2 4.0.5 @@ -1736,7 +1736,7 @@ com.fasterxml classmate - 1.6.0 + 1.7.0 com.fasterxml.jackson.core From d7b0e26179f9b0812afb722fd13efa00eae4fa57 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 15 Jan 2025 21:02:16 +0000 Subject: [PATCH 298/979] Bump the apache-commons group with 11 updates Bumps the apache-commons group with 11 updates: | Package | From | To | | --- | --- | --- | | commons-beanutils:commons-beanutils | `1.9.4` | `1.10.0` | | commons-cli:commons-cli | `1.6.0` | `1.9.0` | | [commons-codec:commons-codec](https://github.com/apache/commons-codec) | `1.16.0` | `1.17.2` | | org.apache.commons:commons-configuration2 | `2.10.1` | `2.11.0` | | org.apache.commons:commons-dbcp2 | `2.11.0` | `2.13.0` | | commons-io:commons-io | `2.15.1` | `2.18.0` | | org.apache.commons:commons-lang3 | `3.14.0` | `3.17.0` | | commons-logging:commons-logging | `1.3.0` | `1.3.4` | | org.apache.commons:commons-compress | `1.26.0` | `1.27.1` | | org.apache.commons:commons-text | `1.10.0` | `1.13.0` | | commons-validator:commons-validator | `1.7` | `1.9.0` | Updates `commons-beanutils:commons-beanutils` from 1.9.4 to 1.10.0 Updates `commons-cli:commons-cli` from 1.6.0 to 1.9.0 Updates `commons-codec:commons-codec` from 1.16.0 to 1.17.2 - [Changelog](https://github.com/apache/commons-codec/blob/master/RELEASE-NOTES.txt) - [Commits](https://github.com/apache/commons-codec/compare/rel/commons-codec-1.16.0...rel/commons-codec-1.17.2) Updates `org.apache.commons:commons-configuration2` from 2.10.1 to 2.11.0 Updates `org.apache.commons:commons-dbcp2` from 2.11.0 to 2.13.0 Updates `commons-io:commons-io` from 2.15.1 to 2.18.0 Updates `org.apache.commons:commons-lang3` from 3.14.0 to 3.17.0 Updates `commons-logging:commons-logging` from 1.3.0 to 1.3.4 Updates `org.apache.commons:commons-compress` from 1.26.0 to 1.27.1 Updates `org.apache.commons:commons-text` from 1.10.0 to 1.13.0 Updates `commons-validator:commons-validator` from 1.7 to 1.9.0 --- updated-dependencies: - dependency-name: commons-beanutils:commons-beanutils dependency-type: direct:production update-type: version-update:semver-minor dependency-group: apache-commons - dependency-name: commons-cli:commons-cli dependency-type: direct:production update-type: version-update:semver-minor dependency-group: apache-commons - dependency-name: commons-codec:commons-codec dependency-type: direct:production update-type: version-update:semver-minor dependency-group: apache-commons - dependency-name: org.apache.commons:commons-configuration2 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: apache-commons - dependency-name: org.apache.commons:commons-dbcp2 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: apache-commons - dependency-name: commons-io:commons-io dependency-type: direct:production update-type: version-update:semver-minor dependency-group: apache-commons - dependency-name: org.apache.commons:commons-lang3 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: apache-commons - dependency-name: commons-logging:commons-logging dependency-type: direct:production update-type: version-update:semver-patch dependency-group: apache-commons - dependency-name: org.apache.commons:commons-compress dependency-type: direct:production update-type: version-update:semver-minor dependency-group: apache-commons - dependency-name: org.apache.commons:commons-text dependency-type: direct:production update-type: version-update:semver-minor dependency-group: apache-commons - dependency-name: commons-validator:commons-validator dependency-type: direct:production update-type: version-update:semver-minor dependency-group: apache-commons ... Signed-off-by: dependabot[bot] --- pom.xml | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/pom.xml b/pom.xml index 50faf1f7372e..242c03d1b178 100644 --- a/pom.xml +++ b/pom.xml @@ -1463,17 +1463,17 @@ commons-beanutils commons-beanutils - 1.9.4 + 1.10.0 commons-cli commons-cli - 1.6.0 + 1.9.0 commons-codec commons-codec - 1.16.0 + 1.17.2 org.apache.commons @@ -1483,12 +1483,12 @@ org.apache.commons commons-configuration2 - 2.10.1 + 2.11.0 org.apache.commons commons-dbcp2 - 2.11.0 + 2.13.0 commons-fileupload @@ -1498,24 +1498,24 @@ commons-io commons-io - 2.15.1 + 2.18.0 org.apache.commons commons-lang3 - 3.14.0 + 3.17.0 commons-logging commons-logging - 1.3.0 + 1.3.4 org.apache.commons commons-compress - 1.26.0 + 1.27.1 org.apache.commons @@ -1525,12 +1525,12 @@ org.apache.commons commons-text - 1.10.0 + 1.13.0 commons-validator commons-validator - 1.7 + 1.9.0 joda-time From 4a20a4c37fa43c33eb6ba11ea49422e15e786212 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 15 Jan 2025 21:02:28 +0000 Subject: [PATCH 299/979] Bump the fasterxml group with 4 updates Bumps the fasterxml group with 4 updates: [com.fasterxml:classmate](https://github.com/FasterXML/java-classmate), [com.fasterxml.jackson.core:jackson-annotations](https://github.com/FasterXML/jackson), [com.fasterxml.jackson.core:jackson-core](https://github.com/FasterXML/jackson-core) and [com.fasterxml.jackson.core:jackson-databind](https://github.com/FasterXML/jackson). Updates `com.fasterxml:classmate` from 1.6.0 to 1.7.0 - [Commits](https://github.com/FasterXML/java-classmate/compare/classmate-1.6.0...classmate-1.7.0) Updates `com.fasterxml.jackson.core:jackson-annotations` from 2.16.0 to 2.18.2 - [Commits](https://github.com/FasterXML/jackson/commits) Updates `com.fasterxml.jackson.core:jackson-core` from 2.16.0 to 2.18.2 - [Commits](https://github.com/FasterXML/jackson-core/compare/jackson-core-2.16.0...jackson-core-2.18.2) Updates `com.fasterxml.jackson.core:jackson-core` from 2.16.0 to 2.18.2 - [Commits](https://github.com/FasterXML/jackson-core/compare/jackson-core-2.16.0...jackson-core-2.18.2) Updates `com.fasterxml.jackson.core:jackson-databind` from 2.16.0 to 2.18.2 - [Commits](https://github.com/FasterXML/jackson/commits) --- updated-dependencies: - dependency-name: com.fasterxml:classmate dependency-type: direct:production update-type: version-update:semver-minor dependency-group: fasterxml - dependency-name: com.fasterxml.jackson.core:jackson-annotations dependency-type: direct:production update-type: version-update:semver-minor dependency-group: fasterxml - dependency-name: com.fasterxml.jackson.core:jackson-core dependency-type: direct:production update-type: version-update:semver-minor dependency-group: fasterxml - dependency-name: com.fasterxml.jackson.core:jackson-core dependency-type: direct:production update-type: version-update:semver-minor dependency-group: fasterxml - dependency-name: com.fasterxml.jackson.core:jackson-databind dependency-type: direct:production update-type: version-update:semver-minor dependency-group: fasterxml ... Signed-off-by: dependabot[bot] --- pom.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pom.xml b/pom.xml index 50faf1f7372e..a7a87a7938ff 100644 --- a/pom.xml +++ b/pom.xml @@ -30,8 +30,8 @@ 3.10.8 2.10.0 - 2.16.0 - 2.16.0 + 2.18.2 + 2.18.2 1.3.2 2.3.1 2.3.9 @@ -1742,7 +1742,7 @@ com.fasterxml classmate - 1.6.0 + 1.7.0 com.fasterxml.jackson.core From f57c927a2e6440aa929dab7767d475864bdcf427 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 15 Jan 2025 21:02:32 +0000 Subject: [PATCH 300/979] Bump jakarta.servlet:jakarta.servlet-api in the jakarta group Bumps the jakarta group with 1 update: [jakarta.servlet:jakarta.servlet-api](https://github.com/eclipse-ee4j/servlet-api). Updates `jakarta.servlet:jakarta.servlet-api` from 6.0.0 to 6.1.0 - [Commits](https://github.com/eclipse-ee4j/servlet-api/compare/6.0.0-RELEASE...6.1.0-RELEASE) --- updated-dependencies: - dependency-name: jakarta.servlet:jakarta.servlet-api dependency-type: direct:production update-type: version-update:semver-minor dependency-group: jakarta ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index bb093621ff2f..1e837f48c04e 100644 --- a/pom.xml +++ b/pom.xml @@ -1569,7 +1569,7 @@ jakarta.servlet jakarta.servlet-api - 6.0.0 + 6.1.0 jakarta.el From bec1ee84e51e6a22766c58348791292fe9d6f36d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 15 Jan 2025 21:03:40 +0000 Subject: [PATCH 301/979] Bump org.webjars.npm:json-editor__json-editor in the webjars group Bumps the webjars group with 1 update: [org.webjars.npm:json-editor__json-editor](https://github.com/json-editor/json-editor). Updates `org.webjars.npm:json-editor__json-editor` from 2.6.1 to 2.15.1 - [Changelog](https://github.com/json-editor/json-editor/blob/master/CHANGELOG.md) - [Commits](https://github.com/json-editor/json-editor/compare/2.6.1...2.15.1) --- updated-dependencies: - dependency-name: org.webjars.npm:json-editor__json-editor dependency-type: direct:production update-type: version-update:semver-minor dependency-group: webjars ... Signed-off-by: dependabot[bot] --- dspace-server-webapp/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dspace-server-webapp/pom.xml b/dspace-server-webapp/pom.xml index 35fa473fc170..2715338ed0fa 100644 --- a/dspace-server-webapp/pom.xml +++ b/dspace-server-webapp/pom.xml @@ -345,7 +345,7 @@ org.webjars.npm json-editor__json-editor - 2.6.1 + 2.15.1 From d9945b27b94f372979ac3268d2c85887edb00fa3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 15 Jan 2025 21:03:55 +0000 Subject: [PATCH 303/979] Bump jersey.version from 2.39.1 to 2.46 Bumps `jersey.version` from 2.39.1 to 2.46. Updates `org.glassfish.jersey.core:jersey-client` from 2.39.1 to 2.46 Updates `org.glassfish.jersey.inject:jersey-hk2` from 2.39.1 to 2.46 Updates `org.glassfish.jersey.core:jersey-server` from 2.39.1 to 2.46 Updates `org.glassfish.jersey.containers:jersey-container-servlet` from 2.39.1 to 2.46 Updates `org.glassfish.jersey.media:jersey-media-json-jackson` from 2.39.1 to 2.46 Updates `org.glassfish.jersey.media:jersey-media-jaxb` from 2.39.1 to 2.46 Updates `org.glassfish.jersey.ext:jersey-spring5` from 2.39.1 to 2.46 --- updated-dependencies: - dependency-name: org.glassfish.jersey.core:jersey-client dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: org.glassfish.jersey.inject:jersey-hk2 dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: org.glassfish.jersey.core:jersey-server dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: org.glassfish.jersey.containers:jersey-container-servlet dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: org.glassfish.jersey.media:jersey-media-json-jackson dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: org.glassfish.jersey.media:jersey-media-jaxb dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: org.glassfish.jersey.ext:jersey-spring5 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 50faf1f7372e..1e83cbbb9bb9 100644 --- a/pom.xml +++ b/pom.xml @@ -58,7 +58,7 @@ https://jena.apache.org/documentation/migrate_jena2_jena3.html --> 2.13.0 - 2.39.1 + 2.46 UTF-8 From 9d17009cf390ea403e439bc5287bf6c118c88836 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 15 Jan 2025 21:04:17 +0000 Subject: [PATCH 304/979] Bump jetty.version from 9.4.54.v20240208 to 9.4.57.v20241219 Bumps `jetty.version` from 9.4.54.v20240208 to 9.4.57.v20241219. Updates `org.eclipse.jetty:jetty-server` from 9.4.54.v20240208 to 9.4.57.v20241219 Updates `org.eclipse.jetty:jetty-alpn-java-server` from 9.4.54.v20240208 to 9.4.57.v20241219 Updates `org.eclipse.jetty:jetty-deploy` from 9.4.54.v20240208 to 9.4.57.v20241219 Updates `org.eclipse.jetty:jetty-http` from 9.4.54.v20240208 to 9.4.57.v20241219 Updates `org.eclipse.jetty:jetty-io` from 9.4.54.v20240208 to 9.4.57.v20241219 Updates `org.eclipse.jetty:jetty-servlet` from 9.4.54.v20240208 to 9.4.57.v20241219 Updates `org.eclipse.jetty:jetty-servlets` from 9.4.54.v20240208 to 9.4.57.v20241219 Updates `org.eclipse.jetty:jetty-util` from 9.4.54.v20240208 to 9.4.57.v20241219 Updates `org.eclipse.jetty:jetty-webapp` from 9.4.54.v20240208 to 9.4.57.v20241219 Updates `org.eclipse.jetty:jetty-xml` from 9.4.54.v20240208 to 9.4.57.v20241219 Updates `org.eclipse.jetty.http2:http2-common` from 9.4.54.v20240208 to 9.4.57.v20241219 Updates `org.eclipse.jetty.http2:http2-server` from 9.4.54.v20240208 to 9.4.57.v20241219 --- updated-dependencies: - dependency-name: org.eclipse.jetty:jetty-server dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.eclipse.jetty:jetty-alpn-java-server dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.eclipse.jetty:jetty-deploy dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.eclipse.jetty:jetty-http dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.eclipse.jetty:jetty-io dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.eclipse.jetty:jetty-servlet dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.eclipse.jetty:jetty-servlets dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.eclipse.jetty:jetty-util dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.eclipse.jetty:jetty-webapp dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.eclipse.jetty:jetty-xml dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.eclipse.jetty.http2:http2-common dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.eclipse.jetty.http2:http2-server dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 50faf1f7372e..6575c7e5277f 100644 --- a/pom.xml +++ b/pom.xml @@ -37,7 +37,7 @@ 2.3.9 1.1.1 - 9.4.54.v20240208 + 9.4.57.v20241219 2.23.1 2.0.31 1.19.0 From bff9792ff1411f9a23780aa4ceaf1e48003eef6f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 15 Jan 2025 21:04:19 +0000 Subject: [PATCH 305/979] Bump the spring group with 24 updates Bumps the spring group with 24 updates: | Package | From | To | | --- | --- | --- | | [org.springframework:spring-orm](https://github.com/spring-projects/spring-framework) | `6.1.14` | `6.2.1` | | [org.springframework:spring-core](https://github.com/spring-projects/spring-framework) | `6.1.14` | `6.2.1` | | [org.springframework:spring-beans](https://github.com/spring-projects/spring-framework) | `6.1.14` | `6.2.1` | | [org.springframework:spring-aop](https://github.com/spring-projects/spring-framework) | `6.1.14` | `6.2.1` | | [org.springframework:spring-context](https://github.com/spring-projects/spring-framework) | `6.1.14` | `6.2.1` | | [org.springframework:spring-context-support](https://github.com/spring-projects/spring-framework) | `6.1.14` | `6.2.1` | | [org.springframework:spring-tx](https://github.com/spring-projects/spring-framework) | `6.1.14` | `6.2.1` | | [org.springframework:spring-jdbc](https://github.com/spring-projects/spring-framework) | `6.1.14` | `6.2.1` | | [org.springframework:spring-web](https://github.com/spring-projects/spring-framework) | `6.1.14` | `6.2.1` | | [org.springframework:spring-webmvc](https://github.com/spring-projects/spring-framework) | `6.1.14` | `6.2.1` | | [org.springframework:spring-expression](https://github.com/spring-projects/spring-framework) | `6.1.14` | `6.2.1` | | [org.springframework:spring-test](https://github.com/spring-projects/spring-framework) | `6.1.14` | `6.2.1` | | [org.springframework.boot:spring-boot-starter-test](https://github.com/spring-projects/spring-boot) | `3.3.4` | `3.4.1` | | [org.springframework.boot:spring-boot-starter-tomcat](https://github.com/spring-projects/spring-boot) | `3.3.4` | `3.4.1` | | [org.springframework.boot:spring-boot-maven-plugin](https://github.com/spring-projects/spring-boot) | `3.3.4` | `3.4.1` | | [org.springframework.boot:spring-boot-starter-cache](https://github.com/spring-projects/spring-boot) | `3.3.4` | `3.4.1` | | [org.springframework.boot:spring-boot-starter](https://github.com/spring-projects/spring-boot) | `3.3.4` | `3.4.1` | | [org.springframework.boot:spring-boot-starter-web](https://github.com/spring-projects/spring-boot) | `3.3.4` | `3.4.1` | | [org.springframework.boot:spring-boot-starter-data-rest](https://github.com/spring-projects/spring-boot) | `3.3.4` | `3.4.1` | | [org.springframework.boot:spring-boot-starter-security](https://github.com/spring-projects/spring-boot) | `3.3.4` | `3.4.1` | | [org.springframework.boot:spring-boot-starter-aop](https://github.com/spring-projects/spring-boot) | `3.3.4` | `3.4.1` | | [org.springframework.boot:spring-boot-starter-actuator](https://github.com/spring-projects/spring-boot) | `3.3.4` | `3.4.1` | | [org.springframework.boot:spring-boot-starter-log4j2](https://github.com/spring-projects/spring-boot) | `3.3.4` | `3.4.1` | | [org.springframework.security:spring-security-test](https://github.com/spring-projects/spring-security) | `6.3.3` | `6.4.2` | Updates `org.springframework:spring-orm` from 6.1.14 to 6.2.1 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.1.14...v6.2.1) Updates `org.springframework:spring-core` from 6.1.14 to 6.2.1 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.1.14...v6.2.1) Updates `org.springframework:spring-beans` from 6.1.14 to 6.2.1 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.1.14...v6.2.1) Updates `org.springframework:spring-aop` from 6.1.14 to 6.2.1 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.1.14...v6.2.1) Updates `org.springframework:spring-context` from 6.1.14 to 6.2.1 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.1.14...v6.2.1) Updates `org.springframework:spring-context-support` from 6.1.14 to 6.2.1 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.1.14...v6.2.1) Updates `org.springframework:spring-tx` from 6.1.14 to 6.2.1 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.1.14...v6.2.1) Updates `org.springframework:spring-jdbc` from 6.1.14 to 6.2.1 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.1.14...v6.2.1) Updates `org.springframework:spring-web` from 6.1.14 to 6.2.1 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.1.14...v6.2.1) Updates `org.springframework:spring-webmvc` from 6.1.14 to 6.2.1 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.1.14...v6.2.1) Updates `org.springframework:spring-expression` from 6.1.14 to 6.2.1 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.1.14...v6.2.1) Updates `org.springframework:spring-test` from 6.1.14 to 6.2.1 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.1.14...v6.2.1) Updates `org.springframework:spring-core` from 6.1.14 to 6.2.1 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.1.14...v6.2.1) Updates `org.springframework:spring-beans` from 6.1.14 to 6.2.1 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.1.14...v6.2.1) Updates `org.springframework:spring-aop` from 6.1.14 to 6.2.1 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.1.14...v6.2.1) Updates `org.springframework:spring-context` from 6.1.14 to 6.2.1 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.1.14...v6.2.1) Updates `org.springframework:spring-context-support` from 6.1.14 to 6.2.1 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.1.14...v6.2.1) Updates `org.springframework:spring-tx` from 6.1.14 to 6.2.1 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.1.14...v6.2.1) Updates `org.springframework:spring-jdbc` from 6.1.14 to 6.2.1 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.1.14...v6.2.1) Updates `org.springframework:spring-web` from 6.1.14 to 6.2.1 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.1.14...v6.2.1) Updates `org.springframework:spring-webmvc` from 6.1.14 to 6.2.1 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.1.14...v6.2.1) Updates `org.springframework:spring-expression` from 6.1.14 to 6.2.1 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.1.14...v6.2.1) Updates `org.springframework:spring-test` from 6.1.14 to 6.2.1 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.1.14...v6.2.1) Updates `org.springframework.boot:spring-boot-starter-test` from 3.3.4 to 3.4.1 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.3.4...v3.4.1) Updates `org.springframework.boot:spring-boot-starter-tomcat` from 3.3.4 to 3.4.1 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.3.4...v3.4.1) Updates `org.springframework.boot:spring-boot-maven-plugin` from 3.3.4 to 3.4.1 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.3.4...v3.4.1) Updates `org.springframework.boot:spring-boot-starter-cache` from 3.3.4 to 3.4.1 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.3.4...v3.4.1) Updates `org.springframework.boot:spring-boot-starter` from 3.3.4 to 3.4.1 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.3.4...v3.4.1) Updates `org.springframework.boot:spring-boot-starter-web` from 3.3.4 to 3.4.1 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.3.4...v3.4.1) Updates `org.springframework.boot:spring-boot-starter-data-rest` from 3.3.4 to 3.4.1 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.3.4...v3.4.1) Updates `org.springframework.boot:spring-boot-starter-security` from 3.3.4 to 3.4.1 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.3.4...v3.4.1) Updates `org.springframework.boot:spring-boot-starter-aop` from 3.3.4 to 3.4.1 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.3.4...v3.4.1) Updates `org.springframework.boot:spring-boot-starter-actuator` from 3.3.4 to 3.4.1 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.3.4...v3.4.1) Updates `org.springframework.boot:spring-boot-starter-log4j2` from 3.3.4 to 3.4.1 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.3.4...v3.4.1) Updates `org.springframework.boot:spring-boot-starter-tomcat` from 3.3.4 to 3.4.1 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.3.4...v3.4.1) Updates `org.springframework.security:spring-security-test` from 6.3.3 to 6.4.2 - [Release notes](https://github.com/spring-projects/spring-security/releases) - [Changelog](https://github.com/spring-projects/spring-security/blob/main/RELEASE.adoc) - [Commits](https://github.com/spring-projects/spring-security/compare/6.3.3...6.4.2) Updates `org.springframework.boot:spring-boot-maven-plugin` from 3.3.4 to 3.4.1 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.3.4...v3.4.1) Updates `org.springframework.boot:spring-boot-starter-cache` from 3.3.4 to 3.4.1 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.3.4...v3.4.1) Updates `org.springframework.boot:spring-boot-starter` from 3.3.4 to 3.4.1 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.3.4...v3.4.1) Updates `org.springframework.boot:spring-boot-starter-web` from 3.3.4 to 3.4.1 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.3.4...v3.4.1) Updates `org.springframework.boot:spring-boot-starter-data-rest` from 3.3.4 to 3.4.1 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.3.4...v3.4.1) Updates `org.springframework.boot:spring-boot-starter-security` from 3.3.4 to 3.4.1 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.3.4...v3.4.1) Updates `org.springframework.boot:spring-boot-starter-aop` from 3.3.4 to 3.4.1 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.3.4...v3.4.1) Updates `org.springframework.boot:spring-boot-starter-actuator` from 3.3.4 to 3.4.1 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.3.4...v3.4.1) Updates `org.springframework.boot:spring-boot-starter-log4j2` from 3.3.4 to 3.4.1 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.3.4...v3.4.1) --- updated-dependencies: - dependency-name: org.springframework:spring-orm dependency-type: direct:production update-type: version-update:semver-minor dependency-group: spring - dependency-name: org.springframework:spring-core dependency-type: direct:production update-type: version-update:semver-minor dependency-group: spring - dependency-name: org.springframework:spring-beans dependency-type: direct:production update-type: version-update:semver-minor dependency-group: spring - dependency-name: org.springframework:spring-aop dependency-type: direct:production update-type: version-update:semver-minor dependency-group: spring - dependency-name: org.springframework:spring-context dependency-type: direct:production update-type: version-update:semver-minor dependency-group: spring - dependency-name: org.springframework:spring-context-support dependency-type: direct:production update-type: version-update:semver-minor dependency-group: spring - dependency-name: org.springframework:spring-tx dependency-type: direct:production update-type: version-update:semver-minor dependency-group: spring - dependency-name: org.springframework:spring-jdbc dependency-type: direct:production update-type: version-update:semver-minor dependency-group: spring - dependency-name: org.springframework:spring-web dependency-type: direct:production update-type: version-update:semver-minor dependency-group: spring - dependency-name: org.springframework:spring-webmvc dependency-type: direct:production update-type: version-update:semver-minor dependency-group: spring - dependency-name: org.springframework:spring-expression dependency-type: direct:production update-type: version-update:semver-minor dependency-group: spring - dependency-name: org.springframework:spring-test dependency-type: direct:production update-type: version-update:semver-minor dependency-group: spring - dependency-name: org.springframework:spring-core dependency-type: direct:production update-type: version-update:semver-minor dependency-group: spring - dependency-name: org.springframework:spring-beans dependency-type: direct:production update-type: version-update:semver-minor dependency-group: spring - dependency-name: org.springframework:spring-aop dependency-type: direct:production update-type: version-update:semver-minor dependency-group: spring - dependency-name: org.springframework:spring-context dependency-type: direct:production update-type: version-update:semver-minor dependency-group: spring - dependency-name: org.springframework:spring-context-support dependency-type: direct:production update-type: version-update:semver-minor dependency-group: spring - dependency-name: org.springframework:spring-tx dependency-type: direct:production update-type: version-update:semver-minor dependency-group: spring - dependency-name: org.springframework:spring-jdbc dependency-type: direct:production update-type: version-update:semver-minor dependency-group: spring - dependency-name: org.springframework:spring-web dependency-type: direct:production update-type: version-update:semver-minor dependency-group: spring - dependency-name: org.springframework:spring-webmvc dependency-type: direct:production update-type: version-update:semver-minor dependency-group: spring - dependency-name: org.springframework:spring-expression dependency-type: direct:production update-type: version-update:semver-minor dependency-group: spring - dependency-name: org.springframework:spring-test dependency-type: direct:production update-type: version-update:semver-minor dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter-test dependency-type: direct:production update-type: version-update:semver-minor dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter-tomcat dependency-type: direct:production update-type: version-update:semver-minor dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-maven-plugin dependency-type: direct:production update-type: version-update:semver-minor dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter-cache dependency-type: direct:production update-type: version-update:semver-minor dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter dependency-type: direct:production update-type: version-update:semver-minor dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter-web dependency-type: direct:production update-type: version-update:semver-minor dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter-data-rest dependency-type: direct:production update-type: version-update:semver-minor dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter-security dependency-type: direct:production update-type: version-update:semver-minor dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter-aop dependency-type: direct:production update-type: version-update:semver-minor dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter-actuator dependency-type: direct:production update-type: version-update:semver-minor dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter-log4j2 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter-tomcat dependency-type: direct:production update-type: version-update:semver-minor dependency-group: spring - dependency-name: org.springframework.security:spring-security-test dependency-type: direct:production update-type: version-update:semver-minor dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-maven-plugin dependency-type: direct:production update-type: version-update:semver-minor dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter-cache dependency-type: direct:production update-type: version-update:semver-minor dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter dependency-type: direct:production update-type: version-update:semver-minor dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter-web dependency-type: direct:production update-type: version-update:semver-minor dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter-data-rest dependency-type: direct:production update-type: version-update:semver-minor dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter-security dependency-type: direct:production update-type: version-update:semver-minor dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter-aop dependency-type: direct:production update-type: version-update:semver-minor dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter-actuator dependency-type: direct:production update-type: version-update:semver-minor dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter-log4j2 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: spring ... Signed-off-by: dependabot[bot] --- pom.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pom.xml b/pom.xml index bb093621ff2f..a8d4dffc8ba8 100644 --- a/pom.xml +++ b/pom.xml @@ -19,9 +19,9 @@ 17 - 6.1.14 - 3.3.4 - 6.3.3 + 6.2.1 + 3.4.1 + 6.4.2 6.4.8.Final 8.0.1.Final 42.7.3 From 3b63097fc2096551fd5188bb61f7a212bd42e207 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 15 Jan 2025 21:04:29 +0000 Subject: [PATCH 306/979] Bump org.webjars.npm:json-editor__json-editor in the webjars group Bumps the webjars group with 1 update: [org.webjars.npm:json-editor__json-editor](https://github.com/json-editor/json-editor). Updates `org.webjars.npm:json-editor__json-editor` from 2.6.1 to 2.15.1 - [Changelog](https://github.com/json-editor/json-editor/blob/master/CHANGELOG.md) - [Commits](https://github.com/json-editor/json-editor/compare/2.6.1...2.15.1) --- updated-dependencies: - dependency-name: org.webjars.npm:json-editor__json-editor dependency-type: direct:production update-type: version-update:semver-minor dependency-group: webjars ... Signed-off-by: dependabot[bot] --- dspace-server-webapp/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dspace-server-webapp/pom.xml b/dspace-server-webapp/pom.xml index 290f8afb0dc6..8d6be38d09fb 100644 --- a/dspace-server-webapp/pom.xml +++ b/dspace-server-webapp/pom.xml @@ -431,7 +431,7 @@ org.webjars.npm json-editor__json-editor - 2.6.1 + 2.15.1 - + From a2daffe81e125df9720bd18894528878b5d9c8f5 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Tue, 29 Oct 2024 17:02:46 -0500 Subject: [PATCH 310/979] Minor checkstyle fixes after bump to 8.45.1. All are indentation / spacing fixes which are more strict now. --- .../rest/AuthenticationRestController.java | 2 +- .../app/rest/model/AuthorizationRest.java | 6 +-- .../dspace/app/rest/model/BitstreamRest.java | 15 ++----- .../app/rest/model/BrowseIndexRest.java | 10 +---- .../org/dspace/app/rest/model/BundleRest.java | 15 ++----- .../app/rest/model/ClaimedTaskRest.java | 5 +-- .../dspace/app/rest/model/CollectionRest.java | 40 ++++------------- .../dspace/app/rest/model/CommunityRest.java | 25 +++-------- .../dspace/app/rest/model/EPersonRest.java | 5 +-- .../dspace/app/rest/model/EntityTypeRest.java | 5 +-- .../app/rest/model/ExternalSourceRest.java | 5 +-- .../org/dspace/app/rest/model/GroupRest.java | 15 ++----- .../org/dspace/app/rest/model/ItemRest.java | 45 ++++--------------- .../app/rest/model/OrcidHistoryRest.java | 2 +- .../dspace/app/rest/model/PoolTaskRest.java | 5 +-- .../dspace/app/rest/model/ProcessRest.java | 15 ++----- .../app/rest/model/RelationshipRest.java | 5 +-- .../app/rest/model/ResearcherProfileRest.java | 4 +- .../dspace/app/rest/model/SuggestionRest.java | 4 +- .../app/rest/model/SuggestionTargetRest.java | 2 +- .../app/rest/model/VersionHistoryRest.java | 10 +---- .../dspace/app/rest/model/VersionRest.java | 10 +---- .../model/VocabularyEntryDetailsRest.java | 6 +-- .../dspace/app/rest/model/VocabularyRest.java | 4 +- .../rest/model/WorkflowDefinitionRest.java | 10 +---- .../app/rest/model/WorkflowItemRest.java | 20 ++------- .../app/rest/model/WorkflowStepRest.java | 5 +-- .../app/rest/model/WorkspaceItemRest.java | 20 ++------- .../org/dspace/app/rest/utils/RegexUtils.java | 2 +- .../app/rest/matcher/RegistrationMatcher.java | 2 +- 30 files changed, 74 insertions(+), 245 deletions(-) diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/AuthenticationRestController.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/AuthenticationRestController.java index 070f3d8a1868..63ac50b6ea06 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/AuthenticationRestController.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/AuthenticationRestController.java @@ -220,7 +220,7 @@ private AuthenticationTokenResource shortLivedTokenResponse(HttpServletRequest r * @return ResponseEntity */ @RequestMapping(value = "/login", method = { RequestMethod.GET, RequestMethod.PUT, RequestMethod.PATCH, - RequestMethod.DELETE }) + RequestMethod.DELETE }) public ResponseEntity login() { return ResponseEntity.status(HttpStatus.METHOD_NOT_ALLOWED).body("Only POST is allowed for login requests."); } diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/AuthorizationRest.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/AuthorizationRest.java index fa463a7c3968..cd3e33b9e2fa 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/AuthorizationRest.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/AuthorizationRest.java @@ -18,9 +18,9 @@ * @author Andrea Bollini (andrea.bollini at 4science.it) */ @LinksRest(links = { - @LinkRest(method = "getEperson", name = AuthorizationRest.EPERSON), - @LinkRest(method = "getFeature", name = AuthorizationRest.FEATURE), - @LinkRest(method = "getObject", name = AuthorizationRest.OBJECT) + @LinkRest(method = "getEperson", name = AuthorizationRest.EPERSON), + @LinkRest(method = "getFeature", name = AuthorizationRest.FEATURE), + @LinkRest(method = "getObject", name = AuthorizationRest.OBJECT) }) public class AuthorizationRest extends BaseObjectRest { public static final String NAME = "authorization"; diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/BitstreamRest.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/BitstreamRest.java index d2c2268b3f35..d456f7222308 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/BitstreamRest.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/BitstreamRest.java @@ -16,18 +16,9 @@ * @author Andrea Bollini (andrea.bollini at 4science.it) */ @LinksRest(links = { - @LinkRest( - name = BitstreamRest.BUNDLE, - method = "getBundle" - ), - @LinkRest( - name = BitstreamRest.FORMAT, - method = "getFormat" - ), - @LinkRest( - name = BitstreamRest.THUMBNAIL, - method = "getThumbnail" - ) + @LinkRest(name = BitstreamRest.BUNDLE, method = "getBundle"), + @LinkRest(name = BitstreamRest.FORMAT, method = "getFormat"), + @LinkRest(name = BitstreamRest.THUMBNAIL, method = "getThumbnail") }) public class BitstreamRest extends DSpaceObjectRest { public static final String PLURAL_NAME = "bitstreams"; diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/BrowseIndexRest.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/BrowseIndexRest.java index a3c0b37ba576..e5b089479971 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/BrowseIndexRest.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/BrowseIndexRest.java @@ -20,14 +20,8 @@ * @author Andrea Bollini (andrea.bollini at 4science.it) */ @LinksRest(links = { - @LinkRest( - name = BrowseIndexRest.LINK_ITEMS, - method = "listBrowseItems" - ), - @LinkRest( - name = BrowseIndexRest.LINK_ENTRIES, - method = "listBrowseEntries" - ) + @LinkRest(name = BrowseIndexRest.LINK_ITEMS, method = "listBrowseItems"), + @LinkRest(name = BrowseIndexRest.LINK_ENTRIES, method = "listBrowseEntries") }) public class BrowseIndexRest extends BaseObjectRest { private static final long serialVersionUID = -4870333170249999559L; diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/BundleRest.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/BundleRest.java index 1ec9f448dde4..4a417e6c54c3 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/BundleRest.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/BundleRest.java @@ -16,18 +16,9 @@ * @author Jelle Pelgrims (jelle.pelgrims at atmire.com) */ @LinksRest(links = { - @LinkRest( - name = BundleRest.ITEM, - method = "getItem" - ), - @LinkRest( - name = BundleRest.BITSTREAMS, - method = "getBitstreams" - ), - @LinkRest( - name = BundleRest.PRIMARY_BITSTREAM, - method = "getPrimaryBitstream" - ) + @LinkRest(name = BundleRest.ITEM, method = "getItem"), + @LinkRest(name = BundleRest.BITSTREAMS, method = "getBitstreams"), + @LinkRest(name = BundleRest.PRIMARY_BITSTREAM, method = "getPrimaryBitstream") }) public class BundleRest extends DSpaceObjectRest { public static final String NAME = "bundle"; diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/ClaimedTaskRest.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/ClaimedTaskRest.java index 0973fac987d2..d29bf7a7ce6b 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/ClaimedTaskRest.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/ClaimedTaskRest.java @@ -16,10 +16,7 @@ * @author Andrea Bollini (andrea.bollini at 4science.it) */ @LinksRest(links = { - @LinkRest( - name = ClaimedTaskRest.STEP, - method = "getStep" - ) + @LinkRest(name = ClaimedTaskRest.STEP, method = "getStep") }) public class ClaimedTaskRest extends BaseObjectRest { public static final String NAME = "claimedtask"; diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/CollectionRest.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/CollectionRest.java index f00bb883959c..34faba4cb4d9 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/CollectionRest.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/CollectionRest.java @@ -15,38 +15,14 @@ * @author Andrea Bollini (andrea.bollini at 4science.it) */ @LinksRest(links = { - @LinkRest( - name = CollectionRest.LICENSE, - method = "getLicense" - ), - @LinkRest( - name = CollectionRest.LOGO, - method = "getLogo" - ), - @LinkRest( - name = CollectionRest.MAPPED_ITEMS, - method = "getMappedItems" - ), - @LinkRest( - name = CollectionRest.PARENT_COMMUNITY, - method = "getParentCommunity" - ), - @LinkRest( - name = CollectionRest.ADMIN_GROUP, - method = "getAdminGroup" - ), - @LinkRest( - name = CollectionRest.SUBMITTERS_GROUP, - method = "getSubmittersGroup" - ), - @LinkRest( - name = CollectionRest.ITEM_READ_GROUP, - method = "getItemReadGroup" - ), - @LinkRest( - name = CollectionRest.BITSTREAM_READ_GROUP, - method = "getBitstreamReadGroup" - ), + @LinkRest(name = CollectionRest.LICENSE, method = "getLicense"), + @LinkRest(name = CollectionRest.LOGO, method = "getLogo"), + @LinkRest(name = CollectionRest.MAPPED_ITEMS, method = "getMappedItems"), + @LinkRest(name = CollectionRest.PARENT_COMMUNITY, method = "getParentCommunity"), + @LinkRest(name = CollectionRest.ADMIN_GROUP, method = "getAdminGroup"), + @LinkRest(name = CollectionRest.SUBMITTERS_GROUP, method = "getSubmittersGroup"), + @LinkRest(name = CollectionRest.ITEM_READ_GROUP, method = "getItemReadGroup"), + @LinkRest(name = CollectionRest.BITSTREAM_READ_GROUP, method = "getBitstreamReadGroup"), }) public class CollectionRest extends DSpaceObjectRest { public static final String NAME = "collection"; diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/CommunityRest.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/CommunityRest.java index 0004e0b91ca4..e70b30803da3 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/CommunityRest.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/CommunityRest.java @@ -15,26 +15,11 @@ * @author Andrea Bollini (andrea.bollini at 4science.it) */ @LinksRest(links = { - @LinkRest( - name = CommunityRest.COLLECTIONS, - method = "getCollections" - ), - @LinkRest( - name = CommunityRest.LOGO, - method = "getLogo" - ), - @LinkRest( - name = CommunityRest.SUBCOMMUNITIES, - method = "getSubcommunities" - ), - @LinkRest( - name = CommunityRest.PARENT_COMMUNITY, - method = "getParentCommunity" - ), - @LinkRest( - name = CommunityRest.ADMIN_GROUP, - method = "getAdminGroup" - ) + @LinkRest(name = CommunityRest.COLLECTIONS, method = "getCollections"), + @LinkRest(name = CommunityRest.LOGO, method = "getLogo"), + @LinkRest(name = CommunityRest.SUBCOMMUNITIES, method = "getSubcommunities"), + @LinkRest(name = CommunityRest.PARENT_COMMUNITY, method = "getParentCommunity"), + @LinkRest(name = CommunityRest.ADMIN_GROUP, method = "getAdminGroup") }) public class CommunityRest extends DSpaceObjectRest { public static final String NAME = "community"; diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/EPersonRest.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/EPersonRest.java index c06ed0e3fe1f..db243400259d 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/EPersonRest.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/EPersonRest.java @@ -20,10 +20,7 @@ * @author Andrea Bollini (andrea.bollini at 4science.it) */ @LinksRest(links = { - @LinkRest( - name = EPersonRest.GROUPS, - method = "getGroups" - ) + @LinkRest(name = EPersonRest.GROUPS, method = "getGroups") }) public class EPersonRest extends DSpaceObjectRest { public static final String NAME = "eperson"; diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/EntityTypeRest.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/EntityTypeRest.java index 9d4a729ded93..e73aa709180d 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/EntityTypeRest.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/EntityTypeRest.java @@ -15,10 +15,7 @@ * Refer to {@link org.dspace.content.EntityType} for explanation of the properties */ @LinksRest(links = { - @LinkRest( - name = EntityTypeRest.RELATION_SHIP_TYPES, - method = "getEntityTypeRelationship" - ) + @LinkRest(name = EntityTypeRest.RELATION_SHIP_TYPES, method = "getEntityTypeRelationship") }) public class EntityTypeRest extends BaseObjectRest { diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/ExternalSourceRest.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/ExternalSourceRest.java index 58402954e8db..21f41241b293 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/ExternalSourceRest.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/ExternalSourceRest.java @@ -13,10 +13,7 @@ * This class serves as a REST representation for an External Source */ @LinksRest(links = { - @LinkRest( - name = ExternalSourceRest.ENTITY_TYPES, - method = "getSupportedEntityTypes" - ) + @LinkRest(name = ExternalSourceRest.ENTITY_TYPES, method = "getSupportedEntityTypes") }) public class ExternalSourceRest extends BaseObjectRest { diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/GroupRest.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/GroupRest.java index 7d56af2e7204..0a4963b66fa0 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/GroupRest.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/GroupRest.java @@ -18,18 +18,9 @@ */ @JsonIgnoreProperties(ignoreUnknown = true) @LinksRest(links = { - @LinkRest( - name = GroupRest.SUBGROUPS, - method = "getGroups" - ), - @LinkRest( - name = GroupRest.EPERSONS, - method = "getMembers" - ), - @LinkRest( - name = GroupRest.OBJECT, - method = "getParentObject" - ) + @LinkRest(name = GroupRest.SUBGROUPS, method = "getGroups"), + @LinkRest(name = GroupRest.EPERSONS, method = "getMembers"), + @LinkRest(name = GroupRest.OBJECT, method = "getParentObject") }) public class GroupRest extends DSpaceObjectRest { public static final String NAME = "group"; diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/ItemRest.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/ItemRest.java index b2f540c0ac4a..293f95c4de39 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/ItemRest.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/ItemRest.java @@ -17,42 +17,15 @@ * @author Andrea Bollini (andrea.bollini at 4science.it) */ @LinksRest(links = { - @LinkRest( - name = ItemRest.ACCESS_STATUS, - method = "getAccessStatus" - ), - @LinkRest( - name = ItemRest.BUNDLES, - method = "getBundles" - ), - @LinkRest( - name = ItemRest.IDENTIFIERS, - method = "getIdentifiers" - ), - @LinkRest( - name = ItemRest.MAPPED_COLLECTIONS, - method = "getMappedCollections" - ), - @LinkRest( - name = ItemRest.OWNING_COLLECTION, - method = "getOwningCollection" - ), - @LinkRest( - name = ItemRest.RELATIONSHIPS, - method = "getRelationships" - ), - @LinkRest( - name = ItemRest.VERSION, - method = "getItemVersion" - ), - @LinkRest( - name = ItemRest.TEMPLATE_ITEM_OF, - method = "getTemplateItemOf" - ), - @LinkRest( - name = ItemRest.THUMBNAIL, - method = "getThumbnail" - ) + @LinkRest(name = ItemRest.ACCESS_STATUS, method = "getAccessStatus"), + @LinkRest(name = ItemRest.BUNDLES, method = "getBundles"), + @LinkRest(name = ItemRest.IDENTIFIERS, method = "getIdentifiers"), + @LinkRest(name = ItemRest.MAPPED_COLLECTIONS, method = "getMappedCollections"), + @LinkRest(name = ItemRest.OWNING_COLLECTION, method = "getOwningCollection"), + @LinkRest(name = ItemRest.RELATIONSHIPS, method = "getRelationships"), + @LinkRest(name = ItemRest.VERSION, method = "getItemVersion"), + @LinkRest(name = ItemRest.TEMPLATE_ITEM_OF, method = "getTemplateItemOf"), + @LinkRest(name = ItemRest.THUMBNAIL, method = "getThumbnail") }) public class ItemRest extends DSpaceObjectRest { public static final String NAME = "item"; diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/OrcidHistoryRest.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/OrcidHistoryRest.java index 2c4c7cbe6043..433d5626ca42 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/OrcidHistoryRest.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/OrcidHistoryRest.java @@ -39,7 +39,7 @@ public class OrcidHistoryRest extends BaseObjectRest { private String responseMessage; - public OrcidHistoryRest(){} + public OrcidHistoryRest() {} @Override @JsonProperty(access = JsonProperty.Access.READ_ONLY) diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/PoolTaskRest.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/PoolTaskRest.java index 0b66f0604b2e..94c70037330e 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/PoolTaskRest.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/PoolTaskRest.java @@ -17,10 +17,7 @@ * @author Andrea Bollini (andrea.bollini at 4science.it) */ @LinksRest(links = { - @LinkRest( - name = PoolTaskRest.STEP, - method = "getStep" - ) + @LinkRest(name = PoolTaskRest.STEP, method = "getStep") }) public class PoolTaskRest extends BaseObjectRest { public static final String NAME = "pooltask"; diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/ProcessRest.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/ProcessRest.java index d3d88c2776ce..fee104b4e389 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/ProcessRest.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/ProcessRest.java @@ -21,18 +21,9 @@ * This class serves as a REST representation for the {@link Process} class */ @LinksRest(links = { - @LinkRest( - name = ProcessRest.FILES, - method = "getFilesFromProcess" - ), - @LinkRest( - name = ProcessRest.FILE_TYPES, - method = "getFileTypesFromProcess" - ), - @LinkRest( - name = ProcessRest.OUTPUT, - method = "getOutputFromProcess" - ) + @LinkRest(name = ProcessRest.FILES, method = "getFilesFromProcess"), + @LinkRest(name = ProcessRest.FILE_TYPES, method = "getFileTypesFromProcess"), + @LinkRest(name = ProcessRest.OUTPUT, method = "getOutputFromProcess") }) public class ProcessRest extends BaseObjectRest { public static final String NAME = "process"; diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/RelationshipRest.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/RelationshipRest.java index 76a7a4348682..723f7e148b27 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/RelationshipRest.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/RelationshipRest.java @@ -19,10 +19,7 @@ * Refer to {@link org.dspace.content.Relationship} for explanation about the properties */ @LinksRest(links = { - @LinkRest( - name = RelationshipRest.RELATIONSHIP_TYPE, - method = "getRelationshipType" - ) + @LinkRest(name = RelationshipRest.RELATIONSHIP_TYPE, method = "getRelationshipType") }) public class RelationshipRest extends BaseObjectRest { public static final String NAME = "relationship"; diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/ResearcherProfileRest.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/ResearcherProfileRest.java index 13faa2e2bbdf..629dbdf85821 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/ResearcherProfileRest.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/ResearcherProfileRest.java @@ -20,8 +20,8 @@ * */ @LinksRest(links = { - @LinkRest(name = ResearcherProfileRest.ITEM, method = "getItem"), - @LinkRest(name = ResearcherProfileRest.EPERSON, method = "getEPerson") + @LinkRest(name = ResearcherProfileRest.ITEM, method = "getItem"), + @LinkRest(name = ResearcherProfileRest.EPERSON, method = "getEPerson") }) public class ResearcherProfileRest extends BaseObjectRest { diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/SuggestionRest.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/SuggestionRest.java index c7210e892558..7b1a05127fc7 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/SuggestionRest.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/SuggestionRest.java @@ -21,7 +21,9 @@ * * @author Andrea Bollini (andrea.bollini at 4science.it) */ -@LinksRest(links = { @LinkRest(name = SuggestionRest.TARGET, method = "getTarget") }) +@LinksRest(links = { + @LinkRest(name = SuggestionRest.TARGET, method = "getTarget") +}) public class SuggestionRest extends BaseObjectRest { private static final long serialVersionUID = 1L; public static final String NAME = "suggestion"; diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/SuggestionTargetRest.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/SuggestionTargetRest.java index 65764507e247..b6518eff7488 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/SuggestionTargetRest.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/SuggestionTargetRest.java @@ -19,7 +19,7 @@ * @author Andrea Bollini (andrea.bollini at 4science.it) */ @LinksRest(links = { - @LinkRest(name = SuggestionTargetRest.TARGET, method = "getTarget") + @LinkRest(name = SuggestionTargetRest.TARGET, method = "getTarget") }) public class SuggestionTargetRest extends BaseObjectRest { private static final long serialVersionUID = 1L; diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/VersionHistoryRest.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/VersionHistoryRest.java index 5aab7028a8c6..80f704c77936 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/VersionHistoryRest.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/VersionHistoryRest.java @@ -13,14 +13,8 @@ * The REST object for the {@link org.dspace.versioning.VersionHistory} object */ @LinksRest(links = { - @LinkRest( - name = VersionHistoryRest.VERSIONS, - method = "getVersions" - ), - @LinkRest( - name = VersionHistoryRest.DRAFT_VERSION, - method = "getDraftVersion" - ) + @LinkRest(name = VersionHistoryRest.VERSIONS, method = "getVersions"), + @LinkRest(name = VersionHistoryRest.DRAFT_VERSION, method = "getDraftVersion") }) public class VersionHistoryRest extends BaseObjectRest { diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/VersionRest.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/VersionRest.java index 21bf82804dd2..d9ebdd67e408 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/VersionRest.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/VersionRest.java @@ -16,14 +16,8 @@ * The REST object for the {@link org.dspace.versioning.Version} objects */ @LinksRest(links = { - @LinkRest( - name = VersionRest.VERSION_HISTORY, - method = "getVersionHistory" - ), - @LinkRest( - name = VersionRest.ITEM, - method = "getVersionItem" - ) + @LinkRest(name = VersionRest.VERSION_HISTORY, method = "getVersionHistory"), + @LinkRest(name = VersionRest.ITEM, method = "getVersionItem") }) public class VersionRest extends BaseObjectRest { diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/VocabularyEntryDetailsRest.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/VocabularyEntryDetailsRest.java index e5869a852521..884e14642cf9 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/VocabularyEntryDetailsRest.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/VocabularyEntryDetailsRest.java @@ -18,9 +18,9 @@ * @author Andrea Bollini (andrea.bollini at 4science.it) */ @LinksRest(links = { - @LinkRest(name = VocabularyEntryDetailsRest.PARENT, method = "getParent"), - @LinkRest(name = VocabularyEntryDetailsRest.CHILDREN, method = "getChildren") - }) + @LinkRest(name = VocabularyEntryDetailsRest.PARENT, method = "getParent"), + @LinkRest(name = VocabularyEntryDetailsRest.CHILDREN, method = "getChildren") +}) public class VocabularyEntryDetailsRest extends BaseObjectRest { public static final String PLURAL_NAME = "vocabularyEntryDetails"; public static final String NAME = "vocabularyEntryDetail"; diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/VocabularyRest.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/VocabularyRest.java index f119059c2bb7..a54d93c643b4 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/VocabularyRest.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/VocabularyRest.java @@ -15,9 +15,7 @@ * @author Andrea Bollini (andrea.bollini at 4science.it) */ @LinksRest(links = { - @LinkRest(name = VocabularyRest.ENTRIES, - method = "filter" - ), + @LinkRest(name = VocabularyRest.ENTRIES, method = "filter"), }) public class VocabularyRest extends BaseObjectRest { diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/WorkflowDefinitionRest.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/WorkflowDefinitionRest.java index 0ec967d09876..9cef79aaf3be 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/WorkflowDefinitionRest.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/WorkflowDefinitionRest.java @@ -18,14 +18,8 @@ * @author Maria Verdonck (Atmire) on 11/12/2019 */ @LinksRest(links = { - @LinkRest( - name = WorkflowDefinitionRest.COLLECTIONS_MAPPED_TO, - method = "getCollections" - ), - @LinkRest( - name = WorkflowDefinitionRest.STEPS, - method = "getSteps" - ) + @LinkRest(name = WorkflowDefinitionRest.COLLECTIONS_MAPPED_TO, method = "getCollections"), + @LinkRest(name = WorkflowDefinitionRest.STEPS, method = "getSteps") }) public class WorkflowDefinitionRest extends BaseObjectRest { diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/WorkflowItemRest.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/WorkflowItemRest.java index 65fa531c5e42..d08abb3546a3 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/WorkflowItemRest.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/WorkflowItemRest.java @@ -15,22 +15,10 @@ * @author Andrea Bollini (andrea.bollini at 4science.it) */ @LinksRest(links = { - @LinkRest( - name = WorkflowItemRest.STEP, - method = "getStep" - ), - @LinkRest( - name = WorkflowItemRest.SUBMITTER, - method = "getWorkflowItemSubmitter" - ), - @LinkRest( - name = WorkflowItemRest.ITEM, - method = "getWorkflowItemItem" - ), - @LinkRest( - name = WorkflowItemRest.COLLECTION, - method = "getWorkflowItemCollection" - ) + @LinkRest(name = WorkflowItemRest.STEP, method = "getStep"), + @LinkRest(name = WorkflowItemRest.SUBMITTER, method = "getWorkflowItemSubmitter"), + @LinkRest(name = WorkflowItemRest.ITEM, method = "getWorkflowItemItem"), + @LinkRest(name = WorkflowItemRest.COLLECTION, method = "getWorkflowItemCollection") }) public class WorkflowItemRest extends AInprogressSubmissionRest { public static final String NAME = "workflowitem"; diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/WorkflowStepRest.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/WorkflowStepRest.java index b3397721c117..53ddf38709e4 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/WorkflowStepRest.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/WorkflowStepRest.java @@ -18,10 +18,7 @@ * @author Maria Verdonck (Atmire) on 10/01/2020 */ @LinksRest(links = { - @LinkRest( - name = WorkflowStepRest.ACTIONS, - method = "getActions" - ), + @LinkRest(name = WorkflowStepRest.ACTIONS, method = "getActions"), }) public class WorkflowStepRest extends BaseObjectRest { diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/WorkspaceItemRest.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/WorkspaceItemRest.java index e311cd259231..8e0d52123f99 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/WorkspaceItemRest.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/WorkspaceItemRest.java @@ -15,22 +15,10 @@ * @author Andrea Bollini (andrea.bollini at 4science.it) */ @LinksRest(links = { - @LinkRest( - name = WorkspaceItemRest.SUPERVISION_ORDERS, - method = "getSupervisionOrders" - ), - @LinkRest( - name = WorkspaceItemRest.SUBMITTER, - method = "getWorkspaceItemSubmitter" - ), - @LinkRest( - name = WorkspaceItemRest.ITEM, - method = "getWorkspaceItemItem" - ), - @LinkRest( - name = WorkspaceItemRest.COLLECTION, - method = "getWorkspaceItemCollection" - ) + @LinkRest(name = WorkspaceItemRest.SUPERVISION_ORDERS, method = "getSupervisionOrders"), + @LinkRest(name = WorkspaceItemRest.SUBMITTER, method = "getWorkspaceItemSubmitter"), + @LinkRest(name = WorkspaceItemRest.ITEM, method = "getWorkspaceItemItem"), + @LinkRest(name = WorkspaceItemRest.COLLECTION, method = "getWorkspaceItemCollection") }) public class WorkspaceItemRest extends AInprogressSubmissionRest { public static final String NAME = "workspaceitem"; diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/utils/RegexUtils.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/utils/RegexUtils.java index b358e785c3b3..df525f679323 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/utils/RegexUtils.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/utils/RegexUtils.java @@ -13,7 +13,7 @@ */ public class RegexUtils { - private RegexUtils(){} + private RegexUtils() {} /** * Regular expression in the request mapping to accept UUID as identifier diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/matcher/RegistrationMatcher.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/matcher/RegistrationMatcher.java index a154091a2eff..2a4cee8375be 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/matcher/RegistrationMatcher.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/matcher/RegistrationMatcher.java @@ -17,7 +17,7 @@ public class RegistrationMatcher { - private RegistrationMatcher(){} + private RegistrationMatcher() {} public static Matcher matchRegistration(String email, UUID epersonUuid) { return allOf( From abfb86b791ee5dff051492f82e6b1e664f05d8ab Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Tue, 3 Dec 2024 09:34:02 -0600 Subject: [PATCH 311/979] Add newly required "should-stop" flag to errorprone config. See https://errorprone.info/docs/installation --- pom.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/pom.xml b/pom.xml index 7a2f9792aa7a..9f76eb74fb23 100644 --- a/pom.xml +++ b/pom.xml @@ -147,6 +147,7 @@ true -XDcompilePolicy=simple + --should-stop=ifError=FLOW -Xplugin:ErrorProne -J--add-exports=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED -J--add-exports=jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED From 52b310077617921aea05e4cec4c69a5b250dd632 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Tue, 3 Dec 2024 09:44:56 -0600 Subject: [PATCH 312/979] Fix duplicate code warning from errorprone. This "else if" clause is the same as the "else" and can be removed --- .../src/main/java/org/dspace/discovery/SolrServiceImpl.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/discovery/SolrServiceImpl.java b/dspace-api/src/main/java/org/dspace/discovery/SolrServiceImpl.java index 0769310b8175..75f0a0ac4d05 100644 --- a/dspace-api/src/main/java/org/dspace/discovery/SolrServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/discovery/SolrServiceImpl.java @@ -1445,8 +1445,6 @@ protected String transformFacetField(DiscoverFacetField facetFieldConfig, String } else { return field + "_acid"; } - } else if (facetFieldConfig.getType().equals(DiscoveryConfigurationParameters.TYPE_STANDARD)) { - return field; } else { return field; } From a01983c23027642163f7a8f88525e5638784f428 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Tue, 29 Oct 2024 17:02:14 -0500 Subject: [PATCH 313/979] Fix checkstyle.xml syntax for bump to 8.45.1 --- checkstyle.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/checkstyle.xml b/checkstyle.xml index e0fa808d83cb..a33fc4831950 100644 --- a/checkstyle.xml +++ b/checkstyle.xml @@ -92,7 +92,7 @@ For more information on CheckStyle configurations below, see: http://checkstyle. - + From eee743a72d779d7af940a0ecf83393f26c2b4de2 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Tue, 29 Oct 2024 17:02:46 -0500 Subject: [PATCH 314/979] Minor checkstyle fixes after bump to 8.45.1. All are indentation / spacing fixes which are more strict now. --- .../rest/AuthenticationRestController.java | 2 +- .../app/rest/model/AuthorizationRest.java | 6 +-- .../dspace/app/rest/model/BitstreamRest.java | 15 ++----- .../app/rest/model/BrowseIndexRest.java | 10 +---- .../org/dspace/app/rest/model/BundleRest.java | 15 ++----- .../app/rest/model/ClaimedTaskRest.java | 5 +-- .../dspace/app/rest/model/CollectionRest.java | 40 ++++------------- .../dspace/app/rest/model/CommunityRest.java | 25 +++-------- .../dspace/app/rest/model/EPersonRest.java | 5 +-- .../dspace/app/rest/model/EntityTypeRest.java | 5 +-- .../app/rest/model/ExternalSourceRest.java | 5 +-- .../org/dspace/app/rest/model/GroupRest.java | 15 ++----- .../org/dspace/app/rest/model/ItemRest.java | 45 ++++--------------- .../app/rest/model/OrcidHistoryRest.java | 2 +- .../dspace/app/rest/model/PoolTaskRest.java | 5 +-- .../dspace/app/rest/model/ProcessRest.java | 15 ++----- .../app/rest/model/RelationshipRest.java | 5 +-- .../app/rest/model/ResearcherProfileRest.java | 4 +- .../app/rest/model/VersionHistoryRest.java | 10 +---- .../dspace/app/rest/model/VersionRest.java | 10 +---- .../model/VocabularyEntryDetailsRest.java | 6 +-- .../dspace/app/rest/model/VocabularyRest.java | 4 +- .../rest/model/WorkflowDefinitionRest.java | 10 +---- .../app/rest/model/WorkflowItemRest.java | 20 ++------- .../app/rest/model/WorkflowStepRest.java | 5 +-- .../app/rest/model/WorkspaceItemRest.java | 20 ++------- .../org/dspace/app/rest/utils/RegexUtils.java | 2 +- .../app/rest/matcher/RegistrationMatcher.java | 2 +- 28 files changed, 70 insertions(+), 243 deletions(-) diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/AuthenticationRestController.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/AuthenticationRestController.java index e8b8eb8e70da..2fbc116843ca 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/AuthenticationRestController.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/AuthenticationRestController.java @@ -224,7 +224,7 @@ private AuthenticationTokenResource shortLivedTokenResponse(HttpServletRequest r * @return ResponseEntity */ @RequestMapping(value = "/login", method = { RequestMethod.GET, RequestMethod.PUT, RequestMethod.PATCH, - RequestMethod.DELETE }) + RequestMethod.DELETE }) public ResponseEntity login() { return ResponseEntity.status(HttpStatus.METHOD_NOT_ALLOWED).body("Only POST is allowed for login requests."); } diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/AuthorizationRest.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/AuthorizationRest.java index 95f288831303..7aec2cb4b76e 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/AuthorizationRest.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/AuthorizationRest.java @@ -18,9 +18,9 @@ * @author Andrea Bollini (andrea.bollini at 4science.it) */ @LinksRest(links = { - @LinkRest(method = "getEperson", name = AuthorizationRest.EPERSON), - @LinkRest(method = "getFeature", name = AuthorizationRest.FEATURE), - @LinkRest(method = "getObject", name = AuthorizationRest.OBJECT) + @LinkRest(method = "getEperson", name = AuthorizationRest.EPERSON), + @LinkRest(method = "getFeature", name = AuthorizationRest.FEATURE), + @LinkRest(method = "getObject", name = AuthorizationRest.OBJECT) }) public class AuthorizationRest extends BaseObjectRest { public static final String NAME = "authorization"; diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/BitstreamRest.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/BitstreamRest.java index 8e9efc2680b7..77b558c0faab 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/BitstreamRest.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/BitstreamRest.java @@ -16,18 +16,9 @@ * @author Andrea Bollini (andrea.bollini at 4science.it) */ @LinksRest(links = { - @LinkRest( - name = BitstreamRest.BUNDLE, - method = "getBundle" - ), - @LinkRest( - name = BitstreamRest.FORMAT, - method = "getFormat" - ), - @LinkRest( - name = BitstreamRest.THUMBNAIL, - method = "getThumbnail" - ) + @LinkRest(name = BitstreamRest.BUNDLE, method = "getBundle"), + @LinkRest(name = BitstreamRest.FORMAT, method = "getFormat"), + @LinkRest(name = BitstreamRest.THUMBNAIL, method = "getThumbnail") }) public class BitstreamRest extends DSpaceObjectRest { public static final String PLURAL_NAME = "bitstreams"; diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/BrowseIndexRest.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/BrowseIndexRest.java index f7978f00fdf5..176b268e4be1 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/BrowseIndexRest.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/BrowseIndexRest.java @@ -20,14 +20,8 @@ * @author Andrea Bollini (andrea.bollini at 4science.it) */ @LinksRest(links = { - @LinkRest( - name = BrowseIndexRest.LINK_ITEMS, - method = "listBrowseItems" - ), - @LinkRest( - name = BrowseIndexRest.LINK_ENTRIES, - method = "listBrowseEntries" - ) + @LinkRest(name = BrowseIndexRest.LINK_ITEMS, method = "listBrowseItems"), + @LinkRest(name = BrowseIndexRest.LINK_ENTRIES, method = "listBrowseEntries") }) public class BrowseIndexRest extends BaseObjectRest { private static final long serialVersionUID = -4870333170249999559L; diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/BundleRest.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/BundleRest.java index dd4a80d488a8..7ef1ceed92c9 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/BundleRest.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/BundleRest.java @@ -16,18 +16,9 @@ * @author Jelle Pelgrims (jelle.pelgrims at atmire.com) */ @LinksRest(links = { - @LinkRest( - name = BundleRest.ITEM, - method = "getItem" - ), - @LinkRest( - name = BundleRest.BITSTREAMS, - method = "getBitstreams" - ), - @LinkRest( - name = BundleRest.PRIMARY_BITSTREAM, - method = "getPrimaryBitstream" - ) + @LinkRest(name = BundleRest.ITEM, method = "getItem"), + @LinkRest(name = BundleRest.BITSTREAMS, method = "getBitstreams"), + @LinkRest(name = BundleRest.PRIMARY_BITSTREAM, method = "getPrimaryBitstream") }) public class BundleRest extends DSpaceObjectRest { public static final String NAME = "bundle"; diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/ClaimedTaskRest.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/ClaimedTaskRest.java index dd97fef44d0b..d9c907697573 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/ClaimedTaskRest.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/ClaimedTaskRest.java @@ -16,10 +16,7 @@ * @author Andrea Bollini (andrea.bollini at 4science.it) */ @LinksRest(links = { - @LinkRest( - name = ClaimedTaskRest.STEP, - method = "getStep" - ) + @LinkRest(name = ClaimedTaskRest.STEP, method = "getStep") }) public class ClaimedTaskRest extends BaseObjectRest { public static final String NAME = "claimedtask"; diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/CollectionRest.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/CollectionRest.java index 3f5ae3bb34c2..28ff9bcceb6b 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/CollectionRest.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/CollectionRest.java @@ -15,38 +15,14 @@ * @author Andrea Bollini (andrea.bollini at 4science.it) */ @LinksRest(links = { - @LinkRest( - name = CollectionRest.LICENSE, - method = "getLicense" - ), - @LinkRest( - name = CollectionRest.LOGO, - method = "getLogo" - ), - @LinkRest( - name = CollectionRest.MAPPED_ITEMS, - method = "getMappedItems" - ), - @LinkRest( - name = CollectionRest.PARENT_COMMUNITY, - method = "getParentCommunity" - ), - @LinkRest( - name = CollectionRest.ADMIN_GROUP, - method = "getAdminGroup" - ), - @LinkRest( - name = CollectionRest.SUBMITTERS_GROUP, - method = "getSubmittersGroup" - ), - @LinkRest( - name = CollectionRest.ITEM_READ_GROUP, - method = "getItemReadGroup" - ), - @LinkRest( - name = CollectionRest.BITSTREAM_READ_GROUP, - method = "getBitstreamReadGroup" - ), + @LinkRest(name = CollectionRest.LICENSE, method = "getLicense"), + @LinkRest(name = CollectionRest.LOGO, method = "getLogo"), + @LinkRest(name = CollectionRest.MAPPED_ITEMS, method = "getMappedItems"), + @LinkRest(name = CollectionRest.PARENT_COMMUNITY, method = "getParentCommunity"), + @LinkRest(name = CollectionRest.ADMIN_GROUP, method = "getAdminGroup"), + @LinkRest(name = CollectionRest.SUBMITTERS_GROUP, method = "getSubmittersGroup"), + @LinkRest(name = CollectionRest.ITEM_READ_GROUP, method = "getItemReadGroup"), + @LinkRest(name = CollectionRest.BITSTREAM_READ_GROUP, method = "getBitstreamReadGroup"), }) public class CollectionRest extends DSpaceObjectRest { public static final String NAME = "collection"; diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/CommunityRest.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/CommunityRest.java index 86dc4b2c3900..08758ef9cc27 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/CommunityRest.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/CommunityRest.java @@ -15,26 +15,11 @@ * @author Andrea Bollini (andrea.bollini at 4science.it) */ @LinksRest(links = { - @LinkRest( - name = CommunityRest.COLLECTIONS, - method = "getCollections" - ), - @LinkRest( - name = CommunityRest.LOGO, - method = "getLogo" - ), - @LinkRest( - name = CommunityRest.SUBCOMMUNITIES, - method = "getSubcommunities" - ), - @LinkRest( - name = CommunityRest.PARENT_COMMUNITY, - method = "getParentCommunity" - ), - @LinkRest( - name = CommunityRest.ADMIN_GROUP, - method = "getAdminGroup" - ) + @LinkRest(name = CommunityRest.COLLECTIONS, method = "getCollections"), + @LinkRest(name = CommunityRest.LOGO, method = "getLogo"), + @LinkRest(name = CommunityRest.SUBCOMMUNITIES, method = "getSubcommunities"), + @LinkRest(name = CommunityRest.PARENT_COMMUNITY, method = "getParentCommunity"), + @LinkRest(name = CommunityRest.ADMIN_GROUP, method = "getAdminGroup") }) public class CommunityRest extends DSpaceObjectRest { public static final String NAME = "community"; diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/EPersonRest.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/EPersonRest.java index 7b4c683322a9..0c4f4d7b290b 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/EPersonRest.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/EPersonRest.java @@ -20,10 +20,7 @@ * @author Andrea Bollini (andrea.bollini at 4science.it) */ @LinksRest(links = { - @LinkRest( - name = EPersonRest.GROUPS, - method = "getGroups" - ) + @LinkRest(name = EPersonRest.GROUPS, method = "getGroups") }) public class EPersonRest extends DSpaceObjectRest { public static final String NAME = "eperson"; diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/EntityTypeRest.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/EntityTypeRest.java index f777b3a29c18..f62ef2767ea9 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/EntityTypeRest.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/EntityTypeRest.java @@ -15,10 +15,7 @@ * Refer to {@link org.dspace.content.EntityType} for explanation of the properties */ @LinksRest(links = { - @LinkRest( - name = EntityTypeRest.RELATION_SHIP_TYPES, - method = "getEntityTypeRelationship" - ) + @LinkRest(name = EntityTypeRest.RELATION_SHIP_TYPES, method = "getEntityTypeRelationship") }) public class EntityTypeRest extends BaseObjectRest { diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/ExternalSourceRest.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/ExternalSourceRest.java index 639c2cf72e2e..2cd3712bdd53 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/ExternalSourceRest.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/ExternalSourceRest.java @@ -13,10 +13,7 @@ * This class serves as a REST representation for an External Source */ @LinksRest(links = { - @LinkRest( - name = ExternalSourceRest.ENTITY_TYPES, - method = "getSupportedEntityTypes" - ) + @LinkRest(name = ExternalSourceRest.ENTITY_TYPES, method = "getSupportedEntityTypes") }) public class ExternalSourceRest extends BaseObjectRest { diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/GroupRest.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/GroupRest.java index 3f60b2d61fd6..332f5a5055fb 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/GroupRest.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/GroupRest.java @@ -18,18 +18,9 @@ */ @JsonIgnoreProperties(ignoreUnknown = true) @LinksRest(links = { - @LinkRest( - name = GroupRest.SUBGROUPS, - method = "getGroups" - ), - @LinkRest( - name = GroupRest.EPERSONS, - method = "getMembers" - ), - @LinkRest( - name = GroupRest.OBJECT, - method = "getParentObject" - ) + @LinkRest(name = GroupRest.SUBGROUPS, method = "getGroups"), + @LinkRest(name = GroupRest.EPERSONS, method = "getMembers"), + @LinkRest(name = GroupRest.OBJECT, method = "getParentObject") }) public class GroupRest extends DSpaceObjectRest { public static final String NAME = "group"; diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/ItemRest.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/ItemRest.java index 1254ef8f9372..bd0530be5dc4 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/ItemRest.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/ItemRest.java @@ -17,42 +17,15 @@ * @author Andrea Bollini (andrea.bollini at 4science.it) */ @LinksRest(links = { - @LinkRest( - name = ItemRest.ACCESS_STATUS, - method = "getAccessStatus" - ), - @LinkRest( - name = ItemRest.BUNDLES, - method = "getBundles" - ), - @LinkRest( - name = ItemRest.IDENTIFIERS, - method = "getIdentifiers" - ), - @LinkRest( - name = ItemRest.MAPPED_COLLECTIONS, - method = "getMappedCollections" - ), - @LinkRest( - name = ItemRest.OWNING_COLLECTION, - method = "getOwningCollection" - ), - @LinkRest( - name = ItemRest.RELATIONSHIPS, - method = "getRelationships" - ), - @LinkRest( - name = ItemRest.VERSION, - method = "getItemVersion" - ), - @LinkRest( - name = ItemRest.TEMPLATE_ITEM_OF, - method = "getTemplateItemOf" - ), - @LinkRest( - name = ItemRest.THUMBNAIL, - method = "getThumbnail" - ) + @LinkRest(name = ItemRest.ACCESS_STATUS, method = "getAccessStatus"), + @LinkRest(name = ItemRest.BUNDLES, method = "getBundles"), + @LinkRest(name = ItemRest.IDENTIFIERS, method = "getIdentifiers"), + @LinkRest(name = ItemRest.MAPPED_COLLECTIONS, method = "getMappedCollections"), + @LinkRest(name = ItemRest.OWNING_COLLECTION, method = "getOwningCollection"), + @LinkRest(name = ItemRest.RELATIONSHIPS, method = "getRelationships"), + @LinkRest(name = ItemRest.VERSION, method = "getItemVersion"), + @LinkRest(name = ItemRest.TEMPLATE_ITEM_OF, method = "getTemplateItemOf"), + @LinkRest(name = ItemRest.THUMBNAIL, method = "getThumbnail") }) public class ItemRest extends DSpaceObjectRest { public static final String NAME = "item"; diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/OrcidHistoryRest.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/OrcidHistoryRest.java index 02e0f4706208..abfc0a31dc6a 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/OrcidHistoryRest.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/OrcidHistoryRest.java @@ -38,7 +38,7 @@ public class OrcidHistoryRest extends BaseObjectRest { private String responseMessage; - public OrcidHistoryRest(){} + public OrcidHistoryRest() {} @Override @JsonProperty(access = JsonProperty.Access.READ_ONLY) diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/PoolTaskRest.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/PoolTaskRest.java index c32c2c95783a..320558f94671 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/PoolTaskRest.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/PoolTaskRest.java @@ -17,10 +17,7 @@ * @author Andrea Bollini (andrea.bollini at 4science.it) */ @LinksRest(links = { - @LinkRest( - name = PoolTaskRest.STEP, - method = "getStep" - ) + @LinkRest(name = PoolTaskRest.STEP, method = "getStep") }) public class PoolTaskRest extends BaseObjectRest { public static final String NAME = "pooltask"; diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/ProcessRest.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/ProcessRest.java index 8216e1617195..f7d8088d8a30 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/ProcessRest.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/ProcessRest.java @@ -21,18 +21,9 @@ * This class serves as a REST representation for the {@link Process} class */ @LinksRest(links = { - @LinkRest( - name = ProcessRest.FILES, - method = "getFilesFromProcess" - ), - @LinkRest( - name = ProcessRest.FILE_TYPES, - method = "getFileTypesFromProcess" - ), - @LinkRest( - name = ProcessRest.OUTPUT, - method = "getOutputFromProcess" - ) + @LinkRest(name = ProcessRest.FILES, method = "getFilesFromProcess"), + @LinkRest(name = ProcessRest.FILE_TYPES, method = "getFileTypesFromProcess"), + @LinkRest(name = ProcessRest.OUTPUT, method = "getOutputFromProcess") }) public class ProcessRest extends BaseObjectRest { public static final String NAME = "process"; diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/RelationshipRest.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/RelationshipRest.java index dd35a0726e9d..d4050cda9a34 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/RelationshipRest.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/RelationshipRest.java @@ -19,10 +19,7 @@ * Refer to {@link org.dspace.content.Relationship} for explanation about the properties */ @LinksRest(links = { - @LinkRest( - name = RelationshipRest.RELATIONSHIP_TYPE, - method = "getRelationshipType" - ) + @LinkRest(name = RelationshipRest.RELATIONSHIP_TYPE, method = "getRelationshipType") }) public class RelationshipRest extends BaseObjectRest { public static final String NAME = "relationship"; diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/ResearcherProfileRest.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/ResearcherProfileRest.java index 4224cfeeb924..1ebde12e854f 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/ResearcherProfileRest.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/ResearcherProfileRest.java @@ -20,8 +20,8 @@ * */ @LinksRest(links = { - @LinkRest(name = ResearcherProfileRest.ITEM, method = "getItem"), - @LinkRest(name = ResearcherProfileRest.EPERSON, method = "getEPerson") + @LinkRest(name = ResearcherProfileRest.ITEM, method = "getItem"), + @LinkRest(name = ResearcherProfileRest.EPERSON, method = "getEPerson") }) public class ResearcherProfileRest extends BaseObjectRest { diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/VersionHistoryRest.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/VersionHistoryRest.java index e93e131aadb7..51d926082e33 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/VersionHistoryRest.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/VersionHistoryRest.java @@ -13,14 +13,8 @@ * The REST object for the {@link org.dspace.versioning.VersionHistory} object */ @LinksRest(links = { - @LinkRest( - name = VersionHistoryRest.VERSIONS, - method = "getVersions" - ), - @LinkRest( - name = VersionHistoryRest.DRAFT_VERSION, - method = "getDraftVersion" - ) + @LinkRest(name = VersionHistoryRest.VERSIONS, method = "getVersions"), + @LinkRest(name = VersionHistoryRest.DRAFT_VERSION, method = "getDraftVersion") }) public class VersionHistoryRest extends BaseObjectRest { diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/VersionRest.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/VersionRest.java index abdc64295fdc..b4a7ce2b016c 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/VersionRest.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/VersionRest.java @@ -16,14 +16,8 @@ * The REST object for the {@link org.dspace.versioning.Version} objects */ @LinksRest(links = { - @LinkRest( - name = VersionRest.VERSION_HISTORY, - method = "getVersionHistory" - ), - @LinkRest( - name = VersionRest.ITEM, - method = "getVersionItem" - ) + @LinkRest(name = VersionRest.VERSION_HISTORY, method = "getVersionHistory"), + @LinkRest(name = VersionRest.ITEM, method = "getVersionItem") }) public class VersionRest extends BaseObjectRest { diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/VocabularyEntryDetailsRest.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/VocabularyEntryDetailsRest.java index 30e5eb71cbff..964c67dbbe8e 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/VocabularyEntryDetailsRest.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/VocabularyEntryDetailsRest.java @@ -18,9 +18,9 @@ * @author Andrea Bollini (andrea.bollini at 4science.it) */ @LinksRest(links = { - @LinkRest(name = VocabularyEntryDetailsRest.PARENT, method = "getParent"), - @LinkRest(name = VocabularyEntryDetailsRest.CHILDREN, method = "getChildren") - }) + @LinkRest(name = VocabularyEntryDetailsRest.PARENT, method = "getParent"), + @LinkRest(name = VocabularyEntryDetailsRest.CHILDREN, method = "getChildren") +}) public class VocabularyEntryDetailsRest extends BaseObjectRest { public static final String PLURAL_NAME = "vocabularyEntryDetails"; public static final String NAME = "vocabularyEntryDetail"; diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/VocabularyRest.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/VocabularyRest.java index cc848b945b2f..45d13076acb2 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/VocabularyRest.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/VocabularyRest.java @@ -15,9 +15,7 @@ * @author Andrea Bollini (andrea.bollini at 4science.it) */ @LinksRest(links = { - @LinkRest(name = VocabularyRest.ENTRIES, - method = "filter" - ), + @LinkRest(name = VocabularyRest.ENTRIES, method = "filter"), }) public class VocabularyRest extends BaseObjectRest { diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/WorkflowDefinitionRest.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/WorkflowDefinitionRest.java index 7c2de7071bde..9ea4e2ae8c22 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/WorkflowDefinitionRest.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/WorkflowDefinitionRest.java @@ -18,14 +18,8 @@ * @author Maria Verdonck (Atmire) on 11/12/2019 */ @LinksRest(links = { - @LinkRest( - name = WorkflowDefinitionRest.COLLECTIONS_MAPPED_TO, - method = "getCollections" - ), - @LinkRest( - name = WorkflowDefinitionRest.STEPS, - method = "getSteps" - ) + @LinkRest(name = WorkflowDefinitionRest.COLLECTIONS_MAPPED_TO, method = "getCollections"), + @LinkRest(name = WorkflowDefinitionRest.STEPS, method = "getSteps") }) public class WorkflowDefinitionRest extends BaseObjectRest { diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/WorkflowItemRest.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/WorkflowItemRest.java index 4a840a0bb77b..e94cfd54644d 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/WorkflowItemRest.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/WorkflowItemRest.java @@ -15,22 +15,10 @@ * @author Andrea Bollini (andrea.bollini at 4science.it) */ @LinksRest(links = { - @LinkRest( - name = WorkflowItemRest.STEP, - method = "getStep" - ), - @LinkRest( - name = WorkflowItemRest.SUBMITTER, - method = "getWorkflowItemSubmitter" - ), - @LinkRest( - name = WorkflowItemRest.ITEM, - method = "getWorkflowItemItem" - ), - @LinkRest( - name = WorkflowItemRest.COLLECTION, - method = "getWorkflowItemCollection" - ) + @LinkRest(name = WorkflowItemRest.STEP, method = "getStep"), + @LinkRest(name = WorkflowItemRest.SUBMITTER, method = "getWorkflowItemSubmitter"), + @LinkRest(name = WorkflowItemRest.ITEM, method = "getWorkflowItemItem"), + @LinkRest(name = WorkflowItemRest.COLLECTION, method = "getWorkflowItemCollection") }) public class WorkflowItemRest extends AInprogressSubmissionRest { public static final String NAME = "workflowitem"; diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/WorkflowStepRest.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/WorkflowStepRest.java index 648cffbca80d..91128fd05a7d 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/WorkflowStepRest.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/WorkflowStepRest.java @@ -18,10 +18,7 @@ * @author Maria Verdonck (Atmire) on 10/01/2020 */ @LinksRest(links = { - @LinkRest( - name = WorkflowStepRest.ACTIONS, - method = "getActions" - ), + @LinkRest(name = WorkflowStepRest.ACTIONS, method = "getActions"), }) public class WorkflowStepRest extends BaseObjectRest { diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/WorkspaceItemRest.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/WorkspaceItemRest.java index b40d1a4494f4..7693771fb9f2 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/WorkspaceItemRest.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/WorkspaceItemRest.java @@ -15,22 +15,10 @@ * @author Andrea Bollini (andrea.bollini at 4science.it) */ @LinksRest(links = { - @LinkRest( - name = WorkspaceItemRest.SUPERVISION_ORDERS, - method = "getSupervisionOrders" - ), - @LinkRest( - name = WorkspaceItemRest.SUBMITTER, - method = "getWorkspaceItemSubmitter" - ), - @LinkRest( - name = WorkspaceItemRest.ITEM, - method = "getWorkspaceItemItem" - ), - @LinkRest( - name = WorkspaceItemRest.COLLECTION, - method = "getWorkspaceItemCollection" - ) + @LinkRest(name = WorkspaceItemRest.SUPERVISION_ORDERS, method = "getSupervisionOrders"), + @LinkRest(name = WorkspaceItemRest.SUBMITTER, method = "getWorkspaceItemSubmitter"), + @LinkRest(name = WorkspaceItemRest.ITEM, method = "getWorkspaceItemItem"), + @LinkRest(name = WorkspaceItemRest.COLLECTION, method = "getWorkspaceItemCollection") }) public class WorkspaceItemRest extends AInprogressSubmissionRest { public static final String NAME = "workspaceitem"; diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/utils/RegexUtils.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/utils/RegexUtils.java index 8739f6b8d57f..856e36457057 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/utils/RegexUtils.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/utils/RegexUtils.java @@ -13,7 +13,7 @@ */ public class RegexUtils { - private RegexUtils(){} + private RegexUtils() {} /** * Regular expression in the request mapping to accept UUID as identifier diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/matcher/RegistrationMatcher.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/matcher/RegistrationMatcher.java index a154091a2eff..2a4cee8375be 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/matcher/RegistrationMatcher.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/matcher/RegistrationMatcher.java @@ -17,7 +17,7 @@ public class RegistrationMatcher { - private RegistrationMatcher(){} + private RegistrationMatcher() {} public static Matcher matchRegistration(String email, UUID epersonUuid) { return allOf( From 5d7b42603d5d68bbffef0226fca3116e7029e401 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Tue, 3 Dec 2024 09:34:02 -0600 Subject: [PATCH 315/979] Add newly required "should-stop" flag to errorprone config. See https://errorprone.info/docs/installation --- pom.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/pom.xml b/pom.xml index f496ed7e879d..f3b09f3a9dd9 100644 --- a/pom.xml +++ b/pom.xml @@ -147,6 +147,7 @@ true -XDcompilePolicy=simple + --should-stop=ifError=FLOW -Xplugin:ErrorProne -J--add-exports=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED -J--add-exports=jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED From f7ee509423591c33621e9b6abf80135c08f16522 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Tue, 3 Dec 2024 09:44:56 -0600 Subject: [PATCH 316/979] Fix duplicate code warning from errorprone. This "else if" clause is the same as the "else" and can be removed --- .../src/main/java/org/dspace/discovery/SolrServiceImpl.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/discovery/SolrServiceImpl.java b/dspace-api/src/main/java/org/dspace/discovery/SolrServiceImpl.java index 9339b574b578..34efe96ae7f8 100644 --- a/dspace-api/src/main/java/org/dspace/discovery/SolrServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/discovery/SolrServiceImpl.java @@ -1445,8 +1445,6 @@ protected String transformFacetField(DiscoverFacetField facetFieldConfig, String } else { return field + "_acid"; } - } else if (facetFieldConfig.getType().equals(DiscoveryConfigurationParameters.TYPE_STANDARD)) { - return field; } else { return field; } From b98696f4a7cbf5d1065fa18e22f025334f263b03 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Tue, 14 Jan 2025 14:46:27 -0600 Subject: [PATCH 317/979] Dependency convergence fix --- dspace-api/pom.xml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/dspace-api/pom.xml b/dspace-api/pom.xml index 58bd8106e9af..dbc21d14ecb0 100644 --- a/dspace-api/pom.xml +++ b/dspace-api/pom.xml @@ -756,6 +756,11 @@ org.javassist javassist + + + org.yaml + snakeyaml + From 8c88e215b4741ce04ff54c1a0581fd50d50a4e15 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Tue, 14 Jan 2025 16:23:21 -0600 Subject: [PATCH 318/979] Tell Spring Boot to use the simple HttpURLConnection for RestClient, like in Spring boot 3.3 --- .../src/main/resources/application.properties | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/dspace-server-webapp/src/main/resources/application.properties b/dspace-server-webapp/src/main/resources/application.properties index 8233298ef0b0..0d9b9cd836ec 100644 --- a/dspace-server-webapp/src/main/resources/application.properties +++ b/dspace-server-webapp/src/main/resources/application.properties @@ -135,3 +135,15 @@ spring.servlet.multipart.max-file-size = 512MB # Maximum size of a multipart request (i.e. max total size of all files in one request) (default = 10MB) spring.servlet.multipart.max-request-size = 512MB + +################################## +# Spring Boot's HTTP Client configuration (for RestClient and RestTemplate) +# https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-3.4-Release-Notes#restclient-and-resttemplate +# +# "simple" tells Spring Boot to use JDK's HttpURLConnection (SimpleClientHttpRequestFactory) +# We have to configure this explicitly for DSpace because Spring Boot's autoconfiguration will attempt to +# use Jetty or similar if found on the classpath (and Jetty is on the classpath for Handle Server, etc) +spring.http.client.factory = simple +# "dont_follow" tells Spring Boot not to follow any redirects itself, but instead return the 3xx code to +# the user's browser. +spring.http.client.redirects = dont_follow \ No newline at end of file From 745e9c468a083b916f89682f414126e0f044bc7c Mon Sep 17 00:00:00 2001 From: Yana De Pauw Date: Thu, 16 Jan 2025 12:01:13 +0100 Subject: [PATCH 319/979] 124362: Fix issue with the VersionedHandleIdentifierProviderWithCanonicalHandles and creating communities / collections --- ...dentifierProviderWithCanonicalHandles.java | 38 ++++++++++++------- .../config/spring/api/identifier-service.xml | 9 ++--- 2 files changed, 29 insertions(+), 18 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/identifier/VersionedHandleIdentifierProviderWithCanonicalHandles.java b/dspace-api/src/main/java/org/dspace/identifier/VersionedHandleIdentifierProviderWithCanonicalHandles.java index 78ad6b7b79bb..7be5cbc0d8a8 100644 --- a/dspace-api/src/main/java/org/dspace/identifier/VersionedHandleIdentifierProviderWithCanonicalHandles.java +++ b/dspace-api/src/main/java/org/dspace/identifier/VersionedHandleIdentifierProviderWithCanonicalHandles.java @@ -15,11 +15,14 @@ import org.apache.commons.lang3.StringUtils; import org.apache.logging.log4j.Logger; import org.dspace.authorize.AuthorizeException; +import org.dspace.content.Collection; +import org.dspace.content.Community; import org.dspace.content.DSpaceObject; import org.dspace.content.Item; import org.dspace.content.MetadataSchemaEnum; import org.dspace.content.MetadataValue; -import org.dspace.content.service.ItemService; +import org.dspace.content.factory.ContentServiceFactory; +import org.dspace.content.service.DSpaceObjectService; import org.dspace.core.Constants; import org.dspace.core.Context; import org.dspace.core.LogHelper; @@ -64,9 +67,6 @@ public class VersionedHandleIdentifierProviderWithCanonicalHandles extends Ident @Autowired(required = true) private HandleService handleService; - @Autowired(required = true) - private ItemService itemService; - /** * After all the properties are set check that the versioning is enabled * @@ -173,6 +173,16 @@ public String register(Context context, DSpaceObject dso) { throw new RuntimeException("The current user is not authorized to change this item.", ex); } } + if (dso instanceof Collection || dso instanceof Community) { + try { + // Update the metadata with the handle for collections and communities. + modifyHandleMetadata(context, dso, getCanonical(id)); + } catch (SQLException ex) { + throw new RuntimeException("A problem with the database connection occured.", ex); + } catch (AuthorizeException ex) { + throw new RuntimeException("The current user is not authorized to change this item.", ex); + } + } return id; } @@ -490,27 +500,29 @@ protected String getCanonical(String identifier) { * Remove all handles from an item's metadata and add the supplied handle instead. * * @param context The relevant DSpace Context. - * @param item which item to modify + * @param dso which dso to modify * @param handle which handle to add * @throws SQLException if database error * @throws AuthorizeException if authorization error */ - protected void modifyHandleMetadata(Context context, Item item, String handle) + protected void modifyHandleMetadata(Context context, DSpaceObject dso, String handle) throws SQLException, AuthorizeException { // we want to exchange the old handle against the new one. To do so, we // load all identifiers, clear the metadata field, re add all // identifiers which are not from type handle and add the new handle. String handleref = handleService.getCanonicalForm(handle); - List identifiers = itemService - .getMetadata(item, MetadataSchemaEnum.DC.getName(), "identifier", "uri", Item.ANY); - itemService.clearMetadata(context, item, MetadataSchemaEnum.DC.getName(), "identifier", "uri", Item.ANY); + DSpaceObjectService dSpaceObjectService = + ContentServiceFactory.getInstance().getDSpaceObjectService(dso); + List identifiers = dSpaceObjectService + .getMetadata(dso, MetadataSchemaEnum.DC.getName(), "identifier", "uri", Item.ANY); + dSpaceObjectService.clearMetadata(context, dso, MetadataSchemaEnum.DC.getName(), "identifier", "uri", Item.ANY); for (MetadataValue identifier : identifiers) { if (this.supports(identifier.getValue())) { // ignore handles continue; } - itemService.addMetadata(context, - item, + dSpaceObjectService.addMetadata(context, + dso, identifier.getMetadataField(), identifier.getLanguage(), identifier.getValue(), @@ -518,9 +530,9 @@ protected void modifyHandleMetadata(Context context, Item item, String handle) identifier.getConfidence()); } if (!StringUtils.isEmpty(handleref)) { - itemService.addMetadata(context, item, MetadataSchemaEnum.DC.getName(), + dSpaceObjectService.addMetadata(context, dso, MetadataSchemaEnum.DC.getName(), "identifier", "uri", null, handleref); } - itemService.update(context, item); + dSpaceObjectService.update(context, dso); } } diff --git a/dspace/config/spring/api/identifier-service.xml b/dspace/config/spring/api/identifier-service.xml index 0c58cc1de932..9e599527444a 100644 --- a/dspace/config/spring/api/identifier-service.xml +++ b/dspace/config/spring/api/identifier-service.xml @@ -17,9 +17,9 @@ The VersionedHandleIdentifierProvider creates a new versioned handle for every new version. --> - - - + + + - - - - + + + - - - - + + + + - + - - - - + + + + - + 2.16.0 2.16.0 From c657f7770f30309b1656018771dd44e28b8c1c02 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Fri, 17 Jan 2025 14:12:38 -0600 Subject: [PATCH 330/979] Sync a few dependency exclusions from dspace-8_x to dspace-7_x to fix convergence issues --- dspace-server-webapp/pom.xml | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/dspace-server-webapp/pom.xml b/dspace-server-webapp/pom.xml index 35fa473fc170..9f630cf60df9 100644 --- a/dspace-server-webapp/pom.xml +++ b/dspace-server-webapp/pom.xml @@ -316,6 +316,13 @@ org.webjars.bowergithub.codeseven toastr 2.1.4 + + + + org.webjars.bowergithub.jquery + jquery-dist + + @@ -337,6 +344,13 @@ org.webjars.bowergithub.jashkenas backbone 1.4.1 + + + + org.webjars.bowergithub.jashkenas + underscore + + + + net.minidev + json-smart + + + + + + + net.minidev + json-smart + 2.5.0 From 4c46bc33be2692684ca87cb572c628dfca544ae2 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Fri, 17 Jan 2025 15:07:10 -0600 Subject: [PATCH 331/979] Remove unused dependency --- dspace-sword/pom.xml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/dspace-sword/pom.xml b/dspace-sword/pom.xml index 12325533c5c5..c48266454604 100644 --- a/dspace-sword/pom.xml +++ b/dspace-sword/pom.xml @@ -36,11 +36,6 @@ - - org.dspace - dspace-api-lang - - org.springframework.boot From 1fc7587ddd64f02026c13d7f946a34092b23559b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 17 Jan 2025 21:12:07 +0000 Subject: [PATCH 332/979] Bump de.digitalcollections.iiif:iiif-apis from 0.3.10 to 0.3.11 Bumps [de.digitalcollections.iiif:iiif-apis](https://github.com/dbmdz/iiif-apis) from 0.3.10 to 0.3.11. - [Release notes](https://github.com/dbmdz/iiif-apis/releases) - [Commits](https://github.com/dbmdz/iiif-apis/compare/0.3.10...0.3.11) --- updated-dependencies: - dependency-name: de.digitalcollections.iiif:iiif-apis dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- dspace-iiif/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dspace-iiif/pom.xml b/dspace-iiif/pom.xml index 030ac31b3843..e1d6c4cbfbe4 100644 --- a/dspace-iiif/pom.xml +++ b/dspace-iiif/pom.xml @@ -111,7 +111,7 @@ de.digitalcollections.iiif iiif-apis - 0.3.10 + 0.3.11 org.javassist From 311880207a48daad3a7b7c3dbbd821c86d80f1a0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 17 Jan 2025 21:12:10 +0000 Subject: [PATCH 333/979] Bump com.opencsv:opencsv from 5.9 to 5.10 Bumps com.opencsv:opencsv from 5.9 to 5.10. --- updated-dependencies: - dependency-name: com.opencsv:opencsv dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- dspace-api/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dspace-api/pom.xml b/dspace-api/pom.xml index 2f0e20ed228f..ea5eb69d3721 100644 --- a/dspace-api/pom.xml +++ b/dspace-api/pom.xml @@ -777,7 +777,7 @@ com.opencsv opencsv - 5.9 + 5.10 From 2e1bcc6ded0c340517ef2a9253854d9fd04069f0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 17 Jan 2025 21:12:20 +0000 Subject: [PATCH 334/979] Bump com.amazonaws:aws-java-sdk-s3 from 1.12.261 to 1.12.780 Bumps [com.amazonaws:aws-java-sdk-s3](https://github.com/aws/aws-sdk-java) from 1.12.261 to 1.12.780. - [Changelog](https://github.com/aws/aws-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java/compare/1.12.261...1.12.780) --- updated-dependencies: - dependency-name: com.amazonaws:aws-java-sdk-s3 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- dspace-api/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dspace-api/pom.xml b/dspace-api/pom.xml index 2f0e20ed228f..f239c380e6bc 100644 --- a/dspace-api/pom.xml +++ b/dspace-api/pom.xml @@ -741,7 +741,7 @@ com.amazonaws aws-java-sdk-s3 - 1.12.261 + 1.12.780 9.4.55.v20240627 2.23.1 - 2.0.31 + 2.0.33 1.19.0 2.0.11 2.9.2 From 6007753cc7b122ca69b7208244107422f7a1e2fa Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 17 Jan 2025 21:12:30 +0000 Subject: [PATCH 336/979] Bump org.apache.ant:ant from 1.10.14 to 1.10.15 Bumps org.apache.ant:ant from 1.10.14 to 1.10.15. --- updated-dependencies: - dependency-name: org.apache.ant:ant dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index b45d029bd361..13536fcd80af 100644 --- a/pom.xml +++ b/pom.xml @@ -1380,7 +1380,7 @@ org.apache.ant ant - 1.10.14 + 1.10.15 org.apache.jena From f38fbe666e3f66248d71f981bba4dc6050b60168 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 17 Jan 2025 21:12:30 +0000 Subject: [PATCH 337/979] Bump slf4j.version from 2.0.11 to 2.0.16 Bumps `slf4j.version` from 2.0.11 to 2.0.16. Updates `org.slf4j:jcl-over-slf4j` from 2.0.11 to 2.0.16 Updates `org.slf4j:slf4j-api` from 2.0.11 to 2.0.16 --- updated-dependencies: - dependency-name: org.slf4j:jcl-over-slf4j dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.slf4j:slf4j-api dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index b45d029bd361..73a162a07abb 100644 --- a/pom.xml +++ b/pom.xml @@ -42,7 +42,7 @@ 2.23.1 2.0.31 1.19.0 - 2.0.11 + 2.0.16 2.9.2 1.78.1 From 2b5db943b228e9fb6efe286743669604a644c29d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 17 Jan 2025 21:12:56 +0000 Subject: [PATCH 338/979] Bump org.scala-lang:scala-library from 2.13.11 to 2.13.16 Bumps [org.scala-lang:scala-library](https://github.com/scala/scala) from 2.13.11 to 2.13.16. - [Release notes](https://github.com/scala/scala/releases) - [Commits](https://github.com/scala/scala/compare/v2.13.11...v2.13.16) --- updated-dependencies: - dependency-name: org.scala-lang:scala-library dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- dspace-api/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dspace-api/pom.xml b/dspace-api/pom.xml index c42895ab4c49..943e0d6d005e 100644 --- a/dspace-api/pom.xml +++ b/dspace-api/pom.xml @@ -943,7 +943,7 @@ org.scala-lang scala-library - 2.13.11 + 2.13.16 test From bd16f7a9821e1acfbdf5691ed3a9f2562533159e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 17 Jan 2025 21:13:00 +0000 Subject: [PATCH 339/979] Bump org.apache.bcel:bcel from 6.7.0 to 6.10.0 Bumps [org.apache.bcel:bcel](https://github.com/apache/commons-bcel) from 6.7.0 to 6.10.0. - [Changelog](https://github.com/apache/commons-bcel/blob/master/RELEASE-NOTES.txt) - [Commits](https://github.com/apache/commons-bcel/compare/rel/commons-bcel-6.7.0...rel/commons-bcel-6.10.0) --- updated-dependencies: - dependency-name: org.apache.bcel:bcel dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- dspace-api/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dspace-api/pom.xml b/dspace-api/pom.xml index c42895ab4c49..17031911579f 100644 --- a/dspace-api/pom.xml +++ b/dspace-api/pom.xml @@ -823,7 +823,7 @@ org.apache.bcel bcel - 6.7.0 + 6.10.0 test From 80e39f7669edf1fbe195d2860845ca1b04ab383f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 17 Jan 2025 21:13:06 +0000 Subject: [PATCH 340/979] Bump log4j.version from 2.23.1 to 2.24.3 Bumps `log4j.version` from 2.23.1 to 2.24.3. Updates `org.apache.logging.log4j:log4j-api` from 2.23.1 to 2.24.3 Updates `org.apache.logging.log4j:log4j-core` from 2.23.1 to 2.24.3 Updates `org.apache.logging.log4j:log4j-1.2-api` from 2.23.1 to 2.24.3 --- updated-dependencies: - dependency-name: org.apache.logging.log4j:log4j-api dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: org.apache.logging.log4j:log4j-core dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: org.apache.logging.log4j:log4j-1.2-api dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 2a97e8767bf9..fcf03e61655a 100644 --- a/pom.xml +++ b/pom.xml @@ -38,7 +38,7 @@ 1.1.1 9.4.57.v20241219 - 2.23.1 + 2.24.3 2.0.31 1.19.0 1.7.36 From 492f37307b32250ae41a78109ed7260083a08a67 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Fri, 17 Jan 2025 16:55:18 -0600 Subject: [PATCH 341/979] Fix startup bug by upgrading gson --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index a8d4dffc8ba8..6a911b008857 100644 --- a/pom.xml +++ b/pom.xml @@ -1351,7 +1351,7 @@ com.google.code.gson gson - 2.10.1 + 2.11.0 @@ -882,32 +882,32 @@ io.netty netty-buffer - 4.1.106.Final + 4.1.117.Final io.netty netty-transport - 4.1.106.Final + 4.1.117.Final io.netty netty-transport-native-unix-common - 4.1.106.Final + 4.1.117.Final io.netty netty-common - 4.1.106.Final + 4.1.117.Final io.netty netty-handler - 4.1.106.Final + 4.1.117.Final io.netty netty-codec - 4.1.106.Final + 4.1.117.Final org.apache.velocity diff --git a/pom.xml b/pom.xml index 523f50c38308..a92a91c81223 100644 --- a/pom.xml +++ b/pom.xml @@ -1685,7 +1685,7 @@ com.h2database h2 - 2.2.224 + 2.3.232 test From 9dcc4e6f7ce32e22c8492b3429077f1a991f1d1f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 20 Jan 2025 22:43:38 +0000 Subject: [PATCH 346/979] Bump the spring group with 4 updates Bumps the spring group with 4 updates: [org.springframework.security:spring-security-test](https://github.com/spring-projects/spring-security), [org.springframework.security:spring-security-core](https://github.com/spring-projects/spring-security), [org.springframework.security:spring-security-web](https://github.com/spring-projects/spring-security) and [org.springframework.security:spring-security-config](https://github.com/spring-projects/spring-security). Updates `org.springframework.security:spring-security-test` from 5.7.12 to 5.7.14 - [Release notes](https://github.com/spring-projects/spring-security/releases) - [Changelog](https://github.com/spring-projects/spring-security/blob/main/RELEASE.adoc) - [Commits](https://github.com/spring-projects/spring-security/compare/5.7.12...5.7.14) Updates `org.springframework.security:spring-security-core` from 5.7.12 to 5.7.14 - [Release notes](https://github.com/spring-projects/spring-security/releases) - [Changelog](https://github.com/spring-projects/spring-security/blob/main/RELEASE.adoc) - [Commits](https://github.com/spring-projects/spring-security/compare/5.7.12...5.7.14) Updates `org.springframework.security:spring-security-web` from 5.7.12 to 5.7.14 - [Release notes](https://github.com/spring-projects/spring-security/releases) - [Changelog](https://github.com/spring-projects/spring-security/blob/main/RELEASE.adoc) - [Commits](https://github.com/spring-projects/spring-security/compare/5.7.12...5.7.14) Updates `org.springframework.security:spring-security-config` from 5.7.12 to 5.7.14 - [Release notes](https://github.com/spring-projects/spring-security/releases) - [Changelog](https://github.com/spring-projects/spring-security/blob/main/RELEASE.adoc) - [Commits](https://github.com/spring-projects/spring-security/compare/5.7.12...5.7.14) Updates `org.springframework.security:spring-security-core` from 5.7.12 to 5.7.14 - [Release notes](https://github.com/spring-projects/spring-security/releases) - [Changelog](https://github.com/spring-projects/spring-security/blob/main/RELEASE.adoc) - [Commits](https://github.com/spring-projects/spring-security/compare/5.7.12...5.7.14) Updates `org.springframework.security:spring-security-web` from 5.7.12 to 5.7.14 - [Release notes](https://github.com/spring-projects/spring-security/releases) - [Changelog](https://github.com/spring-projects/spring-security/blob/main/RELEASE.adoc) - [Commits](https://github.com/spring-projects/spring-security/compare/5.7.12...5.7.14) Updates `org.springframework.security:spring-security-config` from 5.7.12 to 5.7.14 - [Release notes](https://github.com/spring-projects/spring-security/releases) - [Changelog](https://github.com/spring-projects/spring-security/blob/main/RELEASE.adoc) - [Commits](https://github.com/spring-projects/spring-security/compare/5.7.12...5.7.14) --- updated-dependencies: - dependency-name: org.springframework.security:spring-security-test dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.security:spring-security-core dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.security:spring-security-web dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.security:spring-security-config dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.security:spring-security-core dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.security:spring-security-web dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.security:spring-security-config dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 523f50c38308..40870b0b777f 100644 --- a/pom.xml +++ b/pom.xml @@ -21,7 +21,7 @@ 11 5.3.39 2.7.18 - 5.7.12 + 5.7.14 5.6.15.Final 6.2.5.Final 42.7.3 From bf1aa3da7aabff7825b57e74851649f7d12c5c24 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 20 Jan 2025 22:43:50 +0000 Subject: [PATCH 347/979] Bump solr.client.version from 8.11.3 to 8.11.4 Bumps `solr.client.version` from 8.11.3 to 8.11.4. Updates `org.apache.solr:solr-solrj` from 8.11.3 to 8.11.4 Updates `org.apache.lucene:lucene-core` from 8.11.3 to 8.11.4 Updates `org.apache.lucene:lucene-analyzers-icu` from 8.11.3 to 8.11.4 Updates `org.apache.lucene:lucene-analyzers-smartcn` from 8.11.3 to 8.11.4 Updates `org.apache.lucene:lucene-analyzers-stempel` from 8.11.3 to 8.11.4 Updates `org.apache.solr:solr-core` from 8.11.3 to 8.11.4 --- updated-dependencies: - dependency-name: org.apache.solr:solr-solrj dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.apache.lucene:lucene-core dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.apache.lucene:lucene-analyzers-icu dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.apache.lucene:lucene-analyzers-smartcn dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.apache.lucene:lucene-analyzers-stempel dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.apache.solr:solr-core dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 523f50c38308..57cd8066aa97 100644 --- a/pom.xml +++ b/pom.xml @@ -25,7 +25,7 @@ 5.6.15.Final 6.2.5.Final 42.7.3 - 8.11.3 + 8.11.4 3.10.8 2.31.0 From 8076745a09236ebd6636f1c6c83d906ee6cec0cd Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 20 Jan 2025 22:43:53 +0000 Subject: [PATCH 348/979] Bump io.swagger:swagger-core from 1.6.2 to 1.6.15 Bumps [io.swagger:swagger-core](https://github.com/swagger-api/swagger-core) from 1.6.2 to 1.6.15. - [Release notes](https://github.com/swagger-api/swagger-core/releases) - [Commits](https://github.com/swagger-api/swagger-core/compare/v1.6.2...v1.6.15) --- updated-dependencies: - dependency-name: io.swagger:swagger-core dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- dspace-api/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dspace-api/pom.xml b/dspace-api/pom.xml index cf81408a2659..2ad962d88a3d 100644 --- a/dspace-api/pom.xml +++ b/dspace-api/pom.xml @@ -938,7 +938,7 @@ io.swagger swagger-core - 1.6.2 + 1.6.15 org.scala-lang From 88c094051e4d8223803661598859f217f8e61656 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 20 Jan 2025 23:04:51 +0000 Subject: [PATCH 349/979] Bump com.github.spotbugs:spotbugs in the build-tools group Bumps the build-tools group with 1 update: [com.github.spotbugs:spotbugs](https://github.com/spotbugs/spotbugs). Updates `com.github.spotbugs:spotbugs` from 4.8.6 to 4.9.0 - [Release notes](https://github.com/spotbugs/spotbugs/releases) - [Changelog](https://github.com/spotbugs/spotbugs/blob/master/CHANGELOG.md) - [Commits](https://github.com/spotbugs/spotbugs/compare/4.8.6...4.9.0) --- updated-dependencies: - dependency-name: com.github.spotbugs:spotbugs dependency-type: direct:production update-type: version-update:semver-minor dependency-group: build-tools ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index b45d029bd361..311cde285f92 100644 --- a/pom.xml +++ b/pom.xml @@ -313,7 +313,7 @@ com.github.spotbugs spotbugs - 4.8.6 + 4.9.0 From 4e73138c15151a94871cda778aa546f0e8b0413c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 21 Jan 2025 19:07:26 +0000 Subject: [PATCH 350/979] Bump the spring group across 1 directory with 12 updates Bumps the spring group with 12 updates in the / directory: | Package | From | To | | --- | --- | --- | | [org.springframework:spring-orm](https://github.com/spring-projects/spring-framework) | `6.2.1` | `6.2.2` | | [org.springframework:spring-core](https://github.com/spring-projects/spring-framework) | `6.2.1` | `6.2.2` | | [org.springframework:spring-beans](https://github.com/spring-projects/spring-framework) | `6.2.1` | `6.2.2` | | [org.springframework:spring-aop](https://github.com/spring-projects/spring-framework) | `6.2.1` | `6.2.2` | | [org.springframework:spring-context](https://github.com/spring-projects/spring-framework) | `6.2.1` | `6.2.2` | | [org.springframework:spring-context-support](https://github.com/spring-projects/spring-framework) | `6.2.1` | `6.2.2` | | [org.springframework:spring-tx](https://github.com/spring-projects/spring-framework) | `6.2.1` | `6.2.2` | | [org.springframework:spring-jdbc](https://github.com/spring-projects/spring-framework) | `6.2.1` | `6.2.2` | | [org.springframework:spring-web](https://github.com/spring-projects/spring-framework) | `6.2.1` | `6.2.2` | | [org.springframework:spring-webmvc](https://github.com/spring-projects/spring-framework) | `6.2.1` | `6.2.2` | | [org.springframework:spring-expression](https://github.com/spring-projects/spring-framework) | `6.2.1` | `6.2.2` | | [org.springframework:spring-test](https://github.com/spring-projects/spring-framework) | `6.2.1` | `6.2.2` | Updates `org.springframework:spring-orm` from 6.2.1 to 6.2.2 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.1...v6.2.2) Updates `org.springframework:spring-core` from 6.2.1 to 6.2.2 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.1...v6.2.2) Updates `org.springframework:spring-beans` from 6.2.1 to 6.2.2 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.1...v6.2.2) Updates `org.springframework:spring-aop` from 6.2.1 to 6.2.2 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.1...v6.2.2) Updates `org.springframework:spring-context` from 6.2.1 to 6.2.2 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.1...v6.2.2) Updates `org.springframework:spring-context-support` from 6.2.1 to 6.2.2 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.1...v6.2.2) Updates `org.springframework:spring-tx` from 6.2.1 to 6.2.2 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.1...v6.2.2) Updates `org.springframework:spring-jdbc` from 6.2.1 to 6.2.2 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.1...v6.2.2) Updates `org.springframework:spring-web` from 6.2.1 to 6.2.2 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.1...v6.2.2) Updates `org.springframework:spring-webmvc` from 6.2.1 to 6.2.2 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.1...v6.2.2) Updates `org.springframework:spring-expression` from 6.2.1 to 6.2.2 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.1...v6.2.2) Updates `org.springframework:spring-test` from 6.2.1 to 6.2.2 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.1...v6.2.2) Updates `org.springframework:spring-core` from 6.2.1 to 6.2.2 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.1...v6.2.2) Updates `org.springframework:spring-beans` from 6.2.1 to 6.2.2 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.1...v6.2.2) Updates `org.springframework:spring-aop` from 6.2.1 to 6.2.2 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.1...v6.2.2) Updates `org.springframework:spring-context` from 6.2.1 to 6.2.2 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.1...v6.2.2) Updates `org.springframework:spring-context-support` from 6.2.1 to 6.2.2 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.1...v6.2.2) Updates `org.springframework:spring-tx` from 6.2.1 to 6.2.2 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.1...v6.2.2) Updates `org.springframework:spring-jdbc` from 6.2.1 to 6.2.2 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.1...v6.2.2) Updates `org.springframework:spring-web` from 6.2.1 to 6.2.2 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.1...v6.2.2) Updates `org.springframework:spring-webmvc` from 6.2.1 to 6.2.2 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.1...v6.2.2) Updates `org.springframework:spring-expression` from 6.2.1 to 6.2.2 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.1...v6.2.2) Updates `org.springframework:spring-test` from 6.2.1 to 6.2.2 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.1...v6.2.2) --- updated-dependencies: - dependency-name: org.springframework:spring-orm dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-core dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-beans dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-aop dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-context dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-context-support dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-tx dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-jdbc dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-web dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-webmvc dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-expression dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-test dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-core dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-beans dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-aop dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-context dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-context-support dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-tx dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-jdbc dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-web dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-webmvc dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-expression dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-test dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index d127c52a90ea..1362d83f5738 100644 --- a/pom.xml +++ b/pom.xml @@ -19,7 +19,7 @@ 17 - 6.2.1 + 6.2.2 3.4.1 6.4.2 6.4.8.Final From b3e113900603d8aacbad84ae84543f4e59ad245b Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Tue, 21 Jan 2025 13:41:10 -0600 Subject: [PATCH 351/979] Remove swagger-core as it is unused --- dspace-api/pom.xml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/dspace-api/pom.xml b/dspace-api/pom.xml index 2ad962d88a3d..8071b24b0841 100644 --- a/dspace-api/pom.xml +++ b/dspace-api/pom.xml @@ -935,11 +935,6 @@ validation-api 2.0.1.Final - - io.swagger - swagger-core - 1.6.15 - org.scala-lang scala-library From 7d4a5fc311cad860b18591fbd1d6381b340d5e5a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 21 Jan 2025 23:28:06 +0000 Subject: [PATCH 352/979] Bump bouncycastle.version from 1.78.1 to 1.80 Bumps `bouncycastle.version` from 1.78.1 to 1.80. Updates `org.bouncycastle:bcpkix-jdk18on` from 1.78.1 to 1.80 - [Changelog](https://github.com/bcgit/bc-java/blob/main/docs/releasenotes.html) - [Commits](https://github.com/bcgit/bc-java/commits) Updates `org.bouncycastle:bcprov-jdk18on` from 1.78.1 to 1.80 - [Changelog](https://github.com/bcgit/bc-java/blob/main/docs/releasenotes.html) - [Commits](https://github.com/bcgit/bc-java/commits) Updates `org.bouncycastle:bcutil-jdk18on` from 1.78.1 to 1.80 - [Changelog](https://github.com/bcgit/bc-java/blob/main/docs/releasenotes.html) - [Commits](https://github.com/bcgit/bc-java/commits) --- updated-dependencies: - dependency-name: org.bouncycastle:bcpkix-jdk18on dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: org.bouncycastle:bcprov-jdk18on dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: org.bouncycastle:bcutil-jdk18on dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 3597b20f3311..6bdb1399fd7a 100644 --- a/pom.xml +++ b/pom.xml @@ -44,7 +44,7 @@ 1.7.36 2.9.2 - 1.78.1 + 1.80 From 9705c30f8a811b62b317532f2078e955c81d2548 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 21 Jan 2025 23:28:11 +0000 Subject: [PATCH 353/979] Bump net.bytebuddy:byte-buddy from 1.11.13 to 1.16.1 Bumps [net.bytebuddy:byte-buddy](https://github.com/raphw/byte-buddy) from 1.11.13 to 1.16.1. - [Release notes](https://github.com/raphw/byte-buddy/releases) - [Changelog](https://github.com/raphw/byte-buddy/blob/master/release-notes.md) - [Commits](https://github.com/raphw/byte-buddy/compare/byte-buddy-1.11.13...byte-buddy-1.16.1) --- updated-dependencies: - dependency-name: net.bytebuddy:byte-buddy dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 3597b20f3311..a7d025a57cf6 100644 --- a/pom.xml +++ b/pom.xml @@ -1814,7 +1814,7 @@ net.bytebuddy byte-buddy - 1.11.13 + 1.16.1 From 39e2ef2b304e0ba0b09cba3e67a2c67922c7e5ba Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 21 Jan 2025 23:28:19 +0000 Subject: [PATCH 354/979] Bump pdfbox-version from 2.0.31 to 2.0.33 Bumps `pdfbox-version` from 2.0.31 to 2.0.33. Updates `org.apache.pdfbox:pdfbox` from 2.0.31 to 2.0.33 Updates `org.apache.pdfbox:fontbox` from 2.0.31 to 2.0.33 --- updated-dependencies: - dependency-name: org.apache.pdfbox:pdfbox dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.apache.pdfbox:fontbox dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 3597b20f3311..04cd35050b45 100644 --- a/pom.xml +++ b/pom.xml @@ -39,7 +39,7 @@ 9.4.57.v20241219 2.24.3 - 2.0.31 + 2.0.33 1.19.0 1.7.36 2.9.2 From f547dc7957b1d82e2660de20e34bfde35bdd513e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 21 Jan 2025 23:28:22 +0000 Subject: [PATCH 355/979] Bump bouncycastle.version from 1.78.1 to 1.80 Bumps `bouncycastle.version` from 1.78.1 to 1.80. Updates `org.bouncycastle:bcpkix-jdk18on` from 1.78.1 to 1.80 - [Changelog](https://github.com/bcgit/bc-java/blob/main/docs/releasenotes.html) - [Commits](https://github.com/bcgit/bc-java/commits) Updates `org.bouncycastle:bcprov-jdk18on` from 1.78.1 to 1.80 - [Changelog](https://github.com/bcgit/bc-java/blob/main/docs/releasenotes.html) - [Commits](https://github.com/bcgit/bc-java/commits) Updates `org.bouncycastle:bcutil-jdk18on` from 1.78.1 to 1.80 - [Changelog](https://github.com/bcgit/bc-java/blob/main/docs/releasenotes.html) - [Commits](https://github.com/bcgit/bc-java/commits) --- updated-dependencies: - dependency-name: org.bouncycastle:bcpkix-jdk18on dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: org.bouncycastle:bcprov-jdk18on dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: org.bouncycastle:bcutil-jdk18on dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 7c52f6642ad9..1a9592eefc75 100644 --- a/pom.xml +++ b/pom.xml @@ -45,7 +45,7 @@ 2.0.16 2.9.2 - 1.78.1 + 1.80 8.0.1 3.1.5 From e3abd1c129ed22daea69764f8d451f3ee7ec941d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 21 Jan 2025 23:28:34 +0000 Subject: [PATCH 356/979] Bump org.apache.velocity:velocity-engine-core from 2.3 to 2.4.1 Bumps org.apache.velocity:velocity-engine-core from 2.3 to 2.4.1. --- updated-dependencies: - dependency-name: org.apache.velocity:velocity-engine-core dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- dspace-api/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dspace-api/pom.xml b/dspace-api/pom.xml index 38c490c4dbd9..05a9b1ba8531 100644 --- a/dspace-api/pom.xml +++ b/dspace-api/pom.xml @@ -912,7 +912,7 @@ org.apache.velocity velocity-engine-core - 2.3 + 2.4.1 org.xmlunit From 9bca0c928b5b1f8319bbfdb4786b881636eecdc7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 21 Jan 2025 23:28:37 +0000 Subject: [PATCH 357/979] Bump net.cnri:cnri-servlet-container from 3.0.0 to 3.1.0 Bumps [net.cnri:cnri-servlet-container](https://gitlab.com/cnri/cnri-servlet-container) from 3.0.0 to 3.1.0. - [Commits](https://gitlab.com/cnri/cnri-servlet-container/compare/v3.0.0...v3.1.0) --- updated-dependencies: - dependency-name: net.cnri:cnri-servlet-container dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 3597b20f3311..a2de1a0f5fbb 100644 --- a/pom.xml +++ b/pom.xml @@ -1389,7 +1389,7 @@ net.cnri cnri-servlet-container - 3.0.0 + 3.1.0 From 9d46a45191da6126bf2aa06dda8660ac63c604e3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 21 Jan 2025 23:28:48 +0000 Subject: [PATCH 358/979] Bump org.apache.ant:ant from 1.10.14 to 1.10.15 Bumps org.apache.ant:ant from 1.10.14 to 1.10.15. --- updated-dependencies: - dependency-name: org.apache.ant:ant dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 3597b20f3311..ecdcf1e39237 100644 --- a/pom.xml +++ b/pom.xml @@ -1354,7 +1354,7 @@ org.apache.ant ant - 1.10.14 + 1.10.15 org.apache.jena From cd2fbbf51bbcb19c7e3eda091c00ebb03e7ecb6e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 21 Jan 2025 23:28:54 +0000 Subject: [PATCH 359/979] Bump dnsjava:dnsjava from 3.6.0 to 3.6.2 Bumps [dnsjava:dnsjava](https://github.com/dnsjava/dnsjava) from 3.6.0 to 3.6.2. - [Release notes](https://github.com/dnsjava/dnsjava/releases) - [Changelog](https://github.com/dnsjava/dnsjava/blob/master/Changelog) - [Commits](https://github.com/dnsjava/dnsjava/compare/v3.6.0...v3.6.2) --- updated-dependencies: - dependency-name: dnsjava:dnsjava dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- dspace-api/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dspace-api/pom.xml b/dspace-api/pom.xml index 38c490c4dbd9..7c91f117254b 100644 --- a/dspace-api/pom.xml +++ b/dspace-api/pom.xml @@ -646,7 +646,7 @@ dnsjava dnsjava - 3.6.0 + 3.6.2 From 4900a1a5276b516a4e99da2136fef4f98722b2d9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 21 Jan 2025 23:29:40 +0000 Subject: [PATCH 360/979] Bump org.jboss.logging:jboss-logging from 3.4.3.Final to 3.6.1.Final Bumps [org.jboss.logging:jboss-logging](https://github.com/jboss-logging/jboss-logging) from 3.4.3.Final to 3.6.1.Final. - [Release notes](https://github.com/jboss-logging/jboss-logging/releases) - [Commits](https://github.com/jboss-logging/jboss-logging/compare/3.4.3.Final...3.6.1.Final) --- updated-dependencies: - dependency-name: org.jboss.logging:jboss-logging dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 7c52f6642ad9..06e4c4a7bb09 100644 --- a/pom.xml +++ b/pom.xml @@ -1131,7 +1131,7 @@ org.jboss.logging jboss-logging - 3.4.3.Final + 3.6.1.Final From a10808556578cc551fd8bb35bf08d205101b3a15 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 21 Jan 2025 23:29:44 +0000 Subject: [PATCH 361/979] Bump net.minidev:json-smart from 2.5.0 to 2.5.1 Bumps [net.minidev:json-smart](https://github.com/netplex/json-smart-v2) from 2.5.0 to 2.5.1. - [Release notes](https://github.com/netplex/json-smart-v2/releases) - [Commits](https://github.com/netplex/json-smart-v2/compare/2.5.0...2.5.1) --- updated-dependencies: - dependency-name: net.minidev:json-smart dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- dspace-server-webapp/pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dspace-server-webapp/pom.xml b/dspace-server-webapp/pom.xml index cf0279ec9797..f4b00dcd3a6b 100644 --- a/dspace-server-webapp/pom.xml +++ b/dspace-server-webapp/pom.xml @@ -299,7 +299,7 @@ @@ -560,7 +560,7 @@ net.minidev json-smart - 2.5.0 + 2.5.1 From e4e11cd82a01eacb775da0922e0dc47ada0ba0ed Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 21 Jan 2025 23:29:53 +0000 Subject: [PATCH 362/979] Bump flyway.version from 10.10.0 to 10.22.0 Bumps `flyway.version` from 10.10.0 to 10.22.0. Updates `org.flywaydb:flyway-core` from 10.10.0 to 10.22.0 - [Release notes](https://github.com/flyway/flyway/releases) - [Commits](https://github.com/flyway/flyway/compare/flyway-10.10.0...flyway-10.22.0) Updates `org.flywaydb:flyway-database-postgresql` from 10.10.0 to 10.22.0 --- updated-dependencies: - dependency-name: org.flywaydb:flyway-core dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: org.flywaydb:flyway-database-postgresql dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 7c52f6642ad9..de4ac0dfe6b3 100644 --- a/pom.xml +++ b/pom.xml @@ -25,7 +25,7 @@ 6.4.8.Final 8.0.1.Final 42.7.3 - 10.10.0 + 10.22.0 8.11.4 3.10.8 From b4a22b4e51523706fc6d7cc4ef614553f276823a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 21 Jan 2025 23:30:00 +0000 Subject: [PATCH 363/979] Bump org.apache.httpcomponents.client5:httpclient5 from 5.3.1 to 5.4.1 Bumps [org.apache.httpcomponents.client5:httpclient5](https://github.com/apache/httpcomponents-client) from 5.3.1 to 5.4.1. - [Changelog](https://github.com/apache/httpcomponents-client/blob/rel/v5.4.1/RELEASE_NOTES.txt) - [Commits](https://github.com/apache/httpcomponents-client/compare/rel/v5.3.1...rel/v5.4.1) --- updated-dependencies: - dependency-name: org.apache.httpcomponents.client5:httpclient5 dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- dspace-server-webapp/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dspace-server-webapp/pom.xml b/dspace-server-webapp/pom.xml index cf0279ec9797..802589db2523 100644 --- a/dspace-server-webapp/pom.xml +++ b/dspace-server-webapp/pom.xml @@ -598,7 +598,7 @@ org.apache.httpcomponents.client5 httpclient5 - 5.3.1 + 5.4.1 test From 995af98886840ce9b9e979235d5cd8e82f2c4037 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 21 Jan 2025 23:30:10 +0000 Subject: [PATCH 364/979] Bump log4j.version from 2.23.1 to 2.24.3 Bumps `log4j.version` from 2.23.1 to 2.24.3. Updates `org.apache.logging.log4j:log4j-api` from 2.23.1 to 2.24.3 Updates `org.apache.logging.log4j:log4j-core` from 2.23.1 to 2.24.3 Updates `org.apache.logging.log4j:log4j-slf4j2-impl` from 2.23.1 to 2.24.3 --- updated-dependencies: - dependency-name: org.apache.logging.log4j:log4j-api dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: org.apache.logging.log4j:log4j-core dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: org.apache.logging.log4j:log4j-slf4j2-impl dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 7c52f6642ad9..316ac10b6900 100644 --- a/pom.xml +++ b/pom.xml @@ -39,7 +39,7 @@ 1.1.1 9.4.55.v20240627 - 2.23.1 + 2.24.3 2.0.33 1.19.0 2.0.16 From 0b1d4c09306e3c4fc56cc1ec6e3f10c761767d8a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 21 Jan 2025 23:30:13 +0000 Subject: [PATCH 365/979] Bump org.apache.jena:apache-jena-libs from 4.9.0 to 4.10.0 Bumps org.apache.jena:apache-jena-libs from 4.9.0 to 4.10.0. --- updated-dependencies: - dependency-name: org.apache.jena:apache-jena-libs dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 7c52f6642ad9..4f5bf159af1c 100644 --- a/pom.xml +++ b/pom.xml @@ -58,7 +58,7 @@ - 4.9.0 + 4.10.0 UTF-8 From da3584759b3478d4c37e85e05359015ec03d6eb5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eike=20Martin=20L=C3=B6hden?= Date: Wed, 15 Jan 2025 08:05:07 +0100 Subject: [PATCH 366/979] Add check to avoid adding empty array of values to an item. (cherry picked from commit 6e0124fcbb5e95bf7400deeedf8df1ccfca478f1) --- .../dspace/identifier/VersionedDOIIdentifierProvider.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/dspace-api/src/main/java/org/dspace/identifier/VersionedDOIIdentifierProvider.java b/dspace-api/src/main/java/org/dspace/identifier/VersionedDOIIdentifierProvider.java index e5a90907c7b6..b42c915163dc 100644 --- a/dspace-api/src/main/java/org/dspace/identifier/VersionedDOIIdentifierProvider.java +++ b/dspace-api/src/main/java/org/dspace/identifier/VersionedDOIIdentifierProvider.java @@ -354,7 +354,10 @@ void removePreviousVersionDOIsOutOfObject(Context c, Item item, String oldDoi) if (changed) { try { itemService.clearMetadata(c, item, MD_SCHEMA, DOI_ELEMENT, DOI_QUALIFIER, Item.ANY); - itemService.addMetadata(c, item, MD_SCHEMA, DOI_ELEMENT, DOI_QUALIFIER, null, newIdentifiers); + // Checks if Array newIdentifiers is empty to avoid adding null values to the metadata field. + if (!newIdentifiers.isEmpty()) { + itemService.addMetadata(c, item, MD_SCHEMA, DOI_ELEMENT, DOI_QUALIFIER, null, newIdentifiers); + } itemService.update(c, item); } catch (SQLException ex) { throw new RuntimeException("A problem with the database connection occured.", ex); From 33efc550e44fffb727c70d29d02608b2a3e89c2b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eike=20Martin=20L=C3=B6hden?= Date: Wed, 15 Jan 2025 08:05:07 +0100 Subject: [PATCH 367/979] Add check to avoid adding empty array of values to an item. (cherry picked from commit 6e0124fcbb5e95bf7400deeedf8df1ccfca478f1) --- .../dspace/identifier/VersionedDOIIdentifierProvider.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/dspace-api/src/main/java/org/dspace/identifier/VersionedDOIIdentifierProvider.java b/dspace-api/src/main/java/org/dspace/identifier/VersionedDOIIdentifierProvider.java index e5a90907c7b6..b42c915163dc 100644 --- a/dspace-api/src/main/java/org/dspace/identifier/VersionedDOIIdentifierProvider.java +++ b/dspace-api/src/main/java/org/dspace/identifier/VersionedDOIIdentifierProvider.java @@ -354,7 +354,10 @@ void removePreviousVersionDOIsOutOfObject(Context c, Item item, String oldDoi) if (changed) { try { itemService.clearMetadata(c, item, MD_SCHEMA, DOI_ELEMENT, DOI_QUALIFIER, Item.ANY); - itemService.addMetadata(c, item, MD_SCHEMA, DOI_ELEMENT, DOI_QUALIFIER, null, newIdentifiers); + // Checks if Array newIdentifiers is empty to avoid adding null values to the metadata field. + if (!newIdentifiers.isEmpty()) { + itemService.addMetadata(c, item, MD_SCHEMA, DOI_ELEMENT, DOI_QUALIFIER, null, newIdentifiers); + } itemService.update(c, item); } catch (SQLException ex) { throw new RuntimeException("A problem with the database connection occured.", ex); From 8525273f17a4b0f228007dd0a76c862fb37e6f5d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 22 Jan 2025 21:26:34 +0000 Subject: [PATCH 368/979] Bump jetty.version from 9.4.55.v20240627 to 9.4.57.v20241219 Bumps `jetty.version` from 9.4.55.v20240627 to 9.4.57.v20241219. Updates `org.eclipse.jetty:jetty-server` from 9.4.55.v20240627 to 9.4.57.v20241219 Updates `org.eclipse.jetty:jetty-deploy` from 9.4.55.v20240627 to 9.4.57.v20241219 Updates `org.eclipse.jetty:jetty-http` from 9.4.55.v20240627 to 9.4.57.v20241219 Updates `org.eclipse.jetty:jetty-io` from 9.4.55.v20240627 to 9.4.57.v20241219 Updates `org.eclipse.jetty:jetty-servlet` from 9.4.55.v20240627 to 9.4.57.v20241219 Updates `org.eclipse.jetty:jetty-util` from 9.4.55.v20240627 to 9.4.57.v20241219 Updates `org.eclipse.jetty:jetty-webapp` from 9.4.55.v20240627 to 9.4.57.v20241219 Updates `org.eclipse.jetty.http2:http2-common` from 9.4.55.v20240627 to 9.4.57.v20241219 --- updated-dependencies: - dependency-name: org.eclipse.jetty:jetty-server dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.eclipse.jetty:jetty-deploy dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.eclipse.jetty:jetty-http dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.eclipse.jetty:jetty-io dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.eclipse.jetty:jetty-servlet dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.eclipse.jetty:jetty-util dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.eclipse.jetty:jetty-webapp dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.eclipse.jetty.http2:http2-common dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 5cd47d01e6af..5c6d8c1b8dac 100644 --- a/pom.xml +++ b/pom.xml @@ -38,7 +38,7 @@ 4.0.5 1.1.1 - 9.4.55.v20240627 + 9.4.57.v20241219 2.24.3 2.0.33 1.19.0 From 1d44a6ebde25c3791465fe0d117d1b910187752c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 22 Jan 2025 21:32:01 +0000 Subject: [PATCH 369/979] Bump org.postgresql:postgresql from 42.7.3 to 42.7.5 Bumps [org.postgresql:postgresql](https://github.com/pgjdbc/pgjdbc) from 42.7.3 to 42.7.5. - [Release notes](https://github.com/pgjdbc/pgjdbc/releases) - [Changelog](https://github.com/pgjdbc/pgjdbc/blob/master/CHANGELOG.md) - [Commits](https://github.com/pgjdbc/pgjdbc/compare/REL42.7.3...REL42.7.5) --- updated-dependencies: - dependency-name: org.postgresql:postgresql dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 5cd47d01e6af..baf52604b76b 100644 --- a/pom.xml +++ b/pom.xml @@ -24,7 +24,7 @@ 6.4.2 6.4.8.Final 8.0.1.Final - 42.7.3 + 42.7.5 10.22.0 8.11.4 From 569e0af6590d6ae1e091290d2a0281d80bdeac03 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Fri, 15 Nov 2024 13:41:37 -0600 Subject: [PATCH 370/979] Resolve dependency convergence issue --- dspace-server-webapp/pom.xml | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/dspace-server-webapp/pom.xml b/dspace-server-webapp/pom.xml index f4b00dcd3a6b..ceb7d5cbb7dd 100644 --- a/dspace-server-webapp/pom.xml +++ b/dspace-server-webapp/pom.xml @@ -293,14 +293,6 @@ spring-expression ${spring.version} - - - @@ -610,6 +602,13 @@ com.jayway.jsonpath json-path + + + + net.minidev + json-smart + + com.jayway.jsonpath From 403cebb15cc46c2e32620adccb94e249256d4c8e Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Tue, 21 Jan 2025 15:02:42 -0600 Subject: [PATCH 371/979] Fix dependency convergence issue with Jena --- dspace-swordv2/pom.xml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/dspace-swordv2/pom.xml b/dspace-swordv2/pom.xml index d218406a16cc..2e6b1a27d838 100644 --- a/dspace-swordv2/pom.xml +++ b/dspace-swordv2/pom.xml @@ -59,6 +59,11 @@ org.apache.geronimo.specs geronimo-stax-api_1.0_spec + + + org.apache.jena + jena-core + From 0a55995b7c49b4e276c7d2f55d022d2d3e793273 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 23 Jan 2025 17:08:10 +0000 Subject: [PATCH 372/979] Remove unused joad-time dependency Bumps [joda-time:joda-time](https://github.com/JodaOrg/joda-time) from 2.12.5 to 2.13.0. - [Release notes](https://github.com/JodaOrg/joda-time/releases) - [Changelog](https://github.com/JodaOrg/joda-time/blob/main/RELEASE-NOTES.txt) - [Commits](https://github.com/JodaOrg/joda-time/compare/v2.12.5...v2.13.0) --- updated-dependencies: - dependency-name: joda-time:joda-time dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 59564fafff40..730cfe2cc938 100644 --- a/pom.xml +++ b/pom.xml @@ -1536,7 +1536,7 @@ joda-time joda-time - 2.12.5 + 2.13.0 com.sun.mail From f451307e6f5c69301b491837f3f33cd689958b69 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 23 Jan 2025 18:23:01 +0000 Subject: [PATCH 373/979] Bump com.amazonaws:aws-java-sdk-s3 from 1.12.261 to 1.12.780 Bumps [com.amazonaws:aws-java-sdk-s3](https://github.com/aws/aws-sdk-java) from 1.12.261 to 1.12.780. - [Changelog](https://github.com/aws/aws-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java/compare/1.12.261...1.12.780) --- updated-dependencies: - dependency-name: com.amazonaws:aws-java-sdk-s3 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- dspace-api/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dspace-api/pom.xml b/dspace-api/pom.xml index ce6cd41a8f0f..ff2a26cb61dd 100644 --- a/dspace-api/pom.xml +++ b/dspace-api/pom.xml @@ -756,7 +756,7 @@ com.amazonaws aws-java-sdk-s3 - 1.12.261 + 1.12.780 From 3767093c158b0fdddf66f33283128a41b234558c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 23 Jan 2025 18:23:11 +0000 Subject: [PATCH 374/979] Bump org.atteo:evo-inflector from 1.2.1 to 1.3 Bumps [org.atteo:evo-inflector](https://github.com/atteo/evo-inflector) from 1.2.1 to 1.3. - [Commits](https://github.com/atteo/evo-inflector/compare/1.2.1...1.3) --- updated-dependencies: - dependency-name: org.atteo:evo-inflector dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- dspace-rest/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dspace-rest/pom.xml b/dspace-rest/pom.xml index 3f4bdaf1d0d8..fa4e15e8b626 100644 --- a/dspace-rest/pom.xml +++ b/dspace-rest/pom.xml @@ -175,7 +175,7 @@ org.atteo evo-inflector - 1.2.1 + 1.3 org.apache.logging.log4j From 23321e84a5884d3a45d54b8c96f80a84fa3b122b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 23 Jan 2025 18:23:12 +0000 Subject: [PATCH 375/979] Bump the spring group with 11 updates Bumps the spring group with 11 updates: | Package | From | To | | --- | --- | --- | | [org.springframework.boot:spring-boot-starter-test](https://github.com/spring-projects/spring-boot) | `3.4.1` | `3.4.2` | | [org.springframework.boot:spring-boot-starter-tomcat](https://github.com/spring-projects/spring-boot) | `3.4.1` | `3.4.2` | | [org.springframework.boot:spring-boot-maven-plugin](https://github.com/spring-projects/spring-boot) | `3.4.1` | `3.4.2` | | [org.springframework.boot:spring-boot-starter-cache](https://github.com/spring-projects/spring-boot) | `3.4.1` | `3.4.2` | | [org.springframework.boot:spring-boot-starter](https://github.com/spring-projects/spring-boot) | `3.4.1` | `3.4.2` | | [org.springframework.boot:spring-boot-starter-web](https://github.com/spring-projects/spring-boot) | `3.4.1` | `3.4.2` | | [org.springframework.boot:spring-boot-starter-data-rest](https://github.com/spring-projects/spring-boot) | `3.4.1` | `3.4.2` | | [org.springframework.boot:spring-boot-starter-security](https://github.com/spring-projects/spring-boot) | `3.4.1` | `3.4.2` | | [org.springframework.boot:spring-boot-starter-aop](https://github.com/spring-projects/spring-boot) | `3.4.1` | `3.4.2` | | [org.springframework.boot:spring-boot-starter-actuator](https://github.com/spring-projects/spring-boot) | `3.4.1` | `3.4.2` | | [org.springframework.boot:spring-boot-starter-log4j2](https://github.com/spring-projects/spring-boot) | `3.4.1` | `3.4.2` | Updates `org.springframework.boot:spring-boot-starter-test` from 3.4.1 to 3.4.2 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.4.1...v3.4.2) Updates `org.springframework.boot:spring-boot-starter-tomcat` from 3.4.1 to 3.4.2 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.4.1...v3.4.2) Updates `org.springframework.boot:spring-boot-maven-plugin` from 3.4.1 to 3.4.2 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.4.1...v3.4.2) Updates `org.springframework.boot:spring-boot-starter-cache` from 3.4.1 to 3.4.2 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.4.1...v3.4.2) Updates `org.springframework.boot:spring-boot-starter` from 3.4.1 to 3.4.2 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.4.1...v3.4.2) Updates `org.springframework.boot:spring-boot-starter-web` from 3.4.1 to 3.4.2 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.4.1...v3.4.2) Updates `org.springframework.boot:spring-boot-starter-data-rest` from 3.4.1 to 3.4.2 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.4.1...v3.4.2) Updates `org.springframework.boot:spring-boot-starter-security` from 3.4.1 to 3.4.2 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.4.1...v3.4.2) Updates `org.springframework.boot:spring-boot-starter-aop` from 3.4.1 to 3.4.2 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.4.1...v3.4.2) Updates `org.springframework.boot:spring-boot-starter-actuator` from 3.4.1 to 3.4.2 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.4.1...v3.4.2) Updates `org.springframework.boot:spring-boot-starter-log4j2` from 3.4.1 to 3.4.2 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.4.1...v3.4.2) Updates `org.springframework.boot:spring-boot-starter-tomcat` from 3.4.1 to 3.4.2 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.4.1...v3.4.2) Updates `org.springframework.boot:spring-boot-maven-plugin` from 3.4.1 to 3.4.2 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.4.1...v3.4.2) Updates `org.springframework.boot:spring-boot-starter-cache` from 3.4.1 to 3.4.2 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.4.1...v3.4.2) Updates `org.springframework.boot:spring-boot-starter` from 3.4.1 to 3.4.2 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.4.1...v3.4.2) Updates `org.springframework.boot:spring-boot-starter-web` from 3.4.1 to 3.4.2 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.4.1...v3.4.2) Updates `org.springframework.boot:spring-boot-starter-data-rest` from 3.4.1 to 3.4.2 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.4.1...v3.4.2) Updates `org.springframework.boot:spring-boot-starter-security` from 3.4.1 to 3.4.2 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.4.1...v3.4.2) Updates `org.springframework.boot:spring-boot-starter-aop` from 3.4.1 to 3.4.2 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.4.1...v3.4.2) Updates `org.springframework.boot:spring-boot-starter-actuator` from 3.4.1 to 3.4.2 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.4.1...v3.4.2) Updates `org.springframework.boot:spring-boot-starter-log4j2` from 3.4.1 to 3.4.2 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.4.1...v3.4.2) --- updated-dependencies: - dependency-name: org.springframework.boot:spring-boot-starter-test dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter-tomcat dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-maven-plugin dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter-cache dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter-web dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter-data-rest dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter-security dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter-aop dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter-actuator dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter-log4j2 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter-tomcat dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-maven-plugin dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter-cache dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter-web dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter-data-rest dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter-security dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter-aop dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter-actuator dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter-log4j2 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 49a0ebaad801..a7b547490fae 100644 --- a/pom.xml +++ b/pom.xml @@ -20,7 +20,7 @@ 17 6.2.2 - 3.4.1 + 3.4.2 6.4.2 6.4.8.Final 8.0.1.Final From 35f664166b699e9291cd46ca4053cf1f95a034d9 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Thu, 23 Jan 2025 11:27:45 -0600 Subject: [PATCH 376/979] Remove outdated, unused Google Analytics APIs. Remove dependabot rules (as they only work for main branch) --- .github/dependabot.yml | 118 ----------------------------------------- dspace-api/pom.xml | 6 --- pom.xml | 6 --- 3 files changed, 130 deletions(-) delete mode 100644 .github/dependabot.yml diff --git a/.github/dependabot.yml b/.github/dependabot.yml deleted file mode 100644 index b6412b25b660..000000000000 --- a/.github/dependabot.yml +++ /dev/null @@ -1,118 +0,0 @@ -#------------------- -# DSpace's dependabot rules. Enables maven updates for all dependencies on a weekly basis -# for main and any maintenance branches. Security updates only apply to main. -#------------------- -version: 2 -updates: - - package-ecosystem: "maven" - directory: "/" - schedule: - interval: "weekly" - # Allow up to 10 open PRs for dependencies - open-pull-requests-limit: 10 - # Group together some upgrades in a single PR - groups: - # Group together all Build Tools in a single PR - build-tools: - applies-to: version-updates - patterns: - - "org.apache.maven.plugins:*" - - "*:*-maven-plugin" - - "*:maven-*-plugin" - - "com.github.spotbugs:spotbugs" - - "com.google.code.findbugs:*" - - "com.google.errorprone:*" - - "com.puppycrawl.tools:checkstyle" - - "org.sonatype.plugins:*" - exclude-patterns: - # Exclude anything from Spring, as that is in a separate group - - "org.springframework.*:*" - update-types: - - "minor" - - "patch" - test-tools: - applies-to: version-updates - patterns: - - "junit:*" - - "com.github.stefanbirker:system-rules" - - "com.h2database:*" - - "io.findify:s3mock*" - - "io.netty:*" - - "org.hamcrest:*" - - "org.mock-server:*" - - "org.mockito:*" - update-types: - - "minor" - - "patch" - # Group together all Apache Commons deps in a single PR - apache-commons: - applies-to: version-updates - patterns: - - "org.apache.commons:*" - - "commons-*:commons-*" - update-types: - - "minor" - - "patch" - # Group together all fasterxml deps in a single PR - fasterxml: - applies-to: version-updates - patterns: - - "com.fasterxml:*" - - "com.fasterxml.*:*" - update-types: - - "minor" - - "patch" - # Group together all Hibernate deps in a single PR - hibernate: - applies-to: version-updates - patterns: - - "org.hibernate.*:*" - update-types: - - "minor" - - "patch" - # Group together all Jakarta deps in a single PR - jakarta: - applies-to: version-updates - patterns: - - "jakarta.*:*" - - "org.eclipse.angus:jakarta.mail" - - "org.glassfish.jaxb:jaxb-runtime" - update-types: - - "minor" - - "patch" - # Group together all Google deps in a single PR - google-apis: - applies-to: version-updates - patterns: - - "com.google.apis:*" - - "com.google.api-client:*" - - "com.google.http-client:*" - - "com.google.oauth-client:*" - update-types: - - "minor" - - "patch" - # Group together all Spring deps in a single PR - spring: - applies-to: version-updates - patterns: - - "org.springframework:*" - - "org.springframework.*:*" - update-types: - - "minor" - - "patch" - # Group together all WebJARs deps in a single PR - webjars: - applies-to: version-updates - patterns: - - "org.webjars:*" - - "org.webjars.*:*" - update-types: - - "minor" - - "patch" - ignore: - # Don't try to auto-update any DSpace dependencies - - dependency-name: "org.dspace:*" - - dependency-name: "org.dspace.*:*" - # Ignore all major version updates for all dependencies. We'll only automate minor/patch updates. - - dependency-name: "*" - update-types: ["version-update:semver-major"] diff --git a/dspace-api/pom.xml b/dspace-api/pom.xml index f59d976c3358..bf16e9a34e78 100644 --- a/dspace-api/pom.xml +++ b/dspace-api/pom.xml @@ -692,12 +692,6 @@ ${flyway.version} - - - com.google.apis - google-api-services-analytics - - com.google.code.findbugs diff --git a/pom.xml b/pom.xml index 49a0ebaad801..afa6ed25c69c 100644 --- a/pom.xml +++ b/pom.xml @@ -1712,12 +1712,6 @@ 2.3.232 test - - - com.google.apis - google-api-services-analytics - v3-rev145-1.23.0 - From 1b850aaf0b042ade92c1d7ef2ff56a9b78704d6d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 23 Jan 2025 18:23:27 +0000 Subject: [PATCH 377/979] Bump net.minidev:json-smart from 2.5.0 to 2.5.1 Bumps [net.minidev:json-smart](https://github.com/netplex/json-smart-v2) from 2.5.0 to 2.5.1. - [Release notes](https://github.com/netplex/json-smart-v2/releases) - [Commits](https://github.com/netplex/json-smart-v2/compare/2.5.0...2.5.1) --- updated-dependencies: - dependency-name: net.minidev:json-smart dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- dspace-server-webapp/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dspace-server-webapp/pom.xml b/dspace-server-webapp/pom.xml index e078bfea6253..7c4739dc79e9 100644 --- a/dspace-server-webapp/pom.xml +++ b/dspace-server-webapp/pom.xml @@ -509,7 +509,7 @@ net.minidev json-smart - 2.5.0 + 2.5.1 From 5fc18dc98bec8f6a4d421973c0618f54cea8f27b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 23 Jan 2025 18:23:28 +0000 Subject: [PATCH 378/979] Bump org.antlr:antlr4-runtime from 4.13.1 to 4.13.2 Bumps [org.antlr:antlr4-runtime](https://github.com/antlr/antlr4) from 4.13.1 to 4.13.2. - [Release notes](https://github.com/antlr/antlr4/releases) - [Changelog](https://github.com/antlr/antlr4/blob/dev/CHANGES.txt) - [Commits](https://github.com/antlr/antlr4/compare/4.13.1...4.13.2) --- updated-dependencies: - dependency-name: org.antlr:antlr4-runtime dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 49a0ebaad801..6f4fb5985584 100644 --- a/pom.xml +++ b/pom.xml @@ -1138,7 +1138,7 @@ org.antlr antlr4-runtime - 4.13.1 + 4.13.2 From 9282a281ff4d3484f72bfa348df55164642c7401 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 23 Jan 2025 18:23:32 +0000 Subject: [PATCH 379/979] Bump org.apache.james:apache-mime4j-core from 0.8.10 to 0.8.12 Bumps org.apache.james:apache-mime4j-core from 0.8.10 to 0.8.12. --- updated-dependencies: - dependency-name: org.apache.james:apache-mime4j-core dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 49a0ebaad801..f00daa6384c1 100644 --- a/pom.xml +++ b/pom.xml @@ -1316,7 +1316,7 @@ org.apache.james apache-mime4j-core - 0.8.10 + 0.8.12 From 0f3fc0eccb86a9e0d2a4df2f9d044b2ae55d43b7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 23 Jan 2025 18:23:37 +0000 Subject: [PATCH 380/979] Bump org.postgresql:postgresql from 42.7.3 to 42.7.5 Bumps [org.postgresql:postgresql](https://github.com/pgjdbc/pgjdbc) from 42.7.3 to 42.7.5. - [Release notes](https://github.com/pgjdbc/pgjdbc/releases) - [Changelog](https://github.com/pgjdbc/pgjdbc/blob/master/CHANGELOG.md) - [Commits](https://github.com/pgjdbc/pgjdbc/compare/REL42.7.3...REL42.7.5) --- updated-dependencies: - dependency-name: org.postgresql:postgresql dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 59564fafff40..e41992328c66 100644 --- a/pom.xml +++ b/pom.xml @@ -24,7 +24,7 @@ 5.7.14 5.6.15.Final 6.2.5.Final - 42.7.3 + 42.7.5 8.11.4 3.10.8 From b94be265273e32a8f55e03ac8b905e677ffc5458 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 23 Jan 2025 18:23:37 +0000 Subject: [PATCH 381/979] Bump joda-time:joda-time from 2.12.5 to 2.13.0 Bumps [joda-time:joda-time](https://github.com/JodaOrg/joda-time) from 2.12.5 to 2.13.0. - [Release notes](https://github.com/JodaOrg/joda-time/releases) - [Changelog](https://github.com/JodaOrg/joda-time/blob/main/RELEASE-NOTES.txt) - [Commits](https://github.com/JodaOrg/joda-time/compare/v2.12.5...v2.13.0) --- updated-dependencies: - dependency-name: joda-time:joda-time dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 49a0ebaad801..d5aec94b4c40 100644 --- a/pom.xml +++ b/pom.xml @@ -1541,7 +1541,7 @@ joda-time joda-time - 2.12.5 + 2.13.0 jakarta.activation From daa29e582a9c5d2c52e66c319cf981614073e503 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 23 Jan 2025 18:23:43 +0000 Subject: [PATCH 382/979] Bump org.jboss.logging:jboss-logging from 3.4.3.Final to 3.6.1.Final Bumps [org.jboss.logging:jboss-logging](https://github.com/jboss-logging/jboss-logging) from 3.4.3.Final to 3.6.1.Final. - [Release notes](https://github.com/jboss-logging/jboss-logging/releases) - [Commits](https://github.com/jboss-logging/jboss-logging/compare/3.4.3.Final...3.6.1.Final) --- updated-dependencies: - dependency-name: org.jboss.logging:jboss-logging dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 59564fafff40..1c386f0f1163 100644 --- a/pom.xml +++ b/pom.xml @@ -1148,7 +1148,7 @@ org.jboss.logging jboss-logging - 3.4.3.Final + 3.6.1.Final From c1ee2e5c95b6f6addb6a93a9fb22709e29ea24e6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 23 Jan 2025 18:23:43 +0000 Subject: [PATCH 383/979] Bump com.ibm.icu:icu4j from 62.1 to 62.2 Bumps [com.ibm.icu:icu4j](https://github.com/unicode-org/icu) from 62.1 to 62.2. - [Release notes](https://github.com/unicode-org/icu/releases) - [Commits](https://github.com/unicode-org/icu/commits) --- updated-dependencies: - dependency-name: com.ibm.icu:icu4j dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 49a0ebaad801..573155020588 100644 --- a/pom.xml +++ b/pom.xml @@ -1634,7 +1634,7 @@ com.ibm.icu icu4j - 62.1 + 62.2 From d2d6740efd224717f03eedaf0971614b1cbe2ce6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 23 Jan 2025 18:23:45 +0000 Subject: [PATCH 384/979] Bump org.apache.velocity:velocity-engine-core from 2.3 to 2.4.1 Bumps org.apache.velocity:velocity-engine-core from 2.3 to 2.4.1. --- updated-dependencies: - dependency-name: org.apache.velocity:velocity-engine-core dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- dspace-api/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dspace-api/pom.xml b/dspace-api/pom.xml index f59d976c3358..3af940c3d8be 100644 --- a/dspace-api/pom.xml +++ b/dspace-api/pom.xml @@ -789,7 +789,7 @@ org.apache.velocity velocity-engine-core - 2.3 + 2.4.1 From 75d747e40ad3c63a50a57d17d4270d8930c07104 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 23 Jan 2025 18:23:46 +0000 Subject: [PATCH 385/979] Bump org.apache.james:apache-mime4j-core from 0.8.10 to 0.8.12 Bumps org.apache.james:apache-mime4j-core from 0.8.10 to 0.8.12. --- updated-dependencies: - dependency-name: org.apache.james:apache-mime4j-core dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 59564fafff40..f7b20122e02f 100644 --- a/pom.xml +++ b/pom.xml @@ -1320,7 +1320,7 @@ org.apache.james apache-mime4j-core - 0.8.10 + 0.8.12 From 20178bfcac780935fe59142005932466f611f778 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 23 Jan 2025 18:24:00 +0000 Subject: [PATCH 386/979] Bump jersey.version from 3.1.5 to 3.1.10 Bumps `jersey.version` from 3.1.5 to 3.1.10. Updates `org.glassfish.jersey.core:jersey-client` from 3.1.5 to 3.1.10 Updates `org.glassfish.jersey.inject:jersey-hk2` from 3.1.5 to 3.1.10 --- updated-dependencies: - dependency-name: org.glassfish.jersey.core:jersey-client dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.glassfish.jersey.inject:jersey-hk2 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 49a0ebaad801..13b734d66b1d 100644 --- a/pom.xml +++ b/pom.xml @@ -48,7 +48,7 @@ 1.80 8.0.1 - 3.1.5 + 3.1.10 2.9.0 From 0d069354f7dbba1afcdfe44a6642c4763e7eb478 Mon Sep 17 00:00:00 2001 From: Nona Luypaert Date: Mon, 23 Dec 2024 15:17:32 +0100 Subject: [PATCH 387/979] 122350: Move failRunningProcesses during tomcat startup to RestRepository --- .../dspace/scripts/ProcessServiceImpl.java | 47 +++++++------------ .../scripts/service/ProcessService.java | 10 ++++ .../repository/ProcessRestRepository.java | 7 +++ 3 files changed, 35 insertions(+), 29 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/scripts/ProcessServiceImpl.java b/dspace-api/src/main/java/org/dspace/scripts/ProcessServiceImpl.java index ab5147221cfc..b3893f744380 100644 --- a/dspace-api/src/main/java/org/dspace/scripts/ProcessServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/scripts/ProcessServiceImpl.java @@ -47,13 +47,12 @@ import org.dspace.eperson.Group; import org.dspace.scripts.service.ProcessService; import org.dspace.services.ConfigurationService; -import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.annotation.Autowired; /** * The implementation for the {@link ProcessService} class */ -public class ProcessServiceImpl implements ProcessService, InitializingBean { +public class ProcessServiceImpl implements ProcessService { private static final Logger log = org.apache.logging.log4j.LogManager.getLogger(ProcessService.class); @@ -75,33 +74,6 @@ public class ProcessServiceImpl implements ProcessService, InitializingBean { @Autowired private ConfigurationService configurationService; - @Override - public void afterPropertiesSet() throws Exception { - try { - Context context = new Context(); - - // Processes that were running or scheduled when tomcat crashed, should be cleaned up during startup. - List processesToBeFailed = findByStatusAndCreationTimeOlderThan( - context, List.of(ProcessStatus.RUNNING, ProcessStatus.SCHEDULED), new Date()); - for (Process process : processesToBeFailed) { - context.setCurrentUser(process.getEPerson()); - // Fail the process. - log.info("Process with ID {} did not complete before tomcat shutdown, failing it now.", - process.getID()); - fail(context, process); - // But still attach its log to the process. - appendLog(process.getID(), process.getName(), - "Process did not complete before tomcat shutdown.", - ProcessLogLevel.ERROR); - createLogBitstream(context, process); - } - - context.complete(); - } catch (Exception e) { - log.error("Unable to clean up Processes: ", e); - } - } - @Override public Process create(Context context, EPerson ePerson, String scriptName, List parameters, @@ -359,6 +331,23 @@ public int countByUser(Context context, EPerson user) throws SQLException { return processDAO.countByUser(context, user); } + @Override + public void failRunningProcesses(Context context) throws SQLException, IOException, AuthorizeException { + List processesToBeFailed = findByStatusAndCreationTimeOlderThan( + context, List.of(ProcessStatus.RUNNING, ProcessStatus.SCHEDULED), new Date()); + for (Process process : processesToBeFailed) { + context.setCurrentUser(process.getEPerson()); + // Fail the process. + log.info("Process with ID {} did not complete before tomcat shutdown, failing it now.", process.getID()); + fail(context, process); + // But still attach its log to the process. + appendLog(process.getID(), process.getName(), + "Process did not complete before tomcat shutdown.", + ProcessLogLevel.ERROR); + createLogBitstream(context, process); + } + } + private String formatLogLine(int processId, String scriptName, String output, ProcessLogLevel processLogLevel) { SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS"); StringBuilder sb = new StringBuilder(); diff --git a/dspace-api/src/main/java/org/dspace/scripts/service/ProcessService.java b/dspace-api/src/main/java/org/dspace/scripts/service/ProcessService.java index c6fc24888155..5df2ca15aad8 100644 --- a/dspace-api/src/main/java/org/dspace/scripts/service/ProcessService.java +++ b/dspace-api/src/main/java/org/dspace/scripts/service/ProcessService.java @@ -277,4 +277,14 @@ List findByStatusAndCreationTimeOlderThan(Context context, List Date: Mon, 23 Dec 2024 15:17:32 +0100 Subject: [PATCH 388/979] 122350: Move failRunningProcesses during tomcat startup to RestRepository --- .../dspace/scripts/ProcessServiceImpl.java | 47 +++++++------------ .../scripts/service/ProcessService.java | 10 ++++ .../repository/ProcessRestRepository.java | 7 +++ 3 files changed, 35 insertions(+), 29 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/scripts/ProcessServiceImpl.java b/dspace-api/src/main/java/org/dspace/scripts/ProcessServiceImpl.java index ab5147221cfc..b3893f744380 100644 --- a/dspace-api/src/main/java/org/dspace/scripts/ProcessServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/scripts/ProcessServiceImpl.java @@ -47,13 +47,12 @@ import org.dspace.eperson.Group; import org.dspace.scripts.service.ProcessService; import org.dspace.services.ConfigurationService; -import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.annotation.Autowired; /** * The implementation for the {@link ProcessService} class */ -public class ProcessServiceImpl implements ProcessService, InitializingBean { +public class ProcessServiceImpl implements ProcessService { private static final Logger log = org.apache.logging.log4j.LogManager.getLogger(ProcessService.class); @@ -75,33 +74,6 @@ public class ProcessServiceImpl implements ProcessService, InitializingBean { @Autowired private ConfigurationService configurationService; - @Override - public void afterPropertiesSet() throws Exception { - try { - Context context = new Context(); - - // Processes that were running or scheduled when tomcat crashed, should be cleaned up during startup. - List processesToBeFailed = findByStatusAndCreationTimeOlderThan( - context, List.of(ProcessStatus.RUNNING, ProcessStatus.SCHEDULED), new Date()); - for (Process process : processesToBeFailed) { - context.setCurrentUser(process.getEPerson()); - // Fail the process. - log.info("Process with ID {} did not complete before tomcat shutdown, failing it now.", - process.getID()); - fail(context, process); - // But still attach its log to the process. - appendLog(process.getID(), process.getName(), - "Process did not complete before tomcat shutdown.", - ProcessLogLevel.ERROR); - createLogBitstream(context, process); - } - - context.complete(); - } catch (Exception e) { - log.error("Unable to clean up Processes: ", e); - } - } - @Override public Process create(Context context, EPerson ePerson, String scriptName, List parameters, @@ -359,6 +331,23 @@ public int countByUser(Context context, EPerson user) throws SQLException { return processDAO.countByUser(context, user); } + @Override + public void failRunningProcesses(Context context) throws SQLException, IOException, AuthorizeException { + List processesToBeFailed = findByStatusAndCreationTimeOlderThan( + context, List.of(ProcessStatus.RUNNING, ProcessStatus.SCHEDULED), new Date()); + for (Process process : processesToBeFailed) { + context.setCurrentUser(process.getEPerson()); + // Fail the process. + log.info("Process with ID {} did not complete before tomcat shutdown, failing it now.", process.getID()); + fail(context, process); + // But still attach its log to the process. + appendLog(process.getID(), process.getName(), + "Process did not complete before tomcat shutdown.", + ProcessLogLevel.ERROR); + createLogBitstream(context, process); + } + } + private String formatLogLine(int processId, String scriptName, String output, ProcessLogLevel processLogLevel) { SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS"); StringBuilder sb = new StringBuilder(); diff --git a/dspace-api/src/main/java/org/dspace/scripts/service/ProcessService.java b/dspace-api/src/main/java/org/dspace/scripts/service/ProcessService.java index c6fc24888155..5df2ca15aad8 100644 --- a/dspace-api/src/main/java/org/dspace/scripts/service/ProcessService.java +++ b/dspace-api/src/main/java/org/dspace/scripts/service/ProcessService.java @@ -277,4 +277,14 @@ List findByStatusAndCreationTimeOlderThan(Context context, List Date: Fri, 24 Jan 2025 00:20:45 +0000 Subject: [PATCH 389/979] Bump net.bytebuddy:byte-buddy from 1.11.13 to 1.16.1 Bumps [net.bytebuddy:byte-buddy](https://github.com/raphw/byte-buddy) from 1.11.13 to 1.16.1. - [Release notes](https://github.com/raphw/byte-buddy/releases) - [Changelog](https://github.com/raphw/byte-buddy/blob/master/release-notes.md) - [Commits](https://github.com/raphw/byte-buddy/compare/byte-buddy-1.11.13...byte-buddy-1.16.1) --- updated-dependencies: - dependency-name: net.bytebuddy:byte-buddy dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 3bd2c52ebd00..0d2175ccdcab 100644 --- a/pom.xml +++ b/pom.xml @@ -1813,7 +1813,7 @@ net.bytebuddy byte-buddy - 1.11.13 + 1.16.1 From 406e932f070a0d3a029391d524279ca99d658174 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 24 Jan 2025 00:21:01 +0000 Subject: [PATCH 390/979] Bump org.apache.bcel:bcel from 6.7.0 to 6.10.0 Bumps [org.apache.bcel:bcel](https://github.com/apache/commons-bcel) from 6.7.0 to 6.10.0. - [Changelog](https://github.com/apache/commons-bcel/blob/master/RELEASE-NOTES.txt) - [Commits](https://github.com/apache/commons-bcel/compare/rel/commons-bcel-6.7.0...rel/commons-bcel-6.10.0) --- updated-dependencies: - dependency-name: org.apache.bcel:bcel dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- dspace-api/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dspace-api/pom.xml b/dspace-api/pom.xml index fd00cc919a07..acb59fb2d492 100644 --- a/dspace-api/pom.xml +++ b/dspace-api/pom.xml @@ -796,7 +796,7 @@ org.apache.bcel bcel - 6.7.0 + 6.10.0 test From b59ad5ba556b5d52660aa9572b1072993ec12d5c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 24 Jan 2025 00:21:06 +0000 Subject: [PATCH 391/979] Bump com.nimbusds:nimbus-jose-jwt from 9.37.3 to 9.48 Bumps [com.nimbusds:nimbus-jose-jwt](https://bitbucket.org/connect2id/nimbus-jose-jwt) from 9.37.3 to 9.48. - [Changelog](https://bitbucket.org/connect2id/nimbus-jose-jwt/src/master/CHANGELOG.txt) - [Commits](https://bitbucket.org/connect2id/nimbus-jose-jwt/branches/compare/9.48..9.37.3) --- updated-dependencies: - dependency-name: com.nimbusds:nimbus-jose-jwt dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 3bd2c52ebd00..73b0dbd4c506 100644 --- a/pom.xml +++ b/pom.xml @@ -54,7 +54,7 @@ 2.9.0 - 9.37.3 + 9.48 From 9d2da83a7edfacda3046671a545f66c15547b6f3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 24 Jan 2025 00:21:29 +0000 Subject: [PATCH 392/979] Bump org.checkerframework:checker-qual from 3.31.0 to 3.48.4 Bumps [org.checkerframework:checker-qual](https://github.com/typetools/checker-framework) from 3.31.0 to 3.48.4. - [Release notes](https://github.com/typetools/checker-framework/releases) - [Changelog](https://github.com/typetools/checker-framework/blob/master/docs/CHANGELOG.md) - [Commits](https://github.com/typetools/checker-framework/compare/checker-framework-3.31.0...checker-framework-3.48.4) --- updated-dependencies: - dependency-name: org.checkerframework:checker-qual dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 3bd2c52ebd00..ef60604796d6 100644 --- a/pom.xml +++ b/pom.xml @@ -1344,7 +1344,7 @@ org.checkerframework checker-qual - 3.31.0 + 3.48.4 From 77efa5ce8872b4e14fb765534165355ad280cc9f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 24 Jan 2025 00:21:41 +0000 Subject: [PATCH 394/979] Bump com.ibm.icu:icu4j from 62.1 to 62.2 Bumps [com.ibm.icu:icu4j](https://github.com/unicode-org/icu) from 62.1 to 62.2. - [Release notes](https://github.com/unicode-org/icu/releases) - [Commits](https://github.com/unicode-org/icu/commits) --- updated-dependencies: - dependency-name: com.ibm.icu:icu4j dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 1e396be2cd1d..a08f27485891 100644 --- a/pom.xml +++ b/pom.xml @@ -1606,7 +1606,7 @@ com.ibm.icu icu4j - 62.1 + 62.2 From 9b7f142decccb470538d437ccf8e6bb09bfaffeb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 24 Jan 2025 00:21:45 +0000 Subject: [PATCH 395/979] Bump com.google.guava:guava from 32.0.0-jre to 32.1.3-jre Bumps [com.google.guava:guava](https://github.com/google/guava) from 32.0.0-jre to 32.1.3-jre. - [Release notes](https://github.com/google/guava/releases) - [Commits](https://github.com/google/guava/commits) --- updated-dependencies: - dependency-name: com.google.guava:guava dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 3bd2c52ebd00..82b4b615a509 100644 --- a/pom.xml +++ b/pom.xml @@ -1756,7 +1756,7 @@ com.google.guava guava - 32.0.0-jre + 32.1.3-jre From 63081494d87d36acdb3f9026ac14b431881ce41c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 24 Jan 2025 00:22:13 +0000 Subject: [PATCH 396/979] Bump com.opencsv:opencsv from 5.9 to 5.10 Bumps com.opencsv:opencsv from 5.9 to 5.10. --- updated-dependencies: - dependency-name: com.opencsv:opencsv dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- dspace-api/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dspace-api/pom.xml b/dspace-api/pom.xml index ff2a26cb61dd..07d7f1d21c00 100644 --- a/dspace-api/pom.xml +++ b/dspace-api/pom.xml @@ -805,7 +805,7 @@ com.opencsv opencsv - 5.9 + 5.10 From 25be075b3c9679458cebc131cc692984da751121 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 24 Jan 2025 00:22:28 +0000 Subject: [PATCH 397/979] Bump de.digitalcollections.iiif:iiif-apis from 0.3.10 to 0.3.11 Bumps [de.digitalcollections.iiif:iiif-apis](https://github.com/dbmdz/iiif-apis) from 0.3.10 to 0.3.11. - [Release notes](https://github.com/dbmdz/iiif-apis/releases) - [Commits](https://github.com/dbmdz/iiif-apis/compare/0.3.10...0.3.11) --- updated-dependencies: - dependency-name: de.digitalcollections.iiif:iiif-apis dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- dspace-iiif/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dspace-iiif/pom.xml b/dspace-iiif/pom.xml index 49ee02b56500..86702fe01b96 100644 --- a/dspace-iiif/pom.xml +++ b/dspace-iiif/pom.xml @@ -98,7 +98,7 @@ de.digitalcollections.iiif iiif-apis - 0.3.10 + 0.3.11 org.javassist From 70ed55bb552cf4bfbce964291867b3950bfe0730 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oscar=20Chac=C3=B3n?= <167455436+oscar-escire@users.noreply.github.com> Date: Fri, 24 Jan 2025 10:08:22 -0600 Subject: [PATCH 398/979] added expunge request param and enabled workflowitem delete based on it (#9950) * added expunge request param and enabled workflowitem delete based on it * enabled expunge param only in workflowitem delete endpoint and IT * Remove invalid param from JavaDocs --------- Co-authored-by: Tim Donohue --- .../WorkflowItemRestRepository.java | 15 ++++- .../rest/WorkflowItemRestRepositoryIT.java | 65 +++++++++++++++++++ 2 files changed, 79 insertions(+), 1 deletion(-) diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/WorkflowItemRestRepository.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/WorkflowItemRestRepository.java index f6c879b3efdf..3d852e9aabf0 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/WorkflowItemRestRepository.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/WorkflowItemRestRepository.java @@ -71,6 +71,7 @@ public class WorkflowItemRestRepository extends DSpaceRestRepository { public static final String OPERATION_PATH_SECTIONS = "sections"; + public static final String REQUESTPARAMETER_EXPUNGE = "expunge"; private static final Logger log = LogManager.getLogger(); @@ -239,13 +240,25 @@ public void patch(Context context, HttpServletRequest request, String apiCategor * move the workflowitem back to the submitter workspace regardless to how the workflow is designed */ protected void delete(Context context, Integer id) { + String expungeParam = getRequestService() + .getCurrentRequest() + .getServletRequest() + .getParameter(REQUESTPARAMETER_EXPUNGE); + boolean expunge = false; + if (expungeParam != null) { + expunge = Boolean.parseBoolean(expungeParam); + } XmlWorkflowItem witem = null; try { witem = wis.find(context, id); if (witem == null) { throw new ResourceNotFoundException("WorkflowItem ID " + id + " not found"); } - wfs.abort(context, witem, context.getCurrentUser()); + if (expunge) { + wis.delete(context, witem); + } else { + wfs.abort(context, witem, context.getCurrentUser()); + } } catch (AuthorizeException e) { throw new RESTAuthorizationException(e); } catch (SQLException e) { diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/WorkflowItemRestRepositoryIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/WorkflowItemRestRepositoryIT.java index 6275360b3c5b..a9bbc752f875 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/WorkflowItemRestRepositoryIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/WorkflowItemRestRepositoryIT.java @@ -686,6 +686,71 @@ public void deleteOneTest() throws Exception { .andExpect(jsonPath("$.page.totalElements", is(1))); } + @Test + /** + * A delete request over a workflowitem with expunge param should result in delete item over detabase + * workspace + * + * @throws Exception + */ + public void deleteOneWithExpungeTest() throws Exception { + context.turnOffAuthorisationSystem(); + + //** GIVEN ** + //1. A community with one collection. + parentCommunity = CommunityBuilder.createCommunity(context) + .withName("Parent Community") + .build(); + Collection col1 = CollectionBuilder.createCollection(context, parentCommunity).withName("Collection 1") + .withWorkflowGroup(1, admin).build(); + + //2. create a normal user to use as submitter + EPerson submitter = EPersonBuilder.createEPerson(context) + .withEmail("submitter@example.com") + .withPassword("dspace") + .build(); + + context.setCurrentUser(submitter); + + //3. a workflow item + XmlWorkflowItem witem = WorkflowItemBuilder.createWorkflowItem(context, col1) + .withTitle("Workflow Item 1") + .withIssueDate("2017-10-17") + .build(); + + Item item = witem.getItem(); + + //Add a bitstream to the item + String bitstreamContent = "ThisIsSomeDummyText"; + Bitstream bitstream = null; + try (InputStream is = IOUtils.toInputStream(bitstreamContent, Charset.defaultCharset())) { + bitstream = BitstreamBuilder + .createBitstream(context, item, is) + .withName("Bitstream1") + .withMimeType("text/plain").build(); + } + + context.restoreAuthSystemState(); + + String token = getAuthToken(admin.getEmail(), password); + + // Delete the workflowitem + getClient(token).perform(delete("/api/workflow/workflowitems/" + witem.getID() + "?expunge=true")) + .andExpect(status().is(204)); + + // Trying to get deleted workflowitem should fail with 404 + getClient(token).perform(get("/api/workflow/workflowitems/" + witem.getID())) + .andExpect(status().is(404)); + + // the workflowitem's item should fail with 404 + getClient(token).perform(get("/api/core/items/" + item.getID())) + .andExpect(status().is(404)); + + // the workflowitem's bitstream should fail with 404 + getClient(token).perform(get("/api/core/bitstreams/" + bitstream.getID())) + .andExpect(status().is(404)); + } + @Test /** * Test the deposit of a workspaceitem POSTing to the resource workflowitems collection endpoint a workspaceitem (as From 10b3f06480ca43de24ce1ed81874a671a27e2963 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oscar=20Chac=C3=B3n?= <167455436+oscar-escire@users.noreply.github.com> Date: Fri, 24 Jan 2025 10:08:22 -0600 Subject: [PATCH 399/979] added expunge request param and enabled workflowitem delete based on it (#9950) * added expunge request param and enabled workflowitem delete based on it * enabled expunge param only in workflowitem delete endpoint and IT * Remove invalid param from JavaDocs --------- Co-authored-by: Tim Donohue --- .../WorkflowItemRestRepository.java | 15 ++++- .../rest/WorkflowItemRestRepositoryIT.java | 65 +++++++++++++++++++ 2 files changed, 79 insertions(+), 1 deletion(-) diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/WorkflowItemRestRepository.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/WorkflowItemRestRepository.java index de39ff69fb9c..f34961727e25 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/WorkflowItemRestRepository.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/WorkflowItemRestRepository.java @@ -71,6 +71,7 @@ public class WorkflowItemRestRepository extends DSpaceRestRepository { public static final String OPERATION_PATH_SECTIONS = "sections"; + public static final String REQUESTPARAMETER_EXPUNGE = "expunge"; private static final Logger log = LogManager.getLogger(); @@ -239,13 +240,25 @@ public void patch(Context context, HttpServletRequest request, String apiCategor * move the workflowitem back to the submitter workspace regardless to how the workflow is designed */ protected void delete(Context context, Integer id) { + String expungeParam = getRequestService() + .getCurrentRequest() + .getServletRequest() + .getParameter(REQUESTPARAMETER_EXPUNGE); + boolean expunge = false; + if (expungeParam != null) { + expunge = Boolean.parseBoolean(expungeParam); + } XmlWorkflowItem witem = null; try { witem = wis.find(context, id); if (witem == null) { throw new ResourceNotFoundException("WorkflowItem ID " + id + " not found"); } - wfs.abort(context, witem, context.getCurrentUser()); + if (expunge) { + wis.delete(context, witem); + } else { + wfs.abort(context, witem, context.getCurrentUser()); + } } catch (AuthorizeException e) { throw new RESTAuthorizationException(e); } catch (SQLException e) { diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/WorkflowItemRestRepositoryIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/WorkflowItemRestRepositoryIT.java index 72612fc5eb53..fe8e67bbf564 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/WorkflowItemRestRepositoryIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/WorkflowItemRestRepositoryIT.java @@ -686,6 +686,71 @@ public void deleteOneTest() throws Exception { .andExpect(jsonPath("$.page.totalElements", is(1))); } + @Test + /** + * A delete request over a workflowitem with expunge param should result in delete item over detabase + * workspace + * + * @throws Exception + */ + public void deleteOneWithExpungeTest() throws Exception { + context.turnOffAuthorisationSystem(); + + //** GIVEN ** + //1. A community with one collection. + parentCommunity = CommunityBuilder.createCommunity(context) + .withName("Parent Community") + .build(); + Collection col1 = CollectionBuilder.createCollection(context, parentCommunity).withName("Collection 1") + .withWorkflowGroup(1, admin).build(); + + //2. create a normal user to use as submitter + EPerson submitter = EPersonBuilder.createEPerson(context) + .withEmail("submitter@example.com") + .withPassword("dspace") + .build(); + + context.setCurrentUser(submitter); + + //3. a workflow item + XmlWorkflowItem witem = WorkflowItemBuilder.createWorkflowItem(context, col1) + .withTitle("Workflow Item 1") + .withIssueDate("2017-10-17") + .build(); + + Item item = witem.getItem(); + + //Add a bitstream to the item + String bitstreamContent = "ThisIsSomeDummyText"; + Bitstream bitstream = null; + try (InputStream is = IOUtils.toInputStream(bitstreamContent, Charset.defaultCharset())) { + bitstream = BitstreamBuilder + .createBitstream(context, item, is) + .withName("Bitstream1") + .withMimeType("text/plain").build(); + } + + context.restoreAuthSystemState(); + + String token = getAuthToken(admin.getEmail(), password); + + // Delete the workflowitem + getClient(token).perform(delete("/api/workflow/workflowitems/" + witem.getID() + "?expunge=true")) + .andExpect(status().is(204)); + + // Trying to get deleted workflowitem should fail with 404 + getClient(token).perform(get("/api/workflow/workflowitems/" + witem.getID())) + .andExpect(status().is(404)); + + // the workflowitem's item should fail with 404 + getClient(token).perform(get("/api/core/items/" + item.getID())) + .andExpect(status().is(404)); + + // the workflowitem's bitstream should fail with 404 + getClient(token).perform(get("/api/core/bitstreams/" + bitstream.getID())) + .andExpect(status().is(404)); + } + @Test /** * Test the deposit of a workspaceitem POSTing to the resource workflowitems collection endpoint a workspaceitem (as From 96451f7391ec30548c2c61cc0288440a59dde527 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Fri, 24 Jan 2025 15:06:43 -0600 Subject: [PATCH 400/979] Dependency convergence fixes --- pom.xml | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/pom.xml b/pom.xml index 7be509838d00..dfe2347fd64c 100644 --- a/pom.xml +++ b/pom.xml @@ -1703,6 +1703,23 @@ com.google.http-client google-http-client 1.45.3 + + + com.google.errorprone + error_prone_annotations + + + com.google.j2objc + j2objc-annotations + + + + + + io.grpc + grpc-context + 1.69.0 com.google.http-client @@ -1724,6 +1741,13 @@ google-oauth-client 1.37.0 + + + com.google.http-client + google-http-client-gson + 1.43.3 + From 5169a55437f39756bf914336640619a2a6090e48 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Fri, 15 Nov 2024 13:41:37 -0600 Subject: [PATCH 401/979] Resolve dependency convergence issue --- dspace-server-webapp/pom.xml | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/dspace-server-webapp/pom.xml b/dspace-server-webapp/pom.xml index 7c4739dc79e9..63de9fe721cd 100644 --- a/dspace-server-webapp/pom.xml +++ b/dspace-server-webapp/pom.xml @@ -270,6 +270,11 @@ com.fasterxml.jackson.datatype jackson-datatype-jdk8 + + + net.minidev + json-smart + @@ -553,6 +558,13 @@ com.jayway.jsonpath json-path test + + + + net.minidev + json-smart + + com.jayway.jsonpath From 1cc98e6c2d6a5f2ec6d713dfad326593362d8437 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Fri, 24 Jan 2025 16:31:05 -0600 Subject: [PATCH 402/979] Fix missing dependency errors for javax.el --- dspace-api/pom.xml | 5 +++++ pom.xml | 6 ++++++ 2 files changed, 11 insertions(+) diff --git a/dspace-api/pom.xml b/dspace-api/pom.xml index 38c490c4dbd9..a3184d9adff8 100644 --- a/dspace-api/pom.xml +++ b/dspace-api/pom.xml @@ -505,6 +505,11 @@ javax.servlet-api provided + + + javax.el + javax.el-api + javax.annotation javax.annotation-api diff --git a/pom.xml b/pom.xml index a2de1a0f5fbb..1399e1c05491 100644 --- a/pom.xml +++ b/pom.xml @@ -1548,6 +1548,12 @@ javax.servlet-api 3.1.0 + + + javax.el + javax.el-api + 3.0.0 + From f7902bce00f7e480f034a151b6b180294963e573 Mon Sep 17 00:00:00 2001 From: "Mark H. Wood" Date: Tue, 17 Sep 2024 14:36:06 -0400 Subject: [PATCH 403/979] On error in bulk operations, continue instead of panic-stopping. (cherry picked from commit bfeba1aa7a8119d083fd6a9e15b58bf55988d006) --- .../dspace/identifier/doi/DOIOrganiser.java | 61 +++++++++++++------ 1 file changed, 41 insertions(+), 20 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/identifier/doi/DOIOrganiser.java b/dspace-api/src/main/java/org/dspace/identifier/doi/DOIOrganiser.java index 12f6d7c4fca4..88738226755d 100644 --- a/dspace-api/src/main/java/org/dspace/identifier/doi/DOIOrganiser.java +++ b/dspace-api/src/main/java/org/dspace/identifier/doi/DOIOrganiser.java @@ -13,7 +13,6 @@ import java.sql.SQLException; import java.util.Arrays; import java.util.Date; -import java.util.Iterator; import java.util.List; import java.util.Locale; import java.util.UUID; @@ -227,8 +226,16 @@ public static void runCLI(Context context, DOIOrganiser organiser, String[] args } for (DOI doi : dois) { - organiser.reserve(doi); - context.uncacheEntity(doi); + doi = context.reloadEntity(doi); + try { + organiser.reserve(doi); + context.commit(); + } catch (RuntimeException e) { + System.err.format("DOI %s for object %s reservation failed, skipping: %s%n", + doi.getDSpaceObject().getID().toString(), + doi.getDoi(), e.getMessage()); + context.rollback(); + } } } catch (SQLException ex) { System.err.println("Error in database connection:" + ex.getMessage()); @@ -245,8 +252,16 @@ public static void runCLI(Context context, DOIOrganiser organiser, String[] args + "that could be registered."); } for (DOI doi : dois) { - organiser.register(doi); - context.uncacheEntity(doi); + doi = context.reloadEntity(doi); + try { + organiser.register(doi); + context.commit(); + } catch (SQLException e) { + System.err.format("DOI %s for object %s registration failed, skipping: %s%n", + doi.getDSpaceObject().getID().toString(), + doi.getDoi(), e.getMessage()); + context.rollback(); + } } } catch (SQLException ex) { System.err.println("Error in database connection:" + ex.getMessage()); @@ -268,8 +283,9 @@ public static void runCLI(Context context, DOIOrganiser organiser, String[] args } for (DOI doi : dois) { + doi = context.reloadEntity(doi); organiser.update(doi); - context.uncacheEntity(doi); + context.commit(); } } catch (SQLException ex) { System.err.println("Error in database connection:" + ex.getMessage()); @@ -286,12 +302,17 @@ public static void runCLI(Context context, DOIOrganiser organiser, String[] args + "that could be deleted."); } - Iterator iterator = dois.iterator(); - while (iterator.hasNext()) { - DOI doi = iterator.next(); - iterator.remove(); - organiser.delete(doi.getDoi()); - context.uncacheEntity(doi); + for (DOI doi : dois) { + doi = context.reloadEntity(doi); + try { + organiser.delete(doi.getDoi()); + context.commit(); + } catch (SQLException e) { + System.err.format("DOI %s for object %s deletion failed, skipping: %s%n", + doi.getDSpaceObject().getID().toString(), + doi.getDoi(), e.getMessage()); + context.rollback(); + } } } catch (SQLException ex) { System.err.println("Error in database connection:" + ex.getMessage()); @@ -473,10 +494,10 @@ public void register(DOI doiRow, Filter filter) throws SQLException, DOIIdentifi } /** - * Register DOI with the provider + * Register DOI with the provider. * @param doiRow - doi to register - * @throws SQLException - * @throws DOIIdentifierException + * @throws SQLException passed through + * @throws DOIIdentifierException passed through */ public void register(DOI doiRow) throws SQLException, DOIIdentifierException { register(doiRow, this.filter); @@ -485,18 +506,18 @@ public void register(DOI doiRow) throws SQLException, DOIIdentifierException { /** * Reserve DOI with the provider, * @param doiRow - doi to reserve - * @throws SQLException - * @throws DOIIdentifierException */ public void reserve(DOI doiRow) { reserve(doiRow, this.filter); } /** - * Reserve DOI with the provider + * Reserve DOI with the provider. * @param doiRow - doi to reserve - * @throws SQLException - * @throws DOIIdentifierException + * @param filter - Logical item filter to determine whether this + * identifier should be reserved online. + * @throws IllegalStateException on invalid DOI. + * @throws RuntimeException on database error. */ public void reserve(DOI doiRow, Filter filter) { DSpaceObject dso = doiRow.getDSpaceObject(); From cc3841d2122e1df9595d2dade28f7614f00a0a6b Mon Sep 17 00:00:00 2001 From: "Mark H. Wood" Date: Tue, 17 Sep 2024 16:22:01 -0400 Subject: [PATCH 404/979] Reorganize some documentation. (cherry picked from commit 77425163f2d14567013fad1b8e4f73eee631bd63) --- .../dspace/identifier/doi/package-info.java | 19 +++++++---------- .../dspace/identifier/ezid/package-info.java | 21 +++++++++++++++++++ 2 files changed, 29 insertions(+), 11 deletions(-) create mode 100644 dspace-api/src/main/java/org/dspace/identifier/ezid/package-info.java diff --git a/dspace-api/src/main/java/org/dspace/identifier/doi/package-info.java b/dspace-api/src/main/java/org/dspace/identifier/doi/package-info.java index e92170daf0cf..30ee5c45dd33 100644 --- a/dspace-api/src/main/java/org/dspace/identifier/doi/package-info.java +++ b/dspace-api/src/main/java/org/dspace/identifier/doi/package-info.java @@ -6,17 +6,14 @@ * http://www.dspace.org/license/ */ /** - * Make requests to the DOI registration angencies, f.e.to - * EZID DOI service, and analyze the responses. - * + * Make requests to the DOI registration agencies and analyze the responses. + * + *

    + * {@link DOIOrganiser} is a tool for managing DOI registrations. + * *

    - * Use {@link org.dspace.identifier.ezid.EZIDRequestFactory#getInstance} to - * configure an {@link org.dspace.identifier.ezid.EZIDRequest} - * with your authority number and credentials. {@code EZIDRequest} encapsulates - * EZID's operations (lookup, create/mint, modify, delete...). - * An operation returns an {@link org.dspace.identifier.ezid.EZIDResponse} which - * gives easy access to EZID's status code and value, status of the underlying - * HTTP request, and key/value pairs found in the response body (if any). - *

    + * Classes specific to the DataCite + * registrar are here. See {@link org.dspace.identifier.ezid} for the + * EZID registrar. */ package org.dspace.identifier.doi; diff --git a/dspace-api/src/main/java/org/dspace/identifier/ezid/package-info.java b/dspace-api/src/main/java/org/dspace/identifier/ezid/package-info.java new file mode 100644 index 000000000000..bff0bdea26e0 --- /dev/null +++ b/dspace-api/src/main/java/org/dspace/identifier/ezid/package-info.java @@ -0,0 +1,21 @@ +/** + * The contents of this file are subject to the license and copyright + * detailed in the LICENSE and NOTICE files at the root of the source + * tree and available online at + * + * http://www.dspace.org/license/ + */ +/** + * DOI classes specific to the EZID registrar. + * + *

    + * Use {@link org.dspace.identifier.ezid.EZIDRequestFactory#getInstance} to + * configure an {@link org.dspace.identifier.ezid.EZIDRequest} + * with your authority number and credentials. {@code EZIDRequest} encapsulates + * EZID's operations (lookup, create/mint, modify, delete...). + * An operation returns an {@link org.dspace.identifier.ezid.EZIDResponse} which + * gives easy access to EZID's status code and value, status of the underlying + * HTTP request, and key/value pairs found in the response body (if any). + *

    + */ +package org.dspace.identifier.ezid; From 6d187a08fda16b5c24a281cc8e0e4d41dc335e66 Mon Sep 17 00:00:00 2001 From: "Mark H. Wood" Date: Tue, 21 Jan 2025 14:03:06 -0500 Subject: [PATCH 405/979] Document and handle the actual exceptions returned by register. (cherry picked from commit 2edea69ca3cbb1448557517bce25b96fbfc3bd8e) --- .../dspace/identifier/doi/DOIOrganiser.java | 33 ++++++++++++------- 1 file changed, 21 insertions(+), 12 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/identifier/doi/DOIOrganiser.java b/dspace-api/src/main/java/org/dspace/identifier/doi/DOIOrganiser.java index 88738226755d..507503ffaa15 100644 --- a/dspace-api/src/main/java/org/dspace/identifier/doi/DOIOrganiser.java +++ b/dspace-api/src/main/java/org/dspace/identifier/doi/DOIOrganiser.java @@ -264,10 +264,10 @@ public static void runCLI(Context context, DOIOrganiser organiser, String[] args } } } catch (SQLException ex) { - System.err.println("Error in database connection:" + ex.getMessage()); + System.err.format("Error in database connection: %s%n", ex.getMessage()); ex.printStackTrace(System.err); - } catch (DOIIdentifierException ex) { - System.err.println("Error registering DOI identifier:" + ex.getMessage()); + } catch (RuntimeException ex) { + System.err.format("Error registering DOI identifier: %s%n", ex.getMessage()); } } @@ -422,12 +422,18 @@ public void list(String processName, PrintStream out, PrintStream err, Integer . /** * Register DOI with the provider - * @param doiRow - doi to register - * @param filter - logical item filter to override - * @throws SQLException - * @throws DOIIdentifierException + * @param doiRow DOI to register + * @param filter logical item filter to override + * @throws IllegalArgumentException + * if {@link doiRow} does not name an Item. + * @throws IllegalStateException + * on invalid DOI. + * @throws RuntimeException + * on database error. */ - public void register(DOI doiRow, Filter filter) throws SQLException, DOIIdentifierException { + public void register(DOI doiRow, Filter filter) + throws IllegalArgumentException, IllegalStateException, + RuntimeException { DSpaceObject dso = doiRow.getDSpaceObject(); if (Constants.ITEM != dso.getType()) { throw new IllegalArgumentException("Currenty DSpace supports DOIs for Items only."); @@ -495,11 +501,14 @@ public void register(DOI doiRow, Filter filter) throws SQLException, DOIIdentifi /** * Register DOI with the provider. - * @param doiRow - doi to register - * @throws SQLException passed through - * @throws DOIIdentifierException passed through + * @param doiRow DOI to register + * @throws IllegalArgumentException passed through. + * @throws IllegalStateException passed through. + * @throws RuntimeException passed through. */ - public void register(DOI doiRow) throws SQLException, DOIIdentifierException { + public void register(DOI doiRow) + throws IllegalStateException, IllegalArgumentException, + RuntimeException { register(doiRow, this.filter); } From e11c0bce4a23fda2ab5586f274e8a4ac17d4b432 Mon Sep 17 00:00:00 2001 From: "Mark H. Wood" Date: Tue, 17 Sep 2024 14:36:06 -0400 Subject: [PATCH 406/979] On error in bulk operations, continue instead of panic-stopping. (cherry picked from commit bfeba1aa7a8119d083fd6a9e15b58bf55988d006) --- .../dspace/identifier/doi/DOIOrganiser.java | 61 +++++++++++++------ 1 file changed, 41 insertions(+), 20 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/identifier/doi/DOIOrganiser.java b/dspace-api/src/main/java/org/dspace/identifier/doi/DOIOrganiser.java index 05769e94bce5..9b12d37ce286 100644 --- a/dspace-api/src/main/java/org/dspace/identifier/doi/DOIOrganiser.java +++ b/dspace-api/src/main/java/org/dspace/identifier/doi/DOIOrganiser.java @@ -13,7 +13,6 @@ import java.sql.SQLException; import java.util.Arrays; import java.util.Date; -import java.util.Iterator; import java.util.List; import java.util.Locale; import java.util.UUID; @@ -227,8 +226,16 @@ public static void runCLI(Context context, DOIOrganiser organiser, String[] args } for (DOI doi : dois) { - organiser.reserve(doi); - context.uncacheEntity(doi); + doi = context.reloadEntity(doi); + try { + organiser.reserve(doi); + context.commit(); + } catch (RuntimeException e) { + System.err.format("DOI %s for object %s reservation failed, skipping: %s%n", + doi.getDSpaceObject().getID().toString(), + doi.getDoi(), e.getMessage()); + context.rollback(); + } } } catch (SQLException ex) { System.err.println("Error in database connection:" + ex.getMessage()); @@ -245,8 +252,16 @@ public static void runCLI(Context context, DOIOrganiser organiser, String[] args + "that could be registered."); } for (DOI doi : dois) { - organiser.register(doi); - context.uncacheEntity(doi); + doi = context.reloadEntity(doi); + try { + organiser.register(doi); + context.commit(); + } catch (SQLException e) { + System.err.format("DOI %s for object %s registration failed, skipping: %s%n", + doi.getDSpaceObject().getID().toString(), + doi.getDoi(), e.getMessage()); + context.rollback(); + } } } catch (SQLException ex) { System.err.println("Error in database connection:" + ex.getMessage()); @@ -268,8 +283,9 @@ public static void runCLI(Context context, DOIOrganiser organiser, String[] args } for (DOI doi : dois) { + doi = context.reloadEntity(doi); organiser.update(doi); - context.uncacheEntity(doi); + context.commit(); } } catch (SQLException ex) { System.err.println("Error in database connection:" + ex.getMessage()); @@ -286,12 +302,17 @@ public static void runCLI(Context context, DOIOrganiser organiser, String[] args + "that could be deleted."); } - Iterator iterator = dois.iterator(); - while (iterator.hasNext()) { - DOI doi = iterator.next(); - iterator.remove(); - organiser.delete(doi.getDoi()); - context.uncacheEntity(doi); + for (DOI doi : dois) { + doi = context.reloadEntity(doi); + try { + organiser.delete(doi.getDoi()); + context.commit(); + } catch (SQLException e) { + System.err.format("DOI %s for object %s deletion failed, skipping: %s%n", + doi.getDSpaceObject().getID().toString(), + doi.getDoi(), e.getMessage()); + context.rollback(); + } } } catch (SQLException ex) { System.err.println("Error in database connection:" + ex.getMessage()); @@ -473,10 +494,10 @@ public void register(DOI doiRow, Filter filter) throws SQLException, DOIIdentifi } /** - * Register DOI with the provider + * Register DOI with the provider. * @param doiRow - doi to register - * @throws SQLException - * @throws DOIIdentifierException + * @throws SQLException passed through + * @throws DOIIdentifierException passed through */ public void register(DOI doiRow) throws SQLException, DOIIdentifierException { register(doiRow, this.filter); @@ -485,18 +506,18 @@ public void register(DOI doiRow) throws SQLException, DOIIdentifierException { /** * Reserve DOI with the provider, * @param doiRow - doi to reserve - * @throws SQLException - * @throws DOIIdentifierException */ public void reserve(DOI doiRow) { reserve(doiRow, this.filter); } /** - * Reserve DOI with the provider + * Reserve DOI with the provider. * @param doiRow - doi to reserve - * @throws SQLException - * @throws DOIIdentifierException + * @param filter - Logical item filter to determine whether this + * identifier should be reserved online. + * @throws IllegalStateException on invalid DOI. + * @throws RuntimeException on database error. */ public void reserve(DOI doiRow, Filter filter) { DSpaceObject dso = doiRow.getDSpaceObject(); From 6f1cfa20bdc68ecb26a0825fac146a5b2cd395e3 Mon Sep 17 00:00:00 2001 From: "Mark H. Wood" Date: Tue, 17 Sep 2024 16:22:01 -0400 Subject: [PATCH 407/979] Reorganize some documentation. (cherry picked from commit 77425163f2d14567013fad1b8e4f73eee631bd63) --- .../dspace/identifier/doi/package-info.java | 19 +++++++---------- .../dspace/identifier/ezid/package-info.java | 21 +++++++++++++++++++ 2 files changed, 29 insertions(+), 11 deletions(-) create mode 100644 dspace-api/src/main/java/org/dspace/identifier/ezid/package-info.java diff --git a/dspace-api/src/main/java/org/dspace/identifier/doi/package-info.java b/dspace-api/src/main/java/org/dspace/identifier/doi/package-info.java index e92170daf0cf..30ee5c45dd33 100644 --- a/dspace-api/src/main/java/org/dspace/identifier/doi/package-info.java +++ b/dspace-api/src/main/java/org/dspace/identifier/doi/package-info.java @@ -6,17 +6,14 @@ * http://www.dspace.org/license/ */ /** - * Make requests to the DOI registration angencies, f.e.to - * EZID DOI service, and analyze the responses. - * + * Make requests to the DOI registration agencies and analyze the responses. + * + *

    + * {@link DOIOrganiser} is a tool for managing DOI registrations. + * *

    - * Use {@link org.dspace.identifier.ezid.EZIDRequestFactory#getInstance} to - * configure an {@link org.dspace.identifier.ezid.EZIDRequest} - * with your authority number and credentials. {@code EZIDRequest} encapsulates - * EZID's operations (lookup, create/mint, modify, delete...). - * An operation returns an {@link org.dspace.identifier.ezid.EZIDResponse} which - * gives easy access to EZID's status code and value, status of the underlying - * HTTP request, and key/value pairs found in the response body (if any). - *

    + * Classes specific to the DataCite + * registrar are here. See {@link org.dspace.identifier.ezid} for the + * EZID registrar. */ package org.dspace.identifier.doi; diff --git a/dspace-api/src/main/java/org/dspace/identifier/ezid/package-info.java b/dspace-api/src/main/java/org/dspace/identifier/ezid/package-info.java new file mode 100644 index 000000000000..bff0bdea26e0 --- /dev/null +++ b/dspace-api/src/main/java/org/dspace/identifier/ezid/package-info.java @@ -0,0 +1,21 @@ +/** + * The contents of this file are subject to the license and copyright + * detailed in the LICENSE and NOTICE files at the root of the source + * tree and available online at + * + * http://www.dspace.org/license/ + */ +/** + * DOI classes specific to the EZID registrar. + * + *

    + * Use {@link org.dspace.identifier.ezid.EZIDRequestFactory#getInstance} to + * configure an {@link org.dspace.identifier.ezid.EZIDRequest} + * with your authority number and credentials. {@code EZIDRequest} encapsulates + * EZID's operations (lookup, create/mint, modify, delete...). + * An operation returns an {@link org.dspace.identifier.ezid.EZIDResponse} which + * gives easy access to EZID's status code and value, status of the underlying + * HTTP request, and key/value pairs found in the response body (if any). + *

    + */ +package org.dspace.identifier.ezid; From 4d71205c684a44ae4ffa15e09ed5d0f12ca27e2d Mon Sep 17 00:00:00 2001 From: "Mark H. Wood" Date: Tue, 21 Jan 2025 14:03:06 -0500 Subject: [PATCH 408/979] Document and handle the actual exceptions returned by register. (cherry picked from commit 2edea69ca3cbb1448557517bce25b96fbfc3bd8e) --- .../dspace/identifier/doi/DOIOrganiser.java | 33 ++++++++++++------- 1 file changed, 21 insertions(+), 12 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/identifier/doi/DOIOrganiser.java b/dspace-api/src/main/java/org/dspace/identifier/doi/DOIOrganiser.java index 9b12d37ce286..fc978d3813fb 100644 --- a/dspace-api/src/main/java/org/dspace/identifier/doi/DOIOrganiser.java +++ b/dspace-api/src/main/java/org/dspace/identifier/doi/DOIOrganiser.java @@ -264,10 +264,10 @@ public static void runCLI(Context context, DOIOrganiser organiser, String[] args } } } catch (SQLException ex) { - System.err.println("Error in database connection:" + ex.getMessage()); + System.err.format("Error in database connection: %s%n", ex.getMessage()); ex.printStackTrace(System.err); - } catch (DOIIdentifierException ex) { - System.err.println("Error registering DOI identifier:" + ex.getMessage()); + } catch (RuntimeException ex) { + System.err.format("Error registering DOI identifier: %s%n", ex.getMessage()); } } @@ -422,12 +422,18 @@ public void list(String processName, PrintStream out, PrintStream err, Integer . /** * Register DOI with the provider - * @param doiRow - doi to register - * @param filter - logical item filter to override - * @throws SQLException - * @throws DOIIdentifierException + * @param doiRow DOI to register + * @param filter logical item filter to override + * @throws IllegalArgumentException + * if {@link doiRow} does not name an Item. + * @throws IllegalStateException + * on invalid DOI. + * @throws RuntimeException + * on database error. */ - public void register(DOI doiRow, Filter filter) throws SQLException, DOIIdentifierException { + public void register(DOI doiRow, Filter filter) + throws IllegalArgumentException, IllegalStateException, + RuntimeException { DSpaceObject dso = doiRow.getDSpaceObject(); if (Constants.ITEM != dso.getType()) { throw new IllegalArgumentException("Currenty DSpace supports DOIs for Items only."); @@ -495,11 +501,14 @@ public void register(DOI doiRow, Filter filter) throws SQLException, DOIIdentifi /** * Register DOI with the provider. - * @param doiRow - doi to register - * @throws SQLException passed through - * @throws DOIIdentifierException passed through + * @param doiRow DOI to register + * @throws IllegalArgumentException passed through. + * @throws IllegalStateException passed through. + * @throws RuntimeException passed through. */ - public void register(DOI doiRow) throws SQLException, DOIIdentifierException { + public void register(DOI doiRow) + throws IllegalStateException, IllegalArgumentException, + RuntimeException { register(doiRow, this.filter); } From 338e17d1973c4a910b2a3ef4e03068127070378b Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Fri, 24 Jan 2025 16:45:20 -0600 Subject: [PATCH 409/979] Remove unused configuration (cherry picked from commit 845c706cacdc13706acd120a63083764d3141194) --- dspace-api/src/test/data/dspaceFolder/config/local.cfg | 2 -- dspace/config/dspace.cfg | 2 -- 2 files changed, 4 deletions(-) diff --git a/dspace-api/src/test/data/dspaceFolder/config/local.cfg b/dspace-api/src/test/data/dspaceFolder/config/local.cfg index 35ed8a235b75..0b4fe8288cfa 100644 --- a/dspace-api/src/test/data/dspaceFolder/config/local.cfg +++ b/dspace-api/src/test/data/dspaceFolder/config/local.cfg @@ -156,8 +156,6 @@ useProxies = true proxies.trusted.ipranges = 7.7.7.7 proxies.trusted.include_ui_ip = true -csvexport.dir = dspace-server-webapp/src/test/data/dspaceFolder/exports - # For the tests we have to disable this health indicator because there isn't a mock server and the calculated status was DOWN management.health.solrOai.enabled = false diff --git a/dspace/config/dspace.cfg b/dspace/config/dspace.cfg index c21fb54655b8..14d962f56839 100644 --- a/dspace/config/dspace.cfg +++ b/dspace/config/dspace.cfg @@ -19,8 +19,6 @@ # Windows note: Please remember to use forward slashes for all paths (e.g. C:/dspace) dspace.dir = /dspace -csvexport.dir = ${dspace.dir}/exports - # Public URL of DSpace backend ('server' webapp). May require a port number if not using standard ports (80 or 443) # DO NOT end it with '/'. # This is where REST API and all enabled server modules (OAI-PMH, SWORD, SWORDv2, RDF, etc) will respond. From 3978c806b1f3eda7c8f5b60b41924a6ef212066c Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Fri, 24 Jan 2025 16:45:20 -0600 Subject: [PATCH 410/979] Remove unused configuration (cherry picked from commit 845c706cacdc13706acd120a63083764d3141194) --- dspace-api/src/test/data/dspaceFolder/config/local.cfg | 2 -- dspace/config/dspace.cfg | 2 -- 2 files changed, 4 deletions(-) diff --git a/dspace-api/src/test/data/dspaceFolder/config/local.cfg b/dspace-api/src/test/data/dspaceFolder/config/local.cfg index b44f319a35f6..43b6d05e5e42 100644 --- a/dspace-api/src/test/data/dspaceFolder/config/local.cfg +++ b/dspace-api/src/test/data/dspaceFolder/config/local.cfg @@ -156,8 +156,6 @@ useProxies = true proxies.trusted.ipranges = 7.7.7.7 proxies.trusted.include_ui_ip = true -csvexport.dir = dspace-server-webapp/src/test/data/dspaceFolder/exports - # For the tests we have to disable this health indicator because there isn't a mock server and the calculated status was DOWN management.health.solrOai.enabled = false diff --git a/dspace/config/dspace.cfg b/dspace/config/dspace.cfg index 7896560c12c8..8ea7e113bb79 100644 --- a/dspace/config/dspace.cfg +++ b/dspace/config/dspace.cfg @@ -19,8 +19,6 @@ # Windows note: Please remember to use forward slashes for all paths (e.g. C:/dspace) dspace.dir = /dspace -csvexport.dir = ${dspace.dir}/exports - # Public URL of DSpace backend ('server' webapp). May require a port number if not using standard ports (80 or 443) # DO NOT end it with '/'. # This is where REST API and all enabled server modules (OAI-PMH, SWORD, SWORDv2, RDF, etc) will respond. From 576561d6183e4a97d0e4100ba6875fc675cac06d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 27 Jan 2025 22:22:57 +0000 Subject: [PATCH 411/979] Bump dnsjava:dnsjava from 3.6.2 to 3.6.3 Bumps [dnsjava:dnsjava](https://github.com/dnsjava/dnsjava) from 3.6.2 to 3.6.3. - [Release notes](https://github.com/dnsjava/dnsjava/releases) - [Changelog](https://github.com/dnsjava/dnsjava/blob/master/Changelog) - [Commits](https://github.com/dnsjava/dnsjava/compare/v3.6.2...v3.6.3) --- updated-dependencies: - dependency-name: dnsjava:dnsjava dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- dspace-api/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dspace-api/pom.xml b/dspace-api/pom.xml index acb59fb2d492..6ed22b9177b0 100644 --- a/dspace-api/pom.xml +++ b/dspace-api/pom.xml @@ -648,7 +648,7 @@ dnsjava dnsjava - 3.6.2 + 3.6.3 From 859dd4c69d6b91a311a0f8fccf742777c915748b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 27 Jan 2025 23:07:18 +0000 Subject: [PATCH 412/979] Bump io.grpc:grpc-context from 1.69.0 to 1.70.0 Bumps [io.grpc:grpc-context](https://github.com/grpc/grpc-java) from 1.69.0 to 1.70.0. - [Release notes](https://github.com/grpc/grpc-java/releases) - [Commits](https://github.com/grpc/grpc-java/compare/v1.69.0...v1.70.0) --- updated-dependencies: - dependency-name: io.grpc:grpc-context dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index aec280f63aa3..21074f3def70 100644 --- a/pom.xml +++ b/pom.xml @@ -1725,7 +1725,7 @@ io.grpc grpc-context - 1.69.0 + 1.70.0 com.google.http-client From 3885966b4dd58cfa1ead8c6d24f53acf2fa3b3cc Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 27 Jan 2025 23:07:21 +0000 Subject: [PATCH 413/979] Bump dnsjava:dnsjava from 3.6.2 to 3.6.3 Bumps [dnsjava:dnsjava](https://github.com/dnsjava/dnsjava) from 3.6.2 to 3.6.3. - [Release notes](https://github.com/dnsjava/dnsjava/releases) - [Changelog](https://github.com/dnsjava/dnsjava/blob/master/Changelog) - [Commits](https://github.com/dnsjava/dnsjava/compare/v3.6.2...v3.6.3) --- updated-dependencies: - dependency-name: dnsjava:dnsjava dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- dspace-api/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dspace-api/pom.xml b/dspace-api/pom.xml index 77adf3b9675f..7bbcf26b6a10 100644 --- a/dspace-api/pom.xml +++ b/dspace-api/pom.xml @@ -651,7 +651,7 @@ dnsjava dnsjava - 3.6.2 + 3.6.3 From e8a54e698c9f92eb0f7e049d76049c9fbe77203e Mon Sep 17 00:00:00 2001 From: Marie Verdonck Date: Wed, 29 Jan 2025 12:42:25 +0100 Subject: [PATCH 414/979] cli speed improvement: periodically uncache entities when processing many --- .../src/main/java/org/dspace/core/Context.java | 12 ++++++++++++ .../src/main/java/org/dspace/core/DBConnection.java | 12 ++++++++++++ .../java/org/dspace/core/HibernateDBConnection.java | 6 ++++++ .../java/org/dspace/discovery/SolrServiceImpl.java | 6 ++++++ 4 files changed, 36 insertions(+) diff --git a/dspace-api/src/main/java/org/dspace/core/Context.java b/dspace-api/src/main/java/org/dspace/core/Context.java index 82b39dd2dfc7..391405c0ae66 100644 --- a/dspace-api/src/main/java/org/dspace/core/Context.java +++ b/dspace-api/src/main/java/org/dspace/core/Context.java @@ -951,4 +951,16 @@ public void setAuthenticationMethod(final String authenticationMethod) { public boolean isContextUserSwitched() { return currentUserPreviousState != null; } + + /** + * Remove all entities from the cache and reload the current user entity. This is useful when batch processing + * a large number of entities when the calling code requires the cache to be completely cleared before continuing. + * + * @throws SQLException if a database error occurs. + */ + public void uncacheEntities() throws SQLException { + dbConnection.uncacheEntities(); + reloadContextBoundEntities(); + } + } diff --git a/dspace-api/src/main/java/org/dspace/core/DBConnection.java b/dspace-api/src/main/java/org/dspace/core/DBConnection.java index cb5825eec1d9..210998902c52 100644 --- a/dspace-api/src/main/java/org/dspace/core/DBConnection.java +++ b/dspace-api/src/main/java/org/dspace/core/DBConnection.java @@ -148,4 +148,16 @@ public interface DBConnection { * @throws java.sql.SQLException passed through. */ public void uncacheEntity(E entity) throws SQLException; + + /** + * Remove all entities from the session cache. + * + *

    Entities removed from cache are not saved in any way. Therefore, if you + * have modified any entities, you should be sure to {@link #commit()} changes + * before calling this method. + * + * @throws SQLException passed through. + */ + public void uncacheEntities() throws SQLException; + } diff --git a/dspace-api/src/main/java/org/dspace/core/HibernateDBConnection.java b/dspace-api/src/main/java/org/dspace/core/HibernateDBConnection.java index 3321e4d837e5..11661ee64cc8 100644 --- a/dspace-api/src/main/java/org/dspace/core/HibernateDBConnection.java +++ b/dspace-api/src/main/java/org/dspace/core/HibernateDBConnection.java @@ -337,4 +337,10 @@ public void uncacheEntity(E entity) throws SQLExcep } } } + + @Override + public void uncacheEntities() throws SQLException { + getSession().clear(); + } + } diff --git a/dspace-api/src/main/java/org/dspace/discovery/SolrServiceImpl.java b/dspace-api/src/main/java/org/dspace/discovery/SolrServiceImpl.java index 0cf2aa50af67..8cb3c34db57f 100644 --- a/dspace-api/src/main/java/org/dspace/discovery/SolrServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/discovery/SolrServiceImpl.java @@ -72,6 +72,7 @@ import org.dspace.discovery.indexobject.IndexableItem; import org.dspace.discovery.indexobject.factory.IndexFactory; import org.dspace.discovery.indexobject.factory.IndexObjectFactoryFactory; +import org.dspace.discovery.indexobject.factory.ItemIndexFactory; import org.dspace.eperson.Group; import org.dspace.eperson.factory.EPersonServiceFactory; import org.dspace.eperson.service.GroupService; @@ -341,6 +342,7 @@ public void updateIndex(Context context, boolean force, String type) { try { final List indexableObjectServices = indexObjectServiceFactory. getIndexFactories(); + int indexObject = 0; for (IndexFactory indexableObjectService : indexableObjectServices) { if (type == null || StringUtils.equals(indexableObjectService.getType(), type)) { final Iterator indexableObjects = indexableObjectService.findAll(context); @@ -348,6 +350,10 @@ public void updateIndex(Context context, boolean force, String type) { final IndexableObject indexableObject = indexableObjects.next(); indexContent(context, indexableObject, force); context.uncacheEntity(indexableObject.getIndexedObject()); + indexObject++; + if ((indexObject % 100) == 0 && indexableObjectService instanceof ItemIndexFactory) { + context.uncacheEntities(); + } } } } From 932e1ce9de8774b4f838c90cf3e21ccb93f232bd Mon Sep 17 00:00:00 2001 From: Sascha Szott Date: Tue, 19 Nov 2024 15:08:51 +0100 Subject: [PATCH 415/979] allow remote debugging (replace CATALINA_OPTS) (cherry picked from commit 2a4beeeb8bb03af739d5092d9a3210cce3129660) --- Dockerfile.test | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Dockerfile.test b/Dockerfile.test index 1c36b31b66bb..c9627e439fd7 100644 --- a/Dockerfile.test +++ b/Dockerfile.test @@ -68,7 +68,7 @@ RUN apt-get update \ EXPOSE 8080 8000 # Give java extra memory (2GB) ENV JAVA_OPTS=-Xmx2000m -# Set up debugging -ENV CATALINA_OPTS=-Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=*:8000 +# enable JVM debugging via JDWP +ENV JAVA_TOOL_OPTIONS=-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:8000 # On startup, run DSpace Runnable JAR ENTRYPOINT ["java", "-jar", "webapps/server-boot.jar", "--dspace.dir=$DSPACE_INSTALL"] From 401e164f9ed5477646c25a387d8523e5d8b5f6ee Mon Sep 17 00:00:00 2001 From: Alphonse Bendt Date: Mon, 24 Jun 2024 16:05:54 +0300 Subject: [PATCH 416/979] Bugfix: BitstreamRestController etag/content-length calculation does not consider cover page Copied Alphonse Bendt's PR #9666 to DSpace 8 branch (squashed 5 commits). This PR fixes a bug where the etag/content-length calculation did not respect the potential existence of a coverpage. The controller now will use the post processed pdf if coverpages are enabled. --- .../app/rest/BitstreamRestController.java | 14 +- .../app/rest/utils/BitstreamResource.java | 121 ++++++++++------ .../app/rest/BitstreamRestControllerIT.java | 130 +++++++++++++++++- 3 files changed, 208 insertions(+), 57 deletions(-) diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/BitstreamRestController.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/BitstreamRestController.java index db9d26a5f6ca..11b048e23ef1 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/BitstreamRestController.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/BitstreamRestController.java @@ -135,11 +135,16 @@ public ResponseEntity retrieve(@PathVariable UUID uuid, HttpServletResponse resp long filesize = bit.getSizeBytes(); Boolean citationEnabledForBitstream = citationDocumentService.isCitationEnabledForBitstream(bit, context); + var bitstreamResource = + new org.dspace.app.rest.utils.BitstreamResource(name, uuid, + currentUser != null ? currentUser.getID() : null, + context.getSpecialGroupUuids(), citationEnabledForBitstream); + HttpHeadersInitializer httpHeadersInitializer = new HttpHeadersInitializer() .withBufferSize(BUFFER_SIZE) .withFileName(name) - .withChecksum(bit.getChecksum()) - .withLength(bit.getSizeBytes()) + .withChecksum(bitstreamResource.getChecksum()) + .withLength(bitstreamResource.contentLength()) .withMimetype(mimetype) .with(request) .with(response); @@ -157,11 +162,6 @@ public ResponseEntity retrieve(@PathVariable UUID uuid, HttpServletResponse resp httpHeadersInitializer.withDisposition(HttpHeadersInitializer.CONTENT_DISPOSITION_ATTACHMENT); } - org.dspace.app.rest.utils.BitstreamResource bitstreamResource = - new org.dspace.app.rest.utils.BitstreamResource(name, uuid, - currentUser != null ? currentUser.getID() : null, - context.getSpecialGroupUuids(), citationEnabledForBitstream); - //We have all the data we need, close the connection to the database so that it doesn't stay open during //download/streaming context.complete(); diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/utils/BitstreamResource.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/utils/BitstreamResource.java index 4e5545fabc7f..1ac6a320d9c2 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/utils/BitstreamResource.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/utils/BitstreamResource.java @@ -15,7 +15,8 @@ import java.util.UUID; import org.apache.commons.io.IOUtils; -import org.apache.commons.lang3.tuple.Pair; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.dspace.authorize.AuthorizeException; import org.dspace.content.Bitstream; import org.dspace.content.factory.ContentServiceFactory; @@ -27,6 +28,7 @@ import org.dspace.eperson.service.EPersonService; import org.dspace.utils.DSpace; import org.springframework.core.io.AbstractResource; +import org.springframework.util.DigestUtils; /** * This class acts as a {@link AbstractResource} used by Spring's framework to send the data in a proper and @@ -36,21 +38,24 @@ */ public class BitstreamResource extends AbstractResource { - private String name; - private UUID uuid; - private UUID currentUserUUID; - private boolean shouldGenerateCoverPage; - private byte[] file; - private Set currentSpecialGroups; - - private BitstreamService bitstreamService = ContentServiceFactory.getInstance().getBitstreamService(); - private EPersonService ePersonService = EPersonServiceFactory.getInstance().getEPersonService(); - private CitationDocumentService citationDocumentService = - new DSpace().getServiceManager() + private static final Logger LOG = LogManager.getLogger(BitstreamResource.class); + + private final String name; + private final UUID uuid; + private final UUID currentUserUUID; + private final boolean shouldGenerateCoverPage; + private final Set currentSpecialGroups; + + private final BitstreamService bitstreamService = ContentServiceFactory.getInstance().getBitstreamService(); + private final EPersonService ePersonService = EPersonServiceFactory.getInstance().getEPersonService(); + private final CitationDocumentService citationDocumentService = + new DSpace().getServiceManager() .getServicesByType(CitationDocumentService.class).get(0); + private BitstreamDocument document; + public BitstreamResource(String name, UUID uuid, UUID currentUserUUID, Set currentSpecialGroups, - boolean shouldGenerateCoverPage) { + boolean shouldGenerateCoverPage) { this.name = name; this.uuid = uuid; this.currentUserUUID = currentUserUUID; @@ -67,17 +72,15 @@ public BitstreamResource(String name, UUID uuid, UUID currentUserUUID, Set * @return a byte array containing the cover page */ private byte[] getCoverpageByteArray(Context context, Bitstream bitstream) - throws IOException, SQLException, AuthorizeException { - if (file == null) { - try { - Pair citedDocument = citationDocumentService.makeCitedDocument(context, bitstream); - this.file = citedDocument.getLeft(); - } catch (Exception e) { - // Return the original bitstream without the cover page - this.file = IOUtils.toByteArray(bitstreamService.retrieve(context, bitstream)); - } + throws IOException, SQLException, AuthorizeException { + try { + var citedDocument = citationDocumentService.makeCitedDocument(context, bitstream); + return citedDocument.getLeft(); + } catch (Exception e) { + LOG.warn("Could not generate cover page. Will fallback to original document", e); + // Return the original bitstream without the cover page + return IOUtils.toByteArray(bitstreamService.retrieve(context, bitstream)); } - return file; } @Override @@ -87,22 +90,9 @@ public String getDescription() { @Override public InputStream getInputStream() throws IOException { - try (Context context = initializeContext()) { - - Bitstream bitstream = bitstreamService.find(context, uuid); - InputStream out; + fetchDocument(); - if (shouldGenerateCoverPage) { - out = new ByteArrayInputStream(getCoverpageByteArray(context, bitstream)); - } else { - out = bitstreamService.retrieve(context, bitstream); - } - - this.file = null; - return out; - } catch (SQLException | AuthorizeException e) { - throw new IOException(e); - } + return document.inputStream(); } @Override @@ -111,17 +101,60 @@ public String getFilename() { } @Override - public long contentLength() throws IOException { + public long contentLength() { + fetchDocument(); + + return document.length(); + } + + public String getChecksum() { + fetchDocument(); + + return document.etag(); + } + + private void fetchDocument() { + if (document != null) { + return; + } + try (Context context = initializeContext()) { Bitstream bitstream = bitstreamService.find(context, uuid); if (shouldGenerateCoverPage) { - return getCoverpageByteArray(context, bitstream).length; + var coverPage = getCoverpageByteArray(context, bitstream); + + this.document = new BitstreamDocument(etag(bitstream), + coverPage.length, + new ByteArrayInputStream(coverPage)); } else { - return bitstream.getSizeBytes(); + this.document = new BitstreamDocument(bitstream.getChecksum(), + bitstream.getSizeBytes(), + bitstreamService.retrieve(context, bitstream)); } - } catch (SQLException | AuthorizeException e) { - throw new IOException(e); + } catch (SQLException | AuthorizeException | IOException e) { + throw new RuntimeException(e); } + + LOG.debug("fetched document {} {}", shouldGenerateCoverPage, document); + } + + private String etag(Bitstream bitstream) { + + /* Ideally we would calculate the md5 checksum based on the document with coverpage. + However it looks like the coverpage generation is not stable (e.g. if invoked twice it will return + different results). This means we cannot use it for etag calculation/comparison! + + Instead we will create the MD5 based off the original checksum plus fixed prefix. This ensures + that checksums will differ when coverpage is on/off. + However the checksum will _not_ change if the coverpage content changes. + */ + + var content = "coverpage:" + bitstream.getChecksum(); + + StringBuilder builder = new StringBuilder(37); + DigestUtils.appendMd5DigestAsHex(content.getBytes(), builder); + + return builder.toString(); } private Context initializeContext() throws SQLException { @@ -131,4 +164,6 @@ private Context initializeContext() throws SQLException { currentSpecialGroups.forEach(context::setSpecialGroup); return context; } + + private record BitstreamDocument(String etag, long length, InputStream inputStream) {} } diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/BitstreamRestControllerIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/BitstreamRestControllerIT.java index 4dca18d65d6e..691927c6e457 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/BitstreamRestControllerIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/BitstreamRestControllerIT.java @@ -23,6 +23,7 @@ import static org.hamcrest.CoreMatchers.not; import static org.hamcrest.CoreMatchers.nullValue; import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.greaterThan; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertThat; @@ -49,9 +50,11 @@ import java.io.InputStream; import java.io.StringWriter; import java.io.Writer; +import java.nio.file.Files; import java.time.Period; import java.util.UUID; +import org.apache.commons.codec.digest.DigestUtils; import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.CharEncoding; import org.apache.commons.lang3.StringUtils; @@ -91,6 +94,7 @@ import org.mockito.Mockito; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.util.ReflectionTestUtils; +import org.springframework.test.web.servlet.result.MockMvcResultHandlers; /** * Integration test to test the /api/core/bitstreams/[id]/* endpoints @@ -925,7 +929,6 @@ public void retrieveCitationCoverpageOfBitstream() throws Exception { //2. A public item with a bitstream File originalPdf = new File(testProps.getProperty("test.bitstream")); - try (InputStream is = new FileInputStream(originalPdf)) { Item publicItem1 = ItemBuilder.createItem(context, col1) @@ -949,12 +952,11 @@ public void retrieveCitationCoverpageOfBitstream() throws Exception { //** THEN ** .andExpect(status().isOk()) - //The Content Length must match the full length + // exact content-length and etag values are verified in s separate test .andExpect(header().string("Content-Length", not(nullValue()))) + .andExpect(header().string("ETag", not(nullValue()))) //The server should indicate we support Range requests .andExpect(header().string("Accept-Ranges", "bytes")) - //The ETag has to be based on the checksum - .andExpect(header().string("ETag", "\"" + bitstream.getChecksum() + "\"")) //We expect the content type to match the bitstream mime type .andExpect(content().contentType("application/pdf;charset=UTF-8")) //THe bytes of the content must match the original content @@ -963,20 +965,22 @@ public void retrieveCitationCoverpageOfBitstream() throws Exception { // The citation cover page contains the item title. // We will now verify that the pdf text contains this title. String pdfText = extractPDFText(content); - System.out.println(pdfText); assertTrue(StringUtils.contains(pdfText,"Public item citation cover page test 1")); // The dspace-api/src/test/data/dspaceFolder/assetstore/ConstitutionofIreland.pdf file contains 64 pages, // manually counted + 1 citation cover page assertEquals(65,getNumberOfPdfPages(content)); + var etagHeader = getClient().perform(get("/api/core/bitstreams/" + bitstream.getID() + "/content")) + .andReturn().getResponse().getHeader("ETag"); + //A If-None-Match HEAD request on the ETag must tell is the bitstream is not modified getClient().perform(head("/api/core/bitstreams/" + bitstream.getID() + "/content") - .header("If-None-Match", bitstream.getChecksum())) + .header("If-None-Match", etagHeader)) .andExpect(status().isNotModified()); //The download and head request should also be logged as a statistics record - checkNumberOfStatsRecords(bitstream, 2); + checkNumberOfStatsRecords(bitstream, 3); } private String extractPDFText(byte[] content) throws IOException { @@ -1384,4 +1388,116 @@ private void verifyBitstreamDownload(Bitstream file, String contentType, boolean header.contains("attachment")); } } + + @Test + public void contentLengthAndEtagUsesOriginalBitstream() throws Exception { + givenPdf(false, originalPdf -> { + var originalMd5 = md5Checksum(originalPdf); + long originalLength = Files.size(originalPdf.toPath()); + + assertThat(originalLength, greaterThan(0L)); + + getClient().perform(get("/api/core/bitstreams/" + bitstream.getID() + "/content")) + .andDo(MockMvcResultHandlers.print()) + .andExpect(status().isOk()) + .andExpect(header().longValue("Content-Length", originalLength)) + .andExpect(header().string("ETag", "\"" + originalMd5 + "\"")); + }); + } + + private static String md5Checksum(File file) throws IOException { + final String md5; + try (InputStream is = new FileInputStream(file)) { + md5 = DigestUtils.md5Hex(is); + } + return md5; + } + + @Test + public void withCoverPageContentLengthAndEtagChanges() throws Exception { + givenPdf(true, originalPdf -> { + var originalMd5 = md5Checksum(originalPdf); + long originalLength = Files.size(originalPdf.toPath()); + + getClient().perform(get("/api/core/bitstreams/" + bitstream.getID() + "/content")) + .andDo(MockMvcResultHandlers.print()) + .andExpect(status().isOk()) + .andExpect(header().string("Content-Length", not(Long.toString(originalLength)))) + .andExpect(header().string("ETag", not("\"" + originalMd5 + "\""))); + }); + } + + @Test + public void etagAndContentLengthIsStable() { + givenPdf(false, ignored -> { + var etag1 = getClient().perform(get("/api/core/bitstreams/" + bitstream.getID() + "/content")) + .andReturn().getResponse().getHeader("Etag"); + + var etag2 = getClient().perform(get("/api/core/bitstreams/" + bitstream.getID() + "/content")) + .andReturn().getResponse().getHeader("Etag"); + + assertThat(etag1, equalTo(etag2)); + }); + } + + @Test + public void withCoverPageEtagAndContentLengthIsStable() { + givenPdf(true, ignored -> { + var etag1 = getClient().perform(get("/api/core/bitstreams/" + bitstream.getID() + "/content")) + .andReturn().getResponse().getHeader("Etag"); + + var etag2 = getClient().perform(get("/api/core/bitstreams/" + bitstream.getID() + "/content")) + .andReturn().getResponse().getHeader("Etag"); + + assertThat(etag1, equalTo(etag2)); + }); + } + + @FunctionalInterface + interface ThrowingConsumer { + void accept(T t) throws Exception; + } + + private void givenPdf(boolean coverPageEnabled, ThrowingConsumer block) { + configurationService.setProperty("citation-page.enable_globally", coverPageEnabled); + + try { + citationDocumentService.afterPropertiesSet(); + context.turnOffAuthorisationSystem(); + + //** GIVEN ** + //1. A community-collection structure with one parent community and one collections. + parentCommunity = CommunityBuilder.createCommunity(context) + .withName("Parent Community") + .build(); + + Collection col1 = + CollectionBuilder.createCollection(context, parentCommunity).withName("Collection 1").build(); + + //2. A public item with a bitstream + File originalPdf = new File(testProps.getProperty("test.bitstream")); + + try (InputStream is = new FileInputStream(originalPdf)) { + Item publicItem1 = ItemBuilder.createItem(context, col1) + .withTitle("Public item citation cover page test 1") + .withIssueDate("2017-10-17") + .withAuthor("Smith, Donald").withAuthor("Doe, John") + .build(); + + bitstream = BitstreamBuilder + .createBitstream(context, publicItem1, is) + .withName("Test bitstream") + .withDescription("This is a bitstream to test the citation cover page.") + .withMimeType("application/pdf") + .build(); + } + context.restoreAuthSystemState(); + + block.accept(originalPdf); + + } catch (Exception e) { + throw new RuntimeException(e); + } + } + } From 6b4c7a773d4e16d4eebfa73539b85e6b7f44575a Mon Sep 17 00:00:00 2001 From: Jens Vannerum Date: Wed, 22 Jan 2025 13:33:58 +0100 Subject: [PATCH 417/979] 124579: If no ORIGINAL bundle exists, end the process gracefully instead of throwing NPE (cherry picked from commit 046c1b5d0561b6bca558c71265c2bbc7cb331230) --- .../src/main/java/org/dspace/ctask/general/ClamScan.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/dspace-api/src/main/java/org/dspace/ctask/general/ClamScan.java b/dspace-api/src/main/java/org/dspace/ctask/general/ClamScan.java index d1fa70565dfd..c939b67af56d 100644 --- a/dspace-api/src/main/java/org/dspace/ctask/general/ClamScan.java +++ b/dspace-api/src/main/java/org/dspace/ctask/general/ClamScan.java @@ -19,6 +19,7 @@ import java.util.ArrayList; import java.util.List; +import org.apache.commons.collections4.ListUtils; import org.dspace.authorize.AuthorizeException; import org.dspace.content.Bitstream; import org.dspace.content.Bundle; @@ -99,7 +100,12 @@ public int perform(DSpaceObject dso) throws IOException { } try { - Bundle bundle = itemService.getBundles(item, "ORIGINAL").get(0); + List bundles = itemService.getBundles(item, "ORIGINAL"); + if (ListUtils.emptyIfNull(bundles).isEmpty()) { + setResult("No ORIGINAL bundle found for item: " + getItemHandle(item)); + return Curator.CURATE_SKIP; + } + Bundle bundle = bundles.get(0); results = new ArrayList(); for (Bitstream bitstream : bundle.getBitstreams()) { InputStream inputstream = bitstreamService.retrieve(Curator.curationContext(), bitstream); From 094af861b305ff5d4dd3f0ffcdc0c3a62ea3fc91 Mon Sep 17 00:00:00 2001 From: Jens Vannerum Date: Thu, 23 Jan 2025 12:02:15 +0100 Subject: [PATCH 418/979] 124579: catch any exceptions during process and end task gracefully (cherry picked from commit 11a1c9ab06703b90c87e14843def82571fc6c51f) --- .../main/java/org/dspace/ctask/general/ClamScan.java | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/ctask/general/ClamScan.java b/dspace-api/src/main/java/org/dspace/ctask/general/ClamScan.java index c939b67af56d..6b84efe31963 100644 --- a/dspace-api/src/main/java/org/dspace/ctask/general/ClamScan.java +++ b/dspace-api/src/main/java/org/dspace/ctask/general/ClamScan.java @@ -15,12 +15,10 @@ import java.net.InetSocketAddress; import java.net.Socket; import java.net.SocketException; -import java.sql.SQLException; import java.util.ArrayList; import java.util.List; import org.apache.commons.collections4.ListUtils; -import org.dspace.authorize.AuthorizeException; import org.dspace.content.Bitstream; import org.dspace.content.Bundle; import org.dspace.content.DSpaceObject; @@ -127,10 +125,11 @@ public int perform(DSpaceObject dso) throws IOException { } } - } catch (AuthorizeException authE) { - throw new IOException(authE.getMessage(), authE); - } catch (SQLException sqlE) { - throw new IOException(sqlE.getMessage(), sqlE); + } catch (Exception e) { + // Any exception which may occur during the performance of the task should be caught here + // And end the process gracefully + log.error("Error scanning item: " + getItemHandle(item), e); + status = Curator.CURATE_ERROR; } finally { closeSession(); } From fe59557e6cf663da973cdcf82edd5617dffa336a Mon Sep 17 00:00:00 2001 From: Jens Vannerum Date: Wed, 22 Jan 2025 13:33:58 +0100 Subject: [PATCH 419/979] 124579: If no ORIGINAL bundle exists, end the process gracefully instead of throwing NPE (cherry picked from commit 046c1b5d0561b6bca558c71265c2bbc7cb331230) --- .../main/java/org/dspace/ctask/general/ClamScan.java | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/ctask/general/ClamScan.java b/dspace-api/src/main/java/org/dspace/ctask/general/ClamScan.java index 47fa6ee6452d..bfcbde189b9b 100644 --- a/dspace-api/src/main/java/org/dspace/ctask/general/ClamScan.java +++ b/dspace-api/src/main/java/org/dspace/ctask/general/ClamScan.java @@ -19,6 +19,7 @@ import java.util.ArrayList; import java.util.List; +import org.apache.commons.collections4.ListUtils; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.dspace.authorize.AuthorizeException; @@ -99,8 +100,13 @@ public int perform(DSpaceObject dso) throws IOException { } try { - Bundle bundle = itemService.getBundles(item, "ORIGINAL").get(0); - results = new ArrayList<>(); + List bundles = itemService.getBundles(item, "ORIGINAL"); + if (ListUtils.emptyIfNull(bundles).isEmpty()) { + setResult("No ORIGINAL bundle found for item: " + getItemHandle(item)); + return Curator.CURATE_SKIP; + } + Bundle bundle = bundles.get(0); + results = new ArrayList(); for (Bitstream bitstream : bundle.getBitstreams()) { InputStream inputstream = bitstreamService.retrieve(Curator.curationContext(), bitstream); logDebugMessage("Scanning " + bitstream.getName() + " . . . "); From ca6c23d2e6bf4cdd8b5199332acb646e9ae0df7a Mon Sep 17 00:00:00 2001 From: Jens Vannerum Date: Thu, 23 Jan 2025 12:02:15 +0100 Subject: [PATCH 420/979] 124579: catch any exceptions during process and end task gracefully (cherry picked from commit 11a1c9ab06703b90c87e14843def82571fc6c51f) --- .../main/java/org/dspace/ctask/general/ClamScan.java | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/ctask/general/ClamScan.java b/dspace-api/src/main/java/org/dspace/ctask/general/ClamScan.java index bfcbde189b9b..5a717fe1e479 100644 --- a/dspace-api/src/main/java/org/dspace/ctask/general/ClamScan.java +++ b/dspace-api/src/main/java/org/dspace/ctask/general/ClamScan.java @@ -15,14 +15,12 @@ import java.net.InetSocketAddress; import java.net.Socket; import java.net.SocketException; -import java.sql.SQLException; import java.util.ArrayList; import java.util.List; import org.apache.commons.collections4.ListUtils; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import org.dspace.authorize.AuthorizeException; import org.dspace.content.Bitstream; import org.dspace.content.Bundle; import org.dspace.content.DSpaceObject; @@ -127,10 +125,11 @@ public int perform(DSpaceObject dso) throws IOException { } } - } catch (AuthorizeException authE) { - throw new IOException(authE.getMessage(), authE); - } catch (SQLException sqlE) { - throw new IOException(sqlE.getMessage(), sqlE); + } catch (Exception e) { + // Any exception which may occur during the performance of the task should be caught here + // And end the process gracefully + log.error("Error scanning item: " + getItemHandle(item), e); + status = Curator.CURATE_ERROR; } finally { closeSession(); } From d198864ee04675ac54079ee065b162e4d21c98ea Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 3 Feb 2025 22:50:06 +0000 Subject: [PATCH 421/979] Bump the apache-commons group with 2 updates Bumps the apache-commons group with 2 updates: [commons-codec:commons-codec](https://github.com/apache/commons-codec) and org.apache.commons:commons-pool2. Updates `commons-codec:commons-codec` from 1.17.2 to 1.18.0 - [Changelog](https://github.com/apache/commons-codec/blob/master/RELEASE-NOTES.txt) - [Commits](https://github.com/apache/commons-codec/compare/rel/commons-codec-1.17.2...rel/commons-codec-1.18.0) Updates `org.apache.commons:commons-pool2` from 2.12.0 to 2.12.1 --- updated-dependencies: - dependency-name: commons-codec:commons-codec dependency-type: direct:production update-type: version-update:semver-minor dependency-group: apache-commons - dependency-name: org.apache.commons:commons-pool2 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: apache-commons ... Signed-off-by: dependabot[bot] --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index ba9af988ab41..c5011cfe4714 100644 --- a/pom.xml +++ b/pom.xml @@ -1472,7 +1472,7 @@ commons-codec commons-codec - 1.17.2 + 1.18.0 org.apache.commons @@ -1526,7 +1526,7 @@ org.apache.commons commons-pool2 - 2.12.0 + 2.12.1 org.apache.commons From 75efc026742a878aa0d29873eb36c20af47eb890 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 3 Feb 2025 22:51:28 +0000 Subject: [PATCH 422/979] Bump org.apache.httpcomponents.client5:httpclient5 from 5.4.1 to 5.4.2 Bumps [org.apache.httpcomponents.client5:httpclient5](https://github.com/apache/httpcomponents-client) from 5.4.1 to 5.4.2. - [Changelog](https://github.com/apache/httpcomponents-client/blob/rel/v5.4.2/RELEASE_NOTES.txt) - [Commits](https://github.com/apache/httpcomponents-client/compare/rel/v5.4.1...rel/v5.4.2) --- updated-dependencies: - dependency-name: org.apache.httpcomponents.client5:httpclient5 dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- dspace-server-webapp/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dspace-server-webapp/pom.xml b/dspace-server-webapp/pom.xml index c8bb82f0ea2e..8a2b50ad76fd 100644 --- a/dspace-server-webapp/pom.xml +++ b/dspace-server-webapp/pom.xml @@ -590,7 +590,7 @@ org.apache.httpcomponents.client5 httpclient5 - 5.4.1 + 5.4.2 test From 18edaf9cd760bf028d1fb89a8a4e52e6b4c9c437 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 3 Feb 2025 22:51:35 +0000 Subject: [PATCH 423/979] Bump com.google.code.gson:gson from 2.11.0 to 2.12.1 Bumps [com.google.code.gson:gson](https://github.com/google/gson) from 2.11.0 to 2.12.1. - [Release notes](https://github.com/google/gson/releases) - [Changelog](https://github.com/google/gson/blob/main/CHANGELOG.md) - [Commits](https://github.com/google/gson/compare/gson-parent-2.11.0...gson-parent-2.12.1) --- updated-dependencies: - dependency-name: com.google.code.gson:gson dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index ba9af988ab41..3fca3cf7969f 100644 --- a/pom.xml +++ b/pom.xml @@ -1352,7 +1352,7 @@ com.google.code.gson gson - 2.11.0 + 2.12.1 Apache Software License, Version 2.0|Similar to Apache License but with the acknowledgment clause removed - BSD License|The BSD License|BSD licence|BSD license|BSD|BSD-style license|New BSD License|New BSD license|Revised BSD License|BSD 2-Clause license|3-Clause BSD License|BSD 2-Clause|BSD 3-clause New License|BSD Licence 3|BSD-2-Clause|BSD-3-Clause|Modified BSD|The New BSD License|The BSD 3-Clause License (BSD3)|BSD License 3|BSD License 2.0 + BSD License|The BSD License|BSD licence|BSD license|BSD|BSD-style license|New BSD License|New BSD license|Revised BSD License|BSD 2-Clause license|3-Clause BSD License|BSD 2-Clause|BSD 3-clause New License|BSD Licence 3|BSD-2-Clause|BSD-3-Clause|Modified BSD|The New BSD License|The BSD 3-Clause License (BSD3)|BSD License 3|BSD License 2.0|The (New) BSD License BSD License|DSpace BSD License|DSpace Sourcecode License @@ -735,7 +735,7 @@ GNU Lesser General Public License (LGPL)|The GNU Lesser General Public License, Version 2.1|GNU Lesser General Public License (LGPL), Version 2.1|GNU LESSER GENERAL PUBLIC LICENSE, Version 2.1|GNU Lesser General Public License, version 2.1|GNU LESSER GENERAL PUBLIC LICENSE|GNU Lesser General Public License|GNU Lesser Public License|GNU Lesser General Public License, Version 2.1|Lesser General Public License (LGPL) v 2.1|LGPL 2.1|LGPL 2.1 license|LGPL 3.0 license|LGPL, v2.1 or later|LGPL|LGPL, version 2.1|lgpl|Lesser General Public License, version 3 or greater GNU Lesser General Public License (LGPL)|GNU Library General Public License v2.1 or later - MIT License|The MIT License|MIT LICENSE|MIT|MIT license|MIT License (MIT) + MIT License|The MIT License|MIT LICENSE|MIT|MIT license|MIT License (MIT)|The MIT License (MIT) MIT License|Bouncy Castle Licence From c1ca307b5319a089876778c4ca0265bba1280f56 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Fri, 7 Feb 2025 10:37:51 -0600 Subject: [PATCH 445/979] UPdate LICENSES_THIRD_PARTY for 8.1 --- LICENSES_THIRD_PARTY | 551 ++++++++++++++++++++++--------------------- pom.xml | 2 +- 2 files changed, 281 insertions(+), 272 deletions(-) diff --git a/LICENSES_THIRD_PARTY b/LICENSES_THIRD_PARTY index d7e928147c89..1d39b851ad61 100644 --- a/LICENSES_THIRD_PARTY +++ b/LICENSES_THIRD_PARTY @@ -21,56 +21,52 @@ 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.261 - https://aws.amazon.com/sdkforjava) - * AWS Java SDK for AWS KMS (com.amazonaws:aws-java-sdk-kms:1.12.261 - https://aws.amazon.com/sdkforjava) - * AWS Java SDK for Amazon S3 (com.amazonaws:aws-java-sdk-s3:1.12.261 - https://aws.amazon.com/sdkforjava) - * JMES Path Query library (com.amazonaws:jmespath-java:1.12.261 - https://aws.amazon.com/sdkforjava) + * AWS SDK for Java - Core (com.amazonaws:aws-java-sdk-core:1.12.780 - https://aws.amazon.com/sdkforjava) + * AWS Java SDK for AWS KMS (com.amazonaws:aws-java-sdk-kms:1.12.780 - https://aws.amazon.com/sdkforjava) + * AWS Java SDK for Amazon S3 (com.amazonaws:aws-java-sdk-s3:1.12.780 - https://aws.amazon.com/sdkforjava) + * JMES Path Query library (com.amazonaws:jmespath-java:1.12.780 - https://aws.amazon.com/sdkforjava) * Titanium JSON-LD 1.1 (JRE11) (com.apicatalog:titanium-json-ld:1.3.2 - https://github.com/filip26/titanium-json-ld) * 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) - * ClassMate (com.fasterxml:classmate:1.6.0 - https://github.com/FasterXML/java-classmate) - * Jackson-annotations (com.fasterxml.jackson.core:jackson-annotations:2.16.0 - https://github.com/FasterXML/jackson) - * Jackson-core (com.fasterxml.jackson.core:jackson-core:2.16.0 - https://github.com/FasterXML/jackson-core) - * jackson-databind (com.fasterxml.jackson.core:jackson-databind:2.16.0 - https://github.com/FasterXML/jackson) - * Jackson dataformat: CBOR (com.fasterxml.jackson.dataformat:jackson-dataformat-cbor:2.12.6 - http://github.com/FasterXML/jackson-dataformats-binary) + * 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.18.2 - https://github.com/FasterXML/jackson) + * Jackson-core (com.fasterxml.jackson.core:jackson-core:2.18.2 - https://github.com/FasterXML/jackson-core) + * jackson-databind (com.fasterxml.jackson.core:jackson-databind:2.18.2 - 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) * Jackson dataformat: Smile (com.fasterxml.jackson.dataformat:jackson-dataformat-smile:2.15.2 - https://github.com/FasterXML/jackson-dataformats-binary) * Jackson-dataformat-TOML (com.fasterxml.jackson.dataformat:jackson-dataformat-toml:2.15.2 - https://github.com/FasterXML/jackson-dataformats-text) * Jackson-dataformat-YAML (com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:2.16.2 - https://github.com/FasterXML/jackson-dataformats-text) - * Jackson datatype: jdk8 (com.fasterxml.jackson.datatype:jackson-datatype-jdk8:2.15.4 - https://github.com/FasterXML/jackson-modules-java8/jackson-datatype-jdk8) - * Jackson datatype: JSR310 (com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.16.0 - https://github.com/FasterXML/jackson-modules-java8/jackson-datatype-jsr310) + * Jackson datatype: jdk8 (com.fasterxml.jackson.datatype:jackson-datatype-jdk8:2.18.2 - https://github.com/FasterXML/jackson-modules-java8/jackson-datatype-jdk8) + * Jackson datatype: JSR310 (com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.18.2 - https://github.com/FasterXML/jackson-modules-java8/jackson-datatype-jsr310) * Jackson Jakarta-RS: base (com.fasterxml.jackson.jakarta.rs:jackson-jakarta-rs-base:2.16.2 - https://github.com/FasterXML/jackson-jakarta-rs-providers/jackson-jakarta-rs-base) * Jackson Jakarta-RS: JSON (com.fasterxml.jackson.jakarta.rs:jackson-jakarta-rs-json-provider:2.16.2 - https://github.com/FasterXML/jackson-jakarta-rs-providers/jackson-jakarta-rs-json-provider) * Jackson module: Jakarta XML Bind Annotations (jakarta.xml.bind) (com.fasterxml.jackson.module:jackson-module-jakarta-xmlbind-annotations:2.16.2 - https://github.com/FasterXML/jackson-modules-base) - * Jackson-module-parameter-names (com.fasterxml.jackson.module:jackson-module-parameter-names:2.15.4 - https://github.com/FasterXML/jackson-modules-java8/jackson-module-parameter-names) - * Java UUID Generator (com.fasterxml.uuid:java-uuid-generator:4.0.1 - https://github.com/cowtowncoder/java-uuid-generator) + * Jackson-module-parameter-names (com.fasterxml.jackson.module:jackson-module-parameter-names:2.18.2 - https://github.com/FasterXML/jackson-modules-java8/jackson-module-parameter-names) + * Java UUID Generator (com.fasterxml.uuid:java-uuid-generator:4.1.0 - https://github.com/cowtowncoder/java-uuid-generator) * Woodstox (com.fasterxml.woodstox:woodstox-core:6.5.1 - https://github.com/FasterXML/woodstox) * 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) - * Caffeine cache (com.github.ben-manes.caffeine:caffeine:3.1.6 - https://github.com/ben-manes/caffeine) + * Caffeine cache (com.github.ben-manes.caffeine:caffeine:3.1.8 - https://github.com/ben-manes/caffeine) + * JSON.simple (com.github.cliftonlabs:json-simple:3.0.2 - https://cliftonlabs.github.io/json-simple/) * 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) + * json-patch (com.github.java-json-tools:json-patch:1.13 - https://github.com/java-json-tools/json-patch) * json-schema-core (com.github.java-json-tools:json-schema-core:1.2.14 - https://github.com/java-json-tools/json-schema-core) * json-schema-validator (com.github.java-json-tools:json-schema-validator:2.2.14 - https://github.com/java-json-tools/json-schema-validator) * 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) * JCIP Annotations under Apache License (com.github.stephenc.jcip:jcip-annotations:1.0-1 - http://stephenc.github.com/jcip-annotations) - * Google APIs Client Library for Java (com.google.api-client:google-api-client:1.23.0 - https://github.com/google/google-api-java-client/google-api-client) - * 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.10.1 - https://github.com/google/gson/gson) - * error-prone annotations (com.google.errorprone:error_prone_annotations:2.10.0 - https://errorprone.info/error_prone_annotations) + * Gson (com.google.code.gson:gson:2.11.0 - https://github.com/google/gson) + * error-prone annotations (com.google.errorprone:error_prone_annotations:2.36.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.0.0-jre - https://github.com/google/guava) - * Guava: Google Core Libraries for Java (JDK5 Backport) (com.google.guava:guava-jdk5:17.0 - http://code.google.com/p/guava-libraries/guava-jdk5) + * Guava: Google Core Libraries for Java (com.google.guava:guava:32.1.3-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.23.0 - https://github.com/google/google-http-java-client/google-http-client) - * GSON extensions to the Google HTTP Client Library for Java. (com.google.http-client:google-http-client-gson:1.41.7 - 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.23.0 - https://github.com/google/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/) - * Google OAuth Client Library for Java (com.google.oauth-client:google-oauth-client:1.33.3 - 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.5 - https://jackcess.sourceforge.io) @@ -80,8 +76,10 @@ https://wiki.lyrasis.org/display/DSPACE/Code+Contribution+Guidelines * 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) - * Nimbus JOSE+JWT (com.nimbusds:nimbus-jose-jwt:9.37.3 - https://bitbucket.org/connect2id/nimbus-jose-jwt) - * opencsv (com.opencsv:opencsv:5.9 - http://opencsv.sf.net) + * JsonSchemaValidator (com.networknt:json-schema-validator:1.0.76 - https://github.com/networknt/json-schema-validator) + * Nimbus JOSE+JWT (com.nimbusds:nimbus-jose-jwt:9.28 - https://bitbucket.org/connect2id/nimbus-jose-jwt) + * Nimbus JOSE+JWT (com.nimbusds:nimbus-jose-jwt:9.48 - https://bitbucket.org/connect2id/nimbus-jose-jwt) + * opencsv (com.opencsv:opencsv:5.10 - 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) @@ -98,15 +96,15 @@ https://wiki.lyrasis.org/display/DSPACE/Code+Contribution+Guidelines * 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.9.4 - https://commons.apache.org/proper/commons-beanutils/) - * Apache Commons CLI (commons-cli:commons-cli:1.6.0 - https://commons.apache.org/proper/commons-cli/) - * Apache Commons Codec (commons-codec:commons-codec:1.16.0 - https://commons.apache.org/proper/commons-codec/) + * Apache Commons BeanUtils (commons-beanutils:commons-beanutils:1.10.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.17.2 - 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/) - * Apache Commons IO (commons-io:commons-io:2.15.1 - https://commons.apache.org/proper/commons-io/) + * Apache Commons IO (commons-io:commons-io:2.18.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.0 - https://commons.apache.org/proper/commons-logging/) - * Apache Commons Validator (commons-validator:commons-validator:1.7 - http://commons.apache.org/proper/commons-validator/) + * Apache Commons Logging (commons-logging:commons-logging:1.3.4 - https://commons.apache.org/proper/commons-logging/) + * Apache Commons Validator (commons-validator:commons-validator:1.9.0 - http://commons.apache.org/proper/commons-validator/) * GeoJson POJOs for Jackson (de.grundid.opendatalab:geojson-jackson:1.14 - https://github.com/opendatalab-de/geojson-jackson) * broker-client (eu.openaire:broker-client:1.1.2 - http://api.openaire.eu/broker/broker-client) * OpenAIRE Funders Model (eu.openaire:funders-model:2.0.0 - https://api.openaire.eu) @@ -116,84 +114,91 @@ https://wiki.lyrasis.org/display/DSPACE/Code+Contribution+Guidelines * 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) * SWORD v2 Common Server Library (forked) (io.gdcc:sword2-server:2.0.0 - https://github.com/gdcc/sword2-server) - * micrometer-commons (io.micrometer:micrometer-commons:1.12.6 - https://github.com/micrometer-metrics/micrometer) - * micrometer-core (io.micrometer:micrometer-core:1.12.6 - https://github.com/micrometer-metrics/micrometer) - * micrometer-jakarta9 (io.micrometer:micrometer-jakarta9:1.12.6 - https://github.com/micrometer-metrics/micrometer) - * micrometer-observation (io.micrometer:micrometer-observation:1.12.6 - https://github.com/micrometer-metrics/micrometer) - * Netty/Buffer (io.netty:netty-buffer:4.1.106.Final - https://netty.io/netty-buffer/) + * micrometer-commons (io.micrometer:micrometer-commons:1.14.2 - https://github.com/micrometer-metrics/micrometer) + * micrometer-commons (io.micrometer:micrometer-commons:1.14.3 - https://github.com/micrometer-metrics/micrometer) + * micrometer-core (io.micrometer:micrometer-core:1.14.3 - https://github.com/micrometer-metrics/micrometer) + * micrometer-jakarta9 (io.micrometer:micrometer-jakarta9:1.14.3 - https://github.com/micrometer-metrics/micrometer) + * micrometer-observation (io.micrometer:micrometer-observation:1.14.2 - https://github.com/micrometer-metrics/micrometer) + * micrometer-observation (io.micrometer:micrometer-observation:1.14.3 - https://github.com/micrometer-metrics/micrometer) * Netty/Buffer (io.netty:netty-buffer:4.1.99.Final - https://netty.io/netty-buffer/) - * Netty/Codec (io.netty:netty-codec:4.1.106.Final - https://netty.io/netty-codec/) * Netty/Codec (io.netty:netty-codec:4.1.99.Final - https://netty.io/netty-codec/) - * Netty/Codec/HTTP (io.netty:netty-codec-http:4.1.53.Final - https://netty.io/netty-codec-http/) - * Netty/Codec/Socks (io.netty:netty-codec-socks:4.1.53.Final - https://netty.io/netty-codec-socks/) - * Netty/Common (io.netty:netty-common:4.1.106.Final - https://netty.io/netty-common/) + * 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/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/Handler (io.netty:netty-handler:4.1.106.Final - https://netty.io/netty-handler/) * Netty/Handler (io.netty:netty-handler:4.1.99.Final - https://netty.io/netty-handler/) - * Netty/Handler/Proxy (io.netty:netty-handler-proxy:4.1.53.Final - https://netty.io/netty-handler-proxy/) + * 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/Transport (io.netty:netty-transport:4.1.106.Final - https://netty.io/netty-transport/) + * 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/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.106.Final - https://netty.io/netty-transport-native-unix-common/) * Netty/Transport/Native/Unix/Common (io.netty:netty-transport-native-unix-common:4.1.99.Final - https://netty.io/netty-transport-native-unix-common/) * OpenTracing API (io.opentracing:opentracing-api:0.33.0 - https://github.com/opentracing/opentracing-java/opentracing-api) * OpenTracing-noop (io.opentracing:opentracing-noop:0.33.0 - https://github.com/opentracing/opentracing-java/opentracing-noop) * OpenTracing-util (io.opentracing:opentracing-util:0.33.0 - https://github.com/opentracing/opentracing-java/opentracing-util) + * Prometheus Java Simpleclient (io.prometheus:simpleclient:0.16.0 - http://github.com/prometheus/client_java/simpleclient) + * Prometheus Java Simpleclient Common (io.prometheus:simpleclient_common:0.16.0 - http://github.com/prometheus/client_java/simpleclient_common) + * Prometheus Java Simpleclient Httpserver (io.prometheus:simpleclient_httpserver:0.16.0 - http://github.com/prometheus/client_java/simpleclient_httpserver) + * Prometheus Java Span Context Supplier - Common (io.prometheus:simpleclient_tracer_common:0.16.0 - http://github.com/prometheus/client_java/simpleclient_tracer/simpleclient_tracer_common) + * Prometheus Java Span Context Supplier - OpenTelemetry (io.prometheus:simpleclient_tracer_otel:0.16.0 - http://github.com/prometheus/client_java/simpleclient_tracer/simpleclient_tracer_otel) + * Prometheus Java Span Context Supplier - OpenTelemetry Agent (io.prometheus:simpleclient_tracer_otel_agent:0.16.0 - http://github.com/prometheus/client_java/simpleclient_tracer/simpleclient_tracer_otel_agent) * Google S2 geometry library (io.sgr:s2-geometry-library-java:1.0.0 - https://github.com/sgr-io/s2-geometry-library-java) * Jandex: Core (io.smallrye:jandex:3.1.2 - https://smallrye.io) - * swagger-annotations (io.swagger:swagger-annotations:1.6.2 - https://github.com/swagger-api/swagger-core/modules/swagger-annotations) - * swagger-compat-spec-parser (io.swagger:swagger-compat-spec-parser:1.0.52 - http://nexus.sonatype.org/oss-repository-hosting.html/swagger-parser-project/modules/swagger-compat-spec-parser) - * swagger-core (io.swagger:swagger-core:1.6.2 - https://github.com/swagger-api/swagger-core/modules/swagger-core) - * swagger-models (io.swagger:swagger-models:1.6.2 - https://github.com/swagger-api/swagger-core/modules/swagger-models) - * swagger-parser (io.swagger:swagger-parser:1.0.52 - http://nexus.sonatype.org/oss-repository-hosting.html/swagger-parser-project/modules/swagger-parser) - * swagger-annotations (io.swagger.core.v3:swagger-annotations:2.1.5 - https://github.com/swagger-api/swagger-core/modules/swagger-annotations) + * swagger-annotations (io.swagger:swagger-annotations:1.6.9 - https://github.com/swagger-api/swagger-core/modules/swagger-annotations) + * swagger-compat-spec-parser (io.swagger:swagger-compat-spec-parser:1.0.64 - http://nexus.sonatype.org/oss-repository-hosting.html/swagger-parser-project/modules/swagger-compat-spec-parser) + * swagger-core (io.swagger:swagger-core:1.6.9 - https://github.com/swagger-api/swagger-core/modules/swagger-core) + * swagger-models (io.swagger:swagger-models:1.6.9 - https://github.com/swagger-api/swagger-core/modules/swagger-models) + * swagger-parser (io.swagger:swagger-parser:1.0.64 - http://nexus.sonatype.org/oss-repository-hosting.html/swagger-parser-project/modules/swagger-parser) + * swagger-annotations (io.swagger.core.v3:swagger-annotations:2.2.8 - https://github.com/swagger-api/swagger-core/modules/swagger-annotations) * swagger-annotations-jakarta (io.swagger.core.v3:swagger-annotations-jakarta:2.2.21 - https://github.com/swagger-api/swagger-core/modules/swagger-annotations-jakarta) - * swagger-core (io.swagger.core.v3:swagger-core:2.1.5 - https://github.com/swagger-api/swagger-core/modules/swagger-core) + * swagger-core (io.swagger.core.v3:swagger-core:2.2.8 - https://github.com/swagger-api/swagger-core/modules/swagger-core) * swagger-core-jakarta (io.swagger.core.v3:swagger-core-jakarta:2.2.21 - https://github.com/swagger-api/swagger-core/modules/swagger-core-jakarta) * swagger-integration-jakarta (io.swagger.core.v3:swagger-integration-jakarta:2.2.21 - https://github.com/swagger-api/swagger-core/modules/swagger-integration-jakarta) * swagger-jaxrs2-jakarta (io.swagger.core.v3:swagger-jaxrs2-jakarta:2.2.21 - https://github.com/swagger-api/swagger-core/modules/swagger-jaxrs2-jakarta) - * swagger-models (io.swagger.core.v3:swagger-models:2.1.5 - https://github.com/swagger-api/swagger-core/modules/swagger-models) + * swagger-models (io.swagger.core.v3:swagger-models:2.2.8 - https://github.com/swagger-api/swagger-core/modules/swagger-models) * swagger-models-jakarta (io.swagger.core.v3:swagger-models-jakarta:2.2.21 - https://github.com/swagger-api/swagger-core/modules/swagger-models-jakarta) - * swagger-parser (io.swagger.parser.v3:swagger-parser:2.0.23 - http://nexus.sonatype.org/oss-repository-hosting.html/swagger-parser-project/modules/swagger-parser) - * swagger-parser (io.swagger.parser.v3:swagger-parser-core:2.0.23 - http://nexus.sonatype.org/oss-repository-hosting.html/swagger-parser-project/modules/swagger-parser-core) - * swagger-parser-v2-converter (io.swagger.parser.v3:swagger-parser-v2-converter:2.0.23 - http://nexus.sonatype.org/oss-repository-hosting.html/swagger-parser-project/modules/swagger-parser-v2-converter) - * swagger-parser-v3 (io.swagger.parser.v3:swagger-parser-v3:2.0.23 - http://nexus.sonatype.org/oss-repository-hosting.html/swagger-parser-project/modules/swagger-parser-v3) + * swagger-parser (io.swagger.parser.v3:swagger-parser:2.1.10 - http://nexus.sonatype.org/oss-repository-hosting.html/swagger-parser-project/modules/swagger-parser) + * swagger-parser (io.swagger.parser.v3:swagger-parser-core:2.1.10 - http://nexus.sonatype.org/oss-repository-hosting.html/swagger-parser-project/modules/swagger-parser-core) + * swagger-parser-v2-converter (io.swagger.parser.v3:swagger-parser-v2-converter:2.1.10 - http://nexus.sonatype.org/oss-repository-hosting.html/swagger-parser-project/modules/swagger-parser-v2-converter) + * swagger-parser-v3 (io.swagger.parser.v3:swagger-parser-v3:2.1.10 - http://nexus.sonatype.org/oss-repository-hosting.html/swagger-parser-project/modules/swagger-parser-v3) * Jakarta Dependency Injection (jakarta.inject:jakarta.inject-api:2.0.1 - https://github.com/eclipse-ee4j/injection-api) * Jakarta Bean Validation API (jakarta.validation:jakarta.validation-api:3.0.2 - https://beanvalidation.org) * 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/) - * Bean Validation API (javax.validation:validation-api:1.1.0.Final - http://beanvalidation.org) * jdbm (jdbm:jdbm:1.0 - no url defined) - * Joda-Time (joda-time:joda-time:2.12.5 - https://www.joda.org/joda-time/) - * Byte Buddy (without dependencies) (net.bytebuddy:byte-buddy:1.11.13 - https://bytebuddy.net/byte-buddy) + * Joda-Time (joda-time:joda-time:2.13.0 - https://www.joda.org/joda-time/) + * Byte Buddy (without dependencies) (net.bytebuddy:byte-buddy:1.16.1 - 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) - * json-unit-core (net.javacrumbs.json-unit:json-unit-core:2.19.0 - https://github.com/lukas-krecan/JsonUnit/json-unit-core) + * 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.1 - 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.1 - 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) * Abdera Parser (org.apache.abdera:abdera-parser:1.1.3 - http://abdera.apache.org/abdera-parser) - * Apache Ant Core (org.apache.ant:ant:1.10.14 - https://ant.apache.org/) - * Apache Ant Launcher (org.apache.ant:ant-launcher:1.10.14 - https://ant.apache.org/) - * Apache Commons BCEL (org.apache.bcel:bcel:6.7.0 - https://commons.apache.org/proper/commons-bcel) + * 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) * 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.4 - https://commons.apache.org/proper/commons-collections/) - * Apache Commons Compress (org.apache.commons:commons-compress:1.26.0 - https://commons.apache.org/proper/commons-compress/) - * Apache Commons Configuration (org.apache.commons:commons-configuration2:2.10.1 - https://commons.apache.org/proper/commons-configuration/) - * Apache Commons CSV (org.apache.commons:commons-csv:1.10.0 - https://commons.apache.org/proper/commons-csv/) - * Apache Commons DBCP (org.apache.commons:commons-dbcp2:2.11.0 - https://commons.apache.org/dbcp/) + * 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.11.0 - https://commons.apache.org/proper/commons-configuration/) + * Apache Commons CSV (org.apache.commons:commons-csv:1.13.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 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.14.0 - https://commons.apache.org/proper/commons-lang/) + * Apache Commons Lang (org.apache.commons:commons-lang3:3.17.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.0 - https://commons.apache.org/proper/commons-pool/) - * Apache Commons Text (org.apache.commons:commons-text:1.10.0 - https://commons.apache.org/proper/commons-text) + * Apache Commons Text (org.apache.commons:commons-text:1.13.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) @@ -207,79 +212,79 @@ https://wiki.lyrasis.org/display/DSPACE/Code+Contribution+Guidelines * Apache HttpCore (org.apache.httpcomponents:httpcore:4.4.16 - http://hc.apache.org/httpcomponents-core-ga) * Apache HttpClient Mime (org.apache.httpcomponents:httpmime:4.5.14 - http://hc.apache.org/httpcomponents-client-ga) * Apache HttpClient (org.apache.httpcomponents.client5:httpclient5:5.1.3 - https://hc.apache.org/httpcomponents-client-5.0.x/5.1.3/httpclient5/) - * Apache HttpClient (org.apache.httpcomponents.client5:httpclient5:5.3.1 - https://hc.apache.org/httpcomponents-client-5.0.x/5.3.1/httpclient5/) + * Apache HttpClient (org.apache.httpcomponents.client5:httpclient5:5.4.1 - https://hc.apache.org/httpcomponents-client-5.4.x/5.4.1/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/1.1 (org.apache.httpcomponents.core5:httpcore5:5.2.4 - https://hc.apache.org/httpcomponents-core-5.2.x/5.2.4/httpcore5/) + * Apache HttpComponents Core HTTP/1.1 (org.apache.httpcomponents.core5:httpcore5:5.3.1 - https://hc.apache.org/httpcomponents-core-5.3.x/5.3.1/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 HttpComponents Core HTTP/2 (org.apache.httpcomponents.core5:httpcore5-h2:5.2.4 - https://hc.apache.org/httpcomponents-core-5.2.x/5.2.4/httpcore5-h2/) - * Apache James :: Mime4j :: Core (org.apache.james:apache-mime4j-core:0.8.10 - http://james.apache.org/mime4j/apache-mime4j-core) + * Apache HttpComponents Core HTTP/2 (org.apache.httpcomponents.core5:httpcore5-h2:5.3.1 - https://hc.apache.org/httpcomponents-core-5.3.x/5.3.1/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.11 - http://james.apache.org/mime4j/apache-mime4j-dom) - * Apache Jena - Libraries POM (org.apache.jena:apache-jena-libs:4.9.0 - https://jena.apache.org/apache-jena-libs/) - * Apache Jena - ARQ (org.apache.jena:jena-arq:4.9.0 - https://jena.apache.org/jena-arq/) - * Apache Jena - Base (org.apache.jena:jena-base:4.9.0 - https://jena.apache.org/jena-base/) - * Apache Jena - Core (org.apache.jena:jena-core:4.9.0 - https://jena.apache.org/jena-core/) - * Apache Jena - DBOE Base (org.apache.jena:jena-dboe-base:4.9.0 - https://jena.apache.org/jena-dboe-base/) - * Apache Jena - DBOE Indexes (org.apache.jena:jena-dboe-index:4.9.0 - https://jena.apache.org/jena-dboe-index/) - * Apache Jena - DBOE Storage (org.apache.jena:jena-dboe-storage:4.9.0 - https://jena.apache.org/jena-dboe-storage/) - * Apache Jena - DBOE Transactional Datastructures (org.apache.jena:jena-dboe-trans-data:4.9.0 - https://jena.apache.org/jena-dboe-trans-data/) - * Apache Jena - DBOE Transactions (org.apache.jena:jena-dboe-transaction:4.9.0 - https://jena.apache.org/jena-dboe-transaction/) - * Apache Jena - IRI (org.apache.jena:jena-iri:4.9.0 - https://jena.apache.org/jena-iri/) - * Apache Jena - RDF Connection (org.apache.jena:jena-rdfconnection:4.9.0 - https://jena.apache.org/jena-rdfconnection/) - * Apache Jena - RDF Patch (org.apache.jena:jena-rdfpatch:4.9.0 - https://jena.apache.org/jena-rdfpatch/) - * Apache Jena - SHACL (org.apache.jena:jena-shacl:4.9.0 - https://jena.apache.org/jena-shacl/) - * Apache Jena - ShEx (org.apache.jena:jena-shex:4.9.0 - https://jena.apache.org/jena-shex/) - * Apache Jena - TDB1 (Native Triple Store) (org.apache.jena:jena-tdb:4.9.0 - https://jena.apache.org/jena-tdb/) - * Apache Jena - TDB2 (Native Triple Store) (org.apache.jena:jena-tdb2:4.9.0 - https://jena.apache.org/jena-tdb2/) + * Apache Jena - Libraries POM (org.apache.jena:apache-jena-libs:4.10.0 - https://jena.apache.org/apache-jena-libs/) + * Apache Jena - ARQ (org.apache.jena:jena-arq:4.10.0 - https://jena.apache.org/jena-arq/) + * Apache Jena - Base (org.apache.jena:jena-base:4.10.0 - https://jena.apache.org/jena-base/) + * Apache Jena - Core (org.apache.jena:jena-core:4.10.0 - https://jena.apache.org/jena-core/) + * Apache Jena - DBOE Base (org.apache.jena:jena-dboe-base:4.10.0 - https://jena.apache.org/jena-dboe-base/) + * Apache Jena - DBOE Indexes (org.apache.jena:jena-dboe-index:4.10.0 - https://jena.apache.org/jena-dboe-index/) + * Apache Jena - DBOE Storage (org.apache.jena:jena-dboe-storage:4.10.0 - https://jena.apache.org/jena-dboe-storage/) + * Apache Jena - DBOE Transactional Datastructures (org.apache.jena:jena-dboe-trans-data:4.10.0 - https://jena.apache.org/jena-dboe-trans-data/) + * Apache Jena - DBOE Transactions (org.apache.jena:jena-dboe-transaction:4.10.0 - https://jena.apache.org/jena-dboe-transaction/) + * Apache Jena - IRI (org.apache.jena:jena-iri:4.10.0 - https://jena.apache.org/jena-iri/) + * Apache Jena - RDF Connection (org.apache.jena:jena-rdfconnection:4.10.0 - https://jena.apache.org/jena-rdfconnection/) + * Apache Jena - RDF Patch (org.apache.jena:jena-rdfpatch:4.10.0 - https://jena.apache.org/jena-rdfpatch/) + * Apache Jena - SHACL (org.apache.jena:jena-shacl:4.10.0 - https://jena.apache.org/jena-shacl/) + * Apache Jena - ShEx (org.apache.jena:jena-shex:4.10.0 - https://jena.apache.org/jena-shex/) + * Apache Jena - TDB1 (Native Triple Store) (org.apache.jena:jena-tdb:4.10.0 - https://jena.apache.org/jena-tdb/) + * Apache Jena - TDB2 (Native Triple Store) (org.apache.jena:jena-tdb2:4.10.0 - https://jena.apache.org/jena-tdb2/) * Kerby-kerb core (org.apache.kerby:kerb-core:1.0.1 - http://directory.apache.org/kerby/kerby-kerb/kerb-core) * 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.23.1 - https://logging.apache.org/log4j/2.x/log4j/log4j-1.2-api/) - * Apache Log4j API (org.apache.logging.log4j:log4j-api:2.23.1 - https://logging.apache.org/log4j/2.x/log4j/log4j-api/) - * Apache Log4j Core (org.apache.logging.log4j:log4j-core:2.23.1 - https://logging.apache.org/log4j/2.x/log4j/log4j-core/) - * Apache Log4j JUL Adapter (org.apache.logging.log4j:log4j-jul:2.23.1 - https://logging.apache.org/log4j/2.x/log4j/log4j-jul/) + * Apache Log4j 1.x Compatibility API (org.apache.logging.log4j:log4j-1.2-api:2.17.2 - https://logging.apache.org/log4j/2.x/log4j-1.2-api/) + * Apache Log4j API (org.apache.logging.log4j:log4j-api:2.24.3 - https://logging.apache.org/log4j/2.x/log4j/log4j-api/) + * Apache Log4j Core (org.apache.logging.log4j:log4j-core:2.24.3 - https://logging.apache.org/log4j/2.x/log4j/log4j-core/) + * Apache Log4j JUL Adapter (org.apache.logging.log4j:log4j-jul:2.24.3 - https://logging.apache.org/log4j/2.x/log4j/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.23.1 - https://logging.apache.org/log4j/2.x/log4j/log4j-slf4j-impl/) - * Apache Log4j SLF4J 2.0 Binding (org.apache.logging.log4j:log4j-slf4j2-impl:2.21.1 - https://logging.apache.org/log4j/2.x/log4j/log4j-slf4j2-impl/) - * Apache Log4j Web (org.apache.logging.log4j:log4j-web:2.23.1 - https://logging.apache.org/log4j/2.x/log4j/log4j-web/) - * Lucene Common Analyzers (org.apache.lucene:lucene-analyzers-common:8.11.3 - https://lucene.apache.org/lucene-parent/lucene-analyzers-common) - * Lucene ICU Analysis Components (org.apache.lucene:lucene-analyzers-icu:8.11.3 - https://lucene.apache.org/lucene-parent/lucene-analyzers-icu) - * Lucene Kuromoji Japanese Morphological Analyzer (org.apache.lucene:lucene-analyzers-kuromoji:8.11.3 - https://lucene.apache.org/lucene-parent/lucene-analyzers-kuromoji) - * Lucene Nori Korean Morphological Analyzer (org.apache.lucene:lucene-analyzers-nori:8.11.3 - https://lucene.apache.org/lucene-parent/lucene-analyzers-nori) - * Lucene Phonetic Filters (org.apache.lucene:lucene-analyzers-phonetic:8.11.3 - https://lucene.apache.org/lucene-parent/lucene-analyzers-phonetic) - * Lucene Smart Chinese Analyzer (org.apache.lucene:lucene-analyzers-smartcn:8.11.3 - https://lucene.apache.org/lucene-parent/lucene-analyzers-smartcn) - * Lucene Stempel Analyzer (org.apache.lucene:lucene-analyzers-stempel:8.11.3 - https://lucene.apache.org/lucene-parent/lucene-analyzers-stempel) - * Lucene Memory (org.apache.lucene:lucene-backward-codecs:8.11.3 - https://lucene.apache.org/lucene-parent/lucene-backward-codecs) - * Lucene Classification (org.apache.lucene:lucene-classification:8.11.3 - https://lucene.apache.org/lucene-parent/lucene-classification) - * Lucene codecs (org.apache.lucene:lucene-codecs:8.11.3 - https://lucene.apache.org/lucene-parent/lucene-codecs) - * Lucene Core (org.apache.lucene:lucene-core:8.11.3 - https://lucene.apache.org/lucene-parent/lucene-core) - * Lucene Expressions (org.apache.lucene:lucene-expressions:8.11.3 - https://lucene.apache.org/lucene-parent/lucene-expressions) - * Lucene Grouping (org.apache.lucene:lucene-grouping:8.11.3 - https://lucene.apache.org/lucene-parent/lucene-grouping) - * Lucene Highlighter (org.apache.lucene:lucene-highlighter:8.11.3 - https://lucene.apache.org/lucene-parent/lucene-highlighter) - * Lucene Join (org.apache.lucene:lucene-join:8.11.3 - https://lucene.apache.org/lucene-parent/lucene-join) - * Lucene Memory (org.apache.lucene:lucene-memory:8.11.3 - https://lucene.apache.org/lucene-parent/lucene-memory) - * Lucene Miscellaneous (org.apache.lucene:lucene-misc:8.11.3 - https://lucene.apache.org/lucene-parent/lucene-misc) - * Lucene Queries (org.apache.lucene:lucene-queries:8.11.3 - https://lucene.apache.org/lucene-parent/lucene-queries) - * Lucene QueryParsers (org.apache.lucene:lucene-queryparser:8.11.3 - https://lucene.apache.org/lucene-parent/lucene-queryparser) - * Lucene Sandbox (org.apache.lucene:lucene-sandbox:8.11.3 - https://lucene.apache.org/lucene-parent/lucene-sandbox) - * Lucene Spatial Extras (org.apache.lucene:lucene-spatial-extras:8.11.3 - https://lucene.apache.org/lucene-parent/lucene-spatial-extras) - * Lucene Spatial 3D (org.apache.lucene:lucene-spatial3d:8.11.3 - https://lucene.apache.org/lucene-parent/lucene-spatial3d) - * Lucene Suggest (org.apache.lucene:lucene-suggest:8.11.3 - https://lucene.apache.org/lucene-parent/lucene-suggest) - * Apache FontBox (org.apache.pdfbox:fontbox:2.0.31 - http://pdfbox.apache.org/) + * Apache Log4j SLF4J Binding (org.apache.logging.log4j:log4j-slf4j-impl:2.17.2 - https://logging.apache.org/log4j/2.x/log4j-slf4j-impl/) + * SLF4J 2 Provider for Log4j API (org.apache.logging.log4j:log4j-slf4j2-impl:2.24.3 - https://logging.apache.org/log4j/2.x/log4j/log4j-slf4j2-impl/) + * Apache Log4j Web (org.apache.logging.log4j:log4j-web:2.17.2 - https://logging.apache.org/log4j/2.x/log4j-web/) + * Lucene Common Analyzers (org.apache.lucene:lucene-analyzers-common:8.11.4 - https://lucene.apache.org/lucene-parent/lucene-analyzers-common) + * Lucene ICU Analysis Components (org.apache.lucene:lucene-analyzers-icu:8.11.4 - https://lucene.apache.org/lucene-parent/lucene-analyzers-icu) + * Lucene Kuromoji Japanese Morphological Analyzer (org.apache.lucene:lucene-analyzers-kuromoji:8.11.4 - https://lucene.apache.org/lucene-parent/lucene-analyzers-kuromoji) + * Lucene Nori Korean Morphological Analyzer (org.apache.lucene:lucene-analyzers-nori:8.11.4 - https://lucene.apache.org/lucene-parent/lucene-analyzers-nori) + * Lucene Phonetic Filters (org.apache.lucene:lucene-analyzers-phonetic:8.11.4 - https://lucene.apache.org/lucene-parent/lucene-analyzers-phonetic) + * Lucene Smart Chinese Analyzer (org.apache.lucene:lucene-analyzers-smartcn:8.11.4 - https://lucene.apache.org/lucene-parent/lucene-analyzers-smartcn) + * Lucene Stempel Analyzer (org.apache.lucene:lucene-analyzers-stempel:8.11.4 - https://lucene.apache.org/lucene-parent/lucene-analyzers-stempel) + * Lucene Memory (org.apache.lucene:lucene-backward-codecs:8.11.4 - https://lucene.apache.org/lucene-parent/lucene-backward-codecs) + * Lucene Classification (org.apache.lucene:lucene-classification:8.11.4 - https://lucene.apache.org/lucene-parent/lucene-classification) + * Lucene codecs (org.apache.lucene:lucene-codecs:8.11.4 - https://lucene.apache.org/lucene-parent/lucene-codecs) + * Lucene Core (org.apache.lucene:lucene-core:8.11.4 - https://lucene.apache.org/lucene-parent/lucene-core) + * Lucene Expressions (org.apache.lucene:lucene-expressions:8.11.4 - https://lucene.apache.org/lucene-parent/lucene-expressions) + * Lucene Grouping (org.apache.lucene:lucene-grouping:8.11.4 - https://lucene.apache.org/lucene-parent/lucene-grouping) + * Lucene Highlighter (org.apache.lucene:lucene-highlighter:8.11.4 - https://lucene.apache.org/lucene-parent/lucene-highlighter) + * Lucene Join (org.apache.lucene:lucene-join:8.11.4 - https://lucene.apache.org/lucene-parent/lucene-join) + * Lucene Memory (org.apache.lucene:lucene-memory:8.11.4 - https://lucene.apache.org/lucene-parent/lucene-memory) + * Lucene Miscellaneous (org.apache.lucene:lucene-misc:8.11.4 - https://lucene.apache.org/lucene-parent/lucene-misc) + * Lucene Queries (org.apache.lucene:lucene-queries:8.11.4 - https://lucene.apache.org/lucene-parent/lucene-queries) + * Lucene QueryParsers (org.apache.lucene:lucene-queryparser:8.11.4 - https://lucene.apache.org/lucene-parent/lucene-queryparser) + * Lucene Sandbox (org.apache.lucene:lucene-sandbox:8.11.4 - https://lucene.apache.org/lucene-parent/lucene-sandbox) + * 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.33 - 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.31 - https://www.apache.org/pdfbox-parent/pdfbox/) + * Apache PDFBox (org.apache.pdfbox:pdfbox:2.0.33 - https://www.apache.org/pdfbox-parent/pdfbox/) * Apache PDFBox tools (org.apache.pdfbox:pdfbox-tools:2.0.31 - https://www.apache.org/pdfbox-parent/pdfbox-tools/) * Apache XmpBox (org.apache.pdfbox:xmpbox:2.0.31 - https://www.apache.org/pdfbox-parent/xmpbox/) * Apache POI - Common (org.apache.poi:poi:5.2.5 - https://poi.apache.org/) * Apache POI - API based on OPC and OOXML schemas (org.apache.poi:poi-ooxml:5.2.5 - https://poi.apache.org/) * Apache POI (org.apache.poi:poi-ooxml-lite:5.2.5 - https://poi.apache.org/) * Apache POI (org.apache.poi:poi-scratchpad:5.2.5 - https://poi.apache.org/) - * Apache Solr Core (org.apache.solr:solr-core:8.11.3 - https://lucene.apache.org/solr-parent/solr-core) - * Apache Solr Solrj (org.apache.solr:solr-solrj:8.11.3 - https://lucene.apache.org/solr-parent/solr-solrj) + * 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.18.1 - http://thrift.apache.org) + * Apache Thrift (org.apache.thrift:libthrift:0.19.0 - http://thrift.apache.org) * Apache Tika core (org.apache.tika:tika-core:2.9.2 - https://tika.apache.org/) * Apache Tika Apple parser module (org.apache.tika:tika-parser-apple-module:2.9.2 - https://tika.apache.org/tika-parser-apple-module/) * Apache Tika audiovideo parser module (org.apache.tika:tika-parser-audiovideo-module:2.9.2 - https://tika.apache.org/tika-parser-audiovideo-module/) @@ -304,20 +309,21 @@ https://wiki.lyrasis.org/display/DSPACE/Code+Contribution+Guidelines * Apache Tika XMP commons (org.apache.tika:tika-parser-xmp-commons:2.9.2 - https://tika.apache.org/tika-parser-xmp-commons/) * Apache Tika ZIP commons (org.apache.tika:tika-parser-zip-commons:2.9.2 - https://tika.apache.org/tika-parser-zip-commons/) * Apache Tika standard parser package (org.apache.tika:tika-parsers-standard-package:2.9.2 - https://tika.apache.org/tika-parsers/tika-parsers-standard/tika-parsers-standard-package/) - * tomcat-embed-core (org.apache.tomcat.embed:tomcat-embed-core:10.1.24 - https://tomcat.apache.org/) - * tomcat-embed-el (org.apache.tomcat.embed:tomcat-embed-el:10.1.24 - https://tomcat.apache.org/) - * tomcat-embed-websocket (org.apache.tomcat.embed:tomcat-embed-websocket:10.1.24 - https://tomcat.apache.org/) - * Apache Velocity - Engine (org.apache.velocity:velocity-engine-core:2.3 - http://velocity.apache.org/engine/devel/velocity-engine-core/) - * Apache Velocity - JSR 223 Scripting (org.apache.velocity:velocity-engine-scripting:2.2 - http://velocity.apache.org/engine/devel/velocity-engine-scripting/) + * tomcat-embed-core (org.apache.tomcat.embed:tomcat-embed-core:10.1.34 - https://tomcat.apache.org/) + * tomcat-embed-el (org.apache.tomcat.embed:tomcat-embed-el:10.1.34 - https://tomcat.apache.org/) + * tomcat-embed-websocket (org.apache.tomcat.embed:tomcat-embed-websocket:10.1.34 - https://tomcat.apache.org/) + * Apache Velocity - Engine (org.apache.velocity:velocity-engine-core:2.4.1 - http://velocity.apache.org/engine/devel/velocity-engine-core/) + * Apache Velocity - JSR 223 Scripting (org.apache.velocity:velocity-engine-scripting:2.3 - http://velocity.apache.org/engine/devel/velocity-engine-scripting/) + * Apache Velocity Tools - Generic tools (org.apache.velocity.tools:velocity-tools-generic:3.1 - https://velocity.apache.org/tools/devel/velocity-tools-generic/) * Axiom API (org.apache.ws.commons.axiom:axiom-api:1.2.14 - http://ws.apache.org/axiom/) * Axiom Impl (org.apache.ws.commons.axiom:axiom-impl:1.2.14 - http://ws.apache.org/axiom/) * XmlBeans (org.apache.xmlbeans:xmlbeans:5.2.0 - https://xmlbeans.apache.org/) * Apache ZooKeeper - Server (org.apache.zookeeper:zookeeper:3.6.2 - http://zookeeper.apache.org/zookeeper) * Apache ZooKeeper - Jute (org.apache.zookeeper:zookeeper-jute:3.6.2 - http://zookeeper.apache.org/zookeeper-jute) * org.apiguardian:apiguardian-api (org.apiguardian:apiguardian-api:1.1.2 - https://github.com/apiguardian-team/apiguardian) - * AssertJ Core (org.assertj:assertj-core:3.24.2 - https://assertj.github.io/doc/#assertj-core) + * AssertJ Core (org.assertj:assertj-core:3.26.3 - https://assertj.github.io/doc/#assertj-core) * Evo Inflector (org.atteo:evo-inflector:1.3 - http://atteo.org/static/evo-inflector) - * Awaitility (org.awaitility:awaitility:4.2.1 - http://awaitility.org) + * Awaitility (org.awaitility:awaitility:4.2.2 - http://awaitility.org) * 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/) * Woodstox (org.codehaus.woodstox:wstx-asl:3.2.6 - http://woodstox.codehaus.org) @@ -335,45 +341,48 @@ https://wiki.lyrasis.org/display/DSPACE/Code+Contribution+Guidelines * 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.15.v20190215 - http://www.eclipse.org/jetty) * Jetty :: Continuation (org.eclipse.jetty:jetty-continuation:9.4.53.v20231009 - https://eclipse.org/jetty/jetty-continuation) - * Jetty :: Deployers (org.eclipse.jetty:jetty-deploy:9.4.54.v20240208 - https://eclipse.org/jetty/jetty-deploy) - * Jetty :: Http Utility (org.eclipse.jetty:jetty-http:9.4.54.v20240208 - https://eclipse.org/jetty/jetty-http) - * Jetty :: IO Utility (org.eclipse.jetty:jetty-io:9.4.54.v20240208 - https://eclipse.org/jetty/jetty-io) + * 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 :: 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.54.v20240208 - https://eclipse.org/jetty/jetty-security) - * Jetty :: Server Core (org.eclipse.jetty:jetty-server:9.4.54.v20240208 - https://eclipse.org/jetty/jetty-server) - * Jetty :: Servlet Handling (org.eclipse.jetty:jetty-servlet:9.4.54.v20240208 - https://eclipse.org/jetty/jetty-servlet) + * 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.15.v20190215 - http://www.eclipse.org/jetty) * Jetty :: Utility Servlets and Filters (org.eclipse.jetty:jetty-servlets:9.4.53.v20231009 - https://eclipse.org/jetty/jetty-servlets) - * Jetty :: Utilities (org.eclipse.jetty:jetty-util:9.4.54.v20240208 - https://eclipse.org/jetty/jetty-util) - * Jetty :: Utilities :: Ajax(JSON) (org.eclipse.jetty:jetty-util-ajax:9.4.54.v20240208 - https://eclipse.org/jetty/jetty-util-ajax) - * Jetty :: Webapp Application Support (org.eclipse.jetty:jetty-webapp:9.4.54.v20240208 - https://eclipse.org/jetty/jetty-webapp) + * 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.53.v20231009 - https://eclipse.org/jetty/jetty-xml) - * Jetty :: XML utilities (org.eclipse.jetty:jetty-xml:9.4.54.v20240208 - https://eclipse.org/jetty/jetty-xml) + * Jetty :: XML utilities (org.eclipse.jetty:jetty-xml:9.4.57.v20241219 - https://jetty.org/jetty-xml/) * Jetty :: ALPN :: API (org.eclipse.jetty.alpn:alpn-api:1.1.3.v20160715 - http://www.eclipse.org/jetty/alpn-api) * 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.54.v20240208 - https://eclipse.org/jetty/http2-parent/http2-common) + * Jetty :: HTTP2 :: Common (org.eclipse.jetty.http2:http2-common:9.4.57.v20241219 - 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.15.v20190215 - https://eclipse.org/jetty/http2-parent/http2-server) * Jetty :: HTTP2 :: Server (org.eclipse.jetty.http2:http2-server:9.4.53.v20231009 - https://eclipse.org/jetty/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) - * flyway-core (org.flywaydb:flyway-core:10.10.0 - https://flywaydb.org/flyway-core) - * flyway-database-postgresql (org.flywaydb:flyway-database-postgresql:10.10.0 - https://flywaydb.org/flyway-database-postgresql) + * flyway-core (org.flywaydb:flyway-core:10.22.0 - https://flywaydb.org/flyway-core) + * flyway-database-postgresql (org.flywaydb:flyway-database-postgresql:10.22.0 - https://flywaydb.org/flyway-database-postgresql) * 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-common (org.glassfish.jersey.core:jersey-common:3.1.5 - https://projects.eclipse.org/projects/ee4j.jersey/jersey-common) + * jersey-core-client (org.glassfish.jersey.core:jersey-client:3.1.10 - https://projects.eclipse.org/projects/ee4j.jersey/jersey-client) + * jersey-core-common (org.glassfish.jersey.core:jersey-common:3.1.10 - https://projects.eclipse.org/projects/ee4j.jersey/jersey-common) + * jersey-inject-hk2 (org.glassfish.jersey.inject:jersey-hk2:3.1.10 - https://projects.eclipse.org/projects/ee4j.jersey/project/jersey-hk2) + * jersey-media-multipart (org.glassfish.jersey.media:jersey-media-multipart:3.1.3 - https://projects.eclipse.org/projects/ee4j.jersey/project/jersey-media-multipart) * Hibernate Validator Engine (org.hibernate.validator:hibernate-validator:8.0.1.Final - http://hibernate.org/validator/hibernate-validator) * Hibernate Validator Portable Extension (org.hibernate.validator:hibernate-validator-cdi:8.0.1.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.29.2-GA - http://www.javassist.org/) - * JBoss Logging 3 (org.jboss.logging:jboss-logging:3.4.3.Final - http://www.jboss.org) + * Javassist (org.javassist:javassist:3.30.2-GA - https://www.javassist.org/) + * JBoss Logging 3 (org.jboss.logging:jboss-logging:3.6.1.Final - http://www.jboss.org) * JDOM (org.jdom:jdom2:2.0.6.1 - http://www.jdom.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) @@ -382,77 +391,70 @@ https://wiki.lyrasis.org/display/DSPACE/Code+Contribution+Guidelines * jtwig-web (org.jtwig:jtwig-web:5.87.0.RELEASE - http://jtwig.org) * Proj4J (org.locationtech.proj4j:proj4j:1.1.5 - https://github.com/locationtech/proj4j) * Spatial4J (org.locationtech.spatial4j:spatial4j:0.7 - https://projects.eclipse.org/projects/locationtech.spatial4j) - * MockServer Java Client (org.mock-server:mockserver-client-java:5.11.2 - http://www.mock-server.com) - * MockServer Core (org.mock-server:mockserver-core:5.11.2 - http://www.mock-server.com) - * MockServer JUnit 4 Integration (org.mock-server:mockserver-junit-rule:5.11.2 - http://www.mock-server.com) - * MockServer & Proxy Netty (org.mock-server:mockserver-netty:5.11.2 - http://www.mock-server.com) - * Jetty Server (org.mortbay.jetty:jetty:6.1.26 - http://www.eclipse.org/jetty/jetty-parent/project/modules/jetty) - * 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) + * MockServer Java Client (org.mock-server:mockserver-client-java:5.15.0 - https://www.mock-server.com) + * MockServer Core (org.mock-server:mockserver-core:5.15.0 - https://www.mock-server.com) + * MockServer JUnit 4 Integration (org.mock-server:mockserver-junit-rule:5.15.0 - https://www.mock-server.com) + * MockServer & Proxy Netty (org.mock-server:mockserver-netty:5.15.0 - https://www.mock-server.com) * jwarc (org.netpreserve:jwarc:0.29.0 - https://github.com/iipc/jwarc) * Objenesis (org.objenesis:objenesis:3.2 - http://objenesis.org/objenesis) - * parboiled-core (org.parboiled:parboiled-core:1.3.1 - http://parboiled.org) - * parboiled-java (org.parboiled:parboiled-java:1.3.1 - http://parboiled.org) - * org.roaringbitmap:RoaringBitmap (org.roaringbitmap:RoaringBitmap:0.9.45 - https://github.com/RoaringBitmap/RoaringBitmap) - * org.roaringbitmap:shims (org.roaringbitmap:shims:0.9.45 - https://github.com/RoaringBitmap/RoaringBitmap) + * parboiled-core (org.parboiled:parboiled-core:1.1.7 - http://parboiled.org) + * parboiled-java (org.parboiled:parboiled-java:1.1.7 - http://parboiled.org) + * org.roaringbitmap:RoaringBitmap (org.roaringbitmap:RoaringBitmap:1.0.0 - https://github.com/RoaringBitmap/RoaringBitmap) * RRD4J (org.rrd4j:rrd4j:3.5 - https://github.com/rrd4j/rrd4j/) - * Scala Library (org.scala-lang:scala-library:2.13.11 - https://www.scala-lang.org/) + * Scala Library (org.scala-lang:scala-library:2.13.2 - 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:2.0.11 - http://www.slf4j.org) - * Spring AOP (org.springframework:spring-aop:6.1.8 - https://github.com/spring-projects/spring-framework) - * Spring Beans (org.springframework:spring-beans:6.1.8 - https://github.com/spring-projects/spring-framework) - * Spring Context (org.springframework:spring-context:6.1.8 - https://github.com/spring-projects/spring-framework) - * Spring Context Support (org.springframework:spring-context-support:6.1.8 - https://github.com/spring-projects/spring-framework) - * Spring Core (org.springframework:spring-core:6.1.8 - https://github.com/spring-projects/spring-framework) - * Spring Expression Language (SpEL) (org.springframework:spring-expression:6.1.8 - https://github.com/spring-projects/spring-framework) - * Spring Commons Logging Bridge (org.springframework:spring-jcl:6.1.8 - https://github.com/spring-projects/spring-framework) - * Spring JDBC (org.springframework:spring-jdbc:6.1.8 - https://github.com/spring-projects/spring-framework) - * Spring Object/Relational Mapping (org.springframework:spring-orm:6.1.8 - https://github.com/spring-projects/spring-framework) - * Spring TestContext Framework (org.springframework:spring-test:6.1.8 - https://github.com/spring-projects/spring-framework) - * Spring Transaction (org.springframework:spring-tx:6.1.8 - https://github.com/spring-projects/spring-framework) - * Spring Web (org.springframework:spring-web:6.1.8 - https://github.com/spring-projects/spring-framework) - * Spring Web MVC (org.springframework:spring-webmvc:6.1.8 - https://github.com/spring-projects/spring-framework) - * spring-boot (org.springframework.boot:spring-boot:3.2.6 - https://spring.io/projects/spring-boot) - * spring-boot-actuator (org.springframework.boot:spring-boot-actuator:3.2.6 - https://spring.io/projects/spring-boot) - * spring-boot-actuator-autoconfigure (org.springframework.boot:spring-boot-actuator-autoconfigure:3.2.6 - https://spring.io/projects/spring-boot) - * spring-boot-autoconfigure (org.springframework.boot:spring-boot-autoconfigure:3.2.6 - https://spring.io/projects/spring-boot) + * JSONassert (org.skyscreamer:jsonassert:1.5.3 - https://github.com/skyscreamer/JSONassert) + * JCL 1.2 implemented over SLF4J (org.slf4j:jcl-over-slf4j:2.0.16 - http://www.slf4j.org) + * Spring AOP (org.springframework:spring-aop:6.2.2 - https://github.com/spring-projects/spring-framework) + * Spring Beans (org.springframework:spring-beans:6.2.2 - https://github.com/spring-projects/spring-framework) + * Spring Context (org.springframework:spring-context:6.2.2 - https://github.com/spring-projects/spring-framework) + * Spring Context Support (org.springframework:spring-context-support:6.2.2 - https://github.com/spring-projects/spring-framework) + * Spring Core (org.springframework:spring-core:6.2.2 - https://github.com/spring-projects/spring-framework) + * Spring Expression Language (SpEL) (org.springframework:spring-expression:6.2.2 - https://github.com/spring-projects/spring-framework) + * Spring Commons Logging Bridge (org.springframework:spring-jcl:6.2.2 - https://github.com/spring-projects/spring-framework) + * Spring JDBC (org.springframework:spring-jdbc:6.2.2 - https://github.com/spring-projects/spring-framework) + * Spring Object/Relational Mapping (org.springframework:spring-orm:6.2.2 - https://github.com/spring-projects/spring-framework) + * Spring TestContext Framework (org.springframework:spring-test:6.2.2 - https://github.com/spring-projects/spring-framework) + * Spring Transaction (org.springframework:spring-tx:6.2.2 - https://github.com/spring-projects/spring-framework) + * Spring Web (org.springframework:spring-web:6.2.2 - https://github.com/spring-projects/spring-framework) + * Spring Web MVC (org.springframework:spring-webmvc:6.2.2 - https://github.com/spring-projects/spring-framework) + * spring-boot (org.springframework.boot:spring-boot:3.4.2 - https://spring.io/projects/spring-boot) + * spring-boot-actuator (org.springframework.boot:spring-boot-actuator:3.4.2 - https://spring.io/projects/spring-boot) + * spring-boot-actuator-autoconfigure (org.springframework.boot:spring-boot-actuator-autoconfigure:3.4.2 - https://spring.io/projects/spring-boot) + * spring-boot-autoconfigure (org.springframework.boot:spring-boot-autoconfigure:3.4.2 - https://spring.io/projects/spring-boot) * Spring Boot Configuration Processor (org.springframework.boot:spring-boot-configuration-processor:2.0.0.RELEASE - https://projects.spring.io/spring-boot/#/spring-boot-parent/spring-boot-tools/spring-boot-configuration-processor) - * spring-boot-starter (org.springframework.boot:spring-boot-starter:3.2.6 - https://spring.io/projects/spring-boot) - * spring-boot-starter-actuator (org.springframework.boot:spring-boot-starter-actuator:3.2.6 - https://spring.io/projects/spring-boot) - * spring-boot-starter-aop (org.springframework.boot:spring-boot-starter-aop:3.2.6 - https://spring.io/projects/spring-boot) - * spring-boot-starter-cache (org.springframework.boot:spring-boot-starter-cache:3.2.6 - https://spring.io/projects/spring-boot) - * spring-boot-starter-data-rest (org.springframework.boot:spring-boot-starter-data-rest:3.2.6 - https://spring.io/projects/spring-boot) - * spring-boot-starter-json (org.springframework.boot:spring-boot-starter-json:3.2.6 - https://spring.io/projects/spring-boot) - * spring-boot-starter-log4j2 (org.springframework.boot:spring-boot-starter-log4j2:3.2.6 - https://spring.io/projects/spring-boot) - * spring-boot-starter-security (org.springframework.boot:spring-boot-starter-security:3.2.6 - https://spring.io/projects/spring-boot) - * spring-boot-starter-test (org.springframework.boot:spring-boot-starter-test:3.2.6 - https://spring.io/projects/spring-boot) - * spring-boot-starter-tomcat (org.springframework.boot:spring-boot-starter-tomcat:3.2.6 - https://spring.io/projects/spring-boot) - * spring-boot-starter-web (org.springframework.boot:spring-boot-starter-web:3.2.6 - https://spring.io/projects/spring-boot) - * spring-boot-test (org.springframework.boot:spring-boot-test:3.2.6 - https://spring.io/projects/spring-boot) - * spring-boot-test-autoconfigure (org.springframework.boot:spring-boot-test-autoconfigure:3.2.6 - https://spring.io/projects/spring-boot) - * Spring Data Core (org.springframework.data:spring-data-commons:3.2.6 - https://spring.io/projects/spring-data) - * Spring Data REST - Core (org.springframework.data:spring-data-rest-core:4.2.6 - https://www.spring.io/spring-data/spring-data-rest-parent/spring-data-rest-core) - * Spring Data REST - WebMVC (org.springframework.data:spring-data-rest-webmvc:4.2.6 - https://www.spring.io/spring-data/spring-data-rest-parent/spring-data-rest-webmvc) - * Spring HATEOAS (org.springframework.hateoas:spring-hateoas:2.2.2 - https://github.com/spring-projects/spring-hateoas) + * spring-boot-starter (org.springframework.boot:spring-boot-starter:3.4.2 - https://spring.io/projects/spring-boot) + * spring-boot-starter-actuator (org.springframework.boot:spring-boot-starter-actuator:3.4.2 - https://spring.io/projects/spring-boot) + * spring-boot-starter-aop (org.springframework.boot:spring-boot-starter-aop:3.4.2 - https://spring.io/projects/spring-boot) + * spring-boot-starter-cache (org.springframework.boot:spring-boot-starter-cache:3.4.2 - https://spring.io/projects/spring-boot) + * spring-boot-starter-data-rest (org.springframework.boot:spring-boot-starter-data-rest:3.4.2 - https://spring.io/projects/spring-boot) + * spring-boot-starter-json (org.springframework.boot:spring-boot-starter-json:3.4.2 - https://spring.io/projects/spring-boot) + * spring-boot-starter-log4j2 (org.springframework.boot:spring-boot-starter-log4j2:3.4.2 - https://spring.io/projects/spring-boot) + * spring-boot-starter-security (org.springframework.boot:spring-boot-starter-security:3.4.2 - https://spring.io/projects/spring-boot) + * spring-boot-starter-test (org.springframework.boot:spring-boot-starter-test:3.4.2 - https://spring.io/projects/spring-boot) + * spring-boot-starter-tomcat (org.springframework.boot:spring-boot-starter-tomcat:3.4.2 - https://spring.io/projects/spring-boot) + * spring-boot-starter-web (org.springframework.boot:spring-boot-starter-web:3.4.2 - https://spring.io/projects/spring-boot) + * spring-boot-test (org.springframework.boot:spring-boot-test:3.4.2 - https://spring.io/projects/spring-boot) + * spring-boot-test-autoconfigure (org.springframework.boot:spring-boot-test-autoconfigure:3.4.2 - https://spring.io/projects/spring-boot) + * Spring Data Core (org.springframework.data:spring-data-commons:3.4.2 - https://spring.io/projects/spring-data) + * Spring Data REST - Core (org.springframework.data:spring-data-rest-core:4.4.2 - https://www.spring.io/spring-data/spring-data-rest-parent/spring-data-rest-core) + * Spring Data REST - WebMVC (org.springframework.data:spring-data-rest-webmvc:4.4.2 - https://www.spring.io/spring-data/spring-data-rest-parent/spring-data-rest-webmvc) + * Spring HATEOAS (org.springframework.hateoas:spring-hateoas:2.4.1 - https://github.com/spring-projects/spring-hateoas) * Spring Plugin - Core (org.springframework.plugin:spring-plugin-core:3.0.0 - https://github.com/spring-projects/spring-plugin/spring-plugin-core) - * spring-security-config (org.springframework.security:spring-security-config:6.2.4 - https://spring.io/projects/spring-security) - * spring-security-core (org.springframework.security:spring-security-core:6.2.4 - https://spring.io/projects/spring-security) - * spring-security-crypto (org.springframework.security:spring-security-crypto:6.2.4 - https://spring.io/projects/spring-security) - * spring-security-test (org.springframework.security:spring-security-test:6.2.4 - https://spring.io/projects/spring-security) - * spring-security-web (org.springframework.security:spring-security-web:6.2.4 - https://spring.io/projects/spring-security) + * spring-security-config (org.springframework.security:spring-security-config:6.4.2 - https://spring.io/projects/spring-security) + * spring-security-core (org.springframework.security:spring-security-core:6.4.2 - https://spring.io/projects/spring-security) + * spring-security-crypto (org.springframework.security:spring-security-crypto:6.4.2 - https://spring.io/projects/spring-security) + * spring-security-test (org.springframework.security:spring-security-test:6.4.2 - https://spring.io/projects/spring-security) + * spring-security-web (org.springframework.security:spring-security-web:6.4.2 - https://spring.io/projects/spring-security) * 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.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.8.0 - https://www.xmlunit.org/xmlunit-placeholders/) - * SnakeYAML (org.yaml:snakeyaml:2.2 - https://bitbucket.org/snakeyaml/snakeyaml) - * software.amazon.ion:ion-java (software.amazon.ion:ion-java:1.0.2 - https://github.com/amznlabs/ion-java/) + * org.xmlunit:xmlunit-placeholders (org.xmlunit:xmlunit-placeholders:2.9.1 - https://www.xmlunit.org/xmlunit-placeholders/) + * SnakeYAML (org.yaml:snakeyaml:2.3 - https://bitbucket.org/snakeyaml/snakeyaml) * Xerces2-j (xerces:xercesImpl:2.12.2 - https://xerces.apache.org/xerces2-j/) BSD License: @@ -463,25 +465,29 @@ https://wiki.lyrasis.org/display/DSPACE/Code+Contribution+Guidelines * JSONLD Java :: Core (com.github.jsonld-java:jsonld-java:0.13.4 - http://github.com/jsonld-java/jsonld-java/jsonld-java/) * curvesapi (com.github.virtuald:curvesapi:1.08 - https://github.com/virtuald/curvesapi) * Protocol Buffers [Core] (com.google.protobuf:protobuf-java:3.15.0 - https://developers.google.com/protocol-buffers/protobuf-java/) - * Protocol Buffers [Core] (com.google.protobuf:protobuf-java:3.23.3 - https://developers.google.com/protocol-buffers/protobuf-java/) + * Protocol Buffers [Core] (com.google.protobuf:protobuf-java:3.24.3 - https://developers.google.com/protocol-buffers/protobuf-java/) * JZlib (com.jcraft:jzlib:1.1.3 - http://www.jcraft.com/jzlib/) - * dnsjava (dnsjava:dnsjava:2.1.9 - http://www.dnsjava.org) + * jmustache (com.samskivert:jmustache:1.15 - http://github.com/samskivert/jmustache) + * dnsjava (dnsjava:dnsjava:3.6.2 - https://github.com/dnsjava/dnsjava) * jaxen (jaxen:jaxen:2.0.0 - http://www.cafeconleche.org/jaxen/jaxen) - * ANTLR 4 Runtime (org.antlr:antlr4-runtime:4.13.1 - https://www.antlr.org/antlr4-runtime/) + * ANTLR 4 Runtime (org.antlr:antlr4-runtime:4.13.2 - https://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:3.1.10 - https://projects.eclipse.org/projects/ee4j.jersey/jersey-client) + * jersey-inject-hk2 (org.glassfish.jersey.inject:jersey-hk2:3.1.10 - https://projects.eclipse.org/projects/ee4j.jersey/project/jersey-hk2) + * jersey-media-multipart (org.glassfish.jersey.media:jersey-media-multipart:3.1.3 - https://projects.eclipse.org/projects/ee4j.jersey/project/jersey-media-multipart) * 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/) + * HdrHistogram (org.hdrhistogram:HdrHistogram:2.2.2 - http://hdrhistogram.github.io/HdrHistogram/) * JBibTeX (org.jbibtex:jbibtex:1.0.20 - http://www.jbibtex.org) * asm (org.ow2.asm:asm:8.0.1 - http://asm.ow2.io/) * asm-analysis (org.ow2.asm:asm-analysis:8.0.1 - http://asm.ow2.io/) * asm-commons (org.ow2.asm:asm-commons:8.0.1 - http://asm.ow2.io/) * asm-tree (org.ow2.asm:asm-tree:8.0.1 - http://asm.ow2.io/) - * asm-util (org.ow2.asm:asm-util:7.1 - http://asm.ow2.org/) - * PostgreSQL JDBC Driver (org.postgresql:postgresql:42.7.3 - https://jdbc.postgresql.org) + * ASM Util (org.ow2.asm:asm-util:5.0.3 - http://asm.objectweb.org/asm-util/) + * PostgreSQL JDBC Driver (org.postgresql:postgresql:42.7.5 - 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) * XMLUnit for Java (xmlunit:xmlunit:1.3 - http://xmlunit.sourceforge.net/) @@ -497,7 +503,7 @@ https://wiki.lyrasis.org/display/DSPACE/Code+Contribution+Guidelines * Old JAXB Runtime (com.sun.xml.bind:jaxb-impl:2.3.1 - http://jaxb.java.net/jaxb-bundles/jaxb-impl) * Jakarta Annotations API (jakarta.annotation:jakarta.annotation-api:2.1.1 - https://projects.eclipse.org/projects/ee4j.ca) * Jakarta Mail API (jakarta.mail:jakarta.mail-api:2.1.3 - https://projects.eclipse.org/projects/ee4j/jakarta.mail-api) - * Jakarta Servlet (jakarta.servlet:jakarta.servlet-api:6.0.0 - https://projects.eclipse.org/projects/ee4j.servlet) + * Jakarta Servlet (jakarta.servlet:jakarta.servlet-api:6.1.0 - https://projects.eclipse.org/projects/ee4j.servlet) * jakarta.transaction API (jakarta.transaction:jakarta.transaction-api:2.0.1 - https://projects.eclipse.org/projects/ee4j.jta) * JavaBeans Activation Framework API jar (javax.activation:javax.activation-api:1.2.0 - http://java.net/all/javax.activation-api/) * javax.annotation API (javax.annotation:javax.annotation-api:1.3 - http://jcp.org/en/jsr/detail?id=250) @@ -506,13 +512,13 @@ https://wiki.lyrasis.org/display/DSPACE/Code+Contribution+Guidelines * 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) * Angus Mail default provider (org.eclipse.angus:jakarta.mail:2.0.3 - http://eclipse-ee4j.github.io/angus-mail/jakarta.mail) - * HK2 API module (org.glassfish.hk2:hk2-api:3.0.5 - https://github.com/eclipse-ee4j/glassfish-hk2/hk2-api) - * ServiceLocator Default Implementation (org.glassfish.hk2:hk2-locator:3.0.5 - https://github.com/eclipse-ee4j/glassfish-hk2/hk2-locator) - * HK2 Implementation Utilities (org.glassfish.hk2:hk2-utils:3.0.5 - https://github.com/eclipse-ee4j/glassfish-hk2/hk2-utils) + * HK2 API module (org.glassfish.hk2:hk2-api:3.0.6 - https://github.com/eclipse-ee4j/glassfish-hk2/hk2-api) + * ServiceLocator Default Implementation (org.glassfish.hk2:hk2-locator:3.0.6 - https://github.com/eclipse-ee4j/glassfish-hk2/hk2-locator) + * HK2 Implementation Utilities (org.glassfish.hk2:hk2-utils:3.0.6 - 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:3.0.5 - https://github.com/eclipse-ee4j/glassfish-hk2/external/aopalliance-repackaged) - * jersey-core-client (org.glassfish.jersey.core:jersey-client:3.1.5 - https://projects.eclipse.org/projects/ee4j.jersey/jersey-client) - * jersey-inject-hk2 (org.glassfish.jersey.inject:jersey-hk2:3.1.5 - https://projects.eclipse.org/projects/ee4j.jersey/project/jersey-hk2) + * aopalliance version 1.0 repackaged as a module (org.glassfish.hk2.external:aopalliance-repackaged:3.0.6 - https://github.com/eclipse-ee4j/glassfish-hk2/external/aopalliance-repackaged) + * jersey-core-client (org.glassfish.jersey.core:jersey-client:3.1.10 - https://projects.eclipse.org/projects/ee4j.jersey/jersey-client) + * jersey-inject-hk2 (org.glassfish.jersey.inject:jersey-hk2:3.1.10 - https://projects.eclipse.org/projects/ee4j.jersey/project/jersey-hk2) * jersey-media-multipart (org.glassfish.jersey.media:jersey-media-multipart:3.1.3 - https://projects.eclipse.org/projects/ee4j.jersey/project/jersey-media-multipart) Cordra (Version 2) License Agreement: @@ -536,8 +542,8 @@ https://wiki.lyrasis.org/display/DSPACE/Code+Contribution+Guidelines * JAXB Core (org.glassfish.jaxb:jaxb-core:4.0.5 - https://eclipse-ee4j.github.io/jaxb-ri/) * JAXB Runtime (org.glassfish.jaxb:jaxb-runtime:4.0.5 - https://eclipse-ee4j.github.io/jaxb-ri/) * TXW2 Runtime (org.glassfish.jaxb:txw2:4.0.5 - https://eclipse-ee4j.github.io/jaxb-ri/) - * jersey-core-client (org.glassfish.jersey.core:jersey-client:3.1.5 - https://projects.eclipse.org/projects/ee4j.jersey/jersey-client) - * jersey-inject-hk2 (org.glassfish.jersey.inject:jersey-hk2:3.1.5 - https://projects.eclipse.org/projects/ee4j.jersey/project/jersey-hk2) + * jersey-core-client (org.glassfish.jersey.core:jersey-client:3.1.10 - https://projects.eclipse.org/projects/ee4j.jersey/jersey-client) + * jersey-inject-hk2 (org.glassfish.jersey.inject:jersey-hk2:3.1.10 - https://projects.eclipse.org/projects/ee4j.jersey/project/jersey-hk2) * jersey-media-multipart (org.glassfish.jersey.media:jersey-media-multipart:3.1.3 - https://projects.eclipse.org/projects/ee4j.jersey/project/jersey-media-multipart) * MIME streaming extension (org.jvnet.mimepull:mimepull:1.9.15 - https://github.com/eclipse-ee4j/metro-mimepull) * org.locationtech.jts:jts-core (org.locationtech.jts:jts-core:1.19.0 - https://www.locationtech.org/projects/technology.jts/jts-modules/jts-core) @@ -546,16 +552,16 @@ 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.2.224 - https://h2database.com) + * H2 Database Engine (com.h2database:h2:2.3.232 - https://h2database.com) * Jakarta Annotations API (jakarta.annotation:jakarta.annotation-api:2.1.1 - https://projects.eclipse.org/projects/ee4j.ca) * Jakarta Expression Language API (jakarta.el:jakarta.el-api:5.0.1 - https://projects.eclipse.org/projects/ee4j.el) * Jakarta Mail API (jakarta.mail:jakarta.mail-api:2.1.3 - https://projects.eclipse.org/projects/ee4j/jakarta.mail-api) * Jakarta Persistence API (jakarta.persistence:jakarta.persistence-api:3.1.0 - https://github.com/eclipse-ee4j/jpa-api) - * Jakarta Servlet (jakarta.servlet:jakarta.servlet-api:6.0.0 - https://projects.eclipse.org/projects/ee4j.servlet) + * Jakarta Servlet (jakarta.servlet:jakarta.servlet-api:6.1.0 - https://projects.eclipse.org/projects/ee4j.servlet) * jakarta.transaction API (jakarta.transaction:jakarta.transaction-api:2.0.1 - https://projects.eclipse.org/projects/ee4j.jta) * Jakarta RESTful WS API (jakarta.ws.rs:jakarta.ws.rs-api:3.1.0 - https://github.com/eclipse-ee4j/jaxrs-api) * JUnit (junit:junit:4.13.2 - http://junit.org) - * AspectJ Weaver (org.aspectj:aspectjweaver:1.9.22 - https://www.eclipse.org/aspectj/) + * AspectJ Weaver (org.aspectj:aspectjweaver:1.9.22.1 - https://www.eclipse.org/aspectj/) * Angus Mail default provider (org.eclipse.angus:jakarta.mail:2.0.3 - http://eclipse-ee4j.github.io/angus-mail/jakarta.mail) * 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/) @@ -569,53 +575,59 @@ https://wiki.lyrasis.org/display/DSPACE/Code+Contribution+Guidelines * 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.15.v20190215 - http://www.eclipse.org/jetty) * Jetty :: Continuation (org.eclipse.jetty:jetty-continuation:9.4.53.v20231009 - https://eclipse.org/jetty/jetty-continuation) - * Jetty :: Deployers (org.eclipse.jetty:jetty-deploy:9.4.54.v20240208 - https://eclipse.org/jetty/jetty-deploy) - * Jetty :: Http Utility (org.eclipse.jetty:jetty-http:9.4.54.v20240208 - https://eclipse.org/jetty/jetty-http) - * Jetty :: IO Utility (org.eclipse.jetty:jetty-io:9.4.54.v20240208 - https://eclipse.org/jetty/jetty-io) + * 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 :: 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.54.v20240208 - https://eclipse.org/jetty/jetty-security) - * Jetty :: Server Core (org.eclipse.jetty:jetty-server:9.4.54.v20240208 - https://eclipse.org/jetty/jetty-server) - * Jetty :: Servlet Handling (org.eclipse.jetty:jetty-servlet:9.4.54.v20240208 - https://eclipse.org/jetty/jetty-servlet) + * 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.15.v20190215 - http://www.eclipse.org/jetty) * Jetty :: Utility Servlets and Filters (org.eclipse.jetty:jetty-servlets:9.4.53.v20231009 - https://eclipse.org/jetty/jetty-servlets) - * Jetty :: Utilities (org.eclipse.jetty:jetty-util:9.4.54.v20240208 - https://eclipse.org/jetty/jetty-util) - * Jetty :: Utilities :: Ajax(JSON) (org.eclipse.jetty:jetty-util-ajax:9.4.54.v20240208 - https://eclipse.org/jetty/jetty-util-ajax) - * Jetty :: Webapp Application Support (org.eclipse.jetty:jetty-webapp:9.4.54.v20240208 - https://eclipse.org/jetty/jetty-webapp) + * 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.53.v20231009 - https://eclipse.org/jetty/jetty-xml) - * Jetty :: XML utilities (org.eclipse.jetty:jetty-xml:9.4.54.v20240208 - https://eclipse.org/jetty/jetty-xml) + * Jetty :: XML utilities (org.eclipse.jetty:jetty-xml:9.4.57.v20241219 - https://jetty.org/jetty-xml/) * Jetty :: ALPN :: API (org.eclipse.jetty.alpn:alpn-api:1.1.3.v20160715 - http://www.eclipse.org/jetty/alpn-api) * 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.54.v20240208 - https://eclipse.org/jetty/http2-parent/http2-common) + * Jetty :: HTTP2 :: Common (org.eclipse.jetty.http2:http2-common:9.4.57.v20241219 - 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.15.v20190215 - https://eclipse.org/jetty/http2-parent/http2-server) * Jetty :: HTTP2 :: Server (org.eclipse.jetty.http2:http2-server:9.4.53.v20231009 - https://eclipse.org/jetty/http2-parent/http2-server) * Jetty :: Schemas (org.eclipse.jetty.toolchain:jetty-schemas:3.1.2 - https://eclipse.org/jetty/jetty-schemas) * JSON-P Default Provider (org.glassfish:jakarta.json:2.0.1 - https://github.com/eclipse-ee4j/jsonp) - * HK2 API module (org.glassfish.hk2:hk2-api:3.0.5 - https://github.com/eclipse-ee4j/glassfish-hk2/hk2-api) - * ServiceLocator Default Implementation (org.glassfish.hk2:hk2-locator:3.0.5 - https://github.com/eclipse-ee4j/glassfish-hk2/hk2-locator) - * HK2 Implementation Utilities (org.glassfish.hk2:hk2-utils:3.0.5 - https://github.com/eclipse-ee4j/glassfish-hk2/hk2-utils) + * HK2 API module (org.glassfish.hk2:hk2-api:3.0.6 - https://github.com/eclipse-ee4j/glassfish-hk2/hk2-api) + * ServiceLocator Default Implementation (org.glassfish.hk2:hk2-locator:3.0.6 - https://github.com/eclipse-ee4j/glassfish-hk2/hk2-locator) + * HK2 Implementation Utilities (org.glassfish.hk2:hk2-utils:3.0.6 - 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:3.0.5 - https://github.com/eclipse-ee4j/glassfish-hk2/external/aopalliance-repackaged) - * jersey-core-client (org.glassfish.jersey.core:jersey-client:3.1.5 - https://projects.eclipse.org/projects/ee4j.jersey/jersey-client) - * jersey-core-common (org.glassfish.jersey.core:jersey-common:3.1.5 - https://projects.eclipse.org/projects/ee4j.jersey/jersey-common) - * jersey-inject-hk2 (org.glassfish.jersey.inject:jersey-hk2:3.1.5 - https://projects.eclipse.org/projects/ee4j.jersey/project/jersey-hk2) + * aopalliance version 1.0 repackaged as a module (org.glassfish.hk2.external:aopalliance-repackaged:3.0.6 - https://github.com/eclipse-ee4j/glassfish-hk2/external/aopalliance-repackaged) + * jersey-core-client (org.glassfish.jersey.core:jersey-client:3.1.10 - https://projects.eclipse.org/projects/ee4j.jersey/jersey-client) + * jersey-core-common (org.glassfish.jersey.core:jersey-common:3.1.10 - https://projects.eclipse.org/projects/ee4j.jersey/jersey-common) + * jersey-inject-hk2 (org.glassfish.jersey.inject:jersey-hk2:3.1.10 - https://projects.eclipse.org/projects/ee4j.jersey/project/jersey-hk2) * jersey-media-multipart (org.glassfish.jersey.media:jersey-media-multipart:3.1.3 - https://projects.eclipse.org/projects/ee4j.jersey/project/jersey-media-multipart) * 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) - * 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) + + GENERAL PUBLIC LICENSE, version 3 (GPL-3.0): + + * juniversalchardet (com.github.albfernandez:juniversalchardet:2.4.0 - https://github.com/albfernandez/juniversalchardet) + + GNU LESSER GENERAL PUBLIC LICENSE, version 3 (LGPL-3.0): + + * juniversalchardet (com.github.albfernandez:juniversalchardet:2.4.0 - https://github.com/albfernandez/juniversalchardet) GNU Lesser General Public License (LGPL): * 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) + * json-patch (com.github.java-json-tools:json-patch:1.13 - https://github.com/java-json-tools/json-patch) * json-schema-core (com.github.java-json-tools:json-schema-core:1.2.14 - https://github.com/java-json-tools/json-schema-core) * json-schema-validator (com.github.java-json-tools:json-schema-validator:2.2.14 - https://github.com/java-json-tools/json-schema-validator) * msg-simple (com.github.java-json-tools:msg-simple:1.2 - https://github.com/java-json-tools/msg-simple) @@ -627,7 +639,7 @@ https://wiki.lyrasis.org/display/DSPACE/Code+Contribution+Guidelines * Hibernate ORM - hibernate-jcache (org.hibernate.orm:hibernate-jcache:6.4.8.Final - https://hibernate.org/orm) * Hibernate ORM - hibernate-jpamodelgen (org.hibernate.orm:hibernate-jpamodelgen:6.4.8.Final - https://hibernate.org/orm) * im4java (org.im4java:im4java:1.4.0 - http://sourceforge.net/projects/im4java/) - * Javassist (org.javassist:javassist:3.29.2-GA - http://www.javassist.org/) + * Javassist (org.javassist:javassist:3.30.2-GA - https://www.javassist.org/) * XOM (xom:xom:1.3.9 - https://xom.nu) Go License: @@ -648,25 +660,22 @@ https://wiki.lyrasis.org/display/DSPACE/Code+Contribution+Guidelines * 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) - * DigitalCollections: IIIF API Library (de.digitalcollections.iiif:iiif-apis:0.3.10 - https://github.com/dbmdz/iiif-apis) + * 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.165 - 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 S/MIME API (org.bouncycastle:bcmail-jdk18on:1.77 - https://www.bouncycastle.org/java.html) - * Bouncy Castle PKIX, CMS, EAC, TSP, PKCS, OCSP, CMP, and CRMF APIs (org.bouncycastle:bcpkix-jdk15on:1.67 - http://www.bouncycastle.org/java.html) - * Bouncy Castle PKIX, CMS, EAC, TSP, PKCS, OCSP, CMP, and CRMF APIs (org.bouncycastle:bcpkix-jdk18on:1.78.1 - https://www.bouncycastle.org/java.html) - * Bouncy Castle Provider (org.bouncycastle:bcprov-jdk15on:1.67 - http://www.bouncycastle.org/java.html) - * Bouncy Castle Provider (org.bouncycastle:bcprov-jdk18on:1.78.1 - https://www.bouncycastle.org/java.html) - * Bouncy Castle ASN.1 Extension and Utility APIs (org.bouncycastle:bcutil-jdk18on:1.78.1 - https://www.bouncycastle.org/java.html) + * Bouncy Castle PKIX, CMS, EAC, TSP, PKCS, OCSP, CMP, and CRMF APIs (org.bouncycastle:bcpkix-jdk18on:1.80 - https://www.bouncycastle.org/download/bouncy-castle-java/) + * Bouncy Castle Provider (org.bouncycastle:bcprov-jdk18on:1.80 - https://www.bouncycastle.org/download/bouncy-castle-java/) + * Bouncy Castle ASN.1 Extension and Utility APIs (org.bouncycastle:bcutil-jdk18on:1.80 - 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.31.0 - https://checkerframework.org) - * jersey-core-client (org.glassfish.jersey.core:jersey-client:3.1.5 - https://projects.eclipse.org/projects/ee4j.jersey/jersey-client) - * jersey-inject-hk2 (org.glassfish.jersey.inject:jersey-hk2:3.1.5 - https://projects.eclipse.org/projects/ee4j.jersey/project/jersey-hk2) + * Checker Qual (org.checkerframework:checker-qual:3.48.4 - https://checkerframework.org/) + * jersey-core-client (org.glassfish.jersey.core:jersey-client:3.1.10 - https://projects.eclipse.org/projects/ee4j.jersey/jersey-client) + * jersey-inject-hk2 (org.glassfish.jersey.inject:jersey-hk2:3.1.10 - https://projects.eclipse.org/projects/ee4j.jersey/project/jersey-hk2) * jersey-media-multipart (org.glassfish.jersey.media:jersey-media-multipart:3.1.3 - https://projects.eclipse.org/projects/ee4j.jersey/project/jersey-media-multipart) * 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) - * SLF4J API Module (org.slf4j:slf4j-api:2.0.11 - http://www.slf4j.org) - * SLF4J Extensions Module (org.slf4j:slf4j-ext:1.7.28 - http://www.slf4j.org) + * SLF4J API Module (org.slf4j:slf4j-api:2.0.16 - http://www.slf4j.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) @@ -674,24 +683,24 @@ 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.37.1 - https://www.webjars.org) - * @json-editor/json-editor (org.webjars.npm:json-editor__json-editor:2.6.1 - https://www.webjars.org) + * core-js (org.webjars.npm:core-js:3.40.0 - https://www.webjars.org) + * @json-editor/json-editor (org.webjars.npm:json-editor__json-editor:2.15.1 - https://www.webjars.org) Mozilla Public License: * juniversalchardet (com.github.albfernandez:juniversalchardet:2.4.0 - https://github.com/albfernandez/juniversalchardet) - * H2 Database Engine (com.h2database:h2:2.2.224 - https://h2database.com) + * H2 Database Engine (com.h2database:h2:2.3.232 - https://h2database.com) * Saxon-HE (net.sf.saxon:Saxon-HE:9.8.0-14 - http://www.saxonica.com/) - * Javassist (org.javassist:javassist:3.29.2-GA - http://www.javassist.org/) + * 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:3.1.5 - https://projects.eclipse.org/projects/ee4j.jersey/jersey-client) - * jersey-core-common (org.glassfish.jersey.core:jersey-common:3.1.5 - https://projects.eclipse.org/projects/ee4j.jersey/jersey-common) - * jersey-inject-hk2 (org.glassfish.jersey.inject:jersey-hk2:3.1.5 - https://projects.eclipse.org/projects/ee4j.jersey/project/jersey-hk2) + * jersey-core-client (org.glassfish.jersey.core:jersey-client:3.1.10 - https://projects.eclipse.org/projects/ee4j.jersey/jersey-client) + * jersey-core-common (org.glassfish.jersey.core:jersey-common:3.1.10 - https://projects.eclipse.org/projects/ee4j.jersey/jersey-common) + * jersey-inject-hk2 (org.glassfish.jersey.inject:jersey-hk2:3.1.10 - https://projects.eclipse.org/projects/ee4j.jersey/project/jersey-hk2) * jersey-media-multipart (org.glassfish.jersey.media:jersey-media-multipart:3.1.3 - https://projects.eclipse.org/projects/ee4j.jersey/project/jersey-media-multipart) - * HdrHistogram (org.hdrhistogram:HdrHistogram:2.1.12 - http://hdrhistogram.github.io/HdrHistogram/) + * HdrHistogram (org.hdrhistogram:HdrHistogram:2.2.2 - 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) @@ -703,16 +712,16 @@ https://wiki.lyrasis.org/display/DSPACE/Code+Contribution+Guidelines Unicode/ICU License: - * ICU4J (com.ibm.icu:icu4j:62.1 - http://icu-project.org/) + * ICU4J (com.ibm.icu:icu4j:62.2 - http://icu-project.org/) W3C license: - * jersey-core-client (org.glassfish.jersey.core:jersey-client:3.1.5 - https://projects.eclipse.org/projects/ee4j.jersey/jersey-client) - * jersey-inject-hk2 (org.glassfish.jersey.inject:jersey-hk2:3.1.5 - https://projects.eclipse.org/projects/ee4j.jersey/project/jersey-hk2) + * jersey-core-client (org.glassfish.jersey.core:jersey-client:3.1.10 - https://projects.eclipse.org/projects/ee4j.jersey/jersey-client) + * jersey-inject-hk2 (org.glassfish.jersey.inject:jersey-hk2:3.1.10 - https://projects.eclipse.org/projects/ee4j.jersey/project/jersey-hk2) * jersey-media-multipart (org.glassfish.jersey.media:jersey-media-multipart:3.1.3 - https://projects.eclipse.org/projects/ee4j.jersey/project/jersey-media-multipart) jQuery license: - * jersey-core-client (org.glassfish.jersey.core:jersey-client:3.1.5 - https://projects.eclipse.org/projects/ee4j.jersey/jersey-client) - * jersey-inject-hk2 (org.glassfish.jersey.inject:jersey-hk2:3.1.5 - https://projects.eclipse.org/projects/ee4j.jersey/project/jersey-hk2) + * jersey-core-client (org.glassfish.jersey.core:jersey-client:3.1.10 - https://projects.eclipse.org/projects/ee4j.jersey/jersey-client) + * jersey-inject-hk2 (org.glassfish.jersey.inject:jersey-hk2:3.1.10 - https://projects.eclipse.org/projects/ee4j.jersey/project/jersey-hk2) * jersey-media-multipart (org.glassfish.jersey.media:jersey-media-multipart:3.1.3 - https://projects.eclipse.org/projects/ee4j.jersey/project/jersey-media-multipart) diff --git a/pom.xml b/pom.xml index ba9af988ab41..ae8cca4a57a0 100644 --- a/pom.xml +++ b/pom.xml @@ -719,7 +719,7 @@ Apache Software License, Version 2.0|The SAX License|The W3C License Apache Software License, Version 2.0|Similar to Apache License but with the acknowledgment clause removed - BSD License|The BSD License|BSD licence|BSD license|BSD|BSD-style license|New BSD License|New BSD license|Revised BSD License|BSD 2-Clause license|3-Clause BSD License|BSD 2-Clause|BSD 3-clause New License|BSD Licence 3|BSD-2-Clause|BSD-3-Clause|Modified BSD|The New BSD License|The BSD 3-Clause License (BSD3)|BSD License 3|BSD License 2.0 + BSD License|The BSD License|BSD licence|BSD license|BSD|BSD-style license|New BSD License|New BSD license|Revised BSD License|BSD 2-Clause license|3-Clause BSD License|BSD 2-Clause|BSD 3-clause New License|BSD Licence 3|BSD-2-Clause|BSD-3-Clause|Modified BSD|The New BSD License|The BSD 3-Clause License (BSD3)|BSD License 3|BSD License 2.0|The (New) BSD License BSD License|DSpace BSD License|DSpace Sourcecode License From 6181f2723ccc0cba6b8260e8241d6b20f5bcfcd9 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Fri, 7 Feb 2025 10:57:02 -0600 Subject: [PATCH 446/979] [maven-release-plugin] prepare release dspace-7.6.3 --- dspace-api/pom.xml | 2 +- dspace-iiif/pom.xml | 2 +- dspace-oai/pom.xml | 2 +- dspace-rdf/pom.xml | 2 +- dspace-rest/pom.xml | 4 ++-- dspace-server-webapp/pom.xml | 2 +- dspace-services/pom.xml | 2 +- dspace-sword/pom.xml | 2 +- dspace-swordv2/pom.xml | 2 +- dspace/modules/additions/pom.xml | 2 +- dspace/modules/pom.xml | 2 +- dspace/modules/rest/pom.xml | 2 +- dspace/modules/server/pom.xml | 2 +- dspace/pom.xml | 2 +- pom.xml | 32 ++++++++++++++++---------------- 15 files changed, 31 insertions(+), 31 deletions(-) diff --git a/dspace-api/pom.xml b/dspace-api/pom.xml index 77adf3b9675f..fa46d10104de 100644 --- a/dspace-api/pom.xml +++ b/dspace-api/pom.xml @@ -12,7 +12,7 @@ org.dspace dspace-parent - 7.6.3-SNAPSHOT + 7.6.3 .. diff --git a/dspace-iiif/pom.xml b/dspace-iiif/pom.xml index 86702fe01b96..1dbdd4ab73e1 100644 --- a/dspace-iiif/pom.xml +++ b/dspace-iiif/pom.xml @@ -15,7 +15,7 @@ org.dspace dspace-parent - 7.6.3-SNAPSHOT + 7.6.3 .. diff --git a/dspace-oai/pom.xml b/dspace-oai/pom.xml index f7acdae99292..ac9805ee6917 100644 --- a/dspace-oai/pom.xml +++ b/dspace-oai/pom.xml @@ -8,7 +8,7 @@ dspace-parent org.dspace - 7.6.3-SNAPSHOT + 7.6.3 .. diff --git a/dspace-rdf/pom.xml b/dspace-rdf/pom.xml index 64d88b63c780..d7550e4693f6 100644 --- a/dspace-rdf/pom.xml +++ b/dspace-rdf/pom.xml @@ -9,7 +9,7 @@ org.dspace dspace-parent - 7.6.3-SNAPSHOT + 7.6.3 .. diff --git a/dspace-rest/pom.xml b/dspace-rest/pom.xml index fa4e15e8b626..e67b1e391007 100644 --- a/dspace-rest/pom.xml +++ b/dspace-rest/pom.xml @@ -3,7 +3,7 @@ org.dspace dspace-rest war - 7.6.3-SNAPSHOT + 7.6.3 DSpace (Deprecated) REST Webapp DSpace RESTful Web Services API. NOTE: this REST API is DEPRECATED. Please consider using the REST API in the dspace-server-webapp instead! @@ -12,7 +12,7 @@ org.dspace dspace-parent - 7.6.3-SNAPSHOT + 7.6.3 .. diff --git a/dspace-server-webapp/pom.xml b/dspace-server-webapp/pom.xml index 63de9fe721cd..79e5320ed97b 100644 --- a/dspace-server-webapp/pom.xml +++ b/dspace-server-webapp/pom.xml @@ -15,7 +15,7 @@ org.dspace dspace-parent - 7.6.3-SNAPSHOT + 7.6.3 .. diff --git a/dspace-services/pom.xml b/dspace-services/pom.xml index 6504ae2e4b2a..5c8d1ac1b0ef 100644 --- a/dspace-services/pom.xml +++ b/dspace-services/pom.xml @@ -9,7 +9,7 @@ org.dspace dspace-parent - 7.6.3-SNAPSHOT + 7.6.3 diff --git a/dspace-sword/pom.xml b/dspace-sword/pom.xml index c48266454604..f3bc891ddc86 100644 --- a/dspace-sword/pom.xml +++ b/dspace-sword/pom.xml @@ -15,7 +15,7 @@ org.dspace dspace-parent - 7.6.3-SNAPSHOT + 7.6.3 .. diff --git a/dspace-swordv2/pom.xml b/dspace-swordv2/pom.xml index 7cae05672470..944c8df23af6 100644 --- a/dspace-swordv2/pom.xml +++ b/dspace-swordv2/pom.xml @@ -13,7 +13,7 @@ org.dspace dspace-parent - 7.6.3-SNAPSHOT + 7.6.3 .. diff --git a/dspace/modules/additions/pom.xml b/dspace/modules/additions/pom.xml index db9343804013..e91395783368 100644 --- a/dspace/modules/additions/pom.xml +++ b/dspace/modules/additions/pom.xml @@ -17,7 +17,7 @@ org.dspace modules - 7.6.3-SNAPSHOT + 7.6.3 .. diff --git a/dspace/modules/pom.xml b/dspace/modules/pom.xml index 3f15bdd01f4f..d48c14803b09 100644 --- a/dspace/modules/pom.xml +++ b/dspace/modules/pom.xml @@ -11,7 +11,7 @@ org.dspace dspace-parent - 7.6.3-SNAPSHOT + 7.6.3 ../../pom.xml diff --git a/dspace/modules/rest/pom.xml b/dspace/modules/rest/pom.xml index e2e0041cb298..39271f9599d7 100644 --- a/dspace/modules/rest/pom.xml +++ b/dspace/modules/rest/pom.xml @@ -13,7 +13,7 @@ org.dspace modules - 7.6.3-SNAPSHOT + 7.6.3 .. diff --git a/dspace/modules/server/pom.xml b/dspace/modules/server/pom.xml index cdbec2f60c8b..ea04eddd9501 100644 --- a/dspace/modules/server/pom.xml +++ b/dspace/modules/server/pom.xml @@ -13,7 +13,7 @@ just adding new jar in the classloader modules org.dspace - 7.6.3-SNAPSHOT + 7.6.3 .. diff --git a/dspace/pom.xml b/dspace/pom.xml index b3b474bfbcbc..7fd41160492a 100644 --- a/dspace/pom.xml +++ b/dspace/pom.xml @@ -16,7 +16,7 @@ org.dspace dspace-parent - 7.6.3-SNAPSHOT + 7.6.3 ../pom.xml diff --git a/pom.xml b/pom.xml index e8a2b39faf4e..afd5cef2e972 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ org.dspace dspace-parent pom - 7.6.3-SNAPSHOT + 7.6.3 DSpace Parent Project DSpace open source software is a turnkey institutional repository application. @@ -875,14 +875,14 @@ org.dspace dspace-rest - 7.6.3-SNAPSHOT + 7.6.3 jar classes org.dspace dspace-rest - 7.6.3-SNAPSHOT + 7.6.3 war @@ -1033,69 +1033,69 @@ org.dspace dspace-api - 7.6.3-SNAPSHOT + 7.6.3 org.dspace dspace-api test-jar - 7.6.3-SNAPSHOT + 7.6.3 test org.dspace.modules additions - 7.6.3-SNAPSHOT + 7.6.3 org.dspace dspace-sword - 7.6.3-SNAPSHOT + 7.6.3 org.dspace dspace-swordv2 - 7.6.3-SNAPSHOT + 7.6.3 org.dspace dspace-oai - 7.6.3-SNAPSHOT + 7.6.3 org.dspace dspace-services - 7.6.3-SNAPSHOT + 7.6.3 org.dspace dspace-server-webapp test-jar - 7.6.3-SNAPSHOT + 7.6.3 test org.dspace dspace-rdf - 7.6.3-SNAPSHOT + 7.6.3 org.dspace dspace-iiif - 7.6.3-SNAPSHOT + 7.6.3 org.dspace dspace-server-webapp - 7.6.3-SNAPSHOT + 7.6.3 jar classes org.dspace dspace-server-webapp - 7.6.3-SNAPSHOT + 7.6.3 war @@ -1948,7 +1948,7 @@ scm:git:git@github.com:DSpace/DSpace.git scm:git:git@github.com:DSpace/DSpace.git git@github.com:DSpace/DSpace.git - dspace-7_x + dspace-7.6.3 From e0aa56ca825b3f9d5e42ec8d86b583eb6371bcb3 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Fri, 7 Feb 2025 10:57:05 -0600 Subject: [PATCH 447/979] [maven-release-plugin] prepare for next development iteration --- dspace-api/pom.xml | 2 +- dspace-iiif/pom.xml | 2 +- dspace-oai/pom.xml | 2 +- dspace-rdf/pom.xml | 2 +- dspace-rest/pom.xml | 4 ++-- dspace-server-webapp/pom.xml | 2 +- dspace-services/pom.xml | 2 +- dspace-sword/pom.xml | 2 +- dspace-swordv2/pom.xml | 2 +- dspace/modules/additions/pom.xml | 2 +- dspace/modules/pom.xml | 2 +- dspace/modules/rest/pom.xml | 2 +- dspace/modules/server/pom.xml | 2 +- dspace/pom.xml | 2 +- pom.xml | 32 ++++++++++++++++---------------- 15 files changed, 31 insertions(+), 31 deletions(-) diff --git a/dspace-api/pom.xml b/dspace-api/pom.xml index fa46d10104de..50f618fd5f4e 100644 --- a/dspace-api/pom.xml +++ b/dspace-api/pom.xml @@ -12,7 +12,7 @@ org.dspace dspace-parent - 7.6.3 + 7.6.4-SNAPSHOT .. diff --git a/dspace-iiif/pom.xml b/dspace-iiif/pom.xml index 1dbdd4ab73e1..24c3d5f1642c 100644 --- a/dspace-iiif/pom.xml +++ b/dspace-iiif/pom.xml @@ -15,7 +15,7 @@ org.dspace dspace-parent - 7.6.3 + 7.6.4-SNAPSHOT .. diff --git a/dspace-oai/pom.xml b/dspace-oai/pom.xml index ac9805ee6917..0a7fcf3a1b23 100644 --- a/dspace-oai/pom.xml +++ b/dspace-oai/pom.xml @@ -8,7 +8,7 @@ dspace-parent org.dspace - 7.6.3 + 7.6.4-SNAPSHOT .. diff --git a/dspace-rdf/pom.xml b/dspace-rdf/pom.xml index d7550e4693f6..ee06135cdce4 100644 --- a/dspace-rdf/pom.xml +++ b/dspace-rdf/pom.xml @@ -9,7 +9,7 @@ org.dspace dspace-parent - 7.6.3 + 7.6.4-SNAPSHOT .. diff --git a/dspace-rest/pom.xml b/dspace-rest/pom.xml index e67b1e391007..e7f2c20ec8e2 100644 --- a/dspace-rest/pom.xml +++ b/dspace-rest/pom.xml @@ -3,7 +3,7 @@ org.dspace dspace-rest war - 7.6.3 + 7.6.4-SNAPSHOT DSpace (Deprecated) REST Webapp DSpace RESTful Web Services API. NOTE: this REST API is DEPRECATED. Please consider using the REST API in the dspace-server-webapp instead! @@ -12,7 +12,7 @@ org.dspace dspace-parent - 7.6.3 + 7.6.4-SNAPSHOT .. diff --git a/dspace-server-webapp/pom.xml b/dspace-server-webapp/pom.xml index 79e5320ed97b..f3fd8613fbf5 100644 --- a/dspace-server-webapp/pom.xml +++ b/dspace-server-webapp/pom.xml @@ -15,7 +15,7 @@ org.dspace dspace-parent - 7.6.3 + 7.6.4-SNAPSHOT .. diff --git a/dspace-services/pom.xml b/dspace-services/pom.xml index 5c8d1ac1b0ef..6d783349f165 100644 --- a/dspace-services/pom.xml +++ b/dspace-services/pom.xml @@ -9,7 +9,7 @@ org.dspace dspace-parent - 7.6.3 + 7.6.4-SNAPSHOT diff --git a/dspace-sword/pom.xml b/dspace-sword/pom.xml index f3bc891ddc86..d0ff123d741c 100644 --- a/dspace-sword/pom.xml +++ b/dspace-sword/pom.xml @@ -15,7 +15,7 @@ org.dspace dspace-parent - 7.6.3 + 7.6.4-SNAPSHOT .. diff --git a/dspace-swordv2/pom.xml b/dspace-swordv2/pom.xml index 944c8df23af6..755dc30d696b 100644 --- a/dspace-swordv2/pom.xml +++ b/dspace-swordv2/pom.xml @@ -13,7 +13,7 @@ org.dspace dspace-parent - 7.6.3 + 7.6.4-SNAPSHOT .. diff --git a/dspace/modules/additions/pom.xml b/dspace/modules/additions/pom.xml index e91395783368..dd38555808a2 100644 --- a/dspace/modules/additions/pom.xml +++ b/dspace/modules/additions/pom.xml @@ -17,7 +17,7 @@ org.dspace modules - 7.6.3 + 7.6.4-SNAPSHOT .. diff --git a/dspace/modules/pom.xml b/dspace/modules/pom.xml index d48c14803b09..bc902913e967 100644 --- a/dspace/modules/pom.xml +++ b/dspace/modules/pom.xml @@ -11,7 +11,7 @@ org.dspace dspace-parent - 7.6.3 + 7.6.4-SNAPSHOT ../../pom.xml diff --git a/dspace/modules/rest/pom.xml b/dspace/modules/rest/pom.xml index 39271f9599d7..c424668c8980 100644 --- a/dspace/modules/rest/pom.xml +++ b/dspace/modules/rest/pom.xml @@ -13,7 +13,7 @@ org.dspace modules - 7.6.3 + 7.6.4-SNAPSHOT .. diff --git a/dspace/modules/server/pom.xml b/dspace/modules/server/pom.xml index ea04eddd9501..318c01edd214 100644 --- a/dspace/modules/server/pom.xml +++ b/dspace/modules/server/pom.xml @@ -13,7 +13,7 @@ just adding new jar in the classloader modules org.dspace - 7.6.3 + 7.6.4-SNAPSHOT .. diff --git a/dspace/pom.xml b/dspace/pom.xml index 7fd41160492a..af6881303b96 100644 --- a/dspace/pom.xml +++ b/dspace/pom.xml @@ -16,7 +16,7 @@ org.dspace dspace-parent - 7.6.3 + 7.6.4-SNAPSHOT ../pom.xml diff --git a/pom.xml b/pom.xml index afd5cef2e972..9695ae68300c 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ org.dspace dspace-parent pom - 7.6.3 + 7.6.4-SNAPSHOT DSpace Parent Project DSpace open source software is a turnkey institutional repository application. @@ -875,14 +875,14 @@ org.dspace dspace-rest - 7.6.3 + 7.6.4-SNAPSHOT jar classes org.dspace dspace-rest - 7.6.3 + 7.6.4-SNAPSHOT war @@ -1033,69 +1033,69 @@ org.dspace dspace-api - 7.6.3 + 7.6.4-SNAPSHOT org.dspace dspace-api test-jar - 7.6.3 + 7.6.4-SNAPSHOT test org.dspace.modules additions - 7.6.3 + 7.6.4-SNAPSHOT org.dspace dspace-sword - 7.6.3 + 7.6.4-SNAPSHOT org.dspace dspace-swordv2 - 7.6.3 + 7.6.4-SNAPSHOT org.dspace dspace-oai - 7.6.3 + 7.6.4-SNAPSHOT org.dspace dspace-services - 7.6.3 + 7.6.4-SNAPSHOT org.dspace dspace-server-webapp test-jar - 7.6.3 + 7.6.4-SNAPSHOT test org.dspace dspace-rdf - 7.6.3 + 7.6.4-SNAPSHOT org.dspace dspace-iiif - 7.6.3 + 7.6.4-SNAPSHOT org.dspace dspace-server-webapp - 7.6.3 + 7.6.4-SNAPSHOT jar classes org.dspace dspace-server-webapp - 7.6.3 + 7.6.4-SNAPSHOT war @@ -1948,7 +1948,7 @@ scm:git:git@github.com:DSpace/DSpace.git scm:git:git@github.com:DSpace/DSpace.git git@github.com:DSpace/DSpace.git - dspace-7.6.3 + dspace-7_x From 708826c5f4b762ab30adc091b1c6b9a21a143c9c Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Fri, 7 Feb 2025 11:53:02 -0600 Subject: [PATCH 448/979] [maven-release-plugin] prepare release dspace-8.1 --- dspace-api/pom.xml | 2 +- dspace-iiif/pom.xml | 2 +- dspace-oai/pom.xml | 2 +- dspace-rdf/pom.xml | 2 +- dspace-server-webapp/pom.xml | 2 +- dspace-services/pom.xml | 2 +- dspace-sword/pom.xml | 2 +- dspace-swordv2/pom.xml | 2 +- dspace/modules/additions/pom.xml | 2 +- dspace/modules/pom.xml | 2 +- dspace/modules/server-boot/pom.xml | 2 +- dspace/modules/server/pom.xml | 2 +- dspace/pom.xml | 2 +- pom.xml | 28 ++++++++++++++-------------- 14 files changed, 27 insertions(+), 27 deletions(-) diff --git a/dspace-api/pom.xml b/dspace-api/pom.xml index acb59fb2d492..487d5cab953f 100644 --- a/dspace-api/pom.xml +++ b/dspace-api/pom.xml @@ -12,7 +12,7 @@ org.dspace dspace-parent - 8.1-SNAPSHOT + 8.1 .. diff --git a/dspace-iiif/pom.xml b/dspace-iiif/pom.xml index e1d6c4cbfbe4..d1a71e664e65 100644 --- a/dspace-iiif/pom.xml +++ b/dspace-iiif/pom.xml @@ -15,7 +15,7 @@ org.dspace dspace-parent - 8.1-SNAPSHOT + 8.1 .. diff --git a/dspace-oai/pom.xml b/dspace-oai/pom.xml index 1447fc6e19a0..813c1f594b27 100644 --- a/dspace-oai/pom.xml +++ b/dspace-oai/pom.xml @@ -8,7 +8,7 @@ dspace-parent org.dspace - 8.1-SNAPSHOT + 8.1 .. diff --git a/dspace-rdf/pom.xml b/dspace-rdf/pom.xml index f8e1bb9e36c4..aeaecf8a9849 100644 --- a/dspace-rdf/pom.xml +++ b/dspace-rdf/pom.xml @@ -9,7 +9,7 @@ org.dspace dspace-parent - 8.1-SNAPSHOT + 8.1 .. diff --git a/dspace-server-webapp/pom.xml b/dspace-server-webapp/pom.xml index c8bb82f0ea2e..edc34eafcf92 100644 --- a/dspace-server-webapp/pom.xml +++ b/dspace-server-webapp/pom.xml @@ -14,7 +14,7 @@ org.dspace dspace-parent - 8.1-SNAPSHOT + 8.1 .. diff --git a/dspace-services/pom.xml b/dspace-services/pom.xml index 05b490df7610..0b3c3f33da38 100644 --- a/dspace-services/pom.xml +++ b/dspace-services/pom.xml @@ -9,7 +9,7 @@ org.dspace dspace-parent - 8.1-SNAPSHOT + 8.1 diff --git a/dspace-sword/pom.xml b/dspace-sword/pom.xml index 48a6392d3dba..0487692a8202 100644 --- a/dspace-sword/pom.xml +++ b/dspace-sword/pom.xml @@ -15,7 +15,7 @@ org.dspace dspace-parent - 8.1-SNAPSHOT + 8.1 .. diff --git a/dspace-swordv2/pom.xml b/dspace-swordv2/pom.xml index 2e6b1a27d838..64754b1995ca 100644 --- a/dspace-swordv2/pom.xml +++ b/dspace-swordv2/pom.xml @@ -13,7 +13,7 @@ org.dspace dspace-parent - 8.1-SNAPSHOT + 8.1 .. diff --git a/dspace/modules/additions/pom.xml b/dspace/modules/additions/pom.xml index f658388c3594..49a09d303893 100644 --- a/dspace/modules/additions/pom.xml +++ b/dspace/modules/additions/pom.xml @@ -17,7 +17,7 @@ org.dspace modules - 8.1-SNAPSHOT + 8.1 .. diff --git a/dspace/modules/pom.xml b/dspace/modules/pom.xml index c78450137759..8a4684617770 100644 --- a/dspace/modules/pom.xml +++ b/dspace/modules/pom.xml @@ -11,7 +11,7 @@ org.dspace dspace-parent - 8.1-SNAPSHOT + 8.1 ../../pom.xml diff --git a/dspace/modules/server-boot/pom.xml b/dspace/modules/server-boot/pom.xml index f2f79bb73869..066378348061 100644 --- a/dspace/modules/server-boot/pom.xml +++ b/dspace/modules/server-boot/pom.xml @@ -11,7 +11,7 @@ modules org.dspace - 8.1-SNAPSHOT + 8.1 .. diff --git a/dspace/modules/server/pom.xml b/dspace/modules/server/pom.xml index 916689516a1c..5b9eb4d43044 100644 --- a/dspace/modules/server/pom.xml +++ b/dspace/modules/server/pom.xml @@ -7,7 +7,7 @@ modules org.dspace - 8.1-SNAPSHOT + 8.1 .. diff --git a/dspace/pom.xml b/dspace/pom.xml index ef17eb40862d..abb081b32369 100644 --- a/dspace/pom.xml +++ b/dspace/pom.xml @@ -16,7 +16,7 @@ org.dspace dspace-parent - 8.1-SNAPSHOT + 8.1 ../pom.xml diff --git a/pom.xml b/pom.xml index ae8cca4a57a0..7541b2effee2 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ org.dspace dspace-parent pom - 8.1-SNAPSHOT + 8.1 DSpace Parent Project DSpace open source software is a turnkey institutional repository application. @@ -1018,68 +1018,68 @@ org.dspace dspace-api - 8.1-SNAPSHOT + 8.1 org.dspace dspace-api test-jar - 8.1-SNAPSHOT + 8.1 test org.dspace.modules additions - 8.1-SNAPSHOT + 8.1 org.dspace.modules server classes - 8.1-SNAPSHOT + 8.1 org.dspace dspace-sword - 8.1-SNAPSHOT + 8.1 org.dspace dspace-swordv2 - 8.1-SNAPSHOT + 8.1 org.dspace dspace-oai - 8.1-SNAPSHOT + 8.1 org.dspace dspace-services - 8.1-SNAPSHOT + 8.1 org.dspace dspace-server-webapp test-jar - 8.1-SNAPSHOT + 8.1 test org.dspace dspace-rdf - 8.1-SNAPSHOT + 8.1 org.dspace dspace-iiif - 8.1-SNAPSHOT + 8.1 org.dspace dspace-server-webapp - 8.1-SNAPSHOT + 8.1 @@ -1917,7 +1917,7 @@ scm:git:git@github.com:DSpace/DSpace.git scm:git:git@github.com:DSpace/DSpace.git git@github.com:DSpace/DSpace.git - dspace-8_x + dspace-8.1 From 8773da722271f98e1770a8e496db304b0f6143b4 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Fri, 7 Feb 2025 11:53:06 -0600 Subject: [PATCH 449/979] [maven-release-plugin] prepare for next development iteration --- dspace-api/pom.xml | 2 +- dspace-iiif/pom.xml | 2 +- dspace-oai/pom.xml | 2 +- dspace-rdf/pom.xml | 2 +- dspace-server-webapp/pom.xml | 2 +- dspace-services/pom.xml | 2 +- dspace-sword/pom.xml | 2 +- dspace-swordv2/pom.xml | 2 +- dspace/modules/additions/pom.xml | 2 +- dspace/modules/pom.xml | 2 +- dspace/modules/server-boot/pom.xml | 2 +- dspace/modules/server/pom.xml | 2 +- dspace/pom.xml | 2 +- pom.xml | 28 ++++++++++++++-------------- 14 files changed, 27 insertions(+), 27 deletions(-) diff --git a/dspace-api/pom.xml b/dspace-api/pom.xml index 487d5cab953f..6c24afee9507 100644 --- a/dspace-api/pom.xml +++ b/dspace-api/pom.xml @@ -12,7 +12,7 @@ org.dspace dspace-parent - 8.1 + 8.2-SNAPSHOT .. diff --git a/dspace-iiif/pom.xml b/dspace-iiif/pom.xml index d1a71e664e65..71b88d92ca9b 100644 --- a/dspace-iiif/pom.xml +++ b/dspace-iiif/pom.xml @@ -15,7 +15,7 @@ org.dspace dspace-parent - 8.1 + 8.2-SNAPSHOT .. diff --git a/dspace-oai/pom.xml b/dspace-oai/pom.xml index 813c1f594b27..b824b7396e03 100644 --- a/dspace-oai/pom.xml +++ b/dspace-oai/pom.xml @@ -8,7 +8,7 @@ dspace-parent org.dspace - 8.1 + 8.2-SNAPSHOT .. diff --git a/dspace-rdf/pom.xml b/dspace-rdf/pom.xml index aeaecf8a9849..4662433d49c1 100644 --- a/dspace-rdf/pom.xml +++ b/dspace-rdf/pom.xml @@ -9,7 +9,7 @@ org.dspace dspace-parent - 8.1 + 8.2-SNAPSHOT .. diff --git a/dspace-server-webapp/pom.xml b/dspace-server-webapp/pom.xml index edc34eafcf92..91e83fc40435 100644 --- a/dspace-server-webapp/pom.xml +++ b/dspace-server-webapp/pom.xml @@ -14,7 +14,7 @@ org.dspace dspace-parent - 8.1 + 8.2-SNAPSHOT .. diff --git a/dspace-services/pom.xml b/dspace-services/pom.xml index 0b3c3f33da38..973790ec435f 100644 --- a/dspace-services/pom.xml +++ b/dspace-services/pom.xml @@ -9,7 +9,7 @@ org.dspace dspace-parent - 8.1 + 8.2-SNAPSHOT diff --git a/dspace-sword/pom.xml b/dspace-sword/pom.xml index 0487692a8202..bdab97fee787 100644 --- a/dspace-sword/pom.xml +++ b/dspace-sword/pom.xml @@ -15,7 +15,7 @@ org.dspace dspace-parent - 8.1 + 8.2-SNAPSHOT .. diff --git a/dspace-swordv2/pom.xml b/dspace-swordv2/pom.xml index 64754b1995ca..84e1e412b945 100644 --- a/dspace-swordv2/pom.xml +++ b/dspace-swordv2/pom.xml @@ -13,7 +13,7 @@ org.dspace dspace-parent - 8.1 + 8.2-SNAPSHOT .. diff --git a/dspace/modules/additions/pom.xml b/dspace/modules/additions/pom.xml index 49a09d303893..ec1153e1c9a5 100644 --- a/dspace/modules/additions/pom.xml +++ b/dspace/modules/additions/pom.xml @@ -17,7 +17,7 @@ org.dspace modules - 8.1 + 8.2-SNAPSHOT .. diff --git a/dspace/modules/pom.xml b/dspace/modules/pom.xml index 8a4684617770..5394ccd3248a 100644 --- a/dspace/modules/pom.xml +++ b/dspace/modules/pom.xml @@ -11,7 +11,7 @@ org.dspace dspace-parent - 8.1 + 8.2-SNAPSHOT ../../pom.xml diff --git a/dspace/modules/server-boot/pom.xml b/dspace/modules/server-boot/pom.xml index 066378348061..9d7251e78e2b 100644 --- a/dspace/modules/server-boot/pom.xml +++ b/dspace/modules/server-boot/pom.xml @@ -11,7 +11,7 @@ modules org.dspace - 8.1 + 8.2-SNAPSHOT .. diff --git a/dspace/modules/server/pom.xml b/dspace/modules/server/pom.xml index 5b9eb4d43044..35c7f161e91b 100644 --- a/dspace/modules/server/pom.xml +++ b/dspace/modules/server/pom.xml @@ -7,7 +7,7 @@ modules org.dspace - 8.1 + 8.2-SNAPSHOT .. diff --git a/dspace/pom.xml b/dspace/pom.xml index abb081b32369..6690264d7c07 100644 --- a/dspace/pom.xml +++ b/dspace/pom.xml @@ -16,7 +16,7 @@ org.dspace dspace-parent - 8.1 + 8.2-SNAPSHOT ../pom.xml diff --git a/pom.xml b/pom.xml index 7541b2effee2..d115999d9328 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ org.dspace dspace-parent pom - 8.1 + 8.2-SNAPSHOT DSpace Parent Project DSpace open source software is a turnkey institutional repository application. @@ -1018,68 +1018,68 @@ org.dspace dspace-api - 8.1 + 8.2-SNAPSHOT org.dspace dspace-api test-jar - 8.1 + 8.2-SNAPSHOT test org.dspace.modules additions - 8.1 + 8.2-SNAPSHOT org.dspace.modules server classes - 8.1 + 8.2-SNAPSHOT org.dspace dspace-sword - 8.1 + 8.2-SNAPSHOT org.dspace dspace-swordv2 - 8.1 + 8.2-SNAPSHOT org.dspace dspace-oai - 8.1 + 8.2-SNAPSHOT org.dspace dspace-services - 8.1 + 8.2-SNAPSHOT org.dspace dspace-server-webapp test-jar - 8.1 + 8.2-SNAPSHOT test org.dspace dspace-rdf - 8.1 + 8.2-SNAPSHOT org.dspace dspace-iiif - 8.1 + 8.2-SNAPSHOT org.dspace dspace-server-webapp - 8.1 + 8.2-SNAPSHOT @@ -1917,7 +1917,7 @@ scm:git:git@github.com:DSpace/DSpace.git scm:git:git@github.com:DSpace/DSpace.git git@github.com:DSpace/DSpace.git - dspace-8.1 + dspace-8_x From 56977216a0453a9e16c6f2ae56324024d653b672 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Mon, 10 Feb 2025 10:14:36 -0600 Subject: [PATCH 450/979] Remove unused byte-buddy (cherry picked from commit ccfee04f4ac994ed2ee436c73f1c0071d9c46d05) --- pom.xml | 7 ------- 1 file changed, 7 deletions(-) diff --git a/pom.xml b/pom.xml index 370b61c79c0c..2b0da5fa008b 100644 --- a/pom.xml +++ b/pom.xml @@ -1808,13 +1808,6 @@ jakarta.annotation-api ${jakarta-annotation.version} - - - - net.bytebuddy - byte-buddy - 1.16.1 - From f2243468df9c6e981a262d15d4a3e1d5adc7dd37 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Mon, 10 Feb 2025 10:22:42 -0600 Subject: [PATCH 451/979] Remove joda-time and the one place it is used. Fix to use java.time instead (cherry picked from commit d71265d17e3462a11b8d47b65dee702dabcc98e1) --- .../org/dspace/access/status/DefaultAccessStatusHelper.java | 4 ++-- pom.xml | 5 ----- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/access/status/DefaultAccessStatusHelper.java b/dspace-api/src/main/java/org/dspace/access/status/DefaultAccessStatusHelper.java index 5f0e6d8b259b..52cdec3517bc 100644 --- a/dspace-api/src/main/java/org/dspace/access/status/DefaultAccessStatusHelper.java +++ b/dspace-api/src/main/java/org/dspace/access/status/DefaultAccessStatusHelper.java @@ -8,6 +8,7 @@ package org.dspace.access.status; import java.sql.SQLException; +import java.time.Instant; import java.util.Date; import java.util.List; import java.util.Objects; @@ -26,7 +27,6 @@ import org.dspace.core.Constants; import org.dspace.core.Context; import org.dspace.eperson.Group; -import org.joda.time.LocalDate; /** * Default plugin implementation of the access status helper. @@ -230,7 +230,7 @@ private Date retrieveShortestEmbargo(Context context, Bitstream bitstream) throw // If the policy is not valid there is an active embargo Date startDate = policy.getStartDate(); - if (startDate != null && !startDate.before(LocalDate.now().toDate())) { + if (startDate != null && !startDate.before(Date.from(Instant.now()))) { // There is an active embargo: aim to take the shortest embargo (account for rare cases where // more than one resource policy exists) if (embargoDate == null) { diff --git a/pom.xml b/pom.xml index 2b0da5fa008b..68f848d987db 100644 --- a/pom.xml +++ b/pom.xml @@ -1538,11 +1538,6 @@ commons-validator 1.9.0 - - joda-time - joda-time - 2.13.0 - jakarta.activation jakarta.activation-api From a2cb8cc8383aa64beb51509a730fcaea0965df37 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Mon, 10 Feb 2025 10:14:36 -0600 Subject: [PATCH 452/979] Remove unused byte-buddy --- pom.xml | 7 ------- 1 file changed, 7 deletions(-) diff --git a/pom.xml b/pom.xml index 9695ae68300c..28be939a9923 100644 --- a/pom.xml +++ b/pom.xml @@ -1839,13 +1839,6 @@ javax.annotation-api ${javax-annotation.version} - - - - net.bytebuddy - byte-buddy - 1.16.1 - From 8e411ac70cab3d30de693b929fc4c54d46022d61 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 10 Feb 2025 22:25:47 +0000 Subject: [PATCH 453/979] Bump com.github.spotbugs:spotbugs in the build-tools group Bumps the build-tools group with 1 update: [com.github.spotbugs:spotbugs](https://github.com/spotbugs/spotbugs). Updates `com.github.spotbugs:spotbugs` from 4.9.0 to 4.9.1 - [Release notes](https://github.com/spotbugs/spotbugs/releases) - [Changelog](https://github.com/spotbugs/spotbugs/blob/master/CHANGELOG.md) - [Commits](https://github.com/spotbugs/spotbugs/compare/4.9.0...4.9.1) --- updated-dependencies: - dependency-name: com.github.spotbugs:spotbugs dependency-type: direct:production update-type: version-update:semver-patch dependency-group: build-tools ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 7710ec7eed46..6c9376118057 100644 --- a/pom.xml +++ b/pom.xml @@ -305,7 +305,7 @@ com.github.spotbugs spotbugs - 4.9.0 + 4.9.1 From 06a5458205dd13de9580974cdf9cf76e823edfc8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 10 Feb 2025 22:25:53 +0000 Subject: [PATCH 454/979] Bump the google-apis group across 1 directory with 3 updates Bumps the google-apis group with 3 updates in the / directory: [com.google.http-client:google-http-client](https://github.com/googleapis/google-http-java-client), [com.google.http-client:google-http-client-jackson2](https://github.com/googleapis/google-http-java-client) and [com.google.http-client:google-http-client-gson](https://github.com/googleapis/google-http-java-client). Updates `com.google.http-client:google-http-client` from 1.45.3 to 1.46.1 - [Release notes](https://github.com/googleapis/google-http-java-client/releases) - [Changelog](https://github.com/googleapis/google-http-java-client/blob/main/CHANGELOG.md) - [Commits](https://github.com/googleapis/google-http-java-client/compare/v1.45.3...v1.46.1) Updates `com.google.http-client:google-http-client-jackson2` from 1.45.3 to 1.46.1 - [Release notes](https://github.com/googleapis/google-http-java-client/releases) - [Changelog](https://github.com/googleapis/google-http-java-client/blob/main/CHANGELOG.md) - [Commits](https://github.com/googleapis/google-http-java-client/compare/v1.45.3...v1.46.1) Updates `com.google.http-client:google-http-client-gson` from 1.43.3 to 1.46.1 - [Release notes](https://github.com/googleapis/google-http-java-client/releases) - [Changelog](https://github.com/googleapis/google-http-java-client/blob/main/CHANGELOG.md) - [Commits](https://github.com/googleapis/google-http-java-client/compare/v1.43.3...v1.46.1) --- updated-dependencies: - dependency-name: com.google.http-client:google-http-client dependency-type: direct:production update-type: version-update:semver-minor dependency-group: google-apis - dependency-name: com.google.http-client:google-http-client-jackson2 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: google-apis - dependency-name: com.google.http-client:google-http-client-gson dependency-type: direct:production update-type: version-update:semver-minor dependency-group: google-apis ... Signed-off-by: dependabot[bot] --- pom.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pom.xml b/pom.xml index 7710ec7eed46..da8f18e3b96f 100644 --- a/pom.xml +++ b/pom.xml @@ -1708,7 +1708,7 @@ com.google.http-client google-http-client - 1.45.3 + 1.46.1 com.google.errorprone @@ -1730,7 +1730,7 @@ com.google.http-client google-http-client-jackson2 - 1.45.3 + 1.46.1 jackson-core @@ -1752,7 +1752,7 @@ com.google.http-client google-http-client-gson - 1.43.3 + 1.46.1 From ba318d971049ef01b1ec4a7e5daaeb06f4d53967 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 10 Feb 2025 22:26:17 +0000 Subject: [PATCH 455/979] Bump the test-tools group with 6 updates Bumps the test-tools group with 6 updates: | Package | From | To | | --- | --- | --- | | [io.netty:netty-buffer](https://github.com/netty/netty) | `4.1.117.Final` | `4.1.118.Final` | | [io.netty:netty-transport](https://github.com/netty/netty) | `4.1.117.Final` | `4.1.118.Final` | | [io.netty:netty-transport-native-unix-common](https://github.com/netty/netty) | `4.1.117.Final` | `4.1.118.Final` | | [io.netty:netty-common](https://github.com/netty/netty) | `4.1.117.Final` | `4.1.118.Final` | | [io.netty:netty-handler](https://github.com/netty/netty) | `4.1.117.Final` | `4.1.118.Final` | | [io.netty:netty-codec](https://github.com/netty/netty) | `4.1.117.Final` | `4.1.118.Final` | Updates `io.netty:netty-buffer` from 4.1.117.Final to 4.1.118.Final - [Commits](https://github.com/netty/netty/compare/netty-4.1.117.Final...netty-4.1.118.Final) Updates `io.netty:netty-transport` from 4.1.117.Final to 4.1.118.Final - [Commits](https://github.com/netty/netty/compare/netty-4.1.117.Final...netty-4.1.118.Final) Updates `io.netty:netty-transport-native-unix-common` from 4.1.117.Final to 4.1.118.Final - [Commits](https://github.com/netty/netty/compare/netty-4.1.117.Final...netty-4.1.118.Final) Updates `io.netty:netty-common` from 4.1.117.Final to 4.1.118.Final - [Commits](https://github.com/netty/netty/compare/netty-4.1.117.Final...netty-4.1.118.Final) Updates `io.netty:netty-handler` from 4.1.117.Final to 4.1.118.Final - [Commits](https://github.com/netty/netty/compare/netty-4.1.117.Final...netty-4.1.118.Final) Updates `io.netty:netty-codec` from 4.1.117.Final to 4.1.118.Final - [Commits](https://github.com/netty/netty/compare/netty-4.1.117.Final...netty-4.1.118.Final) --- updated-dependencies: - dependency-name: io.netty:netty-buffer dependency-type: direct:production update-type: version-update:semver-patch dependency-group: test-tools - dependency-name: io.netty:netty-transport dependency-type: direct:production update-type: version-update:semver-patch dependency-group: test-tools - dependency-name: io.netty:netty-transport-native-unix-common dependency-type: direct:production update-type: version-update:semver-patch dependency-group: test-tools - dependency-name: io.netty:netty-common dependency-type: direct:production update-type: version-update:semver-patch dependency-group: test-tools - dependency-name: io.netty:netty-handler dependency-type: direct:production update-type: version-update:semver-patch dependency-group: test-tools - dependency-name: io.netty:netty-codec dependency-type: direct:production update-type: version-update:semver-patch dependency-group: test-tools ... Signed-off-by: dependabot[bot] --- dspace-api/pom.xml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/dspace-api/pom.xml b/dspace-api/pom.xml index e90773d1def4..29b9865a5a7c 100644 --- a/dspace-api/pom.xml +++ b/dspace-api/pom.xml @@ -887,32 +887,32 @@ io.netty netty-buffer - 4.1.117.Final + 4.1.118.Final io.netty netty-transport - 4.1.117.Final + 4.1.118.Final io.netty netty-transport-native-unix-common - 4.1.117.Final + 4.1.118.Final io.netty netty-common - 4.1.117.Final + 4.1.118.Final io.netty netty-handler - 4.1.117.Final + 4.1.118.Final io.netty netty-codec - 4.1.117.Final + 4.1.118.Final org.apache.velocity From 090001b685c6595450210e5fa311f928afe2c1be Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 10 Feb 2025 22:26:31 +0000 Subject: [PATCH 456/979] Bump commons-logging:commons-logging in the apache-commons group Bumps the apache-commons group with 1 update: commons-logging:commons-logging. Updates `commons-logging:commons-logging` from 1.3.4 to 1.3.5 --- updated-dependencies: - dependency-name: commons-logging:commons-logging dependency-type: direct:production update-type: version-update:semver-patch dependency-group: apache-commons ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 7710ec7eed46..505395fafec7 100644 --- a/pom.xml +++ b/pom.xml @@ -1511,7 +1511,7 @@ commons-logging commons-logging - 1.3.4 + 1.3.5 org.apache.commons From 2c13ee40fef393dabf56d35b93e7e537608af9f5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 10 Feb 2025 22:27:33 +0000 Subject: [PATCH 457/979] Bump tika.version from 2.9.2 to 2.9.3 Bumps `tika.version` from 2.9.2 to 2.9.3. Updates `org.apache.tika:tika-core` from 2.9.2 to 2.9.3 - [Changelog](https://github.com/apache/tika/blob/2.9.3/CHANGES.txt) - [Commits](https://github.com/apache/tika/compare/2.9.2...2.9.3) Updates `org.apache.tika:tika-parsers-standard-package` from 2.9.2 to 2.9.3 --- updated-dependencies: - dependency-name: org.apache.tika:tika-core dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.apache.tika:tika-parsers-standard-package dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 7710ec7eed46..ce859ee7e78f 100644 --- a/pom.xml +++ b/pom.xml @@ -42,7 +42,7 @@ 2.0.33 1.19.0 1.7.36 - 2.9.2 + 2.9.3 1.80 From 4f54d582a3afda744d81cdd54bc11b78b478c807 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 10 Feb 2025 23:22:10 +0000 Subject: [PATCH 458/979] Bump com.github.spotbugs:spotbugs in the build-tools group Bumps the build-tools group with 1 update: [com.github.spotbugs:spotbugs](https://github.com/spotbugs/spotbugs). Updates `com.github.spotbugs:spotbugs` from 4.9.0 to 4.9.1 - [Release notes](https://github.com/spotbugs/spotbugs/releases) - [Changelog](https://github.com/spotbugs/spotbugs/blob/master/CHANGELOG.md) - [Commits](https://github.com/spotbugs/spotbugs/compare/4.9.0...4.9.1) --- updated-dependencies: - dependency-name: com.github.spotbugs:spotbugs dependency-type: direct:production update-type: version-update:semver-patch dependency-group: build-tools ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 3477e48edd96..d7e0a48509a5 100644 --- a/pom.xml +++ b/pom.xml @@ -313,7 +313,7 @@ com.github.spotbugs spotbugs - 4.9.0 + 4.9.1 From 6ebde005fe76309238124bd9fda874de43a45651 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 10 Feb 2025 23:22:30 +0000 Subject: [PATCH 459/979] Bump commons-logging:commons-logging in the apache-commons group Bumps the apache-commons group with 1 update: commons-logging:commons-logging. Updates `commons-logging:commons-logging` from 1.3.4 to 1.3.5 --- updated-dependencies: - dependency-name: commons-logging:commons-logging dependency-type: direct:production update-type: version-update:semver-patch dependency-group: apache-commons ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 3477e48edd96..03a4d1ef6a98 100644 --- a/pom.xml +++ b/pom.xml @@ -1511,7 +1511,7 @@ commons-logging commons-logging - 1.3.4 + 1.3.5 org.apache.commons From d7261481d1d46eaeaca716b8aff77b684c602898 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 10 Feb 2025 23:23:18 +0000 Subject: [PATCH 460/979] Bump tika.version from 2.9.2 to 2.9.3 Bumps `tika.version` from 2.9.2 to 2.9.3. Updates `org.apache.tika:tika-core` from 2.9.2 to 2.9.3 - [Changelog](https://github.com/apache/tika/blob/2.9.3/CHANGES.txt) - [Commits](https://github.com/apache/tika/compare/2.9.2...2.9.3) Updates `org.apache.tika:tika-parsers-standard-package` from 2.9.2 to 2.9.3 --- updated-dependencies: - dependency-name: org.apache.tika:tika-core dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.apache.tika:tika-parsers-standard-package dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 3477e48edd96..a2883f98eac4 100644 --- a/pom.xml +++ b/pom.xml @@ -43,7 +43,7 @@ 2.0.33 1.19.0 2.0.16 - 2.9.2 + 2.9.3 1.80 8.0.1 From 102c347455329181205a2be040def3f30e74b8ca Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Tue, 11 Feb 2025 10:39:16 -0600 Subject: [PATCH 461/979] Dependency convergence fix --- pom.xml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/pom.xml b/pom.xml index ce859ee7e78f..313a6bbfe9af 100644 --- a/pom.xml +++ b/pom.xml @@ -1316,6 +1316,12 @@ bcutil-jdk18on ${bouncycastle.version} + + + com.healthmarketscience.jackcess + jackcess + 4.0.8 + org.apache.james From 6265c3d45b508c2bc35d5c23c3d7fca40fdf2568 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Tue, 11 Feb 2025 10:39:16 -0600 Subject: [PATCH 462/979] Dependency convergence fix --- pom.xml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/pom.xml b/pom.xml index a2883f98eac4..7cb9ee80c4e8 100644 --- a/pom.xml +++ b/pom.xml @@ -1312,6 +1312,12 @@ bcutil-jdk18on ${bouncycastle.version} + + + com.healthmarketscience.jackcess + jackcess + 4.0.8 + org.apache.james From ee762260cf423075ceb6129b00cb8cb981f725ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Paulo=20Gra=C3=A7a?= Date: Wed, 12 Feb 2025 12:26:27 +0000 Subject: [PATCH 463/979] [Port dspace-7_x] Fixing Crossref document type issue with new metadata mapping processor (#9909) * new metadata mapping processor for crossref document type * licence and code style fixes * adjust crossref test to consider mapped dc.type to Article * correcting english * remove trailing space --- ...nValueMappingMetadataProcessorService.java | 85 +++++++++++++++++++ ...CrossRefImportMetadataSourceServiceIT.java | 6 +- ...sref-to-dspace-publication-type.properties | 29 +++++++ .../spring/api/crossref-integration.xml | 10 +++ 4 files changed, 128 insertions(+), 2 deletions(-) create mode 100644 dspace-api/src/main/java/org/dspace/importer/external/metadatamapping/transform/StringJsonValueMappingMetadataProcessorService.java create mode 100644 dspace/config/crosswalks/mapConverter-crossref-to-dspace-publication-type.properties diff --git a/dspace-api/src/main/java/org/dspace/importer/external/metadatamapping/transform/StringJsonValueMappingMetadataProcessorService.java b/dspace-api/src/main/java/org/dspace/importer/external/metadatamapping/transform/StringJsonValueMappingMetadataProcessorService.java new file mode 100644 index 000000000000..a8f667eb9792 --- /dev/null +++ b/dspace-api/src/main/java/org/dspace/importer/external/metadatamapping/transform/StringJsonValueMappingMetadataProcessorService.java @@ -0,0 +1,85 @@ +/** + * The contents of this file are subject to the license and copyright + * detailed in the LICENSE and NOTICE files at the root of the source + * tree and available online at + * + * http://www.dspace.org/license/ + */ +package org.dspace.importer.external.metadatamapping.transform; + +import static java.util.Optional.ofNullable; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Optional; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.JsonNodeType; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.dspace.importer.external.metadatamapping.contributor.JsonPathMetadataProcessor; +import org.dspace.util.SimpleMapConverter; + +/** + * This class is a Metadata processor from a structured JSON Metadata result + * and uses a SimpleMapConverter, with a mapping properties file + * to map to a single string value based on mapped keys.
    + * Like:
    + * journal-article = Article + * + * @author paulo-graca + */ +public class StringJsonValueMappingMetadataProcessorService implements JsonPathMetadataProcessor { + + private final static Logger log = LogManager.getLogger(); + /** + * The value map converter. + * a list of values to map from + */ + private SimpleMapConverter valueMapConverter; + private String path; + + @Override + public Collection processMetadata(String json) { + JsonNode rootNode = convertStringJsonToJsonNode(json); + Optional abstractNode = Optional.of(rootNode.at(path)); + Collection values = new ArrayList<>(); + + if (abstractNode.isPresent() && abstractNode.get().getNodeType().equals(JsonNodeType.STRING)) { + + String stringValue = abstractNode.get().asText(); + values.add(ofNullable(stringValue) + .map(value -> valueMapConverter != null ? valueMapConverter.getValue(value) : value) + .orElse(valueMapConverter.getValue(null))); + } + return values; + } + + private JsonNode convertStringJsonToJsonNode(String json) { + ObjectMapper mapper = new ObjectMapper(); + JsonNode body = null; + try { + body = mapper.readTree(json); + } catch (JsonProcessingException e) { + log.error("Unable to process json response.", e); + } + return body; + } + + /* Getters and Setters */ + + public String convertType(String type) { + return valueMapConverter != null ? valueMapConverter.getValue(type) : type; + } + + public void setValueMapConverter(SimpleMapConverter valueMapConverter) { + this.valueMapConverter = valueMapConverter; + } + + public void setPath(String path) { + this.path = path; + } + +} diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/CrossRefImportMetadataSourceServiceIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/CrossRefImportMetadataSourceServiceIT.java index f61a81140ddc..a182e7d89070 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/CrossRefImportMetadataSourceServiceIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/CrossRefImportMetadataSourceServiceIT.java @@ -163,7 +163,8 @@ private ArrayList getRecords() { "State of Awareness of Freshers’ Groups Chortkiv State" + " Medical College of Prevention of Iodine Deficiency Diseases"); MetadatumDTO author = createMetadatumDTO("dc", "contributor", "author", "Senyuk, L.V."); - MetadatumDTO type = createMetadatumDTO("dc", "type", null, "journal-article"); + // is expected the dc.type to be mapped from journal-article to Article + MetadatumDTO type = createMetadatumDTO("dc", "type", null, "Article"); MetadatumDTO date = createMetadatumDTO("dc", "date", "issued", "2016-05-19"); MetadatumDTO ispartof = createMetadatumDTO("dc", "relation", "ispartof", "Ukraïnsʹkij žurnal medicini, bìologìï ta sportu"); @@ -192,7 +193,8 @@ private ArrayList getRecords() { MetadatumDTO title2 = createMetadatumDTO("dc", "title", null, "Ischemic Heart Disease and Role of Nurse of Cardiology Department"); MetadatumDTO author2 = createMetadatumDTO("dc", "contributor", "author", "Kozak, K. І."); - MetadatumDTO type2 = createMetadatumDTO("dc", "type", null, "journal-article"); + // is expected the dc.type to be mapped from journal-article to Article + MetadatumDTO type2 = createMetadatumDTO("dc", "type", null, "Article"); MetadatumDTO date2 = createMetadatumDTO("dc", "date", "issued", "2016-05-19"); MetadatumDTO ispartof2 = createMetadatumDTO("dc", "relation", "ispartof", "Ukraïnsʹkij žurnal medicini, bìologìï ta sportu"); diff --git a/dspace/config/crosswalks/mapConverter-crossref-to-dspace-publication-type.properties b/dspace/config/crosswalks/mapConverter-crossref-to-dspace-publication-type.properties new file mode 100644 index 000000000000..3bd4468148d6 --- /dev/null +++ b/dspace/config/crosswalks/mapConverter-crossref-to-dspace-publication-type.properties @@ -0,0 +1,29 @@ +# based on Crossref content type at https://crossref.gitlab.io/knowledge_base/docs/topics/content-types/#types-in-cayenne-rest-api +# Mapping between work type supported by Crossref and the DSpace common publication's types +journal-article = Article +journal-issue = Other +journal-volume = Other +journal = Other +proceedings-article = Other +proceedings = Other +dataset = Dataset +component = Other +report = Other +report-series = Other +standard = Other +standard-series = Other +edited-book = Other +monograph = Other +reference-book = Other +book = Book +book-series = Other +book-set = Other +book-chapter = Book chapter +book-section = Other +book-part = Other +book-track = Other +reference-entry = Other +dissertation = Other +posted-content = Other +peer-review = Other +other = Other \ No newline at end of file diff --git a/dspace/config/spring/api/crossref-integration.xml b/dspace/config/spring/api/crossref-integration.xml index 4786c44a7865..6fe9723d796b 100644 --- a/dspace/config/spring/api/crossref-integration.xml +++ b/dspace/config/spring/api/crossref-integration.xml @@ -49,9 +49,19 @@ + + + + + + + + + + From 0c0a0d56ffd5b82467298f4d4d80767949efd9b8 Mon Sep 17 00:00:00 2001 From: DSpace Bot <68393067+dspace-bot@users.noreply.github.com> Date: Wed, 12 Feb 2025 07:57:47 -0600 Subject: [PATCH 464/979] [Port dspace-8_x] Fixing Crossref document type issue with new metadata mapping processor (#10401) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * new metadata mapping processor for crossref document type (cherry picked from commit 9ab6b8fce22445da658f90010b00a071d2f4e3a1) * adjust crossref test to consider mapped dc.type to Article (cherry picked from commit 2d15f3ef54fbdc2b320eaf0503bd8ea5b684b4fa) * correcting english (cherry picked from commit d9c8366be08d29c2d7ad46c4c110b2fcbd7ae485) * dspace-api: remove trailing whitespace (cherry picked from commit 175075cf5ee83821468e9cc61404912f06e6b24d) --------- Co-authored-by: Paulo Graça Co-authored-by: Alan Orth --- ...nValueMappingMetadataProcessorService.java | 86 +++++++++++++++++++ ...CrossRefImportMetadataSourceServiceIT.java | 6 +- ...sref-to-dspace-publication-type.properties | 29 +++++++ .../spring/api/crossref-integration.xml | 10 +++ 4 files changed, 129 insertions(+), 2 deletions(-) create mode 100644 dspace-api/src/main/java/org/dspace/importer/external/metadatamapping/transform/StringJsonValueMappingMetadataProcessorService.java create mode 100644 dspace/config/crosswalks/mapConverter-crossref-to-dspace-publication-type.properties diff --git a/dspace-api/src/main/java/org/dspace/importer/external/metadatamapping/transform/StringJsonValueMappingMetadataProcessorService.java b/dspace-api/src/main/java/org/dspace/importer/external/metadatamapping/transform/StringJsonValueMappingMetadataProcessorService.java new file mode 100644 index 000000000000..1a5e50e4ac39 --- /dev/null +++ b/dspace-api/src/main/java/org/dspace/importer/external/metadatamapping/transform/StringJsonValueMappingMetadataProcessorService.java @@ -0,0 +1,86 @@ +/** + * The contents of this file are subject to the license and copyright + * detailed in the LICENSE and NOTICE files at the root of the source + * tree and available online at + * + * http://www.dspace.org/license/ + */ +package org.dspace.importer.external.metadatamapping.transform; + +import static java.util.Optional.ofNullable; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Optional; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.JsonNodeType; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.dspace.importer.external.metadatamapping.contributor.JsonPathMetadataProcessor; +import org.dspace.util.SimpleMapConverter; + +/** + * This class is a Metadata processor from a structured JSON Metadata result + * and uses a SimpleMapConverter, with a mapping properties file + * to map to a single string value based on mapped keys.
    + * Like:
    + * journal-article = Article + * + * @author paulo-graca + * + */ +public class StringJsonValueMappingMetadataProcessorService implements JsonPathMetadataProcessor { + + private final static Logger log = LogManager.getLogger(); + /** + * The value map converter. + * a list of values to map from + */ + private SimpleMapConverter valueMapConverter; + private String path; + + @Override + public Collection processMetadata(String json) { + JsonNode rootNode = convertStringJsonToJsonNode(json); + Optional abstractNode = Optional.of(rootNode.at(path)); + Collection values = new ArrayList<>(); + + if (abstractNode.isPresent() && abstractNode.get().getNodeType().equals(JsonNodeType.STRING)) { + + String stringValue = abstractNode.get().asText(); + values.add(ofNullable(stringValue) + .map(value -> valueMapConverter != null ? valueMapConverter.getValue(value) : value) + .orElse(valueMapConverter.getValue(null))); + } + return values; + } + + private JsonNode convertStringJsonToJsonNode(String json) { + ObjectMapper mapper = new ObjectMapper(); + JsonNode body = null; + try { + body = mapper.readTree(json); + } catch (JsonProcessingException e) { + log.error("Unable to process json response.", e); + } + return body; + } + + /* Getters and Setters */ + + public String convertType(String type) { + return valueMapConverter != null ? valueMapConverter.getValue(type) : type; + } + + public void setValueMapConverter(SimpleMapConverter valueMapConverter) { + this.valueMapConverter = valueMapConverter; + } + + public void setPath(String path) { + this.path = path; + } + +} diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/CrossRefImportMetadataSourceServiceIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/CrossRefImportMetadataSourceServiceIT.java index 37bd3a90eeda..f68bfe48db1b 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/CrossRefImportMetadataSourceServiceIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/CrossRefImportMetadataSourceServiceIT.java @@ -163,7 +163,8 @@ private ArrayList getRecords() { "State of Awareness of Freshers’ Groups Chortkiv State" + " Medical College of Prevention of Iodine Deficiency Diseases"); MetadatumDTO author = createMetadatumDTO("dc", "contributor", "author", "Senyuk, L.V."); - MetadatumDTO type = createMetadatumDTO("dc", "type", null, "journal-article"); + // is expected the dc.type to be mapped from journal-article to Article + MetadatumDTO type = createMetadatumDTO("dc", "type", null, "Article"); MetadatumDTO date = createMetadatumDTO("dc", "date", "issued", "2016-05-19"); MetadatumDTO ispartof = createMetadatumDTO("dc", "relation", "ispartof", "Ukraïnsʹkij žurnal medicini, bìologìï ta sportu"); @@ -192,7 +193,8 @@ private ArrayList getRecords() { MetadatumDTO title2 = createMetadatumDTO("dc", "title", null, "Ischemic Heart Disease and Role of Nurse of Cardiology Department"); MetadatumDTO author2 = createMetadatumDTO("dc", "contributor", "author", "Kozak, K. І."); - MetadatumDTO type2 = createMetadatumDTO("dc", "type", null, "journal-article"); + // is expected the dc.type to be mapped from journal-article to Article + MetadatumDTO type2 = createMetadatumDTO("dc", "type", null, "Article"); MetadatumDTO date2 = createMetadatumDTO("dc", "date", "issued", "2016-05-19"); MetadatumDTO ispartof2 = createMetadatumDTO("dc", "relation", "ispartof", "Ukraïnsʹkij žurnal medicini, bìologìï ta sportu"); diff --git a/dspace/config/crosswalks/mapConverter-crossref-to-dspace-publication-type.properties b/dspace/config/crosswalks/mapConverter-crossref-to-dspace-publication-type.properties new file mode 100644 index 000000000000..3bd4468148d6 --- /dev/null +++ b/dspace/config/crosswalks/mapConverter-crossref-to-dspace-publication-type.properties @@ -0,0 +1,29 @@ +# based on Crossref content type at https://crossref.gitlab.io/knowledge_base/docs/topics/content-types/#types-in-cayenne-rest-api +# Mapping between work type supported by Crossref and the DSpace common publication's types +journal-article = Article +journal-issue = Other +journal-volume = Other +journal = Other +proceedings-article = Other +proceedings = Other +dataset = Dataset +component = Other +report = Other +report-series = Other +standard = Other +standard-series = Other +edited-book = Other +monograph = Other +reference-book = Other +book = Book +book-series = Other +book-set = Other +book-chapter = Book chapter +book-section = Other +book-part = Other +book-track = Other +reference-entry = Other +dissertation = Other +posted-content = Other +peer-review = Other +other = Other \ No newline at end of file diff --git a/dspace/config/spring/api/crossref-integration.xml b/dspace/config/spring/api/crossref-integration.xml index 4786c44a7865..6fe9723d796b 100644 --- a/dspace/config/spring/api/crossref-integration.xml +++ b/dspace/config/spring/api/crossref-integration.xml @@ -49,9 +49,19 @@
    + + + + + + + + + + From 4c044adcf388a291dced77b53a9b816a6a11659c Mon Sep 17 00:00:00 2001 From: Martin Walk Date: Thu, 13 Feb 2025 14:34:28 +0100 Subject: [PATCH 465/979] Fix #10405 bug in log4j-cli --- dspace/config/log4j2-cli.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/dspace/config/log4j2-cli.xml b/dspace/config/log4j2-cli.xml index 73acab877f1f..6cac529978fc 100644 --- a/dspace/config/log4j2-cli.xml +++ b/dspace/config/log4j2-cli.xml @@ -25,6 +25,7 @@ From 268b5fc8b703a0fabfdb1ed8b3336e4bbca48ee2 Mon Sep 17 00:00:00 2001 From: Martin Walk Date: Thu, 13 Feb 2025 14:34:28 +0100 Subject: [PATCH 466/979] Fix #10405 bug in log4j-cli (cherry picked from commit 4c044adcf388a291dced77b53a9b816a6a11659c) --- dspace/config/log4j2-cli.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/dspace/config/log4j2-cli.xml b/dspace/config/log4j2-cli.xml index 73acab877f1f..6cac529978fc 100644 --- a/dspace/config/log4j2-cli.xml +++ b/dspace/config/log4j2-cli.xml @@ -25,6 +25,7 @@ From b63329b45a758212048f235c9966ade1ba314021 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 17 Feb 2025 22:25:23 +0000 Subject: [PATCH 467/979] Bump com.github.spotbugs:spotbugs-maven-plugin in the build-tools group Bumps the build-tools group with 1 update: [com.github.spotbugs:spotbugs-maven-plugin](https://github.com/spotbugs/spotbugs-maven-plugin). Updates `com.github.spotbugs:spotbugs-maven-plugin` from 4.8.6.6 to 4.9.1.0 - [Release notes](https://github.com/spotbugs/spotbugs-maven-plugin/releases) - [Commits](https://github.com/spotbugs/spotbugs-maven-plugin/compare/spotbugs-maven-plugin-4.8.6.6...spotbugs-maven-plugin-4.9.1.0) --- updated-dependencies: - dependency-name: com.github.spotbugs:spotbugs-maven-plugin dependency-type: direct:production update-type: version-update:semver-minor dependency-group: build-tools ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 84bba3847341..0de103004d56 100644 --- a/pom.xml +++ b/pom.xml @@ -295,7 +295,7 @@ com.github.spotbugs spotbugs-maven-plugin - 4.8.6.6 + 4.9.1.0 Max Low From edbf9ef60598f40f0147f2563150d5a047623362 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 17 Feb 2025 22:25:48 +0000 Subject: [PATCH 468/979] Bump commons-beanutils:commons-beanutils in the apache-commons group Bumps the apache-commons group with 1 update: commons-beanutils:commons-beanutils. Updates `commons-beanutils:commons-beanutils` from 1.10.0 to 1.10.1 --- updated-dependencies: - dependency-name: commons-beanutils:commons-beanutils dependency-type: direct:production update-type: version-update:semver-patch dependency-group: apache-commons ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 84bba3847341..a6d2a6084637 100644 --- a/pom.xml +++ b/pom.xml @@ -1470,7 +1470,7 @@ commons-beanutils commons-beanutils - 1.10.0 + 1.10.1 commons-cli From 51c766caa32d73aa3d3415cccee59d175499506c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 17 Feb 2025 22:26:49 +0000 Subject: [PATCH 469/979] Bump net.minidev:json-smart from 2.5.1 to 2.5.2 Bumps [net.minidev:json-smart](https://github.com/netplex/json-smart-v2) from 2.5.1 to 2.5.2. - [Release notes](https://github.com/netplex/json-smart-v2/releases) - [Commits](https://github.com/netplex/json-smart-v2/compare/2.5.1...2.5.2) --- updated-dependencies: - dependency-name: net.minidev:json-smart dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- dspace-server-webapp/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dspace-server-webapp/pom.xml b/dspace-server-webapp/pom.xml index f3fd8613fbf5..52105803889e 100644 --- a/dspace-server-webapp/pom.xml +++ b/dspace-server-webapp/pom.xml @@ -514,7 +514,7 @@ net.minidev json-smart - 2.5.1 + 2.5.2 From 1d85653ed0e524d95765bfbf64b18e1fd8436fda Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 17 Feb 2025 22:27:07 +0000 Subject: [PATCH 470/979] Bump com.amazonaws:aws-java-sdk-s3 from 1.12.780 to 1.12.781 Bumps [com.amazonaws:aws-java-sdk-s3](https://github.com/aws/aws-sdk-java) from 1.12.780 to 1.12.781. - [Changelog](https://github.com/aws/aws-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java/compare/1.12.780...1.12.781) --- updated-dependencies: - dependency-name: com.amazonaws:aws-java-sdk-s3 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- dspace-api/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dspace-api/pom.xml b/dspace-api/pom.xml index 29b9865a5a7c..322e1820d325 100644 --- a/dspace-api/pom.xml +++ b/dspace-api/pom.xml @@ -761,7 +761,7 @@ com.amazonaws aws-java-sdk-s3 - 1.12.780 + 1.12.781 From 474c7d763cd1447ef1215623e32994b2e1dac3be Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 17 Feb 2025 22:43:55 +0000 Subject: [PATCH 471/979] Bump com.github.spotbugs:spotbugs-maven-plugin in the build-tools group Bumps the build-tools group with 1 update: [com.github.spotbugs:spotbugs-maven-plugin](https://github.com/spotbugs/spotbugs-maven-plugin). Updates `com.github.spotbugs:spotbugs-maven-plugin` from 4.8.6.6 to 4.9.1.0 - [Release notes](https://github.com/spotbugs/spotbugs-maven-plugin/releases) - [Commits](https://github.com/spotbugs/spotbugs-maven-plugin/compare/spotbugs-maven-plugin-4.8.6.6...spotbugs-maven-plugin-4.9.1.0) --- updated-dependencies: - dependency-name: com.github.spotbugs:spotbugs-maven-plugin dependency-type: direct:production update-type: version-update:semver-minor dependency-group: build-tools ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 9701c50884b4..2be10fe819b5 100644 --- a/pom.xml +++ b/pom.xml @@ -303,7 +303,7 @@ com.github.spotbugs spotbugs-maven-plugin - 4.8.6.6 + 4.9.1.0 Max Low From e4e7fb16d129cd0b56e6e2fcec4730bf68b30cbb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 17 Feb 2025 22:44:13 +0000 Subject: [PATCH 472/979] Bump commons-beanutils:commons-beanutils in the apache-commons group Bumps the apache-commons group with 1 update: commons-beanutils:commons-beanutils. Updates `commons-beanutils:commons-beanutils` from 1.10.0 to 1.10.1 --- updated-dependencies: - dependency-name: commons-beanutils:commons-beanutils dependency-type: direct:production update-type: version-update:semver-patch dependency-group: apache-commons ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 9701c50884b4..cc3d6e1433c4 100644 --- a/pom.xml +++ b/pom.xml @@ -1468,7 +1468,7 @@ commons-beanutils commons-beanutils - 1.10.0 + 1.10.1 commons-cli From de85bb4b9358f7b4a9e7f26e4c72462328edb646 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 17 Feb 2025 22:45:37 +0000 Subject: [PATCH 473/979] Bump the spring group with 12 updates Bumps the spring group with 12 updates: | Package | From | To | | --- | --- | --- | | [org.springframework:spring-orm](https://github.com/spring-projects/spring-framework) | `6.2.2` | `6.2.3` | | [org.springframework:spring-core](https://github.com/spring-projects/spring-framework) | `6.2.2` | `6.2.3` | | [org.springframework:spring-beans](https://github.com/spring-projects/spring-framework) | `6.2.2` | `6.2.3` | | [org.springframework:spring-aop](https://github.com/spring-projects/spring-framework) | `6.2.2` | `6.2.3` | | [org.springframework:spring-context](https://github.com/spring-projects/spring-framework) | `6.2.2` | `6.2.3` | | [org.springframework:spring-context-support](https://github.com/spring-projects/spring-framework) | `6.2.2` | `6.2.3` | | [org.springframework:spring-tx](https://github.com/spring-projects/spring-framework) | `6.2.2` | `6.2.3` | | [org.springframework:spring-jdbc](https://github.com/spring-projects/spring-framework) | `6.2.2` | `6.2.3` | | [org.springframework:spring-web](https://github.com/spring-projects/spring-framework) | `6.2.2` | `6.2.3` | | [org.springframework:spring-webmvc](https://github.com/spring-projects/spring-framework) | `6.2.2` | `6.2.3` | | [org.springframework:spring-expression](https://github.com/spring-projects/spring-framework) | `6.2.2` | `6.2.3` | | [org.springframework:spring-test](https://github.com/spring-projects/spring-framework) | `6.2.2` | `6.2.3` | Updates `org.springframework:spring-orm` from 6.2.2 to 6.2.3 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.2...v6.2.3) Updates `org.springframework:spring-core` from 6.2.2 to 6.2.3 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.2...v6.2.3) Updates `org.springframework:spring-beans` from 6.2.2 to 6.2.3 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.2...v6.2.3) Updates `org.springframework:spring-aop` from 6.2.2 to 6.2.3 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.2...v6.2.3) Updates `org.springframework:spring-context` from 6.2.2 to 6.2.3 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.2...v6.2.3) Updates `org.springframework:spring-context-support` from 6.2.2 to 6.2.3 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.2...v6.2.3) Updates `org.springframework:spring-tx` from 6.2.2 to 6.2.3 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.2...v6.2.3) Updates `org.springframework:spring-jdbc` from 6.2.2 to 6.2.3 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.2...v6.2.3) Updates `org.springframework:spring-web` from 6.2.2 to 6.2.3 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.2...v6.2.3) Updates `org.springframework:spring-webmvc` from 6.2.2 to 6.2.3 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.2...v6.2.3) Updates `org.springframework:spring-expression` from 6.2.2 to 6.2.3 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.2...v6.2.3) Updates `org.springframework:spring-test` from 6.2.2 to 6.2.3 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.2...v6.2.3) Updates `org.springframework:spring-core` from 6.2.2 to 6.2.3 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.2...v6.2.3) Updates `org.springframework:spring-beans` from 6.2.2 to 6.2.3 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.2...v6.2.3) Updates `org.springframework:spring-aop` from 6.2.2 to 6.2.3 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.2...v6.2.3) Updates `org.springframework:spring-context` from 6.2.2 to 6.2.3 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.2...v6.2.3) Updates `org.springframework:spring-context-support` from 6.2.2 to 6.2.3 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.2...v6.2.3) Updates `org.springframework:spring-tx` from 6.2.2 to 6.2.3 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.2...v6.2.3) Updates `org.springframework:spring-jdbc` from 6.2.2 to 6.2.3 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.2...v6.2.3) Updates `org.springframework:spring-web` from 6.2.2 to 6.2.3 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.2...v6.2.3) Updates `org.springframework:spring-webmvc` from 6.2.2 to 6.2.3 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.2...v6.2.3) Updates `org.springframework:spring-expression` from 6.2.2 to 6.2.3 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.2...v6.2.3) Updates `org.springframework:spring-test` from 6.2.2 to 6.2.3 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.2...v6.2.3) --- updated-dependencies: - dependency-name: org.springframework:spring-orm dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-core dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-beans dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-aop dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-context dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-context-support dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-tx dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-jdbc dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-web dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-webmvc dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-expression dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-test dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-core dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-beans dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-aop dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-context dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-context-support dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-tx dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-jdbc dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-web dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-webmvc dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-expression dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-test dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 9701c50884b4..6e2f7fdb818c 100644 --- a/pom.xml +++ b/pom.xml @@ -19,7 +19,7 @@ 17 - 6.2.2 + 6.2.3 3.4.2 6.4.2 6.4.8.Final From 013b0e6b84aaeccf113d8bf56dd9d754800f9044 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 17 Feb 2025 22:45:59 +0000 Subject: [PATCH 474/979] Bump net.minidev:json-smart from 2.5.1 to 2.5.2 Bumps [net.minidev:json-smart](https://github.com/netplex/json-smart-v2) from 2.5.1 to 2.5.2. - [Release notes](https://github.com/netplex/json-smart-v2/releases) - [Commits](https://github.com/netplex/json-smart-v2/compare/2.5.1...2.5.2) --- updated-dependencies: - dependency-name: net.minidev:json-smart dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- dspace-server-webapp/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dspace-server-webapp/pom.xml b/dspace-server-webapp/pom.xml index fdd884cd5c26..4b0e612a7608 100644 --- a/dspace-server-webapp/pom.xml +++ b/dspace-server-webapp/pom.xml @@ -552,7 +552,7 @@ net.minidev json-smart - 2.5.1 + 2.5.2 From c2b5cd0b88abdf5fb7531dca3f0ad866a291900d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 17 Feb 2025 22:46:41 +0000 Subject: [PATCH 475/979] Bump com.amazonaws:aws-java-sdk-s3 from 1.12.780 to 1.12.781 Bumps [com.amazonaws:aws-java-sdk-s3](https://github.com/aws/aws-sdk-java) from 1.12.780 to 1.12.781. - [Changelog](https://github.com/aws/aws-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java/compare/1.12.780...1.12.781) --- updated-dependencies: - dependency-name: com.amazonaws:aws-java-sdk-s3 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- dspace-api/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dspace-api/pom.xml b/dspace-api/pom.xml index 94270abf4cf9..dda9683e1ebd 100644 --- a/dspace-api/pom.xml +++ b/dspace-api/pom.xml @@ -735,7 +735,7 @@ com.amazonaws aws-java-sdk-s3 - 1.12.780 + 1.12.781 + + diff --git a/dspace/config/ldn/announce-relationship b/dspace/config/ldn/announce-relationship index 205a61adb46c..c9b0cf6c3591 100644 --- a/dspace/config/ldn/announce-relationship +++ b/dspace/config/ldn/announce-relationship @@ -17,7 +17,7 @@ { "@context": [ "https://www.w3.org/ns/activitystreams", - "https://purl.org/coar/notify" + "https://coar-notify.net/" ], "actor": { "id": "${params[0]}", diff --git a/dspace/config/ldn/request-endorsement b/dspace/config/ldn/request-endorsement index 443a7029159f..7c241db9cec2 100644 --- a/dspace/config/ldn/request-endorsement +++ b/dspace/config/ldn/request-endorsement @@ -1,6 +1,6 @@ ## generate LDN message json when request-endorsement of an item ## -## Parameters: {0} config 'dspace.ui.url' +## Parameters: {0} config 'dspace.ui.url' or submitter email id (e.g. 'mailto:submitter-email') depending on the service configuration ## {1} config 'ldn.notify.inbox' ## {2} config 'dspace.name' ## {3} Notify Service url @@ -10,16 +10,18 @@ ## {7} the url to the primary bitstream or the first bitstream in the ORIGINAL bundle if there is no primary bitstream. The url is 'dspace.ui.url'/bitstreams/:uuid/download ## {8} the bitstream MimeType or get User Format MimeType if getFormat is 'Unknown' ## {9} id of the created LDNMessage +## {13} type of actor: Service or Person +## {14} config 'dspace.ui.url' { "@context": [ "https://www.w3.org/ns/activitystreams", - "https://purl.org/coar/notify" + "https://coar-notify.net/" ], "actor": { "id": "${params[0]}", "name": "${params[2]}", - "type": "Service" + "type": "${params[13]}" }, "id": "${params[9]}", "object": { @@ -36,7 +38,7 @@ } }, "origin": { - "id": "${params[0]}", + "id": "${params[14]}", "inbox": "${params[1]}", "type": "Service" }, diff --git a/dspace/config/ldn/request-endorsement-resubmission b/dspace/config/ldn/request-endorsement-resubmission new file mode 100644 index 000000000000..edb6bf151ae5 --- /dev/null +++ b/dspace/config/ldn/request-endorsement-resubmission @@ -0,0 +1,56 @@ +## generate LDN message json when request-endorsement of an item +## +## Parameters: {0} config 'dspace.ui.url' or submitter email id (e.g. 'mailto:submitter-email') depending on the service configuration +## {1} config 'ldn.notify.inbox' +## {2} config 'dspace.name' +## {3} Notify Service url +## {4} Notify Service ldnUrl +## {5} 'dspace.ui.url'/handle/xxxx/yyy +## {6} metadata value of 'dc.identifier.uri' +## {7} the url to the primary bitstream or the first bitstream in the ORIGINAL bundle if there is no primary bitstream. The url is 'dspace.ui.url'/bitstreams/:uuid/download +## {8} the bitstream MimeType or get User Format MimeType if getFormat is 'Unknown' +## {9} id of the created LDNMessage +## {13} type of actor: Service or Person +## {14} config 'dspace.ui.url' +## {15} for endorsement resubmissions the inReplyTo notification URN + +{ + "@context": [ + "https://www.w3.org/ns/activitystreams", + "https://coar-notify.net/" + ], + "actor": { + "id": "${params[0]}", + "name": "${params[2]}", + "type": "${params[13]}" + }, + "id": "${params[9]}", + "object": { + "id": "${params[5]}", + "ietf:cite-as": "${params[6]}", + "type": "sorg:AboutPage", + "ietf:item": { + "id": "${params[7]}", + "mediaType": "${params[8]}", + "type": [ + "Article", + "sorg:ScholarlyArticle" + ] + } + }, + ${params[15]} + "origin": { + "id": "${params[14]}", + "inbox": "${params[1]}", + "type": "Service" + }, + "target": { + "id": "${params[3]}", + "inbox": "${params[4]}", + "type": "Service" + }, + "type": [ + "Offer", + "coar-notify:EndorsementAction" + ] +} \ No newline at end of file diff --git a/dspace/config/ldn/request-ingest b/dspace/config/ldn/request-ingest index 701f3929c826..e34072ae0ce6 100644 --- a/dspace/config/ldn/request-ingest +++ b/dspace/config/ldn/request-ingest @@ -14,7 +14,7 @@ { "@context": [ "https://www.w3.org/ns/activitystreams", - "https://purl.org/coar/notify" + "https://coar-notify.net/" ], "actor": { "id": "${params[0]}", diff --git a/dspace/config/ldn/request-review b/dspace/config/ldn/request-review index 01f177b6cc18..1e711c0b7ae9 100644 --- a/dspace/config/ldn/request-review +++ b/dspace/config/ldn/request-review @@ -1,6 +1,6 @@ ## generate LDN message json when request-review of an item ## -## Parameters: {0} config 'dspace.ui.url' +## Parameters: {0} config 'dspace.ui.url' or submitter email id (e.g. 'mailto:submitter-email') depending on the service configuration ## {1} config 'ldn.notify.inbox' ## {2} config 'dspace.name' ## {3} Notify Service url @@ -10,11 +10,12 @@ ## {7} the url to the primary bitstream or the first bitstream in the ORIGINAL bundle if there is no primary bitstream. The url is 'dspace.ui.url'/bitstreams/:uuid/download ## {8} the bitstream MimeType or get User Format MimeType if getFormat is 'Unknown' ## {9} id of the created LDNMessage +## {14} config 'dspace.ui.url' { "@context": [ "https://www.w3.org/ns/activitystreams", - "https://purl.org/coar/notify" + "https://coar-notify.net/" ], "actor": { "id": "${params[0]}", @@ -36,7 +37,7 @@ } }, "origin": { - "id": "${params[0]}", + "id": "${params[14]}", "inbox": "${params[1]}", "type": "Service" }, diff --git a/dspace/config/modules/ldn.cfg b/dspace/config/modules/ldn.cfg index 57f1c878ed35..96d4e39ffc11 100644 --- a/dspace/config/modules/ldn.cfg +++ b/dspace/config/modules/ldn.cfg @@ -25,7 +25,7 @@ ldn.queue.timeout.checker.cron = 0 0 */1 * * ? # LDN Queue extractor elaborates LDN Message entities with max_attempts < than ldn.processor.max.attempts ldn.processor.max.attempts = 5 -# LDN Queue extractor sets LDN Message Entity queue_timeout property every time it tryies a new elaboration +# LDN Queue extractor sets LDN Message Entity queue_timeout property every time it tries a new elaboration # of the message. LDN Message with a future queue_timeout is not elaborated. This property is used to calculateas: # a new timeout, such as: new_timeout = now + ldn.processor.queue.msg.timeout (in minutes) ldn.processor.queue.msg.timeout = 60 @@ -42,5 +42,13 @@ ldn.notify.inbox.block-untrusted-ip = true # EMAIL CONFIGURATION - +# Supported values for actionSendFilter are: +# single email, "GROUP:" or "SUBMITTER" ldn.notification.email = ${mail.admin} +# Review and endorsement notifications may make use of submitter email for requests, as this could be null, +# provide a configurable fallback email option +ldn.notification.email.submitter.fallback = ${mail.admin} +# Review and endorsement notifications may require using an Email Actor ID +# Configure whether the specified service (by service ID) requires using an Email Actor ID +# when sending requests as opposed to a Service URL +# ldn.notification.supportsActorEmailId. = true diff --git a/dspace/config/modules/signposting.cfg b/dspace/config/modules/signposting.cfg index fba80da41481..6acfa74bdd07 100644 --- a/dspace/config/modules/signposting.cfg +++ b/dspace/config/modules/signposting.cfg @@ -32,4 +32,7 @@ signposting.enabled = true signposting.describedby.crosswalk-name = DataCite # Mime-type of response of handling of 'describedby' links. -signposting.describedby.mime-type = application/vnd.datacite.datacite+xml \ No newline at end of file +signposting.describedby.mime-type = application/vnd.datacite.datacite+xml + +# Optional, to expose the profile attribute, required by PCI workflow () +# signposting.describedby.profile = http://datacite.org/schema/kernel-4 \ No newline at end of file diff --git a/dspace/config/spring/api/ldn-coar-notify.xml b/dspace/config/spring/api/ldn-coar-notify.xml index 2329925a5413..ce00d7b30353 100644 --- a/dspace/config/spring/api/ldn-coar-notify.xml +++ b/dspace/config/spring/api/ldn-coar-notify.xml @@ -70,6 +70,15 @@ + + + + TentativeAccept + coar-notify:EndorsementAction + + + + @@ -77,6 +86,15 @@ coar-notify:EndorsementAction + + + + + + Reject + coar-notify:EndorsementAction + + @@ -260,6 +278,18 @@ + + + + + + + + + + + + From 5c19f73bf8c3fe0a3df285c737504ac7f1fdd08a Mon Sep 17 00:00:00 2001 From: Agustina Martinez Date: Wed, 26 Feb 2025 08:59:04 +0000 Subject: [PATCH 478/979] Add integration tests --- .../dspace/app/ldn/LDNMessageConsumer.java | 5 +- .../dspace/app/rest/LDNInboxControllerIT.java | 138 ++++++++++++++++++ .../app/rest/ldn_ack_endorsement_reject.json | 34 +++++ .../app/rest/ldn_ack_review_reject.json | 2 +- 4 files changed, 177 insertions(+), 2 deletions(-) create mode 100644 dspace-server-webapp/src/test/resources/org/dspace/app/rest/ldn_ack_endorsement_reject.json diff --git a/dspace-api/src/main/java/org/dspace/app/ldn/LDNMessageConsumer.java b/dspace-api/src/main/java/org/dspace/app/ldn/LDNMessageConsumer.java index d5a756093f6f..58c365557c01 100644 --- a/dspace-api/src/main/java/org/dspace/app/ldn/LDNMessageConsumer.java +++ b/dspace-api/src/main/java/org/dspace/app/ldn/LDNMessageConsumer.java @@ -89,6 +89,9 @@ public void consume(Context context, Event event) throws Exception { } Item item = (Item) event.getSubject(context); + if (item == null) { + return; + } createManualLDNMessages(context, item); createAutomaticLDNMessages(context, item); } @@ -190,7 +193,7 @@ private void createLDNMessage(Context context, Item item, NotifyServiceEntity se appendGeneratedMessage(ldn, ldnMessage, actorID, - (actorID != null && item.getSubmitter() != null) ? item.getSubmitter().getFullName() : null, + (actorID != null && item.getSubmitter() != null) ? item.getSubmitter().getName() : null, resubmissionID); ObjectMapper mapper = new ObjectMapper(); diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/LDNInboxControllerIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/LDNInboxControllerIT.java index 78410af2e686..1092f0273f52 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/LDNInboxControllerIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/LDNInboxControllerIT.java @@ -26,23 +26,32 @@ import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.io.IOUtils; import org.dspace.app.ldn.LDNMessageEntity; +import org.dspace.app.ldn.NotifyPatternToTrigger; import org.dspace.app.ldn.NotifyServiceEntity; import org.dspace.app.ldn.model.Notification; import org.dspace.app.ldn.service.LDNMessageService; +import org.dspace.app.ldn.service.NotifyPatternToTriggerService; import org.dspace.app.rest.test.AbstractControllerIntegrationTest; import org.dspace.builder.CollectionBuilder; import org.dspace.builder.CommunityBuilder; import org.dspace.builder.ItemBuilder; import org.dspace.builder.NotifyServiceBuilder; +import org.dspace.builder.WorkspaceItemBuilder; import org.dspace.content.Collection; import org.dspace.content.Community; import org.dspace.content.Item; +import org.dspace.content.WorkspaceItem; +import org.dspace.content.service.InstallItemService; +import org.dspace.content.service.WorkspaceItemService; import org.dspace.matcher.QASourceMatcher; import org.dspace.matcher.QATopicMatcher; import org.dspace.qaevent.QANotifyPatterns; import org.dspace.qaevent.service.QAEventService; import org.dspace.services.ConfigurationService; import org.dspace.utils.DSpace; +import org.dspace.versioning.Version; +import org.dspace.versioning.service.VersioningService; +import org.dspace.xmlworkflow.service.XmlWorkflowService; import org.junit.After; import org.junit.Test; import org.springframework.beans.factory.annotation.Autowired; @@ -65,6 +74,21 @@ public class LDNInboxControllerIT extends AbstractControllerIntegrationTest { private QAEventService qaEventService = new DSpace().getSingletonService(QAEventService.class); + @Autowired + private XmlWorkflowService workflowService; + + @Autowired + private VersioningService versioningService; + + @Autowired + private InstallItemService installItemService; + + @Autowired + private WorkspaceItemService workspaceItemService; + + @Autowired + private NotifyPatternToTriggerService notifyPatternToTriggerService; + @Test public void ldnInboxAnnounceEndorsementTest() throws Exception { context.turnOffAuthorisationSystem(); @@ -213,6 +237,46 @@ public void ldnInboxOfferReviewAndACKTest() throws Exception { } + @Test + public void ldnInboxOfferReviewAndTentativeRejectTest() throws Exception { + context.turnOffAuthorisationSystem(); + Community community = CommunityBuilder.createCommunity(context).withName("community").build(); + Collection collection = CollectionBuilder.createCollection(context, community).build(); + Item item = ItemBuilder.createItem(context, collection) + .withTitle("Test Item") + .build(); + String object = configurationService.getProperty("dspace.ui.url") + "/handle/" + item.getHandle(); + NotifyServiceEntity notifyServiceEntity = + NotifyServiceBuilder.createNotifyServiceBuilder(context, "service name") + .withDescription("service description") + .withUrl("https://review-service.com/inbox/about/") + .withLdnUrl("https://review-service.com/inbox/") + .withScore(BigDecimal.valueOf(0.6d)) + .withStatus(true) + .withLowerIp("127.0.0.1") + .withUpperIp("127.0.0.3") + .build(); + InputStream ackReviewStream = getClass().getResourceAsStream("ldn_ack_review_reject.json"); + String ackReview = IOUtils.toString(ackReviewStream, Charset.defaultCharset()); + String ackMessage = ackReview.replaceAll("<>", object); + ackMessage = ackMessage.replaceAll("<>", + "urn:uuid:0370c0fb-bb78-4a9b-87f5-bed307a509da"); + + ObjectMapper mapper = new ObjectMapper(); + Notification notification = mapper.readValue(ackMessage, Notification.class); + getClient() + .perform(post("/ldn/inbox") + .contentType("application/ld+json") + .content(ackMessage)) + .andExpect(status().isAccepted()); + + int processed = ldnMessageService.extractAndProcessMessageFromQueue(context); + assertEquals(processed, 1); + processed = ldnMessageService.extractAndProcessMessageFromQueue(context); + assertEquals(processed, 0); + // check Tentative Reject is correctly received + } + @Test public void ldnInboxAnnounceReleaseTest() throws Exception { context.turnOffAuthorisationSystem(); @@ -387,6 +451,80 @@ public void ldnInboxOutOfRangeIPwithDisabledCheckTest() throws Exception { assertEquals(ldnMessage.getQueueStatus(), LDNMessageEntity.QUEUE_STATUS_UNTRUSTED_IP); } + + @Test + public void testLDNMessageConsumerRequestEndorsementResubmission() throws Exception { + context.turnOffAuthorisationSystem(); + + Community community = CommunityBuilder.createCommunity(context).withName("community").build(); + Collection collection = CollectionBuilder.createCollection(context, community).build(); + NotifyServiceEntity notifyServiceEntity = + NotifyServiceBuilder.createNotifyServiceBuilder(context, "service name") + .withDescription("service description") + .withUrl("https://review-service.com/inbox/about/") + .withLdnUrl("https://review-service.com/inbox/") + .withScore(BigDecimal.valueOf(0.6d)) + .withStatus(true) + .withLowerIp("127.0.0.1") + .withUpperIp("127.0.0.3") + .build(); + WorkspaceItem workspaceItem = WorkspaceItemBuilder.createWorkspaceItem(context, collection) + .withTitle("Submission Item") + .withIssueDate("2023-11-20") + .withFulltext("test.txt", "test", InputStream.nullInputStream()) + .grantLicense() + .withCOARNotifyService(notifyServiceEntity, "request-endorsement") + .build(); + Item item = installItemService.installItem(context, workspaceItem); + + context.commit(); + int processed = ldnMessageService.extractAndProcessMessageFromQueue(context); + assertEquals(processed, 1); + List messages = ldnMessageService.findAll(context); + String notificationId = messages.get(0).getID(); + processed = ldnMessageService.extractAndProcessMessageFromQueue(context); + assertEquals(processed, 0); + + String object = configurationService.getProperty("dspace.ui.url") + "/handle/" + item.getHandle(); + InputStream ackReviewStream = getClass().getResourceAsStream("ldn_ack_review_reject.json"); + String ackReview = IOUtils.toString(ackReviewStream, Charset.defaultCharset()); + String ackMessage = ackReview.replaceAll("<>", object); + ackMessage = ackMessage.replaceAll("<>", notificationId); + + ObjectMapper mapper = new ObjectMapper(); + Notification notification = mapper.readValue(ackMessage, Notification.class); + getClient() + .perform(post("/ldn/inbox") + .contentType("application/ld+json") + .content(ackMessage)) + .andExpect(status().isAccepted()); + + processed = ldnMessageService.extractAndProcessMessageFromQueue(context); + assertEquals(processed, 1); + processed = ldnMessageService.extractAndProcessMessageFromQueue(context); + assertEquals(processed, 0); + + // We received a first notification for rejectance + + + Version version = versioningService.createNewVersion(context, context.reloadEntity(item)); + WorkspaceItem wsi = workspaceItemService.findByItem(context, version.getItem()); + + NotifyPatternToTrigger notifyPatternToTrigger = notifyPatternToTriggerService.create(context); + notifyPatternToTrigger.setItem(wsi.getItem()); + notifyPatternToTrigger.setNotifyService(notifyServiceEntity); + notifyPatternToTrigger.setPattern("request-endorsement"); + notifyPatternToTriggerService.update(context, notifyPatternToTrigger); + installItemService.installItem(context, wsi); + context.commit(); + + processed = ldnMessageService.extractAndProcessMessageFromQueue(context); + assertEquals(processed, 1); + processed = ldnMessageService.extractAndProcessMessageFromQueue(context); + assertEquals(processed, 0); + + } + private static RequestPostProcessor remoteHost(final String remoteHost, final String remoteAddr) { return request -> { request.setRemoteHost(remoteHost); diff --git a/dspace-server-webapp/src/test/resources/org/dspace/app/rest/ldn_ack_endorsement_reject.json b/dspace-server-webapp/src/test/resources/org/dspace/app/rest/ldn_ack_endorsement_reject.json new file mode 100644 index 000000000000..6d06ecbce853 --- /dev/null +++ b/dspace-server-webapp/src/test/resources/org/dspace/app/rest/ldn_ack_endorsement_reject.json @@ -0,0 +1,34 @@ +{ + "@context": [ + "https://www.w3.org/ns/activitystreams", + "https://purl.org/coar/notify" + ], + "actor": { + "id": "https://generic-service.com", + "name": "Generic Service", + "type": "Service" + }, + "context": { + "id": "<>", + "ietf:cite-as": "https://doi.org/10.4598/12123487", + "type": "Document" + }, + "id": "urn:uuid:668f26e0-2c8d-4117-a0d2-ee713523bcb4", + "inReplyTo": "<>", + "object": { + "id": "<>", + "object": "https://some-organisation.org/resource/0021", + "type": "Offer" + }, + "origin": { + "id": "https://review-service.com/inbox/about/", + "inbox": "https://review-service.com/inbox/", + "type": "Service" + }, + "target": { + "id": "https://some-organisation.org", + "inbox": "hop", + "type": "Organization" + }, + "type": ["TentativeReject"] +} \ No newline at end of file diff --git a/dspace-server-webapp/src/test/resources/org/dspace/app/rest/ldn_ack_review_reject.json b/dspace-server-webapp/src/test/resources/org/dspace/app/rest/ldn_ack_review_reject.json index 2ac1f1a15a4a..4323571ec87c 100644 --- a/dspace-server-webapp/src/test/resources/org/dspace/app/rest/ldn_ack_review_reject.json +++ b/dspace-server-webapp/src/test/resources/org/dspace/app/rest/ldn_ack_review_reject.json @@ -21,7 +21,7 @@ "type": "Offer" }, "origin": { - "id": "https://generic-service.com/system", + "id": "https://review-service.com/inbox/about/", "inbox": "https://review-service.com/inbox/", "type": "Service" }, From 05a3d30b04798d449f54dcd02960ac3400cebc0f Mon Sep 17 00:00:00 2001 From: Agustina Martinez Date: Thu, 27 Feb 2025 18:18:20 +0000 Subject: [PATCH 479/979] Update LDNMessageConsumer.java Replace getName call with getFullName to retrieve full name rather than email --- .../src/main/java/org/dspace/app/ldn/LDNMessageConsumer.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dspace-api/src/main/java/org/dspace/app/ldn/LDNMessageConsumer.java b/dspace-api/src/main/java/org/dspace/app/ldn/LDNMessageConsumer.java index 58c365557c01..8698559a5f89 100644 --- a/dspace-api/src/main/java/org/dspace/app/ldn/LDNMessageConsumer.java +++ b/dspace-api/src/main/java/org/dspace/app/ldn/LDNMessageConsumer.java @@ -193,7 +193,7 @@ private void createLDNMessage(Context context, Item item, NotifyServiceEntity se appendGeneratedMessage(ldn, ldnMessage, actorID, - (actorID != null && item.getSubmitter() != null) ? item.getSubmitter().getName() : null, + (actorID != null && item.getSubmitter() != null) ? item.getSubmitter().getFullName() : null, resubmissionID); ObjectMapper mapper = new ObjectMapper(); From f74fe75b60d5ec188dc151aae29626e34b9d78f7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 3 Mar 2025 22:52:24 +0000 Subject: [PATCH 480/979] Bump slf4j.version from 2.0.16 to 2.0.17 Bumps `slf4j.version` from 2.0.16 to 2.0.17. Updates `org.slf4j:jcl-over-slf4j` from 2.0.16 to 2.0.17 Updates `org.slf4j:slf4j-api` from 2.0.16 to 2.0.17 --- updated-dependencies: - dependency-name: org.slf4j:jcl-over-slf4j dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.slf4j:slf4j-api dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 155fac66ddb2..8a93c0bf067b 100644 --- a/pom.xml +++ b/pom.xml @@ -42,7 +42,7 @@ 2.24.3 2.0.33 1.19.0 - 2.0.16 + 2.0.17 2.9.3 1.80 From 15dab1e41e3b0734e434c08e43db914750182a88 Mon Sep 17 00:00:00 2001 From: Toni Prieto Date: Fri, 28 Feb 2025 11:27:36 +0100 Subject: [PATCH 481/979] Use NestableJsonFacet to process browse entries count response (cherry picked from commit 7ba09b7a857f6f3545d88b26940c1089692a860a) --- .../java/org/dspace/discovery/SolrServiceImpl.java | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/discovery/SolrServiceImpl.java b/dspace-api/src/main/java/org/dspace/discovery/SolrServiceImpl.java index 34efe96ae7f8..29b695b5e785 100644 --- a/dspace-api/src/main/java/org/dspace/discovery/SolrServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/discovery/SolrServiceImpl.java @@ -41,6 +41,8 @@ import org.apache.solr.client.solrj.SolrServerException; import org.apache.solr.client.solrj.response.FacetField; import org.apache.solr.client.solrj.response.QueryResponse; +import org.apache.solr.client.solrj.response.json.BucketBasedJsonFacet; +import org.apache.solr.client.solrj.response.json.NestableJsonFacet; import org.apache.solr.client.solrj.util.ClientUtils; import org.apache.solr.common.SolrDocument; import org.apache.solr.common.SolrDocumentList; @@ -1105,13 +1107,11 @@ protected DiscoverResult retrieveResult(Context context, DiscoverQuery query) */ private void resolveEntriesCount(DiscoverResult result, QueryResponse solrQueryResponse) { - Object facetsObj = solrQueryResponse.getResponse().get("facets"); - if (facetsObj instanceof NamedList) { - NamedList facets = (NamedList) facetsObj; - Object bucketsInfoObj = facets.get("entries_count"); - if (bucketsInfoObj instanceof NamedList) { - NamedList bucketsInfo = (NamedList) bucketsInfoObj; - result.setTotalEntries((int) bucketsInfo.get("numBuckets")); + NestableJsonFacet response = solrQueryResponse.getJsonFacetingResponse(); + if (response != null) { + BucketBasedJsonFacet facet = response.getBucketBasedFacets("entries_count"); + if (facet != null) { + result.setTotalEntries(facet.getNumBucketsCount()); } } } From 113553dc620e4bae480b4b392e9744bbc4516db6 Mon Sep 17 00:00:00 2001 From: Toni Prieto Date: Fri, 28 Feb 2025 11:27:36 +0100 Subject: [PATCH 482/979] Use NestableJsonFacet to process browse entries count response (cherry picked from commit 7ba09b7a857f6f3545d88b26940c1089692a860a) --- .../java/org/dspace/discovery/SolrServiceImpl.java | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/discovery/SolrServiceImpl.java b/dspace-api/src/main/java/org/dspace/discovery/SolrServiceImpl.java index 75f0a0ac4d05..a493edad310b 100644 --- a/dspace-api/src/main/java/org/dspace/discovery/SolrServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/discovery/SolrServiceImpl.java @@ -41,6 +41,8 @@ import org.apache.solr.client.solrj.SolrServerException; import org.apache.solr.client.solrj.response.FacetField; import org.apache.solr.client.solrj.response.QueryResponse; +import org.apache.solr.client.solrj.response.json.BucketBasedJsonFacet; +import org.apache.solr.client.solrj.response.json.NestableJsonFacet; import org.apache.solr.client.solrj.util.ClientUtils; import org.apache.solr.common.SolrDocument; import org.apache.solr.common.SolrDocumentList; @@ -1105,13 +1107,11 @@ protected DiscoverResult retrieveResult(Context context, DiscoverQuery query) */ private void resolveEntriesCount(DiscoverResult result, QueryResponse solrQueryResponse) { - Object facetsObj = solrQueryResponse.getResponse().get("facets"); - if (facetsObj instanceof NamedList) { - NamedList facets = (NamedList) facetsObj; - Object bucketsInfoObj = facets.get("entries_count"); - if (bucketsInfoObj instanceof NamedList) { - NamedList bucketsInfo = (NamedList) bucketsInfoObj; - result.setTotalEntries((int) bucketsInfo.get("numBuckets")); + NestableJsonFacet response = solrQueryResponse.getJsonFacetingResponse(); + if (response != null) { + BucketBasedJsonFacet facet = response.getBucketBasedFacets("entries_count"); + if (facet != null) { + result.setTotalEntries(facet.getNumBucketsCount()); } } } From 238893ce6dc5376338bf3add87fef288209bcd44 Mon Sep 17 00:00:00 2001 From: Zahraa Chreim Date: Tue, 11 Mar 2025 16:02:03 +0200 Subject: [PATCH 483/979] Fix invalid cast in DOIOrganiser exception handling --- .../dspace/identifier/doi/DOIOrganiser.java | 70 +++++++++---------- 1 file changed, 35 insertions(+), 35 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/identifier/doi/DOIOrganiser.java b/dspace-api/src/main/java/org/dspace/identifier/doi/DOIOrganiser.java index 507503ffaa15..1a142802d41a 100644 --- a/dspace-api/src/main/java/org/dspace/identifier/doi/DOIOrganiser.java +++ b/dspace-api/src/main/java/org/dspace/identifier/doi/DOIOrganiser.java @@ -448,28 +448,28 @@ public void register(DOI doiRow, Filter filter) + " is successfully registered."); } } catch (IdentifierException ex) { + String message; if (!(ex instanceof DOIIdentifierException)) { - LOG.error("It wasn't possible to register this identifier: " - + DOI.SCHEME + doiRow.getDoi() - + " online. ", ex); + message = "It wasn't possible to register this identifier: " + + DOI.SCHEME + doiRow.getDoi() + + " online. "; + } else { + DOIIdentifierException doiIdentifierException = (DOIIdentifierException) ex; + message = "It wasn't possible to register this identifier : " + + DOI.SCHEME + doiRow.getDoi() + + " online. Exceptions code: " + + DOIIdentifierException.codeToString(doiIdentifierException.getCode()); } - DOIIdentifierException doiIdentifierException = (DOIIdentifierException) ex; - try { sendAlertMail("Register", dso, DOI.SCHEME + doiRow.getDoi(), - doiIdentifierException.codeToString(doiIdentifierException - .getCode())); + message); } catch (IOException ioe) { LOG.error("Couldn't send mail", ioe); } - LOG.error("It wasn't possible to register this identifier : " - + DOI.SCHEME + doiRow.getDoi() - + " online. Exceptions code: " - + doiIdentifierException - .codeToString(doiIdentifierException.getCode()), ex); + LOG.error(message, ex); if (!quiet) { System.err.println("It wasn't possible to register this identifier: " @@ -541,27 +541,27 @@ public void reserve(DOI doiRow, Filter filter) { System.out.println("This identifier : " + DOI.SCHEME + doiRow.getDoi() + " is successfully reserved."); } } catch (IdentifierException ex) { + String message; if (!(ex instanceof DOIIdentifierException)) { - LOG.error("It wasn't possible to register this identifier : " - + DOI.SCHEME + doiRow.getDoi() - + " online. ", ex); + message = "It wasn't possible to register this identifier : " + + DOI.SCHEME + doiRow.getDoi() + + " online. "; + } else { + DOIIdentifierException doiIdentifierException = (DOIIdentifierException) ex; + message = "It wasn't possible to reserve the identifier online. " + + " Exceptions code: " + + DOIIdentifierException.codeToString(doiIdentifierException.getCode()); } - DOIIdentifierException doiIdentifierException = (DOIIdentifierException) ex; - try { sendAlertMail("Reserve", dso, DOI.SCHEME + doiRow.getDoi(), - DOIIdentifierException.codeToString( - doiIdentifierException.getCode())); + message); } catch (IOException ioe) { LOG.error("Couldn't send mail", ioe); } - LOG.error("It wasn't possible to reserve the identifier online. " - + " Exceptions code: " - + DOIIdentifierException - .codeToString(doiIdentifierException.getCode()), ex); + LOG.error(message, ex); if (!quiet) { System.err.println("It wasn't possible to reserve this identifier: " + DOI.SCHEME + doiRow.getDoi()); @@ -606,27 +606,27 @@ public void update(DOI doiRow) { + doiRow.getDoi() + "."); } } catch (IdentifierException ex) { + String message; if (!(ex instanceof DOIIdentifierException)) { - LOG.error("Registering DOI {} for object {}: the registrar returned an error.", - doiRow.getDoi(), dso.getID(), ex); + message = String.format("Registering DOI %s for object %s: the registrar returned an error.", + doiRow.getDoi(), dso.getID()); + } else { + DOIIdentifierException doiIdentifierException = (DOIIdentifierException) ex; + message = "It wasn't possible to update this identifier: " + + DOI.SCHEME + doiRow.getDoi() + + " Exceptions code: " + + DOIIdentifierException.codeToString(doiIdentifierException.getCode()); } - DOIIdentifierException doiIdentifierException = (DOIIdentifierException) ex; - try { sendAlertMail("Update", dso, DOI.SCHEME + doiRow.getDoi(), - doiIdentifierException.codeToString(doiIdentifierException - .getCode())); + message); } catch (IOException ioe) { LOG.error("Couldn't send mail", ioe); } - LOG.error("It wasn't possible to update this identifier: " - + DOI.SCHEME + doiRow.getDoi() - + " Exceptions code: " - + doiIdentifierException - .codeToString(doiIdentifierException.getCode()), ex); + LOG.error(message, ex); if (!quiet) { System.err.println("It wasn't possible to update this identifier: " + DOI.SCHEME + doiRow.getDoi()); @@ -830,4 +830,4 @@ private void setQuiet() { this.quiet = true; } -} \ No newline at end of file +} From d77982683dce105f96990f4715397da2077f31db Mon Sep 17 00:00:00 2001 From: Kim Shepherd Date: Tue, 11 Mar 2025 14:37:32 +0100 Subject: [PATCH 484/979] #10476: Fix debug port conflicts in Dockerfile test env Reset JAVA_TOOL_OPTIONS in the launcher Include commented example of CLI debugging in launcher (cherry picked from commit 1dcb880e00256fb6251d6c8d92907819f0fd7b5f) --- dspace/bin/dspace | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/dspace/bin/dspace b/dspace/bin/dspace index 24644aae9112..05d0ec2d2376 100644 --- a/dspace/bin/dspace +++ b/dspace/bin/dspace @@ -33,7 +33,14 @@ if [ "$1" = "classpath" ]; then exit 0 fi -#Allow user to specify java options through JAVA_OPTS variable +# Reset JAVA_TOOL_OPTIONS to empty, so that any debug ports don't conflict +JAVA_TOOL_OPTIONS="" +# Note, if you wish to also debug the CLI launcher, the following line is an +# example of how to do so - note the different port number and 'suspend=y' which +# means the JVM will not begin execution until a debugger is attached. +#JAVA_TOOL_OPTIONS="-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=*:1043" + +# Allow user to specify java options through JAVA_OPTS variable if [ "$JAVA_OPTS" = "" ]; then #Default Java to use 256MB of memory JAVA_OPTS="-Xmx256m -Dfile.encoding=UTF-8" From 08be12a9329a2dfcb844806d0225d2992887e08e Mon Sep 17 00:00:00 2001 From: Kim Shepherd Date: Tue, 11 Mar 2025 16:02:28 +0100 Subject: [PATCH 485/979] #10476: Fix debug port conflicts in Dockerfile test env Unset JAVA_TOOL_OPTIONS rather than overwriting (cherry picked from commit f93e498f9b7a7509c508a2739fd32ede335c4884) --- dspace/bin/dspace | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dspace/bin/dspace b/dspace/bin/dspace index 05d0ec2d2376..ca9832fe2b2f 100644 --- a/dspace/bin/dspace +++ b/dspace/bin/dspace @@ -33,8 +33,8 @@ if [ "$1" = "classpath" ]; then exit 0 fi -# Reset JAVA_TOOL_OPTIONS to empty, so that any debug ports don't conflict -JAVA_TOOL_OPTIONS="" +# Unset JAVA_TOOL_OPTIONS (exported by Dockerfile) so that any debug ports don't conflict +unset JAVA_TOOL_OPTIONS # Note, if you wish to also debug the CLI launcher, the following line is an # example of how to do so - note the different port number and 'suspend=y' which # means the JVM will not begin execution until a debugger is attached. From 0cb255ca7cd891d672dabd53b91041999c496189 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 17 Mar 2025 23:20:36 +0000 Subject: [PATCH 486/979] Bump the spring group across 1 directory with 24 updates Bumps the spring group with 24 updates in the / directory: | Package | From | To | | --- | --- | --- | | [org.springframework:spring-orm](https://github.com/spring-projects/spring-framework) | `6.2.3` | `6.2.4` | | [org.springframework:spring-core](https://github.com/spring-projects/spring-framework) | `6.2.3` | `6.2.4` | | [org.springframework:spring-beans](https://github.com/spring-projects/spring-framework) | `6.2.3` | `6.2.4` | | [org.springframework:spring-aop](https://github.com/spring-projects/spring-framework) | `6.2.3` | `6.2.4` | | [org.springframework:spring-context](https://github.com/spring-projects/spring-framework) | `6.2.3` | `6.2.4` | | [org.springframework:spring-context-support](https://github.com/spring-projects/spring-framework) | `6.2.3` | `6.2.4` | | [org.springframework:spring-tx](https://github.com/spring-projects/spring-framework) | `6.2.3` | `6.2.4` | | [org.springframework:spring-jdbc](https://github.com/spring-projects/spring-framework) | `6.2.3` | `6.2.4` | | [org.springframework:spring-web](https://github.com/spring-projects/spring-framework) | `6.2.3` | `6.2.4` | | [org.springframework:spring-webmvc](https://github.com/spring-projects/spring-framework) | `6.2.3` | `6.2.4` | | [org.springframework:spring-expression](https://github.com/spring-projects/spring-framework) | `6.2.3` | `6.2.4` | | [org.springframework:spring-test](https://github.com/spring-projects/spring-framework) | `6.2.3` | `6.2.4` | | [org.springframework.boot:spring-boot-starter-test](https://github.com/spring-projects/spring-boot) | `3.4.2` | `3.4.3` | | [org.springframework.boot:spring-boot-starter-tomcat](https://github.com/spring-projects/spring-boot) | `3.4.2` | `3.4.3` | | [org.springframework.boot:spring-boot-maven-plugin](https://github.com/spring-projects/spring-boot) | `3.4.2` | `3.4.3` | | [org.springframework.boot:spring-boot-starter-cache](https://github.com/spring-projects/spring-boot) | `3.4.2` | `3.4.3` | | [org.springframework.boot:spring-boot-starter](https://github.com/spring-projects/spring-boot) | `3.4.2` | `3.4.3` | | [org.springframework.boot:spring-boot-starter-web](https://github.com/spring-projects/spring-boot) | `3.4.2` | `3.4.3` | | [org.springframework.boot:spring-boot-starter-data-rest](https://github.com/spring-projects/spring-boot) | `3.4.2` | `3.4.3` | | [org.springframework.boot:spring-boot-starter-security](https://github.com/spring-projects/spring-boot) | `3.4.2` | `3.4.3` | | [org.springframework.boot:spring-boot-starter-aop](https://github.com/spring-projects/spring-boot) | `3.4.2` | `3.4.3` | | [org.springframework.boot:spring-boot-starter-actuator](https://github.com/spring-projects/spring-boot) | `3.4.2` | `3.4.3` | | [org.springframework.boot:spring-boot-starter-log4j2](https://github.com/spring-projects/spring-boot) | `3.4.2` | `3.4.3` | | [org.springframework.security:spring-security-test](https://github.com/spring-projects/spring-security) | `6.4.2` | `6.4.4` | Updates `org.springframework:spring-orm` from 6.2.3 to 6.2.4 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.3...v6.2.4) Updates `org.springframework:spring-core` from 6.2.3 to 6.2.4 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.3...v6.2.4) Updates `org.springframework:spring-beans` from 6.2.3 to 6.2.4 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.3...v6.2.4) Updates `org.springframework:spring-aop` from 6.2.3 to 6.2.4 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.3...v6.2.4) Updates `org.springframework:spring-context` from 6.2.3 to 6.2.4 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.3...v6.2.4) Updates `org.springframework:spring-context-support` from 6.2.3 to 6.2.4 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.3...v6.2.4) Updates `org.springframework:spring-tx` from 6.2.3 to 6.2.4 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.3...v6.2.4) Updates `org.springframework:spring-jdbc` from 6.2.3 to 6.2.4 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.3...v6.2.4) Updates `org.springframework:spring-web` from 6.2.3 to 6.2.4 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.3...v6.2.4) Updates `org.springframework:spring-webmvc` from 6.2.3 to 6.2.4 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.3...v6.2.4) Updates `org.springframework:spring-expression` from 6.2.3 to 6.2.4 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.3...v6.2.4) Updates `org.springframework:spring-test` from 6.2.3 to 6.2.4 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.3...v6.2.4) Updates `org.springframework:spring-core` from 6.2.3 to 6.2.4 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.3...v6.2.4) Updates `org.springframework:spring-beans` from 6.2.3 to 6.2.4 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.3...v6.2.4) Updates `org.springframework:spring-aop` from 6.2.3 to 6.2.4 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.3...v6.2.4) Updates `org.springframework:spring-context` from 6.2.3 to 6.2.4 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.3...v6.2.4) Updates `org.springframework:spring-context-support` from 6.2.3 to 6.2.4 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.3...v6.2.4) Updates `org.springframework:spring-tx` from 6.2.3 to 6.2.4 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.3...v6.2.4) Updates `org.springframework:spring-jdbc` from 6.2.3 to 6.2.4 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.3...v6.2.4) Updates `org.springframework:spring-web` from 6.2.3 to 6.2.4 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.3...v6.2.4) Updates `org.springframework:spring-webmvc` from 6.2.3 to 6.2.4 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.3...v6.2.4) Updates `org.springframework:spring-expression` from 6.2.3 to 6.2.4 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.3...v6.2.4) Updates `org.springframework:spring-test` from 6.2.3 to 6.2.4 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.3...v6.2.4) Updates `org.springframework.boot:spring-boot-starter-test` from 3.4.2 to 3.4.3 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.4.2...v3.4.3) Updates `org.springframework.boot:spring-boot-starter-tomcat` from 3.4.2 to 3.4.3 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.4.2...v3.4.3) Updates `org.springframework.boot:spring-boot-maven-plugin` from 3.4.2 to 3.4.3 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.4.2...v3.4.3) Updates `org.springframework.boot:spring-boot-starter-cache` from 3.4.2 to 3.4.3 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.4.2...v3.4.3) Updates `org.springframework.boot:spring-boot-starter` from 3.4.2 to 3.4.3 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.4.2...v3.4.3) Updates `org.springframework.boot:spring-boot-starter-web` from 3.4.2 to 3.4.3 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.4.2...v3.4.3) Updates `org.springframework.boot:spring-boot-starter-data-rest` from 3.4.2 to 3.4.3 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.4.2...v3.4.3) Updates `org.springframework.boot:spring-boot-starter-security` from 3.4.2 to 3.4.3 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.4.2...v3.4.3) Updates `org.springframework.boot:spring-boot-starter-aop` from 3.4.2 to 3.4.3 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.4.2...v3.4.3) Updates `org.springframework.boot:spring-boot-starter-actuator` from 3.4.2 to 3.4.3 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.4.2...v3.4.3) Updates `org.springframework.boot:spring-boot-starter-log4j2` from 3.4.2 to 3.4.3 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.4.2...v3.4.3) Updates `org.springframework.boot:spring-boot-starter-tomcat` from 3.4.2 to 3.4.3 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.4.2...v3.4.3) Updates `org.springframework.security:spring-security-test` from 6.4.2 to 6.4.4 - [Release notes](https://github.com/spring-projects/spring-security/releases) - [Changelog](https://github.com/spring-projects/spring-security/blob/main/RELEASE.adoc) - [Commits](https://github.com/spring-projects/spring-security/compare/6.4.2...6.4.4) Updates `org.springframework.boot:spring-boot-maven-plugin` from 3.4.2 to 3.4.3 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.4.2...v3.4.3) Updates `org.springframework.boot:spring-boot-starter-cache` from 3.4.2 to 3.4.3 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.4.2...v3.4.3) Updates `org.springframework.boot:spring-boot-starter` from 3.4.2 to 3.4.3 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.4.2...v3.4.3) Updates `org.springframework.boot:spring-boot-starter-web` from 3.4.2 to 3.4.3 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.4.2...v3.4.3) Updates `org.springframework.boot:spring-boot-starter-data-rest` from 3.4.2 to 3.4.3 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.4.2...v3.4.3) Updates `org.springframework.boot:spring-boot-starter-security` from 3.4.2 to 3.4.3 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.4.2...v3.4.3) Updates `org.springframework.boot:spring-boot-starter-aop` from 3.4.2 to 3.4.3 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.4.2...v3.4.3) Updates `org.springframework.boot:spring-boot-starter-actuator` from 3.4.2 to 3.4.3 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.4.2...v3.4.3) Updates `org.springframework.boot:spring-boot-starter-log4j2` from 3.4.2 to 3.4.3 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.4.2...v3.4.3) --- updated-dependencies: - dependency-name: org.springframework:spring-orm dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-core dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-beans dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-aop dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-context dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-context-support dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-tx dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-jdbc dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-web dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-webmvc dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-expression dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-test dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-core dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-beans dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-aop dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-context dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-context-support dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-tx dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-jdbc dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-web dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-webmvc dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-expression dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-test dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter-test dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter-tomcat dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-maven-plugin dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter-cache dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter-web dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter-data-rest dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter-security dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter-aop dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter-actuator dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter-log4j2 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter-tomcat dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.security:spring-security-test dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-maven-plugin dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter-cache dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter-web dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter-data-rest dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter-security dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter-aop dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter-actuator dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter-log4j2 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring ... Signed-off-by: dependabot[bot] --- pom.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pom.xml b/pom.xml index 155fac66ddb2..4907e7a12ee6 100644 --- a/pom.xml +++ b/pom.xml @@ -19,9 +19,9 @@ 17 - 6.2.3 - 3.4.2 - 6.4.2 + 6.2.4 + 3.4.3 + 6.4.4 6.4.8.Final 8.0.1.Final 42.7.5 From fbec7f2e56076de7575ea7404f9c36c47ef82539 Mon Sep 17 00:00:00 2001 From: Sascha Szott Date: Fri, 21 Mar 2025 14:20:07 +0100 Subject: [PATCH 487/979] add missing whitespace in exception message (cherry picked from commit 9a904ab4c93775257f1e17d3bd3991e6c7257754) --- .../java/org/dspace/discovery/utils/DiscoverQueryBuilder.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dspace-api/src/main/java/org/dspace/discovery/utils/DiscoverQueryBuilder.java b/dspace-api/src/main/java/org/dspace/discovery/utils/DiscoverQueryBuilder.java index 92a973dff883..b816e222539a 100644 --- a/dspace-api/src/main/java/org/dspace/discovery/utils/DiscoverQueryBuilder.java +++ b/dspace-api/src/main/java/org/dspace/discovery/utils/DiscoverQueryBuilder.java @@ -302,7 +302,7 @@ private void configureSorting(String sortProperty, String sortDirection, Discove if (StringUtils.isNotBlank(sortBy) && !isConfigured(sortBy, searchSortConfiguration)) { throw new SearchServiceException( - "The field: " + sortBy + "is not configured for the configuration!"); + "The field: " + sortBy + " is not configured for the configuration!"); } From 00da667ba51362f134a4938514e3949c9bd60277 Mon Sep 17 00:00:00 2001 From: Sascha Szott Date: Fri, 21 Mar 2025 14:34:04 +0100 Subject: [PATCH 488/979] add missing whitespace (cherry picked from commit 4ea49580937e60978035daf76b22588e3b979215) --- .../src/main/java/org/dspace/app/util/DCInputsReader.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dspace-api/src/main/java/org/dspace/app/util/DCInputsReader.java b/dspace-api/src/main/java/org/dspace/app/util/DCInputsReader.java index 8dc8239ca507..c77c3bf10e08 100644 --- a/dspace-api/src/main/java/org/dspace/app/util/DCInputsReader.java +++ b/dspace-api/src/main/java/org/dspace/app/util/DCInputsReader.java @@ -379,7 +379,7 @@ private void processRow(String formName, int rowIdx, Node n, List Date: Fri, 21 Mar 2025 14:20:07 +0100 Subject: [PATCH 489/979] add missing whitespace in exception message (cherry picked from commit 9a904ab4c93775257f1e17d3bd3991e6c7257754) --- .../java/org/dspace/discovery/utils/DiscoverQueryBuilder.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dspace-api/src/main/java/org/dspace/discovery/utils/DiscoverQueryBuilder.java b/dspace-api/src/main/java/org/dspace/discovery/utils/DiscoverQueryBuilder.java index 92a973dff883..b816e222539a 100644 --- a/dspace-api/src/main/java/org/dspace/discovery/utils/DiscoverQueryBuilder.java +++ b/dspace-api/src/main/java/org/dspace/discovery/utils/DiscoverQueryBuilder.java @@ -302,7 +302,7 @@ private void configureSorting(String sortProperty, String sortDirection, Discove if (StringUtils.isNotBlank(sortBy) && !isConfigured(sortBy, searchSortConfiguration)) { throw new SearchServiceException( - "The field: " + sortBy + "is not configured for the configuration!"); + "The field: " + sortBy + " is not configured for the configuration!"); } From 21df5d0f38552e3325d2d75b335ce0e6d1e7c29f Mon Sep 17 00:00:00 2001 From: Sascha Szott Date: Fri, 21 Mar 2025 14:34:04 +0100 Subject: [PATCH 490/979] add missing whitespace (cherry picked from commit 4ea49580937e60978035daf76b22588e3b979215) --- .../src/main/java/org/dspace/app/util/DCInputsReader.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dspace-api/src/main/java/org/dspace/app/util/DCInputsReader.java b/dspace-api/src/main/java/org/dspace/app/util/DCInputsReader.java index 8dc8239ca507..c77c3bf10e08 100644 --- a/dspace-api/src/main/java/org/dspace/app/util/DCInputsReader.java +++ b/dspace-api/src/main/java/org/dspace/app/util/DCInputsReader.java @@ -379,7 +379,7 @@ private void processRow(String formName, int rowIdx, Node n, List Date: Fri, 21 Mar 2025 19:01:09 +0000 Subject: [PATCH 491/979] Add bitstream null check to XOAI (cherry picked from commit 54602f47b1d35a7cb75b28acde08d43a93461e8a) --- dspace-oai/src/main/java/org/dspace/xoai/app/XOAI.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dspace-oai/src/main/java/org/dspace/xoai/app/XOAI.java b/dspace-oai/src/main/java/org/dspace/xoai/app/XOAI.java index 2d2679577802..16c16b0115b6 100644 --- a/dspace-oai/src/main/java/org/dspace/xoai/app/XOAI.java +++ b/dspace-oai/src/main/java/org/dspace/xoai/app/XOAI.java @@ -110,7 +110,7 @@ private List getFileFormats(Item item) { try { for (Bundle b : itemService.getBundles(item, "ORIGINAL")) { for (Bitstream bs : b.getBitstreams()) { - if (!formats.contains(bs.getFormat(context).getMIMEType())) { + if (bs != null && !formats.contains(bs.getFormat(context).getMIMEType())) { formats.add(bs.getFormat(context).getMIMEType()); } } From 3b0cfd058a7662c4cfd87c1e392416c130d7f340 Mon Sep 17 00:00:00 2001 From: jameswsullivan <81947235+jameswsullivan@users.noreply.github.com> Date: Fri, 21 Mar 2025 19:01:09 +0000 Subject: [PATCH 492/979] Add bitstream null check to XOAI (cherry picked from commit 54602f47b1d35a7cb75b28acde08d43a93461e8a) --- dspace-oai/src/main/java/org/dspace/xoai/app/XOAI.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dspace-oai/src/main/java/org/dspace/xoai/app/XOAI.java b/dspace-oai/src/main/java/org/dspace/xoai/app/XOAI.java index 5c29b259deab..c8babc036e3c 100644 --- a/dspace-oai/src/main/java/org/dspace/xoai/app/XOAI.java +++ b/dspace-oai/src/main/java/org/dspace/xoai/app/XOAI.java @@ -110,7 +110,7 @@ private List getFileFormats(Item item) { try { for (Bundle b : itemService.getBundles(item, "ORIGINAL")) { for (Bitstream bs : b.getBitstreams()) { - if (!formats.contains(bs.getFormat(context).getMIMEType())) { + if (bs != null && !formats.contains(bs.getFormat(context).getMIMEType())) { formats.add(bs.getFormat(context).getMIMEType()); } } From f0abeea477b0740bae704c535757f53bf22626da Mon Sep 17 00:00:00 2001 From: DSpace Bot <68393067+dspace-bot@users.noreply.github.com> Date: Mon, 24 Mar 2025 14:03:50 -0500 Subject: [PATCH 493/979] [Port dspace-8_x] Add null check in SolrServiceFileInfoPlugin for index-discovery (#10518) * Add null check in SolrServiceFileInfoPlugin for index-discovery (cherry picked from commit d07f1e0caad7632231a7a98cf2f2ff119d6a3b89) * Fix starting curly brace. (cherry picked from commit e11994c0ee75baf7389446e67ca8252f5f16a8d2) * Update SolrServiceFileInfoPlugin.java (cherry picked from commit 18372ae07246c5b0829286f7bb20263017528518) --------- Co-authored-by: jameswsullivan <81947235+jameswsullivan@users.noreply.github.com> --- .../discovery/SolrServiceFileInfoPlugin.java | 30 ++++++++++--------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/discovery/SolrServiceFileInfoPlugin.java b/dspace-api/src/main/java/org/dspace/discovery/SolrServiceFileInfoPlugin.java index 7aece5acf313..6142dd0dba4b 100644 --- a/dspace-api/src/main/java/org/dspace/discovery/SolrServiceFileInfoPlugin.java +++ b/dspace-api/src/main/java/org/dspace/discovery/SolrServiceFileInfoPlugin.java @@ -52,21 +52,23 @@ public void additionalIndex(Context context, IndexableObject indexableObject, So List bitstreams = bundle.getBitstreams(); if (bitstreams != null) { for (Bitstream bitstream : bitstreams) { - document.addField(SOLR_FIELD_NAME_FOR_FILENAMES, bitstream.getName()); - // Add _keyword and _filter fields which are necessary to support filtering and faceting - // for the file names - document.addField(SOLR_FIELD_NAME_FOR_FILENAMES + "_keyword", bitstream.getName()); - document.addField(SOLR_FIELD_NAME_FOR_FILENAMES + "_filter", bitstream.getName()); + if (bitstream != null) { + document.addField(SOLR_FIELD_NAME_FOR_FILENAMES, bitstream.getName()); + // Add _keyword and _filter fields which are necessary to + // support filtering and faceting for the file names + document.addField(SOLR_FIELD_NAME_FOR_FILENAMES + "_keyword", bitstream.getName()); + document.addField(SOLR_FIELD_NAME_FOR_FILENAMES + "_filter", bitstream.getName()); - String description = bitstream.getDescription(); - if ((description != null) && !description.isEmpty()) { - document.addField(SOLR_FIELD_NAME_FOR_DESCRIPTIONS, description); - // Add _keyword and _filter fields which are necessary to support filtering and - // faceting for the descriptions - document.addField(SOLR_FIELD_NAME_FOR_DESCRIPTIONS + "_keyword", - description); - document.addField(SOLR_FIELD_NAME_FOR_DESCRIPTIONS + "_filter", - description); + String description = bitstream.getDescription(); + if ((description != null) && !description.isEmpty()) { + document.addField(SOLR_FIELD_NAME_FOR_DESCRIPTIONS, description); + // Add _keyword and _filter fields which are necessary to support filtering and + // faceting for the descriptions + document.addField(SOLR_FIELD_NAME_FOR_DESCRIPTIONS + "_keyword", + description); + document.addField(SOLR_FIELD_NAME_FOR_DESCRIPTIONS + "_filter", + description); + } } } } From c797441b868d76f415e8df35191471ecc966720e Mon Sep 17 00:00:00 2001 From: DSpace Bot <68393067+dspace-bot@users.noreply.github.com> Date: Mon, 24 Mar 2025 14:04:03 -0500 Subject: [PATCH 494/979] [Port dspace-7_x] Add null check in SolrServiceFileInfoPlugin for index-discovery (#10517) * Add null check in SolrServiceFileInfoPlugin for index-discovery (cherry picked from commit d07f1e0caad7632231a7a98cf2f2ff119d6a3b89) * Fix starting curly brace. (cherry picked from commit e11994c0ee75baf7389446e67ca8252f5f16a8d2) * Update SolrServiceFileInfoPlugin.java (cherry picked from commit 18372ae07246c5b0829286f7bb20263017528518) --------- Co-authored-by: jameswsullivan <81947235+jameswsullivan@users.noreply.github.com> --- .../discovery/SolrServiceFileInfoPlugin.java | 30 ++++++++++--------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/discovery/SolrServiceFileInfoPlugin.java b/dspace-api/src/main/java/org/dspace/discovery/SolrServiceFileInfoPlugin.java index 7aece5acf313..6142dd0dba4b 100644 --- a/dspace-api/src/main/java/org/dspace/discovery/SolrServiceFileInfoPlugin.java +++ b/dspace-api/src/main/java/org/dspace/discovery/SolrServiceFileInfoPlugin.java @@ -52,21 +52,23 @@ public void additionalIndex(Context context, IndexableObject indexableObject, So List bitstreams = bundle.getBitstreams(); if (bitstreams != null) { for (Bitstream bitstream : bitstreams) { - document.addField(SOLR_FIELD_NAME_FOR_FILENAMES, bitstream.getName()); - // Add _keyword and _filter fields which are necessary to support filtering and faceting - // for the file names - document.addField(SOLR_FIELD_NAME_FOR_FILENAMES + "_keyword", bitstream.getName()); - document.addField(SOLR_FIELD_NAME_FOR_FILENAMES + "_filter", bitstream.getName()); + if (bitstream != null) { + document.addField(SOLR_FIELD_NAME_FOR_FILENAMES, bitstream.getName()); + // Add _keyword and _filter fields which are necessary to + // support filtering and faceting for the file names + document.addField(SOLR_FIELD_NAME_FOR_FILENAMES + "_keyword", bitstream.getName()); + document.addField(SOLR_FIELD_NAME_FOR_FILENAMES + "_filter", bitstream.getName()); - String description = bitstream.getDescription(); - if ((description != null) && !description.isEmpty()) { - document.addField(SOLR_FIELD_NAME_FOR_DESCRIPTIONS, description); - // Add _keyword and _filter fields which are necessary to support filtering and - // faceting for the descriptions - document.addField(SOLR_FIELD_NAME_FOR_DESCRIPTIONS + "_keyword", - description); - document.addField(SOLR_FIELD_NAME_FOR_DESCRIPTIONS + "_filter", - description); + String description = bitstream.getDescription(); + if ((description != null) && !description.isEmpty()) { + document.addField(SOLR_FIELD_NAME_FOR_DESCRIPTIONS, description); + // Add _keyword and _filter fields which are necessary to support filtering and + // faceting for the descriptions + document.addField(SOLR_FIELD_NAME_FOR_DESCRIPTIONS + "_keyword", + description); + document.addField(SOLR_FIELD_NAME_FOR_DESCRIPTIONS + "_filter", + description); + } } } } From d137f8bb60af9bee04fa606783b8a47da085fc8f Mon Sep 17 00:00:00 2001 From: Vincenzo Mecca Date: Wed, 26 Mar 2025 18:06:08 +0100 Subject: [PATCH 495/979] [DURACOM-243] Adds rotation handling inside JPEGFilter Conflicts: (cherry picked from commit 08e330c1c0a62ecfb83c2e7617472bd5a50f8c5a) --- .../mediafilter/BrandedPreviewJPEGFilter.java | 23 +- .../dspace/app/mediafilter/JPEGFilter.java | 274 ++++++++++++++---- .../app/mediafilter/PDFBoxThumbnail.java | 1 + 3 files changed, 231 insertions(+), 67 deletions(-) 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 7b082c6c21a4..483e4f5f6ea2 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/JPEGFilter.java b/dspace-api/src/main/java/org/dspace/app/mediafilter/JPEGFilter.java index 502f71eb5ca8..181e3bcc4b58 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,22 +8,36 @@ 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; +import org.w3c.dom.Node; /** * Filter image bitstreams, scaling the image to be within the bounds of @@ -33,6 +47,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 +78,134 @@ 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; + } + } + + /** + * Helper method to find a node with given name in metadata tree + */ + private static Node findNode(Node node, String name) { + if (node.getNodeName().equalsIgnoreCase(name)) { + return node; + } + + Node child = node.getFirstChild(); + while (child != null) { + Node found = findNode(child, name); + if (found != null) { + return found; + } + child = child.getNextSibling(); + } + return null; + } + + /** + * 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 +216,59 @@ public String getDescription() { @Override public InputStream getDestinationStream(Item currentItem, InputStream source, boolean verbose) throws Exception { + return getThumb(currentItem, source, 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 = getImageRotationUsingImageReader(new FileInputStream(tempFile)); // read in bitstream's image - BufferedImage buf = ImageIO.read(source); + BufferedImage buf = ImageIO.read(new FileInputStream(tempFile)); - return getThumb(currentItem, buf, verbose); + return getThumbDim( + currentItem, buf, verbose, xmax, ymax, blurring, hqscaling, brandHeight, brandFontPoint, rotation, + brandFont + ); } public InputStream getThumb(Item currentItem, BufferedImage buf, boolean verbose) @@ -83,25 +276,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,77 +305,53 @@ 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 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 3acb6900dbda..577f1dec4a18 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 @@ -81,6 +81,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); } } From 2f477e370629935c09bed100f8dc791d8e052f2a Mon Sep 17 00:00:00 2001 From: Vincenzo Mecca Date: Thu, 27 Mar 2025 12:38:59 +0100 Subject: [PATCH 496/979] [DURACOM-243] Adds Test for JPEGFilter (cherry picked from commit 82d04061c084486b92c9f643b96f59ab81fd4a87) --- .../dspace/app/mediafilter/JPEGFilter.java | 53 ++-- .../app/mediafilter/JPEGFilterTest.java | 270 ++++++++++++++++++ .../dspace/app/mediafilter/cat-rotated-90.jpg | Bin 0 -> 36813 bytes .../org/dspace/app/mediafilter/cat.jpg | Bin 0 -> 36813 bytes 4 files changed, 290 insertions(+), 33 deletions(-) create mode 100644 dspace-api/src/test/java/org/dspace/app/mediafilter/JPEGFilterTest.java create mode 100644 dspace-api/src/test/resources/org/dspace/app/mediafilter/cat-rotated-90.jpg create mode 100644 dspace-api/src/test/resources/org/dspace/app/mediafilter/cat.jpg 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 181e3bcc4b58..2ccc2afbb2d2 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 @@ -37,7 +37,6 @@ import org.dspace.content.Item; import org.dspace.services.ConfigurationService; import org.dspace.services.factory.DSpaceServicesFactory; -import org.w3c.dom.Node; /** * Filter image bitstreams, scaling the image to be within the bounds of @@ -117,25 +116,6 @@ public static int convertRotationToDegrees(int valueNode) { } } - /** - * Helper method to find a node with given name in metadata tree - */ - private static Node findNode(Node node, String name) { - if (node.getNodeName().equalsIgnoreCase(name)) { - return node; - } - - Node child = node.getFirstChild(); - while (child != null) { - Node found = findNode(child, name); - if (found != null) { - return found; - } - child = child.getNextSibling(); - } - return null; - } - /** * Rotates an image by the specified angle * @@ -261,14 +241,20 @@ protected InputStream getThumb( } } - int rotation = getImageRotationUsingImageReader(new FileInputStream(tempFile)); - // read in bitstream's image - BufferedImage buf = ImageIO.read(new FileInputStream(tempFile)); + int rotation = 0; + try (FileInputStream fis = new FileInputStream(tempFile)) { + rotation = getImageRotationUsingImageReader(fis); + } - return getThumbDim( - currentItem, buf, verbose, xmax, ymax, blurring, hqscaling, brandHeight, brandFontPoint, rotation, - brandFont - ); + 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) @@ -354,13 +340,14 @@ public InputStream getThumbDim(Item currentItem, BufferedImage buf, boolean verb 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/test/java/org/dspace/app/mediafilter/JPEGFilterTest.java b/dspace-api/src/test/java/org/dspace/app/mediafilter/JPEGFilterTest.java new file mode 100644 index 000000000000..1181dc7a60f0 --- /dev/null +++ b/dspace-api/src/test/java/org/dspace/app/mediafilter/JPEGFilterTest.java @@ -0,0 +1,270 @@ +/** + * The contents of this file are subject to the license and copyright + * detailed in the LICENSE and NOTICE files at the root of the source + * tree and available online at + * + * http://www.dspace.org/license/ + */ +package org.dspace.app.mediafilter; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertSame; + +import java.awt.RenderingHints; +import java.awt.image.BufferedImage; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; + +import org.dspace.AbstractUnitTest; +import org.dspace.content.Item; +import org.dspace.services.ConfigurationService; +import org.dspace.services.factory.DSpaceServicesFactory; +import org.junit.Test; +import org.mockito.Mock; + +public class JPEGFilterTest extends AbstractUnitTest { + + @Mock + private ConfigurationService mockConfigurationService; + + @Mock + private DSpaceServicesFactory mockDSpaceServicesFactory; + + @Mock + private InputStream mockInputStream; + + @Mock + private Item mockItem; + + /** + * Tests that the convertRotationToDegrees method returns 0 for an input value + * that doesn't match any of the defined rotation cases. + */ + @Test + public void testConvertRotationToDegrees_UnknownValue_ReturnsZero() { + int result = JPEGFilter.convertRotationToDegrees(5); + assertEquals(0, result); + } + + /** + * Test getNormalizedInstance method with a null input. + * This tests the edge case of passing a null BufferedImage to the method. + * The method should throw a NullPointerException when given a null input. + */ + @Test(expected = NullPointerException.class) + public void testGetNormalizedInstanceWithNullInput() { + JPEGFilter filter = new JPEGFilter(); + filter.getNormalizedInstance(null); + } + + /** + * Test getThumbDim method with a null BufferedImage input. + * This tests the edge case where the input image is null, which should result in an exception. + */ + @Test(expected = NullPointerException.class) + public void testGetThumbDimWithNullBufferedImage() throws Exception { + JPEGFilter filter = new JPEGFilter(); + Item currentItem = null; + BufferedImage buf = null; + boolean verbose = false; + int xmax = 100; + int ymax = 100; + boolean blurring = false; + boolean hqscaling = false; + int brandHeight = 0; + int brandFontPoint = 0; + int rotation = 0; + String brandFont = null; + + filter.getThumbDim( + currentItem, buf, verbose, xmax, ymax, blurring, hqscaling, + brandHeight, brandFontPoint, rotation, brandFont + ); + } + + /** + * Tests that the rotateImage method returns the original image when the rotation angle is 0. + * This is an edge case explicitly handled in the method implementation. + */ + @Test + public void testRotateImageWithZeroAngle() { + BufferedImage originalImage = new BufferedImage(100, 100, BufferedImage.TYPE_INT_RGB); + BufferedImage rotatedImage = JPEGFilter.rotateImage(originalImage, 0); + assertSame( + "When rotation angle is 0, the original image should be returned", + originalImage, rotatedImage + ); + } + + /** + * Test case for convertRotationToDegrees method when input is 6. + * Expected to return 90 degrees for the rotation value of 6. + */ + @Test + public void test_convertRotationToDegrees_whenInputIs6_returns90() { + int input = 6; + int expected = 90; + int result = JPEGFilter.convertRotationToDegrees(input); + assertEquals(expected, result); + } + + /** + * Tests that getBlurredInstance method applies a blur effect to the input image. + * It verifies that the returned image is not null, has the same dimensions as the input, + * and is different from the original image (indicating that blurring has occurred). + */ + @Test + public void test_getBlurredInstance_appliesBlurEffect() { + JPEGFilter filter = new JPEGFilter(); + BufferedImage original = new BufferedImage(100, 100, BufferedImage.TYPE_INT_RGB); + + BufferedImage blurred = filter.getBlurredInstance(original); + + assertNotNull("Blurred image should not be null", blurred); + assertEquals("Width should be the same", original.getWidth(), blurred.getWidth()); + assertEquals("Height should be the same", original.getHeight(), blurred.getHeight()); + assertNotEquals("Blurred image should be different from original", original, blurred); + } + + /** + * Test case for getBundleName method of JPEGFilter class. + * This test verifies that the getBundleName method returns the expected string "THUMBNAIL". + */ + @Test + public void test_getBundleName_returnsExpectedString() { + JPEGFilter filter = new JPEGFilter(); + String result = filter.getBundleName(); + assertEquals("THUMBNAIL", result); + } + + /** + * Tests that the getDescription method returns the expected string "Generated Thumbnail". + * This verifies that the method correctly provides the description for the JPEG filter. + */ + @Test + public void test_getDescription_1() { + JPEGFilter filter = new JPEGFilter(); + String description = filter.getDescription(); + assertEquals("Generated Thumbnail", description); + } + + /** + * Tests that getFilteredName method appends ".jpg" to the input filename. + */ + @Test + public void test_getFilteredName_appendsJpgExtension() { + JPEGFilter filter = new JPEGFilter(); + String oldFilename = "testimage"; + String expectedResult = "testimage.jpg"; + String actualResult = filter.getFilteredName(oldFilename); + assertEquals(expectedResult, actualResult); + } + + /** + * Test case for getFormatString method of JPEGFilter class. + * Verifies that the method returns the expected string "JPEG". + */ + @Test + public void test_getFormatString_returnsJPEG() { + JPEGFilter filter = new JPEGFilter(); + String result = filter.getFormatString(); + assertEquals("JPEG", result); + } + + /** + * Tests the behavior of getImageRotationUsingImageReader when an ImageProcessingException occurs. + * This test verifies that the method handles an ImageProcessingException by logging the error + * and returning 0 degrees rotation. + */ + @Test + public void test_getImageRotationUsingImageReader_imageProcessingException() { + InputStream errorStream = new InputStream() { + @Override + public int read() throws IOException { + throw new IOException("Simulated image processing error"); + } + }; + int result = JPEGFilter.getImageRotationUsingImageReader(errorStream); + assertEquals(0, result); + } + + /** + * Testcase for getImageRotationUsingImageReader when the image doesn't contain orientation metadata. + * This test verifies that the method returns 0 when there's no ExifIFD0Directory + * or when it doesn't contain the TAG_ORIENTATION. + */ + @Test + public void test_getImageRotationUsingImageReader_noOrientationMetadata() throws IOException { + URL resource = this.getClass().getResource("cat.jpg"); + int rotationAngle = -1; + try (InputStream inputStream = new FileInputStream(resource.getFile())) { + // Call the method under test + rotationAngle = JPEGFilter.getImageRotationUsingImageReader(inputStream); + } + assertEquals(0, rotationAngle); + } + + /** + * Tests the getImageRotationUsingImageReader method when the image contains + * valid EXIF orientation metadata. + * + * This test verifies that the method correctly reads the orientation tag + * from the EXIF metadata and returns the appropriate rotation angle in degrees. + */ + @Test + public void test_getImageRotationUsingImageReader_withValidExifOrientation() throws Exception { + // Create a mock InputStream with EXIF metadata containing orientation information + URL resource = this.getClass().getResource("cat-rotated-90.jpg"); + int rotationAngle = -1; + try (InputStream inputStream = new FileInputStream(resource.getFile())) { + // Call the method under test + rotationAngle = JPEGFilter.getImageRotationUsingImageReader(inputStream); + } + + // Assert the expected rotation angle + // Note: The expected value should be adjusted based on the mock data + assertEquals(90, rotationAngle); + } + + /** + * Tests the getScaledInstance method of JPEGFilter class with higher quality scaling. + * This test verifies that the method correctly scales down an image in multiple passes + * when higherQuality is true and the image dimensions are larger than the target dimensions. + */ + @Test + public void test_getScaledInstance() { + JPEGFilter filter = new JPEGFilter(); + BufferedImage originalImage = new BufferedImage(400, 300, BufferedImage.TYPE_INT_RGB); + int targetWidth = 100; + int targetHeight = 75; + Object hint = RenderingHints.VALUE_INTERPOLATION_BILINEAR; + boolean higherQuality = true; + + BufferedImage result = filter.getScaledInstance(originalImage, targetWidth, targetHeight, hint, higherQuality); + + assertNotNull(result); + assertEquals(targetWidth, result.getWidth()); + assertEquals(targetHeight, result.getHeight()); + } + + /** + * Tests the rotateImage method with a non-zero angle. + * This test verifies that the image is rotated correctly when given a non-zero angle. + */ + @Test + public void test_rotateImage_nonZeroAngle() { + BufferedImage originalImage = new BufferedImage(100, 50, BufferedImage.TYPE_INT_RGB); + int angle = 90; + + BufferedImage rotatedImage = JPEGFilter.rotateImage(originalImage, angle); + + assertNotNull(rotatedImage); + assertEquals(50, rotatedImage.getWidth()); + assertEquals(100, rotatedImage.getHeight()); + } + +} diff --git a/dspace-api/src/test/resources/org/dspace/app/mediafilter/cat-rotated-90.jpg b/dspace-api/src/test/resources/org/dspace/app/mediafilter/cat-rotated-90.jpg new file mode 100644 index 0000000000000000000000000000000000000000..5c0f91c4eda737b23e82a092905cacbf9dc56f29 GIT binary patch literal 36813 zcmeFZXIKAj+Ii}DVLV}iR=yZK2Iq_M6V$U&5x78g%*e>h!n})xWfvz43kxUd#=^Ok zh3$VLfcOkxqXQ5?IweF1pkRYgvO$QwU?Ulbbr3DEmedgP1y-1JQ34PODry>9I(i01 z$lr+&fP!*|xC?+#K`1GxD5z;^=%^`~*N!f7RB@;6(;rX?K&|SJ?MQ7hX0K2ImdMu4P7) zUaxBzTF5GU)jGUr?;UV4DZBi2+sKl#k%LcQpIb`G(Z{$!j)VsE2w zPg|v^<>8jo_iXtZ1DP2M0@489>WZA{i!yVETWfMYgz=WpHigE2&5C;!=fZ!c+P~MJ zsEvc~aJUfb?I=uX!rfTqUox2$&e^9cw`gOG73yVHBJ!Sg6 z-_NWEQp)YufBNFX)f~@~keRmknpZtu?e7eJMjLkf9?dhO;uQzA&bapQc4;DTR{IU} zQGK&m2&{X(x5_>Jp^C~Gh3O}oF^io~^^V+hx_O>{dCfNzwdg!LKdwfw4MB(h&>G&1 z#iRTZA+7uOOX`#zr4fzF7KFe>^fzsxFdx05b9_B*Y_;)RuA_=^eV;NmYkOh~kU+Bs zU>V*JIsUlo1j}bNORdQ#4uxw55%uM1QJX_X&Jt9mOKqCQ9dzNu&!ZtXD%jNzQ_eU^ z^0yYXmZryxMkK66t9*Ma*{gZy^*dNdeQOf;!U+#H!L;h@!zz?L{pS`xo|#p3l@EfP zqfJZ{%~*scO*K8ki-+Ff)nU(cZa>NK68b^4(elV*MP~br3pnb~Uekafdv5`4wV3kw z*_&yf?AGI*us8c1Lq_$~WaU^a4%1c$h%FQdRJK*&Q28sGDXBNsc^5{frv$DCW7_yr zdzh+bBaRgC6!6;B&}dCfq*25V!EaKlzr0 z)a-Ly=kdO2AoTVptsFIkYu%wm(@Xv>DO%^Q(tKZ)o!7d%X!y1J_}vSK(~ci6(Ud9d z`7Ay-_Tp2?>&JDz*$QpPr?`@)csaECZt@*s@k18(g)9?+w+DI8$Di4Ipr(0M!_-Ji zdVLtsbt5*8R=$mXvuUI4bZkoCKvByiVH9;L@-1yC>qpzuudaW}#tRv(ZXDQaoNYoG zs$3ZgoBAPbJ&t{^s@>eN`xbsl#rd-itq!g3Q#Be5-mYF_-{zsLxcAuJme;sgEWwKU z4p*0_8mu7rJ4YSgBxoLIU&GV@XPkf4UxWCvb zs%ZEQ0bgeG>z!jpbTw~=G`$-qG#SJ~@0_5Phz%$$Wh^{J1Tq$CkMQyB-cuejkoAcW z@$#!SI;|7kayqNUf3CP-F{L65ulN>x3yTcj;H*O;nMzHjWX&t_!f0q9lSMA zkpEdl0n0~o|N$Yc%~bjMr<1DM|656 zdVLo&0(E&UyQhlBU1du3jK$S@eA}vn{oYaEwMU!GcK%d=VU1gKPTldi$SLGS z1!O>z*p|pn zZ{Ky)@`=$SLpQ2=zM2G#H-79HW|$!;m0p#UVYIksSbeZ#Bs5U+dA+FbcQ9hFv+; z@ut|UGE|ypL2d)hRoX*4lQ4}=ZQ1ZlpK!V|AeniJ=}p(!06kUo9)#P(8nqqA!ksbc zTJd+`1y!E!$2T0kdY8=RCp@lDp4jA_Td@2NPP$fSjs3pWyi3{bIw660dApsxZ8iMy z>nKd4v*MBE+7|DLJ*DTqe^tJ9WyW;u8)jxh&B3UpGA!wsLZ$1=0~d@n`Rr-$LS21& zf>l4JDP&11FkB{7xhAJMNz}sAGTd)!wa4bQvLN2Jrxg_3b*BjRf z1^#^Edm|}x7Ee6glkMEx#o6;vGr#+&#|WLeVojbJUDNXwIMl#sdZE>B==e-h{>^e{~Pv*S)K>Uy6D< zXi45hTdZ%?Vx+kChuHVSmKW?3d!M}_SV{zzKcOyX=lwiG@wFl(Vxc{nvS*~%U8w3M zeaDYYLYfgSToI>uW_dMU?ah~~W%m?2T{{*gpy8%rMBvGK`>KKWowgz6rbi1G!q{gr zq04$Z(r(`8V((^bYfn0<;UD;Uy^Gp3S#K2`X=&GUd0gdMKdYyIvj2hA>(x*_ zogucIrRzQFr)=DV-K9oC;V<6oe)9VG{+y~H=U$cgOtWkH$on@kUQz&iuU9swL$D46 z`!B^2l|56A9a1t!0$y(%Ku}KFh2C;Ct9d7KQ6coDVdUxFGWiwPoK-c>xw{!`kMbK; zL+Ov5N}AZuNSWt8qN0@Z+>g2{!=-rv+TGo`nQ)>cQAa-KHntnS%S#9qjEv(@i0!)9 z_5(hvYrsteh%NKwH7B%}0d+QSCz0o0mIuf3&Ybz8)8>^HU8WaktKG&@yK-Yx8GGj9 zo5Rz5d|kn^T3YLg0o4gbEb8FQYr0}=?})#YsMv0!)oP7!qtQ}TsxM9Wq$Sd2{^%m| zKppdIS71naKCr5*zlR89pM`6F@phxGGknzTcJI@@LMKNBm9wRe15zo*-&)P5dUU)W z@i{AsxvkBN|POJjHB1I~ALVWpq&DApzTuq1f2?AUHxV55EWSb6T&>nX&ihA3a;OSK!zgtyaHC zfvl;_wT-y-HKh3%&9Ll@R`vrFG%97^LQlg+?dXPrw`#H!=b1(?bWi7bU~GJ2W}R6| z($2hVx~!9FQMPAa{`q$h8KBpugm-z>KJ;nro8ue8d1_PP(EgfaHO&`p$IrGtoa)c$ zUN6di^4`M~k^Abxlfu?Q{E_rE!*&KcV_EiGWY@5)rsl^d%(JV&*7e^*9lj)v}zo zmFv8ts3AOo0-g)&5rNa!y`yQqnC-4IKfZo!Y#|IM;PZtDWW}$#-6?w1yfC>5O<0?o zH$Tzd@Dg|4#D9OyC}eOx4R@XK;WYE&;rq$FUOcy^#^;`N-#C{2e7(PEMwSSq2(@c| z%IXtRA_6dGlncL&K=!Efa=W40QZ+5bCghgdgQX48e7$s^F+>CH6S1t z>$Ob$uV|ahm*n<7U-jQh1jOcBL!sZM(=3$ZG$S_zLeROGD(f#xH$qTXgnU*cA$Du7 zj2a82N9JhC&k<&q4>4DDR^Z3^F_pCvZR^C@r5d*+DF2D+hxADYakYo-MG#&;+6Hp`q2_`-DkXTZhm7LcG9z^2Mh2u$i zNaQV0vw5lqsbycIz5?N&)ms$OB-q=5fqZ(?N^K0e`~R& zA0CcD<3Rdu5~rRY9=+2Du#ukFp9yOpocYg$9u}?lD}l!UN|<>faX%>*KKOv22?G>T z|7QZ!<1f*=P=wF6BDS;ySR3i;fwiF|Rms{*P|yoQ@YKe9AtlL|5f=RqI9=pF^66r| zZ0*o^Lt$$qsc(N?x=1g01S%9VD&xl9;6nKD{19ph1;$SpQLI7W-#w9F@G!87W8lavtn(a$Su4T7OL$;l0d$8L9rM=2VWT2xuAu@(LOus zV<8c&!8++;@OTUgi9!2pXJOi*kgD(-o_Pmv4fpZ=oxr$50PFUXpEOn|r?%>_?em1} zJjvroluTpUE);xdodLG&jhT!id3^cFoO}M7rkp3I>B)ES1|jDHy}_i5VSA8(XmSk} z$yj4R+S?nXAi+E6iv};qHs^i z|Fj&41em13A>mLM&K_w-vVF*3%AqFHKr}n}M@vqa=>Jw0tq&GM@cRpj27`tBz<*9_ z1~w$=mSh|#gV}?C#~8uTFsuh2<^>`E+hD()S(#yrnS>!xOi(_8;N$~^z<`G*=&%$7!ympNj0gX4WC(Q70v!K=LlDpdLnTTifn?7RYdQ!5G~6hRAIaHI z@{p1WB3-oL-xdtscf7mXI0z|CV*2eO^X}lOc7T8NBxNK?`1AKi2Y+nf|4keCee$>l zCVoY1SZqD8J}^8O?j?orNU;)f^vfKN*ZWJ%gu%iC4-bGHBY2SF8|2U@DWtg_H6hPX zq_F4jX;NtP_cR#HMeT$SfMZB}@HUY&oiTVK30NHz7!$&iqNyNe=VFDSJYilYdU{|C z*&B|8?L-+FNC`s>76pb}w!+^GhCeZ+%tx&ZjKBvI8e{H($HT}0f}PA@1RUdoh2d~u zlXh@SAz;+N3v3_~jt~Az+v%N26ce;J29!q%PSHbsJdKPj@v@r#$N0bL{k_FJcL{*7#e1;bXq(b@k-w}yHBM*Vwk8$8$< z+jLT|{#LjN%EJd{<$?G8OT38F4hJJsba1~4+EE=7sj2_Af!tsjTsp`=k6@gRmzNF_ zuY<>9;hqFMxl>6sVE&2G1EZ1KRU=t1lO7U-gXw^wEf1fceL`n|hGM+Haj?w@@&W85 ztFrM30I~nJE({d_jDQ`BB598K7pu7kbf>deeqrq_OdNO6zscR+HASM3ZDVUqi#Zqxg!_Rb z8Pv?re&E^C_rKHz{dVgi+fUBz=Z0A>+7SV4i`)^tcRT$L zqCt^tJ68Qu_dAUyQOv;g5C-6q95jx!v}H$8yNobq;AFJZ1;6IGUF2Rg_wX~rfEEO1 z*)Ev0osDUSMv91T<3TjQYJ!F$30@>=D3THF6wkhmcZOlXNf8SMw11LlOwe8+D@n-q ze8y-7#=8yBq~UE~$sDVNR0&jSj>;tWuJ_+ULS zl)NL0ly8R!hKJ$c?2Gq6ZWlzF=~%aOl7hkD;Q5`zWCOlDV7RT>|F?EC+v+>Oz3{%q zu;12^kc^EM9OZwT0BFFj(6h1G(LZS>G6Ne=YSz!*+0h%fISlXN1+K5zYWu&P2S^Jd z7{S>{kAMSLD41C9$e;yr2973%J82p)?PzYbQ$EL5(gs7oLdk={&e~*$#z>yItuWvm z^7}iJWv5nup^*BB)d35~!_47mILZULT?l!OA>%=WEyf1!1%rBE|1#`oK~P#fdawR94MSZAo!#p|61BwvqB5L48d)95P&$qCgh99`>9Gxqj6Fmq&<^TPz*{s z$iq)sR!T-1P}d3q%>V(z3wpvptJ4sgsj3wcgnMZSIVhUTnEUC$eBs6+SeQ+Sg)KA$ z0X^a+q@^jS9;6zC@;55P5H-AlNoRJq#8m zs3;{b36+tR6;x7^l2uStQj(Pr1h;F-Ny~u$l_bG^n~F-Ra>|0+mk?MQ*2`PfTHkQH zEHI_9)2P6}K&e1^DGb&}TK341BP1Dea*`m2BrX`V+8{|ZPIyOxJ`4xN!pZB_1W6LX zHCY6_h7efOttO!Sev194y4qp%g8t<51J`np3-^Lb!;moYkOuWCOKLt@sNc^1)Sp@Y zBKjwp+!;HWK-HR?|MLtx{f0vQECPo&2mqV$?|K;+ZzCV!P`2!W%|2PS>e z59T8z__GgG$%7dz8LXcC@AzMzxEew@aEYOyiGiMy>=7j;NjWK5a(&IsRZY-1yayTz zGtt)&0vjs@hkL2Y8!G6@=&R`IACc2FP>_|?QPNc~R5ma)P?1&ESJKnn$*Yfn5=geQ zlh=!s_sIV&@77W$59EI(gEW#!#-?fv2j@g=@b*}u>4Hkfdz#TR`=nj^_V?J%O(TRj>bQ4%;esYYFFnIfs$# zf!x^N-tRjo@S}kQ{`^|e-1(~b^ZcWMKN|R>fj=7fqk%sf_W za2*Io1rSJkV<^o)_+kJY26~eyTR>O@NkG9tn6&qa69w}C9n$1o6C!vR)E9(hKs*!H z)>;pQ)j>xTlh1Fk=Wj3`47-550LckY-nJol5PC=ubl9i}8p8sSV1!H33UoqwV7)*W z5$Neg2Lr%Xn@PC<_J8=%O-66s{w?u0zJCXj`gV&xe~jE`5bpQCaKCc?!eKz)n+mwk zjPlAaoaZe7sE7ano~~awk#x}KcL4y(KmFDoZj!xh`)f!JEI14*MN;U`>pwF5Fane( zQb7vjXTO=z+k;C>0Kh?Dbj$8R7^IW$f4Nb_gGmY;oV;@k(6hD`gc7g;TP#$h2Q|1m zkrm(s_JAkc2?GZKNk9%z2Gjs;zyLT3SOT_y6W|I!!Nn^maQ|H(a0)mJTm)i(E5LQ& zHjoBn0=YmTPy$o{)xdM$HSiAj4738>z!zW`m;`2lC14Fahm97(4B>$6fe1hjKn_FX zASw_|hylb5VhwSExI=s(Xb1sv3UVG214)G3hTMhZLW&_zAkQIhA&rnu$N*#<@*T2D zK}o?x!AY@?LX<+9LWM$y!j!_6!iBS5|78X6jI8ZjDW8eOshd_ zN$Ww2r@csfi}nF+Eo~F+FzpXIMmhmHIXXi+XF4?9Il3geJh~dXCc06&HTqrj2k2Gl zE$O}JgX!bxGwCboKhY1-b^V2WePVS2{Y$u!T*%q+^R&FsvKXO3geWq!%r$NYnZ zgGGwPgvE;`oF$p1oTZ6nnw6gQ0ILqGD{BaA66+(@Ppsc|(eD!7rMJst*Xdo!yPoW7 z-?hlb!6wUQ$%bOP%$CRYmTiokmR*$HfZdDz0(%DgOZGtyN)BNTJq{?xd5%nuR~*Bf zG@N3bCY&cZV>k;qKXT4;v2!VK*>eSPC38LH`oc}gEy``ejpUB!F5zzDUfsQKx6W?Z z?n}E1cQ@@`=HcVf=7I4<^Az#4@~rL=++(l@u_s|q#hzYXDqeBkW4wX9sl2avzwvSM zsqsPiqWMbry7p4+72j*UH*{~t-uHVK_wnyD+UK|L*1nhfzVUPOYxAGvzs6s~KQ6!_ zpeXdMbQ87^m(MZt>(J?U|F%z*6v3#+fgUkoD z4*DOwcd+FU%^{UTh(jrdK8sU`D~kJyCyReNOmSH0F#K@J;YJB+i6atdiF*?5l1!31 zk^z$Wk^@rQQf5-;q$;GQrA4Hjr4yvF;Rp?OUP&8MJRD7Yhp`@&YRVq{(R~AurSH7d%uEMEerE*#2 z%@OJ&x<|r~R3BMURZ_*PmZ;9CNva{#^3=xE#nfTynd*ZYLK+?#cQw9f3TnD*rfYuD z+OOrIbx-T7wy?IBcDDAY&LN$XIuCTFb!Bw1x@Ee{da8P1de8JJ^^NqS^*J+ooU5#Lci~ zmF5)YN6oLA_gEaTKwDH;0+uG0S1o(3#H{?Ss*cedvpSZ1Y{XjD`jmCO4Tp`JO`gq? zt*&jnZMWS)JA&N{dsh1s_PO@U4*Cv>4qqIl9m5>oIq^EdogO>WI@>#EJ1-tLIDY;3 z@Cn5e7f-agh`9v0ymjSuMY=w9V|DXzd*n{-?%A--gefG(MSjav5QX$UzX14#*-D z9m*Z`1kH{{pzARFm>^7}-(kOteqa1m{jdAaVokBxI0()K_ZZI!eoVb593(^#1_Cq# zk^_DO+6O)gVhchCy$==-z7#wfVi=MYN)-wXeQ|34sk5gB!gRv!o`#(EJpCeEIQ&BR z@EM~s_s=q%MV$R`PUc+VxrOr%=PNJlyKv^hV1!Xb{zaCH*o*CvYLRJCR8jD#kC)^x z-MX|H?G@b+BNLMpvwj(Rxgl0I_GTg=+53dm+ma5c%?L@ zYNr;a?MsVETTO?jcic6)TYgXUUQz~4MnJ|$rhR68mP%G`Hg9%J_C^jm=gWQT`*pd> zxw(0J^WyU<@(KCl4^BMzT%cd@xKOh2ZV`7;%tPQI;o(HFdvRNdX~~O6Dvt_F50u_1 z<0y+Rhm;4G&sO+We0l8jxbex+Cod}1D$A;*s&cCLS0_K^dKzEDP;;S%SQ}Ei_{{Iw zx92|32Vc0o=zi(=vZd}=-N#p^uim^ieEqUsr@r=$#+&N5s&Ah(s5Df(Q+ikaUh#d| z2gMI%AC$K}PHx6x7ZklfP5K&;X{@2N_q;G+(lU-@3 zsHmu^XsD@a=xM+|MmicAIz}c221W)3rd>?rYx}X4`1>6KzT=pgn0a>X;`!g4?D_^G zK?wnmLj85Jt0=J!0JlR^P*6gC`_dtuk_w@q1?a%Db4e$@k{YsoLCD9t?gA*l16?V> zCV}U-zqsc8=WL?Fwf%vWw!c z;S276Zo9A1Kks;g+-~U zBrkTE(OSmoL91VCe~9~#NJ}0M&DYGn^u97keoVAqzNyUMD#9%JyuWd086=s3l8L$W z9`InfWnO9Z(AE8^Ei{>mRrjextxcw~E{SX9dMb{7%e$l&eW{}6^=7l31*?kQ%h(^G z;lg)cMTs%g5_COxLCi znZYwn@8Yw{g&jC@>D%MKvj!Ka_%zkz4=Cmt)ZJ+Cvs9&d;5fn07l*%_8KNLLI&q|} zN51Gj5h!QK9Y1BeH=t+DtzijvvA0nIp8y8m+i9MN&mzzj7={;*L5Xy92)SgphLfKTX;OCGOh)Zongn3^hP-CzrHp7dhruMuH9?H$$9$fLf6wQqexct<{>A+A-+23T zc=53YvI5+x{U~8%b4*8QF2y{JVA+y1%AtwfX&1scx#Riu8>Gyiw@;>by-TD* zDHImJ;^`GBe16P`-6{4Am(nFg+vK%Z8>JTm>M>S>CUVoXZ`qA+)E;6x8lG1|1khSZ z&5ixDCHT0Crpnhm@5gV+j(EzJd}8Z~coMS|-JY72x@Ymjje{ShmS>v3Wm)$Wcl-B+ z+sU8AKmC}>hUrbe6{i3jJWcW7l8-Li+r4xsjf>eaj7(3HzY!!fDxLFoN#AkyM*)78 z%^@Ks2XUsamsF>@X`kkX#TM#4yf&ro7&n>wRISm`5})c*hpJf*oq1id)}IhhY|8R1 zsPt8=o3z)^M@Fkny6(Y8#vij!dS89$LODZ_f$;GVc6Qsq=&{9;@rC}3;>%&QT;YJ0 z9iso;5p~h(Y!};>%RV0x6jYm@iOb$h)y9>XpMi2fx;x-Z)*G1dZgDcT)8F^x<2l-7Su2KRP+7ZX)dW-8($}S_YCeMgb#eshQi1>o6kP@VSRJ1+=Quj>F`kUE=61f%0>^-(SwICTO_`OU=NU z01kv6`f@f|knUL>@V)=WC;s1Tm2)fkmxnX^OCK zOBSMSeMNTDL#JjYifmVJw64TTJKTBIe4gV{<2_H=gkTJBa1mxLalN0VYd$P>vj1J{ zV#txf0ge?F>Xxi%f9|peq12_hvC@Yfmfp3l@Fy=CDjk;RztQ62-VlKVkBNMHu$IP1 z_y;1~?a^f5?8x$J4R{ov|AK8q>xYP?ve>G~sUkz8>kpnQwou;fqODmNlH~PdNP}w` za5ffPm2f_vUY@dWsY>2VD%A1xBO7>zvzR>adU-DNahzR_qDoy*0g4EmKmGLeb&B^h zl!-aoXzeA}jISxEOA1$$fOxt@i#~^$oKx5M8p7q#wJe)!sNQ3d@xc+f+#ej!yCrf!pIw2|m#C zP!XoRlsNE{uWU5;G!H>yMM>0Nf|A1a$2B(pPJbO=_93L|c&PuB&>ijwiD;`E?s?Sj zWPt#G1#1cQ4|N&6tg@aG@G-dN2`eu7qxT)hpPuVk8!O-Y-DP#ulqzFHci+)-$!*Ih z?ZLY5xoeg~n-xEFS6{Y=Q8v4X32O-jeV6m}j@(V7H1{r1YifKJJod0RJRzLR=uz*H z+s&=xlT~q)nroNFk|MC3gb*6BMe zSs<+@;0YZPdHmV@qwnCA)ZRh=B1}wm!^)c6Ag*K;ez(2TKrF6FO-In0D(h@##Xh5` zv#YsZ_C&wFjCyGrSZ1+jPpa&p)7mlu2Dw*aBu*bc_#+_CtxMX*SMthR(Ggn1?aN;{D|KiG$f3H@P8jz5I%7t9LgT=H{fkUDRdA7imq-NHK{YIk+Z{Ic*{c zZF=Tr>$AT&o8eF3Gag% zg|c-bzUOFM3lmZew;K9TYI$Aa6-#pOosKn=VN>{9L7u0U@`1PGbcNCL^vc%EE$5eC z0n3&g1hdDjg#n1K%7U_0vSKs5@^81lN@E>UE7m=wqt3VnN#X0Qc2`!+bV56$ z=Jr$CU6%@ffBnI*_z2qc)}FxmABGt{p$GFvGHkKb_ ziVr^U_|`-B`6Gy7hD8mBO|eaN0VENqA_6%F5+W(H@*UrsoiaxAmnSFH(=IhzKQO~0HfjB{LAwaGkcA)&p*@FM#_V_Vw@ydj%k zQFPJz^2sR9+eHtbJ2Ki{K1pAG{wUoZ6=sIX>l)Ni>o_zuJ zRegW;)3OiKw+Nl9y+QTj6HS9}GR0>F0mWKPyAQ25C!&|!EMg8c8I~H#;;+UHFWK8v zyPVVK#;_*}x$sp}=CydQC)7AQ_==n5@OZ`{z%dox-pvl?;alxRjti4F_!Ihi#q!pMalka*D5UH1+*{Uo<^ zND4a71E1$uh)%Y2RCys;m@~k#I8H*=BXp8W8t77w$VNsx_-hav_(guE>%&^pd9*J^jS$t5)9`ojNxB{!{on!a!$P8v*quFEdW76<4E^UU^ z>UrH*eB>3&G*wq>uby(+=&J*l;FZgq) zGs}N7dhBUEwe7PY*-q58mT{yd*2?>s(bva%X^Fd zK=EX|{dYuCMLWFaDY!gPhIfG6UZux*&W2YHC>MvdaX%IrD=pY$5uNpa`Is*M@fbzV z`K(g0$T0THsvWQc4!W;b-}IC=>jq1Q*=F`uzIeuN&-D2`;o7bX$0LgB+Nv9pg|u2+ z5O)b?^2t)_k-4w)3f~M^`+j&)H6_q;9NCOO6|9sUiE>R~T1iOYc?1NU#SzMrmS%G& zO2IFQd#bKpVVIo3$n~lkGs9p#Y9)JS#6Dzj#mf^nKH-Rf^K$rCA}~0dhPpGGmmDio_X2w@OzWO;rLj=^ZPzVnZ_2r zoIJMH5{`&cnQJg&Kt4kEG{#JE1JsBB^uTgHBX5H`ef16V{5Mb5(tA=r3i{>t*vnl~ zk3VE>ex{4Lw4~?zpsBve;C`2e99`Xeai{sNuEE7c>avzzEIU3HFPH|8sO81odnl0_ z8QYSj>0y2L`fbUoMhaFs3&!fSOZiUU-o)n#w}erCPvP>v$H)Vxy4)0$6DaheG3$qP zX}Rf;WA48Tn1? zl+SSGADILXo%ZRkSRW2JIKY>%+kh9+qrf$36l3vxxSs#E8B@sjQ{O3&XMIA08jlTuqUwOU8dIGry=j!dMXRT{9nYhv_BD za6tP@M5sxIPSKlhQk>&{Nvq>mx`#UJU%g)cu_j)cM*N(=Xnvpqw%PDCuJD?~n%1$~ z>-j-%(|OODMbv3~iKRfaF0c$Owc@_3;?wj*r|)t_GK^hDlz!zX3%ho?ZBq5NUmk8> zGi(XilShe}CP|FvrQsu>VzPLG8GL z$&k$;jq!#~+(c$Q6=pY1^+%L?~e0czJOirfiJX;1qLU~bgw#cT2*q{i?;2{={jla!=;{C>5>K1bA;kW zPK$!y_)XbZ9uc{uTMu%*3~1IB2N%mEX{}9!uOcyJyUpLFKeZK?e;F&PK9CSNCHCQ3 z2PInMI}xz4o44_lPk*&io%RlR_w)r??3I82@ncVhdul5jX?doCl@&jBEHa#dqfSHX zKpQPke_VU=8~y;p?m+ja8%src{2^-chdvD2csxs}e}O(&8Y3jHnt#gdncL)p@!O7^ z2L=86EFb!c$U*{d8Z7#6oVf1`L)7OB9y1lEEZ8tqbTYg26DJnCrZB5)yWW5Cj z?Rwuo%$=#Tf+$~B?p`f&4m-s`Y|JCjCyXF^_qrK!(`;OPoE6X~o~w2AHhhTSlE{&x z=rR4n9Hn--_TvRrZ;OoxB~!!&b)M54tGVN02-z=Q&kyu6#HvIq)Rc=p{Cwj$bEx== zrNyl=!0@E(K(`AM;^Cli_A~!)xlF?&&o9=X^)5sZyw$75KNj1hAj6>JV{>LNHQ8h4 zxcrQ32|sMWpEb3giU!z;Ku`Zs#@f-w7s+zDBI9?pUcR>_OpmU>HTH!w?lxJ}GMG5Z z!i}`)IYH%T{`AgCAM17NYNvS)-Jn)Rd1^}Q`iT2=HK#VGz#osrszp{BS^`k_uSe)L z`#ByrmM2=C3D0{R*7p`|gvn|3>i#||@I5H_gg2iwhO5B8NR4;&^(yC&wU9y%$6(pA zHLGQr;i_Sed7F`7?!pbNH}L8YXQ~)`Vp5w6?7R}{Z+~o{^aq2;0vRT_&o z%n$6&8B{t-(cF|Z*dBM&=R=HxG9H|yos+u!av^RUIY zwylZe1nYq*{O zv1z1p!`f5-;#c|X%5P|oD%*{L@9oKm7Ek^xh5Nb%nY<|mH(5@GhBIsMcuGAuR%cz9 z>8jC{&-RAjcJswcD}v*3n;NZa=O`nVqC*^geegd9oer44!b|e3PBr9(N*F92O&#Me z9p9&E1%)2wmraqJ8J;Qd`Sz%`AfLubkmk0I?RA-?6a}ZSllG`T}z3z?h8G% zh1%mV&V@t`ftFyCgew0?A>l8H@)eT;)A@t1%;)zxWQAj&>cuwt)XBeSGsX;@8>4iEoBJIG$wfo9v0lrqKdLt1t+7lY|BeYR!FCS3`7}q zY@~R1yJM&(<6y|u2ZM`29&V#w8X~$5(LOIv*EG>}U+gY1r(+S&5v1xWWCO69= z&d+3qEU1(DTJY_GYHU`^l1Q9O3ci?s?nMssbS>BF@oOUT0QD2TCY6yVy`mI+!h(>Y zvqNt%*_GXoB97Qa!NV-R-%#OMj-c;Xs1a(o%?=qTaQHz4Bxb5Kc~>#5dp4}5QgqKg z%X+pK85(RCs^yBkwZ@(8Gns;*d&cK=Gnh*JQSu{Mk$n||<$bER#&4v!X-=Z4He3(! zHpNS=avehiE;%TlsLV2kQeUAlbf9T}JZx2fZfKm|e{4vLQP`zwCHdGn8`<~vyI3-> zD4jsJ-yAp=({fQH)i&Y5g{pmqx3j7iG7&`JuIhu14Yz%5+EJr*N7z2acOG92yVc?` zy*Q)TyGx|_L5yd?SE|s%9rf{N6_nj+SqQ?^X8PR-y9Sx+4)+srs{xlK{JTbvVrihd z6;#*_f}zK;K>nqtc_HQ|(66s;>Lk2ui-hu2>P@y%Dj%*O_9ddc~k z^xXT;JW>8Ld9`mX2lDp2ah$qWUmb_{ zrD}M1G$VGCqqie8-$Lq9zJpovhiCM=iamk3o(}AE!K#n==%pt0_rA|N818pG`%#24 zsYMy-+N&1j6wlAPu^yl_9JHxF_CA2^C?Uf8$)?fLXyM>T8`c9NQR|{sdIvWMMSb=T z5$~G!9aweKuCEJZz2-E0JrYsD7vN}12q?tGC`q991|Cm7X&ZnmJ9>FoTkhd)K_qkR z)XU!2*CW-Bq!khYd3H^8BV14*szvg3mSv|Azxd3{RL|jTranVY9gcwpKSE%5En5 zf6+vpR9gyqGrqVw|G~t^ec)*2y0$;}(h1(t9=tnG)2DNGftVEw}UJyLf zDfjz>Q)Lsrm-ZeJzOrkxjW59OTW+!6H@`QrBNL5p?<%*jbz{11Y)e7{ye~DjB;>_U#$h;~_-<0uh+w7Xy72m5Jrq8~rGRu-Sh_Joiv(vO9#8l5l)B|GHwlw6#%ru0orVd;D{;=A$pzk#D|LnZF&F&VopP5Rh7X zGCTcr?a;Uuv1MfDG8OVnWV|{dFK+jFY0=Uy+GA2$`FCvlc&v-Ie4s* z1)<7p&^}G)s}SYKTnx0MdQ5Pvs*<-6fqo`f>0X)odKEH|^k`wN}w)s8JG zx-qE~+2`-sqAZ@?OMkOinA==w&p7I}uiM~m%t*o~Dt;mmwO;zAPFer$t&l?#DUM3( z?GnXo6)$n6kU2T$TiGEq*6V3zPWQs4u<5T0Pz@|Mil^^2B~Zq0JooK8Cz$SCWIjR{{@5^d*#pn0FZe+(<!eo1Ff{soBq$& zNZWZ46?KKyb%CUNQ$B`fb6cBhn?7d~vNctz!s#Bz6DYlWI&}m;m>Y2p<0LO>y zyWL2s8vDN5(lf0eM*3){%fnObBkEdkt3mNG{Ww(BKxJ|p-YTH1r00RAiyP?o_kDFb zPb_4U*azEDK%Pg-_&;4Qu*d(^_2d#*E!Dmd)U<|+%~^Cl0yQn! z!(Q8V(yE4ym4jb(t_k2f#Y_zuk=2R|72=oFAH1i9nx0`0Iddh;0#ZMTd5Y8Psn(4o z4C=vwE2@#D_XFWHNpMgmR1#ga+_xUyVy~K-k@Uw>iyn0n5KNjc5O3ecw#HZGd&cVu z#YW-*(!*b-^)>~M8!8oy65_;O`j57v!&1FLk(25kvx$bI%Ed`8#k3+cH+OFPd?JXf z#mr=4zmp>(w0NopC_fUY76<66#HhZg>oaC$&hi3?tYeKh5Uap@4M*j2cvQ@}88Q)Y zQ9c9NZXo+PRs1E?eG%#flO7(KGPdZ``BLkugY#dQzaP6(MlLTJ$rD7tGB_ZEz=KK> zmDhbc6fR{*!GP$&wYSrHc&XC2E)*#?#ba;#WSunmZ_B!(#*(tQM*470DZzmpM}0}@ z?w``;bG-+FZ@3%$J=C!DSuo>MxPtMW0Xh-%)4;=GO0p`3cZ}E-RsarQ=|L=g8`D`b zGL!{_U*Z6e4{o%qx8YLbr21ph@Z%MhXvdgsaba8fy&sy#+wc5GQR!=JRR9YM0bZQ= zdDrCc0s3;IPZ|-%{{W@v5rsOF-cQwPdR8^zbKCnVDioOA{)(top*A{j)|y3vT`$}Z z)kYD7b+JE$R(%tHIe#(TYgAx=|JLG2R6T+48?=1NH$6G4c2|+FRP-{k#L-9t#OriF z6_@`2gZ;DuTUHqgja9T=bR(9uu-PD}+B+_n=SvS;MKL<1v={BM_I%W=61GBLBXMiF zxgCeJYfGbp%5=TRwT-=+YAQ@Hs2OJ2v9(s?w5j9tRm=EJ0_vV&Rv@dJt8(G>Rd0eT zHarfJsuCpdl_TN>d#dMGO9^!ee78%I0|Cd5yl+?Q=Tp^Z9zz&O7Tc8gNxviARWFqF zJT4eHtt@fM9aDj~%}&O0t~e6Qmk=jg0l28tdv8%i&u}GI8_prjRf>v$CjCd_=ndRac#5o>g8w z3p)m1%g-A|<5h8}V0nvCQ!0Pc{{WiC%1I6^l0|WI8WsSJH*prJ!06c7auI0-)c0De z5Z@vgvLj%4o+8y!61SJ>{5NyC0qhpx>^+pev0rM&H$%i%-Nm>vo_w{Xc!&3?n6a^= zj0o3a5vb?%(UKS0;nVTld2z1LbR_GiWf*^B*dCh2EV}3ptDzr7ALc*Nap~BRAXQZW z4FLkT=ayY`v0^QNvEf@t%c`|tZSogF4wbtjC8--yc+eULQOAu2^P7We0a9iL+9E*r53+(3I{RK>s7~)NirtKg$wZI45bTm*D!V(+>D92JR zHiK0(sZ6P7bq^ARxU(@JSg&W^tL9INn^DdD)mL{1VcZvp97(yYzC0l05y>EFtyXs=GP|3k)9dg3o8b-0_wfhgZ zm2EoMBE!-8R!>Pp&c=nO$|C#7-LM^PwD9*-_0^$d>HTZe8F{c7pko%_<~Gu!Z)oqV z{EUly8Cb*00vVXE!*OCgwRn`K9FZ9b3hBH508sc(vWx>}WJMMCkgNs4ULQpm5l=Um zOQVJ#dei7>-Z)bv$4;A3uAP-os>{>Z z=x@iew&pXnJ%@L7QN*4Vw=tcoRx@pkC?>+%8r23PHvsn&NNdCbZ9vwC8s_xVtQY_k zDpKyYZrx}-1=jxnL8eX8Gh^LqR*|&E48Gt#y3W#L@`H8z_|)k?|IpylIc5rqcPTcv zXD~af%aQ5pc;`^Tf$%NE^>9_Amej5u0tv5@+Zc=H3_ zMDiM$1C6VwOFc$BM?kYkZZZo>8sdNvBWBV%iuKYMZTPzr`e-pYHs0<@}2t#ETX(%><1WG#C8e zP`23{{{WW&KhoX)f0@4JUbC?&)4Hohh|jVNM;F7vQK*jg^B?z{;J=Pm@q9HMaw1nD zu_w0ZR0ZPX_=z3$o+?l>q`*vh5kONVuA=7TaN|#ydf%n>mXE_YF|oK0d#?=*RfMqO z^^_{4O?xewK^ph_MNSe=8>s?8X`0LNY5=!=JO&%o5X4)^{{YBs50*){xYD*mlDwTt zY^_zwF#iCy;9OGCf2p2!CbN{RW8!to<=86|ZC1Yi@Z*Rb<3*VbATYWMyf|`s#Gxq$is(w%GX&_CF}^ zttW(hkAv%Mr^*ns#_Yp+f;~uI-r-Mtor`}@>v&lHPRgyvQy{@lrXR*Zrmop=PgsCFni`a@# zFwE|ONwkB+yL>13PXkQei+-*K#XVf&BMuh(WHEuYAP;6vj z+>M&AXMHPG9*YE@5^(^Hd+B|T5_plkWCv10@dsW%v{L&~kjDQ2C;DKrk##4HA5+$2 z>oUgSw%M=4k@p&SksqneJ8lZ0Nwkr$xb8ix{4`oIBZ)l0Y`U$!0(X5KqN*mHff4jW zZhl$ef5LfHVQNzb83Dg8e63YEO{-1=8vsuMTXPxIyg5@D;92z>D?4_zlNp0G`dBFpi<{*uNqEa2Hg@6WFdJAW3O)vD?wi%R zlpthvIp>_a10E8wypu8TFNcEC=HHrqN%&f^dj9}W#m0(O1I%ah8DR}9Xv1D#H+U7V z+rvMvmFLrc+ygl^W%rr%XQdX*hx_)Zz#mGs!l#7pbQ#=Ex zvVccB)yPPYgYLaKK>L@< z-}4O`=K6lF>W|5Trr$K(q9;pSj=(|tYJic+DyakqMz{fV*N_8=rDmHFZdk&p-dOWn zYuUJWbEgWACN@{)WAf9>iL{Q##LP~n%zhI~@(JW3-Z)kFECiWk;ylLCkfT;(ZLzQ6 z{p#DFD5YGh#t~e`ft-XkJ;j*(&fhWr04-Lh4fMFcK*CZ5_Msqf;>S+qsq5ua;jU)g z$fE2N0^h~m?Ee6&jncrLCEF&(Cc{>>h`S5ki9opL$(-s~vs?mn3N$yTYH@1BG0hOL z)L0(vD>`P^8*R#s2Oi2mWk-)`3NO!2R340X?cwoh?w~dhu^OI#Md;AkdRTkidk?;W zrW2zAbkLD+Xs3fu!PAUtHL(?PCeyINw~ekoZ`)N8lf;!(u167L`WmT9q@u_w2Hh>f zt5bmWexa1TJ;pd~$4NHm!B0CHud!mH*;zs-jz0#?T-+))?ko6Q^Z9Bo!$^OobuOB? zEhkW86j8R{E)#9-_nQ9z4XJZvo<-E}PV2bQV~BCk#}cS@^Me)u>P&~3*OgTEXTg0E zG4;hsRpOEzj-z~=f})`3V`gT*1*i8fs#m1-E%K9>I!})w-2{r4TX1WG;u?HZzW)H7 zJHlXfX*xbsdDxLWhj0>kYyyCO6T{)B*LmeLo$*~Gr*#Z|tB?7JmA*~Hj}6G+8-rV& zZ_l!IJTU8=DUyXH_LKJ2R2G)ybp17@p|lAw`zk0K^7CJRy59Pb|I_)hXL9TtQ^?mZ zG2cgqNTiHAAXi(E$L|h0_taTs8gB+-*8xI}L0(ooKRr`$QXG3723DG?rs*Ue3d60;F$r?C6!1QD}RWw=Og!g zKMhw?;JLm@I2Id{&Ikz|_!4d|YT|FgTce^k<^c(+HMa5KS3%2-XWz=BNt81&J_aP) zQLenrM5gkxd2E5Vn+5<2XbsPczp|*F2sZ`VrHLRfZoEkxh3L{u^gNi5x{)3eb4G*B)vdU66?qHSwcy1s!$M zl|QtlmK^Axl!gb#iaAe^NCO}SQ+^`i$Avkc1$8YJFISNn@FhuO1bJ|l*y^W>l5{rj z_?DcOd=}5j>FEBraiDBGwEVbMvm40BIuMQQvJVK8_DHl`o``?zwQ*Aa@Q`Dld^VOgtVN{HN{V!kpx2 zaow6k04wQoEvK;dQ=m+3-c*gdd||ALKo{*Dzq?DXkqzOL0n=bx8=yM)`2}7Vxzx2P z4$Zx=)JGFAh4!O4V5UF_@Tt|#;!QHtWRb@dW;R>xC5tw`A;-cr)9%yLAuKW~w8fYc z>i)r86N)>7nPY?rh0s!WA@bKG}I!59161M z`M^d`j9TE3Z~hb&O|~dt3Tk_K)Q7y8A_Blzeebr74TJb>PNdjS1{ncv4-v$6Yigh( zOBr7Y)LT>EOq$CgY1*elMu)ujAb79c`zh&Qk-gh!B(dS!dNdl72Oz5NWnISG*wLc| zYzZ;qEh{XG*r#7@Keh=OhZ$lpgmZ{opDAs*P4!~d=Tmz8szi~WCIhlfk~DrLvD$VY zyMGR}{?xIwC36ep#0t$2xEBk&0r3?YZvOyvR*W^#8>h(fN>s#L1|`Ro*0*Ka{6Wy$^wyY3-~k(?nC{B*d_qD5ZQXypS@r#y ztmc_w1%X@hQ@Yr`es`7QS=CBzByCGD-EDO@@Slpav&&5^(-)1J_a#VegmE3Z zbD~1Dcx8z~vW4IQdtX7u!v6s3qJwcfOnfV|b&9bTA5f{LmFeCF=d{@0;a$Cg+U=9@`pN9-9Ur1ECJYi&~xnQuO?_x6h_i~~JCC^aNH<|#JJ0aleNjpFz#eJffO-F?gf)kPAX1wn9x|JGiBY`dcnsU-j z^h{xm{L;lSr~aHI_(3N{7vWk@{{RtPhaHP!#Le*(QhlUXWN}d`ho}!wAOF_*p{?OO zE^Ne`*k7`_NMi5%G+q_`4iz{#q=*y$br8!!YqY11a^*mwXF6i1L!`x z7n>roJO_fPvtP7Puu9vfdMB*Tt7T=79uks8D`CZ!FGyo!Lm10;X~w5+fc+IVnrOlTkgK{=Pq8w|;q=Bma+6=x9PE6R@IeBc0)0tQ< zGU?*Bja3ql^rusN?1$+=O0F|+Eo}=P&vg{j$k`F($Bc2;WViB=>JGjy^3iC)vGH*~ z<}x1%NF~5Le5-&o)0OIN@_644X!54Wg`$tlhD*DG8)*l`5r@%FMlEYGNY*&iIF2*s zsp7g^Ui@m1f2T3>)m)gsAR!=Gu0K^@De2XESqG=QW06$-^1U+x&F?&X?hn@i*K0|BK_-L!}ZeG5A@6osn3#$z4f~=Hy!n# zUn+^V^zTq1NQkp2w(bf~?uv@2N?%X)U(5p}8=1}bST(-Iry$g-`fo8d4ZDMCcM?hc zJEO)MIe>75@U7;O48X{U!y{v^?*Pd`ah6@vKfVrdvF(6LZ2 zo-Ul!fI;_CF+bwERIlZXa8y{_>-N$1EET#(MU0YRKpNZlYid5!VY5CKG-`2SP(JV! zS}?4JT!}3k-PqlQNxhHMmm4Lb$kS&5Psl$HRfNGL#N7O#QGlySHdJ@t$A@U5g7ISo zn`F|LBY@*-kjygmM$8KlQmfm>s2a+fGC2W~AkX+s0pT_WQ-s5of+D9Aa+qE(DV`=a1$k42Q1JALv%0gW+}>FP z#@qp?XvMFmV&SGhY}ZpPDnPdZZbFurvJ4l>Y34f~ViqV{iA!;{G9Ugk5?+^*T`v>F z!BBnSs3M%91JSXbEQFDm#gQoT%Ui;G%^;h>X_5gP&XVN+05cvXwXJ`ifP11Wcvun* zyU^brVBEuT+9^Y&;OS7|=Sw~$Eear^8O0obznF0AFTL#4re~4rTz(cumm8)X@G^C1H4a@=!o6Gd4P$Ij*gy= ziJqQ`jgf(ojf<6uiIt0ogM*8MgNKcYd~H9rGJn5Am>C(FS(taRuuc zim?4p7!aQWY;*tu$e@G>0TgTyN;U|w7i=U0u@<5ON=XeNU!cIGixPlPP*Ky+($O<8 zf?fYtBm|(K+(GUFAXE@a3MvX}S{gcPN+tym$wo=FpIwex?}#UdP{0{^8qUN!5A}sb zxNKi|DJWvkHX6{1CfUsn%t3KV+?gfk#0;<4dmFvHi&x&=k!7N#p;jV` zuwRay3X~u~h=W@GOd=;qf)}=pvkD?!1K6`k26kLfoZ_4)4M_l6F{PJHpb&R=dmA|@ z<6&Qaitw)`5dS3tu?t|LB-hCX=l~;AeEumLwvGylQra|*ZSEEHHFJo)^e5salX@Hd zdfF<*tPi!E%CzTe3}R+13`_@jt1EJ+FUZavYJHabA)L2_wka&(Yj*tW_~ZPis{?wC zirYB&4n+vD-ipSQCf z4;xy0$B=Uo_`o)Wkgf@$MV z>tU*zjXZiNX+qauux+rics?S92o&DKGAuIhdAQ4jzI99;^P_ou>9JFbTzae+bxT!y z@#;r(S21SkP#L^p-DATgSm6p(Cy1aNcK-W^!l0&2*Y9_Xq@KLaK!^0!Uf|yJ-z*VL zc#?~NP>9jW>V&Nv)(Lexh1j@}Hu!d7W@YBHq?FLRE>;=ECm9h(@fzzbhn_tPcP~By zTdEf=+od?TQm-&BqjbvnOC28-S}NdcL{QM8P0G=@A+{%R=N^_A-wk@&zg(-e^5alP zRP30R^<ruioZ=Vm2c+Dz zlAe8m>pa>w4TRnLq?4a`QTO7LC7lAH8$_Q2NoMCEBt@ zJ)b28$6kIase4@Om!sHrbc!o^ikCyD?*`vN7JuZ!zR+bN@b&=jxrEbuMV~dVYMGnp z$gB?o`tHQW(aN_mZ#M08U5-o%h!(d@5=K!cqu$b%vVOEb_4?YU9K4Xp>W1iE(;PF( zFqO(M*whah+i~oBb=~HU-8b<|s;-~)X!U6IpK8!(@pko^`ZW(_$G^w+w$$O`uml_G zj}ImR$>zoDnO+YUdsFNr^In%bT0F_(SEkk!xUa<_x0$MTjgP*uNJD&6rltF-kct1D zJs6|r%B5zrhocB*ksIkRLfqTYfjB9^i6b^h4f7nL7#n`U7GO=GtSP~<*w7|FwdtcJn<>o>E2?e zn3C~31bmszzjuxq(bc>e+VpOm&}0+`y?u;YGA^*Rl(Fa}5xBchqsGU#drx`jK=vm> z%c<;^fVqc-i>Vdi7(f5n?mQxpbvJt%HZ+k7lY+NCA9#qQ4!D+)m@YbR)A(HNJ0($hC>B$t1Xf0Eqw2~ z@MiHm5sU3%ff_e1DWxLjVG^{_E)D%>aU zLN!XnUzF#~`OD+scF$G%4V6lVk3c06bvd-L?rU)x9Z&fm!({@x8f7)F^DXMNI{9d! zqFiT=lnZ>^Me|~#)rx(lFqb+r9n<;Q9+m`9~& zGkM$98Lo%(z_~ZpbU&XDog|zZzZ^mY+;1h^Egq}ol10P3pOoBa?Lv>31wAGb*eA}v%_CLOf`W|F+8m*e~`rT z*?UQD?yyK#Y-_tis@_O9?Y)VPt`0ZfE~>SlaDnV^F2~I$Hjled^eC%0#CFZrsmgr| z<~v?-@>Kk%$wR#Y^0(%?snEf|1QpvG2-Gg46XYQ;dUw$8GxYlE!WfEbB4ZnP{ z;t!2Y2W5VU~fK=8=rZ-(@0u9vBdl2puYt#-L3%AE) zY9!u86jph?AK!5H?p?B&pYXg)d2Ew+Zo&FHIO*D)G4=oU>|N?^w+Ttat6S~tZL1NF z>!LA@u1adlH7!09drHrK|EhBH@{IY|H_Xh2hLcH4Wq9%t#Y(qVqUTMu`5bBQK;3+M zLexK|D`rb6GF&25xuvALNY=p9?|R(OX^+cqWkI}cPc_vVw9K!POGUWVD<6HI{z0eB z?k?Lk?zrr(aa!c#Q>&LQ*c_jZOZ3r(SqH}Fz35tM-@Lo*l)a%`R;wq-uGyZ7ce{R# zP#C}`u{Vk`ck$TMJvpw;U7S4+vBU`?`0P_DfSw z2QMic*A?#@wHkR?^F#dmA?x#wNxjeC5UeGG%AZh|v-5tQq4-)68oAIOL)kOZ>mgM2 zioWB=CL!Gf7omhxI=#G_pz-F*m9k8wPPdMQ3220QI1zZV-o9$&bGvOwrRmYa`Ed4` zEaVc2~OHR13956`Y;Cd~&Uj*ya_rQjy39b9?n(u8K7I;BHdlvarS|bf{cBER7UN~L@ zCEdA^Z6~~&YiT#{3-Na|_BAJ5H1PNRz28M|nyt5rjkL5I9DiKpRzGWCc%uKlP2Fmk zf!+{X?$Wg$&69Q>As*5rVepr4c0Z{*x<9un*tJ(RAbA@bh!yRRsKz1J$6Gay(e zg5#Hmk(E7D&K=USYJqhdq6o@Khp?M&7SG;^Tu=;qWgK;?w@hKhEq7IebMDSvwnqhx z>S6RpP9{(6XQa&c7*SQuec?}Cb@zDl0<^okb2IT+Ns^vI?k#LLe3!QnDg+tNp%~Yd z+4ciIt8c_j1c)v3<UNrDo;&s0#M< zg*S($`S`j*)oZsCuTEkz6BcrLz(q94tUum(_HC!--f|=cOmraVGml*BVWGk6Q`omOjGA(FLv9 z0cIECLeBj_Z#Hq~T3sk_qpdI#n3Q;TQ0lXy$(;QINhM9kDIWaHr}&y%-a%UU^_(#uF?G-q3TBp@N zN+5eGYi%QbeGO?jMl&ooqmy$V1&vPKx6sqDQ8T)s=%bz@&3U@f8{N}+4j3EXm|16* zmU6J{nl9^PT9oVAS8(nfL>B0^E8$&Ubqsr2^XBM=aK6S=1hoHIiiY+}_oHW8A58V% z?Ord=dGg-V9Fh0>{F9>ABD`A0nsGa8@2WNtc)wimqg|m1%5P8z*N)3FrCK|&p{Rm+ zT5}`%7L!}rNOJX=`m-kr9z^%vagvjO&cVYkEkB8qzVi6}DIzc`9LjZSB6;dUIa4(z^T&^Y<{o{ov26Mt2#{^Izv*oJ$m0WQ>{V~@5ku;hSRHi zyOYuBp5FB%;RhFT1QyCSgQg?eK+aMYR!0}AO!DdlQi%XV^pVhyX=X2SBK)!U^EY%> z_qn$^a-DgANPS1_1c*~&6TkdM3evKGs=k>el_=Sfv5S2F?MB9_lL5ovorEWOasYtQ z9}7q0tqFL40v^ng99@774sYj$!63;LW@tPNh9;m$2vVB#G(ciWc~){+J9sdOLLZJN zl_8{4p5nv;|us<`lzBtRD83QcZ;8zBX|CO=uLgId6tbFl-KQl%s zq~XsDsK;Nd^`QvgZAENp39vQMHvpxfB#C6}Ehy-XA$aLxzL2uy%LI%52bezcAL;b5 z-u4b?ys@w?lGL|9FMXu9;6H=g;*fYU+$I>QYx|R&ArJ<|W3Yyvcu!Ib$U@rq+TeDq zf|Mo^gHMv!fCbEFi_PDvwFmv1|EyX&C~^yKgN5q4kT{U9T~I8>-^mXKb}ne4aJ27^ z`dCN^TTmu_3?7d`Au(v*?IKJ&7?KFT!I^j9ws2p+-w}*E2vD}4^rW#uIkhFjw$Br? z^CXWWF*1&2yHfC>a~jyPH)b-7Jzs4?rCLl>u3~6Z4 zd3oYszfCdJfCGR7=MF5016%EdA)vi+e;Zg(yc~!l^$$R@#-Cjd=Kp3gfc`JjdPAbO z{b@N6Nia);L&BjjoFmeLWc!f6)I&|ifnavIvVOUQ*%o_v%_96Z|vogaLF$qG#n4x?H!N>n~`zZ-n-HA1qwqV?P0-qGn}ctrNzz+L$?F|3o*SP1{HGd*CeMrt#n(j+_D=v;fC{pb!KMz)*=Yi6PlD#F_yn0UB-;#-HTu zCwWLo6_GAl@NX*(-gmsa+b{?zPa^v5BGc}`sdf_o>Pad{;_&D1j}HFW!2g>z@cZO( z70mpK*s$1pVtrwFFx*QD;gMn`IPf|#8J8D9n zp-5rR-}9u<=gTRX*y%@LK3igC@?03Cq+|1$j-$ILwUix%?u2{ z7_tu>3EPP>GLSOH7%U15xom~M8H|5INQDpE7@2?%CN##<6OV_H0|YyT!3a3U7YoDT zz$Wd$m_os*fj8JdBpe^|m$uWpk}zgy9}LKk6r7@m`gx+!FyvNeLFSp)h=0_?X=;rtug1Pg|(e#5i>4Q~tc{tf&0(sp>T zGq&-hUj40dGnA(<%*GS%_m_APrvnZ~#^~XGRkWizCQ?)XZ3DT%I=J+ZL7pKvJ#TM4 zBwi1X$HKh`cygzbBw+ptF#w~H+oF-Im&pK$!NK&v(3Yp~&px3uLPIg$;5gVO1nB^F zl2zGx27=IkD+>ci%VL1>5BY0DNJ8u}B>02zLQpp(UZjwyKFkl~__O;-MBJu$6mkb| zfI<0#HiyRV1Tbl~C8F4V^8AMgV*@C1CpuN(5x0Dz4o z6{%KmlzYMdVFc`06iIW;zep`Tp*x+$@(b!V04JlJF8DRi?IQQ0rKi6I2DBis z$ack~?QBdtI8sD(8xDd2Rx>mdN$@6dLy?SVr+W5nxGM|`PKsDCp#2j?V}|wyNl8q$ z=QBnNFy3v1CJhJNHg8h*Gj8>p^G-0BR3G~h0tQbSOg~54Prht+{+>`67H5R=$A=hz zq2wK2q;fk*Fgy$gXJ5Q0a=RkZOvk!ik`xRE2hZ;)COh!u0mE(0{=c=G+1}6z?v3{| zh5fdUgk)^A;3)sw1V96Jg@K*jj{ZqAkp-hX*YPGjcXF-pSK|d1p(To%%VpvUV5(7D^rr4z^}HI7af!ZG!>l zkl){#EIU&D1w-m1RwpbR53_`$;V4h!b|vIFh71P*_82?3Hw@~D{mZbU1yxNl%AXn~ z{W4IZ!yoAn-{+{>(4Tbv;93rH<=#*k7!pPv(x6`DNX;iR_21c_`m@Mi zSpP(mJ7cF1s9Hawz1q?X{z-Y4WANw@d%{)KdV2KbM}-cWTP@>i*r5IFjLVKOKD zVZK6wKl?zPJea|nLGcuRhyVJ-)e^#iOAH0gj0}|J)RdK_*J|o1t-dPc#%} zW~e0uHdY!A_f}UhRy2?`R5dVElh-#=l#|m_)>kxEF)}t%l~XZPHqhTGYlwjoNVc<6 z)|*sT?SEEwYpIhb^1tFi8p$MMQ#XZ!b0Rimd#u^P0=82~xFFfS)IFi3H=LFbl+@=i zZ=r24_@4U5R{uxIw^#;&D-{2&A#9Q1Fh2MoPb^H&7u3*yvNM@~B{$A9@IOec_n-9r zucRj1q^#VIP5u{BZ!44Z+r<|KPQ5Zh|BH4Q{jb`gB>VqQJD?aeX^GjdKJoX&f*-ng z@I9v`YhKiXFikJ-+U2kgI84frJuey9C3&EFaRtWEI0WBs44=Rc10e``Je z2U?dQz2de8vdrJd_*ULi={!{}1d5d?l!R|5tO^R=rzGIRDEzjARew z#{Twx-vNOi4W#7HuNCc`uZlmfj=7fqk%sf_@jaUhcxiZ-vL8|U)n*SH-q?< z$r=2K=Wb(bZER*?L=H1EnSwqx3WmNK;w6x>1l51(vMe=HblfY^Za3f`dS0!*I= z)5st^DM}5Q1=E~f2vVAYY>Cko~XI;6?FCPeTss2`Y?1>sCs zds_oAtqD4!n0$Xrd;ONigJBnt79cs{$=fyr4?qtJf({#1K~q=|5{z(3*?>+cPpmiS zA_6_#=nw$dYBQ-6!2SvSCyAgV zXl0=L8tiV+jZN|_2*S~SajX2s;lC{Q8wXNy%dWu$N7g35=`IcI`osxPPQ3@HI2ZxS zV`(4;^0VJ8=pDhOB>>d}e?|->bB!F289Gtv!3^1^@7lab9fm5O5MW16%-Nfy=-( z;1-Y$WC3|V5l{kD0M)Qd60*YCy*DAw~$6iCu9IJ4*3pQ zrJ$r>qTr<1MJ-%_s#{d|sLH8oshX(Lca|=hE}!lhT@&3X-5UKadQp0H zdTV-b`VjgA`YifN`cL%3^lJ=k4B`x04E78s7|t-`W3&229782u$%zxlGTQI+^B~nVH3yb(vk6@yzkedCaev`Q zaIi?Tn6Y@XM6jf=l(RIkOtaFnin8jly0M0`CbK?b{lxlh7yT}=T?V^6cb(dmvg^sN z_Fao?9BguI)@&%YOKkaUZ`sD!Y1zftjo7`}&$HiUf5kq?LCGP^VZZ_9ILDF2@tR|p zlZI2A(~R>3XDnwS=SR+2E_N9b9 zJ`Fx7UkqOfU)NrWy%KwE_lE7gyZ8Ox#eMwyO!oQjySeYxzHj{8{JQ)n_^T`6`b9_cfaX=!u~t^Kki=>5*Kn1IxAEx)GN#&tSamy zd{y|R@T`b{h^5F$kpht}QF>8TQ9se^qV=N7V&Y;>Vo_ohVq@Yw;%4HZ;sxS82bd4& z90)j&d7$MW%|X?Jh=ZvIKTA+ZC`tH9q)2=^L~%&@5d2W;p+-q+Ni|8dWTs@h6qA&m zRG?IW)POX%w1xCp=?dv-84(#*nM9d-nN3+GS)^>1Y_}Y{++n%1a+Px5L}(m-hBYm8}%Yr-_MGzYbWv^=%$XnoNZ)b`NM(Eg&c zU&m7?Q|GI$u&%dmj_#=5LA?`t_w}arW%aT8W%|no>IUHk&kZRJO$=iUKN_(cIT+nC z>N6HH_BAdro;6W22{U8R(zf1?!_M6<-)_lX z-#)><+u?u%!QrJNtK%`pJjZ1xL#HICFU~T~;m+?|cwOKwk6me99bI!=7mpepy>@i? zn9{Ke$J&mI9}hnM){WN<>Gsr})!ozmkq5PhlSjVCx~H{grstxUnb&QvS*QW@I&{)o z*ZZpXI7|zc1RM3y@=5X;^VRab;ydoAFx1)n=%dLsSAGQt{>iy$JMki{rE zln3ewnjMWm*JAiF!I(z>L;e^1zXYfUTnm`RnqzZt5ZrOxV>~DLG4-BsfDlO-2-FTt z3H%Y{81yKZEf^jAK13qqV#sKyacFiJRTwnv<;ndg&zu|x*9*UM3UbQp)XNCri1QJ{ zr%g`ZJHv1WapuEW*|SM!7tT4It31E&{OR+9ktUG^7g#P}FSJK#M5RYlMZ=>%UR1bv z^WtWVcT7X9Y;1Du`X%V4hB&#n8*#*VpZJdn$_Z(gX)dELw*s4<;UZJZvj5FM0V$^-)o&XzA@T zj1IeD44J z+Y8?pgD>4*cE56d)lz$;_Ty{w*Kg{K>t5CC)z`eydQ<&Y{q2(m)rN|9%J0hGE4?rK zp!A{aqcV67lgg*ZpVdBBHflCLYtm_Y(QMdU*J9T4uGOlwvCW~aqy1R>mk#fau});? zTvuS%TKDN5>YkY1UA@VDd;2o`5B3*-QU3CDz;NLGSG%v>gV4drA>7dV@c9wuk>pW< z(Y!JFvFdT-@y`=SCx$2eC)d7Rm|~l{J$+!hbVhgP{jBrs@EmrI`2EuSp84Db#f6uP zHj8~rsHL^#m>+w7+*?sysb6(k9bF4rr(M6Xad4w@(|ogshytVazfN`~eG6=z>`FsL zMMX_TLrqOXPXqoj($Ub+F)}eQFfuSO?P4Nd+mEfx-|rCc9mmYX%(H73&;RCR*EbMJ zN(guq>aUYs#fY^4xE-2;f)euEmk#NaR0stvKnI?kOFHqD)R659LO#}Y7eE0X=t>DT z2|Ujg00FU2&GXYD~H{9lN7Uiar z{J3RCTUnR;t^R5Kp&mmbE%`h&U$go$`pO^$u`&Jy=CXsU2#b_+0j8a0kQ4?=Cg##i z;Qn&Uyz=V7EBn)0XtI>5?oo-^noVV2l+elZQX2i1e^DdmV#Tw%&1MHHR#k&naX-Q& z>@OkcRpRI#Qe3n>p-kmG$1Px73MjBC2Ha(?w?;W~DP1TROf#ZwHM9A$+lH0-RG0Cd zNMKd_ks+0v;NH9eUy3vlp9@+Ukph{mx-pk6N2ucpE44QzFekFo=Ci&pAFbaoUz;*! z2G2CTgU>D(cH+pRZ%_En8d9k0+w`npKq=p-_IiWAwK~mx=Lvqkc>JBLP(`WH3AMH! zh2ncepqwFZ{G|Qfz@9a;r|@qp(A|fE@%LMH-_{dst{uag7hQcjba^hLN%bQg|726a zIobzVRE|_`m$Z<~!l9?+m+aHaKNvECh+=K5T0n1Iz&qvNb6zS=as;W?OOr~dgcDy3|Yu~`jIHqzxU1>shSkRTj3rZz0 zcFPs6%p)1*g6s#LRUKo6D`iO_>ln^-*mF#`U9_gx+w0eTtG3rZ=y;)%jrNaIPm?#* zjfstS@pDKD#KpE^!n~ges4-X*oAHHGb4OkVPnBjLPp$-BisnWqTtQmn(b-SAP5s@o zLSsC=6UIkqD#k7Axiux#<^W}>xe<r-6ifcm-0m=`;@iU8>JTl>oGQiX7batZ`n<+*BoR!9Fbo_1kgIk z&5ixDCHVM?rph{=_v1I^M!e)oKC$&gK8an5X-~^e+q3xL`hkzq%QMa2vTb`Fb_euD zI4GRJKmC}-hUv|?8LtQ%JVkN;qOU&N+r4xstqVD^j7(2cz7Zs~DqZt;$=r4gKmq>N z&7q-Y2XN+fOX}0yv`_QGrYG|Hf4Ji zR{AN`PC9BCB4acr-S*(45{@_~zpp-czMLW0Ncdxgr1^ z2SoonHBGVVoa6Q_mwZ1YDylax`}(CO#OQMk%gK2T3Qo4RH@=h;t?tPqRp;%?`a<^ir2cmI6FMk z-mY`-@sI~$u+#CJ=|JQmu0x0z;}@Q~6wsrH>J#=zrqS$`*dh1G@jMMGc&F45SHfDOsC5B*lLy9qLN$dSAUGw2-ll|{n z7emzs2RK$#savvR0=UcWhf$a2#mO9UT6)*I!k@Betb9m;|9Z=DkA_GjcueHm12r@z z!aooZ?vEyeW=ED+pMgj51uWP{wtk3QDvPU%nkqInxpx1BQVZpsF4|`cLsGn64C!zk zBhJRcE0V6~GRjjIE>ng4Q)Gg12J&t$CRZ^`DE<_Q5bElrxU88tE zLz$GTi`HFoyZbd2by4w3GLS%*WYy<1lY8Q}H=HyQ0t}jXiZ{u z<*@B#kIXbhj<@So;EB{kfY+XN^ao)`0?FG7Gw%s_EVwrAiHHST87{TkWFl9H2^)%W z^7W)RJ8>lduS4M7^lF!>xD>Qm^Jq$8es8}@&bwxxWB2#)%T`{n4HDFeMwG8_N{l?x zthcE8TBx12xv^St=jB6!Y$}4ywQ*(CyH4ZeBSVSCqvZB`GQFe_Uk?=nT;FV;@4QkB0?J3Ek$7l#H>t?vYRZ zP7VkRP_&iQ{7`$hmsQS756_qdfO6NoQ(&7CiQ_HzF~D%j8k7 z+O6i+@yV)qN^SRJi>wY;?k`?+5?02+m`vkER=gsmyyHwDn;5Q;qT2bEdv*^?%B@RY8sfKlG%SjkgI5Bvzsckhz1^OL&#R&0dU_-I1l z)ic3ZLwWUQr;R5xg5<{}vM7l_!>gK454(qu!*k!d(fdk0+KDaq8kD~XPI0~7N3dEH zSZpdE1Wj|S{=&VKgoy(=95=WjaD#%19GiF78Rq6>x{qthjW5!got9>jP&=?DfjMO+ z2yJ@qc6AKs+J`W;yy|uic2~KA!PTNZZr;A{f^fLy_pm>^}ZR zL%CuVp`G*IX`dUq{^M~=@wyLh#^+x@QsFj^65M6Ra-serrTg{ScIK7(0?iNW61^u5 zBx#vMCR~E}=O#AS#2Ll39zMB#C^r82A|ILBL;v6hL z#vC6a`uOGpk9jr3FvFsj)28^QrT~%%R1tw((Zndq>;mWa7AGx|s$#KBkSVd!i$0cf zNrkTst~A^bzfmHGNN0|P|IjV7R82@zw$=U0g{peC#&wxz;E) zg7PmulxQ@Biug-wpK%ps8;VdMtn(G3g6*S-t$D+z<)HgsK86-&E41(qLwb+S&(;?w z(P;|ZPZ8VDp7tG&VX5)(~>Z?Yt21p%cRZHEu7Hzs11+^u3on~Y11a^?_u8y? zy-Zo_<(1oI$~_vnLDy&Y;+xoR$Fe{0;y|(|j3uG>Eu29+Zl0|SI zXS^b#2M`y`IG5d!v_Z{J`-oMl=Ny_zw>kZt)0=`z=G=B7Zg9Q@74>rnn&p$Nxsv&3 zTD;R)WrPI;wSU~cePp3MM?vvKPc_&&frlbb_ zlH#w*6i=#F-hS_L5v};DUHp8!fJKuhqgU>(>GdC+Qk(fpk4Ht)Yfp|kp=uiRLPG;6 zt)SiD5x2m@#D=c0(u~Fp-TXJBrLwD$YA40+mXzqR0H0G>!y`?O(*u(CySi)N;ijMD zl@3Wm=Xv1s91Afi*3POgrHXO~m=|#weyGcD?A<2lMZ)rjdAftX>+nx@wMLvDB(~*` zG8*tqgL6*!L9O6(H_5*_Sx|# z$}t+_8TV%nn&+6-@M_<+Hhd9%V%-8VF%eXEC zaA-0sd^36Mf4JLRg@51IXu>lwh0>cma}<5H7f-uM8Wm+0q#rD`%8L@QVUDplWtuE& zT*Y^`!n0!SMCH(vv0S^?s^KaE2kJ{r**>Q-_xLZfF0^b|n|hgOK8}Ci9@(mY^y~5W zR{cQ|DGvK@i=>Hlc+XRCd7_MO19`p5kMmuPuZXHV3~%FpEHYMFxXB_m8}RBeUBTlq zik@@XrQ%WH?3dI#V4_a?b*pcBN}KgVWWw#UdMjT(XLn@!e2#E+*ZHH7#kFnK4Jkr8 zEyode2o?${(wb3ub@@ea25kL4ysVlMXgP{(MxY8;%G9FW5}8&K6L}s1foE`p^5mu2 zyoplqOCnR<%{v^EI~cWI^~}OJ#DH4akr}ZM8B+1;*!531BH+3l@s$V+jxSuLcZp*D z!tzqJy?Ukj$9&ZEXO4x>{?CE$dA2HrLea5tJd}&A*5_MKguD@9ef2W)!fX}WoeFlX z>O&e9SSk;GWuSZ6@}Lpx2c{HMeT1)qLskNlL#aP4HOAv zMa_hRww9;6m`h7~z7Luknhowh-jJ)WpBaCO@5&lnLZmi(>E*KXV~N6P@Q7MoT;>DG zw5Yh2Y;8~5GuLiORW(wu(pfQ9r(Y~^`SvCuU$`Zl@_Q;*Kqey(oa$0jaBh&$%f{>< zGEwWT#rz-c0t+MJcF*5*>~@Yhooj3P;wcf}nz8O2K3V0ITM3z@<<_qn=@Go)x68zT zTCaSDt3YiMJapQ(zhZqj@W23H;%*~eNRJ}dq)DvRi{X0yTh0gDyH32&EV>U(YD_{Q zx+BZ7k>?ZAWrRf{WM_Hfou;yv8qW{MS$=qYPn)xN(K*jDwA70Gu8vPP5SzZk6~!=i2~ql$qb&UDrM5}+TmJdD zea*17(ri!4#Ik4sOWC#qg|8wl^Dj8?l`JY9t}j*}H6PcKOIIw>pb$EMTwWLaaV&gT zDXQX$(9@HB#Zm7!oMf#p4@sCXJ{0!)W@A?KT>8+53Pf`{-TQm%HpaOwF2?<@!wPH0 z70rh125C$;^x`M7>Zvdzf_src{QMsdx$NmG2{q4!>6;JbCXc+|J)Jj)s_51~(Jhf6 z1+}Hw48NLZB)k@VV?!eg8V&6#(Td6Vx>|BpE31s_q(rdoMRnn!N3KSJOp|#n?3Qac zQo_3$%FYM$x%hOP^Y#NAYDB-#HWnHkv(Ufd%xP1};DSXy^GnvHm6cKxwRyf_lM8i|6i>_s4HJ za~=>3=(B#{CxL%knR1PHjJCPZo`=!v2~A%E9_s$CQm)jN14MU zR;;aVjseCe@M)%eGUcB#m4==j*2#Vc+0 z*f}nL(;C7LJMd>s&8Ol4b|TQzf0(gmwDDz%e4fbo9i3P2?FrMPD{!rS5sbUd7Ilm! z4zqA0?Rt(;`CC4{eZtpv-L~3go-)_ z^xFL#j~mOAtWQVeKMwDEi#EaJwt9De9~Jl>9CFNuPX@zP7*MRiJ6gBO`C~1#h{HKV zu58U_S$4Q;*mK@)B!s(YL+1^=`orle#-7-;=0XSW#QIww8)$u&rt{>!B-~H4>CLQrw=X{f2h)0 zylyGFJ9khKY1}Fqd?suHgXZE2Or@KEh8>Fm1r_=gSd#?6qQmfU2;)B-K_f@KV~> z6SG~F(+B_$U6iuCct@Mj^-J3BB{n;nSQ>3TORN5iwCKuPxFngCixCP#-ImEjV5&93 zK!DgZ(z#*lWq9GMLQds3v}cw5#=!UX6hw;`f41U1{lYBXRHGX#C&D6_wRpUw?;okP zEy{A!>MCG+!*9R&@|6w2`KaA9ovUXlBbQ=Ao&9|AKL%YyEnnlMcvhzx^1~#J77wS5 z@t2P8)3$*^5An;TO3e(<6#9O9R8v?$`jCuW|K)MF%qcqs4R+(YmGDL?NP>*{)78Zw_#J+1OvDJTR zfVR?j9L~9rq$SW2VwPAH5G5r1B}t)TQee7Z@U`XqKBw#m>{EldM&DY6x3aP&uSC+> zr)kfzMi&zLVTm6$vF*y&GxvUOg56Ysz|3P)mf+zb?JVjjM|E&AO3$&rm~4Y|3A~Fa zqmGM`=x%oo(`FnDy>fqWG1$|6^h-lz*FoAB@fckbmxFF7tE^*Xq%$A_@TY6TT+Zkte-k6nw&h zkfAd}Z!kHP-H#&G9HQajR^P9y@+?Qv_bWaVYPiJ?87Or6K?EdcsNm%)r@CuTqNz6A z4)Qi7NUw4oK?E&1sT`}!Hic4OrZIM+X@5LyQ;2S8oZf$ANQY7Qc-2bEk+XJk?;Urs zWL;K1hHk$xa3r?nf=HTu;{Eeg`;2d8S1n{Ah`=57`yCtZ``UD)M{CvCJ|%P>T@An4 z;yJxIqtv@g+BT}g|81X7=O4F^R6>&L&;S(efVzqMr>HXrcS5* zbz<`{N4<#1jc-sKs+usEp)*x1j%Yn_rC{IN0hKCoVYFASg^RaOg1?GBp1IaNaiesz z?$gNe>cg+SkJ3<0pmKSGGCD@hAGqrCoD{(cv;a7 z7-d$2GSPQbFV1~9KkLpaN@+Z3SAXPvAlqR=q|cL0lcmw3!H;&Vq9W1jVm1Z`HVDOi zj!u#9n)ivWy6e{02C-gs8NL>UsNf59wkHG@;bN5~QG0`qrkt=3#FZVsG^{KC;Fchg zId1AzZ{4*hO||qQBA~#ot!aV_4nnm^)n!|En(#}^yh`&L&SB~^_Tu5HHg$HZzesyM z93Q0J2fdzmVb*tiL(5y$2@VT?!Trr>(}8mM{_0crs~>G|sH)v`VH;hf6|%Rv@jz}f zDd3AX>V(Ep@SE|))%g!*z8(XIE7x@cxR;Lcj`rX^c$z+)bqK;lBYcJqJZNI`(f5Yn znNE7#8=NYe@VmHIP5APz%{IP3|8IE@{lEFYi5r<{e0xWwg{>RYZD(H+8t8Meu_f8i z!=aS7zx>gUf_3f4g+^S{=dy3lxgHN8`WJ}69KSf|tEfyW&)MilA%xXG-7+}(?Alo+ zNxpv1g~qcFlqb7GSt$ueXA7<=CCJ#Cl;kwN;W-d`7Pe;{VK5p$fy`iZz)SiDfiTi@SxA;p`&7v$3$UAY$GKGW3 z7Figk!UpZrcD({oDL5UR2Y#GqG=H9#lkUc#slyWF^e$eCjCrgrqAW72(p1-T&{Y-3e$JvNQj94V zs7`mCRbz8?8MJ$Lc*rc|o1S36!-s99gD)lCjbHw5{FW!NjB46jxI5e3V({KVr)ITt zOR9cs8b!{zOna2o)69%Fi$!_Om5z*~-ut?Z?!=BHexl+h0@3TGUusnh-`)&8IFahC zyxuPPkgehst`ssS?|L&QbjEf)-NGd^LK>SZsUbt-vN2R=<+w0 zw{t+h+|m)2S9YNUu}}YB0D~HQ<^KSXc|6l9-PGBT5%Ama_fUUt7zbA9@C;UxOf->r z4+v!+y?U>*aZR2ad}4fvQVACOffjB&yK644w2&1t6XL$>aIq%c-L(~P&fNp8w9uRW z&(}!Xc@gB}uyR9<2(|snd)v0A#Y&!w z&4G24FayGXToLUx7nRDfdU(f?AORE*ct7mDWz$IdC)27^q+=T`#c5|JCIfeN4qEM*jfu++N&3zb|JsVO~UfwVdTqa)!~c z@b0k#@b|C#YJVn-4``0jpf@YazSMqAtYv{F;g^k%eFpX95?C$Oz7W*3hKtQvbUp$# zE!o3f+ji2bhK-ejUv;ht;5)@k4H=QuiVGFum((A;r-hoHVGucUCCdU*KZ$vY)9b0$ zjU)`}!GSBPk)`(o;WSBbP$pCoUA5e|9^PWFnwpXH$5M+PbrKLvnlBJ<-^RAaSLJ)g z>k7q2;sMgbU#9go1&K?O+hNH^GNiM~-A~ZL5Zu@*9 zh^)oTWMaRQBOIIV?9+@(>=+pU9>#BqEUzooiyHiFkFB-`cM8GmQAcMez zN)naVeLEB`Wk|t*=)twO(|UNR(zh-YDK^DpZ~J7OH2H7Kx}wICvbaY2a84<~fgDGD zN$KvN(&lr$2Z3+68~i=gu=H6l<5ReT@tpxW5%kl*!(vLZDu#EA*cDa)4q)j)EPWf( zSu!$|1%qGW0FV!Ew5+$`QsbohW76>B6_#knm~C-kTl&2pn#bGk{6|sgYiv~j3kv~W zocMXyG2LraV1NJC;z(3If$$r&e9AXHIjeS8k*`$rGP1zzJe4MI)^~A;ik)iX80>GQDocrqjiZn8D^YEsSFgdfbs8W1Hy8u;Jon@X? zUOfvt24Bn18%E<*aj0NA{eqGV0fM))lw3-m+AaBbGZTR7UAqYl)kZFYQ{H1#8=(LxH6u6wWWB6_o!)QHe`DAln#C--=nku)A4MPLKhbgN*pVPr zRR9eE0=MUuU39TxEr7A%TS&{QwP0=X7eWq|yCWs38&Y`C8V6CwjRy0ZgK7a;Z@b@B z2o^ovwM5z?K=%)_f)qOdZP`&_V_%!`Uf1{15C7NU)5<)>kP@yePTg($YsimO+${3# zXl>?cxdxi{i}vDSvfR7sVzE%k!Dt z54V+VI@lt^(fU?TNkq=Zg{R6Q`^ep}9c{Gm_f+-Op=9a(Yt$Keuo<9Z7T@MJ(xY!^ z@2vcci+mYa!^#2~n6JZeVm-BZl%^b!83_vMyZ-=C_)oHo17>7J759*=1;Jh)MHmrJ zH<(MKh97#@A5|doC>rh;9KoPAN$0)SYXEydKSeAv&}6XyvXS06QzXYun^CTvl~Ah7 z)7a>5$FjEOGqpX3cXd(3o)xz-ovT(eZHy=;!rB_u1|v5B_Y+8K!~$(V)`uGA^wX>u z02C@x?zV2-Xgvki{{TUzP0}-C-D*~mw8jj+;6A#}(qr<2b^G|#=|BI_;LFapsP{D!lK7jE6_8V(&_R_qK8f>Xtfhy`W40RsnAFhlzT5YkA`jB|@ z1K&jQ8kqx)tEl8TTeymN3`ECp+zpB4uD$gZ959YH9poLScI`MCn-#5ESj!dMx3SOz zb>=ELG3chtTsg)#EXsp{B?I!>QwEYEX_0N*lxM>tePs(E27!+!ot!2N(B%Pn`{B-5~iyp*_7BbBQjTST) z{NGTv*&F`=mjFM~-Tr@>zU5xCu_@EKt44^=vJ6KT!@*Igj`s5(_nY9qj#lw}H63yy zS0S+{w&+v^;^g>=9rd0nP%@;zOnDJNQzfpV=HzhWPnde&rS+DN!#FXqxDR`;4GmR< zu;TTUDx^(&Etx?Y_xnXo5>Fec0zqk-%kgRew|zVY8`KcQTgd+a$ZZdnNw>JtwnLJ< zol0!2Rmw2`0Jh*dx*NAZ8} zP!D1>#DWNaWz_u!i3Y6wF)S6YW7unMc}G=b#BpKI?X6^GY03KPbl{{Xn=!W7`409! zDDSN&gnW;K>ujgW5VOYY!+C-|NMGLJPkfz=e^2XpSvF8iUJH*i!>?sqI+HJss2{hN8 zswoTR5Ada-|IzvUvTnYiTTV8y?F84Gk4d|;r5!ot%-q{v5ZB_ zvf@Rn*j7sn77mfbv{8BfGs?!CD{N1s^ zxgiAEO^GJfF63y;2L2jsQ7BUROTi+R%^@zQZ?CB8-c{Ozh!6CG@3?){{S^^ID=4Z zWMSNmny+VlD^(tg1fLRd0F8U;eUB1&k-TIFQbO?uUO%)_`%#d_{{ScYV6u^QCygId z)?@23#^JWvufvh|8hDW(sm(iX3ZY4~k+8V#J*)gQS}`MuJi%b=ec~xO*QwA9Uzb<^ORXI(oP6HbNPXSwV8PvQvQNHGUSBtO z6|dXFKd+VN(|_E29JOe(UGBCbc0z=1*372gRX%!^9dTmeht#@$YVO6zNxPJbk9AW# z1E{iqM?2NXNRZ=yzLhr3k^nOI(h?LHlZek;y8l1P4aA0d&`p1Bs<(n-XqV!l~X^ z^IL1#xOa1>3Xdi>SLI{!)60ppj>p8zPNvL$6HD?50JnEcM)G5-K9R;La0xWGWdQU&&*AaLTxPUWfV=Xju#og@x0IH4Bz@8=BCdMYiR<($`3*L!9xaY~7>R7W}0(1&AH>YZGYQ!=uiu)(*Du0C(uRT7iLl~t}s5o7uqsY;}x$SMZi zEyAl)fc1W%l)XL1IBmyCHt4}mI~uRCVxrkuLMM(t2F+aDDmLyb_+0b(YA?e`f2MUV znz$_|P-7HPw%;xjZSD7({{RiCb7Y=H)bCE~xY1*XanQ#SsCDy$769r@hnd%vRQ6}V zeG)PC#Yt7-k{ynte4B!zpyy*|X1@ie_b;kfr1dTGlb1SAk0IRzikDk(YlGq%d{n;w z0G&I+V0CFaK2&+wkvxZR5_xO_fPNFh;iuPmzpZ)g(dcr_SIAtmgRK)HKn1n2{8LAC>!$gUw^vZ`jG$A`Lbtn>>E?a z*Do>OM}|nGj5{D#Tad@^4m$VLS!Eh;24dF%LXAOQRy;uLsO~CPoo_C}ZP;4tuWiQ~ zM$2vT!;%4AJky1ga-tljW^u7#5Rb+7+ z_@gBmf!%9rZ^+()5+pJsW$PG;d@{%wc>WdIx4rIe7OH%eCmi6J0njCtNEa)Ah_UA* z_k2GMS5x4*zDYP18%A-k?Gci5} zB->H0yv;kAQRxQ z7h~P~#+3_1Yh)Y<<9bw#EI`{$O{`5L88Nq99W~eWngE9w9c`yR`%nx=6pLb4!+)yO z>?me|+APA$03^;d+G_kmx0i}mwqed`t8(lrBX=uhIWu69# zP#s9n9w7Hxu2QO(P3gJaM=pFwWd2kei_It^J@@db=dX1>hbt#b=&|BJqibZ#7$!#8 z0A?HxylwLS8qZXh$IqEs(vrkou7hx}9}5w{e6)Sf>S8468E_bm0)AbPi4ryOqj3ct zb<>qUw566D=%18^2giyzPmo9hAO=%@BI3t|IiCe}Efz0Vks0tMNn-?gaF*EWr;3tv zHt+bBoR)kR&&uiO{4$8`LfCe;B-D6d$@7uT(0IK0JCmy z2^Q?s@dk<_Ljgd(^ko2PBHvNpZ7V*G4>gr9q>=8qaMmDq4pk~Iku*#^9vl3p?cu_l z2fWnu=Z1+Ol{s&jk|ndtcpMv?H#|nORtd);gkW>U|SoYI{5hoUKhF4 zwJHwHy|C0r6EKDLqd8!vKnU=u)z0EgGSp;|#}sBZTkR!_HohUp!Zg$F)6*d=GAgvi zm=ojJ2gTlRSIwUZu_@6Xb1Zgd5-TP4js#lf#@Z3vQ{3_;@X;79=$%Lfwz0T2*Ow1< zMO+lH4hbG-k_eG52ZW1h-`h$RY7$Qn19JiZ=63M#+m$*-;q&9L3_Q>{k}uu`xKg2Q z=yUnNMo)}d;E-?r6ctUjC}0X|dwJA{yqO{bz*v3nwv7#g_-sz3*iZ%;0d5Zw#CB_H zpdw2dUkTJ(Q{POQ%OYvor$a`Ey!Rk@uig78>0pt)+h`=Q;oEvN8j}Ygs_$i8#@pD@ zqXcXTG2$&NEQ{EuUu{3O2^ohOVlaerh+Lm3ZMjYLV%FzVdi<(Hk)I|5vQ3gSekHNm zb|1Tc4z&K%v9u*~3*^KK%@DX33%mjG6&h~;0CiT3HPIWV$nr{5#9Rg?2>^RR(^{Xj z(PcjdNb_XH$i`;?0T4~0*-oXw2ZgxPozZzY&?Fd;x=Xe^eXNpeZB6Zd{{VeY7Vq?Z zNSIuwSumauGEz1PGB=CDHCHF*UzNWLR+jiiI_@||f;S1Xa)YE=-%JO_dLIiEyf4y1t z{h6%hnPLTjTk})8-%k-y6%o%Xxh6&`tI0*1+1*uvm|{>vviL#V9FT3;JIxk@pZOBV z0w`8s0w0);XK2&->Su^)d`t#5^Fa=vd2xkPe`p>Rr3I~etH!wKv9;uFHx{+P7Pz+r z9i!r=NSA`e8Ycdbu-jml?Z0KuI(w#4NOG2Na(0;5knB#k=WA=-S2OYQky|EAUZE6` z?N!svLb?lEjr+gL^3$GTZ;F0*mE&2}N^T@=OEBGSbvN*zinFuJO)S$Fjhgo*NNt31 z9lCR(LbP~gi9)i4-~oGILB_)W0P3QHaXd_XE3m>mjwDk}iEk}f0mL5L8dx5i1|S2W4#bOEo&r+z{I7~MFMc*0X#ZXpSWsKypwhCQQ?c&e0)!352Q`Ef~jB0Hv8IIk}aO!95A z@!`rw81fgwVxdnT=Gx=~*tJ?%XQRi3g@PoWa=v1;Y+s0R@Ysr(PYo>CDy=3wm&bjg zWdiI)u1fcEq6Q_;P|-J<0G2x;;Oa>`KqJL{qL@ubg%E-hk>X~&?)JKs8f+thE&iHv z(oOVCVU7IK#WAP;oFw=`Cq)hq*r8dQ7MP04^SWf*7>2W z;XE#E#GBY(vbp1~yrN53i#^D{vWte4z}!`VyI$Ab;{9Jxth)J8`IQh_M;q;+UkZ!% zsJ^u|Q6DOj#!HZZXaE2ZIqO8AXtAYu!&_i&Y@^MgqRj zq>u-O=iy(&M5-a3fM$X~ArY)A;cM?;M`^GXeB7zNNn;#|FcoBja;Q6<&ZUyzp59`# z>&Hmv;KxbndHHxtjT}zoJ(TJ+;;qWtXzKYB;bO(~FH*>Xzcj)ug*OCtj(oMO_9z4B zKD!s2BC|mfF!IZ@|w zLc&sPqWlS?L5`7;14v$N7-U&G zSS~W@;TU9P-wtT{6&bn!O7gCFFnG+)Y>t4h4(%295%nYf|l8L?byD&E$ z^`BoViMI6bP$5W&vnaOi3Qz8eil|CoPxW8S10)-n&G%R}zQv~?)T;V#F*XgmgKBpY zN&P#i9H%7o#G)WfDo-WkKCMkG8kFgsA0ilR`ZH;#hfe+^(WOs6NmCVq`0Zk87%R}R zP%oY?oYa6p_fj!G;<{9?<&1DtSlsLO(e^AAx<^Hfl3_p^+xTm0KGb2eJ{B};abZwC z@Dy4wtcF~PEgRj~-GxcLkJOhNC8EgFX8}*hKMz%e!6d}o{Gd^Qt4TIgci+c{XrhAg zV+EUJ(w8HE<7$x1GWABx3lUPQ+s3FG%9}Dd0g@ok_)P)fHV0FL!~r&4rkHwH6} z>28?CnOn?=8-~BtMvOe&KMxK)`#_KG+fz&Az!`FwUN0%0CN>3mQ-x6Q^og^&v#8wO zSp>%10jFriucu<+ra)}hQ!FY#w*hWKmYA{(7s_eoJ04;dC|ik3akVlZ{xcF@my%sC z6U4z#ec`AgoS_5Jv7Ri1k(kAiDDul&!h6jio55+40UXYfq)9?8Vd;b9Gui0Ik{5bE)ie&Wx>I47T8ucyd literal 0 HcmV?d00001 From d68c5ac8eb5ed5aaf21dca245ca77548c29d71cd Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 31 Mar 2025 22:52:59 +0000 Subject: [PATCH 497/979] Bump joda-time:joda-time from 2.13.1 to 2.14.0 Bumps [joda-time:joda-time](https://github.com/JodaOrg/joda-time) from 2.13.1 to 2.14.0. - [Release notes](https://github.com/JodaOrg/joda-time/releases) - [Changelog](https://github.com/JodaOrg/joda-time/blob/main/RELEASE-NOTES.txt) - [Commits](https://github.com/JodaOrg/joda-time/compare/v2.13.1...v2.14.0) --- updated-dependencies: - dependency-name: joda-time:joda-time dependency-version: 2.14.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 886aa3fb36cb..6fdf08157363 100644 --- a/pom.xml +++ b/pom.xml @@ -1542,7 +1542,7 @@ joda-time joda-time - 2.13.1 + 2.14.0 com.sun.mail From 867efc951dbcf5e4b442518a3ac75a44e0b3ad51 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 31 Mar 2025 22:55:01 +0000 Subject: [PATCH 498/979] Bump org.apache.httpcomponents.client5:httpclient5 from 5.4.2 to 5.4.3 Bumps [org.apache.httpcomponents.client5:httpclient5](https://github.com/apache/httpcomponents-client) from 5.4.2 to 5.4.3. - [Changelog](https://github.com/apache/httpcomponents-client/blob/rel/v5.4.3/RELEASE_NOTES.txt) - [Commits](https://github.com/apache/httpcomponents-client/compare/rel/v5.4.2...rel/v5.4.3) --- updated-dependencies: - dependency-name: org.apache.httpcomponents.client5:httpclient5 dependency-version: 5.4.3 dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- dspace-server-webapp/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dspace-server-webapp/pom.xml b/dspace-server-webapp/pom.xml index 4b0e612a7608..4b92e9f40170 100644 --- a/dspace-server-webapp/pom.xml +++ b/dspace-server-webapp/pom.xml @@ -590,7 +590,7 @@ org.apache.httpcomponents.client5 httpclient5 - 5.4.2 + 5.4.3 test From 3fa57d7c0e4da46e745afd995578c5ae9922565b Mon Sep 17 00:00:00 2001 From: Kim Shepherd Date: Mon, 7 Oct 2024 11:42:35 +0200 Subject: [PATCH 499/979] More robust ORCID accessToken init, REST con. usage * Ensure that http client / IO exceptions don't cause a total DSpace startup failure because of unhandled exceptions in Spring service init methods. * Centralise access token retrieval method in factory utils. * Check for NULL rest connector since that can now happen and handle gracefully, with error logging (cherry picked from commit b72344ecfbdd85ce1cb98bfe0ee0d76ebd9439e6) --- .../orcid/Orcidv3SolrAuthorityImpl.java | 93 +++++++++---------- .../impl/OrcidV3AuthorDataProvider.java | 83 +++++++++-------- .../model/factory/OrcidFactoryUtils.java | 55 +++++++++++ .../org/dspace/authority/orcid/MockOrcid.java | 17 +++- 4 files changed, 157 insertions(+), 91 deletions(-) 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 6753a5d113b7..494daa97734a 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 @@ -7,27 +7,22 @@ */ package org.dspace.authority.orcid; -import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; -import java.io.InputStreamReader; import java.net.URLEncoder; import java.util.ArrayList; +import java.util.Collections; import java.util.LinkedList; import java.util.List; import org.apache.commons.lang.StringUtils; -import org.apache.http.HttpResponse; -import org.apache.http.client.HttpClient; -import org.apache.http.client.methods.HttpPost; -import org.apache.http.impl.client.HttpClientBuilder; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.dspace.authority.AuthorityValue; import org.dspace.authority.SolrAuthorityInterface; import org.dspace.external.OrcidRestConnector; import org.dspace.external.provider.orcid.xml.XMLtoBio; -import org.json.JSONObject; +import org.dspace.orcid.model.factory.OrcidFactoryUtils; import org.orcid.jaxb.model.v3.release.common.OrcidIdentifier; import org.orcid.jaxb.model.v3.release.record.Person; import org.orcid.jaxb.model.v3.release.search.Result; @@ -50,6 +45,11 @@ public class Orcidv3SolrAuthorityImpl implements SolrAuthorityInterface { private String accessToken; + /** + * Maximum retries to allow for the access token retrieval + */ + private int maxClientRetries = 3; + public void setOAUTHUrl(String oAUTHUrl) { OAUTHUrl = oAUTHUrl; } @@ -62,46 +62,32 @@ public void setClientSecret(String clientSecret) { this.clientSecret = clientSecret; } + public String getAccessToken() { + return accessToken; + } + + public void setAccessToken(String accessToken) { + this.accessToken = accessToken; + } + /** * Initialize the accessToken that is required for all subsequent calls to ORCID */ public void init() { - if (StringUtils.isBlank(accessToken) - && StringUtils.isNotBlank(clientSecret) - && StringUtils.isNotBlank(clientId) - && StringUtils.isNotBlank(OAUTHUrl)) { - String authenticationParameters = "?client_id=" + clientId + - "&client_secret=" + clientSecret + - "&scope=/read-public&grant_type=client_credentials"; - try { - HttpPost httpPost = new HttpPost(OAUTHUrl + authenticationParameters); - httpPost.addHeader("Accept", "application/json"); - httpPost.addHeader("Content-Type", "application/x-www-form-urlencoded"); - - HttpClient httpClient = HttpClientBuilder.create().build(); - HttpResponse getResponse = httpClient.execute(httpPost); - - JSONObject responseObject = null; - try (InputStream is = getResponse.getEntity().getContent(); - BufferedReader streamReader = new BufferedReader(new InputStreamReader(is, "UTF-8"))) { - String inputStr; - while ((inputStr = streamReader.readLine()) != null && responseObject == null) { - if (inputStr.startsWith("{") && inputStr.endsWith("}") && inputStr.contains("access_token")) { - try { - responseObject = new JSONObject(inputStr); - } catch (Exception e) { - //Not as valid as I'd hoped, move along - responseObject = null; - } - } - } - } - if (responseObject != null && responseObject.has("access_token")) { - accessToken = (String) responseObject.get("access_token"); - } - } catch (Exception e) { - throw new RuntimeException("Error during initialization of the Orcid connector", e); - } + // Initialize access token at spring instantiation. If it fails, the access token will be null rather + // than causing a fatal Spring startup error + initializeAccessToken(); + } + + public void initializeAccessToken() { + // If we have reaches max retries or the access token is already set, return immediately + if (maxClientRetries <= 0 || org.apache.commons.lang3.StringUtils.isNotBlank(accessToken)) { + return; + } + try { + accessToken = OrcidFactoryUtils.retrieveAccessToken(clientId, clientSecret, OAUTHUrl).orElse(null); + } catch (IOException e) { + log.error("Error retrieving ORCID access token, {} retries left", --maxClientRetries); } } @@ -116,7 +102,7 @@ public void setOrcidRestConnector(OrcidRestConnector orcidRestConnector) { */ @Override public List queryAuthorities(String text, int max) { - init(); + initializeAccessToken(); List bios = queryBio(text, max); List result = new ArrayList<>(); for (Person person : bios) { @@ -135,7 +121,7 @@ public List queryAuthorities(String text, int max) { */ @Override public AuthorityValue queryAuthorityID(String id) { - init(); + initializeAccessToken(); Person person = getBio(id); AuthorityValue valueFromPerson = Orcidv3AuthorityValue.create(person); return valueFromPerson; @@ -151,11 +137,14 @@ public Person getBio(String id) { if (!isValid(id)) { return null; } - init(); + if (orcidRestConnector == null) { + log.error("ORCID REST connector is null, returning null Person"); + return null; + } + initializeAccessToken(); InputStream bioDocument = orcidRestConnector.get(id + ((id.endsWith("/person")) ? "" : "/person"), accessToken); XMLtoBio converter = new XMLtoBio(); - Person person = converter.convertSinglePerson(bioDocument); - return person; + return converter.convertSinglePerson(bioDocument); } @@ -167,10 +156,16 @@ public Person getBio(String id) { * @return List */ public List queryBio(String text, int start, int rows) { - init(); if (rows > 100) { throw new IllegalArgumentException("The maximum number of results to retrieve cannot exceed 100."); } + // Check REST connector is initialized + if (orcidRestConnector == null) { + log.error("ORCID REST connector is not initialized, returning empty list"); + return Collections.emptyList(); + } + // Check / init access token + initializeAccessToken(); String searchPath = "search?q=" + URLEncoder.encode(text) + "&start=" + start + "&rows=" + rows; log.debug("queryBio searchPath=" + searchPath + " accessToken=" + accessToken); diff --git a/dspace-api/src/main/java/org/dspace/external/provider/impl/OrcidV3AuthorDataProvider.java b/dspace-api/src/main/java/org/dspace/external/provider/impl/OrcidV3AuthorDataProvider.java index 125da8f7c67b..c7e41171a5bb 100644 --- a/dspace-api/src/main/java/org/dspace/external/provider/impl/OrcidV3AuthorDataProvider.java +++ b/dspace-api/src/main/java/org/dspace/external/provider/impl/OrcidV3AuthorDataProvider.java @@ -7,24 +7,17 @@ */ package org.dspace.external.provider.impl; -import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; -import java.io.InputStreamReader; import java.net.URLEncoder; import java.nio.charset.StandardCharsets; import java.util.Collections; import java.util.LinkedList; import java.util.List; -import java.util.Objects; import java.util.Optional; import java.util.stream.Collectors; import org.apache.commons.lang3.StringUtils; -import org.apache.http.HttpResponse; -import org.apache.http.client.HttpClient; -import org.apache.http.client.methods.HttpPost; -import org.apache.http.impl.client.HttpClientBuilder; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.dspace.content.dto.MetadataValueDTO; @@ -32,7 +25,7 @@ import org.dspace.external.model.ExternalDataObject; import org.dspace.external.provider.AbstractExternalDataProvider; import org.dspace.external.provider.orcid.xml.XMLtoBio; -import org.json.JSONObject; +import org.dspace.orcid.model.factory.OrcidFactoryUtils; import org.orcid.jaxb.model.v3.release.common.OrcidIdentifier; import org.orcid.jaxb.model.v3.release.record.Person; import org.orcid.jaxb.model.v3.release.search.Result; @@ -60,6 +53,11 @@ public class OrcidV3AuthorDataProvider extends AbstractExternalDataProvider { private XMLtoBio converter; + /** + * Maximum retries to allow for the access token retrieval + */ + private int maxClientRetries = 3; + public static final String ORCID_ID_SYNTAX = "\\d{4}-\\d{4}-\\d{4}-(\\d{3}X|\\d{4})"; private static final int MAX_INDEX = 10000; @@ -78,47 +76,37 @@ public OrcidV3AuthorDataProvider() { * @throws java.io.IOException passed through from HTTPclient. */ public void init() throws IOException { - if (StringUtils.isNotBlank(clientSecret) && StringUtils.isNotBlank(clientId) - && StringUtils.isNotBlank(OAUTHUrl)) { - String authenticationParameters = "?client_id=" + clientId + - "&client_secret=" + clientSecret + - "&scope=/read-public&grant_type=client_credentials"; - HttpPost httpPost = new HttpPost(OAUTHUrl + authenticationParameters); - httpPost.addHeader("Accept", "application/json"); - httpPost.addHeader("Content-Type", "application/x-www-form-urlencoded"); - - HttpClient httpClient = HttpClientBuilder.create().build(); - HttpResponse getResponse = httpClient.execute(httpPost); - - JSONObject responseObject = null; - try (InputStream is = getResponse.getEntity().getContent(); - BufferedReader streamReader = new BufferedReader(new InputStreamReader(is, "UTF-8"))) { - String inputStr; - while ((inputStr = streamReader.readLine()) != null && responseObject == null) { - if (inputStr.startsWith("{") && inputStr.endsWith("}") && inputStr.contains("access_token")) { - try { - responseObject = new JSONObject(inputStr); - } catch (Exception e) { - //Not as valid as I'd hoped, move along - responseObject = null; - } - } - } - } - if (responseObject != null && responseObject.has("access_token")) { - accessToken = (String) responseObject.get("access_token"); - } + // Initialize access token at spring instantiation. If it fails, the access token will be null rather + // than causing a fatal Spring startup error + initializeAccessToken(); + } + + /** + * Initialize access token, logging an error and decrementing remaining retries if an IOException is thrown. + * If the optional access token result is empty, set to null instead. + */ + public void initializeAccessToken() { + // If we have reaches max retries or the access token is already set, return immediately + if (maxClientRetries <= 0 || StringUtils.isNotBlank(accessToken)) { + return; + } + try { + accessToken = OrcidFactoryUtils.retrieveAccessToken(clientId, clientSecret, OAUTHUrl).orElse(null); + } catch (IOException e) { + log.error("Error retrieving ORCID access token, {} retries left", --maxClientRetries); } } @Override public Optional getExternalDataObject(String id) { + initializeAccessToken(); Person person = getBio(id); ExternalDataObject externalDataObject = convertToExternalDataObject(person); return Optional.of(externalDataObject); } protected ExternalDataObject convertToExternalDataObject(Person person) { + initializeAccessToken(); ExternalDataObject externalDataObject = new ExternalDataObject(sourceIdentifier); if (person.getName() != null) { String lastName = ""; @@ -167,6 +155,11 @@ public Person getBio(String id) { if (!isValid(id)) { return null; } + if (orcidRestConnector == null) { + log.error("ORCID REST connector is null, returning null ORCID Person Bio"); + return null; + } + initializeAccessToken(); InputStream bioDocument = orcidRestConnector.get(id + ((id.endsWith("/person")) ? "" : "/person"), accessToken); Person person = converter.convertSinglePerson(bioDocument); try { @@ -188,12 +181,18 @@ private boolean isValid(String text) { @Override public List searchExternalDataObjects(String query, int start, int limit) { + initializeAccessToken(); if (limit > 100) { throw new IllegalArgumentException("The maximum number of results to retrieve cannot exceed 100."); } if (start > MAX_INDEX) { throw new IllegalArgumentException("The starting number of results to retrieve cannot exceed 10000."); } + // Check REST connector is initialized + if (orcidRestConnector == null) { + log.error("ORCID REST connector is not initialized, returning empty list"); + return Collections.emptyList(); + } String searchPath = "search?q=" + URLEncoder.encode(query, StandardCharsets.UTF_8) + "&start=" + start @@ -218,9 +217,6 @@ public List searchExternalDataObjects(String query, int star } catch (IOException e) { log.error(e.getMessage(), e); } - if (Objects.isNull(bios)) { - return Collections.emptyList(); - } return bios.stream().map(bio -> convertToExternalDataObject(bio)).collect(Collectors.toList()); } @@ -231,6 +227,11 @@ public boolean supports(String source) { @Override public int getNumberOfResults(String query) { + if (orcidRestConnector == null) { + log.error("ORCID REST connector is null, returning 0"); + return 0; + } + initializeAccessToken(); String searchPath = "search?q=" + URLEncoder.encode(query, StandardCharsets.UTF_8) + "&start=" + 0 + "&rows=" + 0; diff --git a/dspace-api/src/main/java/org/dspace/orcid/model/factory/OrcidFactoryUtils.java b/dspace-api/src/main/java/org/dspace/orcid/model/factory/OrcidFactoryUtils.java index 4b8c1178efeb..38aa611ff3a0 100644 --- a/dspace-api/src/main/java/org/dspace/orcid/model/factory/OrcidFactoryUtils.java +++ b/dspace-api/src/main/java/org/dspace/orcid/model/factory/OrcidFactoryUtils.java @@ -7,10 +7,21 @@ */ package org.dspace.orcid.model.factory; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.nio.charset.StandardCharsets; import java.util.HashMap; import java.util.Map; +import java.util.Optional; import org.apache.commons.lang3.StringUtils; +import org.apache.http.HttpResponse; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClientBuilder; +import org.json.JSONObject; /** * Utility class for Orcid factory classes. This is used to parse the @@ -65,4 +76,48 @@ private static String[] parseConfiguration(String configuration) { return configurations; } + /** + * Retrieve access token from ORCID, given a client ID, client secret and OAuth URL + * + * @param clientId ORCID client ID + * @param clientSecret ORCID client secret + * @param oauthUrl ORCID oauth redirect URL + * @return response object as Optional string + * @throws IOException if any errors are encountered making the connection or reading a response + */ + public static Optional retrieveAccessToken(String clientId, String clientSecret, String oauthUrl) + throws IOException { + if (StringUtils.isNotBlank(clientSecret) && StringUtils.isNotBlank(clientId) + && StringUtils.isNotBlank(oauthUrl)) { + String authenticationParameters = "?client_id=" + clientId + + "&client_secret=" + clientSecret + + "&scope=/read-public&grant_type=client_credentials"; + HttpPost httpPost = new HttpPost(oauthUrl + authenticationParameters); + httpPost.addHeader("Accept", "application/json"); + httpPost.addHeader("Content-Type", "application/x-www-form-urlencoded"); + + HttpResponse response; + try (CloseableHttpClient httpClient = HttpClientBuilder.create().build()) { + response = httpClient.execute(httpPost); + } + JSONObject responseObject = null; + if (response != null && response.getStatusLine().getStatusCode() == 200) { + try (InputStream is = response.getEntity().getContent(); + BufferedReader streamReader = new BufferedReader(new InputStreamReader(is, + StandardCharsets.UTF_8))) { + String inputStr; + while ((inputStr = streamReader.readLine()) != null && responseObject == null) { + if (inputStr.startsWith("{") && inputStr.endsWith("}") && inputStr.contains("access_token")) { + responseObject = new JSONObject(inputStr); + } + } + } + } + if (responseObject != null && responseObject.has("access_token")) { + return Optional.of((String) responseObject.get("access_token")); + } + } + // Return empty by default + return Optional.empty(); + } } diff --git a/dspace-api/src/test/java/org/dspace/authority/orcid/MockOrcid.java b/dspace-api/src/test/java/org/dspace/authority/orcid/MockOrcid.java index 562aa86a585e..82dc3fa5cc12 100644 --- a/dspace-api/src/test/java/org/dspace/authority/orcid/MockOrcid.java +++ b/dspace-api/src/test/java/org/dspace/authority/orcid/MockOrcid.java @@ -26,8 +26,24 @@ */ public class MockOrcid extends Orcidv3SolrAuthorityImpl { + public MockOrcid() { + setupMockConnector(); + setAccessToken("mock-access-token"); + } + @Override public void init() { + // Empty implementation as setup is now done in constructor + } + + @Override + public void initializeAccessToken() { + if (getAccessToken() == null) { + setAccessToken("mock-access-token"); + } + } + + private void setupMockConnector() { OrcidRestConnector orcidRestConnector = Mockito.mock(OrcidRestConnector.class); when(orcidRestConnector.get(ArgumentMatchers.startsWith("search?"), ArgumentMatchers.any())) .thenAnswer(new Answer() { @@ -53,5 +69,4 @@ public InputStream answer(InvocationOnMock invocation) { setOrcidRestConnector(orcidRestConnector); } - } From 1fe697a19ec24b7223346e00c658df1cfec0788b Mon Sep 17 00:00:00 2001 From: Kim Shepherd Date: Sun, 30 Mar 2025 22:30:25 +0200 Subject: [PATCH 500/979] Fix some ORCID mock / test usage (cherry picked from commit 038ddeee9795c75b876ce1ea73db2b908a8c939c) --- .../spring/api/orcid-authority-services.xml | 2 +- .../org/dspace/authority/orcid/MockOrcid.java | 57 ++++++++++++------- .../app/rest/VocabularyRestRepositoryIT.java | 14 +++++ 3 files changed, 50 insertions(+), 23 deletions(-) diff --git a/dspace-api/src/test/data/dspaceFolder/config/spring/api/orcid-authority-services.xml b/dspace-api/src/test/data/dspaceFolder/config/spring/api/orcid-authority-services.xml index 4a73b215cd4b..3e38055b678a 100644 --- a/dspace-api/src/test/data/dspaceFolder/config/spring/api/orcid-authority-services.xml +++ b/dspace-api/src/test/data/dspaceFolder/config/spring/api/orcid-authority-services.xml @@ -16,7 +16,7 @@ - + diff --git a/dspace-api/src/test/java/org/dspace/authority/orcid/MockOrcid.java b/dspace-api/src/test/java/org/dspace/authority/orcid/MockOrcid.java index 82dc3fa5cc12..511df79f1e50 100644 --- a/dspace-api/src/test/java/org/dspace/authority/orcid/MockOrcid.java +++ b/dspace-api/src/test/java/org/dspace/authority/orcid/MockOrcid.java @@ -26,41 +26,47 @@ */ public class MockOrcid extends Orcidv3SolrAuthorityImpl { - public MockOrcid() { - setupMockConnector(); - setAccessToken("mock-access-token"); - } + OrcidRestConnector orcidRestConnector; @Override public void init() { - // Empty implementation as setup is now done in constructor + initializeAccessToken(); + orcidRestConnector = Mockito.mock(OrcidRestConnector.class); } - @Override - public void initializeAccessToken() { - if (getAccessToken() == null) { - setAccessToken("mock-access-token"); - } - } - - private void setupMockConnector() { - OrcidRestConnector orcidRestConnector = Mockito.mock(OrcidRestConnector.class); + /** + * Call this to set up mocking for any test classes that need it. We don't set it in init() + * or other AbstractIntegrationTest implementations will complain of unnecessary Mockito stubbing + */ + public void setupNoResultsSearch() { when(orcidRestConnector.get(ArgumentMatchers.startsWith("search?"), ArgumentMatchers.any())) - .thenAnswer(new Answer() { - @Override - public InputStream answer(InvocationOnMock invocation) { - return this.getClass().getResourceAsStream("orcid-search-noresults.xml"); - } - }); + .thenAnswer(new Answer() { + @Override + public InputStream answer(InvocationOnMock invocation) { + return this.getClass().getResourceAsStream("orcid-search-noresults.xml"); + } + }); + } + /** + * Call this to set up mocking for any test classes that need it. We don't set it in init() + * or other AbstractIntegrationTest implementations will complain of unnecessary Mockito stubbing + */ + public void setupSingleSearch() { when(orcidRestConnector.get(ArgumentMatchers.startsWith("search?q=Bollini"), ArgumentMatchers.any())) - .thenAnswer(new Answer() { + .thenAnswer(new Answer() { @Override public InputStream answer(InvocationOnMock invocation) { return this.getClass().getResourceAsStream("orcid-search.xml"); } }); + } + /** + * Call this to set up mocking for any test classes that need it. We don't set it in init() + * or other AbstractIntegrationTest implementations will complain of unnecessary Mockito stubbing + */ + public void setupSearchWithResults() { when(orcidRestConnector.get(ArgumentMatchers.endsWith("/person"), ArgumentMatchers.any())) - .thenAnswer(new Answer() { + .thenAnswer(new Answer() { @Override public InputStream answer(InvocationOnMock invocation) { return this.getClass().getResourceAsStream("orcid-person-record.xml"); @@ -69,4 +75,11 @@ public InputStream answer(InvocationOnMock invocation) { setOrcidRestConnector(orcidRestConnector); } + + @Override + public void initializeAccessToken() { + if (getAccessToken() == null) { + setAccessToken("mock-access-token"); + } + } } diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/VocabularyRestRepositoryIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/VocabularyRestRepositoryIT.java index 30890d7ef838..7a3bc738eb66 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/VocabularyRestRepositoryIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/VocabularyRestRepositoryIT.java @@ -22,6 +22,7 @@ import org.dspace.authority.AuthorityValueServiceImpl; import org.dspace.authority.PersonAuthorityValue; import org.dspace.authority.factory.AuthorityServiceFactory; +import org.dspace.authority.orcid.MockOrcid; import org.dspace.builder.CollectionBuilder; import org.dspace.builder.CommunityBuilder; import org.dspace.content.Collection; @@ -29,11 +30,13 @@ import org.dspace.content.authority.service.ChoiceAuthorityService; import org.dspace.core.service.PluginService; import org.dspace.services.ConfigurationService; +import org.dspace.services.factory.DSpaceServicesFactory; import org.hamcrest.Matchers; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationContext; /** * This class handles all Authority related IT. It alters some config to run the tests, but it gets cleared again @@ -56,6 +59,17 @@ public class VocabularyRestRepositoryIT extends AbstractControllerIntegrationTes @Before public void setup() throws Exception { super.setUp(); + + // Explicitly set stubbing for the MockOrcid class. We don't do it in the init() or constructor + // of the MockOrcid class itself or Mockito will complain of unnecessary stubbing in certain other + // AbstractIntegrationTest implementations (depending on how config is (re)loaded) + ApplicationContext applicationContext = DSpaceServicesFactory.getInstance() + .getServiceManager().getApplicationContext(); + MockOrcid mockOrcid = applicationContext.getBean(MockOrcid.class); + mockOrcid.setupNoResultsSearch(); + mockOrcid.setupSingleSearch(); + mockOrcid.setupSearchWithResults(); + configurationService.setProperty("plugin.named.org.dspace.content.authority.ChoiceAuthority", new String[] { "org.dspace.content.authority.SolrAuthority = SolrAuthorAuthority", From 8e2dc93cc8e6fc371768ea8518397160398e7cac Mon Sep 17 00:00:00 2001 From: Kim Shepherd Date: Mon, 7 Oct 2024 11:42:35 +0200 Subject: [PATCH 501/979] More robust ORCID accessToken init, REST con. usage * Ensure that http client / IO exceptions don't cause a total DSpace startup failure because of unhandled exceptions in Spring service init methods. * Centralise access token retrieval method in factory utils. * Check for NULL rest connector since that can now happen and handle gracefully, with error logging (cherry picked from commit b72344ecfbdd85ce1cb98bfe0ee0d76ebd9439e6) --- .../orcid/Orcidv3SolrAuthorityImpl.java | 93 +++++++++---------- .../impl/OrcidV3AuthorDataProvider.java | 83 +++++++++-------- .../model/factory/OrcidFactoryUtils.java | 55 +++++++++++ .../org/dspace/authority/orcid/MockOrcid.java | 17 +++- 4 files changed, 157 insertions(+), 91 deletions(-) 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 6753a5d113b7..494daa97734a 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 @@ -7,27 +7,22 @@ */ package org.dspace.authority.orcid; -import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; -import java.io.InputStreamReader; import java.net.URLEncoder; import java.util.ArrayList; +import java.util.Collections; import java.util.LinkedList; import java.util.List; import org.apache.commons.lang.StringUtils; -import org.apache.http.HttpResponse; -import org.apache.http.client.HttpClient; -import org.apache.http.client.methods.HttpPost; -import org.apache.http.impl.client.HttpClientBuilder; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.dspace.authority.AuthorityValue; import org.dspace.authority.SolrAuthorityInterface; import org.dspace.external.OrcidRestConnector; import org.dspace.external.provider.orcid.xml.XMLtoBio; -import org.json.JSONObject; +import org.dspace.orcid.model.factory.OrcidFactoryUtils; import org.orcid.jaxb.model.v3.release.common.OrcidIdentifier; import org.orcid.jaxb.model.v3.release.record.Person; import org.orcid.jaxb.model.v3.release.search.Result; @@ -50,6 +45,11 @@ public class Orcidv3SolrAuthorityImpl implements SolrAuthorityInterface { private String accessToken; + /** + * Maximum retries to allow for the access token retrieval + */ + private int maxClientRetries = 3; + public void setOAUTHUrl(String oAUTHUrl) { OAUTHUrl = oAUTHUrl; } @@ -62,46 +62,32 @@ public void setClientSecret(String clientSecret) { this.clientSecret = clientSecret; } + public String getAccessToken() { + return accessToken; + } + + public void setAccessToken(String accessToken) { + this.accessToken = accessToken; + } + /** * Initialize the accessToken that is required for all subsequent calls to ORCID */ public void init() { - if (StringUtils.isBlank(accessToken) - && StringUtils.isNotBlank(clientSecret) - && StringUtils.isNotBlank(clientId) - && StringUtils.isNotBlank(OAUTHUrl)) { - String authenticationParameters = "?client_id=" + clientId + - "&client_secret=" + clientSecret + - "&scope=/read-public&grant_type=client_credentials"; - try { - HttpPost httpPost = new HttpPost(OAUTHUrl + authenticationParameters); - httpPost.addHeader("Accept", "application/json"); - httpPost.addHeader("Content-Type", "application/x-www-form-urlencoded"); - - HttpClient httpClient = HttpClientBuilder.create().build(); - HttpResponse getResponse = httpClient.execute(httpPost); - - JSONObject responseObject = null; - try (InputStream is = getResponse.getEntity().getContent(); - BufferedReader streamReader = new BufferedReader(new InputStreamReader(is, "UTF-8"))) { - String inputStr; - while ((inputStr = streamReader.readLine()) != null && responseObject == null) { - if (inputStr.startsWith("{") && inputStr.endsWith("}") && inputStr.contains("access_token")) { - try { - responseObject = new JSONObject(inputStr); - } catch (Exception e) { - //Not as valid as I'd hoped, move along - responseObject = null; - } - } - } - } - if (responseObject != null && responseObject.has("access_token")) { - accessToken = (String) responseObject.get("access_token"); - } - } catch (Exception e) { - throw new RuntimeException("Error during initialization of the Orcid connector", e); - } + // Initialize access token at spring instantiation. If it fails, the access token will be null rather + // than causing a fatal Spring startup error + initializeAccessToken(); + } + + public void initializeAccessToken() { + // If we have reaches max retries or the access token is already set, return immediately + if (maxClientRetries <= 0 || org.apache.commons.lang3.StringUtils.isNotBlank(accessToken)) { + return; + } + try { + accessToken = OrcidFactoryUtils.retrieveAccessToken(clientId, clientSecret, OAUTHUrl).orElse(null); + } catch (IOException e) { + log.error("Error retrieving ORCID access token, {} retries left", --maxClientRetries); } } @@ -116,7 +102,7 @@ public void setOrcidRestConnector(OrcidRestConnector orcidRestConnector) { */ @Override public List queryAuthorities(String text, int max) { - init(); + initializeAccessToken(); List bios = queryBio(text, max); List result = new ArrayList<>(); for (Person person : bios) { @@ -135,7 +121,7 @@ public List queryAuthorities(String text, int max) { */ @Override public AuthorityValue queryAuthorityID(String id) { - init(); + initializeAccessToken(); Person person = getBio(id); AuthorityValue valueFromPerson = Orcidv3AuthorityValue.create(person); return valueFromPerson; @@ -151,11 +137,14 @@ public Person getBio(String id) { if (!isValid(id)) { return null; } - init(); + if (orcidRestConnector == null) { + log.error("ORCID REST connector is null, returning null Person"); + return null; + } + initializeAccessToken(); InputStream bioDocument = orcidRestConnector.get(id + ((id.endsWith("/person")) ? "" : "/person"), accessToken); XMLtoBio converter = new XMLtoBio(); - Person person = converter.convertSinglePerson(bioDocument); - return person; + return converter.convertSinglePerson(bioDocument); } @@ -167,10 +156,16 @@ public Person getBio(String id) { * @return List */ public List queryBio(String text, int start, int rows) { - init(); if (rows > 100) { throw new IllegalArgumentException("The maximum number of results to retrieve cannot exceed 100."); } + // Check REST connector is initialized + if (orcidRestConnector == null) { + log.error("ORCID REST connector is not initialized, returning empty list"); + return Collections.emptyList(); + } + // Check / init access token + initializeAccessToken(); String searchPath = "search?q=" + URLEncoder.encode(text) + "&start=" + start + "&rows=" + rows; log.debug("queryBio searchPath=" + searchPath + " accessToken=" + accessToken); diff --git a/dspace-api/src/main/java/org/dspace/external/provider/impl/OrcidV3AuthorDataProvider.java b/dspace-api/src/main/java/org/dspace/external/provider/impl/OrcidV3AuthorDataProvider.java index 125da8f7c67b..c7e41171a5bb 100644 --- a/dspace-api/src/main/java/org/dspace/external/provider/impl/OrcidV3AuthorDataProvider.java +++ b/dspace-api/src/main/java/org/dspace/external/provider/impl/OrcidV3AuthorDataProvider.java @@ -7,24 +7,17 @@ */ package org.dspace.external.provider.impl; -import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; -import java.io.InputStreamReader; import java.net.URLEncoder; import java.nio.charset.StandardCharsets; import java.util.Collections; import java.util.LinkedList; import java.util.List; -import java.util.Objects; import java.util.Optional; import java.util.stream.Collectors; import org.apache.commons.lang3.StringUtils; -import org.apache.http.HttpResponse; -import org.apache.http.client.HttpClient; -import org.apache.http.client.methods.HttpPost; -import org.apache.http.impl.client.HttpClientBuilder; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.dspace.content.dto.MetadataValueDTO; @@ -32,7 +25,7 @@ import org.dspace.external.model.ExternalDataObject; import org.dspace.external.provider.AbstractExternalDataProvider; import org.dspace.external.provider.orcid.xml.XMLtoBio; -import org.json.JSONObject; +import org.dspace.orcid.model.factory.OrcidFactoryUtils; import org.orcid.jaxb.model.v3.release.common.OrcidIdentifier; import org.orcid.jaxb.model.v3.release.record.Person; import org.orcid.jaxb.model.v3.release.search.Result; @@ -60,6 +53,11 @@ public class OrcidV3AuthorDataProvider extends AbstractExternalDataProvider { private XMLtoBio converter; + /** + * Maximum retries to allow for the access token retrieval + */ + private int maxClientRetries = 3; + public static final String ORCID_ID_SYNTAX = "\\d{4}-\\d{4}-\\d{4}-(\\d{3}X|\\d{4})"; private static final int MAX_INDEX = 10000; @@ -78,47 +76,37 @@ public OrcidV3AuthorDataProvider() { * @throws java.io.IOException passed through from HTTPclient. */ public void init() throws IOException { - if (StringUtils.isNotBlank(clientSecret) && StringUtils.isNotBlank(clientId) - && StringUtils.isNotBlank(OAUTHUrl)) { - String authenticationParameters = "?client_id=" + clientId + - "&client_secret=" + clientSecret + - "&scope=/read-public&grant_type=client_credentials"; - HttpPost httpPost = new HttpPost(OAUTHUrl + authenticationParameters); - httpPost.addHeader("Accept", "application/json"); - httpPost.addHeader("Content-Type", "application/x-www-form-urlencoded"); - - HttpClient httpClient = HttpClientBuilder.create().build(); - HttpResponse getResponse = httpClient.execute(httpPost); - - JSONObject responseObject = null; - try (InputStream is = getResponse.getEntity().getContent(); - BufferedReader streamReader = new BufferedReader(new InputStreamReader(is, "UTF-8"))) { - String inputStr; - while ((inputStr = streamReader.readLine()) != null && responseObject == null) { - if (inputStr.startsWith("{") && inputStr.endsWith("}") && inputStr.contains("access_token")) { - try { - responseObject = new JSONObject(inputStr); - } catch (Exception e) { - //Not as valid as I'd hoped, move along - responseObject = null; - } - } - } - } - if (responseObject != null && responseObject.has("access_token")) { - accessToken = (String) responseObject.get("access_token"); - } + // Initialize access token at spring instantiation. If it fails, the access token will be null rather + // than causing a fatal Spring startup error + initializeAccessToken(); + } + + /** + * Initialize access token, logging an error and decrementing remaining retries if an IOException is thrown. + * If the optional access token result is empty, set to null instead. + */ + public void initializeAccessToken() { + // If we have reaches max retries or the access token is already set, return immediately + if (maxClientRetries <= 0 || StringUtils.isNotBlank(accessToken)) { + return; + } + try { + accessToken = OrcidFactoryUtils.retrieveAccessToken(clientId, clientSecret, OAUTHUrl).orElse(null); + } catch (IOException e) { + log.error("Error retrieving ORCID access token, {} retries left", --maxClientRetries); } } @Override public Optional getExternalDataObject(String id) { + initializeAccessToken(); Person person = getBio(id); ExternalDataObject externalDataObject = convertToExternalDataObject(person); return Optional.of(externalDataObject); } protected ExternalDataObject convertToExternalDataObject(Person person) { + initializeAccessToken(); ExternalDataObject externalDataObject = new ExternalDataObject(sourceIdentifier); if (person.getName() != null) { String lastName = ""; @@ -167,6 +155,11 @@ public Person getBio(String id) { if (!isValid(id)) { return null; } + if (orcidRestConnector == null) { + log.error("ORCID REST connector is null, returning null ORCID Person Bio"); + return null; + } + initializeAccessToken(); InputStream bioDocument = orcidRestConnector.get(id + ((id.endsWith("/person")) ? "" : "/person"), accessToken); Person person = converter.convertSinglePerson(bioDocument); try { @@ -188,12 +181,18 @@ private boolean isValid(String text) { @Override public List searchExternalDataObjects(String query, int start, int limit) { + initializeAccessToken(); if (limit > 100) { throw new IllegalArgumentException("The maximum number of results to retrieve cannot exceed 100."); } if (start > MAX_INDEX) { throw new IllegalArgumentException("The starting number of results to retrieve cannot exceed 10000."); } + // Check REST connector is initialized + if (orcidRestConnector == null) { + log.error("ORCID REST connector is not initialized, returning empty list"); + return Collections.emptyList(); + } String searchPath = "search?q=" + URLEncoder.encode(query, StandardCharsets.UTF_8) + "&start=" + start @@ -218,9 +217,6 @@ public List searchExternalDataObjects(String query, int star } catch (IOException e) { log.error(e.getMessage(), e); } - if (Objects.isNull(bios)) { - return Collections.emptyList(); - } return bios.stream().map(bio -> convertToExternalDataObject(bio)).collect(Collectors.toList()); } @@ -231,6 +227,11 @@ public boolean supports(String source) { @Override public int getNumberOfResults(String query) { + if (orcidRestConnector == null) { + log.error("ORCID REST connector is null, returning 0"); + return 0; + } + initializeAccessToken(); String searchPath = "search?q=" + URLEncoder.encode(query, StandardCharsets.UTF_8) + "&start=" + 0 + "&rows=" + 0; diff --git a/dspace-api/src/main/java/org/dspace/orcid/model/factory/OrcidFactoryUtils.java b/dspace-api/src/main/java/org/dspace/orcid/model/factory/OrcidFactoryUtils.java index 4b8c1178efeb..38aa611ff3a0 100644 --- a/dspace-api/src/main/java/org/dspace/orcid/model/factory/OrcidFactoryUtils.java +++ b/dspace-api/src/main/java/org/dspace/orcid/model/factory/OrcidFactoryUtils.java @@ -7,10 +7,21 @@ */ package org.dspace.orcid.model.factory; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.nio.charset.StandardCharsets; import java.util.HashMap; import java.util.Map; +import java.util.Optional; import org.apache.commons.lang3.StringUtils; +import org.apache.http.HttpResponse; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClientBuilder; +import org.json.JSONObject; /** * Utility class for Orcid factory classes. This is used to parse the @@ -65,4 +76,48 @@ private static String[] parseConfiguration(String configuration) { return configurations; } + /** + * Retrieve access token from ORCID, given a client ID, client secret and OAuth URL + * + * @param clientId ORCID client ID + * @param clientSecret ORCID client secret + * @param oauthUrl ORCID oauth redirect URL + * @return response object as Optional string + * @throws IOException if any errors are encountered making the connection or reading a response + */ + public static Optional retrieveAccessToken(String clientId, String clientSecret, String oauthUrl) + throws IOException { + if (StringUtils.isNotBlank(clientSecret) && StringUtils.isNotBlank(clientId) + && StringUtils.isNotBlank(oauthUrl)) { + String authenticationParameters = "?client_id=" + clientId + + "&client_secret=" + clientSecret + + "&scope=/read-public&grant_type=client_credentials"; + HttpPost httpPost = new HttpPost(oauthUrl + authenticationParameters); + httpPost.addHeader("Accept", "application/json"); + httpPost.addHeader("Content-Type", "application/x-www-form-urlencoded"); + + HttpResponse response; + try (CloseableHttpClient httpClient = HttpClientBuilder.create().build()) { + response = httpClient.execute(httpPost); + } + JSONObject responseObject = null; + if (response != null && response.getStatusLine().getStatusCode() == 200) { + try (InputStream is = response.getEntity().getContent(); + BufferedReader streamReader = new BufferedReader(new InputStreamReader(is, + StandardCharsets.UTF_8))) { + String inputStr; + while ((inputStr = streamReader.readLine()) != null && responseObject == null) { + if (inputStr.startsWith("{") && inputStr.endsWith("}") && inputStr.contains("access_token")) { + responseObject = new JSONObject(inputStr); + } + } + } + } + if (responseObject != null && responseObject.has("access_token")) { + return Optional.of((String) responseObject.get("access_token")); + } + } + // Return empty by default + return Optional.empty(); + } } diff --git a/dspace-api/src/test/java/org/dspace/authority/orcid/MockOrcid.java b/dspace-api/src/test/java/org/dspace/authority/orcid/MockOrcid.java index 562aa86a585e..82dc3fa5cc12 100644 --- a/dspace-api/src/test/java/org/dspace/authority/orcid/MockOrcid.java +++ b/dspace-api/src/test/java/org/dspace/authority/orcid/MockOrcid.java @@ -26,8 +26,24 @@ */ public class MockOrcid extends Orcidv3SolrAuthorityImpl { + public MockOrcid() { + setupMockConnector(); + setAccessToken("mock-access-token"); + } + @Override public void init() { + // Empty implementation as setup is now done in constructor + } + + @Override + public void initializeAccessToken() { + if (getAccessToken() == null) { + setAccessToken("mock-access-token"); + } + } + + private void setupMockConnector() { OrcidRestConnector orcidRestConnector = Mockito.mock(OrcidRestConnector.class); when(orcidRestConnector.get(ArgumentMatchers.startsWith("search?"), ArgumentMatchers.any())) .thenAnswer(new Answer() { @@ -53,5 +69,4 @@ public InputStream answer(InvocationOnMock invocation) { setOrcidRestConnector(orcidRestConnector); } - } From d9ee07ba4d76c0b8957fd9d23b174538b4afea98 Mon Sep 17 00:00:00 2001 From: Kim Shepherd Date: Sun, 30 Mar 2025 22:30:25 +0200 Subject: [PATCH 502/979] Fix some ORCID mock / test usage (cherry picked from commit 038ddeee9795c75b876ce1ea73db2b908a8c939c) --- .../spring/api/orcid-authority-services.xml | 2 +- .../org/dspace/authority/orcid/MockOrcid.java | 57 ++++++++++++------- .../app/rest/VocabularyRestRepositoryIT.java | 14 +++++ 3 files changed, 50 insertions(+), 23 deletions(-) diff --git a/dspace-api/src/test/data/dspaceFolder/config/spring/api/orcid-authority-services.xml b/dspace-api/src/test/data/dspaceFolder/config/spring/api/orcid-authority-services.xml index 4a73b215cd4b..3e38055b678a 100644 --- a/dspace-api/src/test/data/dspaceFolder/config/spring/api/orcid-authority-services.xml +++ b/dspace-api/src/test/data/dspaceFolder/config/spring/api/orcid-authority-services.xml @@ -16,7 +16,7 @@ - + diff --git a/dspace-api/src/test/java/org/dspace/authority/orcid/MockOrcid.java b/dspace-api/src/test/java/org/dspace/authority/orcid/MockOrcid.java index 82dc3fa5cc12..511df79f1e50 100644 --- a/dspace-api/src/test/java/org/dspace/authority/orcid/MockOrcid.java +++ b/dspace-api/src/test/java/org/dspace/authority/orcid/MockOrcid.java @@ -26,41 +26,47 @@ */ public class MockOrcid extends Orcidv3SolrAuthorityImpl { - public MockOrcid() { - setupMockConnector(); - setAccessToken("mock-access-token"); - } + OrcidRestConnector orcidRestConnector; @Override public void init() { - // Empty implementation as setup is now done in constructor + initializeAccessToken(); + orcidRestConnector = Mockito.mock(OrcidRestConnector.class); } - @Override - public void initializeAccessToken() { - if (getAccessToken() == null) { - setAccessToken("mock-access-token"); - } - } - - private void setupMockConnector() { - OrcidRestConnector orcidRestConnector = Mockito.mock(OrcidRestConnector.class); + /** + * Call this to set up mocking for any test classes that need it. We don't set it in init() + * or other AbstractIntegrationTest implementations will complain of unnecessary Mockito stubbing + */ + public void setupNoResultsSearch() { when(orcidRestConnector.get(ArgumentMatchers.startsWith("search?"), ArgumentMatchers.any())) - .thenAnswer(new Answer() { - @Override - public InputStream answer(InvocationOnMock invocation) { - return this.getClass().getResourceAsStream("orcid-search-noresults.xml"); - } - }); + .thenAnswer(new Answer() { + @Override + public InputStream answer(InvocationOnMock invocation) { + return this.getClass().getResourceAsStream("orcid-search-noresults.xml"); + } + }); + } + /** + * Call this to set up mocking for any test classes that need it. We don't set it in init() + * or other AbstractIntegrationTest implementations will complain of unnecessary Mockito stubbing + */ + public void setupSingleSearch() { when(orcidRestConnector.get(ArgumentMatchers.startsWith("search?q=Bollini"), ArgumentMatchers.any())) - .thenAnswer(new Answer() { + .thenAnswer(new Answer() { @Override public InputStream answer(InvocationOnMock invocation) { return this.getClass().getResourceAsStream("orcid-search.xml"); } }); + } + /** + * Call this to set up mocking for any test classes that need it. We don't set it in init() + * or other AbstractIntegrationTest implementations will complain of unnecessary Mockito stubbing + */ + public void setupSearchWithResults() { when(orcidRestConnector.get(ArgumentMatchers.endsWith("/person"), ArgumentMatchers.any())) - .thenAnswer(new Answer() { + .thenAnswer(new Answer() { @Override public InputStream answer(InvocationOnMock invocation) { return this.getClass().getResourceAsStream("orcid-person-record.xml"); @@ -69,4 +75,11 @@ public InputStream answer(InvocationOnMock invocation) { setOrcidRestConnector(orcidRestConnector); } + + @Override + public void initializeAccessToken() { + if (getAccessToken() == null) { + setAccessToken("mock-access-token"); + } + } } diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/VocabularyRestRepositoryIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/VocabularyRestRepositoryIT.java index 30890d7ef838..7a3bc738eb66 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/VocabularyRestRepositoryIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/VocabularyRestRepositoryIT.java @@ -22,6 +22,7 @@ import org.dspace.authority.AuthorityValueServiceImpl; import org.dspace.authority.PersonAuthorityValue; import org.dspace.authority.factory.AuthorityServiceFactory; +import org.dspace.authority.orcid.MockOrcid; import org.dspace.builder.CollectionBuilder; import org.dspace.builder.CommunityBuilder; import org.dspace.content.Collection; @@ -29,11 +30,13 @@ import org.dspace.content.authority.service.ChoiceAuthorityService; import org.dspace.core.service.PluginService; import org.dspace.services.ConfigurationService; +import org.dspace.services.factory.DSpaceServicesFactory; import org.hamcrest.Matchers; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationContext; /** * This class handles all Authority related IT. It alters some config to run the tests, but it gets cleared again @@ -56,6 +59,17 @@ public class VocabularyRestRepositoryIT extends AbstractControllerIntegrationTes @Before public void setup() throws Exception { super.setUp(); + + // Explicitly set stubbing for the MockOrcid class. We don't do it in the init() or constructor + // of the MockOrcid class itself or Mockito will complain of unnecessary stubbing in certain other + // AbstractIntegrationTest implementations (depending on how config is (re)loaded) + ApplicationContext applicationContext = DSpaceServicesFactory.getInstance() + .getServiceManager().getApplicationContext(); + MockOrcid mockOrcid = applicationContext.getBean(MockOrcid.class); + mockOrcid.setupNoResultsSearch(); + mockOrcid.setupSingleSearch(); + mockOrcid.setupSearchWithResults(); + configurationService.setProperty("plugin.named.org.dspace.content.authority.ChoiceAuthority", new String[] { "org.dspace.content.authority.SolrAuthority = SolrAuthorAuthority", From 70017e1c8e8c07d1673883db1f777d7d0d27312f Mon Sep 17 00:00:00 2001 From: Jens Vannerum Date: Tue, 11 Mar 2025 10:58:08 +0100 Subject: [PATCH 503/979] Implement a SEOHealthIndicator which verifies all relevant parameters for SEO are ok (cherry picked from commit 4bd8a24ca75f6d2e6384e850b45c96c4f1229f02) --- .../configuration/ActuatorConfiguration.java | 7 ++ .../app/rest/health/SEOHealthIndicator.java | 77 +++++++++++++++++++ 2 files changed, 84 insertions(+) create mode 100644 dspace-server-webapp/src/main/java/org/dspace/app/rest/health/SEOHealthIndicator.java diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/configuration/ActuatorConfiguration.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/configuration/ActuatorConfiguration.java index 08a7e9aec8e9..670cff84581e 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/configuration/ActuatorConfiguration.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/configuration/ActuatorConfiguration.java @@ -14,6 +14,7 @@ import org.apache.solr.client.solrj.SolrServerException; import org.dspace.app.rest.DiscoverableEndpointsService; import org.dspace.app.rest.health.GeoIpHealthIndicator; +import org.dspace.app.rest.health.SEOHealthIndicator; import org.dspace.app.rest.health.SolrHealthIndicator; import org.dspace.authority.AuthoritySolrServiceImpl; import org.dspace.discovery.SolrSearchCore; @@ -82,6 +83,12 @@ public SolrHealthIndicator solrOaiCoreHealthIndicator(SolrServerResolver solrSer return new SolrHealthIndicator(solrServerResolver.getServer()); } + @Bean + @ConditionalOnEnabledHealthIndicator("seo") + public SEOHealthIndicator seoHealthIndicator() { + return new SEOHealthIndicator(); + } + @Bean @ConditionalOnEnabledHealthIndicator("geoIp") public GeoIpHealthIndicator geoIpHealthIndicator() { diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/health/SEOHealthIndicator.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/health/SEOHealthIndicator.java new file mode 100644 index 000000000000..d936fce635e6 --- /dev/null +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/health/SEOHealthIndicator.java @@ -0,0 +1,77 @@ +/** + * The contents of this file are subject to the license and copyright + * detailed in the LICENSE and NOTICE files at the root of the source + * tree and available online at + * + * http://www.dspace.org/license/ + */ +package org.dspace.app.rest.health; + +import org.apache.commons.lang3.StringUtils; +import org.dspace.services.ConfigurationService; +import org.dspace.services.factory.DSpaceServicesFactory; +import org.springframework.boot.actuate.health.AbstractHealthIndicator; +import org.springframework.boot.actuate.health.Health; +import org.springframework.web.client.RestTemplate; + +/** + * Implementation of {@link org.springframework.boot.actuate.health.HealthIndicator} that verifies if the SEO of the + * DSpace instance is configured correctly. + * + * This is only relevant in a production environment, where the DSpace instance is exposed to the public. + */ +public class SEOHealthIndicator extends AbstractHealthIndicator { + + ConfigurationService configurationService = DSpaceServicesFactory.getInstance().getConfigurationService(); + + private final RestTemplate restTemplate = new RestTemplate(); + + @Override + protected void doHealthCheck(Health.Builder builder) { + String baseUrl = configurationService.getProperty("dspace.ui.url"); + + boolean sitemapOk = checkUrl(baseUrl + "/sitemap_index.xml") || checkUrl(baseUrl + "/sitemap_index.html"); + boolean robotsTxtOk = checkRobotsTxt(baseUrl + "/robots.txt"); + boolean ssrOk = checkSSR(baseUrl); + + if (sitemapOk && robotsTxtOk && ssrOk) { + builder.up() + .withDetail("sitemap", "OK") + .withDetail("robots.txt", "OK") + .withDetail("ssr", "OK"); + } else { + builder.down() + .withDetail("sitemap", sitemapOk ? "OK" : "Missing or inaccessible") + .withDetail("robots.txt", robotsTxtOk ? "OK" : "Empty or contains local URLs") + .withDetail("ssr", ssrOk ? "OK" : "Server-side rendering might be disabled"); + } + } + + private boolean checkUrl(String url) { + try { + restTemplate.getForEntity(url, String.class); + return true; + } catch (Exception e) { + return false; + } + } + + private boolean checkRobotsTxt(String url) { + try { + String content = restTemplate.getForObject(url, String.class); + return StringUtils.isNotBlank(content) && !content.contains("localhost"); + } catch (Exception e) { + return false; + } + } + + private boolean checkSSR(String url) { + try { + String content = restTemplate.getForObject(url, String.class); + return content != null && !content.contains(""); + } catch (Exception e) { + return false; + } + } +} + From 54a3aea70dbfea9b38ca2c30ae6cf6a2f7427904 Mon Sep 17 00:00:00 2001 From: Jens Vannerum Date: Tue, 18 Mar 2025 10:04:36 +0100 Subject: [PATCH 504/979] Disable new actuator in IT (cherry picked from commit 20ab43ccccf84c83d6db9b431321e60256d30355) --- dspace-api/src/test/data/dspaceFolder/config/local.cfg | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/dspace-api/src/test/data/dspaceFolder/config/local.cfg b/dspace-api/src/test/data/dspaceFolder/config/local.cfg index 43b6d05e5e42..962a1bcd9b22 100644 --- a/dspace-api/src/test/data/dspaceFolder/config/local.cfg +++ b/dspace-api/src/test/data/dspaceFolder/config/local.cfg @@ -158,6 +158,7 @@ proxies.trusted.include_ui_ip = true # For the tests we have to disable this health indicator because there isn't a mock server and the calculated status was DOWN management.health.solrOai.enabled = false +management.health.seo.enabled = false # Enable researcher profiles and orcid synchronization for tests researcher-profile.entity-type = Person @@ -189,4 +190,4 @@ ldn.notify.inbox.block-untrusted-ip = true # ERROR LOGGING # ########################################### # Log full stacktrace of other common 4xx errors (for easier debugging of these errors in tests) -logging.server.include-stacktrace-for-httpcode = 422, 400 \ No newline at end of file +logging.server.include-stacktrace-for-httpcode = 422, 400 From 698db1d3496b9cb14c0ff21b81c7570de8cf2837 Mon Sep 17 00:00:00 2001 From: Jens Vannerum Date: Tue, 1 Apr 2025 17:54:48 +0200 Subject: [PATCH 505/979] 127746: Implement different failures for robots file so we can differentiate between a missing file or an invalid file (cherry picked from commit 32c048428026f890c2f3bc7eca5a2f20717dc587) --- .../app/rest/health/SEOHealthIndicator.java | 37 ++++++++++++++----- 1 file changed, 28 insertions(+), 9 deletions(-) diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/health/SEOHealthIndicator.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/health/SEOHealthIndicator.java index d936fce635e6..a071e12088bd 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/health/SEOHealthIndicator.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/health/SEOHealthIndicator.java @@ -31,19 +31,28 @@ protected void doHealthCheck(Health.Builder builder) { String baseUrl = configurationService.getProperty("dspace.ui.url"); boolean sitemapOk = checkUrl(baseUrl + "/sitemap_index.xml") || checkUrl(baseUrl + "/sitemap_index.html"); - boolean robotsTxtOk = checkRobotsTxt(baseUrl + "/robots.txt"); + RobotsTxtStatus robotsTxtStatus = checkRobotsTxt(baseUrl + "/robots.txt"); boolean ssrOk = checkSSR(baseUrl); - if (sitemapOk && robotsTxtOk && ssrOk) { + if (sitemapOk && robotsTxtStatus == RobotsTxtStatus.VALID && ssrOk) { builder.up() .withDetail("sitemap", "OK") .withDetail("robots.txt", "OK") .withDetail("ssr", "OK"); } else { - builder.down() - .withDetail("sitemap", sitemapOk ? "OK" : "Missing or inaccessible") - .withDetail("robots.txt", robotsTxtOk ? "OK" : "Empty or contains local URLs") - .withDetail("ssr", ssrOk ? "OK" : "Server-side rendering might be disabled"); + builder.down(); + builder.withDetail("sitemap", sitemapOk ? "OK" : "Missing or inaccessible"); + + if (robotsTxtStatus == RobotsTxtStatus.MISSING) { + builder.withDetail("robots.txt", "Missing or inaccessible. Please see the DSpace Documentation on " + + "Search Engine Optimization for how to create a robots.txt."); + } else if (robotsTxtStatus == RobotsTxtStatus.INVALID) { + builder.withDetail("robots.txt", "Invalid because it contains localhost URLs. This is often a sign " + + "that a proxy is failing to pass X-Forwarded headers to DSpace. Please see the DSpace " + + "Documentation on Search Engine Optimization for how to pass X-Forwarded headers."); + } + + builder.withDetail("ssr", ssrOk ? "OK" : "Server-side rendering might be disabled"); } } @@ -56,12 +65,18 @@ private boolean checkUrl(String url) { } } - private boolean checkRobotsTxt(String url) { + private RobotsTxtStatus checkRobotsTxt(String url) { try { String content = restTemplate.getForObject(url, String.class); - return StringUtils.isNotBlank(content) && !content.contains("localhost"); + if (StringUtils.isBlank(content)) { + return RobotsTxtStatus.MISSING; + } + if (content.contains("localhost")) { + return RobotsTxtStatus.INVALID; + } + return RobotsTxtStatus.VALID; } catch (Exception e) { - return false; + return RobotsTxtStatus.MISSING; } } @@ -73,5 +88,9 @@ private boolean checkSSR(String url) { return false; } } + + private enum RobotsTxtStatus { + VALID, MISSING, INVALID + } } From 980976484e51fca3d5a905a9caebfb7f12b3a2fe Mon Sep 17 00:00:00 2001 From: Jens Vannerum Date: Tue, 1 Apr 2025 17:58:47 +0200 Subject: [PATCH 506/979] 127746: Add more detailed information messages on how to solve problems (cherry picked from commit 170dc9a44c5b16c28298a9e3133534e70967147d) --- .../org/dspace/app/rest/health/SEOHealthIndicator.java | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/health/SEOHealthIndicator.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/health/SEOHealthIndicator.java index a071e12088bd..740c6ab6493e 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/health/SEOHealthIndicator.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/health/SEOHealthIndicator.java @@ -41,7 +41,8 @@ protected void doHealthCheck(Health.Builder builder) { .withDetail("ssr", "OK"); } else { builder.down(); - builder.withDetail("sitemap", sitemapOk ? "OK" : "Missing or inaccessible"); + builder.withDetail("sitemap", sitemapOk ? "OK" : "Sitemaps are missing or inaccessible. Please see the " + + "DSpace Documentation on Search Engine Optimization for how to enable Sitemaps."); if (robotsTxtStatus == RobotsTxtStatus.MISSING) { builder.withDetail("robots.txt", "Missing or inaccessible. Please see the DSpace Documentation on " + @@ -51,8 +52,9 @@ protected void doHealthCheck(Health.Builder builder) { "that a proxy is failing to pass X-Forwarded headers to DSpace. Please see the DSpace " + "Documentation on Search Engine Optimization for how to pass X-Forwarded headers."); } - - builder.withDetail("ssr", ssrOk ? "OK" : "Server-side rendering might be disabled"); + builder.withDetail("ssr", ssrOk ? "OK" : "Server-side rendering (SSR) appears to be disabled. Most " + + "search engines require enabling SSR for proper indexing. Please see the DSpace Documentation on" + + " Search Engine Optimization for more details."); } } From d9ade02675d90c81f27bdf52417a7111a25c9536 Mon Sep 17 00:00:00 2001 From: Jens Vannerum Date: Fri, 4 Apr 2025 16:51:56 +0200 Subject: [PATCH 507/979] 127746: Include success result for robots.txt check if other checks fail (cherry picked from commit 5dc12775fac0006dbc1d0106ffcdffbe893919d1) --- .../java/org/dspace/app/rest/health/SEOHealthIndicator.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/health/SEOHealthIndicator.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/health/SEOHealthIndicator.java index 740c6ab6493e..5b57f2d537fc 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/health/SEOHealthIndicator.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/health/SEOHealthIndicator.java @@ -51,6 +51,8 @@ protected void doHealthCheck(Health.Builder builder) { builder.withDetail("robots.txt", "Invalid because it contains localhost URLs. This is often a sign " + "that a proxy is failing to pass X-Forwarded headers to DSpace. Please see the DSpace " + "Documentation on Search Engine Optimization for how to pass X-Forwarded headers."); + } else { + builder.withDetail("robots.txt", "OK"); } builder.withDetail("ssr", ssrOk ? "OK" : "Server-side rendering (SSR) appears to be disabled. Most " + "search engines require enabling SSR for proper indexing. Please see the DSpace Documentation on" + From cdffd0639cac3078151bbad622ca87785651b4ba Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Mon, 7 Apr 2025 10:54:33 -0500 Subject: [PATCH 508/979] Update reusable-docker-build to use Ubuntu ARM64 runner for those images (cherry picked from commit 0177c123b3bcbf4cb2406d4ee4216b2c22c8c2a8) --- .github/workflows/reusable-docker-build.yml | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/.github/workflows/reusable-docker-build.yml b/.github/workflows/reusable-docker-build.yml index 7a8abda3e106..36907459747b 100644 --- a/.github/workflows/reusable-docker-build.yml +++ b/.github/workflows/reusable-docker-build.yml @@ -86,17 +86,16 @@ jobs: matrix: # Architectures / Platforms for which we will build Docker images arch: [ 'linux/amd64', 'linux/arm64' ] - os: [ ubuntu-latest ] isPr: - ${{ github.event_name == 'pull_request' }} # If this is a PR, we ONLY build for AMD64. For PRs we only do a sanity check test to ensure Docker builds work. # The below exclude therefore ensures we do NOT build ARM64 for PRs. exclude: - isPr: true - os: ubuntu-latest arch: linux/arm64 - runs-on: ${{ matrix.os }} + # If ARM64, then use the Ubuntu ARM64 runner. Otherwise, use the Ubuntu AMD64 runner + runs-on: ${{ matrix.arch == 'linux/arm64' && 'ubuntu-24.04-arm' || 'ubuntu-latest' }} steps: # This step converts the slashes in the "arch" matrix values above into dashes & saves to env.ARCH_NAME @@ -122,10 +121,6 @@ jobs: username: ${{ github.repository_owner }} password: ${{ secrets.GITHUB_TOKEN }} - # https://github.com/docker/setup-qemu-action - - name: Set up QEMU emulation to build for multiple architectures - uses: docker/setup-qemu-action@v3 - # https://github.com/docker/setup-buildx-action - name: Setup Docker Buildx uses: docker/setup-buildx-action@v3 From 4c73e4b01f5a11b48e6e9e4f2e07bb6b1ef0670e Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Mon, 7 Apr 2025 10:54:33 -0500 Subject: [PATCH 509/979] Update reusable-docker-build to use Ubuntu ARM64 runner for those images --- .github/workflows/reusable-docker-build.yml | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/.github/workflows/reusable-docker-build.yml b/.github/workflows/reusable-docker-build.yml index 3b74f250b539..aec03603cef0 100644 --- a/.github/workflows/reusable-docker-build.yml +++ b/.github/workflows/reusable-docker-build.yml @@ -87,17 +87,16 @@ jobs: matrix: # Architectures / Platforms for which we will build Docker images arch: [ 'linux/amd64', 'linux/arm64' ] - os: [ ubuntu-latest ] isPr: - ${{ github.event_name == 'pull_request' }} # If this is a PR, we ONLY build for AMD64. For PRs we only do a sanity check test to ensure Docker builds work. # The below exclude therefore ensures we do NOT build ARM64 for PRs. exclude: - isPr: true - os: ubuntu-latest arch: linux/arm64 - runs-on: ${{ matrix.os }} + # If ARM64, then use the Ubuntu ARM64 runner. Otherwise, use the Ubuntu AMD64 runner + runs-on: ${{ matrix.arch == 'linux/arm64' && 'ubuntu-24.04-arm' || 'ubuntu-latest' }} steps: # This step converts the slashes in the "arch" matrix values above into dashes & saves to env.ARCH_NAME @@ -123,10 +122,6 @@ jobs: username: ${{ github.repository_owner }} password: ${{ secrets.GITHUB_TOKEN }} - # https://github.com/docker/setup-qemu-action - - name: Set up QEMU emulation to build for multiple architectures - uses: docker/setup-qemu-action@v3 - # https://github.com/docker/setup-buildx-action - name: Setup Docker Buildx uses: docker/setup-buildx-action@v3 From d7a3f32397f542b04e2bb887e64722c2a98a0889 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Mon, 7 Apr 2025 14:57:31 -0500 Subject: [PATCH 510/979] Remove unused SWORD v1 client code. This is "dead code" which is unmaintained and obsolete --- .../java/org/purl/sword/client/Client.java | 463 ---------- .../purl/sword/client/ClientConstants.java | 40 - .../org/purl/sword/client/ClientFactory.java | 116 --- .../org/purl/sword/client/ClientOptions.java | 605 ------------- .../org/purl/sword/client/ClientType.java | 23 - .../java/org/purl/sword/client/CmdClient.java | 445 --------- .../purl/sword/client/DebugOutputStream.java | 44 - .../purl/sword/client/MessageOutputPanel.java | 138 --- .../purl/sword/client/PostDestination.java | 140 --- .../org/purl/sword/client/PostDialog.java | 599 ------------- .../org/purl/sword/client/PostMessage.java | 344 ------- .../purl/sword/client/PropertiesDialog.java | 240 ----- .../org/purl/sword/client/SWORDClient.java | 85 -- .../sword/client/SWORDClientException.java | 42 - .../org/purl/sword/client/SWORDComboBox.java | 129 --- .../org/purl/sword/client/SWORDFormPanel.java | 144 --- .../org/purl/sword/client/ServiceDialog.java | 227 ----- .../org/purl/sword/client/ServicePanel.java | 843 ------------------ .../sword/client/ServiceSelectedListener.java | 23 - .../java/org/purl/sword/client/Status.java | 61 -- 20 files changed, 4751 deletions(-) delete mode 100644 dspace-sword/src/main/java/org/purl/sword/client/Client.java delete mode 100644 dspace-sword/src/main/java/org/purl/sword/client/ClientConstants.java delete mode 100644 dspace-sword/src/main/java/org/purl/sword/client/ClientFactory.java delete mode 100644 dspace-sword/src/main/java/org/purl/sword/client/ClientOptions.java delete mode 100644 dspace-sword/src/main/java/org/purl/sword/client/ClientType.java delete mode 100644 dspace-sword/src/main/java/org/purl/sword/client/CmdClient.java delete mode 100644 dspace-sword/src/main/java/org/purl/sword/client/DebugOutputStream.java delete mode 100644 dspace-sword/src/main/java/org/purl/sword/client/MessageOutputPanel.java delete mode 100644 dspace-sword/src/main/java/org/purl/sword/client/PostDestination.java delete mode 100644 dspace-sword/src/main/java/org/purl/sword/client/PostDialog.java delete mode 100644 dspace-sword/src/main/java/org/purl/sword/client/PostMessage.java delete mode 100644 dspace-sword/src/main/java/org/purl/sword/client/PropertiesDialog.java delete mode 100644 dspace-sword/src/main/java/org/purl/sword/client/SWORDClient.java delete mode 100644 dspace-sword/src/main/java/org/purl/sword/client/SWORDClientException.java delete mode 100644 dspace-sword/src/main/java/org/purl/sword/client/SWORDComboBox.java delete mode 100644 dspace-sword/src/main/java/org/purl/sword/client/SWORDFormPanel.java delete mode 100644 dspace-sword/src/main/java/org/purl/sword/client/ServiceDialog.java delete mode 100644 dspace-sword/src/main/java/org/purl/sword/client/ServicePanel.java delete mode 100644 dspace-sword/src/main/java/org/purl/sword/client/ServiceSelectedListener.java delete mode 100644 dspace-sword/src/main/java/org/purl/sword/client/Status.java diff --git a/dspace-sword/src/main/java/org/purl/sword/client/Client.java b/dspace-sword/src/main/java/org/purl/sword/client/Client.java deleted file mode 100644 index c6d335d8dd16..000000000000 --- a/dspace-sword/src/main/java/org/purl/sword/client/Client.java +++ /dev/null @@ -1,463 +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.purl.sword.client; - -import java.io.BufferedReader; -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.UnsupportedEncodingException; -import java.net.MalformedURLException; -import java.net.URL; -import java.security.NoSuchAlgorithmException; -import java.util.Properties; - -import org.apache.http.HttpHost; -import org.apache.http.HttpResponse; -import org.apache.http.HttpStatus; -import org.apache.http.StatusLine; -import org.apache.http.auth.AuthScope; -import org.apache.http.auth.UsernamePasswordCredentials; -import org.apache.http.client.methods.HttpGet; -import org.apache.http.client.methods.HttpPost; -import org.apache.http.conn.params.ConnRoutePNames; -import org.apache.http.entity.ContentType; -import org.apache.http.entity.FileEntity; -import org.apache.http.impl.client.DefaultHttpClient; -import org.apache.http.params.HttpParams; -import org.apache.logging.log4j.Logger; -import org.purl.sword.base.ChecksumUtils; -import org.purl.sword.base.DepositResponse; -import org.purl.sword.base.HttpHeaders; -import org.purl.sword.base.ServiceDocument; -import org.purl.sword.base.SwordValidationInfo; -import org.purl.sword.base.UnmarshallException; - -/** - * This is an example Client implementation to demonstrate how to connect to a - * SWORD server. The client supports BASIC HTTP Authentication. This can be - * initialised by setting a username and password. - * - * @author Neil Taylor - */ -public class Client implements SWORDClient { - /** - * The status field for the response code from the recent network access. - */ - private Status status; - - /** - * The name of the server to contact. - */ - private String server; - - /** - * The port number for the server. - */ - private int port; - - /** - * Specifies if the network access should use HTTP authentication. - */ - private boolean doAuthentication; - - /** - * The username to use for Basic Authentication. - */ - private String username; - - /** - * User password that is to be used. - */ - private String password; - - /** - * The userAgent to identify this application. - */ - private String userAgent; - - /** - * The client that is used to send data to the specified server. - */ - private final DefaultHttpClient client; - - /** - * The default connection timeout. This can be modified by using the - * setSocketTimeout method. - */ - public static final int DEFAULT_TIMEOUT = 20000; - - /** - * Logger. - */ - private static final Logger log = org.apache.logging.log4j.LogManager.getLogger(Client.class); - - /** - * Create a new Client. The client will not use authentication by default. - */ - public Client() { - client = new DefaultHttpClient(); - HttpParams params = client.getParams(); - params.setParameter("http.socket.timeout", - Integer.valueOf(DEFAULT_TIMEOUT)); - HttpHost proxyHost = (HttpHost) params - .getParameter(ConnRoutePNames.DEFAULT_PROXY); // XXX does this really work? - log.debug("proxy host: " + proxyHost.getHostName()); - log.debug("proxy port: " + proxyHost.getPort()); - doAuthentication = false; - } - - /** - * Initialise the server that will be used to send the network access. - * - * @param server server address/hostname - * @param port server port - */ - public void setServer(String server, int port) { - this.server = server; - this.port = port; - } - - /** - * Set the user credentials that will be used when making the access to the - * server. - * - * @param username The username. - * @param password The password. - */ - public void setCredentials(String username, String password) { - this.username = username; - this.password = password; - doAuthentication = true; - } - - /** - * Set the basic credentials. You must have previously set the server and - * port using setServer. - * - * @param username The username. - * @param password The password. - */ - private void setBasicCredentials(String username, String password) { - log.debug("server: " + server + " port: " + port + " u: '" + username - + "' p '" + password + "'"); - client.getCredentialsProvider().setCredentials(new AuthScope(server, port), - new UsernamePasswordCredentials(username, password)); - } - - /** - * Set a proxy that should be used by the client when trying to access the - * server. If this is not set, the client will attempt to make a direct - * direct connection to the server. The port is set to 80. - * - * @param host The hostname. - */ - public void setProxy(String host) { - setProxy(host, 80); - } - - /** - * Set a proxy that should be used by the client when trying to access the - * server. If this is not set, the client will attempt to make a direct - * direct connection to the server. - * - * @param host The name of the host. - * @param port The port. - */ - public void setProxy(String host, int port) { - client.getParams().setParameter(ConnRoutePNames.DEFAULT_PROXY, - new HttpHost(host, port)); // XXX does this really work? - } - - /** - * Clear the proxy setting. - */ - public void clearProxy() { - client.getParams().removeParameter(ConnRoutePNames.DEFAULT_PROXY); // XXX does this really work? - } - - /** - * Clear any user credentials that have been set for this client. - */ - public void clearCredentials() { - client.getCredentialsProvider().clear(); - doAuthentication = false; - } - - public void setUserAgent(String userAgent) { - this.userAgent = userAgent; - } - - /** - * Set the connection timeout for the socket. - * - * @param milliseconds The time, expressed as a number of milliseconds. - */ - public void setSocketTimeout(int milliseconds) { - client.getParams().setParameter("http.socket.timeout", - Integer.valueOf(milliseconds)); - } - - /** - * Retrieve the service document. The service document is located at the - * specified URL. This calls getServiceDocument(url,onBehalfOf). - * - * @param url The location of the service document. - * @return The ServiceDocument, or null if there was a - * problem accessing the document. e.g. invalid access. - * @throws SWORDClientException If there is an error accessing the resource. - */ - public ServiceDocument getServiceDocument(String url) - throws SWORDClientException { - return getServiceDocument(url, null); - } - - /** - * Retrieve the service document. The service document is located at the - * specified URL. This calls getServiceDocument(url,onBehalfOf). - * - * @param url The location of the service document. - * @return The ServiceDocument, or null if there was a - * problem accessing the document. e.g. invalid access. - * @throws SWORDClientException If there is an error accessing the resource. - */ - public ServiceDocument getServiceDocument(String url, String onBehalfOf) - throws SWORDClientException { - URL serviceDocURL = null; - try { - serviceDocURL = new URL(url); - } catch (MalformedURLException e) { - // Try relative URL - URL baseURL = null; - try { - baseURL = new URL("http", server, Integer.valueOf(port), "/"); - serviceDocURL = new URL(baseURL, (url == null) ? "" : url); - } catch (MalformedURLException e1) { - // No dice, can't even form base URL... - throw new SWORDClientException(url + " is not a valid URL (" - + e1.getMessage() - + "), and could not form a relative one from: " - + baseURL + " / " + url, e1); - } - } - - HttpGet httpget = new HttpGet(serviceDocURL.toExternalForm()); - if (doAuthentication) { - // this does not perform any check on the username password. It - // relies on the server to determine if the values are correct. - setBasicCredentials(username, password); - } - - Properties properties = new Properties(); - - if (containsValue(onBehalfOf)) { - log.debug("Setting on-behalf-of: " + onBehalfOf); - httpget.addHeader(url, url); - httpget.addHeader(HttpHeaders.X_ON_BEHALF_OF, onBehalfOf); - properties.put(HttpHeaders.X_ON_BEHALF_OF, onBehalfOf); - } - - if (containsValue(userAgent)) { - log.debug("Setting userAgent: " + userAgent); - httpget.addHeader(HttpHeaders.USER_AGENT, userAgent); - properties.put(HttpHeaders.USER_AGENT, userAgent); - } - - ServiceDocument doc = null; - - try { - HttpResponse response = client.execute(httpget); - StatusLine statusLine = response.getStatusLine(); - int statusCode = statusLine.getStatusCode(); - // store the status code - status = new Status(statusCode, statusLine.getReasonPhrase()); - - if (status.getCode() == HttpStatus.SC_OK) { - String message = readResponse(response.getEntity().getContent()); - log.debug("returned message is: " + message); - doc = new ServiceDocument(); - lastUnmarshallInfo = doc.unmarshall(message, properties); - } else { - throw new SWORDClientException( - "Received error from service document request: " - + status); - } - } catch (IOException ioex) { - throw new SWORDClientException(ioex.getMessage(), ioex); - } catch (UnmarshallException uex) { - throw new SWORDClientException(uex.getMessage(), uex); - } finally { - httpget.releaseConnection(); - } - - return doc; - } - - private SwordValidationInfo lastUnmarshallInfo; - - /** - * @return SWORD validation info - */ - public SwordValidationInfo getLastUnmarshallInfo() { - return lastUnmarshallInfo; - } - - /** - * Post a file to the server. The different elements of the post are encoded - * in the specified message. - * - * @param message The message that contains the post information. - * @throws SWORDClientException if there is an error during the post operation. - */ - public DepositResponse postFile(PostMessage message) - throws SWORDClientException { - if (message == null) { - throw new SWORDClientException("Message cannot be null."); - } - - HttpPost httppost = new HttpPost(message.getDestination()); - - if (doAuthentication) { - setBasicCredentials(username, password); - } - - DepositResponse response = null; - - String messageBody = ""; - - try { - if (message.isUseMD5()) { - String md5 = ChecksumUtils.generateMD5(message.getFilepath()); - if (message.getChecksumError()) { - md5 = "1234567890"; - } - log.debug("checksum error is: " + md5); - if (md5 != null) { - httppost.addHeader(HttpHeaders.CONTENT_MD5, md5); - } - } - - String filename = message.getFilename(); - if (!"".equals(filename)) { - httppost.addHeader(HttpHeaders.CONTENT_DISPOSITION, - " filename=" + filename); - } - - if (containsValue(message.getSlug())) { - httppost.addHeader(HttpHeaders.SLUG, message.getSlug()); - } - - if (message.getCorruptRequest()) { - // insert a header with an invalid boolean value - httppost.addHeader(HttpHeaders.X_NO_OP, "Wibble"); - } else { - httppost.addHeader(HttpHeaders.X_NO_OP, Boolean - .toString(message.isNoOp())); - } - httppost.addHeader(HttpHeaders.X_VERBOSE, Boolean - .toString(message.isVerbose())); - - String packaging = message.getPackaging(); - if (packaging != null && packaging.length() > 0) { - httppost.addHeader(HttpHeaders.X_PACKAGING, packaging); - } - - String onBehalfOf = message.getOnBehalfOf(); - if (containsValue(onBehalfOf)) { - httppost.addHeader(HttpHeaders.X_ON_BEHALF_OF, onBehalfOf); - } - - String userAgent = message.getUserAgent(); - if (containsValue(userAgent)) { - httppost.addHeader(HttpHeaders.USER_AGENT, userAgent); - } - - - FileEntity requestEntity = new FileEntity( - new File(message.getFilepath()), - ContentType.create(message.getFiletype())); - httppost.setEntity(requestEntity); - - HttpResponse httpResponse = client.execute(httppost); - StatusLine statusLine = httpResponse.getStatusLine(); - int statusCode = statusLine.getStatusCode(); - status = new Status(statusCode, statusLine.getReasonPhrase()); - - log.info("Checking the status code: " + status.getCode()); - - if (status.getCode() == HttpStatus.SC_ACCEPTED - || status.getCode() == HttpStatus.SC_CREATED) { - messageBody = readResponse(httpResponse.getEntity().getContent()); - response = new DepositResponse(status.getCode()); - response.setLocation(httpResponse.getFirstHeader("Location").getValue()); - // added call for the status code. - lastUnmarshallInfo = response.unmarshall(messageBody, new Properties()); - } else { - messageBody = readResponse(httpResponse.getEntity().getContent()); - response = new DepositResponse(status.getCode()); - response.unmarshallErrorDocument(messageBody); - } - return response; - - } catch (NoSuchAlgorithmException nex) { - throw new SWORDClientException("Unable to use MD5. " - + nex.getMessage(), nex); - } catch (IOException ioex) { - throw new SWORDClientException(ioex.getMessage(), ioex); - } catch (UnmarshallException uex) { - throw new SWORDClientException(uex.getMessage() + "(
    " + messageBody + "
    )", uex); - } finally { - httppost.releaseConnection(); - } - } - - /** - * Read a response from the stream and return it as a string. - * - * @param stream The stream that contains the response. - * @return The string extracted from the screen. - * @throws UnsupportedEncodingException - * @throws IOException A general class of exceptions produced by failed or interrupted I/O - * operations. - */ - private String readResponse(InputStream stream) - throws UnsupportedEncodingException, IOException { - BufferedReader reader = new BufferedReader(new InputStreamReader( - stream, "UTF-8")); - String line = null; - StringBuffer buffer = new StringBuffer(); - while ((line = reader.readLine()) != null) { - buffer.append(line); - buffer.append("\n"); - } - return buffer.toString(); - } - - /** - * Return the status information that was returned from the most recent - * request sent to the server. - * - * @return The status code returned from the most recent access. - */ - public Status getStatus() { - return status; - } - - /** - * Check to see if the specified item contains a non-empty string. - * - * @param item The string to check. - * @return True if the string is not null and has a length greater than 0 - * after any whitespace is trimmed from the start and end. - * Otherwise, false. - */ - private boolean containsValue(String item) { - return ((item != null) && (item.trim().length() > 0)); - } -} diff --git a/dspace-sword/src/main/java/org/purl/sword/client/ClientConstants.java b/dspace-sword/src/main/java/org/purl/sword/client/ClientConstants.java deleted file mode 100644 index 124ed38d33d8..000000000000 --- a/dspace-sword/src/main/java/org/purl/sword/client/ClientConstants.java +++ /dev/null @@ -1,40 +0,0 @@ -/** - * The contents of this file are subject to the license and copyright - * detailed in the LICENSE and NOTICE files at the root of the source - * tree and available online at - * - * http://www.dspace.org/license/ - */ -package org.purl.sword.client; - -/** - * Hold general constants for the client. - * - * @author Neil Taylor - */ -public class ClientConstants { - /** - * Current software version. - */ - public static final String CLIENT_VERSION = "1.1"; - - /** - * the name of this application - */ - public static final String SERVICE_NAME = "CASIS Test Client"; - - /** - * the name of this application - */ - public static final String NOT_DEFINED_TEXT = "Not defined"; - - /** - * The logging property file. - */ - public static final String LOGGING_PROPERTY_FILE = "log4j.properties"; - - /** - * Default constructor - */ - private ClientConstants() { } -} diff --git a/dspace-sword/src/main/java/org/purl/sword/client/ClientFactory.java b/dspace-sword/src/main/java/org/purl/sword/client/ClientFactory.java deleted file mode 100644 index 00bd9c17a541..000000000000 --- a/dspace-sword/src/main/java/org/purl/sword/client/ClientFactory.java +++ /dev/null @@ -1,116 +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.purl.sword.client; - -/** - * Entry point for the SWORD Demonstration Client. This will parse the list of - * command line options and load either a Command Line client or a GUI client. - * - * @author Neil Taylor - */ -public class ClientFactory { - /** - * Generate a string that specifies the command line options for this - * program. - * - * @return A list of the options for this program. - */ - public static String usage() { - StringBuilder buffer = new StringBuilder(); - buffer.append("swordclient: version "); - buffer.append(ClientConstants.CLIENT_VERSION); - buffer.append("\n"); - - buffer.append("GUI Mode: "); - buffer.append("swordclient [-gui] [-nocapture]"); - buffer.append("\n\n"); - - buffer.append("Command Mode: Service - Request a Service Document\n"); - buffer.append("swordclient -cmd -t service [user-options] [proxy-options] -href url [-onBehalfOf name] "); - buffer.append("\n\n"); - - buffer.append("Command Mode: Post - Post a file to a remote service.\n"); - buffer.append("swordclient -cmd -t post [user-options] [proxy-options] [post-options] \n"); - buffer.append(" [-file file] [-filetype type] [-onBehalfOf name]"); - buffer.append("\n\n"); - - buffer.append("Command Mode: MultiPost - Post a file to multiple remote services.\n"); - buffer.append("swordclient -cmd -t multipost [user-options] [proxy-options] [post-options] \n"); - buffer.append(" [-dest dest]"); - - buffer.append("\n\n"); - buffer.append("User options: \n"); - buffer.append(" -u username Specify a username to access the remote service.\n"); - buffer.append(" -p password Specify a password to access the remote service.\n"); - buffer.append(" Required if -u option is used."); - - buffer.append("\n\n"); - buffer.append("Proxy options: \n"); - buffer.append(" -host host Hostname of a proxy, wwwproxy.aber.ac.uk.\n"); - buffer.append(" -port port Proxy port number, e.g. 8080.\n"); - - buffer.append("\n\n"); - buffer.append("Post options: \n"); - buffer.append(" -noOp Specified to indicate that the post is a test operation.\n"); - buffer.append(" -md5 Use an MD5 checksum in the message header.\n"); - buffer.append(" -checksumError Mis-calculate the file checksum for server test purposes.\n"); - buffer.append(" -formatNamespace ns The format namespace value.\n"); - buffer.append(" -slug name The slug value.\n"); - buffer.append(" -verbose Request a verbose response from the server.\n"); - - buffer.append("\n\n"); - buffer.append("Other options: \n"); - buffer.append(" -help Show this message.\n"); - buffer.append(" -t type The type of operation: service, post or multipost.\n"); - buffer.append(" -href url The URL for the service or post document.\n"); - buffer.append(" Required for service. The post and multipost operations \n"); - buffer.append(" will prompt you if the value is not provided.\n"); - buffer.append(" -filetype type The filetype, e.g. application/zip. The post and multipost\n"); - buffer.append(" will prompt you for the value if it is not provided.\n"); - buffer.append(" -onBehalfOf name Specify this parameter to set the On Behalf Of value.\n"); - buffer.append(" -dest dest Specify the destination for a deposit. This can be repeated\n"); - buffer.append(" multiple times. The format is: \n"); - buffer.append(" []:@\n"); - buffer.append(" e.g. sword[nst]:swordpass@http://sword.aber.ac.uk/post/\n"); - buffer.append(" nst:pass@http://sword.aber.ac.uk/post\n"); - buffer.append(" -nocapture Do not capture System.out and System.err to a debug panel\n"); - buffer.append(" in the GUI panel."); - - return buffer.toString(); - } - - /** - * Create a client. If GUI mode is set, a GUI client is created. Otherwise, - * a command line client is created. - * - * @param options The list of options extracted from the command line. - * @return A new client. - */ - public ClientType createClient(ClientOptions options) { - return new CmdClient(); - } - - /** - * Start the application and determine which client should be loaded. The - * application currently has two modes: GUI and client. The GUI mode is the - * default option. - * - * @param args the command line arguments given - */ - public static void main(String[] args) { - ClientFactory factory = new ClientFactory(); - - ClientOptions options = new ClientOptions(); - if (options.parseOptions(args)) { - ClientType client = factory.createClient(options); - client.run(options); - } else { - System.out.println(usage()); - } - } -} diff --git a/dspace-sword/src/main/java/org/purl/sword/client/ClientOptions.java b/dspace-sword/src/main/java/org/purl/sword/client/ClientOptions.java deleted file mode 100644 index 1ffbfca022b2..000000000000 --- a/dspace-sword/src/main/java/org/purl/sword/client/ClientOptions.java +++ /dev/null @@ -1,605 +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.purl.sword.client; - -import java.util.ArrayList; -import java.util.Iterator; -import java.util.List; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import org.apache.commons.cli.CommandLine; -import org.apache.commons.cli.DefaultParser; -import org.apache.commons.cli.Option; -import org.apache.commons.cli.Options; -import org.apache.commons.cli.ParseException; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; - -/** - * List of options that are parsed from the command line. - * - * @author Neil Taylor - */ -public class ClientOptions { - /** - * Label for the service operation. - */ - public static final String TYPE_SERVICE = "service"; - - /** - * Label for the post operation. - */ - public static final String TYPE_POST = "post"; - - /** - * Label for the multipost operation. - */ - public static final String TYPE_MULTI_POST = "multipost"; - - /** - * The access type. - */ - private String accessType = null; - - /** - * Proxy host name. - */ - private String proxyHost = null; - - /** - * Proxy host port. - */ - private int proxyPort = 8080; - - /** - * Username to access the service/post server. - */ - private String username = null; - - /** - * Password to access the service/post server. - */ - private String password = null; - - /** - * HREF of the server to access. - */ - private String href = null; - - /** - * Filename to post. - */ - private String filename = null; - - /** - * File type. - */ - private String filetype = null; - - /** - * Specifies that the output streams are not to be captured by the GUI client. - */ - private boolean noCapture = false; - - - /** - * SLUG Header field. - */ - private String slug = null; - - /** - * NoOp, used to indicate an operation on the server that does not - * require the file to be stored. - */ - private boolean noOp = false; - - /** - * Request verbose output from the server. - */ - private boolean verbose = false; - - /** - * OnBehalfOf user id. - */ - private String onBehalfOf = null; - - /** - * Format namespace to be used for the posted file. - */ - private String formatNamespace = null; - - /** - * Introduce a checksum error. This is used to simulate an error with the - * MD5 value. - */ - private boolean checksumError = false; - - /** - * Logger. - */ - private static final Logger log = LogManager.getLogger(); - - /** - * List of multiple destination items. Used if the mode is set to multipost. - */ - private final List multiPost = new ArrayList<>(); - - /** - * Pattern string to extract the data from a destination parameter in multipost mode. - */ - private static final Pattern MULTI_PATTERN - = Pattern.compile("(.*?)(\\[(.*?)\\]) {0,1}(:(.*)) {0,1}@(http://.*)"); - - /** - * Flag that indicates if the GUI mode has been set. This is - * true by default. - */ - private boolean guiMode = true; - - /** - * Flat that indicates if the MD5 option has been selected. This - * is true by default. - */ - private boolean md5 = false; - - /** - * Parse the list of options contained in the specified array. - * - * @param args The array of options. - * @return True if the options were parsed successfully. - */ - public boolean parseOptions(String[] args) { - Options options = new Options(); - options.addOption(Option.builder().longOpt("md5").build()) - .addOption(Option.builder().longOpt("noOp").build()) - .addOption(Option.builder().longOpt("verbose").build()) - .addOption(Option.builder().longOpt("cmd").build()) - .addOption(Option.builder().longOpt("gui").build()) - .addOption(Option.builder().longOpt("help").build()) - .addOption(Option.builder().longOpt("nocapture").build()); - - Option option; - - option = Option.builder().longOpt("host").hasArg().build(); - options.addOption(option); - - option = Option.builder().longOpt("port").hasArg().build(); - options.addOption(option); - - option = Option.builder("u").hasArg().build(); - options.addOption(option); - - option = Option.builder("p").hasArg().build(); - options.addOption(option); - - option = Option.builder().longOpt("href").hasArg().build(); - options.addOption(option); - - option = Option.builder("t").hasArg().build(); - options.addOption(option); - - option = Option.builder().longOpt("file").hasArg().build(); - options.addOption(option); - - option = Option.builder().longOpt("filetype").hasArg().build(); - options.addOption(option); - - option = Option.builder().longOpt("slug").hasArg().build(); - options.addOption(option); - - option = Option.builder().longOpt("onBehalfOf").hasArg().build(); - options.addOption(option); - - option = Option.builder().longOpt("formatNamespace").hasArg().build(); - options.addOption(option); - - option = Option.builder().longOpt("checksumError").build(); - options.addOption(option); - - option = Option.builder().longOpt("dest").hasArg().build(); - options.addOption(option); - - DefaultParser parser = new DefaultParser(); - CommandLine command; - try { - command = parser.parse(options, args); - } catch (ParseException ex) { - log.error(ex.getMessage()); - return false; - } - - if (command.hasOption("help")) { - return false; // force the calling code to display the usage information. - } - md5 = command.hasOption("md5"); - noOp = command.hasOption("noOp"); - verbose = command.hasOption("verbose"); - if (command.hasOption("cmd")) { - guiMode = false; - } - if (command.hasOption("gui")) { - guiMode = true; - } - proxyHost = command.getOptionValue("host"); - if (command.hasOption("port")) { - proxyPort = Integer.parseInt(command.getOptionValue("port")); - } - username = command.getOptionValue("u"); - password = command.getOptionValue("p"); - href = command.getOptionValue("href"); - accessType = command.getOptionValue("t"); - filename = command.getOptionValue("file"); - filetype = command.getOptionValue("filetype"); - slug = command.getOptionValue("slug"); - onBehalfOf = command.getOptionValue("onBehalfOf"); - formatNamespace = command.getOptionValue("formatNamespace"); - checksumError = command.hasOption("checksumError"); - noCapture = command.hasOption("nocapture"); - if (command.hasOption("dest")) { - String dest = command.getOptionValue("dest"); - Matcher m = MULTI_PATTERN.matcher(dest); - if (!m.matches()) { - log.debug("Error with dest parameter. Ignoring value: {}", dest); - } else { - int numGroups = m.groupCount(); - for (int g = 0; g <= numGroups; g++) { - log.debug("Group ({}) is: {}", g, m.group(g)); - } - - String group_username = m.group(1); - String group_onBehalfOf = m.group(3); - String group_password = m.group(5); - String group_url = m.group(6); - PostDestination destination = new PostDestination(group_url, - group_username, group_password, group_onBehalfOf); - - multiPost.add(destination); - } - } - - try { - // apply any settings - if (href == null && "service".equals(accessType)) { - log.error("No href specified."); - return false; - } - - if (multiPost.isEmpty() && "multipost".equals(accessType)) { - log.error("No destinations specified"); - return false; - } - - if (accessType == null && !guiMode) { - log.error("No access type specified"); - return false; - } - - if ((username == null && password != null) || (username != null && password == null)) { - log.error( - "The username and/or password are not specified. If one is specified, the other must also be " + - "specified."); - return false; - } - } catch (ArrayIndexOutOfBoundsException ex) { - log.error("Error with parameters."); - return false; - } - - return true; - } - - /** - * Get the access type. - * - * @return The value, or null if the value is not set. - */ - public String getAccessType() { - return accessType; - } - - /** - * Set the access type. - * - * @param accessType The value, or null to clear the value. - */ - public void setAccessType(String accessType) { - this.accessType = accessType; - } - - /** - * Get the proxy host. - * - * @return The value, or null if the value is not set. - */ - public String getProxyHost() { - return proxyHost; - } - - /** - * Set the proxy host. - * - * @param proxyHost The value, or null to clear the value. - */ - public void setProxyHost(String proxyHost) { - this.proxyHost = proxyHost; - } - - /** - * Get the proxy port. - * - * @return The proxy port. Default value is 80. - */ - public int getProxyPort() { - return proxyPort; - } - - /** - * Set the proxy port. - * - * @param proxyPort The proxy port. - */ - public void setProxyPort(int proxyPort) { - this.proxyPort = proxyPort; - } - - /** - * Get the username. - * - * @return The value, or null if the value is not set. - */ - public String getUsername() { - return username; - } - - /** - * Set the username. - * - * @param username The value, or null to clear the value. - */ - public void setUsername(String username) { - this.username = username; - } - - /** - * Get the password. - * - * @return The value, or null if the value is not set. - */ - public String getPassword() { - return password; - } - - /** - * Set the password. - * - * @param password The value, or null to clear the value. - */ - public void setPassword(String password) { - this.password = password; - } - - /** - * Get the HREF of the service to access. - * - * @return The value, or null if the value is not set. - */ - public String getHref() { - return href; - } - - /** - * Set the HREF of the service to access. - * - * @param href The value, or null to clear the value. - */ - public void setHref(String href) { - this.href = href; - } - - /** - * Get the name of the file to post. - * - * @return The value, or null if the value is not set. - */ - public String getFilename() { - return filename; - } - - /** - * Set the name of the file to post. - * - * @param filename The value, or null to clear the value. - */ - public void setFilename(String filename) { - this.filename = filename; - } - - /** - * Get the type of the file to post. - * - * @return The filetype, or null if the value is not set. - */ - public String getFiletype() { - return filetype; - } - - /** - * Set the type of the file to post. - * - * @param filetype The value, or null to clear the value. - */ - public void setFiletype(String filetype) { - this.filetype = filetype; - } - - /** - * Determine if the tool is to be run in GUI mode. - * - * @return True if the tool is set for GUI mode. - */ - public boolean isGuiMode() { - return guiMode; - } - - /** - * Set the tool to run in GUI mode. - * - * @param guiMode True if the tool is to run in gui mode. - */ - public void setGuiMode(boolean guiMode) { - this.guiMode = guiMode; - } - - /** - * Get the MD5 setting. True if the tool is to use MD5 for post operations. - * - * @return The MD5 setting. - */ - public boolean isMd5() { - return md5; - } - - /** - * Set the MD5 setting. - * - * @param md5 True if the tool should use MD5 for post operations. - */ - public void setMd5(boolean md5) { - this.md5 = md5; - } - - /** - * Determine if the NoOp header should be sent. - * - * @return True if the header should be sent. - */ - public boolean isNoOp() { - return noOp; - } - - /** - * Set the NoOp setting. - * - * @param noOp True if the NoOp header should be used. - */ - public void setNoOp(boolean noOp) { - this.noOp = noOp; - } - - /** - * Determine if the verbose option is set. - * - * @return True if verbose option is set. - */ - public boolean isVerbose() { - return verbose; - } - - /** - * Set the verbose option. - * - * @param verbose True if verbose should be set. - */ - public void setVerbose(boolean verbose) { - this.verbose = verbose; - } - - /** - * Get the onBehalfOf value. - * - * @return The value, or null to clear the value. - */ - public String getOnBehalfOf() { - return onBehalfOf; - } - - /** - * Set the onBehalf of Value. - * - * @param onBehalfOf The value, or null to clear the value. - */ - public void setOnBehalfOf(String onBehalfOf) { - this.onBehalfOf = onBehalfOf; - } - - /** - * Get the format namespace value. - * - * @return The value, or null if the value is not set. - */ - public String getFormatNamespace() { - return formatNamespace; - } - - /** - * Set the format namespace value. - * - * @param formatNamespace The value, or null to clear the value. - */ - public void setFormatNamespace(String formatNamespace) { - this.formatNamespace = formatNamespace; - } - - /** - * Get the checksum error value. - * - * @return True if an error should be introduced into the checksum. - */ - public boolean getChecksumError() { - return checksumError; - } - - /** - * Set the checksum error value. - * - * @param checksumError True if the error should be introduced. - */ - public void setChecksumError(boolean checksumError) { - this.checksumError = checksumError; - } - - /** - * Get the current slug header. - * - * @return The slug value, or null if the value is not set. - */ - public String getSlug() { - return this.slug; - } - - /** - * Set the text that is to be used for the slug header. - * - * @param slug The value, or null to clear the value. - */ - public void setSlug(String slug) { - this.slug = slug; - } - - /** - * Get the list of post destinations. - * - * @return An iterator over the list of PostDestination objects. - */ - public Iterator getMultiPost() { - return multiPost.iterator(); - } - - - /** - * Determine if the noCapture option is set. This indicates that the code - * should not attempt to redirect stdout and stderr to a different output - * destination. Intended for use in a GUI client. - * - * @return The noCapture setting. True if set. - */ - public boolean isNoCapture() { - return noCapture; - } -} diff --git a/dspace-sword/src/main/java/org/purl/sword/client/ClientType.java b/dspace-sword/src/main/java/org/purl/sword/client/ClientType.java deleted file mode 100644 index 11474782c1cf..000000000000 --- a/dspace-sword/src/main/java/org/purl/sword/client/ClientType.java +++ /dev/null @@ -1,23 +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.purl.sword.client; - -/** - * Interface for a client. This contains a single method that allows the factory - * to pass a set of command line options to the client. - * - * @author Neil Taylor - */ -public interface ClientType { - /** - * Run the client, processing the specified options. - * - * @param options The options extracted from the command line. - */ - public void run(ClientOptions options); -} diff --git a/dspace-sword/src/main/java/org/purl/sword/client/CmdClient.java b/dspace-sword/src/main/java/org/purl/sword/client/CmdClient.java deleted file mode 100644 index 842e9d483dca..000000000000 --- a/dspace-sword/src/main/java/org/purl/sword/client/CmdClient.java +++ /dev/null @@ -1,445 +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.purl.sword.client; - -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStreamReader; -import java.net.MalformedURLException; -import java.net.URL; -import java.util.Iterator; -import java.util.List; - -import org.apache.logging.log4j.Logger; -import org.purl.sword.atom.Author; -import org.purl.sword.atom.Content; -import org.purl.sword.atom.Contributor; -import org.purl.sword.atom.Generator; -import org.purl.sword.atom.Link; -import org.purl.sword.atom.Rights; -import org.purl.sword.atom.Summary; -import org.purl.sword.atom.Title; -import org.purl.sword.base.Collection; -import org.purl.sword.base.DepositResponse; -import org.purl.sword.base.SWORDEntry; -import org.purl.sword.base.ServiceDocument; -import org.purl.sword.base.SwordAcceptPackaging; -import org.purl.sword.base.Workspace; - -/** - * Example implementation of a command line client. This can send out service - * document requests and print out the results and process posting a file to - * either a single or multiple destinations. The command line options are - * initialised prior to calling the class. The options are passed into the - * run(ClientOptions) method. - * - * @author Neil Taylor - */ -public class CmdClient implements ClientType { - /** - * The client that is used to process the service and post requests. - */ - private SWORDClient client; - - /** - * List of the options that can be specified on the command line. - */ - private ClientOptions options; - - /** - * The logger. - */ - private static Logger log = org.apache.logging.log4j.LogManager.getLogger(CmdClient.class); - - /** - * Create a new instance of the class and create an instance of the - * client. - */ - public CmdClient() { - client = new Client(); - } - - /** - * Process the options that have been initialised from the command line. - * This will call one of service(), post() or multiPost(). - */ - public void process() { - if (options.getProxyHost() != null) { - client.setProxy(options.getProxyHost(), options.getProxyPort()); - } - - try { - String accessType = options.getAccessType(); - if (ClientOptions.TYPE_SERVICE.equals(accessType)) { - service(); - } else if (ClientOptions.TYPE_POST.equals(accessType)) { - post(); - } else if (ClientOptions.TYPE_MULTI_POST.equals(accessType)) { - System.out.println("checking multi-post"); - multiPost(); - } else { - System.out.println("Access type not recognised."); - } - - } catch (MalformedURLException mex) { - System.out - .println("The specified href was not valid: " + options.getHref() + " message: " + mex.getMessage()); - } catch (SWORDClientException ex) { - System.out.println("Exception: " + ex.getMessage()); - log.error("Unable to process request", ex); - } - } - - /** - * Process the service operation. Output the results of the service request. - * - * @throws SWORDClientException if there is an error processing the service request. - * @throws MalformedURLException if there is an error with the URL for the service request. - */ - private void service() - throws SWORDClientException, MalformedURLException { - String href = options.getHref(); - initialiseServer(href, options.getUsername(), options.getPassword()); - - ServiceDocument document = client.getServiceDocument(href, options.getOnBehalfOf()); - Status status = client.getStatus(); - System.out.println("The status is: " + status); - - if (status.getCode() == 200) { - log.debug("message is: " + document.marshall()); - - System.out.println("\nThe following Details were retrieved: "); - System.out.println("SWORD Version: " - + document.getService().getVersion()); - System.out.println("Supports NoOp? " + document.getService().isNoOp()); - System.out.println("Supports Verbose? " - + document.getService().isVerbose()); - System.out.println("Max Upload File Size " - + document.getService().getMaxUploadSize() + " kB"); - - Iterator workspaces = document.getService().getWorkspaces(); - for (; workspaces.hasNext(); ) { - Workspace workspace = workspaces.next(); - System.out.println("\nWorkspace Title: '" - + workspace.getTitle() + "'"); - - System.out.println("\n+ Collections ---"); - // process the collections - Iterator collections = workspace - .collectionIterator(); - for (; collections.hasNext(); ) { - Collection collection = collections.next(); - System.out.println("\nCollection location: " - + collection.getLocation()); - System.out.println("Collection title: " - + collection.getTitle()); - System.out - .println("Abstract: " + collection.getAbstract()); - System.out.println("Collection Policy: " - + collection.getCollectionPolicy()); - System.out.println("Treatment: " - + collection.getTreatment()); - System.out.println("Mediation: " - + collection.getMediation()); - - String[] accepts = collection.getAccepts(); - if (accepts != null && accepts.length == 0) { - System.out.println("Accepts: none specified"); - } else { - for (String s : accepts) { - System.out.println("Accepts: " + s); - } - } - List acceptsPackaging = collection.getAcceptPackaging(); - - StringBuilder acceptPackagingList = new StringBuilder(); - for (Iterator i = acceptsPackaging.iterator(); i.hasNext(); ) { - SwordAcceptPackaging accept = (SwordAcceptPackaging) i.next(); - acceptPackagingList.append(accept.getContent()).append(" (").append(accept.getQualityValue()) - .append("), "); - } - - System.out.println("Accepts Packaging: " + acceptPackagingList.toString()); - } - System.out.println("+ End of Collections ---"); - } - } - } - - /** - * Perform a post. If any of the destination URL, the filename and the - * filetype are missing, the user will be prompted to enter the values. - * - * @throws SWORDClientException if there is an error processing the post for a requested - * destination. - * @throws MalformedURLException if there is an error with the URL for the post. - */ - private void post() - throws SWORDClientException, MalformedURLException { - String url = options.getHref(); - if (url == null) { - url = readLine("Please enter the URL for the deposit: "); - } - - initialiseServer(url, options.getUsername(), options.getPassword()); - String file = options.getFilename(); - if (file == null) { - file = readLine("Please enter the filename to deposit: "); - } - String type = options.getFiletype(); - if (type == null) { - type = readLine("Please enter the file type, e.g. application/zip: "); - } - - PostMessage message = new PostMessage(); - message.setFilepath(file); - message.setDestination(url); - message.setFiletype(type); - message.setUseMD5(options.isMd5()); - message.setVerbose(options.isVerbose()); - message.setNoOp(options.isNoOp()); - message.setFormatNamespace(options.getFormatNamespace()); - message.setOnBehalfOf(options.getOnBehalfOf()); - message.setChecksumError(options.getChecksumError()); - message.setUserAgent(ClientConstants.SERVICE_NAME); - - processPost(message); - - } - - /** - * Perform a multi-post. Iterate over the list of -dest arguments in the command line - * options. For each -dest argument, attempt to post the file to the server. - * - * @throws SWORDClientException if there is an error processing the post for a requested - * destination. - * @throws MalformedURLException if there is an error with the URL for the post. - */ - private void multiPost() - throws SWORDClientException, MalformedURLException { - // request the common information - String file = options.getFilename(); - if (file == null) { - file = readLine("Please enter the filename to deposit: "); - } - String type = options.getFiletype(); - if (type == null) { - type = readLine("Please enter the file type, e.g. application/zip: "); - } - - // process this information for each of the specified destinations - PostDestination destination; - String url = null; - - Iterator iterator = options.getMultiPost(); - while (iterator.hasNext()) { - destination = iterator.next(); - url = destination.getUrl(); - initialiseServer(url, destination.getUsername(), destination.getPassword()); - - String onBehalfOf = destination.getOnBehalfOf(); - if (onBehalfOf == null) { - onBehalfOf = ""; - } else { - onBehalfOf = " on behalf of: " + onBehalfOf; - } - - System.out.println("Sending file to: " + url + " for: " + destination.getUsername() + - onBehalfOf); - PostMessage message = new PostMessage(); - message.setFilepath(file); - message.setDestination(url); - message.setFiletype(type); - message.setUseMD5(options.isMd5()); - message.setVerbose(options.isVerbose()); - message.setNoOp(options.isNoOp()); - message.setFormatNamespace(options.getFormatNamespace()); - message.setOnBehalfOf(destination.getOnBehalfOf()); - message.setChecksumError(options.getChecksumError()); - message.setUserAgent(ClientConstants.SERVICE_NAME); - - processPost(message); - } - - } - - /** - * Process the post response. The message contains the list of arguments - * for the post. The method will then print out the details of the - * response. - * - * @param message The post options. - * @throws SWORDClientException if there is an error accessing the - * post response. - */ - protected void processPost(PostMessage message) - throws SWORDClientException { - DepositResponse response = client.postFile(message); - - System.out.println("The status is: " + client.getStatus()); - - if (response != null) { - log.debug("message is: " + response.marshall()); - - // iterate over the data and output it - SWORDEntry entry = response.getEntry(); - - - System.out.println("Id: " + entry.getId()); - Title title = entry.getTitle(); - if (title != null) { - System.out.print("Title: " + title.getContent() + " type: "); - if (title.getType() != null) { - System.out.println(title.getType().toString()); - } else { - System.out.println("Not specified."); - } - } - - // process the authors - Iterator authors = entry.getAuthors(); - while (authors.hasNext()) { - Author author = authors.next(); - System.out.println("Author - " + author.toString()); - } - - Iterator categories = entry.getCategories(); - while (categories.hasNext()) { - System.out.println("Category: " + categories.next()); - } - - Iterator contributors = entry.getContributors(); - while (contributors.hasNext()) { - Contributor contributor = contributors.next(); - System.out.println("Contributor - " + contributor.toString()); - } - - Iterator links = entry.getLinks(); - while (links.hasNext()) { - Link link = links.next(); - System.out.println(link.toString()); - } - - Generator generator = entry.getGenerator(); - if (generator != null) { - System.out.println("Generator - " + generator.toString()); - } else { - System.out.println("There is no generator"); - } - - System.out.println("Published: " + entry.getPublished()); - - Content content = entry.getContent(); - if (content != null) { - System.out.println(content.toString()); - } else { - System.out.println("There is no content element."); - } - - Rights right = entry.getRights(); - if (right != null) { - System.out.println(right.toString()); - } else { - System.out.println("There is no right element."); - } - - Summary summary = entry.getSummary(); - if (summary != null) { - - System.out.println(summary.toString()); - } else { - System.out.println("There is no summary element."); - } - - System.out.println("Update: " + entry.getUpdated()); - System.out.println("Published: " + entry.getPublished()); - System.out.println("Verbose Description: " + entry.getVerboseDescription()); - System.out.println("Treatment: " + entry.getTreatment()); - System.out.println("Packaging: " + entry.getPackaging()); - - if (entry.isNoOpSet()) { - System.out.println("NoOp: " + entry.isNoOp()); - } - } else { - System.out.println("No valid Entry document was received from the server"); - } - } - - /** - * Initialise the server. Set the server that will be connected to and - * initialise any username and password. If the username and password are - * either null or contain empty strings, the user credentials will be cleared. - * - * @param location The location to connect to. This is a URL, of the format, - * http://a.host.com:port/. The host name and port number will - * be extracted. If the port is not specified, a default port of - * 80 will be used. - * @param username The username. If this is null or an empty string, the basic - * credentials will be cleared. - * @param password The password. If this is null or an empty string, the basic - * credentials will be cleared. - * @throws MalformedURLException if there is an error processing the URL. - */ - private void initialiseServer(String location, String username, String password) - throws MalformedURLException { - URL url = new URL(location); - int port = url.getPort(); - if (port == -1) { - port = 80; - } - - client.setServer(url.getHost(), port); - - if (username != null && username.length() > 0 && - password != null && password.length() > 0) { - log.info("Setting the username/password: " + username + " " - + password); - client.setCredentials(username, password); - } else { - client.clearCredentials(); - } - } - - /** - * Read a line of text from System.in. If there is an error reading - * from the input, the prompt will be redisplayed and the user asked - * to try again. - * - * @param prompt The prompt to display before the prompt. - * @return The string that is read from the line. - */ - private String readLine(String prompt) { - BufferedReader reader = new BufferedReader(new InputStreamReader( - System.in)); - String result = null; - - boolean ok = false; - while (!ok) { - try { - System.out.print(prompt); - System.out.flush(); - result = reader.readLine(); - ok = true; - } catch (IOException ex) { - System.out.println("There was an error with your input. Please try again."); - } - } - - return result; - } - - /** - * Run the client and process the specified options. - * - * @param options The command line options. - */ - public void run(ClientOptions options) { - this.options = options; - process(); - } -} diff --git a/dspace-sword/src/main/java/org/purl/sword/client/DebugOutputStream.java b/dspace-sword/src/main/java/org/purl/sword/client/DebugOutputStream.java deleted file mode 100644 index dfd8b6c60f26..000000000000 --- a/dspace-sword/src/main/java/org/purl/sword/client/DebugOutputStream.java +++ /dev/null @@ -1,44 +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.purl.sword.client; - -import java.io.IOException; -import java.io.OutputStream; - -/** - * A stream that will write any output to the specified panel. - * - * @author Neil Taylor - */ -public class DebugOutputStream extends OutputStream { - /** - * Panel that will display the messages. - */ - private MessageOutputPanel panel; - - /** - * Create a new instance and specify the panel that will receive the output. - * - * @param panel The panel. - */ - public DebugOutputStream(MessageOutputPanel panel) { - this.panel = panel; - } - - /** - * Override the write method from OutputStream. Capture the char and - * send it to the panel. - * - * @param arg0 The output character, expressed as an integer. - * @see java.io.OutputStream#write(int) - */ - public void write(int arg0) throws IOException { - panel.addCharacter(Character.valueOf((char) arg0)); - } - -} diff --git a/dspace-sword/src/main/java/org/purl/sword/client/MessageOutputPanel.java b/dspace-sword/src/main/java/org/purl/sword/client/MessageOutputPanel.java deleted file mode 100644 index e703f79ccccb..000000000000 --- a/dspace-sword/src/main/java/org/purl/sword/client/MessageOutputPanel.java +++ /dev/null @@ -1,138 +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/ - */ - -/** - * Copyright (c) 2007, Aberystwyth University - * - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * - Redistributions of source code must retain the above - * copyright notice, this list of conditions and the - * following disclaimer. - * - * - Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * - * - Neither the name of the Centre for Advanced Software and - * Intelligent Systems (CASIS) nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR - * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF - * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ -package org.purl.sword.client; - -import java.awt.GridBagConstraints; -import java.awt.GridBagLayout; -import java.awt.Insets; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import javax.swing.JButton; -import javax.swing.JPanel; -import javax.swing.JScrollPane; -import javax.swing.JTextArea; - -/** - * Panel to display output messages. Text or characters can be sent to the - * panel for display. The panel also includes a button to clear any - * text that is currently displayed. - * - * @author Neil Taylor - */ -public class MessageOutputPanel extends JPanel - implements ActionListener { - - /** - * The text area that displays the messages. - */ - private JTextArea messages = null; - - /** - * Create a new instance and initialise the panel. - */ - public MessageOutputPanel() { - super(); - - setLayout(new GridBagLayout()); - - messages = new JTextArea(); - - JScrollPane detailsPane = new JScrollPane(messages, - JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, - JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS); - JButton clearButton = new JButton("Clear"); - clearButton.addActionListener(this); - - //add components and set constraints - //dpc = details pane constraint - GridBagConstraints dpc = new GridBagConstraints(); - dpc.gridx = 0; - dpc.gridy = 0; - dpc.fill = GridBagConstraints.BOTH; - dpc.weightx = 0.75; - dpc.weighty = 0.45; - dpc.gridwidth = 2; - dpc.insets = new Insets(5, 5, 5, 5); - add(detailsPane, dpc); - - //cbc = clear button constraint - GridBagConstraints cbc = new GridBagConstraints(); - cbc.gridx = 1; - cbc.gridy = 1; - cbc.insets = new Insets(0, 0, 5, 5); - cbc.anchor = GridBagConstraints.LINE_END; - add(clearButton, cbc); - - } - - /** - * Add a message to the text area. The message will be added with a carriage return. - * - * @param message The message. - */ - public void addMessage(String message) { - messages.insert(message + "\n", messages.getDocument().getLength()); - } - - /** - * Add a single character to the text area. - * - * @param character The character. - */ - public void addCharacter(Character character) { - messages.insert(character.toString(), messages.getDocument().getLength()); - } - - /** - * Clear the text from the display. - * - * @param arg0 The action event. - * - * @see java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent) - */ - public void actionPerformed(ActionEvent arg0) { - messages.setText(""); - } -} diff --git a/dspace-sword/src/main/java/org/purl/sword/client/PostDestination.java b/dspace-sword/src/main/java/org/purl/sword/client/PostDestination.java deleted file mode 100644 index 3124f02b45d7..000000000000 --- a/dspace-sword/src/main/java/org/purl/sword/client/PostDestination.java +++ /dev/null @@ -1,140 +0,0 @@ -/** - * The contents of this file are subject to the license and copyright - * detailed in the LICENSE and NOTICE files at the root of the source - * tree and available online at - * - * http://www.dspace.org/license/ - */ -package org.purl.sword.client; - -/** - * Details for a destination. This is used to represent a destination. If - * expressed as a string, the destination looks like: - *
    - * <user>[<onBehalfOf>]:<password>@<url>
    - * 
    - * - * @author Neil Taylor - */ -public class PostDestination { - /** - * URL for the post destination. - */ - private String url; - - /** - * The username. - */ - private String username; - - /** - * The password. - */ - private String password; - - /** - * The onBehalfOf ID. - */ - private String onBehalfOf; - - /** - * Create a new instance. - */ - public PostDestination() { - // No-Op - } - - /** - * Create a new instance. - * - * @param url The url. - * @param username The username. - * @param password The password. - * @param onBehalfOf The onBehalfOf id. - */ - public PostDestination(String url, String username, String password, String onBehalfOf) { - this.url = url; - this.username = username; - this.password = password; - this.onBehalfOf = onBehalfOf; - } - - /** - * @return the url - */ - public String getUrl() { - return url; - } - - /** - * @param url the url to set - */ - public void setUrl(String url) { - this.url = url; - } - - /** - * @return the username - */ - public String getUsername() { - return username; - } - - /** - * @param username the username to set - */ - public void setUsername(String username) { - this.username = username; - } - - /** - * @return the password - */ - public String getPassword() { - return password; - } - - /** - * @param password the password to set - */ - public void setPassword(String password) { - this.password = password; - } - - /** - * @return the onBehalfOf - */ - public String getOnBehalfOf() { - return onBehalfOf; - } - - /** - * @param onBehalfOf the onBehalfOf to set - */ - public void setOnBehalfOf(String onBehalfOf) { - this.onBehalfOf = onBehalfOf; - } - - /** - * Create a string representation of this object. - * - * @return The string. - */ - public String toString() { - StringBuffer buffer = new StringBuffer(); - buffer.append(username); - if (onBehalfOf != null) { - buffer.append("["); - buffer.append(onBehalfOf); - buffer.append("]"); - } - - if (password != null) { - buffer.append(":******"); - } - buffer.append("@"); - buffer.append(url); - - return buffer.toString(); - } -} diff --git a/dspace-sword/src/main/java/org/purl/sword/client/PostDialog.java b/dspace-sword/src/main/java/org/purl/sword/client/PostDialog.java deleted file mode 100644 index 84815b327e3b..000000000000 --- a/dspace-sword/src/main/java/org/purl/sword/client/PostDialog.java +++ /dev/null @@ -1,599 +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/ - */ - -/** - * Copyright (c) 2007, Aberystwyth University - * - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * - Redistributions of source code must retain the above - * copyright notice, this list of conditions and the - * following disclaimer. - * - * - Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * - * - Neither the name of the Centre for Advanced Software and - * Intelligent Systems (CASIS) nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR - * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF - * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ -package org.purl.sword.client; - -import java.awt.BorderLayout; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.io.File; -import javax.swing.DefaultListModel; -import javax.swing.JButton; -import javax.swing.JCheckBox; -import javax.swing.JFileChooser; -import javax.swing.JFrame; -import javax.swing.JLabel; -import javax.swing.JList; -import javax.swing.JOptionPane; -import javax.swing.JPanel; -import javax.swing.JPasswordField; -import javax.swing.JScrollPane; -import javax.swing.event.ChangeEvent; -import javax.swing.event.ChangeListener; - -/** - * Dialog for users to enter details of post destinations. - * - * @author Neil Taylor - */ -public class PostDialog - implements ActionListener, ChangeListener { - /** - * label for the browse command. - */ - protected static final String BROWSE = "browse"; - - /** - * label for the add command. - */ - protected static final String ADD = "add"; - - /** - * label for the edit command. - */ - protected static final String EDIT = "edit"; - - /** - * label for the delete command. - */ - protected static final String DELETE = "delete"; - - /** - * label for the clear command. - */ - protected static final String CLEAR = "clear"; - - /** - * Username combo box. - */ - private SWORDComboBox username; - - /** - * Post Location combo box. - */ - private SWORDComboBox postLocation; - - /** - * Password field. - */ - private JPasswordField password; - - /** - * The file combo box. - */ - private SWORDComboBox file; - - /** - * The filetype combo box. - */ - private SWORDComboBox fileType; - - /** - * The onBehalfOf combo box. - */ - private SWORDComboBox onBehalfOf; - - /** - * The md5 checkbox. - */ - private JCheckBox useMD5; - - /** - * The corruptMD5 checkbox. - */ - private JCheckBox corruptMD5; - - /** - * The corruptRequest checkbox. - */ - private JCheckBox corruptRequest; - - /** - * The useNoOp checkbox. - */ - private JCheckBox useNoOp; - - /** - * The verbose checkbox. - */ - private JCheckBox useVerbose; - - /** - * The format namespace combo box. - */ - private SWORDComboBox formatNamespace; - - /** - * The list of post destinations. - */ - private JList list; - - /** - * The parent frame for the dialog that is displayed. - */ - private JFrame parentFrame = null; - - /** - * Array that lists the labels for the buttons on the panel. - */ - private static Object[] options = {"Post File", "Cancel"}; - - /** - * The panel that holds the controls to show. - */ - private JPanel controls = null; - - /** - * - * @param parentFrame the parent of this dialog. - */ - public PostDialog(JFrame parentFrame) { - this.parentFrame = parentFrame; - controls = createControls(); - } - - /** - * Show the dialog with ok and cancel options. - * @return The return value from displaying JOptionPane. Either - * JOptionPane.OK_OPTION or JOptionPane.CANCEL_OPTION. - */ - public int show() { - int result = JOptionPane.showOptionDialog(parentFrame, - controls, - "Post Document", - JOptionPane.OK_CANCEL_OPTION, - JOptionPane.PLAIN_MESSAGE, - null, - options, - null); - - if (result == JOptionPane.OK_OPTION) { - // update the combo boxes with the values - username.updateList(); - file.updateList(); - fileType.updateList(); - onBehalfOf.updateList(); - formatNamespace.updateList(); - } - - return result; - } - - /** - * Create the controls for the main panel. - * - * @return The panel. - */ - protected final JPanel createControls() { - file = new SWORDComboBox(); - JPanel filePanel = new JPanel(new BorderLayout()); - filePanel.add(file, BorderLayout.CENTER); - JButton browse = new JButton("Browse..."); - browse.setActionCommand(BROWSE); - browse.addActionListener(this); - - filePanel.add(browse, BorderLayout.SOUTH); - - fileType = new SWORDComboBox(); - String type = "application/zip"; - fileType.addItem(type); - fileType.setSelectedItem(type); - - // controls that will be used in the second dialog - postLocation = new SWORDComboBox(); - username = new SWORDComboBox(); - password = new JPasswordField(); - onBehalfOf = new SWORDComboBox(); - - - useMD5 = new JCheckBox(); - useMD5.addChangeListener(this); - corruptMD5 = new JCheckBox(); - corruptRequest = new JCheckBox(); - useNoOp = new JCheckBox(); - useVerbose = new JCheckBox(); - formatNamespace = new SWORDComboBox(); - - JLabel fileLabel = new JLabel("File:", JLabel.TRAILING); - JLabel fileTypeLabel = new JLabel("File Type:", JLabel.TRAILING); - JLabel useMD5Label = new JLabel("Use MD5:", JLabel.TRAILING); - JLabel corruptMD5Label = new JLabel("Corrupt MD5:", JLabel.TRAILING); - JLabel corruptRequestLabel = new JLabel("Corrupt Request:", JLabel.TRAILING); - //JLabel corruptMD5Label = new JLabel("Corrupt MD5:", JLabel.TRAILING); - JLabel useNoOpLabel = new JLabel("Use noOp:", JLabel.TRAILING); - JLabel useVerboseLabel = new JLabel("Use verbose:", JLabel.TRAILING); - JLabel formatNamespaceLabel = new JLabel("X-Packaging:", JLabel.TRAILING); - JLabel userAgentLabel = new JLabel("User Agent:", JLabel.TRAILING); - JLabel userAgentNameLabel = new JLabel(ClientConstants.SERVICE_NAME, JLabel.LEADING); - - SWORDFormPanel panel = new SWORDFormPanel(); - panel.addFirstRow(new JLabel("Please enter the details for the post operation")); - - JPanel destinations = createDestinationsPanel(); - - panel.addRow(new JLabel("Destinations:"), destinations); - panel.addRow(fileLabel, filePanel); - panel.addRow(fileTypeLabel, fileType); - panel.addRow(useMD5Label, useMD5); - panel.addRow(corruptMD5Label, corruptMD5); - panel.addRow(corruptRequestLabel, corruptRequest); - panel.addRow(useNoOpLabel, useNoOp); - panel.addRow(useVerboseLabel, useVerbose); - panel.addRow(formatNamespaceLabel, formatNamespace); - panel.addRow(userAgentLabel, userAgentNameLabel); - - return panel; - } - - /** - * Create the destinations panel. This contains a list and four buttons - * to operate on values in the list. - * - * @return The panel containing the controls. - */ - protected JPanel createDestinationsPanel() { - DefaultListModel model = new DefaultListModel(); - list = new JList(model); - JScrollPane jsp = new JScrollPane(list); - - JPanel destinations = new JPanel(new BorderLayout()); - destinations.add(jsp, BorderLayout.CENTER); - JPanel destinationButtons = new JPanel(); - - JButton addButton = new JButton("Add"); - addButton.setActionCommand(ADD); - addButton.addActionListener(this); - - JButton editButton = new JButton("Edit"); - editButton.setActionCommand(EDIT); - editButton.addActionListener(this); - - JButton deleteButton = new JButton("Delete"); - deleteButton.setActionCommand(DELETE); - deleteButton.addActionListener(this); - - JButton clearButton = new JButton("Clear"); - clearButton.setActionCommand(CLEAR); - clearButton.addActionListener(this); - - destinationButtons.add(addButton); - destinationButtons.add(editButton); - destinationButtons.add(deleteButton); - destinationButtons.add(clearButton); - - destinations.add(destinationButtons, BorderLayout.SOUTH); - - return destinations; - } - - /** - * Handle the button click to select a file to upload. - */ - public void actionPerformed(ActionEvent evt) { - String cmd = evt.getActionCommand(); - - if (BROWSE.equals(cmd)) { - JFileChooser chooser = new JFileChooser(); - chooser.setCurrentDirectory(new File(System.getProperty("user.dir"))); - int returnVal = chooser.showOpenDialog(parentFrame); - if (returnVal == JFileChooser.APPROVE_OPTION) { - file.setSelectedItem(chooser.getSelectedFile().getAbsolutePath()); - } - } else if (ADD.equals(cmd)) { - PostDestination dest = showDestinationDialog(null); - if (dest != null) { - ((DefaultListModel) list.getModel()).addElement(dest); - } - } else if (EDIT.equals(cmd)) { - PostDestination dest = (PostDestination) list.getSelectedValue(); - if (dest != null) { - showDestinationDialog(dest); - list.repaint(); - } - } else if (DELETE.equals(cmd)) { - if (list.getSelectedIndex() != -1) { - ((DefaultListModel) list.getModel()).removeElementAt(list.getSelectedIndex()); - } - } else if (CLEAR.equals(cmd)) { - ((DefaultListModel) list.getModel()).clear(); - } - } - - /** - * Show the destination dialog. This is used to enter the URL, - * username, password and onBehalfOf name for a destination. - * - * @param destination The post destination. If this is not null, the values - * in the object are used to set the current values - * in the dialog controls. - * @return The post destination value. - */ - public PostDestination showDestinationDialog(PostDestination destination) { - SWORDFormPanel panel = new SWORDFormPanel(); - panel.addFirstRow(new JLabel("Please enter the details for the post operation")); - - JLabel postLabel = new JLabel("Post Location:", JLabel.TRAILING); - JLabel userLabel = new JLabel("Username:", JLabel.TRAILING); - JLabel passwordLabel = new JLabel("Password:", JLabel.TRAILING); - JLabel onBehalfOfLabel = new JLabel("On Behalf Of:", JLabel.TRAILING); - - panel.addRow(postLabel, postLocation); - panel.addRow(userLabel, username); - panel.addRow(passwordLabel, password); - panel.addRow(onBehalfOfLabel, onBehalfOf); - - if (destination != null) { - postLocation.insertItem(destination.getUrl()); - username.insertItem(destination.getUsername()); - password.setText(destination.getPassword()); - onBehalfOf.insertItem(destination.getOnBehalfOf()); - } else { - String s = ""; - postLocation.insertItem(s); - //postLocation.setSelectedItem(s); - username.insertItem(s); - username.setSelectedItem(s); - password.setText(s); - onBehalfOf.insertItem(s); - onBehalfOf.setSelectedItem(s); - } - - int result = JOptionPane.showOptionDialog(null, - panel, - "Destination", - JOptionPane.OK_CANCEL_OPTION, - JOptionPane.PLAIN_MESSAGE, - null, - new String[] {"OK", "Cancel"}, - null); - - if (result == JOptionPane.OK_OPTION) { - postLocation.updateList(); - username.updateList(); - onBehalfOf.updateList(); - - if (destination == null) { - destination = new PostDestination(); - } - - destination.setUrl(postLocation.getText()); - destination.setUsername(username.getText()); - String pass = new String(password.getPassword()); - if (pass.length() > 0) { - destination.setPassword(pass); - } else { - destination.setPassword(null); - } - - String obo = onBehalfOf.getText(); - if (obo.length() > 0) { - destination.setOnBehalfOf(onBehalfOf.getText()); - } else { - destination.setOnBehalfOf(null); - } - - } - - return destination; - } - - /** - * Get the list of Post Destinations. - * @return The destinations. - */ - public PostDestination[] getDestinations() { - DefaultListModel model = (DefaultListModel) list.getModel(); - PostDestination[] destinations = new PostDestination[model.size()]; - for (int i = 0; i < model.size(); i++) { - destinations[i] = (PostDestination) model.get(i); - } - return destinations; - } - - /** - * Get the file details. - * @return The value. - */ - public String getFile() { - return file.getText(); - } - - /** - * Get the filetype value. - * @return The value. - */ - public String getFileType() { - return fileType.getText(); - } - - /** - * Get the onBehalfOf value. - * @return The value. - */ - public String getOnBehalfOf() { - return onBehalfOf.getText(); - } - - /** - * Get the format namespace value. - * @return The value. - */ - public String getFormatNamespace() { - return formatNamespace.getText(); - } - - /** - * Determine if the MD5 checkbox is selected. - * - * @return True if the MD5 checkbox is selected. - */ - public boolean useMd5() { - return useMD5.isSelected(); - } - - /** - * Determine if the noOp checkbox is selected. - * - * @return True if the checkbox is selected. - */ - public boolean useNoOp() { - return useNoOp.isSelected(); - } - - /** - * Determine if the verbose checkbox is selected. - * - * @return True if the checkbox is selected. - */ - public boolean useVerbose() { - return useVerbose.isSelected(); - } - - /** - * Get the post location. - * @return The post location. - */ - public String getPostLocation() { - return postLocation.getText(); - } - - /** - * Determine if the MD5 hash should be corrupted. - * @return True if the corrupt MD5 checkbox is selected. The MD5 checkbox - * must also be selected. - */ - public boolean corruptMD5() { - return (corruptMD5.isEnabled() && corruptMD5.isSelected()); - } - - /** - * Determine if the POST request should be corrupted. - * @return True if the corrupt request checkbox is selected. - */ - public boolean corruptRequest() { - return (corruptRequest.isSelected()); - } - - /** - * Detect a state change event for the checkbox. - * - * @param evt The event. - */ - public void stateChanged(ChangeEvent evt) { - corruptMD5.setEnabled(useMD5.isSelected()); - } - - /** - * Add a list of user ids. - * - * @param users The user ids. - */ - public void addUserIds(String[] users) { - username.insertItems(users); - } - - /** - * Add a list of deposit URLs. - * - * @param deposits The URLs. - */ - public void addDepositUrls(String[] deposits) { - postLocation.insertItems(deposits); - } - - /** - * Add a list of onBehalfOf names. - * - * @param users The names. - */ - public void addOnBehalfOf(String[] users) { - onBehalfOf.insertItems(users); - } - - /** - * Add the list of formatNamespace strings. - * - * @param namespaces list of strings. - */ - public void addFormatNamespaces(String[] namespaces) { - formatNamespace.insertItems(namespaces); - } - - /** - * Add a list of file types. - * - * @param types The file types. - */ - public void addFileTypes(String[] types) { - fileType.insertItems(types); - } - - /** - * Add a list of file names. - * @param files The list of files. - */ - public void addFiles(String[] files) { - file.insertItems(files); - } - - /** - * Set the deposit location. - * - * @param location The location. - */ - public void setDepositLocation(String location) { - postLocation.insertItem(location); - postLocation.setSelectedItem(location); - } -} diff --git a/dspace-sword/src/main/java/org/purl/sword/client/PostMessage.java b/dspace-sword/src/main/java/org/purl/sword/client/PostMessage.java deleted file mode 100644 index a44f21ce2b88..000000000000 --- a/dspace-sword/src/main/java/org/purl/sword/client/PostMessage.java +++ /dev/null @@ -1,344 +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/ - */ - -/** - * Copyright (c) 2007, Aberystwyth University - * - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * - Redistributions of source code must retain the above - * copyright notice, this list of conditions and the - * following disclaimer. - * - * - Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * - * - Neither the name of the Centre for Advanced Software and - * Intelligent Systems (CASIS) nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR - * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF - * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ -package org.purl.sword.client; - -import java.io.File; - -/** - * Represents the details of a post to a server. The message holds all of the possible values - * that are to be sent from the client to the server. Not all elements of the message - * must be filled in. Any required fields are defined in the current SWORD specification. - * - * @author Neil Taylor - */ -public class PostMessage { - /** - * The local filepath for the file to upload/deposit. - */ - private String filepath; - - /** - * The URL of the destination server. - */ - private String destination; - - /** - * The filetype of the package that is to be uploaded. - */ - private String filetype; - - /** - * The string with the username if the deposit is on behalf of another user. - */ - private String onBehalfOf; - - /** - * True if an MD5 checksum should be sent with the deposit. - */ - private boolean useMD5; - - /** - * True if the deposit is a test and should not result in an actual deposit. - */ - private boolean noOp; - - /** - * True if the verbose operation is requested. - */ - private boolean verbose; - - /** - * The packaging format for the deposit. - */ - private String packaging; - - /** - * True if the deposit should simulate a checksum error. The client should check this - * field to determine if a correct MD5 checksum should be sent or whether the checksum should - * be modified so that it generates an error at the server. - */ - private boolean checksumError; - - /** - * True if the deposit should corrupt the POST header. The client should check this - * field to determine if a correct header should be sent or whether the header should - * be modified so that it generates an error at the server. - */ - private boolean corruptRequest; - - /** - * The Slug header value. - */ - private String slug; - - /** - * The user agent name - */ - private String userAgent; - - /** - * Get the filepath. - * - * @return The filepath. - */ - public String getFilepath() { - return filepath; - } - - /** - * Get the filename. This is the last element of the filepath - * that has been set in this class. - * - * @return filename - */ - public String getFilename() { - File file = new File(filepath); - return file.getName(); - } - - /** - * Set the filepath. - * - * @param filepath The filepath. - */ - public void setFilepath(String filepath) { - this.filepath = filepath; - } - - /** - * Get the destination collection. - * - * @return The collection. - */ - public String getDestination() { - return destination; - } - - /** - * Set the destination collection. - * - * @param destination The destination. - */ - public void setDestination(String destination) { - this.destination = destination; - } - - /** - * Get the filetype. - * @return The filetype. - */ - public String getFiletype() { - return filetype; - } - - /** - * Set the filetype. - * - * @param filetype The filetype. - */ - public void setFiletype(String filetype) { - this.filetype = filetype; - } - - /** - * Get the onBehalfOf value. - * - * @return The value. - */ - public String getOnBehalfOf() { - return onBehalfOf; - } - - /** - * Set the onBehalfOf value. - * - * @param onBehalfOf The value. - */ - public void setOnBehalfOf(String onBehalfOf) { - this.onBehalfOf = onBehalfOf; - } - - /** - * Get the MD5 status. - * @return The value. - */ - public boolean isUseMD5() { - return useMD5; - } - - /** - * Set the md5 state. - * - * @param useMD5 True if the message should use an MD5 checksum. - */ - public void setUseMD5(boolean useMD5) { - this.useMD5 = useMD5; - } - - /** - * Get the no-op state. - * - * @return The value. - */ - public boolean isNoOp() { - return noOp; - } - - /** - * Set the no-op state. - * - * @param noOp The no-op. - */ - public void setNoOp(boolean noOp) { - this.noOp = noOp; - } - - /** - * Get the verbose value. - * - * @return The value. - */ - public boolean isVerbose() { - return verbose; - } - - /** - * Set the verbose state. - * - * @param verbose True if the post message should send a - * verbose header. - */ - public void setVerbose(boolean verbose) { - this.verbose = verbose; - } - - /** - * Get the packaging format. - * - * @return The value. - */ - public String getPackaging() { - return packaging; - } - - /** - * Set the packaging format. - * - * @param packaging The packaging format. - */ - public void setFormatNamespace(String packaging) { - this.packaging = packaging; - } - - /** - * Get the status of the checksum error. - * - * @return True if the client should simulate a checksum error. - */ - public boolean getChecksumError() { - return checksumError; - } - - /** - * Set the state of the checksum error. - * - * @param checksumError True if the item should include a checksum error. - */ - public void setChecksumError(boolean checksumError) { - this.checksumError = checksumError; - } - - /** - * Get the status of the corrupt request flag. - * - * @return True if the client should corrupt the POST header. - */ - public boolean getCorruptRequest() { - return corruptRequest; - } - - /** - * Set the state of the corrupt request flag. - * - * @param corruptRequest True if the item should corrupt the POST header. - */ - public void setCorruptRequest(boolean corruptRequest) { - this.corruptRequest = corruptRequest; - } - - /** - * Set the Slug value. - * - * @param slug The value. - */ - public void setSlug(String slug) { - this.slug = slug; - } - - /** - * Get the Slug value. - * - * @return The Slug. - */ - public String getSlug() { - return this.slug; - } - - /** - * @return the userAgent - */ - public String getUserAgent() { - return userAgent; - } - - /** - * Set the user agent - * - * @param userAgent the userAgent to set - */ - public void setUserAgent(String userAgent) { - this.userAgent = userAgent; - } -} diff --git a/dspace-sword/src/main/java/org/purl/sword/client/PropertiesDialog.java b/dspace-sword/src/main/java/org/purl/sword/client/PropertiesDialog.java deleted file mode 100644 index 6d43cb68ef10..000000000000 --- a/dspace-sword/src/main/java/org/purl/sword/client/PropertiesDialog.java +++ /dev/null @@ -1,240 +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.purl.sword.client; - -import java.awt.BorderLayout; -import java.util.Enumeration; -import java.util.Properties; -import javax.swing.DefaultCellEditor; -import javax.swing.JFrame; -import javax.swing.JOptionPane; -import javax.swing.JPanel; -import javax.swing.JScrollPane; -import javax.swing.JTable; -import javax.swing.table.AbstractTableModel; -import javax.swing.table.TableCellEditor; - -/** - * Dialog that is used to edit the collection of properties. - * - * @author Neil Taylor, Suzana Barreto - */ -public class PropertiesDialog { - /** - * The parent frame for the dialog that is displayed. - */ - private JFrame parentFrame = null; - - /** - * Array that lists the labels for the buttons on the panel. - */ - private static Object[] options = {"OK", "Cancel"}; - - /** - * The panel that holds the controls to show. - */ - private JPanel controls = null; - - /** - * The configuration properties - */ - private Properties properties = null; - - /** - * Table that is used to display the list of properties. - */ - private JTable propertiesTable; - - /** - * Create a new instance. - * - * @param parentFrame The parent frame for the dialog. - * @param props The properties lisst to display - */ - public PropertiesDialog(JFrame parentFrame, Properties props) { - this.parentFrame = parentFrame; - properties = props; - controls = createControls(); - } - - /** - * Create the controls that are to be displayed in the system. - * - * @return A panel that contains the controls. - */ - protected final JPanel createControls() { - JPanel panel = new JPanel(new BorderLayout()); - propertiesTable = new JTable(new PropertiesModel()); - ((DefaultCellEditor) propertiesTable.getDefaultEditor(String.class)).setClickCountToStart(1); - JScrollPane scrollpane = new JScrollPane(propertiesTable, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, - JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS); - panel.add(scrollpane, BorderLayout.CENTER); - return panel; - } - - - /** - * Show the dialog and return the status code. - * - * @return The status code returned from the dialog. - */ - public int show() { - int result = JOptionPane.showOptionDialog(parentFrame, - controls, - "Edit Properties", - JOptionPane.OK_CANCEL_OPTION, - JOptionPane.PLAIN_MESSAGE, - null, - options, - null); - - // cancel any edit in the table. If there is a cell editing, the getEditingColumn will - // return a non-negative column number. This can be used to retreive the cell editor. - // The code then gets the default editor and calls the stopCellEditing. If custom - // editors are used, an additional check must be made to get the cell editor - // for a specific cell. - int column = propertiesTable.getEditingColumn(); - - if (column > -1) { - TableCellEditor editor = propertiesTable.getDefaultEditor(propertiesTable.getColumnClass(column)); - if (editor != null) { - editor.stopCellEditing(); - } - } - - return result; - } - - - /** - * A table model that is used to show the properties. The model links directly - * to the underlying properties object. As changes are made in the table, the - * corresponding changes are made in the properties object. The user can only - * edit the value column in the table. - */ - public class PropertiesModel extends AbstractTableModel { - /** - * Column names. - */ - private String columns[] = {"Property Name", "Value"}; - - /** - * Create a new instance of the model. If no properties object exists, - * a default model is created. Note, this will allow the table to - * continue editing, although this value will not be passed back to - * the calling window. - */ - public PropertiesModel() { - super(); - if (properties == null) { - properties = new Properties(); - } - } - - /** - * Get the number of columns. - * - * @return The number of columns. - */ - public int getColumnCount() { - return columns.length; - } - - /** - * Get the number of rows. - * - * @return The number of rows. - */ - public int getRowCount() { - return properties.size(); - } - - /** - * Get the value that is at the specified cell. - * - * @param row The row for the cell. - * @param col The column for the cell. - * @return The data value from the properties. - */ - public Object getValueAt(int row, int col) { - if (col == 0) { - return getKeyValue(row); - } else { - String key = getKeyValue(row); - return properties.get(key); - } - } - - /** - * Retrieve the column name for the specified column. - * - * @param col The column number. - * @return The column name. - */ - public String getColumnName(int col) { - return columns[col]; - } - - /** - * Retrieve the column class. - * - * @param col The column number. - * @return The class for the object found at the column position. - */ - public Class getColumnClass(int col) { - return getValueAt(0, col).getClass(); - } - - /** - * Determine if the cell can be edited. This model will only - * allow the second column to be edited. - * - * @param row The cell row. - * @param col The cell column. - * @return True if the cell can be edited. Otherwise, false. - */ - public boolean isCellEditable(int row, int col) { - if (col == 1) { - return true; - } - return false; - } - - /** - * Set the value for the specified cell. - * - * @param value The value to set. - * @param row The row for the cell. - * @param col The column. - */ - public void setValueAt(Object value, int row, int col) { - String key = getKeyValue(row); - properties.setProperty(key, ((String) value)); - fireTableCellUpdated(row, col); - } - - /** - * Get the Key value for the specified row. - * - * @param row The row. - * @return A string that shows the key value. - */ - public String getKeyValue(int row) { - int count = 0; - Enumeration k = properties.keys(); - while (k.hasMoreElements()) { - String key = (String) k.nextElement(); - if (count == row) { - return key; - } - count++; - } - return null; - } - } -} diff --git a/dspace-sword/src/main/java/org/purl/sword/client/SWORDClient.java b/dspace-sword/src/main/java/org/purl/sword/client/SWORDClient.java deleted file mode 100644 index 0df7fd53104a..000000000000 --- a/dspace-sword/src/main/java/org/purl/sword/client/SWORDClient.java +++ /dev/null @@ -1,85 +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.purl.sword.client; - -import org.purl.sword.base.DepositResponse; -import org.purl.sword.base.ServiceDocument; - -/** - * Interface for any SWORD client implementation. - */ -public interface SWORDClient { - /** - * Set the server that is to be contacted on the next access. - * - * @param server The name of the server, e.g. www.aber.ac.uk - * @param port The port number, e.g. 80. - */ - public void setServer(String server, int port); - - /** - * Set the user credentials that are to be used for subsequent accesses. - * - * @param username The username. - * @param password The password. - */ - public void setCredentials(String username, String password); - - /** - * Clear the credentials settings on the client. - */ - public void clearCredentials(); - - /** - * Set the proxy that is to be used for subsequent accesses. - * - * @param host The host name, e.g. cache.host.com. - * @param port The port, e.g. 8080. - */ - public void setProxy(String host, int port); - - /** - * Get the status result returned from the most recent network test. - * - * @return The status code and message. - */ - public Status getStatus(); - - /** - * Get a service document, specified in the URL. - * - * @param url The URL to connect to. - * @return A ServiceDocument that contains the Service details that were - * obained from the specified URL. - * @throws SWORDClientException If there is an error accessing the - * URL. - */ - public ServiceDocument getServiceDocument(String url) throws SWORDClientException; - - /** - * Get a service document, specified in the URL. The document is accessed on - * behalf of the specified user. - * - * @param url The URL to connect to. - * @param onBehalfOf The username for the onBehalfOf access. - * @return A ServiceDocument that contains the Service details that were - * obtained from the specified URL. - * @throws SWORDClientException If there is an error accessing the URL. - */ - public ServiceDocument getServiceDocument(String url, String onBehalfOf) throws SWORDClientException; - - /** - * Post a file to the specified destination URL. - * - * @param message The message that defines the requirements for the operation. - * @return A DespoitResponse if the response is successful. If there was an error, - * null should be returned. - * @throws SWORDClientException If there is an error accessing the URL. - */ - public DepositResponse postFile(PostMessage message) throws SWORDClientException; -} diff --git a/dspace-sword/src/main/java/org/purl/sword/client/SWORDClientException.java b/dspace-sword/src/main/java/org/purl/sword/client/SWORDClientException.java deleted file mode 100644 index 1355b9d4ab61..000000000000 --- a/dspace-sword/src/main/java/org/purl/sword/client/SWORDClientException.java +++ /dev/null @@ -1,42 +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.purl.sword.client; - -/** - * Represents an exception thrown by the SWORD Client. - * - * @author Neil Taylor - */ -public class SWORDClientException extends Exception { - /** - * Create a new exception, without a message. - */ - public SWORDClientException() { - super(); - } - - /** - * Create a new exception with the specified message. - * - * @param message The message. - */ - public SWORDClientException(String message) { - super(message); - } - - /** - * Create a new exception with the specified message and set - * the exception that generated this error. - * - * @param message The message. - * @param cause The original exception. - */ - public SWORDClientException(String message, Exception cause) { - super(message, cause); - } -} diff --git a/dspace-sword/src/main/java/org/purl/sword/client/SWORDComboBox.java b/dspace-sword/src/main/java/org/purl/sword/client/SWORDComboBox.java deleted file mode 100644 index 42fcc082ff58..000000000000 --- a/dspace-sword/src/main/java/org/purl/sword/client/SWORDComboBox.java +++ /dev/null @@ -1,129 +0,0 @@ -/** - * The contents of this file are subject to the license and copyright - * detailed in the LICENSE and NOTICE files at the root of the source - * tree and available online at - * - * http://www.dspace.org/license/ - */ - -/** - * Copyright (c) 2007, Aberystwyth University - * - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * - Redistributions of source code must retain the above - * copyright notice, this list of conditions and the - * following disclaimer. - * - * - Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * - * - Neither the name of the Centre for Advanced Software and - * Intelligent Systems (CASIS) nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR - * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF - * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ -package org.purl.sword.client; - -import javax.swing.JComboBox; - -/** - * An extension of the JComboBox class. This adds a method that - * can update the list of items with the item. The update will only - * work on combo boxes that are set to editable. - * - * @author Neil Taylor - */ -public class SWORDComboBox extends JComboBox { - /** - * Create an instance of the SWORD Combo box. - */ - public SWORDComboBox() { - super(); - setEditable(true); - } - - /** - * Update the list for the Combo box with the currently selected - * item. This will only add an item to the list if: i) the control - * is editable, ii) the selected item is not empty and iii) the - * item is not already in the list. - */ - public void updateList() { - Object s = getSelectedItem(); - - if (!isEditable() || s == null || ((String) s).trim().length() == 0) { - // don't update with an empty item or if the combo box is not editable. - return; - } - - insertItem(s); - } - - /** - * Insert an item into the combo box. This will only be added - * if the item is not already present in the combo box. - * - * @param newItem The item to insert. - */ - public void insertItem(Object newItem) { - int count = getItemCount(); - - boolean found = false; - - for (int i = 0; i < count && !found; i++) { - Object item = getItemAt(i); - if (item != null && item.equals(newItem)) { - found = true; - } - } - - if (!found) { - addItem(newItem); - } - } - - /** - * Insert multiple items into the combo box. - * - * @param items The array of items. - */ - public void insertItems(String[] items) { - for (String item : items) { - insertItem(item); - } - } - - /** - * Get the text of the currently selected item in the combo box. - * @return The text. null is returned if no item - * is selected. - */ - public String getText() { - Object o = getSelectedItem(); - if (o != null) { - return o.toString().trim(); - } - - return null; - } -} diff --git a/dspace-sword/src/main/java/org/purl/sword/client/SWORDFormPanel.java b/dspace-sword/src/main/java/org/purl/sword/client/SWORDFormPanel.java deleted file mode 100644 index 81de59ae68c2..000000000000 --- a/dspace-sword/src/main/java/org/purl/sword/client/SWORDFormPanel.java +++ /dev/null @@ -1,144 +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.purl.sword.client; - -import java.awt.Component; -import java.awt.GridBagConstraints; -import java.awt.GridBagLayout; -import java.awt.Insets; -import javax.swing.JPanel; - -/** - * Utility class. Creates a two column form. The left column is used to show - * the label for the row. The right column is used to show the control, e.g. - * text box, combo box or checkbox, for the row. - * - * @author Neil Taylor - */ -public class SWORDFormPanel extends JPanel { - /** - * Constraints used to control the layout on the panel. - */ - private GridBagConstraints labelConstraints; - - /** - * Constraints used to control the layout of the input controls on the panel. - */ - private GridBagConstraints controlConstraints; - - /** - * Index to the next row. - */ - private int rowIndex = 0; - - /** - * Insets for the top row of the label column. - */ - private Insets labelTop = new Insets(10, 10, 0, 0); - - /** - * Insets for the top row of the control column. - */ - private Insets controlTop = new Insets(10, 4, 0, 10); - - /** - * Insets for a general row in the label column. - */ - private Insets labelGeneral = new Insets(3, 10, 0, 0); - - /** - * Insets for a general row in the control column. - */ - private Insets controlGeneral = new Insets(3, 4, 0, 10); - - /** - * Create a new instance of the class. - */ - public SWORDFormPanel() { - super(); - setLayout(new GridBagLayout()); - - labelConstraints = new GridBagConstraints(); - labelConstraints.fill = GridBagConstraints.NONE; - labelConstraints.anchor = GridBagConstraints.LINE_END; - labelConstraints.weightx = 0.1; - - controlConstraints = new GridBagConstraints(); - controlConstraints.fill = GridBagConstraints.HORIZONTAL; - controlConstraints.weightx = 0.9; - } - - /** - * Add the specified component as the first row. It will occupy two - * columns. - * - * @param one The control to add. - */ - public void addFirstRow(Component one) { - addRow(one, null, labelTop, controlTop); - } - - /** - * Add the specified components as the first row in the form. - * - * @param one The label component. - * @param two The control component. - */ - public void addFirstRow(Component one, Component two) { - addRow(one, two, labelTop, controlTop); - } - - /** - * Add a component to the general row. This will be added in the label column. - * - * @param one The component. - */ - public void addRow(Component one) { - addRow(one, null); - } - - /** - * Add a component to the general row. - * - * @param one The component to add to the label column. - * @param two The component to add to the control column. - */ - public void addRow(Component one, Component two) { - addRow(one, two, labelGeneral, controlGeneral); - } - - /** - * Add a row to the table. - * - * @param one The component to display in the label column. - * @param two The component to display in the control column. - * @param labels The insets for the label column. - * @param controls The insets for the controls column. - */ - protected void addRow(Component one, Component two, Insets labels, Insets controls) { - labelConstraints.insets = labels; - labelConstraints.gridx = 0; - labelConstraints.gridy = rowIndex; - if (two == null) { - labelConstraints.gridwidth = 2; - } else { - labelConstraints.gridwidth = 1; - } - - add(one, labelConstraints); - - if (two != null) { - controlConstraints.insets = controls; - controlConstraints.gridx = 1; - controlConstraints.gridy = rowIndex; - add(two, controlConstraints); - } - - rowIndex++; - } -} diff --git a/dspace-sword/src/main/java/org/purl/sword/client/ServiceDialog.java b/dspace-sword/src/main/java/org/purl/sword/client/ServiceDialog.java deleted file mode 100644 index 03489f0d2e77..000000000000 --- a/dspace-sword/src/main/java/org/purl/sword/client/ServiceDialog.java +++ /dev/null @@ -1,227 +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/ - */ - -/** - * Copyright (c) 2007, Aberystwyth University - * - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * - Redistributions of source code must retain the above - * copyright notice, this list of conditions and the - * following disclaimer. - * - * - Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * - * - Neither the name of the Centre for Advanced Software and - * Intelligent Systems (CASIS) nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR - * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF - * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ -package org.purl.sword.client; - -import javax.swing.JFrame; -import javax.swing.JLabel; -import javax.swing.JOptionPane; -import javax.swing.JPanel; -import javax.swing.JPasswordField; - -/** - * Dialog that prompts the user to enter the details for a service - * document location. - * - * @author Neil Taylor - */ -public class ServiceDialog { - /** - * The username. - */ - private SWORDComboBox username; - - /** - * The password. - */ - private JPasswordField password; - - /** - * Holds the URL for the collection. - */ - private SWORDComboBox location; - - /** - * The combo box that shows the list of onBehalfOf items. - */ - private SWORDComboBox onBehalfOf; - - /** - * Parent frame for the dialog. - */ - private JFrame parentFrame = null; - - /** - * The panel that holds the controls. - */ - private JPanel controls = null; - - /** - * List of buttons. - */ - private static Object[] options = {"Get Service Document", "Cancel"}; - - /** - * Create a new instance. - * - * @param parentFrame The parent frame. The dialog will be shown over the - * centre of this frame. - */ - public ServiceDialog(JFrame parentFrame) { - this.parentFrame = parentFrame; - controls = createControls(); - } - - /** - * Show the dialog. - * - * @return The close option. This is one of the dialog options from - * JOptionPane. - */ - public int show() { - int result = JOptionPane.showOptionDialog(parentFrame, - controls, - "Get Service Document", - JOptionPane.OK_CANCEL_OPTION, - JOptionPane.PLAIN_MESSAGE, - null, - options, - options[1]); - - if (result == JOptionPane.OK_OPTION) { - // update the combo boxes with the values - username.updateList(); - location.updateList(); - onBehalfOf.updateList(); - } - - return result; - } - - /** - * Create the controls that are displayed in the dialog. - * - * @return The panel that contains the controls. - */ - protected final JPanel createControls() { - username = new SWORDComboBox(); - username.setEditable(true); - password = new JPasswordField(); - location = new SWORDComboBox(); - location.setEditable(true); - onBehalfOf = new SWORDComboBox(); - onBehalfOf.setEditable(true); - - JLabel userLabel = new JLabel("Username:", JLabel.TRAILING); - JLabel passwordLabel = new JLabel("Password:", JLabel.TRAILING); - JLabel locationLabel = new JLabel("Location:", JLabel.TRAILING); - JLabel onBehalfOfLabel = new JLabel("On Behalf Of:", JLabel.TRAILING); - - SWORDFormPanel panel = new SWORDFormPanel(); - panel.addFirstRow(userLabel, username); - panel.addRow(passwordLabel, password); - panel.addRow(locationLabel, location); - panel.addRow(onBehalfOfLabel, onBehalfOf); - - return panel; - } - - /** - * Get the username from the controls on the dialog. - * - * @return The username. - */ - public String getUsername() { - return username.getText(); - } - - /** - * Get the password from the dialog. - * - * @return The password. - */ - public String getPassword() { - return new String(password.getPassword()); - } - - /** - * The location from the dialog. - * - * @return The location. - */ - public String getLocation() { - return location.getText(); - } - - /** - * The onBehalfOf value from the dialog. - * - * @return The onBehalfOf value. - */ - public String getOnBehalfOf() { - String text = onBehalfOf.getText().trim(); - if (text.length() == 0) { - return null; - } - return text; - } - - /** - * Add the list of user ids to the dialog. - * - * @param users The list of user ids. - */ - public void addUserIds(String[] users) { - username.insertItems(users); - } - - /** - * Add the list of service URLs. - * - * @param services The service URLs. - */ - public void addServiceUrls(String[] services) { - location.insertItems(services); - } - - /** - * Add a list of onBehalfOf names. - * - * @param users The list of onBehalfOf items. - */ - public void addOnBehalfOf(String[] users) { - onBehalfOf.insertItems(users); - } - -} diff --git a/dspace-sword/src/main/java/org/purl/sword/client/ServicePanel.java b/dspace-sword/src/main/java/org/purl/sword/client/ServicePanel.java deleted file mode 100644 index bc3031c33354..000000000000 --- a/dspace-sword/src/main/java/org/purl/sword/client/ServicePanel.java +++ /dev/null @@ -1,843 +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/ - */ - -/** - * Copyright (c) 2007, Aberystwyth University - * - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * - Redistributions of source code must retain the above - * copyright notice, this list of conditions and the - * following disclaimer. - * - * - Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * - * - Neither the name of the Centre for Advanced Software and - * Intelligent Systems (CASIS) nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR - * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF - * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ -package org.purl.sword.client; - -import java.awt.BorderLayout; -import java.awt.Component; -import java.util.ArrayList; -import java.util.Enumeration; -import java.util.Iterator; -import java.util.List; -import javax.swing.Icon; -import javax.swing.ImageIcon; -import javax.swing.JComponent; -import javax.swing.JEditorPane; -import javax.swing.JPanel; -import javax.swing.JScrollPane; -import javax.swing.JSplitPane; -import javax.swing.JTree; -import javax.swing.ToolTipManager; -import javax.swing.event.TreeSelectionEvent; -import javax.swing.event.TreeSelectionListener; -import javax.swing.tree.DefaultMutableTreeNode; -import javax.swing.tree.DefaultTreeCellRenderer; -import javax.swing.tree.DefaultTreeModel; -import javax.swing.tree.TreePath; - -import org.purl.sword.atom.Author; -import org.purl.sword.atom.Content; -import org.purl.sword.atom.Contributor; -import org.purl.sword.atom.Generator; -import org.purl.sword.atom.Link; -import org.purl.sword.atom.TextConstruct; -import org.purl.sword.base.Collection; -import org.purl.sword.base.DepositResponse; -import org.purl.sword.base.SWORDEntry; -import org.purl.sword.base.Service; -import org.purl.sword.base.ServiceDocument; -import org.purl.sword.base.SwordAcceptPackaging; -import org.purl.sword.base.Workspace; - -/** - * The main panel for the GUI client. This contains the top-two sub-panels: the - * tree and the text area to show the details of the selected node. - * - * @author Neil Taylor - */ -public class ServicePanel extends JPanel - implements TreeSelectionListener { - /** - * The top level item in the tree that lists services. - */ - DefaultMutableTreeNode top; - - /** - * The tree model used to display the items. - */ - DefaultTreeModel treeModel = null; - - /** - * Tree that holds the list of services. - */ - private JTree services; - - /** - * The panel that shows an HTML table with any details for the selected - * node in the services tree. - */ - private JEditorPane details; - - /** - * A registered listener. This listener will be notified when there is a - * different node selected in the service tree. - */ - private ServiceSelectedListener listener; - - /** - * Create a new instance of the panel. - */ - public ServicePanel() { - super(); - setLayout(new BorderLayout()); - - top = new DefaultMutableTreeNode("Services & Posted Files"); - treeModel = new DefaultTreeModel(top); - - services = new JTree(treeModel); - services.setCellRenderer(new ServicePostTreeRenderer()); - - JScrollPane servicesPane = new JScrollPane(services, - JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, - JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS); - - details = new JEditorPane("text/html", - "

    Details

    This panel will show the details for the currently " + - "selected item in the tree.

    "); - - JScrollPane detailsPane = new JScrollPane(details, - JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, - JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS); - - JSplitPane splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, - servicesPane, - detailsPane); - splitPane.setOneTouchExpandable(true); - splitPane.setResizeWeight(0.5); - splitPane.setDividerLocation(200); - - services.addTreeSelectionListener(this); - ToolTipManager.sharedInstance().registerComponent(services); - - add(splitPane, BorderLayout.CENTER); - } - - /** - * Renderer that displays the icons for the tree nodes. - * - * @author Neil Taylor - */ - static class ServicePostTreeRenderer extends DefaultTreeCellRenderer { - Icon workspaceIcon; - Icon serviceIcon; - Icon collectionIcon; - Icon fileIcon; - - /** - * Initialise the renderer. Load the icons. - */ - public ServicePostTreeRenderer() { - ClassLoader loader = this.getClass().getClassLoader(); - workspaceIcon = new ImageIcon(loader.getResource("images/WorkspaceNodeImage.gif")); - serviceIcon = new ImageIcon(loader.getResource("images/ServiceNodeImage.gif")); - collectionIcon = new ImageIcon(loader.getResource("images/CollectionNodeImage.gif")); - fileIcon = new ImageIcon(loader.getResource("images/ServiceNodeImage.gif")); - } - - /** - * Return the cell renderer. This will be the default tree cell renderer - * with a different icon depending upon the type of data in the node. - * - * @param tree The JTree control. - * @param value The value to display. - * @param sel True if the node is selected. - * @param expanded True if the node is expanded. - * @param leaf True if the node is a leaf. - * @param row The row. - * @param hasFocus True if the node has focus. - */ - public Component getTreeCellRendererComponent( - JTree tree, - Object value, - boolean sel, - boolean expanded, - boolean leaf, - int row, - boolean hasFocus) { - - JComponent comp = (JComponent) super.getTreeCellRendererComponent( - tree, value, sel, - expanded, leaf, row, - hasFocus); - - DefaultMutableTreeNode node = - (DefaultMutableTreeNode) value; - - Object o = node.getUserObject(); - if (o instanceof TreeNodeWrapper) { - TreeNodeWrapper wrapper = (TreeNodeWrapper) o; - comp.setToolTipText(wrapper.toString()); - Object data = wrapper.getData(); - if (data instanceof Service) { - setIcon(serviceIcon); - } else if (data instanceof Workspace) { - setIcon(workspaceIcon); - } else if (data instanceof Collection) { - setIcon(collectionIcon); - } else if (data instanceof SWORDEntry) { - setIcon(fileIcon); - } - } else { - comp.setToolTipText(null); - } - return comp; - } - - - } - - /** - * Set the service selected listener. This listener will be notified when - * there is a selection change in the tree. - * - * @param listener The listener. - */ - public void setServiceSelectedListener(ServiceSelectedListener listener) { - this.listener = listener; - } - - /** - * Process the specified service document. Add the details as a new child of the - * root of the tree. - * - * @param url The url used to access the service document. - * @param doc The service document. - */ - public void processServiceDocument(String url, - ServiceDocument doc) { - TreeNodeWrapper wrapper = null; - - Service service = doc.getService(); - wrapper = new TreeNodeWrapper(url, service); - DefaultMutableTreeNode serviceNode = new DefaultMutableTreeNode(wrapper); - treeModel.insertNodeInto(serviceNode, top, top.getChildCount()); - services.scrollPathToVisible(new TreePath(serviceNode.getPath())); - - // process the workspaces - DefaultMutableTreeNode workspaceNode = null; - - Iterator workspaces = service.getWorkspaces(); - for (; workspaces.hasNext(); ) { - Workspace workspace = workspaces.next(); - wrapper = new TreeNodeWrapper(workspace.getTitle(), workspace); - workspaceNode = new DefaultMutableTreeNode(wrapper); - treeModel.insertNodeInto(workspaceNode, serviceNode, serviceNode.getChildCount()); - services.scrollPathToVisible(new TreePath(workspaceNode.getPath())); - - DefaultMutableTreeNode collectionNode = null; - Iterator collections = workspace.collectionIterator(); - for (; collections.hasNext(); ) { - Collection collection = collections.next(); - wrapper = new TreeNodeWrapper(collection.getTitle(), collection); - collectionNode = new DefaultMutableTreeNode(wrapper); - treeModel.insertNodeInto(collectionNode, workspaceNode, workspaceNode.getChildCount()); - services.scrollPathToVisible(new TreePath(collectionNode.getPath())); - } - } // for - } - - /** - * Holds the data for a tree node. It specifies the name that will be displayed - * in the node, and stores associated data. - * - * @author Neil Taylor - */ - static class TreeNodeWrapper { - /** - * The node name. - */ - private String name; - - /** - * The user data. - */ - private Object userObject; - - /** - * Create a new instance. - * - * @param name The name of the node. - * @param data The data in the node. - */ - public TreeNodeWrapper(String name, Object data) { - this.name = name; - this.userObject = data; - } - - /** - * Retrieve the data that is stored in this node. - * - * @return The data. - */ - public Object getData() { - return userObject; - } - - /** - * Get a string description for this node. - */ - public String toString() { - if (name == null || name.trim().equals("")) { - return "Unspecified"; - } - - return name; - } - } - - /** - * Respond to a changed tree selection event. Update the details panel to - * show an appropriate message for the newly selected node. Also, - * alert the selection listener for this panel. The listener will receive - * a path, if a collection has been selected. Otherwise, the listener - * will receive null. - */ - public void valueChanged(TreeSelectionEvent evt) { - // Get all nodes whose selection status has changed - TreePath[] paths = evt.getPaths(); - - for (int i = 0; i < paths.length; i++) { - if (evt.isAddedPath(i)) { - // process new selections - DefaultMutableTreeNode node; - node = (DefaultMutableTreeNode) (paths[i].getLastPathComponent()); - - Object o = node.getUserObject(); - if (o instanceof TreeNodeWrapper) { - try { - TreeNodeWrapper wrapper = (TreeNodeWrapper) o; - Object data = wrapper.getData(); - if (data instanceof Service) { - showService((Service) data); - alertListener(null); - } else if (data instanceof Workspace) { - showWorkspace((Workspace) data); - if (listener != null) { - alertListener(null); - } - } else if (data instanceof Collection) { - Collection c = (Collection) data; - showCollection(c); - alertListener(c.getLocation()); - } else if (data instanceof SWORDEntry) { - showEntry((SWORDEntry) data); - alertListener(null); - } else { - details.setText("unknown"); - alertListener(null); - } - } catch (Exception e) { - details.setText( - "An error occurred. The message was: " + e.getMessage() + ""); - alertListener(null); - e.printStackTrace(); - } - } else { - details.setText("please select one of the other nodes"); - alertListener(null); - } - } - - } - } - - /** - * Notify the listener that there has been a change to the currently selected - * item in the tree. - * - * @param value The value to send to the listener. - */ - private void alertListener(String value) { - if (listener != null) { - listener.selected(value); - } - } - - /** - * Add a new HTML table row to the specified StringBuffer. The label is displayed in - * the left column and the value is displayed in the right column. - * - * @param buffer The destination string buffer. - * @param label The label to add. - * @param value The corresponding value to add. - */ - private void addTableRow(StringBuffer buffer, String label, Object value) { - buffer.append(""); - buffer.append(label); - buffer.append(""); - buffer.append(displayableValue(value)); - buffer.append(""); - } - - /** - * Show the specified service data in the details panel. - * - * @param service The service node to display. - */ - private void showService(Service service) { - StringBuffer buffer = new StringBuffer(); - buffer.append(""); - buffer.append(""); - - buffer.append(""); - buffer.append(""); - addTableRow(buffer, "SWORD Version", service.getVersion()); - addTableRow(buffer, "NoOp Support ", service.isNoOp()); - addTableRow(buffer, "Verbose Support ", service.isVerbose()); - - String maxSize = ""; - - // Commented out the following code as the client code is out of step with the - // Sword 'base' library and wont compile. - Robin Taylor. - //if ( service.maxUploadIsDefined() ) - //{ - // maxSize = "" + service.getMaxUploadSize() + "kB"; - //} - //else - //{ - maxSize = "undefined"; - //} - - addTableRow(buffer, "Max File Upload Size ", maxSize); - - buffer.append("
    Service Summary
    "); - - buffer.append(""); - buffer.append(""); - details.setText(buffer.toString()); - } - - /** - * Display the workspace data in the details panel. - * - * @param workspace The workspace. - */ - private void showWorkspace(Workspace workspace) { - StringBuffer buffer = new StringBuffer(); - buffer.append(""); - buffer.append(""); - - buffer.append(""); - buffer - .append(""); - addTableRow(buffer, "Workspace Title", workspace.getTitle()); - buffer.append("
    Workspace Summary
    "); - - buffer.append(""); - buffer.append(""); - details.setText(buffer.toString()); - } - - /** - * Return the parameter unmodified if set, or the not defined text if null - * @param s - * @return s or ClientConstants.NOT_DEFINED_TEXT - */ - private Object displayableValue(Object s) { - if (null == s) { - return ClientConstants.NOT_DEFINED_TEXT; - } else { - return s; - } - } - - /** - * Add a string within paragraph tags. - * - * @param buffer The buffer to add the message to. - * @param message The message to add. - */ - private void addPara(StringBuffer buffer, String message) { - buffer.append("

    " + message + "

    "); - } - - /** - * Show the specified collection data in the details panel. - * - * @param collection The collection data. - */ - private void showCollection(Collection collection) { - StringBuffer buffer = new StringBuffer(); - buffer.append(""); - buffer.append(""); - - if (collection == null) { - addPara(buffer, "Invalid Collection object. Unable to display details."); - } else { - buffer.append(""); - buffer.append( - ""); - addTableRow(buffer, "Collection location", collection.getLocation()); - addTableRow(buffer, "Collection title", collection.getTitle()); - addTableRow(buffer, "Abstract", collection.getAbstract()); - addTableRow(buffer, "Collection Policy", collection.getCollectionPolicy()); - addTableRow(buffer, "Treatment", collection.getTreatment()); - addTableRow(buffer, "Mediation", collection.getMediation()); - addTableRow(buffer, "Nested Service Document", collection.getService()); - - String[] accepts = collection.getAccepts(); - StringBuilder acceptList = new StringBuilder(); - if (accepts != null && accepts.length == 0) { - acceptList.append("None specified"); - } else { - for (String s : accepts) { - acceptList.append(s).append("
    "); - } - } - addTableRow(buffer, "Accepts", acceptList.toString()); - - List acceptsPackaging = collection.getAcceptPackaging(); - - StringBuilder acceptPackagingList = new StringBuilder(); - for (Iterator i = acceptsPackaging.iterator(); i.hasNext(); ) { - SwordAcceptPackaging accept = (SwordAcceptPackaging) i.next(); - acceptPackagingList.append(accept.getContent()).append(" (").append(accept.getQualityValue()) - .append(")"); - - // add a , separator if there are any more items in the list - if (i.hasNext()) { - acceptPackagingList.append(", "); - } - } - - addTableRow(buffer, "Accepts Packaging", acceptPackagingList.toString()); - - buffer.append("
    Collection Summary
    "); - } - - buffer.append(""); - buffer.append(""); - details.setText(buffer.toString()); - } - - /** - * Display the contents of a Post entry in the display panel. - * - * @param entry The entry to display. - */ - private void showEntry(SWORDEntry entry) { - StringBuffer buffer = new StringBuffer(); - buffer.append(""); - buffer.append(""); - - if (entry == null) { - addPara(buffer, "Invalid Entry object. Unable to display details."); - } else { - buffer.append(""); - buffer - .append(""); - - // process atom:title - String titleString = getTextConstructDetails(entry.getSummary()); - addTableRow(buffer, "Title", titleString); - - // process id - addTableRow(buffer, "ID", entry.getId()); - - // process updated - addTableRow(buffer, "Date Updated", entry.getUpdated()); - - String authorString = getAuthorDetails(entry.getAuthors()); - addTableRow(buffer, "Authors", authorString); - - // process summary - String summaryString = getTextConstructDetails(entry.getSummary()); - addTableRow(buffer, "Summary", summaryString); - - // process content - Content content = entry.getContent(); - String contentString = ""; - if (content == null) { - contentString = "Not defined."; - } else { - contentString += "Source: '" + content.getSource() + "', Type: '" + - content.getType() + "'"; - } - addTableRow(buffer, "Content", contentString); - - // process links - Iterator links = entry.getLinks(); - StringBuffer linkBuffer = new StringBuffer(); - for (; links.hasNext(); ) { - Link link = links.next(); - linkBuffer.append("href: '"); - linkBuffer.append(link.getHref()); - linkBuffer.append("', href lang: '"); - linkBuffer.append(link.getHreflang()); - linkBuffer.append("', rel: '"); - linkBuffer.append(link.getRel()); - linkBuffer.append("')
    "); - } - if (linkBuffer.length() == 0) { - linkBuffer.append("Not defined"); - } - addTableRow(buffer, "Links", linkBuffer.toString()); - - // process contributors - String contributorString = getContributorDetails(entry.getContributors()); - addTableRow(buffer, "Contributors", contributorString); - - // process source - String sourceString = ""; - Generator generator = entry.getGenerator(); - if (generator != null) { - sourceString += "Content: '" + generator.getContent() + "'
    '"; - sourceString += "Version: '" + generator.getVersion() + "'
    '"; - sourceString += "Uri: '" + generator.getUri() + "'"; - } else { - sourceString += "No generator defined."; - } - - addTableRow(buffer, "Generator", sourceString); - - // process treatment - addTableRow(buffer, "Treatment", entry.getTreatment()); - - // process verboseDescription - addTableRow(buffer, "Verbose Description", entry.getVerboseDescription()); - - // process noOp - addTableRow(buffer, "NoOp", entry.isNoOp()); - - // process formatNamespace - addTableRow(buffer, "Packaging", entry.getPackaging()); - - // process userAgent - addTableRow(buffer, "User Agent", entry.getUserAgent()); - - - buffer.append("
    Entry Summary
    "); - } - - buffer.append(""); - buffer.append(""); - details.setText(buffer.toString()); - } - - /** - * Retrieve the details for a TextConstruct object. - * - * @param data The text construct object to display. - * - * @return Either 'Not defined' if the data is null, or - * details of the text content element. - */ - private String getTextConstructDetails(TextConstruct data) { - String summaryStr = ""; - if (data == null) { - summaryStr = "Not defined"; - } else { - summaryStr = "Content: '" + data.getContent() + "', Type: "; - if (data.getType() != null) { - summaryStr += "'" + data.getType().toString() + "'"; - } else { - summaryStr += "undefined."; - } - } - - return summaryStr; - } - - /** - * Get the author details and insert them into a string. - * - * @param authors the list of authors to process. - * - * @return A string containing the list of authors. - */ - private String getAuthorDetails(Iterator authors) { - // process author - StringBuffer authorBuffer = new StringBuffer(); - for (; authors.hasNext(); ) { - Author a = authors.next(); - authorBuffer.append(getAuthorDetails(a)); - } - - if (authorBuffer.length() == 0) { - authorBuffer.append("Not defined"); - } - - return authorBuffer.toString(); - } - - /** - * Get the contributor details and insert them into a string. - * - * @param contributors The contributors. - * - * @return The string that lists the details of the contributors. - */ - private String getContributorDetails(Iterator contributors) { - // process author - StringBuffer authorBuffer = new StringBuffer(); - for (; contributors.hasNext(); ) { - Contributor c = contributors.next(); - authorBuffer.append(getAuthorDetails(c)); - } - - if (authorBuffer.length() == 0) { - authorBuffer.append("Not defined"); - } - - return authorBuffer.toString(); - } - - /** - * Build a string that describes the specified author. - * - * @param author The author. - * - * @return The string description. - */ - private String getAuthorDetails(Author author) { - // process author - StringBuffer authorBuffer = new StringBuffer(); - authorBuffer.append(author.getName()); - authorBuffer.append(" (email: '"); - authorBuffer.append(author.getEmail()); - authorBuffer.append("', uri: '"); - authorBuffer.append(author.getUri()); - authorBuffer.append("')
    "); - - return authorBuffer.toString(); - } - - /** - * Process the deposit response and insert the details into the tree. If the url - * matches one of the collections in the tree, the deposit is added as a child - * node. Otherwise, the node is added as a child of the root. - * - * @param url The url of the collection that the file was posted to. - * - * @param response The details of the deposit. - */ - public void processDepositResponse(String url, - DepositResponse response) { - SWORDEntry entry = response.getEntry(); - Object title = entry.getTitle(); - if (title == null) { - title = "Undefined"; - } else { - title = entry.getTitle().getContent(); - } - - TreeNodeWrapper wrapper = new TreeNodeWrapper(title.toString(), entry); - DefaultMutableTreeNode entryNode = new DefaultMutableTreeNode(wrapper); - - DefaultMutableTreeNode newParentNode = top; - List nodes = getCollectionNodes(); - for (DefaultMutableTreeNode node : nodes) { - Object o = node.getUserObject(); - if (o instanceof TreeNodeWrapper) { - TreeNodeWrapper collectionWrapper = (TreeNodeWrapper) o; - Object data = collectionWrapper.getData(); - if (data instanceof Collection) { - Collection col = (Collection) data; - String location = col.getLocation(); - if (location != null && location.equals(url)) { - newParentNode = node; - break; - } - } - } - } - - treeModel.insertNodeInto(entryNode, newParentNode, newParentNode.getChildCount()); - services.scrollPathToVisible(new TreePath(entryNode.getPath())); - } - - /** - * Get a list of all current collections displayed in the tree. - * - * @return An array of the URLs for the collections. - */ - public String[] getCollectionLocations() { - List nodes = getCollectionNodes(); - String[] locations = new String[nodes.size()]; - - DefaultMutableTreeNode node; - for (int i = 0; i < nodes.size(); i++) { - node = nodes.get(i); - Object o = node.getUserObject(); - if (o instanceof TreeNodeWrapper) { - TreeNodeWrapper collectionWrapper = (TreeNodeWrapper) o; - Object data = collectionWrapper.getData(); - if (data instanceof Collection) { - Collection col = (Collection) data; - String location = col.getLocation(); - if (location != null) { - locations[i] = location; - } - } - } - } - return locations; - } - - /** - * Get a list of nodes that contain collections. - * - * @return A vector of the collection nodes. - */ - private List getCollectionNodes() { - List nodes = new ArrayList(); - - DefaultMutableTreeNode node; - Enumeration treeNodes = top.depthFirstEnumeration(); - - while (treeNodes.hasMoreElements()) { - node = (DefaultMutableTreeNode) treeNodes.nextElement(); - Object o = node.getUserObject(); - if (o instanceof TreeNodeWrapper) { - TreeNodeWrapper wrapper = (TreeNodeWrapper) o; - Object data = wrapper.getData(); - if (data instanceof Collection) { - nodes.add(node); - } - } - } - - return nodes; - } -} diff --git a/dspace-sword/src/main/java/org/purl/sword/client/ServiceSelectedListener.java b/dspace-sword/src/main/java/org/purl/sword/client/ServiceSelectedListener.java deleted file mode 100644 index fa97549fd924..000000000000 --- a/dspace-sword/src/main/java/org/purl/sword/client/ServiceSelectedListener.java +++ /dev/null @@ -1,23 +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.purl.sword.client; - -/** - * Listener for any objects that want to be notified when a collection has been selected in the - * ServicePanel. - * - * @author Neil Taylor - */ -public interface ServiceSelectedListener { - /** - * Called to provide an update on whether the selected node is a Collection. - * - * @param collection The location of the collection. null, otherwise. - */ - public void selected(String collection); -} diff --git a/dspace-sword/src/main/java/org/purl/sword/client/Status.java b/dspace-sword/src/main/java/org/purl/sword/client/Status.java deleted file mode 100644 index ff80fa6e52e2..000000000000 --- a/dspace-sword/src/main/java/org/purl/sword/client/Status.java +++ /dev/null @@ -1,61 +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.purl.sword.client; - -/** - * Representation of the status code and message. - * - * @author Neil Taylor - */ -public class Status { - /** - * The status code. - */ - private int code; - - /** - * The status message. - */ - private String message; - - /** - * Create a new status message. - * - * @param code The code. - * @param message The message. - */ - public Status(int code, String message) { - this.code = code; - this.message = message; - } - - /** - * Retrieve the code. - * - * @return The code. - */ - public int getCode() { - return code; - } - - /** - * Get the message. - * - * @return The message. - */ - public String getMessage() { - return message; - } - - /** - * Get a string representation of the status. - */ - public String toString() { - return "Code: " + code + ", Message: '" + message + "'"; - } -} From bfe7e7871e443aefe783976672c5e36ef349dd9b Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Mon, 7 Apr 2025 14:57:31 -0500 Subject: [PATCH 511/979] Remove unused SWORD v1 client code. This is "dead code" which is unmaintained and obsolete --- .../java/org/purl/sword/client/Client.java | 463 ---------- .../purl/sword/client/ClientConstants.java | 40 - .../org/purl/sword/client/ClientFactory.java | 116 --- .../org/purl/sword/client/ClientOptions.java | 605 ------------- .../org/purl/sword/client/ClientType.java | 23 - .../java/org/purl/sword/client/CmdClient.java | 445 --------- .../purl/sword/client/DebugOutputStream.java | 44 - .../purl/sword/client/MessageOutputPanel.java | 138 --- .../purl/sword/client/PostDestination.java | 140 --- .../org/purl/sword/client/PostDialog.java | 599 ------------- .../org/purl/sword/client/PostMessage.java | 344 ------- .../purl/sword/client/PropertiesDialog.java | 240 ----- .../org/purl/sword/client/SWORDClient.java | 85 -- .../sword/client/SWORDClientException.java | 42 - .../org/purl/sword/client/SWORDComboBox.java | 129 --- .../org/purl/sword/client/SWORDFormPanel.java | 144 --- .../org/purl/sword/client/ServiceDialog.java | 227 ----- .../org/purl/sword/client/ServicePanel.java | 843 ------------------ .../sword/client/ServiceSelectedListener.java | 23 - .../org/purl/sword/client/ServletClient.java | 455 ---------- .../java/org/purl/sword/client/Status.java | 61 -- 21 files changed, 5206 deletions(-) delete mode 100644 dspace-sword/src/main/java/org/purl/sword/client/Client.java delete mode 100644 dspace-sword/src/main/java/org/purl/sword/client/ClientConstants.java delete mode 100644 dspace-sword/src/main/java/org/purl/sword/client/ClientFactory.java delete mode 100644 dspace-sword/src/main/java/org/purl/sword/client/ClientOptions.java delete mode 100644 dspace-sword/src/main/java/org/purl/sword/client/ClientType.java delete mode 100644 dspace-sword/src/main/java/org/purl/sword/client/CmdClient.java delete mode 100644 dspace-sword/src/main/java/org/purl/sword/client/DebugOutputStream.java delete mode 100644 dspace-sword/src/main/java/org/purl/sword/client/MessageOutputPanel.java delete mode 100644 dspace-sword/src/main/java/org/purl/sword/client/PostDestination.java delete mode 100644 dspace-sword/src/main/java/org/purl/sword/client/PostDialog.java delete mode 100644 dspace-sword/src/main/java/org/purl/sword/client/PostMessage.java delete mode 100644 dspace-sword/src/main/java/org/purl/sword/client/PropertiesDialog.java delete mode 100644 dspace-sword/src/main/java/org/purl/sword/client/SWORDClient.java delete mode 100644 dspace-sword/src/main/java/org/purl/sword/client/SWORDClientException.java delete mode 100644 dspace-sword/src/main/java/org/purl/sword/client/SWORDComboBox.java delete mode 100644 dspace-sword/src/main/java/org/purl/sword/client/SWORDFormPanel.java delete mode 100644 dspace-sword/src/main/java/org/purl/sword/client/ServiceDialog.java delete mode 100644 dspace-sword/src/main/java/org/purl/sword/client/ServicePanel.java delete mode 100644 dspace-sword/src/main/java/org/purl/sword/client/ServiceSelectedListener.java delete mode 100644 dspace-sword/src/main/java/org/purl/sword/client/ServletClient.java delete mode 100644 dspace-sword/src/main/java/org/purl/sword/client/Status.java diff --git a/dspace-sword/src/main/java/org/purl/sword/client/Client.java b/dspace-sword/src/main/java/org/purl/sword/client/Client.java deleted file mode 100644 index c6d335d8dd16..000000000000 --- a/dspace-sword/src/main/java/org/purl/sword/client/Client.java +++ /dev/null @@ -1,463 +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.purl.sword.client; - -import java.io.BufferedReader; -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.UnsupportedEncodingException; -import java.net.MalformedURLException; -import java.net.URL; -import java.security.NoSuchAlgorithmException; -import java.util.Properties; - -import org.apache.http.HttpHost; -import org.apache.http.HttpResponse; -import org.apache.http.HttpStatus; -import org.apache.http.StatusLine; -import org.apache.http.auth.AuthScope; -import org.apache.http.auth.UsernamePasswordCredentials; -import org.apache.http.client.methods.HttpGet; -import org.apache.http.client.methods.HttpPost; -import org.apache.http.conn.params.ConnRoutePNames; -import org.apache.http.entity.ContentType; -import org.apache.http.entity.FileEntity; -import org.apache.http.impl.client.DefaultHttpClient; -import org.apache.http.params.HttpParams; -import org.apache.logging.log4j.Logger; -import org.purl.sword.base.ChecksumUtils; -import org.purl.sword.base.DepositResponse; -import org.purl.sword.base.HttpHeaders; -import org.purl.sword.base.ServiceDocument; -import org.purl.sword.base.SwordValidationInfo; -import org.purl.sword.base.UnmarshallException; - -/** - * This is an example Client implementation to demonstrate how to connect to a - * SWORD server. The client supports BASIC HTTP Authentication. This can be - * initialised by setting a username and password. - * - * @author Neil Taylor - */ -public class Client implements SWORDClient { - /** - * The status field for the response code from the recent network access. - */ - private Status status; - - /** - * The name of the server to contact. - */ - private String server; - - /** - * The port number for the server. - */ - private int port; - - /** - * Specifies if the network access should use HTTP authentication. - */ - private boolean doAuthentication; - - /** - * The username to use for Basic Authentication. - */ - private String username; - - /** - * User password that is to be used. - */ - private String password; - - /** - * The userAgent to identify this application. - */ - private String userAgent; - - /** - * The client that is used to send data to the specified server. - */ - private final DefaultHttpClient client; - - /** - * The default connection timeout. This can be modified by using the - * setSocketTimeout method. - */ - public static final int DEFAULT_TIMEOUT = 20000; - - /** - * Logger. - */ - private static final Logger log = org.apache.logging.log4j.LogManager.getLogger(Client.class); - - /** - * Create a new Client. The client will not use authentication by default. - */ - public Client() { - client = new DefaultHttpClient(); - HttpParams params = client.getParams(); - params.setParameter("http.socket.timeout", - Integer.valueOf(DEFAULT_TIMEOUT)); - HttpHost proxyHost = (HttpHost) params - .getParameter(ConnRoutePNames.DEFAULT_PROXY); // XXX does this really work? - log.debug("proxy host: " + proxyHost.getHostName()); - log.debug("proxy port: " + proxyHost.getPort()); - doAuthentication = false; - } - - /** - * Initialise the server that will be used to send the network access. - * - * @param server server address/hostname - * @param port server port - */ - public void setServer(String server, int port) { - this.server = server; - this.port = port; - } - - /** - * Set the user credentials that will be used when making the access to the - * server. - * - * @param username The username. - * @param password The password. - */ - public void setCredentials(String username, String password) { - this.username = username; - this.password = password; - doAuthentication = true; - } - - /** - * Set the basic credentials. You must have previously set the server and - * port using setServer. - * - * @param username The username. - * @param password The password. - */ - private void setBasicCredentials(String username, String password) { - log.debug("server: " + server + " port: " + port + " u: '" + username - + "' p '" + password + "'"); - client.getCredentialsProvider().setCredentials(new AuthScope(server, port), - new UsernamePasswordCredentials(username, password)); - } - - /** - * Set a proxy that should be used by the client when trying to access the - * server. If this is not set, the client will attempt to make a direct - * direct connection to the server. The port is set to 80. - * - * @param host The hostname. - */ - public void setProxy(String host) { - setProxy(host, 80); - } - - /** - * Set a proxy that should be used by the client when trying to access the - * server. If this is not set, the client will attempt to make a direct - * direct connection to the server. - * - * @param host The name of the host. - * @param port The port. - */ - public void setProxy(String host, int port) { - client.getParams().setParameter(ConnRoutePNames.DEFAULT_PROXY, - new HttpHost(host, port)); // XXX does this really work? - } - - /** - * Clear the proxy setting. - */ - public void clearProxy() { - client.getParams().removeParameter(ConnRoutePNames.DEFAULT_PROXY); // XXX does this really work? - } - - /** - * Clear any user credentials that have been set for this client. - */ - public void clearCredentials() { - client.getCredentialsProvider().clear(); - doAuthentication = false; - } - - public void setUserAgent(String userAgent) { - this.userAgent = userAgent; - } - - /** - * Set the connection timeout for the socket. - * - * @param milliseconds The time, expressed as a number of milliseconds. - */ - public void setSocketTimeout(int milliseconds) { - client.getParams().setParameter("http.socket.timeout", - Integer.valueOf(milliseconds)); - } - - /** - * Retrieve the service document. The service document is located at the - * specified URL. This calls getServiceDocument(url,onBehalfOf). - * - * @param url The location of the service document. - * @return The ServiceDocument, or null if there was a - * problem accessing the document. e.g. invalid access. - * @throws SWORDClientException If there is an error accessing the resource. - */ - public ServiceDocument getServiceDocument(String url) - throws SWORDClientException { - return getServiceDocument(url, null); - } - - /** - * Retrieve the service document. The service document is located at the - * specified URL. This calls getServiceDocument(url,onBehalfOf). - * - * @param url The location of the service document. - * @return The ServiceDocument, or null if there was a - * problem accessing the document. e.g. invalid access. - * @throws SWORDClientException If there is an error accessing the resource. - */ - public ServiceDocument getServiceDocument(String url, String onBehalfOf) - throws SWORDClientException { - URL serviceDocURL = null; - try { - serviceDocURL = new URL(url); - } catch (MalformedURLException e) { - // Try relative URL - URL baseURL = null; - try { - baseURL = new URL("http", server, Integer.valueOf(port), "/"); - serviceDocURL = new URL(baseURL, (url == null) ? "" : url); - } catch (MalformedURLException e1) { - // No dice, can't even form base URL... - throw new SWORDClientException(url + " is not a valid URL (" - + e1.getMessage() - + "), and could not form a relative one from: " - + baseURL + " / " + url, e1); - } - } - - HttpGet httpget = new HttpGet(serviceDocURL.toExternalForm()); - if (doAuthentication) { - // this does not perform any check on the username password. It - // relies on the server to determine if the values are correct. - setBasicCredentials(username, password); - } - - Properties properties = new Properties(); - - if (containsValue(onBehalfOf)) { - log.debug("Setting on-behalf-of: " + onBehalfOf); - httpget.addHeader(url, url); - httpget.addHeader(HttpHeaders.X_ON_BEHALF_OF, onBehalfOf); - properties.put(HttpHeaders.X_ON_BEHALF_OF, onBehalfOf); - } - - if (containsValue(userAgent)) { - log.debug("Setting userAgent: " + userAgent); - httpget.addHeader(HttpHeaders.USER_AGENT, userAgent); - properties.put(HttpHeaders.USER_AGENT, userAgent); - } - - ServiceDocument doc = null; - - try { - HttpResponse response = client.execute(httpget); - StatusLine statusLine = response.getStatusLine(); - int statusCode = statusLine.getStatusCode(); - // store the status code - status = new Status(statusCode, statusLine.getReasonPhrase()); - - if (status.getCode() == HttpStatus.SC_OK) { - String message = readResponse(response.getEntity().getContent()); - log.debug("returned message is: " + message); - doc = new ServiceDocument(); - lastUnmarshallInfo = doc.unmarshall(message, properties); - } else { - throw new SWORDClientException( - "Received error from service document request: " - + status); - } - } catch (IOException ioex) { - throw new SWORDClientException(ioex.getMessage(), ioex); - } catch (UnmarshallException uex) { - throw new SWORDClientException(uex.getMessage(), uex); - } finally { - httpget.releaseConnection(); - } - - return doc; - } - - private SwordValidationInfo lastUnmarshallInfo; - - /** - * @return SWORD validation info - */ - public SwordValidationInfo getLastUnmarshallInfo() { - return lastUnmarshallInfo; - } - - /** - * Post a file to the server. The different elements of the post are encoded - * in the specified message. - * - * @param message The message that contains the post information. - * @throws SWORDClientException if there is an error during the post operation. - */ - public DepositResponse postFile(PostMessage message) - throws SWORDClientException { - if (message == null) { - throw new SWORDClientException("Message cannot be null."); - } - - HttpPost httppost = new HttpPost(message.getDestination()); - - if (doAuthentication) { - setBasicCredentials(username, password); - } - - DepositResponse response = null; - - String messageBody = ""; - - try { - if (message.isUseMD5()) { - String md5 = ChecksumUtils.generateMD5(message.getFilepath()); - if (message.getChecksumError()) { - md5 = "1234567890"; - } - log.debug("checksum error is: " + md5); - if (md5 != null) { - httppost.addHeader(HttpHeaders.CONTENT_MD5, md5); - } - } - - String filename = message.getFilename(); - if (!"".equals(filename)) { - httppost.addHeader(HttpHeaders.CONTENT_DISPOSITION, - " filename=" + filename); - } - - if (containsValue(message.getSlug())) { - httppost.addHeader(HttpHeaders.SLUG, message.getSlug()); - } - - if (message.getCorruptRequest()) { - // insert a header with an invalid boolean value - httppost.addHeader(HttpHeaders.X_NO_OP, "Wibble"); - } else { - httppost.addHeader(HttpHeaders.X_NO_OP, Boolean - .toString(message.isNoOp())); - } - httppost.addHeader(HttpHeaders.X_VERBOSE, Boolean - .toString(message.isVerbose())); - - String packaging = message.getPackaging(); - if (packaging != null && packaging.length() > 0) { - httppost.addHeader(HttpHeaders.X_PACKAGING, packaging); - } - - String onBehalfOf = message.getOnBehalfOf(); - if (containsValue(onBehalfOf)) { - httppost.addHeader(HttpHeaders.X_ON_BEHALF_OF, onBehalfOf); - } - - String userAgent = message.getUserAgent(); - if (containsValue(userAgent)) { - httppost.addHeader(HttpHeaders.USER_AGENT, userAgent); - } - - - FileEntity requestEntity = new FileEntity( - new File(message.getFilepath()), - ContentType.create(message.getFiletype())); - httppost.setEntity(requestEntity); - - HttpResponse httpResponse = client.execute(httppost); - StatusLine statusLine = httpResponse.getStatusLine(); - int statusCode = statusLine.getStatusCode(); - status = new Status(statusCode, statusLine.getReasonPhrase()); - - log.info("Checking the status code: " + status.getCode()); - - if (status.getCode() == HttpStatus.SC_ACCEPTED - || status.getCode() == HttpStatus.SC_CREATED) { - messageBody = readResponse(httpResponse.getEntity().getContent()); - response = new DepositResponse(status.getCode()); - response.setLocation(httpResponse.getFirstHeader("Location").getValue()); - // added call for the status code. - lastUnmarshallInfo = response.unmarshall(messageBody, new Properties()); - } else { - messageBody = readResponse(httpResponse.getEntity().getContent()); - response = new DepositResponse(status.getCode()); - response.unmarshallErrorDocument(messageBody); - } - return response; - - } catch (NoSuchAlgorithmException nex) { - throw new SWORDClientException("Unable to use MD5. " - + nex.getMessage(), nex); - } catch (IOException ioex) { - throw new SWORDClientException(ioex.getMessage(), ioex); - } catch (UnmarshallException uex) { - throw new SWORDClientException(uex.getMessage() + "(
    " + messageBody + "
    )", uex); - } finally { - httppost.releaseConnection(); - } - } - - /** - * Read a response from the stream and return it as a string. - * - * @param stream The stream that contains the response. - * @return The string extracted from the screen. - * @throws UnsupportedEncodingException - * @throws IOException A general class of exceptions produced by failed or interrupted I/O - * operations. - */ - private String readResponse(InputStream stream) - throws UnsupportedEncodingException, IOException { - BufferedReader reader = new BufferedReader(new InputStreamReader( - stream, "UTF-8")); - String line = null; - StringBuffer buffer = new StringBuffer(); - while ((line = reader.readLine()) != null) { - buffer.append(line); - buffer.append("\n"); - } - return buffer.toString(); - } - - /** - * Return the status information that was returned from the most recent - * request sent to the server. - * - * @return The status code returned from the most recent access. - */ - public Status getStatus() { - return status; - } - - /** - * Check to see if the specified item contains a non-empty string. - * - * @param item The string to check. - * @return True if the string is not null and has a length greater than 0 - * after any whitespace is trimmed from the start and end. - * Otherwise, false. - */ - private boolean containsValue(String item) { - return ((item != null) && (item.trim().length() > 0)); - } -} diff --git a/dspace-sword/src/main/java/org/purl/sword/client/ClientConstants.java b/dspace-sword/src/main/java/org/purl/sword/client/ClientConstants.java deleted file mode 100644 index 124ed38d33d8..000000000000 --- a/dspace-sword/src/main/java/org/purl/sword/client/ClientConstants.java +++ /dev/null @@ -1,40 +0,0 @@ -/** - * The contents of this file are subject to the license and copyright - * detailed in the LICENSE and NOTICE files at the root of the source - * tree and available online at - * - * http://www.dspace.org/license/ - */ -package org.purl.sword.client; - -/** - * Hold general constants for the client. - * - * @author Neil Taylor - */ -public class ClientConstants { - /** - * Current software version. - */ - public static final String CLIENT_VERSION = "1.1"; - - /** - * the name of this application - */ - public static final String SERVICE_NAME = "CASIS Test Client"; - - /** - * the name of this application - */ - public static final String NOT_DEFINED_TEXT = "Not defined"; - - /** - * The logging property file. - */ - public static final String LOGGING_PROPERTY_FILE = "log4j.properties"; - - /** - * Default constructor - */ - private ClientConstants() { } -} diff --git a/dspace-sword/src/main/java/org/purl/sword/client/ClientFactory.java b/dspace-sword/src/main/java/org/purl/sword/client/ClientFactory.java deleted file mode 100644 index 00bd9c17a541..000000000000 --- a/dspace-sword/src/main/java/org/purl/sword/client/ClientFactory.java +++ /dev/null @@ -1,116 +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.purl.sword.client; - -/** - * Entry point for the SWORD Demonstration Client. This will parse the list of - * command line options and load either a Command Line client or a GUI client. - * - * @author Neil Taylor - */ -public class ClientFactory { - /** - * Generate a string that specifies the command line options for this - * program. - * - * @return A list of the options for this program. - */ - public static String usage() { - StringBuilder buffer = new StringBuilder(); - buffer.append("swordclient: version "); - buffer.append(ClientConstants.CLIENT_VERSION); - buffer.append("\n"); - - buffer.append("GUI Mode: "); - buffer.append("swordclient [-gui] [-nocapture]"); - buffer.append("\n\n"); - - buffer.append("Command Mode: Service - Request a Service Document\n"); - buffer.append("swordclient -cmd -t service [user-options] [proxy-options] -href url [-onBehalfOf name] "); - buffer.append("\n\n"); - - buffer.append("Command Mode: Post - Post a file to a remote service.\n"); - buffer.append("swordclient -cmd -t post [user-options] [proxy-options] [post-options] \n"); - buffer.append(" [-file file] [-filetype type] [-onBehalfOf name]"); - buffer.append("\n\n"); - - buffer.append("Command Mode: MultiPost - Post a file to multiple remote services.\n"); - buffer.append("swordclient -cmd -t multipost [user-options] [proxy-options] [post-options] \n"); - buffer.append(" [-dest dest]"); - - buffer.append("\n\n"); - buffer.append("User options: \n"); - buffer.append(" -u username Specify a username to access the remote service.\n"); - buffer.append(" -p password Specify a password to access the remote service.\n"); - buffer.append(" Required if -u option is used."); - - buffer.append("\n\n"); - buffer.append("Proxy options: \n"); - buffer.append(" -host host Hostname of a proxy, wwwproxy.aber.ac.uk.\n"); - buffer.append(" -port port Proxy port number, e.g. 8080.\n"); - - buffer.append("\n\n"); - buffer.append("Post options: \n"); - buffer.append(" -noOp Specified to indicate that the post is a test operation.\n"); - buffer.append(" -md5 Use an MD5 checksum in the message header.\n"); - buffer.append(" -checksumError Mis-calculate the file checksum for server test purposes.\n"); - buffer.append(" -formatNamespace ns The format namespace value.\n"); - buffer.append(" -slug name The slug value.\n"); - buffer.append(" -verbose Request a verbose response from the server.\n"); - - buffer.append("\n\n"); - buffer.append("Other options: \n"); - buffer.append(" -help Show this message.\n"); - buffer.append(" -t type The type of operation: service, post or multipost.\n"); - buffer.append(" -href url The URL for the service or post document.\n"); - buffer.append(" Required for service. The post and multipost operations \n"); - buffer.append(" will prompt you if the value is not provided.\n"); - buffer.append(" -filetype type The filetype, e.g. application/zip. The post and multipost\n"); - buffer.append(" will prompt you for the value if it is not provided.\n"); - buffer.append(" -onBehalfOf name Specify this parameter to set the On Behalf Of value.\n"); - buffer.append(" -dest dest Specify the destination for a deposit. This can be repeated\n"); - buffer.append(" multiple times. The format is: \n"); - buffer.append(" []:@\n"); - buffer.append(" e.g. sword[nst]:swordpass@http://sword.aber.ac.uk/post/\n"); - buffer.append(" nst:pass@http://sword.aber.ac.uk/post\n"); - buffer.append(" -nocapture Do not capture System.out and System.err to a debug panel\n"); - buffer.append(" in the GUI panel."); - - return buffer.toString(); - } - - /** - * Create a client. If GUI mode is set, a GUI client is created. Otherwise, - * a command line client is created. - * - * @param options The list of options extracted from the command line. - * @return A new client. - */ - public ClientType createClient(ClientOptions options) { - return new CmdClient(); - } - - /** - * Start the application and determine which client should be loaded. The - * application currently has two modes: GUI and client. The GUI mode is the - * default option. - * - * @param args the command line arguments given - */ - public static void main(String[] args) { - ClientFactory factory = new ClientFactory(); - - ClientOptions options = new ClientOptions(); - if (options.parseOptions(args)) { - ClientType client = factory.createClient(options); - client.run(options); - } else { - System.out.println(usage()); - } - } -} diff --git a/dspace-sword/src/main/java/org/purl/sword/client/ClientOptions.java b/dspace-sword/src/main/java/org/purl/sword/client/ClientOptions.java deleted file mode 100644 index 1ffbfca022b2..000000000000 --- a/dspace-sword/src/main/java/org/purl/sword/client/ClientOptions.java +++ /dev/null @@ -1,605 +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.purl.sword.client; - -import java.util.ArrayList; -import java.util.Iterator; -import java.util.List; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import org.apache.commons.cli.CommandLine; -import org.apache.commons.cli.DefaultParser; -import org.apache.commons.cli.Option; -import org.apache.commons.cli.Options; -import org.apache.commons.cli.ParseException; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; - -/** - * List of options that are parsed from the command line. - * - * @author Neil Taylor - */ -public class ClientOptions { - /** - * Label for the service operation. - */ - public static final String TYPE_SERVICE = "service"; - - /** - * Label for the post operation. - */ - public static final String TYPE_POST = "post"; - - /** - * Label for the multipost operation. - */ - public static final String TYPE_MULTI_POST = "multipost"; - - /** - * The access type. - */ - private String accessType = null; - - /** - * Proxy host name. - */ - private String proxyHost = null; - - /** - * Proxy host port. - */ - private int proxyPort = 8080; - - /** - * Username to access the service/post server. - */ - private String username = null; - - /** - * Password to access the service/post server. - */ - private String password = null; - - /** - * HREF of the server to access. - */ - private String href = null; - - /** - * Filename to post. - */ - private String filename = null; - - /** - * File type. - */ - private String filetype = null; - - /** - * Specifies that the output streams are not to be captured by the GUI client. - */ - private boolean noCapture = false; - - - /** - * SLUG Header field. - */ - private String slug = null; - - /** - * NoOp, used to indicate an operation on the server that does not - * require the file to be stored. - */ - private boolean noOp = false; - - /** - * Request verbose output from the server. - */ - private boolean verbose = false; - - /** - * OnBehalfOf user id. - */ - private String onBehalfOf = null; - - /** - * Format namespace to be used for the posted file. - */ - private String formatNamespace = null; - - /** - * Introduce a checksum error. This is used to simulate an error with the - * MD5 value. - */ - private boolean checksumError = false; - - /** - * Logger. - */ - private static final Logger log = LogManager.getLogger(); - - /** - * List of multiple destination items. Used if the mode is set to multipost. - */ - private final List multiPost = new ArrayList<>(); - - /** - * Pattern string to extract the data from a destination parameter in multipost mode. - */ - private static final Pattern MULTI_PATTERN - = Pattern.compile("(.*?)(\\[(.*?)\\]) {0,1}(:(.*)) {0,1}@(http://.*)"); - - /** - * Flag that indicates if the GUI mode has been set. This is - * true by default. - */ - private boolean guiMode = true; - - /** - * Flat that indicates if the MD5 option has been selected. This - * is true by default. - */ - private boolean md5 = false; - - /** - * Parse the list of options contained in the specified array. - * - * @param args The array of options. - * @return True if the options were parsed successfully. - */ - public boolean parseOptions(String[] args) { - Options options = new Options(); - options.addOption(Option.builder().longOpt("md5").build()) - .addOption(Option.builder().longOpt("noOp").build()) - .addOption(Option.builder().longOpt("verbose").build()) - .addOption(Option.builder().longOpt("cmd").build()) - .addOption(Option.builder().longOpt("gui").build()) - .addOption(Option.builder().longOpt("help").build()) - .addOption(Option.builder().longOpt("nocapture").build()); - - Option option; - - option = Option.builder().longOpt("host").hasArg().build(); - options.addOption(option); - - option = Option.builder().longOpt("port").hasArg().build(); - options.addOption(option); - - option = Option.builder("u").hasArg().build(); - options.addOption(option); - - option = Option.builder("p").hasArg().build(); - options.addOption(option); - - option = Option.builder().longOpt("href").hasArg().build(); - options.addOption(option); - - option = Option.builder("t").hasArg().build(); - options.addOption(option); - - option = Option.builder().longOpt("file").hasArg().build(); - options.addOption(option); - - option = Option.builder().longOpt("filetype").hasArg().build(); - options.addOption(option); - - option = Option.builder().longOpt("slug").hasArg().build(); - options.addOption(option); - - option = Option.builder().longOpt("onBehalfOf").hasArg().build(); - options.addOption(option); - - option = Option.builder().longOpt("formatNamespace").hasArg().build(); - options.addOption(option); - - option = Option.builder().longOpt("checksumError").build(); - options.addOption(option); - - option = Option.builder().longOpt("dest").hasArg().build(); - options.addOption(option); - - DefaultParser parser = new DefaultParser(); - CommandLine command; - try { - command = parser.parse(options, args); - } catch (ParseException ex) { - log.error(ex.getMessage()); - return false; - } - - if (command.hasOption("help")) { - return false; // force the calling code to display the usage information. - } - md5 = command.hasOption("md5"); - noOp = command.hasOption("noOp"); - verbose = command.hasOption("verbose"); - if (command.hasOption("cmd")) { - guiMode = false; - } - if (command.hasOption("gui")) { - guiMode = true; - } - proxyHost = command.getOptionValue("host"); - if (command.hasOption("port")) { - proxyPort = Integer.parseInt(command.getOptionValue("port")); - } - username = command.getOptionValue("u"); - password = command.getOptionValue("p"); - href = command.getOptionValue("href"); - accessType = command.getOptionValue("t"); - filename = command.getOptionValue("file"); - filetype = command.getOptionValue("filetype"); - slug = command.getOptionValue("slug"); - onBehalfOf = command.getOptionValue("onBehalfOf"); - formatNamespace = command.getOptionValue("formatNamespace"); - checksumError = command.hasOption("checksumError"); - noCapture = command.hasOption("nocapture"); - if (command.hasOption("dest")) { - String dest = command.getOptionValue("dest"); - Matcher m = MULTI_PATTERN.matcher(dest); - if (!m.matches()) { - log.debug("Error with dest parameter. Ignoring value: {}", dest); - } else { - int numGroups = m.groupCount(); - for (int g = 0; g <= numGroups; g++) { - log.debug("Group ({}) is: {}", g, m.group(g)); - } - - String group_username = m.group(1); - String group_onBehalfOf = m.group(3); - String group_password = m.group(5); - String group_url = m.group(6); - PostDestination destination = new PostDestination(group_url, - group_username, group_password, group_onBehalfOf); - - multiPost.add(destination); - } - } - - try { - // apply any settings - if (href == null && "service".equals(accessType)) { - log.error("No href specified."); - return false; - } - - if (multiPost.isEmpty() && "multipost".equals(accessType)) { - log.error("No destinations specified"); - return false; - } - - if (accessType == null && !guiMode) { - log.error("No access type specified"); - return false; - } - - if ((username == null && password != null) || (username != null && password == null)) { - log.error( - "The username and/or password are not specified. If one is specified, the other must also be " + - "specified."); - return false; - } - } catch (ArrayIndexOutOfBoundsException ex) { - log.error("Error with parameters."); - return false; - } - - return true; - } - - /** - * Get the access type. - * - * @return The value, or null if the value is not set. - */ - public String getAccessType() { - return accessType; - } - - /** - * Set the access type. - * - * @param accessType The value, or null to clear the value. - */ - public void setAccessType(String accessType) { - this.accessType = accessType; - } - - /** - * Get the proxy host. - * - * @return The value, or null if the value is not set. - */ - public String getProxyHost() { - return proxyHost; - } - - /** - * Set the proxy host. - * - * @param proxyHost The value, or null to clear the value. - */ - public void setProxyHost(String proxyHost) { - this.proxyHost = proxyHost; - } - - /** - * Get the proxy port. - * - * @return The proxy port. Default value is 80. - */ - public int getProxyPort() { - return proxyPort; - } - - /** - * Set the proxy port. - * - * @param proxyPort The proxy port. - */ - public void setProxyPort(int proxyPort) { - this.proxyPort = proxyPort; - } - - /** - * Get the username. - * - * @return The value, or null if the value is not set. - */ - public String getUsername() { - return username; - } - - /** - * Set the username. - * - * @param username The value, or null to clear the value. - */ - public void setUsername(String username) { - this.username = username; - } - - /** - * Get the password. - * - * @return The value, or null if the value is not set. - */ - public String getPassword() { - return password; - } - - /** - * Set the password. - * - * @param password The value, or null to clear the value. - */ - public void setPassword(String password) { - this.password = password; - } - - /** - * Get the HREF of the service to access. - * - * @return The value, or null if the value is not set. - */ - public String getHref() { - return href; - } - - /** - * Set the HREF of the service to access. - * - * @param href The value, or null to clear the value. - */ - public void setHref(String href) { - this.href = href; - } - - /** - * Get the name of the file to post. - * - * @return The value, or null if the value is not set. - */ - public String getFilename() { - return filename; - } - - /** - * Set the name of the file to post. - * - * @param filename The value, or null to clear the value. - */ - public void setFilename(String filename) { - this.filename = filename; - } - - /** - * Get the type of the file to post. - * - * @return The filetype, or null if the value is not set. - */ - public String getFiletype() { - return filetype; - } - - /** - * Set the type of the file to post. - * - * @param filetype The value, or null to clear the value. - */ - public void setFiletype(String filetype) { - this.filetype = filetype; - } - - /** - * Determine if the tool is to be run in GUI mode. - * - * @return True if the tool is set for GUI mode. - */ - public boolean isGuiMode() { - return guiMode; - } - - /** - * Set the tool to run in GUI mode. - * - * @param guiMode True if the tool is to run in gui mode. - */ - public void setGuiMode(boolean guiMode) { - this.guiMode = guiMode; - } - - /** - * Get the MD5 setting. True if the tool is to use MD5 for post operations. - * - * @return The MD5 setting. - */ - public boolean isMd5() { - return md5; - } - - /** - * Set the MD5 setting. - * - * @param md5 True if the tool should use MD5 for post operations. - */ - public void setMd5(boolean md5) { - this.md5 = md5; - } - - /** - * Determine if the NoOp header should be sent. - * - * @return True if the header should be sent. - */ - public boolean isNoOp() { - return noOp; - } - - /** - * Set the NoOp setting. - * - * @param noOp True if the NoOp header should be used. - */ - public void setNoOp(boolean noOp) { - this.noOp = noOp; - } - - /** - * Determine if the verbose option is set. - * - * @return True if verbose option is set. - */ - public boolean isVerbose() { - return verbose; - } - - /** - * Set the verbose option. - * - * @param verbose True if verbose should be set. - */ - public void setVerbose(boolean verbose) { - this.verbose = verbose; - } - - /** - * Get the onBehalfOf value. - * - * @return The value, or null to clear the value. - */ - public String getOnBehalfOf() { - return onBehalfOf; - } - - /** - * Set the onBehalf of Value. - * - * @param onBehalfOf The value, or null to clear the value. - */ - public void setOnBehalfOf(String onBehalfOf) { - this.onBehalfOf = onBehalfOf; - } - - /** - * Get the format namespace value. - * - * @return The value, or null if the value is not set. - */ - public String getFormatNamespace() { - return formatNamespace; - } - - /** - * Set the format namespace value. - * - * @param formatNamespace The value, or null to clear the value. - */ - public void setFormatNamespace(String formatNamespace) { - this.formatNamespace = formatNamespace; - } - - /** - * Get the checksum error value. - * - * @return True if an error should be introduced into the checksum. - */ - public boolean getChecksumError() { - return checksumError; - } - - /** - * Set the checksum error value. - * - * @param checksumError True if the error should be introduced. - */ - public void setChecksumError(boolean checksumError) { - this.checksumError = checksumError; - } - - /** - * Get the current slug header. - * - * @return The slug value, or null if the value is not set. - */ - public String getSlug() { - return this.slug; - } - - /** - * Set the text that is to be used for the slug header. - * - * @param slug The value, or null to clear the value. - */ - public void setSlug(String slug) { - this.slug = slug; - } - - /** - * Get the list of post destinations. - * - * @return An iterator over the list of PostDestination objects. - */ - public Iterator getMultiPost() { - return multiPost.iterator(); - } - - - /** - * Determine if the noCapture option is set. This indicates that the code - * should not attempt to redirect stdout and stderr to a different output - * destination. Intended for use in a GUI client. - * - * @return The noCapture setting. True if set. - */ - public boolean isNoCapture() { - return noCapture; - } -} diff --git a/dspace-sword/src/main/java/org/purl/sword/client/ClientType.java b/dspace-sword/src/main/java/org/purl/sword/client/ClientType.java deleted file mode 100644 index 11474782c1cf..000000000000 --- a/dspace-sword/src/main/java/org/purl/sword/client/ClientType.java +++ /dev/null @@ -1,23 +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.purl.sword.client; - -/** - * Interface for a client. This contains a single method that allows the factory - * to pass a set of command line options to the client. - * - * @author Neil Taylor - */ -public interface ClientType { - /** - * Run the client, processing the specified options. - * - * @param options The options extracted from the command line. - */ - public void run(ClientOptions options); -} diff --git a/dspace-sword/src/main/java/org/purl/sword/client/CmdClient.java b/dspace-sword/src/main/java/org/purl/sword/client/CmdClient.java deleted file mode 100644 index 842e9d483dca..000000000000 --- a/dspace-sword/src/main/java/org/purl/sword/client/CmdClient.java +++ /dev/null @@ -1,445 +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.purl.sword.client; - -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStreamReader; -import java.net.MalformedURLException; -import java.net.URL; -import java.util.Iterator; -import java.util.List; - -import org.apache.logging.log4j.Logger; -import org.purl.sword.atom.Author; -import org.purl.sword.atom.Content; -import org.purl.sword.atom.Contributor; -import org.purl.sword.atom.Generator; -import org.purl.sword.atom.Link; -import org.purl.sword.atom.Rights; -import org.purl.sword.atom.Summary; -import org.purl.sword.atom.Title; -import org.purl.sword.base.Collection; -import org.purl.sword.base.DepositResponse; -import org.purl.sword.base.SWORDEntry; -import org.purl.sword.base.ServiceDocument; -import org.purl.sword.base.SwordAcceptPackaging; -import org.purl.sword.base.Workspace; - -/** - * Example implementation of a command line client. This can send out service - * document requests and print out the results and process posting a file to - * either a single or multiple destinations. The command line options are - * initialised prior to calling the class. The options are passed into the - * run(ClientOptions) method. - * - * @author Neil Taylor - */ -public class CmdClient implements ClientType { - /** - * The client that is used to process the service and post requests. - */ - private SWORDClient client; - - /** - * List of the options that can be specified on the command line. - */ - private ClientOptions options; - - /** - * The logger. - */ - private static Logger log = org.apache.logging.log4j.LogManager.getLogger(CmdClient.class); - - /** - * Create a new instance of the class and create an instance of the - * client. - */ - public CmdClient() { - client = new Client(); - } - - /** - * Process the options that have been initialised from the command line. - * This will call one of service(), post() or multiPost(). - */ - public void process() { - if (options.getProxyHost() != null) { - client.setProxy(options.getProxyHost(), options.getProxyPort()); - } - - try { - String accessType = options.getAccessType(); - if (ClientOptions.TYPE_SERVICE.equals(accessType)) { - service(); - } else if (ClientOptions.TYPE_POST.equals(accessType)) { - post(); - } else if (ClientOptions.TYPE_MULTI_POST.equals(accessType)) { - System.out.println("checking multi-post"); - multiPost(); - } else { - System.out.println("Access type not recognised."); - } - - } catch (MalformedURLException mex) { - System.out - .println("The specified href was not valid: " + options.getHref() + " message: " + mex.getMessage()); - } catch (SWORDClientException ex) { - System.out.println("Exception: " + ex.getMessage()); - log.error("Unable to process request", ex); - } - } - - /** - * Process the service operation. Output the results of the service request. - * - * @throws SWORDClientException if there is an error processing the service request. - * @throws MalformedURLException if there is an error with the URL for the service request. - */ - private void service() - throws SWORDClientException, MalformedURLException { - String href = options.getHref(); - initialiseServer(href, options.getUsername(), options.getPassword()); - - ServiceDocument document = client.getServiceDocument(href, options.getOnBehalfOf()); - Status status = client.getStatus(); - System.out.println("The status is: " + status); - - if (status.getCode() == 200) { - log.debug("message is: " + document.marshall()); - - System.out.println("\nThe following Details were retrieved: "); - System.out.println("SWORD Version: " - + document.getService().getVersion()); - System.out.println("Supports NoOp? " + document.getService().isNoOp()); - System.out.println("Supports Verbose? " - + document.getService().isVerbose()); - System.out.println("Max Upload File Size " - + document.getService().getMaxUploadSize() + " kB"); - - Iterator workspaces = document.getService().getWorkspaces(); - for (; workspaces.hasNext(); ) { - Workspace workspace = workspaces.next(); - System.out.println("\nWorkspace Title: '" - + workspace.getTitle() + "'"); - - System.out.println("\n+ Collections ---"); - // process the collections - Iterator collections = workspace - .collectionIterator(); - for (; collections.hasNext(); ) { - Collection collection = collections.next(); - System.out.println("\nCollection location: " - + collection.getLocation()); - System.out.println("Collection title: " - + collection.getTitle()); - System.out - .println("Abstract: " + collection.getAbstract()); - System.out.println("Collection Policy: " - + collection.getCollectionPolicy()); - System.out.println("Treatment: " - + collection.getTreatment()); - System.out.println("Mediation: " - + collection.getMediation()); - - String[] accepts = collection.getAccepts(); - if (accepts != null && accepts.length == 0) { - System.out.println("Accepts: none specified"); - } else { - for (String s : accepts) { - System.out.println("Accepts: " + s); - } - } - List acceptsPackaging = collection.getAcceptPackaging(); - - StringBuilder acceptPackagingList = new StringBuilder(); - for (Iterator i = acceptsPackaging.iterator(); i.hasNext(); ) { - SwordAcceptPackaging accept = (SwordAcceptPackaging) i.next(); - acceptPackagingList.append(accept.getContent()).append(" (").append(accept.getQualityValue()) - .append("), "); - } - - System.out.println("Accepts Packaging: " + acceptPackagingList.toString()); - } - System.out.println("+ End of Collections ---"); - } - } - } - - /** - * Perform a post. If any of the destination URL, the filename and the - * filetype are missing, the user will be prompted to enter the values. - * - * @throws SWORDClientException if there is an error processing the post for a requested - * destination. - * @throws MalformedURLException if there is an error with the URL for the post. - */ - private void post() - throws SWORDClientException, MalformedURLException { - String url = options.getHref(); - if (url == null) { - url = readLine("Please enter the URL for the deposit: "); - } - - initialiseServer(url, options.getUsername(), options.getPassword()); - String file = options.getFilename(); - if (file == null) { - file = readLine("Please enter the filename to deposit: "); - } - String type = options.getFiletype(); - if (type == null) { - type = readLine("Please enter the file type, e.g. application/zip: "); - } - - PostMessage message = new PostMessage(); - message.setFilepath(file); - message.setDestination(url); - message.setFiletype(type); - message.setUseMD5(options.isMd5()); - message.setVerbose(options.isVerbose()); - message.setNoOp(options.isNoOp()); - message.setFormatNamespace(options.getFormatNamespace()); - message.setOnBehalfOf(options.getOnBehalfOf()); - message.setChecksumError(options.getChecksumError()); - message.setUserAgent(ClientConstants.SERVICE_NAME); - - processPost(message); - - } - - /** - * Perform a multi-post. Iterate over the list of -dest arguments in the command line - * options. For each -dest argument, attempt to post the file to the server. - * - * @throws SWORDClientException if there is an error processing the post for a requested - * destination. - * @throws MalformedURLException if there is an error with the URL for the post. - */ - private void multiPost() - throws SWORDClientException, MalformedURLException { - // request the common information - String file = options.getFilename(); - if (file == null) { - file = readLine("Please enter the filename to deposit: "); - } - String type = options.getFiletype(); - if (type == null) { - type = readLine("Please enter the file type, e.g. application/zip: "); - } - - // process this information for each of the specified destinations - PostDestination destination; - String url = null; - - Iterator iterator = options.getMultiPost(); - while (iterator.hasNext()) { - destination = iterator.next(); - url = destination.getUrl(); - initialiseServer(url, destination.getUsername(), destination.getPassword()); - - String onBehalfOf = destination.getOnBehalfOf(); - if (onBehalfOf == null) { - onBehalfOf = ""; - } else { - onBehalfOf = " on behalf of: " + onBehalfOf; - } - - System.out.println("Sending file to: " + url + " for: " + destination.getUsername() + - onBehalfOf); - PostMessage message = new PostMessage(); - message.setFilepath(file); - message.setDestination(url); - message.setFiletype(type); - message.setUseMD5(options.isMd5()); - message.setVerbose(options.isVerbose()); - message.setNoOp(options.isNoOp()); - message.setFormatNamespace(options.getFormatNamespace()); - message.setOnBehalfOf(destination.getOnBehalfOf()); - message.setChecksumError(options.getChecksumError()); - message.setUserAgent(ClientConstants.SERVICE_NAME); - - processPost(message); - } - - } - - /** - * Process the post response. The message contains the list of arguments - * for the post. The method will then print out the details of the - * response. - * - * @param message The post options. - * @throws SWORDClientException if there is an error accessing the - * post response. - */ - protected void processPost(PostMessage message) - throws SWORDClientException { - DepositResponse response = client.postFile(message); - - System.out.println("The status is: " + client.getStatus()); - - if (response != null) { - log.debug("message is: " + response.marshall()); - - // iterate over the data and output it - SWORDEntry entry = response.getEntry(); - - - System.out.println("Id: " + entry.getId()); - Title title = entry.getTitle(); - if (title != null) { - System.out.print("Title: " + title.getContent() + " type: "); - if (title.getType() != null) { - System.out.println(title.getType().toString()); - } else { - System.out.println("Not specified."); - } - } - - // process the authors - Iterator authors = entry.getAuthors(); - while (authors.hasNext()) { - Author author = authors.next(); - System.out.println("Author - " + author.toString()); - } - - Iterator categories = entry.getCategories(); - while (categories.hasNext()) { - System.out.println("Category: " + categories.next()); - } - - Iterator contributors = entry.getContributors(); - while (contributors.hasNext()) { - Contributor contributor = contributors.next(); - System.out.println("Contributor - " + contributor.toString()); - } - - Iterator links = entry.getLinks(); - while (links.hasNext()) { - Link link = links.next(); - System.out.println(link.toString()); - } - - Generator generator = entry.getGenerator(); - if (generator != null) { - System.out.println("Generator - " + generator.toString()); - } else { - System.out.println("There is no generator"); - } - - System.out.println("Published: " + entry.getPublished()); - - Content content = entry.getContent(); - if (content != null) { - System.out.println(content.toString()); - } else { - System.out.println("There is no content element."); - } - - Rights right = entry.getRights(); - if (right != null) { - System.out.println(right.toString()); - } else { - System.out.println("There is no right element."); - } - - Summary summary = entry.getSummary(); - if (summary != null) { - - System.out.println(summary.toString()); - } else { - System.out.println("There is no summary element."); - } - - System.out.println("Update: " + entry.getUpdated()); - System.out.println("Published: " + entry.getPublished()); - System.out.println("Verbose Description: " + entry.getVerboseDescription()); - System.out.println("Treatment: " + entry.getTreatment()); - System.out.println("Packaging: " + entry.getPackaging()); - - if (entry.isNoOpSet()) { - System.out.println("NoOp: " + entry.isNoOp()); - } - } else { - System.out.println("No valid Entry document was received from the server"); - } - } - - /** - * Initialise the server. Set the server that will be connected to and - * initialise any username and password. If the username and password are - * either null or contain empty strings, the user credentials will be cleared. - * - * @param location The location to connect to. This is a URL, of the format, - * http://a.host.com:port/. The host name and port number will - * be extracted. If the port is not specified, a default port of - * 80 will be used. - * @param username The username. If this is null or an empty string, the basic - * credentials will be cleared. - * @param password The password. If this is null or an empty string, the basic - * credentials will be cleared. - * @throws MalformedURLException if there is an error processing the URL. - */ - private void initialiseServer(String location, String username, String password) - throws MalformedURLException { - URL url = new URL(location); - int port = url.getPort(); - if (port == -1) { - port = 80; - } - - client.setServer(url.getHost(), port); - - if (username != null && username.length() > 0 && - password != null && password.length() > 0) { - log.info("Setting the username/password: " + username + " " - + password); - client.setCredentials(username, password); - } else { - client.clearCredentials(); - } - } - - /** - * Read a line of text from System.in. If there is an error reading - * from the input, the prompt will be redisplayed and the user asked - * to try again. - * - * @param prompt The prompt to display before the prompt. - * @return The string that is read from the line. - */ - private String readLine(String prompt) { - BufferedReader reader = new BufferedReader(new InputStreamReader( - System.in)); - String result = null; - - boolean ok = false; - while (!ok) { - try { - System.out.print(prompt); - System.out.flush(); - result = reader.readLine(); - ok = true; - } catch (IOException ex) { - System.out.println("There was an error with your input. Please try again."); - } - } - - return result; - } - - /** - * Run the client and process the specified options. - * - * @param options The command line options. - */ - public void run(ClientOptions options) { - this.options = options; - process(); - } -} diff --git a/dspace-sword/src/main/java/org/purl/sword/client/DebugOutputStream.java b/dspace-sword/src/main/java/org/purl/sword/client/DebugOutputStream.java deleted file mode 100644 index dfd8b6c60f26..000000000000 --- a/dspace-sword/src/main/java/org/purl/sword/client/DebugOutputStream.java +++ /dev/null @@ -1,44 +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.purl.sword.client; - -import java.io.IOException; -import java.io.OutputStream; - -/** - * A stream that will write any output to the specified panel. - * - * @author Neil Taylor - */ -public class DebugOutputStream extends OutputStream { - /** - * Panel that will display the messages. - */ - private MessageOutputPanel panel; - - /** - * Create a new instance and specify the panel that will receive the output. - * - * @param panel The panel. - */ - public DebugOutputStream(MessageOutputPanel panel) { - this.panel = panel; - } - - /** - * Override the write method from OutputStream. Capture the char and - * send it to the panel. - * - * @param arg0 The output character, expressed as an integer. - * @see java.io.OutputStream#write(int) - */ - public void write(int arg0) throws IOException { - panel.addCharacter(Character.valueOf((char) arg0)); - } - -} diff --git a/dspace-sword/src/main/java/org/purl/sword/client/MessageOutputPanel.java b/dspace-sword/src/main/java/org/purl/sword/client/MessageOutputPanel.java deleted file mode 100644 index e703f79ccccb..000000000000 --- a/dspace-sword/src/main/java/org/purl/sword/client/MessageOutputPanel.java +++ /dev/null @@ -1,138 +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/ - */ - -/** - * Copyright (c) 2007, Aberystwyth University - * - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * - Redistributions of source code must retain the above - * copyright notice, this list of conditions and the - * following disclaimer. - * - * - Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * - * - Neither the name of the Centre for Advanced Software and - * Intelligent Systems (CASIS) nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR - * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF - * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ -package org.purl.sword.client; - -import java.awt.GridBagConstraints; -import java.awt.GridBagLayout; -import java.awt.Insets; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import javax.swing.JButton; -import javax.swing.JPanel; -import javax.swing.JScrollPane; -import javax.swing.JTextArea; - -/** - * Panel to display output messages. Text or characters can be sent to the - * panel for display. The panel also includes a button to clear any - * text that is currently displayed. - * - * @author Neil Taylor - */ -public class MessageOutputPanel extends JPanel - implements ActionListener { - - /** - * The text area that displays the messages. - */ - private JTextArea messages = null; - - /** - * Create a new instance and initialise the panel. - */ - public MessageOutputPanel() { - super(); - - setLayout(new GridBagLayout()); - - messages = new JTextArea(); - - JScrollPane detailsPane = new JScrollPane(messages, - JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, - JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS); - JButton clearButton = new JButton("Clear"); - clearButton.addActionListener(this); - - //add components and set constraints - //dpc = details pane constraint - GridBagConstraints dpc = new GridBagConstraints(); - dpc.gridx = 0; - dpc.gridy = 0; - dpc.fill = GridBagConstraints.BOTH; - dpc.weightx = 0.75; - dpc.weighty = 0.45; - dpc.gridwidth = 2; - dpc.insets = new Insets(5, 5, 5, 5); - add(detailsPane, dpc); - - //cbc = clear button constraint - GridBagConstraints cbc = new GridBagConstraints(); - cbc.gridx = 1; - cbc.gridy = 1; - cbc.insets = new Insets(0, 0, 5, 5); - cbc.anchor = GridBagConstraints.LINE_END; - add(clearButton, cbc); - - } - - /** - * Add a message to the text area. The message will be added with a carriage return. - * - * @param message The message. - */ - public void addMessage(String message) { - messages.insert(message + "\n", messages.getDocument().getLength()); - } - - /** - * Add a single character to the text area. - * - * @param character The character. - */ - public void addCharacter(Character character) { - messages.insert(character.toString(), messages.getDocument().getLength()); - } - - /** - * Clear the text from the display. - * - * @param arg0 The action event. - * - * @see java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent) - */ - public void actionPerformed(ActionEvent arg0) { - messages.setText(""); - } -} diff --git a/dspace-sword/src/main/java/org/purl/sword/client/PostDestination.java b/dspace-sword/src/main/java/org/purl/sword/client/PostDestination.java deleted file mode 100644 index 3124f02b45d7..000000000000 --- a/dspace-sword/src/main/java/org/purl/sword/client/PostDestination.java +++ /dev/null @@ -1,140 +0,0 @@ -/** - * The contents of this file are subject to the license and copyright - * detailed in the LICENSE and NOTICE files at the root of the source - * tree and available online at - * - * http://www.dspace.org/license/ - */ -package org.purl.sword.client; - -/** - * Details for a destination. This is used to represent a destination. If - * expressed as a string, the destination looks like: - *
    - * <user>[<onBehalfOf>]:<password>@<url>
    - * 
    - * - * @author Neil Taylor - */ -public class PostDestination { - /** - * URL for the post destination. - */ - private String url; - - /** - * The username. - */ - private String username; - - /** - * The password. - */ - private String password; - - /** - * The onBehalfOf ID. - */ - private String onBehalfOf; - - /** - * Create a new instance. - */ - public PostDestination() { - // No-Op - } - - /** - * Create a new instance. - * - * @param url The url. - * @param username The username. - * @param password The password. - * @param onBehalfOf The onBehalfOf id. - */ - public PostDestination(String url, String username, String password, String onBehalfOf) { - this.url = url; - this.username = username; - this.password = password; - this.onBehalfOf = onBehalfOf; - } - - /** - * @return the url - */ - public String getUrl() { - return url; - } - - /** - * @param url the url to set - */ - public void setUrl(String url) { - this.url = url; - } - - /** - * @return the username - */ - public String getUsername() { - return username; - } - - /** - * @param username the username to set - */ - public void setUsername(String username) { - this.username = username; - } - - /** - * @return the password - */ - public String getPassword() { - return password; - } - - /** - * @param password the password to set - */ - public void setPassword(String password) { - this.password = password; - } - - /** - * @return the onBehalfOf - */ - public String getOnBehalfOf() { - return onBehalfOf; - } - - /** - * @param onBehalfOf the onBehalfOf to set - */ - public void setOnBehalfOf(String onBehalfOf) { - this.onBehalfOf = onBehalfOf; - } - - /** - * Create a string representation of this object. - * - * @return The string. - */ - public String toString() { - StringBuffer buffer = new StringBuffer(); - buffer.append(username); - if (onBehalfOf != null) { - buffer.append("["); - buffer.append(onBehalfOf); - buffer.append("]"); - } - - if (password != null) { - buffer.append(":******"); - } - buffer.append("@"); - buffer.append(url); - - return buffer.toString(); - } -} diff --git a/dspace-sword/src/main/java/org/purl/sword/client/PostDialog.java b/dspace-sword/src/main/java/org/purl/sword/client/PostDialog.java deleted file mode 100644 index 84815b327e3b..000000000000 --- a/dspace-sword/src/main/java/org/purl/sword/client/PostDialog.java +++ /dev/null @@ -1,599 +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/ - */ - -/** - * Copyright (c) 2007, Aberystwyth University - * - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * - Redistributions of source code must retain the above - * copyright notice, this list of conditions and the - * following disclaimer. - * - * - Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * - * - Neither the name of the Centre for Advanced Software and - * Intelligent Systems (CASIS) nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR - * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF - * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ -package org.purl.sword.client; - -import java.awt.BorderLayout; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.io.File; -import javax.swing.DefaultListModel; -import javax.swing.JButton; -import javax.swing.JCheckBox; -import javax.swing.JFileChooser; -import javax.swing.JFrame; -import javax.swing.JLabel; -import javax.swing.JList; -import javax.swing.JOptionPane; -import javax.swing.JPanel; -import javax.swing.JPasswordField; -import javax.swing.JScrollPane; -import javax.swing.event.ChangeEvent; -import javax.swing.event.ChangeListener; - -/** - * Dialog for users to enter details of post destinations. - * - * @author Neil Taylor - */ -public class PostDialog - implements ActionListener, ChangeListener { - /** - * label for the browse command. - */ - protected static final String BROWSE = "browse"; - - /** - * label for the add command. - */ - protected static final String ADD = "add"; - - /** - * label for the edit command. - */ - protected static final String EDIT = "edit"; - - /** - * label for the delete command. - */ - protected static final String DELETE = "delete"; - - /** - * label for the clear command. - */ - protected static final String CLEAR = "clear"; - - /** - * Username combo box. - */ - private SWORDComboBox username; - - /** - * Post Location combo box. - */ - private SWORDComboBox postLocation; - - /** - * Password field. - */ - private JPasswordField password; - - /** - * The file combo box. - */ - private SWORDComboBox file; - - /** - * The filetype combo box. - */ - private SWORDComboBox fileType; - - /** - * The onBehalfOf combo box. - */ - private SWORDComboBox onBehalfOf; - - /** - * The md5 checkbox. - */ - private JCheckBox useMD5; - - /** - * The corruptMD5 checkbox. - */ - private JCheckBox corruptMD5; - - /** - * The corruptRequest checkbox. - */ - private JCheckBox corruptRequest; - - /** - * The useNoOp checkbox. - */ - private JCheckBox useNoOp; - - /** - * The verbose checkbox. - */ - private JCheckBox useVerbose; - - /** - * The format namespace combo box. - */ - private SWORDComboBox formatNamespace; - - /** - * The list of post destinations. - */ - private JList list; - - /** - * The parent frame for the dialog that is displayed. - */ - private JFrame parentFrame = null; - - /** - * Array that lists the labels for the buttons on the panel. - */ - private static Object[] options = {"Post File", "Cancel"}; - - /** - * The panel that holds the controls to show. - */ - private JPanel controls = null; - - /** - * - * @param parentFrame the parent of this dialog. - */ - public PostDialog(JFrame parentFrame) { - this.parentFrame = parentFrame; - controls = createControls(); - } - - /** - * Show the dialog with ok and cancel options. - * @return The return value from displaying JOptionPane. Either - * JOptionPane.OK_OPTION or JOptionPane.CANCEL_OPTION. - */ - public int show() { - int result = JOptionPane.showOptionDialog(parentFrame, - controls, - "Post Document", - JOptionPane.OK_CANCEL_OPTION, - JOptionPane.PLAIN_MESSAGE, - null, - options, - null); - - if (result == JOptionPane.OK_OPTION) { - // update the combo boxes with the values - username.updateList(); - file.updateList(); - fileType.updateList(); - onBehalfOf.updateList(); - formatNamespace.updateList(); - } - - return result; - } - - /** - * Create the controls for the main panel. - * - * @return The panel. - */ - protected final JPanel createControls() { - file = new SWORDComboBox(); - JPanel filePanel = new JPanel(new BorderLayout()); - filePanel.add(file, BorderLayout.CENTER); - JButton browse = new JButton("Browse..."); - browse.setActionCommand(BROWSE); - browse.addActionListener(this); - - filePanel.add(browse, BorderLayout.SOUTH); - - fileType = new SWORDComboBox(); - String type = "application/zip"; - fileType.addItem(type); - fileType.setSelectedItem(type); - - // controls that will be used in the second dialog - postLocation = new SWORDComboBox(); - username = new SWORDComboBox(); - password = new JPasswordField(); - onBehalfOf = new SWORDComboBox(); - - - useMD5 = new JCheckBox(); - useMD5.addChangeListener(this); - corruptMD5 = new JCheckBox(); - corruptRequest = new JCheckBox(); - useNoOp = new JCheckBox(); - useVerbose = new JCheckBox(); - formatNamespace = new SWORDComboBox(); - - JLabel fileLabel = new JLabel("File:", JLabel.TRAILING); - JLabel fileTypeLabel = new JLabel("File Type:", JLabel.TRAILING); - JLabel useMD5Label = new JLabel("Use MD5:", JLabel.TRAILING); - JLabel corruptMD5Label = new JLabel("Corrupt MD5:", JLabel.TRAILING); - JLabel corruptRequestLabel = new JLabel("Corrupt Request:", JLabel.TRAILING); - //JLabel corruptMD5Label = new JLabel("Corrupt MD5:", JLabel.TRAILING); - JLabel useNoOpLabel = new JLabel("Use noOp:", JLabel.TRAILING); - JLabel useVerboseLabel = new JLabel("Use verbose:", JLabel.TRAILING); - JLabel formatNamespaceLabel = new JLabel("X-Packaging:", JLabel.TRAILING); - JLabel userAgentLabel = new JLabel("User Agent:", JLabel.TRAILING); - JLabel userAgentNameLabel = new JLabel(ClientConstants.SERVICE_NAME, JLabel.LEADING); - - SWORDFormPanel panel = new SWORDFormPanel(); - panel.addFirstRow(new JLabel("Please enter the details for the post operation")); - - JPanel destinations = createDestinationsPanel(); - - panel.addRow(new JLabel("Destinations:"), destinations); - panel.addRow(fileLabel, filePanel); - panel.addRow(fileTypeLabel, fileType); - panel.addRow(useMD5Label, useMD5); - panel.addRow(corruptMD5Label, corruptMD5); - panel.addRow(corruptRequestLabel, corruptRequest); - panel.addRow(useNoOpLabel, useNoOp); - panel.addRow(useVerboseLabel, useVerbose); - panel.addRow(formatNamespaceLabel, formatNamespace); - panel.addRow(userAgentLabel, userAgentNameLabel); - - return panel; - } - - /** - * Create the destinations panel. This contains a list and four buttons - * to operate on values in the list. - * - * @return The panel containing the controls. - */ - protected JPanel createDestinationsPanel() { - DefaultListModel model = new DefaultListModel(); - list = new JList(model); - JScrollPane jsp = new JScrollPane(list); - - JPanel destinations = new JPanel(new BorderLayout()); - destinations.add(jsp, BorderLayout.CENTER); - JPanel destinationButtons = new JPanel(); - - JButton addButton = new JButton("Add"); - addButton.setActionCommand(ADD); - addButton.addActionListener(this); - - JButton editButton = new JButton("Edit"); - editButton.setActionCommand(EDIT); - editButton.addActionListener(this); - - JButton deleteButton = new JButton("Delete"); - deleteButton.setActionCommand(DELETE); - deleteButton.addActionListener(this); - - JButton clearButton = new JButton("Clear"); - clearButton.setActionCommand(CLEAR); - clearButton.addActionListener(this); - - destinationButtons.add(addButton); - destinationButtons.add(editButton); - destinationButtons.add(deleteButton); - destinationButtons.add(clearButton); - - destinations.add(destinationButtons, BorderLayout.SOUTH); - - return destinations; - } - - /** - * Handle the button click to select a file to upload. - */ - public void actionPerformed(ActionEvent evt) { - String cmd = evt.getActionCommand(); - - if (BROWSE.equals(cmd)) { - JFileChooser chooser = new JFileChooser(); - chooser.setCurrentDirectory(new File(System.getProperty("user.dir"))); - int returnVal = chooser.showOpenDialog(parentFrame); - if (returnVal == JFileChooser.APPROVE_OPTION) { - file.setSelectedItem(chooser.getSelectedFile().getAbsolutePath()); - } - } else if (ADD.equals(cmd)) { - PostDestination dest = showDestinationDialog(null); - if (dest != null) { - ((DefaultListModel) list.getModel()).addElement(dest); - } - } else if (EDIT.equals(cmd)) { - PostDestination dest = (PostDestination) list.getSelectedValue(); - if (dest != null) { - showDestinationDialog(dest); - list.repaint(); - } - } else if (DELETE.equals(cmd)) { - if (list.getSelectedIndex() != -1) { - ((DefaultListModel) list.getModel()).removeElementAt(list.getSelectedIndex()); - } - } else if (CLEAR.equals(cmd)) { - ((DefaultListModel) list.getModel()).clear(); - } - } - - /** - * Show the destination dialog. This is used to enter the URL, - * username, password and onBehalfOf name for a destination. - * - * @param destination The post destination. If this is not null, the values - * in the object are used to set the current values - * in the dialog controls. - * @return The post destination value. - */ - public PostDestination showDestinationDialog(PostDestination destination) { - SWORDFormPanel panel = new SWORDFormPanel(); - panel.addFirstRow(new JLabel("Please enter the details for the post operation")); - - JLabel postLabel = new JLabel("Post Location:", JLabel.TRAILING); - JLabel userLabel = new JLabel("Username:", JLabel.TRAILING); - JLabel passwordLabel = new JLabel("Password:", JLabel.TRAILING); - JLabel onBehalfOfLabel = new JLabel("On Behalf Of:", JLabel.TRAILING); - - panel.addRow(postLabel, postLocation); - panel.addRow(userLabel, username); - panel.addRow(passwordLabel, password); - panel.addRow(onBehalfOfLabel, onBehalfOf); - - if (destination != null) { - postLocation.insertItem(destination.getUrl()); - username.insertItem(destination.getUsername()); - password.setText(destination.getPassword()); - onBehalfOf.insertItem(destination.getOnBehalfOf()); - } else { - String s = ""; - postLocation.insertItem(s); - //postLocation.setSelectedItem(s); - username.insertItem(s); - username.setSelectedItem(s); - password.setText(s); - onBehalfOf.insertItem(s); - onBehalfOf.setSelectedItem(s); - } - - int result = JOptionPane.showOptionDialog(null, - panel, - "Destination", - JOptionPane.OK_CANCEL_OPTION, - JOptionPane.PLAIN_MESSAGE, - null, - new String[] {"OK", "Cancel"}, - null); - - if (result == JOptionPane.OK_OPTION) { - postLocation.updateList(); - username.updateList(); - onBehalfOf.updateList(); - - if (destination == null) { - destination = new PostDestination(); - } - - destination.setUrl(postLocation.getText()); - destination.setUsername(username.getText()); - String pass = new String(password.getPassword()); - if (pass.length() > 0) { - destination.setPassword(pass); - } else { - destination.setPassword(null); - } - - String obo = onBehalfOf.getText(); - if (obo.length() > 0) { - destination.setOnBehalfOf(onBehalfOf.getText()); - } else { - destination.setOnBehalfOf(null); - } - - } - - return destination; - } - - /** - * Get the list of Post Destinations. - * @return The destinations. - */ - public PostDestination[] getDestinations() { - DefaultListModel model = (DefaultListModel) list.getModel(); - PostDestination[] destinations = new PostDestination[model.size()]; - for (int i = 0; i < model.size(); i++) { - destinations[i] = (PostDestination) model.get(i); - } - return destinations; - } - - /** - * Get the file details. - * @return The value. - */ - public String getFile() { - return file.getText(); - } - - /** - * Get the filetype value. - * @return The value. - */ - public String getFileType() { - return fileType.getText(); - } - - /** - * Get the onBehalfOf value. - * @return The value. - */ - public String getOnBehalfOf() { - return onBehalfOf.getText(); - } - - /** - * Get the format namespace value. - * @return The value. - */ - public String getFormatNamespace() { - return formatNamespace.getText(); - } - - /** - * Determine if the MD5 checkbox is selected. - * - * @return True if the MD5 checkbox is selected. - */ - public boolean useMd5() { - return useMD5.isSelected(); - } - - /** - * Determine if the noOp checkbox is selected. - * - * @return True if the checkbox is selected. - */ - public boolean useNoOp() { - return useNoOp.isSelected(); - } - - /** - * Determine if the verbose checkbox is selected. - * - * @return True if the checkbox is selected. - */ - public boolean useVerbose() { - return useVerbose.isSelected(); - } - - /** - * Get the post location. - * @return The post location. - */ - public String getPostLocation() { - return postLocation.getText(); - } - - /** - * Determine if the MD5 hash should be corrupted. - * @return True if the corrupt MD5 checkbox is selected. The MD5 checkbox - * must also be selected. - */ - public boolean corruptMD5() { - return (corruptMD5.isEnabled() && corruptMD5.isSelected()); - } - - /** - * Determine if the POST request should be corrupted. - * @return True if the corrupt request checkbox is selected. - */ - public boolean corruptRequest() { - return (corruptRequest.isSelected()); - } - - /** - * Detect a state change event for the checkbox. - * - * @param evt The event. - */ - public void stateChanged(ChangeEvent evt) { - corruptMD5.setEnabled(useMD5.isSelected()); - } - - /** - * Add a list of user ids. - * - * @param users The user ids. - */ - public void addUserIds(String[] users) { - username.insertItems(users); - } - - /** - * Add a list of deposit URLs. - * - * @param deposits The URLs. - */ - public void addDepositUrls(String[] deposits) { - postLocation.insertItems(deposits); - } - - /** - * Add a list of onBehalfOf names. - * - * @param users The names. - */ - public void addOnBehalfOf(String[] users) { - onBehalfOf.insertItems(users); - } - - /** - * Add the list of formatNamespace strings. - * - * @param namespaces list of strings. - */ - public void addFormatNamespaces(String[] namespaces) { - formatNamespace.insertItems(namespaces); - } - - /** - * Add a list of file types. - * - * @param types The file types. - */ - public void addFileTypes(String[] types) { - fileType.insertItems(types); - } - - /** - * Add a list of file names. - * @param files The list of files. - */ - public void addFiles(String[] files) { - file.insertItems(files); - } - - /** - * Set the deposit location. - * - * @param location The location. - */ - public void setDepositLocation(String location) { - postLocation.insertItem(location); - postLocation.setSelectedItem(location); - } -} diff --git a/dspace-sword/src/main/java/org/purl/sword/client/PostMessage.java b/dspace-sword/src/main/java/org/purl/sword/client/PostMessage.java deleted file mode 100644 index a44f21ce2b88..000000000000 --- a/dspace-sword/src/main/java/org/purl/sword/client/PostMessage.java +++ /dev/null @@ -1,344 +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/ - */ - -/** - * Copyright (c) 2007, Aberystwyth University - * - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * - Redistributions of source code must retain the above - * copyright notice, this list of conditions and the - * following disclaimer. - * - * - Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * - * - Neither the name of the Centre for Advanced Software and - * Intelligent Systems (CASIS) nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR - * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF - * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ -package org.purl.sword.client; - -import java.io.File; - -/** - * Represents the details of a post to a server. The message holds all of the possible values - * that are to be sent from the client to the server. Not all elements of the message - * must be filled in. Any required fields are defined in the current SWORD specification. - * - * @author Neil Taylor - */ -public class PostMessage { - /** - * The local filepath for the file to upload/deposit. - */ - private String filepath; - - /** - * The URL of the destination server. - */ - private String destination; - - /** - * The filetype of the package that is to be uploaded. - */ - private String filetype; - - /** - * The string with the username if the deposit is on behalf of another user. - */ - private String onBehalfOf; - - /** - * True if an MD5 checksum should be sent with the deposit. - */ - private boolean useMD5; - - /** - * True if the deposit is a test and should not result in an actual deposit. - */ - private boolean noOp; - - /** - * True if the verbose operation is requested. - */ - private boolean verbose; - - /** - * The packaging format for the deposit. - */ - private String packaging; - - /** - * True if the deposit should simulate a checksum error. The client should check this - * field to determine if a correct MD5 checksum should be sent or whether the checksum should - * be modified so that it generates an error at the server. - */ - private boolean checksumError; - - /** - * True if the deposit should corrupt the POST header. The client should check this - * field to determine if a correct header should be sent or whether the header should - * be modified so that it generates an error at the server. - */ - private boolean corruptRequest; - - /** - * The Slug header value. - */ - private String slug; - - /** - * The user agent name - */ - private String userAgent; - - /** - * Get the filepath. - * - * @return The filepath. - */ - public String getFilepath() { - return filepath; - } - - /** - * Get the filename. This is the last element of the filepath - * that has been set in this class. - * - * @return filename - */ - public String getFilename() { - File file = new File(filepath); - return file.getName(); - } - - /** - * Set the filepath. - * - * @param filepath The filepath. - */ - public void setFilepath(String filepath) { - this.filepath = filepath; - } - - /** - * Get the destination collection. - * - * @return The collection. - */ - public String getDestination() { - return destination; - } - - /** - * Set the destination collection. - * - * @param destination The destination. - */ - public void setDestination(String destination) { - this.destination = destination; - } - - /** - * Get the filetype. - * @return The filetype. - */ - public String getFiletype() { - return filetype; - } - - /** - * Set the filetype. - * - * @param filetype The filetype. - */ - public void setFiletype(String filetype) { - this.filetype = filetype; - } - - /** - * Get the onBehalfOf value. - * - * @return The value. - */ - public String getOnBehalfOf() { - return onBehalfOf; - } - - /** - * Set the onBehalfOf value. - * - * @param onBehalfOf The value. - */ - public void setOnBehalfOf(String onBehalfOf) { - this.onBehalfOf = onBehalfOf; - } - - /** - * Get the MD5 status. - * @return The value. - */ - public boolean isUseMD5() { - return useMD5; - } - - /** - * Set the md5 state. - * - * @param useMD5 True if the message should use an MD5 checksum. - */ - public void setUseMD5(boolean useMD5) { - this.useMD5 = useMD5; - } - - /** - * Get the no-op state. - * - * @return The value. - */ - public boolean isNoOp() { - return noOp; - } - - /** - * Set the no-op state. - * - * @param noOp The no-op. - */ - public void setNoOp(boolean noOp) { - this.noOp = noOp; - } - - /** - * Get the verbose value. - * - * @return The value. - */ - public boolean isVerbose() { - return verbose; - } - - /** - * Set the verbose state. - * - * @param verbose True if the post message should send a - * verbose header. - */ - public void setVerbose(boolean verbose) { - this.verbose = verbose; - } - - /** - * Get the packaging format. - * - * @return The value. - */ - public String getPackaging() { - return packaging; - } - - /** - * Set the packaging format. - * - * @param packaging The packaging format. - */ - public void setFormatNamespace(String packaging) { - this.packaging = packaging; - } - - /** - * Get the status of the checksum error. - * - * @return True if the client should simulate a checksum error. - */ - public boolean getChecksumError() { - return checksumError; - } - - /** - * Set the state of the checksum error. - * - * @param checksumError True if the item should include a checksum error. - */ - public void setChecksumError(boolean checksumError) { - this.checksumError = checksumError; - } - - /** - * Get the status of the corrupt request flag. - * - * @return True if the client should corrupt the POST header. - */ - public boolean getCorruptRequest() { - return corruptRequest; - } - - /** - * Set the state of the corrupt request flag. - * - * @param corruptRequest True if the item should corrupt the POST header. - */ - public void setCorruptRequest(boolean corruptRequest) { - this.corruptRequest = corruptRequest; - } - - /** - * Set the Slug value. - * - * @param slug The value. - */ - public void setSlug(String slug) { - this.slug = slug; - } - - /** - * Get the Slug value. - * - * @return The Slug. - */ - public String getSlug() { - return this.slug; - } - - /** - * @return the userAgent - */ - public String getUserAgent() { - return userAgent; - } - - /** - * Set the user agent - * - * @param userAgent the userAgent to set - */ - public void setUserAgent(String userAgent) { - this.userAgent = userAgent; - } -} diff --git a/dspace-sword/src/main/java/org/purl/sword/client/PropertiesDialog.java b/dspace-sword/src/main/java/org/purl/sword/client/PropertiesDialog.java deleted file mode 100644 index 6d43cb68ef10..000000000000 --- a/dspace-sword/src/main/java/org/purl/sword/client/PropertiesDialog.java +++ /dev/null @@ -1,240 +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.purl.sword.client; - -import java.awt.BorderLayout; -import java.util.Enumeration; -import java.util.Properties; -import javax.swing.DefaultCellEditor; -import javax.swing.JFrame; -import javax.swing.JOptionPane; -import javax.swing.JPanel; -import javax.swing.JScrollPane; -import javax.swing.JTable; -import javax.swing.table.AbstractTableModel; -import javax.swing.table.TableCellEditor; - -/** - * Dialog that is used to edit the collection of properties. - * - * @author Neil Taylor, Suzana Barreto - */ -public class PropertiesDialog { - /** - * The parent frame for the dialog that is displayed. - */ - private JFrame parentFrame = null; - - /** - * Array that lists the labels for the buttons on the panel. - */ - private static Object[] options = {"OK", "Cancel"}; - - /** - * The panel that holds the controls to show. - */ - private JPanel controls = null; - - /** - * The configuration properties - */ - private Properties properties = null; - - /** - * Table that is used to display the list of properties. - */ - private JTable propertiesTable; - - /** - * Create a new instance. - * - * @param parentFrame The parent frame for the dialog. - * @param props The properties lisst to display - */ - public PropertiesDialog(JFrame parentFrame, Properties props) { - this.parentFrame = parentFrame; - properties = props; - controls = createControls(); - } - - /** - * Create the controls that are to be displayed in the system. - * - * @return A panel that contains the controls. - */ - protected final JPanel createControls() { - JPanel panel = new JPanel(new BorderLayout()); - propertiesTable = new JTable(new PropertiesModel()); - ((DefaultCellEditor) propertiesTable.getDefaultEditor(String.class)).setClickCountToStart(1); - JScrollPane scrollpane = new JScrollPane(propertiesTable, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, - JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS); - panel.add(scrollpane, BorderLayout.CENTER); - return panel; - } - - - /** - * Show the dialog and return the status code. - * - * @return The status code returned from the dialog. - */ - public int show() { - int result = JOptionPane.showOptionDialog(parentFrame, - controls, - "Edit Properties", - JOptionPane.OK_CANCEL_OPTION, - JOptionPane.PLAIN_MESSAGE, - null, - options, - null); - - // cancel any edit in the table. If there is a cell editing, the getEditingColumn will - // return a non-negative column number. This can be used to retreive the cell editor. - // The code then gets the default editor and calls the stopCellEditing. If custom - // editors are used, an additional check must be made to get the cell editor - // for a specific cell. - int column = propertiesTable.getEditingColumn(); - - if (column > -1) { - TableCellEditor editor = propertiesTable.getDefaultEditor(propertiesTable.getColumnClass(column)); - if (editor != null) { - editor.stopCellEditing(); - } - } - - return result; - } - - - /** - * A table model that is used to show the properties. The model links directly - * to the underlying properties object. As changes are made in the table, the - * corresponding changes are made in the properties object. The user can only - * edit the value column in the table. - */ - public class PropertiesModel extends AbstractTableModel { - /** - * Column names. - */ - private String columns[] = {"Property Name", "Value"}; - - /** - * Create a new instance of the model. If no properties object exists, - * a default model is created. Note, this will allow the table to - * continue editing, although this value will not be passed back to - * the calling window. - */ - public PropertiesModel() { - super(); - if (properties == null) { - properties = new Properties(); - } - } - - /** - * Get the number of columns. - * - * @return The number of columns. - */ - public int getColumnCount() { - return columns.length; - } - - /** - * Get the number of rows. - * - * @return The number of rows. - */ - public int getRowCount() { - return properties.size(); - } - - /** - * Get the value that is at the specified cell. - * - * @param row The row for the cell. - * @param col The column for the cell. - * @return The data value from the properties. - */ - public Object getValueAt(int row, int col) { - if (col == 0) { - return getKeyValue(row); - } else { - String key = getKeyValue(row); - return properties.get(key); - } - } - - /** - * Retrieve the column name for the specified column. - * - * @param col The column number. - * @return The column name. - */ - public String getColumnName(int col) { - return columns[col]; - } - - /** - * Retrieve the column class. - * - * @param col The column number. - * @return The class for the object found at the column position. - */ - public Class getColumnClass(int col) { - return getValueAt(0, col).getClass(); - } - - /** - * Determine if the cell can be edited. This model will only - * allow the second column to be edited. - * - * @param row The cell row. - * @param col The cell column. - * @return True if the cell can be edited. Otherwise, false. - */ - public boolean isCellEditable(int row, int col) { - if (col == 1) { - return true; - } - return false; - } - - /** - * Set the value for the specified cell. - * - * @param value The value to set. - * @param row The row for the cell. - * @param col The column. - */ - public void setValueAt(Object value, int row, int col) { - String key = getKeyValue(row); - properties.setProperty(key, ((String) value)); - fireTableCellUpdated(row, col); - } - - /** - * Get the Key value for the specified row. - * - * @param row The row. - * @return A string that shows the key value. - */ - public String getKeyValue(int row) { - int count = 0; - Enumeration k = properties.keys(); - while (k.hasMoreElements()) { - String key = (String) k.nextElement(); - if (count == row) { - return key; - } - count++; - } - return null; - } - } -} diff --git a/dspace-sword/src/main/java/org/purl/sword/client/SWORDClient.java b/dspace-sword/src/main/java/org/purl/sword/client/SWORDClient.java deleted file mode 100644 index 0df7fd53104a..000000000000 --- a/dspace-sword/src/main/java/org/purl/sword/client/SWORDClient.java +++ /dev/null @@ -1,85 +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.purl.sword.client; - -import org.purl.sword.base.DepositResponse; -import org.purl.sword.base.ServiceDocument; - -/** - * Interface for any SWORD client implementation. - */ -public interface SWORDClient { - /** - * Set the server that is to be contacted on the next access. - * - * @param server The name of the server, e.g. www.aber.ac.uk - * @param port The port number, e.g. 80. - */ - public void setServer(String server, int port); - - /** - * Set the user credentials that are to be used for subsequent accesses. - * - * @param username The username. - * @param password The password. - */ - public void setCredentials(String username, String password); - - /** - * Clear the credentials settings on the client. - */ - public void clearCredentials(); - - /** - * Set the proxy that is to be used for subsequent accesses. - * - * @param host The host name, e.g. cache.host.com. - * @param port The port, e.g. 8080. - */ - public void setProxy(String host, int port); - - /** - * Get the status result returned from the most recent network test. - * - * @return The status code and message. - */ - public Status getStatus(); - - /** - * Get a service document, specified in the URL. - * - * @param url The URL to connect to. - * @return A ServiceDocument that contains the Service details that were - * obained from the specified URL. - * @throws SWORDClientException If there is an error accessing the - * URL. - */ - public ServiceDocument getServiceDocument(String url) throws SWORDClientException; - - /** - * Get a service document, specified in the URL. The document is accessed on - * behalf of the specified user. - * - * @param url The URL to connect to. - * @param onBehalfOf The username for the onBehalfOf access. - * @return A ServiceDocument that contains the Service details that were - * obtained from the specified URL. - * @throws SWORDClientException If there is an error accessing the URL. - */ - public ServiceDocument getServiceDocument(String url, String onBehalfOf) throws SWORDClientException; - - /** - * Post a file to the specified destination URL. - * - * @param message The message that defines the requirements for the operation. - * @return A DespoitResponse if the response is successful. If there was an error, - * null should be returned. - * @throws SWORDClientException If there is an error accessing the URL. - */ - public DepositResponse postFile(PostMessage message) throws SWORDClientException; -} diff --git a/dspace-sword/src/main/java/org/purl/sword/client/SWORDClientException.java b/dspace-sword/src/main/java/org/purl/sword/client/SWORDClientException.java deleted file mode 100644 index 1355b9d4ab61..000000000000 --- a/dspace-sword/src/main/java/org/purl/sword/client/SWORDClientException.java +++ /dev/null @@ -1,42 +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.purl.sword.client; - -/** - * Represents an exception thrown by the SWORD Client. - * - * @author Neil Taylor - */ -public class SWORDClientException extends Exception { - /** - * Create a new exception, without a message. - */ - public SWORDClientException() { - super(); - } - - /** - * Create a new exception with the specified message. - * - * @param message The message. - */ - public SWORDClientException(String message) { - super(message); - } - - /** - * Create a new exception with the specified message and set - * the exception that generated this error. - * - * @param message The message. - * @param cause The original exception. - */ - public SWORDClientException(String message, Exception cause) { - super(message, cause); - } -} diff --git a/dspace-sword/src/main/java/org/purl/sword/client/SWORDComboBox.java b/dspace-sword/src/main/java/org/purl/sword/client/SWORDComboBox.java deleted file mode 100644 index 42fcc082ff58..000000000000 --- a/dspace-sword/src/main/java/org/purl/sword/client/SWORDComboBox.java +++ /dev/null @@ -1,129 +0,0 @@ -/** - * The contents of this file are subject to the license and copyright - * detailed in the LICENSE and NOTICE files at the root of the source - * tree and available online at - * - * http://www.dspace.org/license/ - */ - -/** - * Copyright (c) 2007, Aberystwyth University - * - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * - Redistributions of source code must retain the above - * copyright notice, this list of conditions and the - * following disclaimer. - * - * - Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * - * - Neither the name of the Centre for Advanced Software and - * Intelligent Systems (CASIS) nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR - * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF - * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ -package org.purl.sword.client; - -import javax.swing.JComboBox; - -/** - * An extension of the JComboBox class. This adds a method that - * can update the list of items with the item. The update will only - * work on combo boxes that are set to editable. - * - * @author Neil Taylor - */ -public class SWORDComboBox extends JComboBox { - /** - * Create an instance of the SWORD Combo box. - */ - public SWORDComboBox() { - super(); - setEditable(true); - } - - /** - * Update the list for the Combo box with the currently selected - * item. This will only add an item to the list if: i) the control - * is editable, ii) the selected item is not empty and iii) the - * item is not already in the list. - */ - public void updateList() { - Object s = getSelectedItem(); - - if (!isEditable() || s == null || ((String) s).trim().length() == 0) { - // don't update with an empty item or if the combo box is not editable. - return; - } - - insertItem(s); - } - - /** - * Insert an item into the combo box. This will only be added - * if the item is not already present in the combo box. - * - * @param newItem The item to insert. - */ - public void insertItem(Object newItem) { - int count = getItemCount(); - - boolean found = false; - - for (int i = 0; i < count && !found; i++) { - Object item = getItemAt(i); - if (item != null && item.equals(newItem)) { - found = true; - } - } - - if (!found) { - addItem(newItem); - } - } - - /** - * Insert multiple items into the combo box. - * - * @param items The array of items. - */ - public void insertItems(String[] items) { - for (String item : items) { - insertItem(item); - } - } - - /** - * Get the text of the currently selected item in the combo box. - * @return The text. null is returned if no item - * is selected. - */ - public String getText() { - Object o = getSelectedItem(); - if (o != null) { - return o.toString().trim(); - } - - return null; - } -} diff --git a/dspace-sword/src/main/java/org/purl/sword/client/SWORDFormPanel.java b/dspace-sword/src/main/java/org/purl/sword/client/SWORDFormPanel.java deleted file mode 100644 index 81de59ae68c2..000000000000 --- a/dspace-sword/src/main/java/org/purl/sword/client/SWORDFormPanel.java +++ /dev/null @@ -1,144 +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.purl.sword.client; - -import java.awt.Component; -import java.awt.GridBagConstraints; -import java.awt.GridBagLayout; -import java.awt.Insets; -import javax.swing.JPanel; - -/** - * Utility class. Creates a two column form. The left column is used to show - * the label for the row. The right column is used to show the control, e.g. - * text box, combo box or checkbox, for the row. - * - * @author Neil Taylor - */ -public class SWORDFormPanel extends JPanel { - /** - * Constraints used to control the layout on the panel. - */ - private GridBagConstraints labelConstraints; - - /** - * Constraints used to control the layout of the input controls on the panel. - */ - private GridBagConstraints controlConstraints; - - /** - * Index to the next row. - */ - private int rowIndex = 0; - - /** - * Insets for the top row of the label column. - */ - private Insets labelTop = new Insets(10, 10, 0, 0); - - /** - * Insets for the top row of the control column. - */ - private Insets controlTop = new Insets(10, 4, 0, 10); - - /** - * Insets for a general row in the label column. - */ - private Insets labelGeneral = new Insets(3, 10, 0, 0); - - /** - * Insets for a general row in the control column. - */ - private Insets controlGeneral = new Insets(3, 4, 0, 10); - - /** - * Create a new instance of the class. - */ - public SWORDFormPanel() { - super(); - setLayout(new GridBagLayout()); - - labelConstraints = new GridBagConstraints(); - labelConstraints.fill = GridBagConstraints.NONE; - labelConstraints.anchor = GridBagConstraints.LINE_END; - labelConstraints.weightx = 0.1; - - controlConstraints = new GridBagConstraints(); - controlConstraints.fill = GridBagConstraints.HORIZONTAL; - controlConstraints.weightx = 0.9; - } - - /** - * Add the specified component as the first row. It will occupy two - * columns. - * - * @param one The control to add. - */ - public void addFirstRow(Component one) { - addRow(one, null, labelTop, controlTop); - } - - /** - * Add the specified components as the first row in the form. - * - * @param one The label component. - * @param two The control component. - */ - public void addFirstRow(Component one, Component two) { - addRow(one, two, labelTop, controlTop); - } - - /** - * Add a component to the general row. This will be added in the label column. - * - * @param one The component. - */ - public void addRow(Component one) { - addRow(one, null); - } - - /** - * Add a component to the general row. - * - * @param one The component to add to the label column. - * @param two The component to add to the control column. - */ - public void addRow(Component one, Component two) { - addRow(one, two, labelGeneral, controlGeneral); - } - - /** - * Add a row to the table. - * - * @param one The component to display in the label column. - * @param two The component to display in the control column. - * @param labels The insets for the label column. - * @param controls The insets for the controls column. - */ - protected void addRow(Component one, Component two, Insets labels, Insets controls) { - labelConstraints.insets = labels; - labelConstraints.gridx = 0; - labelConstraints.gridy = rowIndex; - if (two == null) { - labelConstraints.gridwidth = 2; - } else { - labelConstraints.gridwidth = 1; - } - - add(one, labelConstraints); - - if (two != null) { - controlConstraints.insets = controls; - controlConstraints.gridx = 1; - controlConstraints.gridy = rowIndex; - add(two, controlConstraints); - } - - rowIndex++; - } -} diff --git a/dspace-sword/src/main/java/org/purl/sword/client/ServiceDialog.java b/dspace-sword/src/main/java/org/purl/sword/client/ServiceDialog.java deleted file mode 100644 index 03489f0d2e77..000000000000 --- a/dspace-sword/src/main/java/org/purl/sword/client/ServiceDialog.java +++ /dev/null @@ -1,227 +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/ - */ - -/** - * Copyright (c) 2007, Aberystwyth University - * - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * - Redistributions of source code must retain the above - * copyright notice, this list of conditions and the - * following disclaimer. - * - * - Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * - * - Neither the name of the Centre for Advanced Software and - * Intelligent Systems (CASIS) nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR - * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF - * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ -package org.purl.sword.client; - -import javax.swing.JFrame; -import javax.swing.JLabel; -import javax.swing.JOptionPane; -import javax.swing.JPanel; -import javax.swing.JPasswordField; - -/** - * Dialog that prompts the user to enter the details for a service - * document location. - * - * @author Neil Taylor - */ -public class ServiceDialog { - /** - * The username. - */ - private SWORDComboBox username; - - /** - * The password. - */ - private JPasswordField password; - - /** - * Holds the URL for the collection. - */ - private SWORDComboBox location; - - /** - * The combo box that shows the list of onBehalfOf items. - */ - private SWORDComboBox onBehalfOf; - - /** - * Parent frame for the dialog. - */ - private JFrame parentFrame = null; - - /** - * The panel that holds the controls. - */ - private JPanel controls = null; - - /** - * List of buttons. - */ - private static Object[] options = {"Get Service Document", "Cancel"}; - - /** - * Create a new instance. - * - * @param parentFrame The parent frame. The dialog will be shown over the - * centre of this frame. - */ - public ServiceDialog(JFrame parentFrame) { - this.parentFrame = parentFrame; - controls = createControls(); - } - - /** - * Show the dialog. - * - * @return The close option. This is one of the dialog options from - * JOptionPane. - */ - public int show() { - int result = JOptionPane.showOptionDialog(parentFrame, - controls, - "Get Service Document", - JOptionPane.OK_CANCEL_OPTION, - JOptionPane.PLAIN_MESSAGE, - null, - options, - options[1]); - - if (result == JOptionPane.OK_OPTION) { - // update the combo boxes with the values - username.updateList(); - location.updateList(); - onBehalfOf.updateList(); - } - - return result; - } - - /** - * Create the controls that are displayed in the dialog. - * - * @return The panel that contains the controls. - */ - protected final JPanel createControls() { - username = new SWORDComboBox(); - username.setEditable(true); - password = new JPasswordField(); - location = new SWORDComboBox(); - location.setEditable(true); - onBehalfOf = new SWORDComboBox(); - onBehalfOf.setEditable(true); - - JLabel userLabel = new JLabel("Username:", JLabel.TRAILING); - JLabel passwordLabel = new JLabel("Password:", JLabel.TRAILING); - JLabel locationLabel = new JLabel("Location:", JLabel.TRAILING); - JLabel onBehalfOfLabel = new JLabel("On Behalf Of:", JLabel.TRAILING); - - SWORDFormPanel panel = new SWORDFormPanel(); - panel.addFirstRow(userLabel, username); - panel.addRow(passwordLabel, password); - panel.addRow(locationLabel, location); - panel.addRow(onBehalfOfLabel, onBehalfOf); - - return panel; - } - - /** - * Get the username from the controls on the dialog. - * - * @return The username. - */ - public String getUsername() { - return username.getText(); - } - - /** - * Get the password from the dialog. - * - * @return The password. - */ - public String getPassword() { - return new String(password.getPassword()); - } - - /** - * The location from the dialog. - * - * @return The location. - */ - public String getLocation() { - return location.getText(); - } - - /** - * The onBehalfOf value from the dialog. - * - * @return The onBehalfOf value. - */ - public String getOnBehalfOf() { - String text = onBehalfOf.getText().trim(); - if (text.length() == 0) { - return null; - } - return text; - } - - /** - * Add the list of user ids to the dialog. - * - * @param users The list of user ids. - */ - public void addUserIds(String[] users) { - username.insertItems(users); - } - - /** - * Add the list of service URLs. - * - * @param services The service URLs. - */ - public void addServiceUrls(String[] services) { - location.insertItems(services); - } - - /** - * Add a list of onBehalfOf names. - * - * @param users The list of onBehalfOf items. - */ - public void addOnBehalfOf(String[] users) { - onBehalfOf.insertItems(users); - } - -} diff --git a/dspace-sword/src/main/java/org/purl/sword/client/ServicePanel.java b/dspace-sword/src/main/java/org/purl/sword/client/ServicePanel.java deleted file mode 100644 index bc3031c33354..000000000000 --- a/dspace-sword/src/main/java/org/purl/sword/client/ServicePanel.java +++ /dev/null @@ -1,843 +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/ - */ - -/** - * Copyright (c) 2007, Aberystwyth University - * - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * - Redistributions of source code must retain the above - * copyright notice, this list of conditions and the - * following disclaimer. - * - * - Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * - * - Neither the name of the Centre for Advanced Software and - * Intelligent Systems (CASIS) nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR - * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF - * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ -package org.purl.sword.client; - -import java.awt.BorderLayout; -import java.awt.Component; -import java.util.ArrayList; -import java.util.Enumeration; -import java.util.Iterator; -import java.util.List; -import javax.swing.Icon; -import javax.swing.ImageIcon; -import javax.swing.JComponent; -import javax.swing.JEditorPane; -import javax.swing.JPanel; -import javax.swing.JScrollPane; -import javax.swing.JSplitPane; -import javax.swing.JTree; -import javax.swing.ToolTipManager; -import javax.swing.event.TreeSelectionEvent; -import javax.swing.event.TreeSelectionListener; -import javax.swing.tree.DefaultMutableTreeNode; -import javax.swing.tree.DefaultTreeCellRenderer; -import javax.swing.tree.DefaultTreeModel; -import javax.swing.tree.TreePath; - -import org.purl.sword.atom.Author; -import org.purl.sword.atom.Content; -import org.purl.sword.atom.Contributor; -import org.purl.sword.atom.Generator; -import org.purl.sword.atom.Link; -import org.purl.sword.atom.TextConstruct; -import org.purl.sword.base.Collection; -import org.purl.sword.base.DepositResponse; -import org.purl.sword.base.SWORDEntry; -import org.purl.sword.base.Service; -import org.purl.sword.base.ServiceDocument; -import org.purl.sword.base.SwordAcceptPackaging; -import org.purl.sword.base.Workspace; - -/** - * The main panel for the GUI client. This contains the top-two sub-panels: the - * tree and the text area to show the details of the selected node. - * - * @author Neil Taylor - */ -public class ServicePanel extends JPanel - implements TreeSelectionListener { - /** - * The top level item in the tree that lists services. - */ - DefaultMutableTreeNode top; - - /** - * The tree model used to display the items. - */ - DefaultTreeModel treeModel = null; - - /** - * Tree that holds the list of services. - */ - private JTree services; - - /** - * The panel that shows an HTML table with any details for the selected - * node in the services tree. - */ - private JEditorPane details; - - /** - * A registered listener. This listener will be notified when there is a - * different node selected in the service tree. - */ - private ServiceSelectedListener listener; - - /** - * Create a new instance of the panel. - */ - public ServicePanel() { - super(); - setLayout(new BorderLayout()); - - top = new DefaultMutableTreeNode("Services & Posted Files"); - treeModel = new DefaultTreeModel(top); - - services = new JTree(treeModel); - services.setCellRenderer(new ServicePostTreeRenderer()); - - JScrollPane servicesPane = new JScrollPane(services, - JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, - JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS); - - details = new JEditorPane("text/html", - "

    Details

    This panel will show the details for the currently " + - "selected item in the tree.

    "); - - JScrollPane detailsPane = new JScrollPane(details, - JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, - JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS); - - JSplitPane splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, - servicesPane, - detailsPane); - splitPane.setOneTouchExpandable(true); - splitPane.setResizeWeight(0.5); - splitPane.setDividerLocation(200); - - services.addTreeSelectionListener(this); - ToolTipManager.sharedInstance().registerComponent(services); - - add(splitPane, BorderLayout.CENTER); - } - - /** - * Renderer that displays the icons for the tree nodes. - * - * @author Neil Taylor - */ - static class ServicePostTreeRenderer extends DefaultTreeCellRenderer { - Icon workspaceIcon; - Icon serviceIcon; - Icon collectionIcon; - Icon fileIcon; - - /** - * Initialise the renderer. Load the icons. - */ - public ServicePostTreeRenderer() { - ClassLoader loader = this.getClass().getClassLoader(); - workspaceIcon = new ImageIcon(loader.getResource("images/WorkspaceNodeImage.gif")); - serviceIcon = new ImageIcon(loader.getResource("images/ServiceNodeImage.gif")); - collectionIcon = new ImageIcon(loader.getResource("images/CollectionNodeImage.gif")); - fileIcon = new ImageIcon(loader.getResource("images/ServiceNodeImage.gif")); - } - - /** - * Return the cell renderer. This will be the default tree cell renderer - * with a different icon depending upon the type of data in the node. - * - * @param tree The JTree control. - * @param value The value to display. - * @param sel True if the node is selected. - * @param expanded True if the node is expanded. - * @param leaf True if the node is a leaf. - * @param row The row. - * @param hasFocus True if the node has focus. - */ - public Component getTreeCellRendererComponent( - JTree tree, - Object value, - boolean sel, - boolean expanded, - boolean leaf, - int row, - boolean hasFocus) { - - JComponent comp = (JComponent) super.getTreeCellRendererComponent( - tree, value, sel, - expanded, leaf, row, - hasFocus); - - DefaultMutableTreeNode node = - (DefaultMutableTreeNode) value; - - Object o = node.getUserObject(); - if (o instanceof TreeNodeWrapper) { - TreeNodeWrapper wrapper = (TreeNodeWrapper) o; - comp.setToolTipText(wrapper.toString()); - Object data = wrapper.getData(); - if (data instanceof Service) { - setIcon(serviceIcon); - } else if (data instanceof Workspace) { - setIcon(workspaceIcon); - } else if (data instanceof Collection) { - setIcon(collectionIcon); - } else if (data instanceof SWORDEntry) { - setIcon(fileIcon); - } - } else { - comp.setToolTipText(null); - } - return comp; - } - - - } - - /** - * Set the service selected listener. This listener will be notified when - * there is a selection change in the tree. - * - * @param listener The listener. - */ - public void setServiceSelectedListener(ServiceSelectedListener listener) { - this.listener = listener; - } - - /** - * Process the specified service document. Add the details as a new child of the - * root of the tree. - * - * @param url The url used to access the service document. - * @param doc The service document. - */ - public void processServiceDocument(String url, - ServiceDocument doc) { - TreeNodeWrapper wrapper = null; - - Service service = doc.getService(); - wrapper = new TreeNodeWrapper(url, service); - DefaultMutableTreeNode serviceNode = new DefaultMutableTreeNode(wrapper); - treeModel.insertNodeInto(serviceNode, top, top.getChildCount()); - services.scrollPathToVisible(new TreePath(serviceNode.getPath())); - - // process the workspaces - DefaultMutableTreeNode workspaceNode = null; - - Iterator workspaces = service.getWorkspaces(); - for (; workspaces.hasNext(); ) { - Workspace workspace = workspaces.next(); - wrapper = new TreeNodeWrapper(workspace.getTitle(), workspace); - workspaceNode = new DefaultMutableTreeNode(wrapper); - treeModel.insertNodeInto(workspaceNode, serviceNode, serviceNode.getChildCount()); - services.scrollPathToVisible(new TreePath(workspaceNode.getPath())); - - DefaultMutableTreeNode collectionNode = null; - Iterator collections = workspace.collectionIterator(); - for (; collections.hasNext(); ) { - Collection collection = collections.next(); - wrapper = new TreeNodeWrapper(collection.getTitle(), collection); - collectionNode = new DefaultMutableTreeNode(wrapper); - treeModel.insertNodeInto(collectionNode, workspaceNode, workspaceNode.getChildCount()); - services.scrollPathToVisible(new TreePath(collectionNode.getPath())); - } - } // for - } - - /** - * Holds the data for a tree node. It specifies the name that will be displayed - * in the node, and stores associated data. - * - * @author Neil Taylor - */ - static class TreeNodeWrapper { - /** - * The node name. - */ - private String name; - - /** - * The user data. - */ - private Object userObject; - - /** - * Create a new instance. - * - * @param name The name of the node. - * @param data The data in the node. - */ - public TreeNodeWrapper(String name, Object data) { - this.name = name; - this.userObject = data; - } - - /** - * Retrieve the data that is stored in this node. - * - * @return The data. - */ - public Object getData() { - return userObject; - } - - /** - * Get a string description for this node. - */ - public String toString() { - if (name == null || name.trim().equals("")) { - return "Unspecified"; - } - - return name; - } - } - - /** - * Respond to a changed tree selection event. Update the details panel to - * show an appropriate message for the newly selected node. Also, - * alert the selection listener for this panel. The listener will receive - * a path, if a collection has been selected. Otherwise, the listener - * will receive null. - */ - public void valueChanged(TreeSelectionEvent evt) { - // Get all nodes whose selection status has changed - TreePath[] paths = evt.getPaths(); - - for (int i = 0; i < paths.length; i++) { - if (evt.isAddedPath(i)) { - // process new selections - DefaultMutableTreeNode node; - node = (DefaultMutableTreeNode) (paths[i].getLastPathComponent()); - - Object o = node.getUserObject(); - if (o instanceof TreeNodeWrapper) { - try { - TreeNodeWrapper wrapper = (TreeNodeWrapper) o; - Object data = wrapper.getData(); - if (data instanceof Service) { - showService((Service) data); - alertListener(null); - } else if (data instanceof Workspace) { - showWorkspace((Workspace) data); - if (listener != null) { - alertListener(null); - } - } else if (data instanceof Collection) { - Collection c = (Collection) data; - showCollection(c); - alertListener(c.getLocation()); - } else if (data instanceof SWORDEntry) { - showEntry((SWORDEntry) data); - alertListener(null); - } else { - details.setText("unknown"); - alertListener(null); - } - } catch (Exception e) { - details.setText( - "An error occurred. The message was: " + e.getMessage() + ""); - alertListener(null); - e.printStackTrace(); - } - } else { - details.setText("please select one of the other nodes"); - alertListener(null); - } - } - - } - } - - /** - * Notify the listener that there has been a change to the currently selected - * item in the tree. - * - * @param value The value to send to the listener. - */ - private void alertListener(String value) { - if (listener != null) { - listener.selected(value); - } - } - - /** - * Add a new HTML table row to the specified StringBuffer. The label is displayed in - * the left column and the value is displayed in the right column. - * - * @param buffer The destination string buffer. - * @param label The label to add. - * @param value The corresponding value to add. - */ - private void addTableRow(StringBuffer buffer, String label, Object value) { - buffer.append(""); - buffer.append(label); - buffer.append(""); - buffer.append(displayableValue(value)); - buffer.append(""); - } - - /** - * Show the specified service data in the details panel. - * - * @param service The service node to display. - */ - private void showService(Service service) { - StringBuffer buffer = new StringBuffer(); - buffer.append(""); - buffer.append(""); - - buffer.append(""); - buffer.append(""); - addTableRow(buffer, "SWORD Version", service.getVersion()); - addTableRow(buffer, "NoOp Support ", service.isNoOp()); - addTableRow(buffer, "Verbose Support ", service.isVerbose()); - - String maxSize = ""; - - // Commented out the following code as the client code is out of step with the - // Sword 'base' library and wont compile. - Robin Taylor. - //if ( service.maxUploadIsDefined() ) - //{ - // maxSize = "" + service.getMaxUploadSize() + "kB"; - //} - //else - //{ - maxSize = "undefined"; - //} - - addTableRow(buffer, "Max File Upload Size ", maxSize); - - buffer.append("
    Service Summary
    "); - - buffer.append(""); - buffer.append(""); - details.setText(buffer.toString()); - } - - /** - * Display the workspace data in the details panel. - * - * @param workspace The workspace. - */ - private void showWorkspace(Workspace workspace) { - StringBuffer buffer = new StringBuffer(); - buffer.append(""); - buffer.append(""); - - buffer.append(""); - buffer - .append(""); - addTableRow(buffer, "Workspace Title", workspace.getTitle()); - buffer.append("
    Workspace Summary
    "); - - buffer.append(""); - buffer.append(""); - details.setText(buffer.toString()); - } - - /** - * Return the parameter unmodified if set, or the not defined text if null - * @param s - * @return s or ClientConstants.NOT_DEFINED_TEXT - */ - private Object displayableValue(Object s) { - if (null == s) { - return ClientConstants.NOT_DEFINED_TEXT; - } else { - return s; - } - } - - /** - * Add a string within paragraph tags. - * - * @param buffer The buffer to add the message to. - * @param message The message to add. - */ - private void addPara(StringBuffer buffer, String message) { - buffer.append("

    " + message + "

    "); - } - - /** - * Show the specified collection data in the details panel. - * - * @param collection The collection data. - */ - private void showCollection(Collection collection) { - StringBuffer buffer = new StringBuffer(); - buffer.append(""); - buffer.append(""); - - if (collection == null) { - addPara(buffer, "Invalid Collection object. Unable to display details."); - } else { - buffer.append(""); - buffer.append( - ""); - addTableRow(buffer, "Collection location", collection.getLocation()); - addTableRow(buffer, "Collection title", collection.getTitle()); - addTableRow(buffer, "Abstract", collection.getAbstract()); - addTableRow(buffer, "Collection Policy", collection.getCollectionPolicy()); - addTableRow(buffer, "Treatment", collection.getTreatment()); - addTableRow(buffer, "Mediation", collection.getMediation()); - addTableRow(buffer, "Nested Service Document", collection.getService()); - - String[] accepts = collection.getAccepts(); - StringBuilder acceptList = new StringBuilder(); - if (accepts != null && accepts.length == 0) { - acceptList.append("None specified"); - } else { - for (String s : accepts) { - acceptList.append(s).append("
    "); - } - } - addTableRow(buffer, "Accepts", acceptList.toString()); - - List acceptsPackaging = collection.getAcceptPackaging(); - - StringBuilder acceptPackagingList = new StringBuilder(); - for (Iterator i = acceptsPackaging.iterator(); i.hasNext(); ) { - SwordAcceptPackaging accept = (SwordAcceptPackaging) i.next(); - acceptPackagingList.append(accept.getContent()).append(" (").append(accept.getQualityValue()) - .append(")"); - - // add a , separator if there are any more items in the list - if (i.hasNext()) { - acceptPackagingList.append(", "); - } - } - - addTableRow(buffer, "Accepts Packaging", acceptPackagingList.toString()); - - buffer.append("
    Collection Summary
    "); - } - - buffer.append(""); - buffer.append(""); - details.setText(buffer.toString()); - } - - /** - * Display the contents of a Post entry in the display panel. - * - * @param entry The entry to display. - */ - private void showEntry(SWORDEntry entry) { - StringBuffer buffer = new StringBuffer(); - buffer.append(""); - buffer.append(""); - - if (entry == null) { - addPara(buffer, "Invalid Entry object. Unable to display details."); - } else { - buffer.append(""); - buffer - .append(""); - - // process atom:title - String titleString = getTextConstructDetails(entry.getSummary()); - addTableRow(buffer, "Title", titleString); - - // process id - addTableRow(buffer, "ID", entry.getId()); - - // process updated - addTableRow(buffer, "Date Updated", entry.getUpdated()); - - String authorString = getAuthorDetails(entry.getAuthors()); - addTableRow(buffer, "Authors", authorString); - - // process summary - String summaryString = getTextConstructDetails(entry.getSummary()); - addTableRow(buffer, "Summary", summaryString); - - // process content - Content content = entry.getContent(); - String contentString = ""; - if (content == null) { - contentString = "Not defined."; - } else { - contentString += "Source: '" + content.getSource() + "', Type: '" + - content.getType() + "'"; - } - addTableRow(buffer, "Content", contentString); - - // process links - Iterator links = entry.getLinks(); - StringBuffer linkBuffer = new StringBuffer(); - for (; links.hasNext(); ) { - Link link = links.next(); - linkBuffer.append("href: '"); - linkBuffer.append(link.getHref()); - linkBuffer.append("', href lang: '"); - linkBuffer.append(link.getHreflang()); - linkBuffer.append("', rel: '"); - linkBuffer.append(link.getRel()); - linkBuffer.append("')
    "); - } - if (linkBuffer.length() == 0) { - linkBuffer.append("Not defined"); - } - addTableRow(buffer, "Links", linkBuffer.toString()); - - // process contributors - String contributorString = getContributorDetails(entry.getContributors()); - addTableRow(buffer, "Contributors", contributorString); - - // process source - String sourceString = ""; - Generator generator = entry.getGenerator(); - if (generator != null) { - sourceString += "Content: '" + generator.getContent() + "'
    '"; - sourceString += "Version: '" + generator.getVersion() + "'
    '"; - sourceString += "Uri: '" + generator.getUri() + "'"; - } else { - sourceString += "No generator defined."; - } - - addTableRow(buffer, "Generator", sourceString); - - // process treatment - addTableRow(buffer, "Treatment", entry.getTreatment()); - - // process verboseDescription - addTableRow(buffer, "Verbose Description", entry.getVerboseDescription()); - - // process noOp - addTableRow(buffer, "NoOp", entry.isNoOp()); - - // process formatNamespace - addTableRow(buffer, "Packaging", entry.getPackaging()); - - // process userAgent - addTableRow(buffer, "User Agent", entry.getUserAgent()); - - - buffer.append("
    Entry Summary
    "); - } - - buffer.append(""); - buffer.append(""); - details.setText(buffer.toString()); - } - - /** - * Retrieve the details for a TextConstruct object. - * - * @param data The text construct object to display. - * - * @return Either 'Not defined' if the data is null, or - * details of the text content element. - */ - private String getTextConstructDetails(TextConstruct data) { - String summaryStr = ""; - if (data == null) { - summaryStr = "Not defined"; - } else { - summaryStr = "Content: '" + data.getContent() + "', Type: "; - if (data.getType() != null) { - summaryStr += "'" + data.getType().toString() + "'"; - } else { - summaryStr += "undefined."; - } - } - - return summaryStr; - } - - /** - * Get the author details and insert them into a string. - * - * @param authors the list of authors to process. - * - * @return A string containing the list of authors. - */ - private String getAuthorDetails(Iterator authors) { - // process author - StringBuffer authorBuffer = new StringBuffer(); - for (; authors.hasNext(); ) { - Author a = authors.next(); - authorBuffer.append(getAuthorDetails(a)); - } - - if (authorBuffer.length() == 0) { - authorBuffer.append("Not defined"); - } - - return authorBuffer.toString(); - } - - /** - * Get the contributor details and insert them into a string. - * - * @param contributors The contributors. - * - * @return The string that lists the details of the contributors. - */ - private String getContributorDetails(Iterator contributors) { - // process author - StringBuffer authorBuffer = new StringBuffer(); - for (; contributors.hasNext(); ) { - Contributor c = contributors.next(); - authorBuffer.append(getAuthorDetails(c)); - } - - if (authorBuffer.length() == 0) { - authorBuffer.append("Not defined"); - } - - return authorBuffer.toString(); - } - - /** - * Build a string that describes the specified author. - * - * @param author The author. - * - * @return The string description. - */ - private String getAuthorDetails(Author author) { - // process author - StringBuffer authorBuffer = new StringBuffer(); - authorBuffer.append(author.getName()); - authorBuffer.append(" (email: '"); - authorBuffer.append(author.getEmail()); - authorBuffer.append("', uri: '"); - authorBuffer.append(author.getUri()); - authorBuffer.append("')
    "); - - return authorBuffer.toString(); - } - - /** - * Process the deposit response and insert the details into the tree. If the url - * matches one of the collections in the tree, the deposit is added as a child - * node. Otherwise, the node is added as a child of the root. - * - * @param url The url of the collection that the file was posted to. - * - * @param response The details of the deposit. - */ - public void processDepositResponse(String url, - DepositResponse response) { - SWORDEntry entry = response.getEntry(); - Object title = entry.getTitle(); - if (title == null) { - title = "Undefined"; - } else { - title = entry.getTitle().getContent(); - } - - TreeNodeWrapper wrapper = new TreeNodeWrapper(title.toString(), entry); - DefaultMutableTreeNode entryNode = new DefaultMutableTreeNode(wrapper); - - DefaultMutableTreeNode newParentNode = top; - List nodes = getCollectionNodes(); - for (DefaultMutableTreeNode node : nodes) { - Object o = node.getUserObject(); - if (o instanceof TreeNodeWrapper) { - TreeNodeWrapper collectionWrapper = (TreeNodeWrapper) o; - Object data = collectionWrapper.getData(); - if (data instanceof Collection) { - Collection col = (Collection) data; - String location = col.getLocation(); - if (location != null && location.equals(url)) { - newParentNode = node; - break; - } - } - } - } - - treeModel.insertNodeInto(entryNode, newParentNode, newParentNode.getChildCount()); - services.scrollPathToVisible(new TreePath(entryNode.getPath())); - } - - /** - * Get a list of all current collections displayed in the tree. - * - * @return An array of the URLs for the collections. - */ - public String[] getCollectionLocations() { - List nodes = getCollectionNodes(); - String[] locations = new String[nodes.size()]; - - DefaultMutableTreeNode node; - for (int i = 0; i < nodes.size(); i++) { - node = nodes.get(i); - Object o = node.getUserObject(); - if (o instanceof TreeNodeWrapper) { - TreeNodeWrapper collectionWrapper = (TreeNodeWrapper) o; - Object data = collectionWrapper.getData(); - if (data instanceof Collection) { - Collection col = (Collection) data; - String location = col.getLocation(); - if (location != null) { - locations[i] = location; - } - } - } - } - return locations; - } - - /** - * Get a list of nodes that contain collections. - * - * @return A vector of the collection nodes. - */ - private List getCollectionNodes() { - List nodes = new ArrayList(); - - DefaultMutableTreeNode node; - Enumeration treeNodes = top.depthFirstEnumeration(); - - while (treeNodes.hasMoreElements()) { - node = (DefaultMutableTreeNode) treeNodes.nextElement(); - Object o = node.getUserObject(); - if (o instanceof TreeNodeWrapper) { - TreeNodeWrapper wrapper = (TreeNodeWrapper) o; - Object data = wrapper.getData(); - if (data instanceof Collection) { - nodes.add(node); - } - } - } - - return nodes; - } -} diff --git a/dspace-sword/src/main/java/org/purl/sword/client/ServiceSelectedListener.java b/dspace-sword/src/main/java/org/purl/sword/client/ServiceSelectedListener.java deleted file mode 100644 index fa97549fd924..000000000000 --- a/dspace-sword/src/main/java/org/purl/sword/client/ServiceSelectedListener.java +++ /dev/null @@ -1,23 +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.purl.sword.client; - -/** - * Listener for any objects that want to be notified when a collection has been selected in the - * ServicePanel. - * - * @author Neil Taylor - */ -public interface ServiceSelectedListener { - /** - * Called to provide an update on whether the selected node is a Collection. - * - * @param collection The location of the collection. null, otherwise. - */ - public void selected(String collection); -} diff --git a/dspace-sword/src/main/java/org/purl/sword/client/ServletClient.java b/dspace-sword/src/main/java/org/purl/sword/client/ServletClient.java deleted file mode 100644 index 963e9637dd72..000000000000 --- a/dspace-sword/src/main/java/org/purl/sword/client/ServletClient.java +++ /dev/null @@ -1,455 +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/ - */ - -/** - * Copyright (c) 2008, Aberystwyth University - * All rights reserved. - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * - Redistributions of source code must retain the above - * copyright notice, this list of conditions and the - * following disclaimer. - * - * - Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * - * - Neither the name of the Centre for Advanced Software and - * Intelligent Systems (CASIS) nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR - * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF - * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -package org.purl.sword.client; - -import java.io.File; -import java.io.IOException; -import java.net.URL; -import java.util.Iterator; -import java.util.List; -import javax.servlet.ServletException; -import javax.servlet.http.HttpServlet; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -import org.apache.commons.fileupload.FileItem; -import org.apache.commons.fileupload.FileItemFactory; -import org.apache.commons.fileupload.disk.DiskFileItemFactory; -import org.apache.commons.fileupload.servlet.ServletFileUpload; -import org.purl.sword.base.DepositResponse; -import org.purl.sword.base.SWORDEntry; -import org.purl.sword.base.ServiceDocument; - -/** - * Example client that runs as a Servlet. - * - * @author Stuart Lewis - */ -public class ServletClient extends HttpServlet { - - /** - * The user agent name of this library - */ - public static final String userAgent = "SWORDAPP Java Client: SWORD version 1.3 compatible (http://sourceforge" + - ".net/projects/sword-app/)"; - - /** - * Temporary directory. - */ - private String tempDirectory; - - /** - * List of urls for the destination services to access. - */ - private String[] urls; - - /** - * Used to determine if a proxy value should be set. - */ - private boolean useProxy = false; - - /** - * The proxy host name. - */ - private String pHost; - - /** - * The proxy port name. - */ - private int pPort; - - /** Counter used during Deposit information. */ - private static int counter = 0; - - /** - * Initialise the servlet. - */ - public void init() { - tempDirectory = getServletContext().getInitParameter( - "upload-temp-directory"); - if ((tempDirectory == null) || (tempDirectory.equals(""))) { - tempDirectory = System.getProperty("java.io.tmpdir"); - } - String lots = getServletContext().getInitParameter("client-urls"); - urls = lots.split(","); - - pHost = getServletContext().getInitParameter("proxy-host"); - String pPortstr = getServletContext().getInitParameter("proxy-port"); - if (((pHost != null) && (!pHost.equals(""))) - && ((pPortstr != null) && (!pPortstr.equals("")))) { - try { - pPort = Integer.parseInt(pPortstr); - useProxy = true; - } catch (Exception e) { - // Port number not numeric - } - } - } - - /** - * Handle a get request. Simply show the default form (form.jsp) - * - * @param request The request details - * @param response The response to write to. - * - * @throws ServletException - * An exception that provides information on a database access error or other errors. - * @throws IOException - * A general class of exceptions produced by failed or interrupted I/O operations. - */ - protected void doGet(HttpServletRequest request, - HttpServletResponse response) throws ServletException, IOException { - // Get request, so show the default page - request.setAttribute("urls", urls); - request.getRequestDispatcher("form.jsp").forward(request, response); - } - - /** - * Process the post. Determine if the request is for a post or service - * document. Then, dispatch the request to the appropriate handler. - * - * @param request The request details. - * @param response The response to write to. - * - * @throws ServletException - * An exception that provides information on a database access error or other errors. - * @throws IOException - * A general class of exceptions produced by failed or interrupted I/O operations. - */ - - protected void doPost(HttpServletRequest request, - HttpServletResponse response) throws ServletException, IOException { - if (request.getParameter("servicedocument") != null) { - this.doServiceDocument(request, response); - } else if (request.getParameter("deposit") != null) { - request.setAttribute("url", request.getParameter("url")); - request.setAttribute("u", request.getParameter("u")); - request.setAttribute("p", request.getParameter("p")); - request.setAttribute("obo", request.getParameter("obo")); - request.setAttribute("abstract", request.getParameter("abstract")); - request.setAttribute("policy", request.getParameter("policy")); - request.setAttribute("treatment", request.getParameter("treatment")); - request.setAttribute("mediation", request.getParameter("mediation")); - request.setAttribute("accepts", request.getParameter("accepts")); - request.setAttribute("acceptsp", request.getParameter("acceptsp")); - request.setAttribute("maxuploadsize", request.getParameter("maxuploadsize")); - request.getRequestDispatcher("depositform.jsp").forward(request, response); - } else if (ServletFileUpload.isMultipartContent(request)) { - this.doDeposit(request, response); - } else { - request.setAttribute("urls", urls); - request.getRequestDispatcher("form.jsp").forward(request, response); - } - } - - /** - * Process the request for a service document. - * - * @param request The request details. - * @param response The response to write to. - * - * @throws ServletException - * An exception that provides information on a database access error or other errors. - * @throws IOException - * A general class of exceptions produced by failed or interrupted I/O operations. - */ - - private void doServiceDocument(HttpServletRequest request, - HttpServletResponse response) throws ServletException, IOException { - // Get the service document - Client client = new Client(); - // Which URL do we want? - URL url = new URL(request.getParameter("url")); - String theUrl = request.getParameter("url"); - - if ((request.getParameter("ownurl") != null) - && (!request.getParameter("ownurl").equals(""))) { - url = new URL(request.getParameter("ownurl")); - theUrl = request.getParameter("ownurl"); - } - - int port = url.getPort(); - if (port == -1) { - port = 80; - } - - // Set up the server - client.setServer(url.getHost(), port); - client.setCredentials(request.getParameter("u"), request.getParameter("p")); - if (useProxy) { - client.setProxy(pHost, pPort); - } - - try { - ServiceDocument sd = client.getServiceDocument(theUrl, - request.getParameter("obo")); - - // Set the status - Status status = client.getStatus(); - request.setAttribute("status", status.toString()); - if (status.getCode() == 200) { - // Set the debug response - String xml = sd.marshall(); - - String validateXml = xml; - validateXml = validateXml.replaceAll("&", "&"); - validateXml = validateXml.replaceAll("<", "<"); - validateXml = validateXml.replaceAll(">", ">"); - validateXml = validateXml.replaceAll("\"", """); - validateXml = validateXml.replaceAll("'", "'"); - request.setAttribute("xmlValidate", validateXml); // for passing to validation - - xml = xml.replaceAll("<", "<"); - xml = xml.replaceAll(">", ">"); - request.setAttribute("xml", xml); - - // Set the ServiceDocument and associated values - request.setAttribute("sd", sd); - request.setAttribute("sdURL", theUrl); - request.setAttribute("u", request.getParameter("u")); - request.setAttribute("p", request.getParameter("p")); - request.setAttribute("sdOBO", request.getParameter("obo")); - request.getRequestDispatcher("servicedocument.jsp").forward( - request, response); - return; - } else { - request.setAttribute("error", status.getCode() + " " - + status.getMessage()); - request.setAttribute("urls", urls); - request.getRequestDispatcher("form.jsp").forward(request, - response); - return; - } - } catch (SWORDClientException e) { - e.printStackTrace(); - request.setAttribute("error", e.toString()); - request.setAttribute("urls", urls); - request.getRequestDispatcher("form.jsp").forward(request, response); - } - } - - /** - * Process a deposit. - * - * @param request The request details. - * @param response The response to output to. - * - * @throws ServletException - * An exception that provides information on a database access error or other errors. - * @throws IOException - * A general class of exceptions produced by failed or interrupted I/O operations. - */ - private void doDeposit(HttpServletRequest request, - HttpServletResponse response) throws ServletException, IOException { - // Do the deposit - Client client = new Client(); - try { - PostMessage message = new PostMessage(); - message.setUserAgent(ClientConstants.SERVICE_NAME); - - // Get the file - FileItemFactory factory = new DiskFileItemFactory(); - - // Create a new file upload handler - ServletFileUpload upload = new ServletFileUpload(factory); - - // Parse the request - List items = upload.parseRequest(request); - Iterator iter = items.iterator(); - String u = null; - String p = null; - String contentDisposition = null; - String filetype = null; - boolean useMD5 = false; - boolean errorMD5 = false; - boolean verbose = false; - boolean noOp = false; - boolean login = false; - while (iter.hasNext()) { - FileItem item = iter.next(); - if (item.isFormField()) { - String name = item.getFieldName(); - String value = item.getString(); - if (name.equals("url")) { - message.setDestination(value); - URL url = new URL(value); - int port = url.getPort(); - if (port == -1) { - port = 80; - } - client.setServer(url.getHost(), port); - } else if (name.equals("usemd5")) { - useMD5 = true; - } else if (name.equals("errormd5")) { - errorMD5 = true; - } else if (name.equals("verbose")) { - verbose = true; - } else if (name.equals("noop")) { - noOp = true; - } else if (name.equals("obo")) { - message.setOnBehalfOf(value); - } else if (name.equals("slug")) { - if ((value != null) && (!value.trim().equals(""))) { - message.setSlug(value); - } - } else if (name.equals("cd")) { - contentDisposition = value; - } else if (name.equals("filetype")) { - filetype = value; - } else if (name.equals("formatnamespace")) { - if ((value != null) && (!value.trim().equals(""))) { - message.setFormatNamespace(value); - } - } else if (name.equals("u")) { - u = value; - login = true; - request.setAttribute("u", value); - } else if (name.equals("p")) { - p = value; - login = true; - } - request.setAttribute(name, value); - } else { - String fname = tempDirectory + File.separator + - "ServletClient-" + counter++; - if ((contentDisposition != null) && (!contentDisposition.equals(""))) { - fname = tempDirectory + File.separator + contentDisposition; - } - - File uploadedFile = new File(fname); - item.write(uploadedFile); - message.setFilepath(fname); - - if ((filetype == null) || (filetype.trim().equals(""))) { - message.setFiletype(item.getContentType()); - } else { - message.setFiletype(filetype); - } - } - } - - if (login) { - client.setCredentials(u, p); - } - - if (useProxy) { - client.setProxy(pHost, pPort); - } - - message.setUseMD5(useMD5); - message.setChecksumError(errorMD5); - message.setVerbose(verbose); - message.setNoOp(noOp); - - // Post the file - DepositResponse resp = client.postFile(message); - - // Set the status - Status status = client.getStatus(); - request.setAttribute("status", status.toString()); - if ((status.getCode() == 201) || (status.getCode() == 202)) { - // Set the debug response - String xml = resp.marshall(); - - String validateXml = xml; - validateXml = validateXml.replaceAll("&", "&"); - validateXml = validateXml.replaceAll("<", "<"); - validateXml = validateXml.replaceAll(">", ">"); - validateXml = validateXml.replaceAll("\"", """); - validateXml = validateXml.replaceAll("'", "'"); - request.setAttribute("xmlValidate", validateXml); // for passing to validation - - xml = xml.replaceAll("<", "<"); - xml = xml.replaceAll(">", ">"); - request.setAttribute("xml", xml); - SWORDEntry se = resp.getEntry(); - request.setAttribute("id", se.getId()); - request.setAttribute("authors", se.getAuthors()); - request.setAttribute("contributors", se.getContributors()); - request.setAttribute("title", se.getTitle().getContent()); - request.setAttribute("updated", se.getUpdated()); - request.setAttribute("categories", se.getCategories()); - request.setAttribute("treatment", se.getTreatment()); - request.setAttribute("summary", se.getSummary().getContent()); - request.setAttribute("generator", se.getGenerator().getContent()); - request.setAttribute("userAgent", se.getUserAgent()); - request.setAttribute("packaging", se.getPackaging()); - request.setAttribute("links", se.getLinks()); - request.setAttribute("location", resp.getLocation()); - - // Set the ServiceDocument and associated values - request.getRequestDispatcher("deposit.jsp").forward(request, response); - return; - } else { - String error = status.getCode() + " " + status.getMessage() + " - "; - try { - error += resp.getEntry().getSummary().getContent(); - } catch (Exception e) { - // Do nothing - we have default error message - e.printStackTrace(); - } - request.setAttribute("error", error); - - // Try and get an error document in xml - String xml = resp.marshall(); - xml = xml.replaceAll("<", "<"); - xml = xml.replaceAll(">", ">"); - request.setAttribute("xml", xml); - - request.getRequestDispatcher("depositform.jsp").forward(request, response); - return; - } - } catch (RuntimeException e) { - e.printStackTrace(); - request.setAttribute("error", "value: " + e.toString()); - request.setAttribute("urls", urls); - request.getRequestDispatcher("depositform.jsp").forward(request, response); - } catch (Exception e) { - e.printStackTrace(); - request.setAttribute("error", "value: " + e.toString()); - request.setAttribute("urls", urls); - request.getRequestDispatcher("depositform.jsp").forward(request, response); - } - } -} diff --git a/dspace-sword/src/main/java/org/purl/sword/client/Status.java b/dspace-sword/src/main/java/org/purl/sword/client/Status.java deleted file mode 100644 index ff80fa6e52e2..000000000000 --- a/dspace-sword/src/main/java/org/purl/sword/client/Status.java +++ /dev/null @@ -1,61 +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.purl.sword.client; - -/** - * Representation of the status code and message. - * - * @author Neil Taylor - */ -public class Status { - /** - * The status code. - */ - private int code; - - /** - * The status message. - */ - private String message; - - /** - * Create a new status message. - * - * @param code The code. - * @param message The message. - */ - public Status(int code, String message) { - this.code = code; - this.message = message; - } - - /** - * Retrieve the code. - * - * @return The code. - */ - public int getCode() { - return code; - } - - /** - * Get the message. - * - * @return The message. - */ - public String getMessage() { - return message; - } - - /** - * Get a string representation of the status. - */ - public String toString() { - return "Code: " + code + ", Message: '" + message + "'"; - } -} From 68cffba5fa0557b4e3d1165373e4c35b00b33d1f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 7 Apr 2025 23:48:45 +0000 Subject: [PATCH 512/979] Bump the test-tools group with 6 updates Bumps the test-tools group with 6 updates: | Package | From | To | | --- | --- | --- | | [io.netty:netty-buffer](https://github.com/netty/netty) | `4.1.118.Final` | `4.1.119.Final` | | [io.netty:netty-transport](https://github.com/netty/netty) | `4.1.118.Final` | `4.1.119.Final` | | [io.netty:netty-transport-native-unix-common](https://github.com/netty/netty) | `4.1.118.Final` | `4.1.119.Final` | | [io.netty:netty-common](https://github.com/netty/netty) | `4.1.118.Final` | `4.1.119.Final` | | [io.netty:netty-handler](https://github.com/netty/netty) | `4.1.118.Final` | `4.1.119.Final` | | [io.netty:netty-codec](https://github.com/netty/netty) | `4.1.118.Final` | `4.1.119.Final` | Updates `io.netty:netty-buffer` from 4.1.118.Final to 4.1.119.Final - [Commits](https://github.com/netty/netty/compare/netty-4.1.118.Final...netty-4.1.119.Final) Updates `io.netty:netty-transport` from 4.1.118.Final to 4.1.119.Final - [Commits](https://github.com/netty/netty/compare/netty-4.1.118.Final...netty-4.1.119.Final) Updates `io.netty:netty-transport-native-unix-common` from 4.1.118.Final to 4.1.119.Final - [Commits](https://github.com/netty/netty/compare/netty-4.1.118.Final...netty-4.1.119.Final) Updates `io.netty:netty-common` from 4.1.118.Final to 4.1.119.Final - [Commits](https://github.com/netty/netty/compare/netty-4.1.118.Final...netty-4.1.119.Final) Updates `io.netty:netty-handler` from 4.1.118.Final to 4.1.119.Final - [Commits](https://github.com/netty/netty/compare/netty-4.1.118.Final...netty-4.1.119.Final) Updates `io.netty:netty-codec` from 4.1.118.Final to 4.1.119.Final - [Commits](https://github.com/netty/netty/compare/netty-4.1.118.Final...netty-4.1.119.Final) --- updated-dependencies: - dependency-name: io.netty:netty-buffer dependency-type: direct:production update-type: version-update:semver-patch dependency-group: test-tools - dependency-name: io.netty:netty-transport dependency-type: direct:production update-type: version-update:semver-patch dependency-group: test-tools - dependency-name: io.netty:netty-transport-native-unix-common dependency-type: direct:production update-type: version-update:semver-patch dependency-group: test-tools - dependency-name: io.netty:netty-common dependency-type: direct:production update-type: version-update:semver-patch dependency-group: test-tools - dependency-name: io.netty:netty-handler dependency-type: direct:production update-type: version-update:semver-patch dependency-group: test-tools - dependency-name: io.netty:netty-codec dependency-type: direct:production update-type: version-update:semver-patch dependency-group: test-tools ... Signed-off-by: dependabot[bot] --- dspace-api/pom.xml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/dspace-api/pom.xml b/dspace-api/pom.xml index 322e1820d325..dc5ce96df3ca 100644 --- a/dspace-api/pom.xml +++ b/dspace-api/pom.xml @@ -887,32 +887,32 @@ io.netty netty-buffer - 4.1.118.Final + 4.2.0.Final io.netty netty-transport - 4.1.118.Final + 4.2.0.Final io.netty netty-transport-native-unix-common - 4.1.118.Final + 4.2.0.Final io.netty netty-common - 4.1.118.Final + 4.2.0.Final io.netty netty-handler - 4.1.118.Final + 4.2.0.Final io.netty netty-codec - 4.1.118.Final + 4.2.0.Final org.apache.velocity From e356335b56ff9b490fe99a15fcab6a14a3a60b60 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 8 Apr 2025 00:03:37 +0000 Subject: [PATCH 513/979] Bump org.checkerframework:checker-qual from 3.49.0 to 3.49.2 Bumps [org.checkerframework:checker-qual](https://github.com/typetools/checker-framework) from 3.49.0 to 3.49.2. - [Release notes](https://github.com/typetools/checker-framework/releases) - [Changelog](https://github.com/typetools/checker-framework/blob/master/docs/CHANGELOG.md) - [Commits](https://github.com/typetools/checker-framework/compare/checker-framework-3.49.0...checker-framework-3.49.2) --- updated-dependencies: - dependency-name: org.checkerframework:checker-qual dependency-version: 3.49.2 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 4907e7a12ee6..17992113bc85 100644 --- a/pom.xml +++ b/pom.xml @@ -1350,7 +1350,7 @@ org.checkerframework checker-qual - 3.49.0 + 3.49.2 - 2.18.2 - 2.18.2 + 2.18.3 + 2.18.3 1.3.2 2.3.1 2.3.9 From a5806fb518031e99d82a4af0bb625271f4830860 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 8 Apr 2025 15:35:52 +0000 Subject: [PATCH 517/979] Bump the google-apis group across 1 directory with 4 updates Bumps the google-apis group with 4 updates in the / directory: [com.google.http-client:google-http-client](https://github.com/googleapis/google-http-java-client), [com.google.http-client:google-http-client-jackson2](https://github.com/googleapis/google-http-java-client), [com.google.oauth-client:google-oauth-client](https://github.com/googleapis/google-oauth-java-client) and [com.google.http-client:google-http-client-gson](https://github.com/googleapis/google-http-java-client). Updates `com.google.http-client:google-http-client` from 1.46.1 to 1.46.3 - [Release notes](https://github.com/googleapis/google-http-java-client/releases) - [Changelog](https://github.com/googleapis/google-http-java-client/blob/main/CHANGELOG.md) - [Commits](https://github.com/googleapis/google-http-java-client/compare/v1.46.1...v1.46.3) Updates `com.google.http-client:google-http-client-jackson2` from 1.46.1 to 1.46.3 - [Release notes](https://github.com/googleapis/google-http-java-client/releases) - [Changelog](https://github.com/googleapis/google-http-java-client/blob/main/CHANGELOG.md) - [Commits](https://github.com/googleapis/google-http-java-client/compare/v1.46.1...v1.46.3) Updates `com.google.oauth-client:google-oauth-client` from 1.37.0 to 1.38.0 - [Release notes](https://github.com/googleapis/google-oauth-java-client/releases) - [Changelog](https://github.com/googleapis/google-oauth-java-client/blob/main/CHANGELOG.md) - [Commits](https://github.com/googleapis/google-oauth-java-client/compare/v1.37.0...v1.38.0) Updates `com.google.http-client:google-http-client-gson` from 1.46.1 to 1.46.3 - [Release notes](https://github.com/googleapis/google-http-java-client/releases) - [Changelog](https://github.com/googleapis/google-http-java-client/blob/main/CHANGELOG.md) - [Commits](https://github.com/googleapis/google-http-java-client/compare/v1.46.1...v1.46.3) --- updated-dependencies: - dependency-name: com.google.http-client:google-http-client dependency-type: direct:production update-type: version-update:semver-patch dependency-group: google-apis - dependency-name: com.google.http-client:google-http-client-jackson2 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: google-apis - dependency-name: com.google.oauth-client:google-oauth-client dependency-type: direct:production update-type: version-update:semver-minor dependency-group: google-apis - dependency-name: com.google.http-client:google-http-client-gson dependency-type: direct:production update-type: version-update:semver-patch dependency-group: google-apis ... Signed-off-by: dependabot[bot] --- pom.xml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pom.xml b/pom.xml index 886aa3fb36cb..6debf4a73f5c 100644 --- a/pom.xml +++ b/pom.xml @@ -1714,7 +1714,7 @@ com.google.http-client google-http-client - 1.46.1 + 1.46.3 com.google.errorprone @@ -1736,7 +1736,7 @@ com.google.http-client google-http-client-jackson2 - 1.46.1 + 1.46.3 jackson-core @@ -1751,14 +1751,14 @@ com.google.oauth-client google-oauth-client - 1.37.0 + 1.39.0 com.google.http-client google-http-client-gson - 1.46.1 + 1.46.3 From bb2ed04ed3f42ff86f51fe00c9805f646a009049 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 8 Apr 2025 15:36:26 +0000 Subject: [PATCH 518/979] Bump the build-tools group across 1 directory with 5 updates Bumps the build-tools group with 5 updates in the / directory: | Package | From | To | | --- | --- | --- | | [com.github.spotbugs:spotbugs](https://github.com/spotbugs/spotbugs) | `4.9.1` | `4.9.3` | | [org.apache.maven.plugins:maven-compiler-plugin](https://github.com/apache/maven-compiler-plugin) | `3.13.0` | `3.14.0` | | [com.github.spotbugs:spotbugs-maven-plugin](https://github.com/spotbugs/spotbugs-maven-plugin) | `4.9.1.0` | `4.9.3.0` | | [org.apache.maven.plugins:maven-clean-plugin](https://github.com/apache/maven-clean-plugin) | `3.4.0` | `3.4.1` | | [org.jacoco:jacoco-maven-plugin](https://github.com/jacoco/jacoco) | `0.8.12` | `0.8.13` | Updates `com.github.spotbugs:spotbugs` from 4.9.1 to 4.9.3 - [Release notes](https://github.com/spotbugs/spotbugs/releases) - [Changelog](https://github.com/spotbugs/spotbugs/blob/master/CHANGELOG.md) - [Commits](https://github.com/spotbugs/spotbugs/compare/4.9.1...4.9.3) Updates `org.apache.maven.plugins:maven-compiler-plugin` from 3.13.0 to 3.14.0 - [Release notes](https://github.com/apache/maven-compiler-plugin/releases) - [Commits](https://github.com/apache/maven-compiler-plugin/compare/maven-compiler-plugin-3.13.0...maven-compiler-plugin-3.14.0) Updates `com.github.spotbugs:spotbugs-maven-plugin` from 4.9.1.0 to 4.9.3.0 - [Release notes](https://github.com/spotbugs/spotbugs-maven-plugin/releases) - [Commits](https://github.com/spotbugs/spotbugs-maven-plugin/compare/spotbugs-maven-plugin-4.9.1.0...spotbugs-maven-plugin-4.9.3.0) Updates `org.apache.maven.plugins:maven-clean-plugin` from 3.4.0 to 3.4.1 - [Release notes](https://github.com/apache/maven-clean-plugin/releases) - [Commits](https://github.com/apache/maven-clean-plugin/compare/maven-clean-plugin-3.4.0...maven-clean-plugin-3.4.1) Updates `org.jacoco:jacoco-maven-plugin` from 0.8.12 to 0.8.13 - [Release notes](https://github.com/jacoco/jacoco/releases) - [Commits](https://github.com/jacoco/jacoco/compare/v0.8.12...v0.8.13) --- updated-dependencies: - dependency-name: com.github.spotbugs:spotbugs dependency-version: 4.9.3 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: build-tools - dependency-name: org.apache.maven.plugins:maven-compiler-plugin dependency-version: 3.14.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: build-tools - dependency-name: com.github.spotbugs:spotbugs-maven-plugin dependency-version: 4.9.3.0 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: build-tools - dependency-name: org.apache.maven.plugins:maven-clean-plugin dependency-version: 3.4.1 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: build-tools - dependency-name: org.jacoco:jacoco-maven-plugin dependency-version: 0.8.13 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: build-tools ... Signed-off-by: dependabot[bot] --- pom.xml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/pom.xml b/pom.xml index 886aa3fb36cb..3e8092aa5cac 100644 --- a/pom.xml +++ b/pom.xml @@ -140,7 +140,7 @@ org.apache.maven.plugins maven-compiler-plugin - 3.13.0 + 3.14.0 11 @@ -295,7 +295,7 @@ com.github.spotbugs spotbugs-maven-plugin - 4.9.1.0 + 4.9.3.0 Max Low @@ -305,7 +305,7 @@ com.github.spotbugs spotbugs - 4.9.1 + 4.9.3 @@ -321,7 +321,7 @@ maven-clean-plugin - 3.4.0 + 3.4.1 @@ -392,7 +392,7 @@ org.jacoco jacoco-maven-plugin - 0.8.12 + 0.8.13 From 7c062a48b6fd64856524cf65029fd97a3e037b4a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 8 Apr 2025 15:36:46 +0000 Subject: [PATCH 519/979] Bump the build-tools group across 1 directory with 10 updates Bumps the build-tools group with 10 updates in the / directory: | Package | From | To | | --- | --- | --- | | [com.google.errorprone:error_prone_core](https://github.com/google/error-prone) | `2.36.0` | `2.37.0` | | [com.google.errorprone:error_prone_annotations](https://github.com/google/error-prone) | `2.36.0` | `2.37.0` | | [com.github.spotbugs:spotbugs](https://github.com/spotbugs/spotbugs) | `4.9.1` | `4.9.3` | | [org.apache.maven.plugins:maven-compiler-plugin](https://github.com/apache/maven-compiler-plugin) | `3.13.0` | `3.14.0` | | [org.apache.maven.plugins:maven-surefire-plugin](https://github.com/apache/maven-surefire) | `3.5.2` | `3.5.3` | | [org.apache.maven.plugins:maven-failsafe-plugin](https://github.com/apache/maven-surefire) | `3.5.2` | `3.5.3` | | [com.github.spotbugs:spotbugs-maven-plugin](https://github.com/spotbugs/spotbugs-maven-plugin) | `4.9.1.0` | `4.9.3.0` | | [org.apache.maven.plugins:maven-clean-plugin](https://github.com/apache/maven-clean-plugin) | `3.4.0` | `3.4.1` | | [org.jacoco:jacoco-maven-plugin](https://github.com/jacoco/jacoco) | `0.8.12` | `0.8.13` | | [org.codehaus.mojo:jaxb2-maven-plugin](https://github.com/mojohaus/jaxb2-maven-plugin) | `3.2.0` | `3.3.0` | Updates `com.google.errorprone:error_prone_core` from 2.36.0 to 2.37.0 - [Release notes](https://github.com/google/error-prone/releases) - [Commits](https://github.com/google/error-prone/compare/v2.36.0...v2.37.0) Updates `com.google.errorprone:error_prone_annotations` from 2.36.0 to 2.37.0 - [Release notes](https://github.com/google/error-prone/releases) - [Commits](https://github.com/google/error-prone/compare/v2.36.0...v2.37.0) Updates `com.github.spotbugs:spotbugs` from 4.9.1 to 4.9.3 - [Release notes](https://github.com/spotbugs/spotbugs/releases) - [Changelog](https://github.com/spotbugs/spotbugs/blob/master/CHANGELOG.md) - [Commits](https://github.com/spotbugs/spotbugs/compare/4.9.1...4.9.3) Updates `com.google.errorprone:error_prone_annotations` from 2.36.0 to 2.37.0 - [Release notes](https://github.com/google/error-prone/releases) - [Commits](https://github.com/google/error-prone/compare/v2.36.0...v2.37.0) Updates `org.apache.maven.plugins:maven-compiler-plugin` from 3.13.0 to 3.14.0 - [Release notes](https://github.com/apache/maven-compiler-plugin/releases) - [Commits](https://github.com/apache/maven-compiler-plugin/compare/maven-compiler-plugin-3.13.0...maven-compiler-plugin-3.14.0) Updates `org.apache.maven.plugins:maven-surefire-plugin` from 3.5.2 to 3.5.3 - [Release notes](https://github.com/apache/maven-surefire/releases) - [Commits](https://github.com/apache/maven-surefire/compare/surefire-3.5.2...surefire-3.5.3) Updates `org.apache.maven.plugins:maven-failsafe-plugin` from 3.5.2 to 3.5.3 - [Release notes](https://github.com/apache/maven-surefire/releases) - [Commits](https://github.com/apache/maven-surefire/compare/surefire-3.5.2...surefire-3.5.3) Updates `com.github.spotbugs:spotbugs-maven-plugin` from 4.9.1.0 to 4.9.3.0 - [Release notes](https://github.com/spotbugs/spotbugs-maven-plugin/releases) - [Commits](https://github.com/spotbugs/spotbugs-maven-plugin/compare/spotbugs-maven-plugin-4.9.1.0...spotbugs-maven-plugin-4.9.3.0) Updates `org.apache.maven.plugins:maven-clean-plugin` from 3.4.0 to 3.4.1 - [Release notes](https://github.com/apache/maven-clean-plugin/releases) - [Commits](https://github.com/apache/maven-clean-plugin/compare/maven-clean-plugin-3.4.0...maven-clean-plugin-3.4.1) Updates `org.jacoco:jacoco-maven-plugin` from 0.8.12 to 0.8.13 - [Release notes](https://github.com/jacoco/jacoco/releases) - [Commits](https://github.com/jacoco/jacoco/compare/v0.8.12...v0.8.13) Updates `org.codehaus.mojo:jaxb2-maven-plugin` from 3.2.0 to 3.3.0 - [Release notes](https://github.com/mojohaus/jaxb2-maven-plugin/releases) - [Commits](https://github.com/mojohaus/jaxb2-maven-plugin/compare/jaxb2-maven-plugin-3.2.0...jaxb2-maven-plugin-3.3.0) --- updated-dependencies: - dependency-name: com.google.errorprone:error_prone_core dependency-version: 2.37.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: build-tools - dependency-name: com.google.errorprone:error_prone_annotations dependency-version: 2.37.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: build-tools - dependency-name: com.github.spotbugs:spotbugs dependency-version: 4.9.3 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: build-tools - dependency-name: com.google.errorprone:error_prone_annotations dependency-version: 2.37.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: build-tools - dependency-name: org.apache.maven.plugins:maven-compiler-plugin dependency-version: 3.14.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: build-tools - dependency-name: org.apache.maven.plugins:maven-surefire-plugin dependency-version: 3.5.3 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: build-tools - dependency-name: org.apache.maven.plugins:maven-failsafe-plugin dependency-version: 3.5.3 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: build-tools - dependency-name: com.github.spotbugs:spotbugs-maven-plugin dependency-version: 4.9.3.0 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: build-tools - dependency-name: org.apache.maven.plugins:maven-clean-plugin dependency-version: 3.4.1 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: build-tools - dependency-name: org.jacoco:jacoco-maven-plugin dependency-version: 0.8.13 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: build-tools - dependency-name: org.codehaus.mojo:jaxb2-maven-plugin dependency-version: 3.3.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: build-tools ... Signed-off-by: dependabot[bot] --- dspace-api/pom.xml | 2 +- pom.xml | 16 ++++++++-------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/dspace-api/pom.xml b/dspace-api/pom.xml index dda9683e1ebd..7ce7313064f2 100644 --- a/dspace-api/pom.xml +++ b/dspace-api/pom.xml @@ -177,7 +177,7 @@ org.codehaus.mojo jaxb2-maven-plugin - 3.2.0 + 3.3.0 workflow-curation diff --git a/pom.xml b/pom.xml index 388dd0cf8393..1cdc4f2bcdda 100644 --- a/pom.xml +++ b/pom.xml @@ -29,7 +29,7 @@ 8.11.4 3.10.8 - 2.36.0 + 2.37.0 2.18.2 2.18.2 @@ -140,7 +140,7 @@ org.apache.maven.plugins maven-compiler-plugin - 3.13.0 + 3.14.0 ${java.version} @@ -208,7 +208,7 @@ org.apache.maven.plugins maven-surefire-plugin - 3.5.2 + 3.5.3 @@ -235,7 +235,7 @@ maven-failsafe-plugin - 3.5.2 + 3.5.3 @@ -303,7 +303,7 @@ com.github.spotbugs spotbugs-maven-plugin - 4.9.1.0 + 4.9.3.0 Max Low @@ -313,7 +313,7 @@ com.github.spotbugs spotbugs - 4.9.1 + 4.9.3 @@ -329,7 +329,7 @@ maven-clean-plugin - 3.4.0 + 3.4.1 @@ -405,7 +405,7 @@ org.jacoco jacoco-maven-plugin - 0.8.12 + 0.8.13 From 16239433f62233244d73f7e92715d7b6d2768694 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 8 Apr 2025 15:47:34 +0000 Subject: [PATCH 520/979] Bump io.grpc:grpc-context from 1.70.0 to 1.71.0 Bumps [io.grpc:grpc-context](https://github.com/grpc/grpc-java) from 1.70.0 to 1.71.0. - [Release notes](https://github.com/grpc/grpc-java/releases) - [Commits](https://github.com/grpc/grpc-java/compare/v1.70.0...v1.71.0) --- updated-dependencies: - dependency-name: io.grpc:grpc-context dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 886aa3fb36cb..6bc32b120619 100644 --- a/pom.xml +++ b/pom.xml @@ -1731,7 +1731,7 @@ io.grpc grpc-context - 1.70.0 + 1.71.0 com.google.http-client From 6733e56e92b5733913251d863cd2b5488dac2fe5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 8 Apr 2025 16:05:23 +0000 Subject: [PATCH 521/979] Bump the fasterxml group with 4 updates Bumps the fasterxml group with 4 updates: [com.fasterxml.jackson.core:jackson-annotations](https://github.com/FasterXML/jackson), [com.fasterxml.jackson.core:jackson-core](https://github.com/FasterXML/jackson-core), [com.fasterxml.jackson.core:jackson-databind](https://github.com/FasterXML/jackson) and com.fasterxml.jackson.datatype:jackson-datatype-jsr310. Updates `com.fasterxml.jackson.core:jackson-annotations` from 2.18.2 to 2.18.3 - [Commits](https://github.com/FasterXML/jackson/commits) Updates `com.fasterxml.jackson.core:jackson-core` from 2.18.2 to 2.18.3 - [Commits](https://github.com/FasterXML/jackson-core/compare/jackson-core-2.18.2...jackson-core-2.18.3) Updates `com.fasterxml.jackson.core:jackson-core` from 2.18.2 to 2.18.3 - [Commits](https://github.com/FasterXML/jackson-core/compare/jackson-core-2.18.2...jackson-core-2.18.3) Updates `com.fasterxml.jackson.core:jackson-databind` from 2.18.2 to 2.18.3 - [Commits](https://github.com/FasterXML/jackson/commits) Updates `com.fasterxml.jackson.datatype:jackson-datatype-jsr310` from 2.18.2 to 2.18.3 Updates `com.fasterxml.jackson.datatype:jackson-datatype-jsr310` from 2.18.2 to 2.18.3 --- updated-dependencies: - dependency-name: com.fasterxml.jackson.core:jackson-annotations dependency-type: direct:production update-type: version-update:semver-patch dependency-group: fasterxml - dependency-name: com.fasterxml.jackson.core:jackson-core dependency-type: direct:production update-type: version-update:semver-patch dependency-group: fasterxml - dependency-name: com.fasterxml.jackson.core:jackson-core dependency-type: direct:production update-type: version-update:semver-patch dependency-group: fasterxml - dependency-name: com.fasterxml.jackson.core:jackson-databind dependency-type: direct:production update-type: version-update:semver-patch dependency-group: fasterxml - dependency-name: com.fasterxml.jackson.datatype:jackson-datatype-jsr310 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: fasterxml - dependency-name: com.fasterxml.jackson.datatype:jackson-datatype-jsr310 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: fasterxml ... Signed-off-by: dependabot[bot] --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 388dd0cf8393..ad888d1dc339 100644 --- a/pom.xml +++ b/pom.xml @@ -31,8 +31,8 @@ 3.10.8 2.36.0 - 2.18.2 - 2.18.2 + 2.18.3 + 2.18.3 2.1.1 4.0.2 4.0.5 From 754a3450eefaa9d5ee50e4f2741cfdf26bab4ad7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 8 Apr 2025 16:18:07 +0000 Subject: [PATCH 522/979] Bump the spring group with 23 updates Bumps the spring group with 23 updates: | Package | From | To | | --- | --- | --- | | [org.springframework:spring-orm](https://github.com/spring-projects/spring-framework) | `6.2.4` | `6.2.5` | | [org.springframework:spring-core](https://github.com/spring-projects/spring-framework) | `6.2.4` | `6.2.5` | | [org.springframework:spring-beans](https://github.com/spring-projects/spring-framework) | `6.2.4` | `6.2.5` | | [org.springframework:spring-aop](https://github.com/spring-projects/spring-framework) | `6.2.4` | `6.2.5` | | [org.springframework:spring-context](https://github.com/spring-projects/spring-framework) | `6.2.4` | `6.2.5` | | [org.springframework:spring-context-support](https://github.com/spring-projects/spring-framework) | `6.2.4` | `6.2.5` | | [org.springframework:spring-tx](https://github.com/spring-projects/spring-framework) | `6.2.4` | `6.2.5` | | [org.springframework:spring-jdbc](https://github.com/spring-projects/spring-framework) | `6.2.4` | `6.2.5` | | [org.springframework:spring-web](https://github.com/spring-projects/spring-framework) | `6.2.4` | `6.2.5` | | [org.springframework:spring-webmvc](https://github.com/spring-projects/spring-framework) | `6.2.4` | `6.2.5` | | [org.springframework:spring-expression](https://github.com/spring-projects/spring-framework) | `6.2.4` | `6.2.5` | | [org.springframework:spring-test](https://github.com/spring-projects/spring-framework) | `6.2.4` | `6.2.5` | | [org.springframework.boot:spring-boot-starter-test](https://github.com/spring-projects/spring-boot) | `3.4.3` | `3.4.4` | | [org.springframework.boot:spring-boot-starter-tomcat](https://github.com/spring-projects/spring-boot) | `3.4.3` | `3.4.4` | | [org.springframework.boot:spring-boot-maven-plugin](https://github.com/spring-projects/spring-boot) | `3.4.3` | `3.4.4` | | [org.springframework.boot:spring-boot-starter-cache](https://github.com/spring-projects/spring-boot) | `3.4.3` | `3.4.4` | | [org.springframework.boot:spring-boot-starter](https://github.com/spring-projects/spring-boot) | `3.4.3` | `3.4.4` | | [org.springframework.boot:spring-boot-starter-web](https://github.com/spring-projects/spring-boot) | `3.4.3` | `3.4.4` | | [org.springframework.boot:spring-boot-starter-data-rest](https://github.com/spring-projects/spring-boot) | `3.4.3` | `3.4.4` | | [org.springframework.boot:spring-boot-starter-security](https://github.com/spring-projects/spring-boot) | `3.4.3` | `3.4.4` | | [org.springframework.boot:spring-boot-starter-aop](https://github.com/spring-projects/spring-boot) | `3.4.3` | `3.4.4` | | [org.springframework.boot:spring-boot-starter-actuator](https://github.com/spring-projects/spring-boot) | `3.4.3` | `3.4.4` | | [org.springframework.boot:spring-boot-starter-log4j2](https://github.com/spring-projects/spring-boot) | `3.4.3` | `3.4.4` | Updates `org.springframework:spring-orm` from 6.2.4 to 6.2.5 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.4...v6.2.5) Updates `org.springframework:spring-core` from 6.2.4 to 6.2.5 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.4...v6.2.5) Updates `org.springframework:spring-beans` from 6.2.4 to 6.2.5 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.4...v6.2.5) Updates `org.springframework:spring-aop` from 6.2.4 to 6.2.5 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.4...v6.2.5) Updates `org.springframework:spring-context` from 6.2.4 to 6.2.5 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.4...v6.2.5) Updates `org.springframework:spring-context-support` from 6.2.4 to 6.2.5 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.4...v6.2.5) Updates `org.springframework:spring-tx` from 6.2.4 to 6.2.5 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.4...v6.2.5) Updates `org.springframework:spring-jdbc` from 6.2.4 to 6.2.5 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.4...v6.2.5) Updates `org.springframework:spring-web` from 6.2.4 to 6.2.5 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.4...v6.2.5) Updates `org.springframework:spring-webmvc` from 6.2.4 to 6.2.5 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.4...v6.2.5) Updates `org.springframework:spring-expression` from 6.2.4 to 6.2.5 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.4...v6.2.5) Updates `org.springframework:spring-test` from 6.2.4 to 6.2.5 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.4...v6.2.5) Updates `org.springframework:spring-core` from 6.2.4 to 6.2.5 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.4...v6.2.5) Updates `org.springframework:spring-beans` from 6.2.4 to 6.2.5 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.4...v6.2.5) Updates `org.springframework:spring-aop` from 6.2.4 to 6.2.5 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.4...v6.2.5) Updates `org.springframework:spring-context` from 6.2.4 to 6.2.5 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.4...v6.2.5) Updates `org.springframework:spring-context-support` from 6.2.4 to 6.2.5 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.4...v6.2.5) Updates `org.springframework:spring-tx` from 6.2.4 to 6.2.5 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.4...v6.2.5) Updates `org.springframework:spring-jdbc` from 6.2.4 to 6.2.5 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.4...v6.2.5) Updates `org.springframework:spring-web` from 6.2.4 to 6.2.5 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.4...v6.2.5) Updates `org.springframework:spring-webmvc` from 6.2.4 to 6.2.5 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.4...v6.2.5) Updates `org.springframework:spring-expression` from 6.2.4 to 6.2.5 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.4...v6.2.5) Updates `org.springframework:spring-test` from 6.2.4 to 6.2.5 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.4...v6.2.5) Updates `org.springframework.boot:spring-boot-starter-test` from 3.4.3 to 3.4.4 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.4.3...v3.4.4) Updates `org.springframework.boot:spring-boot-starter-tomcat` from 3.4.3 to 3.4.4 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.4.3...v3.4.4) Updates `org.springframework.boot:spring-boot-maven-plugin` from 3.4.3 to 3.4.4 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.4.3...v3.4.4) Updates `org.springframework.boot:spring-boot-starter-cache` from 3.4.3 to 3.4.4 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.4.3...v3.4.4) Updates `org.springframework.boot:spring-boot-starter` from 3.4.3 to 3.4.4 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.4.3...v3.4.4) Updates `org.springframework.boot:spring-boot-starter-web` from 3.4.3 to 3.4.4 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.4.3...v3.4.4) Updates `org.springframework.boot:spring-boot-starter-data-rest` from 3.4.3 to 3.4.4 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.4.3...v3.4.4) Updates `org.springframework.boot:spring-boot-starter-security` from 3.4.3 to 3.4.4 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.4.3...v3.4.4) Updates `org.springframework.boot:spring-boot-starter-aop` from 3.4.3 to 3.4.4 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.4.3...v3.4.4) Updates `org.springframework.boot:spring-boot-starter-actuator` from 3.4.3 to 3.4.4 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.4.3...v3.4.4) Updates `org.springframework.boot:spring-boot-starter-log4j2` from 3.4.3 to 3.4.4 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.4.3...v3.4.4) Updates `org.springframework.boot:spring-boot-starter-tomcat` from 3.4.3 to 3.4.4 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.4.3...v3.4.4) Updates `org.springframework.boot:spring-boot-maven-plugin` from 3.4.3 to 3.4.4 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.4.3...v3.4.4) Updates `org.springframework.boot:spring-boot-starter-cache` from 3.4.3 to 3.4.4 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.4.3...v3.4.4) Updates `org.springframework.boot:spring-boot-starter` from 3.4.3 to 3.4.4 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.4.3...v3.4.4) Updates `org.springframework.boot:spring-boot-starter-web` from 3.4.3 to 3.4.4 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.4.3...v3.4.4) Updates `org.springframework.boot:spring-boot-starter-data-rest` from 3.4.3 to 3.4.4 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.4.3...v3.4.4) Updates `org.springframework.boot:spring-boot-starter-security` from 3.4.3 to 3.4.4 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.4.3...v3.4.4) Updates `org.springframework.boot:spring-boot-starter-aop` from 3.4.3 to 3.4.4 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.4.3...v3.4.4) Updates `org.springframework.boot:spring-boot-starter-actuator` from 3.4.3 to 3.4.4 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.4.3...v3.4.4) Updates `org.springframework.boot:spring-boot-starter-log4j2` from 3.4.3 to 3.4.4 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.4.3...v3.4.4) --- updated-dependencies: - dependency-name: org.springframework:spring-orm dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-core dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-beans dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-aop dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-context dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-context-support dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-tx dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-jdbc dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-web dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-webmvc dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-expression dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-test dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-core dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-beans dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-aop dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-context dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-context-support dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-tx dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-jdbc dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-web dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-webmvc dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-expression dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-test dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter-test dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter-tomcat dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-maven-plugin dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter-cache dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter-web dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter-data-rest dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter-security dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter-aop dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter-actuator dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter-log4j2 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter-tomcat dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-maven-plugin dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter-cache dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter-web dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter-data-rest dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter-security dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter-aop dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter-actuator dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter-log4j2 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring ... Signed-off-by: dependabot[bot] --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 388dd0cf8393..5a3e755c112a 100644 --- a/pom.xml +++ b/pom.xml @@ -19,8 +19,8 @@ 17 - 6.2.4 - 3.4.3 + 6.2.5 + 3.4.4 6.4.4 6.4.8.Final 8.0.1.Final From ffff7cc4d8b63c1b89b605d61594f31bab4b3234 Mon Sep 17 00:00:00 2001 From: Sascha Szott Date: Wed, 9 Apr 2025 13:39:05 +0200 Subject: [PATCH 523/979] remove inclusion of sword-client.cfg (cherry picked from commit 9794df594cc7d155cecc6ae9f86a6fc3811a61db) --- dspace/config/dspace.cfg | 1 - 1 file changed, 1 deletion(-) diff --git a/dspace/config/dspace.cfg b/dspace/config/dspace.cfg index debe4b658fb9..c9ae5d521d90 100644 --- a/dspace/config/dspace.cfg +++ b/dspace/config/dspace.cfg @@ -1672,7 +1672,6 @@ include = ${module_dir}/researcher-profile.cfg include = ${module_dir}/spring.cfg include = ${module_dir}/submission-curation.cfg include = ${module_dir}/suggestion.cfg -include = ${module_dir}/sword-client.cfg include = ${module_dir}/sword-server.cfg include = ${module_dir}/swordv2-server.cfg include = ${module_dir}/translator.cfg From a4314e5df6cebd81acc28f1927ee3fb7b68e5235 Mon Sep 17 00:00:00 2001 From: Sascha Szott Date: Wed, 9 Apr 2025 13:40:02 +0200 Subject: [PATCH 524/979] removal of configuration file sword-client.cfg (cherry picked from commit d299d2268507f55b4b6ab2a43d33222da8c082b9) --- dspace/config/modules/sword-client.cfg | 23 ----------------------- 1 file changed, 23 deletions(-) delete mode 100644 dspace/config/modules/sword-client.cfg diff --git a/dspace/config/modules/sword-client.cfg b/dspace/config/modules/sword-client.cfg deleted file mode 100644 index 9e8a029ae684..000000000000 --- a/dspace/config/modules/sword-client.cfg +++ /dev/null @@ -1,23 +0,0 @@ -#---------------------------------------------------------------# -#--------------SWORD V.1 CLIENT CONFIGURATIONS------------------# -#---------------------------------------------------------------# -# Configuration properties used solely by the UI-based SWORD # -# Client interface (used to submit DSpace content to another # -# SWORD server). # -#---------------------------------------------------------------# -# TODO: UNSUPPORTED in DSpace 7.0 -# List of remote Sword servers. Used to build the drop-down list of selectable Sword targets. -sword-client.targets = http://localhost:8080/sword/servicedocument, \ - http://client.swordapp.org/client/servicedocument, \ - http://dspace.swordapp.org/sword/servicedocument, \ - http://sword.eprints.org/sword-app/servicedocument, \ - http://sword.intralibrary.com/IntraLibrary-Deposit/service, \ - http://fedora.swordapp.org/sword-fedora/servicedocument - -# List of file types from which the user can select. If a type is not supported by the remote server -# it will not appear in the drop-down list. -sword-client.file-types = application/zip - -# List of package formats from which the user can select. If a format is not supported by the remote server -# it will not appear in the drop-down list. -sword-client.package-formats = http://purl.org/net/sword-types/METSDSpaceSIP From 518fb3b1d89795b4995b32e164e3ac9c9e48350e Mon Sep 17 00:00:00 2001 From: Alexandre Vryghem Date: Thu, 20 Feb 2025 18:21:21 +0100 Subject: [PATCH 525/979] 126885: Removed database connection leak on login Also: - Updated EPersonRestAuthenticationProvider to not open an additional DB connection, and reuse the existing one instead - Normalized the behaviour of OidcLoginFilter by not calling the redirectAfterSuccess instead of doing a chain.doFilter(req, res). This way we don't need to reopen a new Context --- .../EPersonRestAuthenticationProvider.java | 64 ++++++++----------- .../app/rest/security/OidcLoginFilter.java | 52 ++++++++++++++- ...JWTTokenRestAuthenticationServiceImpl.java | 3 + 3 files changed, 80 insertions(+), 39 deletions(-) diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/security/EPersonRestAuthenticationProvider.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/security/EPersonRestAuthenticationProvider.java index e55734e513de..00c804ad2d64 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/security/EPersonRestAuthenticationProvider.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/security/EPersonRestAuthenticationProvider.java @@ -85,7 +85,7 @@ public Authentication authenticate(Authentication authentication) throws Authent } else { // Otherwise, this is a new login & we need to attempt authentication log.debug("Request to authenticate new login"); - return authenticateNewLogin(authentication); + return authenticateNewLogin(context, authentication); } } @@ -107,56 +107,44 @@ private Authentication authenticateRefreshTokenRequest(Context context) { * If login is successful, returns a NEW Authentication class containing the logged in EPerson and their list of * GrantedAuthority objects. If login fails, a BadCredentialsException is thrown. If no valid login found implicit * or explicit, then null is returned. + * + * @param context The current DSpace context * @param authentication Authentication class to attempt authentication. * @return new Authentication class containing logged-in user information or null */ - private Authentication authenticateNewLogin(Authentication authentication) { - Context newContext = null; + private Authentication authenticateNewLogin(Context context, Authentication authentication) { Authentication output = null; if (authentication != null) { - try { - newContext = new Context(); - String name = authentication.getName(); - String password = Objects.toString(authentication.getCredentials(), null); + String name = authentication.getName(); + String password = Objects.toString(authentication.getCredentials(), null); - int implicitStatus = authenticationService.authenticateImplicit(newContext, null, null, null, request); + int implicitStatus = authenticationService.authenticateImplicit(context, null, null, null, request); - if (implicitStatus == AuthenticationMethod.SUCCESS) { - log.info(LogHelper.getHeader(newContext, "login", "type=implicit")); - output = createAuthentication(newContext); - } else { - int authenticateResult = authenticationService - .authenticate(newContext, name, password, null, request); - if (AuthenticationMethod.SUCCESS == authenticateResult) { + if (implicitStatus == AuthenticationMethod.SUCCESS) { + log.info(LogHelper.getHeader(context, "login", "type=implicit")); + output = createAuthentication(context); + } else { + int authenticateResult = authenticationService.authenticate(context, name, password, null, request); + if (AuthenticationMethod.SUCCESS == authenticateResult) { - log.info(LogHelper - .getHeader(newContext, "login", "type=explicit")); + log.info(LogHelper.getHeader(context, "login", "type=explicit")); - output = createAuthentication(newContext); + output = createAuthentication(context); - for (PostLoggedInAction action : postLoggedInActions) { - try { - action.loggedIn(newContext); - } catch (Exception ex) { - log.error("An error occurs performing post logged in action", ex); - } + for (PostLoggedInAction action : postLoggedInActions) { + try { + action.loggedIn(context); + } catch (Exception ex) { + log.error("An error occurs performing post logged in action", ex); } - - } else { - log.info(LogHelper.getHeader(newContext, "failed_login", "email=" - + name + ", result=" - + authenticateResult)); - throw new BadCredentialsException("Login failed"); - } - } - } finally { - if (newContext != null && newContext.isValid()) { - try { - newContext.complete(); - } catch (SQLException e) { - log.error(e.getMessage() + " occurred while trying to close", e); } + + } else { + log.info(LogHelper.getHeader(context, "failed_login", "email=" + + name + ", result=" + + authenticateResult)); + throw new BadCredentialsException("Login failed"); } } } diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/security/OidcLoginFilter.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/security/OidcLoginFilter.java index c84840e77041..e73a3e8d3ccb 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/security/OidcLoginFilter.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/security/OidcLoginFilter.java @@ -10,11 +10,18 @@ import static org.dspace.authenticate.OidcAuthenticationBean.OIDC_AUTH_ATTRIBUTE; import java.io.IOException; +import java.util.ArrayList; import javax.servlet.FilterChain; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; +import org.apache.commons.lang3.StringUtils; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.dspace.core.Utils; +import org.dspace.services.ConfigurationService; +import org.dspace.services.factory.DSpaceServicesFactory; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.core.Authentication; import org.springframework.security.core.AuthenticationException; @@ -27,6 +34,11 @@ public class OidcLoginFilter extends StatelessLoginFilter { + private static final Logger log = LogManager.getLogger(OidcLoginFilter.class); + + private final ConfigurationService configurationService = DSpaceServicesFactory.getInstance() + .getConfigurationService(); + public OidcLoginFilter(String url, AuthenticationManager authenticationManager, RestAuthenticationService restAuthenticationService) { super(url, authenticationManager, restAuthenticationService); @@ -44,7 +56,45 @@ public Authentication attemptAuthentication(HttpServletRequest req, HttpServletR protected void successfulAuthentication(HttpServletRequest req, HttpServletResponse res, FilterChain chain, Authentication auth) throws IOException, ServletException { restAuthenticationService.addAuthenticationDataForUser(req, res, (DSpaceAuthentication) auth, true); - chain.doFilter(req, res); + redirectAfterSuccess(req, res); + } + + /** + * After successful login, redirect to the DSpace URL specified by this OIDC + * request (in the "redirectUrl" request parameter). If that 'redirectUrl' is + * not valid or trusted for this DSpace site, then return a 400 error. + * @param request + * @param response + * @throws IOException + */ + private void redirectAfterSuccess(HttpServletRequest request, HttpServletResponse response) throws IOException { + // Get redirect URL from request parameter + String redirectUrl = request.getParameter("redirectUrl"); + + // If redirectUrl unspecified, default to the configured UI + if (StringUtils.isEmpty(redirectUrl)) { + redirectUrl = configurationService.getProperty("dspace.ui.url"); + } + + // Validate that the redirectURL matches either the server or UI hostname. It + // *cannot* be an arbitrary URL. + String redirectHostName = Utils.getHostName(redirectUrl); + String serverHostName = Utils.getHostName(configurationService.getProperty("dspace.server.url")); + ArrayList allowedHostNames = new ArrayList<>(); + allowedHostNames.add(serverHostName); + String[] allowedUrls = configurationService.getArrayProperty("rest.cors.allowed-origins"); + for (String url : allowedUrls) { + allowedHostNames.add(Utils.getHostName(url)); + } + + if (StringUtils.equalsAnyIgnoreCase(redirectHostName, allowedHostNames.toArray(new String[0]))) { + log.debug("OIDC redirecting to " + redirectUrl); + response.sendRedirect(redirectUrl); + } else { + log.error("Invalid OIDC redirectURL=" + redirectUrl + ". URL doesn't match hostname of server or UI!"); + response.sendError(HttpServletResponse.SC_BAD_REQUEST, + "Invalid redirectURL! Must match server or ui hostname."); + } } } diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/security/jwt/JWTTokenRestAuthenticationServiceImpl.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/security/jwt/JWTTokenRestAuthenticationServiceImpl.java index c28729ff83a8..425ba505b431 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/security/jwt/JWTTokenRestAuthenticationServiceImpl.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/security/jwt/JWTTokenRestAuthenticationServiceImpl.java @@ -84,6 +84,9 @@ public void addAuthenticationDataForUser(HttpServletRequest request, HttpServlet String token = loginJWTTokenHandler.createTokenForEPerson(context, request, authentication.getPreviousLoginDate()); context.commit(); + // Close the Context, because the DSpaceRequestContextFilter is not called for requests that trigger + // the authentication filters (filters that extend AbstractAuthenticationProcessingFilter) + context.close(); // Add newly generated auth token to the response addTokenToResponse(request, response, token, addCookie); From a74ff3d96084ed5d954979235c537085002c557e Mon Sep 17 00:00:00 2001 From: Kim Shepherd Date: Fri, 4 Apr 2025 17:46:35 +0200 Subject: [PATCH 526/979] #9778 Replace OAI templating with Thymeleaf (cherry picked from commit f2389e6e7b9996181344ab70dd5f2886153001ab) --- dspace-oai/pom.xml | 20 +++-- .../app/configuration/OAIWebConfig.java | 56 +++++++++---- .../src/main/resources/templates/index.html | 78 ++++++++++++++++++ .../main/resources/templates/index.twig.html | 81 ------------------- 4 files changed, 129 insertions(+), 106 deletions(-) create mode 100644 dspace-oai/src/main/resources/templates/index.html delete mode 100644 dspace-oai/src/main/resources/templates/index.twig.html diff --git a/dspace-oai/pom.xml b/dspace-oai/pom.xml index b824b7396e03..eae700663e6e 100644 --- a/dspace-oai/pom.xml +++ b/dspace-oai/pom.xml @@ -87,18 +87,16 @@ - + - org.jtwig - jtwig-spring-boot-starter - ${jtwig.version} - - - - org.springframework.boot - spring-boot-starter-web - - + org.springframework + spring-webmvc + ${spring.version} + + + org.springframework.boot + spring-boot-starter-thymeleaf + ${spring-boot.version} diff --git a/dspace-oai/src/main/java/org/dspace/app/configuration/OAIWebConfig.java b/dspace-oai/src/main/java/org/dspace/app/configuration/OAIWebConfig.java index dc4efde880d5..565d9f032632 100644 --- a/dspace-oai/src/main/java/org/dspace/app/configuration/OAIWebConfig.java +++ b/dspace-oai/src/main/java/org/dspace/app/configuration/OAIWebConfig.java @@ -11,8 +11,6 @@ import org.dspace.xoai.app.BasicConfiguration; import org.dspace.xoai.services.api.xoai.ItemRepositoryResolver; import org.dspace.xoai.services.impl.xoai.DSpaceItemRepositoryResolver; -import org.jtwig.spring.JtwigViewResolver; -import org.jtwig.spring.boot.config.JtwigViewResolverConfigurer; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; @@ -20,31 +18,37 @@ import org.springframework.context.annotation.Import; import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; +import org.thymeleaf.spring6.SpringTemplateEngine; +import org.thymeleaf.spring6.templateresolver.SpringResourceTemplateResolver; +import org.thymeleaf.spring6.view.ThymeleafViewResolver; +import org.thymeleaf.templatemode.TemplateMode; /** - * OAI-PMH webapp configuration. Replaces the old web.xml + * OAI-PMH webapp configuration. Replaces the old web.xml. + * This webapp used JTwig in earlier versions and has been refactored to + * use Thymeleaf instead. *

    * This @Configuration class is automatically discovered by Spring Boot via a @ComponentScan * on the org.dspace.app.configuration package. *

    * * - * @author Tim Donohue + * @author Kim Shepherd */ @Configuration // Import additional configuration and beans from BasicConfiguration @Import(BasicConfiguration.class) // Scan for controllers in this package @ComponentScan("org.dspace.xoai.controller") -public class OAIWebConfig implements WebMvcConfigurer, JtwigViewResolverConfigurer { +public class OAIWebConfig implements WebMvcConfigurer { // Path where OAI is deployed. Defaults to "oai" // NOTE: deployment on this path is handled by org.dspace.xoai.controller.DSpaceOAIDataProvider @Value("${oai.path:oai}") private String oaiPath; - private static final String TWIG_HTML_EXTENSION = ".twig.html"; private static final String VIEWS_LOCATION = "classpath:/templates/"; + private static final String HTML_EXTENSION = ".html"; /** * Ensure all resources under src/main/resources/static/ directory are available @@ -58,18 +62,42 @@ public void addResourceHandlers(ResourceHandlerRegistry registry) { } /** - * Configure the Jtwig template engine for Spring Boot - * Ensures Jtwig looks for templates in proper location with proper extension + * Configure the Thymeleaf template resolver **/ - @Override - public void configure(JtwigViewResolver viewResolver) { - viewResolver.setPrefix(VIEWS_LOCATION); - viewResolver.setSuffix(TWIG_HTML_EXTENSION); + @Bean + public SpringResourceTemplateResolver templateResolver() { + SpringResourceTemplateResolver templateResolver = new SpringResourceTemplateResolver(); + templateResolver.setPrefix(VIEWS_LOCATION); + templateResolver.setSuffix(HTML_EXTENSION); + templateResolver.setTemplateMode(TemplateMode.HTML); + templateResolver.setCacheable(true); + return templateResolver; + } + + /** + * Configure the Thymeleaf template engine + **/ + @Bean + public SpringTemplateEngine templateEngine() { + SpringTemplateEngine templateEngine = new SpringTemplateEngine(); + templateEngine.setTemplateResolver(templateResolver()); + templateEngine.setEnableSpringELCompiler(true); + return templateEngine; + } + + /** + * Configure the Thymeleaf view resolver + **/ + @Bean + public ThymeleafViewResolver viewResolver() { + ThymeleafViewResolver viewResolver = new ThymeleafViewResolver(); + viewResolver.setTemplateEngine(templateEngine()); + viewResolver.setCharacterEncoding("UTF-8"); + return viewResolver; } @Bean public ItemRepositoryResolver xoaiItemRepositoryResolver() { return new DSpaceItemRepositoryResolver(); } -} - +} \ No newline at end of file diff --git a/dspace-oai/src/main/resources/templates/index.html b/dspace-oai/src/main/resources/templates/index.html new file mode 100644 index 000000000000..a0d35d91dea2 --- /dev/null +++ b/dspace-oai/src/main/resources/templates/index.html @@ -0,0 +1,78 @@ + + + + + + DSpace OAI-PMH Data Provider + + + + + + + + + + + +

    + + + + + + + +
    +

    Available Contexts

    + +
    + +
    +
    +

    DSpace OAI-PMH Data Provider

    +

    + + DSpace + +

    +
    + +
    + + diff --git a/dspace-oai/src/main/resources/templates/index.twig.html b/dspace-oai/src/main/resources/templates/index.twig.html deleted file mode 100644 index c473188e35c0..000000000000 --- a/dspace-oai/src/main/resources/templates/index.twig.html +++ /dev/null @@ -1,81 +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/ - -#} -{# - - DSpace OAI default index template. To override this template, place a customized version in - the [webapp]/WEB-INF/classes/templates/ folder, and reboot your servlet engine. - -#} - - - - DSpace OAI-PMH Data Provider - - - - - {# NOTE: We use JQuery and Bootstrap via WebJars which are configured in dspace-server-webapp #} - - - - - - -
    - - - - - - - -
    -

    Available Contexts

    -
    - {% for item in contexts %} -
    -

    {{ item.name }} {% if (item.description) %}{{ item.description }}{% endif %}

    -

    -

    -

    -

    -
    - {% endfor %} -
    -
    - -
    -
    -

    DSpace OAI-PMH Data Provider

    -

    - - DSpace - -

    -
    - -
    - - From 7aceda3dd29e17d5e60ab27c83fe8e7781df93b5 Mon Sep 17 00:00:00 2001 From: Kim Shepherd Date: Wed, 9 Apr 2025 22:47:15 +0200 Subject: [PATCH 527/979] #9778 Remove jtwig version property from oai pom.xml (cherry picked from commit aa304767cefc41209e91adb91256e426583f0159) --- dspace-oai/pom.xml | 1 - 1 file changed, 1 deletion(-) diff --git a/dspace-oai/pom.xml b/dspace-oai/pom.xml index eae700663e6e..10511740459c 100644 --- a/dspace-oai/pom.xml +++ b/dspace-oai/pom.xml @@ -16,7 +16,6 @@ ${basedir}/.. 3.4.0 - 5.87.0.RELEASE From 6c8adb2d2f1a5285870a666da6c00c52184195c5 Mon Sep 17 00:00:00 2001 From: Kim Shepherd Date: Wed, 9 Apr 2025 22:48:08 +0200 Subject: [PATCH 528/979] #9778 Remove jtwig references from LICENSES_THIRD_PARTY (cherry picked from commit c01170071227bb097b2fc454077690100bb26805) --- LICENSES_THIRD_PARTY | 5 ----- 1 file changed, 5 deletions(-) diff --git a/LICENSES_THIRD_PARTY b/LICENSES_THIRD_PARTY index 1d39b851ad61..5049903ffc62 100644 --- a/LICENSES_THIRD_PARTY +++ b/LICENSES_THIRD_PARTY @@ -384,11 +384,6 @@ https://wiki.lyrasis.org/display/DSPACE/Code+Contribution+Guidelines * Javassist (org.javassist:javassist:3.30.2-GA - https://www.javassist.org/) * JBoss Logging 3 (org.jboss.logging:jboss-logging:3.6.1.Final - http://www.jboss.org) * JDOM (org.jdom:jdom2:2.0.6.1 - http://www.jdom.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) - * jtwig-spring-boot-starter (org.jtwig:jtwig-spring-boot-starter:5.87.0.RELEASE - http://jtwig.org) - * jtwig-web (org.jtwig:jtwig-web:5.87.0.RELEASE - http://jtwig.org) * Proj4J (org.locationtech.proj4j:proj4j:1.1.5 - https://github.com/locationtech/proj4j) * Spatial4J (org.locationtech.spatial4j:spatial4j:0.7 - https://projects.eclipse.org/projects/locationtech.spatial4j) * MockServer Java Client (org.mock-server:mockserver-client-java:5.15.0 - https://www.mock-server.com) From e122a90674e1e36222fd62f46538020d77d2a88f Mon Sep 17 00:00:00 2001 From: Jens Vannerum Date: Tue, 11 Mar 2025 10:58:08 +0100 Subject: [PATCH 529/979] Implement a SEOHealthIndicator which verifies all relevant parameters for SEO are ok (cherry picked from commit 4bd8a24ca75f6d2e6384e850b45c96c4f1229f02) --- .../configuration/ActuatorConfiguration.java | 7 ++ .../app/rest/health/SEOHealthIndicator.java | 77 +++++++++++++++++++ 2 files changed, 84 insertions(+) create mode 100644 dspace-server-webapp/src/main/java/org/dspace/app/rest/health/SEOHealthIndicator.java diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/configuration/ActuatorConfiguration.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/configuration/ActuatorConfiguration.java index ad78fe2db40b..15b9bd9506f9 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/configuration/ActuatorConfiguration.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/configuration/ActuatorConfiguration.java @@ -14,6 +14,7 @@ import org.apache.solr.client.solrj.SolrServerException; import org.dspace.app.rest.DiscoverableEndpointsService; import org.dspace.app.rest.health.GeoIpHealthIndicator; +import org.dspace.app.rest.health.SEOHealthIndicator; import org.dspace.authority.AuthoritySolrServiceImpl; import org.dspace.discovery.SolrSearchCore; import org.dspace.statistics.SolrStatisticsCore; @@ -82,6 +83,12 @@ public SolrHealthIndicator solrOaiCoreHealthIndicator(SolrServerResolver solrSer return new SolrHealthIndicator(solrServerResolver.getServer()); } + @Bean + @ConditionalOnEnabledHealthIndicator("seo") + public SEOHealthIndicator seoHealthIndicator() { + return new SEOHealthIndicator(); + } + @Bean @ConditionalOnEnabledHealthIndicator("geoIp") public GeoIpHealthIndicator geoIpHealthIndicator() { diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/health/SEOHealthIndicator.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/health/SEOHealthIndicator.java new file mode 100644 index 000000000000..d936fce635e6 --- /dev/null +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/health/SEOHealthIndicator.java @@ -0,0 +1,77 @@ +/** + * The contents of this file are subject to the license and copyright + * detailed in the LICENSE and NOTICE files at the root of the source + * tree and available online at + * + * http://www.dspace.org/license/ + */ +package org.dspace.app.rest.health; + +import org.apache.commons.lang3.StringUtils; +import org.dspace.services.ConfigurationService; +import org.dspace.services.factory.DSpaceServicesFactory; +import org.springframework.boot.actuate.health.AbstractHealthIndicator; +import org.springframework.boot.actuate.health.Health; +import org.springframework.web.client.RestTemplate; + +/** + * Implementation of {@link org.springframework.boot.actuate.health.HealthIndicator} that verifies if the SEO of the + * DSpace instance is configured correctly. + * + * This is only relevant in a production environment, where the DSpace instance is exposed to the public. + */ +public class SEOHealthIndicator extends AbstractHealthIndicator { + + ConfigurationService configurationService = DSpaceServicesFactory.getInstance().getConfigurationService(); + + private final RestTemplate restTemplate = new RestTemplate(); + + @Override + protected void doHealthCheck(Health.Builder builder) { + String baseUrl = configurationService.getProperty("dspace.ui.url"); + + boolean sitemapOk = checkUrl(baseUrl + "/sitemap_index.xml") || checkUrl(baseUrl + "/sitemap_index.html"); + boolean robotsTxtOk = checkRobotsTxt(baseUrl + "/robots.txt"); + boolean ssrOk = checkSSR(baseUrl); + + if (sitemapOk && robotsTxtOk && ssrOk) { + builder.up() + .withDetail("sitemap", "OK") + .withDetail("robots.txt", "OK") + .withDetail("ssr", "OK"); + } else { + builder.down() + .withDetail("sitemap", sitemapOk ? "OK" : "Missing or inaccessible") + .withDetail("robots.txt", robotsTxtOk ? "OK" : "Empty or contains local URLs") + .withDetail("ssr", ssrOk ? "OK" : "Server-side rendering might be disabled"); + } + } + + private boolean checkUrl(String url) { + try { + restTemplate.getForEntity(url, String.class); + return true; + } catch (Exception e) { + return false; + } + } + + private boolean checkRobotsTxt(String url) { + try { + String content = restTemplate.getForObject(url, String.class); + return StringUtils.isNotBlank(content) && !content.contains("localhost"); + } catch (Exception e) { + return false; + } + } + + private boolean checkSSR(String url) { + try { + String content = restTemplate.getForObject(url, String.class); + return content != null && !content.contains(""); + } catch (Exception e) { + return false; + } + } +} + From 31549bdaceb9220272d3645100fc8b48f28c7fa2 Mon Sep 17 00:00:00 2001 From: Jens Vannerum Date: Tue, 18 Mar 2025 10:04:36 +0100 Subject: [PATCH 530/979] Disable new actuator in IT (cherry picked from commit 20ab43ccccf84c83d6db9b431321e60256d30355) --- dspace-api/src/test/data/dspaceFolder/config/local.cfg | 1 + 1 file changed, 1 insertion(+) diff --git a/dspace-api/src/test/data/dspaceFolder/config/local.cfg b/dspace-api/src/test/data/dspaceFolder/config/local.cfg index 0b4fe8288cfa..ab0d14de6ee3 100644 --- a/dspace-api/src/test/data/dspaceFolder/config/local.cfg +++ b/dspace-api/src/test/data/dspaceFolder/config/local.cfg @@ -158,6 +158,7 @@ proxies.trusted.include_ui_ip = true # For the tests we have to disable this health indicator because there isn't a mock server and the calculated status was DOWN management.health.solrOai.enabled = false +management.health.seo.enabled = false # Enable researcher profiles and orcid synchronization for tests researcher-profile.entity-type = Person From 7a876999f8af137ced2064f5c9d3b7360399e6c0 Mon Sep 17 00:00:00 2001 From: Jens Vannerum Date: Tue, 1 Apr 2025 17:54:48 +0200 Subject: [PATCH 531/979] 127746: Implement different failures for robots file so we can differentiate between a missing file or an invalid file (cherry picked from commit 32c048428026f890c2f3bc7eca5a2f20717dc587) --- .../app/rest/health/SEOHealthIndicator.java | 37 ++++++++++++++----- 1 file changed, 28 insertions(+), 9 deletions(-) diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/health/SEOHealthIndicator.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/health/SEOHealthIndicator.java index d936fce635e6..a071e12088bd 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/health/SEOHealthIndicator.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/health/SEOHealthIndicator.java @@ -31,19 +31,28 @@ protected void doHealthCheck(Health.Builder builder) { String baseUrl = configurationService.getProperty("dspace.ui.url"); boolean sitemapOk = checkUrl(baseUrl + "/sitemap_index.xml") || checkUrl(baseUrl + "/sitemap_index.html"); - boolean robotsTxtOk = checkRobotsTxt(baseUrl + "/robots.txt"); + RobotsTxtStatus robotsTxtStatus = checkRobotsTxt(baseUrl + "/robots.txt"); boolean ssrOk = checkSSR(baseUrl); - if (sitemapOk && robotsTxtOk && ssrOk) { + if (sitemapOk && robotsTxtStatus == RobotsTxtStatus.VALID && ssrOk) { builder.up() .withDetail("sitemap", "OK") .withDetail("robots.txt", "OK") .withDetail("ssr", "OK"); } else { - builder.down() - .withDetail("sitemap", sitemapOk ? "OK" : "Missing or inaccessible") - .withDetail("robots.txt", robotsTxtOk ? "OK" : "Empty or contains local URLs") - .withDetail("ssr", ssrOk ? "OK" : "Server-side rendering might be disabled"); + builder.down(); + builder.withDetail("sitemap", sitemapOk ? "OK" : "Missing or inaccessible"); + + if (robotsTxtStatus == RobotsTxtStatus.MISSING) { + builder.withDetail("robots.txt", "Missing or inaccessible. Please see the DSpace Documentation on " + + "Search Engine Optimization for how to create a robots.txt."); + } else if (robotsTxtStatus == RobotsTxtStatus.INVALID) { + builder.withDetail("robots.txt", "Invalid because it contains localhost URLs. This is often a sign " + + "that a proxy is failing to pass X-Forwarded headers to DSpace. Please see the DSpace " + + "Documentation on Search Engine Optimization for how to pass X-Forwarded headers."); + } + + builder.withDetail("ssr", ssrOk ? "OK" : "Server-side rendering might be disabled"); } } @@ -56,12 +65,18 @@ private boolean checkUrl(String url) { } } - private boolean checkRobotsTxt(String url) { + private RobotsTxtStatus checkRobotsTxt(String url) { try { String content = restTemplate.getForObject(url, String.class); - return StringUtils.isNotBlank(content) && !content.contains("localhost"); + if (StringUtils.isBlank(content)) { + return RobotsTxtStatus.MISSING; + } + if (content.contains("localhost")) { + return RobotsTxtStatus.INVALID; + } + return RobotsTxtStatus.VALID; } catch (Exception e) { - return false; + return RobotsTxtStatus.MISSING; } } @@ -73,5 +88,9 @@ private boolean checkSSR(String url) { return false; } } + + private enum RobotsTxtStatus { + VALID, MISSING, INVALID + } } From 64c5f822091133e95e668d6d05a11887ef572153 Mon Sep 17 00:00:00 2001 From: Jens Vannerum Date: Tue, 1 Apr 2025 17:58:47 +0200 Subject: [PATCH 532/979] 127746: Add more detailed information messages on how to solve problems (cherry picked from commit 170dc9a44c5b16c28298a9e3133534e70967147d) --- .../org/dspace/app/rest/health/SEOHealthIndicator.java | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/health/SEOHealthIndicator.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/health/SEOHealthIndicator.java index a071e12088bd..740c6ab6493e 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/health/SEOHealthIndicator.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/health/SEOHealthIndicator.java @@ -41,7 +41,8 @@ protected void doHealthCheck(Health.Builder builder) { .withDetail("ssr", "OK"); } else { builder.down(); - builder.withDetail("sitemap", sitemapOk ? "OK" : "Missing or inaccessible"); + builder.withDetail("sitemap", sitemapOk ? "OK" : "Sitemaps are missing or inaccessible. Please see the " + + "DSpace Documentation on Search Engine Optimization for how to enable Sitemaps."); if (robotsTxtStatus == RobotsTxtStatus.MISSING) { builder.withDetail("robots.txt", "Missing or inaccessible. Please see the DSpace Documentation on " + @@ -51,8 +52,9 @@ protected void doHealthCheck(Health.Builder builder) { "that a proxy is failing to pass X-Forwarded headers to DSpace. Please see the DSpace " + "Documentation on Search Engine Optimization for how to pass X-Forwarded headers."); } - - builder.withDetail("ssr", ssrOk ? "OK" : "Server-side rendering might be disabled"); + builder.withDetail("ssr", ssrOk ? "OK" : "Server-side rendering (SSR) appears to be disabled. Most " + + "search engines require enabling SSR for proper indexing. Please see the DSpace Documentation on" + + " Search Engine Optimization for more details."); } } From 5b8782509fd857214934dca457f304b77e2c43d8 Mon Sep 17 00:00:00 2001 From: Jens Vannerum Date: Fri, 4 Apr 2025 16:51:56 +0200 Subject: [PATCH 533/979] 127746: Include success result for robots.txt check if other checks fail (cherry picked from commit 5dc12775fac0006dbc1d0106ffcdffbe893919d1) --- .../java/org/dspace/app/rest/health/SEOHealthIndicator.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/health/SEOHealthIndicator.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/health/SEOHealthIndicator.java index 740c6ab6493e..5b57f2d537fc 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/health/SEOHealthIndicator.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/health/SEOHealthIndicator.java @@ -51,6 +51,8 @@ protected void doHealthCheck(Health.Builder builder) { builder.withDetail("robots.txt", "Invalid because it contains localhost URLs. This is often a sign " + "that a proxy is failing to pass X-Forwarded headers to DSpace. Please see the DSpace " + "Documentation on Search Engine Optimization for how to pass X-Forwarded headers."); + } else { + builder.withDetail("robots.txt", "OK"); } builder.withDetail("ssr", ssrOk ? "OK" : "Server-side rendering (SSR) appears to be disabled. Most " + "search engines require enabling SSR for proper indexing. Please see the DSpace Documentation on" + From 7cb9eb49a3a431aa121fe0e55f2f47df8d22d8b5 Mon Sep 17 00:00:00 2001 From: "Mark H. Wood" Date: Thu, 10 Apr 2025 17:01:08 -0400 Subject: [PATCH 534/979] Translate null message parameters to "". Reorganize and add minimal test suite. (cherry picked from commit 66fe8d862ecb462791deb5d6d79649403ed1b5e0) --- .../src/main/java/org/dspace/core/Email.java | 120 ++++++++++++------ .../test/java/org/dspace/core/EmailTest.java | 68 ++++++++++ 2 files changed, 152 insertions(+), 36 deletions(-) create mode 100644 dspace-api/src/test/java/org/dspace/core/EmailTest.java diff --git a/dspace-api/src/main/java/org/dspace/core/Email.java b/dspace-api/src/main/java/org/dspace/core/Email.java index bb434c07cb96..74a48b3d82c9 100644 --- a/dspace-api/src/main/java/org/dspace/core/Email.java +++ b/dspace-api/src/main/java/org/dspace/core/Email.java @@ -55,7 +55,7 @@ import org.dspace.services.factory.DSpaceServicesFactory; /** - * Class representing an e-mail message. The {@link send} method causes the + * Builder representing an e-mail message. The {@link send} method causes the * assembled message to be formatted and sent. *

    * Typical use: @@ -168,6 +168,9 @@ public class Email { */ private String charset; + /** The message being assembled. */ + MimeMessage message; + private static final Logger LOG = LogManager.getLogger(); /** Velocity template settings. */ @@ -188,6 +191,9 @@ public class Email { /** Velocity template for a message body */ private Template template; + /** The message text. */ + private String body; + /** * Create a new email message. */ @@ -254,9 +260,15 @@ public void setReplyTo(String email) { /** * Fill out the next argument in the template. * - * @param arg the value for the next argument + * @param arg the value for the next argument. If {@code null}, + * a zero-length string is substituted. */ public void addArgument(Object arg) { + if (null == arg) { + arg = ""; + LOG.warn("Null argument {} to email template {} replaced with zero-length string", + arguments.size(), contentName); + } arguments.add(arg); } @@ -327,7 +339,27 @@ public void reset() { } /** - * Sends the email. If the template defines a Velocity context property + * Sends the email. If sending is disabled then the assembled message is + * logged instead. + * + * @throws MessagingException if there was a problem sending the mail. + * @throws IOException if IO error + */ + public void send() throws MessagingException, IOException { + build(); + + ConfigurationService config + = DSpaceServicesFactory.getInstance().getConfigurationService(); + boolean disabled = config.getBooleanProperty("mail.server.disabled", false); + if (disabled) { + LOG.info(format(message, body)); + } else { + Transport.send(message); + } + } + + /** + * Build the message. If the template defines a Velocity context property * named among the values of DSpace configuration property * {@code mail.message.headers} then that name and its value will be added * to the message's headers. @@ -336,11 +368,12 @@ public void reset() { * called, the value of any "subject" property will be used as if setSubject * had been called with that value. Thus a template may define its subject, * but the caller may override it. - * - * @throws MessagingException if there was a problem sending the mail. - * @throws IOException if IO error + * + * @throws MessagingException if there is no template, or passed through. + * @throws IOException passed through. */ - public void send() throws MessagingException, IOException { + void build() + throws MessagingException, IOException { if (null == template) { // No template -- no content -- PANIC!!! throw new MessagingException("Email has no body"); @@ -351,7 +384,6 @@ public void send() throws MessagingException, IOException { // Get the mail configuration properties String from = config.getProperty("mail.from.address"); - boolean disabled = config.getBooleanProperty("mail.server.disabled", false); // If no character set specified, attempt to retrieve a default if (charset == null) { @@ -362,7 +394,7 @@ public void send() throws MessagingException, IOException { Session session = DSpaceServicesFactory.getInstance().getEmailService().getSession(); // Create message - MimeMessage message = new MimeMessage(session); + message = new MimeMessage(session); // Set the recipients of the message for (String recipient : recipients) { @@ -385,7 +417,7 @@ public void send() throws MessagingException, IOException { LOG.error("Template not merged: {}", ex.getMessage()); throw new MessagingException("Template not merged", ex); } - String fullMessage = writer.toString(); + body = writer.toString(); // Set some message header fields Date date = new Date(); @@ -412,20 +444,19 @@ public void send() throws MessagingException, IOException { message.setSubject(subject); } - // Add attachments - if (attachments.isEmpty() && moreAttachments.isEmpty()) { - // If a character set has been specified, or a default exists + // Attach the body. + if (attachments.isEmpty() && moreAttachments.isEmpty()) { // Flat body. if (charset != null) { - message.setText(fullMessage, charset); + message.setText(body, charset); } else { - message.setText(fullMessage); + message.setText(body); } - } else { + } else { // Add attachments. Multipart multipart = new MimeMultipart(); // create the first part of the email BodyPart messageBodyPart = new MimeBodyPart(); - messageBodyPart.setText(fullMessage); + messageBodyPart.setText(body); multipart.addBodyPart(messageBodyPart); // Add file attachments @@ -457,30 +488,47 @@ public void send() throws MessagingException, IOException { replyToAddr[0] = new InternetAddress(replyTo); message.setReplyTo(replyToAddr); } + } - if (disabled) { - StringBuilder text = new StringBuilder( - "Message not sent due to mail.server.disabled:\n"); - - Enumeration headers = message.getAllHeaderLines(); - while (headers.hasMoreElements()) { - text.append(headers.nextElement()).append('\n'); - } + /** + * Flatten the email into a string. + * + * @param message the message headers, attachments, etc. + * @param body the message body. + * @return stringified email message. + * @throws MessagingException passed through. + */ + private String format(MimeMessage message, String body) + throws MessagingException { + StringBuilder text = new StringBuilder( + "Message not sent due to mail.server.disabled:\n"); + + Enumeration headers = message.getAllHeaderLines(); + while (headers.hasMoreElements()) { + text.append(headers.nextElement()).append('\n'); + } - if (!attachments.isEmpty()) { - text.append("\nAttachments:\n"); - for (FileAttachment f : attachments) { - text.append(f.name).append('\n'); - } - text.append('\n'); + if (!attachments.isEmpty()) { + text.append("\nAttachments:\n"); + for (FileAttachment f : attachments) { + text.append(f.name).append('\n'); } + text.append('\n'); + } - text.append('\n').append(fullMessage); + text.append('\n').append(body); + return text.toString(); + } - LOG.info(text.toString()); - } else { - Transport.send(message); - } + /** + * Get the formatted message for testing. + * + * @return the message flattened to a String. + * @throws MessagingException passed through. + */ + String getMessage() + throws MessagingException { + return format(message, body); } /** diff --git a/dspace-api/src/test/java/org/dspace/core/EmailTest.java b/dspace-api/src/test/java/org/dspace/core/EmailTest.java new file mode 100644 index 000000000000..61b8dd31e1f0 --- /dev/null +++ b/dspace-api/src/test/java/org/dspace/core/EmailTest.java @@ -0,0 +1,68 @@ +/** + * 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.core; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.not; + +import java.io.IOException; + +import jakarta.mail.MessagingException; +import org.dspace.AbstractDSpaceTest; +import org.dspace.services.ConfigurationService; +import org.junit.Before; +import org.junit.Test; + +/** + * Tests for email sender. + * + * @author mwood + */ +public class EmailTest + extends AbstractDSpaceTest { + private ConfigurationService config; + + @Before + public void init_test() { + config = kernelImpl.getConfigurationService(); + } + + @Test + public void testNullParameter() + throws MessagingException, IOException { + // Ensure that no mail goes out + config.setProperty("mail.server.disabled", "true"); + + Email email = new Email(); + email.setContent("null test", + "Testing: parameter value is /${params[0]}/."); + email.addArgument(null); + email.build(); + String message = email.getMessage(); + assertThat("Null message parameter should be transformed to empty", + message, not(containsString("(null)"))); + } + + @Test + public void testNotNullParameter() + throws MessagingException, IOException { + // Ensure that no mail goes out + config.setProperty("mail.server.disabled", "true"); + + Email email = new Email(); + email.setContent("not-null test", + "Testing: parameter value is /${params[0]}/."); + String testParam = "axolotl"; + email.addArgument(testParam); + email.build(); + String message = email.getMessage(); + assertThat("Null message parameter should be transformed to empty", + message, containsString(testParam)); + } +} From 5c034c6c02d046226587947ce1a40d0363c1fdcc Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 14 Apr 2025 23:03:29 +0000 Subject: [PATCH 535/979] Bump the apache-commons group with 2 updates Bumps the apache-commons group with 2 updates: commons-io:commons-io and org.apache.commons:commons-text. Updates `commons-io:commons-io` from 2.18.0 to 2.19.0 Updates `org.apache.commons:commons-text` from 1.13.0 to 1.13.1 --- updated-dependencies: - dependency-name: commons-io:commons-io dependency-version: 2.19.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: apache-commons - dependency-name: org.apache.commons:commons-text dependency-version: 1.13.1 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: apache-commons ... Signed-off-by: dependabot[bot] --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 62d6d2d0b55d..f7951285d56b 100644 --- a/pom.xml +++ b/pom.xml @@ -1505,7 +1505,7 @@ commons-io commons-io - 2.18.0 + 2.19.0 org.apache.commons @@ -1537,7 +1537,7 @@ org.apache.commons commons-text - 1.13.0 + 1.13.1 commons-validator From 24ed6f733dcf5211dbb5b9fc2f74fce4ac7db208 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 14 Apr 2025 23:11:20 +0000 Subject: [PATCH 536/979] Bump com.google.code.gson:gson from 2.12.1 to 2.13.0 Bumps [com.google.code.gson:gson](https://github.com/google/gson) from 2.12.1 to 2.13.0. - [Release notes](https://github.com/google/gson/releases) - [Changelog](https://github.com/google/gson/blob/main/CHANGELOG.md) - [Commits](https://github.com/google/gson/compare/gson-parent-2.12.1...gson-parent-2.13.0) --- updated-dependencies: - dependency-name: com.google.code.gson:gson dependency-version: 2.13.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 62d6d2d0b55d..4857766c0b9e 100644 --- a/pom.xml +++ b/pom.xml @@ -1358,7 +1358,7 @@ com.google.code.gson gson - 2.12.1 + 2.13.0 2.18.3 2.18.3 From 2cb34b095e99976d4ee745a783c3c9d0ccaa7642 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 21 Apr 2025 22:27:51 +0000 Subject: [PATCH 550/979] Bump the spring group with 13 updates Bumps the spring group with 13 updates: | Package | From | To | | --- | --- | --- | | [org.springframework:spring-orm](https://github.com/spring-projects/spring-framework) | `6.2.5` | `6.2.6` | | [org.springframework:spring-core](https://github.com/spring-projects/spring-framework) | `6.2.5` | `6.2.6` | | [org.springframework:spring-beans](https://github.com/spring-projects/spring-framework) | `6.2.5` | `6.2.6` | | [org.springframework:spring-aop](https://github.com/spring-projects/spring-framework) | `6.2.5` | `6.2.6` | | [org.springframework:spring-context](https://github.com/spring-projects/spring-framework) | `6.2.5` | `6.2.6` | | [org.springframework:spring-context-support](https://github.com/spring-projects/spring-framework) | `6.2.5` | `6.2.6` | | [org.springframework:spring-tx](https://github.com/spring-projects/spring-framework) | `6.2.5` | `6.2.6` | | [org.springframework:spring-jdbc](https://github.com/spring-projects/spring-framework) | `6.2.5` | `6.2.6` | | [org.springframework:spring-web](https://github.com/spring-projects/spring-framework) | `6.2.5` | `6.2.6` | | [org.springframework:spring-webmvc](https://github.com/spring-projects/spring-framework) | `6.2.5` | `6.2.6` | | [org.springframework:spring-expression](https://github.com/spring-projects/spring-framework) | `6.2.5` | `6.2.6` | | [org.springframework:spring-test](https://github.com/spring-projects/spring-framework) | `6.2.5` | `6.2.6` | | [org.springframework.security:spring-security-test](https://github.com/spring-projects/spring-security) | `6.4.4` | `6.4.5` | Updates `org.springframework:spring-orm` from 6.2.5 to 6.2.6 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.5...v6.2.6) Updates `org.springframework:spring-core` from 6.2.5 to 6.2.6 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.5...v6.2.6) Updates `org.springframework:spring-beans` from 6.2.5 to 6.2.6 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.5...v6.2.6) Updates `org.springframework:spring-aop` from 6.2.5 to 6.2.6 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.5...v6.2.6) Updates `org.springframework:spring-context` from 6.2.5 to 6.2.6 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.5...v6.2.6) Updates `org.springframework:spring-context-support` from 6.2.5 to 6.2.6 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.5...v6.2.6) Updates `org.springframework:spring-tx` from 6.2.5 to 6.2.6 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.5...v6.2.6) Updates `org.springframework:spring-jdbc` from 6.2.5 to 6.2.6 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.5...v6.2.6) Updates `org.springframework:spring-web` from 6.2.5 to 6.2.6 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.5...v6.2.6) Updates `org.springframework:spring-webmvc` from 6.2.5 to 6.2.6 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.5...v6.2.6) Updates `org.springframework:spring-expression` from 6.2.5 to 6.2.6 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.5...v6.2.6) Updates `org.springframework:spring-test` from 6.2.5 to 6.2.6 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.5...v6.2.6) Updates `org.springframework:spring-core` from 6.2.5 to 6.2.6 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.5...v6.2.6) Updates `org.springframework:spring-beans` from 6.2.5 to 6.2.6 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.5...v6.2.6) Updates `org.springframework:spring-aop` from 6.2.5 to 6.2.6 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.5...v6.2.6) Updates `org.springframework:spring-context` from 6.2.5 to 6.2.6 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.5...v6.2.6) Updates `org.springframework:spring-context-support` from 6.2.5 to 6.2.6 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.5...v6.2.6) Updates `org.springframework:spring-tx` from 6.2.5 to 6.2.6 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.5...v6.2.6) Updates `org.springframework:spring-jdbc` from 6.2.5 to 6.2.6 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.5...v6.2.6) Updates `org.springframework:spring-web` from 6.2.5 to 6.2.6 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.5...v6.2.6) Updates `org.springframework:spring-webmvc` from 6.2.5 to 6.2.6 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.5...v6.2.6) Updates `org.springframework:spring-expression` from 6.2.5 to 6.2.6 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.5...v6.2.6) Updates `org.springframework:spring-test` from 6.2.5 to 6.2.6 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.5...v6.2.6) Updates `org.springframework.security:spring-security-test` from 6.4.4 to 6.4.5 - [Release notes](https://github.com/spring-projects/spring-security/releases) - [Changelog](https://github.com/spring-projects/spring-security/blob/main/RELEASE.adoc) - [Commits](https://github.com/spring-projects/spring-security/compare/6.4.4...6.4.5) --- updated-dependencies: - dependency-name: org.springframework:spring-orm dependency-version: 6.2.6 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-core dependency-version: 6.2.6 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-beans dependency-version: 6.2.6 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-aop dependency-version: 6.2.6 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-context dependency-version: 6.2.6 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-context-support dependency-version: 6.2.6 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-tx dependency-version: 6.2.6 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-jdbc dependency-version: 6.2.6 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-web dependency-version: 6.2.6 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-webmvc dependency-version: 6.2.6 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-expression dependency-version: 6.2.6 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-test dependency-version: 6.2.6 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-core dependency-version: 6.2.6 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-beans dependency-version: 6.2.6 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-aop dependency-version: 6.2.6 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-context dependency-version: 6.2.6 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-context-support dependency-version: 6.2.6 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-tx dependency-version: 6.2.6 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-jdbc dependency-version: 6.2.6 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-web dependency-version: 6.2.6 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-webmvc dependency-version: 6.2.6 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-expression dependency-version: 6.2.6 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-test dependency-version: 6.2.6 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.security:spring-security-test dependency-version: 6.4.5 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring ... Signed-off-by: dependabot[bot] --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 5fbfddd7827d..3568c95996a4 100644 --- a/pom.xml +++ b/pom.xml @@ -19,9 +19,9 @@ 17 - 6.2.5 + 6.2.6 3.4.4 - 6.4.4 + 6.4.5 6.4.8.Final 8.0.1.Final 42.7.5 From 5d880bcf2da5391700ae9bc5fec6d84dc3314bfe Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 21 Apr 2025 23:03:07 +0000 Subject: [PATCH 551/979] Bump io.grpc:grpc-context from 1.71.0 to 1.72.0 Bumps [io.grpc:grpc-context](https://github.com/grpc/grpc-java) from 1.71.0 to 1.72.0. - [Release notes](https://github.com/grpc/grpc-java/releases) - [Commits](https://github.com/grpc/grpc-java/compare/v1.71.0...v1.72.0) --- updated-dependencies: - dependency-name: io.grpc:grpc-context dependency-version: 1.72.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index b5d4fdfb66d0..aff93df4a23c 100644 --- a/pom.xml +++ b/pom.xml @@ -1731,7 +1731,7 @@ io.grpc grpc-context - 1.71.0 + 1.72.0 com.google.http-client From 7db6f8922d21290053f24863a455554c96411ad0 Mon Sep 17 00:00:00 2001 From: Sascha Szott Date: Tue, 22 Apr 2025 15:55:52 +0200 Subject: [PATCH 552/979] fix log message (cherry picked from commit 4c9c79bf7bd4882be4157d5ae293a81184086c68) --- .../external/service/impl/ExternalDataServiceImpl.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/external/service/impl/ExternalDataServiceImpl.java b/dspace-api/src/main/java/org/dspace/external/service/impl/ExternalDataServiceImpl.java index 59cbe4f9d087..b241f5d782db 100644 --- a/dspace-api/src/main/java/org/dspace/external/service/impl/ExternalDataServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/external/service/impl/ExternalDataServiceImpl.java @@ -108,8 +108,9 @@ public WorkspaceItem createWorkspaceItemFromExternalDataObject(Context context, } log.info(LogHelper.getHeader(context, "create_item_from_externalDataObject", "Created item" + - "with id: " + item.getID() + " from source: " + externalDataObject.getSource() + " with identifier: " + - externalDataObject.getId())); + " with id: " + item.getID() + + " from source: " + externalDataObject.getSource() + + " with identifier: " + externalDataObject.getId())); try { List providers = suggestionService.getSuggestionProviders(); if (providers != null) { From 440bb648090ebe7992faf406c491b9168bb91009 Mon Sep 17 00:00:00 2001 From: Agustina Martinez Date: Thu, 20 Mar 2025 12:56:11 +0000 Subject: [PATCH 553/979] Update dim.xsl Added template to correctly parse elements under "others" metadata element (cherry picked from commit ac7da6a477cb7bb42b41560a751223efc870dafa) --- .../config/crosswalks/oai/metadataFormats/dim.xsl | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/dspace/config/crosswalks/oai/metadataFormats/dim.xsl b/dspace/config/crosswalks/oai/metadataFormats/dim.xsl index ea0aad182092..b659fef931a8 100644 --- a/dspace/config/crosswalks/oai/metadataFormats/dim.xsl +++ b/dspace/config/crosswalks/oai/metadataFormats/dim.xsl @@ -36,6 +36,18 @@ + + + + + + + + + + + + @@ -62,6 +74,7 @@ + From 5313b9673d0cd5b089abef298662d5d1f7d6f7be Mon Sep 17 00:00:00 2001 From: Agustina Martinez Date: Thu, 20 Mar 2025 12:56:11 +0000 Subject: [PATCH 554/979] Update dim.xsl Added template to correctly parse elements under "others" metadata element (cherry picked from commit ac7da6a477cb7bb42b41560a751223efc870dafa) --- .../config/crosswalks/oai/metadataFormats/dim.xsl | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/dspace/config/crosswalks/oai/metadataFormats/dim.xsl b/dspace/config/crosswalks/oai/metadataFormats/dim.xsl index ea0aad182092..b659fef931a8 100644 --- a/dspace/config/crosswalks/oai/metadataFormats/dim.xsl +++ b/dspace/config/crosswalks/oai/metadataFormats/dim.xsl @@ -36,6 +36,18 @@ + + + + + + + + + + + + @@ -62,6 +74,7 @@ + From 35b48592421574d78a84953109565c0ce492f3bb Mon Sep 17 00:00:00 2001 From: Sascha Szott Date: Tue, 1 Apr 2025 19:49:28 +0200 Subject: [PATCH 555/979] add method getMaxNumOfItemsPerRequest --- .../java/org/dspace/app/util/service/OpenSearchService.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/dspace-api/src/main/java/org/dspace/app/util/service/OpenSearchService.java b/dspace-api/src/main/java/org/dspace/app/util/service/OpenSearchService.java index 03f41e535c53..08900f8fff9c 100644 --- a/dspace-api/src/main/java/org/dspace/app/util/service/OpenSearchService.java +++ b/dspace-api/src/main/java/org/dspace/app/util/service/OpenSearchService.java @@ -117,4 +117,10 @@ public Document getResultsDoc(Context context, String format, String query, int public DSpaceObject resolveScope(Context context, String scope) throws SQLException; + /** + * Retrieves the maximum number of items that can be included in a single opensearch request. + * + * @return the maximum number of items allowed per request + */ + int getMaxNumOfItemsPerRequest(); } From a01405cd1816725c305c5049ffc9f24a275138c1 Mon Sep 17 00:00:00 2001 From: Sascha Szott Date: Tue, 1 Apr 2025 19:50:48 +0200 Subject: [PATCH 556/979] implement method getMaxNumOfItemsPerRequest --- .../java/org/dspace/app/util/OpenSearchServiceImpl.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/dspace-api/src/main/java/org/dspace/app/util/OpenSearchServiceImpl.java b/dspace-api/src/main/java/org/dspace/app/util/OpenSearchServiceImpl.java index bff741b5ca42..2075ef7a3816 100644 --- a/dspace-api/src/main/java/org/dspace/app/util/OpenSearchServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/app/util/OpenSearchServiceImpl.java @@ -101,6 +101,14 @@ protected String getBaseSearchUIURL() { configurationService.getProperty("websvc.opensearch.uicontext"); } + /** + * Get base search UI URL (websvc.opensearch.max_num_of_items_per_request) + */ + public int getMaxNumOfItemsPerRequest() { + return configurationService.getIntProperty( + "websvc.opensearch.max_num_of_items_per_request", 100); + } + @Override public String getContentType(String format) { return "html".equals(format) ? "text/html" : From fe614d5fc5827338058aa43f184c495f9dc1439e Mon Sep 17 00:00:00 2001 From: Sascha Szott Date: Tue, 1 Apr 2025 19:51:28 +0200 Subject: [PATCH 557/979] add configuration key websvc.opensearch.max_num_of_items_per_request --- dspace/config/dspace.cfg | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/dspace/config/dspace.cfg b/dspace/config/dspace.cfg index c9ae5d521d90..2abe436387dd 100644 --- a/dspace/config/dspace.cfg +++ b/dspace/config/dspace.cfg @@ -1376,7 +1376,8 @@ websvc.opensearch.tags = IR DSpace # result formats offered - use 1 or more comma-separated from: html,atom,rss # html uses the normal search module websvc.opensearch.formats = html,atom,rss - +# maximum number of item per request +websvc.opensearch.max_num_of_items_per_request = 100 #### Content Inline Disposition Threshold #### # From 6ad43a508d775c470c44f8af9760d72d5f862c8a Mon Sep 17 00:00:00 2001 From: Sascha Szott Date: Tue, 1 Apr 2025 19:59:48 +0200 Subject: [PATCH 558/979] restrict maximum value of URL parameter rpp --- .../dspace/app/rest/OpenSearchController.java | 39 ++++++++----------- 1 file changed, 17 insertions(+), 22 deletions(-) diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/OpenSearchController.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/OpenSearchController.java index baf45c14b6eb..85ec441e20c1 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/OpenSearchController.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/OpenSearchController.java @@ -21,17 +21,13 @@ import jakarta.servlet.ServletException; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; +import org.apache.commons.lang3.StringUtils; import org.apache.logging.log4j.Logger; import org.dspace.app.rest.utils.ContextUtil; import org.dspace.app.rest.utils.ScopeResolver; import org.dspace.app.util.SyndicationFeed; import org.dspace.app.util.factory.UtilServiceFactory; import org.dspace.app.util.service.OpenSearchService; -import org.dspace.authorize.factory.AuthorizeServiceFactory; -import org.dspace.authorize.service.AuthorizeService; -import org.dspace.content.factory.ContentServiceFactory; -import org.dspace.content.service.CollectionService; -import org.dspace.content.service.CommunityService; import org.dspace.core.Context; import org.dspace.core.LogHelper; import org.dspace.core.Utils; @@ -50,7 +46,6 @@ import org.dspace.discovery.indexobject.IndexableItem; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; -import org.springframework.ui.Model; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; @@ -67,12 +62,9 @@ public class OpenSearchController { private static final Logger log = org.apache.logging.log4j.LogManager.getLogger(); - private static final String errorpath = "/error"; + private List searchIndices = null; - private CommunityService communityService; - private CollectionService collectionService; - private AuthorizeService authorizeService; private OpenSearchService openSearchService; @Autowired @@ -99,22 +91,28 @@ public void search(HttpServletRequest request, @RequestParam(name = "format", required = false) String format, @RequestParam(name = "sort", required = false) String sort, @RequestParam(name = "sort_direction", required = false) String sortDirection, - @RequestParam(name = "scope", required = false) String dsoObject, - Model model) throws IOException, ServletException { + @RequestParam(name = "scope", required = false) String dsoObject) + throws IOException, ServletException { context = ContextUtil.obtainContext(request); - if (start == null) { - start = 0; - } - if (count == null) { - count = -1; - } + if (openSearchService == null) { openSearchService = UtilServiceFactory.getInstance().getOpenSearchService(); } + if (openSearchService.isEnabled()) { init(); + + if (start == null) { + start = 0; + } + + if (count == null) { + count = -1; + } + count = Math.min(count, openSearchService.getMaxNumOfItemsPerRequest()); + // get enough request parameters to decide on action to take - if (format == null || "".equals(format)) { + if (StringUtils.isEmpty(format)) { // default to atom format = "atom"; } @@ -266,9 +264,6 @@ private void init() { searchIndices.add(sFilter.getIndexFieldName()); } } - communityService = ContentServiceFactory.getInstance().getCommunityService(); - collectionService = ContentServiceFactory.getInstance().getCollectionService(); - authorizeService = AuthorizeServiceFactory.getInstance().getAuthorizeService(); } public void setOpenSearchService(OpenSearchService oSS) { From 045a5c0b0eff8bc52f52d452e98fc9b22a8e45d4 Mon Sep 17 00:00:00 2001 From: Sascha Szott Date: Tue, 1 Apr 2025 19:49:28 +0200 Subject: [PATCH 559/979] add method getMaxNumOfItemsPerRequest --- .../java/org/dspace/app/util/service/OpenSearchService.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/dspace-api/src/main/java/org/dspace/app/util/service/OpenSearchService.java b/dspace-api/src/main/java/org/dspace/app/util/service/OpenSearchService.java index 03f41e535c53..08900f8fff9c 100644 --- a/dspace-api/src/main/java/org/dspace/app/util/service/OpenSearchService.java +++ b/dspace-api/src/main/java/org/dspace/app/util/service/OpenSearchService.java @@ -117,4 +117,10 @@ public Document getResultsDoc(Context context, String format, String query, int public DSpaceObject resolveScope(Context context, String scope) throws SQLException; + /** + * Retrieves the maximum number of items that can be included in a single opensearch request. + * + * @return the maximum number of items allowed per request + */ + int getMaxNumOfItemsPerRequest(); } From 869d122eaca9d052693bb13fe2faf061b6836026 Mon Sep 17 00:00:00 2001 From: Sascha Szott Date: Tue, 1 Apr 2025 19:50:48 +0200 Subject: [PATCH 560/979] implement method getMaxNumOfItemsPerRequest --- .../java/org/dspace/app/util/OpenSearchServiceImpl.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/dspace-api/src/main/java/org/dspace/app/util/OpenSearchServiceImpl.java b/dspace-api/src/main/java/org/dspace/app/util/OpenSearchServiceImpl.java index bff741b5ca42..2075ef7a3816 100644 --- a/dspace-api/src/main/java/org/dspace/app/util/OpenSearchServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/app/util/OpenSearchServiceImpl.java @@ -101,6 +101,14 @@ protected String getBaseSearchUIURL() { configurationService.getProperty("websvc.opensearch.uicontext"); } + /** + * Get base search UI URL (websvc.opensearch.max_num_of_items_per_request) + */ + public int getMaxNumOfItemsPerRequest() { + return configurationService.getIntProperty( + "websvc.opensearch.max_num_of_items_per_request", 100); + } + @Override public String getContentType(String format) { return "html".equals(format) ? "text/html" : From ca3b2de1a83a3ee654c054cb70666d7c15f8bee4 Mon Sep 17 00:00:00 2001 From: Sascha Szott Date: Tue, 1 Apr 2025 19:51:28 +0200 Subject: [PATCH 561/979] add configuration key websvc.opensearch.max_num_of_items_per_request --- dspace/config/dspace.cfg | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/dspace/config/dspace.cfg b/dspace/config/dspace.cfg index 91057ef1ec68..94b731cd3488 100644 --- a/dspace/config/dspace.cfg +++ b/dspace/config/dspace.cfg @@ -1362,7 +1362,8 @@ websvc.opensearch.tags = IR DSpace # result formats offered - use 1 or more comma-separated from: html,atom,rss # html uses the normal search module websvc.opensearch.formats = html,atom,rss - +# maximum number of item per request +websvc.opensearch.max_num_of_items_per_request = 100 #### Content Inline Disposition Threshold #### # From 1a619b28336030e01211497cd8e466d4f3beb92c Mon Sep 17 00:00:00 2001 From: Sascha Szott Date: Tue, 1 Apr 2025 19:59:48 +0200 Subject: [PATCH 562/979] restrict maximum value of URL parameter rpp --- .../dspace/app/rest/OpenSearchController.java | 39 ++++++++----------- 1 file changed, 17 insertions(+), 22 deletions(-) diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/OpenSearchController.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/OpenSearchController.java index fef6269e3eee..d4e0036449a4 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/OpenSearchController.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/OpenSearchController.java @@ -21,17 +21,13 @@ import javax.xml.transform.dom.DOMSource; import javax.xml.transform.stream.StreamResult; +import org.apache.commons.lang3.StringUtils; import org.apache.logging.log4j.Logger; import org.dspace.app.rest.utils.ContextUtil; import org.dspace.app.rest.utils.ScopeResolver; import org.dspace.app.util.SyndicationFeed; import org.dspace.app.util.factory.UtilServiceFactory; import org.dspace.app.util.service.OpenSearchService; -import org.dspace.authorize.factory.AuthorizeServiceFactory; -import org.dspace.authorize.service.AuthorizeService; -import org.dspace.content.factory.ContentServiceFactory; -import org.dspace.content.service.CollectionService; -import org.dspace.content.service.CommunityService; import org.dspace.core.Context; import org.dspace.core.LogHelper; import org.dspace.core.Utils; @@ -50,7 +46,6 @@ import org.dspace.discovery.indexobject.IndexableItem; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; -import org.springframework.ui.Model; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; @@ -67,12 +62,9 @@ public class OpenSearchController { private static final Logger log = org.apache.logging.log4j.LogManager.getLogger(); - private static final String errorpath = "/error"; + private List searchIndices = null; - private CommunityService communityService; - private CollectionService collectionService; - private AuthorizeService authorizeService; private OpenSearchService openSearchService; @Autowired @@ -99,22 +91,28 @@ public void search(HttpServletRequest request, @RequestParam(name = "format", required = false) String format, @RequestParam(name = "sort", required = false) String sort, @RequestParam(name = "sort_direction", required = false) String sortDirection, - @RequestParam(name = "scope", required = false) String dsoObject, - Model model) throws IOException, ServletException { + @RequestParam(name = "scope", required = false) String dsoObject) + throws IOException, ServletException { context = ContextUtil.obtainContext(request); - if (start == null) { - start = 0; - } - if (count == null) { - count = -1; - } + if (openSearchService == null) { openSearchService = UtilServiceFactory.getInstance().getOpenSearchService(); } + if (openSearchService.isEnabled()) { init(); + + if (start == null) { + start = 0; + } + + if (count == null) { + count = -1; + } + count = Math.min(count, openSearchService.getMaxNumOfItemsPerRequest()); + // get enough request parameters to decide on action to take - if (format == null || "".equals(format)) { + if (StringUtils.isEmpty(format)) { // default to atom format = "atom"; } @@ -266,9 +264,6 @@ private void init() { searchIndices.add(sFilter.getIndexFieldName()); } } - communityService = ContentServiceFactory.getInstance().getCommunityService(); - collectionService = ContentServiceFactory.getInstance().getCollectionService(); - authorizeService = AuthorizeServiceFactory.getInstance().getAuthorizeService(); } public void setOpenSearchService(OpenSearchService oSS) { From ac7dfc562fd90d161c7fa0f99cd54b00b16f64b3 Mon Sep 17 00:00:00 2001 From: Jesiel Viana Date: Tue, 22 Apr 2025 22:12:00 -0300 Subject: [PATCH 563/979] fix: import public email from ORCID person (cherry picked from commit 82ca80cd6b5d340f5d3d2e042803c2fc43d8a973) --- .../impl/OrcidV3AuthorDataProvider.java | 22 +- .../impl/OrcidV3AuthorDataProviderTest.java | 231 ++++++++++++++++++ .../provider/impl/orcid-person/person1.xml | 51 ++++ .../provider/impl/orcid-person/person2.xml | 64 +++++ .../provider/impl/orcid-person/person3.xml | 35 +++ .../provider/impl/orcid-person/search.xml | 25 ++ .../provider/orcid-v3-author/person1.xml | 51 ++++ .../provider/orcid-v3-author/person2.xml | 64 +++++ .../provider/orcid-v3-author/person3.xml | 35 +++ .../provider/orcid-v3-author/search.xml | 25 ++ 10 files changed, 596 insertions(+), 7 deletions(-) create mode 100644 dspace-api/src/test/java/org/dspace/external/provider/impl/OrcidV3AuthorDataProviderTest.java create mode 100644 dspace-api/src/test/resources/org/dspace/external/provider/impl/orcid-person/person1.xml create mode 100644 dspace-api/src/test/resources/org/dspace/external/provider/impl/orcid-person/person2.xml create mode 100644 dspace-api/src/test/resources/org/dspace/external/provider/impl/orcid-person/person3.xml create mode 100644 dspace-api/src/test/resources/org/dspace/external/provider/impl/orcid-person/search.xml create mode 100644 dspace-api/src/test/resources/org/dspace/external/provider/orcid-v3-author/person1.xml create mode 100644 dspace-api/src/test/resources/org/dspace/external/provider/orcid-v3-author/person2.xml create mode 100644 dspace-api/src/test/resources/org/dspace/external/provider/orcid-v3-author/person3.xml create mode 100644 dspace-api/src/test/resources/org/dspace/external/provider/orcid-v3-author/search.xml diff --git a/dspace-api/src/main/java/org/dspace/external/provider/impl/OrcidV3AuthorDataProvider.java b/dspace-api/src/main/java/org/dspace/external/provider/impl/OrcidV3AuthorDataProvider.java index c7e41171a5bb..dfbd07a83a02 100644 --- a/dspace-api/src/main/java/org/dspace/external/provider/impl/OrcidV3AuthorDataProvider.java +++ b/dspace-api/src/main/java/org/dspace/external/provider/impl/OrcidV3AuthorDataProvider.java @@ -27,6 +27,7 @@ import org.dspace.external.provider.orcid.xml.XMLtoBio; import org.dspace.orcid.model.factory.OrcidFactoryUtils; import org.orcid.jaxb.model.v3.release.common.OrcidIdentifier; +import org.orcid.jaxb.model.v3.release.record.Email; import org.orcid.jaxb.model.v3.release.record.Person; import org.orcid.jaxb.model.v3.release.search.Result; import org.springframework.beans.factory.annotation.Autowired; @@ -114,13 +115,20 @@ protected ExternalDataObject convertToExternalDataObject(Person person) { if (person.getName().getFamilyName() != null) { lastName = person.getName().getFamilyName().getContent(); externalDataObject.addMetadata(new MetadataValueDTO("person", "familyName", null, null, - lastName)); + lastName)); } if (person.getName().getGivenNames() != null) { firstName = person.getName().getGivenNames().getContent(); externalDataObject.addMetadata(new MetadataValueDTO("person", "givenName", null, null, - firstName)); - + firstName)); + } + if (person.getEmails().getEmails() != null && !person.getEmails().getEmails().isEmpty()) { + Email email = person.getEmails().getEmails().get(0); + if (person.getEmails().getEmails().size() > 1) { + email = person.getEmails().getEmails().stream().filter(Email::isPrimary).findFirst().orElse(email); + } + externalDataObject.addMetadata(new MetadataValueDTO("person", "email", null, + null, email.getEmail())); } externalDataObject.setId(person.getName().getPath()); externalDataObject @@ -128,7 +136,7 @@ protected ExternalDataObject convertToExternalDataObject(Person person) { new MetadataValueDTO("person", "identifier", "orcid", null, person.getName().getPath())); externalDataObject .addMetadata(new MetadataValueDTO("dc", "identifier", "uri", null, - orcidUrl + "/" + person.getName().getPath())); + orcidUrl + "/" + person.getName().getPath())); if (!StringUtils.isBlank(lastName) && !StringUtils.isBlank(firstName)) { externalDataObject.setDisplayValue(lastName + ", " + firstName); externalDataObject.setValue(lastName + ", " + firstName); @@ -139,8 +147,8 @@ protected ExternalDataObject convertToExternalDataObject(Person person) { externalDataObject.setDisplayValue(firstName); externalDataObject.setValue(firstName); } - } else if (person.getPath() != null ) { - externalDataObject.setId(StringUtils.substringBetween(person.getPath(),"/","/person")); + } else if (person.getPath() != null) { + externalDataObject.setId(StringUtils.substringBetween(person.getPath(), "/", "/person")); } return externalDataObject; } @@ -204,7 +212,7 @@ public List searchExternalDataObjects(String query, int star for (Result result : results) { OrcidIdentifier orcidIdentifier = result.getOrcidIdentifier(); if (orcidIdentifier != null) { - log.debug("Found OrcidId=" + orcidIdentifier.toString()); + log.debug("Found OrcidId=" + orcidIdentifier.getPath()); String orcid = orcidIdentifier.getPath(); Person bio = getBio(orcid); if (bio != null) { diff --git a/dspace-api/src/test/java/org/dspace/external/provider/impl/OrcidV3AuthorDataProviderTest.java b/dspace-api/src/test/java/org/dspace/external/provider/impl/OrcidV3AuthorDataProviderTest.java new file mode 100644 index 000000000000..34b3a6838d4e --- /dev/null +++ b/dspace-api/src/test/java/org/dspace/external/provider/impl/OrcidV3AuthorDataProviderTest.java @@ -0,0 +1,231 @@ +/** + * The contents of this file are subject to the license and copyright + * detailed in the LICENSE and NOTICE files at the root of the source + * tree and available online at + * + * http://www.dspace.org/license/ + */ +package org.dspace.external.provider.impl; + +import org.dspace.AbstractDSpaceTest; +import org.dspace.external.OrcidRestConnector; +import org.dspace.external.model.ExternalDataObject; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import java.io.InputStream; +import java.util.List; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.*; +import static org.junit.Assert.assertThrows; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.*; + +/** + * Unit tests for {@link OrcidV3AuthorDataProvider}. + * + * @author Jesiel Viana (jesielviana at proton.me) + * + */ +public class OrcidV3AuthorDataProviderTest extends AbstractDSpaceTest { + + private static final String SEARCH_XML_PATH = "org/dspace/external/provider/orcid-v3-author/search.xml"; + private static final String PERSON1_XML_PATH = "org/dspace/external/provider/orcid-v3-author/person1.xml"; + private static final String PERSON2_XML_PATH = "org/dspace/external/provider/orcid-v3-author/person2.xml"; + private static final String PERSON3_XML_PATH = "org/dspace/external/provider/orcid-v3-author/person3.xml"; + + public static final String ORCID_SEARCH_QUERY = "search?q=0000-0000-0000-0000"; + + private OrcidV3AuthorDataProvider dataProvider; + + @Before + public void setup() throws Exception { + dataProvider = new OrcidV3AuthorDataProvider(); + + OrcidRestConnector mockRestConnector = mock(OrcidRestConnector.class); + + dataProvider.setOrcidRestConnector(mockRestConnector); + dataProvider.setSourceIdentifier("orcid"); + dataProvider.setOrcidUrl("https://orcid.org"); + + dataProvider.setClientId("client-id"); + dataProvider.setClientSecret("client-secret"); + dataProvider.setOAUTHUrl("https://orcid.org/oauth"); + + InputStream searchXmlStream = getClass().getClassLoader().getResourceAsStream(SEARCH_XML_PATH); + InputStream person1XmlStream = getClass().getClassLoader().getResourceAsStream(PERSON1_XML_PATH); + InputStream person2XmlStream = getClass().getClassLoader().getResourceAsStream(PERSON2_XML_PATH); + InputStream person3XmlStream = getClass().getClassLoader().getResourceAsStream(PERSON3_XML_PATH); + + when(mockRestConnector.get("search?q=search%3Fq%3D0000-0000-0000-0000&start=0&rows=10",null )).thenReturn(searchXmlStream); + when(mockRestConnector.get("0000-0000-0000-0001/person",null )).thenReturn(person1XmlStream); + when(mockRestConnector.get("0000-0000-0000-0002/person",null )).thenReturn(person2XmlStream); + when(mockRestConnector.get("0000-0000-0000-0003/person",null )).thenReturn(person3XmlStream); + + } + + @Test + public void testGetExternalDataObjectSizeIsCorrect() { + List optional = dataProvider.searchExternalDataObjects(ORCID_SEARCH_QUERY, 0, 10); + assertThat(optional, hasSize(3)); + } + + @Test + public void testGetExternalDataObjectGetPersonWithAllFieldsPopulated() { + List optional = dataProvider.searchExternalDataObjects(ORCID_SEARCH_QUERY, 0, 10); + + assertThat(optional, hasSize(3)); + + ExternalDataObject externalDataObject1 = optional.get(0); + + // Basic field assertions + assertThat(externalDataObject1.getId(), equalTo("0000-0000-0000-0001")); + assertThat(externalDataObject1.getValue(), equalTo("FamilyName1, GivenNames1")); + assertThat(externalDataObject1.getSource(), equalTo("orcid")); + assertThat(externalDataObject1.getDisplayValue(), equalTo("FamilyName1, GivenNames1")); + + // Metadata assertions + assertThat(externalDataObject1.getMetadata(), hasItem( + allOf( + hasProperty("schema", equalTo("person")), + hasProperty("element", equalTo("familyName")), + hasProperty("value", equalTo("FamilyName1")) + ) + )); + assertThat(externalDataObject1.getMetadata(), hasItem( + allOf( + hasProperty("schema", equalTo("person")), + hasProperty("element", equalTo("givenName")), + hasProperty("value", equalTo("GivenNames1")) + ) + )); + assertThat(externalDataObject1.getMetadata(), hasItem( + allOf( + hasProperty("schema", equalTo("person")), + hasProperty("element", equalTo("email")), + hasProperty("value", equalTo("person1@email.com")) + ) + )); + assertThat(externalDataObject1.getMetadata(), hasItem( + allOf( + hasProperty("schema", equalTo("person")), + hasProperty("element", equalTo("identifier")), + hasProperty("qualifier", equalTo("orcid")), + hasProperty("value", equalTo("0000-0000-0000-0001")) + ) + )); + assertThat(externalDataObject1.getMetadata(), hasItem( + allOf( + hasProperty("schema", equalTo("dc")), + hasProperty("element", equalTo("identifier")), + hasProperty("qualifier", equalTo("uri")), + hasProperty("value", equalTo("https://orcid.org/0000-0000-0000-0001")) + ) + )); + } + + @Test + public void testGetExternalDataObjectGetPrimaryEmailFromPersonWithTwoEmails() { + List optional = dataProvider.searchExternalDataObjects(ORCID_SEARCH_QUERY, 0, 10); + + assertThat(optional, hasSize(3)); + + ExternalDataObject externalDataObject2 = optional.get(1); // Test person2 (with two emails) + + // Basic field assertions + assertThat(externalDataObject2.getId(), equalTo("0000-0000-0000-0002")); + assertThat(externalDataObject2.getValue(), equalTo("FamilyName2, GivenNames2")); + assertThat(externalDataObject2.getSource(), equalTo("orcid")); + assertThat(externalDataObject2.getDisplayValue(), equalTo("FamilyName2, GivenNames2")); + + // Metadata assertions + assertThat(externalDataObject2.getMetadata(), hasItem( + allOf( + hasProperty("schema", equalTo("person")), + hasProperty("element", equalTo("familyName")), + hasProperty("value", equalTo("FamilyName2")) + ) + )); + assertThat(externalDataObject2.getMetadata(), hasItem( + allOf( + hasProperty("schema", equalTo("person")), + hasProperty("element", equalTo("givenName")), + hasProperty("value", equalTo("GivenNames2")) + ) + )); + assertThat(externalDataObject2.getMetadata(), hasItem( + allOf( + hasProperty("schema", equalTo("person")), + hasProperty("element", equalTo("email")), + hasProperty("value", equalTo("person2primary@email.com")) // Primary email + ) + )); + assertThat(externalDataObject2.getMetadata(), hasItem( + allOf( + hasProperty("schema", equalTo("person")), + hasProperty("element", equalTo("identifier")), + hasProperty("qualifier", equalTo("orcid")), + hasProperty("value", equalTo("0000-0000-0000-0002")) + ) + )); + assertThat(externalDataObject2.getMetadata(), hasItem( + allOf( + hasProperty("schema", equalTo("dc")), + hasProperty("element", equalTo("identifier")), + hasProperty("qualifier", equalTo("uri")), + hasProperty("value", equalTo("https://orcid.org/0000-0000-0000-0002")) + ) + )); + } + + + @Test + public void testGetExternalDataObjectGetPersonOnlyWithNameFilled() { + List optional = dataProvider.searchExternalDataObjects(ORCID_SEARCH_QUERY, 0, 10); + + assertThat(optional, hasSize(3)); + + ExternalDataObject externalDataObject2 = optional.get(2); // Test person2 (with two emails) + + // Basic field assertions + assertThat(externalDataObject2.getId(), equalTo("0000-0000-0000-0003")); + assertThat(externalDataObject2.getValue(), equalTo("FamilyName3, GivenNames3")); + assertThat(externalDataObject2.getSource(), equalTo("orcid")); + assertThat(externalDataObject2.getDisplayValue(), equalTo("FamilyName3, GivenNames3")); + + // Metadata assertions + assertThat(externalDataObject2.getMetadata(), hasItem( + allOf( + hasProperty("schema", equalTo("person")), + hasProperty("element", equalTo("familyName")), + hasProperty("value", equalTo("FamilyName3")) + ) + )); + assertThat(externalDataObject2.getMetadata(), hasItem( + allOf( + hasProperty("schema", equalTo("person")), + hasProperty("element", equalTo("givenName")), + hasProperty("value", equalTo("GivenNames3")) + ) + )); + assertThat(externalDataObject2.getMetadata(), hasItem( + allOf( + hasProperty("schema", equalTo("person")), + hasProperty("element", equalTo("identifier")), + hasProperty("qualifier", equalTo("orcid")), + hasProperty("value", equalTo("0000-0000-0000-0003")) + ) + )); + assertThat(externalDataObject2.getMetadata(), hasItem( + allOf( + hasProperty("schema", equalTo("dc")), + hasProperty("element", equalTo("identifier")), + hasProperty("qualifier", equalTo("uri")), + hasProperty("value", equalTo("https://orcid.org/0000-0000-0000-0003")) + ) + )); + } +} diff --git a/dspace-api/src/test/resources/org/dspace/external/provider/impl/orcid-person/person1.xml b/dspace-api/src/test/resources/org/dspace/external/provider/impl/orcid-person/person1.xml new file mode 100644 index 000000000000..64e4b292b92b --- /dev/null +++ b/dspace-api/src/test/resources/org/dspace/external/provider/impl/orcid-person/person1.xml @@ -0,0 +1,51 @@ + + + 2025-04-21T22:28:18.862Z + + 2025-04-11T15:41:21.340Z + 2025-04-11T15:41:21.340Z + GivenNames1 + FamilyName1 + + + + + 2025-04-21T22:28:18.862Z + + 2025-04-21T22:23:14.698Z + 2025-04-21T22:28:18.862Z + + + https://sandbox.orcid.org/0000-0000-0000-0001 + 0000-0000-0000-0001 + sandbox.orcid.org + + GivenNames1 FamilyName1 + + person1@email.com + + + + + + diff --git a/dspace-api/src/test/resources/org/dspace/external/provider/impl/orcid-person/person2.xml b/dspace-api/src/test/resources/org/dspace/external/provider/impl/orcid-person/person2.xml new file mode 100644 index 000000000000..c91b0207247d --- /dev/null +++ b/dspace-api/src/test/resources/org/dspace/external/provider/impl/orcid-person/person2.xml @@ -0,0 +1,64 @@ + + + 2025-04-21T22:28:18.862Z + + 2025-04-11T15:41:21.340Z + 2025-04-11T15:41:21.340Z + GivenNames2 + FamilyName2 + + + + + 2025-04-21T22:28:18.862Z + + 2025-04-21T22:23:14.698Z + 2025-04-21T22:28:18.862Z + + + https://sandbox.orcid.org/0000-0000-0000-0002 + 0000-0000-0000-0002 + sandbox.orcid.org + + GivenNames2 FamilyName2 + + person2@email.com + + + 2025-04-21T16:42:54.961Z + 2025-04-21T16:48:32.642Z + + + https://sandbox.orcid.org/0000-0000-0000-0001 + 0000-0000-0000-0001 + sandbox.orcid.org + + GivenNames1 FamilyName1 + + person2primary@email.com + + + + + + diff --git a/dspace-api/src/test/resources/org/dspace/external/provider/impl/orcid-person/person3.xml b/dspace-api/src/test/resources/org/dspace/external/provider/impl/orcid-person/person3.xml new file mode 100644 index 000000000000..b24ed9d3547a --- /dev/null +++ b/dspace-api/src/test/resources/org/dspace/external/provider/impl/orcid-person/person3.xml @@ -0,0 +1,35 @@ + + + + 2024-06-11T20:01:28.538Z + 2024-06-11T20:01:28.538Z + GivenNames3 + FamilyName3 + + + + + + + + diff --git a/dspace-api/src/test/resources/org/dspace/external/provider/impl/orcid-person/search.xml b/dspace-api/src/test/resources/org/dspace/external/provider/impl/orcid-person/search.xml new file mode 100644 index 000000000000..98ec721be9b8 --- /dev/null +++ b/dspace-api/src/test/resources/org/dspace/external/provider/impl/orcid-person/search.xml @@ -0,0 +1,25 @@ + + + + + https://sandbox.orcid.org/0000-0000-0000-0001 + 0000-0000-0000-0001 + sandbox.orcid.org + + + + + https://sandbox.orcid.org/0000-0000-0000-0002 + 0000-0000-0000-0002 + sandbox.orcid.org + + + + + https://sandbox.orcid.org/0000-0000-0000-0003 + 0000-0000-0000-0003 + sandbox.orcid.org + + + diff --git a/dspace-api/src/test/resources/org/dspace/external/provider/orcid-v3-author/person1.xml b/dspace-api/src/test/resources/org/dspace/external/provider/orcid-v3-author/person1.xml new file mode 100644 index 000000000000..64e4b292b92b --- /dev/null +++ b/dspace-api/src/test/resources/org/dspace/external/provider/orcid-v3-author/person1.xml @@ -0,0 +1,51 @@ + + + 2025-04-21T22:28:18.862Z + + 2025-04-11T15:41:21.340Z + 2025-04-11T15:41:21.340Z + GivenNames1 + FamilyName1 + + + + + 2025-04-21T22:28:18.862Z + + 2025-04-21T22:23:14.698Z + 2025-04-21T22:28:18.862Z + + + https://sandbox.orcid.org/0000-0000-0000-0001 + 0000-0000-0000-0001 + sandbox.orcid.org + + GivenNames1 FamilyName1 + + person1@email.com + + + + + + diff --git a/dspace-api/src/test/resources/org/dspace/external/provider/orcid-v3-author/person2.xml b/dspace-api/src/test/resources/org/dspace/external/provider/orcid-v3-author/person2.xml new file mode 100644 index 000000000000..c91b0207247d --- /dev/null +++ b/dspace-api/src/test/resources/org/dspace/external/provider/orcid-v3-author/person2.xml @@ -0,0 +1,64 @@ + + + 2025-04-21T22:28:18.862Z + + 2025-04-11T15:41:21.340Z + 2025-04-11T15:41:21.340Z + GivenNames2 + FamilyName2 + + + + + 2025-04-21T22:28:18.862Z + + 2025-04-21T22:23:14.698Z + 2025-04-21T22:28:18.862Z + + + https://sandbox.orcid.org/0000-0000-0000-0002 + 0000-0000-0000-0002 + sandbox.orcid.org + + GivenNames2 FamilyName2 + + person2@email.com + + + 2025-04-21T16:42:54.961Z + 2025-04-21T16:48:32.642Z + + + https://sandbox.orcid.org/0000-0000-0000-0001 + 0000-0000-0000-0001 + sandbox.orcid.org + + GivenNames1 FamilyName1 + + person2primary@email.com + + + + + + diff --git a/dspace-api/src/test/resources/org/dspace/external/provider/orcid-v3-author/person3.xml b/dspace-api/src/test/resources/org/dspace/external/provider/orcid-v3-author/person3.xml new file mode 100644 index 000000000000..b24ed9d3547a --- /dev/null +++ b/dspace-api/src/test/resources/org/dspace/external/provider/orcid-v3-author/person3.xml @@ -0,0 +1,35 @@ + + + + 2024-06-11T20:01:28.538Z + 2024-06-11T20:01:28.538Z + GivenNames3 + FamilyName3 + + + + + + + + diff --git a/dspace-api/src/test/resources/org/dspace/external/provider/orcid-v3-author/search.xml b/dspace-api/src/test/resources/org/dspace/external/provider/orcid-v3-author/search.xml new file mode 100644 index 000000000000..98ec721be9b8 --- /dev/null +++ b/dspace-api/src/test/resources/org/dspace/external/provider/orcid-v3-author/search.xml @@ -0,0 +1,25 @@ + + + + + https://sandbox.orcid.org/0000-0000-0000-0001 + 0000-0000-0000-0001 + sandbox.orcid.org + + + + + https://sandbox.orcid.org/0000-0000-0000-0002 + 0000-0000-0000-0002 + sandbox.orcid.org + + + + + https://sandbox.orcid.org/0000-0000-0000-0003 + 0000-0000-0000-0003 + sandbox.orcid.org + + + From cf9e5c1cd4c91f2b3d072c2a18eed3ca2441454a Mon Sep 17 00:00:00 2001 From: Jesiel Viana Date: Tue, 22 Apr 2025 23:19:56 -0300 Subject: [PATCH 564/979] fix: Checkstyle violations (cherry picked from commit 9a831e53933771228c7e141db3d0217651d5b32c) --- .../impl/OrcidV3AuthorDataProviderTest.java | 31 ++++++++++--------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/dspace-api/src/test/java/org/dspace/external/provider/impl/OrcidV3AuthorDataProviderTest.java b/dspace-api/src/test/java/org/dspace/external/provider/impl/OrcidV3AuthorDataProviderTest.java index 34b3a6838d4e..a68c0519bae5 100644 --- a/dspace-api/src/test/java/org/dspace/external/provider/impl/OrcidV3AuthorDataProviderTest.java +++ b/dspace-api/src/test/java/org/dspace/external/provider/impl/OrcidV3AuthorDataProviderTest.java @@ -7,22 +7,24 @@ */ package org.dspace.external.provider.impl; +import static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.CoreMatchers.hasItem; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.beans.HasPropertyWithValue.hasProperty; +import static org.hamcrest.collection.IsCollectionWithSize.hasSize; +import static org.hamcrest.core.AllOf.allOf; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.io.InputStream; +import java.util.List; + import org.dspace.AbstractDSpaceTest; import org.dspace.external.OrcidRestConnector; import org.dspace.external.model.ExternalDataObject; -import org.junit.After; import org.junit.Before; import org.junit.Test; -import java.io.InputStream; -import java.util.List; - -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.*; -import static org.junit.Assert.assertThrows; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.*; /** * Unit tests for {@link OrcidV3AuthorDataProvider}. @@ -60,10 +62,11 @@ public void setup() throws Exception { InputStream person2XmlStream = getClass().getClassLoader().getResourceAsStream(PERSON2_XML_PATH); InputStream person3XmlStream = getClass().getClassLoader().getResourceAsStream(PERSON3_XML_PATH); - when(mockRestConnector.get("search?q=search%3Fq%3D0000-0000-0000-0000&start=0&rows=10",null )).thenReturn(searchXmlStream); - when(mockRestConnector.get("0000-0000-0000-0001/person",null )).thenReturn(person1XmlStream); - when(mockRestConnector.get("0000-0000-0000-0002/person",null )).thenReturn(person2XmlStream); - when(mockRestConnector.get("0000-0000-0000-0003/person",null )).thenReturn(person3XmlStream); + when(mockRestConnector.get("search?q=search%3Fq%3D0000-0000-0000-0000&start=0&rows=10", null)) + .thenReturn(searchXmlStream); + when(mockRestConnector.get("0000-0000-0000-0001/person", null)).thenReturn(person1XmlStream); + when(mockRestConnector.get("0000-0000-0000-0002/person", null)).thenReturn(person2XmlStream); + when(mockRestConnector.get("0000-0000-0000-0003/person", null)).thenReturn(person3XmlStream); } From 106936967e6d2bcd861cefe9ecc05bbb47cb9a93 Mon Sep 17 00:00:00 2001 From: Jesiel Viana Date: Tue, 22 Apr 2025 23:44:10 -0300 Subject: [PATCH 565/979] removing duplicated files (cherry picked from commit c6d1121cbe90ee9c12847f283a3d00601acceaa3) --- .../provider/impl/orcid-person/person1.xml | 51 --------------- .../provider/impl/orcid-person/person2.xml | 64 ------------------- .../provider/impl/orcid-person/person3.xml | 35 ---------- .../provider/impl/orcid-person/search.xml | 25 -------- 4 files changed, 175 deletions(-) delete mode 100644 dspace-api/src/test/resources/org/dspace/external/provider/impl/orcid-person/person1.xml delete mode 100644 dspace-api/src/test/resources/org/dspace/external/provider/impl/orcid-person/person2.xml delete mode 100644 dspace-api/src/test/resources/org/dspace/external/provider/impl/orcid-person/person3.xml delete mode 100644 dspace-api/src/test/resources/org/dspace/external/provider/impl/orcid-person/search.xml diff --git a/dspace-api/src/test/resources/org/dspace/external/provider/impl/orcid-person/person1.xml b/dspace-api/src/test/resources/org/dspace/external/provider/impl/orcid-person/person1.xml deleted file mode 100644 index 64e4b292b92b..000000000000 --- a/dspace-api/src/test/resources/org/dspace/external/provider/impl/orcid-person/person1.xml +++ /dev/null @@ -1,51 +0,0 @@ - - - 2025-04-21T22:28:18.862Z - - 2025-04-11T15:41:21.340Z - 2025-04-11T15:41:21.340Z - GivenNames1 - FamilyName1 - - - - - 2025-04-21T22:28:18.862Z - - 2025-04-21T22:23:14.698Z - 2025-04-21T22:28:18.862Z - - - https://sandbox.orcid.org/0000-0000-0000-0001 - 0000-0000-0000-0001 - sandbox.orcid.org - - GivenNames1 FamilyName1 - - person1@email.com - - - - - - diff --git a/dspace-api/src/test/resources/org/dspace/external/provider/impl/orcid-person/person2.xml b/dspace-api/src/test/resources/org/dspace/external/provider/impl/orcid-person/person2.xml deleted file mode 100644 index c91b0207247d..000000000000 --- a/dspace-api/src/test/resources/org/dspace/external/provider/impl/orcid-person/person2.xml +++ /dev/null @@ -1,64 +0,0 @@ - - - 2025-04-21T22:28:18.862Z - - 2025-04-11T15:41:21.340Z - 2025-04-11T15:41:21.340Z - GivenNames2 - FamilyName2 - - - - - 2025-04-21T22:28:18.862Z - - 2025-04-21T22:23:14.698Z - 2025-04-21T22:28:18.862Z - - - https://sandbox.orcid.org/0000-0000-0000-0002 - 0000-0000-0000-0002 - sandbox.orcid.org - - GivenNames2 FamilyName2 - - person2@email.com - - - 2025-04-21T16:42:54.961Z - 2025-04-21T16:48:32.642Z - - - https://sandbox.orcid.org/0000-0000-0000-0001 - 0000-0000-0000-0001 - sandbox.orcid.org - - GivenNames1 FamilyName1 - - person2primary@email.com - - - - - - diff --git a/dspace-api/src/test/resources/org/dspace/external/provider/impl/orcid-person/person3.xml b/dspace-api/src/test/resources/org/dspace/external/provider/impl/orcid-person/person3.xml deleted file mode 100644 index b24ed9d3547a..000000000000 --- a/dspace-api/src/test/resources/org/dspace/external/provider/impl/orcid-person/person3.xml +++ /dev/null @@ -1,35 +0,0 @@ - - - - 2024-06-11T20:01:28.538Z - 2024-06-11T20:01:28.538Z - GivenNames3 - FamilyName3 - - - - - - - - diff --git a/dspace-api/src/test/resources/org/dspace/external/provider/impl/orcid-person/search.xml b/dspace-api/src/test/resources/org/dspace/external/provider/impl/orcid-person/search.xml deleted file mode 100644 index 98ec721be9b8..000000000000 --- a/dspace-api/src/test/resources/org/dspace/external/provider/impl/orcid-person/search.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - https://sandbox.orcid.org/0000-0000-0000-0001 - 0000-0000-0000-0001 - sandbox.orcid.org - - - - - https://sandbox.orcid.org/0000-0000-0000-0002 - 0000-0000-0000-0002 - sandbox.orcid.org - - - - - https://sandbox.orcid.org/0000-0000-0000-0003 - 0000-0000-0000-0003 - sandbox.orcid.org - - - From df91886375a2a5ddeeaae8e83b94168d37282075 Mon Sep 17 00:00:00 2001 From: Jesiel Viana Date: Tue, 22 Apr 2025 22:12:00 -0300 Subject: [PATCH 566/979] fix: import public email from ORCID person (cherry picked from commit 82ca80cd6b5d340f5d3d2e042803c2fc43d8a973) --- .../impl/OrcidV3AuthorDataProvider.java | 22 +- .../impl/OrcidV3AuthorDataProviderTest.java | 231 ++++++++++++++++++ .../provider/impl/orcid-person/person1.xml | 51 ++++ .../provider/impl/orcid-person/person2.xml | 64 +++++ .../provider/impl/orcid-person/person3.xml | 35 +++ .../provider/impl/orcid-person/search.xml | 25 ++ .../provider/orcid-v3-author/person1.xml | 51 ++++ .../provider/orcid-v3-author/person2.xml | 64 +++++ .../provider/orcid-v3-author/person3.xml | 35 +++ .../provider/orcid-v3-author/search.xml | 25 ++ 10 files changed, 596 insertions(+), 7 deletions(-) create mode 100644 dspace-api/src/test/java/org/dspace/external/provider/impl/OrcidV3AuthorDataProviderTest.java create mode 100644 dspace-api/src/test/resources/org/dspace/external/provider/impl/orcid-person/person1.xml create mode 100644 dspace-api/src/test/resources/org/dspace/external/provider/impl/orcid-person/person2.xml create mode 100644 dspace-api/src/test/resources/org/dspace/external/provider/impl/orcid-person/person3.xml create mode 100644 dspace-api/src/test/resources/org/dspace/external/provider/impl/orcid-person/search.xml create mode 100644 dspace-api/src/test/resources/org/dspace/external/provider/orcid-v3-author/person1.xml create mode 100644 dspace-api/src/test/resources/org/dspace/external/provider/orcid-v3-author/person2.xml create mode 100644 dspace-api/src/test/resources/org/dspace/external/provider/orcid-v3-author/person3.xml create mode 100644 dspace-api/src/test/resources/org/dspace/external/provider/orcid-v3-author/search.xml diff --git a/dspace-api/src/main/java/org/dspace/external/provider/impl/OrcidV3AuthorDataProvider.java b/dspace-api/src/main/java/org/dspace/external/provider/impl/OrcidV3AuthorDataProvider.java index c7e41171a5bb..dfbd07a83a02 100644 --- a/dspace-api/src/main/java/org/dspace/external/provider/impl/OrcidV3AuthorDataProvider.java +++ b/dspace-api/src/main/java/org/dspace/external/provider/impl/OrcidV3AuthorDataProvider.java @@ -27,6 +27,7 @@ import org.dspace.external.provider.orcid.xml.XMLtoBio; import org.dspace.orcid.model.factory.OrcidFactoryUtils; import org.orcid.jaxb.model.v3.release.common.OrcidIdentifier; +import org.orcid.jaxb.model.v3.release.record.Email; import org.orcid.jaxb.model.v3.release.record.Person; import org.orcid.jaxb.model.v3.release.search.Result; import org.springframework.beans.factory.annotation.Autowired; @@ -114,13 +115,20 @@ protected ExternalDataObject convertToExternalDataObject(Person person) { if (person.getName().getFamilyName() != null) { lastName = person.getName().getFamilyName().getContent(); externalDataObject.addMetadata(new MetadataValueDTO("person", "familyName", null, null, - lastName)); + lastName)); } if (person.getName().getGivenNames() != null) { firstName = person.getName().getGivenNames().getContent(); externalDataObject.addMetadata(new MetadataValueDTO("person", "givenName", null, null, - firstName)); - + firstName)); + } + if (person.getEmails().getEmails() != null && !person.getEmails().getEmails().isEmpty()) { + Email email = person.getEmails().getEmails().get(0); + if (person.getEmails().getEmails().size() > 1) { + email = person.getEmails().getEmails().stream().filter(Email::isPrimary).findFirst().orElse(email); + } + externalDataObject.addMetadata(new MetadataValueDTO("person", "email", null, + null, email.getEmail())); } externalDataObject.setId(person.getName().getPath()); externalDataObject @@ -128,7 +136,7 @@ protected ExternalDataObject convertToExternalDataObject(Person person) { new MetadataValueDTO("person", "identifier", "orcid", null, person.getName().getPath())); externalDataObject .addMetadata(new MetadataValueDTO("dc", "identifier", "uri", null, - orcidUrl + "/" + person.getName().getPath())); + orcidUrl + "/" + person.getName().getPath())); if (!StringUtils.isBlank(lastName) && !StringUtils.isBlank(firstName)) { externalDataObject.setDisplayValue(lastName + ", " + firstName); externalDataObject.setValue(lastName + ", " + firstName); @@ -139,8 +147,8 @@ protected ExternalDataObject convertToExternalDataObject(Person person) { externalDataObject.setDisplayValue(firstName); externalDataObject.setValue(firstName); } - } else if (person.getPath() != null ) { - externalDataObject.setId(StringUtils.substringBetween(person.getPath(),"/","/person")); + } else if (person.getPath() != null) { + externalDataObject.setId(StringUtils.substringBetween(person.getPath(), "/", "/person")); } return externalDataObject; } @@ -204,7 +212,7 @@ public List searchExternalDataObjects(String query, int star for (Result result : results) { OrcidIdentifier orcidIdentifier = result.getOrcidIdentifier(); if (orcidIdentifier != null) { - log.debug("Found OrcidId=" + orcidIdentifier.toString()); + log.debug("Found OrcidId=" + orcidIdentifier.getPath()); String orcid = orcidIdentifier.getPath(); Person bio = getBio(orcid); if (bio != null) { diff --git a/dspace-api/src/test/java/org/dspace/external/provider/impl/OrcidV3AuthorDataProviderTest.java b/dspace-api/src/test/java/org/dspace/external/provider/impl/OrcidV3AuthorDataProviderTest.java new file mode 100644 index 000000000000..34b3a6838d4e --- /dev/null +++ b/dspace-api/src/test/java/org/dspace/external/provider/impl/OrcidV3AuthorDataProviderTest.java @@ -0,0 +1,231 @@ +/** + * The contents of this file are subject to the license and copyright + * detailed in the LICENSE and NOTICE files at the root of the source + * tree and available online at + * + * http://www.dspace.org/license/ + */ +package org.dspace.external.provider.impl; + +import org.dspace.AbstractDSpaceTest; +import org.dspace.external.OrcidRestConnector; +import org.dspace.external.model.ExternalDataObject; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import java.io.InputStream; +import java.util.List; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.*; +import static org.junit.Assert.assertThrows; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.*; + +/** + * Unit tests for {@link OrcidV3AuthorDataProvider}. + * + * @author Jesiel Viana (jesielviana at proton.me) + * + */ +public class OrcidV3AuthorDataProviderTest extends AbstractDSpaceTest { + + private static final String SEARCH_XML_PATH = "org/dspace/external/provider/orcid-v3-author/search.xml"; + private static final String PERSON1_XML_PATH = "org/dspace/external/provider/orcid-v3-author/person1.xml"; + private static final String PERSON2_XML_PATH = "org/dspace/external/provider/orcid-v3-author/person2.xml"; + private static final String PERSON3_XML_PATH = "org/dspace/external/provider/orcid-v3-author/person3.xml"; + + public static final String ORCID_SEARCH_QUERY = "search?q=0000-0000-0000-0000"; + + private OrcidV3AuthorDataProvider dataProvider; + + @Before + public void setup() throws Exception { + dataProvider = new OrcidV3AuthorDataProvider(); + + OrcidRestConnector mockRestConnector = mock(OrcidRestConnector.class); + + dataProvider.setOrcidRestConnector(mockRestConnector); + dataProvider.setSourceIdentifier("orcid"); + dataProvider.setOrcidUrl("https://orcid.org"); + + dataProvider.setClientId("client-id"); + dataProvider.setClientSecret("client-secret"); + dataProvider.setOAUTHUrl("https://orcid.org/oauth"); + + InputStream searchXmlStream = getClass().getClassLoader().getResourceAsStream(SEARCH_XML_PATH); + InputStream person1XmlStream = getClass().getClassLoader().getResourceAsStream(PERSON1_XML_PATH); + InputStream person2XmlStream = getClass().getClassLoader().getResourceAsStream(PERSON2_XML_PATH); + InputStream person3XmlStream = getClass().getClassLoader().getResourceAsStream(PERSON3_XML_PATH); + + when(mockRestConnector.get("search?q=search%3Fq%3D0000-0000-0000-0000&start=0&rows=10",null )).thenReturn(searchXmlStream); + when(mockRestConnector.get("0000-0000-0000-0001/person",null )).thenReturn(person1XmlStream); + when(mockRestConnector.get("0000-0000-0000-0002/person",null )).thenReturn(person2XmlStream); + when(mockRestConnector.get("0000-0000-0000-0003/person",null )).thenReturn(person3XmlStream); + + } + + @Test + public void testGetExternalDataObjectSizeIsCorrect() { + List optional = dataProvider.searchExternalDataObjects(ORCID_SEARCH_QUERY, 0, 10); + assertThat(optional, hasSize(3)); + } + + @Test + public void testGetExternalDataObjectGetPersonWithAllFieldsPopulated() { + List optional = dataProvider.searchExternalDataObjects(ORCID_SEARCH_QUERY, 0, 10); + + assertThat(optional, hasSize(3)); + + ExternalDataObject externalDataObject1 = optional.get(0); + + // Basic field assertions + assertThat(externalDataObject1.getId(), equalTo("0000-0000-0000-0001")); + assertThat(externalDataObject1.getValue(), equalTo("FamilyName1, GivenNames1")); + assertThat(externalDataObject1.getSource(), equalTo("orcid")); + assertThat(externalDataObject1.getDisplayValue(), equalTo("FamilyName1, GivenNames1")); + + // Metadata assertions + assertThat(externalDataObject1.getMetadata(), hasItem( + allOf( + hasProperty("schema", equalTo("person")), + hasProperty("element", equalTo("familyName")), + hasProperty("value", equalTo("FamilyName1")) + ) + )); + assertThat(externalDataObject1.getMetadata(), hasItem( + allOf( + hasProperty("schema", equalTo("person")), + hasProperty("element", equalTo("givenName")), + hasProperty("value", equalTo("GivenNames1")) + ) + )); + assertThat(externalDataObject1.getMetadata(), hasItem( + allOf( + hasProperty("schema", equalTo("person")), + hasProperty("element", equalTo("email")), + hasProperty("value", equalTo("person1@email.com")) + ) + )); + assertThat(externalDataObject1.getMetadata(), hasItem( + allOf( + hasProperty("schema", equalTo("person")), + hasProperty("element", equalTo("identifier")), + hasProperty("qualifier", equalTo("orcid")), + hasProperty("value", equalTo("0000-0000-0000-0001")) + ) + )); + assertThat(externalDataObject1.getMetadata(), hasItem( + allOf( + hasProperty("schema", equalTo("dc")), + hasProperty("element", equalTo("identifier")), + hasProperty("qualifier", equalTo("uri")), + hasProperty("value", equalTo("https://orcid.org/0000-0000-0000-0001")) + ) + )); + } + + @Test + public void testGetExternalDataObjectGetPrimaryEmailFromPersonWithTwoEmails() { + List optional = dataProvider.searchExternalDataObjects(ORCID_SEARCH_QUERY, 0, 10); + + assertThat(optional, hasSize(3)); + + ExternalDataObject externalDataObject2 = optional.get(1); // Test person2 (with two emails) + + // Basic field assertions + assertThat(externalDataObject2.getId(), equalTo("0000-0000-0000-0002")); + assertThat(externalDataObject2.getValue(), equalTo("FamilyName2, GivenNames2")); + assertThat(externalDataObject2.getSource(), equalTo("orcid")); + assertThat(externalDataObject2.getDisplayValue(), equalTo("FamilyName2, GivenNames2")); + + // Metadata assertions + assertThat(externalDataObject2.getMetadata(), hasItem( + allOf( + hasProperty("schema", equalTo("person")), + hasProperty("element", equalTo("familyName")), + hasProperty("value", equalTo("FamilyName2")) + ) + )); + assertThat(externalDataObject2.getMetadata(), hasItem( + allOf( + hasProperty("schema", equalTo("person")), + hasProperty("element", equalTo("givenName")), + hasProperty("value", equalTo("GivenNames2")) + ) + )); + assertThat(externalDataObject2.getMetadata(), hasItem( + allOf( + hasProperty("schema", equalTo("person")), + hasProperty("element", equalTo("email")), + hasProperty("value", equalTo("person2primary@email.com")) // Primary email + ) + )); + assertThat(externalDataObject2.getMetadata(), hasItem( + allOf( + hasProperty("schema", equalTo("person")), + hasProperty("element", equalTo("identifier")), + hasProperty("qualifier", equalTo("orcid")), + hasProperty("value", equalTo("0000-0000-0000-0002")) + ) + )); + assertThat(externalDataObject2.getMetadata(), hasItem( + allOf( + hasProperty("schema", equalTo("dc")), + hasProperty("element", equalTo("identifier")), + hasProperty("qualifier", equalTo("uri")), + hasProperty("value", equalTo("https://orcid.org/0000-0000-0000-0002")) + ) + )); + } + + + @Test + public void testGetExternalDataObjectGetPersonOnlyWithNameFilled() { + List optional = dataProvider.searchExternalDataObjects(ORCID_SEARCH_QUERY, 0, 10); + + assertThat(optional, hasSize(3)); + + ExternalDataObject externalDataObject2 = optional.get(2); // Test person2 (with two emails) + + // Basic field assertions + assertThat(externalDataObject2.getId(), equalTo("0000-0000-0000-0003")); + assertThat(externalDataObject2.getValue(), equalTo("FamilyName3, GivenNames3")); + assertThat(externalDataObject2.getSource(), equalTo("orcid")); + assertThat(externalDataObject2.getDisplayValue(), equalTo("FamilyName3, GivenNames3")); + + // Metadata assertions + assertThat(externalDataObject2.getMetadata(), hasItem( + allOf( + hasProperty("schema", equalTo("person")), + hasProperty("element", equalTo("familyName")), + hasProperty("value", equalTo("FamilyName3")) + ) + )); + assertThat(externalDataObject2.getMetadata(), hasItem( + allOf( + hasProperty("schema", equalTo("person")), + hasProperty("element", equalTo("givenName")), + hasProperty("value", equalTo("GivenNames3")) + ) + )); + assertThat(externalDataObject2.getMetadata(), hasItem( + allOf( + hasProperty("schema", equalTo("person")), + hasProperty("element", equalTo("identifier")), + hasProperty("qualifier", equalTo("orcid")), + hasProperty("value", equalTo("0000-0000-0000-0003")) + ) + )); + assertThat(externalDataObject2.getMetadata(), hasItem( + allOf( + hasProperty("schema", equalTo("dc")), + hasProperty("element", equalTo("identifier")), + hasProperty("qualifier", equalTo("uri")), + hasProperty("value", equalTo("https://orcid.org/0000-0000-0000-0003")) + ) + )); + } +} diff --git a/dspace-api/src/test/resources/org/dspace/external/provider/impl/orcid-person/person1.xml b/dspace-api/src/test/resources/org/dspace/external/provider/impl/orcid-person/person1.xml new file mode 100644 index 000000000000..64e4b292b92b --- /dev/null +++ b/dspace-api/src/test/resources/org/dspace/external/provider/impl/orcid-person/person1.xml @@ -0,0 +1,51 @@ + + + 2025-04-21T22:28:18.862Z + + 2025-04-11T15:41:21.340Z + 2025-04-11T15:41:21.340Z + GivenNames1 + FamilyName1 + + + + + 2025-04-21T22:28:18.862Z + + 2025-04-21T22:23:14.698Z + 2025-04-21T22:28:18.862Z + + + https://sandbox.orcid.org/0000-0000-0000-0001 + 0000-0000-0000-0001 + sandbox.orcid.org + + GivenNames1 FamilyName1 + + person1@email.com + + + + + + diff --git a/dspace-api/src/test/resources/org/dspace/external/provider/impl/orcid-person/person2.xml b/dspace-api/src/test/resources/org/dspace/external/provider/impl/orcid-person/person2.xml new file mode 100644 index 000000000000..c91b0207247d --- /dev/null +++ b/dspace-api/src/test/resources/org/dspace/external/provider/impl/orcid-person/person2.xml @@ -0,0 +1,64 @@ + + + 2025-04-21T22:28:18.862Z + + 2025-04-11T15:41:21.340Z + 2025-04-11T15:41:21.340Z + GivenNames2 + FamilyName2 + + + + + 2025-04-21T22:28:18.862Z + + 2025-04-21T22:23:14.698Z + 2025-04-21T22:28:18.862Z + + + https://sandbox.orcid.org/0000-0000-0000-0002 + 0000-0000-0000-0002 + sandbox.orcid.org + + GivenNames2 FamilyName2 + + person2@email.com + + + 2025-04-21T16:42:54.961Z + 2025-04-21T16:48:32.642Z + + + https://sandbox.orcid.org/0000-0000-0000-0001 + 0000-0000-0000-0001 + sandbox.orcid.org + + GivenNames1 FamilyName1 + + person2primary@email.com + + + + + + diff --git a/dspace-api/src/test/resources/org/dspace/external/provider/impl/orcid-person/person3.xml b/dspace-api/src/test/resources/org/dspace/external/provider/impl/orcid-person/person3.xml new file mode 100644 index 000000000000..b24ed9d3547a --- /dev/null +++ b/dspace-api/src/test/resources/org/dspace/external/provider/impl/orcid-person/person3.xml @@ -0,0 +1,35 @@ + + + + 2024-06-11T20:01:28.538Z + 2024-06-11T20:01:28.538Z + GivenNames3 + FamilyName3 + + + + + + + + diff --git a/dspace-api/src/test/resources/org/dspace/external/provider/impl/orcid-person/search.xml b/dspace-api/src/test/resources/org/dspace/external/provider/impl/orcid-person/search.xml new file mode 100644 index 000000000000..98ec721be9b8 --- /dev/null +++ b/dspace-api/src/test/resources/org/dspace/external/provider/impl/orcid-person/search.xml @@ -0,0 +1,25 @@ + + + + + https://sandbox.orcid.org/0000-0000-0000-0001 + 0000-0000-0000-0001 + sandbox.orcid.org + + + + + https://sandbox.orcid.org/0000-0000-0000-0002 + 0000-0000-0000-0002 + sandbox.orcid.org + + + + + https://sandbox.orcid.org/0000-0000-0000-0003 + 0000-0000-0000-0003 + sandbox.orcid.org + + + diff --git a/dspace-api/src/test/resources/org/dspace/external/provider/orcid-v3-author/person1.xml b/dspace-api/src/test/resources/org/dspace/external/provider/orcid-v3-author/person1.xml new file mode 100644 index 000000000000..64e4b292b92b --- /dev/null +++ b/dspace-api/src/test/resources/org/dspace/external/provider/orcid-v3-author/person1.xml @@ -0,0 +1,51 @@ + + + 2025-04-21T22:28:18.862Z + + 2025-04-11T15:41:21.340Z + 2025-04-11T15:41:21.340Z + GivenNames1 + FamilyName1 + + + + + 2025-04-21T22:28:18.862Z + + 2025-04-21T22:23:14.698Z + 2025-04-21T22:28:18.862Z + + + https://sandbox.orcid.org/0000-0000-0000-0001 + 0000-0000-0000-0001 + sandbox.orcid.org + + GivenNames1 FamilyName1 + + person1@email.com + + + + + + diff --git a/dspace-api/src/test/resources/org/dspace/external/provider/orcid-v3-author/person2.xml b/dspace-api/src/test/resources/org/dspace/external/provider/orcid-v3-author/person2.xml new file mode 100644 index 000000000000..c91b0207247d --- /dev/null +++ b/dspace-api/src/test/resources/org/dspace/external/provider/orcid-v3-author/person2.xml @@ -0,0 +1,64 @@ + + + 2025-04-21T22:28:18.862Z + + 2025-04-11T15:41:21.340Z + 2025-04-11T15:41:21.340Z + GivenNames2 + FamilyName2 + + + + + 2025-04-21T22:28:18.862Z + + 2025-04-21T22:23:14.698Z + 2025-04-21T22:28:18.862Z + + + https://sandbox.orcid.org/0000-0000-0000-0002 + 0000-0000-0000-0002 + sandbox.orcid.org + + GivenNames2 FamilyName2 + + person2@email.com + + + 2025-04-21T16:42:54.961Z + 2025-04-21T16:48:32.642Z + + + https://sandbox.orcid.org/0000-0000-0000-0001 + 0000-0000-0000-0001 + sandbox.orcid.org + + GivenNames1 FamilyName1 + + person2primary@email.com + + + + + + diff --git a/dspace-api/src/test/resources/org/dspace/external/provider/orcid-v3-author/person3.xml b/dspace-api/src/test/resources/org/dspace/external/provider/orcid-v3-author/person3.xml new file mode 100644 index 000000000000..b24ed9d3547a --- /dev/null +++ b/dspace-api/src/test/resources/org/dspace/external/provider/orcid-v3-author/person3.xml @@ -0,0 +1,35 @@ + + + + 2024-06-11T20:01:28.538Z + 2024-06-11T20:01:28.538Z + GivenNames3 + FamilyName3 + + + + + + + + diff --git a/dspace-api/src/test/resources/org/dspace/external/provider/orcid-v3-author/search.xml b/dspace-api/src/test/resources/org/dspace/external/provider/orcid-v3-author/search.xml new file mode 100644 index 000000000000..98ec721be9b8 --- /dev/null +++ b/dspace-api/src/test/resources/org/dspace/external/provider/orcid-v3-author/search.xml @@ -0,0 +1,25 @@ + + + + + https://sandbox.orcid.org/0000-0000-0000-0001 + 0000-0000-0000-0001 + sandbox.orcid.org + + + + + https://sandbox.orcid.org/0000-0000-0000-0002 + 0000-0000-0000-0002 + sandbox.orcid.org + + + + + https://sandbox.orcid.org/0000-0000-0000-0003 + 0000-0000-0000-0003 + sandbox.orcid.org + + + From 0c895d07b6e8d98a2cc809e556bc1d015c2ff185 Mon Sep 17 00:00:00 2001 From: Jesiel Viana Date: Tue, 22 Apr 2025 23:19:56 -0300 Subject: [PATCH 567/979] fix: Checkstyle violations (cherry picked from commit 9a831e53933771228c7e141db3d0217651d5b32c) --- .../impl/OrcidV3AuthorDataProviderTest.java | 31 ++++++++++--------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/dspace-api/src/test/java/org/dspace/external/provider/impl/OrcidV3AuthorDataProviderTest.java b/dspace-api/src/test/java/org/dspace/external/provider/impl/OrcidV3AuthorDataProviderTest.java index 34b3a6838d4e..a68c0519bae5 100644 --- a/dspace-api/src/test/java/org/dspace/external/provider/impl/OrcidV3AuthorDataProviderTest.java +++ b/dspace-api/src/test/java/org/dspace/external/provider/impl/OrcidV3AuthorDataProviderTest.java @@ -7,22 +7,24 @@ */ package org.dspace.external.provider.impl; +import static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.CoreMatchers.hasItem; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.beans.HasPropertyWithValue.hasProperty; +import static org.hamcrest.collection.IsCollectionWithSize.hasSize; +import static org.hamcrest.core.AllOf.allOf; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.io.InputStream; +import java.util.List; + import org.dspace.AbstractDSpaceTest; import org.dspace.external.OrcidRestConnector; import org.dspace.external.model.ExternalDataObject; -import org.junit.After; import org.junit.Before; import org.junit.Test; -import java.io.InputStream; -import java.util.List; - -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.*; -import static org.junit.Assert.assertThrows; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.*; /** * Unit tests for {@link OrcidV3AuthorDataProvider}. @@ -60,10 +62,11 @@ public void setup() throws Exception { InputStream person2XmlStream = getClass().getClassLoader().getResourceAsStream(PERSON2_XML_PATH); InputStream person3XmlStream = getClass().getClassLoader().getResourceAsStream(PERSON3_XML_PATH); - when(mockRestConnector.get("search?q=search%3Fq%3D0000-0000-0000-0000&start=0&rows=10",null )).thenReturn(searchXmlStream); - when(mockRestConnector.get("0000-0000-0000-0001/person",null )).thenReturn(person1XmlStream); - when(mockRestConnector.get("0000-0000-0000-0002/person",null )).thenReturn(person2XmlStream); - when(mockRestConnector.get("0000-0000-0000-0003/person",null )).thenReturn(person3XmlStream); + when(mockRestConnector.get("search?q=search%3Fq%3D0000-0000-0000-0000&start=0&rows=10", null)) + .thenReturn(searchXmlStream); + when(mockRestConnector.get("0000-0000-0000-0001/person", null)).thenReturn(person1XmlStream); + when(mockRestConnector.get("0000-0000-0000-0002/person", null)).thenReturn(person2XmlStream); + when(mockRestConnector.get("0000-0000-0000-0003/person", null)).thenReturn(person3XmlStream); } From 103ed21039d18c07ad6a43bb6ca80946a7255fc4 Mon Sep 17 00:00:00 2001 From: Jesiel Viana Date: Tue, 22 Apr 2025 23:44:10 -0300 Subject: [PATCH 568/979] removing duplicated files (cherry picked from commit c6d1121cbe90ee9c12847f283a3d00601acceaa3) --- .../provider/impl/orcid-person/person1.xml | 51 --------------- .../provider/impl/orcid-person/person2.xml | 64 ------------------- .../provider/impl/orcid-person/person3.xml | 35 ---------- .../provider/impl/orcid-person/search.xml | 25 -------- 4 files changed, 175 deletions(-) delete mode 100644 dspace-api/src/test/resources/org/dspace/external/provider/impl/orcid-person/person1.xml delete mode 100644 dspace-api/src/test/resources/org/dspace/external/provider/impl/orcid-person/person2.xml delete mode 100644 dspace-api/src/test/resources/org/dspace/external/provider/impl/orcid-person/person3.xml delete mode 100644 dspace-api/src/test/resources/org/dspace/external/provider/impl/orcid-person/search.xml diff --git a/dspace-api/src/test/resources/org/dspace/external/provider/impl/orcid-person/person1.xml b/dspace-api/src/test/resources/org/dspace/external/provider/impl/orcid-person/person1.xml deleted file mode 100644 index 64e4b292b92b..000000000000 --- a/dspace-api/src/test/resources/org/dspace/external/provider/impl/orcid-person/person1.xml +++ /dev/null @@ -1,51 +0,0 @@ - - - 2025-04-21T22:28:18.862Z - - 2025-04-11T15:41:21.340Z - 2025-04-11T15:41:21.340Z - GivenNames1 - FamilyName1 - - - - - 2025-04-21T22:28:18.862Z - - 2025-04-21T22:23:14.698Z - 2025-04-21T22:28:18.862Z - - - https://sandbox.orcid.org/0000-0000-0000-0001 - 0000-0000-0000-0001 - sandbox.orcid.org - - GivenNames1 FamilyName1 - - person1@email.com - - - - - - diff --git a/dspace-api/src/test/resources/org/dspace/external/provider/impl/orcid-person/person2.xml b/dspace-api/src/test/resources/org/dspace/external/provider/impl/orcid-person/person2.xml deleted file mode 100644 index c91b0207247d..000000000000 --- a/dspace-api/src/test/resources/org/dspace/external/provider/impl/orcid-person/person2.xml +++ /dev/null @@ -1,64 +0,0 @@ - - - 2025-04-21T22:28:18.862Z - - 2025-04-11T15:41:21.340Z - 2025-04-11T15:41:21.340Z - GivenNames2 - FamilyName2 - - - - - 2025-04-21T22:28:18.862Z - - 2025-04-21T22:23:14.698Z - 2025-04-21T22:28:18.862Z - - - https://sandbox.orcid.org/0000-0000-0000-0002 - 0000-0000-0000-0002 - sandbox.orcid.org - - GivenNames2 FamilyName2 - - person2@email.com - - - 2025-04-21T16:42:54.961Z - 2025-04-21T16:48:32.642Z - - - https://sandbox.orcid.org/0000-0000-0000-0001 - 0000-0000-0000-0001 - sandbox.orcid.org - - GivenNames1 FamilyName1 - - person2primary@email.com - - - - - - diff --git a/dspace-api/src/test/resources/org/dspace/external/provider/impl/orcid-person/person3.xml b/dspace-api/src/test/resources/org/dspace/external/provider/impl/orcid-person/person3.xml deleted file mode 100644 index b24ed9d3547a..000000000000 --- a/dspace-api/src/test/resources/org/dspace/external/provider/impl/orcid-person/person3.xml +++ /dev/null @@ -1,35 +0,0 @@ - - - - 2024-06-11T20:01:28.538Z - 2024-06-11T20:01:28.538Z - GivenNames3 - FamilyName3 - - - - - - - - diff --git a/dspace-api/src/test/resources/org/dspace/external/provider/impl/orcid-person/search.xml b/dspace-api/src/test/resources/org/dspace/external/provider/impl/orcid-person/search.xml deleted file mode 100644 index 98ec721be9b8..000000000000 --- a/dspace-api/src/test/resources/org/dspace/external/provider/impl/orcid-person/search.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - https://sandbox.orcid.org/0000-0000-0000-0001 - 0000-0000-0000-0001 - sandbox.orcid.org - - - - - https://sandbox.orcid.org/0000-0000-0000-0002 - 0000-0000-0000-0002 - sandbox.orcid.org - - - - - https://sandbox.orcid.org/0000-0000-0000-0003 - 0000-0000-0000-0003 - sandbox.orcid.org - - - From 7db214597701df1b7ce4135543bb909253a8540c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 28 Apr 2025 23:55:43 +0000 Subject: [PATCH 569/979] Bump the spring group with 12 updates Bumps the spring group with 12 updates: | Package | From | To | | --- | --- | --- | | [org.springframework.boot:spring-boot-starter-test](https://github.com/spring-projects/spring-boot) | `3.4.4` | `3.4.5` | | [org.springframework.boot:spring-boot-starter-tomcat](https://github.com/spring-projects/spring-boot) | `3.4.4` | `3.4.5` | | [org.springframework.boot:spring-boot-maven-plugin](https://github.com/spring-projects/spring-boot) | `3.4.4` | `3.4.5` | | [org.springframework.boot:spring-boot-starter-cache](https://github.com/spring-projects/spring-boot) | `3.4.4` | `3.4.5` | | [org.springframework.boot:spring-boot-starter](https://github.com/spring-projects/spring-boot) | `3.4.4` | `3.4.5` | | [org.springframework.boot:spring-boot-starter-thymeleaf](https://github.com/spring-projects/spring-boot) | `3.4.4` | `3.4.5` | | [org.springframework.boot:spring-boot-starter-web](https://github.com/spring-projects/spring-boot) | `3.4.4` | `3.4.5` | | [org.springframework.boot:spring-boot-starter-data-rest](https://github.com/spring-projects/spring-boot) | `3.4.4` | `3.4.5` | | [org.springframework.boot:spring-boot-starter-security](https://github.com/spring-projects/spring-boot) | `3.4.4` | `3.4.5` | | [org.springframework.boot:spring-boot-starter-aop](https://github.com/spring-projects/spring-boot) | `3.4.4` | `3.4.5` | | [org.springframework.boot:spring-boot-starter-actuator](https://github.com/spring-projects/spring-boot) | `3.4.4` | `3.4.5` | | [org.springframework.boot:spring-boot-starter-log4j2](https://github.com/spring-projects/spring-boot) | `3.4.4` | `3.4.5` | Updates `org.springframework.boot:spring-boot-starter-test` from 3.4.4 to 3.4.5 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.4.4...v3.4.5) Updates `org.springframework.boot:spring-boot-starter-tomcat` from 3.4.4 to 3.4.5 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.4.4...v3.4.5) Updates `org.springframework.boot:spring-boot-maven-plugin` from 3.4.4 to 3.4.5 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.4.4...v3.4.5) Updates `org.springframework.boot:spring-boot-starter-cache` from 3.4.4 to 3.4.5 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.4.4...v3.4.5) Updates `org.springframework.boot:spring-boot-starter` from 3.4.4 to 3.4.5 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.4.4...v3.4.5) Updates `org.springframework.boot:spring-boot-starter-thymeleaf` from 3.4.4 to 3.4.5 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.4.4...v3.4.5) Updates `org.springframework.boot:spring-boot-starter-web` from 3.4.4 to 3.4.5 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.4.4...v3.4.5) Updates `org.springframework.boot:spring-boot-starter-data-rest` from 3.4.4 to 3.4.5 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.4.4...v3.4.5) Updates `org.springframework.boot:spring-boot-starter-security` from 3.4.4 to 3.4.5 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.4.4...v3.4.5) Updates `org.springframework.boot:spring-boot-starter-aop` from 3.4.4 to 3.4.5 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.4.4...v3.4.5) Updates `org.springframework.boot:spring-boot-starter-actuator` from 3.4.4 to 3.4.5 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.4.4...v3.4.5) Updates `org.springframework.boot:spring-boot-starter-log4j2` from 3.4.4 to 3.4.5 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.4.4...v3.4.5) Updates `org.springframework.boot:spring-boot-starter-tomcat` from 3.4.4 to 3.4.5 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.4.4...v3.4.5) Updates `org.springframework.boot:spring-boot-maven-plugin` from 3.4.4 to 3.4.5 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.4.4...v3.4.5) Updates `org.springframework.boot:spring-boot-starter-cache` from 3.4.4 to 3.4.5 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.4.4...v3.4.5) Updates `org.springframework.boot:spring-boot-starter` from 3.4.4 to 3.4.5 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.4.4...v3.4.5) Updates `org.springframework.boot:spring-boot-starter-thymeleaf` from 3.4.4 to 3.4.5 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.4.4...v3.4.5) Updates `org.springframework.boot:spring-boot-starter-web` from 3.4.4 to 3.4.5 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.4.4...v3.4.5) Updates `org.springframework.boot:spring-boot-starter-data-rest` from 3.4.4 to 3.4.5 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.4.4...v3.4.5) Updates `org.springframework.boot:spring-boot-starter-security` from 3.4.4 to 3.4.5 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.4.4...v3.4.5) Updates `org.springframework.boot:spring-boot-starter-aop` from 3.4.4 to 3.4.5 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.4.4...v3.4.5) Updates `org.springframework.boot:spring-boot-starter-actuator` from 3.4.4 to 3.4.5 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.4.4...v3.4.5) Updates `org.springframework.boot:spring-boot-starter-log4j2` from 3.4.4 to 3.4.5 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.4.4...v3.4.5) --- updated-dependencies: - dependency-name: org.springframework.boot:spring-boot-starter-test dependency-version: 3.4.5 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter-tomcat dependency-version: 3.4.5 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-maven-plugin dependency-version: 3.4.5 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter-cache dependency-version: 3.4.5 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter dependency-version: 3.4.5 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter-thymeleaf dependency-version: 3.4.5 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter-web dependency-version: 3.4.5 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter-data-rest dependency-version: 3.4.5 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter-security dependency-version: 3.4.5 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter-aop dependency-version: 3.4.5 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter-actuator dependency-version: 3.4.5 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter-log4j2 dependency-version: 3.4.5 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter-tomcat dependency-version: 3.4.5 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-maven-plugin dependency-version: 3.4.5 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter-cache dependency-version: 3.4.5 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter dependency-version: 3.4.5 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter-thymeleaf dependency-version: 3.4.5 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter-web dependency-version: 3.4.5 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter-data-rest dependency-version: 3.4.5 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter-security dependency-version: 3.4.5 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter-aop dependency-version: 3.4.5 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter-actuator dependency-version: 3.4.5 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter-log4j2 dependency-version: 3.4.5 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 44b0bb628c13..d09af4f235b1 100644 --- a/pom.xml +++ b/pom.xml @@ -20,7 +20,7 @@ 17 6.2.6 - 3.4.4 + 3.4.5 6.4.5 6.4.8.Final 8.0.1.Final From b949bdd4c0c54a42edb343d65af1696e43ebbc4a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 28 Apr 2025 23:58:24 +0000 Subject: [PATCH 570/979] Bump the fasterxml group with 3 updates Bumps the fasterxml group with 3 updates: [com.fasterxml.jackson.core:jackson-annotations](https://github.com/FasterXML/jackson), [com.fasterxml.jackson.core:jackson-core](https://github.com/FasterXML/jackson-core) and [com.fasterxml.jackson.core:jackson-databind](https://github.com/FasterXML/jackson). Updates `com.fasterxml.jackson.core:jackson-annotations` from 2.18.3 to 2.19.0 - [Commits](https://github.com/FasterXML/jackson/commits) Updates `com.fasterxml.jackson.core:jackson-core` from 2.18.3 to 2.19.0 - [Commits](https://github.com/FasterXML/jackson-core/compare/jackson-core-2.18.3...jackson-core-2.19.0) Updates `com.fasterxml.jackson.core:jackson-core` from 2.18.3 to 2.19.0 - [Commits](https://github.com/FasterXML/jackson-core/compare/jackson-core-2.18.3...jackson-core-2.19.0) Updates `com.fasterxml.jackson.core:jackson-databind` from 2.18.3 to 2.19.0 - [Commits](https://github.com/FasterXML/jackson/commits) --- updated-dependencies: - dependency-name: com.fasterxml.jackson.core:jackson-annotations dependency-version: 2.19.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: fasterxml - dependency-name: com.fasterxml.jackson.core:jackson-core dependency-version: 2.19.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: fasterxml - dependency-name: com.fasterxml.jackson.core:jackson-core dependency-version: 2.19.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: fasterxml - dependency-name: com.fasterxml.jackson.core:jackson-databind dependency-version: 2.19.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: fasterxml ... Signed-off-by: dependabot[bot] --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index aff93df4a23c..39dddad33f66 100644 --- a/pom.xml +++ b/pom.xml @@ -30,8 +30,8 @@ 3.10.8 2.31.0 - 2.18.3 - 2.18.3 + 2.19.0 + 2.19.0 1.3.2 2.3.1 2.3.9 From 98be99953a9a6914ad5f874d9a056365264bf744 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 28 Apr 2025 23:59:08 +0000 Subject: [PATCH 571/979] Bump the fasterxml group with 4 updates Bumps the fasterxml group with 4 updates: [com.fasterxml.jackson.core:jackson-annotations](https://github.com/FasterXML/jackson), [com.fasterxml.jackson.core:jackson-core](https://github.com/FasterXML/jackson-core), [com.fasterxml.jackson.core:jackson-databind](https://github.com/FasterXML/jackson) and com.fasterxml.jackson.datatype:jackson-datatype-jsr310. Updates `com.fasterxml.jackson.core:jackson-annotations` from 2.18.3 to 2.19.0 - [Commits](https://github.com/FasterXML/jackson/commits) Updates `com.fasterxml.jackson.core:jackson-core` from 2.18.3 to 2.19.0 - [Commits](https://github.com/FasterXML/jackson-core/compare/jackson-core-2.18.3...jackson-core-2.19.0) Updates `com.fasterxml.jackson.core:jackson-core` from 2.18.3 to 2.19.0 - [Commits](https://github.com/FasterXML/jackson-core/compare/jackson-core-2.18.3...jackson-core-2.19.0) Updates `com.fasterxml.jackson.core:jackson-databind` from 2.18.3 to 2.19.0 - [Commits](https://github.com/FasterXML/jackson/commits) Updates `com.fasterxml.jackson.datatype:jackson-datatype-jsr310` from 2.18.3 to 2.19.0 Updates `com.fasterxml.jackson.datatype:jackson-datatype-jsr310` from 2.18.3 to 2.19.0 --- updated-dependencies: - dependency-name: com.fasterxml.jackson.core:jackson-annotations dependency-version: 2.19.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: fasterxml - dependency-name: com.fasterxml.jackson.core:jackson-core dependency-version: 2.19.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: fasterxml - dependency-name: com.fasterxml.jackson.core:jackson-core dependency-version: 2.19.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: fasterxml - dependency-name: com.fasterxml.jackson.core:jackson-databind dependency-version: 2.19.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: fasterxml - dependency-name: com.fasterxml.jackson.datatype:jackson-datatype-jsr310 dependency-version: 2.19.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: fasterxml - dependency-name: com.fasterxml.jackson.datatype:jackson-datatype-jsr310 dependency-version: 2.19.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: fasterxml ... Signed-off-by: dependabot[bot] --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 44b0bb628c13..2236cd4c2840 100644 --- a/pom.xml +++ b/pom.xml @@ -31,8 +31,8 @@ 3.10.8 2.38.0 - 2.18.3 - 2.18.3 + 2.19.0 + 2.19.0 2.1.1 4.0.2 4.0.5 From 8b617256a00081710fde7128a243cfbe7b375c19 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 29 Apr 2025 00:00:49 +0000 Subject: [PATCH 572/979] Bump org.apache.httpcomponents.client5:httpclient5 from 5.4.3 to 5.4.4 Bumps [org.apache.httpcomponents.client5:httpclient5](https://github.com/apache/httpcomponents-client) from 5.4.3 to 5.4.4. - [Changelog](https://github.com/apache/httpcomponents-client/blob/rel/v5.4.4/RELEASE_NOTES.txt) - [Commits](https://github.com/apache/httpcomponents-client/compare/rel/v5.4.3...rel/v5.4.4) --- updated-dependencies: - dependency-name: org.apache.httpcomponents.client5:httpclient5 dependency-version: 5.4.4 dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- dspace-server-webapp/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dspace-server-webapp/pom.xml b/dspace-server-webapp/pom.xml index 4b92e9f40170..153f59e8d5ab 100644 --- a/dspace-server-webapp/pom.xml +++ b/dspace-server-webapp/pom.xml @@ -590,7 +590,7 @@ org.apache.httpcomponents.client5 httpclient5 - 5.4.3 + 5.4.4 test From ff67241bc9d336e488dad643d7a4af35ddb1518b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 29 Apr 2025 00:01:34 +0000 Subject: [PATCH 573/979] Bump the apache-commons group with 2 updates Bumps the apache-commons group with 2 updates: org.apache.commons:commons-collections4 and org.apache.commons:commons-configuration2. Updates `org.apache.commons:commons-collections4` from 4.4 to 4.5.0 Updates `org.apache.commons:commons-configuration2` from 2.11.0 to 2.12.0 --- updated-dependencies: - dependency-name: org.apache.commons:commons-collections4 dependency-version: 4.5.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: apache-commons - dependency-name: org.apache.commons:commons-configuration2 dependency-version: 2.12.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: apache-commons ... Signed-off-by: dependabot[bot] --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index aff93df4a23c..ac8dde9b55ad 100644 --- a/pom.xml +++ b/pom.xml @@ -1485,12 +1485,12 @@ org.apache.commons commons-collections4 - 4.4 + 4.5.0 org.apache.commons commons-configuration2 - 2.11.0 + 2.12.0 org.apache.commons From ecd55eabb0eb229c5b4cd6ffd94375709dc84d7a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 29 Apr 2025 00:01:56 +0000 Subject: [PATCH 574/979] Bump the apache-commons group with 2 updates Bumps the apache-commons group with 2 updates: org.apache.commons:commons-collections4 and org.apache.commons:commons-configuration2. Updates `org.apache.commons:commons-collections4` from 4.4 to 4.5.0 Updates `org.apache.commons:commons-configuration2` from 2.11.0 to 2.12.0 --- updated-dependencies: - dependency-name: org.apache.commons:commons-collections4 dependency-version: 4.5.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: apache-commons - dependency-name: org.apache.commons:commons-configuration2 dependency-version: 2.12.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: apache-commons ... Signed-off-by: dependabot[bot] --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 44b0bb628c13..9d0c43c08abe 100644 --- a/pom.xml +++ b/pom.xml @@ -1483,12 +1483,12 @@ org.apache.commons commons-collections4 - 4.4 + 4.5.0 org.apache.commons commons-configuration2 - 2.11.0 + 2.12.0 org.apache.commons From 5400e3f8a815bc12fa5e50a56a3626643a8b5bb6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 29 Apr 2025 00:02:22 +0000 Subject: [PATCH 575/979] Bump the google-apis group with 3 updates Bumps the google-apis group with 3 updates: [com.google.http-client:google-http-client](https://github.com/googleapis/google-http-java-client), [com.google.http-client:google-http-client-jackson2](https://github.com/googleapis/google-http-java-client) and [com.google.http-client:google-http-client-gson](https://github.com/googleapis/google-http-java-client). Updates `com.google.http-client:google-http-client` from 1.46.3 to 1.47.0 - [Release notes](https://github.com/googleapis/google-http-java-client/releases) - [Changelog](https://github.com/googleapis/google-http-java-client/blob/main/CHANGELOG.md) - [Commits](https://github.com/googleapis/google-http-java-client/compare/v1.46.3...v1.47.0) Updates `com.google.http-client:google-http-client-jackson2` from 1.46.3 to 1.47.0 - [Release notes](https://github.com/googleapis/google-http-java-client/releases) - [Changelog](https://github.com/googleapis/google-http-java-client/blob/main/CHANGELOG.md) - [Commits](https://github.com/googleapis/google-http-java-client/compare/v1.46.3...v1.47.0) Updates `com.google.http-client:google-http-client-gson` from 1.46.3 to 1.47.0 - [Release notes](https://github.com/googleapis/google-http-java-client/releases) - [Changelog](https://github.com/googleapis/google-http-java-client/blob/main/CHANGELOG.md) - [Commits](https://github.com/googleapis/google-http-java-client/compare/v1.46.3...v1.47.0) --- updated-dependencies: - dependency-name: com.google.http-client:google-http-client dependency-version: 1.47.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: google-apis - dependency-name: com.google.http-client:google-http-client-jackson2 dependency-version: 1.47.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: google-apis - dependency-name: com.google.http-client:google-http-client-gson dependency-version: 1.47.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: google-apis ... Signed-off-by: dependabot[bot] --- pom.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pom.xml b/pom.xml index aff93df4a23c..d847bcc2811b 100644 --- a/pom.xml +++ b/pom.xml @@ -1714,7 +1714,7 @@ com.google.http-client google-http-client - 1.46.3 + 1.47.0 com.google.errorprone @@ -1736,7 +1736,7 @@ com.google.http-client google-http-client-jackson2 - 1.46.3 + 1.47.0 jackson-core @@ -1758,7 +1758,7 @@ com.google.http-client google-http-client-gson - 1.46.3 + 1.47.0 From 845a35319cbc9a41246addf9bf663c5c5acc00cf Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 29 Apr 2025 00:03:05 +0000 Subject: [PATCH 576/979] Bump com.google.code.gson:gson from 2.13.0 to 2.13.1 Bumps [com.google.code.gson:gson](https://github.com/google/gson) from 2.13.0 to 2.13.1. - [Release notes](https://github.com/google/gson/releases) - [Changelog](https://github.com/google/gson/blob/main/CHANGELOG.md) - [Commits](https://github.com/google/gson/compare/gson-parent-2.13.0...gson-parent-2.13.1) --- updated-dependencies: - dependency-name: com.google.code.gson:gson dependency-version: 2.13.1 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 44b0bb628c13..e0367d0a76e0 100644 --- a/pom.xml +++ b/pom.xml @@ -1358,7 +1358,7 @@ com.google.code.gson gson - 2.13.0 + 2.13.1 9.4.57.v20241219 2.24.3 - 2.0.33 + 2.0.34 1.19.0 1.7.36 2.9.3 From 9206a04e07a4e796e971d0af28a8b4be63fedffa Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 29 Apr 2025 00:24:54 +0000 Subject: [PATCH 578/979] Bump pdfbox-version from 2.0.33 to 2.0.34 Bumps `pdfbox-version` from 2.0.33 to 2.0.34. Updates `org.apache.pdfbox:pdfbox` from 2.0.33 to 2.0.34 Updates `org.apache.pdfbox:fontbox` from 2.0.33 to 2.0.34 --- updated-dependencies: - dependency-name: org.apache.pdfbox:pdfbox dependency-version: 2.0.34 dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.apache.pdfbox:fontbox dependency-version: 2.0.34 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 44b0bb628c13..d285ce63d6fd 100644 --- a/pom.xml +++ b/pom.xml @@ -40,7 +40,7 @@ 9.4.57.v20241219 2.24.3 - 2.0.33 + 2.0.34 1.19.0 2.0.17 2.9.3 From e94d934a5ca371a66e65297fefef9cf1f7c37488 Mon Sep 17 00:00:00 2001 From: Kim Shepherd Date: Tue, 29 Apr 2025 17:35:19 +0200 Subject: [PATCH 579/979] Make getAllFacetConfigs unique Improve performance and debuggability by refactoring getAllFacetConfigs to getAllUniqueFacetConfigs. Used only by ChoiceAuthorityService to generate hierarchical vocabulary map for the browse menu, etc. (cherry picked from commit 159bd18529b5f0918cc0bc07e2b4fee16e4f2511) --- .../authority/ChoiceAuthorityServiceImpl.java | 2 +- .../DiscoveryConfigurationService.java | 14 ++++++++++---- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/content/authority/ChoiceAuthorityServiceImpl.java b/dspace-api/src/main/java/org/dspace/content/authority/ChoiceAuthorityServiceImpl.java index f4d1f02710e1..bbe8e4461fe0 100644 --- a/dspace-api/src/main/java/org/dspace/content/authority/ChoiceAuthorityServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/content/authority/ChoiceAuthorityServiceImpl.java @@ -577,7 +577,7 @@ public DSpaceControlledVocabularyIndex getVocabularyIndex(String nameVocab) { .collect(Collectors.toList())); } DiscoverySearchFilterFacet matchingFacet = null; - for (DiscoverySearchFilterFacet facetConfig : searchConfigurationService.getAllFacetsConfig()) { + for (DiscoverySearchFilterFacet facetConfig : searchConfigurationService.getAllUniqueFacetsConfig()) { boolean coversAllFieldsFromVocab = true; for (String fieldFromVocab: metadataFields) { boolean coversFieldFromVocab = false; diff --git a/dspace-api/src/main/java/org/dspace/discovery/configuration/DiscoveryConfigurationService.java b/dspace-api/src/main/java/org/dspace/discovery/configuration/DiscoveryConfigurationService.java index 6cb93e2993f3..9d603941de39 100644 --- a/dspace-api/src/main/java/org/dspace/discovery/configuration/DiscoveryConfigurationService.java +++ b/dspace-api/src/main/java/org/dspace/discovery/configuration/DiscoveryConfigurationService.java @@ -10,8 +10,10 @@ import java.sql.SQLException; import java.util.ArrayList; import java.util.HashMap; +import java.util.LinkedHashSet; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; @@ -197,15 +199,19 @@ public List getIndexAlwaysConfigurations() { } /** - * @return All configurations for {@link org.dspace.discovery.configuration.DiscoverySearchFilterFacet} + * Get the unique set of configured Discovery facets. This is used when inspecting configuration + * to include hierarchical vocabularies in the browse menu. + * + * @return All unique instances of {@link org.dspace.discovery.configuration.DiscoverySearchFilterFacet} + * included in "sidebarFacets" bean, across all Discovery configurations. */ - public List getAllFacetsConfig() { - List configs = new ArrayList<>(); + public List getAllUniqueFacetsConfig() { + Set configs = new LinkedHashSet<>(); for (String key : map.keySet()) { DiscoveryConfiguration config = map.get(key); configs.addAll(config.getSidebarFacets()); } - return configs; + return new ArrayList<>(configs); } public static void main(String[] args) { From b2eaf663bba7bdf0e1e95a393fe1a27123eeeb3e Mon Sep 17 00:00:00 2001 From: Kim Shepherd Date: Tue, 29 Apr 2025 17:35:19 +0200 Subject: [PATCH 580/979] Make getAllFacetConfigs unique Improve performance and debuggability by refactoring getAllFacetConfigs to getAllUniqueFacetConfigs. Used only by ChoiceAuthorityService to generate hierarchical vocabulary map for the browse menu, etc. (cherry picked from commit 159bd18529b5f0918cc0bc07e2b4fee16e4f2511) --- .../authority/ChoiceAuthorityServiceImpl.java | 2 +- .../DiscoveryConfigurationService.java | 14 ++++++++++---- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/content/authority/ChoiceAuthorityServiceImpl.java b/dspace-api/src/main/java/org/dspace/content/authority/ChoiceAuthorityServiceImpl.java index f4d1f02710e1..bbe8e4461fe0 100644 --- a/dspace-api/src/main/java/org/dspace/content/authority/ChoiceAuthorityServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/content/authority/ChoiceAuthorityServiceImpl.java @@ -577,7 +577,7 @@ public DSpaceControlledVocabularyIndex getVocabularyIndex(String nameVocab) { .collect(Collectors.toList())); } DiscoverySearchFilterFacet matchingFacet = null; - for (DiscoverySearchFilterFacet facetConfig : searchConfigurationService.getAllFacetsConfig()) { + for (DiscoverySearchFilterFacet facetConfig : searchConfigurationService.getAllUniqueFacetsConfig()) { boolean coversAllFieldsFromVocab = true; for (String fieldFromVocab: metadataFields) { boolean coversFieldFromVocab = false; diff --git a/dspace-api/src/main/java/org/dspace/discovery/configuration/DiscoveryConfigurationService.java b/dspace-api/src/main/java/org/dspace/discovery/configuration/DiscoveryConfigurationService.java index 6cb93e2993f3..9d603941de39 100644 --- a/dspace-api/src/main/java/org/dspace/discovery/configuration/DiscoveryConfigurationService.java +++ b/dspace-api/src/main/java/org/dspace/discovery/configuration/DiscoveryConfigurationService.java @@ -10,8 +10,10 @@ import java.sql.SQLException; import java.util.ArrayList; import java.util.HashMap; +import java.util.LinkedHashSet; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; @@ -197,15 +199,19 @@ public List getIndexAlwaysConfigurations() { } /** - * @return All configurations for {@link org.dspace.discovery.configuration.DiscoverySearchFilterFacet} + * Get the unique set of configured Discovery facets. This is used when inspecting configuration + * to include hierarchical vocabularies in the browse menu. + * + * @return All unique instances of {@link org.dspace.discovery.configuration.DiscoverySearchFilterFacet} + * included in "sidebarFacets" bean, across all Discovery configurations. */ - public List getAllFacetsConfig() { - List configs = new ArrayList<>(); + public List getAllUniqueFacetsConfig() { + Set configs = new LinkedHashSet<>(); for (String key : map.keySet()) { DiscoveryConfiguration config = map.get(key); configs.addAll(config.getSidebarFacets()); } - return configs; + return new ArrayList<>(configs); } public static void main(String[] args) { From 98921724f41b3e18d21540bb34340c751ff3edd6 Mon Sep 17 00:00:00 2001 From: Kim Shepherd Date: Wed, 23 Apr 2025 18:55:29 +0200 Subject: [PATCH 581/979] Add help opt and javadoc to InitializeEntities (cherry picked from commit 5240a029965807e4757316aa1a965c4102c0d4f4) --- .../dspace/app/util/InitializeEntities.java | 76 +++++++++++++++++-- 1 file changed, 68 insertions(+), 8 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/app/util/InitializeEntities.java b/dspace-api/src/main/java/org/dspace/app/util/InitializeEntities.java index 0a072a9819eb..8d3964a3e3c7 100644 --- a/dspace-api/src/main/java/org/dspace/app/util/InitializeEntities.java +++ b/dspace-api/src/main/java/org/dspace/app/util/InitializeEntities.java @@ -64,20 +64,36 @@ private InitializeEntities() { */ public static void main(String[] argv) throws SQLException, AuthorizeException, ParseException { InitializeEntities initializeEntities = new InitializeEntities(); + // Set up command-line options and parse arguments CommandLineParser parser = new DefaultParser(); Options options = createCommandLineOptions(); CommandLine line = parser.parse(options,argv); - String fileLocation = getFileLocationFromCommandLine(line); + // First of all, check if the help option was entered or a required argument is missing checkHelpEntered(options, line); + // Get the file location from the command line + String fileLocation = getFileLocationFromCommandLine(line); + // Run the script initializeEntities.run(fileLocation); } + + /** + * Check if the help option was entered or a required argument is missing. If so, print help and exit. + * @param options the defined command-line options + * @param line the parsed command-line arguments + */ private static void checkHelpEntered(Options options, CommandLine line) { - if (line.hasOption("h")) { + if (line.hasOption("h") || !line.hasOption("f")) { HelpFormatter formatter = new HelpFormatter(); formatter.printHelp("Intialize Entities", options); System.exit(0); } } + + /** + * Get the file path from the command-line argument. Exits with exit code 1 if no file argument was entered. + * @param line the parsed command-line arguments + * @return the file path + */ private static String getFileLocationFromCommandLine(CommandLine line) { String query = line.getOptionValue("f"); if (StringUtils.isEmpty(query)) { @@ -88,13 +104,25 @@ private static String getFileLocationFromCommandLine(CommandLine line) { return query; } + /** + * Create the command-line options + * @return the command-line options + */ protected static Options createCommandLineOptions() { Options options = new Options(); - options.addOption("f", "file", true, "the location for the file containing the xml data"); + options.addOption("f", "file", true, "the path to the file containing the " + + "relationship definitions (e.g. ${dspace.dir}/config/entities/relationship-types.xml)"); + options.addOption("h", "help", false, "print this message"); return options; } + /** + * Run the script for the given file location + * @param fileLocation the file location + * @throws SQLException If something goes wrong initializing context or inserting relationship types + * @throws AuthorizeException If the script user fails to authorize while inserting relationship types + */ private void run(String fileLocation) throws SQLException, AuthorizeException { Context context = new Context(); context.turnOffAuthorisationSystem(); @@ -102,6 +130,12 @@ private void run(String fileLocation) throws SQLException, AuthorizeException { context.complete(); } + /** + * Parse the XML file at fileLocation to create relationship types in the database + * @param context DSpace context + * @param fileLocation the full or relative file path to the relationship types XML + * @throws AuthorizeException If the script user fails to authorize while inserting relationship types + */ private void parseXMLToRelations(Context context, String fileLocation) throws AuthorizeException { try { File fXmlFile = new File(fileLocation); @@ -158,15 +192,15 @@ private void parseXMLToRelations(Context context, String fileLocation) throws Au for (int j = 0; j < leftCardinalityList.getLength(); j++) { Node node = leftCardinalityList.item(j); - leftCardinalityMin = getString(leftCardinalityMin,(Element) node, "min"); - leftCardinalityMax = getString(leftCardinalityMax,(Element) node, "max"); + leftCardinalityMin = getCardinalityMinString(leftCardinalityMin,(Element) node, "min"); + leftCardinalityMax = getCardinalityMinString(leftCardinalityMax,(Element) node, "max"); } for (int j = 0; j < rightCardinalityList.getLength(); j++) { Node node = rightCardinalityList.item(j); - rightCardinalityMin = getString(rightCardinalityMin,(Element) node, "min"); - rightCardinalityMax = getString(rightCardinalityMax,(Element) node, "max"); + rightCardinalityMin = getCardinalityMinString(rightCardinalityMin,(Element) node, "min"); + rightCardinalityMax = getCardinalityMinString(rightCardinalityMax,(Element) node, "max"); } populateRelationshipType(context, leftType, rightType, leftwardType, rightwardType, @@ -182,13 +216,39 @@ private void parseXMLToRelations(Context context, String fileLocation) throws Au } } - private String getString(String leftCardinalityMin,Element node, String minOrMax) { + /** + * Extract the min or max value for the left or right cardinality from the node text content + * @param leftCardinalityMin current left cardinality min + * @param node node to extract the min or max value from + * @param minOrMax element tag name to parse + * @return final left cardinality min + */ + private String getCardinalityMinString(String leftCardinalityMin, Element node, String minOrMax) { if (node.getElementsByTagName(minOrMax).getLength() > 0) { leftCardinalityMin = node.getElementsByTagName(minOrMax).item(0).getTextContent(); } return leftCardinalityMin; } + /** + * Populate the relationship type based on values parsed from the XML relationship types configuration + * + * @param context DSpace context + * @param leftType left relationship type (e.g. "Publication"). + * @param rightType right relationship type (e.g. "Journal"). + * @param leftwardType leftward relationship type (e.g. "isAuthorOfPublication"). + * @param rightwardType rightward relationship type (e.g. "isPublicationOfAuthor"). + * @param leftCardinalityMin left cardinality min + * @param leftCardinalityMax left cardinality max + * @param rightCardinalityMin right cardinality min + * @param rightCardinalityMax right cardinality max + * @param copyToLeft copy metadata values to left if right side is deleted + * @param copyToRight copy metadata values to right if left side is deleted + * @param tilted set a tilted relationship side (left or right) if there are many relationships going one way + * to help performance (e.g. authors with 1000s of publications) + * @throws SQLException if database error occurs while saving the relationship type + * @throws AuthorizeException if authorization error occurs while saving the relationship type + */ private void populateRelationshipType(Context context, String leftType, String rightType, String leftwardType, String rightwardType, String leftCardinalityMin, String leftCardinalityMax, String rightCardinalityMin, String rightCardinalityMax, From fbb496e1c616e10dc667d794e19cac84e6e04913 Mon Sep 17 00:00:00 2001 From: Kim Shepherd Date: Wed, 23 Apr 2025 19:02:30 +0200 Subject: [PATCH 582/979] Improve help and docs for RegistryLoader And a few other small improvements (cherry picked from commit f1b4e6ef174559f901354176f934b05e3d698d58) --- .../org/dspace/administer/RegistryLoader.java | 132 +++++++++++++----- 1 file changed, 94 insertions(+), 38 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/administer/RegistryLoader.java b/dspace-api/src/main/java/org/dspace/administer/RegistryLoader.java index bbf320a0d5e5..d503bfc00b7f 100644 --- a/dspace-api/src/main/java/org/dspace/administer/RegistryLoader.java +++ b/dspace-api/src/main/java/org/dspace/administer/RegistryLoader.java @@ -21,6 +21,13 @@ import javax.xml.xpath.XPathExpressionException; import javax.xml.xpath.XPathFactory; +import org.apache.commons.cli.CommandLine; +import org.apache.commons.cli.CommandLineParser; +import org.apache.commons.cli.DefaultParser; +import org.apache.commons.cli.HelpFormatter; +import org.apache.commons.cli.Options; +import org.apache.commons.cli.ParseException; +import org.apache.commons.lang3.StringUtils; import org.apache.logging.log4j.Logger; import org.dspace.authorize.AuthorizeException; import org.dspace.content.BitstreamFormat; @@ -41,7 +48,7 @@ *

    * RegistryLoader -bitstream bitstream-formats.xml *

    - * RegistryLoader -dc dc-types.xml + * RegistryLoader -metadata dc-types.xml * * @author Robert Tansley * @version $Revision$ @@ -50,7 +57,7 @@ public class RegistryLoader { /** * log4j category */ - private static Logger log = org.apache.logging.log4j.LogManager.getLogger(RegistryLoader.class); + private static final Logger log = org.apache.logging.log4j.LogManager.getLogger(RegistryLoader.class); protected static BitstreamFormatService bitstreamFormatService = ContentServiceFactory.getInstance() .getBitstreamFormatService(); @@ -67,48 +74,97 @@ private RegistryLoader() { } * @throws Exception if error */ public static void main(String[] argv) throws Exception { - String usage = "Usage: " + RegistryLoader.class.getName() - + " (-bitstream | -metadata) registry-file.xml"; - - Context context = null; + // Set up command-line options and parse arguments + CommandLineParser parser = new DefaultParser(); + Options options = createCommandLineOptions(); try { - context = new Context(); + CommandLine line = parser.parse(options, argv); + + // Check if help option was entered or no options provided + if (line.hasOption('h') || line.getOptions().length == 0) { + printHelp(options); + System.exit(0); + } + + Context context = new Context(); // Can't update registries anonymously, so we need to turn off // authorisation context.turnOffAuthorisationSystem(); - // Work out what we're loading - if (argv[0].equalsIgnoreCase("-bitstream")) { - RegistryLoader.loadBitstreamFormats(context, argv[1]); - } else if (argv[0].equalsIgnoreCase("-metadata")) { - // Call MetadataImporter, as it handles Metadata schema updates - MetadataImporter.loadRegistry(argv[1], true); - } else { - System.err.println(usage); + try { + // Work out what we're loading + if (line.hasOption('b')) { + String filename = line.getOptionValue('b'); + if (StringUtils.isEmpty(filename)) { + System.err.println("No file path provided for bitstream format registry"); + printHelp(options); + System.exit(1); + } + RegistryLoader.loadBitstreamFormats(context, filename); + } else if (line.hasOption('m')) { + String filename = line.getOptionValue('m'); + if (StringUtils.isEmpty(filename)) { + System.err.println("No file path provided for metadata registry"); + printHelp(options); + System.exit(1); + } + // Call MetadataImporter, as it handles Metadata schema updates + MetadataImporter.loadRegistry(filename, true); + } else { + System.err.println("No registry type specified"); + printHelp(options); + System.exit(1); + } + + // Commit changes and close Context + context.complete(); + System.exit(0); + } catch (Exception e) { + log.fatal(LogHelper.getHeader(context, "error_loading_registries", ""), e); + System.err.println("Error: \n - " + e.getMessage()); + System.exit(1); + } finally { + // Clean up our context, if it still exists & it was never completed + if (context != null && context.isValid()) { + context.abort(); + } } + } catch (ParseException e) { + System.err.println("Error parsing command-line arguments: " + e.getMessage()); + printHelp(options); + System.exit(1); + } + } - // Commit changes and close Context - context.complete(); + /** + * Create the command-line options + * @return the command-line options + */ + private static Options createCommandLineOptions() { + Options options = new Options(); - System.exit(0); - } catch (ArrayIndexOutOfBoundsException ae) { - System.err.println(usage); + options.addOption("b", "bitstream", true, "load bitstream format registry from specified file"); + options.addOption("m", "metadata", true, "load metadata registry from specified file"); + options.addOption("h", "help", false, "print this help message"); - System.exit(1); - } catch (Exception e) { - log.fatal(LogHelper.getHeader(context, "error_loading_registries", - ""), e); + return options; + } - System.err.println("Error: \n - " + e.getMessage()); - System.exit(1); - } finally { - // Clean up our context, if it still exists & it was never completed - if (context != null && context.isValid()) { - context.abort(); - } - } + /** + * Print the help message + * @param options the command-line options + */ + private static void printHelp(Options options) { + HelpFormatter formatter = new HelpFormatter(); + formatter.printHelp("RegistryLoader", + "Load bitstream format or metadata registries into the database\n", + options, + "\nExamples:\n" + + " RegistryLoader -b bitstream-formats.xml\n" + + " RegistryLoader -m dc-types.xml", + true); } /** @@ -221,7 +277,7 @@ private static Document loadXML(String filename) throws IOException, * contains: *

    * - * <foo><mimetype>application/pdf</mimetype></foo> + * application/pdf * * passing this the foo node and mimetype will * return application/pdf. @@ -262,10 +318,10 @@ private static String getElementData(Node parentElement, String childName) * document contains: *

    * - * <foo> - * <bar>val1</bar> - * <bar>val2</bar> - * </foo> + * + * val1 + * val2 + * * * passing this the foo node and bar will * return val1 and val2. @@ -295,4 +351,4 @@ private static String[] getRepeatedElementData(Node parentElement, return data; } -} +} \ No newline at end of file From 85a9e4b731982df482f88277027748a2c0b2e0d9 Mon Sep 17 00:00:00 2001 From: Kim Shepherd Date: Wed, 23 Apr 2025 19:22:12 +0200 Subject: [PATCH 583/979] Let Curation CLI accept uuid identifiers (cherry picked from commit 5020689095549054c987c1fe4481a37aa86f3877) --- .../main/java/org/dspace/curate/Curation.java | 31 +++++++++++++++++-- 1 file changed, 28 insertions(+), 3 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/curate/Curation.java b/dspace-api/src/main/java/org/dspace/curate/Curation.java index 625692a866b3..b894dcd85f03 100644 --- a/dspace-api/src/main/java/org/dspace/curate/Curation.java +++ b/dspace-api/src/main/java/org/dspace/curate/Curation.java @@ -24,6 +24,8 @@ import org.apache.commons.cli.ParseException; import org.apache.commons.io.output.NullOutputStream; +import org.dspace.app.util.DSpaceObjectUtilsImpl; +import org.dspace.app.util.service.DSpaceObjectUtils; import org.dspace.authorize.AuthorizeException; import org.dspace.content.DSpaceObject; import org.dspace.content.factory.ContentServiceFactory; @@ -35,6 +37,7 @@ import org.dspace.handle.factory.HandleServiceFactory; import org.dspace.handle.service.HandleService; import org.dspace.scripts.DSpaceRunnable; +import org.dspace.services.factory.DSpaceServicesFactory; import org.dspace.utils.DSpace; /** @@ -45,7 +48,9 @@ public class Curation extends DSpaceRunnable { protected EPersonService ePersonService = EPersonServiceFactory.getInstance().getEPersonService(); - + protected DSpaceObjectUtils dspaceObjectUtils = DSpaceServicesFactory.getInstance().getServiceManager() + .getServiceByName(DSpaceObjectUtilsImpl.class.getName(), DSpaceObjectUtilsImpl.class); + HandleService handleService = HandleServiceFactory.getInstance().getHandleService(); protected Context context; private CurationClientOptions curationClientOptions; @@ -345,9 +350,29 @@ private void initTaskLineOptionsAndCheckIfValid() { if (this.commandLine.hasOption('i')) { this.id = this.commandLine.getOptionValue('i').toLowerCase(); + DSpaceObject dso; if (!this.id.equalsIgnoreCase("all")) { - HandleService handleService = HandleServiceFactory.getInstance().getHandleService(); - DSpaceObject dso; + // First, try to parse the id as a UUID. If that fails, treat it as a handle. + UUID uuid = null; + try { + uuid = UUID.fromString(id); + } catch (Exception e) { + // It's not a UUID, proceed to treat it as a handle. + } + if (uuid != null) { + try { + dso = dspaceObjectUtils.findDSpaceObject(context, uuid); + if (dso != null) { + // We already resolved an object, return early + return; + } + } catch (SQLException e) { + String error = "SQLException trying to find dso with uuid " + uuid; + super.handler.logError(error); + throw new RuntimeException(error, e); + } + } + // If we get here, the id is not a UUID, so we assume it's a handle. try { dso = handleService.resolveToObject(this.context, id); } catch (SQLException e) { From 1f2354a3dc1ebbc239bb9f48ad1b322be74742b7 Mon Sep 17 00:00:00 2001 From: Kim Shepherd Date: Wed, 23 Apr 2025 18:55:29 +0200 Subject: [PATCH 584/979] Add help opt and javadoc to InitializeEntities (cherry picked from commit 5240a029965807e4757316aa1a965c4102c0d4f4) --- .../dspace/app/util/InitializeEntities.java | 76 +++++++++++++++++-- 1 file changed, 68 insertions(+), 8 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/app/util/InitializeEntities.java b/dspace-api/src/main/java/org/dspace/app/util/InitializeEntities.java index 0a072a9819eb..8d3964a3e3c7 100644 --- a/dspace-api/src/main/java/org/dspace/app/util/InitializeEntities.java +++ b/dspace-api/src/main/java/org/dspace/app/util/InitializeEntities.java @@ -64,20 +64,36 @@ private InitializeEntities() { */ public static void main(String[] argv) throws SQLException, AuthorizeException, ParseException { InitializeEntities initializeEntities = new InitializeEntities(); + // Set up command-line options and parse arguments CommandLineParser parser = new DefaultParser(); Options options = createCommandLineOptions(); CommandLine line = parser.parse(options,argv); - String fileLocation = getFileLocationFromCommandLine(line); + // First of all, check if the help option was entered or a required argument is missing checkHelpEntered(options, line); + // Get the file location from the command line + String fileLocation = getFileLocationFromCommandLine(line); + // Run the script initializeEntities.run(fileLocation); } + + /** + * Check if the help option was entered or a required argument is missing. If so, print help and exit. + * @param options the defined command-line options + * @param line the parsed command-line arguments + */ private static void checkHelpEntered(Options options, CommandLine line) { - if (line.hasOption("h")) { + if (line.hasOption("h") || !line.hasOption("f")) { HelpFormatter formatter = new HelpFormatter(); formatter.printHelp("Intialize Entities", options); System.exit(0); } } + + /** + * Get the file path from the command-line argument. Exits with exit code 1 if no file argument was entered. + * @param line the parsed command-line arguments + * @return the file path + */ private static String getFileLocationFromCommandLine(CommandLine line) { String query = line.getOptionValue("f"); if (StringUtils.isEmpty(query)) { @@ -88,13 +104,25 @@ private static String getFileLocationFromCommandLine(CommandLine line) { return query; } + /** + * Create the command-line options + * @return the command-line options + */ protected static Options createCommandLineOptions() { Options options = new Options(); - options.addOption("f", "file", true, "the location for the file containing the xml data"); + options.addOption("f", "file", true, "the path to the file containing the " + + "relationship definitions (e.g. ${dspace.dir}/config/entities/relationship-types.xml)"); + options.addOption("h", "help", false, "print this message"); return options; } + /** + * Run the script for the given file location + * @param fileLocation the file location + * @throws SQLException If something goes wrong initializing context or inserting relationship types + * @throws AuthorizeException If the script user fails to authorize while inserting relationship types + */ private void run(String fileLocation) throws SQLException, AuthorizeException { Context context = new Context(); context.turnOffAuthorisationSystem(); @@ -102,6 +130,12 @@ private void run(String fileLocation) throws SQLException, AuthorizeException { context.complete(); } + /** + * Parse the XML file at fileLocation to create relationship types in the database + * @param context DSpace context + * @param fileLocation the full or relative file path to the relationship types XML + * @throws AuthorizeException If the script user fails to authorize while inserting relationship types + */ private void parseXMLToRelations(Context context, String fileLocation) throws AuthorizeException { try { File fXmlFile = new File(fileLocation); @@ -158,15 +192,15 @@ private void parseXMLToRelations(Context context, String fileLocation) throws Au for (int j = 0; j < leftCardinalityList.getLength(); j++) { Node node = leftCardinalityList.item(j); - leftCardinalityMin = getString(leftCardinalityMin,(Element) node, "min"); - leftCardinalityMax = getString(leftCardinalityMax,(Element) node, "max"); + leftCardinalityMin = getCardinalityMinString(leftCardinalityMin,(Element) node, "min"); + leftCardinalityMax = getCardinalityMinString(leftCardinalityMax,(Element) node, "max"); } for (int j = 0; j < rightCardinalityList.getLength(); j++) { Node node = rightCardinalityList.item(j); - rightCardinalityMin = getString(rightCardinalityMin,(Element) node, "min"); - rightCardinalityMax = getString(rightCardinalityMax,(Element) node, "max"); + rightCardinalityMin = getCardinalityMinString(rightCardinalityMin,(Element) node, "min"); + rightCardinalityMax = getCardinalityMinString(rightCardinalityMax,(Element) node, "max"); } populateRelationshipType(context, leftType, rightType, leftwardType, rightwardType, @@ -182,13 +216,39 @@ private void parseXMLToRelations(Context context, String fileLocation) throws Au } } - private String getString(String leftCardinalityMin,Element node, String minOrMax) { + /** + * Extract the min or max value for the left or right cardinality from the node text content + * @param leftCardinalityMin current left cardinality min + * @param node node to extract the min or max value from + * @param minOrMax element tag name to parse + * @return final left cardinality min + */ + private String getCardinalityMinString(String leftCardinalityMin, Element node, String minOrMax) { if (node.getElementsByTagName(minOrMax).getLength() > 0) { leftCardinalityMin = node.getElementsByTagName(minOrMax).item(0).getTextContent(); } return leftCardinalityMin; } + /** + * Populate the relationship type based on values parsed from the XML relationship types configuration + * + * @param context DSpace context + * @param leftType left relationship type (e.g. "Publication"). + * @param rightType right relationship type (e.g. "Journal"). + * @param leftwardType leftward relationship type (e.g. "isAuthorOfPublication"). + * @param rightwardType rightward relationship type (e.g. "isPublicationOfAuthor"). + * @param leftCardinalityMin left cardinality min + * @param leftCardinalityMax left cardinality max + * @param rightCardinalityMin right cardinality min + * @param rightCardinalityMax right cardinality max + * @param copyToLeft copy metadata values to left if right side is deleted + * @param copyToRight copy metadata values to right if left side is deleted + * @param tilted set a tilted relationship side (left or right) if there are many relationships going one way + * to help performance (e.g. authors with 1000s of publications) + * @throws SQLException if database error occurs while saving the relationship type + * @throws AuthorizeException if authorization error occurs while saving the relationship type + */ private void populateRelationshipType(Context context, String leftType, String rightType, String leftwardType, String rightwardType, String leftCardinalityMin, String leftCardinalityMax, String rightCardinalityMin, String rightCardinalityMax, From 505cb9bbd57693bfcdf80c7ca29d4f8c671687fb Mon Sep 17 00:00:00 2001 From: Kim Shepherd Date: Wed, 23 Apr 2025 19:02:30 +0200 Subject: [PATCH 585/979] Improve help and docs for RegistryLoader And a few other small improvements (cherry picked from commit f1b4e6ef174559f901354176f934b05e3d698d58) --- .../org/dspace/administer/RegistryLoader.java | 132 +++++++++++++----- 1 file changed, 94 insertions(+), 38 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/administer/RegistryLoader.java b/dspace-api/src/main/java/org/dspace/administer/RegistryLoader.java index bbf320a0d5e5..d503bfc00b7f 100644 --- a/dspace-api/src/main/java/org/dspace/administer/RegistryLoader.java +++ b/dspace-api/src/main/java/org/dspace/administer/RegistryLoader.java @@ -21,6 +21,13 @@ import javax.xml.xpath.XPathExpressionException; import javax.xml.xpath.XPathFactory; +import org.apache.commons.cli.CommandLine; +import org.apache.commons.cli.CommandLineParser; +import org.apache.commons.cli.DefaultParser; +import org.apache.commons.cli.HelpFormatter; +import org.apache.commons.cli.Options; +import org.apache.commons.cli.ParseException; +import org.apache.commons.lang3.StringUtils; import org.apache.logging.log4j.Logger; import org.dspace.authorize.AuthorizeException; import org.dspace.content.BitstreamFormat; @@ -41,7 +48,7 @@ *

    * RegistryLoader -bitstream bitstream-formats.xml *

    - * RegistryLoader -dc dc-types.xml + * RegistryLoader -metadata dc-types.xml * * @author Robert Tansley * @version $Revision$ @@ -50,7 +57,7 @@ public class RegistryLoader { /** * log4j category */ - private static Logger log = org.apache.logging.log4j.LogManager.getLogger(RegistryLoader.class); + private static final Logger log = org.apache.logging.log4j.LogManager.getLogger(RegistryLoader.class); protected static BitstreamFormatService bitstreamFormatService = ContentServiceFactory.getInstance() .getBitstreamFormatService(); @@ -67,48 +74,97 @@ private RegistryLoader() { } * @throws Exception if error */ public static void main(String[] argv) throws Exception { - String usage = "Usage: " + RegistryLoader.class.getName() - + " (-bitstream | -metadata) registry-file.xml"; - - Context context = null; + // Set up command-line options and parse arguments + CommandLineParser parser = new DefaultParser(); + Options options = createCommandLineOptions(); try { - context = new Context(); + CommandLine line = parser.parse(options, argv); + + // Check if help option was entered or no options provided + if (line.hasOption('h') || line.getOptions().length == 0) { + printHelp(options); + System.exit(0); + } + + Context context = new Context(); // Can't update registries anonymously, so we need to turn off // authorisation context.turnOffAuthorisationSystem(); - // Work out what we're loading - if (argv[0].equalsIgnoreCase("-bitstream")) { - RegistryLoader.loadBitstreamFormats(context, argv[1]); - } else if (argv[0].equalsIgnoreCase("-metadata")) { - // Call MetadataImporter, as it handles Metadata schema updates - MetadataImporter.loadRegistry(argv[1], true); - } else { - System.err.println(usage); + try { + // Work out what we're loading + if (line.hasOption('b')) { + String filename = line.getOptionValue('b'); + if (StringUtils.isEmpty(filename)) { + System.err.println("No file path provided for bitstream format registry"); + printHelp(options); + System.exit(1); + } + RegistryLoader.loadBitstreamFormats(context, filename); + } else if (line.hasOption('m')) { + String filename = line.getOptionValue('m'); + if (StringUtils.isEmpty(filename)) { + System.err.println("No file path provided for metadata registry"); + printHelp(options); + System.exit(1); + } + // Call MetadataImporter, as it handles Metadata schema updates + MetadataImporter.loadRegistry(filename, true); + } else { + System.err.println("No registry type specified"); + printHelp(options); + System.exit(1); + } + + // Commit changes and close Context + context.complete(); + System.exit(0); + } catch (Exception e) { + log.fatal(LogHelper.getHeader(context, "error_loading_registries", ""), e); + System.err.println("Error: \n - " + e.getMessage()); + System.exit(1); + } finally { + // Clean up our context, if it still exists & it was never completed + if (context != null && context.isValid()) { + context.abort(); + } } + } catch (ParseException e) { + System.err.println("Error parsing command-line arguments: " + e.getMessage()); + printHelp(options); + System.exit(1); + } + } - // Commit changes and close Context - context.complete(); + /** + * Create the command-line options + * @return the command-line options + */ + private static Options createCommandLineOptions() { + Options options = new Options(); - System.exit(0); - } catch (ArrayIndexOutOfBoundsException ae) { - System.err.println(usage); + options.addOption("b", "bitstream", true, "load bitstream format registry from specified file"); + options.addOption("m", "metadata", true, "load metadata registry from specified file"); + options.addOption("h", "help", false, "print this help message"); - System.exit(1); - } catch (Exception e) { - log.fatal(LogHelper.getHeader(context, "error_loading_registries", - ""), e); + return options; + } - System.err.println("Error: \n - " + e.getMessage()); - System.exit(1); - } finally { - // Clean up our context, if it still exists & it was never completed - if (context != null && context.isValid()) { - context.abort(); - } - } + /** + * Print the help message + * @param options the command-line options + */ + private static void printHelp(Options options) { + HelpFormatter formatter = new HelpFormatter(); + formatter.printHelp("RegistryLoader", + "Load bitstream format or metadata registries into the database\n", + options, + "\nExamples:\n" + + " RegistryLoader -b bitstream-formats.xml\n" + + " RegistryLoader -m dc-types.xml", + true); } /** @@ -221,7 +277,7 @@ private static Document loadXML(String filename) throws IOException, * contains: *

    * - * <foo><mimetype>application/pdf</mimetype></foo> + * application/pdf * * passing this the foo node and mimetype will * return application/pdf. @@ -262,10 +318,10 @@ private static String getElementData(Node parentElement, String childName) * document contains: *

    * - * <foo> - * <bar>val1</bar> - * <bar>val2</bar> - * </foo> + * + * val1 + * val2 + * * * passing this the foo node and bar will * return val1 and val2. @@ -295,4 +351,4 @@ private static String[] getRepeatedElementData(Node parentElement, return data; } -} +} \ No newline at end of file From b9a7226d554380e9d3e7293ee7f9c037d1a11b74 Mon Sep 17 00:00:00 2001 From: Kim Shepherd Date: Wed, 23 Apr 2025 19:22:12 +0200 Subject: [PATCH 586/979] Let Curation CLI accept uuid identifiers (cherry picked from commit 5020689095549054c987c1fe4481a37aa86f3877) --- .../main/java/org/dspace/curate/Curation.java | 31 +++++++++++++++++-- 1 file changed, 28 insertions(+), 3 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/curate/Curation.java b/dspace-api/src/main/java/org/dspace/curate/Curation.java index 625692a866b3..b894dcd85f03 100644 --- a/dspace-api/src/main/java/org/dspace/curate/Curation.java +++ b/dspace-api/src/main/java/org/dspace/curate/Curation.java @@ -24,6 +24,8 @@ import org.apache.commons.cli.ParseException; import org.apache.commons.io.output.NullOutputStream; +import org.dspace.app.util.DSpaceObjectUtilsImpl; +import org.dspace.app.util.service.DSpaceObjectUtils; import org.dspace.authorize.AuthorizeException; import org.dspace.content.DSpaceObject; import org.dspace.content.factory.ContentServiceFactory; @@ -35,6 +37,7 @@ import org.dspace.handle.factory.HandleServiceFactory; import org.dspace.handle.service.HandleService; import org.dspace.scripts.DSpaceRunnable; +import org.dspace.services.factory.DSpaceServicesFactory; import org.dspace.utils.DSpace; /** @@ -45,7 +48,9 @@ public class Curation extends DSpaceRunnable { protected EPersonService ePersonService = EPersonServiceFactory.getInstance().getEPersonService(); - + protected DSpaceObjectUtils dspaceObjectUtils = DSpaceServicesFactory.getInstance().getServiceManager() + .getServiceByName(DSpaceObjectUtilsImpl.class.getName(), DSpaceObjectUtilsImpl.class); + HandleService handleService = HandleServiceFactory.getInstance().getHandleService(); protected Context context; private CurationClientOptions curationClientOptions; @@ -345,9 +350,29 @@ private void initTaskLineOptionsAndCheckIfValid() { if (this.commandLine.hasOption('i')) { this.id = this.commandLine.getOptionValue('i').toLowerCase(); + DSpaceObject dso; if (!this.id.equalsIgnoreCase("all")) { - HandleService handleService = HandleServiceFactory.getInstance().getHandleService(); - DSpaceObject dso; + // First, try to parse the id as a UUID. If that fails, treat it as a handle. + UUID uuid = null; + try { + uuid = UUID.fromString(id); + } catch (Exception e) { + // It's not a UUID, proceed to treat it as a handle. + } + if (uuid != null) { + try { + dso = dspaceObjectUtils.findDSpaceObject(context, uuid); + if (dso != null) { + // We already resolved an object, return early + return; + } + } catch (SQLException e) { + String error = "SQLException trying to find dso with uuid " + uuid; + super.handler.logError(error); + throw new RuntimeException(error, e); + } + } + // If we get here, the id is not a UUID, so we assume it's a handle. try { dso = handleService.resolveToObject(this.context, id); } catch (SQLException e) { From 6fe9af84bdb291bff8ddcf510372aab157c6b30e Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Tue, 29 Apr 2025 14:51:19 -0500 Subject: [PATCH 587/979] Potential fix for code scanning alert no. 30: Resolving XML external entity in user-controlled data Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> (cherry picked from commit a0ce50b2a497dcb1711f48ad35cda14eeabf686f) --- .../pubmed/service/PubmedImportMetadataSourceServiceImpl.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/dspace-api/src/main/java/org/dspace/importer/external/pubmed/service/PubmedImportMetadataSourceServiceImpl.java b/dspace-api/src/main/java/org/dspace/importer/external/pubmed/service/PubmedImportMetadataSourceServiceImpl.java index a6cfa625bbcf..13201b8fcde3 100644 --- a/dspace-api/src/main/java/org/dspace/importer/external/pubmed/service/PubmedImportMetadataSourceServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/importer/external/pubmed/service/PubmedImportMetadataSourceServiceImpl.java @@ -234,6 +234,8 @@ private String getSingleElementValue(String src, String elementName) { try { SAXBuilder saxBuilder = new SAXBuilder(); + saxBuilder.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true); + saxBuilder.setFeature("http://xml.org/sax/features/external-general-entities", false); Document document = saxBuilder.build(new StringReader(src)); Element root = document.getRootElement(); From 90ea371e0b0a12a245b094ea057bbcf4117f9849 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Tue, 29 Apr 2025 16:57:31 -0500 Subject: [PATCH 588/979] Cannot disable DTDs with PubMed, so instead disallow external entities & entity expansion (cherry picked from commit f9614c41a6ceaa54756f780164fec40a3b185483) --- .../pubmed/service/PubmedImportMetadataSourceServiceImpl.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/dspace-api/src/main/java/org/dspace/importer/external/pubmed/service/PubmedImportMetadataSourceServiceImpl.java b/dspace-api/src/main/java/org/dspace/importer/external/pubmed/service/PubmedImportMetadataSourceServiceImpl.java index 13201b8fcde3..000ef19eaec5 100644 --- a/dspace-api/src/main/java/org/dspace/importer/external/pubmed/service/PubmedImportMetadataSourceServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/importer/external/pubmed/service/PubmedImportMetadataSourceServiceImpl.java @@ -234,8 +234,10 @@ private String getSingleElementValue(String src, String elementName) { try { SAXBuilder saxBuilder = new SAXBuilder(); - saxBuilder.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true); + // Disallow external entities & entity expansion to protect against XXE attacks + // (NOTE: We receive errors if we disable all DTDs for PubMed, so this is the best we can do) saxBuilder.setFeature("http://xml.org/sax/features/external-general-entities", false); + saxBuilder.setFeature("http://xml.org/sax/features/external-parameter-entities", false); Document document = saxBuilder.build(new StringReader(src)); Element root = document.getRootElement(); From ce11bc9dbdad286b0ef2c690765b63ab1c60b836 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Tue, 29 Apr 2025 14:51:19 -0500 Subject: [PATCH 589/979] Potential fix for code scanning alert no. 30: Resolving XML external entity in user-controlled data Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> (cherry picked from commit a0ce50b2a497dcb1711f48ad35cda14eeabf686f) --- .../pubmed/service/PubmedImportMetadataSourceServiceImpl.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/dspace-api/src/main/java/org/dspace/importer/external/pubmed/service/PubmedImportMetadataSourceServiceImpl.java b/dspace-api/src/main/java/org/dspace/importer/external/pubmed/service/PubmedImportMetadataSourceServiceImpl.java index a6cfa625bbcf..13201b8fcde3 100644 --- a/dspace-api/src/main/java/org/dspace/importer/external/pubmed/service/PubmedImportMetadataSourceServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/importer/external/pubmed/service/PubmedImportMetadataSourceServiceImpl.java @@ -234,6 +234,8 @@ private String getSingleElementValue(String src, String elementName) { try { SAXBuilder saxBuilder = new SAXBuilder(); + saxBuilder.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true); + saxBuilder.setFeature("http://xml.org/sax/features/external-general-entities", false); Document document = saxBuilder.build(new StringReader(src)); Element root = document.getRootElement(); From d09b12280588279cf28dc13d3da7557de06c39d0 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Tue, 29 Apr 2025 16:57:31 -0500 Subject: [PATCH 590/979] Cannot disable DTDs with PubMed, so instead disallow external entities & entity expansion (cherry picked from commit f9614c41a6ceaa54756f780164fec40a3b185483) --- .../pubmed/service/PubmedImportMetadataSourceServiceImpl.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/dspace-api/src/main/java/org/dspace/importer/external/pubmed/service/PubmedImportMetadataSourceServiceImpl.java b/dspace-api/src/main/java/org/dspace/importer/external/pubmed/service/PubmedImportMetadataSourceServiceImpl.java index 13201b8fcde3..000ef19eaec5 100644 --- a/dspace-api/src/main/java/org/dspace/importer/external/pubmed/service/PubmedImportMetadataSourceServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/importer/external/pubmed/service/PubmedImportMetadataSourceServiceImpl.java @@ -234,8 +234,10 @@ private String getSingleElementValue(String src, String elementName) { try { SAXBuilder saxBuilder = new SAXBuilder(); - saxBuilder.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true); + // Disallow external entities & entity expansion to protect against XXE attacks + // (NOTE: We receive errors if we disable all DTDs for PubMed, so this is the best we can do) saxBuilder.setFeature("http://xml.org/sax/features/external-general-entities", false); + saxBuilder.setFeature("http://xml.org/sax/features/external-parameter-entities", false); Document document = saxBuilder.build(new StringReader(src)); Element root = document.getRootElement(); From 822a27d830cdeddd99ae8be74e4e8d0620023fd0 Mon Sep 17 00:00:00 2001 From: Agustina Martinez Date: Tue, 15 Apr 2025 08:59:45 +0100 Subject: [PATCH 591/979] Update ItemUtils.java Do not include start-date info if embargo is expired (cherry picked from commit c167e5c10a92954bcb6e479a8a6cfab345cacaaf) --- dspace-oai/src/main/java/org/dspace/xoai/util/ItemUtils.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/dspace-oai/src/main/java/org/dspace/xoai/util/ItemUtils.java b/dspace-oai/src/main/java/org/dspace/xoai/util/ItemUtils.java index 20dcabcb20c8..498d2f54210a 100644 --- a/dspace-oai/src/main/java/org/dspace/xoai/util/ItemUtils.java +++ b/dspace-oai/src/main/java/org/dspace/xoai/util/ItemUtils.java @@ -39,6 +39,7 @@ import org.dspace.core.Constants; import org.dspace.core.Context; import org.dspace.core.Utils; +import org.dspace.eperson.Group; import org.dspace.services.ConfigurationService; import org.dspace.services.factory.DSpaceServicesFactory; import org.dspace.xoai.data.DSpaceItem; @@ -194,7 +195,8 @@ private static void addResourcePolicyInformation(Context context, Bitstream bits resourcePolicyEl.getField().add(createValue("group", groupName)); resourcePolicyEl.getField().add(createValue("user", user)); resourcePolicyEl.getField().add(createValue("action", action)); - if (startDate != null) { + // Only add start-date if group is different to anonymous, or there is an active embargo + if (startDate != null && (!groupName.equals(Group.ANONYMOUS) || startDate.isAfter(LocalDate.now()))) { resourcePolicyEl.getField().add(createValue("start-date", formatter.format(startDate))); } if (endDate != null) { From edf43a41c1612373c65cf64a9f6d0bf897a818ce Mon Sep 17 00:00:00 2001 From: Agustina Martinez Date: Wed, 30 Apr 2025 09:55:21 +0100 Subject: [PATCH 592/979] Remove unnecessary check of group (cherry picked from commit 6831aa11c17bb1a1a4c1382296b8471a5acdd6b8) --- dspace-oai/src/main/java/org/dspace/xoai/util/ItemUtils.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/dspace-oai/src/main/java/org/dspace/xoai/util/ItemUtils.java b/dspace-oai/src/main/java/org/dspace/xoai/util/ItemUtils.java index 498d2f54210a..6eda37e62fbe 100644 --- a/dspace-oai/src/main/java/org/dspace/xoai/util/ItemUtils.java +++ b/dspace-oai/src/main/java/org/dspace/xoai/util/ItemUtils.java @@ -39,7 +39,6 @@ import org.dspace.core.Constants; import org.dspace.core.Context; import org.dspace.core.Utils; -import org.dspace.eperson.Group; import org.dspace.services.ConfigurationService; import org.dspace.services.factory.DSpaceServicesFactory; import org.dspace.xoai.data.DSpaceItem; @@ -196,7 +195,7 @@ private static void addResourcePolicyInformation(Context context, Bitstream bits resourcePolicyEl.getField().add(createValue("user", user)); resourcePolicyEl.getField().add(createValue("action", action)); // Only add start-date if group is different to anonymous, or there is an active embargo - if (startDate != null && (!groupName.equals(Group.ANONYMOUS) || startDate.isAfter(LocalDate.now()))) { + if (startDate != null && startDate.isAfter(LocalDate.now())) { resourcePolicyEl.getField().add(createValue("start-date", formatter.format(startDate))); } if (endDate != null) { From c6098c0232d73144804663df2ba5eb401efa9dbb Mon Sep 17 00:00:00 2001 From: Stefano Maffei Date: Tue, 29 Apr 2025 11:57:38 +0200 Subject: [PATCH 593/979] [DURACOM-357] fix Collection Admin cannot see withdrawn item metadata (cherry picked from commit 5e2bb4fb9270a22a28a39ebcbcbac3f2781f097d) --- .../app/rest/converter/ItemConverter.java | 6 +- .../dspace/app/rest/ItemRestRepositoryIT.java | 62 +++++++++++++++++++ 2 files changed, 65 insertions(+), 3 deletions(-) diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/ItemConverter.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/ItemConverter.java index fc64b66e8a16..38f829be3476 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/ItemConverter.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/ItemConverter.java @@ -8,7 +8,6 @@ package org.dspace.app.rest.converter; import java.sql.SQLException; -import java.util.ArrayList; import java.util.LinkedList; import java.util.List; import java.util.Objects; @@ -76,8 +75,9 @@ public MetadataValueList getPermissionFilteredMetadata(Context context, Item obj List returnList = new LinkedList<>(); try { if (obj.isWithdrawn() && (Objects.isNull(context) || - Objects.isNull(context.getCurrentUser()) || !authorizeService.isAdmin(context))) { - return new MetadataValueList(new ArrayList()); + Objects.isNull(context.getCurrentUser()) || + !(authorizeService.isAdmin(context) || authorizeService.isCollectionAdmin(context)))) { + return new MetadataValueList(List.of()); } if (context != null && (authorizeService.isAdmin(context) || itemService.canEdit(context, obj))) { return new MetadataValueList(fullList); diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/ItemRestRepositoryIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/ItemRestRepositoryIT.java index 714ad0b419a1..48e70a8d5f1f 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/ItemRestRepositoryIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/ItemRestRepositoryIT.java @@ -422,6 +422,68 @@ public void findOneTest() throws Exception { .andExpect(jsonPath("$", publicItem1Matcher)); } + @Test + public void findOneWithdrawnAsCollectionAdminTest() throws Exception { + context.turnOffAuthorisationSystem(); + + // Create collection admin account + EPerson collectionAdmin = EPersonBuilder.createEPerson(context) + .withEmail("collection-admin@dspace.com") + .withPassword("test") + .withCanLogin(true) + .build(); + parentCommunity = CommunityBuilder.createCommunity(context) + .withName("Parent Community") + .build(); + Community child1 = CommunityBuilder.createSubCommunity(context, parentCommunity) + .withName("Sub Community") + .build(); + + // Create collection + Collection adminCollection = CollectionBuilder.createCollection(context, child1) + .withName("Collection Admin col") + .withAdminGroup(collectionAdmin) + .build(); + Collection noAdminCollection = + CollectionBuilder.createCollection(context, child1).withName("Collection non Admin") + .build(); + + // both items are withdrawn + Item administeredItem = ItemBuilder.createItem(context, adminCollection) + .withTitle("Public item 1") + .withIssueDate("2017-10-17") + .withAuthor("Smith, Donald").withAuthor("Doe, John") + .withSubject("ExtraEntry") + .withdrawn() + .build(); + + Item nonAdministeredItem = ItemBuilder.createItem(context, noAdminCollection) + .withTitle("Public item 2") + .withIssueDate("2016-02-13") + .withAuthor("Smith, Maria").withAuthor("Doe, Jane") + .withSubject("TestingForMore").withSubject("ExtraEntry") + .withdrawn() + .build(); + + context.restoreAuthSystemState(); + + String collectionAdmintoken = getAuthToken(collectionAdmin.getEmail(), "test"); + + // Metadata are retrieved since user is administering the item's collection + getClient(collectionAdmintoken).perform(get("/api/core/items/" + administeredItem.getID()) + .param("projection", "full")) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.metadata").isNotEmpty()); + + // No metadata is retrieved since user is not administering the item's collection + getClient().perform(get("/api/core/items/" + nonAdministeredItem.getID()) + .param("projection", "full")) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.metadata").isEmpty()); + + + } + @Test public void findOneFullProjectionTest() throws Exception { context.turnOffAuthorisationSystem(); From 8ca8bd4543a9727173278f9752cf635fb2abd292 Mon Sep 17 00:00:00 2001 From: Stefano Maffei Date: Wed, 30 Apr 2025 08:31:01 +0200 Subject: [PATCH 594/979] [DURACOM-357] improved check for authorization on objects in ItemConverter (cherry picked from commit a70dede20b9310ec85ec6f441be16c0437c796cf) --- .../main/java/org/dspace/app/rest/converter/ItemConverter.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/ItemConverter.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/ItemConverter.java index 38f829be3476..0c5a36d5fb5a 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/ItemConverter.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/ItemConverter.java @@ -76,7 +76,7 @@ public MetadataValueList getPermissionFilteredMetadata(Context context, Item obj try { if (obj.isWithdrawn() && (Objects.isNull(context) || Objects.isNull(context.getCurrentUser()) || - !(authorizeService.isAdmin(context) || authorizeService.isCollectionAdmin(context)))) { + !(authorizeService.isAdmin(context) || authorizeService.isAdmin(context, obj)))) { return new MetadataValueList(List.of()); } if (context != null && (authorizeService.isAdmin(context) || itemService.canEdit(context, obj))) { From 2429a0ba2932296d7a2f680743fd4106115d03a3 Mon Sep 17 00:00:00 2001 From: Stefano Maffei Date: Wed, 30 Apr 2025 08:56:31 +0200 Subject: [PATCH 595/979] [DURACOM-357] improved admin check (cherry picked from commit bb3935a0473a13fd1804dd6c88cc49a9196809bb) --- .../main/java/org/dspace/app/rest/converter/ItemConverter.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/ItemConverter.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/ItemConverter.java index 0c5a36d5fb5a..abcd707118c8 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/ItemConverter.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/ItemConverter.java @@ -76,7 +76,7 @@ public MetadataValueList getPermissionFilteredMetadata(Context context, Item obj try { if (obj.isWithdrawn() && (Objects.isNull(context) || Objects.isNull(context.getCurrentUser()) || - !(authorizeService.isAdmin(context) || authorizeService.isAdmin(context, obj)))) { + !authorizeService.isAdmin(context, obj))) { return new MetadataValueList(List.of()); } if (context != null && (authorizeService.isAdmin(context) || itemService.canEdit(context, obj))) { From 4a89a68736de365d0ca93a3b22f48309f49c88d6 Mon Sep 17 00:00:00 2001 From: Stefano Maffei Date: Wed, 30 Apr 2025 11:33:40 +0200 Subject: [PATCH 596/979] [DURACOM-357] improved javadoc (cherry picked from commit f1cb3c3ad144444f9ee9baf699a359f19680f38c) --- .../main/java/org/dspace/app/rest/converter/ItemConverter.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/ItemConverter.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/ItemConverter.java index abcd707118c8..a1e9442f7466 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/ItemConverter.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/ItemConverter.java @@ -63,6 +63,9 @@ public ItemRest convert(Item obj, Projection projection) { /** * Retrieves the metadata list filtered according to the hidden metadata configuration * When the context is null, it will return the metadatalist as for an anonymous user + * When the context is not null, it will return the full metadata list if the user + * is allowed to edit the item or if the user is an admin. Otherwise, it will + * return the metadata list filtered according to the hidden metadata configuration * Overrides the parent method to include virtual metadata * @param context The context * @param obj The object of which the filtered metadata will be retrieved From b74c4b958ffc730a757989e3765a6c52c7000735 Mon Sep 17 00:00:00 2001 From: Stefano Maffei Date: Tue, 29 Apr 2025 11:57:38 +0200 Subject: [PATCH 597/979] [DURACOM-357] fix Collection Admin cannot see withdrawn item metadata (cherry picked from commit 5e2bb4fb9270a22a28a39ebcbcbac3f2781f097d) --- .../app/rest/converter/ItemConverter.java | 6 +- .../dspace/app/rest/ItemRestRepositoryIT.java | 62 +++++++++++++++++++ 2 files changed, 65 insertions(+), 3 deletions(-) diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/ItemConverter.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/ItemConverter.java index fc64b66e8a16..38f829be3476 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/ItemConverter.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/ItemConverter.java @@ -8,7 +8,6 @@ package org.dspace.app.rest.converter; import java.sql.SQLException; -import java.util.ArrayList; import java.util.LinkedList; import java.util.List; import java.util.Objects; @@ -76,8 +75,9 @@ public MetadataValueList getPermissionFilteredMetadata(Context context, Item obj List returnList = new LinkedList<>(); try { if (obj.isWithdrawn() && (Objects.isNull(context) || - Objects.isNull(context.getCurrentUser()) || !authorizeService.isAdmin(context))) { - return new MetadataValueList(new ArrayList()); + Objects.isNull(context.getCurrentUser()) || + !(authorizeService.isAdmin(context) || authorizeService.isCollectionAdmin(context)))) { + return new MetadataValueList(List.of()); } if (context != null && (authorizeService.isAdmin(context) || itemService.canEdit(context, obj))) { return new MetadataValueList(fullList); diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/ItemRestRepositoryIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/ItemRestRepositoryIT.java index bde12228e84f..f54fdc38b60e 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/ItemRestRepositoryIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/ItemRestRepositoryIT.java @@ -422,6 +422,68 @@ public void findOneTest() throws Exception { .andExpect(jsonPath("$", publicItem1Matcher)); } + @Test + public void findOneWithdrawnAsCollectionAdminTest() throws Exception { + context.turnOffAuthorisationSystem(); + + // Create collection admin account + EPerson collectionAdmin = EPersonBuilder.createEPerson(context) + .withEmail("collection-admin@dspace.com") + .withPassword("test") + .withCanLogin(true) + .build(); + parentCommunity = CommunityBuilder.createCommunity(context) + .withName("Parent Community") + .build(); + Community child1 = CommunityBuilder.createSubCommunity(context, parentCommunity) + .withName("Sub Community") + .build(); + + // Create collection + Collection adminCollection = CollectionBuilder.createCollection(context, child1) + .withName("Collection Admin col") + .withAdminGroup(collectionAdmin) + .build(); + Collection noAdminCollection = + CollectionBuilder.createCollection(context, child1).withName("Collection non Admin") + .build(); + + // both items are withdrawn + Item administeredItem = ItemBuilder.createItem(context, adminCollection) + .withTitle("Public item 1") + .withIssueDate("2017-10-17") + .withAuthor("Smith, Donald").withAuthor("Doe, John") + .withSubject("ExtraEntry") + .withdrawn() + .build(); + + Item nonAdministeredItem = ItemBuilder.createItem(context, noAdminCollection) + .withTitle("Public item 2") + .withIssueDate("2016-02-13") + .withAuthor("Smith, Maria").withAuthor("Doe, Jane") + .withSubject("TestingForMore").withSubject("ExtraEntry") + .withdrawn() + .build(); + + context.restoreAuthSystemState(); + + String collectionAdmintoken = getAuthToken(collectionAdmin.getEmail(), "test"); + + // Metadata are retrieved since user is administering the item's collection + getClient(collectionAdmintoken).perform(get("/api/core/items/" + administeredItem.getID()) + .param("projection", "full")) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.metadata").isNotEmpty()); + + // No metadata is retrieved since user is not administering the item's collection + getClient().perform(get("/api/core/items/" + nonAdministeredItem.getID()) + .param("projection", "full")) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.metadata").isEmpty()); + + + } + @Test public void findOneFullProjectionTest() throws Exception { context.turnOffAuthorisationSystem(); From 371d4708915959263c655fd2a92dbd6a5daa15f1 Mon Sep 17 00:00:00 2001 From: Stefano Maffei Date: Wed, 30 Apr 2025 08:31:01 +0200 Subject: [PATCH 598/979] [DURACOM-357] improved check for authorization on objects in ItemConverter (cherry picked from commit a70dede20b9310ec85ec6f441be16c0437c796cf) --- .../main/java/org/dspace/app/rest/converter/ItemConverter.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/ItemConverter.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/ItemConverter.java index 38f829be3476..0c5a36d5fb5a 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/ItemConverter.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/ItemConverter.java @@ -76,7 +76,7 @@ public MetadataValueList getPermissionFilteredMetadata(Context context, Item obj try { if (obj.isWithdrawn() && (Objects.isNull(context) || Objects.isNull(context.getCurrentUser()) || - !(authorizeService.isAdmin(context) || authorizeService.isCollectionAdmin(context)))) { + !(authorizeService.isAdmin(context) || authorizeService.isAdmin(context, obj)))) { return new MetadataValueList(List.of()); } if (context != null && (authorizeService.isAdmin(context) || itemService.canEdit(context, obj))) { From 1b09620a01baa9e20da10c9fbf8cad8104bb8577 Mon Sep 17 00:00:00 2001 From: Stefano Maffei Date: Wed, 30 Apr 2025 08:56:31 +0200 Subject: [PATCH 599/979] [DURACOM-357] improved admin check (cherry picked from commit bb3935a0473a13fd1804dd6c88cc49a9196809bb) --- .../main/java/org/dspace/app/rest/converter/ItemConverter.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/ItemConverter.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/ItemConverter.java index 0c5a36d5fb5a..abcd707118c8 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/ItemConverter.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/ItemConverter.java @@ -76,7 +76,7 @@ public MetadataValueList getPermissionFilteredMetadata(Context context, Item obj try { if (obj.isWithdrawn() && (Objects.isNull(context) || Objects.isNull(context.getCurrentUser()) || - !(authorizeService.isAdmin(context) || authorizeService.isAdmin(context, obj)))) { + !authorizeService.isAdmin(context, obj))) { return new MetadataValueList(List.of()); } if (context != null && (authorizeService.isAdmin(context) || itemService.canEdit(context, obj))) { From e5bb4069b45213d3c453db82b1ffbdb5df9d7cd5 Mon Sep 17 00:00:00 2001 From: Stefano Maffei Date: Wed, 30 Apr 2025 11:33:40 +0200 Subject: [PATCH 600/979] [DURACOM-357] improved javadoc (cherry picked from commit f1cb3c3ad144444f9ee9baf699a359f19680f38c) --- .../main/java/org/dspace/app/rest/converter/ItemConverter.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/ItemConverter.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/ItemConverter.java index abcd707118c8..a1e9442f7466 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/ItemConverter.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/ItemConverter.java @@ -63,6 +63,9 @@ public ItemRest convert(Item obj, Projection projection) { /** * Retrieves the metadata list filtered according to the hidden metadata configuration * When the context is null, it will return the metadatalist as for an anonymous user + * When the context is not null, it will return the full metadata list if the user + * is allowed to edit the item or if the user is an admin. Otherwise, it will + * return the metadata list filtered according to the hidden metadata configuration * Overrides the parent method to include virtual metadata * @param context The context * @param obj The object of which the filtered metadata will be retrieved From ee225cb0d34e305258fe161db47af1cb288d1e8a Mon Sep 17 00:00:00 2001 From: Kim Shepherd Date: Wed, 30 Apr 2025 15:42:22 +0200 Subject: [PATCH 601/979] Fix #10608 8.x backport by backporting Date references --- dspace-oai/src/main/java/org/dspace/xoai/util/ItemUtils.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dspace-oai/src/main/java/org/dspace/xoai/util/ItemUtils.java b/dspace-oai/src/main/java/org/dspace/xoai/util/ItemUtils.java index 6eda37e62fbe..40a193ea2905 100644 --- a/dspace-oai/src/main/java/org/dspace/xoai/util/ItemUtils.java +++ b/dspace-oai/src/main/java/org/dspace/xoai/util/ItemUtils.java @@ -195,7 +195,7 @@ private static void addResourcePolicyInformation(Context context, Bitstream bits resourcePolicyEl.getField().add(createValue("user", user)); resourcePolicyEl.getField().add(createValue("action", action)); // Only add start-date if group is different to anonymous, or there is an active embargo - if (startDate != null && startDate.isAfter(LocalDate.now())) { + if (startDate != null && startDate.after(new Date())) { resourcePolicyEl.getField().add(createValue("start-date", formatter.format(startDate))); } if (endDate != null) { From 2772b1c9b06d5ac7c3a194dbff7c179fde246522 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Thu, 1 May 2025 10:52:39 -0500 Subject: [PATCH 602/979] Replace MethodNotFoundException with more appropriate UnsupportedOperationException --- .../MultipleParallelImportMetadataSourceServiceImpl.java | 3 +-- .../external/ads/ADSImportMetadataSourceServiceImpl.java | 3 +-- .../service/ArXivImportMetadataSourceServiceImpl.java | 3 +-- .../cinii/CiniiImportMetadataSourceServiceImpl.java | 3 +-- .../crossref/CrossRefImportMetadataSourceServiceImpl.java | 3 +-- .../datacite/DataCiteImportMetadataSourceServiceImpl.java | 3 +-- .../service/OpenAireImportMetadataSourceServiceImpl.java | 5 ++--- .../PubmedEuropeMetadataSourceServiceImpl.java | 3 +-- .../ror/service/RorImportMetadataSourceServiceImpl.java | 5 ++--- .../service/ScieloImportMetadataSourceServiceImpl.java | 7 +++---- .../service/ScopusImportMetadataSourceServiceImpl.java | 3 +-- .../vufind/VuFindImportMetadataSourceServiceImpl.java | 3 +-- .../wos/service/WOSImportMetadataSourceServiceImpl.java | 7 +++---- .../dspace/app/rest/ADSImportMetadataSourceServiceIT.java | 3 +-- .../app/rest/CrossRefImportMetadataSourceServiceIT.java | 3 +-- .../app/rest/DataCiteImportMetadataSourceServiceIT.java | 3 +-- .../app/rest/ScieloImportMetadataSourceServiceIT.java | 5 ++--- .../app/rest/VuFindImportMetadataSourceServiceIT.java | 3 +-- 18 files changed, 25 insertions(+), 43 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/importer/external/MultipleParallelImportMetadataSourceServiceImpl.java b/dspace-api/src/main/java/org/dspace/importer/external/MultipleParallelImportMetadataSourceServiceImpl.java index 1bb7e9269596..1ab5319d4066 100644 --- a/dspace-api/src/main/java/org/dspace/importer/external/MultipleParallelImportMetadataSourceServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/importer/external/MultipleParallelImportMetadataSourceServiceImpl.java @@ -17,7 +17,6 @@ import java.util.concurrent.Executors; import java.util.concurrent.Future; -import jakarta.el.MethodNotFoundException; import org.dspace.content.Item; import org.dspace.importer.external.datamodel.ImportRecord; import org.dspace.importer.external.datamodel.Query; @@ -144,7 +143,7 @@ public Collection getRecords(Query query) throws MetadataSourceExc @Override public ImportRecord getRecord(Query query) throws MetadataSourceException { - throw new MethodNotFoundException("This method is not implemented for multiple external data sources"); + throw new UnsupportedOperationException("This method is not implemented for multiple external data sources"); } @Override diff --git a/dspace-api/src/main/java/org/dspace/importer/external/ads/ADSImportMetadataSourceServiceImpl.java b/dspace-api/src/main/java/org/dspace/importer/external/ads/ADSImportMetadataSourceServiceImpl.java index 4c72c46732b7..ca3f48da6114 100644 --- a/dspace-api/src/main/java/org/dspace/importer/external/ads/ADSImportMetadataSourceServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/importer/external/ads/ADSImportMetadataSourceServiceImpl.java @@ -21,7 +21,6 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; -import jakarta.el.MethodNotFoundException; import org.apache.commons.collections.CollectionUtils; import org.apache.commons.lang3.StringUtils; import org.apache.http.client.utils.URIBuilder; @@ -99,7 +98,7 @@ public Collection findMatchingRecords(Query query) throws Metadata @Override public Collection findMatchingRecords(Item item) throws MetadataSourceException { - throw new MethodNotFoundException("This method is not implemented for CrossRef"); + throw new UnsupportedOperationException("This method is not implemented for CrossRef"); } @Override diff --git a/dspace-api/src/main/java/org/dspace/importer/external/arxiv/service/ArXivImportMetadataSourceServiceImpl.java b/dspace-api/src/main/java/org/dspace/importer/external/arxiv/service/ArXivImportMetadataSourceServiceImpl.java index a1df4a7f40c1..660e0c9754bd 100644 --- a/dspace-api/src/main/java/org/dspace/importer/external/arxiv/service/ArXivImportMetadataSourceServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/importer/external/arxiv/service/ArXivImportMetadataSourceServiceImpl.java @@ -15,7 +15,6 @@ import java.util.List; import java.util.concurrent.Callable; -import jakarta.el.MethodNotFoundException; import jakarta.ws.rs.client.Client; import jakarta.ws.rs.client.ClientBuilder; import jakarta.ws.rs.client.Invocation; @@ -162,7 +161,7 @@ public String getImportSource() { @Override public Collection findMatchingRecords(Item item) throws MetadataSourceException { // FIXME: we need this method? - throw new MethodNotFoundException("This method is not implemented for ArXiv"); + throw new UnsupportedOperationException("This method is not implemented for ArXiv"); } /** diff --git a/dspace-api/src/main/java/org/dspace/importer/external/cinii/CiniiImportMetadataSourceServiceImpl.java b/dspace-api/src/main/java/org/dspace/importer/external/cinii/CiniiImportMetadataSourceServiceImpl.java index 66572f9a3d16..82a4b2d77968 100644 --- a/dspace-api/src/main/java/org/dspace/importer/external/cinii/CiniiImportMetadataSourceServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/importer/external/cinii/CiniiImportMetadataSourceServiceImpl.java @@ -20,7 +20,6 @@ import java.util.Objects; import java.util.concurrent.Callable; -import jakarta.el.MethodNotFoundException; import org.apache.commons.collections.CollectionUtils; import org.apache.commons.lang.StringUtils; import org.apache.http.HttpException; @@ -113,7 +112,7 @@ public Collection findMatchingRecords(Query query) throws Metadata @Override public Collection findMatchingRecords(Item item) throws MetadataSourceException { - throw new MethodNotFoundException("This method is not implemented for Cinii"); + throw new UnsupportedOperationException("This method is not implemented for Cinii"); } public String getUrl() { diff --git a/dspace-api/src/main/java/org/dspace/importer/external/crossref/CrossRefImportMetadataSourceServiceImpl.java b/dspace-api/src/main/java/org/dspace/importer/external/crossref/CrossRefImportMetadataSourceServiceImpl.java index 37e613d9c5ff..88aeec8d9498 100644 --- a/dspace-api/src/main/java/org/dspace/importer/external/crossref/CrossRefImportMetadataSourceServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/importer/external/crossref/CrossRefImportMetadataSourceServiceImpl.java @@ -20,7 +20,6 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; -import jakarta.el.MethodNotFoundException; import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.lang3.StringUtils; import org.apache.http.client.utils.URIBuilder; @@ -112,7 +111,7 @@ public Collection findMatchingRecords(Query query) throws Metadata @Override public Collection findMatchingRecords(Item item) throws MetadataSourceException { - throw new MethodNotFoundException("This method is not implemented for CrossRef"); + throw new UnsupportedOperationException("This method is not implemented for CrossRef"); } public String getID(String id) { diff --git a/dspace-api/src/main/java/org/dspace/importer/external/datacite/DataCiteImportMetadataSourceServiceImpl.java b/dspace-api/src/main/java/org/dspace/importer/external/datacite/DataCiteImportMetadataSourceServiceImpl.java index ad6e260bd0d5..34405cc3eec4 100644 --- a/dspace-api/src/main/java/org/dspace/importer/external/datacite/DataCiteImportMetadataSourceServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/importer/external/datacite/DataCiteImportMetadataSourceServiceImpl.java @@ -17,7 +17,6 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; -import jakarta.el.MethodNotFoundException; import org.apache.commons.lang3.StringUtils; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -188,7 +187,7 @@ public Collection findMatchingRecords(Query query) throws Metadata @Override public Collection findMatchingRecords(Item item) throws MetadataSourceException { - throw new MethodNotFoundException("This method is not implemented for DataCite"); + throw new UnsupportedOperationException("This method is not implemented for DataCite"); } public String getID(String query) { diff --git a/dspace-api/src/main/java/org/dspace/importer/external/openaire/service/OpenAireImportMetadataSourceServiceImpl.java b/dspace-api/src/main/java/org/dspace/importer/external/openaire/service/OpenAireImportMetadataSourceServiceImpl.java index 0e7bc5e532e8..7fb5f27354f7 100644 --- a/dspace-api/src/main/java/org/dspace/importer/external/openaire/service/OpenAireImportMetadataSourceServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/importer/external/openaire/service/OpenAireImportMetadataSourceServiceImpl.java @@ -15,7 +15,6 @@ import java.util.List; import java.util.concurrent.Callable; -import jakarta.el.MethodNotFoundException; import jakarta.ws.rs.client.Client; import jakarta.ws.rs.client.ClientBuilder; import jakarta.ws.rs.client.Invocation; @@ -135,12 +134,12 @@ public Collection getRecords(Query query) throws MetadataSourceExc @Override public Collection findMatchingRecords(Query query) throws MetadataSourceException { - throw new MethodNotFoundException("This method is not implemented for OpenAIRE"); + throw new UnsupportedOperationException("This method is not implemented for OpenAIRE"); } @Override public Collection findMatchingRecords(Item item) throws MetadataSourceException { - throw new MethodNotFoundException("This method is not implemented for OpenAIRE"); + throw new UnsupportedOperationException("This method is not implemented for OpenAIRE"); } /** diff --git a/dspace-api/src/main/java/org/dspace/importer/external/pubmedeurope/PubmedEuropeMetadataSourceServiceImpl.java b/dspace-api/src/main/java/org/dspace/importer/external/pubmedeurope/PubmedEuropeMetadataSourceServiceImpl.java index 5aae8ca8cf50..7cd297eb2815 100644 --- a/dspace-api/src/main/java/org/dspace/importer/external/pubmedeurope/PubmedEuropeMetadataSourceServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/importer/external/pubmedeurope/PubmedEuropeMetadataSourceServiceImpl.java @@ -18,7 +18,6 @@ import java.util.Objects; import java.util.concurrent.Callable; -import jakarta.el.MethodNotFoundException; import org.apache.commons.lang3.StringUtils; import org.apache.http.HttpException; import org.apache.http.client.ClientProtocolException; @@ -153,7 +152,7 @@ public Collection findMatchingRecords(Query query) throws Metadata @Override public Collection findMatchingRecords(Item item) throws MetadataSourceException { - throw new MethodNotFoundException("This method is not implemented for PubMed Europe"); + throw new UnsupportedOperationException("This method is not implemented for PubMed Europe"); } @Override diff --git a/dspace-api/src/main/java/org/dspace/importer/external/ror/service/RorImportMetadataSourceServiceImpl.java b/dspace-api/src/main/java/org/dspace/importer/external/ror/service/RorImportMetadataSourceServiceImpl.java index aa11ac0bb710..8298b6d6f011 100644 --- a/dspace-api/src/main/java/org/dspace/importer/external/ror/service/RorImportMetadataSourceServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/importer/external/ror/service/RorImportMetadataSourceServiceImpl.java @@ -19,7 +19,6 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; -import jakarta.el.MethodNotFoundException; import org.apache.commons.collections.CollectionUtils; import org.apache.commons.lang3.StringUtils; import org.apache.http.client.utils.URIBuilder; @@ -91,12 +90,12 @@ public ImportRecord getRecord(Query query) throws MetadataSourceException { @Override public Collection findMatchingRecords(Query query) throws MetadataSourceException { - throw new MethodNotFoundException("This method is not implemented for ROR"); + throw new UnsupportedOperationException("This method is not implemented for ROR"); } @Override public Collection findMatchingRecords(Item item) throws MetadataSourceException { - throw new MethodNotFoundException("This method is not implemented for ROR"); + throw new UnsupportedOperationException("This method is not implemented for ROR"); } @Override diff --git a/dspace-api/src/main/java/org/dspace/importer/external/scielo/service/ScieloImportMetadataSourceServiceImpl.java b/dspace-api/src/main/java/org/dspace/importer/external/scielo/service/ScieloImportMetadataSourceServiceImpl.java index ce0c20435ecf..cb988a5e55fb 100644 --- a/dspace-api/src/main/java/org/dspace/importer/external/scielo/service/ScieloImportMetadataSourceServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/importer/external/scielo/service/ScieloImportMetadataSourceServiceImpl.java @@ -21,7 +21,6 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; -import jakarta.el.MethodNotFoundException; import jakarta.ws.rs.BadRequestException; import org.apache.commons.collections4.CollectionUtils; import org.apache.http.client.utils.URIBuilder; @@ -99,17 +98,17 @@ public int getRecordsCount(String query) throws MetadataSourceException { @Override public int getRecordsCount(Query query) throws MetadataSourceException { - throw new MethodNotFoundException("This method is not implemented for Scielo"); + throw new UnsupportedOperationException("This method is not implemented for Scielo"); } @Override public Collection findMatchingRecords(Item item) throws MetadataSourceException { - throw new MethodNotFoundException("This method is not implemented for Scielo"); + throw new UnsupportedOperationException("This method is not implemented for Scielo"); } @Override public Collection findMatchingRecords(Query query) throws MetadataSourceException { - throw new MethodNotFoundException("This method is not implemented for Scielo"); + throw new UnsupportedOperationException("This method is not implemented for Scielo"); } /** diff --git a/dspace-api/src/main/java/org/dspace/importer/external/scopus/service/ScopusImportMetadataSourceServiceImpl.java b/dspace-api/src/main/java/org/dspace/importer/external/scopus/service/ScopusImportMetadataSourceServiceImpl.java index 39b2be7ad5f8..a3f74694becf 100644 --- a/dspace-api/src/main/java/org/dspace/importer/external/scopus/service/ScopusImportMetadataSourceServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/importer/external/scopus/service/ScopusImportMetadataSourceServiceImpl.java @@ -23,7 +23,6 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; -import jakarta.el.MethodNotFoundException; import org.apache.commons.lang3.StringUtils; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -152,7 +151,7 @@ public ImportRecord getRecord(Query query) throws MetadataSourceException { @Override public Collection findMatchingRecords(Item item) throws MetadataSourceException { - throw new MethodNotFoundException("This method is not implemented for Scopus"); + throw new UnsupportedOperationException("This method is not implemented for Scopus"); } @Override diff --git a/dspace-api/src/main/java/org/dspace/importer/external/vufind/VuFindImportMetadataSourceServiceImpl.java b/dspace-api/src/main/java/org/dspace/importer/external/vufind/VuFindImportMetadataSourceServiceImpl.java index 8933569a060f..7eb3743d207b 100644 --- a/dspace-api/src/main/java/org/dspace/importer/external/vufind/VuFindImportMetadataSourceServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/importer/external/vufind/VuFindImportMetadataSourceServiceImpl.java @@ -19,7 +19,6 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; -import jakarta.el.MethodNotFoundException; import org.apache.commons.lang3.StringUtils; import org.apache.http.client.utils.URIBuilder; import org.apache.logging.log4j.LogManager; @@ -104,7 +103,7 @@ public Collection findMatchingRecords(Query query) throws Metadata @Override public Collection findMatchingRecords(Item item) throws MetadataSourceException { - throw new MethodNotFoundException("This method is not implemented for VuFind"); + throw new UnsupportedOperationException("This method is not implemented for VuFind"); } @Override diff --git a/dspace-api/src/main/java/org/dspace/importer/external/wos/service/WOSImportMetadataSourceServiceImpl.java b/dspace-api/src/main/java/org/dspace/importer/external/wos/service/WOSImportMetadataSourceServiceImpl.java index c7b5aaa49e27..c33a2f890123 100644 --- a/dspace-api/src/main/java/org/dspace/importer/external/wos/service/WOSImportMetadataSourceServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/importer/external/wos/service/WOSImportMetadataSourceServiceImpl.java @@ -22,7 +22,6 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; -import jakarta.el.MethodNotFoundException; import org.apache.commons.collections.CollectionUtils; import org.apache.commons.lang3.StringUtils; import org.apache.logging.log4j.LogManager; @@ -109,17 +108,17 @@ public int getRecordsCount(String query) throws MetadataSourceException { @Override public int getRecordsCount(Query query) throws MetadataSourceException { - throw new MethodNotFoundException("This method is not implemented for WOS"); + throw new UnsupportedOperationException("This method is not implemented for WOS"); } @Override public Collection findMatchingRecords(Item item) throws MetadataSourceException { - throw new MethodNotFoundException("This method is not implemented for WOS"); + throw new UnsupportedOperationException("This method is not implemented for WOS"); } @Override public Collection findMatchingRecords(Query query) throws MetadataSourceException { - throw new MethodNotFoundException("This method is not implemented for WOS"); + throw new UnsupportedOperationException("This method is not implemented for WOS"); } /** diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/ADSImportMetadataSourceServiceIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/ADSImportMetadataSourceServiceIT.java index 892e83c79da0..4150ca580e49 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/ADSImportMetadataSourceServiceIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/ADSImportMetadataSourceServiceIT.java @@ -18,7 +18,6 @@ import java.util.Collection; import java.util.List; -import jakarta.el.MethodNotFoundException; import org.apache.commons.io.IOUtils; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.impl.client.CloseableHttpClient; @@ -117,7 +116,7 @@ public void adsImportMetadataGetRecordsCountByQueryTest() throws Exception { } } - @Test(expected = MethodNotFoundException.class) + @Test(expected = UnsupportedOperationException.class) public void adsImportMetadataFindMatchingRecordsTest() throws Exception { context.turnOffAuthorisationSystem(); parentCommunity = CommunityBuilder.createCommunity(context) diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/CrossRefImportMetadataSourceServiceIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/CrossRefImportMetadataSourceServiceIT.java index f68bfe48db1b..27b5e6933ba7 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/CrossRefImportMetadataSourceServiceIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/CrossRefImportMetadataSourceServiceIT.java @@ -18,7 +18,6 @@ import java.util.Collection; import java.util.List; -import jakarta.el.MethodNotFoundException; import org.apache.commons.io.IOUtils; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.impl.client.CloseableHttpClient; @@ -135,7 +134,7 @@ public void crossRefImportMetadataGetRecordByIdTest() throws Exception { } } - @Test(expected = MethodNotFoundException.class) + @Test(expected = UnsupportedOperationException.class) public void crossRefImportMetadataFindMatchingRecordsTest() throws Exception { context.turnOffAuthorisationSystem(); parentCommunity = CommunityBuilder.createCommunity(context) diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/DataCiteImportMetadataSourceServiceIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/DataCiteImportMetadataSourceServiceIT.java index 35b4810d9292..a8cd59a1afc4 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/DataCiteImportMetadataSourceServiceIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/DataCiteImportMetadataSourceServiceIT.java @@ -16,7 +16,6 @@ import java.util.Collection; import java.util.List; -import jakarta.el.MethodNotFoundException; import org.apache.commons.io.IOUtils; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.impl.client.CloseableHttpClient; @@ -90,7 +89,7 @@ public void dataCiteImportMetadataGetRecordsCountTest() throws Exception { } } - @Test(expected = MethodNotFoundException.class) + @Test(expected = UnsupportedOperationException.class) public void dataCiteImportMetadataFindMatchingRecordsTest() throws Exception { context.turnOffAuthorisationSystem(); parentCommunity = CommunityBuilder.createCommunity(context) diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/ScieloImportMetadataSourceServiceIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/ScieloImportMetadataSourceServiceIT.java index c12eff1f176e..af23ff4d14a4 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/ScieloImportMetadataSourceServiceIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/ScieloImportMetadataSourceServiceIT.java @@ -18,7 +18,6 @@ import java.util.Collection; import java.util.List; -import jakarta.el.MethodNotFoundException; import org.apache.commons.io.IOUtils; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.impl.client.CloseableHttpClient; @@ -92,7 +91,7 @@ public void scieloImportMetadataGetRecordsCountTest() throws Exception { } } - @Test(expected = MethodNotFoundException.class) + @Test(expected = UnsupportedOperationException.class) public void scieloImportMetadataFindMatchingRecordsTest() throws Exception { context.turnOffAuthorisationSystem(); parentCommunity = CommunityBuilder.createCommunity(context) @@ -111,7 +110,7 @@ public void scieloImportMetadataFindMatchingRecordsTest() throws Exception { scieloServiceImpl.findMatchingRecords(testItem); } - @Test(expected = MethodNotFoundException.class) + @Test(expected = UnsupportedOperationException.class) public void scieloImportMetadataGetRecordsCountByQueryTest() throws Exception { Query q = new Query(); q.addParameter("query", "test query"); diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/VuFindImportMetadataSourceServiceIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/VuFindImportMetadataSourceServiceIT.java index 76512272444d..89b67bbb4aa2 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/VuFindImportMetadataSourceServiceIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/VuFindImportMetadataSourceServiceIT.java @@ -18,7 +18,6 @@ import java.util.Collection; import java.util.List; -import jakarta.el.MethodNotFoundException; import org.apache.commons.io.IOUtils; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.impl.client.CloseableHttpClient; @@ -119,7 +118,7 @@ public void vuFindImportMetadataGetRecordByIdTest() throws Exception { } } - @Test(expected = MethodNotFoundException.class) + @Test(expected = UnsupportedOperationException.class) public void vuFindImportMetadataFindMatchingRecordsTest() throws Exception { context.turnOffAuthorisationSystem(); parentCommunity = CommunityBuilder.createCommunity(context) From 31220966f7523428b29aa5c31acf7c7b84766da5 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Thu, 1 May 2025 10:53:14 -0500 Subject: [PATCH 603/979] Remove jakarta.el dependency --- dspace-api/pom.xml | 4 ---- pom.xml | 5 ----- 2 files changed, 9 deletions(-) diff --git a/dspace-api/pom.xml b/dspace-api/pom.xml index 91c69fe2154c..8851a5e5256a 100644 --- a/dspace-api/pom.xml +++ b/dspace-api/pom.xml @@ -500,10 +500,6 @@ jakarta.annotation jakarta.annotation-api - - jakarta.el - jakarta.el-api - jaxen jaxen diff --git a/pom.xml b/pom.xml index 779fc9648ec6..2dd4c2d2adb4 100644 --- a/pom.xml +++ b/pom.xml @@ -1573,11 +1573,6 @@ jakarta.servlet-api 6.1.0 - - jakarta.el - jakarta.el-api - 5.0.1 - From b04eb9d72513141ba6ab39a286ba8cee3221bae3 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Thu, 1 May 2025 10:52:39 -0500 Subject: [PATCH 604/979] Replace MethodNotFoundException with more appropriate UnsupportedOperationException --- .../external/ads/ADSImportMetadataSourceServiceImpl.java | 3 +-- .../service/ArXivImportMetadataSourceServiceImpl.java | 3 +-- .../cinii/CiniiImportMetadataSourceServiceImpl.java | 3 +-- .../crossref/CrossRefImportMetadataSourceServiceImpl.java | 3 +-- .../datacite/DataCiteImportMetadataSourceServiceImpl.java | 3 +-- .../PubmedEuropeMetadataSourceServiceImpl.java | 3 +-- .../service/ScieloImportMetadataSourceServiceImpl.java | 7 +++---- .../service/ScopusImportMetadataSourceServiceImpl.java | 3 +-- .../vufind/VuFindImportMetadataSourceServiceImpl.java | 3 +-- .../wos/service/WOSImportMetadataSourceServiceImpl.java | 7 +++---- .../dspace/app/rest/ADSImportMetadataSourceServiceIT.java | 3 +-- .../app/rest/CrossRefImportMetadataSourceServiceIT.java | 3 +-- .../app/rest/DataCiteImportMetadataSourceServiceIT.java | 3 +-- .../app/rest/ScieloImportMetadataSourceServiceIT.java | 5 ++--- .../app/rest/VuFindImportMetadataSourceServiceIT.java | 3 +-- 15 files changed, 20 insertions(+), 35 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/importer/external/ads/ADSImportMetadataSourceServiceImpl.java b/dspace-api/src/main/java/org/dspace/importer/external/ads/ADSImportMetadataSourceServiceImpl.java index 8fbe4ef2cf57..ca3f48da6114 100644 --- a/dspace-api/src/main/java/org/dspace/importer/external/ads/ADSImportMetadataSourceServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/importer/external/ads/ADSImportMetadataSourceServiceImpl.java @@ -17,7 +17,6 @@ import java.util.List; import java.util.Map; import java.util.concurrent.Callable; -import javax.el.MethodNotFoundException; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonNode; @@ -99,7 +98,7 @@ public Collection findMatchingRecords(Query query) throws Metadata @Override public Collection findMatchingRecords(Item item) throws MetadataSourceException { - throw new MethodNotFoundException("This method is not implemented for CrossRef"); + throw new UnsupportedOperationException("This method is not implemented for CrossRef"); } @Override diff --git a/dspace-api/src/main/java/org/dspace/importer/external/arxiv/service/ArXivImportMetadataSourceServiceImpl.java b/dspace-api/src/main/java/org/dspace/importer/external/arxiv/service/ArXivImportMetadataSourceServiceImpl.java index 96689e62ba75..4369b0d48b46 100644 --- a/dspace-api/src/main/java/org/dspace/importer/external/arxiv/service/ArXivImportMetadataSourceServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/importer/external/arxiv/service/ArXivImportMetadataSourceServiceImpl.java @@ -14,7 +14,6 @@ import java.util.Collection; import java.util.List; import java.util.concurrent.Callable; -import javax.el.MethodNotFoundException; import javax.ws.rs.client.Client; import javax.ws.rs.client.ClientBuilder; import javax.ws.rs.client.Invocation; @@ -162,7 +161,7 @@ public String getImportSource() { @Override public Collection findMatchingRecords(Item item) throws MetadataSourceException { // FIXME: we need this method? - throw new MethodNotFoundException("This method is not implemented for ArXiv"); + throw new UnsupportedOperationException("This method is not implemented for ArXiv"); } /** diff --git a/dspace-api/src/main/java/org/dspace/importer/external/cinii/CiniiImportMetadataSourceServiceImpl.java b/dspace-api/src/main/java/org/dspace/importer/external/cinii/CiniiImportMetadataSourceServiceImpl.java index 587ad5b25838..82a4b2d77968 100644 --- a/dspace-api/src/main/java/org/dspace/importer/external/cinii/CiniiImportMetadataSourceServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/importer/external/cinii/CiniiImportMetadataSourceServiceImpl.java @@ -19,7 +19,6 @@ import java.util.Map; import java.util.Objects; import java.util.concurrent.Callable; -import javax.el.MethodNotFoundException; import org.apache.commons.collections.CollectionUtils; import org.apache.commons.lang.StringUtils; @@ -113,7 +112,7 @@ public Collection findMatchingRecords(Query query) throws Metadata @Override public Collection findMatchingRecords(Item item) throws MetadataSourceException { - throw new MethodNotFoundException("This method is not implemented for Cinii"); + throw new UnsupportedOperationException("This method is not implemented for Cinii"); } public String getUrl() { diff --git a/dspace-api/src/main/java/org/dspace/importer/external/crossref/CrossRefImportMetadataSourceServiceImpl.java b/dspace-api/src/main/java/org/dspace/importer/external/crossref/CrossRefImportMetadataSourceServiceImpl.java index 419f6ca8a085..88aeec8d9498 100644 --- a/dspace-api/src/main/java/org/dspace/importer/external/crossref/CrossRefImportMetadataSourceServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/importer/external/crossref/CrossRefImportMetadataSourceServiceImpl.java @@ -16,7 +16,6 @@ import java.util.Map; import java.util.Objects; import java.util.concurrent.Callable; -import javax.el.MethodNotFoundException; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonNode; @@ -112,7 +111,7 @@ public Collection findMatchingRecords(Query query) throws Metadata @Override public Collection findMatchingRecords(Item item) throws MetadataSourceException { - throw new MethodNotFoundException("This method is not implemented for CrossRef"); + throw new UnsupportedOperationException("This method is not implemented for CrossRef"); } public String getID(String id) { diff --git a/dspace-api/src/main/java/org/dspace/importer/external/datacite/DataCiteImportMetadataSourceServiceImpl.java b/dspace-api/src/main/java/org/dspace/importer/external/datacite/DataCiteImportMetadataSourceServiceImpl.java index 6c65d96b375d..34405cc3eec4 100644 --- a/dspace-api/src/main/java/org/dspace/importer/external/datacite/DataCiteImportMetadataSourceServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/importer/external/datacite/DataCiteImportMetadataSourceServiceImpl.java @@ -13,7 +13,6 @@ import java.util.Iterator; import java.util.List; import java.util.Map; -import javax.el.MethodNotFoundException; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonNode; @@ -188,7 +187,7 @@ public Collection findMatchingRecords(Query query) throws Metadata @Override public Collection findMatchingRecords(Item item) throws MetadataSourceException { - throw new MethodNotFoundException("This method is not implemented for DataCite"); + throw new UnsupportedOperationException("This method is not implemented for DataCite"); } public String getID(String query) { diff --git a/dspace-api/src/main/java/org/dspace/importer/external/pubmedeurope/PubmedEuropeMetadataSourceServiceImpl.java b/dspace-api/src/main/java/org/dspace/importer/external/pubmedeurope/PubmedEuropeMetadataSourceServiceImpl.java index 92d7d9fbd3fe..7cd297eb2815 100644 --- a/dspace-api/src/main/java/org/dspace/importer/external/pubmedeurope/PubmedEuropeMetadataSourceServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/importer/external/pubmedeurope/PubmedEuropeMetadataSourceServiceImpl.java @@ -17,7 +17,6 @@ import java.util.Map; import java.util.Objects; import java.util.concurrent.Callable; -import javax.el.MethodNotFoundException; import org.apache.commons.lang3.StringUtils; import org.apache.http.HttpException; @@ -153,7 +152,7 @@ public Collection findMatchingRecords(Query query) throws Metadata @Override public Collection findMatchingRecords(Item item) throws MetadataSourceException { - throw new MethodNotFoundException("This method is not implemented for PubMed Europe"); + throw new UnsupportedOperationException("This method is not implemented for PubMed Europe"); } @Override diff --git a/dspace-api/src/main/java/org/dspace/importer/external/scielo/service/ScieloImportMetadataSourceServiceImpl.java b/dspace-api/src/main/java/org/dspace/importer/external/scielo/service/ScieloImportMetadataSourceServiceImpl.java index 4f83ffe978f7..87b501470b87 100644 --- a/dspace-api/src/main/java/org/dspace/importer/external/scielo/service/ScieloImportMetadataSourceServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/importer/external/scielo/service/ScieloImportMetadataSourceServiceImpl.java @@ -20,7 +20,6 @@ import java.util.concurrent.Callable; import java.util.regex.Matcher; import java.util.regex.Pattern; -import javax.el.MethodNotFoundException; import javax.ws.rs.BadRequestException; import org.apache.commons.collections4.CollectionUtils; @@ -99,17 +98,17 @@ public int getRecordsCount(String query) throws MetadataSourceException { @Override public int getRecordsCount(Query query) throws MetadataSourceException { - throw new MethodNotFoundException("This method is not implemented for Scielo"); + throw new UnsupportedOperationException("This method is not implemented for Scielo"); } @Override public Collection findMatchingRecords(Item item) throws MetadataSourceException { - throw new MethodNotFoundException("This method is not implemented for Scielo"); + throw new UnsupportedOperationException("This method is not implemented for Scielo"); } @Override public Collection findMatchingRecords(Query query) throws MetadataSourceException { - throw new MethodNotFoundException("This method is not implemented for Scielo"); + throw new UnsupportedOperationException("This method is not implemented for Scielo"); } /** diff --git a/dspace-api/src/main/java/org/dspace/importer/external/scopus/service/ScopusImportMetadataSourceServiceImpl.java b/dspace-api/src/main/java/org/dspace/importer/external/scopus/service/ScopusImportMetadataSourceServiceImpl.java index e61ca0528681..a3f74694becf 100644 --- a/dspace-api/src/main/java/org/dspace/importer/external/scopus/service/ScopusImportMetadataSourceServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/importer/external/scopus/service/ScopusImportMetadataSourceServiceImpl.java @@ -22,7 +22,6 @@ import java.util.concurrent.Callable; import java.util.regex.Matcher; import java.util.regex.Pattern; -import javax.el.MethodNotFoundException; import org.apache.commons.lang3.StringUtils; import org.apache.logging.log4j.LogManager; @@ -152,7 +151,7 @@ public ImportRecord getRecord(Query query) throws MetadataSourceException { @Override public Collection findMatchingRecords(Item item) throws MetadataSourceException { - throw new MethodNotFoundException("This method is not implemented for Scopus"); + throw new UnsupportedOperationException("This method is not implemented for Scopus"); } @Override diff --git a/dspace-api/src/main/java/org/dspace/importer/external/vufind/VuFindImportMetadataSourceServiceImpl.java b/dspace-api/src/main/java/org/dspace/importer/external/vufind/VuFindImportMetadataSourceServiceImpl.java index a4f90fa5ba61..7eb3743d207b 100644 --- a/dspace-api/src/main/java/org/dspace/importer/external/vufind/VuFindImportMetadataSourceServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/importer/external/vufind/VuFindImportMetadataSourceServiceImpl.java @@ -15,7 +15,6 @@ import java.util.Map; import java.util.Objects; import java.util.concurrent.Callable; -import javax.el.MethodNotFoundException; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonNode; @@ -104,7 +103,7 @@ public Collection findMatchingRecords(Query query) throws Metadata @Override public Collection findMatchingRecords(Item item) throws MetadataSourceException { - throw new MethodNotFoundException("This method is not implemented for VuFind"); + throw new UnsupportedOperationException("This method is not implemented for VuFind"); } @Override diff --git a/dspace-api/src/main/java/org/dspace/importer/external/wos/service/WOSImportMetadataSourceServiceImpl.java b/dspace-api/src/main/java/org/dspace/importer/external/wos/service/WOSImportMetadataSourceServiceImpl.java index f550b659952b..c33a2f890123 100644 --- a/dspace-api/src/main/java/org/dspace/importer/external/wos/service/WOSImportMetadataSourceServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/importer/external/wos/service/WOSImportMetadataSourceServiceImpl.java @@ -21,7 +21,6 @@ import java.util.concurrent.Callable; import java.util.regex.Matcher; import java.util.regex.Pattern; -import javax.el.MethodNotFoundException; import org.apache.commons.collections.CollectionUtils; import org.apache.commons.lang3.StringUtils; @@ -109,17 +108,17 @@ public int getRecordsCount(String query) throws MetadataSourceException { @Override public int getRecordsCount(Query query) throws MetadataSourceException { - throw new MethodNotFoundException("This method is not implemented for WOS"); + throw new UnsupportedOperationException("This method is not implemented for WOS"); } @Override public Collection findMatchingRecords(Item item) throws MetadataSourceException { - throw new MethodNotFoundException("This method is not implemented for WOS"); + throw new UnsupportedOperationException("This method is not implemented for WOS"); } @Override public Collection findMatchingRecords(Query query) throws MetadataSourceException { - throw new MethodNotFoundException("This method is not implemented for WOS"); + throw new UnsupportedOperationException("This method is not implemented for WOS"); } /** diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/ADSImportMetadataSourceServiceIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/ADSImportMetadataSourceServiceIT.java index 4878cdecab83..4150ca580e49 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/ADSImportMetadataSourceServiceIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/ADSImportMetadataSourceServiceIT.java @@ -17,7 +17,6 @@ import java.util.Arrays; import java.util.Collection; import java.util.List; -import javax.el.MethodNotFoundException; import org.apache.commons.io.IOUtils; import org.apache.http.client.methods.CloseableHttpResponse; @@ -117,7 +116,7 @@ public void adsImportMetadataGetRecordsCountByQueryTest() throws Exception { } } - @Test(expected = MethodNotFoundException.class) + @Test(expected = UnsupportedOperationException.class) public void adsImportMetadataFindMatchingRecordsTest() throws Exception { context.turnOffAuthorisationSystem(); parentCommunity = CommunityBuilder.createCommunity(context) diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/CrossRefImportMetadataSourceServiceIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/CrossRefImportMetadataSourceServiceIT.java index a182e7d89070..27b5e6933ba7 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/CrossRefImportMetadataSourceServiceIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/CrossRefImportMetadataSourceServiceIT.java @@ -17,7 +17,6 @@ import java.util.Arrays; import java.util.Collection; import java.util.List; -import javax.el.MethodNotFoundException; import org.apache.commons.io.IOUtils; import org.apache.http.client.methods.CloseableHttpResponse; @@ -135,7 +134,7 @@ public void crossRefImportMetadataGetRecordByIdTest() throws Exception { } } - @Test(expected = MethodNotFoundException.class) + @Test(expected = UnsupportedOperationException.class) public void crossRefImportMetadataFindMatchingRecordsTest() throws Exception { context.turnOffAuthorisationSystem(); parentCommunity = CommunityBuilder.createCommunity(context) diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/DataCiteImportMetadataSourceServiceIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/DataCiteImportMetadataSourceServiceIT.java index c1481f0573c4..a8cd59a1afc4 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/DataCiteImportMetadataSourceServiceIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/DataCiteImportMetadataSourceServiceIT.java @@ -15,7 +15,6 @@ import java.util.ArrayList; import java.util.Collection; import java.util.List; -import javax.el.MethodNotFoundException; import org.apache.commons.io.IOUtils; import org.apache.http.client.methods.CloseableHttpResponse; @@ -90,7 +89,7 @@ public void dataCiteImportMetadataGetRecordsCountTest() throws Exception { } } - @Test(expected = MethodNotFoundException.class) + @Test(expected = UnsupportedOperationException.class) public void dataCiteImportMetadataFindMatchingRecordsTest() throws Exception { context.turnOffAuthorisationSystem(); parentCommunity = CommunityBuilder.createCommunity(context) diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/ScieloImportMetadataSourceServiceIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/ScieloImportMetadataSourceServiceIT.java index aafc75a065a1..af23ff4d14a4 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/ScieloImportMetadataSourceServiceIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/ScieloImportMetadataSourceServiceIT.java @@ -17,7 +17,6 @@ import java.util.Arrays; import java.util.Collection; import java.util.List; -import javax.el.MethodNotFoundException; import org.apache.commons.io.IOUtils; import org.apache.http.client.methods.CloseableHttpResponse; @@ -92,7 +91,7 @@ public void scieloImportMetadataGetRecordsCountTest() throws Exception { } } - @Test(expected = MethodNotFoundException.class) + @Test(expected = UnsupportedOperationException.class) public void scieloImportMetadataFindMatchingRecordsTest() throws Exception { context.turnOffAuthorisationSystem(); parentCommunity = CommunityBuilder.createCommunity(context) @@ -111,7 +110,7 @@ public void scieloImportMetadataFindMatchingRecordsTest() throws Exception { scieloServiceImpl.findMatchingRecords(testItem); } - @Test(expected = MethodNotFoundException.class) + @Test(expected = UnsupportedOperationException.class) public void scieloImportMetadataGetRecordsCountByQueryTest() throws Exception { Query q = new Query(); q.addParameter("query", "test query"); diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/VuFindImportMetadataSourceServiceIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/VuFindImportMetadataSourceServiceIT.java index c3063ca23459..89b67bbb4aa2 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/VuFindImportMetadataSourceServiceIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/VuFindImportMetadataSourceServiceIT.java @@ -17,7 +17,6 @@ import java.util.Arrays; import java.util.Collection; import java.util.List; -import javax.el.MethodNotFoundException; import org.apache.commons.io.IOUtils; import org.apache.http.client.methods.CloseableHttpResponse; @@ -119,7 +118,7 @@ public void vuFindImportMetadataGetRecordByIdTest() throws Exception { } } - @Test(expected = MethodNotFoundException.class) + @Test(expected = UnsupportedOperationException.class) public void vuFindImportMetadataFindMatchingRecordsTest() throws Exception { context.turnOffAuthorisationSystem(); parentCommunity = CommunityBuilder.createCommunity(context) From d83779022e3d62ea45539a8b051d869a3b19743d Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Thu, 1 May 2025 12:53:41 -0500 Subject: [PATCH 605/979] Remove javax.el dependency --- dspace-api/pom.xml | 5 ----- pom.xml | 6 ------ 2 files changed, 11 deletions(-) diff --git a/dspace-api/pom.xml b/dspace-api/pom.xml index 3de7099ad668..bb246c20ad8f 100644 --- a/dspace-api/pom.xml +++ b/dspace-api/pom.xml @@ -505,11 +505,6 @@ javax.servlet-api provided - - - javax.el - javax.el-api - javax.annotation javax.annotation-api diff --git a/pom.xml b/pom.xml index a58392cd80c1..e2683a5844b7 100644 --- a/pom.xml +++ b/pom.xml @@ -1554,12 +1554,6 @@ javax.servlet-api 3.1.0 - - - javax.el - javax.el-api - 3.0.0 - From 59d637e62384c7d1d2bdb6ab6f1c676e36a9236f Mon Sep 17 00:00:00 2001 From: Jens Vannerum Date: Tue, 19 Nov 2024 12:13:37 +0100 Subject: [PATCH 606/979] 119612: configurable limit on exporting items since it can take up a bunch of resources (cherry picked from commit b634e1e38070e65128c49d702affa3b19842ebb6) --- .../app/bulkedit/MetadataExportSearch.java | 2 +- .../MetadataDSpaceCsvExportServiceImpl.java | 43 +++++++++++++++++-- .../MetadataDSpaceCsvExportService.java | 6 ++- .../MetadataDSpaceCsvExportServiceImplIT.java | 8 +++- dspace/config/dspace.cfg | 9 ++++ 5 files changed, 59 insertions(+), 9 deletions(-) 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 027ad116a7e2..e4bbe335d63e 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 @@ -143,7 +143,7 @@ public void internalRun() throws Exception { Iterator itemIterator = searchService.iteratorSearch(context, dso, discoverQuery); handler.logDebug("creating dspacecsv"); - DSpaceCSV dSpaceCSV = metadataDSpaceCsvExportService.export(context, itemIterator, true); + DSpaceCSV dSpaceCSV = metadataDSpaceCsvExportService.export(context, itemIterator, true, handler); handler.logDebug("writing to file " + getFileNameOrExportFile()); handler.writeFilestream(context, getFileNameOrExportFile(), dSpaceCSV.getInputStream(), EXPORT_CSV); context.restoreAuthSystemState(); diff --git a/dspace-api/src/main/java/org/dspace/content/MetadataDSpaceCsvExportServiceImpl.java b/dspace-api/src/main/java/org/dspace/content/MetadataDSpaceCsvExportServiceImpl.java index 8bc34d3f5ed1..7e313b79511e 100644 --- a/dspace-api/src/main/java/org/dspace/content/MetadataDSpaceCsvExportServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/content/MetadataDSpaceCsvExportServiceImpl.java @@ -15,14 +15,17 @@ import java.util.Set; import java.util.UUID; +import org.apache.commons.collections.IteratorUtils; import org.dspace.app.bulkedit.DSpaceCSV; import org.dspace.app.util.service.DSpaceObjectUtils; import org.dspace.content.service.ItemService; import org.dspace.content.service.MetadataDSpaceCsvExportService; import org.dspace.core.Constants; import org.dspace.core.Context; +import org.dspace.eperson.service.GroupService; import org.dspace.handle.factory.HandleServiceFactory; import org.dspace.scripts.handler.DSpaceRunnableHandler; +import org.dspace.services.ConfigurationService; import org.springframework.beans.factory.annotation.Autowired; /** @@ -36,6 +39,12 @@ public class MetadataDSpaceCsvExportServiceImpl implements MetadataDSpaceCsvExpo @Autowired private DSpaceObjectUtils dSpaceObjectUtils; + @Autowired + private ConfigurationService configurationService; + + @Autowired + private GroupService groupService; + @Override public DSpaceCSV handleExport(Context context, boolean exportAllItems, boolean exportAllMetadata, String identifier, DSpaceRunnableHandler handler) throws Exception { @@ -74,17 +83,19 @@ public DSpaceCSV handleExport(Context context, boolean exportAllItems, boolean e } } - DSpaceCSV csv = this.export(context, toExport, exportAllMetadata); + DSpaceCSV csv = this.export(context, toExport, exportAllMetadata, handler); return csv; } @Override - public DSpaceCSV export(Context context, Iterator toExport, boolean exportAll) throws Exception { + public DSpaceCSV export(Context context, Iterator toExport, + boolean exportAll, DSpaceRunnableHandler handler) throws Exception { Context.Mode originalMode = context.getCurrentMode(); context.setMode(Context.Mode.READ_ONLY); // Process each item DSpaceCSV csv = new DSpaceCSV(exportAll); + toExport = setItemsToExportWithLimit(context, toExport, handler); while (toExport.hasNext()) { Item item = toExport.next(); csv.addItem(item); @@ -97,8 +108,32 @@ public DSpaceCSV export(Context context, Iterator toExport, boolean export } @Override - public DSpaceCSV export(Context context, Community community, boolean exportAll) throws Exception { - return export(context, buildFromCommunity(context, community), exportAll); + public DSpaceCSV export(Context context, Community community, + boolean exportAll, DSpaceRunnableHandler handler) throws Exception { + return export(context, buildFromCommunity(context, community), exportAll, handler); + } + + private Iterator setItemsToExportWithLimit(Context context, Iterator toExport, + DSpaceRunnableHandler handler) throws SQLException { + int itemExportLimit = configurationService.getIntProperty( + "metadataexport.max.items", 500); + String[] ignoreLimitGroups = configurationService.getArrayProperty( + "metadataexport.admin.groups"); + + for (String group : ignoreLimitGroups) { + if (groupService.isMember(context, context.getCurrentUser(), group)) { + itemExportLimit = Integer.MAX_VALUE; + break; + } + } + + List items = IteratorUtils.toList(toExport); + if (items.size() > itemExportLimit) { + handler.logWarning("The amount of items to export is higher than the limit of " + itemExportLimit + + " items. Only the first " + itemExportLimit + " items will be exported."); + items = items.subList(0, itemExportLimit); + } + return items.iterator(); } /** diff --git a/dspace-api/src/main/java/org/dspace/content/service/MetadataDSpaceCsvExportService.java b/dspace-api/src/main/java/org/dspace/content/service/MetadataDSpaceCsvExportService.java index d3fc2e823669..052754a16331 100644 --- a/dspace-api/src/main/java/org/dspace/content/service/MetadataDSpaceCsvExportService.java +++ b/dspace-api/src/main/java/org/dspace/content/service/MetadataDSpaceCsvExportService.java @@ -44,7 +44,8 @@ public DSpaceCSV handleExport(Context context, boolean exportAllItems, boolean e * @return A DSpaceCSV object containing the exported information * @throws Exception If something goes wrong */ - public DSpaceCSV export(Context context, Iterator toExport, boolean exportAll) throws Exception; + public DSpaceCSV export(Context context, Iterator toExport, + boolean exportAll, DSpaceRunnableHandler handler) throws Exception; /** * This method will export all the Items within the given Community to a DSpaceCSV @@ -54,6 +55,7 @@ public DSpaceCSV handleExport(Context context, boolean exportAllItems, boolean e * @return A DSpaceCSV object containing the exported information * @throws Exception If something goes wrong */ - public DSpaceCSV export(Context context, Community community, boolean exportAll) throws Exception; + public DSpaceCSV export(Context context, Community community, + boolean exportAll, DSpaceRunnableHandler handler) throws Exception; } \ No newline at end of file diff --git a/dspace-api/src/test/java/org/dspace/content/MetadataDSpaceCsvExportServiceImplIT.java b/dspace-api/src/test/java/org/dspace/content/MetadataDSpaceCsvExportServiceImplIT.java index c2d4f56ca61a..5a3412646482 100644 --- a/dspace-api/src/test/java/org/dspace/content/MetadataDSpaceCsvExportServiceImplIT.java +++ b/dspace-api/src/test/java/org/dspace/content/MetadataDSpaceCsvExportServiceImplIT.java @@ -16,6 +16,7 @@ import org.dspace.AbstractIntegrationTestWithDatabase; import org.dspace.app.bulkedit.DSpaceCSV; import org.dspace.app.bulkedit.DSpaceCSVLine; +import org.dspace.app.scripts.handler.impl.TestDSpaceRunnableHandler; import org.dspace.builder.CollectionBuilder; import org.dspace.builder.CommunityBuilder; import org.dspace.builder.ItemBuilder; @@ -31,6 +32,9 @@ */ public class MetadataDSpaceCsvExportServiceImplIT extends AbstractIntegrationTestWithDatabase { + + TestDSpaceRunnableHandler testDSpaceRunnableHandler = new TestDSpaceRunnableHandler(); + /** * Test of handleExport method, of class MetadataDSpaceCsvExportServiceImpl. * @throws java.lang.Exception passed through. @@ -66,7 +70,7 @@ public void testExport_3args_1() boolean exportAll = false; MetadataDSpaceCsvExportServiceImpl instance = new MetadataDSpaceCsvExportServiceImpl(); DSpaceCSV expResult = null; - DSpaceCSV result = instance.export(context, toExport, exportAll); + DSpaceCSV result = instance.export(context, toExport, exportAll, testDSpaceRunnableHandler); assertEquals(expResult, result); // TODO review the generated test code and remove the default call to fail. fail("The test case is a prototype."); @@ -105,7 +109,7 @@ public void testMappedItem() .getServiceManager() .getServiceByName(MetadataDSpaceCsvExportServiceImpl.class.getCanonicalName(), MetadataDSpaceCsvExportService.class); - DSpaceCSV result = instance.export(context, parentCommunity, false); + DSpaceCSV result = instance.export(context, parentCommunity, false, testDSpaceRunnableHandler); // Examine the result. List csvLines = result.getCSVLines(); diff --git a/dspace/config/dspace.cfg b/dspace/config/dspace.cfg index 2abe436387dd..76a0c84c757f 100644 --- a/dspace/config/dspace.cfg +++ b/dspace/config/dspace.cfg @@ -901,6 +901,15 @@ org.dspace.app.itemexport.life.span.hours = 48 # cumulative sizes are more than this entry the export is not kicked off org.dspace.app.itemexport.max.size = 200 +### Bulkedit Metadata export settings +# The maximum amount of items that can be exported using the "metadata-export" script / process +# Recommend to keep this at a feasible number, as exporting large amounts of items can be resource intensive +metadataexport.max.items = 1 + +# A list of groups that are allowed to use the metadata-export script without any restrictions +#metadataexport.admin.groups = Administrator + + ### Batch Item import settings ### # The directory where the results of imports will be placed (mapfile, upload file) org.dspace.app.batchitemimport.work.dir = ${dspace.dir}/imports From e9deba8c55da9c442e42ceaaa10fd4354071ea42 Mon Sep 17 00:00:00 2001 From: Jens Vannerum Date: Wed, 20 Nov 2024 17:09:38 +0100 Subject: [PATCH 607/979] 119612: property should be commented by default and have a normal limit (cherry picked from commit a8b98bb7b78c9220e159c587a55def1bff3cb023) --- dspace/config/dspace.cfg | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/dspace/config/dspace.cfg b/dspace/config/dspace.cfg index 76a0c84c757f..d07868af33a6 100644 --- a/dspace/config/dspace.cfg +++ b/dspace/config/dspace.cfg @@ -902,9 +902,10 @@ org.dspace.app.itemexport.life.span.hours = 48 org.dspace.app.itemexport.max.size = 200 ### Bulkedit Metadata export settings -# The maximum amount of items that can be exported using the "metadata-export" script / process +# The maximum amount of items that can be exported using the "metadata-export" / "metadata-export-search" script # Recommend to keep this at a feasible number, as exporting large amounts of items can be resource intensive -metadataexport.max.items = 1 +# If not set, this will default to 500 items +# metadataexport.max.items = 500 # A list of groups that are allowed to use the metadata-export script without any restrictions #metadataexport.admin.groups = Administrator From 1e93108103a6ec26513a0d24eaf184ef6a1f5e0d Mon Sep 17 00:00:00 2001 From: Jens Vannerum Date: Mon, 20 Jan 2025 15:46:26 +0100 Subject: [PATCH 608/979] 119612: Remove group configuration and expose property to angular (cherry picked from commit c73c739deb007c657641f198414da886c2953807) --- .../content/MetadataDSpaceCsvExportServiceImpl.java | 13 ------------- dspace/config/dspace.cfg | 3 --- dspace/config/modules/rest.cfg | 1 + 3 files changed, 1 insertion(+), 16 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/content/MetadataDSpaceCsvExportServiceImpl.java b/dspace-api/src/main/java/org/dspace/content/MetadataDSpaceCsvExportServiceImpl.java index 7e313b79511e..3dd396589344 100644 --- a/dspace-api/src/main/java/org/dspace/content/MetadataDSpaceCsvExportServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/content/MetadataDSpaceCsvExportServiceImpl.java @@ -22,7 +22,6 @@ import org.dspace.content.service.MetadataDSpaceCsvExportService; import org.dspace.core.Constants; import org.dspace.core.Context; -import org.dspace.eperson.service.GroupService; import org.dspace.handle.factory.HandleServiceFactory; import org.dspace.scripts.handler.DSpaceRunnableHandler; import org.dspace.services.ConfigurationService; @@ -42,9 +41,6 @@ public class MetadataDSpaceCsvExportServiceImpl implements MetadataDSpaceCsvExpo @Autowired private ConfigurationService configurationService; - @Autowired - private GroupService groupService; - @Override public DSpaceCSV handleExport(Context context, boolean exportAllItems, boolean exportAllMetadata, String identifier, DSpaceRunnableHandler handler) throws Exception { @@ -117,15 +113,6 @@ private Iterator setItemsToExportWithLimit(Context context, Iterator DSpaceRunnableHandler handler) throws SQLException { int itemExportLimit = configurationService.getIntProperty( "metadataexport.max.items", 500); - String[] ignoreLimitGroups = configurationService.getArrayProperty( - "metadataexport.admin.groups"); - - for (String group : ignoreLimitGroups) { - if (groupService.isMember(context, context.getCurrentUser(), group)) { - itemExportLimit = Integer.MAX_VALUE; - break; - } - } List items = IteratorUtils.toList(toExport); if (items.size() > itemExportLimit) { diff --git a/dspace/config/dspace.cfg b/dspace/config/dspace.cfg index d07868af33a6..e546f1386304 100644 --- a/dspace/config/dspace.cfg +++ b/dspace/config/dspace.cfg @@ -907,9 +907,6 @@ org.dspace.app.itemexport.max.size = 200 # If not set, this will default to 500 items # metadataexport.max.items = 500 -# A list of groups that are allowed to use the metadata-export script without any restrictions -#metadataexport.admin.groups = Administrator - ### Batch Item import settings ### # The directory where the results of imports will be placed (mapfile, upload file) diff --git a/dspace/config/modules/rest.cfg b/dspace/config/modules/rest.cfg index 3bb620510e59..6dd1ab89fbe0 100644 --- a/dspace/config/modules/rest.cfg +++ b/dspace/config/modules/rest.cfg @@ -59,3 +59,4 @@ rest.properties.exposed = ldn.notify.inbox rest.properties.exposed = handle.canonical.prefix rest.properties.exposed = contentreport.enable rest.properties.exposed = duplicate.enable +rest.properties.exposed = metadataexport.max.items From 8ad376b368d065f7fb8cbf8c041836d0dbba3762 Mon Sep 17 00:00:00 2001 From: Jens Vannerum Date: Mon, 3 Feb 2025 10:09:47 +0100 Subject: [PATCH 609/979] 124504: Move configuration to be included in the bulkedit module and apply the configured limit earlier, never obtaining a larger list than actually required (cherry picked from commit b63ffd2eb4a869d17d2caca2d726d7702b05d564) --- .../app/bulkedit/MetadataExportSearch.java | 2 + .../MetadataDSpaceCsvExportServiceImpl.java | 42 +++++++++---------- .../MetadataDSpaceCsvExportService.java | 4 +- dspace/config/dspace.cfg | 7 ---- dspace/config/modules/bulkedit.cfg | 5 +++ dspace/config/modules/rest.cfg | 2 +- 6 files changed, 31 insertions(+), 31 deletions(-) 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 e4bbe335d63e..04c3a75dedd6 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 @@ -139,6 +139,8 @@ public void internalRun() throws Exception { DiscoverQuery discoverQuery = queryBuilder.buildQuery(context, dso, discoveryConfiguration, query, queryBuilderSearchFilters, "Item", 10, Long.getLong("0"), null, SortOption.DESCENDING); + // add configured limit + discoverQuery.setMaxResults(metadataDSpaceCsvExportService.getCsvExportLimit()); handler.logDebug("creating iterator"); Iterator itemIterator = searchService.iteratorSearch(context, dso, discoverQuery); diff --git a/dspace-api/src/main/java/org/dspace/content/MetadataDSpaceCsvExportServiceImpl.java b/dspace-api/src/main/java/org/dspace/content/MetadataDSpaceCsvExportServiceImpl.java index 3dd396589344..33e87a43fcd4 100644 --- a/dspace-api/src/main/java/org/dspace/content/MetadataDSpaceCsvExportServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/content/MetadataDSpaceCsvExportServiceImpl.java @@ -15,7 +15,6 @@ import java.util.Set; import java.util.UUID; -import org.apache.commons.collections.IteratorUtils; import org.dspace.app.bulkedit.DSpaceCSV; import org.dspace.app.util.service.DSpaceObjectUtils; import org.dspace.content.service.ItemService; @@ -41,6 +40,8 @@ public class MetadataDSpaceCsvExportServiceImpl implements MetadataDSpaceCsvExpo @Autowired private ConfigurationService configurationService; + private int csxExportLimit = -1; + @Override public DSpaceCSV handleExport(Context context, boolean exportAllItems, boolean exportAllMetadata, String identifier, DSpaceRunnableHandler handler) throws Exception { @@ -48,7 +49,7 @@ public DSpaceCSV handleExport(Context context, boolean exportAllItems, boolean e if (exportAllItems) { handler.logInfo("Exporting whole repository WARNING: May take some time!"); - toExport = itemService.findAll(context); + toExport = itemService.findAll(context, getCsvExportLimit(), 0); } else { DSpaceObject dso = HandleServiceFactory.getInstance().getHandleService() .resolveToObject(context, identifier); @@ -68,7 +69,7 @@ public DSpaceCSV handleExport(Context context, boolean exportAllItems, boolean e } else if (dso.getType() == Constants.COLLECTION) { handler.logInfo("Exporting collection '" + dso.getName() + "' (" + identifier + ")"); Collection collection = (Collection) dso; - toExport = itemService.findByCollection(context, collection); + toExport = itemService.findByCollection(context, collection, getCsvExportLimit(), 0); } else if (dso.getType() == Constants.COMMUNITY) { handler.logInfo("Exporting community '" + dso.getName() + "' (" + identifier + ")"); toExport = buildFromCommunity(context, (Community) dso); @@ -91,7 +92,6 @@ public DSpaceCSV export(Context context, Iterator toExport, // Process each item DSpaceCSV csv = new DSpaceCSV(exportAll); - toExport = setItemsToExportWithLimit(context, toExport, handler); while (toExport.hasNext()) { Item item = toExport.next(); csv.addItem(item); @@ -109,20 +109,6 @@ public DSpaceCSV export(Context context, Community community, return export(context, buildFromCommunity(context, community), exportAll, handler); } - private Iterator setItemsToExportWithLimit(Context context, Iterator toExport, - DSpaceRunnableHandler handler) throws SQLException { - int itemExportLimit = configurationService.getIntProperty( - "metadataexport.max.items", 500); - - List items = IteratorUtils.toList(toExport); - if (items.size() > itemExportLimit) { - handler.logWarning("The amount of items to export is higher than the limit of " + itemExportLimit - + " items. Only the first " + itemExportLimit + " items will be exported."); - items = items.subList(0, itemExportLimit); - } - return items.iterator(); - } - /** * Build a Java Collection of item IDs that are in a Community (including * its sub-Communities and Collections) @@ -135,25 +121,37 @@ private Iterator setItemsToExportWithLimit(Context context, Iterator private Iterator buildFromCommunity(Context context, Community community) throws SQLException { Set result = new HashSet<>(); + int itemsAdded = 0; // Add all the collections List collections = community.getCollections(); for (Collection collection : collections) { - Iterator items = itemService.findByCollection(context, collection); - while (items.hasNext()) { + // Never obtain more items than the configured limit + Iterator items = itemService.findByCollection(context, collection, getCsvExportLimit(), 0); + while (itemsAdded <= getCsvExportLimit() && items.hasNext()) { result.add(items.next()); + itemsAdded++; } } - // Add all the sub-communities + // Add all the sub-communities List communities = community.getSubcommunities(); for (Community subCommunity : communities) { Iterator items = buildFromCommunity(context, subCommunity); - while (items.hasNext()) { + while (itemsAdded <= getCsvExportLimit() && items.hasNext()) { result.add(items.next()); + itemsAdded++; } } return result.iterator(); } + + @Override + public int getCsvExportLimit() { + if (csxExportLimit == -1) { + csxExportLimit = configurationService.getIntProperty("bulkedit.export.max.items", 500); + } + return csxExportLimit; + } } diff --git a/dspace-api/src/main/java/org/dspace/content/service/MetadataDSpaceCsvExportService.java b/dspace-api/src/main/java/org/dspace/content/service/MetadataDSpaceCsvExportService.java index 052754a16331..a951e8cf7763 100644 --- a/dspace-api/src/main/java/org/dspace/content/service/MetadataDSpaceCsvExportService.java +++ b/dspace-api/src/main/java/org/dspace/content/service/MetadataDSpaceCsvExportService.java @@ -58,4 +58,6 @@ public DSpaceCSV export(Context context, Iterator toExport, public DSpaceCSV export(Context context, Community community, boolean exportAll, DSpaceRunnableHandler handler) throws Exception; -} \ No newline at end of file + int getCsvExportLimit(); + +} diff --git a/dspace/config/dspace.cfg b/dspace/config/dspace.cfg index e546f1386304..2abe436387dd 100644 --- a/dspace/config/dspace.cfg +++ b/dspace/config/dspace.cfg @@ -901,13 +901,6 @@ org.dspace.app.itemexport.life.span.hours = 48 # cumulative sizes are more than this entry the export is not kicked off org.dspace.app.itemexport.max.size = 200 -### Bulkedit Metadata export settings -# The maximum amount of items that can be exported using the "metadata-export" / "metadata-export-search" script -# Recommend to keep this at a feasible number, as exporting large amounts of items can be resource intensive -# If not set, this will default to 500 items -# metadataexport.max.items = 500 - - ### Batch Item import settings ### # The directory where the results of imports will be placed (mapfile, upload file) org.dspace.app.batchitemimport.work.dir = ${dspace.dir}/imports diff --git a/dspace/config/modules/bulkedit.cfg b/dspace/config/modules/bulkedit.cfg index 6174af53a0f3..e326e007f884 100644 --- a/dspace/config/modules/bulkedit.cfg +++ b/dspace/config/modules/bulkedit.cfg @@ -40,3 +40,8 @@ bulkedit.allow-bulk-deletion = dspace.agreements.end-user # By default this is set to 100 bulkedit.change.commit.count = 100 +### Bulkedit Metadata export settings +# The maximum amount of items that can be exported using the "metadata-export" / "metadata-export-search" script +# Recommend to keep this at a feasible number, as exporting large amounts of items can be resource intensive +# If not set, this will default to 500 items +# bulkedit.export.max.items = 500 diff --git a/dspace/config/modules/rest.cfg b/dspace/config/modules/rest.cfg index 6dd1ab89fbe0..4c179d5d8900 100644 --- a/dspace/config/modules/rest.cfg +++ b/dspace/config/modules/rest.cfg @@ -59,4 +59,4 @@ rest.properties.exposed = ldn.notify.inbox rest.properties.exposed = handle.canonical.prefix rest.properties.exposed = contentreport.enable rest.properties.exposed = duplicate.enable -rest.properties.exposed = metadataexport.max.items +rest.properties.exposed = bulkedit.export.max.items From 54e40db4006518172c66f28873213f851087c58f Mon Sep 17 00:00:00 2001 From: Jens Vannerum Date: Wed, 2 Apr 2025 11:09:45 +0200 Subject: [PATCH 610/979] 119612: Fix limit not applying on export (cherry picked from commit bcf48821d94d30cee10c3c2c94b33c52b0bccdb0) --- .../dspace/app/bulkedit/MetadataExportSearch.java | 2 -- .../content/MetadataDSpaceCsvExportServiceImpl.java | 13 ++++++------- 2 files changed, 6 insertions(+), 9 deletions(-) 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 04c3a75dedd6..e4bbe335d63e 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 @@ -139,8 +139,6 @@ public void internalRun() throws Exception { DiscoverQuery discoverQuery = queryBuilder.buildQuery(context, dso, discoveryConfiguration, query, queryBuilderSearchFilters, "Item", 10, Long.getLong("0"), null, SortOption.DESCENDING); - // add configured limit - discoverQuery.setMaxResults(metadataDSpaceCsvExportService.getCsvExportLimit()); handler.logDebug("creating iterator"); Iterator itemIterator = searchService.iteratorSearch(context, dso, discoverQuery); diff --git a/dspace-api/src/main/java/org/dspace/content/MetadataDSpaceCsvExportServiceImpl.java b/dspace-api/src/main/java/org/dspace/content/MetadataDSpaceCsvExportServiceImpl.java index 33e87a43fcd4..757c5d0cd529 100644 --- a/dspace-api/src/main/java/org/dspace/content/MetadataDSpaceCsvExportServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/content/MetadataDSpaceCsvExportServiceImpl.java @@ -90,9 +90,11 @@ public DSpaceCSV export(Context context, Iterator toExport, Context.Mode originalMode = context.getCurrentMode(); context.setMode(Context.Mode.READ_ONLY); - // Process each item + // Process each item until we reach the limit + int itemExportLimit = getCsvExportLimit(); DSpaceCSV csv = new DSpaceCSV(exportAll); - while (toExport.hasNext()) { + + for (int itemsAdded = 0; toExport.hasNext() && itemsAdded < itemExportLimit; itemsAdded++) { Item item = toExport.next(); csv.addItem(item); context.uncacheEntity(item); @@ -121,16 +123,14 @@ public DSpaceCSV export(Context context, Community community, private Iterator buildFromCommunity(Context context, Community community) throws SQLException { Set result = new HashSet<>(); - int itemsAdded = 0; // Add all the collections List collections = community.getCollections(); for (Collection collection : collections) { // Never obtain more items than the configured limit Iterator items = itemService.findByCollection(context, collection, getCsvExportLimit(), 0); - while (itemsAdded <= getCsvExportLimit() && items.hasNext()) { + while (result.size() < getCsvExportLimit() && items.hasNext()) { result.add(items.next()); - itemsAdded++; } } @@ -138,9 +138,8 @@ private Iterator buildFromCommunity(Context context, Community community) List communities = community.getSubcommunities(); for (Community subCommunity : communities) { Iterator items = buildFromCommunity(context, subCommunity); - while (itemsAdded <= getCsvExportLimit() && items.hasNext()) { + while (result.size() < getCsvExportLimit() && items.hasNext()) { result.add(items.next()); - itemsAdded++; } } From 4ec611bf796ac58f60c53f808facfb3211810cff Mon Sep 17 00:00:00 2001 From: Jens Vannerum Date: Tue, 19 Nov 2024 12:13:37 +0100 Subject: [PATCH 611/979] 119612: configurable limit on exporting items since it can take up a bunch of resources (cherry picked from commit b634e1e38070e65128c49d702affa3b19842ebb6) --- .../app/bulkedit/MetadataExportSearch.java | 2 +- .../MetadataDSpaceCsvExportServiceImpl.java | 43 +++++++++++++++++-- .../MetadataDSpaceCsvExportService.java | 6 ++- .../MetadataDSpaceCsvExportServiceImplIT.java | 8 +++- dspace/config/dspace.cfg | 9 ++++ 5 files changed, 59 insertions(+), 9 deletions(-) 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 027ad116a7e2..e4bbe335d63e 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 @@ -143,7 +143,7 @@ public void internalRun() throws Exception { Iterator itemIterator = searchService.iteratorSearch(context, dso, discoverQuery); handler.logDebug("creating dspacecsv"); - DSpaceCSV dSpaceCSV = metadataDSpaceCsvExportService.export(context, itemIterator, true); + DSpaceCSV dSpaceCSV = metadataDSpaceCsvExportService.export(context, itemIterator, true, handler); handler.logDebug("writing to file " + getFileNameOrExportFile()); handler.writeFilestream(context, getFileNameOrExportFile(), dSpaceCSV.getInputStream(), EXPORT_CSV); context.restoreAuthSystemState(); diff --git a/dspace-api/src/main/java/org/dspace/content/MetadataDSpaceCsvExportServiceImpl.java b/dspace-api/src/main/java/org/dspace/content/MetadataDSpaceCsvExportServiceImpl.java index 8bc34d3f5ed1..7e313b79511e 100644 --- a/dspace-api/src/main/java/org/dspace/content/MetadataDSpaceCsvExportServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/content/MetadataDSpaceCsvExportServiceImpl.java @@ -15,14 +15,17 @@ import java.util.Set; import java.util.UUID; +import org.apache.commons.collections.IteratorUtils; import org.dspace.app.bulkedit.DSpaceCSV; import org.dspace.app.util.service.DSpaceObjectUtils; import org.dspace.content.service.ItemService; import org.dspace.content.service.MetadataDSpaceCsvExportService; import org.dspace.core.Constants; import org.dspace.core.Context; +import org.dspace.eperson.service.GroupService; import org.dspace.handle.factory.HandleServiceFactory; import org.dspace.scripts.handler.DSpaceRunnableHandler; +import org.dspace.services.ConfigurationService; import org.springframework.beans.factory.annotation.Autowired; /** @@ -36,6 +39,12 @@ public class MetadataDSpaceCsvExportServiceImpl implements MetadataDSpaceCsvExpo @Autowired private DSpaceObjectUtils dSpaceObjectUtils; + @Autowired + private ConfigurationService configurationService; + + @Autowired + private GroupService groupService; + @Override public DSpaceCSV handleExport(Context context, boolean exportAllItems, boolean exportAllMetadata, String identifier, DSpaceRunnableHandler handler) throws Exception { @@ -74,17 +83,19 @@ public DSpaceCSV handleExport(Context context, boolean exportAllItems, boolean e } } - DSpaceCSV csv = this.export(context, toExport, exportAllMetadata); + DSpaceCSV csv = this.export(context, toExport, exportAllMetadata, handler); return csv; } @Override - public DSpaceCSV export(Context context, Iterator toExport, boolean exportAll) throws Exception { + public DSpaceCSV export(Context context, Iterator toExport, + boolean exportAll, DSpaceRunnableHandler handler) throws Exception { Context.Mode originalMode = context.getCurrentMode(); context.setMode(Context.Mode.READ_ONLY); // Process each item DSpaceCSV csv = new DSpaceCSV(exportAll); + toExport = setItemsToExportWithLimit(context, toExport, handler); while (toExport.hasNext()) { Item item = toExport.next(); csv.addItem(item); @@ -97,8 +108,32 @@ public DSpaceCSV export(Context context, Iterator toExport, boolean export } @Override - public DSpaceCSV export(Context context, Community community, boolean exportAll) throws Exception { - return export(context, buildFromCommunity(context, community), exportAll); + public DSpaceCSV export(Context context, Community community, + boolean exportAll, DSpaceRunnableHandler handler) throws Exception { + return export(context, buildFromCommunity(context, community), exportAll, handler); + } + + private Iterator setItemsToExportWithLimit(Context context, Iterator toExport, + DSpaceRunnableHandler handler) throws SQLException { + int itemExportLimit = configurationService.getIntProperty( + "metadataexport.max.items", 500); + String[] ignoreLimitGroups = configurationService.getArrayProperty( + "metadataexport.admin.groups"); + + for (String group : ignoreLimitGroups) { + if (groupService.isMember(context, context.getCurrentUser(), group)) { + itemExportLimit = Integer.MAX_VALUE; + break; + } + } + + List items = IteratorUtils.toList(toExport); + if (items.size() > itemExportLimit) { + handler.logWarning("The amount of items to export is higher than the limit of " + itemExportLimit + + " items. Only the first " + itemExportLimit + " items will be exported."); + items = items.subList(0, itemExportLimit); + } + return items.iterator(); } /** diff --git a/dspace-api/src/main/java/org/dspace/content/service/MetadataDSpaceCsvExportService.java b/dspace-api/src/main/java/org/dspace/content/service/MetadataDSpaceCsvExportService.java index d3fc2e823669..052754a16331 100644 --- a/dspace-api/src/main/java/org/dspace/content/service/MetadataDSpaceCsvExportService.java +++ b/dspace-api/src/main/java/org/dspace/content/service/MetadataDSpaceCsvExportService.java @@ -44,7 +44,8 @@ public DSpaceCSV handleExport(Context context, boolean exportAllItems, boolean e * @return A DSpaceCSV object containing the exported information * @throws Exception If something goes wrong */ - public DSpaceCSV export(Context context, Iterator toExport, boolean exportAll) throws Exception; + public DSpaceCSV export(Context context, Iterator toExport, + boolean exportAll, DSpaceRunnableHandler handler) throws Exception; /** * This method will export all the Items within the given Community to a DSpaceCSV @@ -54,6 +55,7 @@ public DSpaceCSV handleExport(Context context, boolean exportAllItems, boolean e * @return A DSpaceCSV object containing the exported information * @throws Exception If something goes wrong */ - public DSpaceCSV export(Context context, Community community, boolean exportAll) throws Exception; + public DSpaceCSV export(Context context, Community community, + boolean exportAll, DSpaceRunnableHandler handler) throws Exception; } \ No newline at end of file diff --git a/dspace-api/src/test/java/org/dspace/content/MetadataDSpaceCsvExportServiceImplIT.java b/dspace-api/src/test/java/org/dspace/content/MetadataDSpaceCsvExportServiceImplIT.java index c2d4f56ca61a..5a3412646482 100644 --- a/dspace-api/src/test/java/org/dspace/content/MetadataDSpaceCsvExportServiceImplIT.java +++ b/dspace-api/src/test/java/org/dspace/content/MetadataDSpaceCsvExportServiceImplIT.java @@ -16,6 +16,7 @@ import org.dspace.AbstractIntegrationTestWithDatabase; import org.dspace.app.bulkedit.DSpaceCSV; import org.dspace.app.bulkedit.DSpaceCSVLine; +import org.dspace.app.scripts.handler.impl.TestDSpaceRunnableHandler; import org.dspace.builder.CollectionBuilder; import org.dspace.builder.CommunityBuilder; import org.dspace.builder.ItemBuilder; @@ -31,6 +32,9 @@ */ public class MetadataDSpaceCsvExportServiceImplIT extends AbstractIntegrationTestWithDatabase { + + TestDSpaceRunnableHandler testDSpaceRunnableHandler = new TestDSpaceRunnableHandler(); + /** * Test of handleExport method, of class MetadataDSpaceCsvExportServiceImpl. * @throws java.lang.Exception passed through. @@ -66,7 +70,7 @@ public void testExport_3args_1() boolean exportAll = false; MetadataDSpaceCsvExportServiceImpl instance = new MetadataDSpaceCsvExportServiceImpl(); DSpaceCSV expResult = null; - DSpaceCSV result = instance.export(context, toExport, exportAll); + DSpaceCSV result = instance.export(context, toExport, exportAll, testDSpaceRunnableHandler); assertEquals(expResult, result); // TODO review the generated test code and remove the default call to fail. fail("The test case is a prototype."); @@ -105,7 +109,7 @@ public void testMappedItem() .getServiceManager() .getServiceByName(MetadataDSpaceCsvExportServiceImpl.class.getCanonicalName(), MetadataDSpaceCsvExportService.class); - DSpaceCSV result = instance.export(context, parentCommunity, false); + DSpaceCSV result = instance.export(context, parentCommunity, false, testDSpaceRunnableHandler); // Examine the result. List csvLines = result.getCSVLines(); diff --git a/dspace/config/dspace.cfg b/dspace/config/dspace.cfg index 94b731cd3488..32598bd8ecdb 100644 --- a/dspace/config/dspace.cfg +++ b/dspace/config/dspace.cfg @@ -893,6 +893,15 @@ org.dspace.app.itemexport.life.span.hours = 48 # cumulative sizes are more than this entry the export is not kicked off org.dspace.app.itemexport.max.size = 200 +### Bulkedit Metadata export settings +# The maximum amount of items that can be exported using the "metadata-export" script / process +# Recommend to keep this at a feasible number, as exporting large amounts of items can be resource intensive +metadataexport.max.items = 1 + +# A list of groups that are allowed to use the metadata-export script without any restrictions +#metadataexport.admin.groups = Administrator + + ### Batch Item import settings ### # The directory where the results of imports will be placed (mapfile, upload file) org.dspace.app.batchitemimport.work.dir = ${dspace.dir}/imports From 4626b06d7fd95da1b805ef879d174c155ba65650 Mon Sep 17 00:00:00 2001 From: Jens Vannerum Date: Wed, 20 Nov 2024 17:09:38 +0100 Subject: [PATCH 612/979] 119612: property should be commented by default and have a normal limit (cherry picked from commit a8b98bb7b78c9220e159c587a55def1bff3cb023) --- dspace/config/dspace.cfg | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/dspace/config/dspace.cfg b/dspace/config/dspace.cfg index 32598bd8ecdb..023bc45ff7c1 100644 --- a/dspace/config/dspace.cfg +++ b/dspace/config/dspace.cfg @@ -894,9 +894,10 @@ org.dspace.app.itemexport.life.span.hours = 48 org.dspace.app.itemexport.max.size = 200 ### Bulkedit Metadata export settings -# The maximum amount of items that can be exported using the "metadata-export" script / process +# The maximum amount of items that can be exported using the "metadata-export" / "metadata-export-search" script # Recommend to keep this at a feasible number, as exporting large amounts of items can be resource intensive -metadataexport.max.items = 1 +# If not set, this will default to 500 items +# metadataexport.max.items = 500 # A list of groups that are allowed to use the metadata-export script without any restrictions #metadataexport.admin.groups = Administrator From c5c8417848d7d59f975e32144b99b5fa83549748 Mon Sep 17 00:00:00 2001 From: Jens Vannerum Date: Mon, 20 Jan 2025 15:46:26 +0100 Subject: [PATCH 613/979] 119612: Remove group configuration and expose property to angular (cherry picked from commit c73c739deb007c657641f198414da886c2953807) --- .../content/MetadataDSpaceCsvExportServiceImpl.java | 13 ------------- dspace/config/dspace.cfg | 3 --- dspace/config/modules/rest.cfg | 1 + 3 files changed, 1 insertion(+), 16 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/content/MetadataDSpaceCsvExportServiceImpl.java b/dspace-api/src/main/java/org/dspace/content/MetadataDSpaceCsvExportServiceImpl.java index 7e313b79511e..3dd396589344 100644 --- a/dspace-api/src/main/java/org/dspace/content/MetadataDSpaceCsvExportServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/content/MetadataDSpaceCsvExportServiceImpl.java @@ -22,7 +22,6 @@ import org.dspace.content.service.MetadataDSpaceCsvExportService; import org.dspace.core.Constants; import org.dspace.core.Context; -import org.dspace.eperson.service.GroupService; import org.dspace.handle.factory.HandleServiceFactory; import org.dspace.scripts.handler.DSpaceRunnableHandler; import org.dspace.services.ConfigurationService; @@ -42,9 +41,6 @@ public class MetadataDSpaceCsvExportServiceImpl implements MetadataDSpaceCsvExpo @Autowired private ConfigurationService configurationService; - @Autowired - private GroupService groupService; - @Override public DSpaceCSV handleExport(Context context, boolean exportAllItems, boolean exportAllMetadata, String identifier, DSpaceRunnableHandler handler) throws Exception { @@ -117,15 +113,6 @@ private Iterator setItemsToExportWithLimit(Context context, Iterator DSpaceRunnableHandler handler) throws SQLException { int itemExportLimit = configurationService.getIntProperty( "metadataexport.max.items", 500); - String[] ignoreLimitGroups = configurationService.getArrayProperty( - "metadataexport.admin.groups"); - - for (String group : ignoreLimitGroups) { - if (groupService.isMember(context, context.getCurrentUser(), group)) { - itemExportLimit = Integer.MAX_VALUE; - break; - } - } List items = IteratorUtils.toList(toExport); if (items.size() > itemExportLimit) { diff --git a/dspace/config/dspace.cfg b/dspace/config/dspace.cfg index 023bc45ff7c1..2e5c06c707c3 100644 --- a/dspace/config/dspace.cfg +++ b/dspace/config/dspace.cfg @@ -899,9 +899,6 @@ org.dspace.app.itemexport.max.size = 200 # If not set, this will default to 500 items # metadataexport.max.items = 500 -# A list of groups that are allowed to use the metadata-export script without any restrictions -#metadataexport.admin.groups = Administrator - ### Batch Item import settings ### # The directory where the results of imports will be placed (mapfile, upload file) diff --git a/dspace/config/modules/rest.cfg b/dspace/config/modules/rest.cfg index 537eedbd087b..86f0b801dbd5 100644 --- a/dspace/config/modules/rest.cfg +++ b/dspace/config/modules/rest.cfg @@ -55,6 +55,7 @@ rest.properties.exposed = cc.license.jurisdiction rest.properties.exposed = identifiers.item-status.register-doi rest.properties.exposed = authentication-password.domain.valid rest.properties.exposed = handle.canonical.prefix +rest.properties.exposed = metadataexport.max.items #---------------------------------------------------------------# # These configs are used by the deprecated REST (v4-6) module # From 5929fdc926a5bd3ae5df08bd69860b4562490614 Mon Sep 17 00:00:00 2001 From: Jens Vannerum Date: Mon, 3 Feb 2025 10:09:47 +0100 Subject: [PATCH 614/979] 124504: Move configuration to be included in the bulkedit module and apply the configured limit earlier, never obtaining a larger list than actually required (cherry picked from commit b63ffd2eb4a869d17d2caca2d726d7702b05d564) --- .../app/bulkedit/MetadataExportSearch.java | 2 + .../MetadataDSpaceCsvExportServiceImpl.java | 42 +++++++++---------- .../MetadataDSpaceCsvExportService.java | 4 +- dspace/config/dspace.cfg | 7 ---- dspace/config/modules/bulkedit.cfg | 5 +++ dspace/config/modules/rest.cfg | 2 +- 6 files changed, 31 insertions(+), 31 deletions(-) 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 e4bbe335d63e..04c3a75dedd6 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 @@ -139,6 +139,8 @@ public void internalRun() throws Exception { DiscoverQuery discoverQuery = queryBuilder.buildQuery(context, dso, discoveryConfiguration, query, queryBuilderSearchFilters, "Item", 10, Long.getLong("0"), null, SortOption.DESCENDING); + // add configured limit + discoverQuery.setMaxResults(metadataDSpaceCsvExportService.getCsvExportLimit()); handler.logDebug("creating iterator"); Iterator itemIterator = searchService.iteratorSearch(context, dso, discoverQuery); diff --git a/dspace-api/src/main/java/org/dspace/content/MetadataDSpaceCsvExportServiceImpl.java b/dspace-api/src/main/java/org/dspace/content/MetadataDSpaceCsvExportServiceImpl.java index 3dd396589344..33e87a43fcd4 100644 --- a/dspace-api/src/main/java/org/dspace/content/MetadataDSpaceCsvExportServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/content/MetadataDSpaceCsvExportServiceImpl.java @@ -15,7 +15,6 @@ import java.util.Set; import java.util.UUID; -import org.apache.commons.collections.IteratorUtils; import org.dspace.app.bulkedit.DSpaceCSV; import org.dspace.app.util.service.DSpaceObjectUtils; import org.dspace.content.service.ItemService; @@ -41,6 +40,8 @@ public class MetadataDSpaceCsvExportServiceImpl implements MetadataDSpaceCsvExpo @Autowired private ConfigurationService configurationService; + private int csxExportLimit = -1; + @Override public DSpaceCSV handleExport(Context context, boolean exportAllItems, boolean exportAllMetadata, String identifier, DSpaceRunnableHandler handler) throws Exception { @@ -48,7 +49,7 @@ public DSpaceCSV handleExport(Context context, boolean exportAllItems, boolean e if (exportAllItems) { handler.logInfo("Exporting whole repository WARNING: May take some time!"); - toExport = itemService.findAll(context); + toExport = itemService.findAll(context, getCsvExportLimit(), 0); } else { DSpaceObject dso = HandleServiceFactory.getInstance().getHandleService() .resolveToObject(context, identifier); @@ -68,7 +69,7 @@ public DSpaceCSV handleExport(Context context, boolean exportAllItems, boolean e } else if (dso.getType() == Constants.COLLECTION) { handler.logInfo("Exporting collection '" + dso.getName() + "' (" + identifier + ")"); Collection collection = (Collection) dso; - toExport = itemService.findByCollection(context, collection); + toExport = itemService.findByCollection(context, collection, getCsvExportLimit(), 0); } else if (dso.getType() == Constants.COMMUNITY) { handler.logInfo("Exporting community '" + dso.getName() + "' (" + identifier + ")"); toExport = buildFromCommunity(context, (Community) dso); @@ -91,7 +92,6 @@ public DSpaceCSV export(Context context, Iterator toExport, // Process each item DSpaceCSV csv = new DSpaceCSV(exportAll); - toExport = setItemsToExportWithLimit(context, toExport, handler); while (toExport.hasNext()) { Item item = toExport.next(); csv.addItem(item); @@ -109,20 +109,6 @@ public DSpaceCSV export(Context context, Community community, return export(context, buildFromCommunity(context, community), exportAll, handler); } - private Iterator setItemsToExportWithLimit(Context context, Iterator toExport, - DSpaceRunnableHandler handler) throws SQLException { - int itemExportLimit = configurationService.getIntProperty( - "metadataexport.max.items", 500); - - List items = IteratorUtils.toList(toExport); - if (items.size() > itemExportLimit) { - handler.logWarning("The amount of items to export is higher than the limit of " + itemExportLimit - + " items. Only the first " + itemExportLimit + " items will be exported."); - items = items.subList(0, itemExportLimit); - } - return items.iterator(); - } - /** * Build a Java Collection of item IDs that are in a Community (including * its sub-Communities and Collections) @@ -135,25 +121,37 @@ private Iterator setItemsToExportWithLimit(Context context, Iterator private Iterator buildFromCommunity(Context context, Community community) throws SQLException { Set result = new HashSet<>(); + int itemsAdded = 0; // Add all the collections List collections = community.getCollections(); for (Collection collection : collections) { - Iterator items = itemService.findByCollection(context, collection); - while (items.hasNext()) { + // Never obtain more items than the configured limit + Iterator items = itemService.findByCollection(context, collection, getCsvExportLimit(), 0); + while (itemsAdded <= getCsvExportLimit() && items.hasNext()) { result.add(items.next()); + itemsAdded++; } } - // Add all the sub-communities + // Add all the sub-communities List communities = community.getSubcommunities(); for (Community subCommunity : communities) { Iterator items = buildFromCommunity(context, subCommunity); - while (items.hasNext()) { + while (itemsAdded <= getCsvExportLimit() && items.hasNext()) { result.add(items.next()); + itemsAdded++; } } return result.iterator(); } + + @Override + public int getCsvExportLimit() { + if (csxExportLimit == -1) { + csxExportLimit = configurationService.getIntProperty("bulkedit.export.max.items", 500); + } + return csxExportLimit; + } } diff --git a/dspace-api/src/main/java/org/dspace/content/service/MetadataDSpaceCsvExportService.java b/dspace-api/src/main/java/org/dspace/content/service/MetadataDSpaceCsvExportService.java index 052754a16331..a951e8cf7763 100644 --- a/dspace-api/src/main/java/org/dspace/content/service/MetadataDSpaceCsvExportService.java +++ b/dspace-api/src/main/java/org/dspace/content/service/MetadataDSpaceCsvExportService.java @@ -58,4 +58,6 @@ public DSpaceCSV export(Context context, Iterator toExport, public DSpaceCSV export(Context context, Community community, boolean exportAll, DSpaceRunnableHandler handler) throws Exception; -} \ No newline at end of file + int getCsvExportLimit(); + +} diff --git a/dspace/config/dspace.cfg b/dspace/config/dspace.cfg index 2e5c06c707c3..94b731cd3488 100644 --- a/dspace/config/dspace.cfg +++ b/dspace/config/dspace.cfg @@ -893,13 +893,6 @@ org.dspace.app.itemexport.life.span.hours = 48 # cumulative sizes are more than this entry the export is not kicked off org.dspace.app.itemexport.max.size = 200 -### Bulkedit Metadata export settings -# The maximum amount of items that can be exported using the "metadata-export" / "metadata-export-search" script -# Recommend to keep this at a feasible number, as exporting large amounts of items can be resource intensive -# If not set, this will default to 500 items -# metadataexport.max.items = 500 - - ### Batch Item import settings ### # The directory where the results of imports will be placed (mapfile, upload file) org.dspace.app.batchitemimport.work.dir = ${dspace.dir}/imports diff --git a/dspace/config/modules/bulkedit.cfg b/dspace/config/modules/bulkedit.cfg index 6174af53a0f3..e326e007f884 100644 --- a/dspace/config/modules/bulkedit.cfg +++ b/dspace/config/modules/bulkedit.cfg @@ -40,3 +40,8 @@ bulkedit.allow-bulk-deletion = dspace.agreements.end-user # By default this is set to 100 bulkedit.change.commit.count = 100 +### Bulkedit Metadata export settings +# The maximum amount of items that can be exported using the "metadata-export" / "metadata-export-search" script +# Recommend to keep this at a feasible number, as exporting large amounts of items can be resource intensive +# If not set, this will default to 500 items +# bulkedit.export.max.items = 500 diff --git a/dspace/config/modules/rest.cfg b/dspace/config/modules/rest.cfg index 86f0b801dbd5..b123b5b654b6 100644 --- a/dspace/config/modules/rest.cfg +++ b/dspace/config/modules/rest.cfg @@ -55,7 +55,7 @@ rest.properties.exposed = cc.license.jurisdiction rest.properties.exposed = identifiers.item-status.register-doi rest.properties.exposed = authentication-password.domain.valid rest.properties.exposed = handle.canonical.prefix -rest.properties.exposed = metadataexport.max.items +rest.properties.exposed = bulkedit.export.max.items #---------------------------------------------------------------# # These configs are used by the deprecated REST (v4-6) module # From fae4130d41212972358b8290b1bcacfa6fb82f65 Mon Sep 17 00:00:00 2001 From: Jens Vannerum Date: Wed, 2 Apr 2025 11:09:45 +0200 Subject: [PATCH 615/979] 119612: Fix limit not applying on export (cherry picked from commit bcf48821d94d30cee10c3c2c94b33c52b0bccdb0) --- .../dspace/app/bulkedit/MetadataExportSearch.java | 2 -- .../content/MetadataDSpaceCsvExportServiceImpl.java | 13 ++++++------- 2 files changed, 6 insertions(+), 9 deletions(-) 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 04c3a75dedd6..e4bbe335d63e 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 @@ -139,8 +139,6 @@ public void internalRun() throws Exception { DiscoverQuery discoverQuery = queryBuilder.buildQuery(context, dso, discoveryConfiguration, query, queryBuilderSearchFilters, "Item", 10, Long.getLong("0"), null, SortOption.DESCENDING); - // add configured limit - discoverQuery.setMaxResults(metadataDSpaceCsvExportService.getCsvExportLimit()); handler.logDebug("creating iterator"); Iterator itemIterator = searchService.iteratorSearch(context, dso, discoverQuery); diff --git a/dspace-api/src/main/java/org/dspace/content/MetadataDSpaceCsvExportServiceImpl.java b/dspace-api/src/main/java/org/dspace/content/MetadataDSpaceCsvExportServiceImpl.java index 33e87a43fcd4..757c5d0cd529 100644 --- a/dspace-api/src/main/java/org/dspace/content/MetadataDSpaceCsvExportServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/content/MetadataDSpaceCsvExportServiceImpl.java @@ -90,9 +90,11 @@ public DSpaceCSV export(Context context, Iterator toExport, Context.Mode originalMode = context.getCurrentMode(); context.setMode(Context.Mode.READ_ONLY); - // Process each item + // Process each item until we reach the limit + int itemExportLimit = getCsvExportLimit(); DSpaceCSV csv = new DSpaceCSV(exportAll); - while (toExport.hasNext()) { + + for (int itemsAdded = 0; toExport.hasNext() && itemsAdded < itemExportLimit; itemsAdded++) { Item item = toExport.next(); csv.addItem(item); context.uncacheEntity(item); @@ -121,16 +123,14 @@ public DSpaceCSV export(Context context, Community community, private Iterator buildFromCommunity(Context context, Community community) throws SQLException { Set result = new HashSet<>(); - int itemsAdded = 0; // Add all the collections List collections = community.getCollections(); for (Collection collection : collections) { // Never obtain more items than the configured limit Iterator items = itemService.findByCollection(context, collection, getCsvExportLimit(), 0); - while (itemsAdded <= getCsvExportLimit() && items.hasNext()) { + while (result.size() < getCsvExportLimit() && items.hasNext()) { result.add(items.next()); - itemsAdded++; } } @@ -138,9 +138,8 @@ private Iterator buildFromCommunity(Context context, Community community) List communities = community.getSubcommunities(); for (Community subCommunity : communities) { Iterator items = buildFromCommunity(context, subCommunity); - while (itemsAdded <= getCsvExportLimit() && items.hasNext()) { + while (result.size() < getCsvExportLimit() && items.hasNext()) { result.add(items.next()); - itemsAdded++; } } From 5bcd3ce8c60a11d625c9c0328767a5d581dfcc28 Mon Sep 17 00:00:00 2001 From: "max.nuding" Date: Fri, 2 May 2025 11:13:29 +0200 Subject: [PATCH 616/979] pull primary bitstream out of innerloop (cherry picked from commit 81e2314ec65977a91ec1dea66313209051d7f715) --- .../java/org/dspace/app/rest/submit/step/UploadStep.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/step/UploadStep.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/step/UploadStep.java index e0a9cb17e01c..877e64756df5 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/step/UploadStep.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/step/UploadStep.java @@ -55,13 +55,13 @@ public DataUpload getData(SubmissionService submissionService, InProgressSubmiss DataUpload result = new DataUpload(); List bundles = itemService.getBundles(obj.getItem(), Constants.CONTENT_BUNDLE_NAME); for (Bundle bundle : bundles) { + Bitstream primaryBitstream = bundle.getPrimaryBitstream(); + if (Objects.nonNull(primaryBitstream)) { + result.setPrimary(primaryBitstream.getID()); + } for (Bitstream source : bundle.getBitstreams()) { - Bitstream primaryBitstream = bundle.getPrimaryBitstream(); UploadBitstreamRest b = submissionService.buildUploadBitstream(configurationService, source); result.getFiles().add(b); - if (Objects.nonNull(primaryBitstream)) { - result.setPrimary(primaryBitstream.getID()); - } } } return result; From eb5f09f3f21180afeb7e52ff62ab409368fab3c2 Mon Sep 17 00:00:00 2001 From: Adamo Date: Fri, 2 May 2025 12:42:47 +0200 Subject: [PATCH 617/979] [DURACOM-355] Update to avoid NPE during WOS live import when no api key is found (cherry picked from commit 70f1c83bf0a7b32c10eaf22c5cc990c6b10e5784) --- .../WOSImportMetadataSourceServiceImpl.java | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/importer/external/wos/service/WOSImportMetadataSourceServiceImpl.java b/dspace-api/src/main/java/org/dspace/importer/external/wos/service/WOSImportMetadataSourceServiceImpl.java index c33a2f890123..9bffa2a84a19 100644 --- a/dspace-api/src/main/java/org/dspace/importer/external/wos/service/WOSImportMetadataSourceServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/importer/external/wos/service/WOSImportMetadataSourceServiceImpl.java @@ -56,7 +56,7 @@ public class WOSImportMetadataSourceServiceImpl extends AbstractImportMetadataSo private static final String AI_PATTERN = "^AI=(.*)"; private static final Pattern ISI_PATTERN = Pattern.compile("^\\d{15}$"); - private int timeout = 1000; + private final int timeout = 1000; private String url; private String urlSearch; @@ -126,7 +126,7 @@ public Collection findMatchingRecords(Query query) throws Metadata */ private class SearchNBByQueryCallable implements Callable { - private String query; + private final String query; private SearchNBByQueryCallable(String queryString) { this.query = queryString; @@ -155,7 +155,8 @@ public Integer call() throws Exception { Element tot = xpath.evaluateFirst(root); return Integer.valueOf(tot.getValue()); } - return null; + log.warn("API key is missing: cannot execute count request."); + return 0; } } @@ -166,7 +167,7 @@ public Integer call() throws Exception { */ private class FindByIdCallable implements Callable> { - private String doi; + private final String doi; private FindByIdCallable(String doi) { this.doi = URLEncoder.encode(doi, StandardCharsets.UTF_8); @@ -185,6 +186,8 @@ public List call() throws Exception { for (Element record : elements) { results.add(transformSourceRecords(record)); } + } else { + log.warn("API key is missing: cannot execute live import request."); } return results; } @@ -202,7 +205,7 @@ public List call() throws Exception { */ private class SearchByQueryCallable implements Callable> { - private Query query; + private final Query query; private SearchByQueryCallable(String queryString, Integer maxResult, Integer start) { query = new Query(); @@ -232,6 +235,8 @@ public List call() throws Exception { for (Element el : omElements) { results.add(transformSourceRecords(el)); } + } else { + log.warn("API key is missing: cannot execute live import request."); } return results; } @@ -270,9 +275,7 @@ private String checkQuery(String query) { } else if (isIsi(query)) { return "UT=(" + query + ")"; } - StringBuilder queryBuilder = new StringBuilder("TS=("); - queryBuilder.append(query).append(")"); - return queryBuilder.toString(); + return "TS=(" + query + ")"; } private boolean isIsi(String query) { From c6e5bbed624405a805e0f355626a2d5de0382268 Mon Sep 17 00:00:00 2001 From: Adamo Date: Fri, 2 May 2025 12:42:47 +0200 Subject: [PATCH 618/979] [DURACOM-355] Update to avoid NPE during WOS live import when no api key is found (cherry picked from commit 70f1c83bf0a7b32c10eaf22c5cc990c6b10e5784) --- .../WOSImportMetadataSourceServiceImpl.java | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/importer/external/wos/service/WOSImportMetadataSourceServiceImpl.java b/dspace-api/src/main/java/org/dspace/importer/external/wos/service/WOSImportMetadataSourceServiceImpl.java index c33a2f890123..9bffa2a84a19 100644 --- a/dspace-api/src/main/java/org/dspace/importer/external/wos/service/WOSImportMetadataSourceServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/importer/external/wos/service/WOSImportMetadataSourceServiceImpl.java @@ -56,7 +56,7 @@ public class WOSImportMetadataSourceServiceImpl extends AbstractImportMetadataSo private static final String AI_PATTERN = "^AI=(.*)"; private static final Pattern ISI_PATTERN = Pattern.compile("^\\d{15}$"); - private int timeout = 1000; + private final int timeout = 1000; private String url; private String urlSearch; @@ -126,7 +126,7 @@ public Collection findMatchingRecords(Query query) throws Metadata */ private class SearchNBByQueryCallable implements Callable { - private String query; + private final String query; private SearchNBByQueryCallable(String queryString) { this.query = queryString; @@ -155,7 +155,8 @@ public Integer call() throws Exception { Element tot = xpath.evaluateFirst(root); return Integer.valueOf(tot.getValue()); } - return null; + log.warn("API key is missing: cannot execute count request."); + return 0; } } @@ -166,7 +167,7 @@ public Integer call() throws Exception { */ private class FindByIdCallable implements Callable> { - private String doi; + private final String doi; private FindByIdCallable(String doi) { this.doi = URLEncoder.encode(doi, StandardCharsets.UTF_8); @@ -185,6 +186,8 @@ public List call() throws Exception { for (Element record : elements) { results.add(transformSourceRecords(record)); } + } else { + log.warn("API key is missing: cannot execute live import request."); } return results; } @@ -202,7 +205,7 @@ public List call() throws Exception { */ private class SearchByQueryCallable implements Callable> { - private Query query; + private final Query query; private SearchByQueryCallable(String queryString, Integer maxResult, Integer start) { query = new Query(); @@ -232,6 +235,8 @@ public List call() throws Exception { for (Element el : omElements) { results.add(transformSourceRecords(el)); } + } else { + log.warn("API key is missing: cannot execute live import request."); } return results; } @@ -270,9 +275,7 @@ private String checkQuery(String query) { } else if (isIsi(query)) { return "UT=(" + query + ")"; } - StringBuilder queryBuilder = new StringBuilder("TS=("); - queryBuilder.append(query).append(")"); - return queryBuilder.toString(); + return "TS=(" + query + ")"; } private boolean isIsi(String query) { From 605956b0739372decfc398d1300a0ff3620b4b6c Mon Sep 17 00:00:00 2001 From: Adamo Date: Fri, 2 May 2025 18:08:15 +0200 Subject: [PATCH 619/979] [DURACOM-311] Ensure stable pagination in bulk access control by adding explicit sort (cherry picked from commit ced9e9b9f731162c43d5823a9d2fd05e74ab233a) --- .../bulkaccesscontrol/BulkAccessControl.java | 2 +- .../BulkAccessControlIT.java | 84 +++++++++++++++++++ 2 files changed, 85 insertions(+), 1 deletion(-) 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 7bef232f0450..30f68efaf3cb 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 @@ -416,7 +416,7 @@ private DiscoverQuery buildDiscoveryQuery(String query, int start, int limit) { discoverQuery.setQuery(query); discoverQuery.setStart(start); discoverQuery.setMaxResults(limit); - + discoverQuery.setSortField("search.resourceid", DiscoverQuery.SORT_ORDER.asc); return discoverQuery; } diff --git a/dspace-api/src/test/java/org/dspace/app/bulkaccesscontrol/BulkAccessControlIT.java b/dspace-api/src/test/java/org/dspace/app/bulkaccesscontrol/BulkAccessControlIT.java index 73f02e40494c..106b176ee5b4 100644 --- a/dspace-api/src/test/java/org/dspace/app/bulkaccesscontrol/BulkAccessControlIT.java +++ b/dspace-api/src/test/java/org/dspace/app/bulkaccesscontrol/BulkAccessControlIT.java @@ -32,8 +32,10 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.UUID; import java.util.stream.Collectors; @@ -1831,6 +1833,88 @@ public void performBulkAccessWithHelpParamTest() throws Exception { assertThat(testDSpaceRunnableHandler.getWarningMessages(), empty()); } + + @Test + public void bulkAccessControlShouldProcessEachItemOnceWithPagination() throws Exception { + context.turnOffAuthorisationSystem(); + + Community community = CommunityBuilder.createCommunity(context) + .withName("Test Community") + .build(); + + Collection collection = CollectionBuilder.createCollection(context, community) + .withName("Test Collection") + .build(); + + List createdItemIDs = new ArrayList<>(); + + for (int i = 0; i < 25; i++) { + Item item = ItemBuilder.createItem(context, collection).build(); + + Bundle bundle = BundleBuilder.createBundle(context, item) + .withName("ORIGINAL") + .build(); + + BitstreamBuilder.createBitstream(context, bundle, + IOUtils.toInputStream("Bitstream content " + i, + CharEncoding.UTF_8)) + .withName("bitstream_" + i) + .build(); + + createdItemIDs.add(item.getID()); + } + + context.restoreAuthSystemState(); + + // JSON without constraints: apply to ALL items + String json = """ + { "item": { + "mode": "add", + "accessConditions": [ + { + "name": "openaccess" + } + ] + }} + """; + + buildJsonFile(json); + + String[] args = { + "bulk-access-control", + "-u", community.getID().toString(), + "-f", tempFilePath, + "-e", admin.getEmail() + }; + + TestDSpaceRunnableHandler testHandler = new TestDSpaceRunnableHandler(); + ScriptLauncher.handleScript(args, ScriptLauncher.getConfig(kernelImpl), testHandler, kernelImpl); + + assertThat(testHandler.getErrorMessages(), empty()); + assertThat(testHandler.getWarningMessages(), empty()); + + // Collect item IDs from the info messages + List infoMessages = testHandler.getInfoMessages(); + List updatedItemIDs = infoMessages.stream() + .map(msg -> { + int startIdx = msg.indexOf("Item {") + 6; + int endIdx = msg.indexOf("}", startIdx); + return UUID.fromString(msg.substring(startIdx, endIdx)); + }) + .toList(); + + Set uniqueUpdatedItemIDs = new HashSet<>(updatedItemIDs); + + // Check if any item was processed multiple times + assertThat("Some items were processed more than once!", + uniqueUpdatedItemIDs.size(), is(updatedItemIDs.size())); + + // Check all items were updated once + assertThat("Not all created items were updated!", + createdItemIDs, containsInAnyOrder(uniqueUpdatedItemIDs.toArray())); + } + + private List findItems(String query) throws SearchServiceException { DiscoverQuery discoverQuery = new DiscoverQuery(); From be55a2ae7a3f6acd453ab827521b3eb55610d985 Mon Sep 17 00:00:00 2001 From: Adamo Date: Fri, 2 May 2025 18:08:15 +0200 Subject: [PATCH 620/979] [DURACOM-311] Ensure stable pagination in bulk access control by adding explicit sort (cherry picked from commit ced9e9b9f731162c43d5823a9d2fd05e74ab233a) --- .../bulkaccesscontrol/BulkAccessControl.java | 2 +- .../BulkAccessControlIT.java | 84 +++++++++++++++++++ 2 files changed, 85 insertions(+), 1 deletion(-) 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 7bef232f0450..30f68efaf3cb 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 @@ -416,7 +416,7 @@ private DiscoverQuery buildDiscoveryQuery(String query, int start, int limit) { discoverQuery.setQuery(query); discoverQuery.setStart(start); discoverQuery.setMaxResults(limit); - + discoverQuery.setSortField("search.resourceid", DiscoverQuery.SORT_ORDER.asc); return discoverQuery; } diff --git a/dspace-api/src/test/java/org/dspace/app/bulkaccesscontrol/BulkAccessControlIT.java b/dspace-api/src/test/java/org/dspace/app/bulkaccesscontrol/BulkAccessControlIT.java index 73f02e40494c..106b176ee5b4 100644 --- a/dspace-api/src/test/java/org/dspace/app/bulkaccesscontrol/BulkAccessControlIT.java +++ b/dspace-api/src/test/java/org/dspace/app/bulkaccesscontrol/BulkAccessControlIT.java @@ -32,8 +32,10 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.UUID; import java.util.stream.Collectors; @@ -1831,6 +1833,88 @@ public void performBulkAccessWithHelpParamTest() throws Exception { assertThat(testDSpaceRunnableHandler.getWarningMessages(), empty()); } + + @Test + public void bulkAccessControlShouldProcessEachItemOnceWithPagination() throws Exception { + context.turnOffAuthorisationSystem(); + + Community community = CommunityBuilder.createCommunity(context) + .withName("Test Community") + .build(); + + Collection collection = CollectionBuilder.createCollection(context, community) + .withName("Test Collection") + .build(); + + List createdItemIDs = new ArrayList<>(); + + for (int i = 0; i < 25; i++) { + Item item = ItemBuilder.createItem(context, collection).build(); + + Bundle bundle = BundleBuilder.createBundle(context, item) + .withName("ORIGINAL") + .build(); + + BitstreamBuilder.createBitstream(context, bundle, + IOUtils.toInputStream("Bitstream content " + i, + CharEncoding.UTF_8)) + .withName("bitstream_" + i) + .build(); + + createdItemIDs.add(item.getID()); + } + + context.restoreAuthSystemState(); + + // JSON without constraints: apply to ALL items + String json = """ + { "item": { + "mode": "add", + "accessConditions": [ + { + "name": "openaccess" + } + ] + }} + """; + + buildJsonFile(json); + + String[] args = { + "bulk-access-control", + "-u", community.getID().toString(), + "-f", tempFilePath, + "-e", admin.getEmail() + }; + + TestDSpaceRunnableHandler testHandler = new TestDSpaceRunnableHandler(); + ScriptLauncher.handleScript(args, ScriptLauncher.getConfig(kernelImpl), testHandler, kernelImpl); + + assertThat(testHandler.getErrorMessages(), empty()); + assertThat(testHandler.getWarningMessages(), empty()); + + // Collect item IDs from the info messages + List infoMessages = testHandler.getInfoMessages(); + List updatedItemIDs = infoMessages.stream() + .map(msg -> { + int startIdx = msg.indexOf("Item {") + 6; + int endIdx = msg.indexOf("}", startIdx); + return UUID.fromString(msg.substring(startIdx, endIdx)); + }) + .toList(); + + Set uniqueUpdatedItemIDs = new HashSet<>(updatedItemIDs); + + // Check if any item was processed multiple times + assertThat("Some items were processed more than once!", + uniqueUpdatedItemIDs.size(), is(updatedItemIDs.size())); + + // Check all items were updated once + assertThat("Not all created items were updated!", + createdItemIDs, containsInAnyOrder(uniqueUpdatedItemIDs.toArray())); + } + + private List findItems(String query) throws SearchServiceException { DiscoverQuery discoverQuery = new DiscoverQuery(); From 3f9f5639f64e32ebc56c521f3c67e913fca35de3 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Fri, 2 May 2025 15:49:00 -0500 Subject: [PATCH 621/979] Must use older Java syntax in DSpace 7 --- .../BulkAccessControlIT.java | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/dspace-api/src/test/java/org/dspace/app/bulkaccesscontrol/BulkAccessControlIT.java b/dspace-api/src/test/java/org/dspace/app/bulkaccesscontrol/BulkAccessControlIT.java index 106b176ee5b4..0dd1bf076169 100644 --- a/dspace-api/src/test/java/org/dspace/app/bulkaccesscontrol/BulkAccessControlIT.java +++ b/dspace-api/src/test/java/org/dspace/app/bulkaccesscontrol/BulkAccessControlIT.java @@ -1867,16 +1867,16 @@ public void bulkAccessControlShouldProcessEachItemOnceWithPagination() throws Ex context.restoreAuthSystemState(); // JSON without constraints: apply to ALL items - String json = """ - { "item": { - "mode": "add", - "accessConditions": [ - { - "name": "openaccess" - } - ] - }} - """; + String json = "{\n" + + " \"item\": {\n" + + " \"mode\": \"add\",\n" + + " \"accessConditions\": [\n" + + " {\n" + + " \"name\": \"openaccess\"\n" + + " }\n" + + " ]\n" + + " }\n" + + "}"; buildJsonFile(json); @@ -1901,7 +1901,7 @@ public void bulkAccessControlShouldProcessEachItemOnceWithPagination() throws Ex int endIdx = msg.indexOf("}", startIdx); return UUID.fromString(msg.substring(startIdx, endIdx)); }) - .toList(); + .collect(Collectors.toList()); Set uniqueUpdatedItemIDs = new HashSet<>(updatedItemIDs); From ab0a4531ca157a1b52aedc04c765e250e0d17d63 Mon Sep 17 00:00:00 2001 From: Adamo Date: Thu, 1 May 2025 18:52:26 +0200 Subject: [PATCH 622/979] [DURACOM-356] Updated Sherpa mapping to use creativeworkseries.issn instead of dc.identifier.issn (cherry picked from commit 480bad24440b615d2d10686e37bdbd4c5df97776) --- .../provider/impl/SHERPAv2JournalISSNDataProvider.java | 3 +-- dspace/config/spring/api/sherpa.xml | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/external/provider/impl/SHERPAv2JournalISSNDataProvider.java b/dspace-api/src/main/java/org/dspace/external/provider/impl/SHERPAv2JournalISSNDataProvider.java index 9e61b9ac2ac0..860334847ccb 100644 --- a/dspace-api/src/main/java/org/dspace/external/provider/impl/SHERPAv2JournalISSNDataProvider.java +++ b/dspace-api/src/main/java/org/dspace/external/provider/impl/SHERPAv2JournalISSNDataProvider.java @@ -106,8 +106,7 @@ private ExternalDataObject constructExternalDataObjectFromSherpaJournal(SHERPAJo String issn = sherpaJournal.getIssns().get(0); externalDataObject.setId(issn); externalDataObject.addMetadata(new MetadataValueDTO( - "dc", "identifier", "issn", null, issn)); - + "creativeworkseries", "issn", null, null, issn)); } log.debug("New external data object. Title=" + externalDataObject.getValue() + ". ID=" diff --git a/dspace/config/spring/api/sherpa.xml b/dspace/config/spring/api/sherpa.xml index 0414f3f8e4b4..da42880218ca 100644 --- a/dspace/config/spring/api/sherpa.xml +++ b/dspace/config/spring/api/sherpa.xml @@ -17,7 +17,7 @@ - dc.identifier.issn + creativeworkseries.issn From 29fc51c02aed49920ef87eeb62df3b0a5a37272d Mon Sep 17 00:00:00 2001 From: Adamo Date: Fri, 2 May 2025 19:23:37 +0200 Subject: [PATCH 623/979] [DURACOM-356] Fixed tests (cherry picked from commit fc8a434234e96c74386a958bd1a816d07c9921ad) --- .../org/dspace/app/sherpa/SHERPADataProviderTest.java | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/dspace-api/src/test/java/org/dspace/app/sherpa/SHERPADataProviderTest.java b/dspace-api/src/test/java/org/dspace/app/sherpa/SHERPADataProviderTest.java index cbea55ea0787..b19b35895bb9 100644 --- a/dspace-api/src/test/java/org/dspace/app/sherpa/SHERPADataProviderTest.java +++ b/dspace-api/src/test/java/org/dspace/app/sherpa/SHERPADataProviderTest.java @@ -87,9 +87,8 @@ public void testGetJournalISSNExternalObject() { if (metadataValue.getSchema().equalsIgnoreCase("dc") && metadataValue.getElement().equalsIgnoreCase("title")) { title = metadataValue.getValue(); - } else if (metadataValue.getSchema().equalsIgnoreCase("dc") - && metadataValue.getElement().equalsIgnoreCase("identifier") - && metadataValue.getQualifier().equalsIgnoreCase("issn")) { + } else if (metadataValue.getSchema().equalsIgnoreCase("creativeworkseries") + && metadataValue.getElement().equalsIgnoreCase("issn")) { identifier = metadataValue.getValue(); } } @@ -136,9 +135,8 @@ public void testSearchJournalISSNExternalObjects() { if (metadataValue.getSchema().equalsIgnoreCase("dc") && metadataValue.getElement().equalsIgnoreCase("title")) { title = metadataValue.getValue(); - } else if (metadataValue.getSchema().equalsIgnoreCase("dc") - && metadataValue.getElement().equalsIgnoreCase("identifier") - && metadataValue.getQualifier().equalsIgnoreCase("issn")) { + } else if (metadataValue.getSchema().equalsIgnoreCase("creativeworkseries") + && metadataValue.getElement().equalsIgnoreCase("issn")) { identifier = metadataValue.getValue(); } } From 20e3f2e03039f365cfb5cb85a0ffc4cdfb13a229 Mon Sep 17 00:00:00 2001 From: Adamo Date: Mon, 5 May 2025 12:46:52 +0200 Subject: [PATCH 624/979] [DURACOM-356] Updated Sherpa Journal mapping to use creativeworkseries.issn instead of dc.identifier.issn (cherry picked from commit 979476b3f35c5c60aa13a8fd4233b7286666b94e) --- .../external/provider/impl/SHERPAv2JournalDataProvider.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dspace-api/src/main/java/org/dspace/external/provider/impl/SHERPAv2JournalDataProvider.java b/dspace-api/src/main/java/org/dspace/external/provider/impl/SHERPAv2JournalDataProvider.java index a4276c83ed70..f0ce6a979a1f 100644 --- a/dspace-api/src/main/java/org/dspace/external/provider/impl/SHERPAv2JournalDataProvider.java +++ b/dspace-api/src/main/java/org/dspace/external/provider/impl/SHERPAv2JournalDataProvider.java @@ -97,7 +97,7 @@ private ExternalDataObject constructExternalDataObjectFromSherpaJournal(SHERPAJo if (CollectionUtils.isNotEmpty(sherpaJournal.getIssns())) { String issn = sherpaJournal.getIssns().get(0); externalDataObject.addMetadata(new MetadataValueDTO( - "dc", "identifier", "issn", null, issn)); + "creativeworkseries", "issn", null, null, issn)); } From 27d3d75ca5f8b9fdb12531215e02d2496acffb18 Mon Sep 17 00:00:00 2001 From: Adamo Date: Mon, 5 May 2025 12:49:16 +0200 Subject: [PATCH 625/979] [DURACOM-356] Updated tests to use metadata constants (cherry picked from commit 94c5f5d6f7d085be572e3e62837b1f8eac5191d8) --- .../app/sherpa/SHERPADataProviderTest.java | 105 +++++++++--------- 1 file changed, 53 insertions(+), 52 deletions(-) diff --git a/dspace-api/src/test/java/org/dspace/app/sherpa/SHERPADataProviderTest.java b/dspace-api/src/test/java/org/dspace/app/sherpa/SHERPADataProviderTest.java index b19b35895bb9..f37d9469d12d 100644 --- a/dspace-api/src/test/java/org/dspace/app/sherpa/SHERPADataProviderTest.java +++ b/dspace-api/src/test/java/org/dspace/app/sherpa/SHERPADataProviderTest.java @@ -38,6 +38,12 @@ public class SHERPADataProviderTest extends AbstractDSpaceTest { ExternalDataProvider sherpaPublisherProvider; ExternalDataProvider sherpaJournalIssnProvider; + private static final MetadataFieldRef TITLE_FIELD = new MetadataFieldRef("dc", "title", null); + private static final MetadataFieldRef ISSN_FIELD = new MetadataFieldRef("creativeworkseries", "issn", null); + private static final MetadataFieldRef SHERPA_PUBLISHER_FIELD = + new MetadataFieldRef("dc", "identifier", "sherpaPublisher"); + private static final MetadataFieldRef OTHER_FIELD = new MetadataFieldRef("dc", "identifier", "other"); + @BeforeClass public static void setUpClass() { } @@ -84,11 +90,9 @@ public void testGetJournalISSNExternalObject() { String title = null; String identifier = null; for (MetadataValueDTO metadataValue : dataObject.getMetadata()) { - if (metadataValue.getSchema().equalsIgnoreCase("dc") && - metadataValue.getElement().equalsIgnoreCase("title")) { + if (TITLE_FIELD.matches(metadataValue)) { title = metadataValue.getValue(); - } else if (metadataValue.getSchema().equalsIgnoreCase("creativeworkseries") - && metadataValue.getElement().equalsIgnoreCase("issn")) { + } else if (ISSN_FIELD.matches(metadataValue)) { identifier = metadataValue.getValue(); } } @@ -132,11 +136,9 @@ public void testSearchJournalISSNExternalObjects() { String title = null; String identifier = null; for (MetadataValueDTO metadataValue : dataObject.getMetadata()) { - if (metadataValue.getSchema().equalsIgnoreCase("dc") && - metadataValue.getElement().equalsIgnoreCase("title")) { + if (TITLE_FIELD.matches(metadataValue)) { title = metadataValue.getValue(); - } else if (metadataValue.getSchema().equalsIgnoreCase("creativeworkseries") - && metadataValue.getElement().equalsIgnoreCase("issn")) { + } else if (ISSN_FIELD.matches(metadataValue)) { identifier = metadataValue.getValue(); } } @@ -172,12 +174,9 @@ public void testGetJournalExternalObject() { String title = null; String identifier = null; for (MetadataValueDTO metadataValue : dataObject.getMetadata()) { - if (metadataValue.getSchema().equalsIgnoreCase("dc") && - metadataValue.getElement().equalsIgnoreCase("title")) { + if (TITLE_FIELD.matches(metadataValue)) { title = metadataValue.getValue(); - } else if (metadataValue.getSchema().equalsIgnoreCase("dc") - && metadataValue.getElement().equalsIgnoreCase("identifier") - && metadataValue.getQualifier().equalsIgnoreCase("issn")) { + } else if (ISSN_FIELD.matches(metadataValue)) { identifier = metadataValue.getValue(); } } @@ -222,12 +221,9 @@ public void testSearchJournalObjects() { String title = null; String identifier = null; for (MetadataValueDTO metadataValue : dataObject.getMetadata()) { - if (metadataValue.getSchema().equalsIgnoreCase("dc") && - metadataValue.getElement().equalsIgnoreCase("title")) { + if (TITLE_FIELD.matches(metadataValue)) { title = metadataValue.getValue(); - } else if (metadataValue.getSchema().equalsIgnoreCase("dc") - && metadataValue.getElement().equalsIgnoreCase("identifier") - && metadataValue.getQualifier().equalsIgnoreCase("issn")) { + } else if (ISSN_FIELD.matches(metadataValue)) { identifier = metadataValue.getValue(); } } @@ -268,16 +264,11 @@ public void testGetPublisherExternalObject() { String identifier = null; String url = null; for (MetadataValueDTO metadataValue : dataObject.getMetadata()) { - if (metadataValue.getSchema().equalsIgnoreCase("dc") && - metadataValue.getElement().equalsIgnoreCase("title")) { + if (TITLE_FIELD.matches(metadataValue)) { title = metadataValue.getValue(); - } else if (metadataValue.getSchema().equalsIgnoreCase("dc") - && metadataValue.getElement().equalsIgnoreCase("identifier") - && metadataValue.getQualifier().equalsIgnoreCase("sherpaPublisher")) { + } else if (SHERPA_PUBLISHER_FIELD.matches(metadataValue)) { identifier = metadataValue.getValue(); - } else if (metadataValue.getSchema().equalsIgnoreCase("dc") - && metadataValue.getElement().equalsIgnoreCase("identifier") - && metadataValue.getQualifier().equalsIgnoreCase("other")) { + } else if (OTHER_FIELD.matches(metadataValue)) { url = metadataValue.getValue(); } } @@ -328,16 +319,11 @@ public void testSearchPublisherExternalObjects() { String identifier = null; String url = null; for (MetadataValueDTO metadataValue : dataObject.getMetadata()) { - if (metadataValue.getSchema().equalsIgnoreCase("dc") && - metadataValue.getElement().equalsIgnoreCase("title")) { + if (TITLE_FIELD.matches(metadataValue)) { title = metadataValue.getValue(); - } else if (metadataValue.getSchema().equalsIgnoreCase("dc") - && metadataValue.getElement().equalsIgnoreCase("identifier") - && metadataValue.getQualifier().equalsIgnoreCase("sherpaPublisher")) { + } else if (SHERPA_PUBLISHER_FIELD.matches(metadataValue)) { identifier = metadataValue.getValue(); - } else if (metadataValue.getSchema().equalsIgnoreCase("dc") - && metadataValue.getElement().equalsIgnoreCase("identifier") - && metadataValue.getQualifier().equalsIgnoreCase("other")) { + } else if (OTHER_FIELD.matches(metadataValue)) { url = metadataValue.getValue(); } } @@ -378,12 +364,9 @@ public void testComparePublisherExternalObjects() { exemplarDataObject.setId(validIdentifier); exemplarDataObject.setValue(validName); exemplarDataObject.setDisplayValue(validName); - exemplarDataObject.addMetadata(new MetadataValueDTO("dc", "title", null, null, - validName)); - exemplarDataObject.addMetadata(new MetadataValueDTO("dc", "identifier", "sherpaPublisher", null, - validIdentifier)); - exemplarDataObject.addMetadata(new MetadataValueDTO("dc", "identifier", "other", null, - validUrl)); + exemplarDataObject.addMetadata(TITLE_FIELD.toMetadata(validName)); + exemplarDataObject.addMetadata(SHERPA_PUBLISHER_FIELD.toMetadata(validIdentifier)); + exemplarDataObject.addMetadata(OTHER_FIELD.toMetadata(validUrl)); // Exemplar object 2 has a different order of metadata values // (we still expect it to be 'equal' when comparing since there is no concept of place for DTOs) @@ -392,12 +375,9 @@ public void testComparePublisherExternalObjects() { exemplarDataObject2.setId(validIdentifier); exemplarDataObject2.setValue(validName); exemplarDataObject2.setDisplayValue(validName); - exemplarDataObject2.addMetadata(new MetadataValueDTO("dc", "identifier", "other", null, - validUrl)); - exemplarDataObject2.addMetadata(new MetadataValueDTO("dc", "title", null, null, - validName)); - exemplarDataObject2.addMetadata(new MetadataValueDTO("dc", "identifier", "sherpaPublisher", null, - validIdentifier)); + exemplarDataObject2.addMetadata(OTHER_FIELD.toMetadata(validUrl)); + exemplarDataObject2.addMetadata(TITLE_FIELD.toMetadata(validName)); + exemplarDataObject2.addMetadata(SHERPA_PUBLISHER_FIELD.toMetadata(validIdentifier)); // Nonequal object should NOT evaluate as equal to our data ExternalDataObject nonEqualObject = new ExternalDataObject(); @@ -405,12 +385,9 @@ public void testComparePublisherExternalObjects() { nonEqualObject.setId(validIdentifier); nonEqualObject.setValue(validName); nonEqualObject.setDisplayValue(validName); - nonEqualObject.addMetadata(new MetadataValueDTO("dc", "title", null, null, - "Private Library of Science")); - nonEqualObject.addMetadata(new MetadataValueDTO("dc", "identifier", "sherpaPublisher", null, - validIdentifier)); - nonEqualObject.addMetadata(new MetadataValueDTO("dc", "identifier", "other", null, - validUrl)); + nonEqualObject.addMetadata(TITLE_FIELD.toMetadata("Private Library of Science")); + nonEqualObject.addMetadata(SHERPA_PUBLISHER_FIELD.toMetadata(validIdentifier)); + nonEqualObject.addMetadata(OTHER_FIELD.toMetadata(validUrl)); // Retrieve the dataobject(s) from the data provider @@ -435,4 +412,28 @@ public void testComparePublisherExternalObjects() { // Assert NON-equality to the 3rd object assertNotEquals(nonEqualObject, dataObject); } + + private static class MetadataFieldRef { + public final String schema; + public final String element; + public final String qualifier; + + public MetadataFieldRef(String schema, String element, String qualifier) { + this.schema = schema; + this.element = element; + this.qualifier = qualifier; + } + + public boolean matches(MetadataValueDTO value) { + return schema.equalsIgnoreCase(value.getSchema()) && + element.equalsIgnoreCase(value.getElement()) && + (qualifier == null ? value.getQualifier() == null + : qualifier.equalsIgnoreCase(value.getQualifier())); + } + + public MetadataValueDTO toMetadata(String value) { + return new MetadataValueDTO(schema, element, qualifier, null, value); + } + } + } \ No newline at end of file From a0c2891226156e749e0806ae6728fe92abc313e4 Mon Sep 17 00:00:00 2001 From: Adamo Date: Thu, 1 May 2025 18:52:26 +0200 Subject: [PATCH 626/979] [DURACOM-356] Updated Sherpa mapping to use creativeworkseries.issn instead of dc.identifier.issn --- .../provider/impl/SHERPAv2JournalISSNDataProvider.java | 3 +-- dspace/config/spring/api/sherpa.xml | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/external/provider/impl/SHERPAv2JournalISSNDataProvider.java b/dspace-api/src/main/java/org/dspace/external/provider/impl/SHERPAv2JournalISSNDataProvider.java index 9e61b9ac2ac0..860334847ccb 100644 --- a/dspace-api/src/main/java/org/dspace/external/provider/impl/SHERPAv2JournalISSNDataProvider.java +++ b/dspace-api/src/main/java/org/dspace/external/provider/impl/SHERPAv2JournalISSNDataProvider.java @@ -106,8 +106,7 @@ private ExternalDataObject constructExternalDataObjectFromSherpaJournal(SHERPAJo String issn = sherpaJournal.getIssns().get(0); externalDataObject.setId(issn); externalDataObject.addMetadata(new MetadataValueDTO( - "dc", "identifier", "issn", null, issn)); - + "creativeworkseries", "issn", null, null, issn)); } log.debug("New external data object. Title=" + externalDataObject.getValue() + ". ID=" diff --git a/dspace/config/spring/api/sherpa.xml b/dspace/config/spring/api/sherpa.xml index 0414f3f8e4b4..da42880218ca 100644 --- a/dspace/config/spring/api/sherpa.xml +++ b/dspace/config/spring/api/sherpa.xml @@ -17,7 +17,7 @@ - dc.identifier.issn + creativeworkseries.issn From bacbe06b2c8b121eb3877b4b9520a5d5726dc381 Mon Sep 17 00:00:00 2001 From: Adamo Date: Fri, 2 May 2025 19:23:37 +0200 Subject: [PATCH 627/979] [DURACOM-356] Fixed tests --- .../org/dspace/app/sherpa/SHERPADataProviderTest.java | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/dspace-api/src/test/java/org/dspace/app/sherpa/SHERPADataProviderTest.java b/dspace-api/src/test/java/org/dspace/app/sherpa/SHERPADataProviderTest.java index 6b9666c83038..134561f65e5a 100644 --- a/dspace-api/src/test/java/org/dspace/app/sherpa/SHERPADataProviderTest.java +++ b/dspace-api/src/test/java/org/dspace/app/sherpa/SHERPADataProviderTest.java @@ -86,9 +86,8 @@ public void testGetJournalISSNExternalObject() { if (metadataValue.getSchema().equalsIgnoreCase("dc") && metadataValue.getElement().equalsIgnoreCase("title")) { title = metadataValue.getValue(); - } else if (metadataValue.getSchema().equalsIgnoreCase("dc") - && metadataValue.getElement().equalsIgnoreCase("identifier") - && metadataValue.getQualifier().equalsIgnoreCase("issn")) { + } else if (metadataValue.getSchema().equalsIgnoreCase("creativeworkseries") + && metadataValue.getElement().equalsIgnoreCase("issn")) { identifier = metadataValue.getValue(); } } @@ -135,9 +134,8 @@ public void testSearchJournalISSNExternalObjects() { if (metadataValue.getSchema().equalsIgnoreCase("dc") && metadataValue.getElement().equalsIgnoreCase("title")) { title = metadataValue.getValue(); - } else if (metadataValue.getSchema().equalsIgnoreCase("dc") - && metadataValue.getElement().equalsIgnoreCase("identifier") - && metadataValue.getQualifier().equalsIgnoreCase("issn")) { + } else if (metadataValue.getSchema().equalsIgnoreCase("creativeworkseries") + && metadataValue.getElement().equalsIgnoreCase("issn")) { identifier = metadataValue.getValue(); } } From 5dd4e4248c847927fda47cb89c984eba4e055400 Mon Sep 17 00:00:00 2001 From: Adamo Date: Mon, 5 May 2025 12:46:52 +0200 Subject: [PATCH 628/979] [DURACOM-356] Updated Sherpa Journal mapping to use creativeworkseries.issn instead of dc.identifier.issn --- .../external/provider/impl/SHERPAv2JournalDataProvider.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dspace-api/src/main/java/org/dspace/external/provider/impl/SHERPAv2JournalDataProvider.java b/dspace-api/src/main/java/org/dspace/external/provider/impl/SHERPAv2JournalDataProvider.java index a4276c83ed70..f0ce6a979a1f 100644 --- a/dspace-api/src/main/java/org/dspace/external/provider/impl/SHERPAv2JournalDataProvider.java +++ b/dspace-api/src/main/java/org/dspace/external/provider/impl/SHERPAv2JournalDataProvider.java @@ -97,7 +97,7 @@ private ExternalDataObject constructExternalDataObjectFromSherpaJournal(SHERPAJo if (CollectionUtils.isNotEmpty(sherpaJournal.getIssns())) { String issn = sherpaJournal.getIssns().get(0); externalDataObject.addMetadata(new MetadataValueDTO( - "dc", "identifier", "issn", null, issn)); + "creativeworkseries", "issn", null, null, issn)); } From d7948d5f7bb36f23221844bc81fee571faa6b419 Mon Sep 17 00:00:00 2001 From: Adamo Date: Mon, 5 May 2025 12:49:16 +0200 Subject: [PATCH 629/979] [DURACOM-356] Updated tests to use metadata constants --- .../app/sherpa/SHERPADataProviderTest.java | 77 +++++++++++-------- 1 file changed, 43 insertions(+), 34 deletions(-) diff --git a/dspace-api/src/test/java/org/dspace/app/sherpa/SHERPADataProviderTest.java b/dspace-api/src/test/java/org/dspace/app/sherpa/SHERPADataProviderTest.java index 134561f65e5a..210eedc9fbb4 100644 --- a/dspace-api/src/test/java/org/dspace/app/sherpa/SHERPADataProviderTest.java +++ b/dspace-api/src/test/java/org/dspace/app/sherpa/SHERPADataProviderTest.java @@ -37,6 +37,12 @@ public class SHERPADataProviderTest extends AbstractDSpaceTest { ExternalDataProvider sherpaPublisherProvider; ExternalDataProvider sherpaJournalIssnProvider; + private static final MetadataFieldRef TITLE_FIELD = new MetadataFieldRef("dc", "title", null); + private static final MetadataFieldRef ISSN_FIELD = new MetadataFieldRef("creativeworkseries", "issn", null); + private static final MetadataFieldRef SHERPA_PUBLISHER_FIELD = + new MetadataFieldRef("dc", "identifier", "sherpaPublisher"); + private static final MetadataFieldRef OTHER_FIELD = new MetadataFieldRef("dc", "identifier", "other"); + @BeforeClass public static void setUpClass() { } @@ -83,11 +89,9 @@ public void testGetJournalISSNExternalObject() { String title = null; String identifier = null; for (MetadataValueDTO metadataValue : dataObject.getMetadata()) { - if (metadataValue.getSchema().equalsIgnoreCase("dc") && - metadataValue.getElement().equalsIgnoreCase("title")) { + if (TITLE_FIELD.matches(metadataValue)) { title = metadataValue.getValue(); - } else if (metadataValue.getSchema().equalsIgnoreCase("creativeworkseries") - && metadataValue.getElement().equalsIgnoreCase("issn")) { + } else if (ISSN_FIELD.matches(metadataValue)) { identifier = metadataValue.getValue(); } } @@ -131,11 +135,9 @@ public void testSearchJournalISSNExternalObjects() { String title = null; String identifier = null; for (MetadataValueDTO metadataValue : dataObject.getMetadata()) { - if (metadataValue.getSchema().equalsIgnoreCase("dc") && - metadataValue.getElement().equalsIgnoreCase("title")) { + if (TITLE_FIELD.matches(metadataValue)) { title = metadataValue.getValue(); - } else if (metadataValue.getSchema().equalsIgnoreCase("creativeworkseries") - && metadataValue.getElement().equalsIgnoreCase("issn")) { + } else if (ISSN_FIELD.matches(metadataValue)) { identifier = metadataValue.getValue(); } } @@ -171,12 +173,9 @@ public void testGetJournalExternalObject() { String title = null; String identifier = null; for (MetadataValueDTO metadataValue : dataObject.getMetadata()) { - if (metadataValue.getSchema().equalsIgnoreCase("dc") && - metadataValue.getElement().equalsIgnoreCase("title")) { + if (TITLE_FIELD.matches(metadataValue)) { title = metadataValue.getValue(); - } else if (metadataValue.getSchema().equalsIgnoreCase("dc") - && metadataValue.getElement().equalsIgnoreCase("identifier") - && metadataValue.getQualifier().equalsIgnoreCase("issn")) { + } else if (ISSN_FIELD.matches(metadataValue)) { identifier = metadataValue.getValue(); } } @@ -221,12 +220,9 @@ public void testSearchJournalObjects() { String title = null; String identifier = null; for (MetadataValueDTO metadataValue : dataObject.getMetadata()) { - if (metadataValue.getSchema().equalsIgnoreCase("dc") && - metadataValue.getElement().equalsIgnoreCase("title")) { + if (TITLE_FIELD.matches(metadataValue)) { title = metadataValue.getValue(); - } else if (metadataValue.getSchema().equalsIgnoreCase("dc") - && metadataValue.getElement().equalsIgnoreCase("identifier") - && metadataValue.getQualifier().equalsIgnoreCase("issn")) { + } else if (ISSN_FIELD.matches(metadataValue)) { identifier = metadataValue.getValue(); } } @@ -267,16 +263,11 @@ public void testGetPublisherExternalObject() { String identifier = null; String url = null; for (MetadataValueDTO metadataValue : dataObject.getMetadata()) { - if (metadataValue.getSchema().equalsIgnoreCase("dc") && - metadataValue.getElement().equalsIgnoreCase("title")) { + if (TITLE_FIELD.matches(metadataValue)) { title = metadataValue.getValue(); - } else if (metadataValue.getSchema().equalsIgnoreCase("dc") - && metadataValue.getElement().equalsIgnoreCase("identifier") - && metadataValue.getQualifier().equalsIgnoreCase("sherpaPublisher")) { + } else if (SHERPA_PUBLISHER_FIELD.matches(metadataValue)) { identifier = metadataValue.getValue(); - } else if (metadataValue.getSchema().equalsIgnoreCase("dc") - && metadataValue.getElement().equalsIgnoreCase("identifier") - && metadataValue.getQualifier().equalsIgnoreCase("other")) { + } else if (OTHER_FIELD.matches(metadataValue)) { url = metadataValue.getValue(); } } @@ -327,16 +318,11 @@ public void testSearchPublisherExternalObjects() { String identifier = null; String url = null; for (MetadataValueDTO metadataValue : dataObject.getMetadata()) { - if (metadataValue.getSchema().equalsIgnoreCase("dc") && - metadataValue.getElement().equalsIgnoreCase("title")) { + if (TITLE_FIELD.matches(metadataValue)) { title = metadataValue.getValue(); - } else if (metadataValue.getSchema().equalsIgnoreCase("dc") - && metadataValue.getElement().equalsIgnoreCase("identifier") - && metadataValue.getQualifier().equalsIgnoreCase("sherpaPublisher")) { + } else if (SHERPA_PUBLISHER_FIELD.matches(metadataValue)) { identifier = metadataValue.getValue(); - } else if (metadataValue.getSchema().equalsIgnoreCase("dc") - && metadataValue.getElement().equalsIgnoreCase("identifier") - && metadataValue.getQualifier().equalsIgnoreCase("other")) { + } else if (OTHER_FIELD.matches(metadataValue)) { url = metadataValue.getValue(); } } @@ -350,4 +336,27 @@ public void testSearchPublisherExternalObjects() { // Does dc.identifier.other match the expected value? assertEquals("Publisher URL must equal " + validUrl, validUrl, url); } + + private static class MetadataFieldRef { + public final String schema; + public final String element; + public final String qualifier; + + public MetadataFieldRef(String schema, String element, String qualifier) { + this.schema = schema; + this.element = element; + this.qualifier = qualifier; + } + + public boolean matches(MetadataValueDTO value) { + return schema.equalsIgnoreCase(value.getSchema()) && + element.equalsIgnoreCase(value.getElement()) && + (qualifier == null ? value.getQualifier() == null + : qualifier.equalsIgnoreCase(value.getQualifier())); + } + + public MetadataValueDTO toMetadata(String value) { + return new MetadataValueDTO(schema, element, qualifier, null, value); + } + } } \ No newline at end of file From a26ef22a460bc417a6b05f40a2ba3e84f9993c10 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 5 May 2025 22:58:31 +0000 Subject: [PATCH 630/979] Bump com.amazonaws:aws-java-sdk-s3 from 1.12.782 to 1.12.783 Bumps [com.amazonaws:aws-java-sdk-s3](https://github.com/aws/aws-sdk-java) from 1.12.782 to 1.12.783. - [Changelog](https://github.com/aws/aws-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java/compare/1.12.782...1.12.783) --- updated-dependencies: - dependency-name: com.amazonaws:aws-java-sdk-s3 dependency-version: 1.12.783 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- dspace-api/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dspace-api/pom.xml b/dspace-api/pom.xml index bb246c20ad8f..df1549a98b6b 100644 --- a/dspace-api/pom.xml +++ b/dspace-api/pom.xml @@ -756,7 +756,7 @@ com.amazonaws aws-java-sdk-s3 - 1.12.782 + 1.12.783 From f5e83433c4402ec66170c01e7f87bf2f3b9598e8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 5 May 2025 23:02:34 +0000 Subject: [PATCH 631/979] Bump tika.version from 2.9.3 to 2.9.4 Bumps `tika.version` from 2.9.3 to 2.9.4. Updates `org.apache.tika:tika-core` from 2.9.3 to 2.9.4 - [Changelog](https://github.com/apache/tika/blob/2.9.4/CHANGES.txt) - [Commits](https://github.com/apache/tika/compare/2.9.3...2.9.4) Updates `org.apache.tika:tika-parsers-standard-package` from 2.9.3 to 2.9.4 --- updated-dependencies: - dependency-name: org.apache.tika:tika-core dependency-version: 2.9.4 dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.apache.tika:tika-parsers-standard-package dependency-version: 2.9.4 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index e2683a5844b7..107318fccb15 100644 --- a/pom.xml +++ b/pom.xml @@ -42,7 +42,7 @@ 2.0.34 1.19.0 1.7.36 - 2.9.3 + 2.9.4 1.80 From 8b089be727a2f3888c0b9c3f84727f5cb8d01675 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 5 May 2025 23:11:48 +0000 Subject: [PATCH 632/979] Bump com.opencsv:opencsv from 5.10 to 5.11 Bumps com.opencsv:opencsv from 5.10 to 5.11. --- updated-dependencies: - dependency-name: com.opencsv:opencsv dependency-version: '5.11' dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- dspace-api/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dspace-api/pom.xml b/dspace-api/pom.xml index bb246c20ad8f..5e3e67d704cc 100644 --- a/dspace-api/pom.xml +++ b/dspace-api/pom.xml @@ -805,7 +805,7 @@ com.opencsv opencsv - 5.10 + 5.11 From 9990000e4fdf54cf6418f47d2fb7aeff81177ddd Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 5 May 2025 23:26:55 +0000 Subject: [PATCH 633/979] Bump com.opencsv:opencsv from 5.10 to 5.11 Bumps com.opencsv:opencsv from 5.10 to 5.11. --- updated-dependencies: - dependency-name: com.opencsv:opencsv dependency-version: '5.11' dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- dspace-api/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dspace-api/pom.xml b/dspace-api/pom.xml index 8851a5e5256a..ab795aa3a9c1 100644 --- a/dspace-api/pom.xml +++ b/dspace-api/pom.xml @@ -772,7 +772,7 @@ com.opencsv opencsv - 5.10 + 5.11 From 2bb2d0be2ccc80cb6abf95b1f76e3893b2e7b0a6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 5 May 2025 23:44:09 +0000 Subject: [PATCH 634/979] Bump org.checkerframework:checker-qual from 3.49.2 to 3.49.3 Bumps [org.checkerframework:checker-qual](https://github.com/typetools/checker-framework) from 3.49.2 to 3.49.3. - [Release notes](https://github.com/typetools/checker-framework/releases) - [Changelog](https://github.com/typetools/checker-framework/blob/master/docs/CHANGELOG.md) - [Commits](https://github.com/typetools/checker-framework/compare/checker-framework-3.49.2...checker-framework-3.49.3) --- updated-dependencies: - dependency-name: org.checkerframework:checker-qual dependency-version: 3.49.3 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 2dd4c2d2adb4..20610aaaaeef 100644 --- a/pom.xml +++ b/pom.xml @@ -1350,7 +1350,7 @@ org.checkerframework checker-qual - 3.49.2 + 3.49.3 1.80 8.0.1 From e09f03e6bb2861339f293aab466feeb838fa76bf Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 5 May 2025 23:45:25 +0000 Subject: [PATCH 636/979] Bump com.amazonaws:aws-java-sdk-s3 from 1.12.782 to 1.12.783 Bumps [com.amazonaws:aws-java-sdk-s3](https://github.com/aws/aws-sdk-java) from 1.12.782 to 1.12.783. - [Changelog](https://github.com/aws/aws-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java/compare/1.12.782...1.12.783) --- updated-dependencies: - dependency-name: com.amazonaws:aws-java-sdk-s3 dependency-version: 1.12.783 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- dspace-api/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dspace-api/pom.xml b/dspace-api/pom.xml index 8851a5e5256a..2722b9744c26 100644 --- a/dspace-api/pom.xml +++ b/dspace-api/pom.xml @@ -731,7 +731,7 @@ com.amazonaws aws-java-sdk-s3 - 1.12.782 + 1.12.783 From e5a36f2e0f61756275b3e930ca2c733b957a6b81 Mon Sep 17 00:00:00 2001 From: Elios Buzo Date: Thu, 17 Apr 2025 17:55:05 +0200 Subject: [PATCH 640/979] [DURACOM-109] Continued configuring proxy for other classes --- .../app/client/DSpaceHttpClientFactory.java | 2 +- .../packager/AbstractMETSIngester.java | 12 ++++--- .../ctask/general/BasicLinkChecker.java | 18 ++++++---- .../client/GoogleAnalyticsClientImpl.java | 4 +-- .../dspace/iiif/IIIFApiQueryServiceImpl.java | 15 ++++---- .../service/LiveImportClientImpl.java | 35 ++++--------------- .../CCLicenseConnectorServiceImpl.java | 18 ++-------- .../impl/QAEventActionServiceImpl.java | 4 +-- .../statistics/SolrLoggerServiceImpl.java | 4 +-- dspace/config/dspace.cfg | 2 ++ 10 files changed, 43 insertions(+), 71 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/app/client/DSpaceHttpClientFactory.java b/dspace-api/src/main/java/org/dspace/app/client/DSpaceHttpClientFactory.java index 999f13c1b387..59c8172f722c 100644 --- a/dspace-api/src/main/java/org/dspace/app/client/DSpaceHttpClientFactory.java +++ b/dspace-api/src/main/java/org/dspace/app/client/DSpaceHttpClientFactory.java @@ -93,7 +93,7 @@ public CloseableHttpClient buildWithoutProxy() { public CloseableHttpClient buildWithoutAutomaticRetries(int maxConnTotal) { HttpClientBuilder clientBuilder = HttpClientBuilder.create() .disableAutomaticRetries() - .setMaxConnTotal(5); + .setMaxConnTotal(maxConnTotal); return build(clientBuilder, true); } diff --git a/dspace-api/src/main/java/org/dspace/content/packager/AbstractMETSIngester.java b/dspace-api/src/main/java/org/dspace/content/packager/AbstractMETSIngester.java index 0ed0abe21825..c43d8e797b07 100644 --- a/dspace-api/src/main/java/org/dspace/content/packager/AbstractMETSIngester.java +++ b/dspace-api/src/main/java/org/dspace/content/packager/AbstractMETSIngester.java @@ -11,8 +11,6 @@ import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; -import java.net.URL; -import java.net.URLConnection; import java.sql.SQLException; import java.util.Iterator; import java.util.List; @@ -21,7 +19,11 @@ import java.util.zip.ZipFile; import org.apache.commons.collections4.CollectionUtils; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.impl.client.CloseableHttpClient; import org.apache.logging.log4j.Logger; +import org.dspace.app.client.DSpaceHttpClientFactory; import org.dspace.authorize.AuthorizeException; import org.dspace.content.Bitstream; import org.dspace.content.BitstreamFormat; @@ -1312,11 +1314,11 @@ protected static InputStream getFileInputStream(File pkgFile, // we will assume all external files are available via URLs. try { // attempt to open a connection to given URL - URL fileURL = new URL(path); - URLConnection connection = fileURL.openConnection(); + CloseableHttpClient httpClient = DSpaceHttpClientFactory.getInstance().build(); + CloseableHttpResponse httpResponse = httpClient.execute(new HttpGet(path)); // open stream to access file contents - return connection.getInputStream(); + return httpResponse.getEntity().getContent(); } catch (IOException io) { log .error("Unable to retrieve external file from URL '" diff --git a/dspace-api/src/main/java/org/dspace/ctask/general/BasicLinkChecker.java b/dspace-api/src/main/java/org/dspace/ctask/general/BasicLinkChecker.java index a302159ea9a4..fd6d69c98f04 100644 --- a/dspace-api/src/main/java/org/dspace/ctask/general/BasicLinkChecker.java +++ b/dspace-api/src/main/java/org/dspace/ctask/general/BasicLinkChecker.java @@ -9,11 +9,15 @@ import java.io.IOException; import java.net.HttpURLConnection; -import java.net.URL; import java.util.ArrayList; import java.util.List; +import org.apache.http.client.config.RequestConfig; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.impl.client.CloseableHttpClient; import org.apache.logging.log4j.Logger; +import org.dspace.app.client.DSpaceHttpClientFactory; import org.dspace.content.DSpaceObject; import org.dspace.content.Item; import org.dspace.content.MetadataValue; @@ -136,15 +140,15 @@ protected boolean checkURL(String url, StringBuilder results) { */ protected int getResponseStatus(String url, int redirects) { try { - URL theURL = new URL(url); - HttpURLConnection connection = (HttpURLConnection) theURL.openConnection(); - connection.setInstanceFollowRedirects(true); - int statusCode = connection.getResponseCode(); + RequestConfig config = RequestConfig.custom().setRedirectsEnabled(true).build(); + CloseableHttpClient httpClient = DSpaceHttpClientFactory.getInstance().buildWithRequestConfig(config); + CloseableHttpResponse httpResponse = httpClient.execute(new HttpGet(url)); + int statusCode = httpResponse.getStatusLine().getStatusCode(); int maxRedirect = configurationService.getIntProperty("curate.checklinks.max-redirect", 0); if ((statusCode == HttpURLConnection.HTTP_MOVED_TEMP || statusCode == HttpURLConnection.HTTP_MOVED_PERM || statusCode == HttpURLConnection.HTTP_SEE_OTHER)) { - connection.disconnect(); - String newUrl = connection.getHeaderField("Location"); + httpClient.close(); + String newUrl = httpResponse.getFirstHeader("Location").getValue(); if (newUrl != null && (maxRedirect >= redirects || maxRedirect == -1)) { redirects++; return getResponseStatus(newUrl, redirects); diff --git a/dspace-api/src/main/java/org/dspace/google/client/GoogleAnalyticsClientImpl.java b/dspace-api/src/main/java/org/dspace/google/client/GoogleAnalyticsClientImpl.java index 915bd25b065f..bfecb1fb0861 100644 --- a/dspace-api/src/main/java/org/dspace/google/client/GoogleAnalyticsClientImpl.java +++ b/dspace-api/src/main/java/org/dspace/google/client/GoogleAnalyticsClientImpl.java @@ -18,9 +18,9 @@ import org.apache.http.client.methods.HttpPost; import org.apache.http.entity.StringEntity; import org.apache.http.impl.client.CloseableHttpClient; -import org.apache.http.impl.client.HttpClients; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import org.dspace.app.client.DSpaceHttpClientFactory; import org.dspace.google.GoogleAnalyticsEvent; /** @@ -42,7 +42,7 @@ public class GoogleAnalyticsClientImpl implements GoogleAnalyticsClient { public GoogleAnalyticsClientImpl(String keyPrefix, GoogleAnalyticsClientRequestBuilder requestBuilder) { this.keyPrefix = keyPrefix; this.requestBuilder = requestBuilder; - this.httpclient = HttpClients.createDefault(); + this.httpclient = DSpaceHttpClientFactory.getInstance().build(); } @Override diff --git a/dspace-api/src/main/java/org/dspace/iiif/IIIFApiQueryServiceImpl.java b/dspace-api/src/main/java/org/dspace/iiif/IIIFApiQueryServiceImpl.java index 7c6336ed3c7f..87bd7770648e 100644 --- a/dspace-api/src/main/java/org/dspace/iiif/IIIFApiQueryServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/iiif/IIIFApiQueryServiceImpl.java @@ -12,12 +12,14 @@ import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; -import java.net.HttpURLConnection; -import java.net.URL; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.impl.client.CloseableHttpClient; import org.apache.logging.log4j.Logger; +import org.dspace.app.client.DSpaceHttpClientFactory; import org.dspace.content.Bitstream; import org.dspace.iiif.util.IIIFSharedUtils; @@ -35,14 +37,11 @@ public class IIIFApiQueryServiceImpl implements IIIFApiQueryService { public int[] getImageDimensions(Bitstream bitstream) { int[] arr = new int[2]; String path = IIIFSharedUtils.getInfoJsonPath(bitstream); - URL url; BufferedReader in = null; try { - url = new URL(path); - HttpURLConnection con = (HttpURLConnection) url.openConnection(); - con.setRequestMethod("GET"); - in = new BufferedReader( - new InputStreamReader(con.getInputStream())); + CloseableHttpClient httpClient = DSpaceHttpClientFactory.getInstance().build(); + CloseableHttpResponse httpResponse = httpClient.execute(new HttpGet(path)); + in = new BufferedReader(new InputStreamReader(httpResponse.getEntity().getContent())); String inputLine; StringBuilder response = new StringBuilder(); while ((inputLine = in.readLine()) != null) { diff --git a/dspace-api/src/main/java/org/dspace/importer/external/liveimportclient/service/LiveImportClientImpl.java b/dspace-api/src/main/java/org/dspace/importer/external/liveimportclient/service/LiveImportClientImpl.java index 1a8a7a7861ed..2cb8236842a3 100644 --- a/dspace-api/src/main/java/org/dspace/importer/external/liveimportclient/service/LiveImportClientImpl.java +++ b/dspace-api/src/main/java/org/dspace/importer/external/liveimportclient/service/LiveImportClientImpl.java @@ -17,19 +17,16 @@ import org.apache.commons.collections.MapUtils; import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.StringUtils; -import org.apache.http.HttpHost; import org.apache.http.HttpResponse; import org.apache.http.client.config.RequestConfig; -import org.apache.http.client.config.RequestConfig.Builder; import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpPost; -import org.apache.http.client.methods.HttpRequestBase; import org.apache.http.client.utils.URIBuilder; import org.apache.http.entity.StringEntity; import org.apache.http.impl.client.CloseableHttpClient; -import org.apache.http.impl.client.HttpClients; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import org.dspace.app.client.DSpaceHttpClientFactory; import org.dspace.services.ConfigurationService; import org.springframework.beans.factory.annotation.Autowired; @@ -53,16 +50,12 @@ public class LiveImportClientImpl implements LiveImportClient { @Override public String executeHttpGetRequest(int timeout, String URL, Map> params) { HttpGet method = null; + RequestConfig config = RequestConfig.custom().setConnectionRequestTimeout(timeout).build(); try (CloseableHttpClient httpClient = Optional.ofNullable(this.httpClient) - .orElseGet(HttpClients::createDefault)) { - - Builder requestConfigBuilder = RequestConfig.custom(); - requestConfigBuilder.setConnectionRequestTimeout(timeout); - RequestConfig defaultRequestConfig = requestConfigBuilder.build(); - + .orElse(DSpaceHttpClientFactory.getInstance().buildWithRequestConfig(config))) { String uri = buildUrl(URL, params.get(URI_PARAMETERS)); method = new HttpGet(uri); - method.setConfig(defaultRequestConfig); + method.setConfig(config); Map headerParams = params.get(HEADER_PARAMETERS); if (MapUtils.isNotEmpty(headerParams)) { @@ -71,7 +64,6 @@ public String executeHttpGetRequest(int timeout, String URL, Map> params, String entry) { HttpPost method = null; + RequestConfig config = RequestConfig.custom().build(); try (CloseableHttpClient httpClient = Optional.ofNullable(this.httpClient) - .orElseGet(HttpClients::createDefault)) { - - Builder requestConfigBuilder = RequestConfig.custom(); - RequestConfig defaultRequestConfig = requestConfigBuilder.build(); + .orElse(DSpaceHttpClientFactory.getInstance().buildWithRequestConfig(config))) { String uri = buildUrl(URL, params.get(URI_PARAMETERS)); method = new HttpPost(uri); - method.setConfig(defaultRequestConfig); if (StringUtils.isNotBlank(entry)) { method.setEntity(new StringEntity(entry)); } setHeaderParams(method, params); - configureProxy(method, defaultRequestConfig); if (log.isDebugEnabled()) { log.debug("Performing POST request to \"" + uri + "\"..." ); } @@ -129,17 +117,6 @@ public String executeHttpPostRequest(String URL, Map return StringUtils.EMPTY; } - private void configureProxy(HttpRequestBase method, RequestConfig defaultRequestConfig) { - String proxyHost = configurationService.getProperty("http.proxy.host"); - String proxyPort = configurationService.getProperty("http.proxy.port"); - if (StringUtils.isNotBlank(proxyHost) && StringUtils.isNotBlank(proxyPort)) { - RequestConfig requestConfig = RequestConfig.copy(defaultRequestConfig) - .setProxy(new HttpHost(proxyHost, Integer.parseInt(proxyPort), "http")) - .build(); - method.setConfig(requestConfig); - } - } - /** * Allows to set the header parameters to the HTTP Post method * diff --git a/dspace-api/src/main/java/org/dspace/license/CCLicenseConnectorServiceImpl.java b/dspace-api/src/main/java/org/dspace/license/CCLicenseConnectorServiceImpl.java index 524ff04facec..e9fe49e9b4f6 100644 --- a/dspace-api/src/main/java/org/dspace/license/CCLicenseConnectorServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/license/CCLicenseConnectorServiceImpl.java @@ -10,9 +10,6 @@ import java.io.IOException; import java.io.InputStream; import java.io.StringReader; -import java.net.MalformedURLException; -import java.net.URL; -import java.net.URLConnection; import java.text.MessageFormat; import java.util.Collections; import java.util.HashMap; @@ -328,23 +325,14 @@ private String createAnswerString(final Map parameterMap) { @Override public Document retrieveLicenseRDFDoc(String licenseURI) throws IOException { String ccLicenseUrl = configurationService.getProperty("cc.api.rooturl"); - String issueUrl = ccLicenseUrl + "/details?license-uri=" + licenseURI; - - URL request_url; - try { - request_url = new URL(issueUrl); - } catch (MalformedURLException e) { - return null; - } - URLConnection connection = request_url.openConnection(); - connection.setDoOutput(true); try { + CloseableHttpClient httpClient = DSpaceHttpClientFactory.getInstance().build(); + CloseableHttpResponse httpResponse = httpClient.execute(new HttpPost(issueUrl)); // parsing document from input stream - InputStream stream = connection.getInputStream(); + InputStream stream = httpResponse.getEntity().getContent(); Document doc = parser.build(stream); return doc; - } catch (Exception e) { log.error("Error while retrieving the license document for URI: " + licenseURI, e); } diff --git a/dspace-api/src/main/java/org/dspace/qaevent/service/impl/QAEventActionServiceImpl.java b/dspace-api/src/main/java/org/dspace/qaevent/service/impl/QAEventActionServiceImpl.java index 30875a5105b0..38e7295582eb 100644 --- a/dspace-api/src/main/java/org/dspace/qaevent/service/impl/QAEventActionServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/qaevent/service/impl/QAEventActionServiceImpl.java @@ -22,9 +22,9 @@ import org.apache.http.entity.ContentType; import org.apache.http.entity.StringEntity; import org.apache.http.impl.client.CloseableHttpClient; -import org.apache.http.impl.client.HttpClients; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import org.dspace.app.client.DSpaceHttpClientFactory; import org.dspace.content.Item; import org.dspace.content.QAEvent; import org.dspace.content.service.ItemService; @@ -123,7 +123,7 @@ private void makeAcknowledgement(String eventId, String source, String status) { node.put("eventId", eventId); node.put("status", status); StringEntity requestEntity = new StringEntity(node.toString(), ContentType.APPLICATION_JSON); - CloseableHttpClient httpclient = HttpClients.createDefault(); + CloseableHttpClient httpclient = DSpaceHttpClientFactory.getInstance().buildWithoutProxy(); HttpPost postMethod = new HttpPost(ackwnoledgeCallback); postMethod.setEntity(requestEntity); try { diff --git a/dspace-api/src/main/java/org/dspace/statistics/SolrLoggerServiceImpl.java b/dspace-api/src/main/java/org/dspace/statistics/SolrLoggerServiceImpl.java index fb327ed6db15..9e5ba3256c0b 100644 --- a/dspace-api/src/main/java/org/dspace/statistics/SolrLoggerServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/statistics/SolrLoggerServiceImpl.java @@ -1223,7 +1223,7 @@ public void shardSolrIndex() throws IOException, SolrServerException { + "." + i + ".csv"); - try (CloseableHttpClient hc = DSpaceHttpClientFactory.getInstance().build()) { + try (CloseableHttpClient hc = DSpaceHttpClientFactory.getInstance().buildWithoutProxy()) { HttpResponse response = hc.execute(get); csvInputstream = response.getEntity().getContent(); //Write the csv ouput to a file ! @@ -1365,7 +1365,7 @@ public void reindexBitstreamHits(boolean removeDeletedBitstreams) throws Excepti HttpGet get = new HttpGet(solrRequestUrl); List rows; - try (CloseableHttpClient hc = DSpaceHttpClientFactory.getInstance().build()) { + try (CloseableHttpClient hc = DSpaceHttpClientFactory.getInstance().buildWithoutProxy()) { HttpResponse response = hc.execute(get); InputStream csvOutput = response.getEntity().getContent(); Reader csvReader = new InputStreamReader(csvOutput); diff --git a/dspace/config/dspace.cfg b/dspace/config/dspace.cfg index 2abe436387dd..7151fc6dc56b 100644 --- a/dspace/config/dspace.cfg +++ b/dspace/config/dspace.cfg @@ -411,6 +411,8 @@ http.proxy.host = # port number of proxy server http.proxy.port = +http.proxy.hosts-to-ignore = 127.0.0.1, localhost + # If enabled, the logging and the Solr statistics system will look for an X-Forwarded-For header. # If it finds it, it will use this for the user IP address. # NOTE: This is required to be enabled if you plan to use the Angular UI, as the server-side rendering provided in From 0f77db9785033570d5ade41f4fbf8834538dc185 Mon Sep 17 00:00:00 2001 From: Elios Buzo Date: Fri, 18 Apr 2025 10:24:35 +0200 Subject: [PATCH 641/979] [DURACOM-109] Minor fix --- .../external/liveimportclient/service/LiveImportClientImpl.java | 1 - 1 file changed, 1 deletion(-) diff --git a/dspace-api/src/main/java/org/dspace/importer/external/liveimportclient/service/LiveImportClientImpl.java b/dspace-api/src/main/java/org/dspace/importer/external/liveimportclient/service/LiveImportClientImpl.java index 2cb8236842a3..84475b62c07b 100644 --- a/dspace-api/src/main/java/org/dspace/importer/external/liveimportclient/service/LiveImportClientImpl.java +++ b/dspace-api/src/main/java/org/dspace/importer/external/liveimportclient/service/LiveImportClientImpl.java @@ -55,7 +55,6 @@ public String executeHttpGetRequest(int timeout, String URL, Map headerParams = params.get(HEADER_PARAMETERS); if (MapUtils.isNotEmpty(headerParams)) { From f78c327e5835cc70960bbfb019a4cda972b6d7c5 Mon Sep 17 00:00:00 2001 From: Elios Buzo Date: Fri, 18 Apr 2025 15:00:38 +0200 Subject: [PATCH 642/979] [DURACOM-109] Fixed http connection leaks --- .../org/dspace/app/sherpa/SHERPAService.java | 21 +--- .../dspace/app/util/WebAppServiceImpl.java | 4 +- .../oidc/impl/OidcClientImpl.java | 29 +++-- .../packager/AbstractMETSIngester.java | 3 +- .../ctask/general/BasicLinkChecker.java | 5 +- .../dspace/external/OrcidRestConnector.java | 10 +- .../dspace/iiif/IIIFApiQueryServiceImpl.java | 3 +- .../CCLicenseConnectorServiceImpl.java | 3 +- .../dspace/orcid/client/OrcidClientImpl.java | 103 ++++++++---------- 9 files changed, 73 insertions(+), 108 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/app/sherpa/SHERPAService.java b/dspace-api/src/main/java/org/dspace/app/sherpa/SHERPAService.java index 4f0e99921a95..6f26d0cc2d27 100644 --- a/dspace-api/src/main/java/org/dspace/app/sherpa/SHERPAService.java +++ b/dspace-api/src/main/java/org/dspace/app/sherpa/SHERPAService.java @@ -17,9 +17,9 @@ import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.StringUtils; import org.apache.http.HttpEntity; -import org.apache.http.HttpResponse; import org.apache.http.HttpStatus; import org.apache.http.client.config.RequestConfig; +import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpGet; import org.apache.http.client.utils.URIBuilder; import org.apache.http.impl.client.CloseableHttpClient; @@ -45,8 +45,6 @@ */ public class SHERPAService { - private CloseableHttpClient client = null; - private int maxNumberOfTries; private long sleepBetweenTimeouts; private int timeout = 5000; @@ -59,15 +57,6 @@ public class SHERPAService { @Autowired ConfigurationService configurationService; - /** - * Create a new HTTP builder with sensible defaults in constructor - */ - public SHERPAService() { - // httpclient 4.3+ doesn't appear to have any sensible defaults any more. Setting conservative defaults as - // not to hammer the SHERPA service too much. - client = DSpaceHttpClientFactory.getInstance().buildWithoutAutomaticRetries(5); - } - /** * Complete initialization of the Bean. */ @@ -128,14 +117,14 @@ public SHERPAPublisherResponse performPublisherRequest(String type, String field timeout, sleepBetweenTimeouts)); - try { + try (CloseableHttpClient client = DSpaceHttpClientFactory.getInstance().build()) { Thread.sleep(sleepBetweenTimeouts); // Construct a default HTTP method (first result) method = constructHttpGet(type, field, predicate, value, start, limit); // Execute the method - HttpResponse response = client.execute(method); + CloseableHttpResponse response = client.execute(method); int statusCode = response.getStatusLine().getStatusCode(); log.debug(response.getStatusLine().getStatusCode() + ": " @@ -231,14 +220,14 @@ public SHERPAResponse performRequest(String type, String field, String predicate timeout, sleepBetweenTimeouts)); - try { + try (CloseableHttpClient client = DSpaceHttpClientFactory.getInstance().build()) { Thread.sleep(sleepBetweenTimeouts); // Construct a default HTTP method (first result) method = constructHttpGet(type, field, predicate, value, start, limit); // Execute the method - HttpResponse response = client.execute(method); + CloseableHttpResponse response = client.execute(method); int statusCode = response.getStatusLine().getStatusCode(); log.debug(response.getStatusLine().getStatusCode() + ": " diff --git a/dspace-api/src/main/java/org/dspace/app/util/WebAppServiceImpl.java b/dspace-api/src/main/java/org/dspace/app/util/WebAppServiceImpl.java index 064a9fdc3e9b..fa23903ba431 100644 --- a/dspace-api/src/main/java/org/dspace/app/util/WebAppServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/app/util/WebAppServiceImpl.java @@ -13,8 +13,8 @@ import java.util.Date; import java.util.List; -import org.apache.http.HttpResponse; import org.apache.http.HttpStatus; +import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpHead; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.logging.log4j.Logger; @@ -78,7 +78,7 @@ public List getApps() { method = new HttpHead(app.getUrl()); int status; try (CloseableHttpClient client = DSpaceHttpClientFactory.getInstance().build()) { - HttpResponse response = client.execute(method); + CloseableHttpResponse response = client.execute(method); status = response.getStatusLine().getStatusCode(); } if (status != HttpStatus.SC_OK) { diff --git a/dspace-api/src/main/java/org/dspace/authenticate/oidc/impl/OidcClientImpl.java b/dspace-api/src/main/java/org/dspace/authenticate/oidc/impl/OidcClientImpl.java index f28244268ff1..3e3d4b905b94 100644 --- a/dspace-api/src/main/java/org/dspace/authenticate/oidc/impl/OidcClientImpl.java +++ b/dspace-api/src/main/java/org/dspace/authenticate/oidc/impl/OidcClientImpl.java @@ -22,10 +22,11 @@ import org.apache.http.HttpEntity; import org.apache.http.HttpResponse; import org.apache.http.NameValuePair; -import org.apache.http.client.HttpClient; import org.apache.http.client.entity.UrlEncodedFormEntity; +import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpUriRequest; import org.apache.http.client.methods.RequestBuilder; +import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.message.BasicNameValuePair; import org.dspace.app.client.DSpaceHttpClientFactory; import org.dspace.authenticate.oidc.OidcClient; @@ -83,21 +84,17 @@ public Map getUserInfo(String accessToken) throws OidcClientExce } private T executeAndParseJson(HttpUriRequest httpUriRequest, Class clazz) { - - HttpClient client = DSpaceHttpClientFactory.getInstance().build(); - - return executeAndReturns(() -> { - - HttpResponse response = client.execute(httpUriRequest); - - if (isNotSuccessfull(response)) { - throw new OidcClientException(getStatusCode(response), formatErrorMessage(response)); - } - - return objectMapper.readValue(getContent(response), clazz); - - }); - + try (CloseableHttpClient client = DSpaceHttpClientFactory.getInstance().build()) { + return executeAndReturns(() -> { + CloseableHttpResponse response = client.execute(httpUriRequest); + if (isNotSuccessfull(response)) { + throw new OidcClientException(getStatusCode(response), formatErrorMessage(response)); + } + return objectMapper.readValue(getContent(response), clazz); + }); + } catch (IOException e) { + throw new RuntimeException(e); + } } private T executeAndReturns(ThrowingSupplier supplier) { diff --git a/dspace-api/src/main/java/org/dspace/content/packager/AbstractMETSIngester.java b/dspace-api/src/main/java/org/dspace/content/packager/AbstractMETSIngester.java index c43d8e797b07..835b9f0e9b6f 100644 --- a/dspace-api/src/main/java/org/dspace/content/packager/AbstractMETSIngester.java +++ b/dspace-api/src/main/java/org/dspace/content/packager/AbstractMETSIngester.java @@ -1312,9 +1312,8 @@ protected static InputStream getFileInputStream(File pkgFile, if (params.getBooleanProperty("manifestOnly", false)) { // NOTE: since we are only dealing with a METS manifest, // we will assume all external files are available via URLs. - try { + try (CloseableHttpClient httpClient = DSpaceHttpClientFactory.getInstance().build()) { // attempt to open a connection to given URL - CloseableHttpClient httpClient = DSpaceHttpClientFactory.getInstance().build(); CloseableHttpResponse httpResponse = httpClient.execute(new HttpGet(path)); // open stream to access file contents diff --git a/dspace-api/src/main/java/org/dspace/ctask/general/BasicLinkChecker.java b/dspace-api/src/main/java/org/dspace/ctask/general/BasicLinkChecker.java index fd6d69c98f04..189f77733886 100644 --- a/dspace-api/src/main/java/org/dspace/ctask/general/BasicLinkChecker.java +++ b/dspace-api/src/main/java/org/dspace/ctask/general/BasicLinkChecker.java @@ -139,9 +139,8 @@ protected boolean checkURL(String url, StringBuilder results) { * @return The HTTP response code (e.g. 200 / 301 / 404 / 500) */ protected int getResponseStatus(String url, int redirects) { - try { - RequestConfig config = RequestConfig.custom().setRedirectsEnabled(true).build(); - CloseableHttpClient httpClient = DSpaceHttpClientFactory.getInstance().buildWithRequestConfig(config); + RequestConfig config = RequestConfig.custom().setRedirectsEnabled(true).build(); + try (CloseableHttpClient httpClient = DSpaceHttpClientFactory.getInstance().buildWithRequestConfig(config)) { CloseableHttpResponse httpResponse = httpClient.execute(new HttpGet(url)); int statusCode = httpResponse.getStatusLine().getStatusCode(); int maxRedirect = configurationService.getIntProperty("curate.checklinks.max-redirect", 0); diff --git a/dspace-api/src/main/java/org/dspace/external/OrcidRestConnector.java b/dspace-api/src/main/java/org/dspace/external/OrcidRestConnector.java index 3ccbd257e285..f2c6246f4d06 100644 --- a/dspace-api/src/main/java/org/dspace/external/OrcidRestConnector.java +++ b/dspace-api/src/main/java/org/dspace/external/OrcidRestConnector.java @@ -12,9 +12,9 @@ import java.util.Scanner; import org.apache.commons.lang3.StringUtils; -import org.apache.http.HttpResponse; -import org.apache.http.client.HttpClient; +import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpGet; +import org.apache.http.impl.client.CloseableHttpClient; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.dspace.app.client.DSpaceHttpClientFactory; @@ -39,7 +39,7 @@ public OrcidRestConnector(String url) { } public InputStream get(String path, String accessToken) { - HttpResponse getResponse = null; + CloseableHttpResponse getResponse = null; InputStream result = null; path = trimSlashes(path); @@ -49,10 +49,8 @@ public InputStream get(String path, String accessToken) { httpGet.addHeader("Content-Type", "application/vnd.orcid+xml"); httpGet.addHeader("Authorization","Bearer " + accessToken); } - try { - HttpClient httpClient = DSpaceHttpClientFactory.getInstance().build(); + try (CloseableHttpClient httpClient = DSpaceHttpClientFactory.getInstance().build()) { getResponse = httpClient.execute(httpGet); - //do not close this httpClient result = getResponse.getEntity().getContent(); } catch (Exception e) { getGotError(e, fullPath); diff --git a/dspace-api/src/main/java/org/dspace/iiif/IIIFApiQueryServiceImpl.java b/dspace-api/src/main/java/org/dspace/iiif/IIIFApiQueryServiceImpl.java index 87bd7770648e..ccb2c170d949 100644 --- a/dspace-api/src/main/java/org/dspace/iiif/IIIFApiQueryServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/iiif/IIIFApiQueryServiceImpl.java @@ -38,8 +38,7 @@ public int[] getImageDimensions(Bitstream bitstream) { int[] arr = new int[2]; String path = IIIFSharedUtils.getInfoJsonPath(bitstream); BufferedReader in = null; - try { - CloseableHttpClient httpClient = DSpaceHttpClientFactory.getInstance().build(); + try (CloseableHttpClient httpClient = DSpaceHttpClientFactory.getInstance().build()) { CloseableHttpResponse httpResponse = httpClient.execute(new HttpGet(path)); in = new BufferedReader(new InputStreamReader(httpResponse.getEntity().getContent())); String inputLine; diff --git a/dspace-api/src/main/java/org/dspace/license/CCLicenseConnectorServiceImpl.java b/dspace-api/src/main/java/org/dspace/license/CCLicenseConnectorServiceImpl.java index e9fe49e9b4f6..1d777a2e13c8 100644 --- a/dspace-api/src/main/java/org/dspace/license/CCLicenseConnectorServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/license/CCLicenseConnectorServiceImpl.java @@ -326,8 +326,7 @@ private String createAnswerString(final Map parameterMap) { public Document retrieveLicenseRDFDoc(String licenseURI) throws IOException { String ccLicenseUrl = configurationService.getProperty("cc.api.rooturl"); String issueUrl = ccLicenseUrl + "/details?license-uri=" + licenseURI; - try { - CloseableHttpClient httpClient = DSpaceHttpClientFactory.getInstance().build(); + try (CloseableHttpClient httpClient = DSpaceHttpClientFactory.getInstance().build()) { CloseableHttpResponse httpResponse = httpClient.execute(new HttpPost(issueUrl)); // parsing document from input stream InputStream stream = httpResponse.getEntity().getContent(); diff --git a/dspace-api/src/main/java/org/dspace/orcid/client/OrcidClientImpl.java b/dspace-api/src/main/java/org/dspace/orcid/client/OrcidClientImpl.java index 62e4c1d61c1b..da1de465251d 100644 --- a/dspace-api/src/main/java/org/dspace/orcid/client/OrcidClientImpl.java +++ b/dspace-api/src/main/java/org/dspace/orcid/client/OrcidClientImpl.java @@ -35,11 +35,12 @@ import org.apache.http.HttpResponse; import org.apache.http.HttpStatus; import org.apache.http.NameValuePair; -import org.apache.http.client.HttpClient; import org.apache.http.client.entity.UrlEncodedFormEntity; +import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpUriRequest; import org.apache.http.client.methods.RequestBuilder; import org.apache.http.entity.StringEntity; +import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.message.BasicNameValuePair; import org.dspace.app.client.DSpaceHttpClientFactory; import org.dspace.orcid.OrcidToken; @@ -76,12 +77,9 @@ public class OrcidClientImpl implements OrcidClient { private final ObjectMapper objectMapper; - private final DSpaceHttpClientFactory httpClientFactory; - - public OrcidClientImpl(OrcidConfiguration orcidConfiguration, DSpaceHttpClientFactory httpClientFactory) { + public OrcidClientImpl(OrcidConfiguration orcidConfiguration) { this.orcidConfiguration = orcidConfiguration; this.objectMapper = new ObjectMapper(); - this.httpClientFactory = httpClientFactory; } private static Map, String> initializePathsMap() { @@ -257,10 +255,8 @@ private HttpUriRequest buildDeleteUriRequest(String accessToken, String relative } private void executeSuccessful(HttpUriRequest httpUriRequest) { - try { - HttpClient client = httpClientFactory.build(); - HttpResponse response = client.execute(httpUriRequest); - + try (CloseableHttpClient client = DSpaceHttpClientFactory.getInstance().build()) { + CloseableHttpResponse response = client.execute(httpUriRequest); if (isNotSuccessfull(response)) { throw new OrcidClientException( getStatusCode(response), @@ -275,21 +271,17 @@ private void executeSuccessful(HttpUriRequest httpUriRequest) { } private T executeAndParseJson(HttpUriRequest httpUriRequest, Class clazz) { - - HttpClient client = httpClientFactory.build(); - - return executeAndReturns(() -> { - - HttpResponse response = client.execute(httpUriRequest); - - if (isNotSuccessfull(response)) { - throw new OrcidClientException(getStatusCode(response), formatErrorMessage(response)); - } - - return objectMapper.readValue(response.getEntity().getContent(), clazz); - - }); - + try (CloseableHttpClient client = DSpaceHttpClientFactory.getInstance().build()) { + return executeAndReturns(() -> { + CloseableHttpResponse response = client.execute(httpUriRequest); + if (isNotSuccessfull(response)) { + throw new OrcidClientException(getStatusCode(response), formatErrorMessage(response)); + } + return objectMapper.readValue(response.getEntity().getContent(), clazz); + }); + } catch (IOException e) { + throw new RuntimeException(e); + } } /** @@ -304,44 +296,37 @@ private T executeAndParseJson(HttpUriRequest httpUriRequest, Class clazz) * @throws OrcidClientException if the incoming response is not successfull */ private T executeAndUnmarshall(HttpUriRequest httpUriRequest, boolean handleNotFoundAsNull, Class clazz) { - - HttpClient client = httpClientFactory.build(); - - return executeAndReturns(() -> { - - HttpResponse response = client.execute(httpUriRequest); - - if (handleNotFoundAsNull && isNotFound(response)) { - return null; - } - - if (isNotSuccessfull(response)) { - throw new OrcidClientException(getStatusCode(response), formatErrorMessage(response)); - } - - return unmarshall(response.getEntity(), clazz); - - }); + try (CloseableHttpClient client = DSpaceHttpClientFactory.getInstance().build()) { + return executeAndReturns(() -> { + CloseableHttpResponse response = client.execute(httpUriRequest); + if (handleNotFoundAsNull && isNotFound(response)) { + return null; + } + if (isNotSuccessfull(response)) { + throw new OrcidClientException(getStatusCode(response), formatErrorMessage(response)); + } + return unmarshall(response.getEntity(), clazz); + }); + } catch (IOException e) { + throw new RuntimeException(e); + } } private OrcidResponse execute(HttpUriRequest httpUriRequest, boolean handleNotFoundAsNull) { - HttpClient client = httpClientFactory.build(); - - return executeAndReturns(() -> { - - HttpResponse response = client.execute(httpUriRequest); - - if (handleNotFoundAsNull && isNotFound(response)) { - return new OrcidResponse(getStatusCode(response), null, getContent(response)); - } - - if (isNotSuccessfull(response)) { - throw new OrcidClientException(getStatusCode(response), formatErrorMessage(response)); - } - - return new OrcidResponse(getStatusCode(response), getPutCode(response), getContent(response)); - - }); + try (CloseableHttpClient client = DSpaceHttpClientFactory.getInstance().build()) { + return executeAndReturns(() -> { + CloseableHttpResponse response = client.execute(httpUriRequest); + if (handleNotFoundAsNull && isNotFound(response)) { + return new OrcidResponse(getStatusCode(response), null, getContent(response)); + } + if (isNotSuccessfull(response)) { + throw new OrcidClientException(getStatusCode(response), formatErrorMessage(response)); + } + return new OrcidResponse(getStatusCode(response), getPutCode(response), getContent(response)); + }); + } catch (IOException e) { + throw new RuntimeException(e); + } } private T executeAndReturns(ThrowingSupplier supplier) { From f4b41b701a9586b3168208bade2f54cdec17a27b Mon Sep 17 00:00:00 2001 From: Elios Buzo Date: Fri, 18 Apr 2025 15:12:55 +0200 Subject: [PATCH 643/979] [DURACOM-109] Minor fix --- .../java/org/dspace/service/impl/HttpConnectionPoolService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dspace-api/src/main/java/org/dspace/service/impl/HttpConnectionPoolService.java b/dspace-api/src/main/java/org/dspace/service/impl/HttpConnectionPoolService.java index b7c99a08cecc..f0484a0576f2 100644 --- a/dspace-api/src/main/java/org/dspace/service/impl/HttpConnectionPoolService.java +++ b/dspace-api/src/main/java/org/dspace/service/impl/HttpConnectionPoolService.java @@ -112,7 +112,7 @@ protected void init() { * @return the client. */ public CloseableHttpClient getClient() { - CloseableHttpClient httpClient = DSpaceHttpClientFactory.getInstance().builder(false).create() + CloseableHttpClient httpClient = DSpaceHttpClientFactory.getInstance().builder(true).create() .setKeepAliveStrategy(keepAliveStrategy) .setConnectionManager(connManager) .build(); From 62bde6589f28688ef382d389e194ecc61b1b0082 Mon Sep 17 00:00:00 2001 From: Elios Buzo Date: Wed, 23 Apr 2025 12:31:11 +0200 Subject: [PATCH 644/979] [DURACOM-109] Continued fixing http connection leaks --- .../app/ldn/action/SendLDNMessageAction.java | 29 +++---- .../packager/AbstractMETSIngester.java | 8 +- .../ctask/general/BasicLinkChecker.java | 24 +++--- .../ctask/general/MetadataWebService.java | 85 +++++++++---------- .../ctask/general/MicrosoftTranslator.java | 26 +++--- .../dspace/eperson/CaptchaServiceImpl.java | 19 ++--- .../impl/QAEventActionServiceImpl.java | 7 +- .../export/service/OpenUrlServiceImpl.java | 16 ++-- .../service/OpenUrlServiceImplTest.java | 10 +-- 9 files changed, 106 insertions(+), 118 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/app/ldn/action/SendLDNMessageAction.java b/dspace-api/src/main/java/org/dspace/app/ldn/action/SendLDNMessageAction.java index cbcfd4386b3a..c5a60144e63c 100644 --- a/dspace-api/src/main/java/org/dspace/app/ldn/action/SendLDNMessageAction.java +++ b/dspace-api/src/main/java/org/dspace/app/ldn/action/SendLDNMessageAction.java @@ -34,17 +34,13 @@ public class SendLDNMessageAction implements LDNAction { private static final Logger log = LogManager.getLogger(SendLDNMessageAction.class); - private CloseableHttpClient client = null; + private CloseableHttpClient client; public SendLDNMessageAction() { - client = DSpaceHttpClientFactory.getInstance().buildWithoutAutomaticRetries(5); } public SendLDNMessageAction(CloseableHttpClient client) { - this(); - if (client != null) { - this.client = client; - } + this.client = client; } @Override @@ -62,9 +58,10 @@ public LDNActionStatus execute(Context context, Notification notification, Item // NOTE: Github believes there is a "Potential server-side request forgery due to a user-provided value" // This is a false positive because the LDN Service URL is configured by the user from DSpace. // See the frontend configuration at [dspace.ui.url]/admin/ldn/services - try ( - CloseableHttpResponse response = client.execute(httpPost); - ) { + if (client == null) { + client = DSpaceHttpClientFactory.getInstance().buildWithoutAutomaticRetries(5); + } + try (CloseableHttpResponse response = client.execute(httpPost)) { if (isSuccessful(response.getStatusLine().getStatusCode())) { result = LDNActionStatus.CONTINUE; } else if (isRedirect(response.getStatusLine().getStatusCode())) { @@ -73,6 +70,7 @@ public LDNActionStatus execute(Context context, Notification notification, Item } catch (Exception e) { log.error(e); } + client.close(); return result; } @@ -87,9 +85,9 @@ private boolean isRedirect(int statusCode) { statusCode == HttpStatus.SC_TEMPORARY_REDIRECT; } - private LDNActionStatus handleRedirect(CloseableHttpResponse oldresponse, + private LDNActionStatus handleRedirect(CloseableHttpResponse oldResponse, HttpPost request) throws HttpException { - Header[] urls = oldresponse.getHeaders(HttpHeaders.LOCATION); + Header[] urls = oldResponse.getHeaders(HttpHeaders.LOCATION); String url = urls.length > 0 && urls[0] != null ? urls[0].getValue() : null; if (url == null) { throw new HttpException("Error following redirect, unable to reach" @@ -98,17 +96,14 @@ private LDNActionStatus handleRedirect(CloseableHttpResponse oldresponse, LDNActionStatus result = LDNActionStatus.ABORT; try { request.setURI(new URI(url)); - try ( - CloseableHttpResponse response = client.execute(request); - ) { + try (CloseableHttpResponse response = client.execute(request)) { if (isSuccessful(response.getStatusLine().getStatusCode())) { - return LDNActionStatus.CONTINUE; + result = LDNActionStatus.CONTINUE; } } } catch (Exception e) { log.error("Error following redirect:", e); } - - return LDNActionStatus.ABORT; + return result; } } \ No newline at end of file diff --git a/dspace-api/src/main/java/org/dspace/content/packager/AbstractMETSIngester.java b/dspace-api/src/main/java/org/dspace/content/packager/AbstractMETSIngester.java index 835b9f0e9b6f..77236be9d525 100644 --- a/dspace-api/src/main/java/org/dspace/content/packager/AbstractMETSIngester.java +++ b/dspace-api/src/main/java/org/dspace/content/packager/AbstractMETSIngester.java @@ -1314,10 +1314,10 @@ protected static InputStream getFileInputStream(File pkgFile, // we will assume all external files are available via URLs. try (CloseableHttpClient httpClient = DSpaceHttpClientFactory.getInstance().build()) { // attempt to open a connection to given URL - CloseableHttpResponse httpResponse = httpClient.execute(new HttpGet(path)); - - // open stream to access file contents - return httpResponse.getEntity().getContent(); + try (CloseableHttpResponse httpResponse = httpClient.execute(new HttpGet(path))) { + // open stream to access file contents + return httpResponse.getEntity().getContent(); + } } catch (IOException io) { log .error("Unable to retrieve external file from URL '" diff --git a/dspace-api/src/main/java/org/dspace/ctask/general/BasicLinkChecker.java b/dspace-api/src/main/java/org/dspace/ctask/general/BasicLinkChecker.java index 189f77733886..a6621fcb2b41 100644 --- a/dspace-api/src/main/java/org/dspace/ctask/general/BasicLinkChecker.java +++ b/dspace-api/src/main/java/org/dspace/ctask/general/BasicLinkChecker.java @@ -141,21 +141,19 @@ protected boolean checkURL(String url, StringBuilder results) { protected int getResponseStatus(String url, int redirects) { RequestConfig config = RequestConfig.custom().setRedirectsEnabled(true).build(); try (CloseableHttpClient httpClient = DSpaceHttpClientFactory.getInstance().buildWithRequestConfig(config)) { - CloseableHttpResponse httpResponse = httpClient.execute(new HttpGet(url)); - int statusCode = httpResponse.getStatusLine().getStatusCode(); - int maxRedirect = configurationService.getIntProperty("curate.checklinks.max-redirect", 0); - if ((statusCode == HttpURLConnection.HTTP_MOVED_TEMP || statusCode == HttpURLConnection.HTTP_MOVED_PERM || - statusCode == HttpURLConnection.HTTP_SEE_OTHER)) { - httpClient.close(); - String newUrl = httpResponse.getFirstHeader("Location").getValue(); - if (newUrl != null && (maxRedirect >= redirects || maxRedirect == -1)) { - redirects++; - return getResponseStatus(newUrl, redirects); + try (CloseableHttpResponse httpResponse = httpClient.execute(new HttpGet(url))) { + int statusCode = httpResponse.getStatusLine().getStatusCode(); + int maxRedirect = configurationService.getIntProperty("curate.checklinks.max-redirect", 0); + if ((statusCode == HttpURLConnection.HTTP_MOVED_TEMP || statusCode == HttpURLConnection.HTTP_MOVED_PERM || + statusCode == HttpURLConnection.HTTP_SEE_OTHER)) { + String newUrl = httpResponse.getFirstHeader("Location").getValue(); + if (newUrl != null && (maxRedirect >= redirects || maxRedirect == -1)) { + redirects++; + return getResponseStatus(newUrl, redirects); + } } - + return statusCode; } - return statusCode; - } catch (IOException ioe) { // Must be a bad URL log.debug("Bad link: " + ioe.getMessage()); diff --git a/dspace-api/src/main/java/org/dspace/ctask/general/MetadataWebService.java b/dspace-api/src/main/java/org/dspace/ctask/general/MetadataWebService.java index da7588be6c6f..7f61cad412e2 100644 --- a/dspace-api/src/main/java/org/dspace/ctask/general/MetadataWebService.java +++ b/dspace-api/src/main/java/org/dspace/ctask/general/MetadataWebService.java @@ -30,8 +30,8 @@ import javax.xml.xpath.XPathFactory; import org.apache.http.HttpEntity; -import org.apache.http.HttpResponse; import org.apache.http.HttpStatus; +import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpGet; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.logging.log4j.LogManager; @@ -255,53 +255,50 @@ public int perform(DSpaceObject dso) throws IOException { } protected int callService(String value, Item item, StringBuilder resultSb) throws IOException { - String callUrl = urlTemplate.replaceAll("\\{" + templateParam + "\\}", value); - CloseableHttpClient client = DSpaceHttpClientFactory.getInstance().build(); - HttpGet req = new HttpGet(callUrl); - for (Map.Entry entry : headers.entrySet()) { - req.addHeader(entry.getKey(), entry.getValue()); - } - HttpResponse resp = client.execute(req); - int status = Curator.CURATE_ERROR; - int statusCode = resp.getStatusLine().getStatusCode(); - if (statusCode == HttpStatus.SC_OK) { - HttpEntity entity = resp.getEntity(); - if (entity != null) { - // boiler-plate handling taken from Apache 4.1 javadoc - InputStream instream = entity.getContent(); - try { - // This next line triggers a false-positive XXE warning from LGTM, even though we disallow DTD - // parsing during initialization of docBuilder in init() - Document doc = docBuilder.parse(instream); // lgtm [java/xxe] - status = processResponse(doc, item, resultSb); - } catch (SAXException saxE) { - log.error("caught exception: " + saxE); - resultSb.append(" unable to read response document"); - } catch (RuntimeException ex) { - // In case of an unexpected exception you may want to abort - // the HTTP request in order to shut down the underlying - // connection and release it back to the connection manager. - req.abort(); - log.error("caught exception: " + ex); - throw ex; - } finally { - // Closing the input stream will trigger connection release - instream.close(); + try (CloseableHttpClient client = DSpaceHttpClientFactory.getInstance().build()) { + HttpGet req = new HttpGet(callUrl); + for (Map.Entry entry : headers.entrySet()) { + req.addHeader(entry.getKey(), entry.getValue()); + } + try (CloseableHttpResponse resp = client.execute(req)) { + int status = Curator.CURATE_ERROR; + int statusCode = resp.getStatusLine().getStatusCode(); + if (statusCode == HttpStatus.SC_OK) { + HttpEntity entity = resp.getEntity(); + if (entity != null) { + // boiler-plate handling taken from Apache 4.1 javadoc + InputStream instream = entity.getContent(); + try { + // This next line triggers a false-positive XXE warning from LGTM, even though we disallow DTD + // parsing during initialization of docBuilder in init() + Document doc = docBuilder.parse(instream); // lgtm [java/xxe] + status = processResponse(doc, item, resultSb); + } catch (SAXException saxE) { + log.error("caught exception: " + saxE); + resultSb.append(" unable to read response document"); + } catch (RuntimeException ex) { + // In case of an unexpected exception you may want to abort + // the HTTP request in order to shut down the underlying + // connection and release it back to the connection manager. + req.abort(); + log.error("caught exception: " + ex); + throw ex; + } finally { + // Closing the input stream will trigger connection release + instream.close(); + } + } else { + log.error(" obtained no valid service response"); + resultSb.append("no service response"); + } + } else { + log.error("service returned non-OK status: " + statusCode); + resultSb.append("no service response"); } - // When HttpClient instance is no longer needed, - // shut down the connection manager to ensure - // immediate deallocation of all system resources - client.close(); - } else { - log.error(" obtained no valid service response"); - resultSb.append("no service response"); + return status; } - } else { - log.error("service returned non-OK status: " + statusCode); - resultSb.append("no service response"); } - return status; } protected int processResponse(Document doc, Item item, StringBuilder resultSb) throws IOException { diff --git a/dspace-api/src/main/java/org/dspace/ctask/general/MicrosoftTranslator.java b/dspace-api/src/main/java/org/dspace/ctask/general/MicrosoftTranslator.java index 0d682d9406f4..af9c2de0c8b1 100644 --- a/dspace-api/src/main/java/org/dspace/ctask/general/MicrosoftTranslator.java +++ b/dspace-api/src/main/java/org/dspace/ctask/general/MicrosoftTranslator.java @@ -12,7 +12,7 @@ import java.nio.charset.StandardCharsets; import org.apache.commons.io.IOUtils; -import org.apache.http.HttpResponse; +import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpGet; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.logging.log4j.LogManager; @@ -62,20 +62,18 @@ protected String translateText(String from, String to, String text) throws IOExc try (CloseableHttpClient client = DSpaceHttpClientFactory.getInstance().build()) { HttpGet hm = new HttpGet(url); - HttpResponse httpResponse = client.execute(hm); - log.debug("Response code from API call is " + httpResponse); - - if (httpResponse.getStatusLine().getStatusCode() == 200) { - String response = IOUtils.toString(httpResponse.getEntity().getContent(), - StandardCharsets.ISO_8859_1); - response = response - .replaceAll("", ""); - response = response.replaceAll("", ""); - translatedText = response; + try (CloseableHttpResponse httpResponse = client.execute(hm)) { + log.debug("Response code from API call is " + httpResponse); + if (httpResponse.getStatusLine().getStatusCode() == 200) { + String response = IOUtils.toString(httpResponse.getEntity().getContent(), + StandardCharsets.ISO_8859_1); + response = response + .replaceAll("", ""); + response = response.replaceAll("", ""); + translatedText = response; + } } } - return translatedText; } -} - +} \ No newline at end of file diff --git a/dspace-api/src/main/java/org/dspace/eperson/CaptchaServiceImpl.java b/dspace-api/src/main/java/org/dspace/eperson/CaptchaServiceImpl.java index 8300061cbe4e..15f92247d6cc 100644 --- a/dspace-api/src/main/java/org/dspace/eperson/CaptchaServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/eperson/CaptchaServiceImpl.java @@ -17,11 +17,11 @@ import com.fasterxml.jackson.databind.ObjectMapper; import jakarta.annotation.PostConstruct; -import org.apache.http.HttpResponse; import org.apache.http.NameValuePair; -import org.apache.http.client.HttpClient; import org.apache.http.client.entity.UrlEncodedFormEntity; +import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpPost; +import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.message.BasicNameValuePair; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -82,18 +82,17 @@ public void processResponse(String response, String action) throws InvalidReCapt throw new RuntimeException(e.getMessage(), e); } - HttpClient httpClient = DSpaceHttpClientFactory.getInstance().build(); - HttpResponse httpResponse; - GoogleCaptchaResponse googleResponse; - final ObjectMapper objectMapper = new ObjectMapper(); - try { - httpResponse = httpClient.execute(httpPost); - googleResponse = objectMapper.readValue(httpResponse.getEntity().getContent(), GoogleCaptchaResponse.class); + try (CloseableHttpClient httpClient = DSpaceHttpClientFactory.getInstance().build()) { + final ObjectMapper objectMapper = new ObjectMapper(); + try (CloseableHttpResponse httpResponse = httpClient.execute(httpPost)) { + GoogleCaptchaResponse googleResponse = objectMapper.readValue(httpResponse.getEntity().getContent(), + GoogleCaptchaResponse.class); + validateGoogleResponse(googleResponse, action); + } } catch (IOException e) { log.error(e.getMessage(), e); throw new RuntimeException("Error during verify google recaptcha site", e); } - validateGoogleResponse(googleResponse, action); } private boolean responseSanityCheck(String response) { diff --git a/dspace-api/src/main/java/org/dspace/qaevent/service/impl/QAEventActionServiceImpl.java b/dspace-api/src/main/java/org/dspace/qaevent/service/impl/QAEventActionServiceImpl.java index 38e7295582eb..3faee3a4f9f7 100644 --- a/dspace-api/src/main/java/org/dspace/qaevent/service/impl/QAEventActionServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/qaevent/service/impl/QAEventActionServiceImpl.java @@ -123,10 +123,9 @@ private void makeAcknowledgement(String eventId, String source, String status) { node.put("eventId", eventId); node.put("status", status); StringEntity requestEntity = new StringEntity(node.toString(), ContentType.APPLICATION_JSON); - CloseableHttpClient httpclient = DSpaceHttpClientFactory.getInstance().buildWithoutProxy(); - HttpPost postMethod = new HttpPost(ackwnoledgeCallback); - postMethod.setEntity(requestEntity); - try { + try (CloseableHttpClient httpclient = DSpaceHttpClientFactory.getInstance().buildWithoutProxy()) { + HttpPost postMethod = new HttpPost(ackwnoledgeCallback); + postMethod.setEntity(requestEntity); httpclient.execute(postMethod); } catch (IOException e) { log.error(e.getMessage(), e); diff --git a/dspace-api/src/main/java/org/dspace/statistics/export/service/OpenUrlServiceImpl.java b/dspace-api/src/main/java/org/dspace/statistics/export/service/OpenUrlServiceImpl.java index d8f4aa6b7e7b..64014fcbc07c 100644 --- a/dspace-api/src/main/java/org/dspace/statistics/export/service/OpenUrlServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/statistics/export/service/OpenUrlServiceImpl.java @@ -14,10 +14,10 @@ import java.util.List; import org.apache.commons.lang.StringUtils; -import org.apache.http.HttpResponse; -import org.apache.http.client.HttpClient; import org.apache.http.client.config.RequestConfig; +import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpGet; +import org.apache.http.impl.client.CloseableHttpClient; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.dspace.app.client.DSpaceHttpClientFactory; @@ -68,13 +68,15 @@ public void processUrl(Context c, String urlStr) throws SQLException { * @throws IOException */ protected int getResponseCodeFromUrl(final String urlStr) throws IOException { - HttpGet httpGet = new HttpGet(urlStr); - HttpClient httpClient = getHttpClient(getHttpClientRequestConfig()); - HttpResponse httpResponse = httpClient.execute(httpGet); - return httpResponse.getStatusLine().getStatusCode(); + try (CloseableHttpClient httpClient = getHttpClient(getHttpClientRequestConfig())) { + HttpGet httpGet = new HttpGet(urlStr); + try (CloseableHttpResponse httpResponse = httpClient.execute(httpGet)) { + return httpResponse.getStatusLine().getStatusCode(); + } + } } - protected HttpClient getHttpClient(RequestConfig requestConfig) { + protected CloseableHttpClient getHttpClient(RequestConfig requestConfig) { return DSpaceHttpClientFactory.getInstance().buildWithRequestConfig(requestConfig); } diff --git a/dspace-api/src/test/java/org/dspace/statistics/export/service/OpenUrlServiceImplTest.java b/dspace-api/src/test/java/org/dspace/statistics/export/service/OpenUrlServiceImplTest.java index d214050e6b5a..718ef701e136 100644 --- a/dspace-api/src/test/java/org/dspace/statistics/export/service/OpenUrlServiceImplTest.java +++ b/dspace-api/src/test/java/org/dspace/statistics/export/service/OpenUrlServiceImplTest.java @@ -27,9 +27,9 @@ import java.util.Date; import java.util.List; -import org.apache.http.HttpResponse; import org.apache.http.StatusLine; -import org.apache.http.client.HttpClient; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.impl.client.CloseableHttpClient; import org.dspace.core.Context; import org.dspace.statistics.export.OpenURLTracker; import org.junit.Before; @@ -55,7 +55,7 @@ public class OpenUrlServiceImplTest { private FailedOpenURLTrackerService failedOpenURLTrackerService; @Mock - private HttpClient httpClient; + private CloseableHttpClient httpClient; @Before public void setUp() throws Exception { @@ -74,11 +74,11 @@ public void setUp() throws Exception { * @param statusCode the http status code to use in the mock. * @return a mocked http response. */ - protected HttpResponse createMockHttpResponse(int statusCode) { + protected CloseableHttpResponse createMockHttpResponse(int statusCode) { StatusLine statusLine = mock(StatusLine.class); when(statusLine.getStatusCode()).thenReturn(statusCode); - HttpResponse httpResponse = mock(HttpResponse.class); + CloseableHttpResponse httpResponse = mock(CloseableHttpResponse.class); when(httpResponse.getStatusLine()).thenReturn(statusLine); return httpResponse; From 460b2d1653e2da6a64ccf05c2fdc51961533005d Mon Sep 17 00:00:00 2001 From: Elios Buzo Date: Wed, 23 Apr 2025 15:34:52 +0200 Subject: [PATCH 645/979] [DURACOM-109] Linter error fix --- .../ctask/general/BasicLinkChecker.java | 21 +++++++++---------- .../ctask/general/MetadataWebService.java | 4 ++-- .../impl/QAEventActionServiceImpl.java | 14 ++++++------- 3 files changed, 19 insertions(+), 20 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/ctask/general/BasicLinkChecker.java b/dspace-api/src/main/java/org/dspace/ctask/general/BasicLinkChecker.java index a6621fcb2b41..020331842703 100644 --- a/dspace-api/src/main/java/org/dspace/ctask/general/BasicLinkChecker.java +++ b/dspace-api/src/main/java/org/dspace/ctask/general/BasicLinkChecker.java @@ -141,19 +141,18 @@ protected boolean checkURL(String url, StringBuilder results) { protected int getResponseStatus(String url, int redirects) { RequestConfig config = RequestConfig.custom().setRedirectsEnabled(true).build(); try (CloseableHttpClient httpClient = DSpaceHttpClientFactory.getInstance().buildWithRequestConfig(config)) { - try (CloseableHttpResponse httpResponse = httpClient.execute(new HttpGet(url))) { - int statusCode = httpResponse.getStatusLine().getStatusCode(); - int maxRedirect = configurationService.getIntProperty("curate.checklinks.max-redirect", 0); - if ((statusCode == HttpURLConnection.HTTP_MOVED_TEMP || statusCode == HttpURLConnection.HTTP_MOVED_PERM || - statusCode == HttpURLConnection.HTTP_SEE_OTHER)) { - String newUrl = httpResponse.getFirstHeader("Location").getValue(); - if (newUrl != null && (maxRedirect >= redirects || maxRedirect == -1)) { - redirects++; - return getResponseStatus(newUrl, redirects); - } + CloseableHttpResponse httpResponse = httpClient.execute(new HttpGet(url)); + int statusCode = httpResponse.getStatusLine().getStatusCode(); + int maxRedirect = configurationService.getIntProperty("curate.checklinks.max-redirect", 0); + if ((statusCode == HttpURLConnection.HTTP_MOVED_TEMP || statusCode == HttpURLConnection.HTTP_MOVED_PERM || + statusCode == HttpURLConnection.HTTP_SEE_OTHER)) { + String newUrl = httpResponse.getFirstHeader("Location").getValue(); + if (newUrl != null && (maxRedirect >= redirects || maxRedirect == -1)) { + redirects++; + return getResponseStatus(newUrl, redirects); } - return statusCode; } + return statusCode; } catch (IOException ioe) { // Must be a bad URL log.debug("Bad link: " + ioe.getMessage()); diff --git a/dspace-api/src/main/java/org/dspace/ctask/general/MetadataWebService.java b/dspace-api/src/main/java/org/dspace/ctask/general/MetadataWebService.java index 7f61cad412e2..fc62d7a4b23f 100644 --- a/dspace-api/src/main/java/org/dspace/ctask/general/MetadataWebService.java +++ b/dspace-api/src/main/java/org/dspace/ctask/general/MetadataWebService.java @@ -270,8 +270,8 @@ protected int callService(String value, Item item, StringBuilder resultSb) throw // boiler-plate handling taken from Apache 4.1 javadoc InputStream instream = entity.getContent(); try { - // This next line triggers a false-positive XXE warning from LGTM, even though we disallow DTD - // parsing during initialization of docBuilder in init() + // This next line triggers a false-positive XXE warning from LGTM, even though + // we disallow DTD parsing during initialization of docBuilder in init() Document doc = docBuilder.parse(instream); // lgtm [java/xxe] status = processResponse(doc, item, resultSb); } catch (SAXException saxE) { diff --git a/dspace-api/src/main/java/org/dspace/qaevent/service/impl/QAEventActionServiceImpl.java b/dspace-api/src/main/java/org/dspace/qaevent/service/impl/QAEventActionServiceImpl.java index 3faee3a4f9f7..6b9aea91de76 100644 --- a/dspace-api/src/main/java/org/dspace/qaevent/service/impl/QAEventActionServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/qaevent/service/impl/QAEventActionServiceImpl.java @@ -114,19 +114,19 @@ public void reject(Context context, QAEvent qaevent) { * Make acknowledgement to the configured urls for the event status. */ private void makeAcknowledgement(String eventId, String source, String status) { - String[] ackwnoledgeCallbacks = configurationService + String[] acknowledgeCallbacks = configurationService .getArrayProperty("qaevents." + source + ".acknowledge-url"); - if (ackwnoledgeCallbacks != null) { - for (String ackwnoledgeCallback : ackwnoledgeCallbacks) { - if (StringUtils.isNotBlank(ackwnoledgeCallback)) { + if (acknowledgeCallbacks != null) { + for (String acknowledgeCallback : acknowledgeCallbacks) { + if (StringUtils.isNotBlank(acknowledgeCallback)) { ObjectNode node = jsonMapper.createObjectNode(); node.put("eventId", eventId); node.put("status", status); StringEntity requestEntity = new StringEntity(node.toString(), ContentType.APPLICATION_JSON); - try (CloseableHttpClient httpclient = DSpaceHttpClientFactory.getInstance().buildWithoutProxy()) { - HttpPost postMethod = new HttpPost(ackwnoledgeCallback); + try (CloseableHttpClient httpClient = DSpaceHttpClientFactory.getInstance().buildWithoutProxy()) { + HttpPost postMethod = new HttpPost(acknowledgeCallback); postMethod.setEntity(requestEntity); - httpclient.execute(postMethod); + httpClient.execute(postMethod); } catch (IOException e) { log.error(e.getMessage(), e); } From 838e642b2b2f728e599076c01818b701797fbab4 Mon Sep 17 00:00:00 2001 From: Elios Buzo Date: Wed, 23 Apr 2025 17:43:49 +0200 Subject: [PATCH 646/979] [DURACOM-109] Minor fix --- .../external/OpenaireRestConnector.java | 118 +++++++++--------- 1 file changed, 60 insertions(+), 58 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/external/OpenaireRestConnector.java b/dspace-api/src/main/java/org/dspace/external/OpenaireRestConnector.java index b036a98634e0..87af01401ac0 100644 --- a/dspace-api/src/main/java/org/dspace/external/OpenaireRestConnector.java +++ b/dspace-api/src/main/java/org/dspace/external/OpenaireRestConnector.java @@ -27,10 +27,10 @@ import org.apache.http.NameValuePair; import org.apache.http.NoHttpResponseException; import org.apache.http.StatusLine; -import org.apache.http.client.HttpClient; import org.apache.http.client.entity.UrlEncodedFormEntity; import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpPost; +import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.message.BasicNameValuePair; import org.apache.logging.log4j.Logger; import org.dspace.app.client.DSpaceHttpClientFactory; @@ -120,33 +120,34 @@ public OpenaireRestToken grabNewAccessToken() throws IOException { params.add(new BasicNameValuePair("grant_type", "client_credentials")); httpPost.setEntity(new UrlEncodedFormEntity(params, "UTF-8")); - HttpClient httpClient = DSpaceHttpClientFactory.getInstance().build(); - HttpResponse getResponse = httpClient.execute(httpPost); - - JSONObject responseObject = null; - try (InputStream is = getResponse.getEntity().getContent(); - BufferedReader streamReader = new BufferedReader(new InputStreamReader(is, "UTF-8"))) { - String inputStr; - // verify if we have basic json - while ((inputStr = streamReader.readLine()) != null && responseObject == null) { - if (inputStr.startsWith("{") && inputStr.endsWith("}") && inputStr.contains("access_token") - && inputStr.contains("expires_in")) { - try { - responseObject = new JSONObject(inputStr); - } catch (Exception e) { - // Not as valid as I'd hoped, move along - responseObject = null; + try (CloseableHttpClient httpClient = DSpaceHttpClientFactory.getInstance().build()) { + HttpResponse getResponse = httpClient.execute(httpPost); + + JSONObject responseObject = null; + try (InputStream is = getResponse.getEntity().getContent(); + BufferedReader streamReader = new BufferedReader(new InputStreamReader(is, "UTF-8"))) { + String inputStr; + // verify if we have basic json + while ((inputStr = streamReader.readLine()) != null && responseObject == null) { + if (inputStr.startsWith("{") && inputStr.endsWith("}") && inputStr.contains("access_token") + && inputStr.contains("expires_in")) { + try { + responseObject = new JSONObject(inputStr); + } catch (Exception e) { + // Not as valid as I'd hoped, move along + responseObject = null; + } } } } - } - if (responseObject == null || !responseObject.has("access_token") || !responseObject.has("expires_in")) { - throw new IOException("Unable to grab the access token using provided service url, client id and secret"); - } - - return new OpenaireRestToken(responseObject.get("access_token").toString(), - Long.valueOf(responseObject.get("expires_in").toString())); + if (responseObject == null || !responseObject.has("access_token") || !responseObject.has("expires_in")) { + throw new IOException("Unable to grab the access token using provided service url, " + + "client id and secret"); + } + return new OpenaireRestToken(responseObject.get("access_token").toString(), + Long.valueOf(responseObject.get("expires_in").toString())); + } } /** @@ -171,42 +172,43 @@ public InputStream get(String file, String accessToken) { httpGet.addHeader("Authorization", "Bearer " + accessToken); } - HttpClient httpClient = DSpaceHttpClientFactory.getInstance().build(); - getResponse = httpClient.execute(httpGet); - - StatusLine status = getResponse.getStatusLine(); - - // registering errors - switch (status.getStatusCode()) { - case HttpStatus.SC_NOT_FOUND: - // 404 - Not found - case HttpStatus.SC_FORBIDDEN: - // 403 - Invalid Access Token - case 429: - // 429 - Rate limit abuse for unauthenticated user - Header[] limitUsed = getResponse.getHeaders("x-ratelimit-used"); - Header[] limitMax = getResponse.getHeaders("x-ratelimit-limit"); - - if (limitUsed.length > 0) { - String limitMsg = limitUsed[0].getValue(); - if (limitMax.length > 0) { - limitMsg = limitMsg.concat(" of " + limitMax[0].getValue()); + try (CloseableHttpClient httpClient = DSpaceHttpClientFactory.getInstance().build()) { + getResponse = httpClient.execute(httpGet); + + StatusLine status = getResponse.getStatusLine(); + + // registering errors + switch (status.getStatusCode()) { + case HttpStatus.SC_NOT_FOUND: + // 404 - Not found + case HttpStatus.SC_FORBIDDEN: + // 403 - Invalid Access Token + case 429: + // 429 - Rate limit abuse for unauthenticated user + Header[] limitUsed = getResponse.getHeaders("x-ratelimit-used"); + Header[] limitMax = getResponse.getHeaders("x-ratelimit-limit"); + + if (limitUsed.length > 0) { + String limitMsg = limitUsed[0].getValue(); + if (limitMax.length > 0) { + limitMsg = limitMsg.concat(" of " + limitMax[0].getValue()); + } + getGotError(new NoHttpResponseException(status.getReasonPhrase() + " with usage limit " + + limitMsg), + url + '/' + file); + } else { + // 429 - Rate limit abuse + getGotError(new NoHttpResponseException(status.getReasonPhrase()), url + '/' + file); } - getGotError( - new NoHttpResponseException(status.getReasonPhrase() + " with usage limit " + limitMsg), - url + '/' + file); - } else { - // 429 - Rate limit abuse - getGotError(new NoHttpResponseException(status.getReasonPhrase()), url + '/' + file); - } - break; - default: - // 200 or other - break; - } + break; + default: + // 200 or other + break; + } - // do not close this httpClient - result = getResponse.getEntity().getContent(); + // do not close this httpClient + result = getResponse.getEntity().getContent(); + } } catch (MalformedURLException e1) { getGotError(e1, url + '/' + file); } catch (Exception e) { From 1bf06b78842ccde45ef0e0ffebe0e66cd039fe2e Mon Sep 17 00:00:00 2001 From: Stefano Maffei Date: Mon, 28 Apr 2025 08:51:06 +0200 Subject: [PATCH 647/979] [DURACOM-109] fix typo and correct logic for ORCID connector --- dspace-api/pom.xml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/dspace-api/pom.xml b/dspace-api/pom.xml index 0946c9800eaf..02ca5dfc7778 100644 --- a/dspace-api/pom.xml +++ b/dspace-api/pom.xml @@ -512,10 +512,6 @@ org.apache.pdfbox pdfbox - - org.apache.pdfbox - fontbox - com.ibm.icu icu4j From 644d15f8811555a2c4598ef1f0cf12f9fefd4d3a Mon Sep 17 00:00:00 2001 From: Stefano Maffei Date: Mon, 28 Apr 2025 10:50:32 +0200 Subject: [PATCH 648/979] [DURACOM-109] Orcid connector fix and improvement --- .../provider/impl/OrcidV3AuthorDataProvider.java | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/external/provider/impl/OrcidV3AuthorDataProvider.java b/dspace-api/src/main/java/org/dspace/external/provider/impl/OrcidV3AuthorDataProvider.java index dfbd07a83a02..a9e10f92948d 100644 --- a/dspace-api/src/main/java/org/dspace/external/provider/impl/OrcidV3AuthorDataProvider.java +++ b/dspace-api/src/main/java/org/dspace/external/provider/impl/OrcidV3AuthorDataProvider.java @@ -169,13 +169,7 @@ public Person getBio(String id) { } initializeAccessToken(); InputStream bioDocument = orcidRestConnector.get(id + ((id.endsWith("/person")) ? "" : "/person"), accessToken); - Person person = converter.convertSinglePerson(bioDocument); - try { - bioDocument.close(); - } catch (IOException e) { - log.error(e.getMessage(), e); - } - return person; + return converter.convertSinglePerson(bioDocument); } /** @@ -220,11 +214,6 @@ public List searchExternalDataObjects(String query, int star } } } - try { - bioDocument.close(); - } catch (IOException e) { - log.error(e.getMessage(), e); - } return bios.stream().map(bio -> convertToExternalDataObject(bio)).collect(Collectors.toList()); } From ca671b2aa01c2b70a212ee56c7a37f69b4c097df Mon Sep 17 00:00:00 2001 From: Stefano Maffei Date: Tue, 29 Apr 2025 08:52:36 +0200 Subject: [PATCH 649/979] [DURACOM-109] added checkstyle rules to forbid usage of HttpClientBuilder.create() --- checkstyle-suppressions.xml | 1 + checkstyle.xml | 17 +++++++++++++++++ 2 files changed, 18 insertions(+) diff --git a/checkstyle-suppressions.xml b/checkstyle-suppressions.xml index 77e27b8768ac..46bd9ca80d62 100644 --- a/checkstyle-suppressions.xml +++ b/checkstyle-suppressions.xml @@ -7,4 +7,5 @@ + diff --git a/checkstyle.xml b/checkstyle.xml index a33fc4831950..36d2b15bd89e 100644 --- a/checkstyle.xml +++ b/checkstyle.xml @@ -136,5 +136,22 @@ For more information on CheckStyle configurations below, see: http://checkstyle. + + + + + + + + + + + + + + + + + From 480a919754ba3397b15008b0bcbfd7c26b1c8d97 Mon Sep 17 00:00:00 2001 From: Stefano Maffei Date: Tue, 29 Apr 2025 09:07:25 +0200 Subject: [PATCH 650/979] [DURACOM-109] fix TruncatedChunkException error --- .../main/java/org/dspace/external/OrcidRestConnector.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/dspace-api/src/main/java/org/dspace/external/OrcidRestConnector.java b/dspace-api/src/main/java/org/dspace/external/OrcidRestConnector.java index f2c6246f4d06..aa16af7a524d 100644 --- a/dspace-api/src/main/java/org/dspace/external/OrcidRestConnector.java +++ b/dspace-api/src/main/java/org/dspace/external/OrcidRestConnector.java @@ -7,6 +7,7 @@ */ package org.dspace.external; +import java.io.ByteArrayInputStream; import java.io.InputStream; import java.nio.charset.StandardCharsets; import java.util.Scanner; @@ -51,7 +52,11 @@ public InputStream get(String path, String accessToken) { } try (CloseableHttpClient httpClient = DSpaceHttpClientFactory.getInstance().build()) { getResponse = httpClient.execute(httpGet); - result = getResponse.getEntity().getContent(); + try (InputStream responseStream = getResponse.getEntity().getContent()) { + // Read all the content of the response stream into a byte array to prevent TruncatedChunkException + byte[] content = responseStream.readAllBytes(); + result = new ByteArrayInputStream(content); + } } catch (Exception e) { getGotError(e, fullPath); } From 2c2e23f8bcf0b4ed527716def85d1dfd050823f6 Mon Sep 17 00:00:00 2001 From: Elios Buzo Date: Tue, 6 May 2025 16:43:23 +0200 Subject: [PATCH 651/979] [DURACOM-109] Fixed conflicts --- .../org/dspace/app/sherpa/SHERPAService.java | 120 +++++++++--------- 1 file changed, 61 insertions(+), 59 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/app/sherpa/SHERPAService.java b/dspace-api/src/main/java/org/dspace/app/sherpa/SHERPAService.java index 6f26d0cc2d27..1fec20f51fee 100644 --- a/dspace-api/src/main/java/org/dspace/app/sherpa/SHERPAService.java +++ b/dspace-api/src/main/java/org/dspace/app/sherpa/SHERPAService.java @@ -63,7 +63,7 @@ public class SHERPAService { @SuppressWarnings("unused") @PostConstruct private void init() { - // Get endoint and API key from configuration + // Get endpoint and API key from configuration endpoint = configurationService.getProperty("sherpa.romeo.url", "https://v2.sherpa.ac.uk/cgi/retrieve"); apiKey = configurationService.getProperty("sherpa.romeo.apikey"); @@ -117,46 +117,47 @@ public SHERPAPublisherResponse performPublisherRequest(String type, String field timeout, sleepBetweenTimeouts)); - try (CloseableHttpClient client = DSpaceHttpClientFactory.getInstance().build()) { + try (CloseableHttpClient client = DSpaceHttpClientFactory.getInstance().buildWithoutAutomaticRetries(5)) { Thread.sleep(sleepBetweenTimeouts); // Construct a default HTTP method (first result) method = constructHttpGet(type, field, predicate, value, start, limit); // Execute the method - CloseableHttpResponse response = client.execute(method); - int statusCode = response.getStatusLine().getStatusCode(); + try (CloseableHttpResponse response = client.execute(method)) { + int statusCode = response.getStatusLine().getStatusCode(); - log.debug(response.getStatusLine().getStatusCode() + ": " - + response.getStatusLine().getReasonPhrase()); + log.debug(response.getStatusLine().getStatusCode() + ": " + + response.getStatusLine().getReasonPhrase()); - if (statusCode != HttpStatus.SC_OK) { - sherpaResponse = new SHERPAPublisherResponse("SHERPA/RoMEO return not OK status: " - + statusCode); - String errorBody = IOUtils.toString(response.getEntity().getContent(), StandardCharsets.UTF_8); - log.error("Error from SHERPA HTTP request: " + errorBody); - } + if (statusCode != HttpStatus.SC_OK) { + sherpaResponse = new SHERPAPublisherResponse("SHERPA/RoMEO return not OK status: " + + statusCode); + String errorBody = IOUtils.toString(response.getEntity().getContent(), StandardCharsets.UTF_8); + log.error("Error from SHERPA HTTP request: " + errorBody); + } - HttpEntity responseBody = response.getEntity(); - - // If the response body is valid, pass to SHERPAResponse for parsing as JSON - if (null != responseBody) { - log.debug("Non-null SHERPA resonse received for query of " + value); - InputStream content = null; - try { - content = responseBody.getContent(); - sherpaResponse = - new SHERPAPublisherResponse(content, SHERPAPublisherResponse.SHERPAFormat.JSON); - } catch (IOException e) { - log.error("Encountered exception while contacting SHERPA/RoMEO: " + e.getMessage(), e); - } finally { - if (content != null) { - content.close(); + HttpEntity responseBody = response.getEntity(); + + // If the response body is valid, pass to SHERPAResponse for parsing as JSON + if (null != responseBody) { + log.debug("Non-null SHERPA response received for query of " + value); + InputStream content = null; + try { + content = responseBody.getContent(); + sherpaResponse = + new SHERPAPublisherResponse(content, SHERPAPublisherResponse.SHERPAFormat.JSON); + } catch (IOException e) { + log.error("Encountered exception while contacting SHERPA/RoMEO: " + e.getMessage(), e); + } finally { + if (content != null) { + content.close(); + } } + } else { + log.debug("Empty SHERPA response body for query on " + value); + sherpaResponse = new SHERPAPublisherResponse("SHERPA/RoMEO returned no response"); } - } else { - log.debug("Empty SHERPA response body for query on " + value); - sherpaResponse = new SHERPAPublisherResponse("SHERPA/RoMEO returned no response"); } } catch (URISyntaxException e) { String errorMessage = "Error building SHERPA v2 API URI: " + e.getMessage(); @@ -220,45 +221,46 @@ public SHERPAResponse performRequest(String type, String field, String predicate timeout, sleepBetweenTimeouts)); - try (CloseableHttpClient client = DSpaceHttpClientFactory.getInstance().build()) { + try (CloseableHttpClient client = DSpaceHttpClientFactory.getInstance().buildWithoutAutomaticRetries(5)) { Thread.sleep(sleepBetweenTimeouts); // Construct a default HTTP method (first result) method = constructHttpGet(type, field, predicate, value, start, limit); // Execute the method - CloseableHttpResponse response = client.execute(method); - int statusCode = response.getStatusLine().getStatusCode(); + try (CloseableHttpResponse response = client.execute(method)) { + int statusCode = response.getStatusLine().getStatusCode(); - log.debug(response.getStatusLine().getStatusCode() + ": " - + response.getStatusLine().getReasonPhrase()); + log.debug(response.getStatusLine().getStatusCode() + ": " + + response.getStatusLine().getReasonPhrase()); - if (statusCode != HttpStatus.SC_OK) { - sherpaResponse = new SHERPAResponse("SHERPA/RoMEO return not OK status: " - + statusCode); - String errorBody = IOUtils.toString(response.getEntity().getContent(), StandardCharsets.UTF_8); - log.error("Error from SHERPA HTTP request: " + errorBody); - } + if (statusCode != HttpStatus.SC_OK) { + sherpaResponse = new SHERPAResponse("SHERPA/RoMEO return not OK status: " + + statusCode); + String errorBody = IOUtils.toString(response.getEntity().getContent(), StandardCharsets.UTF_8); + log.error("Error from SHERPA HTTP request: " + errorBody); + } - HttpEntity responseBody = response.getEntity(); - - // If the response body is valid, pass to SHERPAResponse for parsing as JSON - if (null != responseBody) { - log.debug("Non-null SHERPA resonse received for query of " + value); - InputStream content = null; - try { - content = responseBody.getContent(); - sherpaResponse = new SHERPAResponse(content, SHERPAResponse.SHERPAFormat.JSON); - } catch (IOException e) { - log.error("Encountered exception while contacting SHERPA/RoMEO: " + e.getMessage(), e); - } finally { - if (content != null) { - content.close(); + HttpEntity responseBody = response.getEntity(); + + // If the response body is valid, pass to SHERPAResponse for parsing as JSON + if (null != responseBody) { + log.debug("Non-null SHERPA response received for query of " + value); + InputStream content = null; + try { + content = responseBody.getContent(); + sherpaResponse = new SHERPAResponse(content, SHERPAResponse.SHERPAFormat.JSON); + } catch (IOException e) { + log.error("Encountered exception while contacting SHERPA/RoMEO: " + e.getMessage(), e); + } finally { + if (content != null) { + content.close(); + } } + } else { + log.debug("Empty SHERPA response body for query on " + value); + sherpaResponse = new SHERPAResponse("SHERPA/RoMEO returned no response"); } - } else { - log.debug("Empty SHERPA response body for query on " + value); - sherpaResponse = new SHERPAResponse("SHERPA/RoMEO returned no response"); } } catch (URISyntaxException e) { String errorMessage = "Error building SHERPA v2 API URI: " + e.getMessage(); @@ -268,7 +270,7 @@ public SHERPAResponse performRequest(String type, String field, String predicate String errorMessage = "Encountered exception while contacting SHERPA/RoMEO: " + e.getMessage(); log.error(errorMessage, e); sherpaResponse = new SHERPAResponse(errorMessage); - } catch (InterruptedException e) { + } catch (InterruptedException e) { String errorMessage = "Encountered exception while sleeping thread: " + e.getMessage(); log.error(errorMessage, e); sherpaResponse = new SHERPAResponse(errorMessage); From c2c41e65f86dab0a9d4f4128e16336fcd7b9c215 Mon Sep 17 00:00:00 2001 From: Elios Buzo Date: Wed, 16 Apr 2025 14:03:01 +0200 Subject: [PATCH 652/979] [DURACOM-109] Configured proxy settings for all clients --- .../app/client/DSpaceHttpClientFactory.java | 152 +++++++++++ .../app/client/DSpaceProxyRoutePlanner.java | 73 +++++ .../org/dspace/app/sherpa/SHERPAService.java | 8 +- .../dspace/app/util/WebAppServiceImpl.java | 4 +- .../oidc/impl/OidcClientImpl.java | 4 +- .../ctask/general/MetadataWebService.java | 4 +- .../ctask/general/MicrosoftTranslator.java | 4 +- .../dspace/eperson/CaptchaServiceImpl.java | 4 +- .../external/OpenAIRERestConnector.java | 6 +- .../dspace/external/OrcidRestConnector.java | 4 +- .../CCLicenseConnectorServiceImpl.java | 9 +- .../dspace/orcid/client/OrcidClientImpl.java | 15 +- .../impl/HttpConnectionPoolService.java | 4 +- .../statistics/SolrLoggerServiceImpl.java | 6 +- .../export/service/OpenUrlServiceImpl.java | 6 +- .../client/DSpaceHttpClientFactoryTest.java | 256 ++++++++++++++++++ dspace/config/spring/api/core-services.xml | 3 + 17 files changed, 519 insertions(+), 43 deletions(-) create mode 100644 dspace-api/src/main/java/org/dspace/app/client/DSpaceHttpClientFactory.java create mode 100644 dspace-api/src/main/java/org/dspace/app/client/DSpaceProxyRoutePlanner.java create mode 100644 dspace-api/src/test/java/org/dspace/app/client/DSpaceHttpClientFactoryTest.java diff --git a/dspace-api/src/main/java/org/dspace/app/client/DSpaceHttpClientFactory.java b/dspace-api/src/main/java/org/dspace/app/client/DSpaceHttpClientFactory.java new file mode 100644 index 000000000000..999f13c1b387 --- /dev/null +++ b/dspace-api/src/main/java/org/dspace/app/client/DSpaceHttpClientFactory.java @@ -0,0 +1,152 @@ +/** + * The contents of this file are subject to the license and copyright + * detailed in the LICENSE and NOTICE files at the root of the source + * tree and available online at + * + * http://www.dspace.org/license/ + */ +package org.dspace.app.client; + +import static org.apache.commons.collections4.ListUtils.emptyIfNull; + +import java.util.List; + +import org.apache.http.HttpRequestInterceptor; +import org.apache.http.HttpResponseInterceptor; +import org.apache.http.client.HttpClient; +import org.apache.http.client.config.RequestConfig; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClientBuilder; +import org.dspace.services.ConfigurationService; +import org.dspace.utils.DSpace; +import org.springframework.beans.factory.annotation.Autowired; + +/** + * Factory of {@link HttpClient} with common configurations. + * + * @author Luca Giamminonni (luca.giamminonni at 4science.it) + * + */ +public class DSpaceHttpClientFactory { + + @Autowired + private ConfigurationService configurationService; + + @Autowired + private DSpaceProxyRoutePlanner proxyRoutePlanner; + + @Autowired(required = false) + private List requestInterceptors; + + @Autowired(required = false) + private List responseInterceptors; + + /** + * Get an instance of {@link DSpaceHttpClientFactory} from the Spring context. + * @return the bean instance + */ + public static DSpaceHttpClientFactory getInstance() { + return new DSpace().getSingletonService(DSpaceHttpClientFactory.class); + } + + /** + * Build an instance of {@link HttpClient} setting the proxy if configured. + * + * @return the client + */ + public CloseableHttpClient build() { + return build(HttpClientBuilder.create(), true); + } + + /** + * return a Builder if an instance of {@link HttpClient} pre-setting the proxy if configured. + * + * @return the client + */ + public HttpClientBuilder builder(boolean setProxy) { + HttpClientBuilder clientBuilder = HttpClientBuilder.create(); + if (setProxy) { + clientBuilder.setRoutePlanner(proxyRoutePlanner); + } + getRequestInterceptors().forEach(clientBuilder::addInterceptorLast); + getResponseInterceptors().forEach(clientBuilder::addInterceptorLast); + return clientBuilder; + } + + /** + * Build an instance of {@link HttpClient} without setting the proxy, even if + * configured. + * + * @return the client + */ + public CloseableHttpClient buildWithoutProxy() { + return build(HttpClientBuilder.create(), false); + } + + /** + * Build an instance of {@link HttpClient} setting the proxy if configured, + * disabling automatic retries and setting the maximum total connection. + * + * @param maxConnTotal the maximum total connection value + * @return the client + */ + public CloseableHttpClient buildWithoutAutomaticRetries(int maxConnTotal) { + HttpClientBuilder clientBuilder = HttpClientBuilder.create() + .disableAutomaticRetries() + .setMaxConnTotal(5); + return build(clientBuilder, true); + } + + /** + * Build an instance of {@link HttpClient} setting the proxy if configured with + * the given request configuration. + * @param requestConfig the request configuration + * @return the client + */ + public CloseableHttpClient buildWithRequestConfig(RequestConfig requestConfig) { + HttpClientBuilder httpClientBuilder = HttpClientBuilder.create() + .setDefaultRequestConfig(requestConfig); + return build(httpClientBuilder, true); + } + + private CloseableHttpClient build(HttpClientBuilder clientBuilder, boolean setProxy) { + if (setProxy) { + clientBuilder.setRoutePlanner(proxyRoutePlanner); + } + getRequestInterceptors().forEach(clientBuilder::addInterceptorLast); + getResponseInterceptors().forEach(clientBuilder::addInterceptorLast); + return clientBuilder.build(); + } + + public ConfigurationService getConfigurationService() { + return configurationService; + } + + public void setConfigurationService(ConfigurationService configurationService) { + this.configurationService = configurationService; + } + + public List getRequestInterceptors() { + return emptyIfNull(requestInterceptors); + } + + public void setRequestInterceptors(List requestInterceptors) { + this.requestInterceptors = requestInterceptors; + } + + public List getResponseInterceptors() { + return emptyIfNull(responseInterceptors); + } + + public void setResponseInterceptors(List responseInterceptors) { + this.responseInterceptors = responseInterceptors; + } + + public DSpaceProxyRoutePlanner getProxyRoutePlanner() { + return proxyRoutePlanner; + } + + public void setProxyRoutePlanner(DSpaceProxyRoutePlanner proxyRoutePlanner) { + this.proxyRoutePlanner = proxyRoutePlanner; + } +} \ No newline at end of file diff --git a/dspace-api/src/main/java/org/dspace/app/client/DSpaceProxyRoutePlanner.java b/dspace-api/src/main/java/org/dspace/app/client/DSpaceProxyRoutePlanner.java new file mode 100644 index 000000000000..1df7fa4a2985 --- /dev/null +++ b/dspace-api/src/main/java/org/dspace/app/client/DSpaceProxyRoutePlanner.java @@ -0,0 +1,73 @@ +/** + * The contents of this file are subject to the license and copyright + * detailed in the LICENSE and NOTICE files at the root of the source + * tree and available online at + * + * http://www.dspace.org/license/ + */ +package org.dspace.app.client; + +import java.util.Arrays; + +import org.apache.commons.lang3.ArrayUtils; +import org.apache.commons.lang3.StringUtils; +import org.apache.http.HttpException; +import org.apache.http.HttpHost; +import org.apache.http.HttpRequest; +import org.apache.http.impl.conn.DefaultRoutePlanner; +import org.apache.http.protocol.HttpContext; +import org.dspace.services.ConfigurationService; + +/** + * Extension of {@link DefaultRoutePlanner} that determine the proxy based on + * the configuration service, ignoring configured hosts. + * + * @author Luca Giamminonni (luca.giamminonni at 4science.it) + * + */ +public class DSpaceProxyRoutePlanner extends DefaultRoutePlanner { + + private ConfigurationService configurationService; + + public DSpaceProxyRoutePlanner(ConfigurationService configurationService) { + super(null); + this.configurationService = configurationService; + } + + @Override + protected HttpHost determineProxy(HttpHost target, HttpRequest request, HttpContext context) throws HttpException { + if (isTargetHostConfiguredToBeIgnored(target)) { + return null; + } + String proxyHost = configurationService.getProperty("http.proxy.host"); + String proxyPort = configurationService.getProperty("http.proxy.port"); + if (StringUtils.isAnyBlank(proxyHost, proxyPort)) { + return null; + } + try { + return new HttpHost(proxyHost, Integer.parseInt(proxyPort), "http"); + } catch (NumberFormatException e) { + throw new RuntimeException("Invalid proxy port configuration: " + proxyPort); + } + } + + private boolean isTargetHostConfiguredToBeIgnored(HttpHost target) { + String[] hostsToIgnore = configurationService.getArrayProperty("http.proxy.hosts-to-ignore"); + if (ArrayUtils.isEmpty(hostsToIgnore)) { + return false; + } + return Arrays.stream(hostsToIgnore) + .anyMatch(host -> matchesHost(host, target.getHostName())); + } + + private boolean matchesHost(String hostPattern, String hostName) { + if (hostName.equals(hostPattern)) { + return true; + } else if (hostPattern.startsWith("*")) { + return hostName.endsWith(StringUtils.removeStart(hostPattern, "*")); + } else if (hostPattern.endsWith("*")) { + return hostName.startsWith(StringUtils.removeEnd(hostPattern, "*")); + } + return false; + } +} \ No newline at end of file diff --git a/dspace-api/src/main/java/org/dspace/app/sherpa/SHERPAService.java b/dspace-api/src/main/java/org/dspace/app/sherpa/SHERPAService.java index ead725e842c4..73ec0cc58937 100644 --- a/dspace-api/src/main/java/org/dspace/app/sherpa/SHERPAService.java +++ b/dspace-api/src/main/java/org/dspace/app/sherpa/SHERPAService.java @@ -23,9 +23,9 @@ import org.apache.http.client.methods.HttpGet; import org.apache.http.client.utils.URIBuilder; import org.apache.http.impl.client.CloseableHttpClient; -import org.apache.http.impl.client.HttpClientBuilder; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import org.dspace.app.client.DSpaceHttpClientFactory; import org.dspace.app.sherpa.v2.SHERPAPublisherResponse; import org.dspace.app.sherpa.v2.SHERPAResponse; import org.dspace.app.sherpa.v2.SHERPAUtils; @@ -63,13 +63,9 @@ public class SHERPAService { * Create a new HTTP builder with sensible defaults in constructor */ public SHERPAService() { - HttpClientBuilder builder = HttpClientBuilder.create(); // httpclient 4.3+ doesn't appear to have any sensible defaults any more. Setting conservative defaults as // not to hammer the SHERPA service too much. - client = builder - .disableAutomaticRetries() - .setMaxConnTotal(5) - .build(); + client = DSpaceHttpClientFactory.getInstance().buildWithoutAutomaticRetries(5); } /** diff --git a/dspace-api/src/main/java/org/dspace/app/util/WebAppServiceImpl.java b/dspace-api/src/main/java/org/dspace/app/util/WebAppServiceImpl.java index 8dcd78c8823b..064a9fdc3e9b 100644 --- a/dspace-api/src/main/java/org/dspace/app/util/WebAppServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/app/util/WebAppServiceImpl.java @@ -17,8 +17,8 @@ import org.apache.http.HttpStatus; import org.apache.http.client.methods.HttpHead; import org.apache.http.impl.client.CloseableHttpClient; -import org.apache.http.impl.client.HttpClientBuilder; import org.apache.logging.log4j.Logger; +import org.dspace.app.client.DSpaceHttpClientFactory; import org.dspace.app.util.dao.WebAppDAO; import org.dspace.app.util.service.WebAppService; import org.dspace.core.Context; @@ -77,7 +77,7 @@ public List getApps() { for (WebApp app : webApps) { method = new HttpHead(app.getUrl()); int status; - try (CloseableHttpClient client = HttpClientBuilder.create().build()) { + try (CloseableHttpClient client = DSpaceHttpClientFactory.getInstance().build()) { HttpResponse response = client.execute(method); status = response.getStatusLine().getStatusCode(); } diff --git a/dspace-api/src/main/java/org/dspace/authenticate/oidc/impl/OidcClientImpl.java b/dspace-api/src/main/java/org/dspace/authenticate/oidc/impl/OidcClientImpl.java index ddab01e8cb5d..35856d3756d9 100644 --- a/dspace-api/src/main/java/org/dspace/authenticate/oidc/impl/OidcClientImpl.java +++ b/dspace-api/src/main/java/org/dspace/authenticate/oidc/impl/OidcClientImpl.java @@ -26,8 +26,8 @@ import org.apache.http.client.entity.UrlEncodedFormEntity; import org.apache.http.client.methods.HttpUriRequest; import org.apache.http.client.methods.RequestBuilder; -import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.message.BasicNameValuePair; +import org.dspace.app.client.DSpaceHttpClientFactory; import org.dspace.authenticate.oidc.OidcClient; import org.dspace.authenticate.oidc.OidcClientException; import org.dspace.authenticate.oidc.model.OidcTokenResponseDTO; @@ -84,7 +84,7 @@ public Map getUserInfo(String accessToken) throws OidcClientExce private T executeAndParseJson(HttpUriRequest httpUriRequest, Class clazz) { - HttpClient client = HttpClientBuilder.create().build(); + HttpClient client = DSpaceHttpClientFactory.getInstance().build(); return executeAndReturns(() -> { diff --git a/dspace-api/src/main/java/org/dspace/ctask/general/MetadataWebService.java b/dspace-api/src/main/java/org/dspace/ctask/general/MetadataWebService.java index 5891fa017cb0..da7588be6c6f 100644 --- a/dspace-api/src/main/java/org/dspace/ctask/general/MetadataWebService.java +++ b/dspace-api/src/main/java/org/dspace/ctask/general/MetadataWebService.java @@ -34,9 +34,9 @@ import org.apache.http.HttpStatus; import org.apache.http.client.methods.HttpGet; import org.apache.http.impl.client.CloseableHttpClient; -import org.apache.http.impl.client.HttpClientBuilder; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import org.dspace.app.client.DSpaceHttpClientFactory; import org.dspace.authorize.AuthorizeException; import org.dspace.content.DSpaceObject; import org.dspace.content.Item; @@ -257,7 +257,7 @@ public int perform(DSpaceObject dso) throws IOException { protected int callService(String value, Item item, StringBuilder resultSb) throws IOException { String callUrl = urlTemplate.replaceAll("\\{" + templateParam + "\\}", value); - CloseableHttpClient client = HttpClientBuilder.create().build(); + CloseableHttpClient client = DSpaceHttpClientFactory.getInstance().build(); HttpGet req = new HttpGet(callUrl); for (Map.Entry entry : headers.entrySet()) { req.addHeader(entry.getKey(), entry.getValue()); diff --git a/dspace-api/src/main/java/org/dspace/ctask/general/MicrosoftTranslator.java b/dspace-api/src/main/java/org/dspace/ctask/general/MicrosoftTranslator.java index 49c0c36a5917..0d682d9406f4 100644 --- a/dspace-api/src/main/java/org/dspace/ctask/general/MicrosoftTranslator.java +++ b/dspace-api/src/main/java/org/dspace/ctask/general/MicrosoftTranslator.java @@ -15,9 +15,9 @@ import org.apache.http.HttpResponse; import org.apache.http.client.methods.HttpGet; import org.apache.http.impl.client.CloseableHttpClient; -import org.apache.http.impl.client.HttpClientBuilder; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import org.dspace.app.client.DSpaceHttpClientFactory; import org.dspace.services.ConfigurationService; import org.dspace.services.factory.DSpaceServicesFactory; @@ -60,7 +60,7 @@ protected String translateText(String from, String to, String text) throws IOExc String url = baseUrl + "?appId=" + apiKey; url += "&to=" + to + "&from=" + from + "&text=" + text; - try (CloseableHttpClient client = HttpClientBuilder.create().build()) { + try (CloseableHttpClient client = DSpaceHttpClientFactory.getInstance().build()) { HttpGet hm = new HttpGet(url); HttpResponse httpResponse = client.execute(hm); log.debug("Response code from API call is " + httpResponse); diff --git a/dspace-api/src/main/java/org/dspace/eperson/CaptchaServiceImpl.java b/dspace-api/src/main/java/org/dspace/eperson/CaptchaServiceImpl.java index 0ab66aea5c2e..e10de57e47a6 100644 --- a/dspace-api/src/main/java/org/dspace/eperson/CaptchaServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/eperson/CaptchaServiceImpl.java @@ -22,10 +22,10 @@ import org.apache.http.client.HttpClient; import org.apache.http.client.entity.UrlEncodedFormEntity; import org.apache.http.client.methods.HttpPost; -import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.message.BasicNameValuePair; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import org.dspace.app.client.DSpaceHttpClientFactory; import org.dspace.eperson.service.CaptchaService; import org.dspace.services.ConfigurationService; import org.springframework.beans.factory.annotation.Autowired; @@ -82,7 +82,7 @@ public void processResponse(String response, String action) throws InvalidReCapt throw new RuntimeException(e.getMessage(), e); } - HttpClient httpClient = HttpClientBuilder.create().build(); + HttpClient httpClient = DSpaceHttpClientFactory.getInstance().build(); HttpResponse httpResponse; GoogleCaptchaResponse googleResponse; final ObjectMapper objectMapper = new ObjectMapper(); diff --git a/dspace-api/src/main/java/org/dspace/external/OpenAIRERestConnector.java b/dspace-api/src/main/java/org/dspace/external/OpenAIRERestConnector.java index b0aa4aba13a9..06a4b8361436 100644 --- a/dspace-api/src/main/java/org/dspace/external/OpenAIRERestConnector.java +++ b/dspace-api/src/main/java/org/dspace/external/OpenAIRERestConnector.java @@ -32,9 +32,9 @@ import org.apache.http.client.entity.UrlEncodedFormEntity; import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpPost; -import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.message.BasicNameValuePair; import org.apache.logging.log4j.Logger; +import org.dspace.app.client.DSpaceHttpClientFactory; import org.dspace.app.util.Util; import org.json.JSONObject; import org.springframework.beans.factory.annotation.Autowired; @@ -121,7 +121,7 @@ public OpenAIRERestToken grabNewAccessToken() throws IOException { params.add(new BasicNameValuePair("grant_type", "client_credentials")); httpPost.setEntity(new UrlEncodedFormEntity(params, "UTF-8")); - HttpClient httpClient = HttpClientBuilder.create().build(); + HttpClient httpClient = DSpaceHttpClientFactory.getInstance().build(); HttpResponse getResponse = httpClient.execute(httpPost); JSONObject responseObject = null; @@ -172,7 +172,7 @@ public InputStream get(String file, String accessToken) { httpGet.addHeader("Authorization", "Bearer " + accessToken); } - HttpClient httpClient = HttpClientBuilder.create().build(); + HttpClient httpClient = DSpaceHttpClientFactory.getInstance().build(); getResponse = httpClient.execute(httpGet); StatusLine status = getResponse.getStatusLine(); diff --git a/dspace-api/src/main/java/org/dspace/external/OrcidRestConnector.java b/dspace-api/src/main/java/org/dspace/external/OrcidRestConnector.java index d45be7e6b56e..3ccbd257e285 100644 --- a/dspace-api/src/main/java/org/dspace/external/OrcidRestConnector.java +++ b/dspace-api/src/main/java/org/dspace/external/OrcidRestConnector.java @@ -15,9 +15,9 @@ import org.apache.http.HttpResponse; import org.apache.http.client.HttpClient; import org.apache.http.client.methods.HttpGet; -import org.apache.http.impl.client.HttpClientBuilder; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import org.dspace.app.client.DSpaceHttpClientFactory; /** * @author Antoine Snyers (antoine at atmire.com) @@ -50,7 +50,7 @@ public InputStream get(String path, String accessToken) { httpGet.addHeader("Authorization","Bearer " + accessToken); } try { - HttpClient httpClient = HttpClientBuilder.create().build(); + HttpClient httpClient = DSpaceHttpClientFactory.getInstance().build(); getResponse = httpClient.execute(httpGet); //do not close this httpClient result = getResponse.getEntity().getContent(); diff --git a/dspace-api/src/main/java/org/dspace/license/CCLicenseConnectorServiceImpl.java b/dspace-api/src/main/java/org/dspace/license/CCLicenseConnectorServiceImpl.java index cdecadba5242..524ff04facec 100644 --- a/dspace-api/src/main/java/org/dspace/license/CCLicenseConnectorServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/license/CCLicenseConnectorServiceImpl.java @@ -28,9 +28,9 @@ import org.apache.http.client.methods.HttpPost; import org.apache.http.entity.mime.MultipartEntityBuilder; import org.apache.http.impl.client.CloseableHttpClient; -import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.util.EntityUtils; import org.apache.logging.log4j.Logger; +import org.dspace.app.client.DSpaceHttpClientFactory; import org.dspace.services.ConfigurationService; import org.jdom2.Attribute; import org.jdom2.Document; @@ -70,12 +70,7 @@ public class CCLicenseConnectorServiceImpl implements CCLicenseConnectorService, @Override public void afterPropertiesSet() throws Exception { - HttpClientBuilder builder = HttpClientBuilder.create(); - - client = builder - .disableAutomaticRetries() - .setMaxConnTotal(5) - .build(); + client = DSpaceHttpClientFactory.getInstance().buildWithoutAutomaticRetries(5); // disallow DTD parsing to ensure no XXE attacks can occur. // See https://cheatsheetseries.owasp.org/cheatsheets/XML_External_Entity_Prevention_Cheat_Sheet.html diff --git a/dspace-api/src/main/java/org/dspace/orcid/client/OrcidClientImpl.java b/dspace-api/src/main/java/org/dspace/orcid/client/OrcidClientImpl.java index 1532abe63412..2285bb11c784 100644 --- a/dspace-api/src/main/java/org/dspace/orcid/client/OrcidClientImpl.java +++ b/dspace-api/src/main/java/org/dspace/orcid/client/OrcidClientImpl.java @@ -40,8 +40,8 @@ import org.apache.http.client.methods.HttpUriRequest; import org.apache.http.client.methods.RequestBuilder; import org.apache.http.entity.StringEntity; -import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.message.BasicNameValuePair; +import org.dspace.app.client.DSpaceHttpClientFactory; import org.dspace.orcid.OrcidToken; import org.dspace.orcid.exception.OrcidClientException; import org.dspace.orcid.model.OrcidEntityType; @@ -76,9 +76,12 @@ public class OrcidClientImpl implements OrcidClient { private final ObjectMapper objectMapper; - public OrcidClientImpl(OrcidConfiguration orcidConfiguration) { + private final DSpaceHttpClientFactory httpClientFactory; + + public OrcidClientImpl(OrcidConfiguration orcidConfiguration, DSpaceHttpClientFactory httpClientFactory) { this.orcidConfiguration = orcidConfiguration; this.objectMapper = new ObjectMapper(); + this.httpClientFactory = httpClientFactory; } private static Map, String> initializePathsMap() { @@ -255,7 +258,7 @@ private HttpUriRequest buildDeleteUriRequest(String accessToken, String relative private void executeSuccessful(HttpUriRequest httpUriRequest) { try { - HttpClient client = HttpClientBuilder.create().build(); + HttpClient client = httpClientFactory.build(); HttpResponse response = client.execute(httpUriRequest); if (isNotSuccessfull(response)) { @@ -273,7 +276,7 @@ private void executeSuccessful(HttpUriRequest httpUriRequest) { private T executeAndParseJson(HttpUriRequest httpUriRequest, Class clazz) { - HttpClient client = HttpClientBuilder.create().build(); + HttpClient client = httpClientFactory.build(); return executeAndReturns(() -> { @@ -302,7 +305,7 @@ private T executeAndParseJson(HttpUriRequest httpUriRequest, Class clazz) */ private T executeAndUnmarshall(HttpUriRequest httpUriRequest, boolean handleNotFoundAsNull, Class clazz) { - HttpClient client = HttpClientBuilder.create().build(); + HttpClient client = httpClientFactory.build(); return executeAndReturns(() -> { @@ -322,7 +325,7 @@ private T executeAndUnmarshall(HttpUriRequest httpUriRequest, boolean handle } private OrcidResponse execute(HttpUriRequest httpUriRequest, boolean handleNotFoundAsNull) { - HttpClient client = HttpClientBuilder.create().build(); + HttpClient client = httpClientFactory.build(); return executeAndReturns(() -> { diff --git a/dspace-api/src/main/java/org/dspace/service/impl/HttpConnectionPoolService.java b/dspace-api/src/main/java/org/dspace/service/impl/HttpConnectionPoolService.java index c5f7c46b586e..27f2ad3e412b 100644 --- a/dspace-api/src/main/java/org/dspace/service/impl/HttpConnectionPoolService.java +++ b/dspace-api/src/main/java/org/dspace/service/impl/HttpConnectionPoolService.java @@ -19,11 +19,11 @@ import org.apache.http.conn.ConnectionKeepAliveStrategy; import org.apache.http.conn.HttpClientConnectionManager; import org.apache.http.impl.client.CloseableHttpClient; -import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.apache.http.message.BasicHeaderElementIterator; import org.apache.http.protocol.HTTP; import org.apache.http.protocol.HttpContext; +import org.dspace.app.client.DSpaceHttpClientFactory; import org.dspace.services.ConfigurationService; /** @@ -112,7 +112,7 @@ protected void init() { * @return the client. */ public CloseableHttpClient getClient() { - CloseableHttpClient httpClient = HttpClientBuilder.create() + CloseableHttpClient httpClient = DSpaceHttpClientFactory.getInstance().builder(false).create() .setKeepAliveStrategy(keepAliveStrategy) .setConnectionManager(connManager) .build(); diff --git a/dspace-api/src/main/java/org/dspace/statistics/SolrLoggerServiceImpl.java b/dspace-api/src/main/java/org/dspace/statistics/SolrLoggerServiceImpl.java index 5f976bbfd94b..da9c7eb3bf27 100644 --- a/dspace-api/src/main/java/org/dspace/statistics/SolrLoggerServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/statistics/SolrLoggerServiceImpl.java @@ -51,7 +51,6 @@ import org.apache.http.HttpResponse; import org.apache.http.client.methods.HttpGet; import org.apache.http.impl.client.CloseableHttpClient; -import org.apache.http.impl.client.HttpClientBuilder; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.apache.solr.client.solrj.SolrClient; @@ -81,6 +80,7 @@ import org.apache.solr.common.params.ModifiableSolrParams; import org.apache.solr.common.params.ShardParams; import org.apache.solr.common.util.NamedList; +import org.dspace.app.client.DSpaceHttpClientFactory; import org.dspace.authorize.service.AuthorizeService; import org.dspace.content.Bitstream; import org.dspace.content.Bundle; @@ -1303,7 +1303,7 @@ public void shardSolrIndex() throws IOException, SolrServerException { + "." + i + ".csv"); - try ( CloseableHttpClient hc = HttpClientBuilder.create().build(); ) { + try (CloseableHttpClient hc = DSpaceHttpClientFactory.getInstance().build()) { HttpResponse response = hc.execute(get); csvInputstream = response.getEntity().getContent(); //Write the csv ouput to a file ! @@ -1445,7 +1445,7 @@ public void reindexBitstreamHits(boolean removeDeletedBitstreams) throws Excepti HttpGet get = new HttpGet(solrRequestUrl); List rows; - try ( CloseableHttpClient hc = HttpClientBuilder.create().build(); ) { + try (CloseableHttpClient hc = DSpaceHttpClientFactory.getInstance().build()) { HttpResponse response = hc.execute(get); InputStream csvOutput = response.getEntity().getContent(); Reader csvReader = new InputStreamReader(csvOutput); diff --git a/dspace-api/src/main/java/org/dspace/statistics/export/service/OpenUrlServiceImpl.java b/dspace-api/src/main/java/org/dspace/statistics/export/service/OpenUrlServiceImpl.java index b7a9562fb541..d8f4aa6b7e7b 100644 --- a/dspace-api/src/main/java/org/dspace/statistics/export/service/OpenUrlServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/statistics/export/service/OpenUrlServiceImpl.java @@ -18,9 +18,9 @@ import org.apache.http.client.HttpClient; import org.apache.http.client.config.RequestConfig; import org.apache.http.client.methods.HttpGet; -import org.apache.http.impl.client.HttpClientBuilder; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import org.dspace.app.client.DSpaceHttpClientFactory; import org.dspace.core.Context; import org.dspace.statistics.export.OpenURLTracker; import org.springframework.beans.factory.annotation.Autowired; @@ -75,9 +75,7 @@ protected int getResponseCodeFromUrl(final String urlStr) throws IOException { } protected HttpClient getHttpClient(RequestConfig requestConfig) { - return HttpClientBuilder.create() - .setDefaultRequestConfig(requestConfig) - .build(); + return DSpaceHttpClientFactory.getInstance().buildWithRequestConfig(requestConfig); } protected RequestConfig getHttpClientRequestConfig() { diff --git a/dspace-api/src/test/java/org/dspace/app/client/DSpaceHttpClientFactoryTest.java b/dspace-api/src/test/java/org/dspace/app/client/DSpaceHttpClientFactoryTest.java new file mode 100644 index 000000000000..b518f19ff4d3 --- /dev/null +++ b/dspace-api/src/test/java/org/dspace/app/client/DSpaceHttpClientFactoryTest.java @@ -0,0 +1,256 @@ +/** + * The contents of this file are subject to the license and copyright + * detailed in the LICENSE and NOTICE files at the root of the source + * tree and available online at + * + * http://www.dspace.org/license/ + */ +package org.dspace.app.client; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.arrayWithSize; +import static org.hamcrest.Matchers.instanceOf; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.notNullValue; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoInteractions; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.Mockito.when; + +import java.util.List; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicReference; + +import okhttp3.mockwebserver.MockResponse; +import okhttp3.mockwebserver.MockWebServer; +import okhttp3.mockwebserver.RecordedRequest; +import org.apache.http.HttpRequestInterceptor; +import org.apache.http.HttpResponse; +import org.apache.http.HttpResponseInterceptor; +import org.apache.http.client.config.RequestConfig; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.conn.routing.HttpRoute; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.protocol.HttpContext; +import org.dspace.services.ConfigurationService; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnitRunner; + +/** + * Unit tests for {@link DSpaceHttpClientFactory}. + * + * @author Luca Giamminonni (luca.giamminonni at 4science.it) + * + */ +@RunWith(MockitoJUnitRunner.class) +public class DSpaceHttpClientFactoryTest { + + @InjectMocks + private DSpaceHttpClientFactory httpClientFactory; + + @Mock + private ConfigurationService configurationService; + + private MockWebServer mockProxy; + + private MockWebServer mockServer; + + @Before + public void init() { + this.httpClientFactory.setProxyRoutePlanner(new DSpaceProxyRoutePlanner(configurationService)); + this.mockProxy = new MockWebServer(); + this.mockProxy.enqueue(new MockResponse().setResponseCode(200).addHeader("From", "Proxy")); + this.mockServer = new MockWebServer(); + this.mockServer.enqueue(new MockResponse().setResponseCode(200).addHeader("From", "Server")); + } + + @Test + public void testBuildWithProxyConfigured() throws Exception { + setHttpProxyOnConfigurationService(); + CloseableHttpClient httpClient = httpClientFactory.build(); + assertThat(mockProxy.getRequestCount(), is(0)); + assertThat(mockServer.getRequestCount(), is(0)); + CloseableHttpResponse httpResponse = httpClient.execute(new HttpGet(mockServer.url("").toString())); + assertThat(httpResponse.getHeaders("From"), arrayWithSize(1)); + assertThat(httpResponse.getHeaders("From")[0].getValue(), is("Proxy")); + assertThat(mockProxy.getRequestCount(), is(1)); + assertThat(mockServer.getRequestCount(), is(0)); + RecordedRequest request = mockProxy.takeRequest(100, TimeUnit.MILLISECONDS); + assertThat(request, notNullValue()); + assertThat(request.getRequestUrl(), is(mockProxy.url(""))); + assertThat(request.getRequestLine(), is("GET " + mockServer.url("").toString() + " HTTP/1.1")); + verify(configurationService).getProperty("http.proxy.host"); + verify(configurationService).getProperty("http.proxy.port"); + verify(configurationService).getArrayProperty("http.proxy.hosts-to-ignore"); + verifyNoMoreInteractions(configurationService); + } + + @Test + public void testBuildWithProxyConfiguredAndHostToIgnoreSet() throws Exception { + setHttpProxyOnConfigurationService(mockServer.getHostName()); + CloseableHttpClient httpClient = httpClientFactory.build(); + assertThat(mockProxy.getRequestCount(), is(0)); + assertThat(mockServer.getRequestCount(), is(0)); + CloseableHttpResponse httpResponse = httpClient.execute(new HttpGet(mockServer.url("").toString())); + assertThat(httpResponse.getHeaders("From"), arrayWithSize(1)); + assertThat(httpResponse.getHeaders("From")[0].getValue(), is("Server")); + assertThat(mockProxy.getRequestCount(), is(0)); + assertThat(mockServer.getRequestCount(), is(1)); + verify(configurationService).getArrayProperty("http.proxy.hosts-to-ignore"); + verifyNoMoreInteractions(configurationService); + } + + @Test + public void testBuildWithProxyConfiguredAndHostPrefixToIgnoreSet() throws Exception { + setHttpProxyOnConfigurationService("local*", "www.test.com"); + CloseableHttpClient httpClient = httpClientFactory.build(); + assertThat(mockProxy.getRequestCount(), is(0)); + assertThat(mockServer.getRequestCount(), is(0)); + CloseableHttpResponse httpResponse = httpClient.execute(new HttpGet(mockServer.url("").toString())); + assertThat(httpResponse.getHeaders("From"), arrayWithSize(1)); + assertThat(httpResponse.getHeaders("From")[0].getValue(), is("Server")); + assertThat(mockProxy.getRequestCount(), is(0)); + assertThat(mockServer.getRequestCount(), is(1)); + verify(configurationService).getArrayProperty("http.proxy.hosts-to-ignore"); + verifyNoMoreInteractions(configurationService); + } + + @Test + public void testBuildWithProxyConfiguredAndHostSuffixToIgnoreSet() throws Exception { + setHttpProxyOnConfigurationService("www.test.com", "*host"); + CloseableHttpClient httpClient = httpClientFactory.build(); + assertThat(mockProxy.getRequestCount(), is(0)); + assertThat(mockServer.getRequestCount(), is(0)); + CloseableHttpResponse httpResponse = httpClient.execute(new HttpGet(mockServer.url("").toString())); + assertThat(httpResponse.getHeaders("From"), arrayWithSize(1)); + assertThat(httpResponse.getHeaders("From")[0].getValue(), is("Server")); + assertThat(mockProxy.getRequestCount(), is(0)); + assertThat(mockServer.getRequestCount(), is(1)); + verify(configurationService).getArrayProperty("http.proxy.hosts-to-ignore"); + verifyNoMoreInteractions(configurationService); + } + + @Test + public void testBuildWithoutConfiguredProxy() throws Exception { + CloseableHttpClient httpClient = httpClientFactory.build(); + assertThat(mockProxy.getRequestCount(), is(0)); + assertThat(mockServer.getRequestCount(), is(0)); + CloseableHttpResponse httpResponse = httpClient.execute(new HttpGet(mockServer.url("").toString())); + assertThat(httpResponse.getHeaders("From"), arrayWithSize(1)); + assertThat(httpResponse.getHeaders("From")[0].getValue(), is("Server")); + assertThat(mockProxy.getRequestCount(), is(0)); + assertThat(mockServer.getRequestCount(), is(1)); + RecordedRequest request = mockServer.takeRequest(100, TimeUnit.MILLISECONDS); + assertThat(request, notNullValue()); + assertThat(request.getRequestUrl(), is(mockServer.url(""))); + assertThat(request.getRequestLine(), is("GET / HTTP/1.1")); + verify(configurationService).getProperty("http.proxy.host"); + verify(configurationService).getProperty("http.proxy.port"); + verify(configurationService).getArrayProperty("http.proxy.hosts-to-ignore"); + verifyNoMoreInteractions(configurationService); + } + + @Test + public void testBuildWithoutProxy() throws Exception { + CloseableHttpClient httpClient = httpClientFactory.buildWithoutProxy(); + assertThat(mockProxy.getRequestCount(), is(0)); + assertThat(mockServer.getRequestCount(), is(0)); + httpClient.execute(new HttpGet(mockServer.url("").toString())); + assertThat(mockServer.getRequestCount(), is(1)); + assertThat(mockProxy.getRequestCount(), is(0)); + RecordedRequest request = mockServer.takeRequest(100, TimeUnit.MILLISECONDS); + assertThat(request, notNullValue()); + assertThat(request.getRequestUrl(), is(mockServer.url(""))); + assertThat(request.getRequestLine(), is("GET / HTTP/1.1")); + verifyNoInteractions(configurationService); + } + + @Test + public void testBuildWithoutAutomaticRetries() throws Exception { + setHttpProxyOnConfigurationService("www.test.com"); + CloseableHttpClient httpClient = httpClientFactory.buildWithoutAutomaticRetries(10); + httpClient.execute(new HttpGet(mockServer.url("").toString())); + assertThat(mockProxy.getRequestCount(), is(1)); + assertThat(mockServer.getRequestCount(), is(0)); + RecordedRequest request = mockProxy.takeRequest(100, TimeUnit.MILLISECONDS); + assertThat(request, notNullValue()); + assertThat(request.getRequestUrl(), is(mockProxy.url(""))); + assertThat(request.getRequestLine(), is("GET " + mockServer.url("").toString() + " HTTP/1.1")); + verify(configurationService).getProperty("http.proxy.host"); + verify(configurationService).getProperty("http.proxy.port"); + verify(configurationService).getArrayProperty("http.proxy.hosts-to-ignore"); + verifyNoMoreInteractions(configurationService); + } + + @Test + public void testBuildWithHttpRequestInterceptor() throws Exception { + setHttpProxyOnConfigurationService("*test.com", "www.dspace.com"); + AtomicReference contextReference = new AtomicReference(); + HttpRequestInterceptor interceptor = (request, context) -> contextReference.set(context); + httpClientFactory.setRequestInterceptors(List.of(interceptor)); + CloseableHttpClient httpClient = httpClientFactory.build(); + httpClient.execute(new HttpGet(mockServer.url("").toString())); + assertThat(mockProxy.getRequestCount(), is(1)); + assertThat(mockServer.getRequestCount(), is(0)); + HttpContext httpContext = contextReference.get(); + assertThat(httpContext, notNullValue()); + Object httpRouteObj = httpContext.getAttribute("http.route"); + assertThat(httpRouteObj, notNullValue()); + assertThat(httpRouteObj, instanceOf(HttpRoute.class)); + HttpRoute httpRoute = (HttpRoute) httpRouteObj; + assertThat(httpRoute.getHopCount(), is(2)); + assertThat(httpRoute.getHopTarget(0).getPort(), is(mockProxy.getPort())); + assertThat(httpRoute.getHopTarget(1).getPort(), is(mockServer.getPort())); + } + + @Test + public void testBuildWithHttpResponseInterceptor() throws Exception { + AtomicReference responseReference = new AtomicReference(); + HttpResponseInterceptor responseInterceptor = (response, context) -> responseReference.set(response); + httpClientFactory.setResponseInterceptors(List.of(responseInterceptor)); + CloseableHttpClient httpClient = httpClientFactory.build(); + httpClient.execute(new HttpGet(mockServer.url("").toString())); + assertThat(mockProxy.getRequestCount(), is(0)); + assertThat(mockServer.getRequestCount(), is(1)); + HttpResponse httpResponse = responseReference.get(); + assertThat(httpResponse, notNullValue()); + assertThat(httpResponse.getHeaders("From"), arrayWithSize(1)); + assertThat(httpResponse.getHeaders("From")[0].getValue(), is("Server")); + } + + @Test + public void testBuildWithRequestConfig() throws Exception { + setHttpProxyOnConfigurationService(); + RequestConfig requestConfig = RequestConfig.custom() + .setConnectTimeout(2500) + .build(); + AtomicReference contextReference = new AtomicReference(); + HttpRequestInterceptor interceptor = (request, context) -> contextReference.set(context); + httpClientFactory.setRequestInterceptors(List.of(interceptor)); + CloseableHttpClient httpClient = httpClientFactory.buildWithRequestConfig(requestConfig); + httpClient.execute(new HttpGet(mockServer.url("").toString())); + assertThat(mockProxy.getRequestCount(), is(1)); + assertThat(mockServer.getRequestCount(), is(0)); + HttpContext httpContext = contextReference.get(); + assertThat(httpContext, notNullValue()); + Object httpRequestConfigObj = httpContext.getAttribute("http.request-config"); + assertThat(httpRequestConfigObj, notNullValue()); + assertThat(httpRequestConfigObj, instanceOf(RequestConfig.class)); + assertThat(((RequestConfig) httpRequestConfigObj).getConnectTimeout(), is(2500)); + verify(configurationService).getProperty("http.proxy.host"); + verify(configurationService).getProperty("http.proxy.port"); + verify(configurationService).getArrayProperty("http.proxy.hosts-to-ignore"); + verifyNoMoreInteractions(configurationService); + } + + private void setHttpProxyOnConfigurationService(String... hostsToIgnore) { + when(configurationService.getProperty("http.proxy.host")).thenReturn(mockProxy.getHostName()); + when(configurationService.getProperty("http.proxy.port")).thenReturn(String.valueOf(mockProxy.getPort())); + when(configurationService.getArrayProperty("http.proxy.hosts-to-ignore")).thenReturn(hostsToIgnore); + } +} \ No newline at end of file diff --git a/dspace/config/spring/api/core-services.xml b/dspace/config/spring/api/core-services.xml index 7a5642f46439..be0e5977ede4 100644 --- a/dspace/config/spring/api/core-services.xml +++ b/dspace/config/spring/api/core-services.xml @@ -153,6 +153,9 @@ + + + From a8d33d3ad06310a5a49865daf59d4f1511bfdf7a Mon Sep 17 00:00:00 2001 From: Elios Buzo Date: Thu, 17 Apr 2025 17:55:05 +0200 Subject: [PATCH 653/979] [DURACOM-109] Continued configuring proxy for other classes --- .../app/client/DSpaceHttpClientFactory.java | 2 +- .../packager/AbstractMETSIngester.java | 12 ++++--- .../ctask/general/BasicLinkChecker.java | 18 ++++++---- .../dspace/iiif/IIIFApiQueryServiceImpl.java | 15 ++++---- .../service/LiveImportClientImpl.java | 35 ++++--------------- .../CCLicenseConnectorServiceImpl.java | 18 ++-------- .../statistics/SolrLoggerServiceImpl.java | 4 +-- dspace/config/dspace.cfg | 2 ++ 8 files changed, 39 insertions(+), 67 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/app/client/DSpaceHttpClientFactory.java b/dspace-api/src/main/java/org/dspace/app/client/DSpaceHttpClientFactory.java index 999f13c1b387..59c8172f722c 100644 --- a/dspace-api/src/main/java/org/dspace/app/client/DSpaceHttpClientFactory.java +++ b/dspace-api/src/main/java/org/dspace/app/client/DSpaceHttpClientFactory.java @@ -93,7 +93,7 @@ public CloseableHttpClient buildWithoutProxy() { public CloseableHttpClient buildWithoutAutomaticRetries(int maxConnTotal) { HttpClientBuilder clientBuilder = HttpClientBuilder.create() .disableAutomaticRetries() - .setMaxConnTotal(5); + .setMaxConnTotal(maxConnTotal); return build(clientBuilder, true); } diff --git a/dspace-api/src/main/java/org/dspace/content/packager/AbstractMETSIngester.java b/dspace-api/src/main/java/org/dspace/content/packager/AbstractMETSIngester.java index 0ed0abe21825..c43d8e797b07 100644 --- a/dspace-api/src/main/java/org/dspace/content/packager/AbstractMETSIngester.java +++ b/dspace-api/src/main/java/org/dspace/content/packager/AbstractMETSIngester.java @@ -11,8 +11,6 @@ import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; -import java.net.URL; -import java.net.URLConnection; import java.sql.SQLException; import java.util.Iterator; import java.util.List; @@ -21,7 +19,11 @@ import java.util.zip.ZipFile; import org.apache.commons.collections4.CollectionUtils; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.impl.client.CloseableHttpClient; import org.apache.logging.log4j.Logger; +import org.dspace.app.client.DSpaceHttpClientFactory; import org.dspace.authorize.AuthorizeException; import org.dspace.content.Bitstream; import org.dspace.content.BitstreamFormat; @@ -1312,11 +1314,11 @@ protected static InputStream getFileInputStream(File pkgFile, // we will assume all external files are available via URLs. try { // attempt to open a connection to given URL - URL fileURL = new URL(path); - URLConnection connection = fileURL.openConnection(); + CloseableHttpClient httpClient = DSpaceHttpClientFactory.getInstance().build(); + CloseableHttpResponse httpResponse = httpClient.execute(new HttpGet(path)); // open stream to access file contents - return connection.getInputStream(); + return httpResponse.getEntity().getContent(); } catch (IOException io) { log .error("Unable to retrieve external file from URL '" diff --git a/dspace-api/src/main/java/org/dspace/ctask/general/BasicLinkChecker.java b/dspace-api/src/main/java/org/dspace/ctask/general/BasicLinkChecker.java index a302159ea9a4..fd6d69c98f04 100644 --- a/dspace-api/src/main/java/org/dspace/ctask/general/BasicLinkChecker.java +++ b/dspace-api/src/main/java/org/dspace/ctask/general/BasicLinkChecker.java @@ -9,11 +9,15 @@ import java.io.IOException; import java.net.HttpURLConnection; -import java.net.URL; import java.util.ArrayList; import java.util.List; +import org.apache.http.client.config.RequestConfig; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.impl.client.CloseableHttpClient; import org.apache.logging.log4j.Logger; +import org.dspace.app.client.DSpaceHttpClientFactory; import org.dspace.content.DSpaceObject; import org.dspace.content.Item; import org.dspace.content.MetadataValue; @@ -136,15 +140,15 @@ protected boolean checkURL(String url, StringBuilder results) { */ protected int getResponseStatus(String url, int redirects) { try { - URL theURL = new URL(url); - HttpURLConnection connection = (HttpURLConnection) theURL.openConnection(); - connection.setInstanceFollowRedirects(true); - int statusCode = connection.getResponseCode(); + RequestConfig config = RequestConfig.custom().setRedirectsEnabled(true).build(); + CloseableHttpClient httpClient = DSpaceHttpClientFactory.getInstance().buildWithRequestConfig(config); + CloseableHttpResponse httpResponse = httpClient.execute(new HttpGet(url)); + int statusCode = httpResponse.getStatusLine().getStatusCode(); int maxRedirect = configurationService.getIntProperty("curate.checklinks.max-redirect", 0); if ((statusCode == HttpURLConnection.HTTP_MOVED_TEMP || statusCode == HttpURLConnection.HTTP_MOVED_PERM || statusCode == HttpURLConnection.HTTP_SEE_OTHER)) { - connection.disconnect(); - String newUrl = connection.getHeaderField("Location"); + httpClient.close(); + String newUrl = httpResponse.getFirstHeader("Location").getValue(); if (newUrl != null && (maxRedirect >= redirects || maxRedirect == -1)) { redirects++; return getResponseStatus(newUrl, redirects); diff --git a/dspace-api/src/main/java/org/dspace/iiif/IIIFApiQueryServiceImpl.java b/dspace-api/src/main/java/org/dspace/iiif/IIIFApiQueryServiceImpl.java index 7c6336ed3c7f..87bd7770648e 100644 --- a/dspace-api/src/main/java/org/dspace/iiif/IIIFApiQueryServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/iiif/IIIFApiQueryServiceImpl.java @@ -12,12 +12,14 @@ import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; -import java.net.HttpURLConnection; -import java.net.URL; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.impl.client.CloseableHttpClient; import org.apache.logging.log4j.Logger; +import org.dspace.app.client.DSpaceHttpClientFactory; import org.dspace.content.Bitstream; import org.dspace.iiif.util.IIIFSharedUtils; @@ -35,14 +37,11 @@ public class IIIFApiQueryServiceImpl implements IIIFApiQueryService { public int[] getImageDimensions(Bitstream bitstream) { int[] arr = new int[2]; String path = IIIFSharedUtils.getInfoJsonPath(bitstream); - URL url; BufferedReader in = null; try { - url = new URL(path); - HttpURLConnection con = (HttpURLConnection) url.openConnection(); - con.setRequestMethod("GET"); - in = new BufferedReader( - new InputStreamReader(con.getInputStream())); + CloseableHttpClient httpClient = DSpaceHttpClientFactory.getInstance().build(); + CloseableHttpResponse httpResponse = httpClient.execute(new HttpGet(path)); + in = new BufferedReader(new InputStreamReader(httpResponse.getEntity().getContent())); String inputLine; StringBuilder response = new StringBuilder(); while ((inputLine = in.readLine()) != null) { diff --git a/dspace-api/src/main/java/org/dspace/importer/external/liveimportclient/service/LiveImportClientImpl.java b/dspace-api/src/main/java/org/dspace/importer/external/liveimportclient/service/LiveImportClientImpl.java index 1a8a7a7861ed..2cb8236842a3 100644 --- a/dspace-api/src/main/java/org/dspace/importer/external/liveimportclient/service/LiveImportClientImpl.java +++ b/dspace-api/src/main/java/org/dspace/importer/external/liveimportclient/service/LiveImportClientImpl.java @@ -17,19 +17,16 @@ import org.apache.commons.collections.MapUtils; import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.StringUtils; -import org.apache.http.HttpHost; import org.apache.http.HttpResponse; import org.apache.http.client.config.RequestConfig; -import org.apache.http.client.config.RequestConfig.Builder; import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpPost; -import org.apache.http.client.methods.HttpRequestBase; import org.apache.http.client.utils.URIBuilder; import org.apache.http.entity.StringEntity; import org.apache.http.impl.client.CloseableHttpClient; -import org.apache.http.impl.client.HttpClients; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import org.dspace.app.client.DSpaceHttpClientFactory; import org.dspace.services.ConfigurationService; import org.springframework.beans.factory.annotation.Autowired; @@ -53,16 +50,12 @@ public class LiveImportClientImpl implements LiveImportClient { @Override public String executeHttpGetRequest(int timeout, String URL, Map> params) { HttpGet method = null; + RequestConfig config = RequestConfig.custom().setConnectionRequestTimeout(timeout).build(); try (CloseableHttpClient httpClient = Optional.ofNullable(this.httpClient) - .orElseGet(HttpClients::createDefault)) { - - Builder requestConfigBuilder = RequestConfig.custom(); - requestConfigBuilder.setConnectionRequestTimeout(timeout); - RequestConfig defaultRequestConfig = requestConfigBuilder.build(); - + .orElse(DSpaceHttpClientFactory.getInstance().buildWithRequestConfig(config))) { String uri = buildUrl(URL, params.get(URI_PARAMETERS)); method = new HttpGet(uri); - method.setConfig(defaultRequestConfig); + method.setConfig(config); Map headerParams = params.get(HEADER_PARAMETERS); if (MapUtils.isNotEmpty(headerParams)) { @@ -71,7 +64,6 @@ public String executeHttpGetRequest(int timeout, String URL, Map> params, String entry) { HttpPost method = null; + RequestConfig config = RequestConfig.custom().build(); try (CloseableHttpClient httpClient = Optional.ofNullable(this.httpClient) - .orElseGet(HttpClients::createDefault)) { - - Builder requestConfigBuilder = RequestConfig.custom(); - RequestConfig defaultRequestConfig = requestConfigBuilder.build(); + .orElse(DSpaceHttpClientFactory.getInstance().buildWithRequestConfig(config))) { String uri = buildUrl(URL, params.get(URI_PARAMETERS)); method = new HttpPost(uri); - method.setConfig(defaultRequestConfig); if (StringUtils.isNotBlank(entry)) { method.setEntity(new StringEntity(entry)); } setHeaderParams(method, params); - configureProxy(method, defaultRequestConfig); if (log.isDebugEnabled()) { log.debug("Performing POST request to \"" + uri + "\"..." ); } @@ -129,17 +117,6 @@ public String executeHttpPostRequest(String URL, Map return StringUtils.EMPTY; } - private void configureProxy(HttpRequestBase method, RequestConfig defaultRequestConfig) { - String proxyHost = configurationService.getProperty("http.proxy.host"); - String proxyPort = configurationService.getProperty("http.proxy.port"); - if (StringUtils.isNotBlank(proxyHost) && StringUtils.isNotBlank(proxyPort)) { - RequestConfig requestConfig = RequestConfig.copy(defaultRequestConfig) - .setProxy(new HttpHost(proxyHost, Integer.parseInt(proxyPort), "http")) - .build(); - method.setConfig(requestConfig); - } - } - /** * Allows to set the header parameters to the HTTP Post method * diff --git a/dspace-api/src/main/java/org/dspace/license/CCLicenseConnectorServiceImpl.java b/dspace-api/src/main/java/org/dspace/license/CCLicenseConnectorServiceImpl.java index 524ff04facec..e9fe49e9b4f6 100644 --- a/dspace-api/src/main/java/org/dspace/license/CCLicenseConnectorServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/license/CCLicenseConnectorServiceImpl.java @@ -10,9 +10,6 @@ import java.io.IOException; import java.io.InputStream; import java.io.StringReader; -import java.net.MalformedURLException; -import java.net.URL; -import java.net.URLConnection; import java.text.MessageFormat; import java.util.Collections; import java.util.HashMap; @@ -328,23 +325,14 @@ private String createAnswerString(final Map parameterMap) { @Override public Document retrieveLicenseRDFDoc(String licenseURI) throws IOException { String ccLicenseUrl = configurationService.getProperty("cc.api.rooturl"); - String issueUrl = ccLicenseUrl + "/details?license-uri=" + licenseURI; - - URL request_url; - try { - request_url = new URL(issueUrl); - } catch (MalformedURLException e) { - return null; - } - URLConnection connection = request_url.openConnection(); - connection.setDoOutput(true); try { + CloseableHttpClient httpClient = DSpaceHttpClientFactory.getInstance().build(); + CloseableHttpResponse httpResponse = httpClient.execute(new HttpPost(issueUrl)); // parsing document from input stream - InputStream stream = connection.getInputStream(); + InputStream stream = httpResponse.getEntity().getContent(); Document doc = parser.build(stream); return doc; - } catch (Exception e) { log.error("Error while retrieving the license document for URI: " + licenseURI, e); } diff --git a/dspace-api/src/main/java/org/dspace/statistics/SolrLoggerServiceImpl.java b/dspace-api/src/main/java/org/dspace/statistics/SolrLoggerServiceImpl.java index da9c7eb3bf27..6c3ccdc082eb 100644 --- a/dspace-api/src/main/java/org/dspace/statistics/SolrLoggerServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/statistics/SolrLoggerServiceImpl.java @@ -1303,7 +1303,7 @@ public void shardSolrIndex() throws IOException, SolrServerException { + "." + i + ".csv"); - try (CloseableHttpClient hc = DSpaceHttpClientFactory.getInstance().build()) { + try (CloseableHttpClient hc = DSpaceHttpClientFactory.getInstance().buildWithoutProxy()) { HttpResponse response = hc.execute(get); csvInputstream = response.getEntity().getContent(); //Write the csv ouput to a file ! @@ -1445,7 +1445,7 @@ public void reindexBitstreamHits(boolean removeDeletedBitstreams) throws Excepti HttpGet get = new HttpGet(solrRequestUrl); List rows; - try (CloseableHttpClient hc = DSpaceHttpClientFactory.getInstance().build()) { + try (CloseableHttpClient hc = DSpaceHttpClientFactory.getInstance().buildWithoutProxy()) { HttpResponse response = hc.execute(get); InputStream csvOutput = response.getEntity().getContent(); Reader csvReader = new InputStreamReader(csvOutput); diff --git a/dspace/config/dspace.cfg b/dspace/config/dspace.cfg index 94b731cd3488..4621ca51dc13 100644 --- a/dspace/config/dspace.cfg +++ b/dspace/config/dspace.cfg @@ -411,6 +411,8 @@ http.proxy.host = # port number of proxy server http.proxy.port = +http.proxy.hosts-to-ignore = 127.0.0.1, localhost + # If enabled, the logging and the Solr statistics system will look for an X-Forwarded-For header. # If it finds it, it will use this for the user IP address. # NOTE: This is required to be enabled if you plan to use the Angular UI, as the server-side rendering provided in From 32dd1a3dd22c70363c0c7593d395570247ee00aa Mon Sep 17 00:00:00 2001 From: Elios Buzo Date: Fri, 18 Apr 2025 10:24:35 +0200 Subject: [PATCH 654/979] [DURACOM-109] Minor fix --- .../external/liveimportclient/service/LiveImportClientImpl.java | 1 - 1 file changed, 1 deletion(-) diff --git a/dspace-api/src/main/java/org/dspace/importer/external/liveimportclient/service/LiveImportClientImpl.java b/dspace-api/src/main/java/org/dspace/importer/external/liveimportclient/service/LiveImportClientImpl.java index 2cb8236842a3..84475b62c07b 100644 --- a/dspace-api/src/main/java/org/dspace/importer/external/liveimportclient/service/LiveImportClientImpl.java +++ b/dspace-api/src/main/java/org/dspace/importer/external/liveimportclient/service/LiveImportClientImpl.java @@ -55,7 +55,6 @@ public String executeHttpGetRequest(int timeout, String URL, Map headerParams = params.get(HEADER_PARAMETERS); if (MapUtils.isNotEmpty(headerParams)) { From b9352c9149b90ae3c5f007ddae15632b26d9e246 Mon Sep 17 00:00:00 2001 From: Elios Buzo Date: Fri, 18 Apr 2025 15:00:38 +0200 Subject: [PATCH 655/979] [DURACOM-109] Fixed http connection leaks --- .../org/dspace/app/sherpa/SHERPAService.java | 21 +--- .../dspace/app/util/WebAppServiceImpl.java | 4 +- .../oidc/impl/OidcClientImpl.java | 29 +++-- .../packager/AbstractMETSIngester.java | 3 +- .../ctask/general/BasicLinkChecker.java | 5 +- .../dspace/external/OrcidRestConnector.java | 10 +- .../dspace/iiif/IIIFApiQueryServiceImpl.java | 3 +- .../CCLicenseConnectorServiceImpl.java | 3 +- .../dspace/orcid/client/OrcidClientImpl.java | 103 ++++++++---------- 9 files changed, 73 insertions(+), 108 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/app/sherpa/SHERPAService.java b/dspace-api/src/main/java/org/dspace/app/sherpa/SHERPAService.java index 73ec0cc58937..87042c502d83 100644 --- a/dspace-api/src/main/java/org/dspace/app/sherpa/SHERPAService.java +++ b/dspace-api/src/main/java/org/dspace/app/sherpa/SHERPAService.java @@ -17,9 +17,9 @@ import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.StringUtils; import org.apache.http.HttpEntity; -import org.apache.http.HttpResponse; import org.apache.http.HttpStatus; import org.apache.http.client.config.RequestConfig; +import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpGet; import org.apache.http.client.utils.URIBuilder; import org.apache.http.impl.client.CloseableHttpClient; @@ -45,8 +45,6 @@ */ public class SHERPAService { - private CloseableHttpClient client = null; - private int maxNumberOfTries; private long sleepBetweenTimeouts; private int timeout = 5000; @@ -59,15 +57,6 @@ public class SHERPAService { @Autowired ConfigurationService configurationService; - /** - * Create a new HTTP builder with sensible defaults in constructor - */ - public SHERPAService() { - // httpclient 4.3+ doesn't appear to have any sensible defaults any more. Setting conservative defaults as - // not to hammer the SHERPA service too much. - client = DSpaceHttpClientFactory.getInstance().buildWithoutAutomaticRetries(5); - } - /** * Complete initialization of the Bean. */ @@ -128,14 +117,14 @@ public SHERPAPublisherResponse performPublisherRequest(String type, String field timeout, sleepBetweenTimeouts)); - try { + try (CloseableHttpClient client = DSpaceHttpClientFactory.getInstance().build()) { Thread.sleep(sleepBetweenTimeouts); // Construct a default HTTP method (first result) method = constructHttpGet(type, field, predicate, value, start, limit); // Execute the method - HttpResponse response = client.execute(method); + CloseableHttpResponse response = client.execute(method); int statusCode = response.getStatusLine().getStatusCode(); log.debug(response.getStatusLine().getStatusCode() + ": " @@ -231,14 +220,14 @@ public SHERPAResponse performRequest(String type, String field, String predicate timeout, sleepBetweenTimeouts)); - try { + try (CloseableHttpClient client = DSpaceHttpClientFactory.getInstance().build()) { Thread.sleep(sleepBetweenTimeouts); // Construct a default HTTP method (first result) method = constructHttpGet(type, field, predicate, value, start, limit); // Execute the method - HttpResponse response = client.execute(method); + CloseableHttpResponse response = client.execute(method); int statusCode = response.getStatusLine().getStatusCode(); log.debug(response.getStatusLine().getStatusCode() + ": " diff --git a/dspace-api/src/main/java/org/dspace/app/util/WebAppServiceImpl.java b/dspace-api/src/main/java/org/dspace/app/util/WebAppServiceImpl.java index 064a9fdc3e9b..fa23903ba431 100644 --- a/dspace-api/src/main/java/org/dspace/app/util/WebAppServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/app/util/WebAppServiceImpl.java @@ -13,8 +13,8 @@ import java.util.Date; import java.util.List; -import org.apache.http.HttpResponse; import org.apache.http.HttpStatus; +import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpHead; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.logging.log4j.Logger; @@ -78,7 +78,7 @@ public List getApps() { method = new HttpHead(app.getUrl()); int status; try (CloseableHttpClient client = DSpaceHttpClientFactory.getInstance().build()) { - HttpResponse response = client.execute(method); + CloseableHttpResponse response = client.execute(method); status = response.getStatusLine().getStatusCode(); } if (status != HttpStatus.SC_OK) { diff --git a/dspace-api/src/main/java/org/dspace/authenticate/oidc/impl/OidcClientImpl.java b/dspace-api/src/main/java/org/dspace/authenticate/oidc/impl/OidcClientImpl.java index 35856d3756d9..8dd609951126 100644 --- a/dspace-api/src/main/java/org/dspace/authenticate/oidc/impl/OidcClientImpl.java +++ b/dspace-api/src/main/java/org/dspace/authenticate/oidc/impl/OidcClientImpl.java @@ -22,10 +22,11 @@ import org.apache.http.HttpEntity; import org.apache.http.HttpResponse; import org.apache.http.NameValuePair; -import org.apache.http.client.HttpClient; import org.apache.http.client.entity.UrlEncodedFormEntity; +import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpUriRequest; import org.apache.http.client.methods.RequestBuilder; +import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.message.BasicNameValuePair; import org.dspace.app.client.DSpaceHttpClientFactory; import org.dspace.authenticate.oidc.OidcClient; @@ -83,21 +84,17 @@ public Map getUserInfo(String accessToken) throws OidcClientExce } private T executeAndParseJson(HttpUriRequest httpUriRequest, Class clazz) { - - HttpClient client = DSpaceHttpClientFactory.getInstance().build(); - - return executeAndReturns(() -> { - - HttpResponse response = client.execute(httpUriRequest); - - if (isNotSuccessfull(response)) { - throw new OidcClientException(getStatusCode(response), formatErrorMessage(response)); - } - - return objectMapper.readValue(getContent(response), clazz); - - }); - + try (CloseableHttpClient client = DSpaceHttpClientFactory.getInstance().build()) { + return executeAndReturns(() -> { + CloseableHttpResponse response = client.execute(httpUriRequest); + if (isNotSuccessfull(response)) { + throw new OidcClientException(getStatusCode(response), formatErrorMessage(response)); + } + return objectMapper.readValue(getContent(response), clazz); + }); + } catch (IOException e) { + throw new RuntimeException(e); + } } private T executeAndReturns(ThrowingSupplier supplier) { diff --git a/dspace-api/src/main/java/org/dspace/content/packager/AbstractMETSIngester.java b/dspace-api/src/main/java/org/dspace/content/packager/AbstractMETSIngester.java index c43d8e797b07..835b9f0e9b6f 100644 --- a/dspace-api/src/main/java/org/dspace/content/packager/AbstractMETSIngester.java +++ b/dspace-api/src/main/java/org/dspace/content/packager/AbstractMETSIngester.java @@ -1312,9 +1312,8 @@ protected static InputStream getFileInputStream(File pkgFile, if (params.getBooleanProperty("manifestOnly", false)) { // NOTE: since we are only dealing with a METS manifest, // we will assume all external files are available via URLs. - try { + try (CloseableHttpClient httpClient = DSpaceHttpClientFactory.getInstance().build()) { // attempt to open a connection to given URL - CloseableHttpClient httpClient = DSpaceHttpClientFactory.getInstance().build(); CloseableHttpResponse httpResponse = httpClient.execute(new HttpGet(path)); // open stream to access file contents diff --git a/dspace-api/src/main/java/org/dspace/ctask/general/BasicLinkChecker.java b/dspace-api/src/main/java/org/dspace/ctask/general/BasicLinkChecker.java index fd6d69c98f04..189f77733886 100644 --- a/dspace-api/src/main/java/org/dspace/ctask/general/BasicLinkChecker.java +++ b/dspace-api/src/main/java/org/dspace/ctask/general/BasicLinkChecker.java @@ -139,9 +139,8 @@ protected boolean checkURL(String url, StringBuilder results) { * @return The HTTP response code (e.g. 200 / 301 / 404 / 500) */ protected int getResponseStatus(String url, int redirects) { - try { - RequestConfig config = RequestConfig.custom().setRedirectsEnabled(true).build(); - CloseableHttpClient httpClient = DSpaceHttpClientFactory.getInstance().buildWithRequestConfig(config); + RequestConfig config = RequestConfig.custom().setRedirectsEnabled(true).build(); + try (CloseableHttpClient httpClient = DSpaceHttpClientFactory.getInstance().buildWithRequestConfig(config)) { CloseableHttpResponse httpResponse = httpClient.execute(new HttpGet(url)); int statusCode = httpResponse.getStatusLine().getStatusCode(); int maxRedirect = configurationService.getIntProperty("curate.checklinks.max-redirect", 0); diff --git a/dspace-api/src/main/java/org/dspace/external/OrcidRestConnector.java b/dspace-api/src/main/java/org/dspace/external/OrcidRestConnector.java index 3ccbd257e285..f2c6246f4d06 100644 --- a/dspace-api/src/main/java/org/dspace/external/OrcidRestConnector.java +++ b/dspace-api/src/main/java/org/dspace/external/OrcidRestConnector.java @@ -12,9 +12,9 @@ import java.util.Scanner; import org.apache.commons.lang3.StringUtils; -import org.apache.http.HttpResponse; -import org.apache.http.client.HttpClient; +import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpGet; +import org.apache.http.impl.client.CloseableHttpClient; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.dspace.app.client.DSpaceHttpClientFactory; @@ -39,7 +39,7 @@ public OrcidRestConnector(String url) { } public InputStream get(String path, String accessToken) { - HttpResponse getResponse = null; + CloseableHttpResponse getResponse = null; InputStream result = null; path = trimSlashes(path); @@ -49,10 +49,8 @@ public InputStream get(String path, String accessToken) { httpGet.addHeader("Content-Type", "application/vnd.orcid+xml"); httpGet.addHeader("Authorization","Bearer " + accessToken); } - try { - HttpClient httpClient = DSpaceHttpClientFactory.getInstance().build(); + try (CloseableHttpClient httpClient = DSpaceHttpClientFactory.getInstance().build()) { getResponse = httpClient.execute(httpGet); - //do not close this httpClient result = getResponse.getEntity().getContent(); } catch (Exception e) { getGotError(e, fullPath); diff --git a/dspace-api/src/main/java/org/dspace/iiif/IIIFApiQueryServiceImpl.java b/dspace-api/src/main/java/org/dspace/iiif/IIIFApiQueryServiceImpl.java index 87bd7770648e..ccb2c170d949 100644 --- a/dspace-api/src/main/java/org/dspace/iiif/IIIFApiQueryServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/iiif/IIIFApiQueryServiceImpl.java @@ -38,8 +38,7 @@ public int[] getImageDimensions(Bitstream bitstream) { int[] arr = new int[2]; String path = IIIFSharedUtils.getInfoJsonPath(bitstream); BufferedReader in = null; - try { - CloseableHttpClient httpClient = DSpaceHttpClientFactory.getInstance().build(); + try (CloseableHttpClient httpClient = DSpaceHttpClientFactory.getInstance().build()) { CloseableHttpResponse httpResponse = httpClient.execute(new HttpGet(path)); in = new BufferedReader(new InputStreamReader(httpResponse.getEntity().getContent())); String inputLine; diff --git a/dspace-api/src/main/java/org/dspace/license/CCLicenseConnectorServiceImpl.java b/dspace-api/src/main/java/org/dspace/license/CCLicenseConnectorServiceImpl.java index e9fe49e9b4f6..1d777a2e13c8 100644 --- a/dspace-api/src/main/java/org/dspace/license/CCLicenseConnectorServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/license/CCLicenseConnectorServiceImpl.java @@ -326,8 +326,7 @@ private String createAnswerString(final Map parameterMap) { public Document retrieveLicenseRDFDoc(String licenseURI) throws IOException { String ccLicenseUrl = configurationService.getProperty("cc.api.rooturl"); String issueUrl = ccLicenseUrl + "/details?license-uri=" + licenseURI; - try { - CloseableHttpClient httpClient = DSpaceHttpClientFactory.getInstance().build(); + try (CloseableHttpClient httpClient = DSpaceHttpClientFactory.getInstance().build()) { CloseableHttpResponse httpResponse = httpClient.execute(new HttpPost(issueUrl)); // parsing document from input stream InputStream stream = httpResponse.getEntity().getContent(); diff --git a/dspace-api/src/main/java/org/dspace/orcid/client/OrcidClientImpl.java b/dspace-api/src/main/java/org/dspace/orcid/client/OrcidClientImpl.java index 2285bb11c784..954336da2573 100644 --- a/dspace-api/src/main/java/org/dspace/orcid/client/OrcidClientImpl.java +++ b/dspace-api/src/main/java/org/dspace/orcid/client/OrcidClientImpl.java @@ -35,11 +35,12 @@ import org.apache.http.HttpResponse; import org.apache.http.HttpStatus; import org.apache.http.NameValuePair; -import org.apache.http.client.HttpClient; import org.apache.http.client.entity.UrlEncodedFormEntity; +import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpUriRequest; import org.apache.http.client.methods.RequestBuilder; import org.apache.http.entity.StringEntity; +import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.message.BasicNameValuePair; import org.dspace.app.client.DSpaceHttpClientFactory; import org.dspace.orcid.OrcidToken; @@ -76,12 +77,9 @@ public class OrcidClientImpl implements OrcidClient { private final ObjectMapper objectMapper; - private final DSpaceHttpClientFactory httpClientFactory; - - public OrcidClientImpl(OrcidConfiguration orcidConfiguration, DSpaceHttpClientFactory httpClientFactory) { + public OrcidClientImpl(OrcidConfiguration orcidConfiguration) { this.orcidConfiguration = orcidConfiguration; this.objectMapper = new ObjectMapper(); - this.httpClientFactory = httpClientFactory; } private static Map, String> initializePathsMap() { @@ -257,10 +255,8 @@ private HttpUriRequest buildDeleteUriRequest(String accessToken, String relative } private void executeSuccessful(HttpUriRequest httpUriRequest) { - try { - HttpClient client = httpClientFactory.build(); - HttpResponse response = client.execute(httpUriRequest); - + try (CloseableHttpClient client = DSpaceHttpClientFactory.getInstance().build()) { + CloseableHttpResponse response = client.execute(httpUriRequest); if (isNotSuccessfull(response)) { throw new OrcidClientException( getStatusCode(response), @@ -275,21 +271,17 @@ private void executeSuccessful(HttpUriRequest httpUriRequest) { } private T executeAndParseJson(HttpUriRequest httpUriRequest, Class clazz) { - - HttpClient client = httpClientFactory.build(); - - return executeAndReturns(() -> { - - HttpResponse response = client.execute(httpUriRequest); - - if (isNotSuccessfull(response)) { - throw new OrcidClientException(getStatusCode(response), formatErrorMessage(response)); - } - - return objectMapper.readValue(response.getEntity().getContent(), clazz); - - }); - + try (CloseableHttpClient client = DSpaceHttpClientFactory.getInstance().build()) { + return executeAndReturns(() -> { + CloseableHttpResponse response = client.execute(httpUriRequest); + if (isNotSuccessfull(response)) { + throw new OrcidClientException(getStatusCode(response), formatErrorMessage(response)); + } + return objectMapper.readValue(response.getEntity().getContent(), clazz); + }); + } catch (IOException e) { + throw new RuntimeException(e); + } } /** @@ -304,44 +296,37 @@ private T executeAndParseJson(HttpUriRequest httpUriRequest, Class clazz) * @throws OrcidClientException if the incoming response is not successfull */ private T executeAndUnmarshall(HttpUriRequest httpUriRequest, boolean handleNotFoundAsNull, Class clazz) { - - HttpClient client = httpClientFactory.build(); - - return executeAndReturns(() -> { - - HttpResponse response = client.execute(httpUriRequest); - - if (handleNotFoundAsNull && isNotFound(response)) { - return null; - } - - if (isNotSuccessfull(response)) { - throw new OrcidClientException(getStatusCode(response), formatErrorMessage(response)); - } - - return unmarshall(response.getEntity(), clazz); - - }); + try (CloseableHttpClient client = DSpaceHttpClientFactory.getInstance().build()) { + return executeAndReturns(() -> { + CloseableHttpResponse response = client.execute(httpUriRequest); + if (handleNotFoundAsNull && isNotFound(response)) { + return null; + } + if (isNotSuccessfull(response)) { + throw new OrcidClientException(getStatusCode(response), formatErrorMessage(response)); + } + return unmarshall(response.getEntity(), clazz); + }); + } catch (IOException e) { + throw new RuntimeException(e); + } } private OrcidResponse execute(HttpUriRequest httpUriRequest, boolean handleNotFoundAsNull) { - HttpClient client = httpClientFactory.build(); - - return executeAndReturns(() -> { - - HttpResponse response = client.execute(httpUriRequest); - - if (handleNotFoundAsNull && isNotFound(response)) { - return new OrcidResponse(getStatusCode(response), null, getContent(response)); - } - - if (isNotSuccessfull(response)) { - throw new OrcidClientException(getStatusCode(response), formatErrorMessage(response)); - } - - return new OrcidResponse(getStatusCode(response), getPutCode(response), getContent(response)); - - }); + try (CloseableHttpClient client = DSpaceHttpClientFactory.getInstance().build()) { + return executeAndReturns(() -> { + CloseableHttpResponse response = client.execute(httpUriRequest); + if (handleNotFoundAsNull && isNotFound(response)) { + return new OrcidResponse(getStatusCode(response), null, getContent(response)); + } + if (isNotSuccessfull(response)) { + throw new OrcidClientException(getStatusCode(response), formatErrorMessage(response)); + } + return new OrcidResponse(getStatusCode(response), getPutCode(response), getContent(response)); + }); + } catch (IOException e) { + throw new RuntimeException(e); + } } private T executeAndReturns(ThrowingSupplier supplier) { From f9307b617c28575f8b3cfbc3563bf4a2764d902e Mon Sep 17 00:00:00 2001 From: Elios Buzo Date: Fri, 18 Apr 2025 15:12:55 +0200 Subject: [PATCH 656/979] [DURACOM-109] Minor fix --- .../java/org/dspace/service/impl/HttpConnectionPoolService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dspace-api/src/main/java/org/dspace/service/impl/HttpConnectionPoolService.java b/dspace-api/src/main/java/org/dspace/service/impl/HttpConnectionPoolService.java index 27f2ad3e412b..69fea9c72c5f 100644 --- a/dspace-api/src/main/java/org/dspace/service/impl/HttpConnectionPoolService.java +++ b/dspace-api/src/main/java/org/dspace/service/impl/HttpConnectionPoolService.java @@ -112,7 +112,7 @@ protected void init() { * @return the client. */ public CloseableHttpClient getClient() { - CloseableHttpClient httpClient = DSpaceHttpClientFactory.getInstance().builder(false).create() + CloseableHttpClient httpClient = DSpaceHttpClientFactory.getInstance().builder(true).create() .setKeepAliveStrategy(keepAliveStrategy) .setConnectionManager(connManager) .build(); From f4390fef52c9ae352ef2b305668c000d4c84853f Mon Sep 17 00:00:00 2001 From: Elios Buzo Date: Wed, 23 Apr 2025 12:31:11 +0200 Subject: [PATCH 657/979] [DURACOM-109] Continued fixing http connection leaks --- .../packager/AbstractMETSIngester.java | 8 +- .../ctask/general/BasicLinkChecker.java | 24 +++--- .../ctask/general/MetadataWebService.java | 85 +++++++++---------- .../ctask/general/MicrosoftTranslator.java | 26 +++--- .../export/service/OpenUrlServiceImpl.java | 16 ++-- .../service/OpenUrlServiceImplTest.java | 10 +-- 6 files changed, 82 insertions(+), 87 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/content/packager/AbstractMETSIngester.java b/dspace-api/src/main/java/org/dspace/content/packager/AbstractMETSIngester.java index 835b9f0e9b6f..77236be9d525 100644 --- a/dspace-api/src/main/java/org/dspace/content/packager/AbstractMETSIngester.java +++ b/dspace-api/src/main/java/org/dspace/content/packager/AbstractMETSIngester.java @@ -1314,10 +1314,10 @@ protected static InputStream getFileInputStream(File pkgFile, // we will assume all external files are available via URLs. try (CloseableHttpClient httpClient = DSpaceHttpClientFactory.getInstance().build()) { // attempt to open a connection to given URL - CloseableHttpResponse httpResponse = httpClient.execute(new HttpGet(path)); - - // open stream to access file contents - return httpResponse.getEntity().getContent(); + try (CloseableHttpResponse httpResponse = httpClient.execute(new HttpGet(path))) { + // open stream to access file contents + return httpResponse.getEntity().getContent(); + } } catch (IOException io) { log .error("Unable to retrieve external file from URL '" diff --git a/dspace-api/src/main/java/org/dspace/ctask/general/BasicLinkChecker.java b/dspace-api/src/main/java/org/dspace/ctask/general/BasicLinkChecker.java index 189f77733886..a6621fcb2b41 100644 --- a/dspace-api/src/main/java/org/dspace/ctask/general/BasicLinkChecker.java +++ b/dspace-api/src/main/java/org/dspace/ctask/general/BasicLinkChecker.java @@ -141,21 +141,19 @@ protected boolean checkURL(String url, StringBuilder results) { protected int getResponseStatus(String url, int redirects) { RequestConfig config = RequestConfig.custom().setRedirectsEnabled(true).build(); try (CloseableHttpClient httpClient = DSpaceHttpClientFactory.getInstance().buildWithRequestConfig(config)) { - CloseableHttpResponse httpResponse = httpClient.execute(new HttpGet(url)); - int statusCode = httpResponse.getStatusLine().getStatusCode(); - int maxRedirect = configurationService.getIntProperty("curate.checklinks.max-redirect", 0); - if ((statusCode == HttpURLConnection.HTTP_MOVED_TEMP || statusCode == HttpURLConnection.HTTP_MOVED_PERM || - statusCode == HttpURLConnection.HTTP_SEE_OTHER)) { - httpClient.close(); - String newUrl = httpResponse.getFirstHeader("Location").getValue(); - if (newUrl != null && (maxRedirect >= redirects || maxRedirect == -1)) { - redirects++; - return getResponseStatus(newUrl, redirects); + try (CloseableHttpResponse httpResponse = httpClient.execute(new HttpGet(url))) { + int statusCode = httpResponse.getStatusLine().getStatusCode(); + int maxRedirect = configurationService.getIntProperty("curate.checklinks.max-redirect", 0); + if ((statusCode == HttpURLConnection.HTTP_MOVED_TEMP || statusCode == HttpURLConnection.HTTP_MOVED_PERM || + statusCode == HttpURLConnection.HTTP_SEE_OTHER)) { + String newUrl = httpResponse.getFirstHeader("Location").getValue(); + if (newUrl != null && (maxRedirect >= redirects || maxRedirect == -1)) { + redirects++; + return getResponseStatus(newUrl, redirects); + } } - + return statusCode; } - return statusCode; - } catch (IOException ioe) { // Must be a bad URL log.debug("Bad link: " + ioe.getMessage()); diff --git a/dspace-api/src/main/java/org/dspace/ctask/general/MetadataWebService.java b/dspace-api/src/main/java/org/dspace/ctask/general/MetadataWebService.java index da7588be6c6f..7f61cad412e2 100644 --- a/dspace-api/src/main/java/org/dspace/ctask/general/MetadataWebService.java +++ b/dspace-api/src/main/java/org/dspace/ctask/general/MetadataWebService.java @@ -30,8 +30,8 @@ import javax.xml.xpath.XPathFactory; import org.apache.http.HttpEntity; -import org.apache.http.HttpResponse; import org.apache.http.HttpStatus; +import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpGet; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.logging.log4j.LogManager; @@ -255,53 +255,50 @@ public int perform(DSpaceObject dso) throws IOException { } protected int callService(String value, Item item, StringBuilder resultSb) throws IOException { - String callUrl = urlTemplate.replaceAll("\\{" + templateParam + "\\}", value); - CloseableHttpClient client = DSpaceHttpClientFactory.getInstance().build(); - HttpGet req = new HttpGet(callUrl); - for (Map.Entry entry : headers.entrySet()) { - req.addHeader(entry.getKey(), entry.getValue()); - } - HttpResponse resp = client.execute(req); - int status = Curator.CURATE_ERROR; - int statusCode = resp.getStatusLine().getStatusCode(); - if (statusCode == HttpStatus.SC_OK) { - HttpEntity entity = resp.getEntity(); - if (entity != null) { - // boiler-plate handling taken from Apache 4.1 javadoc - InputStream instream = entity.getContent(); - try { - // This next line triggers a false-positive XXE warning from LGTM, even though we disallow DTD - // parsing during initialization of docBuilder in init() - Document doc = docBuilder.parse(instream); // lgtm [java/xxe] - status = processResponse(doc, item, resultSb); - } catch (SAXException saxE) { - log.error("caught exception: " + saxE); - resultSb.append(" unable to read response document"); - } catch (RuntimeException ex) { - // In case of an unexpected exception you may want to abort - // the HTTP request in order to shut down the underlying - // connection and release it back to the connection manager. - req.abort(); - log.error("caught exception: " + ex); - throw ex; - } finally { - // Closing the input stream will trigger connection release - instream.close(); + try (CloseableHttpClient client = DSpaceHttpClientFactory.getInstance().build()) { + HttpGet req = new HttpGet(callUrl); + for (Map.Entry entry : headers.entrySet()) { + req.addHeader(entry.getKey(), entry.getValue()); + } + try (CloseableHttpResponse resp = client.execute(req)) { + int status = Curator.CURATE_ERROR; + int statusCode = resp.getStatusLine().getStatusCode(); + if (statusCode == HttpStatus.SC_OK) { + HttpEntity entity = resp.getEntity(); + if (entity != null) { + // boiler-plate handling taken from Apache 4.1 javadoc + InputStream instream = entity.getContent(); + try { + // This next line triggers a false-positive XXE warning from LGTM, even though we disallow DTD + // parsing during initialization of docBuilder in init() + Document doc = docBuilder.parse(instream); // lgtm [java/xxe] + status = processResponse(doc, item, resultSb); + } catch (SAXException saxE) { + log.error("caught exception: " + saxE); + resultSb.append(" unable to read response document"); + } catch (RuntimeException ex) { + // In case of an unexpected exception you may want to abort + // the HTTP request in order to shut down the underlying + // connection and release it back to the connection manager. + req.abort(); + log.error("caught exception: " + ex); + throw ex; + } finally { + // Closing the input stream will trigger connection release + instream.close(); + } + } else { + log.error(" obtained no valid service response"); + resultSb.append("no service response"); + } + } else { + log.error("service returned non-OK status: " + statusCode); + resultSb.append("no service response"); } - // When HttpClient instance is no longer needed, - // shut down the connection manager to ensure - // immediate deallocation of all system resources - client.close(); - } else { - log.error(" obtained no valid service response"); - resultSb.append("no service response"); + return status; } - } else { - log.error("service returned non-OK status: " + statusCode); - resultSb.append("no service response"); } - return status; } protected int processResponse(Document doc, Item item, StringBuilder resultSb) throws IOException { diff --git a/dspace-api/src/main/java/org/dspace/ctask/general/MicrosoftTranslator.java b/dspace-api/src/main/java/org/dspace/ctask/general/MicrosoftTranslator.java index 0d682d9406f4..af9c2de0c8b1 100644 --- a/dspace-api/src/main/java/org/dspace/ctask/general/MicrosoftTranslator.java +++ b/dspace-api/src/main/java/org/dspace/ctask/general/MicrosoftTranslator.java @@ -12,7 +12,7 @@ import java.nio.charset.StandardCharsets; import org.apache.commons.io.IOUtils; -import org.apache.http.HttpResponse; +import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpGet; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.logging.log4j.LogManager; @@ -62,20 +62,18 @@ protected String translateText(String from, String to, String text) throws IOExc try (CloseableHttpClient client = DSpaceHttpClientFactory.getInstance().build()) { HttpGet hm = new HttpGet(url); - HttpResponse httpResponse = client.execute(hm); - log.debug("Response code from API call is " + httpResponse); - - if (httpResponse.getStatusLine().getStatusCode() == 200) { - String response = IOUtils.toString(httpResponse.getEntity().getContent(), - StandardCharsets.ISO_8859_1); - response = response - .replaceAll("", ""); - response = response.replaceAll("", ""); - translatedText = response; + try (CloseableHttpResponse httpResponse = client.execute(hm)) { + log.debug("Response code from API call is " + httpResponse); + if (httpResponse.getStatusLine().getStatusCode() == 200) { + String response = IOUtils.toString(httpResponse.getEntity().getContent(), + StandardCharsets.ISO_8859_1); + response = response + .replaceAll("", ""); + response = response.replaceAll("", ""); + translatedText = response; + } } } - return translatedText; } -} - +} \ No newline at end of file diff --git a/dspace-api/src/main/java/org/dspace/statistics/export/service/OpenUrlServiceImpl.java b/dspace-api/src/main/java/org/dspace/statistics/export/service/OpenUrlServiceImpl.java index d8f4aa6b7e7b..64014fcbc07c 100644 --- a/dspace-api/src/main/java/org/dspace/statistics/export/service/OpenUrlServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/statistics/export/service/OpenUrlServiceImpl.java @@ -14,10 +14,10 @@ import java.util.List; import org.apache.commons.lang.StringUtils; -import org.apache.http.HttpResponse; -import org.apache.http.client.HttpClient; import org.apache.http.client.config.RequestConfig; +import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpGet; +import org.apache.http.impl.client.CloseableHttpClient; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.dspace.app.client.DSpaceHttpClientFactory; @@ -68,13 +68,15 @@ public void processUrl(Context c, String urlStr) throws SQLException { * @throws IOException */ protected int getResponseCodeFromUrl(final String urlStr) throws IOException { - HttpGet httpGet = new HttpGet(urlStr); - HttpClient httpClient = getHttpClient(getHttpClientRequestConfig()); - HttpResponse httpResponse = httpClient.execute(httpGet); - return httpResponse.getStatusLine().getStatusCode(); + try (CloseableHttpClient httpClient = getHttpClient(getHttpClientRequestConfig())) { + HttpGet httpGet = new HttpGet(urlStr); + try (CloseableHttpResponse httpResponse = httpClient.execute(httpGet)) { + return httpResponse.getStatusLine().getStatusCode(); + } + } } - protected HttpClient getHttpClient(RequestConfig requestConfig) { + protected CloseableHttpClient getHttpClient(RequestConfig requestConfig) { return DSpaceHttpClientFactory.getInstance().buildWithRequestConfig(requestConfig); } diff --git a/dspace-api/src/test/java/org/dspace/statistics/export/service/OpenUrlServiceImplTest.java b/dspace-api/src/test/java/org/dspace/statistics/export/service/OpenUrlServiceImplTest.java index d214050e6b5a..718ef701e136 100644 --- a/dspace-api/src/test/java/org/dspace/statistics/export/service/OpenUrlServiceImplTest.java +++ b/dspace-api/src/test/java/org/dspace/statistics/export/service/OpenUrlServiceImplTest.java @@ -27,9 +27,9 @@ import java.util.Date; import java.util.List; -import org.apache.http.HttpResponse; import org.apache.http.StatusLine; -import org.apache.http.client.HttpClient; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.impl.client.CloseableHttpClient; import org.dspace.core.Context; import org.dspace.statistics.export.OpenURLTracker; import org.junit.Before; @@ -55,7 +55,7 @@ public class OpenUrlServiceImplTest { private FailedOpenURLTrackerService failedOpenURLTrackerService; @Mock - private HttpClient httpClient; + private CloseableHttpClient httpClient; @Before public void setUp() throws Exception { @@ -74,11 +74,11 @@ public void setUp() throws Exception { * @param statusCode the http status code to use in the mock. * @return a mocked http response. */ - protected HttpResponse createMockHttpResponse(int statusCode) { + protected CloseableHttpResponse createMockHttpResponse(int statusCode) { StatusLine statusLine = mock(StatusLine.class); when(statusLine.getStatusCode()).thenReturn(statusCode); - HttpResponse httpResponse = mock(HttpResponse.class); + CloseableHttpResponse httpResponse = mock(CloseableHttpResponse.class); when(httpResponse.getStatusLine()).thenReturn(statusLine); return httpResponse; From 7f865ad95657e9712d6ebec41a765bc9d8b6f2c7 Mon Sep 17 00:00:00 2001 From: Elios Buzo Date: Wed, 23 Apr 2025 15:34:52 +0200 Subject: [PATCH 658/979] [DURACOM-109] Linter error fix --- .../ctask/general/BasicLinkChecker.java | 21 +++++++++---------- .../ctask/general/MetadataWebService.java | 4 ++-- 2 files changed, 12 insertions(+), 13 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/ctask/general/BasicLinkChecker.java b/dspace-api/src/main/java/org/dspace/ctask/general/BasicLinkChecker.java index a6621fcb2b41..020331842703 100644 --- a/dspace-api/src/main/java/org/dspace/ctask/general/BasicLinkChecker.java +++ b/dspace-api/src/main/java/org/dspace/ctask/general/BasicLinkChecker.java @@ -141,19 +141,18 @@ protected boolean checkURL(String url, StringBuilder results) { protected int getResponseStatus(String url, int redirects) { RequestConfig config = RequestConfig.custom().setRedirectsEnabled(true).build(); try (CloseableHttpClient httpClient = DSpaceHttpClientFactory.getInstance().buildWithRequestConfig(config)) { - try (CloseableHttpResponse httpResponse = httpClient.execute(new HttpGet(url))) { - int statusCode = httpResponse.getStatusLine().getStatusCode(); - int maxRedirect = configurationService.getIntProperty("curate.checklinks.max-redirect", 0); - if ((statusCode == HttpURLConnection.HTTP_MOVED_TEMP || statusCode == HttpURLConnection.HTTP_MOVED_PERM || - statusCode == HttpURLConnection.HTTP_SEE_OTHER)) { - String newUrl = httpResponse.getFirstHeader("Location").getValue(); - if (newUrl != null && (maxRedirect >= redirects || maxRedirect == -1)) { - redirects++; - return getResponseStatus(newUrl, redirects); - } + CloseableHttpResponse httpResponse = httpClient.execute(new HttpGet(url)); + int statusCode = httpResponse.getStatusLine().getStatusCode(); + int maxRedirect = configurationService.getIntProperty("curate.checklinks.max-redirect", 0); + if ((statusCode == HttpURLConnection.HTTP_MOVED_TEMP || statusCode == HttpURLConnection.HTTP_MOVED_PERM || + statusCode == HttpURLConnection.HTTP_SEE_OTHER)) { + String newUrl = httpResponse.getFirstHeader("Location").getValue(); + if (newUrl != null && (maxRedirect >= redirects || maxRedirect == -1)) { + redirects++; + return getResponseStatus(newUrl, redirects); } - return statusCode; } + return statusCode; } catch (IOException ioe) { // Must be a bad URL log.debug("Bad link: " + ioe.getMessage()); diff --git a/dspace-api/src/main/java/org/dspace/ctask/general/MetadataWebService.java b/dspace-api/src/main/java/org/dspace/ctask/general/MetadataWebService.java index 7f61cad412e2..fc62d7a4b23f 100644 --- a/dspace-api/src/main/java/org/dspace/ctask/general/MetadataWebService.java +++ b/dspace-api/src/main/java/org/dspace/ctask/general/MetadataWebService.java @@ -270,8 +270,8 @@ protected int callService(String value, Item item, StringBuilder resultSb) throw // boiler-plate handling taken from Apache 4.1 javadoc InputStream instream = entity.getContent(); try { - // This next line triggers a false-positive XXE warning from LGTM, even though we disallow DTD - // parsing during initialization of docBuilder in init() + // This next line triggers a false-positive XXE warning from LGTM, even though + // we disallow DTD parsing during initialization of docBuilder in init() Document doc = docBuilder.parse(instream); // lgtm [java/xxe] status = processResponse(doc, item, resultSb); } catch (SAXException saxE) { From 0f1679ed7283a799364ac317c8a6fcbbfc290dcf Mon Sep 17 00:00:00 2001 From: Stefano Maffei Date: Mon, 28 Apr 2025 08:51:06 +0200 Subject: [PATCH 659/979] [DURACOM-109] fix typo and correct logic for ORCID connector --- dspace-api/pom.xml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/dspace-api/pom.xml b/dspace-api/pom.xml index bb246c20ad8f..247d2da63c16 100644 --- a/dspace-api/pom.xml +++ b/dspace-api/pom.xml @@ -521,10 +521,6 @@ org.apache.pdfbox pdfbox - - org.apache.pdfbox - fontbox - com.ibm.icu icu4j From 9d6c482cc41a4f7923feb5ca03cd11d5552a7d64 Mon Sep 17 00:00:00 2001 From: Stefano Maffei Date: Mon, 28 Apr 2025 10:50:32 +0200 Subject: [PATCH 660/979] [DURACOM-109] Orcid connector fix and improvement --- .../provider/impl/OrcidV3AuthorDataProvider.java | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/external/provider/impl/OrcidV3AuthorDataProvider.java b/dspace-api/src/main/java/org/dspace/external/provider/impl/OrcidV3AuthorDataProvider.java index dfbd07a83a02..a9e10f92948d 100644 --- a/dspace-api/src/main/java/org/dspace/external/provider/impl/OrcidV3AuthorDataProvider.java +++ b/dspace-api/src/main/java/org/dspace/external/provider/impl/OrcidV3AuthorDataProvider.java @@ -169,13 +169,7 @@ public Person getBio(String id) { } initializeAccessToken(); InputStream bioDocument = orcidRestConnector.get(id + ((id.endsWith("/person")) ? "" : "/person"), accessToken); - Person person = converter.convertSinglePerson(bioDocument); - try { - bioDocument.close(); - } catch (IOException e) { - log.error(e.getMessage(), e); - } - return person; + return converter.convertSinglePerson(bioDocument); } /** @@ -220,11 +214,6 @@ public List searchExternalDataObjects(String query, int star } } } - try { - bioDocument.close(); - } catch (IOException e) { - log.error(e.getMessage(), e); - } return bios.stream().map(bio -> convertToExternalDataObject(bio)).collect(Collectors.toList()); } From dbcaac4b0839a922e5e99f9104d617f6b650be8e Mon Sep 17 00:00:00 2001 From: Stefano Maffei Date: Tue, 29 Apr 2025 08:52:36 +0200 Subject: [PATCH 661/979] [DURACOM-109] added checkstyle rules to forbid usage of HttpClientBuilder.create() --- checkstyle-suppressions.xml | 1 + checkstyle.xml | 17 +++++++++++++++++ 2 files changed, 18 insertions(+) diff --git a/checkstyle-suppressions.xml b/checkstyle-suppressions.xml index 77e27b8768ac..46bd9ca80d62 100644 --- a/checkstyle-suppressions.xml +++ b/checkstyle-suppressions.xml @@ -7,4 +7,5 @@ + diff --git a/checkstyle.xml b/checkstyle.xml index a33fc4831950..36d2b15bd89e 100644 --- a/checkstyle.xml +++ b/checkstyle.xml @@ -136,5 +136,22 @@ For more information on CheckStyle configurations below, see: http://checkstyle. + + + + + + + + + + + + + + + + + From 409b775d3531357bf1ed45f02571447d504c2f33 Mon Sep 17 00:00:00 2001 From: Stefano Maffei Date: Tue, 29 Apr 2025 09:07:25 +0200 Subject: [PATCH 662/979] [DURACOM-109] fix TruncatedChunkException error --- .../main/java/org/dspace/external/OrcidRestConnector.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/dspace-api/src/main/java/org/dspace/external/OrcidRestConnector.java b/dspace-api/src/main/java/org/dspace/external/OrcidRestConnector.java index f2c6246f4d06..aa16af7a524d 100644 --- a/dspace-api/src/main/java/org/dspace/external/OrcidRestConnector.java +++ b/dspace-api/src/main/java/org/dspace/external/OrcidRestConnector.java @@ -7,6 +7,7 @@ */ package org.dspace.external; +import java.io.ByteArrayInputStream; import java.io.InputStream; import java.nio.charset.StandardCharsets; import java.util.Scanner; @@ -51,7 +52,11 @@ public InputStream get(String path, String accessToken) { } try (CloseableHttpClient httpClient = DSpaceHttpClientFactory.getInstance().build()) { getResponse = httpClient.execute(httpGet); - result = getResponse.getEntity().getContent(); + try (InputStream responseStream = getResponse.getEntity().getContent()) { + // Read all the content of the response stream into a byte array to prevent TruncatedChunkException + byte[] content = responseStream.readAllBytes(); + result = new ByteArrayInputStream(content); + } } catch (Exception e) { getGotError(e, fullPath); } From c1f73006da27f144357a6e394aa00f1c187d38ca Mon Sep 17 00:00:00 2001 From: Stefano Maffei Date: Wed, 26 Feb 2025 10:53:25 +0100 Subject: [PATCH 663/979] [DURACOM-328] fix error in check for Patch request (cherry picked from commit f787e0bb34b88eecd480466174e29d0f33774ed0) --- .../dspaceFolder/config/item-submission.xml | 13 +++++- .../config/spring/api/access-conditions.xml | 13 ++++++ .../app/rest/submit/step/UploadStep.java | 22 ++++++--- .../rest/WorkflowItemRestRepositoryIT.java | 46 +++++++++++++++++++ 4 files changed, 86 insertions(+), 8 deletions(-) diff --git a/dspace-api/src/test/data/dspaceFolder/config/item-submission.xml b/dspace-api/src/test/data/dspaceFolder/config/item-submission.xml index feb3c7c12b3d..51c6a228d4fb 100644 --- a/dspace-api/src/test/data/dspaceFolder/config/item-submission.xml +++ b/dspace-api/src/test/data/dspaceFolder/config/item-submission.xml @@ -29,6 +29,7 @@ + @@ -194,7 +195,11 @@ org.dspace.app.rest.submit.step.NotifyStep coarnotify - + + submit.progressbar.upload-no-required-metadata + org.dspace.app.rest.submit.step.UploadStep + upload + @@ -300,6 +305,12 @@ + + + + + + diff --git a/dspace-api/src/test/data/dspaceFolder/config/spring/api/access-conditions.xml b/dspace-api/src/test/data/dspaceFolder/config/spring/api/access-conditions.xml index a9af7c66f5e8..bf02e6a23ed2 100644 --- a/dspace-api/src/test/data/dspaceFolder/config/spring/api/access-conditions.xml +++ b/dspace-api/src/test/data/dspaceFolder/config/spring/api/access-conditions.xml @@ -55,6 +55,7 @@ + @@ -116,4 +117,16 @@ + + + + + + + + + + + + diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/step/UploadStep.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/step/UploadStep.java index 877e64756df5..cc5368add735 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/step/UploadStep.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/step/UploadStep.java @@ -11,6 +11,7 @@ import java.io.InputStream; import java.util.List; import java.util.Objects; +import java.util.regex.Pattern; import jakarta.servlet.http.HttpServletRequest; import org.apache.commons.lang3.StringUtils; @@ -48,6 +49,13 @@ public class UploadStep extends AbstractProcessingStep private static final Logger log = org.apache.logging.log4j.LogManager.getLogger(UploadStep.class); + private static final Pattern UPDATE_METADATA_PATTERN = + Pattern.compile("^/sections/[^/]+/files/[^/]+/metadata/[^/]+(/[^/]+)?$"); + private static final Pattern PRIMARY_FLAG_PATTERN = + Pattern.compile("^/sections/[^/]+/primary$"); + private static final Pattern ACCESS_CONDITION_PATTERN = + Pattern.compile("^/sections/[^/]+/files/[^/]+/accessConditions(/[^/]+)?$"); + @Override public DataUpload getData(SubmissionService submissionService, InProgressSubmission obj, SubmissionStepConfig config) throws Exception { @@ -73,27 +81,27 @@ public void doPatchProcessing(Context context, HttpServletRequest currentRequest String instance = null; if ("remove".equals(op.getOp())) { - if (op.getPath().contains(UPLOAD_STEP_METADATA_PATH)) { + if (UPDATE_METADATA_PATTERN.matcher(op.getPath()).matches()) { instance = UPLOAD_STEP_METADATA_OPERATION_ENTRY; - } else if (op.getPath().contains(UPLOAD_STEP_ACCESSCONDITIONS_OPERATION_ENTRY)) { + } else if (ACCESS_CONDITION_PATTERN.matcher(op.getPath()).matches()) { instance = stepConf.getType() + "." + UPLOAD_STEP_ACCESSCONDITIONS_OPERATION_ENTRY; - } else if (op.getPath().contains(PRIMARY_FLAG_ENTRY)) { + } else if (PRIMARY_FLAG_PATTERN.matcher(op.getPath()).matches()) { instance = PRIMARY_FLAG_ENTRY; } else { instance = UPLOAD_STEP_REMOVE_OPERATION_ENTRY; } } else if ("move".equals(op.getOp())) { - if (op.getPath().contains(UPLOAD_STEP_METADATA_PATH)) { + if (UPDATE_METADATA_PATTERN.matcher(op.getPath()).matches()) { instance = UPLOAD_STEP_METADATA_OPERATION_ENTRY; } else { instance = UPLOAD_STEP_MOVE_OPERATION_ENTRY; } } else { - if (op.getPath().contains(UPLOAD_STEP_ACCESSCONDITIONS_OPERATION_ENTRY)) { + if (ACCESS_CONDITION_PATTERN.matcher(op.getPath()).matches()) { instance = stepConf.getType() + "." + UPLOAD_STEP_ACCESSCONDITIONS_OPERATION_ENTRY; - } else if (op.getPath().contains(UPLOAD_STEP_METADATA_PATH)) { + } else if (UPDATE_METADATA_PATTERN.matcher(op.getPath()).matches()) { instance = UPLOAD_STEP_METADATA_OPERATION_ENTRY; - } else if (op.getPath().contains(PRIMARY_FLAG_ENTRY)) { + } else if (PRIMARY_FLAG_PATTERN.matcher(op.getPath()).matches()) { instance = PRIMARY_FLAG_ENTRY; } } diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/WorkflowItemRestRepositoryIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/WorkflowItemRestRepositoryIT.java index a9bbc752f875..9eebed85f652 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/WorkflowItemRestRepositoryIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/WorkflowItemRestRepositoryIT.java @@ -9,6 +9,7 @@ import static com.jayway.jsonpath.JsonPath.read; import static com.jayway.jsonpath.matchers.JsonPathMatchers.hasJsonPath; +import static org.hamcrest.Matchers.hasSize; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.nullValue; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete; @@ -2218,4 +2219,49 @@ public void testWorkflowWithHiddenSections() throws Exception { } + @Test + public void deleteBitstreamTest() + throws Exception { + context.turnOffAuthorisationSystem(); + EPerson submitter = EPersonBuilder.createEPerson(context) + .withEmail("submitter@example.com") + .withPassword(password) + .build(); + parentCommunity = CommunityBuilder.createCommunity(context) + .withName("Parent Community") + .build(); + Collection collection1 = CollectionBuilder.createCollection(context, parentCommunity, + "123456789/collection-test-patch") + .withName("Collection 1") + .build(); + Bitstream bitstream = null; + WorkspaceItem witem = null; + String bitstreamContent = "0123456789"; + try (InputStream is = IOUtils.toInputStream(bitstreamContent, Charset.defaultCharset())) { + context.setCurrentUser(submitter); + witem = WorkspaceItemBuilder.createWorkspaceItem(context, collection1) + .withTitle("Test WorkspaceItem") + .withIssueDate("2019-10-01") + .grantLicense() + .build(); + bitstream = BitstreamBuilder.createBitstream(context, witem.getItem(), is) + .withName("Test bitstream") + .withDescription("This is a bitstream to test range requests") + .withMimeType("text/plain") + .build(); + } + context.restoreAuthSystemState(); + String tokenSubmitter = getAuthToken(submitter.getEmail(), password); + List deleteFile = new ArrayList<>(); + deleteFile.add(new RemoveOperation("/sections/bitstream-metadata-publication/files/0/")); + getClient(tokenSubmitter).perform(patch("/api/submission/workspaceitems/" + witem.getID()) + .content(getPatchContent(deleteFile)) + .contentType(MediaType.APPLICATION_JSON_PATCH_JSON)) + .andExpect(status().isOk()); + // verify that the patch removed bitstream + getClient(tokenSubmitter).perform(get("/api/submission/workspaceitems/" + witem.getID())) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.sections.bitstream-metadata-publication.files",hasSize(0))); + } + } From 742e777e2bfbfcde664a9e91befd8bc73238c0fd Mon Sep 17 00:00:00 2001 From: Stefano Maffei Date: Wed, 26 Feb 2025 11:18:36 +0100 Subject: [PATCH 664/979] [DURACOM-328] fix test (cherry picked from commit a6529b413fe65a7485ca8ed6710b38334e4a8572) --- .../org/dspace/app/rest/WorkflowItemRestRepositoryIT.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/WorkflowItemRestRepositoryIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/WorkflowItemRestRepositoryIT.java index 9eebed85f652..ef475353b93e 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/WorkflowItemRestRepositoryIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/WorkflowItemRestRepositoryIT.java @@ -2253,7 +2253,7 @@ public void deleteBitstreamTest() context.restoreAuthSystemState(); String tokenSubmitter = getAuthToken(submitter.getEmail(), password); List deleteFile = new ArrayList<>(); - deleteFile.add(new RemoveOperation("/sections/bitstream-metadata-publication/files/0/")); + deleteFile.add(new RemoveOperation("/sections/upload-no-required-metadata/files/0/")); getClient(tokenSubmitter).perform(patch("/api/submission/workspaceitems/" + witem.getID()) .content(getPatchContent(deleteFile)) .contentType(MediaType.APPLICATION_JSON_PATCH_JSON)) @@ -2261,7 +2261,7 @@ public void deleteBitstreamTest() // verify that the patch removed bitstream getClient(tokenSubmitter).perform(get("/api/submission/workspaceitems/" + witem.getID())) .andExpect(status().isOk()) - .andExpect(jsonPath("$.sections.bitstream-metadata-publication.files",hasSize(0))); + .andExpect(jsonPath("$.sections.upload-no-required-metadata.files",hasSize(0))); } } From 032252664b3c7997231fdb8b10f3fe3c4951cdd9 Mon Sep 17 00:00:00 2001 From: Stefano Maffei Date: Wed, 26 Feb 2025 10:53:25 +0100 Subject: [PATCH 665/979] [DURACOM-328] fix error in check for Patch request --- .../dspaceFolder/config/item-submission.xml | 12 +++++ .../config/spring/api/access-conditions.xml | 13 ++++++ .../app/rest/submit/step/UploadStep.java | 18 ++++++-- .../rest/WorkflowItemRestRepositoryIT.java | 46 +++++++++++++++++++ 4 files changed, 84 insertions(+), 5 deletions(-) diff --git a/dspace-api/src/test/data/dspaceFolder/config/item-submission.xml b/dspace-api/src/test/data/dspaceFolder/config/item-submission.xml index 2b4cee044916..0ffac4b49db8 100644 --- a/dspace-api/src/test/data/dspaceFolder/config/item-submission.xml +++ b/dspace-api/src/test/data/dspaceFolder/config/item-submission.xml @@ -26,6 +26,7 @@ + @@ -179,6 +180,11 @@ submission + + submit.progressbar.upload-no-required-metadata + org.dspace.app.rest.submit.step.UploadStep + upload + @@ -267,6 +273,12 @@ + + + + + + diff --git a/dspace-api/src/test/data/dspaceFolder/config/spring/api/access-conditions.xml b/dspace-api/src/test/data/dspaceFolder/config/spring/api/access-conditions.xml index a9af7c66f5e8..bf02e6a23ed2 100644 --- a/dspace-api/src/test/data/dspaceFolder/config/spring/api/access-conditions.xml +++ b/dspace-api/src/test/data/dspaceFolder/config/spring/api/access-conditions.xml @@ -55,6 +55,7 @@ + @@ -116,4 +117,16 @@ + + + + + + + + + + + + diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/step/UploadStep.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/step/UploadStep.java index b91916ca3199..89003549f73c 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/step/UploadStep.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/step/UploadStep.java @@ -10,6 +10,7 @@ import java.io.BufferedInputStream; import java.io.InputStream; import java.util.List; +import java.util.regex.Pattern; import javax.servlet.http.HttpServletRequest; import org.apache.logging.log4j.Logger; @@ -48,6 +49,13 @@ public class UploadStep extends AbstractProcessingStep public static final String UPLOAD_STEP_METADATA_SECTION = "bitstream-metadata"; + private static final Pattern UPDATE_METADATA_PATTERN = + Pattern.compile("^/sections/[^/]+/files/[^/]+/metadata/[^/]+(/[^/]+)?$"); + private static final Pattern PRIMARY_FLAG_PATTERN = + Pattern.compile("^/sections/[^/]+/primary$"); + private static final Pattern ACCESS_CONDITION_PATTERN = + Pattern.compile("^/sections/[^/]+/files/[^/]+/accessConditions(/[^/]+)?$"); + @Override public DataUpload getData(SubmissionService submissionService, InProgressSubmission obj, SubmissionStepConfig config) throws Exception { @@ -69,23 +77,23 @@ public void doPatchProcessing(Context context, HttpServletRequest currentRequest String instance = null; if ("remove".equals(op.getOp())) { - if (op.getPath().contains(UPLOAD_STEP_METADATA_PATH)) { + if (UPDATE_METADATA_PATTERN.matcher(op.getPath()).matches()) { instance = UPLOAD_STEP_METADATA_OPERATION_ENTRY; - } else if (op.getPath().contains(UPLOAD_STEP_ACCESSCONDITIONS_OPERATION_ENTRY)) { + } else if (ACCESS_CONDITION_PATTERN.matcher(op.getPath()).matches()) { instance = stepConf.getType() + "." + UPLOAD_STEP_ACCESSCONDITIONS_OPERATION_ENTRY; } else { instance = UPLOAD_STEP_REMOVE_OPERATION_ENTRY; } } else if ("move".equals(op.getOp())) { - if (op.getPath().contains(UPLOAD_STEP_METADATA_PATH)) { + if (UPDATE_METADATA_PATTERN.matcher(op.getPath()).matches()) { instance = UPLOAD_STEP_METADATA_OPERATION_ENTRY; } else { instance = UPLOAD_STEP_MOVE_OPERATION_ENTRY; } } else { - if (op.getPath().contains(UPLOAD_STEP_ACCESSCONDITIONS_OPERATION_ENTRY)) { + if (ACCESS_CONDITION_PATTERN.matcher(op.getPath()).matches()) { instance = stepConf.getType() + "." + UPLOAD_STEP_ACCESSCONDITIONS_OPERATION_ENTRY; - } else if (op.getPath().contains(UPLOAD_STEP_METADATA_PATH)) { + } else if (UPDATE_METADATA_PATTERN.matcher(op.getPath()).matches()) { instance = UPLOAD_STEP_METADATA_OPERATION_ENTRY; } } diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/WorkflowItemRestRepositoryIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/WorkflowItemRestRepositoryIT.java index fe8e67bbf564..de98f1b0cea6 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/WorkflowItemRestRepositoryIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/WorkflowItemRestRepositoryIT.java @@ -9,6 +9,7 @@ import static com.jayway.jsonpath.JsonPath.read; import static com.jayway.jsonpath.matchers.JsonPathMatchers.hasJsonPath; +import static org.hamcrest.Matchers.hasSize; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.nullValue; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete; @@ -2218,4 +2219,49 @@ public void testWorkflowWithHiddenSections() throws Exception { } + @Test + public void deleteBitstreamTest() + throws Exception { + context.turnOffAuthorisationSystem(); + EPerson submitter = EPersonBuilder.createEPerson(context) + .withEmail("submitter@example.com") + .withPassword(password) + .build(); + parentCommunity = CommunityBuilder.createCommunity(context) + .withName("Parent Community") + .build(); + Collection collection1 = CollectionBuilder.createCollection(context, parentCommunity, + "123456789/collection-test-patch") + .withName("Collection 1") + .build(); + Bitstream bitstream = null; + WorkspaceItem witem = null; + String bitstreamContent = "0123456789"; + try (InputStream is = IOUtils.toInputStream(bitstreamContent, Charset.defaultCharset())) { + context.setCurrentUser(submitter); + witem = WorkspaceItemBuilder.createWorkspaceItem(context, collection1) + .withTitle("Test WorkspaceItem") + .withIssueDate("2019-10-01") + .grantLicense() + .build(); + bitstream = BitstreamBuilder.createBitstream(context, witem.getItem(), is) + .withName("Test bitstream") + .withDescription("This is a bitstream to test range requests") + .withMimeType("text/plain") + .build(); + } + context.restoreAuthSystemState(); + String tokenSubmitter = getAuthToken(submitter.getEmail(), password); + List deleteFile = new ArrayList<>(); + deleteFile.add(new RemoveOperation("/sections/bitstream-metadata-publication/files/0/")); + getClient(tokenSubmitter).perform(patch("/api/submission/workspaceitems/" + witem.getID()) + .content(getPatchContent(deleteFile)) + .contentType(MediaType.APPLICATION_JSON_PATCH_JSON)) + .andExpect(status().isOk()); + // verify that the patch removed bitstream + getClient(tokenSubmitter).perform(get("/api/submission/workspaceitems/" + witem.getID())) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.sections.bitstream-metadata-publication.files",hasSize(0))); + } + } From 0ef3b27189dab67f3176831168f8ee4fc26d0c96 Mon Sep 17 00:00:00 2001 From: Stefano Maffei Date: Wed, 26 Feb 2025 11:18:36 +0100 Subject: [PATCH 666/979] [DURACOM-328] fix test --- .../org/dspace/app/rest/WorkflowItemRestRepositoryIT.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/WorkflowItemRestRepositoryIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/WorkflowItemRestRepositoryIT.java index de98f1b0cea6..8a19678f5102 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/WorkflowItemRestRepositoryIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/WorkflowItemRestRepositoryIT.java @@ -2253,7 +2253,7 @@ public void deleteBitstreamTest() context.restoreAuthSystemState(); String tokenSubmitter = getAuthToken(submitter.getEmail(), password); List deleteFile = new ArrayList<>(); - deleteFile.add(new RemoveOperation("/sections/bitstream-metadata-publication/files/0/")); + deleteFile.add(new RemoveOperation("/sections/upload-no-required-metadata/files/0/")); getClient(tokenSubmitter).perform(patch("/api/submission/workspaceitems/" + witem.getID()) .content(getPatchContent(deleteFile)) .contentType(MediaType.APPLICATION_JSON_PATCH_JSON)) @@ -2261,7 +2261,7 @@ public void deleteBitstreamTest() // verify that the patch removed bitstream getClient(tokenSubmitter).perform(get("/api/submission/workspaceitems/" + witem.getID())) .andExpect(status().isOk()) - .andExpect(jsonPath("$.sections.bitstream-metadata-publication.files",hasSize(0))); + .andExpect(jsonPath("$.sections.upload-no-required-metadata.files",hasSize(0))); } } From ac20eefe4b6554439ac54c5ffe95348ec8466a7a Mon Sep 17 00:00:00 2001 From: Stefano Maffei Date: Wed, 7 May 2025 10:43:19 +0200 Subject: [PATCH 667/979] [DURACOM-328] fix tests --- .../org/dspace/app/rest/SubmissionDefinitionsControllerIT.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/SubmissionDefinitionsControllerIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/SubmissionDefinitionsControllerIT.java index 1a1a4576dc89..a4ef63afa6ef 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/SubmissionDefinitionsControllerIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/SubmissionDefinitionsControllerIT.java @@ -35,7 +35,7 @@ public class SubmissionDefinitionsControllerIT extends AbstractControllerIntegra // The total number of expected submission definitions is referred to in multiple tests and assertions as // is the last page (totalDefinitions - 1) // This integer should be maintained along with any changes to item-submissions.xml - private static final int totalDefinitions = 9; + private static final int totalDefinitions = 10; @Test public void findAll() throws Exception { From 1ade96098802cd30880b75dee37cbe92cca6542c Mon Sep 17 00:00:00 2001 From: Elios Buzo Date: Wed, 7 May 2025 12:28:43 +0200 Subject: [PATCH 668/979] [DURACOM-109] Fixed conflicts --- .../org/dspace/app/sherpa/SHERPAService.java | 118 +++++++++--------- .../dspace/eperson/CaptchaServiceImpl.java | 19 ++- .../external/OpenAIRERestConnector.java | 118 +++++++++--------- .../client/GoogleAnalyticsClientImpl.java | 4 +- .../identifier/doi/DataCiteConnector.java | 4 +- .../dspace/identifier/ezid/EZIDRequest.java | 6 +- .../model/factory/OrcidFactoryUtils.java | 4 +- 7 files changed, 138 insertions(+), 135 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/app/sherpa/SHERPAService.java b/dspace-api/src/main/java/org/dspace/app/sherpa/SHERPAService.java index 87042c502d83..cb1125f61c46 100644 --- a/dspace-api/src/main/java/org/dspace/app/sherpa/SHERPAService.java +++ b/dspace-api/src/main/java/org/dspace/app/sherpa/SHERPAService.java @@ -117,46 +117,47 @@ public SHERPAPublisherResponse performPublisherRequest(String type, String field timeout, sleepBetweenTimeouts)); - try (CloseableHttpClient client = DSpaceHttpClientFactory.getInstance().build()) { + try (CloseableHttpClient client = DSpaceHttpClientFactory.getInstance().buildWithoutAutomaticRetries(5)) { Thread.sleep(sleepBetweenTimeouts); // Construct a default HTTP method (first result) method = constructHttpGet(type, field, predicate, value, start, limit); // Execute the method - CloseableHttpResponse response = client.execute(method); - int statusCode = response.getStatusLine().getStatusCode(); + try (CloseableHttpResponse response = client.execute(method)) { + int statusCode = response.getStatusLine().getStatusCode(); - log.debug(response.getStatusLine().getStatusCode() + ": " - + response.getStatusLine().getReasonPhrase()); + log.debug(response.getStatusLine().getStatusCode() + ": " + + response.getStatusLine().getReasonPhrase()); - if (statusCode != HttpStatus.SC_OK) { - sherpaResponse = new SHERPAPublisherResponse("SHERPA/RoMEO return not OK status: " - + statusCode); - String errorBody = IOUtils.toString(response.getEntity().getContent(), StandardCharsets.UTF_8); - log.error("Error from SHERPA HTTP request: " + errorBody); - } + if (statusCode != HttpStatus.SC_OK) { + sherpaResponse = new SHERPAPublisherResponse("SHERPA/RoMEO return not OK status: " + + statusCode); + String errorBody = IOUtils.toString(response.getEntity().getContent(), StandardCharsets.UTF_8); + log.error("Error from SHERPA HTTP request: " + errorBody); + } - HttpEntity responseBody = response.getEntity(); - - // If the response body is valid, pass to SHERPAResponse for parsing as JSON - if (null != responseBody) { - log.debug("Non-null SHERPA resonse received for query of " + value); - InputStream content = null; - try { - content = responseBody.getContent(); - sherpaResponse = - new SHERPAPublisherResponse(content, SHERPAPublisherResponse.SHERPAFormat.JSON); - } catch (IOException e) { - log.error("Encountered exception while contacting SHERPA/RoMEO: " + e.getMessage(), e); - } finally { - if (content != null) { - content.close(); + HttpEntity responseBody = response.getEntity(); + + // If the response body is valid, pass to SHERPAResponse for parsing as JSON + if (null != responseBody) { + log.debug("Non-null SHERPA response received for query of " + value); + InputStream content = null; + try { + content = responseBody.getContent(); + sherpaResponse = + new SHERPAPublisherResponse(content, SHERPAPublisherResponse.SHERPAFormat.JSON); + } catch (IOException e) { + log.error("Encountered exception while contacting SHERPA/RoMEO: " + e.getMessage(), e); + } finally { + if (content != null) { + content.close(); + } } + } else { + log.debug("Empty SHERPA response body for query on " + value); + sherpaResponse = new SHERPAPublisherResponse("SHERPA/RoMEO returned no response"); } - } else { - log.debug("Empty SHERPA response body for query on " + value); - sherpaResponse = new SHERPAPublisherResponse("SHERPA/RoMEO returned no response"); } } catch (URISyntaxException e) { String errorMessage = "Error building SHERPA v2 API URI: " + e.getMessage(); @@ -220,45 +221,46 @@ public SHERPAResponse performRequest(String type, String field, String predicate timeout, sleepBetweenTimeouts)); - try (CloseableHttpClient client = DSpaceHttpClientFactory.getInstance().build()) { + try (CloseableHttpClient client = DSpaceHttpClientFactory.getInstance().buildWithoutAutomaticRetries(5)) { Thread.sleep(sleepBetweenTimeouts); // Construct a default HTTP method (first result) method = constructHttpGet(type, field, predicate, value, start, limit); // Execute the method - CloseableHttpResponse response = client.execute(method); - int statusCode = response.getStatusLine().getStatusCode(); + try (CloseableHttpResponse response = client.execute(method)) { + int statusCode = response.getStatusLine().getStatusCode(); - log.debug(response.getStatusLine().getStatusCode() + ": " - + response.getStatusLine().getReasonPhrase()); + log.debug(response.getStatusLine().getStatusCode() + ": " + + response.getStatusLine().getReasonPhrase()); - if (statusCode != HttpStatus.SC_OK) { - sherpaResponse = new SHERPAResponse("SHERPA/RoMEO return not OK status: " - + statusCode); - String errorBody = IOUtils.toString(response.getEntity().getContent(), StandardCharsets.UTF_8); - log.error("Error from SHERPA HTTP request: " + errorBody); - } + if (statusCode != HttpStatus.SC_OK) { + sherpaResponse = new SHERPAResponse("SHERPA/RoMEO return not OK status: " + + statusCode); + String errorBody = IOUtils.toString(response.getEntity().getContent(), StandardCharsets.UTF_8); + log.error("Error from SHERPA HTTP request: " + errorBody); + } - HttpEntity responseBody = response.getEntity(); - - // If the response body is valid, pass to SHERPAResponse for parsing as JSON - if (null != responseBody) { - log.debug("Non-null SHERPA resonse received for query of " + value); - InputStream content = null; - try { - content = responseBody.getContent(); - sherpaResponse = new SHERPAResponse(content, SHERPAResponse.SHERPAFormat.JSON); - } catch (IOException e) { - log.error("Encountered exception while contacting SHERPA/RoMEO: " + e.getMessage(), e); - } finally { - if (content != null) { - content.close(); + HttpEntity responseBody = response.getEntity(); + + // If the response body is valid, pass to SHERPAResponse for parsing as JSON + if (null != responseBody) { + log.debug("Non-null SHERPA response received for query of " + value); + InputStream content = null; + try { + content = responseBody.getContent(); + sherpaResponse = new SHERPAResponse(content, SHERPAResponse.SHERPAFormat.JSON); + } catch (IOException e) { + log.error("Encountered exception while contacting SHERPA/RoMEO: " + e.getMessage(), e); + } finally { + if (content != null) { + content.close(); + } } + } else { + log.debug("Empty SHERPA response body for query on " + value); + sherpaResponse = new SHERPAResponse("SHERPA/RoMEO returned no response"); } - } else { - log.debug("Empty SHERPA response body for query on " + value); - sherpaResponse = new SHERPAResponse("SHERPA/RoMEO returned no response"); } } catch (URISyntaxException e) { String errorMessage = "Error building SHERPA v2 API URI: " + e.getMessage(); @@ -268,7 +270,7 @@ public SHERPAResponse performRequest(String type, String field, String predicate String errorMessage = "Encountered exception while contacting SHERPA/RoMEO: " + e.getMessage(); log.error(errorMessage, e); sherpaResponse = new SHERPAResponse(errorMessage); - } catch (InterruptedException e) { + } catch (InterruptedException e) { String errorMessage = "Encountered exception while sleeping thread: " + e.getMessage(); log.error(errorMessage, e); sherpaResponse = new SHERPAResponse(errorMessage); diff --git a/dspace-api/src/main/java/org/dspace/eperson/CaptchaServiceImpl.java b/dspace-api/src/main/java/org/dspace/eperson/CaptchaServiceImpl.java index e10de57e47a6..3e967d59b6f3 100644 --- a/dspace-api/src/main/java/org/dspace/eperson/CaptchaServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/eperson/CaptchaServiceImpl.java @@ -17,11 +17,11 @@ import javax.annotation.PostConstruct; import com.fasterxml.jackson.databind.ObjectMapper; -import org.apache.http.HttpResponse; import org.apache.http.NameValuePair; -import org.apache.http.client.HttpClient; import org.apache.http.client.entity.UrlEncodedFormEntity; +import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpPost; +import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.message.BasicNameValuePair; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -82,18 +82,17 @@ public void processResponse(String response, String action) throws InvalidReCapt throw new RuntimeException(e.getMessage(), e); } - HttpClient httpClient = DSpaceHttpClientFactory.getInstance().build(); - HttpResponse httpResponse; - GoogleCaptchaResponse googleResponse; - final ObjectMapper objectMapper = new ObjectMapper(); - try { - httpResponse = httpClient.execute(httpPost); - googleResponse = objectMapper.readValue(httpResponse.getEntity().getContent(), GoogleCaptchaResponse.class); + try (CloseableHttpClient httpClient = DSpaceHttpClientFactory.getInstance().build()) { + final ObjectMapper objectMapper = new ObjectMapper(); + try (CloseableHttpResponse httpResponse = httpClient.execute(httpPost)) { + GoogleCaptchaResponse googleResponse = objectMapper.readValue(httpResponse.getEntity().getContent(), + GoogleCaptchaResponse.class); + validateGoogleResponse(googleResponse, action); + } } catch (IOException e) { log.error(e.getMessage(), e); throw new RuntimeException("Error during verify google recaptcha site", e); } - validateGoogleResponse(googleResponse, action); } private boolean responseSanityCheck(String response) { diff --git a/dspace-api/src/main/java/org/dspace/external/OpenAIRERestConnector.java b/dspace-api/src/main/java/org/dspace/external/OpenAIRERestConnector.java index 06a4b8361436..8b5fb1e523c5 100644 --- a/dspace-api/src/main/java/org/dspace/external/OpenAIRERestConnector.java +++ b/dspace-api/src/main/java/org/dspace/external/OpenAIRERestConnector.java @@ -28,10 +28,10 @@ import org.apache.http.NameValuePair; import org.apache.http.NoHttpResponseException; import org.apache.http.StatusLine; -import org.apache.http.client.HttpClient; import org.apache.http.client.entity.UrlEncodedFormEntity; import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpPost; +import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.message.BasicNameValuePair; import org.apache.logging.log4j.Logger; import org.dspace.app.client.DSpaceHttpClientFactory; @@ -121,33 +121,34 @@ public OpenAIRERestToken grabNewAccessToken() throws IOException { params.add(new BasicNameValuePair("grant_type", "client_credentials")); httpPost.setEntity(new UrlEncodedFormEntity(params, "UTF-8")); - HttpClient httpClient = DSpaceHttpClientFactory.getInstance().build(); - HttpResponse getResponse = httpClient.execute(httpPost); - - JSONObject responseObject = null; - try (InputStream is = getResponse.getEntity().getContent(); - BufferedReader streamReader = new BufferedReader(new InputStreamReader(is, "UTF-8"))) { - String inputStr; - // verify if we have basic json - while ((inputStr = streamReader.readLine()) != null && responseObject == null) { - if (inputStr.startsWith("{") && inputStr.endsWith("}") && inputStr.contains("access_token") - && inputStr.contains("expires_in")) { - try { - responseObject = new JSONObject(inputStr); - } catch (Exception e) { - // Not as valid as I'd hoped, move along - responseObject = null; + try (CloseableHttpClient httpClient = DSpaceHttpClientFactory.getInstance().build()) { + HttpResponse getResponse = httpClient.execute(httpPost); + + JSONObject responseObject = null; + try (InputStream is = getResponse.getEntity().getContent(); + BufferedReader streamReader = new BufferedReader(new InputStreamReader(is, "UTF-8"))) { + String inputStr; + // verify if we have basic json + while ((inputStr = streamReader.readLine()) != null && responseObject == null) { + if (inputStr.startsWith("{") && inputStr.endsWith("}") && inputStr.contains("access_token") + && inputStr.contains("expires_in")) { + try { + responseObject = new JSONObject(inputStr); + } catch (Exception e) { + // Not as valid as I'd hoped, move along + responseObject = null; + } } } } - } - if (responseObject == null || !responseObject.has("access_token") || !responseObject.has("expires_in")) { - throw new IOException("Unable to grab the access token using provided service url, client id and secret"); - } - - return new OpenAIRERestToken(responseObject.get("access_token").toString(), - Long.valueOf(responseObject.get("expires_in").toString())); + if (responseObject == null || !responseObject.has("access_token") || !responseObject.has("expires_in")) { + throw new IOException("Unable to grab the access token using provided service url, " + + "client id and secret"); + } + return new OpenAIRERestToken(responseObject.get("access_token").toString(), + Long.valueOf(responseObject.get("expires_in").toString())); + } } /** @@ -172,42 +173,43 @@ public InputStream get(String file, String accessToken) { httpGet.addHeader("Authorization", "Bearer " + accessToken); } - HttpClient httpClient = DSpaceHttpClientFactory.getInstance().build(); - getResponse = httpClient.execute(httpGet); - - StatusLine status = getResponse.getStatusLine(); - - // registering errors - switch (status.getStatusCode()) { - case HttpStatus.SC_NOT_FOUND: - // 404 - Not found - case HttpStatus.SC_FORBIDDEN: - // 403 - Invalid Access Token - case 429: - // 429 - Rate limit abuse for unauthenticated user - Header[] limitUsed = getResponse.getHeaders("x-ratelimit-used"); - Header[] limitMax = getResponse.getHeaders("x-ratelimit-limit"); - - if (limitUsed.length > 0) { - String limitMsg = limitUsed[0].getValue(); - if (limitMax.length > 0) { - limitMsg = limitMsg.concat(" of " + limitMax[0].getValue()); + try (CloseableHttpClient httpClient = DSpaceHttpClientFactory.getInstance().build()) { + getResponse = httpClient.execute(httpGet); + + StatusLine status = getResponse.getStatusLine(); + + // registering errors + switch (status.getStatusCode()) { + case HttpStatus.SC_NOT_FOUND: + // 404 - Not found + case HttpStatus.SC_FORBIDDEN: + // 403 - Invalid Access Token + case 429: + // 429 - Rate limit abuse for unauthenticated user + Header[] limitUsed = getResponse.getHeaders("x-ratelimit-used"); + Header[] limitMax = getResponse.getHeaders("x-ratelimit-limit"); + + if (limitUsed.length > 0) { + String limitMsg = limitUsed[0].getValue(); + if (limitMax.length > 0) { + limitMsg = limitMsg.concat(" of " + limitMax[0].getValue()); + } + getGotError(new NoHttpResponseException(status.getReasonPhrase() + " with usage limit " + + limitMsg), + url + '/' + file); + } else { + // 429 - Rate limit abuse + getGotError(new NoHttpResponseException(status.getReasonPhrase()), url + '/' + file); } - getGotError( - new NoHttpResponseException(status.getReasonPhrase() + " with usage limit " + limitMsg), - url + '/' + file); - } else { - // 429 - Rate limit abuse - getGotError(new NoHttpResponseException(status.getReasonPhrase()), url + '/' + file); - } - break; - default: - // 200 or other - break; - } + break; + default: + // 200 or other + break; + } - // do not close this httpClient - result = getResponse.getEntity().getContent(); + // do not close this httpClient + result = getResponse.getEntity().getContent(); + } } catch (MalformedURLException e1) { getGotError(e1, url + '/' + file); } catch (Exception e) { diff --git a/dspace-api/src/main/java/org/dspace/google/client/GoogleAnalyticsClientImpl.java b/dspace-api/src/main/java/org/dspace/google/client/GoogleAnalyticsClientImpl.java index b5ee1806cd56..6fa748cfd450 100644 --- a/dspace-api/src/main/java/org/dspace/google/client/GoogleAnalyticsClientImpl.java +++ b/dspace-api/src/main/java/org/dspace/google/client/GoogleAnalyticsClientImpl.java @@ -18,7 +18,7 @@ import org.apache.http.client.methods.HttpPost; import org.apache.http.entity.StringEntity; import org.apache.http.impl.client.CloseableHttpClient; -import org.apache.http.impl.client.HttpClients; +import org.dspace.app.client.DSpaceHttpClientFactory; import org.dspace.google.GoogleAnalyticsEvent; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -42,7 +42,7 @@ public class GoogleAnalyticsClientImpl implements GoogleAnalyticsClient { public GoogleAnalyticsClientImpl(String keyPrefix, GoogleAnalyticsClientRequestBuilder requestBuilder) { this.keyPrefix = keyPrefix; this.requestBuilder = requestBuilder; - this.httpclient = HttpClients.createDefault(); + this.httpclient = DSpaceHttpClientFactory.getInstance().build(); } @Override diff --git a/dspace-api/src/main/java/org/dspace/identifier/doi/DataCiteConnector.java b/dspace-api/src/main/java/org/dspace/identifier/doi/DataCiteConnector.java index 5bb37add2d9a..86c7b8322e81 100644 --- a/dspace-api/src/main/java/org/dspace/identifier/doi/DataCiteConnector.java +++ b/dspace-api/src/main/java/org/dspace/identifier/doi/DataCiteConnector.java @@ -36,8 +36,8 @@ import org.apache.http.entity.StringEntity; import org.apache.http.impl.client.BasicCredentialsProvider; import org.apache.http.impl.client.CloseableHttpClient; -import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.util.EntityUtils; +import org.dspace.app.client.DSpaceHttpClientFactory; import org.dspace.authorize.AuthorizeException; import org.dspace.content.DSpaceObject; import org.dspace.content.crosswalk.CrosswalkException; @@ -722,7 +722,7 @@ protected DataCiteResponse sendHttpRequest(HttpUriRequest req, String doi) httpContext.setCredentialsProvider(credentialsProvider); HttpEntity entity = null; - try ( CloseableHttpClient httpclient = HttpClientBuilder.create().build(); ) { + try (CloseableHttpClient httpclient = DSpaceHttpClientFactory.getInstance().build()) { HttpResponse response = httpclient.execute(req, httpContext); StatusLine status = response.getStatusLine(); diff --git a/dspace-api/src/main/java/org/dspace/identifier/ezid/EZIDRequest.java b/dspace-api/src/main/java/org/dspace/identifier/ezid/EZIDRequest.java index 525ad46b2554..8da40470d6a1 100644 --- a/dspace-api/src/main/java/org/dspace/identifier/ezid/EZIDRequest.java +++ b/dspace-api/src/main/java/org/dspace/identifier/ezid/EZIDRequest.java @@ -26,7 +26,7 @@ import org.apache.http.entity.StringEntity; import org.apache.http.impl.client.BasicCredentialsProvider; import org.apache.http.impl.client.CloseableHttpClient; -import org.apache.http.impl.client.HttpClientBuilder; +import org.dspace.app.client.DSpaceHttpClientFactory; import org.dspace.identifier.DOI; import org.dspace.identifier.IdentifierException; import org.slf4j.Logger; @@ -87,7 +87,7 @@ public class EZIDRequest { this.authority = authority; } - client = HttpClientBuilder.create().build(); + client = DSpaceHttpClientFactory.getInstance().build(); httpContext = HttpClientContext.create(); if (null != username) { URI uri = new URI(scheme, host, path, null); @@ -124,7 +124,7 @@ public class EZIDRequest { this.authority = authority; } - client = HttpClientBuilder.create().build(); + client = DSpaceHttpClientFactory.getInstance().build(); httpContext = HttpClientContext.create(); if (null != username) { CredentialsProvider credentialsProvider = new BasicCredentialsProvider(); diff --git a/dspace-api/src/main/java/org/dspace/orcid/model/factory/OrcidFactoryUtils.java b/dspace-api/src/main/java/org/dspace/orcid/model/factory/OrcidFactoryUtils.java index 38aa611ff3a0..ce68ab47c26e 100644 --- a/dspace-api/src/main/java/org/dspace/orcid/model/factory/OrcidFactoryUtils.java +++ b/dspace-api/src/main/java/org/dspace/orcid/model/factory/OrcidFactoryUtils.java @@ -20,7 +20,7 @@ import org.apache.http.HttpResponse; import org.apache.http.client.methods.HttpPost; import org.apache.http.impl.client.CloseableHttpClient; -import org.apache.http.impl.client.HttpClientBuilder; +import org.dspace.app.client.DSpaceHttpClientFactory; import org.json.JSONObject; /** @@ -97,7 +97,7 @@ public static Optional retrieveAccessToken(String clientId, String clien httpPost.addHeader("Content-Type", "application/x-www-form-urlencoded"); HttpResponse response; - try (CloseableHttpClient httpClient = HttpClientBuilder.create().build()) { + try (CloseableHttpClient httpClient = DSpaceHttpClientFactory.getInstance().build()) { response = httpClient.execute(httpPost); } JSONObject responseObject = null; From 1383f47babbe20f3c0b041cf7450d8c14ce04ef0 Mon Sep 17 00:00:00 2001 From: Elios Buzo Date: Wed, 7 May 2025 12:31:58 +0200 Subject: [PATCH 669/979] [DURACOM-109] Minor fix --- .../org/dspace/orcid/model/factory/OrcidFactoryUtils.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/orcid/model/factory/OrcidFactoryUtils.java b/dspace-api/src/main/java/org/dspace/orcid/model/factory/OrcidFactoryUtils.java index 38aa611ff3a0..ce68ab47c26e 100644 --- a/dspace-api/src/main/java/org/dspace/orcid/model/factory/OrcidFactoryUtils.java +++ b/dspace-api/src/main/java/org/dspace/orcid/model/factory/OrcidFactoryUtils.java @@ -20,7 +20,7 @@ import org.apache.http.HttpResponse; import org.apache.http.client.methods.HttpPost; import org.apache.http.impl.client.CloseableHttpClient; -import org.apache.http.impl.client.HttpClientBuilder; +import org.dspace.app.client.DSpaceHttpClientFactory; import org.json.JSONObject; /** @@ -97,7 +97,7 @@ public static Optional retrieveAccessToken(String clientId, String clien httpPost.addHeader("Content-Type", "application/x-www-form-urlencoded"); HttpResponse response; - try (CloseableHttpClient httpClient = HttpClientBuilder.create().build()) { + try (CloseableHttpClient httpClient = DSpaceHttpClientFactory.getInstance().build()) { response = httpClient.execute(httpPost); } JSONObject responseObject = null; From 6e9579a0d704fe50f66a635056021df5a7a24a93 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Wed, 7 May 2025 09:15:57 -0500 Subject: [PATCH 670/979] Fix test by increasing submission definitions count --- .../org/dspace/app/rest/SubmissionDefinitionsControllerIT.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/SubmissionDefinitionsControllerIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/SubmissionDefinitionsControllerIT.java index 0722949ffd0b..bcea11cbb5a4 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/SubmissionDefinitionsControllerIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/SubmissionDefinitionsControllerIT.java @@ -35,7 +35,7 @@ public class SubmissionDefinitionsControllerIT extends AbstractControllerIntegra // The total number of expected submission definitions is referred to in multiple tests and assertions as // is the last page (totalDefinitions - 1) // This integer should be maintained along with any changes to item-submissions.xml - private static final int totalDefinitions = 12; + private static final int totalDefinitions = 13; @Test public void findAll() throws Exception { From 2c6f02f74c64603c830eaf510d43883147959e30 Mon Sep 17 00:00:00 2001 From: Stefano Maffei Date: Fri, 9 May 2025 15:59:50 +0200 Subject: [PATCH 671/979] [DURACOM-109] fix missing dependency --- dspace-api/pom.xml | 10 ++++++++++ .../org/dspace/google/GoogleRecorderEventListener.java | 4 ++-- pom.xml | 7 ++++++- 3 files changed, 18 insertions(+), 3 deletions(-) diff --git a/dspace-api/pom.xml b/dspace-api/pom.xml index 6898bc13fbbe..44bbbeee1926 100644 --- a/dspace-api/pom.xml +++ b/dspace-api/pom.xml @@ -521,6 +521,10 @@ org.apache.pdfbox pdfbox + + org.apache.pdfbox + fontbox + com.ibm.icu icu4j @@ -937,6 +941,12 @@ 2.13.16 test + + com.squareup.okhttp3 + mockwebserver + 4.12.0 + test + diff --git a/dspace-api/src/main/java/org/dspace/google/GoogleRecorderEventListener.java b/dspace-api/src/main/java/org/dspace/google/GoogleRecorderEventListener.java index 4159661b1ced..90248321e5e9 100644 --- a/dspace-api/src/main/java/org/dspace/google/GoogleRecorderEventListener.java +++ b/dspace-api/src/main/java/org/dspace/google/GoogleRecorderEventListener.java @@ -21,9 +21,9 @@ import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpPost; import org.apache.http.impl.client.CloseableHttpClient; -import org.apache.http.impl.client.HttpClients; import org.apache.http.message.BasicNameValuePair; import org.apache.logging.log4j.Logger; +import org.dspace.app.client.DSpaceHttpClientFactory; import org.dspace.content.factory.ContentServiceFactory; import org.dspace.core.Constants; import org.dspace.service.ClientInfoService; @@ -56,7 +56,7 @@ public class GoogleRecorderEventListener extends AbstractUsageEventListener { public GoogleRecorderEventListener() { // httpclient is threadsafe so we only need one. - httpclient = HttpClients.createDefault(); + httpclient = DSpaceHttpClientFactory.getInstance().build(); } @Autowired diff --git a/pom.xml b/pom.xml index 107318fccb15..132b3bb4e1f9 100644 --- a/pom.xml +++ b/pom.xml @@ -1754,7 +1754,12 @@ google-http-client-gson 1.47.0 - + + com.squareup.okhttp3 + mockwebserver + 4.12.0 + test + com.google.code.findbugs From c4efc12406f49b9ba87b9ae3d85537625300138d Mon Sep 17 00:00:00 2001 From: Stefano Maffei Date: Fri, 9 May 2025 16:02:36 +0200 Subject: [PATCH 672/979] [DURACOM-109] restore missing dependency --- dspace-api/pom.xml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/dspace-api/pom.xml b/dspace-api/pom.xml index e323710dda4c..a7ecdb5ef5f9 100644 --- a/dspace-api/pom.xml +++ b/dspace-api/pom.xml @@ -512,6 +512,10 @@ org.apache.pdfbox pdfbox + + org.apache.pdfbox + fontbox + com.ibm.icu icu4j From 53e41479107aefffbf571e6dceb52834f8f4c674 Mon Sep 17 00:00:00 2001 From: Stefano Maffei Date: Fri, 9 May 2025 16:12:50 +0200 Subject: [PATCH 673/979] [DURACOM-109] fix dependency error --- dspace-api/pom.xml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/dspace-api/pom.xml b/dspace-api/pom.xml index 44bbbeee1926..5f8943bcdb98 100644 --- a/dspace-api/pom.xml +++ b/dspace-api/pom.xml @@ -871,6 +871,12 @@ + + + com.squareup.okhttp3 + mockwebserver + test + @@ -941,12 +947,6 @@ 2.13.16 test - - com.squareup.okhttp3 - mockwebserver - 4.12.0 - test - From 086a26d3b489fad3026f0f3cbd64681558965163 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Fri, 9 May 2025 12:32:19 -0500 Subject: [PATCH 674/979] Potential fix for code scanning alert no. 3549: Arbitrary file access during archive extraction ("Zip Slip") Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> (cherry picked from commit 5fbdfc218f0c4da45fc430f5ee50c3813e7bb851) --- .../org/dspace/sword2/SimpleZipContentIngester.java | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/dspace-swordv2/src/main/java/org/dspace/sword2/SimpleZipContentIngester.java b/dspace-swordv2/src/main/java/org/dspace/sword2/SimpleZipContentIngester.java index bd8301617ba9..7899bf861b32 100644 --- a/dspace-swordv2/src/main/java/org/dspace/sword2/SimpleZipContentIngester.java +++ b/dspace-swordv2/src/main/java/org/dspace/sword2/SimpleZipContentIngester.java @@ -138,12 +138,18 @@ private List unzipToBundle(Context context, File depositFile, Enumeration zenum = zip.entries(); while (zenum.hasMoreElements()) { ZipEntry entry = (ZipEntry) zenum.nextElement(); + String entryName = entry.getName(); + java.nio.file.Path entryPath = java.nio.file.Paths.get(entryName).normalize(); + if (entryPath.isAbsolute() || entryPath.startsWith("..")) { + throw new SwordError(UriRegistry.ERROR_BAD_REQUEST, "Invalid zip entry: " + entryName); + } + InputStream stream = zip.getInputStream(entry); Bitstream bs = bitstreamService.create(context, target, stream); BitstreamFormat format = this - .getFormat(context, entry.getName()); + .getFormat(context, entryName); bs.setFormat(context, format); - bs.setName(context, entry.getName()); + bs.setName(context, entryName); bitstreamService.update(context, bs); derivedResources.add(bs); } From 37dd836e185ec066aa6a66b399eb2ac6cc79cf55 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Fri, 9 May 2025 12:32:19 -0500 Subject: [PATCH 675/979] Potential fix for code scanning alert no. 3549: Arbitrary file access during archive extraction ("Zip Slip") Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> (cherry picked from commit 5fbdfc218f0c4da45fc430f5ee50c3813e7bb851) --- .../org/dspace/sword2/SimpleZipContentIngester.java | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/dspace-swordv2/src/main/java/org/dspace/sword2/SimpleZipContentIngester.java b/dspace-swordv2/src/main/java/org/dspace/sword2/SimpleZipContentIngester.java index bd8301617ba9..7899bf861b32 100644 --- a/dspace-swordv2/src/main/java/org/dspace/sword2/SimpleZipContentIngester.java +++ b/dspace-swordv2/src/main/java/org/dspace/sword2/SimpleZipContentIngester.java @@ -138,12 +138,18 @@ private List unzipToBundle(Context context, File depositFile, Enumeration zenum = zip.entries(); while (zenum.hasMoreElements()) { ZipEntry entry = (ZipEntry) zenum.nextElement(); + String entryName = entry.getName(); + java.nio.file.Path entryPath = java.nio.file.Paths.get(entryName).normalize(); + if (entryPath.isAbsolute() || entryPath.startsWith("..")) { + throw new SwordError(UriRegistry.ERROR_BAD_REQUEST, "Invalid zip entry: " + entryName); + } + InputStream stream = zip.getInputStream(entry); Bitstream bs = bitstreamService.create(context, target, stream); BitstreamFormat format = this - .getFormat(context, entry.getName()); + .getFormat(context, entryName); bs.setFormat(context, format); - bs.setName(context, entry.getName()); + bs.setName(context, entryName); bitstreamService.update(context, bs); derivedResources.add(bs); } From 5c7db5654588316129559a4bbaee85371c387984 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 12 May 2025 22:39:31 +0000 Subject: [PATCH 676/979] Bump org.webjars.npm:json-editor__json-editor in the webjars group Bumps the webjars group with 1 update: [org.webjars.npm:json-editor__json-editor](https://github.com/json-editor/json-editor). Updates `org.webjars.npm:json-editor__json-editor` from 2.15.1 to 2.15.2 - [Changelog](https://github.com/json-editor/json-editor/blob/master/CHANGELOG.md) - [Commits](https://github.com/json-editor/json-editor/compare/2.15.1...2.15.2) --- updated-dependencies: - dependency-name: org.webjars.npm:json-editor__json-editor dependency-version: 2.15.2 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: webjars ... Signed-off-by: dependabot[bot] --- dspace-server-webapp/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dspace-server-webapp/pom.xml b/dspace-server-webapp/pom.xml index 153f59e8d5ab..3efef6469971 100644 --- a/dspace-server-webapp/pom.xml +++ b/dspace-server-webapp/pom.xml @@ -423,7 +423,7 @@ org.webjars.npm json-editor__json-editor - 2.15.1 + 2.15.2 ${basedir}/.. - 3.4.0 + 3.4.1 From a1582d94778f4b25679b1b9b81a9e02fa0996b80 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Mon, 19 May 2025 12:24:49 -0500 Subject: [PATCH 696/979] Bump XOAI to 3.4.1 --- dspace-oai/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dspace-oai/pom.xml b/dspace-oai/pom.xml index 0a7fcf3a1b23..b6c419702d05 100644 --- a/dspace-oai/pom.xml +++ b/dspace-oai/pom.xml @@ -15,7 +15,7 @@ ${basedir}/.. - 3.4.0 + 3.4.1 5.87.0.RELEASE From 71e0125f3f938474e76c4a20b94f02cc2bdb14ca Mon Sep 17 00:00:00 2001 From: Zahraa Chreim Date: Tue, 11 Mar 2025 16:02:03 +0200 Subject: [PATCH 697/979] Fix invalid cast in DOIOrganiser exception handling --- .../dspace/identifier/doi/DOIOrganiser.java | 70 +++++++++---------- 1 file changed, 35 insertions(+), 35 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/identifier/doi/DOIOrganiser.java b/dspace-api/src/main/java/org/dspace/identifier/doi/DOIOrganiser.java index fc978d3813fb..bc73cf8dcf11 100644 --- a/dspace-api/src/main/java/org/dspace/identifier/doi/DOIOrganiser.java +++ b/dspace-api/src/main/java/org/dspace/identifier/doi/DOIOrganiser.java @@ -448,28 +448,28 @@ public void register(DOI doiRow, Filter filter) + " is successfully registered."); } } catch (IdentifierException ex) { + String message; if (!(ex instanceof DOIIdentifierException)) { - LOG.error("It wasn't possible to register this identifier: " - + DOI.SCHEME + doiRow.getDoi() - + " online. ", ex); + message = "It wasn't possible to register this identifier: " + + DOI.SCHEME + doiRow.getDoi() + + " online. "; + } else { + DOIIdentifierException doiIdentifierException = (DOIIdentifierException) ex; + message = "It wasn't possible to register this identifier : " + + DOI.SCHEME + doiRow.getDoi() + + " online. Exceptions code: " + + DOIIdentifierException.codeToString(doiIdentifierException.getCode()); } - DOIIdentifierException doiIdentifierException = (DOIIdentifierException) ex; - try { sendAlertMail("Register", dso, DOI.SCHEME + doiRow.getDoi(), - doiIdentifierException.codeToString(doiIdentifierException - .getCode())); + message); } catch (IOException ioe) { LOG.error("Couldn't send mail", ioe); } - LOG.error("It wasn't possible to register this identifier : " - + DOI.SCHEME + doiRow.getDoi() - + " online. Exceptions code: " - + doiIdentifierException - .codeToString(doiIdentifierException.getCode()), ex); + LOG.error(message, ex); if (!quiet) { System.err.println("It wasn't possible to register this identifier: " @@ -541,27 +541,27 @@ public void reserve(DOI doiRow, Filter filter) { System.out.println("This identifier : " + DOI.SCHEME + doiRow.getDoi() + " is successfully reserved."); } } catch (IdentifierException ex) { + String message; if (!(ex instanceof DOIIdentifierException)) { - LOG.error("It wasn't possible to register this identifier : " - + DOI.SCHEME + doiRow.getDoi() - + " online. ", ex); + message = "It wasn't possible to register this identifier : " + + DOI.SCHEME + doiRow.getDoi() + + " online. "; + } else { + DOIIdentifierException doiIdentifierException = (DOIIdentifierException) ex; + message = "It wasn't possible to reserve the identifier online. " + + " Exceptions code: " + + DOIIdentifierException.codeToString(doiIdentifierException.getCode()); } - DOIIdentifierException doiIdentifierException = (DOIIdentifierException) ex; - try { sendAlertMail("Reserve", dso, DOI.SCHEME + doiRow.getDoi(), - DOIIdentifierException.codeToString( - doiIdentifierException.getCode())); + message); } catch (IOException ioe) { LOG.error("Couldn't send mail", ioe); } - LOG.error("It wasn't possible to reserve the identifier online. " - + " Exceptions code: " - + DOIIdentifierException - .codeToString(doiIdentifierException.getCode()), ex); + LOG.error(message, ex); if (!quiet) { System.err.println("It wasn't possible to reserve this identifier: " + DOI.SCHEME + doiRow.getDoi()); @@ -606,27 +606,27 @@ public void update(DOI doiRow) { + doiRow.getDoi() + "."); } } catch (IdentifierException ex) { + String message; if (!(ex instanceof DOIIdentifierException)) { - LOG.error("Registering DOI {} for object {}: the registrar returned an error.", - doiRow.getDoi(), dso.getID(), ex); + message = String.format("Registering DOI %s for object %s: the registrar returned an error.", + doiRow.getDoi(), dso.getID()); + } else { + DOIIdentifierException doiIdentifierException = (DOIIdentifierException) ex; + message = "It wasn't possible to update this identifier: " + + DOI.SCHEME + doiRow.getDoi() + + " Exceptions code: " + + DOIIdentifierException.codeToString(doiIdentifierException.getCode()); } - DOIIdentifierException doiIdentifierException = (DOIIdentifierException) ex; - try { sendAlertMail("Update", dso, DOI.SCHEME + doiRow.getDoi(), - doiIdentifierException.codeToString(doiIdentifierException - .getCode())); + message); } catch (IOException ioe) { LOG.error("Couldn't send mail", ioe); } - LOG.error("It wasn't possible to update this identifier: " - + DOI.SCHEME + doiRow.getDoi() - + " Exceptions code: " - + doiIdentifierException - .codeToString(doiIdentifierException.getCode()), ex); + LOG.error(message, ex); if (!quiet) { System.err.println("It wasn't possible to update this identifier: " + DOI.SCHEME + doiRow.getDoi()); @@ -830,4 +830,4 @@ private void setQuiet() { this.quiet = true; } -} \ No newline at end of file +} From a2a68383904566c186bf6374ad86ed295d8dd896 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 26 May 2025 22:15:28 +0000 Subject: [PATCH 698/979] Bump org.xmlunit:xmlunit-core from 2.10.0 to 2.10.2 Bumps [org.xmlunit:xmlunit-core](https://github.com/xmlunit/xmlunit) from 2.10.0 to 2.10.2. - [Release notes](https://github.com/xmlunit/xmlunit/releases) - [Changelog](https://github.com/xmlunit/xmlunit/blob/main/RELEASE_NOTES.md) - [Commits](https://github.com/xmlunit/xmlunit/compare/v2.10.0...v2.10.2) --- updated-dependencies: - dependency-name: org.xmlunit:xmlunit-core dependency-version: 2.10.2 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- dspace-api/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dspace-api/pom.xml b/dspace-api/pom.xml index 05cb803cdb85..58ac06c44f76 100644 --- a/dspace-api/pom.xml +++ b/dspace-api/pom.xml @@ -923,7 +923,7 @@ org.xmlunit xmlunit-core - 2.10.0 + 2.10.2 test From 17b93c5c1527122b48f08e7885c3ea553416d860 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 26 May 2025 22:47:15 +0000 Subject: [PATCH 699/979] Bump org.apache.httpcomponents.client5:httpclient5 from 5.4.4 to 5.5 Bumps [org.apache.httpcomponents.client5:httpclient5](https://github.com/apache/httpcomponents-client) from 5.4.4 to 5.5. - [Changelog](https://github.com/apache/httpcomponents-client/blob/master/RELEASE_NOTES.txt) - [Commits](https://github.com/apache/httpcomponents-client/compare/rel/v5.4.4...rel/v5.5) --- updated-dependencies: - dependency-name: org.apache.httpcomponents.client5:httpclient5 dependency-version: '5.5' dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- dspace-server-webapp/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dspace-server-webapp/pom.xml b/dspace-server-webapp/pom.xml index 3efef6469971..47afbfd1374e 100644 --- a/dspace-server-webapp/pom.xml +++ b/dspace-server-webapp/pom.xml @@ -590,7 +590,7 @@ org.apache.httpcomponents.client5 httpclient5 - 5.4.4 + 5.5 test From 2d9d44a768edf0f6a328aa434e5acdf7384c8aac Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 26 May 2025 22:47:34 +0000 Subject: [PATCH 700/979] Bump org.xmlunit:xmlunit-core from 2.10.0 to 2.10.2 Bumps [org.xmlunit:xmlunit-core](https://github.com/xmlunit/xmlunit) from 2.10.0 to 2.10.2. - [Release notes](https://github.com/xmlunit/xmlunit/releases) - [Changelog](https://github.com/xmlunit/xmlunit/blob/main/RELEASE_NOTES.md) - [Commits](https://github.com/xmlunit/xmlunit/compare/v2.10.0...v2.10.2) --- updated-dependencies: - dependency-name: org.xmlunit:xmlunit-core dependency-version: 2.10.2 dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- dspace-api/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dspace-api/pom.xml b/dspace-api/pom.xml index a7ecdb5ef5f9..e476e8508d76 100644 --- a/dspace-api/pom.xml +++ b/dspace-api/pom.xml @@ -785,7 +785,7 @@ org.xmlunit xmlunit-core - 2.10.0 + 2.10.2 test From ea3dc29852353e8d900fc571bc6b69159f69c38e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 28 May 2025 16:25:13 +0000 Subject: [PATCH 701/979] Bump the spring group across 1 directory with 25 updates Bumps the spring group with 25 updates in the / directory: | Package | From | To | | --- | --- | --- | | [org.springframework:spring-orm](https://github.com/spring-projects/spring-framework) | `6.2.6` | `6.2.7` | | [org.springframework:spring-core](https://github.com/spring-projects/spring-framework) | `6.2.6` | `6.2.7` | | [org.springframework:spring-beans](https://github.com/spring-projects/spring-framework) | `6.2.6` | `6.2.7` | | [org.springframework:spring-aop](https://github.com/spring-projects/spring-framework) | `6.2.6` | `6.2.7` | | [org.springframework:spring-context](https://github.com/spring-projects/spring-framework) | `6.2.6` | `6.2.7` | | [org.springframework:spring-context-support](https://github.com/spring-projects/spring-framework) | `6.2.6` | `6.2.7` | | [org.springframework:spring-tx](https://github.com/spring-projects/spring-framework) | `6.2.6` | `6.2.7` | | [org.springframework:spring-jdbc](https://github.com/spring-projects/spring-framework) | `6.2.6` | `6.2.7` | | [org.springframework:spring-web](https://github.com/spring-projects/spring-framework) | `6.2.6` | `6.2.7` | | [org.springframework:spring-webmvc](https://github.com/spring-projects/spring-framework) | `6.2.6` | `6.2.7` | | [org.springframework:spring-expression](https://github.com/spring-projects/spring-framework) | `6.2.6` | `6.2.7` | | [org.springframework:spring-test](https://github.com/spring-projects/spring-framework) | `6.2.6` | `6.2.7` | | [org.springframework.boot:spring-boot-starter-test](https://github.com/spring-projects/spring-boot) | `3.4.5` | `3.5.0` | | [org.springframework.boot:spring-boot-starter-tomcat](https://github.com/spring-projects/spring-boot) | `3.4.5` | `3.5.0` | | [org.springframework.boot:spring-boot-maven-plugin](https://github.com/spring-projects/spring-boot) | `3.4.5` | `3.5.0` | | [org.springframework.boot:spring-boot-starter-cache](https://github.com/spring-projects/spring-boot) | `3.4.5` | `3.5.0` | | [org.springframework.boot:spring-boot-starter](https://github.com/spring-projects/spring-boot) | `3.4.5` | `3.5.0` | | [org.springframework.boot:spring-boot-starter-thymeleaf](https://github.com/spring-projects/spring-boot) | `3.4.5` | `3.5.0` | | [org.springframework.boot:spring-boot-starter-web](https://github.com/spring-projects/spring-boot) | `3.4.5` | `3.5.0` | | [org.springframework.boot:spring-boot-starter-data-rest](https://github.com/spring-projects/spring-boot) | `3.4.5` | `3.5.0` | | [org.springframework.boot:spring-boot-starter-security](https://github.com/spring-projects/spring-boot) | `3.4.5` | `3.5.0` | | [org.springframework.boot:spring-boot-starter-aop](https://github.com/spring-projects/spring-boot) | `3.4.5` | `3.5.0` | | [org.springframework.boot:spring-boot-starter-actuator](https://github.com/spring-projects/spring-boot) | `3.4.5` | `3.5.0` | | [org.springframework.boot:spring-boot-starter-log4j2](https://github.com/spring-projects/spring-boot) | `3.4.5` | `3.5.0` | | [org.springframework.security:spring-security-test](https://github.com/spring-projects/spring-security) | `6.4.5` | `6.5.0` | Updates `org.springframework:spring-orm` from 6.2.6 to 6.2.7 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.6...v6.2.7) Updates `org.springframework:spring-core` from 6.2.6 to 6.2.7 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.6...v6.2.7) Updates `org.springframework:spring-beans` from 6.2.6 to 6.2.7 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.6...v6.2.7) Updates `org.springframework:spring-aop` from 6.2.6 to 6.2.7 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.6...v6.2.7) Updates `org.springframework:spring-context` from 6.2.6 to 6.2.7 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.6...v6.2.7) Updates `org.springframework:spring-context-support` from 6.2.6 to 6.2.7 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.6...v6.2.7) Updates `org.springframework:spring-tx` from 6.2.6 to 6.2.7 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.6...v6.2.7) Updates `org.springframework:spring-jdbc` from 6.2.6 to 6.2.7 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.6...v6.2.7) Updates `org.springframework:spring-web` from 6.2.6 to 6.2.7 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.6...v6.2.7) Updates `org.springframework:spring-webmvc` from 6.2.6 to 6.2.7 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.6...v6.2.7) Updates `org.springframework:spring-expression` from 6.2.6 to 6.2.7 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.6...v6.2.7) Updates `org.springframework:spring-test` from 6.2.6 to 6.2.7 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.6...v6.2.7) Updates `org.springframework:spring-core` from 6.2.6 to 6.2.7 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.6...v6.2.7) Updates `org.springframework:spring-beans` from 6.2.6 to 6.2.7 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.6...v6.2.7) Updates `org.springframework:spring-aop` from 6.2.6 to 6.2.7 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.6...v6.2.7) Updates `org.springframework:spring-context` from 6.2.6 to 6.2.7 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.6...v6.2.7) Updates `org.springframework:spring-context-support` from 6.2.6 to 6.2.7 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.6...v6.2.7) Updates `org.springframework:spring-tx` from 6.2.6 to 6.2.7 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.6...v6.2.7) Updates `org.springframework:spring-jdbc` from 6.2.6 to 6.2.7 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.6...v6.2.7) Updates `org.springframework:spring-web` from 6.2.6 to 6.2.7 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.6...v6.2.7) Updates `org.springframework:spring-webmvc` from 6.2.6 to 6.2.7 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.6...v6.2.7) Updates `org.springframework:spring-expression` from 6.2.6 to 6.2.7 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.6...v6.2.7) Updates `org.springframework:spring-test` from 6.2.6 to 6.2.7 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.6...v6.2.7) Updates `org.springframework.boot:spring-boot-starter-test` from 3.4.5 to 3.5.0 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.4.5...v3.5.0) Updates `org.springframework.boot:spring-boot-starter-tomcat` from 3.4.5 to 3.5.0 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.4.5...v3.5.0) Updates `org.springframework.boot:spring-boot-maven-plugin` from 3.4.5 to 3.5.0 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.4.5...v3.5.0) Updates `org.springframework.boot:spring-boot-starter-cache` from 3.4.5 to 3.5.0 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.4.5...v3.5.0) Updates `org.springframework.boot:spring-boot-starter` from 3.4.5 to 3.5.0 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.4.5...v3.5.0) Updates `org.springframework.boot:spring-boot-starter-thymeleaf` from 3.4.5 to 3.5.0 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.4.5...v3.5.0) Updates `org.springframework.boot:spring-boot-starter-web` from 3.4.5 to 3.5.0 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.4.5...v3.5.0) Updates `org.springframework.boot:spring-boot-starter-data-rest` from 3.4.5 to 3.5.0 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.4.5...v3.5.0) Updates `org.springframework.boot:spring-boot-starter-security` from 3.4.5 to 3.5.0 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.4.5...v3.5.0) Updates `org.springframework.boot:spring-boot-starter-aop` from 3.4.5 to 3.5.0 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.4.5...v3.5.0) Updates `org.springframework.boot:spring-boot-starter-actuator` from 3.4.5 to 3.5.0 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.4.5...v3.5.0) Updates `org.springframework.boot:spring-boot-starter-log4j2` from 3.4.5 to 3.5.0 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.4.5...v3.5.0) Updates `org.springframework.boot:spring-boot-starter-tomcat` from 3.4.5 to 3.5.0 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.4.5...v3.5.0) Updates `org.springframework.security:spring-security-test` from 6.4.5 to 6.5.0 - [Release notes](https://github.com/spring-projects/spring-security/releases) - [Changelog](https://github.com/spring-projects/spring-security/blob/main/RELEASE.adoc) - [Commits](https://github.com/spring-projects/spring-security/compare/6.4.5...6.5.0) Updates `org.springframework.boot:spring-boot-maven-plugin` from 3.4.5 to 3.5.0 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.4.5...v3.5.0) Updates `org.springframework.boot:spring-boot-starter-cache` from 3.4.5 to 3.5.0 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.4.5...v3.5.0) Updates `org.springframework.boot:spring-boot-starter` from 3.4.5 to 3.5.0 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.4.5...v3.5.0) Updates `org.springframework.boot:spring-boot-starter-thymeleaf` from 3.4.5 to 3.5.0 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.4.5...v3.5.0) Updates `org.springframework.boot:spring-boot-starter-web` from 3.4.5 to 3.5.0 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.4.5...v3.5.0) Updates `org.springframework.boot:spring-boot-starter-data-rest` from 3.4.5 to 3.5.0 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.4.5...v3.5.0) Updates `org.springframework.boot:spring-boot-starter-security` from 3.4.5 to 3.5.0 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.4.5...v3.5.0) Updates `org.springframework.boot:spring-boot-starter-aop` from 3.4.5 to 3.5.0 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.4.5...v3.5.0) Updates `org.springframework.boot:spring-boot-starter-actuator` from 3.4.5 to 3.5.0 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.4.5...v3.5.0) Updates `org.springframework.boot:spring-boot-starter-log4j2` from 3.4.5 to 3.5.0 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.4.5...v3.5.0) --- updated-dependencies: - dependency-name: org.springframework:spring-orm dependency-version: 6.2.7 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-core dependency-version: 6.2.7 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-beans dependency-version: 6.2.7 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-aop dependency-version: 6.2.7 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-context dependency-version: 6.2.7 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-context-support dependency-version: 6.2.7 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-tx dependency-version: 6.2.7 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-jdbc dependency-version: 6.2.7 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-web dependency-version: 6.2.7 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-webmvc dependency-version: 6.2.7 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-expression dependency-version: 6.2.7 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-test dependency-version: 6.2.7 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-core dependency-version: 6.2.7 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-beans dependency-version: 6.2.7 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-aop dependency-version: 6.2.7 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-context dependency-version: 6.2.7 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-context-support dependency-version: 6.2.7 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-tx dependency-version: 6.2.7 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-jdbc dependency-version: 6.2.7 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-web dependency-version: 6.2.7 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-webmvc dependency-version: 6.2.7 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-expression dependency-version: 6.2.7 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-test dependency-version: 6.2.7 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter-test dependency-version: 3.5.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter-tomcat dependency-version: 3.5.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-maven-plugin dependency-version: 3.5.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter-cache dependency-version: 3.5.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter dependency-version: 3.5.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter-thymeleaf dependency-version: 3.5.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter-web dependency-version: 3.5.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter-data-rest dependency-version: 3.5.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter-security dependency-version: 3.5.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter-aop dependency-version: 3.5.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter-actuator dependency-version: 3.5.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter-log4j2 dependency-version: 3.5.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter-tomcat dependency-version: 3.5.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: spring - dependency-name: org.springframework.security:spring-security-test dependency-version: 6.5.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-maven-plugin dependency-version: 3.5.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter-cache dependency-version: 3.5.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter dependency-version: 3.5.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter-thymeleaf dependency-version: 3.5.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter-web dependency-version: 3.5.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter-data-rest dependency-version: 3.5.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter-security dependency-version: 3.5.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter-aop dependency-version: 3.5.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter-actuator dependency-version: 3.5.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter-log4j2 dependency-version: 3.5.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: spring ... Signed-off-by: dependabot[bot] --- pom.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pom.xml b/pom.xml index c455f1fb736b..59a2bb52cf0a 100644 --- a/pom.xml +++ b/pom.xml @@ -19,9 +19,9 @@ 17 - 6.2.6 - 3.4.5 - 6.4.5 + 6.2.7 + 3.5.0 + 6.5.0 6.4.8.Final 8.0.1.Final 42.7.5 From 58da9a0ca83f62f43ec697f128051e2923d4c421 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 28 May 2025 16:25:36 +0000 Subject: [PATCH 702/979] Bump commons-beanutils:commons-beanutils in the apache-commons group Bumps the apache-commons group with 1 update: commons-beanutils:commons-beanutils. Updates `commons-beanutils:commons-beanutils` from 1.10.1 to 1.11.0 --- updated-dependencies: - dependency-name: commons-beanutils:commons-beanutils dependency-version: 1.11.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: apache-commons ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index c455f1fb736b..ef97573f26ed 100644 --- a/pom.xml +++ b/pom.xml @@ -1468,7 +1468,7 @@ commons-beanutils commons-beanutils - 1.10.1 + 1.11.0 commons-cli From 3fef856ef06a85befb99003b24fad030db17e38a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 28 May 2025 16:25:45 +0000 Subject: [PATCH 703/979] Bump commons-beanutils:commons-beanutils in the apache-commons group Bumps the apache-commons group with 1 update: commons-beanutils:commons-beanutils. Updates `commons-beanutils:commons-beanutils` from 1.10.1 to 1.11.0 --- updated-dependencies: - dependency-name: commons-beanutils:commons-beanutils dependency-version: 1.11.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: apache-commons ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 132b3bb4e1f9..a6c30c0e57fd 100644 --- a/pom.xml +++ b/pom.xml @@ -1470,7 +1470,7 @@ commons-beanutils commons-beanutils - 1.10.1 + 1.11.0 commons-cli From 140a6f64a962cbcadea9f5bbd7d3d1e163acb42b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 28 May 2025 16:26:12 +0000 Subject: [PATCH 704/979] Bump org.postgresql:postgresql from 42.7.5 to 42.7.6 Bumps [org.postgresql:postgresql](https://github.com/pgjdbc/pgjdbc) from 42.7.5 to 42.7.6. - [Release notes](https://github.com/pgjdbc/pgjdbc/releases) - [Changelog](https://github.com/pgjdbc/pgjdbc/blob/master/CHANGELOG.md) - [Commits](https://github.com/pgjdbc/pgjdbc/compare/REL42.7.5...REL42.7.6) --- updated-dependencies: - dependency-name: org.postgresql:postgresql dependency-version: 42.7.6 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index c455f1fb736b..8105a8a2c053 100644 --- a/pom.xml +++ b/pom.xml @@ -24,7 +24,7 @@ 6.4.5 6.4.8.Final 8.0.1.Final - 42.7.5 + 42.7.6 10.22.0 8.11.4 From b7960bd42e726f4607b4c41cc3e839d471e30f7e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 28 May 2025 16:26:54 +0000 Subject: [PATCH 705/979] Bump io.grpc:grpc-context from 1.72.0 to 1.73.0 Bumps [io.grpc:grpc-context](https://github.com/grpc/grpc-java) from 1.72.0 to 1.73.0. - [Release notes](https://github.com/grpc/grpc-java/releases) - [Commits](https://github.com/grpc/grpc-java/compare/v1.72.0...v1.73.0) --- updated-dependencies: - dependency-name: io.grpc:grpc-context dependency-version: 1.73.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 132b3bb4e1f9..86fec0602c8a 100644 --- a/pom.xml +++ b/pom.xml @@ -1725,7 +1725,7 @@ io.grpc grpc-context - 1.72.0 + 1.73.0 com.google.http-client From 4367eebef221d475733160b654d8afbcafaaf285 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 28 May 2025 16:28:25 +0000 Subject: [PATCH 706/979] Bump org.postgresql:postgresql from 42.7.5 to 42.7.6 Bumps [org.postgresql:postgresql](https://github.com/pgjdbc/pgjdbc) from 42.7.5 to 42.7.6. - [Release notes](https://github.com/pgjdbc/pgjdbc/releases) - [Changelog](https://github.com/pgjdbc/pgjdbc/blob/master/CHANGELOG.md) - [Commits](https://github.com/pgjdbc/pgjdbc/compare/REL42.7.5...REL42.7.6) --- updated-dependencies: - dependency-name: org.postgresql:postgresql dependency-version: 42.7.6 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 132b3bb4e1f9..59b126b10953 100644 --- a/pom.xml +++ b/pom.xml @@ -24,7 +24,7 @@ 5.7.14 5.6.15.Final 6.2.5.Final - 42.7.5 + 42.7.6 8.11.4 3.10.8 From 1506fbe307a115a3f443516cc215779d74d44a89 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 2 Jun 2025 02:25:11 +0000 Subject: [PATCH 707/979] Bump com.opencsv:opencsv from 5.11 to 5.11.1 Bumps com.opencsv:opencsv from 5.11 to 5.11.1. --- updated-dependencies: - dependency-name: com.opencsv:opencsv dependency-version: 5.11.1 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- dspace-api/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dspace-api/pom.xml b/dspace-api/pom.xml index e476e8508d76..154468aed5f0 100644 --- a/dspace-api/pom.xml +++ b/dspace-api/pom.xml @@ -772,7 +772,7 @@ com.opencsv opencsv - 5.11 + 5.11.1 From 32bd615ba8dee1ae433799e75d2dea70607d6700 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 2 Jun 2025 02:30:57 +0000 Subject: [PATCH 708/979] Bump com.opencsv:opencsv from 5.11 to 5.11.1 Bumps com.opencsv:opencsv from 5.11 to 5.11.1. --- updated-dependencies: - dependency-name: com.opencsv:opencsv dependency-version: 5.11.1 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- dspace-api/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dspace-api/pom.xml b/dspace-api/pom.xml index 58ac06c44f76..f89aee3074a8 100644 --- a/dspace-api/pom.xml +++ b/dspace-api/pom.xml @@ -805,7 +805,7 @@ com.opencsv opencsv - 5.11 + 5.11.1 From f04c116b07fdab0eb7ff9ddac88c81fc89d27449 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Mon, 2 Jun 2025 09:16:27 -0500 Subject: [PATCH 709/979] Alphabetize importers by service name --- .../config/spring/api/external-services.xml | 241 ++++++++++-------- 1 file changed, 129 insertions(+), 112 deletions(-) diff --git a/dspace/config/spring/api/external-services.xml b/dspace/config/spring/api/external-services.xml index e3a842efa094..c349a3f9307f 100644 --- a/dspace/config/spring/api/external-services.xml +++ b/dspace/config/spring/api/external-services.xml @@ -7,72 +7,75 @@ - - - - - - - - - + + + + + - Journal + Publication + none - - - - - - - - - + + + + + + - Journal + Publication + none - - - - - - - - - + + + + + + - OrgUnit + Publication + none - - - - - - - + + + + + + - Person + Publication + none - - + + + + + + + + Publication + none + + - - - - + + + + + @@ -82,9 +85,10 @@ - - - + + + + @@ -94,9 +98,10 @@ - - - + + + + @@ -106,9 +111,9 @@ - - - + + + @@ -118,6 +123,7 @@ + @@ -128,21 +134,28 @@ - - - - + + + + + + + - Publication - none + Person - - - + + + + + + + + @@ -164,21 +177,23 @@ - - - - + + + + + + - Publication - none + OrgUnit - - - + + + + @@ -188,21 +203,10 @@ - - - - - - - Publication - none - - - - - - - + + + + @@ -212,35 +216,46 @@ - - - - + + + + + + + + + + - Publication - none + Journal - - - - - + + + + + + + + + - Publication - none + Journal - - - - - - + + + + + + + + + OrgUnit @@ -248,9 +263,10 @@ - - - + + + + @@ -260,10 +276,11 @@ - - - - + + + + + Publication From 19e22c10f1a4ad2b2de4d90b97485ed97b826ff0 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Mon, 2 Jun 2025 09:16:27 -0500 Subject: [PATCH 710/979] Alphabetize importers by service name --- .../config/spring/api/external-services.xml | 196 ++++++++++-------- 1 file changed, 105 insertions(+), 91 deletions(-) diff --git a/dspace/config/spring/api/external-services.xml b/dspace/config/spring/api/external-services.xml index 6d7d50c39f1b..4792a4fd8589 100644 --- a/dspace/config/spring/api/external-services.xml +++ b/dspace/config/spring/api/external-services.xml @@ -7,72 +7,62 @@ - - - - - - - - - + + + + + - Journal + Publication + none - - - - - - - - - + + + + + + - Journal + Publication + none - - - - - - - - - + + + + + + - OrgUnit + Publication + none - - - - - - - + + + + + + - Person + Publication + none - - - - - - - + + + + @@ -82,9 +72,10 @@ - - - + + + + @@ -93,7 +84,8 @@ - + + @@ -104,21 +96,28 @@ - - - - + + + + + + + - Publication - none + Person - - - + + + + + + + + @@ -140,9 +139,10 @@ - - - + + + + @@ -152,9 +152,10 @@ - - - + + + + @@ -164,45 +165,57 @@ - - - - + + + + + + + + + + - Publication - none + Journal - - - - - + + + + + + + + + - Publication - none + Journal - - - - - + + + + + + + + + - Publication - none + OrgUnit - - - + + + + @@ -212,10 +225,11 @@ - - - - + + + + + Publication From 2fd984de62e1a8dc95d3b3af97f4b0bd765249c6 Mon Sep 17 00:00:00 2001 From: Alexandre Vryghem Date: Tue, 3 Jun 2025 13:11:42 +0200 Subject: [PATCH 711/979] 131448: Fixed QAEventMatcher#matchQAEventNotifyEntry sometimes failing when your default local uses a comma as a decimal separator You can view your default decimal separator using new DecimalFormatSymbols(Locale.getDefault()).getDecimalSeparator() (cherry picked from commit d74c3bd9cd3082b05cf82ba6379d25880e28ecd9) --- .../test/java/org/dspace/app/rest/matcher/QAEventMatcher.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/matcher/QAEventMatcher.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/matcher/QAEventMatcher.java index 8eb569468d4b..032696b1df00 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/matcher/QAEventMatcher.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/matcher/QAEventMatcher.java @@ -77,10 +77,11 @@ public static Matcher matchQAEventEntry(QAEvent event) { public static Matcher matchQAEventNotifyEntry(QAEvent event) { try { ObjectMapper jsonMapper = new JsonMapper(); + DecimalFormat decimalFormat = new DecimalFormat("0.000", new DecimalFormatSymbols(Locale.ENGLISH)); return allOf(hasJsonPath("$.id", is(event.getEventId())), hasJsonPath("$.originalId", is(event.getOriginalId())), hasJsonPath("$.title", is(event.getTitle())), - hasJsonPath("$.trust", is(new DecimalFormat("0.000").format(event.getTrust()))), + hasJsonPath("$.trust", is(decimalFormat.format(event.getTrust()))), hasJsonPath("$.status", Matchers.equalToIgnoringCase(event.getStatus())), hasJsonPath("$.message", matchMessage(event.getTopic(), jsonMapper.readValue(event.getMessage(), From 25ad6039dcfe93e7fce3fb6ebe029650ce0e43e3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 4 Jun 2025 14:25:05 +0000 Subject: [PATCH 712/979] Bump org.apache.maven.plugins:maven-clean-plugin Bumps the build-tools group with 1 update: [org.apache.maven.plugins:maven-clean-plugin](https://github.com/apache/maven-clean-plugin). Updates `org.apache.maven.plugins:maven-clean-plugin` from 3.4.1 to 3.5.0 - [Release notes](https://github.com/apache/maven-clean-plugin/releases) - [Commits](https://github.com/apache/maven-clean-plugin/compare/maven-clean-plugin-3.4.1...maven-clean-plugin-3.5.0) --- updated-dependencies: - dependency-name: org.apache.maven.plugins:maven-clean-plugin dependency-version: 3.5.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: build-tools ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index fc0df5c2de29..af2c99bb775b 100644 --- a/pom.xml +++ b/pom.xml @@ -321,7 +321,7 @@ maven-clean-plugin - 3.4.1 + 3.5.0 From 21c56aeda7fa07c35b97510bc963f0cd05f2395e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 4 Jun 2025 14:25:16 +0000 Subject: [PATCH 713/979] Bump bouncycastle.version from 1.80 to 1.81 Bumps `bouncycastle.version` from 1.80 to 1.81. Updates `org.bouncycastle:bcpkix-jdk18on` from 1.80 to 1.81 - [Changelog](https://github.com/bcgit/bc-java/blob/main/docs/releasenotes.html) - [Commits](https://github.com/bcgit/bc-java/commits) Updates `org.bouncycastle:bcprov-jdk18on` from 1.80 to 1.81 - [Changelog](https://github.com/bcgit/bc-java/blob/main/docs/releasenotes.html) - [Commits](https://github.com/bcgit/bc-java/commits) Updates `org.bouncycastle:bcutil-jdk18on` from 1.80 to 1.81 - [Changelog](https://github.com/bcgit/bc-java/blob/main/docs/releasenotes.html) - [Commits](https://github.com/bcgit/bc-java/commits) --- updated-dependencies: - dependency-name: org.bouncycastle:bcpkix-jdk18on dependency-version: '1.81' dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: org.bouncycastle:bcprov-jdk18on dependency-version: '1.81' dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: org.bouncycastle:bcutil-jdk18on dependency-version: '1.81' dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index fc0df5c2de29..ad009b631e1b 100644 --- a/pom.xml +++ b/pom.xml @@ -44,7 +44,7 @@ 1.7.36 2.9.4 - 1.80 + 1.81 From 86c001cb0e721f5ee5006fb4790ab9f2c43e7c5a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 4 Jun 2025 14:25:53 +0000 Subject: [PATCH 714/979] Bump the hibernate group across 1 directory with 2 updates Bumps the hibernate group with 2 updates in the / directory: [org.hibernate.validator:hibernate-validator](https://github.com/hibernate/hibernate-validator) and [org.hibernate.validator:hibernate-validator-cdi](https://github.com/hibernate/hibernate-validator). Updates `org.hibernate.validator:hibernate-validator` from 8.0.1.Final to 8.0.2.Final - [Changelog](https://github.com/hibernate/hibernate-validator/blob/8.0.2.Final/changelog.txt) - [Commits](https://github.com/hibernate/hibernate-validator/compare/8.0.1.Final...8.0.2.Final) Updates `org.hibernate.validator:hibernate-validator-cdi` from 8.0.1.Final to 8.0.2.Final - [Changelog](https://github.com/hibernate/hibernate-validator/blob/8.0.2.Final/changelog.txt) - [Commits](https://github.com/hibernate/hibernate-validator/compare/8.0.1.Final...8.0.2.Final) Updates `org.hibernate.validator:hibernate-validator-cdi` from 8.0.1.Final to 8.0.2.Final - [Changelog](https://github.com/hibernate/hibernate-validator/blob/8.0.2.Final/changelog.txt) - [Commits](https://github.com/hibernate/hibernate-validator/compare/8.0.1.Final...8.0.2.Final) --- updated-dependencies: - dependency-name: org.hibernate.validator:hibernate-validator dependency-version: 8.0.2.Final dependency-type: direct:production update-type: version-update:semver-patch dependency-group: hibernate - dependency-name: org.hibernate.validator:hibernate-validator-cdi dependency-version: 8.0.2.Final dependency-type: direct:production update-type: version-update:semver-patch dependency-group: hibernate - dependency-name: org.hibernate.validator:hibernate-validator-cdi dependency-version: 8.0.2.Final dependency-type: direct:production update-type: version-update:semver-patch dependency-group: hibernate ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 71886500ce50..b4b363e58e08 100644 --- a/pom.xml +++ b/pom.xml @@ -23,7 +23,7 @@ 3.5.0 6.5.0 6.4.8.Final - 8.0.1.Final + 8.0.2.Final 42.7.6 10.22.0 8.11.4 From a6cc912e6230f2bfa8d4e06a4f8794d526d5e191 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 4 Jun 2025 14:25:57 +0000 Subject: [PATCH 715/979] Bump com.amazonaws:aws-java-sdk-s3 from 1.12.783 to 1.12.785 Bumps [com.amazonaws:aws-java-sdk-s3](https://github.com/aws/aws-sdk-java) from 1.12.783 to 1.12.785. - [Changelog](https://github.com/aws/aws-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java/compare/1.12.783...1.12.785) --- updated-dependencies: - dependency-name: com.amazonaws:aws-java-sdk-s3 dependency-version: 1.12.785 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- dspace-api/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dspace-api/pom.xml b/dspace-api/pom.xml index f89aee3074a8..6d5ff4306bc5 100644 --- a/dspace-api/pom.xml +++ b/dspace-api/pom.xml @@ -756,7 +756,7 @@ com.amazonaws aws-java-sdk-s3 - 1.12.783 + 1.12.785 From 7774e9d35dfbc8f62ca90da13561fef59f503aee Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 4 Jun 2025 14:26:09 +0000 Subject: [PATCH 716/979] Bump org.apache.maven.plugins:maven-clean-plugin Bumps the build-tools group with 1 update: [org.apache.maven.plugins:maven-clean-plugin](https://github.com/apache/maven-clean-plugin). Updates `org.apache.maven.plugins:maven-clean-plugin` from 3.4.1 to 3.5.0 - [Release notes](https://github.com/apache/maven-clean-plugin/releases) - [Commits](https://github.com/apache/maven-clean-plugin/compare/maven-clean-plugin-3.4.1...maven-clean-plugin-3.5.0) --- updated-dependencies: - dependency-name: org.apache.maven.plugins:maven-clean-plugin dependency-version: 3.5.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: build-tools ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 71886500ce50..ca708f5f60f0 100644 --- a/pom.xml +++ b/pom.xml @@ -329,7 +329,7 @@ maven-clean-plugin - 3.4.1 + 3.5.0 From c24011144a089f1814be63d54c77bf066a62e824 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 4 Jun 2025 14:26:28 +0000 Subject: [PATCH 717/979] Bump bouncycastle.version from 1.80 to 1.81 Bumps `bouncycastle.version` from 1.80 to 1.81. Updates `org.bouncycastle:bcpkix-jdk18on` from 1.80 to 1.81 - [Changelog](https://github.com/bcgit/bc-java/blob/main/docs/releasenotes.html) - [Commits](https://github.com/bcgit/bc-java/commits) Updates `org.bouncycastle:bcprov-jdk18on` from 1.80 to 1.81 - [Changelog](https://github.com/bcgit/bc-java/blob/main/docs/releasenotes.html) - [Commits](https://github.com/bcgit/bc-java/commits) Updates `org.bouncycastle:bcutil-jdk18on` from 1.80 to 1.81 - [Changelog](https://github.com/bcgit/bc-java/blob/main/docs/releasenotes.html) - [Commits](https://github.com/bcgit/bc-java/commits) --- updated-dependencies: - dependency-name: org.bouncycastle:bcpkix-jdk18on dependency-version: '1.81' dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: org.bouncycastle:bcprov-jdk18on dependency-version: '1.81' dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: org.bouncycastle:bcutil-jdk18on dependency-version: '1.81' dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 71886500ce50..95de239dc9f4 100644 --- a/pom.xml +++ b/pom.xml @@ -45,7 +45,7 @@ 2.0.17 2.9.4 - 1.80 + 1.81 8.0.1 3.1.10 From e44aa3a34aed7888275be47a816b710cdc8865e8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 4 Jun 2025 14:26:36 +0000 Subject: [PATCH 718/979] Bump org.checkerframework:checker-qual from 3.49.3 to 3.49.4 Bumps [org.checkerframework:checker-qual](https://github.com/typetools/checker-framework) from 3.49.3 to 3.49.4. - [Release notes](https://github.com/typetools/checker-framework/releases) - [Changelog](https://github.com/typetools/checker-framework/blob/master/docs/CHANGELOG.md) - [Commits](https://github.com/typetools/checker-framework/compare/checker-framework-3.49.3...checker-framework-3.49.4) --- updated-dependencies: - dependency-name: org.checkerframework:checker-qual dependency-version: 3.49.4 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 71886500ce50..996945f74f4b 100644 --- a/pom.xml +++ b/pom.xml @@ -1350,7 +1350,7 @@ org.checkerframework checker-qual - 3.49.3 + 3.49.4 From 5959b59090641752454b8f41a7bf8efb15217abc Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 9 Jun 2025 03:25:34 +0000 Subject: [PATCH 725/979] Bump org.codehaus.mojo:build-helper-maven-plugin Bumps the build-tools group with 1 update: [org.codehaus.mojo:build-helper-maven-plugin](https://github.com/mojohaus/build-helper-maven-plugin). Updates `org.codehaus.mojo:build-helper-maven-plugin` from 3.6.0 to 3.6.1 - [Release notes](https://github.com/mojohaus/build-helper-maven-plugin/releases) - [Commits](https://github.com/mojohaus/build-helper-maven-plugin/compare/3.6.0...3.6.1) --- updated-dependencies: - dependency-name: org.codehaus.mojo:build-helper-maven-plugin dependency-version: 3.6.1 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: build-tools ... Signed-off-by: dependabot[bot] --- dspace-api/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dspace-api/pom.xml b/dspace-api/pom.xml index 83a694ad5ca3..7303df1fcef0 100644 --- a/dspace-api/pom.xml +++ b/dspace-api/pom.xml @@ -102,7 +102,7 @@ org.codehaus.mojo build-helper-maven-plugin - 3.6.0 + 3.6.1 validate From d956998393e241b1977f817b312051b23c7601a8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 9 Jun 2025 03:28:14 +0000 Subject: [PATCH 726/979] Bump net.handle:handle from 9.3.1 to 9.3.2 Bumps net.handle:handle from 9.3.1 to 9.3.2. --- updated-dependencies: - dependency-name: net.handle:handle dependency-version: 9.3.2 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 33267c71ed44..118595efacc6 100644 --- a/pom.xml +++ b/pom.xml @@ -1397,7 +1397,7 @@ net.handle handle - 9.3.1 + 9.3.2 From a5ae3705e7ea3de503b6e4dd6e35fac3bacb4d96 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Mon, 9 Jun 2025 10:15:00 -0500 Subject: [PATCH 727/979] Remove commons-fileupload as it is no longer used. --- dspace-api/pom.xml | 4 ---- dspace-sword/pom.xml | 5 ----- pom.xml | 5 ----- 3 files changed, 14 deletions(-) diff --git a/dspace-api/pom.xml b/dspace-api/pom.xml index 756f65ff696c..7b3ab4a315e5 100644 --- a/dspace-api/pom.xml +++ b/dspace-api/pom.xml @@ -475,10 +475,6 @@ org.apache.commons commons-dbcp2 - - commons-fileupload - commons-fileupload - commons-io diff --git a/dspace-sword/pom.xml b/dspace-sword/pom.xml index d0ff123d741c..fc436f668921 100644 --- a/dspace-sword/pom.xml +++ b/dspace-sword/pom.xml @@ -54,11 +54,6 @@ - - - commons-fileupload - commons-fileupload - org.apache.httpcomponents httpclient diff --git a/pom.xml b/pom.xml index af2c99bb775b..cd7606e06c0a 100644 --- a/pom.xml +++ b/pom.xml @@ -1497,11 +1497,6 @@ commons-dbcp2 2.13.0 - - commons-fileupload - commons-fileupload - 1.5 - commons-io commons-io From 1f174f465715744d0fa743f464b38dd37db32c07 Mon Sep 17 00:00:00 2001 From: Jens Vannerum Date: Wed, 9 Apr 2025 17:06:43 +0200 Subject: [PATCH 728/979] 129944: Introduce custom abstract xpath contributor for pubmed to respect their labelled structure - modify IT for it (cherry picked from commit 28bc4970b7a5471f164d400a1bbd3e560b8e3e30) --- .../PubmedAbstractMetadatumContributor.java | 67 +++++++++++++++++++ .../PubmedImportMetadataSourceServiceIT.java | 64 ++++++++---------- .../app/rest/pubmedimport-search-test.xml | 4 +- .../config/spring/api/pubmed-integration.xml | 2 +- 4 files changed, 100 insertions(+), 37 deletions(-) create mode 100644 dspace-api/src/main/java/org/dspace/importer/external/pubmed/metadatamapping/contributor/PubmedAbstractMetadatumContributor.java diff --git a/dspace-api/src/main/java/org/dspace/importer/external/pubmed/metadatamapping/contributor/PubmedAbstractMetadatumContributor.java b/dspace-api/src/main/java/org/dspace/importer/external/pubmed/metadatamapping/contributor/PubmedAbstractMetadatumContributor.java new file mode 100644 index 000000000000..727d6b92ae35 --- /dev/null +++ b/dspace-api/src/main/java/org/dspace/importer/external/pubmed/metadatamapping/contributor/PubmedAbstractMetadatumContributor.java @@ -0,0 +1,67 @@ +/** + * The contents of this file are subject to the license and copyright + * detailed in the LICENSE and NOTICE files at the root of the source + * tree and available online at + * + * http://www.dspace.org/license/ + */ +package org.dspace.importer.external.pubmed.metadatamapping.contributor; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.LinkedList; +import java.util.List; + +import org.dspace.importer.external.metadatamapping.MetadatumDTO; +import org.dspace.importer.external.metadatamapping.contributor.SimpleXpathMetadatumContributor; +import org.jdom2.Element; +import org.jdom2.Namespace; +import org.jdom2.filter.Filters; +import org.jdom2.xpath.XPathExpression; +import org.jdom2.xpath.XPathFactory; + +/** + * This class is responsible for extracting the abstract from a PubMed XML document. + * It uses XPath to find the relevant elements and constructs a formatted string for the abstract, respecting + * PubMed's labelled abstract format, and including the labels in the output. + */ +public class PubmedAbstractMetadatumContributor extends SimpleXpathMetadatumContributor { + + @Override + public Collection contributeMetadata(Element t) { + List values = new LinkedList<>(); + + List namespaces = new ArrayList<>(); + for (String ns : prefixToNamespaceMapping.keySet()) { + namespaces.add(Namespace.getNamespace(prefixToNamespaceMapping.get(ns), ns)); + } + + XPathExpression xpath = XPathFactory.instance().compile(query, Filters.element(), null, namespaces); + List nodes = xpath.evaluate(t); + StringBuilder sb = new StringBuilder(); + + for (Element el : nodes) { + String label = el.getAttributeValue("Label"); + String text = el.getTextNormalize(); + + if (text == null || text.isEmpty()) { + continue; + } + + if (sb.length() > 0) { + sb.append("\n\n"); + } + + if (label != null && !label.equalsIgnoreCase("UNLABELLED")) { + sb.append(label).append(": "); + } + sb.append(text); + } + + String fullAbstract = sb.toString().trim(); + if (!fullAbstract.isEmpty()) { + values.add(metadataFieldMapping.toDCValue(field, fullAbstract)); + } + return values; + } +} diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/PubmedImportMetadataSourceServiceIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/PubmedImportMetadataSourceServiceIT.java index 3b39d251216c..5cac853c07b5 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/PubmedImportMetadataSourceServiceIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/PubmedImportMetadataSourceServiceIT.java @@ -101,35 +101,34 @@ private ArrayList getRecords() { //define first record MetadatumDTO title = createMetadatumDTO("dc","title", null, "Teaching strategies of clinical reasoning in advanced nursing clinical practice: A scoping review."); - MetadatumDTO description1 = createMetadatumDTO("dc", "description", "abstract", "To report and synthesize" - + " the main strategies for teaching clinical reasoning described in the literature in the context of" - + " advanced clinical practice and promote new areas of research to improve the pedagogical approach" - + " to clinical reasoning in Advanced Practice Nursing."); - MetadatumDTO description2 = createMetadatumDTO("dc", "description", "abstract", "Clinical reasoning and" - + " clinical thinking are essential elements in the advanced nursing clinical practice decision-making" - + " process. The quality improvement of care is related to the development of those skills." - + " Therefore, it is crucial to optimize teaching strategies that can enhance the role of clinical" - + " reasoning in advanced clinical practice."); - MetadatumDTO description3 = createMetadatumDTO("dc", "description", "abstract", "A scoping review was" - + " conducted using the framework developed by Arksey and O'Malley as a research strategy." - + " Consistent with the nature of scoping reviews, a study protocol has been established."); - MetadatumDTO description4 = createMetadatumDTO("dc", "description", "abstract", "The studies included and" - + " analyzed in this scoping review cover from January 2016 to June 2022. Primary studies and secondary" - + " revision studies, published in biomedical databases, were selected, including qualitative ones." - + " Electronic databases used were: CINAHL, PubMed, Cochrane Library, Scopus, and OVID." - + " Three authors independently evaluated the articles for titles, abstracts, and full text."); - MetadatumDTO description5 = createMetadatumDTO("dc", "description", "abstract", "1433 articles were examined," - + " applying the eligibility and exclusion criteria 73 studies were assessed for eligibility," - + " and 27 were included in the scoping review. The results that emerged from the review were" - + " interpreted and grouped into three macro strategies (simulations-based education, art and visual" - + " thinking, and other learning approaches) and nineteen educational interventions."); - MetadatumDTO description6 = createMetadatumDTO("dc", "description", "abstract", "Among the different" - + " strategies, the simulations are the most used. Despite this, our scoping review reveals that is" - + " necessary to use different teaching strategies to stimulate critical thinking, improve diagnostic" - + " reasoning, refine clinical judgment, and strengthen decision-making. However, it is not possible to" - + " demonstrate which methodology is more effective in obtaining the learning outcomes necessary to" - + " acquire an adequate level of judgment and critical thinking. Therefore, it will be" - + " necessary to relate teaching methodologies with the skills developed."); + MetadatumDTO description1 = createMetadatumDTO("dc", "description", "abstract", + "AIM/OBJECTIVE: To report and synthesize the main strategies for teaching clinical reasoning " + + "described in the literature in the context of advanced clinical practice and promote new " + + "areas of research to improve the pedagogical approach to clinical reasoning in Advanced " + + "Practice Nursing.\n\nBACKGROUND: Clinical reasoning and clinical thinking are essential " + + "elements in the advanced nursing clinical practice decision-making process. The quality " + + "improvement of care is related to the development of those skills. Therefore, it is crucial " + + "to optimize teaching strategies that can enhance the role of clinical reasoning in advanced " + + "clinical practice.\n\nDESIGN: A scoping review was conducted using the framework developed " + + "by Arksey and O'Malley as a research strategy. Consistent with the nature of scoping reviews" + + ", a study protocol has been established.\n\nMETHODS: The studies included and analyzed in " + + "this scoping review cover from January 2016 to June 2022. Primary studies and secondary " + + "revision studies, published in biomedical databases, were selected, including qualitative " + + "ones. Electronic databases used were: CINAHL, PubMed, Cochrane Library, Scopus, and OVID. " + + "Three authors independently evaluated the articles for titles, abstracts, and full text.\n\n" + + "RESULTS: 1433 articles were examined, applying the eligibility and exclusion criteria 73 " + + "studies were assessed for eligibility, and 27 were included in the scoping review. The " + + "results that emerged from the review were interpreted and grouped into three macro " + + "strategies (simulations-based education, art and visual thinking, and other learning " + + "approaches) and nineteen educational interventions.\n\nCONCLUSIONS: Among the different " + + "strategies, the simulations are the most used. Despite this, our scoping review reveals " + + "that is necessary to use different teaching strategies to stimulate critical thinking, " + + "improve diagnostic reasoning, refine clinical judgment, and strengthen decision-making. " + + "However, it is not possible to demonstrate which methodology is more effective in obtaining " + + "the learning outcomes necessary to acquire an adequate level of judgment and critical " + + "thinking. Therefore, it will be necessary to relate teaching methodologies with the skills " + + "developed.\n\nAn unlabeled section of an abstract.\n\nAn abstract section with no attributes" + + " at all, concerning."); MetadatumDTO identifierOther = createMetadatumDTO("dc", "identifier", "other", "36708638"); MetadatumDTO author1 = createMetadatumDTO("dc", "contributor", "author", "Giuffrida, Silvia"); MetadatumDTO author2 = createMetadatumDTO("dc", "contributor", "author", "Silano, Verdiana"); @@ -148,11 +147,6 @@ private ArrayList getRecords() { metadatums.add(title); metadatums.add(description1); - metadatums.add(description2); - metadatums.add(description3); - metadatums.add(description4); - metadatums.add(description5); - metadatums.add(description6); metadatums.add(identifierOther); metadatums.add(author1); metadatums.add(author2); @@ -210,4 +204,4 @@ private ArrayList getRecords2() { return records; } -} \ No newline at end of file +} diff --git a/dspace-server-webapp/src/test/resources/org/dspace/app/rest/pubmedimport-search-test.xml b/dspace-server-webapp/src/test/resources/org/dspace/app/rest/pubmedimport-search-test.xml index 666fb1e7d550..6af527246900 100644 --- a/dspace-server-webapp/src/test/resources/org/dspace/app/rest/pubmedimport-search-test.xml +++ b/dspace-server-webapp/src/test/resources/org/dspace/app/rest/pubmedimport-search-test.xml @@ -41,6 +41,8 @@ The studies included and analyzed in this scoping review cover from January 2016 to June 2022. Primary studies and secondary revision studies, published in biomedical databases, were selected, including qualitative ones. Electronic databases used were: CINAHL, PubMed, Cochrane Library, Scopus, and OVID. Three authors independently evaluated the articles for titles, abstracts, and full text. 1433 articles were examined, applying the eligibility and exclusion criteria 73 studies were assessed for eligibility, and 27 were included in the scoping review. The results that emerged from the review were interpreted and grouped into three macro strategies (simulations-based education, art and visual thinking, and other learning approaches) and nineteen educational interventions. Among the different strategies, the simulations are the most used. Despite this, our scoping review reveals that is necessary to use different teaching strategies to stimulate critical thinking, improve diagnostic reasoning, refine clinical judgment, and strengthen decision-making. However, it is not possible to demonstrate which methodology is more effective in obtaining the learning outcomes necessary to acquire an adequate level of judgment and critical thinking. Therefore, it will be necessary to relate teaching methodologies with the skills developed. + An unlabeled section of an abstract. + An abstract section with no attributes at all, concerning. Copyright © 2023 Elsevier Ltd. All rights reserved. @@ -191,4 +193,4 @@ - \ No newline at end of file + diff --git a/dspace/config/spring/api/pubmed-integration.xml b/dspace/config/spring/api/pubmed-integration.xml index adec4456ea03..f488327789f9 100644 --- a/dspace/config/spring/api/pubmed-integration.xml +++ b/dspace/config/spring/api/pubmed-integration.xml @@ -57,7 +57,7 @@ - + From 162e3c31a52001ec9f0230cac2382ca3e23d91dc Mon Sep 17 00:00:00 2001 From: Jens Vannerum Date: Wed, 9 Apr 2025 17:06:43 +0200 Subject: [PATCH 729/979] 129944: Introduce custom abstract xpath contributor for pubmed to respect their labelled structure - modify IT for it (cherry picked from commit 28bc4970b7a5471f164d400a1bbd3e560b8e3e30) --- .../PubmedAbstractMetadatumContributor.java | 67 +++++++++++++++++++ .../PubmedImportMetadataSourceServiceIT.java | 64 ++++++++---------- .../app/rest/pubmedimport-search-test.xml | 4 +- .../config/spring/api/pubmed-integration.xml | 2 +- 4 files changed, 100 insertions(+), 37 deletions(-) create mode 100644 dspace-api/src/main/java/org/dspace/importer/external/pubmed/metadatamapping/contributor/PubmedAbstractMetadatumContributor.java diff --git a/dspace-api/src/main/java/org/dspace/importer/external/pubmed/metadatamapping/contributor/PubmedAbstractMetadatumContributor.java b/dspace-api/src/main/java/org/dspace/importer/external/pubmed/metadatamapping/contributor/PubmedAbstractMetadatumContributor.java new file mode 100644 index 000000000000..727d6b92ae35 --- /dev/null +++ b/dspace-api/src/main/java/org/dspace/importer/external/pubmed/metadatamapping/contributor/PubmedAbstractMetadatumContributor.java @@ -0,0 +1,67 @@ +/** + * The contents of this file are subject to the license and copyright + * detailed in the LICENSE and NOTICE files at the root of the source + * tree and available online at + * + * http://www.dspace.org/license/ + */ +package org.dspace.importer.external.pubmed.metadatamapping.contributor; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.LinkedList; +import java.util.List; + +import org.dspace.importer.external.metadatamapping.MetadatumDTO; +import org.dspace.importer.external.metadatamapping.contributor.SimpleXpathMetadatumContributor; +import org.jdom2.Element; +import org.jdom2.Namespace; +import org.jdom2.filter.Filters; +import org.jdom2.xpath.XPathExpression; +import org.jdom2.xpath.XPathFactory; + +/** + * This class is responsible for extracting the abstract from a PubMed XML document. + * It uses XPath to find the relevant elements and constructs a formatted string for the abstract, respecting + * PubMed's labelled abstract format, and including the labels in the output. + */ +public class PubmedAbstractMetadatumContributor extends SimpleXpathMetadatumContributor { + + @Override + public Collection contributeMetadata(Element t) { + List values = new LinkedList<>(); + + List namespaces = new ArrayList<>(); + for (String ns : prefixToNamespaceMapping.keySet()) { + namespaces.add(Namespace.getNamespace(prefixToNamespaceMapping.get(ns), ns)); + } + + XPathExpression xpath = XPathFactory.instance().compile(query, Filters.element(), null, namespaces); + List nodes = xpath.evaluate(t); + StringBuilder sb = new StringBuilder(); + + for (Element el : nodes) { + String label = el.getAttributeValue("Label"); + String text = el.getTextNormalize(); + + if (text == null || text.isEmpty()) { + continue; + } + + if (sb.length() > 0) { + sb.append("\n\n"); + } + + if (label != null && !label.equalsIgnoreCase("UNLABELLED")) { + sb.append(label).append(": "); + } + sb.append(text); + } + + String fullAbstract = sb.toString().trim(); + if (!fullAbstract.isEmpty()) { + values.add(metadataFieldMapping.toDCValue(field, fullAbstract)); + } + return values; + } +} diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/PubmedImportMetadataSourceServiceIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/PubmedImportMetadataSourceServiceIT.java index 3b39d251216c..5cac853c07b5 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/PubmedImportMetadataSourceServiceIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/PubmedImportMetadataSourceServiceIT.java @@ -101,35 +101,34 @@ private ArrayList getRecords() { //define first record MetadatumDTO title = createMetadatumDTO("dc","title", null, "Teaching strategies of clinical reasoning in advanced nursing clinical practice: A scoping review."); - MetadatumDTO description1 = createMetadatumDTO("dc", "description", "abstract", "To report and synthesize" - + " the main strategies for teaching clinical reasoning described in the literature in the context of" - + " advanced clinical practice and promote new areas of research to improve the pedagogical approach" - + " to clinical reasoning in Advanced Practice Nursing."); - MetadatumDTO description2 = createMetadatumDTO("dc", "description", "abstract", "Clinical reasoning and" - + " clinical thinking are essential elements in the advanced nursing clinical practice decision-making" - + " process. The quality improvement of care is related to the development of those skills." - + " Therefore, it is crucial to optimize teaching strategies that can enhance the role of clinical" - + " reasoning in advanced clinical practice."); - MetadatumDTO description3 = createMetadatumDTO("dc", "description", "abstract", "A scoping review was" - + " conducted using the framework developed by Arksey and O'Malley as a research strategy." - + " Consistent with the nature of scoping reviews, a study protocol has been established."); - MetadatumDTO description4 = createMetadatumDTO("dc", "description", "abstract", "The studies included and" - + " analyzed in this scoping review cover from January 2016 to June 2022. Primary studies and secondary" - + " revision studies, published in biomedical databases, were selected, including qualitative ones." - + " Electronic databases used were: CINAHL, PubMed, Cochrane Library, Scopus, and OVID." - + " Three authors independently evaluated the articles for titles, abstracts, and full text."); - MetadatumDTO description5 = createMetadatumDTO("dc", "description", "abstract", "1433 articles were examined," - + " applying the eligibility and exclusion criteria 73 studies were assessed for eligibility," - + " and 27 were included in the scoping review. The results that emerged from the review were" - + " interpreted and grouped into three macro strategies (simulations-based education, art and visual" - + " thinking, and other learning approaches) and nineteen educational interventions."); - MetadatumDTO description6 = createMetadatumDTO("dc", "description", "abstract", "Among the different" - + " strategies, the simulations are the most used. Despite this, our scoping review reveals that is" - + " necessary to use different teaching strategies to stimulate critical thinking, improve diagnostic" - + " reasoning, refine clinical judgment, and strengthen decision-making. However, it is not possible to" - + " demonstrate which methodology is more effective in obtaining the learning outcomes necessary to" - + " acquire an adequate level of judgment and critical thinking. Therefore, it will be" - + " necessary to relate teaching methodologies with the skills developed."); + MetadatumDTO description1 = createMetadatumDTO("dc", "description", "abstract", + "AIM/OBJECTIVE: To report and synthesize the main strategies for teaching clinical reasoning " + + "described in the literature in the context of advanced clinical practice and promote new " + + "areas of research to improve the pedagogical approach to clinical reasoning in Advanced " + + "Practice Nursing.\n\nBACKGROUND: Clinical reasoning and clinical thinking are essential " + + "elements in the advanced nursing clinical practice decision-making process. The quality " + + "improvement of care is related to the development of those skills. Therefore, it is crucial " + + "to optimize teaching strategies that can enhance the role of clinical reasoning in advanced " + + "clinical practice.\n\nDESIGN: A scoping review was conducted using the framework developed " + + "by Arksey and O'Malley as a research strategy. Consistent with the nature of scoping reviews" + + ", a study protocol has been established.\n\nMETHODS: The studies included and analyzed in " + + "this scoping review cover from January 2016 to June 2022. Primary studies and secondary " + + "revision studies, published in biomedical databases, were selected, including qualitative " + + "ones. Electronic databases used were: CINAHL, PubMed, Cochrane Library, Scopus, and OVID. " + + "Three authors independently evaluated the articles for titles, abstracts, and full text.\n\n" + + "RESULTS: 1433 articles were examined, applying the eligibility and exclusion criteria 73 " + + "studies were assessed for eligibility, and 27 were included in the scoping review. The " + + "results that emerged from the review were interpreted and grouped into three macro " + + "strategies (simulations-based education, art and visual thinking, and other learning " + + "approaches) and nineteen educational interventions.\n\nCONCLUSIONS: Among the different " + + "strategies, the simulations are the most used. Despite this, our scoping review reveals " + + "that is necessary to use different teaching strategies to stimulate critical thinking, " + + "improve diagnostic reasoning, refine clinical judgment, and strengthen decision-making. " + + "However, it is not possible to demonstrate which methodology is more effective in obtaining " + + "the learning outcomes necessary to acquire an adequate level of judgment and critical " + + "thinking. Therefore, it will be necessary to relate teaching methodologies with the skills " + + "developed.\n\nAn unlabeled section of an abstract.\n\nAn abstract section with no attributes" + + " at all, concerning."); MetadatumDTO identifierOther = createMetadatumDTO("dc", "identifier", "other", "36708638"); MetadatumDTO author1 = createMetadatumDTO("dc", "contributor", "author", "Giuffrida, Silvia"); MetadatumDTO author2 = createMetadatumDTO("dc", "contributor", "author", "Silano, Verdiana"); @@ -148,11 +147,6 @@ private ArrayList getRecords() { metadatums.add(title); metadatums.add(description1); - metadatums.add(description2); - metadatums.add(description3); - metadatums.add(description4); - metadatums.add(description5); - metadatums.add(description6); metadatums.add(identifierOther); metadatums.add(author1); metadatums.add(author2); @@ -210,4 +204,4 @@ private ArrayList getRecords2() { return records; } -} \ No newline at end of file +} diff --git a/dspace-server-webapp/src/test/resources/org/dspace/app/rest/pubmedimport-search-test.xml b/dspace-server-webapp/src/test/resources/org/dspace/app/rest/pubmedimport-search-test.xml index 666fb1e7d550..6af527246900 100644 --- a/dspace-server-webapp/src/test/resources/org/dspace/app/rest/pubmedimport-search-test.xml +++ b/dspace-server-webapp/src/test/resources/org/dspace/app/rest/pubmedimport-search-test.xml @@ -41,6 +41,8 @@ The studies included and analyzed in this scoping review cover from January 2016 to June 2022. Primary studies and secondary revision studies, published in biomedical databases, were selected, including qualitative ones. Electronic databases used were: CINAHL, PubMed, Cochrane Library, Scopus, and OVID. Three authors independently evaluated the articles for titles, abstracts, and full text. 1433 articles were examined, applying the eligibility and exclusion criteria 73 studies were assessed for eligibility, and 27 were included in the scoping review. The results that emerged from the review were interpreted and grouped into three macro strategies (simulations-based education, art and visual thinking, and other learning approaches) and nineteen educational interventions. Among the different strategies, the simulations are the most used. Despite this, our scoping review reveals that is necessary to use different teaching strategies to stimulate critical thinking, improve diagnostic reasoning, refine clinical judgment, and strengthen decision-making. However, it is not possible to demonstrate which methodology is more effective in obtaining the learning outcomes necessary to acquire an adequate level of judgment and critical thinking. Therefore, it will be necessary to relate teaching methodologies with the skills developed. + An unlabeled section of an abstract. + An abstract section with no attributes at all, concerning. Copyright © 2023 Elsevier Ltd. All rights reserved. @@ -191,4 +193,4 @@ - \ No newline at end of file + diff --git a/dspace/config/spring/api/pubmed-integration.xml b/dspace/config/spring/api/pubmed-integration.xml index adec4456ea03..f488327789f9 100644 --- a/dspace/config/spring/api/pubmed-integration.xml +++ b/dspace/config/spring/api/pubmed-integration.xml @@ -57,7 +57,7 @@ - + From 2c400bf2da591bcb9d0978dfd60a44f0de04112e Mon Sep 17 00:00:00 2001 From: DSpace Bot <68393067+dspace-bot@users.noreply.github.com> Date: Wed, 11 Jun 2025 07:20:59 -0500 Subject: [PATCH 730/979] [Port dspace-7_x] Optimization of Solr Queries: Transition to Filter Queries (#10887) * use filter query instead of generic query (cherry picked from commit f2417feeca653aefdd7a201460afe3d9429bacae) * use filter query instead of generic query (cherry picked from commit d83a2525ad96e9d4cb935481c505537bdee0cae5) * use filter query instead of generic query (cherry picked from commit f3a976107e2afef5c2d0fb19e580bbbd754acc14) * remove obsolete comment (cherry picked from commit 3ee2dbcc56c157499c6db96cc3b8e90c2ae47e8f) * use filter query instead of generic query (cherry picked from commit 318afc769a6f0958c795da7506b25a28cf298f09) * add static imports (cherry picked from commit 8ad19c42df754daa6ed70c6e9d1c9146e72c0b32) * move static import to the top of the import block (cherry picked from commit b85585c34e528ce2165e25a0cf23351283360ac7) * move static imports to the top of the import block (cherry picked from commit 4b446e24a068027e8cf3c3935ad256f39b4b65ef) --------- Co-authored-by: Sascha Szott --- .../java/org/dspace/app/sitemap/GenerateSitemaps.java | 11 ++++++++--- .../app/solrdatabaseresync/SolrDatabaseResyncCli.java | 3 ++- .../main/java/org/dspace/browse/SolrBrowseDAO.java | 9 +++++++-- .../java/org/dspace/discovery/SolrSearchCore.java | 8 +++++--- .../org/dspace/statistics/SolrLoggerServiceImpl.java | 1 - 5 files changed, 22 insertions(+), 10 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/app/sitemap/GenerateSitemaps.java b/dspace-api/src/main/java/org/dspace/app/sitemap/GenerateSitemaps.java index 90962d12aa75..41b0b0f6b3dd 100644 --- a/dspace-api/src/main/java/org/dspace/app/sitemap/GenerateSitemaps.java +++ b/dspace-api/src/main/java/org/dspace/app/sitemap/GenerateSitemaps.java @@ -7,6 +7,8 @@ */ package org.dspace.app.sitemap; +import static org.dspace.discovery.SearchUtils.RESOURCE_TYPE_FIELD; + import java.io.File; import java.io.IOException; import java.sql.SQLException; @@ -189,7 +191,8 @@ public static void generateSitemaps(boolean makeHTMLMap, boolean makeSitemapOrg) try { DiscoverQuery discoveryQuery = new DiscoverQuery(); discoveryQuery.setMaxResults(PAGE_SIZE); - discoveryQuery.setQuery("search.resourcetype:Community"); + discoveryQuery.setQuery("*:*"); + discoveryQuery.addFilterQueries(RESOURCE_TYPE_FIELD + ":Community"); do { discoveryQuery.setStart(offset); DiscoverResult discoverResult = searchService.search(c, discoveryQuery); @@ -213,7 +216,8 @@ public static void generateSitemaps(boolean makeHTMLMap, boolean makeSitemapOrg) offset = 0; discoveryQuery = new DiscoverQuery(); discoveryQuery.setMaxResults(PAGE_SIZE); - discoveryQuery.setQuery("search.resourcetype:Collection"); + discoveryQuery.setQuery("*:*"); + discoveryQuery.addFilterQueries(RESOURCE_TYPE_FIELD + ":Collection"); do { discoveryQuery.setStart(offset); DiscoverResult discoverResult = searchService.search(c, discoveryQuery); @@ -237,7 +241,8 @@ public static void generateSitemaps(boolean makeHTMLMap, boolean makeSitemapOrg) offset = 0; discoveryQuery = new DiscoverQuery(); discoveryQuery.setMaxResults(PAGE_SIZE); - discoveryQuery.setQuery("search.resourcetype:Item"); + discoveryQuery.setQuery("*:*"); + discoveryQuery.addFilterQueries(RESOURCE_TYPE_FIELD + ":Item"); discoveryQuery.addSearchField("search.entitytype"); do { diff --git a/dspace-api/src/main/java/org/dspace/app/solrdatabaseresync/SolrDatabaseResyncCli.java b/dspace-api/src/main/java/org/dspace/app/solrdatabaseresync/SolrDatabaseResyncCli.java index aac42ce1acf9..0ce6b1a9ef3d 100644 --- a/dspace-api/src/main/java/org/dspace/app/solrdatabaseresync/SolrDatabaseResyncCli.java +++ b/dspace-api/src/main/java/org/dspace/app/solrdatabaseresync/SolrDatabaseResyncCli.java @@ -98,7 +98,8 @@ public void internalRun() throws Exception { private void performStatusUpdate(Context context) throws SearchServiceException, SolrServerException, IOException { SolrQuery solrQuery = new SolrQuery(); - solrQuery.setQuery(STATUS_FIELD + ":" + STATUS_FIELD_PREDB); + solrQuery.setQuery("*:*"); + solrQuery.addFilterQuery(STATUS_FIELD + ":" + STATUS_FIELD_PREDB); solrQuery.addFilterQuery(SearchUtils.RESOURCE_TYPE_FIELD + ":" + IndexableItem.TYPE); String dateRangeFilter = SearchUtils.LAST_INDEXED_FIELD + ":[* TO " + maxTime + "]"; logDebugAndOut("Date range filter used; " + dateRangeFilter); 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 14dab3e56174..a0a7725fa13a 100644 --- a/dspace-api/src/main/java/org/dspace/browse/SolrBrowseDAO.java +++ b/dspace-api/src/main/java/org/dspace/browse/SolrBrowseDAO.java @@ -7,6 +7,9 @@ */ package org.dspace.browse; +import static org.dspace.discovery.SearchUtils.RESOURCE_ID_FIELD; +import static org.dspace.discovery.SearchUtils.RESOURCE_TYPE_FIELD; + import java.io.Serializable; import java.util.ArrayList; import java.util.Collections; @@ -308,8 +311,10 @@ public List doQuery() throws BrowseException { public String doMaxQuery(String column, String table, int itemID) throws BrowseException { DiscoverQuery query = new DiscoverQuery(); - query.setQuery("search.resourceid:" + itemID - + " AND search.resourcetype:" + IndexableItem.TYPE); + query.setQuery("*:*"); + query.addFilterQueries( + RESOURCE_ID_FIELD + ":" + itemID, + RESOURCE_TYPE_FIELD + ":" + IndexableItem.TYPE); query.setMaxResults(1); DiscoverResult resp = null; try { diff --git a/dspace-api/src/main/java/org/dspace/discovery/SolrSearchCore.java b/dspace-api/src/main/java/org/dspace/discovery/SolrSearchCore.java index f31feab6123a..0d5dfad3b8b6 100644 --- a/dspace-api/src/main/java/org/dspace/discovery/SolrSearchCore.java +++ b/dspace-api/src/main/java/org/dspace/discovery/SolrSearchCore.java @@ -88,9 +88,11 @@ protected void initSolr() { solrServer.setBaseURL(solrService); solrServer.setUseMultiPartPost(true); // Dummy/test query to search for Item (type=2) of ID=1 - SolrQuery solrQuery = new SolrQuery() - .setQuery(SearchUtils.RESOURCE_TYPE_FIELD + ":" + IndexableItem.TYPE + - " AND " + SearchUtils.RESOURCE_ID_FIELD + ":1"); + SolrQuery solrQuery = new SolrQuery(); + solrQuery.setQuery("*:*"); + solrQuery.addFilterQuery( + SearchUtils.RESOURCE_TYPE_FIELD + ":" + IndexableItem.TYPE, + SearchUtils.RESOURCE_ID_FIELD + ":1"); // Only return obj identifier fields in result doc solrQuery.setFields(SearchUtils.RESOURCE_TYPE_FIELD, SearchUtils.RESOURCE_ID_FIELD); solrServer.query(solrQuery, REQUEST_METHOD); diff --git a/dspace-api/src/main/java/org/dspace/statistics/SolrLoggerServiceImpl.java b/dspace-api/src/main/java/org/dspace/statistics/SolrLoggerServiceImpl.java index 6c3ccdc082eb..35f86e3a1c0a 100644 --- a/dspace-api/src/main/java/org/dspace/statistics/SolrLoggerServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/statistics/SolrLoggerServiceImpl.java @@ -1093,7 +1093,6 @@ public QueryResponse query(String query, String filterQuery, String facetField, return null; } - // System.out.println("QUERY"); SolrQuery solrQuery = new SolrQuery().setRows(rows).setQuery(query) .setFacetMinCount(facetMinCount); addAdditionalSolrYearCores(solrQuery); From 64d1c88249b5e1d49d0392b1cdd49b5b90fc26f7 Mon Sep 17 00:00:00 2001 From: DSpace Bot <68393067+dspace-bot@users.noreply.github.com> Date: Wed, 11 Jun 2025 07:21:52 -0500 Subject: [PATCH 731/979] [Port dspace-8_x] Optimization of Solr Queries: Transition to Filter Queries (#10888) * use filter query instead of generic query (cherry picked from commit f2417feeca653aefdd7a201460afe3d9429bacae) * use filter query instead of generic query (cherry picked from commit d83a2525ad96e9d4cb935481c505537bdee0cae5) * use filter query instead of generic query (cherry picked from commit f3a976107e2afef5c2d0fb19e580bbbd754acc14) * remove obsolete comment (cherry picked from commit 3ee2dbcc56c157499c6db96cc3b8e90c2ae47e8f) * use filter query instead of generic query (cherry picked from commit 318afc769a6f0958c795da7506b25a28cf298f09) * add static imports (cherry picked from commit 8ad19c42df754daa6ed70c6e9d1c9146e72c0b32) * move static import to the top of the import block (cherry picked from commit b85585c34e528ce2165e25a0cf23351283360ac7) * move static imports to the top of the import block (cherry picked from commit 4b446e24a068027e8cf3c3935ad256f39b4b65ef) --------- Co-authored-by: Sascha Szott --- .../java/org/dspace/app/sitemap/GenerateSitemaps.java | 11 ++++++++--- .../app/solrdatabaseresync/SolrDatabaseResyncCli.java | 3 ++- .../main/java/org/dspace/browse/SolrBrowseDAO.java | 9 +++++++-- .../java/org/dspace/discovery/SolrSearchCore.java | 8 +++++--- .../org/dspace/statistics/SolrLoggerServiceImpl.java | 1 - 5 files changed, 22 insertions(+), 10 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/app/sitemap/GenerateSitemaps.java b/dspace-api/src/main/java/org/dspace/app/sitemap/GenerateSitemaps.java index 90962d12aa75..41b0b0f6b3dd 100644 --- a/dspace-api/src/main/java/org/dspace/app/sitemap/GenerateSitemaps.java +++ b/dspace-api/src/main/java/org/dspace/app/sitemap/GenerateSitemaps.java @@ -7,6 +7,8 @@ */ package org.dspace.app.sitemap; +import static org.dspace.discovery.SearchUtils.RESOURCE_TYPE_FIELD; + import java.io.File; import java.io.IOException; import java.sql.SQLException; @@ -189,7 +191,8 @@ public static void generateSitemaps(boolean makeHTMLMap, boolean makeSitemapOrg) try { DiscoverQuery discoveryQuery = new DiscoverQuery(); discoveryQuery.setMaxResults(PAGE_SIZE); - discoveryQuery.setQuery("search.resourcetype:Community"); + discoveryQuery.setQuery("*:*"); + discoveryQuery.addFilterQueries(RESOURCE_TYPE_FIELD + ":Community"); do { discoveryQuery.setStart(offset); DiscoverResult discoverResult = searchService.search(c, discoveryQuery); @@ -213,7 +216,8 @@ public static void generateSitemaps(boolean makeHTMLMap, boolean makeSitemapOrg) offset = 0; discoveryQuery = new DiscoverQuery(); discoveryQuery.setMaxResults(PAGE_SIZE); - discoveryQuery.setQuery("search.resourcetype:Collection"); + discoveryQuery.setQuery("*:*"); + discoveryQuery.addFilterQueries(RESOURCE_TYPE_FIELD + ":Collection"); do { discoveryQuery.setStart(offset); DiscoverResult discoverResult = searchService.search(c, discoveryQuery); @@ -237,7 +241,8 @@ public static void generateSitemaps(boolean makeHTMLMap, boolean makeSitemapOrg) offset = 0; discoveryQuery = new DiscoverQuery(); discoveryQuery.setMaxResults(PAGE_SIZE); - discoveryQuery.setQuery("search.resourcetype:Item"); + discoveryQuery.setQuery("*:*"); + discoveryQuery.addFilterQueries(RESOURCE_TYPE_FIELD + ":Item"); discoveryQuery.addSearchField("search.entitytype"); do { diff --git a/dspace-api/src/main/java/org/dspace/app/solrdatabaseresync/SolrDatabaseResyncCli.java b/dspace-api/src/main/java/org/dspace/app/solrdatabaseresync/SolrDatabaseResyncCli.java index aac42ce1acf9..0ce6b1a9ef3d 100644 --- a/dspace-api/src/main/java/org/dspace/app/solrdatabaseresync/SolrDatabaseResyncCli.java +++ b/dspace-api/src/main/java/org/dspace/app/solrdatabaseresync/SolrDatabaseResyncCli.java @@ -98,7 +98,8 @@ public void internalRun() throws Exception { private void performStatusUpdate(Context context) throws SearchServiceException, SolrServerException, IOException { SolrQuery solrQuery = new SolrQuery(); - solrQuery.setQuery(STATUS_FIELD + ":" + STATUS_FIELD_PREDB); + solrQuery.setQuery("*:*"); + solrQuery.addFilterQuery(STATUS_FIELD + ":" + STATUS_FIELD_PREDB); solrQuery.addFilterQuery(SearchUtils.RESOURCE_TYPE_FIELD + ":" + IndexableItem.TYPE); String dateRangeFilter = SearchUtils.LAST_INDEXED_FIELD + ":[* TO " + maxTime + "]"; logDebugAndOut("Date range filter used; " + dateRangeFilter); 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 14dab3e56174..a0a7725fa13a 100644 --- a/dspace-api/src/main/java/org/dspace/browse/SolrBrowseDAO.java +++ b/dspace-api/src/main/java/org/dspace/browse/SolrBrowseDAO.java @@ -7,6 +7,9 @@ */ package org.dspace.browse; +import static org.dspace.discovery.SearchUtils.RESOURCE_ID_FIELD; +import static org.dspace.discovery.SearchUtils.RESOURCE_TYPE_FIELD; + import java.io.Serializable; import java.util.ArrayList; import java.util.Collections; @@ -308,8 +311,10 @@ public List doQuery() throws BrowseException { public String doMaxQuery(String column, String table, int itemID) throws BrowseException { DiscoverQuery query = new DiscoverQuery(); - query.setQuery("search.resourceid:" + itemID - + " AND search.resourcetype:" + IndexableItem.TYPE); + query.setQuery("*:*"); + query.addFilterQueries( + RESOURCE_ID_FIELD + ":" + itemID, + RESOURCE_TYPE_FIELD + ":" + IndexableItem.TYPE); query.setMaxResults(1); DiscoverResult resp = null; try { diff --git a/dspace-api/src/main/java/org/dspace/discovery/SolrSearchCore.java b/dspace-api/src/main/java/org/dspace/discovery/SolrSearchCore.java index 6304f39a8ca9..57c56dc35348 100644 --- a/dspace-api/src/main/java/org/dspace/discovery/SolrSearchCore.java +++ b/dspace-api/src/main/java/org/dspace/discovery/SolrSearchCore.java @@ -88,9 +88,11 @@ protected void initSolr() { solrServer.setBaseURL(solrService); solrServer.setUseMultiPartPost(true); // Dummy/test query to search for Item (type=2) of ID=1 - SolrQuery solrQuery = new SolrQuery() - .setQuery(SearchUtils.RESOURCE_TYPE_FIELD + ":" + IndexableItem.TYPE + - " AND " + SearchUtils.RESOURCE_ID_FIELD + ":1"); + SolrQuery solrQuery = new SolrQuery(); + solrQuery.setQuery("*:*"); + solrQuery.addFilterQuery( + SearchUtils.RESOURCE_TYPE_FIELD + ":" + IndexableItem.TYPE, + SearchUtils.RESOURCE_ID_FIELD + ":1"); // Only return obj identifier fields in result doc solrQuery.setFields(SearchUtils.RESOURCE_TYPE_FIELD, SearchUtils.RESOURCE_ID_FIELD); solrServer.query(solrQuery, REQUEST_METHOD); diff --git a/dspace-api/src/main/java/org/dspace/statistics/SolrLoggerServiceImpl.java b/dspace-api/src/main/java/org/dspace/statistics/SolrLoggerServiceImpl.java index 9e5ba3256c0b..32de86744d13 100644 --- a/dspace-api/src/main/java/org/dspace/statistics/SolrLoggerServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/statistics/SolrLoggerServiceImpl.java @@ -1045,7 +1045,6 @@ public QueryResponse query(String query, String filterQuery, String facetField, return null; } - // System.out.println("QUERY"); SolrQuery solrQuery = new SolrQuery().setRows(rows).setQuery(query) .setFacetMinCount(facetMinCount); addAdditionalSolrYearCores(solrQuery); From 2abbe67ea6de8093f2223b8a44991618ec2fc934 Mon Sep 17 00:00:00 2001 From: DSpace Bot <68393067+dspace-bot@users.noreply.github.com> Date: Wed, 11 Jun 2025 07:26:49 -0500 Subject: [PATCH 732/979] [Port dspace-7_x] improve robustness of search in index field submit (use filter query) (#10890) * improve robustness of search in index field submit (use filter query) (cherry picked from commit a65ef008b74cac3a81c17bfd6ca44e095f6a3771) * fix checkstyle warnings (cherry picked from commit 183d5ca67113ea750a9de1a13413d34eac946169) * fix checkstyle warning (cherry picked from commit fe251f39e38dcdbba6ab3183a12fc5385c320b12) --------- Co-authored-by: Sascha Szott --- .../dspace/content/EntityTypeServiceImpl.java | 24 +++++++++++++------ 1 file changed, 17 insertions(+), 7 deletions(-) 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 b5b066d9c36f..7df892cd56f5 100644 --- a/dspace-api/src/main/java/org/dspace/content/EntityTypeServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/content/EntityTypeServiceImpl.java @@ -30,6 +30,7 @@ import org.dspace.core.Context; import org.dspace.discovery.SolrSearchCore; import org.dspace.discovery.indexobject.IndexableCollection; +import org.dspace.eperson.EPerson; import org.dspace.eperson.Group; import org.dspace.eperson.service.GroupService; import org.springframework.beans.factory.annotation.Autowired; @@ -124,24 +125,33 @@ public void delete(Context context,EntityType entityType) throws SQLException, A public List getSubmitAuthorizedTypes(Context context) throws SQLException, SolrServerException, IOException { List types = new ArrayList<>(); - StringBuilder query = new StringBuilder(); - org.dspace.eperson.EPerson currentUser = context.getCurrentUser(); + StringBuilder query = null; + EPerson currentUser = context.getCurrentUser(); if (!authorizeService.isAdmin(context)) { String userId = ""; if (currentUser != null) { userId = currentUser.getID().toString(); + query = new StringBuilder(); + query.append("submit:(e").append(userId); } - query.append("submit:(e").append(userId); + Set groups = groupService.allMemberGroupsSet(context, currentUser); for (Group group : groups) { - query.append(" OR g").append(group.getID()); + if (query == null) { + query = new StringBuilder(); + query.append("submit:(g"); + } else { + query.append(" OR g"); + } + query.append(group.getID()); } query.append(")"); - } else { - query.append("*:*"); } - SolrQuery sQuery = new SolrQuery(query.toString()); + SolrQuery sQuery = new SolrQuery("*:*"); + if (query != null) { + sQuery.addFilterQuery(query.toString()); + } sQuery.addFilterQuery("search.resourcetype:" + IndexableCollection.TYPE); sQuery.setRows(0); sQuery.addFacetField("search.entitytype"); From 1c473dc1bac1ed9de04756c9f3112648089e294a Mon Sep 17 00:00:00 2001 From: DSpace Bot <68393067+dspace-bot@users.noreply.github.com> Date: Wed, 11 Jun 2025 07:27:13 -0500 Subject: [PATCH 733/979] [Port dspace-8_x] improve robustness of search in index field submit (use filter query) (#10891) * improve robustness of search in index field submit (use filter query) (cherry picked from commit a65ef008b74cac3a81c17bfd6ca44e095f6a3771) * fix checkstyle warnings (cherry picked from commit 183d5ca67113ea750a9de1a13413d34eac946169) * fix checkstyle warning (cherry picked from commit fe251f39e38dcdbba6ab3183a12fc5385c320b12) --------- Co-authored-by: Sascha Szott --- .../dspace/content/EntityTypeServiceImpl.java | 24 +++++++++++++------ 1 file changed, 17 insertions(+), 7 deletions(-) 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 b5b066d9c36f..7df892cd56f5 100644 --- a/dspace-api/src/main/java/org/dspace/content/EntityTypeServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/content/EntityTypeServiceImpl.java @@ -30,6 +30,7 @@ import org.dspace.core.Context; import org.dspace.discovery.SolrSearchCore; import org.dspace.discovery.indexobject.IndexableCollection; +import org.dspace.eperson.EPerson; import org.dspace.eperson.Group; import org.dspace.eperson.service.GroupService; import org.springframework.beans.factory.annotation.Autowired; @@ -124,24 +125,33 @@ public void delete(Context context,EntityType entityType) throws SQLException, A public List getSubmitAuthorizedTypes(Context context) throws SQLException, SolrServerException, IOException { List types = new ArrayList<>(); - StringBuilder query = new StringBuilder(); - org.dspace.eperson.EPerson currentUser = context.getCurrentUser(); + StringBuilder query = null; + EPerson currentUser = context.getCurrentUser(); if (!authorizeService.isAdmin(context)) { String userId = ""; if (currentUser != null) { userId = currentUser.getID().toString(); + query = new StringBuilder(); + query.append("submit:(e").append(userId); } - query.append("submit:(e").append(userId); + Set groups = groupService.allMemberGroupsSet(context, currentUser); for (Group group : groups) { - query.append(" OR g").append(group.getID()); + if (query == null) { + query = new StringBuilder(); + query.append("submit:(g"); + } else { + query.append(" OR g"); + } + query.append(group.getID()); } query.append(")"); - } else { - query.append("*:*"); } - SolrQuery sQuery = new SolrQuery(query.toString()); + SolrQuery sQuery = new SolrQuery("*:*"); + if (query != null) { + sQuery.addFilterQuery(query.toString()); + } sQuery.addFilterQuery("search.resourcetype:" + IndexableCollection.TYPE); sQuery.setRows(0); sQuery.addFacetField("search.entitytype"); From b75f8ec8c4542bf471313549e627360cb3e283a6 Mon Sep 17 00:00:00 2001 From: Alan Orth Date: Sun, 13 Apr 2025 17:34:14 +0300 Subject: [PATCH 734/979] dspace-api: use static variable RESOURCE_TYPE_FIELD --- .../authorize/AuthorizeServiceImpl.java | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) 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 932cd71744d4..b894bfa4b761 100644 --- a/dspace-api/src/main/java/org/dspace/authorize/AuthorizeServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/authorize/AuthorizeServiceImpl.java @@ -9,6 +9,7 @@ import static org.dspace.app.util.AuthorizeUtil.canCollectionAdminManageAccounts; import static org.dspace.app.util.AuthorizeUtil.canCommunityAdminManageAccounts; +import static org.dspace.discovery.SearchUtils.RESOURCE_TYPE_FIELD; import java.sql.SQLException; import java.util.ArrayList; @@ -736,7 +737,7 @@ public List getPoliciesActionFilterExceptRpType(Context c, DSpac */ @Override public boolean isCommunityAdmin(Context context) throws SQLException { - return performCheck(context, "search.resourcetype:" + IndexableCommunity.TYPE); + return performCheck(context, RESOURCE_TYPE_FIELD + ":" + IndexableCommunity.TYPE); } /** @@ -749,7 +750,7 @@ public boolean isCommunityAdmin(Context context) throws SQLException { */ @Override public boolean isCollectionAdmin(Context context) throws SQLException { - return performCheck(context, "search.resourcetype:" + IndexableCollection.TYPE); + return performCheck(context, RESOURCE_TYPE_FIELD + ":" + IndexableCollection.TYPE); } /** @@ -762,7 +763,7 @@ public boolean isCollectionAdmin(Context context) throws SQLException { */ @Override public boolean isItemAdmin(Context context) throws SQLException { - return performCheck(context, "search.resourcetype:" + IndexableItem.TYPE); + return performCheck(context, RESOURCE_TYPE_FIELD + ":" + IndexableItem.TYPE); } /** @@ -776,8 +777,8 @@ public boolean isItemAdmin(Context context) throws SQLException { @Override public boolean isComColAdmin(Context context) throws SQLException { return performCheck(context, - "(search.resourcetype:" + IndexableCommunity.TYPE + " OR search.resourcetype:" + - IndexableCollection.TYPE + ")"); + "(" + RESOURCE_TYPE_FIELD + ":" + IndexableCommunity.TYPE + " OR " + + RESOURCE_TYPE_FIELD + ":" + IndexableCollection.TYPE + ")"); } /** @@ -795,7 +796,7 @@ public List findAdminAuthorizedCommunity(Context context, String quer throws SearchServiceException, SQLException { List communities = new ArrayList<>(); query = formatCustomQuery(query); - DiscoverResult discoverResult = getDiscoverResult(context, query + "search.resourcetype:" + + DiscoverResult discoverResult = getDiscoverResult(context, query + RESOURCE_TYPE_FIELD + ":" + IndexableCommunity.TYPE, offset, limit, null, null); for (IndexableObject solrCollections : discoverResult.getIndexableObjects()) { @@ -817,7 +818,7 @@ public List findAdminAuthorizedCommunity(Context context, String quer public long countAdminAuthorizedCommunity(Context context, String query) throws SearchServiceException, SQLException { query = formatCustomQuery(query); - DiscoverResult discoverResult = getDiscoverResult(context, query + "search.resourcetype:" + + DiscoverResult discoverResult = getDiscoverResult(context, query + RESOURCE_TYPE_FIELD + ":" + IndexableCommunity.TYPE, null, null, null, null); return discoverResult.getTotalSearchResults(); @@ -842,7 +843,7 @@ public List findAdminAuthorizedCollection(Context context, String qu } query = formatCustomQuery(query); - DiscoverResult discoverResult = getDiscoverResult(context, query + "search.resourcetype:" + + DiscoverResult discoverResult = getDiscoverResult(context, query + RESOURCE_TYPE_FIELD + ":" + IndexableCollection.TYPE, offset, limit, CollectionService.SOLR_SORT_FIELD, SORT_ORDER.asc); for (IndexableObject solrCollections : discoverResult.getIndexableObjects()) { @@ -864,7 +865,7 @@ public List findAdminAuthorizedCollection(Context context, String qu public long countAdminAuthorizedCollection(Context context, String query) throws SearchServiceException, SQLException { query = formatCustomQuery(query); - DiscoverResult discoverResult = getDiscoverResult(context, query + "search.resourcetype:" + + DiscoverResult discoverResult = getDiscoverResult(context, query + RESOURCE_TYPE_FIELD + ":" + IndexableCollection.TYPE, null, null, null, null); return discoverResult.getTotalSearchResults(); From 4d67aec3bc5e3b9772fe007be7aad85f0dae042e Mon Sep 17 00:00:00 2001 From: Alan Orth Date: Sun, 13 Apr 2025 17:35:55 +0300 Subject: [PATCH 735/979] dspace-api: do not request actual search hits in count-only query --- .../main/java/org/dspace/authorize/AuthorizeServiceImpl.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 b894bfa4b761..f2692cf394fc 100644 --- a/dspace-api/src/main/java/org/dspace/authorize/AuthorizeServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/authorize/AuthorizeServiceImpl.java @@ -820,7 +820,7 @@ public long countAdminAuthorizedCommunity(Context context, String query) query = formatCustomQuery(query); DiscoverResult discoverResult = getDiscoverResult(context, query + RESOURCE_TYPE_FIELD + ":" + IndexableCommunity.TYPE, - null, null, null, null); + null, 0, null, null); return discoverResult.getTotalSearchResults(); } @@ -867,7 +867,7 @@ public long countAdminAuthorizedCollection(Context context, String query) query = formatCustomQuery(query); DiscoverResult discoverResult = getDiscoverResult(context, query + RESOURCE_TYPE_FIELD + ":" + IndexableCollection.TYPE, - null, null, null, null); + null, 0, null, null); return discoverResult.getTotalSearchResults(); } From 3d133727e97e87cee7f545618d1e65b68c855531 Mon Sep 17 00:00:00 2001 From: Alan Orth Date: Sun, 13 Apr 2025 17:36:35 +0300 Subject: [PATCH 736/979] dspace-api: set search fields in Solr query only if we are interested in the actual search results --- .../org/dspace/discovery/SolrServiceImpl.java | 24 +++++++++++-------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/discovery/SolrServiceImpl.java b/dspace-api/src/main/java/org/dspace/discovery/SolrServiceImpl.java index 3415e6b141c8..a5fa04b3dc04 100644 --- a/dspace-api/src/main/java/org/dspace/discovery/SolrServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/discovery/SolrServiceImpl.java @@ -861,16 +861,20 @@ protected SolrQuery resolveToSolrQuery(Context context, DiscoverQuery discoveryQ solrQuery.setQuery(query); - // Add any search fields to our query. This is the limited list - // of fields that will be returned in the solr result - for (String fieldName : discoveryQuery.getSearchFields()) { - solrQuery.addField(fieldName); - } - // Also ensure a few key obj identifier fields are returned with every query - solrQuery.addField(SearchUtils.RESOURCE_TYPE_FIELD); - solrQuery.addField(SearchUtils.RESOURCE_ID_FIELD); - solrQuery.addField(SearchUtils.RESOURCE_UNIQUE_ID); - solrQuery.addField(STATUS_FIELD); + if (discoveryQuery.getMaxResults() != 0) { + // set search fields in Solr query only if we are interested in the actual search results + + // Add any search fields to our query. This is the limited list + // of fields that will be returned in the solr result + for (String fieldName : discoveryQuery.getSearchFields()) { + solrQuery.addField(fieldName); + } + // Also ensure a few key obj identifier fields are returned with every query + solrQuery.addField(SearchUtils.RESOURCE_TYPE_FIELD); + solrQuery.addField(SearchUtils.RESOURCE_ID_FIELD); + solrQuery.addField(SearchUtils.RESOURCE_UNIQUE_ID); + solrQuery.addField(STATUS_FIELD); + } if (discoveryQuery.isSpellCheck()) { solrQuery.setParam(SpellingParams.SPELLCHECK_Q, query); From d9cc564acec4a4d9add57ed52655745abaa4737c Mon Sep 17 00:00:00 2001 From: Alan Orth Date: Sun, 13 Apr 2025 17:34:14 +0300 Subject: [PATCH 737/979] dspace-api: use static variable RESOURCE_TYPE_FIELD --- .../authorize/AuthorizeServiceImpl.java | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) 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 a99d83764a28..6bd4ed798dd1 100644 --- a/dspace-api/src/main/java/org/dspace/authorize/AuthorizeServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/authorize/AuthorizeServiceImpl.java @@ -9,6 +9,7 @@ import static org.dspace.app.util.AuthorizeUtil.canCollectionAdminManageAccounts; import static org.dspace.app.util.AuthorizeUtil.canCommunityAdminManageAccounts; +import static org.dspace.discovery.SearchUtils.RESOURCE_TYPE_FIELD; import java.sql.SQLException; import java.util.ArrayList; @@ -736,7 +737,7 @@ public List getPoliciesActionFilterExceptRpType(Context c, DSpac */ @Override public boolean isCommunityAdmin(Context context) throws SQLException { - return performCheck(context, "search.resourcetype:" + IndexableCommunity.TYPE); + return performCheck(context, RESOURCE_TYPE_FIELD + ":" + IndexableCommunity.TYPE); } /** @@ -749,7 +750,7 @@ public boolean isCommunityAdmin(Context context) throws SQLException { */ @Override public boolean isCollectionAdmin(Context context) throws SQLException { - return performCheck(context, "search.resourcetype:" + IndexableCollection.TYPE); + return performCheck(context, RESOURCE_TYPE_FIELD + ":" + IndexableCollection.TYPE); } /** @@ -762,7 +763,7 @@ public boolean isCollectionAdmin(Context context) throws SQLException { */ @Override public boolean isItemAdmin(Context context) throws SQLException { - return performCheck(context, "search.resourcetype:" + IndexableItem.TYPE); + return performCheck(context, RESOURCE_TYPE_FIELD + ":" + IndexableItem.TYPE); } /** @@ -776,8 +777,8 @@ public boolean isItemAdmin(Context context) throws SQLException { @Override public boolean isComColAdmin(Context context) throws SQLException { return performCheck(context, - "(search.resourcetype:" + IndexableCommunity.TYPE + " OR search.resourcetype:" + - IndexableCollection.TYPE + ")"); + "(" + RESOURCE_TYPE_FIELD + ":" + IndexableCommunity.TYPE + " OR " + + RESOURCE_TYPE_FIELD + ":" + IndexableCollection.TYPE + ")"); } /** @@ -795,7 +796,7 @@ public List findAdminAuthorizedCommunity(Context context, String quer throws SearchServiceException, SQLException { List communities = new ArrayList<>(); query = formatCustomQuery(query); - DiscoverResult discoverResult = getDiscoverResult(context, query + "search.resourcetype:" + + DiscoverResult discoverResult = getDiscoverResult(context, query + RESOURCE_TYPE_FIELD + ":" + IndexableCommunity.TYPE, offset, limit, null, null); for (IndexableObject solrCollections : discoverResult.getIndexableObjects()) { @@ -817,7 +818,7 @@ public List findAdminAuthorizedCommunity(Context context, String quer public long countAdminAuthorizedCommunity(Context context, String query) throws SearchServiceException, SQLException { query = formatCustomQuery(query); - DiscoverResult discoverResult = getDiscoverResult(context, query + "search.resourcetype:" + + DiscoverResult discoverResult = getDiscoverResult(context, query + RESOURCE_TYPE_FIELD + ":" + IndexableCommunity.TYPE, null, null, null, null); return discoverResult.getTotalSearchResults(); @@ -842,7 +843,7 @@ public List findAdminAuthorizedCollection(Context context, String qu } query = formatCustomQuery(query); - DiscoverResult discoverResult = getDiscoverResult(context, query + "search.resourcetype:" + + DiscoverResult discoverResult = getDiscoverResult(context, query + RESOURCE_TYPE_FIELD + ":" + IndexableCollection.TYPE, offset, limit, CollectionService.SOLR_SORT_FIELD, SORT_ORDER.asc); for (IndexableObject solrCollections : discoverResult.getIndexableObjects()) { @@ -864,7 +865,7 @@ public List findAdminAuthorizedCollection(Context context, String qu public long countAdminAuthorizedCollection(Context context, String query) throws SearchServiceException, SQLException { query = formatCustomQuery(query); - DiscoverResult discoverResult = getDiscoverResult(context, query + "search.resourcetype:" + + DiscoverResult discoverResult = getDiscoverResult(context, query + RESOURCE_TYPE_FIELD + ":" + IndexableCollection.TYPE, null, null, null, null); return discoverResult.getTotalSearchResults(); From bd753005e68dd3c3da0533a4a8aae9cc553e1adb Mon Sep 17 00:00:00 2001 From: Alan Orth Date: Sun, 13 Apr 2025 17:35:55 +0300 Subject: [PATCH 738/979] dspace-api: do not request actual search hits in count-only query --- .../main/java/org/dspace/authorize/AuthorizeServiceImpl.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 6bd4ed798dd1..7198e28d03b9 100644 --- a/dspace-api/src/main/java/org/dspace/authorize/AuthorizeServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/authorize/AuthorizeServiceImpl.java @@ -820,7 +820,7 @@ public long countAdminAuthorizedCommunity(Context context, String query) query = formatCustomQuery(query); DiscoverResult discoverResult = getDiscoverResult(context, query + RESOURCE_TYPE_FIELD + ":" + IndexableCommunity.TYPE, - null, null, null, null); + null, 0, null, null); return discoverResult.getTotalSearchResults(); } @@ -867,7 +867,7 @@ public long countAdminAuthorizedCollection(Context context, String query) query = formatCustomQuery(query); DiscoverResult discoverResult = getDiscoverResult(context, query + RESOURCE_TYPE_FIELD + ":" + IndexableCollection.TYPE, - null, null, null, null); + null, 0, null, null); return discoverResult.getTotalSearchResults(); } From 9eef166b7e90c7206221ad76d6e116c7da41b716 Mon Sep 17 00:00:00 2001 From: Alan Orth Date: Sun, 13 Apr 2025 17:36:35 +0300 Subject: [PATCH 739/979] dspace-api: set search fields in Solr query only if we are interested in the actual search results --- .../org/dspace/discovery/SolrServiceImpl.java | 24 +++++++++++-------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/discovery/SolrServiceImpl.java b/dspace-api/src/main/java/org/dspace/discovery/SolrServiceImpl.java index 8cf1ec85888c..06edf4408c07 100644 --- a/dspace-api/src/main/java/org/dspace/discovery/SolrServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/discovery/SolrServiceImpl.java @@ -861,16 +861,20 @@ protected SolrQuery resolveToSolrQuery(Context context, DiscoverQuery discoveryQ solrQuery.setQuery(query); - // Add any search fields to our query. This is the limited list - // of fields that will be returned in the solr result - for (String fieldName : discoveryQuery.getSearchFields()) { - solrQuery.addField(fieldName); - } - // Also ensure a few key obj identifier fields are returned with every query - solrQuery.addField(SearchUtils.RESOURCE_TYPE_FIELD); - solrQuery.addField(SearchUtils.RESOURCE_ID_FIELD); - solrQuery.addField(SearchUtils.RESOURCE_UNIQUE_ID); - solrQuery.addField(STATUS_FIELD); + if (discoveryQuery.getMaxResults() != 0) { + // set search fields in Solr query only if we are interested in the actual search results + + // Add any search fields to our query. This is the limited list + // of fields that will be returned in the solr result + for (String fieldName : discoveryQuery.getSearchFields()) { + solrQuery.addField(fieldName); + } + // Also ensure a few key obj identifier fields are returned with every query + solrQuery.addField(SearchUtils.RESOURCE_TYPE_FIELD); + solrQuery.addField(SearchUtils.RESOURCE_ID_FIELD); + solrQuery.addField(SearchUtils.RESOURCE_UNIQUE_ID); + solrQuery.addField(STATUS_FIELD); + } if (discoveryQuery.isSpellCheck()) { solrQuery.setParam(SpellingParams.SPELLCHECK_Q, query); From 1b122c1ab2dcdd1a89a078af828061518dfca99b Mon Sep 17 00:00:00 2001 From: "Mark H. Wood" Date: Wed, 11 Jun 2025 15:43:10 -0400 Subject: [PATCH 740/979] Make POI record buffer size adjustable. --- .../mediafilter/TikaTextExtractionFilter.java | 18 ++++++++++++++---- dspace/config/dspace.cfg | 7 +++++++ 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/app/mediafilter/TikaTextExtractionFilter.java b/dspace-api/src/main/java/org/dspace/app/mediafilter/TikaTextExtractionFilter.java index b7a6063165b7..5728f4f42f48 100644 --- a/dspace-api/src/main/java/org/dspace/app/mediafilter/TikaTextExtractionFilter.java +++ b/dspace-api/src/main/java/org/dspace/app/mediafilter/TikaTextExtractionFilter.java @@ -18,6 +18,7 @@ import org.apache.commons.lang.StringUtils; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import org.apache.poi.util.IOUtils; import org.apache.tika.Tika; import org.apache.tika.exception.TikaException; import org.apache.tika.metadata.Metadata; @@ -37,6 +38,8 @@ public class TikaTextExtractionFilter extends MediaFilter { private final static Logger log = LogManager.getLogger(); + private static final int DEFAULT_MAX_CHARS = 100_000; + private static final int DEFAULT_MAX_ARRAY = 100_000_000; @Override public String getFilteredName(String oldFilename) { @@ -70,9 +73,12 @@ public InputStream getDestinationStream(Item currentItem, InputStream source, bo } // Not using temporary file. We'll use Tika's default in-memory parsing. - // Get maximum characters to extract. Default is 100,000 chars, which is also Tika's default setting. String extractedText; - int maxChars = configurationService.getIntProperty("textextractor.max-chars", 100000); + // Get maximum characters to extract. Default is 100,000 chars, which is also Tika's default setting. + int maxChars = configurationService.getIntProperty("textextractor.max-chars", DEFAULT_MAX_CHARS); + // Get maximum size of structure that Tika will try to buffer. + int maxArray = configurationService.getIntProperty("textextractor.max-array", DEFAULT_MAX_ARRAY); + IOUtils.setByteArrayMaxOverride(maxArray); try { // Use Tika to extract text from input. Tika will automatically detect the file type. Tika tika = new Tika(); @@ -80,13 +86,13 @@ public InputStream getDestinationStream(Item currentItem, InputStream source, bo extractedText = tika.parseToString(source); } catch (IOException e) { System.err.format("Unable to extract text from bitstream in Item %s%n", currentItem.getID().toString()); - e.printStackTrace(); + e.printStackTrace(System.err); log.error("Unable to extract text from bitstream in Item {}", currentItem.getID().toString(), e); throw e; } catch (OutOfMemoryError oe) { System.err.format("OutOfMemoryError occurred when extracting text from bitstream in Item %s. " + "You may wish to enable 'textextractor.use-temp-file'.%n", currentItem.getID().toString()); - oe.printStackTrace(); + oe.printStackTrace(System.err); log.error("OutOfMemoryError occurred when extracting text from bitstream in Item {}. " + "You may wish to enable 'textextractor.use-temp-file'.", currentItem.getID().toString(), oe); throw oe; @@ -167,6 +173,10 @@ public void ignorableWhitespace(char[] ch, int start, int length) throws SAXExce } }); + ConfigurationService configurationService = DSpaceServicesFactory.getInstance().getConfigurationService(); + int maxArray = configurationService.getIntProperty("textextractor.max-array", DEFAULT_MAX_ARRAY); + IOUtils.setByteArrayMaxOverride(maxArray); + AutoDetectParser parser = new AutoDetectParser(); Metadata metadata = new Metadata(); // parse our source InputStream using the above custom handler diff --git a/dspace/config/dspace.cfg b/dspace/config/dspace.cfg index 7151fc6dc56b..67490dac3e6d 100644 --- a/dspace/config/dspace.cfg +++ b/dspace/config/dspace.cfg @@ -523,6 +523,13 @@ filter.org.dspace.app.mediafilter.PDFBoxThumbnail.inputFormats = Adobe PDF # text ("filter-media -f" ) and then reindex your site ("index-discovery -b"). #textextractor.use-temp-file = false +# Maximum size of a record buffer for text extraction. Set this if you are +# seeing RecordFormatException calling out excessive array length from +# 'dspace filter-media'. It is likely that you will need to increase the +# size of the Java heap if you greatly increase this value -- see JAVA_OPTS +# in 'bin/dspace' or 'bin/dspace/bat'. +#textextractor.max-array = 1000000 + # Custom settigns for ImageMagick Thumbnail Filters # ImageMagick and GhostScript must be installed on the server, set the path to ImageMagick and GhostScript executable # http://www.imagemagick.org/ From 88d8a54939499942920531a99556685132271651 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 16 Jun 2025 03:15:41 +0000 Subject: [PATCH 741/979] Bump the fasterxml group with 4 updates Bumps the fasterxml group with 4 updates: [com.fasterxml.jackson.core:jackson-annotations](https://github.com/FasterXML/jackson), [com.fasterxml.jackson.core:jackson-core](https://github.com/FasterXML/jackson-core), [com.fasterxml.jackson.core:jackson-databind](https://github.com/FasterXML/jackson) and com.fasterxml.jackson.datatype:jackson-datatype-jsr310. Updates `com.fasterxml.jackson.core:jackson-annotations` from 2.19.0 to 2.19.1 - [Commits](https://github.com/FasterXML/jackson/commits) Updates `com.fasterxml.jackson.core:jackson-core` from 2.19.0 to 2.19.1 - [Commits](https://github.com/FasterXML/jackson-core/compare/jackson-core-2.19.0...jackson-core-2.19.1) Updates `com.fasterxml.jackson.core:jackson-core` from 2.19.0 to 2.19.1 - [Commits](https://github.com/FasterXML/jackson-core/compare/jackson-core-2.19.0...jackson-core-2.19.1) Updates `com.fasterxml.jackson.core:jackson-databind` from 2.19.0 to 2.19.1 - [Commits](https://github.com/FasterXML/jackson/commits) Updates `com.fasterxml.jackson.datatype:jackson-datatype-jsr310` from 2.19.0 to 2.19.1 Updates `com.fasterxml.jackson.datatype:jackson-datatype-jsr310` from 2.19.0 to 2.19.1 --- updated-dependencies: - dependency-name: com.fasterxml.jackson.core:jackson-annotations dependency-version: 2.19.1 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: fasterxml - dependency-name: com.fasterxml.jackson.core:jackson-core dependency-version: 2.19.1 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: fasterxml - dependency-name: com.fasterxml.jackson.core:jackson-core dependency-version: 2.19.1 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: fasterxml - dependency-name: com.fasterxml.jackson.core:jackson-databind dependency-version: 2.19.1 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: fasterxml - dependency-name: com.fasterxml.jackson.datatype:jackson-datatype-jsr310 dependency-version: 2.19.1 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: fasterxml - dependency-name: com.fasterxml.jackson.datatype:jackson-datatype-jsr310 dependency-version: 2.19.1 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: fasterxml ... Signed-off-by: dependabot[bot] --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 118595efacc6..9d1d709e5501 100644 --- a/pom.xml +++ b/pom.xml @@ -31,8 +31,8 @@ 3.10.8 2.38.0 - 2.19.0 - 2.19.0 + 2.19.1 + 2.19.1 2.1.1 4.0.2 4.0.5 From 6df9e2dbfdafd50505b1ac33d1362aed070fa215 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 16 Jun 2025 03:17:49 +0000 Subject: [PATCH 742/979] Bump org.postgresql:postgresql from 42.7.6 to 42.7.7 Bumps [org.postgresql:postgresql](https://github.com/pgjdbc/pgjdbc) from 42.7.6 to 42.7.7. - [Release notes](https://github.com/pgjdbc/pgjdbc/releases) - [Changelog](https://github.com/pgjdbc/pgjdbc/blob/master/CHANGELOG.md) - [Commits](https://github.com/pgjdbc/pgjdbc/compare/REL42.7.6...REL42.7.7) --- updated-dependencies: - dependency-name: org.postgresql:postgresql dependency-version: 42.7.7 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 118595efacc6..00068da716a2 100644 --- a/pom.xml +++ b/pom.xml @@ -24,7 +24,7 @@ 6.5.0 6.4.8.Final 8.0.2.Final - 42.7.6 + 42.7.7 10.22.0 8.11.4 From 34134b3c3b99779d07c8bda9181b9eeaacd2f6a3 Mon Sep 17 00:00:00 2001 From: Sascha Szott Date: Wed, 9 Apr 2025 13:39:05 +0200 Subject: [PATCH 743/979] remove inclusion of sword-client.cfg --- dspace/config/dspace.cfg | 1 - 1 file changed, 1 deletion(-) diff --git a/dspace/config/dspace.cfg b/dspace/config/dspace.cfg index 4621ca51dc13..3047c2ce4db7 100644 --- a/dspace/config/dspace.cfg +++ b/dspace/config/dspace.cfg @@ -1658,7 +1658,6 @@ include = ${module_dir}/solrauthority.cfg include = ${module_dir}/researcher-profile.cfg include = ${module_dir}/spring.cfg include = ${module_dir}/submission-curation.cfg -include = ${module_dir}/sword-client.cfg include = ${module_dir}/sword-server.cfg include = ${module_dir}/swordv2-server.cfg include = ${module_dir}/translator.cfg From 07e840b675ee933868ac53e41e1034b749f0e627 Mon Sep 17 00:00:00 2001 From: Sascha Szott Date: Wed, 9 Apr 2025 13:40:02 +0200 Subject: [PATCH 744/979] removal of configuration file sword-client.cfg --- dspace/config/modules/sword-client.cfg | 23 ----------------------- 1 file changed, 23 deletions(-) delete mode 100644 dspace/config/modules/sword-client.cfg diff --git a/dspace/config/modules/sword-client.cfg b/dspace/config/modules/sword-client.cfg deleted file mode 100644 index 9e8a029ae684..000000000000 --- a/dspace/config/modules/sword-client.cfg +++ /dev/null @@ -1,23 +0,0 @@ -#---------------------------------------------------------------# -#--------------SWORD V.1 CLIENT CONFIGURATIONS------------------# -#---------------------------------------------------------------# -# Configuration properties used solely by the UI-based SWORD # -# Client interface (used to submit DSpace content to another # -# SWORD server). # -#---------------------------------------------------------------# -# TODO: UNSUPPORTED in DSpace 7.0 -# List of remote Sword servers. Used to build the drop-down list of selectable Sword targets. -sword-client.targets = http://localhost:8080/sword/servicedocument, \ - http://client.swordapp.org/client/servicedocument, \ - http://dspace.swordapp.org/sword/servicedocument, \ - http://sword.eprints.org/sword-app/servicedocument, \ - http://sword.intralibrary.com/IntraLibrary-Deposit/service, \ - http://fedora.swordapp.org/sword-fedora/servicedocument - -# List of file types from which the user can select. If a type is not supported by the remote server -# it will not appear in the drop-down list. -sword-client.file-types = application/zip - -# List of package formats from which the user can select. If a format is not supported by the remote server -# it will not appear in the drop-down list. -sword-client.package-formats = http://purl.org/net/sword-types/METSDSpaceSIP From e9963b23532177d2abaece462bcaac3f62da0141 Mon Sep 17 00:00:00 2001 From: Mykhaylo Boychuk Date: Fri, 24 Jan 2025 01:21:25 +0100 Subject: [PATCH 745/979] [DURACOM-318] add new ITs for ResourcePolicy (cherry picked from commit d78d4f00d94ef0d4b147031b075b8df1e8896fe4) (cherry picked from commit 98c2b9942167890d24b71f5c32e6918ac2eee7a6) --- .../rest/ResourcePolicyRestRepositoryIT.java | 547 ++++++++++++++++++ 1 file changed, 547 insertions(+) diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/ResourcePolicyRestRepositoryIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/ResourcePolicyRestRepositoryIT.java index bc6f142abebb..5570332b16b2 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/ResourcePolicyRestRepositoryIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/ResourcePolicyRestRepositoryIT.java @@ -23,6 +23,9 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import java.text.SimpleDateFormat; +import java.io.InputStream; +import java.time.LocalDate; +import java.time.format.DateTimeFormatter; import java.util.ArrayList; import java.util.Calendar; import java.util.Date; @@ -32,6 +35,8 @@ import com.fasterxml.jackson.databind.ObjectMapper; import jakarta.ws.rs.core.MediaType; +import org.apache.commons.codec.CharEncoding; +import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.StringUtils; import org.dspace.app.rest.matcher.ResourcePolicyMatcher; import org.dspace.app.rest.model.ResourcePolicyRest; @@ -43,12 +48,14 @@ import org.dspace.authorize.ResourcePolicy; import org.dspace.authorize.service.AuthorizeService; import org.dspace.authorize.service.ResourcePolicyService; +import org.dspace.builder.BitstreamBuilder; import org.dspace.builder.CollectionBuilder; import org.dspace.builder.CommunityBuilder; import org.dspace.builder.EPersonBuilder; import org.dspace.builder.GroupBuilder; import org.dspace.builder.ItemBuilder; import org.dspace.builder.ResourcePolicyBuilder; +import org.dspace.content.Bitstream; import org.dspace.content.Collection; import org.dspace.content.Community; import org.dspace.content.Item; @@ -1215,6 +1222,376 @@ public void createOneForbiddenTest() throws Exception { .andExpect(jsonPath("$.page.totalElements", is(0))); } + @Test + public void createPolicyByCollectionAdminTest() throws Exception { + context.turnOffAuthorisationSystem(); + EPerson colAdmin = EPersonBuilder.createEPerson(context) + .withEmail("colAdmin@mail.test") + .withPassword(password) + .build(); + + EPerson colAdmin2 = EPersonBuilder.createEPerson(context) + .withEmail("colAdmin2@mail.test") + .withPassword(password) + .build(); + + EPerson submitter = EPersonBuilder.createEPerson(context) + .withEmail("colSubmitter@mail.test") + .withPassword(password) + .build(); + + Community community = CommunityBuilder.createCommunity(context) + .withName("My top commynity") + .build(); + + Collection collection = CollectionBuilder.createCollection(context, community) + .withName("My collection") + .withAdminGroup(colAdmin) + .withSubmitterGroup(submitter) + .withEntityType("Publication") + .build(); + + CollectionBuilder.createCollection(context, community) + .withName("My Second Collection") + .withAdminGroup(colAdmin2) + .withSubmitterGroup(submitter) + .withEntityType("Publication") + .build(); + + Item publication = ItemBuilder.createItem(context, collection) + .withTitle("Public item") + .build(); + + //Add a bitstream to a publication + Bitstream bitstream = null; + try (InputStream is = IOUtils.toInputStream("ThisIsSomeDummyText", CharEncoding.UTF_8)) { + bitstream = BitstreamBuilder.createBitstream(context, publication, is) + .withName("Bitstream") + .withDescription("description") + .withMimeType("text/plain") + .build(); + } + context.restoreAuthSystemState(); + + ResourcePolicyRest resourcePolicyRest = new ResourcePolicyRest(); + resourcePolicyRest.setPolicyType(ResourcePolicy.TYPE_CUSTOM); + resourcePolicyRest.setAction(Constants.actionText[Constants.WRITE]); + resourcePolicyRest.setName("Test for collection admin"); + + String authcolAdminToken = getAuthToken(colAdmin.getEmail(), password); + String authcolAdmin2Token = getAuthToken(colAdmin2.getEmail(), password); + String authSubmitterToken = getAuthToken(submitter.getEmail(), password); + AtomicReference idRef = new AtomicReference(); + + try { + // submitter can't create policy + getClient(authSubmitterToken).perform(post("/api/authz/resourcepolicies") + .content(new ObjectMapper().writeValueAsBytes(resourcePolicyRest)) + .param("resource", bitstream.getID().toString()) + .param("eperson", submitter.getID().toString()) + .param("projections", "full") + .contentType(contentType)) + .andExpect(status().isForbidden()); + + // other collection admin can't create policy for other collection + getClient(authcolAdmin2Token).perform(post("/api/authz/resourcepolicies") + .content(new ObjectMapper().writeValueAsBytes(resourcePolicyRest)) + .param("resource", bitstream.getID().toString()) + .param("eperson", submitter.getID().toString()) + .param("projections", "full") + .contentType(contentType)) + .andExpect(status().isForbidden()); + + // create policy for submitter by collection admin + getClient(authcolAdminToken).perform(post("/api/authz/resourcepolicies") + .content(new ObjectMapper().writeValueAsBytes(resourcePolicyRest)) + .param("resource", bitstream.getID().toString()) + .param("eperson", submitter.getID().toString()) + .param("projections", "full") + .contentType(contentType)) + .andExpect(status().isCreated()) + .andExpect(content().contentType(contentType)) + .andExpect(jsonPath("$", ResourcePolicyMatcher.matchFullEmbeds())) + .andExpect(jsonPath("$", Matchers.allOf( + hasJsonPath("$.name", is(resourcePolicyRest.getName())), + hasJsonPath("$.description", is(resourcePolicyRest.getDescription())), + hasJsonPath("$.policyType", is(resourcePolicyRest.getPolicyType())), + hasJsonPath("$.action", is(resourcePolicyRest.getAction())), + hasJsonPath("$.startDate", is(resourcePolicyRest.getStartDate())), + hasJsonPath("$.endDate", is(resourcePolicyRest.getEndDate())), + hasJsonPath("$.type", is(resourcePolicyRest.getType()))))) + .andDo(result -> idRef.set(read(result.getResponse().getContentAsString(), "$.id"))); + + // submitter can see own policy + getClient(authSubmitterToken).perform(get("/api/authz/resourcepolicies/" + idRef.get())) + .andExpect(status().isOk()) + .andExpect(content().contentType(contentType)) + .andExpect(jsonPath("$._links.self.href", + Matchers.containsString("/api/authz/resourcepolicies/" + idRef.get()))); + + // collection admin can see that policy + getClient(authcolAdminToken).perform(get("/api/authz/resourcepolicies/" + idRef.get())) + .andExpect(status().isOk()) + .andExpect(content().contentType(contentType)) + .andExpect(jsonPath("$._links.self.href", + Matchers.containsString("/api/authz/resourcepolicies/" + idRef.get()))); + } finally { + ResourcePolicyBuilder.delete(idRef.get()); + } + } + + @Test + public void createPolicyBySubCommunityAdminTest() throws Exception { + context.turnOffAuthorisationSystem(); + EPerson comAdmin = EPersonBuilder.createEPerson(context) + .withEmail("comAdmin@mail.test") + .withPassword(password) + .build(); + + EPerson comAdmin2 = EPersonBuilder.createEPerson(context) + .withEmail("comAdmin2@mail.test") + .withPassword(password) + .build(); + + EPerson submitter = EPersonBuilder.createEPerson(context) + .withEmail("colSubmitter@mail.test") + .withPassword(password) + .build(); + + Community community = CommunityBuilder.createSubCommunity(context, parentCommunity) + .withName("My First Commynity") + .withAdminGroup(comAdmin) + .build(); + + Community community2 = CommunityBuilder.createSubCommunity(context, parentCommunity) + .withName("My Second Commynity") + .withAdminGroup(comAdmin2) + .build(); + + Collection collection = CollectionBuilder.createCollection(context, community) + .withName("My collection") + .withSubmitterGroup(submitter) + .withEntityType("Publication") + .build(); + + CollectionBuilder.createCollection(context, community2) + .withName("My Second Collection") + .withSubmitterGroup(submitter) + .withEntityType("Publication") + .build(); + + Item publication = ItemBuilder.createItem(context, collection) + .withTitle("Public item") + .build(); + + context.restoreAuthSystemState(); + + ResourcePolicyRest resourcePolicyRest = new ResourcePolicyRest(); + resourcePolicyRest.setPolicyType(ResourcePolicy.TYPE_CUSTOM); + resourcePolicyRest.setAction(Constants.actionText[Constants.WRITE]); + resourcePolicyRest.setName("Test for collection admin"); + + String authcomAdminToken = getAuthToken(comAdmin.getEmail(), password); + String authcomAdmin2Token = getAuthToken(comAdmin2.getEmail(), password); + String authSubmitterToken = getAuthToken(submitter.getEmail(), password); + AtomicReference idRef = new AtomicReference(); + + try { + // submitter can't create policy + getClient(authSubmitterToken).perform(post("/api/authz/resourcepolicies") + .content(new ObjectMapper().writeValueAsBytes(resourcePolicyRest)) + .param("resource", publication.getID().toString()) + .param("eperson", submitter.getID().toString()) + .param("projections", "full") + .contentType(contentType)) + .andExpect(status().isForbidden()); + + // other Community admin can't create policy for collections into other Community + getClient(authcomAdmin2Token).perform(post("/api/authz/resourcepolicies") + .content(new ObjectMapper().writeValueAsBytes(resourcePolicyRest)) + .param("resource", publication.getID().toString()) + .param("eperson", submitter.getID().toString()) + .param("projections", "full") + .contentType(contentType)) + .andExpect(status().isForbidden()); + + // create policy for submitter by Community admin + getClient(authcomAdminToken).perform(post("/api/authz/resourcepolicies") + .content(new ObjectMapper().writeValueAsBytes(resourcePolicyRest)) + .param("resource", publication.getID().toString()) + .param("eperson", submitter.getID().toString()) + .param("projections", "full") + .contentType(contentType)) + .andExpect(status().isCreated()) + .andExpect(content().contentType(contentType)) + .andExpect(jsonPath("$", ResourcePolicyMatcher.matchFullEmbeds())) + .andExpect(jsonPath("$", Matchers.allOf( + hasJsonPath("$.name", is(resourcePolicyRest.getName())), + hasJsonPath("$.description", is(resourcePolicyRest.getDescription())), + hasJsonPath("$.policyType", is(resourcePolicyRest.getPolicyType())), + hasJsonPath("$.action", is(resourcePolicyRest.getAction())), + hasJsonPath("$.startDate", is(resourcePolicyRest.getStartDate())), + hasJsonPath("$.endDate", is(resourcePolicyRest.getEndDate())), + hasJsonPath("$.type", is(resourcePolicyRest.getType()))))) + .andDo(result -> idRef.set(read(result.getResponse().getContentAsString(), "$.id"))); + + // submitter can see own policy + getClient(authSubmitterToken).perform(get("/api/authz/resourcepolicies/" + idRef.get())) + .andExpect(status().isOk()) + .andExpect(content().contentType(contentType)) + .andExpect(jsonPath("$._links.self.href", + Matchers.containsString("/api/authz/resourcepolicies/" + idRef.get()))); + + // community admin can see policies of own collections/items + getClient(authcomAdminToken).perform(get("/api/authz/resourcepolicies/" + idRef.get())) + .andExpect(status().isOk()) + .andExpect(content().contentType(contentType)) + .andExpect(jsonPath("$._links.self.href", + Matchers.containsString("/api/authz/resourcepolicies/" + idRef.get()))); + + // Other community admin can't see policies of other community's collections/items + getClient(authcomAdmin2Token).perform(get("/api/authz/resourcepolicies/" + idRef.get())) + .andExpect(status().isForbidden()); + } finally { + ResourcePolicyBuilder.delete(idRef.get()); + } + } + + @Test + public void createPolicyByCommunityAdminTest() throws Exception { + context.turnOffAuthorisationSystem(); + EPerson rootComAdmin = EPersonBuilder.createEPerson(context) + .withEmail("rootComAdmin@mail.test") + .withPassword(password) + .build(); + + EPerson submitter = EPersonBuilder.createEPerson(context) + .withEmail("colSubmitter@mail.test") + .withPassword(password) + .build(); + + Community rootCommunity = CommunityBuilder.createCommunity(context) + .withName("Root Community") + .withAdminGroup(rootComAdmin) + .build(); + + Community community = CommunityBuilder.createSubCommunity(context, rootCommunity) + .withName("My First Commynity") + .build(); + + Community community2 = CommunityBuilder.createSubCommunity(context, rootCommunity) + .withName("My Second Commynity") + .build(); + + Collection collection = CollectionBuilder.createCollection(context, community) + .withName("My collection") + .withSubmitterGroup(submitter) + .withEntityType("Publication") + .build(); + + CollectionBuilder.createCollection(context, community2) + .withName("My Second Collection") + .withSubmitterGroup(submitter) + .withEntityType("Publication") + .build(); + + Item publication = ItemBuilder.createItem(context, collection) + .withTitle("Public item") + .build(); + + Collection collection2 = CollectionBuilder.createCollection(context, community) + .withName("My Second Collection") + .withSubmitterGroup(submitter) + .withEntityType("Publication") + .build(); + + Item publication2 = ItemBuilder.createItem(context, collection2) + .withTitle("Item of second collection") + .build(); + + //Add a bitstream to a publication + Bitstream bitstream = null; + try (InputStream is = IOUtils.toInputStream("ThisIsSomeDummyText", CharEncoding.UTF_8)) { + bitstream = BitstreamBuilder.createBitstream(context, publication2, is) + .withName("Bitstream") + .withDescription("description") + .withMimeType("text/plain") + .build(); + } + + context.restoreAuthSystemState(); + + ResourcePolicyRest resourcePolicyRest = new ResourcePolicyRest(); + resourcePolicyRest.setPolicyType(ResourcePolicy.TYPE_CUSTOM); + resourcePolicyRest.setAction(Constants.actionText[Constants.WRITE]); + resourcePolicyRest.setName("Test for collection admin"); + + ResourcePolicyRest resourcePolicyRest2 = new ResourcePolicyRest(); + resourcePolicyRest2.setPolicyType(ResourcePolicy.TYPE_CUSTOM); + resourcePolicyRest2.setAction(Constants.actionText[Constants.WRITE]); + resourcePolicyRest2.setName("Test for root community admin"); + + String authSubmitterToken = getAuthToken(submitter.getEmail(), password); + String authRootAdminToken = getAuthToken(rootComAdmin.getEmail(), password); + + AtomicReference idRef = new AtomicReference(); + AtomicReference idRef2 = new AtomicReference(); + try { + // create policy for submitter by root Community admin + getClient(authRootAdminToken).perform(post("/api/authz/resourcepolicies") + .content(new ObjectMapper().writeValueAsBytes(resourcePolicyRest)) + .param("resource", publication.getID().toString()) + .param("eperson", submitter.getID().toString()) + .contentType(contentType)) + .andExpect(status().isCreated()) + .andExpect(content().contentType(contentType)) + .andExpect(jsonPath("$", ResourcePolicyMatcher.matchFullEmbeds())) + .andExpect(jsonPath("$", Matchers.allOf( + hasJsonPath("$.name", is(resourcePolicyRest.getName())), + hasJsonPath("$.description", is(resourcePolicyRest.getDescription())), + hasJsonPath("$.policyType", is(resourcePolicyRest.getPolicyType())), + hasJsonPath("$.action", is(resourcePolicyRest.getAction())), + hasJsonPath("$.startDate", is(resourcePolicyRest.getStartDate())), + hasJsonPath("$.endDate", is(resourcePolicyRest.getEndDate())), + hasJsonPath("$.type", is(resourcePolicyRest.getType()))))) + .andDo(result -> idRef.set(read(result.getResponse().getContentAsString(), "$.id"))); + + // create policy for submitter by root Community admin + getClient(authRootAdminToken).perform(post("/api/authz/resourcepolicies") + .content(new ObjectMapper().writeValueAsBytes(resourcePolicyRest)) + .param("resource", bitstream.getID().toString()) + .param("eperson", submitter.getID().toString()) + .contentType(contentType)) + .andExpect(status().isCreated()) + .andExpect(content().contentType(contentType)) + .andExpect(jsonPath("$", ResourcePolicyMatcher.matchFullEmbeds())) + .andExpect(jsonPath("$", Matchers.allOf( + hasJsonPath("$.name", is(resourcePolicyRest.getName())), + hasJsonPath("$.description", is(resourcePolicyRest.getDescription())), + hasJsonPath("$.policyType", is(resourcePolicyRest.getPolicyType())), + hasJsonPath("$.action", is(resourcePolicyRest.getAction())), + hasJsonPath("$.startDate", is(resourcePolicyRest.getStartDate())), + hasJsonPath("$.endDate", is(resourcePolicyRest.getEndDate())), + hasJsonPath("$.type", is(resourcePolicyRest.getType()))))) + .andDo(result -> idRef2.set(read(result.getResponse().getContentAsString(), "$.id"))); + + getClient(authSubmitterToken).perform(get("/api/authz/resourcepolicies/" + idRef.get())) + .andExpect(status().isOk()) + .andExpect(content().contentType(contentType)) + .andExpect(jsonPath("$._links.self.href", + Matchers.containsString("/api/authz/resourcepolicies/" + idRef.get()))); + + getClient(authSubmitterToken).perform(get("/api/authz/resourcepolicies/" + idRef2.get())) + .andExpect(status().isOk()) + .andExpect(content().contentType(contentType)) + .andExpect(jsonPath("$._links.self.href", + Matchers.containsString("/api/authz/resourcepolicies/" + idRef2.get()))); + } finally { + ResourcePolicyBuilder.delete(idRef.get()); + ResourcePolicyBuilder.delete(idRef2.get()); + } + } + @Test public void deleteOne() throws Exception { context.turnOffAuthorisationSystem(); @@ -1308,6 +1685,176 @@ public void deleteOneNotFoundTest() throws Exception { .andExpect(status().isNotFound()); } + @Test + public void deletePolicyByCollectionAdminTest() throws Exception { + context.turnOffAuthorisationSystem(); + EPerson colAdmin = EPersonBuilder.createEPerson(context) + .withEmail("colAdmin@mail.test") + .withPassword(password) + .build(); + + EPerson colAdmin2 = EPersonBuilder.createEPerson(context) + .withEmail("colAdmin2@mail.test") + .withPassword(password) + .build(); + + EPerson submitter = EPersonBuilder.createEPerson(context) + .withEmail("colSubmitter@mail.test") + .withPassword(password) + .build(); + + Community community = CommunityBuilder.createCommunity(context) + .withName("My top commynity") + .build(); + + Collection collection = CollectionBuilder.createCollection(context, community) + .withName("My collection") + .withAdminGroup(colAdmin) + .withSubmitterGroup(submitter) + .withEntityType("Publication") + .build(); + + CollectionBuilder.createCollection(context, community) + .withName("My Second Collection") + .withAdminGroup(colAdmin2) + .withSubmitterGroup(submitter) + .withEntityType("Publication") + .build(); + + Item publication = ItemBuilder.createItem(context, collection) + .withTitle("Public item") + .build(); + + //Add a bitstream to a publication + Bitstream bitstream = null; + try (InputStream is = IOUtils.toInputStream("ThisIsSomeDummyText", CharEncoding.UTF_8)) { + bitstream = BitstreamBuilder.createBitstream(context, publication, is) + .withName("Bitstream") + .withDescription("description") + .withMimeType("text/plain") + .build(); + } + + context.restoreAuthSystemState(); + + String adminToken = getAuthToken(admin.getEmail(), password); + String authcolAdminToken = getAuthToken(colAdmin.getEmail(), password); + String authcolAdmin2Token = getAuthToken(colAdmin2.getEmail(), password); + String authSubmitterToken = getAuthToken(submitter.getEmail(), password); + + ResourcePolicy rp = ResourcePolicyBuilder.createResourcePolicy(context) + .withDspaceObject(bitstream) + .withAction(Constants.READ) + .withPolicyType(ResourcePolicy.TYPE_CUSTOM) + .withUser(submitter) + .build(); + + // submitter can't delete own policy + getClient(authSubmitterToken).perform(delete("/api/authz/resourcepolicies/" + rp.getID())) + .andExpect(status().isForbidden()); + + // check that policy wasn't deleted + getClient(adminToken).perform(get("/api/authz/resourcepolicies/" + rp.getID())) + .andExpect(status().isOk()) + .andExpect(content().contentType(contentType)) + .andExpect(jsonPath("$._links.self.href", + Matchers.containsString("/api/authz/resourcepolicies/" + rp.getID()))); + + // other collection admin can't delete policy that belong to items of other collections + getClient(authcolAdmin2Token).perform(delete("/api/authz/resourcepolicies/" + rp.getID())) + .andExpect(status().isForbidden()); + + // check that policy wasn't deleted + getClient(adminToken).perform(get("/api/authz/resourcepolicies/" + rp.getID())) + .andExpect(status().isOk()) + .andExpect(content().contentType(contentType)) + .andExpect(jsonPath("$._links.self.href", + Matchers.containsString("/api/authz/resourcepolicies/" + rp.getID()))); + + // delete policy for submitter by collection admin + getClient(authcolAdminToken).perform(delete("/api/authz/resourcepolicies/" + rp.getID())) + .andExpect(status().isNoContent()); + + getClient(adminToken).perform(get("/api/authz/resourcepolicies/" + rp.getID())) + .andExpect(status().isNotFound()); + } + + @Test + public void deletePolicyBySubCommunityAdminTest() throws Exception { + context.turnOffAuthorisationSystem(); + EPerson comAdmin = EPersonBuilder.createEPerson(context) + .withEmail("comAdmin@mail.test") + .withPassword(password) + .build(); + + EPerson comAdmin2 = EPersonBuilder.createEPerson(context) + .withEmail("comAdmin2@mail.test") + .withPassword(password) + .build(); + + EPerson submitter = EPersonBuilder.createEPerson(context) + .withEmail("colSubmitter@mail.test") + .withPassword(password) + .build(); + + Community community = CommunityBuilder.createSubCommunity(context, parentCommunity) + .withName("My First Commynity") + .withAdminGroup(comAdmin) + .build(); + + Community community2 = CommunityBuilder.createSubCommunity(context, parentCommunity) + .withName("My Second Commynity") + .withAdminGroup(comAdmin2) + .build(); + + Collection collection = CollectionBuilder.createCollection(context, community) + .withName("My collection") + .withSubmitterGroup(submitter) + .withEntityType("Publication") + .build(); + + CollectionBuilder.createCollection(context, community2) + .withName("My Second Collection") + .withSubmitterGroup(submitter) + .withEntityType("Publication") + .build(); + + Item publication = ItemBuilder.createItem(context, collection) + .withTitle("Public item") + .build(); + + context.restoreAuthSystemState(); + + ResourcePolicy rp = ResourcePolicyBuilder.createResourcePolicy(context) + .withDspaceObject(publication) + .withAction(Constants.WRITE) + .withPolicyType(ResourcePolicy.TYPE_CUSTOM) + .withUser(submitter) + .build(); + + String adminToken = getAuthToken(admin.getEmail(), password); + String authcomAdminToken = getAuthToken(comAdmin.getEmail(), password); + String authcomAdmin2Token = getAuthToken(comAdmin2.getEmail(), password); + + // other Community admin can't delete policy of other Community + getClient(authcomAdmin2Token).perform(delete("/api/authz/resourcepolicies/" + rp.getID())) + .andExpect(status().isForbidden()); + + getClient(adminToken).perform(get("/api/authz/resourcepolicies/" + rp.getID())) + .andExpect(status().isOk()) + .andExpect(content().contentType(contentType)) + .andExpect(jsonPath("$._links.self.href", + Matchers.containsString("/api/authz/resourcepolicies/" + rp.getID()))); + + // Community admin can delete policy + getClient(authcomAdminToken).perform(delete("/api/authz/resourcepolicies/" + rp.getID())) + .andExpect(status().isNoContent()); + + // submitter can see own policy + getClient(adminToken).perform(get("/api/authz/resourcepolicies/" + rp.getID())) + .andExpect(status().isNotFound()); + } + @Test public void patchReplaceStartDateTest() throws Exception { context.turnOffAuthorisationSystem(); From d3ff31e50bf329f1a03202f6790f3fc40c730c27 Mon Sep 17 00:00:00 2001 From: Mykhaylo Boychuk Date: Fri, 24 Jan 2025 02:03:23 +0100 Subject: [PATCH 746/979] [DURACOM-318] update security annotations on ResourcePolicyRepository (cherry picked from commit fabcc692db68e5232986ff062e849e4ec5c68c8b) (cherry picked from commit 95836c271cb4af41d3c29f2dda118eab4674f653) --- .../ResourcePolicyRestRepository.java | 22 ++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/ResourcePolicyRestRepository.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/ResourcePolicyRestRepository.java index 8b598aeeae32..1bfc50481078 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/ResourcePolicyRestRepository.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/ResourcePolicyRestRepository.java @@ -25,6 +25,7 @@ import org.dspace.app.rest.model.ResourcePolicyRest; import org.dspace.app.rest.model.patch.Patch; import org.dspace.app.rest.repository.patch.ResourcePatch; +import org.dspace.app.rest.security.DSpacePermissionEvaluator; import org.dspace.app.rest.utils.DSpaceObjectUtils; import org.dspace.app.rest.utils.Utils; import org.dspace.authorize.AuthorizeException; @@ -44,6 +45,8 @@ import org.springframework.data.rest.webmvc.ResourceNotFoundException; import org.springframework.hateoas.Link; import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.stereotype.Component; /** @@ -73,6 +76,9 @@ public class ResourcePolicyRestRepository extends DSpaceRestRepository resourcePatch; + @Autowired + private DSpacePermissionEvaluator permissionEvaluator; + @Autowired DiscoverableEndpointsService discoverableEndpointsService; @@ -222,14 +228,13 @@ public Page findByGroup(@Parameter(value = "uuid", required } @Override - @PreAuthorize("hasAuthority('ADMIN')") + @PreAuthorize("isAuthenticated()") protected ResourcePolicyRest createAndReturn(Context context) throws AuthorizeException, SQLException { String resourceUuidStr = getRequestService().getCurrentRequest().getServletRequest().getParameter("resource"); String epersonUuidStr = getRequestService().getCurrentRequest().getServletRequest().getParameter("eperson"); String groupUuidStr = getRequestService().getCurrentRequest().getServletRequest().getParameter("group"); - if (resourceUuidStr == null) { throw new MissingParameterException("Missing resource (uuid) parameter"); } @@ -244,6 +249,11 @@ protected ResourcePolicyRest createAndReturn(Context context) throws AuthorizeEx UUID resourceUuid = UUID.fromString(resourceUuidStr); + if (isNotAuthorized(resourceUuid, "WRITE")) { + throw new AuthorizeException( + "User unauthorized to create a new ResourcePolicy for resource: " + resourceUuid); + } + try { resourcePolicyRest = mapper.readValue(req.getInputStream(), ResourcePolicyRest.class); } catch (IOException exIO) { @@ -298,7 +308,7 @@ protected ResourcePolicyRest createAndReturn(Context context) throws AuthorizeEx } @Override - @PreAuthorize("hasAuthority('ADMIN')") + @PreAuthorize("hasPermission(#id, 'resourcepolicy', 'ADMIN')") protected void delete(Context context, Integer id) throws AuthorizeException { ResourcePolicy resourcePolicy = null; try { @@ -332,4 +342,10 @@ public void afterPropertiesSet() throws Exception { Link.of("/api/" + ResourcePolicyRest.CATEGORY + "/" + ResourcePolicyRest.PLURAL_NAME + "/search", ResourcePolicyRest.PLURAL_NAME + "-search"))); } + + private boolean isNotAuthorized(UUID id, String permission) { + Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); + return !permissionEvaluator.hasPermission(authentication, id, "resourcepolicy", permission); + } + } From 8fee121b533faa1b81a9e2ba2f5b9879cd412113 Mon Sep 17 00:00:00 2001 From: Mykhaylo Boychuk Date: Fri, 24 Jan 2025 02:06:16 +0100 Subject: [PATCH 747/979] [DURACOM-318] improve sucurity plugin (cherry picked from commit b1ce88925ea36e84a77e667a94ae5577b5ee05b6) (cherry picked from commit e9be8435ec9fffec790ad965c162f89e11fedf97) --- .../authorize/ResourcePolicyServiceImpl.java | 6 ++- .../src/main/java/org/dspace/core/Utils.java | 43 +++++++++++++++++++ ...PolicyAdminPermissionEvalutatorPlugin.java | 42 +++++++++++++----- ...cePolicyRestPermissionEvaluatorPlugin.java | 1 - 4 files changed, 80 insertions(+), 12 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/authorize/ResourcePolicyServiceImpl.java b/dspace-api/src/main/java/org/dspace/authorize/ResourcePolicyServiceImpl.java index 86998a2196e7..08a8a1463c03 100644 --- a/dspace-api/src/main/java/org/dspace/authorize/ResourcePolicyServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/authorize/ResourcePolicyServiceImpl.java @@ -19,6 +19,7 @@ import org.apache.commons.lang3.ObjectUtils; import org.apache.logging.log4j.Logger; import org.dspace.authorize.dao.ResourcePolicyDAO; +import org.dspace.authorize.service.AuthorizeService; import org.dspace.authorize.service.ResourcePolicyService; import org.dspace.content.DSpaceObject; import org.dspace.content.factory.ContentServiceFactory; @@ -51,6 +52,9 @@ public class ResourcePolicyServiceImpl implements ResourcePolicyService { @Autowired private GroupService groupService; + @Autowired + private AuthorizeService authorizeService; + protected ResourcePolicyServiceImpl() { } @@ -422,6 +426,6 @@ public boolean isMyResourcePolicy(Context context, EPerson eperson, Integer id) } else if (group != null && groupService.isMember(context, eperson, group)) { isMy = true; } - return isMy; + return isMy || authorizeService.isAdmin(context, eperson, resourcePolicy.getdSpaceObject()); } } diff --git a/dspace-api/src/main/java/org/dspace/core/Utils.java b/dspace-api/src/main/java/org/dspace/core/Utils.java index ea9ed57eca04..90df7240503e 100644 --- a/dspace-api/src/main/java/org/dspace/core/Utils.java +++ b/dspace-api/src/main/java/org/dspace/core/Utils.java @@ -506,4 +506,47 @@ public static String interpolateConfigsInString(String string) { ConfigurationService config = DSpaceServicesFactory.getInstance().getConfigurationService(); return StringSubstitutor.replace(string, config.getProperties()); } + + /** + * Get the maximum timestamp that can be stored in a PostgreSQL database with hibernate, + * for our "distant future" access expiry date. + * @return the maximum timestamp that can be stored with Postgres + Hibernate + */ + public static Instant getMaxTimestamp() { + return LocalDateTime.of(294276, 12, 31, 23, 59, 59) + .toInstant(ZoneOffset.UTC); + } + + /** + * Get the minimum timestamp that can be stored in a PostgreSQL database, for date validation or any other + * purpose to ensure we don't try to store a date before the epoch. + * @return the minimum timestamp that can be stored with Postgres + Hibernate + */ + public static Instant getMinTimestamp() { + return LocalDateTime.of(-4713, 11, 12, 0, 0, 0) + .toInstant(ZoneOffset.UTC); + } + + /** + * Checks whether a given string can be converted to a valid {@code int} value. + *

    + * This method returns {@code false} if the input string is {@code null}, empty, + * or contains only whitespace. Otherwise, it attempts to parse the string as an + * integer using {@link Integer#parseInt(String)}. + * + * @param str the string to check for integer convertibility + * @return {@code true} if the string is non-blank and can be parsed as an integer; + * {@code false} otherwise + */ + public static boolean isConvertibleToInt(String str) { + if (StringUtils.isBlank(str)) { + return false; + } + try { + Integer.parseInt(str); + return true; + } catch (NumberFormatException e) { + return false; + } + } } diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/security/ResourcePolicyAdminPermissionEvalutatorPlugin.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/security/ResourcePolicyAdminPermissionEvalutatorPlugin.java index ccf272ecefae..230cad1e7127 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/security/ResourcePolicyAdminPermissionEvalutatorPlugin.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/security/ResourcePolicyAdminPermissionEvalutatorPlugin.java @@ -9,17 +9,20 @@ import java.io.Serializable; import java.sql.SQLException; +import java.util.UUID; import org.apache.commons.lang3.StringUtils; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.dspace.app.rest.model.ResourcePolicyRest; import org.dspace.app.rest.utils.ContextUtil; +import org.dspace.app.util.factory.UtilServiceFactory; import org.dspace.authorize.ResourcePolicy; import org.dspace.authorize.service.AuthorizeService; import org.dspace.authorize.service.ResourcePolicyService; import org.dspace.content.DSpaceObject; import org.dspace.core.Context; +import org.dspace.core.Utils; import org.dspace.services.RequestService; import org.dspace.services.model.Request; import org.springframework.beans.factory.annotation.Autowired; @@ -38,7 +41,7 @@ public class ResourcePolicyAdminPermissionEvalutatorPlugin extends RestObjectPer private static final Logger log = LogManager.getLogger(); - public static final String RESOURCE_POLICY_PATCH = "resourcepolicy"; + public static final String RESOURCE_POLICY_TYPE = "resourcepolicy"; @Autowired AuthorizeService authorizeService; @@ -55,8 +58,9 @@ public boolean hasDSpacePermission(Authentication authentication, Serializable t DSpaceRestPermission restPermission = DSpaceRestPermission.convert(permission); - if (!DSpaceRestPermission.ADMIN.equals(restPermission) - || !StringUtils.equalsIgnoreCase(targetType, RESOURCE_POLICY_PATCH)) { + if (!DSpaceRestPermission.ADMIN.equals(restPermission) && + !DSpaceRestPermission.WRITE.equals(restPermission) || + !StringUtils.equalsIgnoreCase(targetType, RESOURCE_POLICY_TYPE)) { return false; } @@ -64,19 +68,37 @@ public boolean hasDSpacePermission(Authentication authentication, Serializable t Context context = ContextUtil.obtainContext(request.getHttpServletRequest()); try { - int resourcePolicyID = Integer.parseInt(targetId.toString()); - ResourcePolicy resourcePolicy = resourcePolicyService.find(context, resourcePolicyID); - if (resourcePolicy == null) { - throw new ResourceNotFoundException( - ResourcePolicyRest.CATEGORY + "." + ResourcePolicyRest.NAME + - " with id: " + resourcePolicyID + " not found"); + DSpaceObject dso = null; + if (Utils.isConvertibleToInt(targetId.toString())) { + var id = Integer.parseInt(targetId.toString()); + dso = getDSO(context, id); + } else { + var uuid = UUID.fromString(targetId.toString()); + dso = getDSO(context, uuid); } - DSpaceObject dso = resourcePolicy.getdSpaceObject(); return authorizeService.isAdmin(context, dso); + } catch (SQLException e) { log.error(e::getMessage, e); } return false; } + private DSpaceObject getDSO(Context context, int id) throws SQLException { + ResourcePolicy resourcePolicy = resourcePolicyService.find(context, id); + if (resourcePolicy == null) { + throw new ResourceNotFoundException( + ResourcePolicyRest.CATEGORY + "." + ResourcePolicyRest.NAME + " with id: " + id + " not found"); + } + return resourcePolicy.getdSpaceObject(); + } + + private DSpaceObject getDSO(Context context, UUID uuid) throws SQLException { + DSpaceObject dso = UtilServiceFactory.getInstance().getDSpaceObjectUtils().findDSpaceObject(context, uuid); + if (dso == null) { + throw new ResourceNotFoundException("DSpaceObject with uuid: " + uuid + " not found"); + } + return dso; + } + } diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/security/ResourcePolicyRestPermissionEvaluatorPlugin.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/security/ResourcePolicyRestPermissionEvaluatorPlugin.java index 9a34ca68110d..6635a87a229a 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/security/ResourcePolicyRestPermissionEvaluatorPlugin.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/security/ResourcePolicyRestPermissionEvaluatorPlugin.java @@ -55,7 +55,6 @@ public boolean hasDSpacePermission(Authentication authentication, Serializable t DSpaceRestPermission restPermission = DSpaceRestPermission.convert(permission); if (!DSpaceRestPermission.READ.equals(restPermission) - && !DSpaceRestPermission.WRITE.equals(restPermission) && !DSpaceRestPermission.DELETE.equals(restPermission) || !StringUtils.equalsIgnoreCase(targetType, ResourcePolicyRest.NAME)) { return false; From 0161f20036419ff9bc7c3163cd3bba5c615fd7b5 Mon Sep 17 00:00:00 2001 From: Adamo Date: Fri, 2 May 2025 09:38:28 +0200 Subject: [PATCH 748/979] [DURACOM-318] IT fix (cherry picked from commit accba0738f7684e31af707bf5bd06508e5571621) --- .../dspace/app/rest/ResourcePolicyRestRepositoryIT.java | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/ResourcePolicyRestRepositoryIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/ResourcePolicyRestRepositoryIT.java index 5570332b16b2..e065bf27c81d 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/ResourcePolicyRestRepositoryIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/ResourcePolicyRestRepositoryIT.java @@ -190,7 +190,7 @@ public void findOneUnAuthenticatedTest() throws Exception { public void findOneNotFoundTest() throws Exception { String authToken = getAuthToken(admin.getEmail(), password); - getClient(authToken).perform(get("/api/authz/resourcepolicies/" + UUID.randomUUID().toString())) + getClient(authToken).perform(get("/api/authz/resourcepolicies/" + UUID.randomUUID())) .andExpect(status().isNotFound()); } @@ -1742,11 +1742,10 @@ public void deletePolicyByCollectionAdminTest() throws Exception { String authcolAdmin2Token = getAuthToken(colAdmin2.getEmail(), password); String authSubmitterToken = getAuthToken(submitter.getEmail(), password); - ResourcePolicy rp = ResourcePolicyBuilder.createResourcePolicy(context) + ResourcePolicy rp = ResourcePolicyBuilder.createResourcePolicy(context, submitter, null) .withDspaceObject(bitstream) .withAction(Constants.READ) .withPolicyType(ResourcePolicy.TYPE_CUSTOM) - .withUser(submitter) .build(); // submitter can't delete own policy @@ -1825,11 +1824,10 @@ public void deletePolicyBySubCommunityAdminTest() throws Exception { context.restoreAuthSystemState(); - ResourcePolicy rp = ResourcePolicyBuilder.createResourcePolicy(context) + ResourcePolicy rp = ResourcePolicyBuilder.createResourcePolicy(context, submitter, null) .withDspaceObject(publication) .withAction(Constants.WRITE) .withPolicyType(ResourcePolicy.TYPE_CUSTOM) - .withUser(submitter) .build(); String adminToken = getAuthToken(admin.getEmail(), password); From ae26a8facd2fb709027359a7ca7a63ea2f65567e Mon Sep 17 00:00:00 2001 From: Mykhaylo Boychuk Date: Mon, 3 Feb 2025 19:30:16 +0100 Subject: [PATCH 749/979] [DURACOM-318] improve code (cherry picked from commit 8e0ca2e6f88b0251edf8a840f65135590c65f088) (cherry picked from commit 4270170d40833b7cd4c505a850c47f4bdff55a1c) --- dspace-api/src/main/java/org/dspace/core/Utils.java | 1 + .../ResourcePolicyAdminPermissionEvalutatorPlugin.java | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/dspace-api/src/main/java/org/dspace/core/Utils.java b/dspace-api/src/main/java/org/dspace/core/Utils.java index 90df7240503e..59d37f71cf42 100644 --- a/dspace-api/src/main/java/org/dspace/core/Utils.java +++ b/dspace-api/src/main/java/org/dspace/core/Utils.java @@ -549,4 +549,5 @@ public static boolean isConvertibleToInt(String str) { return false; } } + } diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/security/ResourcePolicyAdminPermissionEvalutatorPlugin.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/security/ResourcePolicyAdminPermissionEvalutatorPlugin.java index 230cad1e7127..a7a2b767656b 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/security/ResourcePolicyAdminPermissionEvalutatorPlugin.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/security/ResourcePolicyAdminPermissionEvalutatorPlugin.java @@ -11,6 +11,7 @@ import java.sql.SQLException; import java.util.UUID; +import org.apache.commons.lang.math.NumberUtils; import org.apache.commons.lang3.StringUtils; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -69,7 +70,7 @@ public boolean hasDSpacePermission(Authentication authentication, Serializable t try { DSpaceObject dso = null; - if (Utils.isConvertibleToInt(targetId.toString())) { + if (NumberUtils.isNumber(targetId.toString())) { var id = Integer.parseInt(targetId.toString()); dso = getDSO(context, id); } else { From 84deaf3cb6583a7860fabdf35fb6c68583b7b0c2 Mon Sep 17 00:00:00 2001 From: Mykhaylo Boychuk Date: Mon, 3 Feb 2025 22:54:52 +0100 Subject: [PATCH 750/979] [DURACOM-318] remove unused import (cherry picked from commit ed91462ccd7f99f5cf0dc326ed06ab42c13b80e0) (cherry picked from commit 8df4e35e76364bf20dad51073e33cda8f3c3f623) --- .../security/ResourcePolicyAdminPermissionEvalutatorPlugin.java | 1 - 1 file changed, 1 deletion(-) diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/security/ResourcePolicyAdminPermissionEvalutatorPlugin.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/security/ResourcePolicyAdminPermissionEvalutatorPlugin.java index a7a2b767656b..ad83284b84b3 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/security/ResourcePolicyAdminPermissionEvalutatorPlugin.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/security/ResourcePolicyAdminPermissionEvalutatorPlugin.java @@ -23,7 +23,6 @@ import org.dspace.authorize.service.ResourcePolicyService; import org.dspace.content.DSpaceObject; import org.dspace.core.Context; -import org.dspace.core.Utils; import org.dspace.services.RequestService; import org.dspace.services.model.Request; import org.springframework.beans.factory.annotation.Autowired; From 39def525922f9bba8bc0a39e521f6ce7fbaa02cb Mon Sep 17 00:00:00 2001 From: Mykhaylo Boychuk Date: Fri, 24 Jan 2025 01:21:25 +0100 Subject: [PATCH 751/979] [DURACOM-318] add new ITs for ResourcePolicy (cherry picked from commit d78d4f00d94ef0d4b147031b075b8df1e8896fe4) (cherry picked from commit 98c2b9942167890d24b71f5c32e6918ac2eee7a6) --- .../rest/ResourcePolicyRestRepositoryIT.java | 548 ++++++++++++++++++ 1 file changed, 548 insertions(+) diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/ResourcePolicyRestRepositoryIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/ResourcePolicyRestRepositoryIT.java index 5d2a05ab6440..08ce836f3d78 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/ResourcePolicyRestRepositoryIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/ResourcePolicyRestRepositoryIT.java @@ -23,6 +23,9 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import java.text.SimpleDateFormat; +import java.io.InputStream; +import java.time.LocalDate; +import java.time.format.DateTimeFormatter; import java.util.ArrayList; import java.util.Calendar; import java.util.Date; @@ -32,6 +35,9 @@ import javax.ws.rs.core.MediaType; import com.fasterxml.jackson.databind.ObjectMapper; +import jakarta.ws.rs.core.MediaType; +import org.apache.commons.codec.CharEncoding; +import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.StringUtils; import org.dspace.app.rest.matcher.ResourcePolicyMatcher; import org.dspace.app.rest.model.ResourcePolicyRest; @@ -43,12 +49,14 @@ import org.dspace.authorize.ResourcePolicy; import org.dspace.authorize.service.AuthorizeService; import org.dspace.authorize.service.ResourcePolicyService; +import org.dspace.builder.BitstreamBuilder; import org.dspace.builder.CollectionBuilder; import org.dspace.builder.CommunityBuilder; import org.dspace.builder.EPersonBuilder; import org.dspace.builder.GroupBuilder; import org.dspace.builder.ItemBuilder; import org.dspace.builder.ResourcePolicyBuilder; +import org.dspace.content.Bitstream; import org.dspace.content.Collection; import org.dspace.content.Community; import org.dspace.content.Item; @@ -1215,6 +1223,376 @@ public void createOneForbiddenTest() throws Exception { .andExpect(jsonPath("$.page.totalElements", is(0))); } + @Test + public void createPolicyByCollectionAdminTest() throws Exception { + context.turnOffAuthorisationSystem(); + EPerson colAdmin = EPersonBuilder.createEPerson(context) + .withEmail("colAdmin@mail.test") + .withPassword(password) + .build(); + + EPerson colAdmin2 = EPersonBuilder.createEPerson(context) + .withEmail("colAdmin2@mail.test") + .withPassword(password) + .build(); + + EPerson submitter = EPersonBuilder.createEPerson(context) + .withEmail("colSubmitter@mail.test") + .withPassword(password) + .build(); + + Community community = CommunityBuilder.createCommunity(context) + .withName("My top commynity") + .build(); + + Collection collection = CollectionBuilder.createCollection(context, community) + .withName("My collection") + .withAdminGroup(colAdmin) + .withSubmitterGroup(submitter) + .withEntityType("Publication") + .build(); + + CollectionBuilder.createCollection(context, community) + .withName("My Second Collection") + .withAdminGroup(colAdmin2) + .withSubmitterGroup(submitter) + .withEntityType("Publication") + .build(); + + Item publication = ItemBuilder.createItem(context, collection) + .withTitle("Public item") + .build(); + + //Add a bitstream to a publication + Bitstream bitstream = null; + try (InputStream is = IOUtils.toInputStream("ThisIsSomeDummyText", CharEncoding.UTF_8)) { + bitstream = BitstreamBuilder.createBitstream(context, publication, is) + .withName("Bitstream") + .withDescription("description") + .withMimeType("text/plain") + .build(); + } + context.restoreAuthSystemState(); + + ResourcePolicyRest resourcePolicyRest = new ResourcePolicyRest(); + resourcePolicyRest.setPolicyType(ResourcePolicy.TYPE_CUSTOM); + resourcePolicyRest.setAction(Constants.actionText[Constants.WRITE]); + resourcePolicyRest.setName("Test for collection admin"); + + String authcolAdminToken = getAuthToken(colAdmin.getEmail(), password); + String authcolAdmin2Token = getAuthToken(colAdmin2.getEmail(), password); + String authSubmitterToken = getAuthToken(submitter.getEmail(), password); + AtomicReference idRef = new AtomicReference(); + + try { + // submitter can't create policy + getClient(authSubmitterToken).perform(post("/api/authz/resourcepolicies") + .content(new ObjectMapper().writeValueAsBytes(resourcePolicyRest)) + .param("resource", bitstream.getID().toString()) + .param("eperson", submitter.getID().toString()) + .param("projections", "full") + .contentType(contentType)) + .andExpect(status().isForbidden()); + + // other collection admin can't create policy for other collection + getClient(authcolAdmin2Token).perform(post("/api/authz/resourcepolicies") + .content(new ObjectMapper().writeValueAsBytes(resourcePolicyRest)) + .param("resource", bitstream.getID().toString()) + .param("eperson", submitter.getID().toString()) + .param("projections", "full") + .contentType(contentType)) + .andExpect(status().isForbidden()); + + // create policy for submitter by collection admin + getClient(authcolAdminToken).perform(post("/api/authz/resourcepolicies") + .content(new ObjectMapper().writeValueAsBytes(resourcePolicyRest)) + .param("resource", bitstream.getID().toString()) + .param("eperson", submitter.getID().toString()) + .param("projections", "full") + .contentType(contentType)) + .andExpect(status().isCreated()) + .andExpect(content().contentType(contentType)) + .andExpect(jsonPath("$", ResourcePolicyMatcher.matchFullEmbeds())) + .andExpect(jsonPath("$", Matchers.allOf( + hasJsonPath("$.name", is(resourcePolicyRest.getName())), + hasJsonPath("$.description", is(resourcePolicyRest.getDescription())), + hasJsonPath("$.policyType", is(resourcePolicyRest.getPolicyType())), + hasJsonPath("$.action", is(resourcePolicyRest.getAction())), + hasJsonPath("$.startDate", is(resourcePolicyRest.getStartDate())), + hasJsonPath("$.endDate", is(resourcePolicyRest.getEndDate())), + hasJsonPath("$.type", is(resourcePolicyRest.getType()))))) + .andDo(result -> idRef.set(read(result.getResponse().getContentAsString(), "$.id"))); + + // submitter can see own policy + getClient(authSubmitterToken).perform(get("/api/authz/resourcepolicies/" + idRef.get())) + .andExpect(status().isOk()) + .andExpect(content().contentType(contentType)) + .andExpect(jsonPath("$._links.self.href", + Matchers.containsString("/api/authz/resourcepolicies/" + idRef.get()))); + + // collection admin can see that policy + getClient(authcolAdminToken).perform(get("/api/authz/resourcepolicies/" + idRef.get())) + .andExpect(status().isOk()) + .andExpect(content().contentType(contentType)) + .andExpect(jsonPath("$._links.self.href", + Matchers.containsString("/api/authz/resourcepolicies/" + idRef.get()))); + } finally { + ResourcePolicyBuilder.delete(idRef.get()); + } + } + + @Test + public void createPolicyBySubCommunityAdminTest() throws Exception { + context.turnOffAuthorisationSystem(); + EPerson comAdmin = EPersonBuilder.createEPerson(context) + .withEmail("comAdmin@mail.test") + .withPassword(password) + .build(); + + EPerson comAdmin2 = EPersonBuilder.createEPerson(context) + .withEmail("comAdmin2@mail.test") + .withPassword(password) + .build(); + + EPerson submitter = EPersonBuilder.createEPerson(context) + .withEmail("colSubmitter@mail.test") + .withPassword(password) + .build(); + + Community community = CommunityBuilder.createSubCommunity(context, parentCommunity) + .withName("My First Commynity") + .withAdminGroup(comAdmin) + .build(); + + Community community2 = CommunityBuilder.createSubCommunity(context, parentCommunity) + .withName("My Second Commynity") + .withAdminGroup(comAdmin2) + .build(); + + Collection collection = CollectionBuilder.createCollection(context, community) + .withName("My collection") + .withSubmitterGroup(submitter) + .withEntityType("Publication") + .build(); + + CollectionBuilder.createCollection(context, community2) + .withName("My Second Collection") + .withSubmitterGroup(submitter) + .withEntityType("Publication") + .build(); + + Item publication = ItemBuilder.createItem(context, collection) + .withTitle("Public item") + .build(); + + context.restoreAuthSystemState(); + + ResourcePolicyRest resourcePolicyRest = new ResourcePolicyRest(); + resourcePolicyRest.setPolicyType(ResourcePolicy.TYPE_CUSTOM); + resourcePolicyRest.setAction(Constants.actionText[Constants.WRITE]); + resourcePolicyRest.setName("Test for collection admin"); + + String authcomAdminToken = getAuthToken(comAdmin.getEmail(), password); + String authcomAdmin2Token = getAuthToken(comAdmin2.getEmail(), password); + String authSubmitterToken = getAuthToken(submitter.getEmail(), password); + AtomicReference idRef = new AtomicReference(); + + try { + // submitter can't create policy + getClient(authSubmitterToken).perform(post("/api/authz/resourcepolicies") + .content(new ObjectMapper().writeValueAsBytes(resourcePolicyRest)) + .param("resource", publication.getID().toString()) + .param("eperson", submitter.getID().toString()) + .param("projections", "full") + .contentType(contentType)) + .andExpect(status().isForbidden()); + + // other Community admin can't create policy for collections into other Community + getClient(authcomAdmin2Token).perform(post("/api/authz/resourcepolicies") + .content(new ObjectMapper().writeValueAsBytes(resourcePolicyRest)) + .param("resource", publication.getID().toString()) + .param("eperson", submitter.getID().toString()) + .param("projections", "full") + .contentType(contentType)) + .andExpect(status().isForbidden()); + + // create policy for submitter by Community admin + getClient(authcomAdminToken).perform(post("/api/authz/resourcepolicies") + .content(new ObjectMapper().writeValueAsBytes(resourcePolicyRest)) + .param("resource", publication.getID().toString()) + .param("eperson", submitter.getID().toString()) + .param("projections", "full") + .contentType(contentType)) + .andExpect(status().isCreated()) + .andExpect(content().contentType(contentType)) + .andExpect(jsonPath("$", ResourcePolicyMatcher.matchFullEmbeds())) + .andExpect(jsonPath("$", Matchers.allOf( + hasJsonPath("$.name", is(resourcePolicyRest.getName())), + hasJsonPath("$.description", is(resourcePolicyRest.getDescription())), + hasJsonPath("$.policyType", is(resourcePolicyRest.getPolicyType())), + hasJsonPath("$.action", is(resourcePolicyRest.getAction())), + hasJsonPath("$.startDate", is(resourcePolicyRest.getStartDate())), + hasJsonPath("$.endDate", is(resourcePolicyRest.getEndDate())), + hasJsonPath("$.type", is(resourcePolicyRest.getType()))))) + .andDo(result -> idRef.set(read(result.getResponse().getContentAsString(), "$.id"))); + + // submitter can see own policy + getClient(authSubmitterToken).perform(get("/api/authz/resourcepolicies/" + idRef.get())) + .andExpect(status().isOk()) + .andExpect(content().contentType(contentType)) + .andExpect(jsonPath("$._links.self.href", + Matchers.containsString("/api/authz/resourcepolicies/" + idRef.get()))); + + // community admin can see policies of own collections/items + getClient(authcomAdminToken).perform(get("/api/authz/resourcepolicies/" + idRef.get())) + .andExpect(status().isOk()) + .andExpect(content().contentType(contentType)) + .andExpect(jsonPath("$._links.self.href", + Matchers.containsString("/api/authz/resourcepolicies/" + idRef.get()))); + + // Other community admin can't see policies of other community's collections/items + getClient(authcomAdmin2Token).perform(get("/api/authz/resourcepolicies/" + idRef.get())) + .andExpect(status().isForbidden()); + } finally { + ResourcePolicyBuilder.delete(idRef.get()); + } + } + + @Test + public void createPolicyByCommunityAdminTest() throws Exception { + context.turnOffAuthorisationSystem(); + EPerson rootComAdmin = EPersonBuilder.createEPerson(context) + .withEmail("rootComAdmin@mail.test") + .withPassword(password) + .build(); + + EPerson submitter = EPersonBuilder.createEPerson(context) + .withEmail("colSubmitter@mail.test") + .withPassword(password) + .build(); + + Community rootCommunity = CommunityBuilder.createCommunity(context) + .withName("Root Community") + .withAdminGroup(rootComAdmin) + .build(); + + Community community = CommunityBuilder.createSubCommunity(context, rootCommunity) + .withName("My First Commynity") + .build(); + + Community community2 = CommunityBuilder.createSubCommunity(context, rootCommunity) + .withName("My Second Commynity") + .build(); + + Collection collection = CollectionBuilder.createCollection(context, community) + .withName("My collection") + .withSubmitterGroup(submitter) + .withEntityType("Publication") + .build(); + + CollectionBuilder.createCollection(context, community2) + .withName("My Second Collection") + .withSubmitterGroup(submitter) + .withEntityType("Publication") + .build(); + + Item publication = ItemBuilder.createItem(context, collection) + .withTitle("Public item") + .build(); + + Collection collection2 = CollectionBuilder.createCollection(context, community) + .withName("My Second Collection") + .withSubmitterGroup(submitter) + .withEntityType("Publication") + .build(); + + Item publication2 = ItemBuilder.createItem(context, collection2) + .withTitle("Item of second collection") + .build(); + + //Add a bitstream to a publication + Bitstream bitstream = null; + try (InputStream is = IOUtils.toInputStream("ThisIsSomeDummyText", CharEncoding.UTF_8)) { + bitstream = BitstreamBuilder.createBitstream(context, publication2, is) + .withName("Bitstream") + .withDescription("description") + .withMimeType("text/plain") + .build(); + } + + context.restoreAuthSystemState(); + + ResourcePolicyRest resourcePolicyRest = new ResourcePolicyRest(); + resourcePolicyRest.setPolicyType(ResourcePolicy.TYPE_CUSTOM); + resourcePolicyRest.setAction(Constants.actionText[Constants.WRITE]); + resourcePolicyRest.setName("Test for collection admin"); + + ResourcePolicyRest resourcePolicyRest2 = new ResourcePolicyRest(); + resourcePolicyRest2.setPolicyType(ResourcePolicy.TYPE_CUSTOM); + resourcePolicyRest2.setAction(Constants.actionText[Constants.WRITE]); + resourcePolicyRest2.setName("Test for root community admin"); + + String authSubmitterToken = getAuthToken(submitter.getEmail(), password); + String authRootAdminToken = getAuthToken(rootComAdmin.getEmail(), password); + + AtomicReference idRef = new AtomicReference(); + AtomicReference idRef2 = new AtomicReference(); + try { + // create policy for submitter by root Community admin + getClient(authRootAdminToken).perform(post("/api/authz/resourcepolicies") + .content(new ObjectMapper().writeValueAsBytes(resourcePolicyRest)) + .param("resource", publication.getID().toString()) + .param("eperson", submitter.getID().toString()) + .contentType(contentType)) + .andExpect(status().isCreated()) + .andExpect(content().contentType(contentType)) + .andExpect(jsonPath("$", ResourcePolicyMatcher.matchFullEmbeds())) + .andExpect(jsonPath("$", Matchers.allOf( + hasJsonPath("$.name", is(resourcePolicyRest.getName())), + hasJsonPath("$.description", is(resourcePolicyRest.getDescription())), + hasJsonPath("$.policyType", is(resourcePolicyRest.getPolicyType())), + hasJsonPath("$.action", is(resourcePolicyRest.getAction())), + hasJsonPath("$.startDate", is(resourcePolicyRest.getStartDate())), + hasJsonPath("$.endDate", is(resourcePolicyRest.getEndDate())), + hasJsonPath("$.type", is(resourcePolicyRest.getType()))))) + .andDo(result -> idRef.set(read(result.getResponse().getContentAsString(), "$.id"))); + + // create policy for submitter by root Community admin + getClient(authRootAdminToken).perform(post("/api/authz/resourcepolicies") + .content(new ObjectMapper().writeValueAsBytes(resourcePolicyRest)) + .param("resource", bitstream.getID().toString()) + .param("eperson", submitter.getID().toString()) + .contentType(contentType)) + .andExpect(status().isCreated()) + .andExpect(content().contentType(contentType)) + .andExpect(jsonPath("$", ResourcePolicyMatcher.matchFullEmbeds())) + .andExpect(jsonPath("$", Matchers.allOf( + hasJsonPath("$.name", is(resourcePolicyRest.getName())), + hasJsonPath("$.description", is(resourcePolicyRest.getDescription())), + hasJsonPath("$.policyType", is(resourcePolicyRest.getPolicyType())), + hasJsonPath("$.action", is(resourcePolicyRest.getAction())), + hasJsonPath("$.startDate", is(resourcePolicyRest.getStartDate())), + hasJsonPath("$.endDate", is(resourcePolicyRest.getEndDate())), + hasJsonPath("$.type", is(resourcePolicyRest.getType()))))) + .andDo(result -> idRef2.set(read(result.getResponse().getContentAsString(), "$.id"))); + + getClient(authSubmitterToken).perform(get("/api/authz/resourcepolicies/" + idRef.get())) + .andExpect(status().isOk()) + .andExpect(content().contentType(contentType)) + .andExpect(jsonPath("$._links.self.href", + Matchers.containsString("/api/authz/resourcepolicies/" + idRef.get()))); + + getClient(authSubmitterToken).perform(get("/api/authz/resourcepolicies/" + idRef2.get())) + .andExpect(status().isOk()) + .andExpect(content().contentType(contentType)) + .andExpect(jsonPath("$._links.self.href", + Matchers.containsString("/api/authz/resourcepolicies/" + idRef2.get()))); + } finally { + ResourcePolicyBuilder.delete(idRef.get()); + ResourcePolicyBuilder.delete(idRef2.get()); + } + } + @Test public void deleteOne() throws Exception { context.turnOffAuthorisationSystem(); @@ -1308,6 +1686,176 @@ public void deleteOneNotFoundTest() throws Exception { .andExpect(status().isNotFound()); } + @Test + public void deletePolicyByCollectionAdminTest() throws Exception { + context.turnOffAuthorisationSystem(); + EPerson colAdmin = EPersonBuilder.createEPerson(context) + .withEmail("colAdmin@mail.test") + .withPassword(password) + .build(); + + EPerson colAdmin2 = EPersonBuilder.createEPerson(context) + .withEmail("colAdmin2@mail.test") + .withPassword(password) + .build(); + + EPerson submitter = EPersonBuilder.createEPerson(context) + .withEmail("colSubmitter@mail.test") + .withPassword(password) + .build(); + + Community community = CommunityBuilder.createCommunity(context) + .withName("My top commynity") + .build(); + + Collection collection = CollectionBuilder.createCollection(context, community) + .withName("My collection") + .withAdminGroup(colAdmin) + .withSubmitterGroup(submitter) + .withEntityType("Publication") + .build(); + + CollectionBuilder.createCollection(context, community) + .withName("My Second Collection") + .withAdminGroup(colAdmin2) + .withSubmitterGroup(submitter) + .withEntityType("Publication") + .build(); + + Item publication = ItemBuilder.createItem(context, collection) + .withTitle("Public item") + .build(); + + //Add a bitstream to a publication + Bitstream bitstream = null; + try (InputStream is = IOUtils.toInputStream("ThisIsSomeDummyText", CharEncoding.UTF_8)) { + bitstream = BitstreamBuilder.createBitstream(context, publication, is) + .withName("Bitstream") + .withDescription("description") + .withMimeType("text/plain") + .build(); + } + + context.restoreAuthSystemState(); + + String adminToken = getAuthToken(admin.getEmail(), password); + String authcolAdminToken = getAuthToken(colAdmin.getEmail(), password); + String authcolAdmin2Token = getAuthToken(colAdmin2.getEmail(), password); + String authSubmitterToken = getAuthToken(submitter.getEmail(), password); + + ResourcePolicy rp = ResourcePolicyBuilder.createResourcePolicy(context) + .withDspaceObject(bitstream) + .withAction(Constants.READ) + .withPolicyType(ResourcePolicy.TYPE_CUSTOM) + .withUser(submitter) + .build(); + + // submitter can't delete own policy + getClient(authSubmitterToken).perform(delete("/api/authz/resourcepolicies/" + rp.getID())) + .andExpect(status().isForbidden()); + + // check that policy wasn't deleted + getClient(adminToken).perform(get("/api/authz/resourcepolicies/" + rp.getID())) + .andExpect(status().isOk()) + .andExpect(content().contentType(contentType)) + .andExpect(jsonPath("$._links.self.href", + Matchers.containsString("/api/authz/resourcepolicies/" + rp.getID()))); + + // other collection admin can't delete policy that belong to items of other collections + getClient(authcolAdmin2Token).perform(delete("/api/authz/resourcepolicies/" + rp.getID())) + .andExpect(status().isForbidden()); + + // check that policy wasn't deleted + getClient(adminToken).perform(get("/api/authz/resourcepolicies/" + rp.getID())) + .andExpect(status().isOk()) + .andExpect(content().contentType(contentType)) + .andExpect(jsonPath("$._links.self.href", + Matchers.containsString("/api/authz/resourcepolicies/" + rp.getID()))); + + // delete policy for submitter by collection admin + getClient(authcolAdminToken).perform(delete("/api/authz/resourcepolicies/" + rp.getID())) + .andExpect(status().isNoContent()); + + getClient(adminToken).perform(get("/api/authz/resourcepolicies/" + rp.getID())) + .andExpect(status().isNotFound()); + } + + @Test + public void deletePolicyBySubCommunityAdminTest() throws Exception { + context.turnOffAuthorisationSystem(); + EPerson comAdmin = EPersonBuilder.createEPerson(context) + .withEmail("comAdmin@mail.test") + .withPassword(password) + .build(); + + EPerson comAdmin2 = EPersonBuilder.createEPerson(context) + .withEmail("comAdmin2@mail.test") + .withPassword(password) + .build(); + + EPerson submitter = EPersonBuilder.createEPerson(context) + .withEmail("colSubmitter@mail.test") + .withPassword(password) + .build(); + + Community community = CommunityBuilder.createSubCommunity(context, parentCommunity) + .withName("My First Commynity") + .withAdminGroup(comAdmin) + .build(); + + Community community2 = CommunityBuilder.createSubCommunity(context, parentCommunity) + .withName("My Second Commynity") + .withAdminGroup(comAdmin2) + .build(); + + Collection collection = CollectionBuilder.createCollection(context, community) + .withName("My collection") + .withSubmitterGroup(submitter) + .withEntityType("Publication") + .build(); + + CollectionBuilder.createCollection(context, community2) + .withName("My Second Collection") + .withSubmitterGroup(submitter) + .withEntityType("Publication") + .build(); + + Item publication = ItemBuilder.createItem(context, collection) + .withTitle("Public item") + .build(); + + context.restoreAuthSystemState(); + + ResourcePolicy rp = ResourcePolicyBuilder.createResourcePolicy(context) + .withDspaceObject(publication) + .withAction(Constants.WRITE) + .withPolicyType(ResourcePolicy.TYPE_CUSTOM) + .withUser(submitter) + .build(); + + String adminToken = getAuthToken(admin.getEmail(), password); + String authcomAdminToken = getAuthToken(comAdmin.getEmail(), password); + String authcomAdmin2Token = getAuthToken(comAdmin2.getEmail(), password); + + // other Community admin can't delete policy of other Community + getClient(authcomAdmin2Token).perform(delete("/api/authz/resourcepolicies/" + rp.getID())) + .andExpect(status().isForbidden()); + + getClient(adminToken).perform(get("/api/authz/resourcepolicies/" + rp.getID())) + .andExpect(status().isOk()) + .andExpect(content().contentType(contentType)) + .andExpect(jsonPath("$._links.self.href", + Matchers.containsString("/api/authz/resourcepolicies/" + rp.getID()))); + + // Community admin can delete policy + getClient(authcomAdminToken).perform(delete("/api/authz/resourcepolicies/" + rp.getID())) + .andExpect(status().isNoContent()); + + // submitter can see own policy + getClient(adminToken).perform(get("/api/authz/resourcepolicies/" + rp.getID())) + .andExpect(status().isNotFound()); + } + @Test public void patchReplaceStartDateTest() throws Exception { context.turnOffAuthorisationSystem(); From f92e376896eabb56046951b6ddfbf12ba93b3753 Mon Sep 17 00:00:00 2001 From: Mykhaylo Boychuk Date: Fri, 24 Jan 2025 02:03:23 +0100 Subject: [PATCH 752/979] [DURACOM-318] update security annotations on ResourcePolicyRepository (cherry picked from commit fabcc692db68e5232986ff062e849e4ec5c68c8b) (cherry picked from commit 95836c271cb4af41d3c29f2dda118eab4674f653) --- .../ResourcePolicyRestRepository.java | 22 ++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/ResourcePolicyRestRepository.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/ResourcePolicyRestRepository.java index a79a9fe4eaa4..c0341f15e346 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/ResourcePolicyRestRepository.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/ResourcePolicyRestRepository.java @@ -25,6 +25,7 @@ import org.dspace.app.rest.model.ResourcePolicyRest; import org.dspace.app.rest.model.patch.Patch; import org.dspace.app.rest.repository.patch.ResourcePatch; +import org.dspace.app.rest.security.DSpacePermissionEvaluator; import org.dspace.app.rest.utils.DSpaceObjectUtils; import org.dspace.app.rest.utils.Utils; import org.dspace.authorize.AuthorizeException; @@ -44,6 +45,8 @@ import org.springframework.data.rest.webmvc.ResourceNotFoundException; import org.springframework.hateoas.Link; import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.stereotype.Component; /** @@ -73,6 +76,9 @@ public class ResourcePolicyRestRepository extends DSpaceRestRepository resourcePatch; + @Autowired + private DSpacePermissionEvaluator permissionEvaluator; + @Autowired DiscoverableEndpointsService discoverableEndpointsService; @@ -222,14 +228,13 @@ public Page findByGroup(@Parameter(value = "uuid", required } @Override - @PreAuthorize("hasAuthority('ADMIN')") + @PreAuthorize("isAuthenticated()") protected ResourcePolicyRest createAndReturn(Context context) throws AuthorizeException, SQLException { String resourceUuidStr = getRequestService().getCurrentRequest().getServletRequest().getParameter("resource"); String epersonUuidStr = getRequestService().getCurrentRequest().getServletRequest().getParameter("eperson"); String groupUuidStr = getRequestService().getCurrentRequest().getServletRequest().getParameter("group"); - if (resourceUuidStr == null) { throw new MissingParameterException("Missing resource (uuid) parameter"); } @@ -244,6 +249,11 @@ protected ResourcePolicyRest createAndReturn(Context context) throws AuthorizeEx UUID resourceUuid = UUID.fromString(resourceUuidStr); + if (isNotAuthorized(resourceUuid, "WRITE")) { + throw new AuthorizeException( + "User unauthorized to create a new ResourcePolicy for resource: " + resourceUuid); + } + try { resourcePolicyRest = mapper.readValue(req.getInputStream(), ResourcePolicyRest.class); } catch (IOException exIO) { @@ -298,7 +308,7 @@ protected ResourcePolicyRest createAndReturn(Context context) throws AuthorizeEx } @Override - @PreAuthorize("hasAuthority('ADMIN')") + @PreAuthorize("hasPermission(#id, 'resourcepolicy', 'ADMIN')") protected void delete(Context context, Integer id) throws AuthorizeException { ResourcePolicy resourcePolicy = null; try { @@ -332,4 +342,10 @@ public void afterPropertiesSet() throws Exception { Link.of("/api/" + ResourcePolicyRest.CATEGORY + "/" + ResourcePolicyRest.PLURAL_NAME + "/search", ResourcePolicyRest.PLURAL_NAME + "-search"))); } + + private boolean isNotAuthorized(UUID id, String permission) { + Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); + return !permissionEvaluator.hasPermission(authentication, id, "resourcepolicy", permission); + } + } From de5908725af8bc991440506d5af985d04ac5f87c Mon Sep 17 00:00:00 2001 From: Mykhaylo Boychuk Date: Fri, 24 Jan 2025 02:06:16 +0100 Subject: [PATCH 753/979] [DURACOM-318] improve sucurity plugin (cherry picked from commit b1ce88925ea36e84a77e667a94ae5577b5ee05b6) (cherry picked from commit e9be8435ec9fffec790ad965c162f89e11fedf97) --- .../authorize/ResourcePolicyServiceImpl.java | 6 ++- .../src/main/java/org/dspace/core/Utils.java | 43 +++++++++++++++++++ ...PolicyAdminPermissionEvalutatorPlugin.java | 42 +++++++++++++----- ...cePolicyRestPermissionEvaluatorPlugin.java | 1 - 4 files changed, 80 insertions(+), 12 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/authorize/ResourcePolicyServiceImpl.java b/dspace-api/src/main/java/org/dspace/authorize/ResourcePolicyServiceImpl.java index 86998a2196e7..08a8a1463c03 100644 --- a/dspace-api/src/main/java/org/dspace/authorize/ResourcePolicyServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/authorize/ResourcePolicyServiceImpl.java @@ -19,6 +19,7 @@ import org.apache.commons.lang3.ObjectUtils; import org.apache.logging.log4j.Logger; import org.dspace.authorize.dao.ResourcePolicyDAO; +import org.dspace.authorize.service.AuthorizeService; import org.dspace.authorize.service.ResourcePolicyService; import org.dspace.content.DSpaceObject; import org.dspace.content.factory.ContentServiceFactory; @@ -51,6 +52,9 @@ public class ResourcePolicyServiceImpl implements ResourcePolicyService { @Autowired private GroupService groupService; + @Autowired + private AuthorizeService authorizeService; + protected ResourcePolicyServiceImpl() { } @@ -422,6 +426,6 @@ public boolean isMyResourcePolicy(Context context, EPerson eperson, Integer id) } else if (group != null && groupService.isMember(context, eperson, group)) { isMy = true; } - return isMy; + return isMy || authorizeService.isAdmin(context, eperson, resourcePolicy.getdSpaceObject()); } } diff --git a/dspace-api/src/main/java/org/dspace/core/Utils.java b/dspace-api/src/main/java/org/dspace/core/Utils.java index ea9ed57eca04..90df7240503e 100644 --- a/dspace-api/src/main/java/org/dspace/core/Utils.java +++ b/dspace-api/src/main/java/org/dspace/core/Utils.java @@ -506,4 +506,47 @@ public static String interpolateConfigsInString(String string) { ConfigurationService config = DSpaceServicesFactory.getInstance().getConfigurationService(); return StringSubstitutor.replace(string, config.getProperties()); } + + /** + * Get the maximum timestamp that can be stored in a PostgreSQL database with hibernate, + * for our "distant future" access expiry date. + * @return the maximum timestamp that can be stored with Postgres + Hibernate + */ + public static Instant getMaxTimestamp() { + return LocalDateTime.of(294276, 12, 31, 23, 59, 59) + .toInstant(ZoneOffset.UTC); + } + + /** + * Get the minimum timestamp that can be stored in a PostgreSQL database, for date validation or any other + * purpose to ensure we don't try to store a date before the epoch. + * @return the minimum timestamp that can be stored with Postgres + Hibernate + */ + public static Instant getMinTimestamp() { + return LocalDateTime.of(-4713, 11, 12, 0, 0, 0) + .toInstant(ZoneOffset.UTC); + } + + /** + * Checks whether a given string can be converted to a valid {@code int} value. + *

    + * This method returns {@code false} if the input string is {@code null}, empty, + * or contains only whitespace. Otherwise, it attempts to parse the string as an + * integer using {@link Integer#parseInt(String)}. + * + * @param str the string to check for integer convertibility + * @return {@code true} if the string is non-blank and can be parsed as an integer; + * {@code false} otherwise + */ + public static boolean isConvertibleToInt(String str) { + if (StringUtils.isBlank(str)) { + return false; + } + try { + Integer.parseInt(str); + return true; + } catch (NumberFormatException e) { + return false; + } + } } diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/security/ResourcePolicyAdminPermissionEvalutatorPlugin.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/security/ResourcePolicyAdminPermissionEvalutatorPlugin.java index 421d25f9406d..69188b6cca5f 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/security/ResourcePolicyAdminPermissionEvalutatorPlugin.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/security/ResourcePolicyAdminPermissionEvalutatorPlugin.java @@ -9,15 +9,18 @@ import java.io.Serializable; import java.sql.SQLException; +import java.util.UUID; import org.apache.commons.lang3.StringUtils; import org.dspace.app.rest.model.ResourcePolicyRest; import org.dspace.app.rest.utils.ContextUtil; +import org.dspace.app.util.factory.UtilServiceFactory; import org.dspace.authorize.ResourcePolicy; import org.dspace.authorize.service.AuthorizeService; import org.dspace.authorize.service.ResourcePolicyService; import org.dspace.content.DSpaceObject; import org.dspace.core.Context; +import org.dspace.core.Utils; import org.dspace.services.RequestService; import org.dspace.services.model.Request; import org.slf4j.Logger; @@ -38,7 +41,7 @@ public class ResourcePolicyAdminPermissionEvalutatorPlugin extends RestObjectPer private static final Logger log = LoggerFactory.getLogger(ResourcePolicyRestPermissionEvaluatorPlugin.class); - public static final String RESOURCE_POLICY_PATCH = "resourcepolicy"; + public static final String RESOURCE_POLICY_TYPE = "resourcepolicy"; @Autowired AuthorizeService authorizeService; @@ -55,8 +58,9 @@ public boolean hasDSpacePermission(Authentication authentication, Serializable t DSpaceRestPermission restPermission = DSpaceRestPermission.convert(permission); - if (!DSpaceRestPermission.ADMIN.equals(restPermission) - || !StringUtils.equalsIgnoreCase(targetType, RESOURCE_POLICY_PATCH)) { + if (!DSpaceRestPermission.ADMIN.equals(restPermission) && + !DSpaceRestPermission.WRITE.equals(restPermission) || + !StringUtils.equalsIgnoreCase(targetType, RESOURCE_POLICY_TYPE)) { return false; } @@ -64,19 +68,37 @@ public boolean hasDSpacePermission(Authentication authentication, Serializable t Context context = ContextUtil.obtainContext(request.getHttpServletRequest()); try { - int resourcePolicyID = Integer.parseInt(targetId.toString()); - ResourcePolicy resourcePolicy = resourcePolicyService.find(context, resourcePolicyID); - if (resourcePolicy == null) { - throw new ResourceNotFoundException( - ResourcePolicyRest.CATEGORY + "." + ResourcePolicyRest.NAME + - " with id: " + resourcePolicyID + " not found"); + DSpaceObject dso = null; + if (Utils.isConvertibleToInt(targetId.toString())) { + var id = Integer.parseInt(targetId.toString()); + dso = getDSO(context, id); + } else { + var uuid = UUID.fromString(targetId.toString()); + dso = getDSO(context, uuid); } - DSpaceObject dso = resourcePolicy.getdSpaceObject(); return authorizeService.isAdmin(context, dso); + } catch (SQLException e) { log.error(e.getMessage(), e); } return false; } + private DSpaceObject getDSO(Context context, int id) throws SQLException { + ResourcePolicy resourcePolicy = resourcePolicyService.find(context, id); + if (resourcePolicy == null) { + throw new ResourceNotFoundException( + ResourcePolicyRest.CATEGORY + "." + ResourcePolicyRest.NAME + " with id: " + id + " not found"); + } + return resourcePolicy.getdSpaceObject(); + } + + private DSpaceObject getDSO(Context context, UUID uuid) throws SQLException { + DSpaceObject dso = UtilServiceFactory.getInstance().getDSpaceObjectUtils().findDSpaceObject(context, uuid); + if (dso == null) { + throw new ResourceNotFoundException("DSpaceObject with uuid: " + uuid + " not found"); + } + return dso; + } + } diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/security/ResourcePolicyRestPermissionEvaluatorPlugin.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/security/ResourcePolicyRestPermissionEvaluatorPlugin.java index bf7ce3b53f1a..5728fb866729 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/security/ResourcePolicyRestPermissionEvaluatorPlugin.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/security/ResourcePolicyRestPermissionEvaluatorPlugin.java @@ -55,7 +55,6 @@ public boolean hasDSpacePermission(Authentication authentication, Serializable t DSpaceRestPermission restPermission = DSpaceRestPermission.convert(permission); if (!DSpaceRestPermission.READ.equals(restPermission) - && !DSpaceRestPermission.WRITE.equals(restPermission) && !DSpaceRestPermission.DELETE.equals(restPermission) || !StringUtils.equalsIgnoreCase(targetType, ResourcePolicyRest.NAME)) { return false; From a24340a19725610a77b0fba964edccaae02e9852 Mon Sep 17 00:00:00 2001 From: Adamo Date: Fri, 2 May 2025 09:38:28 +0200 Subject: [PATCH 754/979] [DURACOM-318] IT fix (cherry picked from commit accba0738f7684e31af707bf5bd06508e5571621) --- .../dspace/app/rest/ResourcePolicyRestRepositoryIT.java | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/ResourcePolicyRestRepositoryIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/ResourcePolicyRestRepositoryIT.java index 08ce836f3d78..2e5998b999b2 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/ResourcePolicyRestRepositoryIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/ResourcePolicyRestRepositoryIT.java @@ -191,7 +191,7 @@ public void findOneUnAuthenticatedTest() throws Exception { public void findOneNotFoundTest() throws Exception { String authToken = getAuthToken(admin.getEmail(), password); - getClient(authToken).perform(get("/api/authz/resourcepolicies/" + UUID.randomUUID().toString())) + getClient(authToken).perform(get("/api/authz/resourcepolicies/" + UUID.randomUUID())) .andExpect(status().isNotFound()); } @@ -1743,11 +1743,10 @@ public void deletePolicyByCollectionAdminTest() throws Exception { String authcolAdmin2Token = getAuthToken(colAdmin2.getEmail(), password); String authSubmitterToken = getAuthToken(submitter.getEmail(), password); - ResourcePolicy rp = ResourcePolicyBuilder.createResourcePolicy(context) + ResourcePolicy rp = ResourcePolicyBuilder.createResourcePolicy(context, submitter, null) .withDspaceObject(bitstream) .withAction(Constants.READ) .withPolicyType(ResourcePolicy.TYPE_CUSTOM) - .withUser(submitter) .build(); // submitter can't delete own policy @@ -1826,11 +1825,10 @@ public void deletePolicyBySubCommunityAdminTest() throws Exception { context.restoreAuthSystemState(); - ResourcePolicy rp = ResourcePolicyBuilder.createResourcePolicy(context) + ResourcePolicy rp = ResourcePolicyBuilder.createResourcePolicy(context, submitter, null) .withDspaceObject(publication) .withAction(Constants.WRITE) .withPolicyType(ResourcePolicy.TYPE_CUSTOM) - .withUser(submitter) .build(); String adminToken = getAuthToken(admin.getEmail(), password); From 2104d605bd82d1372bb6af468dd8ee3bf210bb77 Mon Sep 17 00:00:00 2001 From: Mykhaylo Boychuk Date: Mon, 3 Feb 2025 19:30:16 +0100 Subject: [PATCH 755/979] [DURACOM-318] improve code (cherry picked from commit 8e0ca2e6f88b0251edf8a840f65135590c65f088) (cherry picked from commit 4270170d40833b7cd4c505a850c47f4bdff55a1c) --- dspace-api/src/main/java/org/dspace/core/Utils.java | 1 + .../ResourcePolicyAdminPermissionEvalutatorPlugin.java | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/dspace-api/src/main/java/org/dspace/core/Utils.java b/dspace-api/src/main/java/org/dspace/core/Utils.java index 90df7240503e..59d37f71cf42 100644 --- a/dspace-api/src/main/java/org/dspace/core/Utils.java +++ b/dspace-api/src/main/java/org/dspace/core/Utils.java @@ -549,4 +549,5 @@ public static boolean isConvertibleToInt(String str) { return false; } } + } diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/security/ResourcePolicyAdminPermissionEvalutatorPlugin.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/security/ResourcePolicyAdminPermissionEvalutatorPlugin.java index 69188b6cca5f..280946e64767 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/security/ResourcePolicyAdminPermissionEvalutatorPlugin.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/security/ResourcePolicyAdminPermissionEvalutatorPlugin.java @@ -11,6 +11,7 @@ import java.sql.SQLException; import java.util.UUID; +import org.apache.commons.lang.math.NumberUtils; import org.apache.commons.lang3.StringUtils; import org.dspace.app.rest.model.ResourcePolicyRest; import org.dspace.app.rest.utils.ContextUtil; @@ -69,7 +70,7 @@ public boolean hasDSpacePermission(Authentication authentication, Serializable t try { DSpaceObject dso = null; - if (Utils.isConvertibleToInt(targetId.toString())) { + if (NumberUtils.isNumber(targetId.toString())) { var id = Integer.parseInt(targetId.toString()); dso = getDSO(context, id); } else { From 03992be08d3756e400a2740597d5f7d8d04cf8ad Mon Sep 17 00:00:00 2001 From: Mykhaylo Boychuk Date: Mon, 3 Feb 2025 22:54:52 +0100 Subject: [PATCH 756/979] [DURACOM-318] remove unused import (cherry picked from commit ed91462ccd7f99f5cf0dc326ed06ab42c13b80e0) (cherry picked from commit 8df4e35e76364bf20dad51073e33cda8f3c3f623) --- .../security/ResourcePolicyAdminPermissionEvalutatorPlugin.java | 1 - 1 file changed, 1 deletion(-) diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/security/ResourcePolicyAdminPermissionEvalutatorPlugin.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/security/ResourcePolicyAdminPermissionEvalutatorPlugin.java index 280946e64767..e544665e434a 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/security/ResourcePolicyAdminPermissionEvalutatorPlugin.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/security/ResourcePolicyAdminPermissionEvalutatorPlugin.java @@ -21,7 +21,6 @@ import org.dspace.authorize.service.ResourcePolicyService; import org.dspace.content.DSpaceObject; import org.dspace.core.Context; -import org.dspace.core.Utils; import org.dspace.services.RequestService; import org.dspace.services.model.Request; import org.slf4j.Logger; From 1732285d59fc143b1a9b8f112ef5292f861016fc Mon Sep 17 00:00:00 2001 From: "max.nuding" Date: Mon, 23 Jun 2025 10:12:56 +0200 Subject: [PATCH 757/979] add import for Intstant --- dspace-api/src/main/java/org/dspace/core/Utils.java | 1 + 1 file changed, 1 insertion(+) diff --git a/dspace-api/src/main/java/org/dspace/core/Utils.java b/dspace-api/src/main/java/org/dspace/core/Utils.java index 59d37f71cf42..df1e0218a1f8 100644 --- a/dspace-api/src/main/java/org/dspace/core/Utils.java +++ b/dspace-api/src/main/java/org/dspace/core/Utils.java @@ -24,6 +24,7 @@ import java.security.NoSuchAlgorithmException; import java.text.ParseException; import java.text.SimpleDateFormat; +import java.time.Instant; import java.util.Arrays; import java.util.Calendar; import java.util.Collections; From e90f792869b20eab9b59d985c4f8be0937c98c35 Mon Sep 17 00:00:00 2001 From: "max.nuding" Date: Mon, 23 Jun 2025 11:36:52 +0200 Subject: [PATCH 758/979] add missing imports --- dspace-api/src/main/java/org/dspace/core/Utils.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/dspace-api/src/main/java/org/dspace/core/Utils.java b/dspace-api/src/main/java/org/dspace/core/Utils.java index df1e0218a1f8..b13eb3ba360c 100644 --- a/dspace-api/src/main/java/org/dspace/core/Utils.java +++ b/dspace-api/src/main/java/org/dspace/core/Utils.java @@ -25,6 +25,8 @@ import java.text.ParseException; import java.text.SimpleDateFormat; import java.time.Instant; +import java.time.LocalDateTime; +import java.time.ZoneOffset; import java.util.Arrays; import java.util.Calendar; import java.util.Collections; From 61c6e59a060c63189974949651861a64279e9ff0 Mon Sep 17 00:00:00 2001 From: "max.nuding" Date: Mon, 23 Jun 2025 11:44:22 +0200 Subject: [PATCH 759/979] remove jakarta import --- .../java/org/dspace/app/rest/ResourcePolicyRestRepositoryIT.java | 1 - 1 file changed, 1 deletion(-) diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/ResourcePolicyRestRepositoryIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/ResourcePolicyRestRepositoryIT.java index 2e5998b999b2..61e86310c340 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/ResourcePolicyRestRepositoryIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/ResourcePolicyRestRepositoryIT.java @@ -35,7 +35,6 @@ import javax.ws.rs.core.MediaType; import com.fasterxml.jackson.databind.ObjectMapper; -import jakarta.ws.rs.core.MediaType; import org.apache.commons.codec.CharEncoding; import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.StringUtils; From 8a84cba371af1370f2b21e53b923733feac88c57 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Mon, 23 Jun 2025 15:13:51 -0500 Subject: [PATCH 760/979] Fix broken ITs by removing unnecessary registrations and managing context permissions better --- .../VersionedHandleIdentifierProviderIT.java | 14 +++++-- ...ntifierProviderWithCanonicalHandlesIT.java | 38 +++++++++---------- 2 files changed, 29 insertions(+), 23 deletions(-) diff --git a/dspace-api/src/test/java/org/dspace/identifier/VersionedHandleIdentifierProviderIT.java b/dspace-api/src/test/java/org/dspace/identifier/VersionedHandleIdentifierProviderIT.java index fd6340fc6381..58ebd7866f82 100644 --- a/dspace-api/src/test/java/org/dspace/identifier/VersionedHandleIdentifierProviderIT.java +++ b/dspace-api/src/test/java/org/dspace/identifier/VersionedHandleIdentifierProviderIT.java @@ -48,15 +48,21 @@ public void setUp() throws Exception { collection = CollectionBuilder.createCollection(context, parentCommunity) .withName("Collection") .build(); + + context.restoreAuthSystemState(); } private void createVersions() throws SQLException, AuthorizeException { + context.turnOffAuthorisationSystem(); + itemV1 = ItemBuilder.createItem(context, collection) .withTitle("First version") .build(); firstHandle = itemV1.getHandle(); itemV2 = VersionBuilder.createVersion(context, itemV1, "Second version").build().getItem(); itemV3 = VersionBuilder.createVersion(context, itemV1, "Third version").build().getItem(); + + context.restoreAuthSystemState(); } @Test @@ -76,8 +82,7 @@ public void testDefaultVersionedHandleProvider() throws Exception { @Test public void testCollectionHandleMetadata() { - registerProvider(VersionedHandleIdentifierProvider.class); - + context.turnOffAuthorisationSystem(); Community testCommunity = CommunityBuilder.createCommunity(context) .withName("Test community") .build(); @@ -85,6 +90,7 @@ public void testCollectionHandleMetadata() { Collection testCollection = CollectionBuilder.createCollection(context, testCommunity) .withName("Test Collection") .build(); + context.restoreAuthSystemState(); List metadata = ContentServiceFactory.getInstance().getDSpaceObjectService(testCollection) .getMetadata(testCollection, "dc", "identifier", "uri", @@ -96,11 +102,11 @@ public void testCollectionHandleMetadata() { @Test public void testCommunityHandleMetadata() { - registerProvider(VersionedHandleIdentifierProvider.class); - + context.turnOffAuthorisationSystem(); Community testCommunity = CommunityBuilder.createCommunity(context) .withName("Test community") .build(); + context.restoreAuthSystemState(); List metadata = ContentServiceFactory.getInstance().getDSpaceObjectService(testCommunity) .getMetadata(testCommunity, "dc", "identifier", "uri", diff --git a/dspace-api/src/test/java/org/dspace/identifier/VersionedHandleIdentifierProviderWithCanonicalHandlesIT.java b/dspace-api/src/test/java/org/dspace/identifier/VersionedHandleIdentifierProviderWithCanonicalHandlesIT.java index b4f2bc9b207f..fde52f42de47 100644 --- a/dspace-api/src/test/java/org/dspace/identifier/VersionedHandleIdentifierProviderWithCanonicalHandlesIT.java +++ b/dspace-api/src/test/java/org/dspace/identifier/VersionedHandleIdentifierProviderWithCanonicalHandlesIT.java @@ -14,7 +14,6 @@ import java.util.ArrayList; import java.util.List; -import org.dspace.AbstractIntegrationTestWithDatabase; import org.dspace.authorize.AuthorizeException; import org.dspace.builder.CollectionBuilder; import org.dspace.builder.CommunityBuilder; @@ -27,10 +26,11 @@ import org.dspace.content.factory.ContentServiceFactory; import org.dspace.kernel.ServiceManager; import org.dspace.services.factory.DSpaceServicesFactory; +import org.junit.After; import org.junit.Before; import org.junit.Test; -public class VersionedHandleIdentifierProviderWithCanonicalHandlesIT extends AbstractIntegrationTestWithDatabase { +public class VersionedHandleIdentifierProviderWithCanonicalHandlesIT extends AbstractIdentifierProviderIT { private ServiceManager serviceManager; private IdentifierServiceImpl identifierService; @@ -62,34 +62,34 @@ public void setUp() throws Exception { .withName("Collection") .build(); - + registerProvider(VersionedHandleIdentifierProviderWithCanonicalHandles.class); } - private void registerProvider(Class type) { - // Register our new provider - List servicesByType = serviceManager.getServicesByType(type); - if (servicesByType.isEmpty()) { - serviceManager.registerServiceClass(type.getName(), type); - } - IdentifierProvider identifierProvider = - (IdentifierProvider) serviceManager.getServiceByName(type.getName(), type); - - // Overwrite the identifier-service's providers with the new one to ensure only this provider is used - identifierService.setProviders(List.of(identifierProvider)); + @After + @Override + public void destroy() throws Exception { + super.destroy(); + // Unregister this non-default provider + unregisterProvider(VersionedHandleIdentifierProviderWithCanonicalHandles.class); + // Re-register the default provider (for later tests) + registerProvider(VersionedHandleIdentifierProvider.class); } private void createVersions() throws SQLException, AuthorizeException { + context.turnOffAuthorisationSystem(); + itemV1 = ItemBuilder.createItem(context, collection) .withTitle("First version") .build(); firstHandle = itemV1.getHandle(); itemV2 = VersionBuilder.createVersion(context, itemV1, "Second version").build().getItem(); itemV3 = VersionBuilder.createVersion(context, itemV1, "Third version").build().getItem(); + + context.restoreAuthSystemState(); } @Test public void testCanonicalVersionedHandleProvider() throws Exception { - registerProvider(VersionedHandleIdentifierProviderWithCanonicalHandles.class); createVersions(); // Confirm the original item only has a version handle @@ -106,8 +106,7 @@ public void testCanonicalVersionedHandleProvider() throws Exception { @Test public void testCollectionHandleMetadata() { - registerProvider(VersionedHandleIdentifierProviderWithCanonicalHandles.class); - + context.turnOffAuthorisationSystem(); Community testCommunity = CommunityBuilder.createCommunity(context) .withName("Test community") .build(); @@ -115,6 +114,7 @@ public void testCollectionHandleMetadata() { Collection testCollection = CollectionBuilder.createCollection(context, testCommunity) .withName("Test Collection") .build(); + context.restoreAuthSystemState(); List metadata = ContentServiceFactory.getInstance().getDSpaceObjectService(testCollection) .getMetadata(testCollection, "dc", "identifier", "uri", @@ -126,11 +126,11 @@ public void testCollectionHandleMetadata() { @Test public void testCommunityHandleMetadata() { - registerProvider(VersionedHandleIdentifierProviderWithCanonicalHandles.class); - + context.turnOffAuthorisationSystem(); Community testCommunity = CommunityBuilder.createCommunity(context) .withName("Test community") .build(); + context.restoreAuthSystemState(); List metadata = ContentServiceFactory.getInstance().getDSpaceObjectService(testCommunity) .getMetadata(testCommunity, "dc", "identifier", "uri", From 01b0f63fff2d2f310e44f7f461e5d020cd6f5a08 Mon Sep 17 00:00:00 2001 From: "max.nuding" Date: Tue, 24 Jun 2025 06:52:17 +0200 Subject: [PATCH 761/979] fix missing imports --- dspace-api/src/main/java/org/dspace/core/Utils.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/dspace-api/src/main/java/org/dspace/core/Utils.java b/dspace-api/src/main/java/org/dspace/core/Utils.java index 59d37f71cf42..2e360e02960b 100644 --- a/dspace-api/src/main/java/org/dspace/core/Utils.java +++ b/dspace-api/src/main/java/org/dspace/core/Utils.java @@ -22,6 +22,10 @@ import java.rmi.dgc.VMID; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; +import java.time.Instant; +import java.time.LocalDateTime +import java.time.Instant; +import java.time.ZoneOffset import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Arrays; From b8923c986fac7cb25066c5685067935b37e8a740 Mon Sep 17 00:00:00 2001 From: "max.nuding" Date: Tue, 24 Jun 2025 07:01:56 +0200 Subject: [PATCH 762/979] fix imports for checkstyle --- .../org/dspace/app/rest/ResourcePolicyRestRepositoryIT.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/ResourcePolicyRestRepositoryIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/ResourcePolicyRestRepositoryIT.java index 61e86310c340..279b3b378fc7 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/ResourcePolicyRestRepositoryIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/ResourcePolicyRestRepositoryIT.java @@ -22,10 +22,8 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; -import java.text.SimpleDateFormat; import java.io.InputStream; -import java.time.LocalDate; -import java.time.format.DateTimeFormatter; +import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Calendar; import java.util.Date; From db6c4362ec867b919a77fc7d090b5d47df56d1d2 Mon Sep 17 00:00:00 2001 From: "max.nuding" Date: Tue, 24 Jun 2025 07:03:38 +0200 Subject: [PATCH 763/979] fix imports for checkstyle --- dspace-api/src/main/java/org/dspace/core/Utils.java | 2 +- .../org/dspace/app/rest/ResourcePolicyRestRepositoryIT.java | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/core/Utils.java b/dspace-api/src/main/java/org/dspace/core/Utils.java index 2e360e02960b..980c1f471688 100644 --- a/dspace-api/src/main/java/org/dspace/core/Utils.java +++ b/dspace-api/src/main/java/org/dspace/core/Utils.java @@ -25,7 +25,7 @@ import java.time.Instant; import java.time.LocalDateTime import java.time.Instant; -import java.time.ZoneOffset +import java.time.ZoneOffset; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Arrays; diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/ResourcePolicyRestRepositoryIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/ResourcePolicyRestRepositoryIT.java index e065bf27c81d..fdf98ab940d0 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/ResourcePolicyRestRepositoryIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/ResourcePolicyRestRepositoryIT.java @@ -22,10 +22,8 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; -import java.text.SimpleDateFormat; import java.io.InputStream; -import java.time.LocalDate; -import java.time.format.DateTimeFormatter; +import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Calendar; import java.util.Date; From 75aecc1d759674c867f1fd2f77d56866b2e76356 Mon Sep 17 00:00:00 2001 From: "max.nuding" Date: Tue, 24 Jun 2025 07:06:59 +0200 Subject: [PATCH 764/979] fix another missing semicolon --- dspace-api/src/main/java/org/dspace/core/Utils.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dspace-api/src/main/java/org/dspace/core/Utils.java b/dspace-api/src/main/java/org/dspace/core/Utils.java index 980c1f471688..d35266ac6c3b 100644 --- a/dspace-api/src/main/java/org/dspace/core/Utils.java +++ b/dspace-api/src/main/java/org/dspace/core/Utils.java @@ -23,7 +23,7 @@ import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.time.Instant; -import java.time.LocalDateTime +import java.time.LocalDateTime; import java.time.Instant; import java.time.ZoneOffset; import java.text.ParseException; From d0ca7bd8b009b6b334045ce4b57755b74a76cd67 Mon Sep 17 00:00:00 2001 From: "max.nuding" Date: Tue, 24 Jun 2025 07:19:39 +0200 Subject: [PATCH 765/979] fix import ordering --- dspace-api/src/main/java/org/dspace/core/Utils.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/core/Utils.java b/dspace-api/src/main/java/org/dspace/core/Utils.java index d35266ac6c3b..b13eb3ba360c 100644 --- a/dspace-api/src/main/java/org/dspace/core/Utils.java +++ b/dspace-api/src/main/java/org/dspace/core/Utils.java @@ -22,12 +22,11 @@ import java.rmi.dgc.VMID; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; +import java.text.ParseException; +import java.text.SimpleDateFormat; import java.time.Instant; import java.time.LocalDateTime; -import java.time.Instant; import java.time.ZoneOffset; -import java.text.ParseException; -import java.text.SimpleDateFormat; import java.util.Arrays; import java.util.Calendar; import java.util.Collections; From 230bf80b5fcde55a2abdf916c282c3439e49654b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 24 Jun 2025 20:04:48 +0000 Subject: [PATCH 766/979] Bump log4j.version from 2.24.3 to 2.25.0 Bumps `log4j.version` from 2.24.3 to 2.25.0. Updates `org.apache.logging.log4j:log4j-api` from 2.24.3 to 2.25.0 Updates `org.apache.logging.log4j:log4j-core` from 2.24.3 to 2.25.0 Updates `org.apache.logging.log4j:log4j-1.2-api` from 2.24.3 to 2.25.0 --- updated-dependencies: - dependency-name: org.apache.logging.log4j:log4j-api dependency-version: 2.25.0 dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: org.apache.logging.log4j:log4j-core dependency-version: 2.25.0 dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: org.apache.logging.log4j:log4j-1.2-api dependency-version: 2.25.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index e819160df561..b6450fe077b0 100644 --- a/pom.xml +++ b/pom.xml @@ -38,7 +38,7 @@ 1.1.1 9.4.57.v20241219 - 2.24.3 + 2.25.0 2.0.34 1.19.0 1.7.36 From b594ebbf9e6ebb838dce7a2cc5b53b8c769e98d6 Mon Sep 17 00:00:00 2001 From: Alan Orth Date: Tue, 10 Jun 2025 14:32:51 +0300 Subject: [PATCH 767/979] dspace-api: improve date parsing for Solr sort Re-use DSpace date parsing from o.d.util.MultiFormatDateParser for more robust date support when creating of Solr browse/sort indexes. --- .../java/org/dspace/sort/OrderFormatDate.java | 29 +++++++++---------- 1 file changed, 13 insertions(+), 16 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/sort/OrderFormatDate.java b/dspace-api/src/main/java/org/dspace/sort/OrderFormatDate.java index f56a97776f64..a8a6e5b98a00 100644 --- a/dspace-api/src/main/java/org/dspace/sort/OrderFormatDate.java +++ b/dspace-api/src/main/java/org/dspace/sort/OrderFormatDate.java @@ -7,31 +7,28 @@ */ package org.dspace.sort; +import java.util.Date; + +import org.dspace.util.MultiFormatDateParser; + /** - * Standard date ordering delegate implementation. The only "special" need is - * to treat dates with less than 4-digit year. + * Standard date ordering delegate implementation using date format + * parsing from o.d.u.MultiFormatDateParser. * * @author Andrea Bollini + * @author Alan Orth */ public class OrderFormatDate implements OrderFormatDelegate { @Override public String makeSortString(String value, String language) { - int padding = 0; - int endYearIdx = value.indexOf('-'); - - if (endYearIdx >= 0 && endYearIdx < 4) { - padding = 4 - endYearIdx; - } else if (value.length() < 4) { - padding = 4 - value.length(); - } + Date result = MultiFormatDateParser.parse(value); - if (padding > 0) { - // padding the value from left with 0 so that 87 -> 0087, 687-11-24 - // -> 0687-11-24 - return String.format("%1$0" + padding + "d", 0) - + value; + // If parsing was successful we return the value as an ISO instant, + // otherwise we return null so Solr does not index this date value. + if (result != null) { + return result.toInstant().toString(); } else { - return value; + return null; } } } From 8839eefbe7c4d67179b9fb7ea4912e4a9abcffb2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 24 Jun 2025 20:09:59 +0000 Subject: [PATCH 768/979] Bump org.postgresql:postgresql from 42.7.6 to 42.7.7 Bumps [org.postgresql:postgresql](https://github.com/pgjdbc/pgjdbc) from 42.7.6 to 42.7.7. - [Release notes](https://github.com/pgjdbc/pgjdbc/releases) - [Changelog](https://github.com/pgjdbc/pgjdbc/blob/master/CHANGELOG.md) - [Commits](https://github.com/pgjdbc/pgjdbc/compare/REL42.7.6...REL42.7.7) --- updated-dependencies: - dependency-name: org.postgresql:postgresql dependency-version: 42.7.7 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index e819160df561..b13dd09b3f76 100644 --- a/pom.xml +++ b/pom.xml @@ -24,7 +24,7 @@ 5.7.14 5.6.15.Final 6.2.5.Final - 42.7.6 + 42.7.7 8.11.4 3.10.8 From 312416a716ee622efae75586112bac9a956fae08 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 24 Jun 2025 20:24:13 +0000 Subject: [PATCH 769/979] Bump jersey.version from 2.46 to 2.47 Bumps `jersey.version` from 2.46 to 2.47. Updates `org.glassfish.jersey.core:jersey-client` from 2.46 to 2.47 Updates `org.glassfish.jersey.inject:jersey-hk2` from 2.46 to 2.47 Updates `org.glassfish.jersey.core:jersey-server` from 2.46 to 2.47 Updates `org.glassfish.jersey.containers:jersey-container-servlet` from 2.46 to 2.47 Updates `org.glassfish.jersey.media:jersey-media-json-jackson` from 2.46 to 2.47 Updates `org.glassfish.jersey.media:jersey-media-jaxb` from 2.46 to 2.47 Updates `org.glassfish.jersey.ext:jersey-spring5` from 2.46 to 2.47 --- updated-dependencies: - dependency-name: org.glassfish.jersey.core:jersey-client dependency-version: '2.47' dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: org.glassfish.jersey.inject:jersey-hk2 dependency-version: '2.47' dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: org.glassfish.jersey.core:jersey-server dependency-version: '2.47' dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: org.glassfish.jersey.containers:jersey-container-servlet dependency-version: '2.47' dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: org.glassfish.jersey.media:jersey-media-json-jackson dependency-version: '2.47' dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: org.glassfish.jersey.media:jersey-media-jaxb dependency-version: '2.47' dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: org.glassfish.jersey.ext:jersey-spring5 dependency-version: '2.47' dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index e819160df561..0baab9fdf1f5 100644 --- a/pom.xml +++ b/pom.xml @@ -58,7 +58,7 @@ https://jena.apache.org/documentation/migrate_jena2_jena3.html --> 2.13.0 - 2.46 + 2.47 UTF-8 From eb4e741bc76b7675b0399b0fae44d4df26e95589 Mon Sep 17 00:00:00 2001 From: "max.nuding" Date: Wed, 25 Jun 2025 10:36:10 +0200 Subject: [PATCH 770/979] remove unnecessary code --- .../src/main/java/org/dspace/core/Utils.java | 46 ------------------- 1 file changed, 46 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/core/Utils.java b/dspace-api/src/main/java/org/dspace/core/Utils.java index b13eb3ba360c..a1294c3317ce 100644 --- a/dspace-api/src/main/java/org/dspace/core/Utils.java +++ b/dspace-api/src/main/java/org/dspace/core/Utils.java @@ -24,9 +24,6 @@ import java.security.NoSuchAlgorithmException; import java.text.ParseException; import java.text.SimpleDateFormat; -import java.time.Instant; -import java.time.LocalDateTime; -import java.time.ZoneOffset; import java.util.Arrays; import java.util.Calendar; import java.util.Collections; @@ -510,47 +507,4 @@ public static String interpolateConfigsInString(String string) { return StringSubstitutor.replace(string, config.getProperties()); } - /** - * Get the maximum timestamp that can be stored in a PostgreSQL database with hibernate, - * for our "distant future" access expiry date. - * @return the maximum timestamp that can be stored with Postgres + Hibernate - */ - public static Instant getMaxTimestamp() { - return LocalDateTime.of(294276, 12, 31, 23, 59, 59) - .toInstant(ZoneOffset.UTC); - } - - /** - * Get the minimum timestamp that can be stored in a PostgreSQL database, for date validation or any other - * purpose to ensure we don't try to store a date before the epoch. - * @return the minimum timestamp that can be stored with Postgres + Hibernate - */ - public static Instant getMinTimestamp() { - return LocalDateTime.of(-4713, 11, 12, 0, 0, 0) - .toInstant(ZoneOffset.UTC); - } - - /** - * Checks whether a given string can be converted to a valid {@code int} value. - *

    - * This method returns {@code false} if the input string is {@code null}, empty, - * or contains only whitespace. Otherwise, it attempts to parse the string as an - * integer using {@link Integer#parseInt(String)}. - * - * @param str the string to check for integer convertibility - * @return {@code true} if the string is non-blank and can be parsed as an integer; - * {@code false} otherwise - */ - public static boolean isConvertibleToInt(String str) { - if (StringUtils.isBlank(str)) { - return false; - } - try { - Integer.parseInt(str); - return true; - } catch (NumberFormatException e) { - return false; - } - } - } From e3b917948218754ef3a02d370c4efe0dc708b849 Mon Sep 17 00:00:00 2001 From: "max.nuding" Date: Wed, 25 Jun 2025 10:36:10 +0200 Subject: [PATCH 771/979] remove unnecessary code --- .../src/main/java/org/dspace/core/Utils.java | 46 ------------------- 1 file changed, 46 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/core/Utils.java b/dspace-api/src/main/java/org/dspace/core/Utils.java index b13eb3ba360c..a1294c3317ce 100644 --- a/dspace-api/src/main/java/org/dspace/core/Utils.java +++ b/dspace-api/src/main/java/org/dspace/core/Utils.java @@ -24,9 +24,6 @@ import java.security.NoSuchAlgorithmException; import java.text.ParseException; import java.text.SimpleDateFormat; -import java.time.Instant; -import java.time.LocalDateTime; -import java.time.ZoneOffset; import java.util.Arrays; import java.util.Calendar; import java.util.Collections; @@ -510,47 +507,4 @@ public static String interpolateConfigsInString(String string) { return StringSubstitutor.replace(string, config.getProperties()); } - /** - * Get the maximum timestamp that can be stored in a PostgreSQL database with hibernate, - * for our "distant future" access expiry date. - * @return the maximum timestamp that can be stored with Postgres + Hibernate - */ - public static Instant getMaxTimestamp() { - return LocalDateTime.of(294276, 12, 31, 23, 59, 59) - .toInstant(ZoneOffset.UTC); - } - - /** - * Get the minimum timestamp that can be stored in a PostgreSQL database, for date validation or any other - * purpose to ensure we don't try to store a date before the epoch. - * @return the minimum timestamp that can be stored with Postgres + Hibernate - */ - public static Instant getMinTimestamp() { - return LocalDateTime.of(-4713, 11, 12, 0, 0, 0) - .toInstant(ZoneOffset.UTC); - } - - /** - * Checks whether a given string can be converted to a valid {@code int} value. - *

    - * This method returns {@code false} if the input string is {@code null}, empty, - * or contains only whitespace. Otherwise, it attempts to parse the string as an - * integer using {@link Integer#parseInt(String)}. - * - * @param str the string to check for integer convertibility - * @return {@code true} if the string is non-blank and can be parsed as an integer; - * {@code false} otherwise - */ - public static boolean isConvertibleToInt(String str) { - if (StringUtils.isBlank(str)) { - return false; - } - try { - Integer.parseInt(str); - return true; - } catch (NumberFormatException e) { - return false; - } - } - } From 8e5bc3c9622999cac18e0871e248554a90e01c5b Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Wed, 25 Jun 2025 16:40:11 -0500 Subject: [PATCH 772/979] Update deploy demo.dspace.org branch to 9.x to ensure older branches never trigger a redeploy. --- .github/workflows/reusable-docker-build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/reusable-docker-build.yml b/.github/workflows/reusable-docker-build.yml index 36907459747b..0c3261da95da 100644 --- a/.github/workflows/reusable-docker-build.yml +++ b/.github/workflows/reusable-docker-build.yml @@ -72,7 +72,7 @@ env: REDEPLOY_SANDBOX_URL: ${{ secrets.REDEPLOY_SANDBOX_URL }} REDEPLOY_DEMO_URL: ${{ secrets.REDEPLOY_DEMO_URL }} # Current DSpace branches (and architecture) which are deployed to demo.dspace.org & sandbox.dspace.org respectively - DEPLOY_DEMO_BRANCH: 'dspace-8_x' + DEPLOY_DEMO_BRANCH: 'dspace-9_x' DEPLOY_SANDBOX_BRANCH: 'main' DEPLOY_ARCH: 'linux/amd64' # Registry used during building of Docker images. (All images are later copied to docker.io registry) From 57a1de5ecdb1a1bec180bf73a05839b8e8b1340a Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Wed, 25 Jun 2025 16:41:48 -0500 Subject: [PATCH 773/979] Update deploy demo.dspace.org branch to 9.x to ensure older branches never trigger a redeploy. --- .github/workflows/reusable-docker-build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/reusable-docker-build.yml b/.github/workflows/reusable-docker-build.yml index aec03603cef0..528f5779ca96 100644 --- a/.github/workflows/reusable-docker-build.yml +++ b/.github/workflows/reusable-docker-build.yml @@ -73,7 +73,7 @@ env: REDEPLOY_DEMO_URL: ${{ secrets.REDEPLOY_DEMO_URL }} # Current DSpace maintenance branch (and architecture) which is deployed to demo.dspace.org / sandbox.dspace.org # (NOTE: No deployment branch specified for sandbox.dspace.org as it uses the default_branch) - DEPLOY_DEMO_BRANCH: 'dspace-8_x' + DEPLOY_DEMO_BRANCH: 'dspace-9_x' DEPLOY_SANDBOX_BRANCH: 'main' DEPLOY_ARCH: 'linux/amd64' # Registry used during building of Docker images. (All images are later copied to docker.io registry) From 47922396d33ccc95262c71fe872c3f7abbcf0dc3 Mon Sep 17 00:00:00 2001 From: nwoodward Date: Thu, 26 Jun 2025 11:16:32 -0500 Subject: [PATCH 774/979] dspace-api: improve date parsing for Solr sort --- .../java/org/dspace/sort/OrderFormatDate.java | 29 +++++++++---------- 1 file changed, 13 insertions(+), 16 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/sort/OrderFormatDate.java b/dspace-api/src/main/java/org/dspace/sort/OrderFormatDate.java index f56a97776f64..a8a6e5b98a00 100644 --- a/dspace-api/src/main/java/org/dspace/sort/OrderFormatDate.java +++ b/dspace-api/src/main/java/org/dspace/sort/OrderFormatDate.java @@ -7,31 +7,28 @@ */ package org.dspace.sort; +import java.util.Date; + +import org.dspace.util.MultiFormatDateParser; + /** - * Standard date ordering delegate implementation. The only "special" need is - * to treat dates with less than 4-digit year. + * Standard date ordering delegate implementation using date format + * parsing from o.d.u.MultiFormatDateParser. * * @author Andrea Bollini + * @author Alan Orth */ public class OrderFormatDate implements OrderFormatDelegate { @Override public String makeSortString(String value, String language) { - int padding = 0; - int endYearIdx = value.indexOf('-'); - - if (endYearIdx >= 0 && endYearIdx < 4) { - padding = 4 - endYearIdx; - } else if (value.length() < 4) { - padding = 4 - value.length(); - } + Date result = MultiFormatDateParser.parse(value); - if (padding > 0) { - // padding the value from left with 0 so that 87 -> 0087, 687-11-24 - // -> 0687-11-24 - return String.format("%1$0" + padding + "d", 0) - + value; + // If parsing was successful we return the value as an ISO instant, + // otherwise we return null so Solr does not index this date value. + if (result != null) { + return result.toInstant().toString(); } else { - return value; + return null; } } } From d0b5911cf644cb27b33887e7e69d71696cf18ce6 Mon Sep 17 00:00:00 2001 From: "Mark H. Wood" Date: Wed, 11 Jun 2025 15:43:10 -0400 Subject: [PATCH 775/979] Make POI record buffer size adjustable. --- .../mediafilter/TikaTextExtractionFilter.java | 18 ++++++++++++++---- dspace/config/dspace.cfg | 7 +++++++ 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/app/mediafilter/TikaTextExtractionFilter.java b/dspace-api/src/main/java/org/dspace/app/mediafilter/TikaTextExtractionFilter.java index b7a6063165b7..5728f4f42f48 100644 --- a/dspace-api/src/main/java/org/dspace/app/mediafilter/TikaTextExtractionFilter.java +++ b/dspace-api/src/main/java/org/dspace/app/mediafilter/TikaTextExtractionFilter.java @@ -18,6 +18,7 @@ import org.apache.commons.lang.StringUtils; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import org.apache.poi.util.IOUtils; import org.apache.tika.Tika; import org.apache.tika.exception.TikaException; import org.apache.tika.metadata.Metadata; @@ -37,6 +38,8 @@ public class TikaTextExtractionFilter extends MediaFilter { private final static Logger log = LogManager.getLogger(); + private static final int DEFAULT_MAX_CHARS = 100_000; + private static final int DEFAULT_MAX_ARRAY = 100_000_000; @Override public String getFilteredName(String oldFilename) { @@ -70,9 +73,12 @@ public InputStream getDestinationStream(Item currentItem, InputStream source, bo } // Not using temporary file. We'll use Tika's default in-memory parsing. - // Get maximum characters to extract. Default is 100,000 chars, which is also Tika's default setting. String extractedText; - int maxChars = configurationService.getIntProperty("textextractor.max-chars", 100000); + // Get maximum characters to extract. Default is 100,000 chars, which is also Tika's default setting. + int maxChars = configurationService.getIntProperty("textextractor.max-chars", DEFAULT_MAX_CHARS); + // Get maximum size of structure that Tika will try to buffer. + int maxArray = configurationService.getIntProperty("textextractor.max-array", DEFAULT_MAX_ARRAY); + IOUtils.setByteArrayMaxOverride(maxArray); try { // Use Tika to extract text from input. Tika will automatically detect the file type. Tika tika = new Tika(); @@ -80,13 +86,13 @@ public InputStream getDestinationStream(Item currentItem, InputStream source, bo extractedText = tika.parseToString(source); } catch (IOException e) { System.err.format("Unable to extract text from bitstream in Item %s%n", currentItem.getID().toString()); - e.printStackTrace(); + e.printStackTrace(System.err); log.error("Unable to extract text from bitstream in Item {}", currentItem.getID().toString(), e); throw e; } catch (OutOfMemoryError oe) { System.err.format("OutOfMemoryError occurred when extracting text from bitstream in Item %s. " + "You may wish to enable 'textextractor.use-temp-file'.%n", currentItem.getID().toString()); - oe.printStackTrace(); + oe.printStackTrace(System.err); log.error("OutOfMemoryError occurred when extracting text from bitstream in Item {}. " + "You may wish to enable 'textextractor.use-temp-file'.", currentItem.getID().toString(), oe); throw oe; @@ -167,6 +173,10 @@ public void ignorableWhitespace(char[] ch, int start, int length) throws SAXExce } }); + ConfigurationService configurationService = DSpaceServicesFactory.getInstance().getConfigurationService(); + int maxArray = configurationService.getIntProperty("textextractor.max-array", DEFAULT_MAX_ARRAY); + IOUtils.setByteArrayMaxOverride(maxArray); + AutoDetectParser parser = new AutoDetectParser(); Metadata metadata = new Metadata(); // parse our source InputStream using the above custom handler diff --git a/dspace/config/dspace.cfg b/dspace/config/dspace.cfg index 4621ca51dc13..c0600dbcebde 100644 --- a/dspace/config/dspace.cfg +++ b/dspace/config/dspace.cfg @@ -523,6 +523,13 @@ filter.org.dspace.app.mediafilter.PDFBoxThumbnail.inputFormats = Adobe PDF # text ("filter-media -f" ) and then reindex your site ("index-discovery -b"). #textextractor.use-temp-file = false +# Maximum size of a record buffer for text extraction. Set this if you are +# seeing RecordFormatException calling out excessive array length from +# 'dspace filter-media'. It is likely that you will need to increase the +# size of the Java heap if you greatly increase this value -- see JAVA_OPTS +# in 'bin/dspace' or 'bin/dspace/bat'. +#textextractor.max-array = 1000000 + # Custom settigns for ImageMagick Thumbnail Filters # ImageMagick and GhostScript must be installed on the server, set the path to ImageMagick and GhostScript executable # http://www.imagemagick.org/ From 172783691b29ecd21bb75fb5ddbda80ab0b9f77d Mon Sep 17 00:00:00 2001 From: abhinav Date: Wed, 25 Jun 2025 16:01:22 +0200 Subject: [PATCH 776/979] fix metadata getting cleared on patch request with invalid field (cherry picked from commit e559af1841ba52890279fdc5efdcc441e7f9eb79) --- .../DSpaceObjectMetadataReplaceOperation.java | 3 +-- .../org/dspace/app/rest/PatchMetadataIT.java | 21 +++++++++++++++++++ 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/operation/DSpaceObjectMetadataReplaceOperation.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/operation/DSpaceObjectMetadataReplaceOperation.java index 1cf15684587b..a3de8d3c9ea6 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/operation/DSpaceObjectMetadataReplaceOperation.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/operation/DSpaceObjectMetadataReplaceOperation.java @@ -80,8 +80,7 @@ private void replace(Context context, DSpaceObject dso, DSpaceObjectService dsoS MetadataValueRest metadataValue, String index, String propertyOfMd, String valueMdProperty) { // replace entire set of metadata if (metadataField == null) { - this.replaceAllMetadata(context, dso, dsoService); - return; + throw new UnprocessableEntityException("Metadata field does not exist"); } // replace all metadata for existing key diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/PatchMetadataIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/PatchMetadataIT.java index 840792f31b92..95ebb480bcf1 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/PatchMetadataIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/PatchMetadataIT.java @@ -1426,6 +1426,27 @@ public void moveMetadataAuthorFourToOneMultiOpTest() throws Exception { moveMetadataAuthorTest(moves, expectedOrder); } + @Test + public void replaceInvalidMetadataShouldFailTest() throws Exception { + initSimplePublicationItem(); + assertEquals(12, publicationItem.getMetadata().size()); + + String patchBody = getPatchContent(List.of( + new ReplaceOperation("/metadata/dc.contributor.invalid/0", "some value") + )); + String token = getAuthToken(admin.getEmail(), password); + getClient(token).perform(patch("/api/core/items/" + publicationItem.getID()) + .content(patchBody) + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isUnprocessableEntity()); + + publicationItem = context.reloadEntity(publicationItem); + + assertEquals(12, publicationItem.getMetadata().size()); + assertEquals(0, + itemService.getMetadata(publicationItem, "dc", "contributor", "invalid", Item.ANY, false).size()); + } + /** * This method moves an author (dc.contributor.author) within a workspace publication's "traditionalpageone" * section from position "from" to "path" using a PATCH request and verifies the order of the authors within the From 934f73f412c6deb3e3c17e9c97d96493f4aa4029 Mon Sep 17 00:00:00 2001 From: abhinav Date: Wed, 25 Jun 2025 16:19:56 +0200 Subject: [PATCH 777/979] Update PatchMetadataIT (cherry picked from commit 27d59085dbcdc702bf779a309b4ed11148ebb5dc) --- .../src/test/java/org/dspace/app/rest/PatchMetadataIT.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/PatchMetadataIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/PatchMetadataIT.java index 95ebb480bcf1..368128fd6e4c 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/PatchMetadataIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/PatchMetadataIT.java @@ -1429,7 +1429,7 @@ public void moveMetadataAuthorFourToOneMultiOpTest() throws Exception { @Test public void replaceInvalidMetadataShouldFailTest() throws Exception { initSimplePublicationItem(); - assertEquals(12, publicationItem.getMetadata().size()); + assertEquals(11, publicationItem.getMetadata().size()); String patchBody = getPatchContent(List.of( new ReplaceOperation("/metadata/dc.contributor.invalid/0", "some value") @@ -1442,7 +1442,7 @@ public void replaceInvalidMetadataShouldFailTest() throws Exception { publicationItem = context.reloadEntity(publicationItem); - assertEquals(12, publicationItem.getMetadata().size()); + assertEquals(11, publicationItem.getMetadata().size()); assertEquals(0, itemService.getMetadata(publicationItem, "dc", "contributor", "invalid", Item.ANY, false).size()); } From 130442746a96819fc8ca2eac9aad7a1f541bc885 Mon Sep 17 00:00:00 2001 From: abhinav Date: Thu, 26 Jun 2025 09:25:40 +0200 Subject: [PATCH 778/979] move the exception to patchUtils (cherry picked from commit a2dc6fbdf89e15db0d51d261dd1708e00a526d27) --- .../operation/DSpaceObjectMetadataPatchUtils.java | 15 +++++++++++++-- .../DSpaceObjectMetadataReplaceOperation.java | 3 ++- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/operation/DSpaceObjectMetadataPatchUtils.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/operation/DSpaceObjectMetadataPatchUtils.java index 954cc844f237..5bbd5c013c9e 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/operation/DSpaceObjectMetadataPatchUtils.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/operation/DSpaceObjectMetadataPatchUtils.java @@ -14,6 +14,7 @@ import com.fasterxml.jackson.databind.ObjectMapper; import org.apache.commons.lang3.StringUtils; import org.dspace.app.rest.exception.DSpaceBadRequestException; +import org.dspace.app.rest.exception.UnprocessableEntityException; import org.dspace.app.rest.model.MetadataValueRest; import org.dspace.app.rest.model.patch.JsonValueEvaluator; import org.dspace.app.rest.model.patch.Operation; @@ -135,10 +136,20 @@ protected String extractNewValueOfMd(Operation operation) { * @param context Context the retrieve metadataField from service with string * @param operation Operation of the patch * @return The metadataField corresponding to the md element string of the operation + * Null if no metadata field is passed in the operation + * @throws UnprocessableEntityException if an invalid metadata field is passed in the operation */ - protected MetadataField getMetadataField(Context context, Operation operation) throws SQLException { + protected MetadataField getMetadataField(Context context, Operation operation) + throws SQLException, UnprocessableEntityException { String mdElement = this.extractMdFieldStringFromOperation(operation); - return metadataFieldService.findByString(context, mdElement, '.'); + if (StringUtils.isBlank(mdElement)) { + return null; + } + MetadataField metadataField = metadataFieldService.findByString(context, mdElement, '.'); + if (metadataField == null) { + throw new UnprocessableEntityException("Metadata field does not exist"); + } + return metadataField; } /** diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/operation/DSpaceObjectMetadataReplaceOperation.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/operation/DSpaceObjectMetadataReplaceOperation.java index a3de8d3c9ea6..1cf15684587b 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/operation/DSpaceObjectMetadataReplaceOperation.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/operation/DSpaceObjectMetadataReplaceOperation.java @@ -80,7 +80,8 @@ private void replace(Context context, DSpaceObject dso, DSpaceObjectService dsoS MetadataValueRest metadataValue, String index, String propertyOfMd, String valueMdProperty) { // replace entire set of metadata if (metadataField == null) { - throw new UnprocessableEntityException("Metadata field does not exist"); + this.replaceAllMetadata(context, dso, dsoService); + return; } // replace all metadata for existing key From 1538920276db3e78535a2e7f097f9a0455f321fe Mon Sep 17 00:00:00 2001 From: abhinav Date: Wed, 25 Jun 2025 16:01:22 +0200 Subject: [PATCH 779/979] fix metadata getting cleared on patch request with invalid field (cherry picked from commit e559af1841ba52890279fdc5efdcc441e7f9eb79) --- .../DSpaceObjectMetadataReplaceOperation.java | 3 +-- .../org/dspace/app/rest/PatchMetadataIT.java | 21 +++++++++++++++++++ 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/operation/DSpaceObjectMetadataReplaceOperation.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/operation/DSpaceObjectMetadataReplaceOperation.java index 1cf15684587b..a3de8d3c9ea6 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/operation/DSpaceObjectMetadataReplaceOperation.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/operation/DSpaceObjectMetadataReplaceOperation.java @@ -80,8 +80,7 @@ private void replace(Context context, DSpaceObject dso, DSpaceObjectService dsoS MetadataValueRest metadataValue, String index, String propertyOfMd, String valueMdProperty) { // replace entire set of metadata if (metadataField == null) { - this.replaceAllMetadata(context, dso, dsoService); - return; + throw new UnprocessableEntityException("Metadata field does not exist"); } // replace all metadata for existing key diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/PatchMetadataIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/PatchMetadataIT.java index 9b9db41a7c92..557401ac1aa3 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/PatchMetadataIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/PatchMetadataIT.java @@ -1426,6 +1426,27 @@ public void moveMetadataAuthorFourToOneMultiOpTest() throws Exception { moveMetadataAuthorTest(moves, expectedOrder); } + @Test + public void replaceInvalidMetadataShouldFailTest() throws Exception { + initSimplePublicationItem(); + assertEquals(12, publicationItem.getMetadata().size()); + + String patchBody = getPatchContent(List.of( + new ReplaceOperation("/metadata/dc.contributor.invalid/0", "some value") + )); + String token = getAuthToken(admin.getEmail(), password); + getClient(token).perform(patch("/api/core/items/" + publicationItem.getID()) + .content(patchBody) + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isUnprocessableEntity()); + + publicationItem = context.reloadEntity(publicationItem); + + assertEquals(12, publicationItem.getMetadata().size()); + assertEquals(0, + itemService.getMetadata(publicationItem, "dc", "contributor", "invalid", Item.ANY, false).size()); + } + /** * This method moves an author (dc.contributor.author) within a workspace publication's "traditionalpageone" * section from position "from" to "path" using a PATCH request and verifies the order of the authors within the From a60f219353619e7a7581198d84c69fe85a942546 Mon Sep 17 00:00:00 2001 From: abhinav Date: Wed, 25 Jun 2025 16:19:56 +0200 Subject: [PATCH 780/979] Update PatchMetadataIT (cherry picked from commit 27d59085dbcdc702bf779a309b4ed11148ebb5dc) --- .../src/test/java/org/dspace/app/rest/PatchMetadataIT.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/PatchMetadataIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/PatchMetadataIT.java index 557401ac1aa3..26e3f209050b 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/PatchMetadataIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/PatchMetadataIT.java @@ -1429,7 +1429,7 @@ public void moveMetadataAuthorFourToOneMultiOpTest() throws Exception { @Test public void replaceInvalidMetadataShouldFailTest() throws Exception { initSimplePublicationItem(); - assertEquals(12, publicationItem.getMetadata().size()); + assertEquals(11, publicationItem.getMetadata().size()); String patchBody = getPatchContent(List.of( new ReplaceOperation("/metadata/dc.contributor.invalid/0", "some value") @@ -1442,7 +1442,7 @@ public void replaceInvalidMetadataShouldFailTest() throws Exception { publicationItem = context.reloadEntity(publicationItem); - assertEquals(12, publicationItem.getMetadata().size()); + assertEquals(11, publicationItem.getMetadata().size()); assertEquals(0, itemService.getMetadata(publicationItem, "dc", "contributor", "invalid", Item.ANY, false).size()); } From d91dc5c691ff14212ef9497f4c1c8ffd40d02706 Mon Sep 17 00:00:00 2001 From: abhinav Date: Thu, 26 Jun 2025 09:25:40 +0200 Subject: [PATCH 781/979] move the exception to patchUtils (cherry picked from commit a2dc6fbdf89e15db0d51d261dd1708e00a526d27) --- .../operation/DSpaceObjectMetadataPatchUtils.java | 15 +++++++++++++-- .../DSpaceObjectMetadataReplaceOperation.java | 3 ++- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/operation/DSpaceObjectMetadataPatchUtils.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/operation/DSpaceObjectMetadataPatchUtils.java index 954cc844f237..5bbd5c013c9e 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/operation/DSpaceObjectMetadataPatchUtils.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/operation/DSpaceObjectMetadataPatchUtils.java @@ -14,6 +14,7 @@ import com.fasterxml.jackson.databind.ObjectMapper; import org.apache.commons.lang3.StringUtils; import org.dspace.app.rest.exception.DSpaceBadRequestException; +import org.dspace.app.rest.exception.UnprocessableEntityException; import org.dspace.app.rest.model.MetadataValueRest; import org.dspace.app.rest.model.patch.JsonValueEvaluator; import org.dspace.app.rest.model.patch.Operation; @@ -135,10 +136,20 @@ protected String extractNewValueOfMd(Operation operation) { * @param context Context the retrieve metadataField from service with string * @param operation Operation of the patch * @return The metadataField corresponding to the md element string of the operation + * Null if no metadata field is passed in the operation + * @throws UnprocessableEntityException if an invalid metadata field is passed in the operation */ - protected MetadataField getMetadataField(Context context, Operation operation) throws SQLException { + protected MetadataField getMetadataField(Context context, Operation operation) + throws SQLException, UnprocessableEntityException { String mdElement = this.extractMdFieldStringFromOperation(operation); - return metadataFieldService.findByString(context, mdElement, '.'); + if (StringUtils.isBlank(mdElement)) { + return null; + } + MetadataField metadataField = metadataFieldService.findByString(context, mdElement, '.'); + if (metadataField == null) { + throw new UnprocessableEntityException("Metadata field does not exist"); + } + return metadataField; } /** diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/operation/DSpaceObjectMetadataReplaceOperation.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/operation/DSpaceObjectMetadataReplaceOperation.java index a3de8d3c9ea6..1cf15684587b 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/operation/DSpaceObjectMetadataReplaceOperation.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/operation/DSpaceObjectMetadataReplaceOperation.java @@ -80,7 +80,8 @@ private void replace(Context context, DSpaceObject dso, DSpaceObjectService dsoS MetadataValueRest metadataValue, String index, String propertyOfMd, String valueMdProperty) { // replace entire set of metadata if (metadataField == null) { - throw new UnprocessableEntityException("Metadata field does not exist"); + this.replaceAllMetadata(context, dso, dsoService); + return; } // replace all metadata for existing key From aac45284d239a54a036eefe455c6f75bb0cd1655 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Fri, 27 Jun 2025 13:05:51 -0500 Subject: [PATCH 782/979] Correct metadata value count for dspace-7_x --- .../src/test/java/org/dspace/app/rest/PatchMetadataIT.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/PatchMetadataIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/PatchMetadataIT.java index 368128fd6e4c..95ebb480bcf1 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/PatchMetadataIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/PatchMetadataIT.java @@ -1429,7 +1429,7 @@ public void moveMetadataAuthorFourToOneMultiOpTest() throws Exception { @Test public void replaceInvalidMetadataShouldFailTest() throws Exception { initSimplePublicationItem(); - assertEquals(11, publicationItem.getMetadata().size()); + assertEquals(12, publicationItem.getMetadata().size()); String patchBody = getPatchContent(List.of( new ReplaceOperation("/metadata/dc.contributor.invalid/0", "some value") @@ -1442,7 +1442,7 @@ public void replaceInvalidMetadataShouldFailTest() throws Exception { publicationItem = context.reloadEntity(publicationItem); - assertEquals(11, publicationItem.getMetadata().size()); + assertEquals(12, publicationItem.getMetadata().size()); assertEquals(0, itemService.getMetadata(publicationItem, "dc", "contributor", "invalid", Item.ANY, false).size()); } From 66a75f522faeba45dc3081460d33a43aa9a864ba Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 30 Jun 2025 02:37:25 +0000 Subject: [PATCH 783/979] Bump the fasterxml group with 3 updates Bumps the fasterxml group with 3 updates: [com.fasterxml.jackson.core:jackson-annotations](https://github.com/FasterXML/jackson), [com.fasterxml.jackson.core:jackson-core](https://github.com/FasterXML/jackson-core) and [com.fasterxml.jackson.core:jackson-databind](https://github.com/FasterXML/jackson). Updates `com.fasterxml.jackson.core:jackson-annotations` from 2.19.0 to 2.19.1 - [Commits](https://github.com/FasterXML/jackson/commits) Updates `com.fasterxml.jackson.core:jackson-core` from 2.19.0 to 2.19.1 - [Commits](https://github.com/FasterXML/jackson-core/compare/jackson-core-2.19.0...jackson-core-2.19.1) Updates `com.fasterxml.jackson.core:jackson-core` from 2.19.0 to 2.19.1 - [Commits](https://github.com/FasterXML/jackson-core/compare/jackson-core-2.19.0...jackson-core-2.19.1) Updates `com.fasterxml.jackson.core:jackson-databind` from 2.19.0 to 2.19.1 - [Commits](https://github.com/FasterXML/jackson/commits) --- updated-dependencies: - dependency-name: com.fasterxml.jackson.core:jackson-annotations dependency-version: 2.19.1 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: fasterxml - dependency-name: com.fasterxml.jackson.core:jackson-core dependency-version: 2.19.1 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: fasterxml - dependency-name: com.fasterxml.jackson.core:jackson-core dependency-version: 2.19.1 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: fasterxml - dependency-name: com.fasterxml.jackson.core:jackson-databind dependency-version: 2.19.1 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: fasterxml ... Signed-off-by: dependabot[bot] --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 6524cf95c093..928d60d1a77d 100644 --- a/pom.xml +++ b/pom.xml @@ -30,8 +30,8 @@ 3.10.8 2.31.0 - 2.19.0 - 2.19.0 + 2.19.1 + 2.19.1 1.3.2 2.3.1 2.3.9 From b713b26daf50d4ee8b90addb854b588981c775cb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 1 Jul 2025 17:15:16 +0000 Subject: [PATCH 784/979] Bump org.checkerframework:checker-qual from 3.49.4 to 3.49.5 Bumps [org.checkerframework:checker-qual](https://github.com/typetools/checker-framework) from 3.49.4 to 3.49.5. - [Release notes](https://github.com/typetools/checker-framework/releases) - [Changelog](https://github.com/typetools/checker-framework/blob/master/docs/CHANGELOG.md) - [Commits](https://github.com/typetools/checker-framework/compare/checker-framework-3.49.4...checker-framework-3.49.5) --- updated-dependencies: - dependency-name: org.checkerframework:checker-qual dependency-version: 3.49.5 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 189115079da2..086e0f46db4a 100644 --- a/pom.xml +++ b/pom.xml @@ -1350,7 +1350,7 @@ org.checkerframework checker-qual - 3.49.4 + 3.49.5 - org.sonatype.plugins - nexus-staging-maven-plugin - 1.7.0 + org.sonatype.central + central-publishing-maven-plugin + 0.8.0 @@ -923,7 +923,7 @@ + - org.sonatype.plugins - nexus-staging-maven-plugin + org.sonatype.central + central-publishing-maven-plugin true - - - ossrh - https://oss.sonatype.org/ - - true - - false - - 10 - - + org.apache.maven.plugins maven-source-plugin @@ -972,7 +970,7 @@ - + org.apache.maven.plugins maven-javadoc-plugin @@ -985,8 +983,8 @@ - + org.apache.maven.plugins maven-gpg-plugin @@ -1911,24 +1909,10 @@ scm:git:git@github.com:DSpace/DSpace.git scm:git:git@github.com:DSpace/DSpace.git - git@github.com:DSpace/DSpace.git + https://github.com/DSpace/DSpace dspace-8_x - - - - - ossrh - https://oss.sonatype.org/content/repositories/snapshots - - - ossrh - https://oss.sonatype.org/service/local/staging/deploy/maven2/ - - - @@ -1938,11 +1922,10 @@ false - + - maven-snapshots - https://oss.sonatype.org/content/repositories/snapshots - default + central-portal-snapshots + https://central.sonatype.com/repository/maven-snapshots/ false From 76c50ac43e929a9ce702b674bb6fdf4335e471ce Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Mon, 30 Jun 2025 16:26:48 -0500 Subject: [PATCH 786/979] Update POM to use central-publishing-maven-plugin and Sonatype's Central Portal --- pom.xml | 77 +++++++++++++++++++++++++-------------------------------- 1 file changed, 33 insertions(+), 44 deletions(-) diff --git a/pom.xml b/pom.xml index 928d60d1a77d..85fb3ce6ccd7 100644 --- a/pom.xml +++ b/pom.xml @@ -355,9 +355,9 @@ - org.sonatype.plugins - nexus-staging-maven-plugin - 1.7.0 + org.sonatype.central + central-publishing-maven-plugin + 0.8.0 @@ -938,7 +938,7 @@ + - org.sonatype.plugins - nexus-staging-maven-plugin + org.sonatype.central + central-publishing-maven-plugin true - - - ossrh - https://oss.sonatype.org/ - - true - - false - - 10 - - + org.apache.maven.plugins maven-source-plugin @@ -987,7 +985,7 @@ - + org.apache.maven.plugins maven-javadoc-plugin @@ -1000,8 +998,8 @@ - + org.apache.maven.plugins maven-gpg-plugin @@ -1940,35 +1938,23 @@ scm:git:git@github.com:DSpace/DSpace.git scm:git:git@github.com:DSpace/DSpace.git - git@github.com:DSpace/DSpace.git + https://github.com/DSpace/DSpace dspace-7_x - - - - - ossrh - https://oss.sonatype.org/content/repositories/snapshots - - - ossrh - https://oss.sonatype.org/service/local/staging/deploy/maven2/ - - - - maven-central + central https://repo.maven.apache.org/maven2 + + false + - + - maven-snapshots - https://oss.sonatype.org/content/repositories/snapshots - default + central-portal-snapshots + https://central.sonatype.com/repository/maven-snapshots/ false @@ -1980,6 +1966,9 @@ handle.net https://handle.net/maven + + false + From 8f9a7f1f92ca320476b110f595d70bcc208bbe44 Mon Sep 17 00:00:00 2001 From: Yury Bondarenko Date: Wed, 2 Jul 2025 10:24:18 +0200 Subject: [PATCH 787/979] Point directly to HTTPS address for ArXiv (cherry picked from commit cf0d6635f2683b1c8a9c116d33a3e82779e70f77) --- .../resources/spring/spring-dspace-addon-import-services.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dspace-api/src/main/resources/spring/spring-dspace-addon-import-services.xml b/dspace-api/src/main/resources/spring/spring-dspace-addon-import-services.xml index 6b0ef3e9b9e3..c758966f449d 100644 --- a/dspace-api/src/main/resources/spring/spring-dspace-addon-import-services.xml +++ b/dspace-api/src/main/resources/spring/spring-dspace-addon-import-services.xml @@ -45,7 +45,7 @@ - + From 59ddbe5ee8765dab8f6a52f80543b0bc19572abc Mon Sep 17 00:00:00 2001 From: Yury Bondarenko Date: Wed, 2 Jul 2025 10:24:18 +0200 Subject: [PATCH 788/979] Point directly to HTTPS address for ArXiv (cherry picked from commit cf0d6635f2683b1c8a9c116d33a3e82779e70f77) --- .../resources/spring/spring-dspace-addon-import-services.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dspace-api/src/main/resources/spring/spring-dspace-addon-import-services.xml b/dspace-api/src/main/resources/spring/spring-dspace-addon-import-services.xml index f7943fb2320c..e693d26e538e 100644 --- a/dspace-api/src/main/resources/spring/spring-dspace-addon-import-services.xml +++ b/dspace-api/src/main/resources/spring/spring-dspace-addon-import-services.xml @@ -59,7 +59,7 @@ - + From 4685450194552effdf832784f37369fef97baf04 Mon Sep 17 00:00:00 2001 From: Kim Shepherd Date: Wed, 2 Jul 2025 10:39:50 +0200 Subject: [PATCH 789/979] Safe and consistent XML entity handling in parsers --- .../dspace/administer/RegistryImporter.java | 7 +- .../org/dspace/administer/RegistryLoader.java | 9 +- .../org/dspace/administer/StructBuilder.java | 6 +- .../app/itemimport/ItemImportServiceImpl.java | 8 +- .../dspace/app/itemupdate/ItemArchive.java | 18 +-- .../dspace/app/launcher/ScriptLauncher.java | 3 +- .../app/sfx/SFXFileReaderServiceImpl.java | 5 +- .../org/dspace/app/util/DCInputsReader.java | 6 +- .../dspace/app/util/InitializeEntities.java | 6 +- .../app/util/SubmissionConfigReader.java | 9 +- .../java/org/dspace/app/util/XMLUtils.java | 123 ++++++++++++++++++ .../crosswalk/METSDisseminationCrosswalk.java | 3 +- .../crosswalk/MODSDisseminationCrosswalk.java | 3 +- .../content/crosswalk/QDCCrosswalk.java | 3 +- .../content/crosswalk/RoleCrosswalk.java | 3 +- .../crosswalk/XSLTIngestionCrosswalk.java | 3 +- .../dspace/content/packager/METSManifest.java | 8 +- .../dspace/content/packager/RoleIngester.java | 5 +- .../ctask/general/MetadataWebService.java | 11 +- .../provider/orcid/xml/Converter.java | 5 +- .../identifier/doi/DataCiteConnector.java | 3 +- .../ArXivImportMetadataSourceServiceImpl.java | 5 +- .../CiniiImportMetadataSourceServiceImpl.java | 15 +-- .../crossref/CrossRefAbstractProcessor.java | 5 +- .../EpoImportMetadataSourceServiceImpl.java | 13 +- ...enAireImportMetadataSourceServiceImpl.java | 5 +- ...PubmedImportMetadataSourceServiceImpl.java | 20 ++- ...PubmedEuropeMetadataSourceServiceImpl.java | 11 +- ...ScopusImportMetadataSourceServiceImpl.java | 9 +- .../WOSImportMetadataSourceServiceImpl.java | 11 +- .../CCLicenseConnectorServiceImpl.java | 3 +- .../dspace/orcid/client/OrcidClientImpl.java | 4 +- .../vocabulary/ControlledVocabulary.java | 4 +- 33 files changed, 229 insertions(+), 123 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/administer/RegistryImporter.java b/dspace-api/src/main/java/org/dspace/administer/RegistryImporter.java index 27a653421312..c74e56bce890 100644 --- a/dspace-api/src/main/java/org/dspace/administer/RegistryImporter.java +++ b/dspace-api/src/main/java/org/dspace/administer/RegistryImporter.java @@ -10,7 +10,6 @@ import java.io.File; import java.io.IOException; import javax.xml.parsers.DocumentBuilder; -import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import javax.xml.transform.TransformerException; import javax.xml.xpath.XPath; @@ -18,6 +17,7 @@ import javax.xml.xpath.XPathExpressionException; import javax.xml.xpath.XPathFactory; +import org.dspace.app.util.XMLUtils; import org.w3c.dom.Document; import org.w3c.dom.Node; import org.w3c.dom.NodeList; @@ -49,8 +49,9 @@ private RegistryImporter() { } */ public static Document loadXML(String filename) throws IOException, ParserConfigurationException, SAXException { - DocumentBuilder builder = DocumentBuilderFactory.newInstance() - .newDocumentBuilder(); + // This XML builder will *not* disable external entities as XML + // registries are considered trusted content + DocumentBuilder builder = XMLUtils.getTrustedDocumentBuilder(); Document document = builder.parse(new File(filename)); diff --git a/dspace-api/src/main/java/org/dspace/administer/RegistryLoader.java b/dspace-api/src/main/java/org/dspace/administer/RegistryLoader.java index d503bfc00b7f..8bb72e18521e 100644 --- a/dspace-api/src/main/java/org/dspace/administer/RegistryLoader.java +++ b/dspace-api/src/main/java/org/dspace/administer/RegistryLoader.java @@ -13,7 +13,6 @@ import java.util.ArrayList; import java.util.Arrays; import javax.xml.parsers.DocumentBuilder; -import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import javax.xml.transform.TransformerException; import javax.xml.xpath.XPath; @@ -29,6 +28,7 @@ import org.apache.commons.cli.ParseException; import org.apache.commons.lang3.StringUtils; import org.apache.logging.log4j.Logger; +import org.dspace.app.util.XMLUtils; import org.dspace.authorize.AuthorizeException; import org.dspace.content.BitstreamFormat; import org.dspace.content.factory.ContentServiceFactory; @@ -266,8 +266,9 @@ private static void loadFormat(Context context, Node node) */ private static Document loadXML(String filename) throws IOException, ParserConfigurationException, SAXException { - DocumentBuilder builder = DocumentBuilderFactory.newInstance() - .newDocumentBuilder(); + // This XML builder will *not* disable external entities as XML + // registries are considered trusted content + DocumentBuilder builder = XMLUtils.getTrustedDocumentBuilder(); return builder.parse(new File(filename)); } @@ -351,4 +352,4 @@ private static String[] getRepeatedElementData(Node parentElement, return data; } -} \ No newline at end of file +} diff --git a/dspace-api/src/main/java/org/dspace/administer/StructBuilder.java b/dspace-api/src/main/java/org/dspace/administer/StructBuilder.java index 8bbcfe0ff753..f2577a37b176 100644 --- a/dspace-api/src/main/java/org/dspace/administer/StructBuilder.java +++ b/dspace-api/src/main/java/org/dspace/administer/StructBuilder.java @@ -27,7 +27,6 @@ import java.util.List; import java.util.Map; import javax.xml.parsers.DocumentBuilder; -import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import javax.xml.transform.TransformerException; import javax.xml.xpath.XPath; @@ -43,6 +42,7 @@ import org.apache.commons.cli.Options; import org.apache.commons.cli.ParseException; import org.apache.commons.lang3.StringUtils; +import org.dspace.app.util.XMLUtils; import org.dspace.authorize.AuthorizeException; import org.dspace.content.Collection; import org.dspace.content.Community; @@ -613,8 +613,8 @@ private static String validateCollections(NodeList collections, int level) */ private static org.w3c.dom.Document loadXML(InputStream input) throws IOException, ParserConfigurationException, SAXException { - DocumentBuilder builder = DocumentBuilderFactory.newInstance() - .newDocumentBuilder(); + // This builder factory does not disable external DTD, entities, etc. + DocumentBuilder builder = XMLUtils.getTrustedDocumentBuilder(); org.w3c.dom.Document document = builder.parse(input); diff --git a/dspace-api/src/main/java/org/dspace/app/itemimport/ItemImportServiceImpl.java b/dspace-api/src/main/java/org/dspace/app/itemimport/ItemImportServiceImpl.java index 087a33026151..b40ecae2f7e0 100644 --- a/dspace-api/src/main/java/org/dspace/app/itemimport/ItemImportServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/app/itemimport/ItemImportServiceImpl.java @@ -47,7 +47,6 @@ import java.util.zip.ZipEntry; import java.util.zip.ZipFile; import javax.xml.parsers.DocumentBuilder; -import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import javax.xml.transform.TransformerException; import javax.xml.xpath.XPath; @@ -67,6 +66,7 @@ import org.dspace.app.itemimport.service.ItemImportService; import org.dspace.app.util.LocalSchemaFilenameFilter; import org.dspace.app.util.RelationshipUtils; +import org.dspace.app.util.XMLUtils; import org.dspace.authorize.AuthorizeException; import org.dspace.authorize.ResourcePolicy; import org.dspace.authorize.service.AuthorizeService; @@ -179,6 +179,8 @@ public class ItemImportServiceImpl implements ItemImportService, InitializingBea @Autowired(required = true) protected MetadataValueService metadataValueService; + protected DocumentBuilder builder; + protected String tempWorkDir; protected boolean isTest = false; @@ -1888,9 +1890,7 @@ protected String getStringValue(Node node) { */ protected Document loadXML(String filename) throws IOException, ParserConfigurationException, SAXException { - DocumentBuilder builder = DocumentBuilderFactory.newInstance() - .newDocumentBuilder(); - + DocumentBuilder builder = XMLUtils.getDocumentBuilder(); return builder.parse(new File(filename)); } diff --git a/dspace-api/src/main/java/org/dspace/app/itemupdate/ItemArchive.java b/dspace-api/src/main/java/org/dspace/app/itemupdate/ItemArchive.java index 26de45caf77e..7dda65a0a75b 100644 --- a/dspace-api/src/main/java/org/dspace/app/itemupdate/ItemArchive.java +++ b/dspace-api/src/main/java/org/dspace/app/itemupdate/ItemArchive.java @@ -23,8 +23,6 @@ import java.util.Iterator; import java.util.List; import java.util.UUID; -import javax.xml.parsers.DocumentBuilder; -import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import javax.xml.transform.Transformer; import javax.xml.transform.TransformerConfigurationException; @@ -33,6 +31,7 @@ import org.apache.logging.log4j.Logger; import org.dspace.app.util.LocalSchemaFilenameFilter; +import org.dspace.app.util.XMLUtils; import org.dspace.authorize.AuthorizeException; import org.dspace.content.DSpaceObject; import org.dspace.content.Item; @@ -52,7 +51,6 @@ public class ItemArchive { public static final String DUBLIN_CORE_XML = "dublin_core.xml"; - protected static DocumentBuilder builder = null; protected Transformer transformer = null; protected List dtomList = null; @@ -95,14 +93,14 @@ public static ItemArchive create(Context context, File dir, String itemField) InputStream is = null; try { is = new FileInputStream(new File(dir, DUBLIN_CORE_XML)); - itarch.dtomList = MetadataUtilities.loadDublinCore(getDocumentBuilder(), is); + itarch.dtomList = MetadataUtilities.loadDublinCore(XMLUtils.getDocumentBuilder(), is); //The code to search for local schema files was copied from org.dspace.app.itemimport // .ItemImportServiceImpl.java File file[] = dir.listFiles(new LocalSchemaFilenameFilter()); for (int i = 0; i < file.length; i++) { is = new FileInputStream(file[i]); - itarch.dtomList.addAll(MetadataUtilities.loadDublinCore(getDocumentBuilder(), is)); + itarch.dtomList.addAll(MetadataUtilities.loadDublinCore(XMLUtils.getDocumentBuilder(), is)); } } finally { if (is != null) { @@ -126,14 +124,6 @@ public static ItemArchive create(Context context, File dir, String itemField) return itarch; } - protected static DocumentBuilder getDocumentBuilder() - throws ParserConfigurationException { - if (builder == null) { - builder = DocumentBuilderFactory.newInstance().newDocumentBuilder(); - } - return builder; - } - /** * Getter for Transformer * @@ -318,7 +308,7 @@ public void writeUndo(File undoDir) try { out = new FileOutputStream(new File(dir, "dublin_core.xml")); - Document doc = MetadataUtilities.writeDublinCore(getDocumentBuilder(), undoDtomList); + Document doc = MetadataUtilities.writeDublinCore(XMLUtils.getDocumentBuilder(), undoDtomList); MetadataUtilities.writeDocument(doc, getTransformer(), out); // if undo has delete bitstream diff --git a/dspace-api/src/main/java/org/dspace/app/launcher/ScriptLauncher.java b/dspace-api/src/main/java/org/dspace/app/launcher/ScriptLauncher.java index 89a416bfa883..ab8807c2cae3 100644 --- a/dspace-api/src/main/java/org/dspace/app/launcher/ScriptLauncher.java +++ b/dspace-api/src/main/java/org/dspace/app/launcher/ScriptLauncher.java @@ -19,6 +19,7 @@ import org.apache.commons.cli.ParseException; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import org.dspace.app.util.XMLUtils; import org.dspace.core.Context; import org.dspace.scripts.DSpaceRunnable; import org.dspace.scripts.DSpaceRunnable.StepResult; @@ -314,7 +315,7 @@ public static Document getConfig(DSpaceKernelImpl kernelImpl) { String config = kernelImpl.getConfigurationService().getProperty("dspace.dir") + System.getProperty("file.separator") + "config" + System.getProperty("file.separator") + "launcher.xml"; - SAXBuilder saxBuilder = new SAXBuilder(); + SAXBuilder saxBuilder = XMLUtils.getSAXBuilder(); Document doc = null; try { doc = saxBuilder.build(config); diff --git a/dspace-api/src/main/java/org/dspace/app/sfx/SFXFileReaderServiceImpl.java b/dspace-api/src/main/java/org/dspace/app/sfx/SFXFileReaderServiceImpl.java index 184f00a53e59..d3b447374a2c 100644 --- a/dspace-api/src/main/java/org/dspace/app/sfx/SFXFileReaderServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/app/sfx/SFXFileReaderServiceImpl.java @@ -18,6 +18,7 @@ import org.apache.commons.lang3.StringUtils; import org.apache.logging.log4j.Logger; import org.dspace.app.sfx.service.SFXFileReaderService; +import org.dspace.app.util.XMLUtils; import org.dspace.content.DCPersonName; import org.dspace.content.Item; import org.dspace.content.MetadataValue; @@ -79,9 +80,9 @@ public Document parseFile(String fileName) { log.info("Parsing XML file... " + fileName); DocumentBuilder docBuilder; Document doc = null; - DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory.newInstance(); - docBuilderFactory.setIgnoringElementContentWhitespace(true); try { + DocumentBuilderFactory docBuilderFactory = XMLUtils.getDocumentBuilderFactory(); + docBuilderFactory.setIgnoringElementContentWhitespace(true); docBuilder = docBuilderFactory.newDocumentBuilder(); } catch (ParserConfigurationException e) { log.error("Wrong parser configuration: " + e.getMessage()); diff --git a/dspace-api/src/main/java/org/dspace/app/util/DCInputsReader.java b/dspace-api/src/main/java/org/dspace/app/util/DCInputsReader.java index c77c3bf10e08..d2f3ac0dc5d9 100644 --- a/dspace-api/src/main/java/org/dspace/app/util/DCInputsReader.java +++ b/dspace-api/src/main/java/org/dspace/app/util/DCInputsReader.java @@ -121,7 +121,11 @@ private void buildInputs(String fileName) String uri = "file:" + new File(fileName).getAbsolutePath(); try { - DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); + // This document builder factory will *not* disable external + // entities as they can be useful in managing large forms, but + // it is up to site administrators to validate the XML they are + // storing + DocumentBuilderFactory factory = XMLUtils.getTrustedDocumentBuilderFactory(); factory.setValidating(false); factory.setIgnoringComments(true); factory.setIgnoringElementContentWhitespace(true); diff --git a/dspace-api/src/main/java/org/dspace/app/util/InitializeEntities.java b/dspace-api/src/main/java/org/dspace/app/util/InitializeEntities.java index 8d3964a3e3c7..4e64c18fcedd 100644 --- a/dspace-api/src/main/java/org/dspace/app/util/InitializeEntities.java +++ b/dspace-api/src/main/java/org/dspace/app/util/InitializeEntities.java @@ -11,7 +11,6 @@ import java.io.IOException; import java.sql.SQLException; import javax.xml.parsers.DocumentBuilder; -import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import org.apache.commons.cli.CommandLine; @@ -139,8 +138,9 @@ private void run(String fileLocation) throws SQLException, AuthorizeException { private void parseXMLToRelations(Context context, String fileLocation) throws AuthorizeException { try { File fXmlFile = new File(fileLocation); - DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance(); - DocumentBuilder dBuilder = dbFactory.newDocumentBuilder(); + // This XML builder will allow external entities, so the relationship types XML should + // be considered trusted by administrators + DocumentBuilder dBuilder = XMLUtils.getTrustedDocumentBuilder(); Document doc = dBuilder.parse(fXmlFile); doc.getDocumentElement().normalize(); diff --git a/dspace-api/src/main/java/org/dspace/app/util/SubmissionConfigReader.java b/dspace-api/src/main/java/org/dspace/app/util/SubmissionConfigReader.java index 78be2bd4a41b..64e62317c515 100644 --- a/dspace-api/src/main/java/org/dspace/app/util/SubmissionConfigReader.java +++ b/dspace-api/src/main/java/org/dspace/app/util/SubmissionConfigReader.java @@ -170,8 +170,11 @@ private void buildInputs(String fileName) throws SubmissionConfigReaderException String uri = "file:" + new File(fileName).getAbsolutePath(); try { - DocumentBuilderFactory factory = DocumentBuilderFactory - .newInstance(); + // This document builder factory will *not* disable external + // entities as they can be useful in managing large forms, but + // it is up to site administrators to validate the XML they are + // storing + DocumentBuilderFactory factory = XMLUtils.getTrustedDocumentBuilderFactory(); factory.setValidating(false); factory.setIgnoringComments(true); factory.setIgnoringElementContentWhitespace(true); @@ -732,4 +735,4 @@ public List getCollectionsBySubmissionConfig(Context context, String } return results; } -} \ No newline at end of file +} diff --git a/dspace-api/src/main/java/org/dspace/app/util/XMLUtils.java b/dspace-api/src/main/java/org/dspace/app/util/XMLUtils.java index c39d0d26fd5e..389d53fe6da4 100644 --- a/dspace-api/src/main/java/org/dspace/app/util/XMLUtils.java +++ b/dspace-api/src/main/java/org/dspace/app/util/XMLUtils.java @@ -9,8 +9,13 @@ import java.util.ArrayList; import java.util.List; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.stream.XMLInputFactory; import org.apache.commons.lang3.StringUtils; +import org.jdom2.input.SAXBuilder; import org.w3c.dom.Element; import org.w3c.dom.NodeList; @@ -161,4 +166,122 @@ public static List getElementValueArrayList(Element rootElement, } return result; } + + /** + * Initialize and return a javax DocumentBuilderFactory with NO security + * applied. This is intended only for internal, administrative/configuration + * use where external entities and other dangerous features are actually + * purposefully included. + * The method here is tiny, but may be expanded with other features like + * whitespace handling, and calling this method name helps to document + * the fact that the caller knows it is trusting the XML source / factory. + * + * @return document builder factory to generate new builders + * @throws ParserConfigurationException + */ + public static DocumentBuilderFactory getTrustedDocumentBuilderFactory() + throws ParserConfigurationException { + DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); + return factory; + } + + /** + * Initialize and return the javax DocumentBuilderFactory with some basic security + * applied to avoid XXE attacks and other unwanted content inclusion + * @return document builder factory to generate new builders + * @throws ParserConfigurationException + */ + public static DocumentBuilderFactory getDocumentBuilderFactory() + throws ParserConfigurationException { + DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); + // No DOCTYPE / DTDs + factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true); + // No external general entities + factory.setFeature("http://xml.org/sax/features/external-general-entities", false); + // No external parameter entities + factory.setFeature("http://xml.org/sax/features/external-parameter-entities", false); + // No external DTDs + factory.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false); + // Even if entities somehow get defined, they will not be expanded + factory.setExpandEntityReferences(false); + // Disable "XInclude" markup processing + factory.setXIncludeAware(false); + + return factory; + } + + /** + * Initialize and return a javax DocumentBuilder with NO security + * applied. This is intended only for internal, administrative/configuration + * use where external entities and other dangerous features are actually + * purposefully included. + * The method here is tiny, but may be expanded with other features like + * whitespace handling, and calling this method name helps to document + * the fact that the caller knows it is trusting the XML source / builder + * + * @return document builder with no security features set + * @throws ParserConfigurationException + */ + public static DocumentBuilder getTrustedDocumentBuilder() + throws ParserConfigurationException { + return getTrustedDocumentBuilderFactory().newDocumentBuilder(); + } + + /** + * Initialize and return the javax DocumentBuilder with some basic security applied + * to avoid XXE attacks and other unwanted content inclusion + * @return document builder for use in XML parsing + * @throws ParserConfigurationException + */ + public static DocumentBuilder getDocumentBuilder() + throws ParserConfigurationException { + return getDocumentBuilderFactory().newDocumentBuilder(); + } + + /** + * Initialize and return the SAX document builder with some basic security applied + * to avoid XXE attacks and other unwanted content inclusion + * @return SAX document builder for use in XML parsing + */ + public static SAXBuilder getSAXBuilder() { + return getSAXBuilder(false); + } + + /** + * Initialize and return the SAX document builder with some basic security applied + * to avoid XXE attacks and other unwanted content inclusion + * @param validate whether to use JDOM XSD validation + * @return SAX document builder for use in XML parsing + */ + public static SAXBuilder getSAXBuilder(boolean validate) { + SAXBuilder saxBuilder = new SAXBuilder(); + if (validate) { + saxBuilder.setValidation(true); + } + // No DOCTYPE / DTDs + saxBuilder.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true); + // No external general entities + saxBuilder.setFeature("http://xml.org/sax/features/external-general-entities", false); + // No external parameter entities + saxBuilder.setFeature("http://xml.org/sax/features/external-parameter-entities", false); + // No external DTDs + saxBuilder.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false); + // Don't expand entities + saxBuilder.setExpandEntities(false); + + return saxBuilder; + } + + /** + * Initialize and return the Java XML Input Factory with some basic security applied + * to avoid XXE attacks and other unwanted content inclusion + * @return XML input factory for use in XML parsing + */ + public static XMLInputFactory getXMLInputFactory() { + XMLInputFactory xmlInputFactory = XMLInputFactory.newFactory(); + xmlInputFactory.setProperty(XMLInputFactory.SUPPORT_DTD, false); + + return xmlInputFactory; + } + } diff --git a/dspace-api/src/main/java/org/dspace/content/crosswalk/METSDisseminationCrosswalk.java b/dspace-api/src/main/java/org/dspace/content/crosswalk/METSDisseminationCrosswalk.java index b8a4a8aef390..5ceacc933e4c 100644 --- a/dspace-api/src/main/java/org/dspace/content/crosswalk/METSDisseminationCrosswalk.java +++ b/dspace-api/src/main/java/org/dspace/content/crosswalk/METSDisseminationCrosswalk.java @@ -14,6 +14,7 @@ import java.util.List; import org.apache.commons.lang3.ArrayUtils; +import org.dspace.app.util.XMLUtils; import org.dspace.authorize.AuthorizeException; import org.dspace.content.DSpaceObject; import org.dspace.content.packager.PackageDisseminator; @@ -129,7 +130,7 @@ public Element disseminateElement(Context context, DSpaceObject dso) try { //Return just the root Element of the METS file - SAXBuilder builder = new SAXBuilder(); + SAXBuilder builder = XMLUtils.getSAXBuilder(); Document metsDocument = builder.build(tempFile); return metsDocument.getRootElement(); } catch (JDOMException je) { diff --git a/dspace-api/src/main/java/org/dspace/content/crosswalk/MODSDisseminationCrosswalk.java b/dspace-api/src/main/java/org/dspace/content/crosswalk/MODSDisseminationCrosswalk.java index 1e63be5ba1b9..205b3ef5b343 100644 --- a/dspace-api/src/main/java/org/dspace/content/crosswalk/MODSDisseminationCrosswalk.java +++ b/dspace-api/src/main/java/org/dspace/content/crosswalk/MODSDisseminationCrosswalk.java @@ -22,6 +22,7 @@ import org.apache.commons.lang3.ArrayUtils; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import org.dspace.app.util.XMLUtils; import org.dspace.authorize.AuthorizeException; import org.dspace.content.Collection; import org.dspace.content.Community; @@ -144,7 +145,7 @@ public static String[] getPluginNames() { MODS_NS.getURI() + " " + MODS_XSD; private static final XMLOutputter outputUgly = new XMLOutputter(); - private static final SAXBuilder builder = new SAXBuilder(); + private static final SAXBuilder builder = XMLUtils.getSAXBuilder(); private Map modsMap = null; diff --git a/dspace-api/src/main/java/org/dspace/content/crosswalk/QDCCrosswalk.java b/dspace-api/src/main/java/org/dspace/content/crosswalk/QDCCrosswalk.java index 2fdbaaad003e..51e6357d93e1 100644 --- a/dspace-api/src/main/java/org/dspace/content/crosswalk/QDCCrosswalk.java +++ b/dspace-api/src/main/java/org/dspace/content/crosswalk/QDCCrosswalk.java @@ -22,6 +22,7 @@ import org.apache.commons.lang3.ArrayUtils; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import org.dspace.app.util.XMLUtils; import org.dspace.authorize.AuthorizeException; import org.dspace.content.DSpaceObject; import org.dspace.content.Item; @@ -125,7 +126,7 @@ public class QDCCrosswalk extends SelfNamedPlugin // XML schemaLocation fragment for this crosswalk, from config. private String schemaLocation = null; - private static final SAXBuilder builder = new SAXBuilder(); + private static final SAXBuilder builder = XMLUtils.getSAXBuilder(); protected ItemService itemService = ContentServiceFactory.getInstance().getItemService(); diff --git a/dspace-api/src/main/java/org/dspace/content/crosswalk/RoleCrosswalk.java b/dspace-api/src/main/java/org/dspace/content/crosswalk/RoleCrosswalk.java index 2c763036ce33..8d5bf49902cc 100644 --- a/dspace-api/src/main/java/org/dspace/content/crosswalk/RoleCrosswalk.java +++ b/dspace-api/src/main/java/org/dspace/content/crosswalk/RoleCrosswalk.java @@ -13,6 +13,7 @@ import java.sql.SQLException; import java.util.List; +import org.dspace.app.util.XMLUtils; import org.dspace.authorize.AuthorizeException; import org.dspace.content.DSpaceObject; import org.dspace.content.packager.PackageDisseminator; @@ -208,7 +209,7 @@ public Element disseminateElement(Context context, DSpaceObject dso) try { //Try to parse our XML results (which were disseminated by the Packager) - SAXBuilder builder = new SAXBuilder(); + SAXBuilder builder = XMLUtils.getSAXBuilder(); Document xmlDocument = builder.build(tempFile); //If XML parsed successfully, return root element of doc if (xmlDocument != null && xmlDocument.hasRootElement()) { diff --git a/dspace-api/src/main/java/org/dspace/content/crosswalk/XSLTIngestionCrosswalk.java b/dspace-api/src/main/java/org/dspace/content/crosswalk/XSLTIngestionCrosswalk.java index 63ef5f7336c7..b07b2b2228e4 100644 --- a/dspace-api/src/main/java/org/dspace/content/crosswalk/XSLTIngestionCrosswalk.java +++ b/dspace-api/src/main/java/org/dspace/content/crosswalk/XSLTIngestionCrosswalk.java @@ -18,6 +18,7 @@ import org.apache.commons.lang3.ArrayUtils; import org.apache.logging.log4j.Logger; +import org.dspace.app.util.XMLUtils; import org.dspace.authorize.AuthorizeException; import org.dspace.content.Collection; import org.dspace.content.Community; @@ -297,7 +298,7 @@ public static void main(String[] argv) throws Exception { "Failed to initialize transformer, probably error loading stylesheet."); } - SAXBuilder builder = new SAXBuilder(); + SAXBuilder builder = XMLUtils.getSAXBuilder(); Document inDoc = builder.build(new FileInputStream(argv[i + 1])); XMLOutputter outputter = new XMLOutputter(Format.getPrettyFormat()); List dimList; diff --git a/dspace-api/src/main/java/org/dspace/content/packager/METSManifest.java b/dspace-api/src/main/java/org/dspace/content/packager/METSManifest.java index 3399bdf0f07e..a1ed3c124374 100644 --- a/dspace-api/src/main/java/org/dspace/content/packager/METSManifest.java +++ b/dspace-api/src/main/java/org/dspace/content/packager/METSManifest.java @@ -20,6 +20,7 @@ import org.apache.commons.codec.binary.Base64; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import org.dspace.app.util.XMLUtils; import org.dspace.authorize.AuthorizeException; import org.dspace.content.Bitstream; import org.dspace.content.Bundle; @@ -265,12 +266,13 @@ protected METSManifest(SAXBuilder builder, Element mets, String configName) { public static METSManifest create(InputStream is, boolean validate, String configName) throws IOException, MetadataValidationException { - SAXBuilder builder = new SAXBuilder(validate); + SAXBuilder builder = XMLUtils.getSAXBuilder(); builder.setIgnoringElementContentWhitespace(true); // Set validation feature if (validate) { + builder.setValidation(true); builder.setFeature("http://apache.org/xml/features/validation/schema", true); // Tell the parser where local copies of schemas are, to speed up @@ -278,10 +280,6 @@ public static METSManifest create(InputStream is, boolean validate, String confi if (localSchemas.length() > 0) { builder.setProperty("http://apache.org/xml/properties/schema/external-schemaLocation", localSchemas); } - } else { - // disallow DTD parsing to ensure no XXE attacks can occur. - // See https://cheatsheetseries.owasp.org/cheatsheets/XML_External_Entity_Prevention_Cheat_Sheet.html - builder.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true); } // Parse the METS file diff --git a/dspace-api/src/main/java/org/dspace/content/packager/RoleIngester.java b/dspace-api/src/main/java/org/dspace/content/packager/RoleIngester.java index ca27abe20614..a0ac43282c32 100644 --- a/dspace-api/src/main/java/org/dspace/content/packager/RoleIngester.java +++ b/dspace-api/src/main/java/org/dspace/content/packager/RoleIngester.java @@ -21,6 +21,7 @@ import org.apache.commons.codec.DecoderException; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import org.dspace.app.util.XMLUtils; import org.dspace.authorize.AuthorizeException; import org.dspace.content.Collection; import org.dspace.content.Community; @@ -385,7 +386,7 @@ public void ingestStream(Context context, DSpaceObject parent, Document document; try { - DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); + DocumentBuilderFactory dbf = XMLUtils.getDocumentBuilderFactory(); dbf.setIgnoringComments(true); dbf.setCoalescing(true); DocumentBuilder db = dbf.newDocumentBuilder(); @@ -419,7 +420,7 @@ public DSpaceObject ingest(Context context, DSpaceObject parent, Document document; try { - DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); + DocumentBuilderFactory dbf = XMLUtils.getDocumentBuilderFactory(); dbf.setIgnoringComments(true); dbf.setCoalescing(true); DocumentBuilder db = dbf.newDocumentBuilder(); diff --git a/dspace-api/src/main/java/org/dspace/ctask/general/MetadataWebService.java b/dspace-api/src/main/java/org/dspace/ctask/general/MetadataWebService.java index fc62d7a4b23f..1b618ad31fb7 100644 --- a/dspace-api/src/main/java/org/dspace/ctask/general/MetadataWebService.java +++ b/dspace-api/src/main/java/org/dspace/ctask/general/MetadataWebService.java @@ -37,6 +37,7 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.dspace.app.client.DSpaceHttpClientFactory; +import org.dspace.app.util.XMLUtils; import org.dspace.authorize.AuthorizeException; import org.dspace.content.DSpaceObject; import org.dspace.content.Item; @@ -176,7 +177,7 @@ public void init(Curator curator, String taskId) throws IOException { fieldSeparator = (fldSep != null) ? fldSep : " "; urlTemplate = taskProperty("template"); templateParam = urlTemplate.substring(urlTemplate.indexOf("{") + 1, - urlTemplate.indexOf("}")); + urlTemplate.indexOf("}")); String[] parsed = parseTransform(templateParam); lookupField = parsed[0]; lookupTransform = parsed[1]; @@ -204,13 +205,9 @@ public void init(Curator curator, String taskId) throws IOException { } } // initialize response document parser - DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); - factory.setNamespaceAware(true); try { - // disallow DTD parsing to ensure no XXE attacks can occur - // See https://cheatsheetseries.owasp.org/cheatsheets/XML_External_Entity_Prevention_Cheat_Sheet.html - factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true); - factory.setXIncludeAware(false); + DocumentBuilderFactory factory = XMLUtils.getDocumentBuilderFactory(); + factory.setNamespaceAware(true); docBuilder = factory.newDocumentBuilder(); } catch (ParserConfigurationException pcE) { log.error("caught exception: " + pcE); diff --git a/dspace-api/src/main/java/org/dspace/external/provider/orcid/xml/Converter.java b/dspace-api/src/main/java/org/dspace/external/provider/orcid/xml/Converter.java index 578db6c56749..ab641c00042b 100644 --- a/dspace-api/src/main/java/org/dspace/external/provider/orcid/xml/Converter.java +++ b/dspace-api/src/main/java/org/dspace/external/provider/orcid/xml/Converter.java @@ -16,6 +16,7 @@ import jakarta.xml.bind.JAXBContext; import jakarta.xml.bind.JAXBException; import jakarta.xml.bind.Unmarshaller; +import org.dspace.app.util.XMLUtils; import org.xml.sax.SAXException; /** @@ -31,9 +32,7 @@ public abstract class Converter { protected Object unmarshall(InputStream input, Class type) throws SAXException, URISyntaxException { try { - XMLInputFactory xmlInputFactory = XMLInputFactory.newFactory(); - // disallow DTD parsing to ensure no XXE attacks can occur - xmlInputFactory.setProperty(XMLInputFactory.SUPPORT_DTD, false); + XMLInputFactory xmlInputFactory = XMLUtils.getXMLInputFactory(); XMLStreamReader xmlStreamReader = xmlInputFactory.createXMLStreamReader(input); JAXBContext context = JAXBContext.newInstance(type); diff --git a/dspace-api/src/main/java/org/dspace/identifier/doi/DataCiteConnector.java b/dspace-api/src/main/java/org/dspace/identifier/doi/DataCiteConnector.java index 59c8b2c701c9..23853979be05 100644 --- a/dspace-api/src/main/java/org/dspace/identifier/doi/DataCiteConnector.java +++ b/dspace-api/src/main/java/org/dspace/identifier/doi/DataCiteConnector.java @@ -40,6 +40,7 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.dspace.app.client.DSpaceHttpClientFactory; +import org.dspace.app.util.XMLUtils; import org.dspace.authorize.AuthorizeException; import org.dspace.content.DSpaceObject; import org.dspace.content.crosswalk.CrosswalkException; @@ -829,7 +830,7 @@ protected String extractAlternateIdentifier(Context context, String content) } // parse the XML - SAXBuilder saxBuilder = new SAXBuilder(); + SAXBuilder saxBuilder = XMLUtils.getSAXBuilder(); Document doc = null; try { doc = saxBuilder.build(new ByteArrayInputStream(content.getBytes("UTF-8"))); diff --git a/dspace-api/src/main/java/org/dspace/importer/external/arxiv/service/ArXivImportMetadataSourceServiceImpl.java b/dspace-api/src/main/java/org/dspace/importer/external/arxiv/service/ArXivImportMetadataSourceServiceImpl.java index 660e0c9754bd..a790c6a9a544 100644 --- a/dspace-api/src/main/java/org/dspace/importer/external/arxiv/service/ArXivImportMetadataSourceServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/importer/external/arxiv/service/ArXivImportMetadataSourceServiceImpl.java @@ -22,6 +22,7 @@ import jakarta.ws.rs.core.MediaType; import jakarta.ws.rs.core.Response; import org.apache.commons.lang3.StringUtils; +import org.dspace.app.util.XMLUtils; import org.dspace.content.Item; import org.dspace.importer.external.datamodel.ImportRecord; import org.dspace.importer.external.datamodel.Query; @@ -218,7 +219,7 @@ public Integer call() throws Exception { if (response.getStatus() == 200) { String responseString = response.readEntity(String.class); - SAXBuilder saxBuilder = new SAXBuilder(); + SAXBuilder saxBuilder = XMLUtils.getSAXBuilder(); Document document = saxBuilder.build(new StringReader(responseString)); Element root = document.getRootElement(); @@ -399,7 +400,7 @@ private String getQuery(Query query) { private List splitToRecords(String recordsSrc) { try { - SAXBuilder saxBuilder = new SAXBuilder(); + SAXBuilder saxBuilder = XMLUtils.getSAXBuilder(); Document document = saxBuilder.build(new StringReader(recordsSrc)); Element root = document.getRootElement(); diff --git a/dspace-api/src/main/java/org/dspace/importer/external/cinii/CiniiImportMetadataSourceServiceImpl.java b/dspace-api/src/main/java/org/dspace/importer/external/cinii/CiniiImportMetadataSourceServiceImpl.java index 82a4b2d77968..41c80ab7fe64 100644 --- a/dspace-api/src/main/java/org/dspace/importer/external/cinii/CiniiImportMetadataSourceServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/importer/external/cinii/CiniiImportMetadataSourceServiceImpl.java @@ -26,6 +26,7 @@ import org.apache.http.client.utils.URIBuilder; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import org.dspace.app.util.XMLUtils; import org.dspace.content.Item; import org.dspace.importer.external.datamodel.ImportRecord; import org.dspace.importer.external.datamodel.Query; @@ -301,9 +302,7 @@ protected List search(String id, String appId) private List splitToRecords(String recordsSrc) { try { - SAXBuilder saxBuilder = new SAXBuilder(); - // disallow DTD parsing to ensure no XXE attacks can occur - saxBuilder.setFeature("http://apache.org/xml/features/disallow-doctype-decl",true); + SAXBuilder saxBuilder = XMLUtils.getSAXBuilder(); Document document = saxBuilder.build(new StringReader(recordsSrc)); Element root = document.getRootElement(); return root.getChildren(); @@ -356,9 +355,7 @@ private List getCiniiIds(String appId, Integer maxResult, String author, Map> params = new HashMap>(); String response = liveImportClient.executeHttpGetRequest(1000, uriBuilder.toString(), params); int url_len = this.url.length() - 1; - SAXBuilder saxBuilder = new SAXBuilder(); - // disallow DTD parsing to ensure no XXE attacks can occur - saxBuilder.setFeature("http://apache.org/xml/features/disallow-doctype-decl",true); + SAXBuilder saxBuilder = XMLUtils.getSAXBuilder(); Document document = saxBuilder.build(new StringReader(response)); Element root = document.getRootElement(); List namespaces = Arrays.asList( @@ -420,9 +417,7 @@ private Integer countCiniiElement(String appId, Integer maxResult, String author Map> params = new HashMap>(); String response = liveImportClient.executeHttpGetRequest(1000, uriBuilder.toString(), params); - SAXBuilder saxBuilder = new SAXBuilder(); - // disallow DTD parsing to ensure no XXE attacks can occur - saxBuilder.setFeature("http://apache.org/xml/features/disallow-doctype-decl",true); + SAXBuilder saxBuilder = XMLUtils.getSAXBuilder(); Document document = saxBuilder.build(new StringReader(response)); Element root = document.getRootElement(); List namespaces = Arrays @@ -449,4 +444,4 @@ private MetadatumDTO createIdentifier(String id) { return metadatumDTO; } -} \ No newline at end of file +} diff --git a/dspace-api/src/main/java/org/dspace/importer/external/crossref/CrossRefAbstractProcessor.java b/dspace-api/src/main/java/org/dspace/importer/external/crossref/CrossRefAbstractProcessor.java index 1b6da9d37b16..99f1ee37a54e 100644 --- a/dspace-api/src/main/java/org/dspace/importer/external/crossref/CrossRefAbstractProcessor.java +++ b/dspace-api/src/main/java/org/dspace/importer/external/crossref/CrossRefAbstractProcessor.java @@ -12,7 +12,6 @@ import java.util.ArrayList; import java.util.Collection; import javax.xml.parsers.DocumentBuilder; -import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import com.fasterxml.jackson.core.JsonProcessingException; @@ -21,6 +20,7 @@ import org.apache.commons.lang3.StringUtils; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import org.dspace.app.util.XMLUtils; import org.dspace.importer.external.metadatamapping.contributor.JsonPathMetadataProcessor; import org.w3c.dom.Document; import org.w3c.dom.Node; @@ -64,10 +64,9 @@ private String prettifyAbstract(String abstractValue) { } String xmlString = "" + abstractValue + ""; - DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); Document xmlDoc; try { - DocumentBuilder builder = factory.newDocumentBuilder(); + DocumentBuilder builder = XMLUtils.getDocumentBuilder(); InputSource is = new InputSource(new StringReader(xmlString)); xmlDoc = builder.parse(is); } catch (SAXException | IOException | ParserConfigurationException e) { diff --git a/dspace-api/src/main/java/org/dspace/importer/external/epo/service/EpoImportMetadataSourceServiceImpl.java b/dspace-api/src/main/java/org/dspace/importer/external/epo/service/EpoImportMetadataSourceServiceImpl.java index fbae302bca6a..7edd3f9d01c5 100644 --- a/dspace-api/src/main/java/org/dspace/importer/external/epo/service/EpoImportMetadataSourceServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/importer/external/epo/service/EpoImportMetadataSourceServiceImpl.java @@ -32,6 +32,7 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.apache.xerces.impl.dv.util.Base64; +import org.dspace.app.util.XMLUtils; import org.dspace.content.Item; import org.dspace.importer.external.datamodel.ImportRecord; import org.dspace.importer.external.datamodel.Query; @@ -397,9 +398,7 @@ private Integer countDocument(String bearer, String query) { String response = liveImportClient.executeHttpGetRequest(1000, uriBuilder.toString(), params); - SAXBuilder saxBuilder = new SAXBuilder(); - // disallow DTD parsing to ensure no XXE attacks can occur - saxBuilder.setFeature("http://apache.org/xml/features/disallow-doctype-decl",true); + SAXBuilder saxBuilder = XMLUtils.getSAXBuilder(); Document document = saxBuilder.build(new StringReader(response)); Element root = document.getRootElement(); @@ -436,9 +435,7 @@ private List searchDocumentIds(String bearer, String query, int s String response = liveImportClient.executeHttpGetRequest(1000, uriBuilder.toString(), params); - SAXBuilder saxBuilder = new SAXBuilder(); - // disallow DTD parsing to ensure no XXE attacks can occur - saxBuilder.setFeature("http://apache.org/xml/features/disallow-doctype-decl",true); + SAXBuilder saxBuilder = XMLUtils.getSAXBuilder(); Document document = saxBuilder.build(new StringReader(response)); Element root = document.getRootElement(); @@ -489,9 +486,7 @@ private List searchDocument(String bearer, String id, String docTy private List splitToRecords(String recordsSrc) { try { - SAXBuilder saxBuilder = new SAXBuilder(); - // disallow DTD parsing to ensure no XXE attacks can occur - saxBuilder.setFeature("http://apache.org/xml/features/disallow-doctype-decl",true); + SAXBuilder saxBuilder = XMLUtils.getSAXBuilder(); Document document = saxBuilder.build(new StringReader(recordsSrc)); Element root = document.getRootElement(); List namespaces = Arrays.asList(Namespace.getNamespace("ns", "http://www.epo.org/exchange")); diff --git a/dspace-api/src/main/java/org/dspace/importer/external/openaire/service/OpenAireImportMetadataSourceServiceImpl.java b/dspace-api/src/main/java/org/dspace/importer/external/openaire/service/OpenAireImportMetadataSourceServiceImpl.java index 7fb5f27354f7..ea5b7a67617c 100644 --- a/dspace-api/src/main/java/org/dspace/importer/external/openaire/service/OpenAireImportMetadataSourceServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/importer/external/openaire/service/OpenAireImportMetadataSourceServiceImpl.java @@ -20,6 +20,7 @@ import jakarta.ws.rs.client.Invocation; import jakarta.ws.rs.client.WebTarget; import jakarta.ws.rs.core.Response; +import org.dspace.app.util.XMLUtils; import org.dspace.content.Item; import org.dspace.importer.external.datamodel.ImportRecord; import org.dspace.importer.external.datamodel.Query; @@ -248,7 +249,7 @@ public Integer call() throws Exception { if (response.getStatus() == 200) { String responseString = response.readEntity(String.class); - SAXBuilder saxBuilder = new SAXBuilder(); + SAXBuilder saxBuilder = XMLUtils.getSAXBuilder(); Document document = saxBuilder.build(new StringReader(responseString)); Element root = document.getRootElement(); @@ -330,7 +331,7 @@ private ImportRecord filterMultipleTitles(ImportRecord transformSourceRecords) { private List splitToRecords(String recordsSrc) { try { - SAXBuilder saxBuilder = new SAXBuilder(); + SAXBuilder saxBuilder = XMLUtils.getSAXBuilder(); Document document = saxBuilder.build(new StringReader(recordsSrc)); Element root = document.getRootElement(); diff --git a/dspace-api/src/main/java/org/dspace/importer/external/pubmed/service/PubmedImportMetadataSourceServiceImpl.java b/dspace-api/src/main/java/org/dspace/importer/external/pubmed/service/PubmedImportMetadataSourceServiceImpl.java index 000ef19eaec5..7718e59e483e 100644 --- a/dspace-api/src/main/java/org/dspace/importer/external/pubmed/service/PubmedImportMetadataSourceServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/importer/external/pubmed/service/PubmedImportMetadataSourceServiceImpl.java @@ -24,6 +24,7 @@ import com.google.common.io.CharStreams; import org.apache.commons.lang3.StringUtils; import org.apache.http.client.utils.URIBuilder; +import org.dspace.app.util.XMLUtils; import org.dspace.content.Item; import org.dspace.importer.external.datamodel.ImportRecord; import org.dspace.importer.external.datamodel.Query; @@ -233,11 +234,13 @@ private String getSingleElementValue(String src, String elementName) { String value = null; try { - SAXBuilder saxBuilder = new SAXBuilder(); - // Disallow external entities & entity expansion to protect against XXE attacks - // (NOTE: We receive errors if we disable all DTDs for PubMed, so this is the best we can do) - saxBuilder.setFeature("http://xml.org/sax/features/external-general-entities", false); - saxBuilder.setFeature("http://xml.org/sax/features/external-parameter-entities", false); + SAXBuilder saxBuilder = XMLUtils.getSAXBuilder(); + // To properly parse PubMed responses, we must allow DOCTYPE/DTDs overall but + // we can still take advantage of entities themselves being disabled, and not + // expanded. + saxBuilder.setFeature("http://apache.org/xml/features/disallow-doctype-decl", false); + saxBuilder.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", + true); Document document = saxBuilder.build(new StringReader(src)); Element root = document.getRootElement(); @@ -354,12 +357,7 @@ public Collection call() throws Exception { private List splitToRecords(String recordsSrc) { try { - SAXBuilder saxBuilder = new SAXBuilder(); - // Disallow external entities & entity expansion to protect against XXE attacks - // (NOTE: We receive errors if we disable all DTDs for PubMed, so this is the best we can do) - saxBuilder.setFeature("http://xml.org/sax/features/external-general-entities", false); - saxBuilder.setFeature("http://xml.org/sax/features/external-parameter-entities", false); - saxBuilder.setExpandEntities(false); + SAXBuilder saxBuilder = XMLUtils.getSAXBuilder(); Document document = saxBuilder.build(new StringReader(recordsSrc)); Element root = document.getRootElement(); diff --git a/dspace-api/src/main/java/org/dspace/importer/external/pubmedeurope/PubmedEuropeMetadataSourceServiceImpl.java b/dspace-api/src/main/java/org/dspace/importer/external/pubmedeurope/PubmedEuropeMetadataSourceServiceImpl.java index 7cd297eb2815..24f40339ddec 100644 --- a/dspace-api/src/main/java/org/dspace/importer/external/pubmedeurope/PubmedEuropeMetadataSourceServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/importer/external/pubmedeurope/PubmedEuropeMetadataSourceServiceImpl.java @@ -24,6 +24,7 @@ import org.apache.http.client.utils.URIBuilder; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import org.dspace.app.util.XMLUtils; import org.dspace.content.Item; import org.dspace.importer.external.datamodel.ImportRecord; import org.dspace.importer.external.datamodel.Query; @@ -292,9 +293,7 @@ public Integer count(String query) throws URISyntaxException, ClientProtocolExce Map> params = new HashMap>(); String response = liveImportClient.executeHttpGetRequest(1000, buildURI(1, query), params); - SAXBuilder saxBuilder = new SAXBuilder(); - // disallow DTD parsing to ensure no XXE attacks can occur - saxBuilder.setFeature("http://apache.org/xml/features/disallow-doctype-decl",true); + SAXBuilder saxBuilder = XMLUtils.getSAXBuilder(); Document document = saxBuilder.build(new StringReader(response)); Element root = document.getRootElement(); Element element = root.getChild("hitCount"); @@ -365,9 +364,7 @@ public List search(String query, Integer size, Integer start) thro String response = liveImportClient.executeHttpGetRequest(1000, uriBuilder.toString(), params); String cursorMark = StringUtils.EMPTY; if (StringUtils.isNotBlank(response)) { - SAXBuilder saxBuilder = new SAXBuilder(); - // disallow DTD parsing to ensure no XXE attacks can occur - saxBuilder.setFeature("http://apache.org/xml/features/disallow-doctype-decl",true); + SAXBuilder saxBuilder = XMLUtils.getSAXBuilder(); Document document = saxBuilder.build(new StringReader(response)); XPathFactory xpfac = XPathFactory.instance(); XPathExpression xPath = xpfac.compile("//responseWrapper/resultList/result", @@ -419,4 +416,4 @@ public void setUrl(String url) { this.url = url; } -} \ No newline at end of file +} diff --git a/dspace-api/src/main/java/org/dspace/importer/external/scopus/service/ScopusImportMetadataSourceServiceImpl.java b/dspace-api/src/main/java/org/dspace/importer/external/scopus/service/ScopusImportMetadataSourceServiceImpl.java index a3f74694becf..22e3534ca89f 100644 --- a/dspace-api/src/main/java/org/dspace/importer/external/scopus/service/ScopusImportMetadataSourceServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/importer/external/scopus/service/ScopusImportMetadataSourceServiceImpl.java @@ -26,6 +26,7 @@ import org.apache.commons.lang3.StringUtils; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import org.dspace.app.util.XMLUtils; import org.dspace.content.Item; import org.dspace.importer.external.datamodel.ImportRecord; import org.dspace.importer.external.datamodel.Query; @@ -208,9 +209,7 @@ public Integer call() throws Exception { return 0; } - SAXBuilder saxBuilder = new SAXBuilder(); - // disallow DTD parsing to ensure no XXE attacks can occur - saxBuilder.setFeature("http://apache.org/xml/features/disallow-doctype-decl",true); + SAXBuilder saxBuilder = XMLUtils.getSAXBuilder(); Document document = saxBuilder.build(new StringReader(response)); Element root = document.getRootElement(); @@ -397,9 +396,7 @@ private Map getRequestParameters(String query, String viewMode, private List splitToRecords(String recordsSrc) { try { - SAXBuilder saxBuilder = new SAXBuilder(); - // disallow DTD parsing to ensure no XXE attacks can occur - saxBuilder.setFeature("http://apache.org/xml/features/disallow-doctype-decl",true); + SAXBuilder saxBuilder = XMLUtils.getSAXBuilder(); Document document = saxBuilder.build(new StringReader(recordsSrc)); Element root = document.getRootElement(); String totalResults = root.getChildText("totalResults", Namespace.getNamespace("http://a9.com/-/spec/opensearch/1.1/")); diff --git a/dspace-api/src/main/java/org/dspace/importer/external/wos/service/WOSImportMetadataSourceServiceImpl.java b/dspace-api/src/main/java/org/dspace/importer/external/wos/service/WOSImportMetadataSourceServiceImpl.java index 9bffa2a84a19..2ac63d50513f 100644 --- a/dspace-api/src/main/java/org/dspace/importer/external/wos/service/WOSImportMetadataSourceServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/importer/external/wos/service/WOSImportMetadataSourceServiceImpl.java @@ -26,6 +26,7 @@ import org.apache.commons.lang3.StringUtils; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import org.dspace.app.util.XMLUtils; import org.dspace.content.Item; import org.dspace.importer.external.datamodel.ImportRecord; import org.dspace.importer.external.datamodel.Query; @@ -145,9 +146,7 @@ public Integer call() throws Exception { params.put(HEADER_PARAMETERS, getRequestParameters()); String response = liveImportClient.executeHttpGetRequest(timeout, url, params); - SAXBuilder saxBuilder = new SAXBuilder(); - // disallow DTD parsing to ensure no XXE attacks can occur - saxBuilder.setFeature("http://apache.org/xml/features/disallow-doctype-decl",true); + SAXBuilder saxBuilder = XMLUtils.getSAXBuilder(); Document document = saxBuilder.build(new StringReader(response)); Element root = document.getRootElement(); XPathExpression xpath = XPathFactory.instance().compile("//*[@name=\"RecordsFound\"]", @@ -288,9 +287,7 @@ private boolean isIsi(String query) { private List splitToRecords(String recordsSrc) { try { - SAXBuilder saxBuilder = new SAXBuilder(); - // disallow DTD parsing to ensure no XXE attacks can occur - saxBuilder.setFeature("http://apache.org/xml/features/disallow-doctype-decl",true); + SAXBuilder saxBuilder = XMLUtils.getSAXBuilder(); Document document = saxBuilder.build(new StringReader(recordsSrc)); Element root = document.getRootElement(); String cData = XPathFactory.instance().compile("//*[@name=\"Records\"]", @@ -332,4 +329,4 @@ public void setApiKey(String apiKey) { this.apiKey = apiKey; } -} \ No newline at end of file +} diff --git a/dspace-api/src/main/java/org/dspace/license/CCLicenseConnectorServiceImpl.java b/dspace-api/src/main/java/org/dspace/license/CCLicenseConnectorServiceImpl.java index 1d777a2e13c8..3c9088bda5df 100644 --- a/dspace-api/src/main/java/org/dspace/license/CCLicenseConnectorServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/license/CCLicenseConnectorServiceImpl.java @@ -28,6 +28,7 @@ import org.apache.http.util.EntityUtils; import org.apache.logging.log4j.Logger; import org.dspace.app.client.DSpaceHttpClientFactory; +import org.dspace.app.util.XMLUtils; import org.dspace.services.ConfigurationService; import org.jdom2.Attribute; import org.jdom2.Document; @@ -50,7 +51,7 @@ public class CCLicenseConnectorServiceImpl implements CCLicenseConnectorService, private Logger log = org.apache.logging.log4j.LogManager.getLogger(CCLicenseConnectorServiceImpl.class); private CloseableHttpClient client; - protected SAXBuilder parser = new SAXBuilder(); + protected SAXBuilder parser = XMLUtils.getSAXBuilder(); private String postArgument = "answers"; private String postAnswerFormat = diff --git a/dspace-api/src/main/java/org/dspace/orcid/client/OrcidClientImpl.java b/dspace-api/src/main/java/org/dspace/orcid/client/OrcidClientImpl.java index da1de465251d..11bf444384cd 100644 --- a/dspace-api/src/main/java/org/dspace/orcid/client/OrcidClientImpl.java +++ b/dspace-api/src/main/java/org/dspace/orcid/client/OrcidClientImpl.java @@ -43,6 +43,7 @@ import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.message.BasicNameValuePair; import org.dspace.app.client.DSpaceHttpClientFactory; +import org.dspace.app.util.XMLUtils; import org.dspace.orcid.OrcidToken; import org.dspace.orcid.exception.OrcidClientException; import org.dspace.orcid.model.OrcidEntityType; @@ -351,8 +352,7 @@ private String marshall(Object object) throws JAXBException { @SuppressWarnings("unchecked") private T unmarshall(HttpEntity entity, Class clazz) throws Exception { JAXBContext jaxbContext = JAXBContext.newInstance(clazz); - XMLInputFactory xmlInputFactory = XMLInputFactory.newFactory(); - xmlInputFactory.setProperty(XMLInputFactory.SUPPORT_DTD, false); + XMLInputFactory xmlInputFactory = XMLUtils.getXMLInputFactory(); XMLStreamReader xmlStreamReader = xmlInputFactory.createXMLStreamReader(entity.getContent()); Unmarshaller unmarshaller = jaxbContext.createUnmarshaller(); return (T) unmarshaller.unmarshal(xmlStreamReader); diff --git a/dspace-api/src/main/java/org/dspace/vocabulary/ControlledVocabulary.java b/dspace-api/src/main/java/org/dspace/vocabulary/ControlledVocabulary.java index 7f2bdc6ef771..bd19a1254fe3 100644 --- a/dspace-api/src/main/java/org/dspace/vocabulary/ControlledVocabulary.java +++ b/dspace-api/src/main/java/org/dspace/vocabulary/ControlledVocabulary.java @@ -12,7 +12,6 @@ import java.util.ArrayList; import java.util.List; import javax.xml.parsers.DocumentBuilder; -import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import javax.xml.transform.TransformerException; import javax.xml.xpath.XPath; @@ -20,6 +19,7 @@ import javax.xml.xpath.XPathExpressionException; import javax.xml.xpath.XPathFactory; +import org.dspace.app.util.XMLUtils; import org.dspace.services.ConfigurationService; import org.dspace.services.factory.DSpaceServicesFactory; import org.w3c.dom.Document; @@ -71,7 +71,7 @@ public static ControlledVocabulary loadVocabulary(String fileName) File controlledVocFile = new File(filePath.toString()); if (controlledVocFile.exists()) { - DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder(); + DocumentBuilder builder = XMLUtils.getDocumentBuilder(); Document document = builder.parse(controlledVocFile); XPath xPath = XPathFactory.newInstance().newXPath(); Node node = (Node) xPath.compile("node").evaluate(document, XPathConstants.NODE); From d3f62c7f1be476c036236ba77661cbed92f44af1 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Wed, 2 Jul 2025 15:30:04 -0500 Subject: [PATCH 790/979] EPO and PubMed only need to allow for DOCTYPEs. All other XML security changes can be used. --- .../service/EpoImportMetadataSourceServiceImpl.java | 4 ++++ .../service/PubmedImportMetadataSourceServiceImpl.java | 10 +++++----- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/importer/external/epo/service/EpoImportMetadataSourceServiceImpl.java b/dspace-api/src/main/java/org/dspace/importer/external/epo/service/EpoImportMetadataSourceServiceImpl.java index 7edd3f9d01c5..552f607827a8 100644 --- a/dspace-api/src/main/java/org/dspace/importer/external/epo/service/EpoImportMetadataSourceServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/importer/external/epo/service/EpoImportMetadataSourceServiceImpl.java @@ -399,6 +399,10 @@ private Integer countDocument(String bearer, String query) { String response = liveImportClient.executeHttpGetRequest(1000, uriBuilder.toString(), params); SAXBuilder saxBuilder = XMLUtils.getSAXBuilder(); + // To properly parse EPO responses, we must allow DOCTYPEs overall. But, we can still apply all the + // other default XXE protections, including disabling external entities and entity expansion. + // NOTE: we only need to allow DOCTYPEs for this initial API call. All other calls have them disabled. + saxBuilder.setFeature("http://apache.org/xml/features/disallow-doctype-decl", false); Document document = saxBuilder.build(new StringReader(response)); Element root = document.getRootElement(); diff --git a/dspace-api/src/main/java/org/dspace/importer/external/pubmed/service/PubmedImportMetadataSourceServiceImpl.java b/dspace-api/src/main/java/org/dspace/importer/external/pubmed/service/PubmedImportMetadataSourceServiceImpl.java index 7718e59e483e..c870161bf9bd 100644 --- a/dspace-api/src/main/java/org/dspace/importer/external/pubmed/service/PubmedImportMetadataSourceServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/importer/external/pubmed/service/PubmedImportMetadataSourceServiceImpl.java @@ -235,12 +235,9 @@ private String getSingleElementValue(String src, String elementName) { try { SAXBuilder saxBuilder = XMLUtils.getSAXBuilder(); - // To properly parse PubMed responses, we must allow DOCTYPE/DTDs overall but - // we can still take advantage of entities themselves being disabled, and not - // expanded. + // To properly parse PubMed responses, we must allow DOCTYPEs overall. But, we can still apply all the + // other default XXE protections, including disabling external entities and entity expansion. saxBuilder.setFeature("http://apache.org/xml/features/disallow-doctype-decl", false); - saxBuilder.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", - true); Document document = saxBuilder.build(new StringReader(src)); Element root = document.getRootElement(); @@ -358,6 +355,9 @@ public Collection call() throws Exception { private List splitToRecords(String recordsSrc) { try { SAXBuilder saxBuilder = XMLUtils.getSAXBuilder(); + // To properly parse PubMed responses, we must allow DOCTYPEs overall. But, we can still apply all the + // other default XXE protections, including disabling external entities and entity expansion. + saxBuilder.setFeature("http://apache.org/xml/features/disallow-doctype-decl", false); Document document = saxBuilder.build(new StringReader(recordsSrc)); Element root = document.getRootElement(); From f7dcbf1b44fb2d973000cedadb4d1f5a48bd70a1 Mon Sep 17 00:00:00 2001 From: Kim Shepherd Date: Wed, 2 Jul 2025 10:39:50 +0200 Subject: [PATCH 791/979] Safe and consistent XML entity handling in parsers --- .../dspace/administer/RegistryImporter.java | 7 +- .../org/dspace/administer/RegistryLoader.java | 9 +- .../org/dspace/administer/StructBuilder.java | 6 +- .../app/itemimport/ItemImportServiceImpl.java | 8 +- .../dspace/app/itemupdate/ItemArchive.java | 18 +-- .../dspace/app/launcher/ScriptLauncher.java | 3 +- .../app/sfx/SFXFileReaderServiceImpl.java | 5 +- .../org/dspace/app/util/DCInputsReader.java | 6 +- .../dspace/app/util/InitializeEntities.java | 6 +- .../app/util/SubmissionConfigReader.java | 9 +- .../java/org/dspace/app/util/XMLUtils.java | 123 ++++++++++++++++++ .../crosswalk/METSDisseminationCrosswalk.java | 3 +- .../crosswalk/MODSDisseminationCrosswalk.java | 3 +- .../content/crosswalk/QDCCrosswalk.java | 3 +- .../content/crosswalk/RoleCrosswalk.java | 3 +- .../crosswalk/XSLTIngestionCrosswalk.java | 3 +- .../dspace/content/packager/METSManifest.java | 8 +- .../dspace/content/packager/RoleIngester.java | 4 +- .../ctask/general/MetadataWebService.java | 11 +- .../provider/orcid/xml/Converter.java | 4 +- .../identifier/doi/DataCiteConnector.java | 3 +- .../ArXivImportMetadataSourceServiceImpl.java | 5 +- .../CiniiImportMetadataSourceServiceImpl.java | 15 +-- .../crossref/CrossRefAbstractProcessor.java | 5 +- .../EpoImportMetadataSourceServiceImpl.java | 13 +- ...PubmedImportMetadataSourceServiceImpl.java | 20 ++- ...PubmedEuropeMetadataSourceServiceImpl.java | 11 +- ...ScopusImportMetadataSourceServiceImpl.java | 9 +- .../WOSImportMetadataSourceServiceImpl.java | 11 +- .../CCLicenseConnectorServiceImpl.java | 3 +- .../dspace/orcid/client/OrcidClientImpl.java | 4 +- .../vocabulary/ControlledVocabulary.java | 4 +- 32 files changed, 224 insertions(+), 121 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/administer/RegistryImporter.java b/dspace-api/src/main/java/org/dspace/administer/RegistryImporter.java index 27a653421312..c74e56bce890 100644 --- a/dspace-api/src/main/java/org/dspace/administer/RegistryImporter.java +++ b/dspace-api/src/main/java/org/dspace/administer/RegistryImporter.java @@ -10,7 +10,6 @@ import java.io.File; import java.io.IOException; import javax.xml.parsers.DocumentBuilder; -import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import javax.xml.transform.TransformerException; import javax.xml.xpath.XPath; @@ -18,6 +17,7 @@ import javax.xml.xpath.XPathExpressionException; import javax.xml.xpath.XPathFactory; +import org.dspace.app.util.XMLUtils; import org.w3c.dom.Document; import org.w3c.dom.Node; import org.w3c.dom.NodeList; @@ -49,8 +49,9 @@ private RegistryImporter() { } */ public static Document loadXML(String filename) throws IOException, ParserConfigurationException, SAXException { - DocumentBuilder builder = DocumentBuilderFactory.newInstance() - .newDocumentBuilder(); + // This XML builder will *not* disable external entities as XML + // registries are considered trusted content + DocumentBuilder builder = XMLUtils.getTrustedDocumentBuilder(); Document document = builder.parse(new File(filename)); diff --git a/dspace-api/src/main/java/org/dspace/administer/RegistryLoader.java b/dspace-api/src/main/java/org/dspace/administer/RegistryLoader.java index d503bfc00b7f..8bb72e18521e 100644 --- a/dspace-api/src/main/java/org/dspace/administer/RegistryLoader.java +++ b/dspace-api/src/main/java/org/dspace/administer/RegistryLoader.java @@ -13,7 +13,6 @@ import java.util.ArrayList; import java.util.Arrays; import javax.xml.parsers.DocumentBuilder; -import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import javax.xml.transform.TransformerException; import javax.xml.xpath.XPath; @@ -29,6 +28,7 @@ import org.apache.commons.cli.ParseException; import org.apache.commons.lang3.StringUtils; import org.apache.logging.log4j.Logger; +import org.dspace.app.util.XMLUtils; import org.dspace.authorize.AuthorizeException; import org.dspace.content.BitstreamFormat; import org.dspace.content.factory.ContentServiceFactory; @@ -266,8 +266,9 @@ private static void loadFormat(Context context, Node node) */ private static Document loadXML(String filename) throws IOException, ParserConfigurationException, SAXException { - DocumentBuilder builder = DocumentBuilderFactory.newInstance() - .newDocumentBuilder(); + // This XML builder will *not* disable external entities as XML + // registries are considered trusted content + DocumentBuilder builder = XMLUtils.getTrustedDocumentBuilder(); return builder.parse(new File(filename)); } @@ -351,4 +352,4 @@ private static String[] getRepeatedElementData(Node parentElement, return data; } -} \ No newline at end of file +} diff --git a/dspace-api/src/main/java/org/dspace/administer/StructBuilder.java b/dspace-api/src/main/java/org/dspace/administer/StructBuilder.java index 8bbcfe0ff753..f2577a37b176 100644 --- a/dspace-api/src/main/java/org/dspace/administer/StructBuilder.java +++ b/dspace-api/src/main/java/org/dspace/administer/StructBuilder.java @@ -27,7 +27,6 @@ import java.util.List; import java.util.Map; import javax.xml.parsers.DocumentBuilder; -import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import javax.xml.transform.TransformerException; import javax.xml.xpath.XPath; @@ -43,6 +42,7 @@ import org.apache.commons.cli.Options; import org.apache.commons.cli.ParseException; import org.apache.commons.lang3.StringUtils; +import org.dspace.app.util.XMLUtils; import org.dspace.authorize.AuthorizeException; import org.dspace.content.Collection; import org.dspace.content.Community; @@ -613,8 +613,8 @@ private static String validateCollections(NodeList collections, int level) */ private static org.w3c.dom.Document loadXML(InputStream input) throws IOException, ParserConfigurationException, SAXException { - DocumentBuilder builder = DocumentBuilderFactory.newInstance() - .newDocumentBuilder(); + // This builder factory does not disable external DTD, entities, etc. + DocumentBuilder builder = XMLUtils.getTrustedDocumentBuilder(); org.w3c.dom.Document document = builder.parse(input); diff --git a/dspace-api/src/main/java/org/dspace/app/itemimport/ItemImportServiceImpl.java b/dspace-api/src/main/java/org/dspace/app/itemimport/ItemImportServiceImpl.java index 1e219ee6314c..3af383b04caf 100644 --- a/dspace-api/src/main/java/org/dspace/app/itemimport/ItemImportServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/app/itemimport/ItemImportServiceImpl.java @@ -48,7 +48,6 @@ import java.util.zip.ZipFile; import javax.mail.MessagingException; import javax.xml.parsers.DocumentBuilder; -import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import javax.xml.transform.TransformerException; import javax.xml.xpath.XPath; @@ -67,6 +66,7 @@ import org.dspace.app.itemimport.service.ItemImportService; import org.dspace.app.util.LocalSchemaFilenameFilter; import org.dspace.app.util.RelationshipUtils; +import org.dspace.app.util.XMLUtils; import org.dspace.authorize.AuthorizeException; import org.dspace.authorize.ResourcePolicy; import org.dspace.authorize.service.AuthorizeService; @@ -179,6 +179,8 @@ public class ItemImportServiceImpl implements ItemImportService, InitializingBea @Autowired(required = true) protected MetadataValueService metadataValueService; + protected DocumentBuilder builder; + protected String tempWorkDir; protected boolean isTest = false; @@ -1888,9 +1890,7 @@ protected String getStringValue(Node node) { */ protected Document loadXML(String filename) throws IOException, ParserConfigurationException, SAXException { - DocumentBuilder builder = DocumentBuilderFactory.newInstance() - .newDocumentBuilder(); - + DocumentBuilder builder = XMLUtils.getDocumentBuilder(); return builder.parse(new File(filename)); } diff --git a/dspace-api/src/main/java/org/dspace/app/itemupdate/ItemArchive.java b/dspace-api/src/main/java/org/dspace/app/itemupdate/ItemArchive.java index 26de45caf77e..7dda65a0a75b 100644 --- a/dspace-api/src/main/java/org/dspace/app/itemupdate/ItemArchive.java +++ b/dspace-api/src/main/java/org/dspace/app/itemupdate/ItemArchive.java @@ -23,8 +23,6 @@ import java.util.Iterator; import java.util.List; import java.util.UUID; -import javax.xml.parsers.DocumentBuilder; -import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import javax.xml.transform.Transformer; import javax.xml.transform.TransformerConfigurationException; @@ -33,6 +31,7 @@ import org.apache.logging.log4j.Logger; import org.dspace.app.util.LocalSchemaFilenameFilter; +import org.dspace.app.util.XMLUtils; import org.dspace.authorize.AuthorizeException; import org.dspace.content.DSpaceObject; import org.dspace.content.Item; @@ -52,7 +51,6 @@ public class ItemArchive { public static final String DUBLIN_CORE_XML = "dublin_core.xml"; - protected static DocumentBuilder builder = null; protected Transformer transformer = null; protected List dtomList = null; @@ -95,14 +93,14 @@ public static ItemArchive create(Context context, File dir, String itemField) InputStream is = null; try { is = new FileInputStream(new File(dir, DUBLIN_CORE_XML)); - itarch.dtomList = MetadataUtilities.loadDublinCore(getDocumentBuilder(), is); + itarch.dtomList = MetadataUtilities.loadDublinCore(XMLUtils.getDocumentBuilder(), is); //The code to search for local schema files was copied from org.dspace.app.itemimport // .ItemImportServiceImpl.java File file[] = dir.listFiles(new LocalSchemaFilenameFilter()); for (int i = 0; i < file.length; i++) { is = new FileInputStream(file[i]); - itarch.dtomList.addAll(MetadataUtilities.loadDublinCore(getDocumentBuilder(), is)); + itarch.dtomList.addAll(MetadataUtilities.loadDublinCore(XMLUtils.getDocumentBuilder(), is)); } } finally { if (is != null) { @@ -126,14 +124,6 @@ public static ItemArchive create(Context context, File dir, String itemField) return itarch; } - protected static DocumentBuilder getDocumentBuilder() - throws ParserConfigurationException { - if (builder == null) { - builder = DocumentBuilderFactory.newInstance().newDocumentBuilder(); - } - return builder; - } - /** * Getter for Transformer * @@ -318,7 +308,7 @@ public void writeUndo(File undoDir) try { out = new FileOutputStream(new File(dir, "dublin_core.xml")); - Document doc = MetadataUtilities.writeDublinCore(getDocumentBuilder(), undoDtomList); + Document doc = MetadataUtilities.writeDublinCore(XMLUtils.getDocumentBuilder(), undoDtomList); MetadataUtilities.writeDocument(doc, getTransformer(), out); // if undo has delete bitstream diff --git a/dspace-api/src/main/java/org/dspace/app/launcher/ScriptLauncher.java b/dspace-api/src/main/java/org/dspace/app/launcher/ScriptLauncher.java index 89a416bfa883..ab8807c2cae3 100644 --- a/dspace-api/src/main/java/org/dspace/app/launcher/ScriptLauncher.java +++ b/dspace-api/src/main/java/org/dspace/app/launcher/ScriptLauncher.java @@ -19,6 +19,7 @@ import org.apache.commons.cli.ParseException; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import org.dspace.app.util.XMLUtils; import org.dspace.core.Context; import org.dspace.scripts.DSpaceRunnable; import org.dspace.scripts.DSpaceRunnable.StepResult; @@ -314,7 +315,7 @@ public static Document getConfig(DSpaceKernelImpl kernelImpl) { String config = kernelImpl.getConfigurationService().getProperty("dspace.dir") + System.getProperty("file.separator") + "config" + System.getProperty("file.separator") + "launcher.xml"; - SAXBuilder saxBuilder = new SAXBuilder(); + SAXBuilder saxBuilder = XMLUtils.getSAXBuilder(); Document doc = null; try { doc = saxBuilder.build(config); diff --git a/dspace-api/src/main/java/org/dspace/app/sfx/SFXFileReaderServiceImpl.java b/dspace-api/src/main/java/org/dspace/app/sfx/SFXFileReaderServiceImpl.java index 184f00a53e59..d3b447374a2c 100644 --- a/dspace-api/src/main/java/org/dspace/app/sfx/SFXFileReaderServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/app/sfx/SFXFileReaderServiceImpl.java @@ -18,6 +18,7 @@ import org.apache.commons.lang3.StringUtils; import org.apache.logging.log4j.Logger; import org.dspace.app.sfx.service.SFXFileReaderService; +import org.dspace.app.util.XMLUtils; import org.dspace.content.DCPersonName; import org.dspace.content.Item; import org.dspace.content.MetadataValue; @@ -79,9 +80,9 @@ public Document parseFile(String fileName) { log.info("Parsing XML file... " + fileName); DocumentBuilder docBuilder; Document doc = null; - DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory.newInstance(); - docBuilderFactory.setIgnoringElementContentWhitespace(true); try { + DocumentBuilderFactory docBuilderFactory = XMLUtils.getDocumentBuilderFactory(); + docBuilderFactory.setIgnoringElementContentWhitespace(true); docBuilder = docBuilderFactory.newDocumentBuilder(); } catch (ParserConfigurationException e) { log.error("Wrong parser configuration: " + e.getMessage()); diff --git a/dspace-api/src/main/java/org/dspace/app/util/DCInputsReader.java b/dspace-api/src/main/java/org/dspace/app/util/DCInputsReader.java index c77c3bf10e08..d2f3ac0dc5d9 100644 --- a/dspace-api/src/main/java/org/dspace/app/util/DCInputsReader.java +++ b/dspace-api/src/main/java/org/dspace/app/util/DCInputsReader.java @@ -121,7 +121,11 @@ private void buildInputs(String fileName) String uri = "file:" + new File(fileName).getAbsolutePath(); try { - DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); + // This document builder factory will *not* disable external + // entities as they can be useful in managing large forms, but + // it is up to site administrators to validate the XML they are + // storing + DocumentBuilderFactory factory = XMLUtils.getTrustedDocumentBuilderFactory(); factory.setValidating(false); factory.setIgnoringComments(true); factory.setIgnoringElementContentWhitespace(true); diff --git a/dspace-api/src/main/java/org/dspace/app/util/InitializeEntities.java b/dspace-api/src/main/java/org/dspace/app/util/InitializeEntities.java index 8d3964a3e3c7..4e64c18fcedd 100644 --- a/dspace-api/src/main/java/org/dspace/app/util/InitializeEntities.java +++ b/dspace-api/src/main/java/org/dspace/app/util/InitializeEntities.java @@ -11,7 +11,6 @@ import java.io.IOException; import java.sql.SQLException; import javax.xml.parsers.DocumentBuilder; -import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import org.apache.commons.cli.CommandLine; @@ -139,8 +138,9 @@ private void run(String fileLocation) throws SQLException, AuthorizeException { private void parseXMLToRelations(Context context, String fileLocation) throws AuthorizeException { try { File fXmlFile = new File(fileLocation); - DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance(); - DocumentBuilder dBuilder = dbFactory.newDocumentBuilder(); + // This XML builder will allow external entities, so the relationship types XML should + // be considered trusted by administrators + DocumentBuilder dBuilder = XMLUtils.getTrustedDocumentBuilder(); Document doc = dBuilder.parse(fXmlFile); doc.getDocumentElement().normalize(); diff --git a/dspace-api/src/main/java/org/dspace/app/util/SubmissionConfigReader.java b/dspace-api/src/main/java/org/dspace/app/util/SubmissionConfigReader.java index 70c5092602a3..1914ded86b66 100644 --- a/dspace-api/src/main/java/org/dspace/app/util/SubmissionConfigReader.java +++ b/dspace-api/src/main/java/org/dspace/app/util/SubmissionConfigReader.java @@ -160,8 +160,11 @@ private void buildInputs(String fileName) throws SubmissionConfigReaderException String uri = "file:" + new File(fileName).getAbsolutePath(); try { - DocumentBuilderFactory factory = DocumentBuilderFactory - .newInstance(); + // This document builder factory will *not* disable external + // entities as they can be useful in managing large forms, but + // it is up to site administrators to validate the XML they are + // storing + DocumentBuilderFactory factory = XMLUtils.getTrustedDocumentBuilderFactory(); factory.setValidating(false); factory.setIgnoringComments(true); factory.setIgnoringElementContentWhitespace(true); @@ -681,4 +684,4 @@ public List getCollectionsBySubmissionConfig(Context context, String } return results; } -} \ No newline at end of file +} diff --git a/dspace-api/src/main/java/org/dspace/app/util/XMLUtils.java b/dspace-api/src/main/java/org/dspace/app/util/XMLUtils.java index c39d0d26fd5e..389d53fe6da4 100644 --- a/dspace-api/src/main/java/org/dspace/app/util/XMLUtils.java +++ b/dspace-api/src/main/java/org/dspace/app/util/XMLUtils.java @@ -9,8 +9,13 @@ import java.util.ArrayList; import java.util.List; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.stream.XMLInputFactory; import org.apache.commons.lang3.StringUtils; +import org.jdom2.input.SAXBuilder; import org.w3c.dom.Element; import org.w3c.dom.NodeList; @@ -161,4 +166,122 @@ public static List getElementValueArrayList(Element rootElement, } return result; } + + /** + * Initialize and return a javax DocumentBuilderFactory with NO security + * applied. This is intended only for internal, administrative/configuration + * use where external entities and other dangerous features are actually + * purposefully included. + * The method here is tiny, but may be expanded with other features like + * whitespace handling, and calling this method name helps to document + * the fact that the caller knows it is trusting the XML source / factory. + * + * @return document builder factory to generate new builders + * @throws ParserConfigurationException + */ + public static DocumentBuilderFactory getTrustedDocumentBuilderFactory() + throws ParserConfigurationException { + DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); + return factory; + } + + /** + * Initialize and return the javax DocumentBuilderFactory with some basic security + * applied to avoid XXE attacks and other unwanted content inclusion + * @return document builder factory to generate new builders + * @throws ParserConfigurationException + */ + public static DocumentBuilderFactory getDocumentBuilderFactory() + throws ParserConfigurationException { + DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); + // No DOCTYPE / DTDs + factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true); + // No external general entities + factory.setFeature("http://xml.org/sax/features/external-general-entities", false); + // No external parameter entities + factory.setFeature("http://xml.org/sax/features/external-parameter-entities", false); + // No external DTDs + factory.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false); + // Even if entities somehow get defined, they will not be expanded + factory.setExpandEntityReferences(false); + // Disable "XInclude" markup processing + factory.setXIncludeAware(false); + + return factory; + } + + /** + * Initialize and return a javax DocumentBuilder with NO security + * applied. This is intended only for internal, administrative/configuration + * use where external entities and other dangerous features are actually + * purposefully included. + * The method here is tiny, but may be expanded with other features like + * whitespace handling, and calling this method name helps to document + * the fact that the caller knows it is trusting the XML source / builder + * + * @return document builder with no security features set + * @throws ParserConfigurationException + */ + public static DocumentBuilder getTrustedDocumentBuilder() + throws ParserConfigurationException { + return getTrustedDocumentBuilderFactory().newDocumentBuilder(); + } + + /** + * Initialize and return the javax DocumentBuilder with some basic security applied + * to avoid XXE attacks and other unwanted content inclusion + * @return document builder for use in XML parsing + * @throws ParserConfigurationException + */ + public static DocumentBuilder getDocumentBuilder() + throws ParserConfigurationException { + return getDocumentBuilderFactory().newDocumentBuilder(); + } + + /** + * Initialize and return the SAX document builder with some basic security applied + * to avoid XXE attacks and other unwanted content inclusion + * @return SAX document builder for use in XML parsing + */ + public static SAXBuilder getSAXBuilder() { + return getSAXBuilder(false); + } + + /** + * Initialize and return the SAX document builder with some basic security applied + * to avoid XXE attacks and other unwanted content inclusion + * @param validate whether to use JDOM XSD validation + * @return SAX document builder for use in XML parsing + */ + public static SAXBuilder getSAXBuilder(boolean validate) { + SAXBuilder saxBuilder = new SAXBuilder(); + if (validate) { + saxBuilder.setValidation(true); + } + // No DOCTYPE / DTDs + saxBuilder.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true); + // No external general entities + saxBuilder.setFeature("http://xml.org/sax/features/external-general-entities", false); + // No external parameter entities + saxBuilder.setFeature("http://xml.org/sax/features/external-parameter-entities", false); + // No external DTDs + saxBuilder.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false); + // Don't expand entities + saxBuilder.setExpandEntities(false); + + return saxBuilder; + } + + /** + * Initialize and return the Java XML Input Factory with some basic security applied + * to avoid XXE attacks and other unwanted content inclusion + * @return XML input factory for use in XML parsing + */ + public static XMLInputFactory getXMLInputFactory() { + XMLInputFactory xmlInputFactory = XMLInputFactory.newFactory(); + xmlInputFactory.setProperty(XMLInputFactory.SUPPORT_DTD, false); + + return xmlInputFactory; + } + } diff --git a/dspace-api/src/main/java/org/dspace/content/crosswalk/METSDisseminationCrosswalk.java b/dspace-api/src/main/java/org/dspace/content/crosswalk/METSDisseminationCrosswalk.java index b8a4a8aef390..5ceacc933e4c 100644 --- a/dspace-api/src/main/java/org/dspace/content/crosswalk/METSDisseminationCrosswalk.java +++ b/dspace-api/src/main/java/org/dspace/content/crosswalk/METSDisseminationCrosswalk.java @@ -14,6 +14,7 @@ import java.util.List; import org.apache.commons.lang3.ArrayUtils; +import org.dspace.app.util.XMLUtils; import org.dspace.authorize.AuthorizeException; import org.dspace.content.DSpaceObject; import org.dspace.content.packager.PackageDisseminator; @@ -129,7 +130,7 @@ public Element disseminateElement(Context context, DSpaceObject dso) try { //Return just the root Element of the METS file - SAXBuilder builder = new SAXBuilder(); + SAXBuilder builder = XMLUtils.getSAXBuilder(); Document metsDocument = builder.build(tempFile); return metsDocument.getRootElement(); } catch (JDOMException je) { diff --git a/dspace-api/src/main/java/org/dspace/content/crosswalk/MODSDisseminationCrosswalk.java b/dspace-api/src/main/java/org/dspace/content/crosswalk/MODSDisseminationCrosswalk.java index 1e63be5ba1b9..205b3ef5b343 100644 --- a/dspace-api/src/main/java/org/dspace/content/crosswalk/MODSDisseminationCrosswalk.java +++ b/dspace-api/src/main/java/org/dspace/content/crosswalk/MODSDisseminationCrosswalk.java @@ -22,6 +22,7 @@ import org.apache.commons.lang3.ArrayUtils; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import org.dspace.app.util.XMLUtils; import org.dspace.authorize.AuthorizeException; import org.dspace.content.Collection; import org.dspace.content.Community; @@ -144,7 +145,7 @@ public static String[] getPluginNames() { MODS_NS.getURI() + " " + MODS_XSD; private static final XMLOutputter outputUgly = new XMLOutputter(); - private static final SAXBuilder builder = new SAXBuilder(); + private static final SAXBuilder builder = XMLUtils.getSAXBuilder(); private Map modsMap = null; diff --git a/dspace-api/src/main/java/org/dspace/content/crosswalk/QDCCrosswalk.java b/dspace-api/src/main/java/org/dspace/content/crosswalk/QDCCrosswalk.java index 2fdbaaad003e..51e6357d93e1 100644 --- a/dspace-api/src/main/java/org/dspace/content/crosswalk/QDCCrosswalk.java +++ b/dspace-api/src/main/java/org/dspace/content/crosswalk/QDCCrosswalk.java @@ -22,6 +22,7 @@ import org.apache.commons.lang3.ArrayUtils; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import org.dspace.app.util.XMLUtils; import org.dspace.authorize.AuthorizeException; import org.dspace.content.DSpaceObject; import org.dspace.content.Item; @@ -125,7 +126,7 @@ public class QDCCrosswalk extends SelfNamedPlugin // XML schemaLocation fragment for this crosswalk, from config. private String schemaLocation = null; - private static final SAXBuilder builder = new SAXBuilder(); + private static final SAXBuilder builder = XMLUtils.getSAXBuilder(); protected ItemService itemService = ContentServiceFactory.getInstance().getItemService(); diff --git a/dspace-api/src/main/java/org/dspace/content/crosswalk/RoleCrosswalk.java b/dspace-api/src/main/java/org/dspace/content/crosswalk/RoleCrosswalk.java index 2c763036ce33..8d5bf49902cc 100644 --- a/dspace-api/src/main/java/org/dspace/content/crosswalk/RoleCrosswalk.java +++ b/dspace-api/src/main/java/org/dspace/content/crosswalk/RoleCrosswalk.java @@ -13,6 +13,7 @@ import java.sql.SQLException; import java.util.List; +import org.dspace.app.util.XMLUtils; import org.dspace.authorize.AuthorizeException; import org.dspace.content.DSpaceObject; import org.dspace.content.packager.PackageDisseminator; @@ -208,7 +209,7 @@ public Element disseminateElement(Context context, DSpaceObject dso) try { //Try to parse our XML results (which were disseminated by the Packager) - SAXBuilder builder = new SAXBuilder(); + SAXBuilder builder = XMLUtils.getSAXBuilder(); Document xmlDocument = builder.build(tempFile); //If XML parsed successfully, return root element of doc if (xmlDocument != null && xmlDocument.hasRootElement()) { diff --git a/dspace-api/src/main/java/org/dspace/content/crosswalk/XSLTIngestionCrosswalk.java b/dspace-api/src/main/java/org/dspace/content/crosswalk/XSLTIngestionCrosswalk.java index 63ef5f7336c7..b07b2b2228e4 100644 --- a/dspace-api/src/main/java/org/dspace/content/crosswalk/XSLTIngestionCrosswalk.java +++ b/dspace-api/src/main/java/org/dspace/content/crosswalk/XSLTIngestionCrosswalk.java @@ -18,6 +18,7 @@ import org.apache.commons.lang3.ArrayUtils; import org.apache.logging.log4j.Logger; +import org.dspace.app.util.XMLUtils; import org.dspace.authorize.AuthorizeException; import org.dspace.content.Collection; import org.dspace.content.Community; @@ -297,7 +298,7 @@ public static void main(String[] argv) throws Exception { "Failed to initialize transformer, probably error loading stylesheet."); } - SAXBuilder builder = new SAXBuilder(); + SAXBuilder builder = XMLUtils.getSAXBuilder(); Document inDoc = builder.build(new FileInputStream(argv[i + 1])); XMLOutputter outputter = new XMLOutputter(Format.getPrettyFormat()); List dimList; diff --git a/dspace-api/src/main/java/org/dspace/content/packager/METSManifest.java b/dspace-api/src/main/java/org/dspace/content/packager/METSManifest.java index 3399bdf0f07e..a1ed3c124374 100644 --- a/dspace-api/src/main/java/org/dspace/content/packager/METSManifest.java +++ b/dspace-api/src/main/java/org/dspace/content/packager/METSManifest.java @@ -20,6 +20,7 @@ import org.apache.commons.codec.binary.Base64; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import org.dspace.app.util.XMLUtils; import org.dspace.authorize.AuthorizeException; import org.dspace.content.Bitstream; import org.dspace.content.Bundle; @@ -265,12 +266,13 @@ protected METSManifest(SAXBuilder builder, Element mets, String configName) { public static METSManifest create(InputStream is, boolean validate, String configName) throws IOException, MetadataValidationException { - SAXBuilder builder = new SAXBuilder(validate); + SAXBuilder builder = XMLUtils.getSAXBuilder(); builder.setIgnoringElementContentWhitespace(true); // Set validation feature if (validate) { + builder.setValidation(true); builder.setFeature("http://apache.org/xml/features/validation/schema", true); // Tell the parser where local copies of schemas are, to speed up @@ -278,10 +280,6 @@ public static METSManifest create(InputStream is, boolean validate, String confi if (localSchemas.length() > 0) { builder.setProperty("http://apache.org/xml/properties/schema/external-schemaLocation", localSchemas); } - } else { - // disallow DTD parsing to ensure no XXE attacks can occur. - // See https://cheatsheetseries.owasp.org/cheatsheets/XML_External_Entity_Prevention_Cheat_Sheet.html - builder.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true); } // Parse the METS file diff --git a/dspace-api/src/main/java/org/dspace/content/packager/RoleIngester.java b/dspace-api/src/main/java/org/dspace/content/packager/RoleIngester.java index 2ce3f50a3cbc..d71012ff8356 100644 --- a/dspace-api/src/main/java/org/dspace/content/packager/RoleIngester.java +++ b/dspace-api/src/main/java/org/dspace/content/packager/RoleIngester.java @@ -386,7 +386,7 @@ public void ingestStream(Context context, DSpaceObject parent, Document document; try { - DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); + DocumentBuilderFactory dbf = XMLUtils.getDocumentBuilderFactory(); dbf.setIgnoringComments(true); dbf.setCoalescing(true); DocumentBuilder db = dbf.newDocumentBuilder(); @@ -420,7 +420,7 @@ public DSpaceObject ingest(Context context, DSpaceObject parent, Document document; try { - DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); + DocumentBuilderFactory dbf = XMLUtils.getDocumentBuilderFactory(); dbf.setIgnoringComments(true); dbf.setCoalescing(true); DocumentBuilder db = dbf.newDocumentBuilder(); diff --git a/dspace-api/src/main/java/org/dspace/ctask/general/MetadataWebService.java b/dspace-api/src/main/java/org/dspace/ctask/general/MetadataWebService.java index fc62d7a4b23f..1b618ad31fb7 100644 --- a/dspace-api/src/main/java/org/dspace/ctask/general/MetadataWebService.java +++ b/dspace-api/src/main/java/org/dspace/ctask/general/MetadataWebService.java @@ -37,6 +37,7 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.dspace.app.client.DSpaceHttpClientFactory; +import org.dspace.app.util.XMLUtils; import org.dspace.authorize.AuthorizeException; import org.dspace.content.DSpaceObject; import org.dspace.content.Item; @@ -176,7 +177,7 @@ public void init(Curator curator, String taskId) throws IOException { fieldSeparator = (fldSep != null) ? fldSep : " "; urlTemplate = taskProperty("template"); templateParam = urlTemplate.substring(urlTemplate.indexOf("{") + 1, - urlTemplate.indexOf("}")); + urlTemplate.indexOf("}")); String[] parsed = parseTransform(templateParam); lookupField = parsed[0]; lookupTransform = parsed[1]; @@ -204,13 +205,9 @@ public void init(Curator curator, String taskId) throws IOException { } } // initialize response document parser - DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); - factory.setNamespaceAware(true); try { - // disallow DTD parsing to ensure no XXE attacks can occur - // See https://cheatsheetseries.owasp.org/cheatsheets/XML_External_Entity_Prevention_Cheat_Sheet.html - factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true); - factory.setXIncludeAware(false); + DocumentBuilderFactory factory = XMLUtils.getDocumentBuilderFactory(); + factory.setNamespaceAware(true); docBuilder = factory.newDocumentBuilder(); } catch (ParserConfigurationException pcE) { log.error("caught exception: " + pcE); diff --git a/dspace-api/src/main/java/org/dspace/external/provider/orcid/xml/Converter.java b/dspace-api/src/main/java/org/dspace/external/provider/orcid/xml/Converter.java index 756b8654f285..af4e31e7fae2 100644 --- a/dspace-api/src/main/java/org/dspace/external/provider/orcid/xml/Converter.java +++ b/dspace-api/src/main/java/org/dspace/external/provider/orcid/xml/Converter.java @@ -31,9 +31,7 @@ public abstract class Converter { protected Object unmarshall(InputStream input, Class type) throws SAXException, URISyntaxException { try { - XMLInputFactory xmlInputFactory = XMLInputFactory.newFactory(); - // disallow DTD parsing to ensure no XXE attacks can occur - xmlInputFactory.setProperty(XMLInputFactory.SUPPORT_DTD, false); + XMLInputFactory xmlInputFactory = XMLUtils.getXMLInputFactory(); XMLStreamReader xmlStreamReader = xmlInputFactory.createXMLStreamReader(input); JAXBContext context = JAXBContext.newInstance(type); diff --git a/dspace-api/src/main/java/org/dspace/identifier/doi/DataCiteConnector.java b/dspace-api/src/main/java/org/dspace/identifier/doi/DataCiteConnector.java index 86c7b8322e81..c8e316eaf66c 100644 --- a/dspace-api/src/main/java/org/dspace/identifier/doi/DataCiteConnector.java +++ b/dspace-api/src/main/java/org/dspace/identifier/doi/DataCiteConnector.java @@ -38,6 +38,7 @@ import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.util.EntityUtils; import org.dspace.app.client.DSpaceHttpClientFactory; +import org.dspace.app.util.XMLUtils; import org.dspace.authorize.AuthorizeException; import org.dspace.content.DSpaceObject; import org.dspace.content.crosswalk.CrosswalkException; @@ -832,7 +833,7 @@ protected String extractAlternateIdentifier(Context context, String content) } // parse the XML - SAXBuilder saxBuilder = new SAXBuilder(); + SAXBuilder saxBuilder = XMLUtils.getSAXBuilder(); Document doc = null; try { doc = saxBuilder.build(new ByteArrayInputStream(content.getBytes("UTF-8"))); diff --git a/dspace-api/src/main/java/org/dspace/importer/external/arxiv/service/ArXivImportMetadataSourceServiceImpl.java b/dspace-api/src/main/java/org/dspace/importer/external/arxiv/service/ArXivImportMetadataSourceServiceImpl.java index 4369b0d48b46..a8d01fc3fd28 100644 --- a/dspace-api/src/main/java/org/dspace/importer/external/arxiv/service/ArXivImportMetadataSourceServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/importer/external/arxiv/service/ArXivImportMetadataSourceServiceImpl.java @@ -22,6 +22,7 @@ import javax.ws.rs.core.Response; import org.apache.commons.lang3.StringUtils; +import org.dspace.app.util.XMLUtils; import org.dspace.content.Item; import org.dspace.importer.external.datamodel.ImportRecord; import org.dspace.importer.external.datamodel.Query; @@ -218,7 +219,7 @@ public Integer call() throws Exception { if (response.getStatus() == 200) { String responseString = response.readEntity(String.class); - SAXBuilder saxBuilder = new SAXBuilder(); + SAXBuilder saxBuilder = XMLUtils.getSAXBuilder(); Document document = saxBuilder.build(new StringReader(responseString)); Element root = document.getRootElement(); @@ -399,7 +400,7 @@ private String getQuery(Query query) { private List splitToRecords(String recordsSrc) { try { - SAXBuilder saxBuilder = new SAXBuilder(); + SAXBuilder saxBuilder = XMLUtils.getSAXBuilder(); Document document = saxBuilder.build(new StringReader(recordsSrc)); Element root = document.getRootElement(); diff --git a/dspace-api/src/main/java/org/dspace/importer/external/cinii/CiniiImportMetadataSourceServiceImpl.java b/dspace-api/src/main/java/org/dspace/importer/external/cinii/CiniiImportMetadataSourceServiceImpl.java index 82a4b2d77968..41c80ab7fe64 100644 --- a/dspace-api/src/main/java/org/dspace/importer/external/cinii/CiniiImportMetadataSourceServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/importer/external/cinii/CiniiImportMetadataSourceServiceImpl.java @@ -26,6 +26,7 @@ import org.apache.http.client.utils.URIBuilder; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import org.dspace.app.util.XMLUtils; import org.dspace.content.Item; import org.dspace.importer.external.datamodel.ImportRecord; import org.dspace.importer.external.datamodel.Query; @@ -301,9 +302,7 @@ protected List search(String id, String appId) private List splitToRecords(String recordsSrc) { try { - SAXBuilder saxBuilder = new SAXBuilder(); - // disallow DTD parsing to ensure no XXE attacks can occur - saxBuilder.setFeature("http://apache.org/xml/features/disallow-doctype-decl",true); + SAXBuilder saxBuilder = XMLUtils.getSAXBuilder(); Document document = saxBuilder.build(new StringReader(recordsSrc)); Element root = document.getRootElement(); return root.getChildren(); @@ -356,9 +355,7 @@ private List getCiniiIds(String appId, Integer maxResult, String author, Map> params = new HashMap>(); String response = liveImportClient.executeHttpGetRequest(1000, uriBuilder.toString(), params); int url_len = this.url.length() - 1; - SAXBuilder saxBuilder = new SAXBuilder(); - // disallow DTD parsing to ensure no XXE attacks can occur - saxBuilder.setFeature("http://apache.org/xml/features/disallow-doctype-decl",true); + SAXBuilder saxBuilder = XMLUtils.getSAXBuilder(); Document document = saxBuilder.build(new StringReader(response)); Element root = document.getRootElement(); List namespaces = Arrays.asList( @@ -420,9 +417,7 @@ private Integer countCiniiElement(String appId, Integer maxResult, String author Map> params = new HashMap>(); String response = liveImportClient.executeHttpGetRequest(1000, uriBuilder.toString(), params); - SAXBuilder saxBuilder = new SAXBuilder(); - // disallow DTD parsing to ensure no XXE attacks can occur - saxBuilder.setFeature("http://apache.org/xml/features/disallow-doctype-decl",true); + SAXBuilder saxBuilder = XMLUtils.getSAXBuilder(); Document document = saxBuilder.build(new StringReader(response)); Element root = document.getRootElement(); List namespaces = Arrays @@ -449,4 +444,4 @@ private MetadatumDTO createIdentifier(String id) { return metadatumDTO; } -} \ No newline at end of file +} diff --git a/dspace-api/src/main/java/org/dspace/importer/external/crossref/CrossRefAbstractProcessor.java b/dspace-api/src/main/java/org/dspace/importer/external/crossref/CrossRefAbstractProcessor.java index 1b6da9d37b16..99f1ee37a54e 100644 --- a/dspace-api/src/main/java/org/dspace/importer/external/crossref/CrossRefAbstractProcessor.java +++ b/dspace-api/src/main/java/org/dspace/importer/external/crossref/CrossRefAbstractProcessor.java @@ -12,7 +12,6 @@ import java.util.ArrayList; import java.util.Collection; import javax.xml.parsers.DocumentBuilder; -import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import com.fasterxml.jackson.core.JsonProcessingException; @@ -21,6 +20,7 @@ import org.apache.commons.lang3.StringUtils; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import org.dspace.app.util.XMLUtils; import org.dspace.importer.external.metadatamapping.contributor.JsonPathMetadataProcessor; import org.w3c.dom.Document; import org.w3c.dom.Node; @@ -64,10 +64,9 @@ private String prettifyAbstract(String abstractValue) { } String xmlString = "" + abstractValue + ""; - DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); Document xmlDoc; try { - DocumentBuilder builder = factory.newDocumentBuilder(); + DocumentBuilder builder = XMLUtils.getDocumentBuilder(); InputSource is = new InputSource(new StringReader(xmlString)); xmlDoc = builder.parse(is); } catch (SAXException | IOException | ParserConfigurationException e) { diff --git a/dspace-api/src/main/java/org/dspace/importer/external/epo/service/EpoImportMetadataSourceServiceImpl.java b/dspace-api/src/main/java/org/dspace/importer/external/epo/service/EpoImportMetadataSourceServiceImpl.java index fbae302bca6a..7edd3f9d01c5 100644 --- a/dspace-api/src/main/java/org/dspace/importer/external/epo/service/EpoImportMetadataSourceServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/importer/external/epo/service/EpoImportMetadataSourceServiceImpl.java @@ -32,6 +32,7 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.apache.xerces.impl.dv.util.Base64; +import org.dspace.app.util.XMLUtils; import org.dspace.content.Item; import org.dspace.importer.external.datamodel.ImportRecord; import org.dspace.importer.external.datamodel.Query; @@ -397,9 +398,7 @@ private Integer countDocument(String bearer, String query) { String response = liveImportClient.executeHttpGetRequest(1000, uriBuilder.toString(), params); - SAXBuilder saxBuilder = new SAXBuilder(); - // disallow DTD parsing to ensure no XXE attacks can occur - saxBuilder.setFeature("http://apache.org/xml/features/disallow-doctype-decl",true); + SAXBuilder saxBuilder = XMLUtils.getSAXBuilder(); Document document = saxBuilder.build(new StringReader(response)); Element root = document.getRootElement(); @@ -436,9 +435,7 @@ private List searchDocumentIds(String bearer, String query, int s String response = liveImportClient.executeHttpGetRequest(1000, uriBuilder.toString(), params); - SAXBuilder saxBuilder = new SAXBuilder(); - // disallow DTD parsing to ensure no XXE attacks can occur - saxBuilder.setFeature("http://apache.org/xml/features/disallow-doctype-decl",true); + SAXBuilder saxBuilder = XMLUtils.getSAXBuilder(); Document document = saxBuilder.build(new StringReader(response)); Element root = document.getRootElement(); @@ -489,9 +486,7 @@ private List searchDocument(String bearer, String id, String docTy private List splitToRecords(String recordsSrc) { try { - SAXBuilder saxBuilder = new SAXBuilder(); - // disallow DTD parsing to ensure no XXE attacks can occur - saxBuilder.setFeature("http://apache.org/xml/features/disallow-doctype-decl",true); + SAXBuilder saxBuilder = XMLUtils.getSAXBuilder(); Document document = saxBuilder.build(new StringReader(recordsSrc)); Element root = document.getRootElement(); List namespaces = Arrays.asList(Namespace.getNamespace("ns", "http://www.epo.org/exchange")); diff --git a/dspace-api/src/main/java/org/dspace/importer/external/pubmed/service/PubmedImportMetadataSourceServiceImpl.java b/dspace-api/src/main/java/org/dspace/importer/external/pubmed/service/PubmedImportMetadataSourceServiceImpl.java index 000ef19eaec5..7718e59e483e 100644 --- a/dspace-api/src/main/java/org/dspace/importer/external/pubmed/service/PubmedImportMetadataSourceServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/importer/external/pubmed/service/PubmedImportMetadataSourceServiceImpl.java @@ -24,6 +24,7 @@ import com.google.common.io.CharStreams; import org.apache.commons.lang3.StringUtils; import org.apache.http.client.utils.URIBuilder; +import org.dspace.app.util.XMLUtils; import org.dspace.content.Item; import org.dspace.importer.external.datamodel.ImportRecord; import org.dspace.importer.external.datamodel.Query; @@ -233,11 +234,13 @@ private String getSingleElementValue(String src, String elementName) { String value = null; try { - SAXBuilder saxBuilder = new SAXBuilder(); - // Disallow external entities & entity expansion to protect against XXE attacks - // (NOTE: We receive errors if we disable all DTDs for PubMed, so this is the best we can do) - saxBuilder.setFeature("http://xml.org/sax/features/external-general-entities", false); - saxBuilder.setFeature("http://xml.org/sax/features/external-parameter-entities", false); + SAXBuilder saxBuilder = XMLUtils.getSAXBuilder(); + // To properly parse PubMed responses, we must allow DOCTYPE/DTDs overall but + // we can still take advantage of entities themselves being disabled, and not + // expanded. + saxBuilder.setFeature("http://apache.org/xml/features/disallow-doctype-decl", false); + saxBuilder.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", + true); Document document = saxBuilder.build(new StringReader(src)); Element root = document.getRootElement(); @@ -354,12 +357,7 @@ public Collection call() throws Exception { private List splitToRecords(String recordsSrc) { try { - SAXBuilder saxBuilder = new SAXBuilder(); - // Disallow external entities & entity expansion to protect against XXE attacks - // (NOTE: We receive errors if we disable all DTDs for PubMed, so this is the best we can do) - saxBuilder.setFeature("http://xml.org/sax/features/external-general-entities", false); - saxBuilder.setFeature("http://xml.org/sax/features/external-parameter-entities", false); - saxBuilder.setExpandEntities(false); + SAXBuilder saxBuilder = XMLUtils.getSAXBuilder(); Document document = saxBuilder.build(new StringReader(recordsSrc)); Element root = document.getRootElement(); diff --git a/dspace-api/src/main/java/org/dspace/importer/external/pubmedeurope/PubmedEuropeMetadataSourceServiceImpl.java b/dspace-api/src/main/java/org/dspace/importer/external/pubmedeurope/PubmedEuropeMetadataSourceServiceImpl.java index 7cd297eb2815..24f40339ddec 100644 --- a/dspace-api/src/main/java/org/dspace/importer/external/pubmedeurope/PubmedEuropeMetadataSourceServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/importer/external/pubmedeurope/PubmedEuropeMetadataSourceServiceImpl.java @@ -24,6 +24,7 @@ import org.apache.http.client.utils.URIBuilder; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import org.dspace.app.util.XMLUtils; import org.dspace.content.Item; import org.dspace.importer.external.datamodel.ImportRecord; import org.dspace.importer.external.datamodel.Query; @@ -292,9 +293,7 @@ public Integer count(String query) throws URISyntaxException, ClientProtocolExce Map> params = new HashMap>(); String response = liveImportClient.executeHttpGetRequest(1000, buildURI(1, query), params); - SAXBuilder saxBuilder = new SAXBuilder(); - // disallow DTD parsing to ensure no XXE attacks can occur - saxBuilder.setFeature("http://apache.org/xml/features/disallow-doctype-decl",true); + SAXBuilder saxBuilder = XMLUtils.getSAXBuilder(); Document document = saxBuilder.build(new StringReader(response)); Element root = document.getRootElement(); Element element = root.getChild("hitCount"); @@ -365,9 +364,7 @@ public List search(String query, Integer size, Integer start) thro String response = liveImportClient.executeHttpGetRequest(1000, uriBuilder.toString(), params); String cursorMark = StringUtils.EMPTY; if (StringUtils.isNotBlank(response)) { - SAXBuilder saxBuilder = new SAXBuilder(); - // disallow DTD parsing to ensure no XXE attacks can occur - saxBuilder.setFeature("http://apache.org/xml/features/disallow-doctype-decl",true); + SAXBuilder saxBuilder = XMLUtils.getSAXBuilder(); Document document = saxBuilder.build(new StringReader(response)); XPathFactory xpfac = XPathFactory.instance(); XPathExpression xPath = xpfac.compile("//responseWrapper/resultList/result", @@ -419,4 +416,4 @@ public void setUrl(String url) { this.url = url; } -} \ No newline at end of file +} diff --git a/dspace-api/src/main/java/org/dspace/importer/external/scopus/service/ScopusImportMetadataSourceServiceImpl.java b/dspace-api/src/main/java/org/dspace/importer/external/scopus/service/ScopusImportMetadataSourceServiceImpl.java index a3f74694becf..22e3534ca89f 100644 --- a/dspace-api/src/main/java/org/dspace/importer/external/scopus/service/ScopusImportMetadataSourceServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/importer/external/scopus/service/ScopusImportMetadataSourceServiceImpl.java @@ -26,6 +26,7 @@ import org.apache.commons.lang3.StringUtils; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import org.dspace.app.util.XMLUtils; import org.dspace.content.Item; import org.dspace.importer.external.datamodel.ImportRecord; import org.dspace.importer.external.datamodel.Query; @@ -208,9 +209,7 @@ public Integer call() throws Exception { return 0; } - SAXBuilder saxBuilder = new SAXBuilder(); - // disallow DTD parsing to ensure no XXE attacks can occur - saxBuilder.setFeature("http://apache.org/xml/features/disallow-doctype-decl",true); + SAXBuilder saxBuilder = XMLUtils.getSAXBuilder(); Document document = saxBuilder.build(new StringReader(response)); Element root = document.getRootElement(); @@ -397,9 +396,7 @@ private Map getRequestParameters(String query, String viewMode, private List splitToRecords(String recordsSrc) { try { - SAXBuilder saxBuilder = new SAXBuilder(); - // disallow DTD parsing to ensure no XXE attacks can occur - saxBuilder.setFeature("http://apache.org/xml/features/disallow-doctype-decl",true); + SAXBuilder saxBuilder = XMLUtils.getSAXBuilder(); Document document = saxBuilder.build(new StringReader(recordsSrc)); Element root = document.getRootElement(); String totalResults = root.getChildText("totalResults", Namespace.getNamespace("http://a9.com/-/spec/opensearch/1.1/")); diff --git a/dspace-api/src/main/java/org/dspace/importer/external/wos/service/WOSImportMetadataSourceServiceImpl.java b/dspace-api/src/main/java/org/dspace/importer/external/wos/service/WOSImportMetadataSourceServiceImpl.java index 9bffa2a84a19..2ac63d50513f 100644 --- a/dspace-api/src/main/java/org/dspace/importer/external/wos/service/WOSImportMetadataSourceServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/importer/external/wos/service/WOSImportMetadataSourceServiceImpl.java @@ -26,6 +26,7 @@ import org.apache.commons.lang3.StringUtils; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import org.dspace.app.util.XMLUtils; import org.dspace.content.Item; import org.dspace.importer.external.datamodel.ImportRecord; import org.dspace.importer.external.datamodel.Query; @@ -145,9 +146,7 @@ public Integer call() throws Exception { params.put(HEADER_PARAMETERS, getRequestParameters()); String response = liveImportClient.executeHttpGetRequest(timeout, url, params); - SAXBuilder saxBuilder = new SAXBuilder(); - // disallow DTD parsing to ensure no XXE attacks can occur - saxBuilder.setFeature("http://apache.org/xml/features/disallow-doctype-decl",true); + SAXBuilder saxBuilder = XMLUtils.getSAXBuilder(); Document document = saxBuilder.build(new StringReader(response)); Element root = document.getRootElement(); XPathExpression xpath = XPathFactory.instance().compile("//*[@name=\"RecordsFound\"]", @@ -288,9 +287,7 @@ private boolean isIsi(String query) { private List splitToRecords(String recordsSrc) { try { - SAXBuilder saxBuilder = new SAXBuilder(); - // disallow DTD parsing to ensure no XXE attacks can occur - saxBuilder.setFeature("http://apache.org/xml/features/disallow-doctype-decl",true); + SAXBuilder saxBuilder = XMLUtils.getSAXBuilder(); Document document = saxBuilder.build(new StringReader(recordsSrc)); Element root = document.getRootElement(); String cData = XPathFactory.instance().compile("//*[@name=\"Records\"]", @@ -332,4 +329,4 @@ public void setApiKey(String apiKey) { this.apiKey = apiKey; } -} \ No newline at end of file +} diff --git a/dspace-api/src/main/java/org/dspace/license/CCLicenseConnectorServiceImpl.java b/dspace-api/src/main/java/org/dspace/license/CCLicenseConnectorServiceImpl.java index 1d777a2e13c8..3c9088bda5df 100644 --- a/dspace-api/src/main/java/org/dspace/license/CCLicenseConnectorServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/license/CCLicenseConnectorServiceImpl.java @@ -28,6 +28,7 @@ import org.apache.http.util.EntityUtils; import org.apache.logging.log4j.Logger; import org.dspace.app.client.DSpaceHttpClientFactory; +import org.dspace.app.util.XMLUtils; import org.dspace.services.ConfigurationService; import org.jdom2.Attribute; import org.jdom2.Document; @@ -50,7 +51,7 @@ public class CCLicenseConnectorServiceImpl implements CCLicenseConnectorService, private Logger log = org.apache.logging.log4j.LogManager.getLogger(CCLicenseConnectorServiceImpl.class); private CloseableHttpClient client; - protected SAXBuilder parser = new SAXBuilder(); + protected SAXBuilder parser = XMLUtils.getSAXBuilder(); private String postArgument = "answers"; private String postAnswerFormat = diff --git a/dspace-api/src/main/java/org/dspace/orcid/client/OrcidClientImpl.java b/dspace-api/src/main/java/org/dspace/orcid/client/OrcidClientImpl.java index 954336da2573..682e90828e36 100644 --- a/dspace-api/src/main/java/org/dspace/orcid/client/OrcidClientImpl.java +++ b/dspace-api/src/main/java/org/dspace/orcid/client/OrcidClientImpl.java @@ -43,6 +43,7 @@ import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.message.BasicNameValuePair; import org.dspace.app.client.DSpaceHttpClientFactory; +import org.dspace.app.util.XMLUtils; import org.dspace.orcid.OrcidToken; import org.dspace.orcid.exception.OrcidClientException; import org.dspace.orcid.model.OrcidEntityType; @@ -351,8 +352,7 @@ private String marshall(Object object) throws JAXBException { @SuppressWarnings("unchecked") private T unmarshall(HttpEntity entity, Class clazz) throws Exception { JAXBContext jaxbContext = JAXBContext.newInstance(clazz); - XMLInputFactory xmlInputFactory = XMLInputFactory.newFactory(); - xmlInputFactory.setProperty(XMLInputFactory.SUPPORT_DTD, false); + XMLInputFactory xmlInputFactory = XMLUtils.getXMLInputFactory(); XMLStreamReader xmlStreamReader = xmlInputFactory.createXMLStreamReader(entity.getContent()); Unmarshaller unmarshaller = jaxbContext.createUnmarshaller(); return (T) unmarshaller.unmarshal(xmlStreamReader); diff --git a/dspace-api/src/main/java/org/dspace/vocabulary/ControlledVocabulary.java b/dspace-api/src/main/java/org/dspace/vocabulary/ControlledVocabulary.java index 7f2bdc6ef771..bd19a1254fe3 100644 --- a/dspace-api/src/main/java/org/dspace/vocabulary/ControlledVocabulary.java +++ b/dspace-api/src/main/java/org/dspace/vocabulary/ControlledVocabulary.java @@ -12,7 +12,6 @@ import java.util.ArrayList; import java.util.List; import javax.xml.parsers.DocumentBuilder; -import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import javax.xml.transform.TransformerException; import javax.xml.xpath.XPath; @@ -20,6 +19,7 @@ import javax.xml.xpath.XPathExpressionException; import javax.xml.xpath.XPathFactory; +import org.dspace.app.util.XMLUtils; import org.dspace.services.ConfigurationService; import org.dspace.services.factory.DSpaceServicesFactory; import org.w3c.dom.Document; @@ -71,7 +71,7 @@ public static ControlledVocabulary loadVocabulary(String fileName) File controlledVocFile = new File(filePath.toString()); if (controlledVocFile.exists()) { - DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder(); + DocumentBuilder builder = XMLUtils.getDocumentBuilder(); Document document = builder.parse(controlledVocFile); XPath xPath = XPathFactory.newInstance().newXPath(); Node node = (Node) xPath.compile("node").evaluate(document, XPathConstants.NODE); From 8c80b67b04028a747515c84f0fbd1ef8ecd1ee40 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Wed, 2 Jul 2025 15:30:04 -0500 Subject: [PATCH 792/979] EPO and PubMed only need to allow for DOCTYPEs. All other XML security changes can be used. --- .../service/EpoImportMetadataSourceServiceImpl.java | 4 ++++ .../service/PubmedImportMetadataSourceServiceImpl.java | 10 +++++----- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/importer/external/epo/service/EpoImportMetadataSourceServiceImpl.java b/dspace-api/src/main/java/org/dspace/importer/external/epo/service/EpoImportMetadataSourceServiceImpl.java index 7edd3f9d01c5..552f607827a8 100644 --- a/dspace-api/src/main/java/org/dspace/importer/external/epo/service/EpoImportMetadataSourceServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/importer/external/epo/service/EpoImportMetadataSourceServiceImpl.java @@ -399,6 +399,10 @@ private Integer countDocument(String bearer, String query) { String response = liveImportClient.executeHttpGetRequest(1000, uriBuilder.toString(), params); SAXBuilder saxBuilder = XMLUtils.getSAXBuilder(); + // To properly parse EPO responses, we must allow DOCTYPEs overall. But, we can still apply all the + // other default XXE protections, including disabling external entities and entity expansion. + // NOTE: we only need to allow DOCTYPEs for this initial API call. All other calls have them disabled. + saxBuilder.setFeature("http://apache.org/xml/features/disallow-doctype-decl", false); Document document = saxBuilder.build(new StringReader(response)); Element root = document.getRootElement(); diff --git a/dspace-api/src/main/java/org/dspace/importer/external/pubmed/service/PubmedImportMetadataSourceServiceImpl.java b/dspace-api/src/main/java/org/dspace/importer/external/pubmed/service/PubmedImportMetadataSourceServiceImpl.java index 7718e59e483e..c870161bf9bd 100644 --- a/dspace-api/src/main/java/org/dspace/importer/external/pubmed/service/PubmedImportMetadataSourceServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/importer/external/pubmed/service/PubmedImportMetadataSourceServiceImpl.java @@ -235,12 +235,9 @@ private String getSingleElementValue(String src, String elementName) { try { SAXBuilder saxBuilder = XMLUtils.getSAXBuilder(); - // To properly parse PubMed responses, we must allow DOCTYPE/DTDs overall but - // we can still take advantage of entities themselves being disabled, and not - // expanded. + // To properly parse PubMed responses, we must allow DOCTYPEs overall. But, we can still apply all the + // other default XXE protections, including disabling external entities and entity expansion. saxBuilder.setFeature("http://apache.org/xml/features/disallow-doctype-decl", false); - saxBuilder.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", - true); Document document = saxBuilder.build(new StringReader(src)); Element root = document.getRootElement(); @@ -358,6 +355,9 @@ public Collection call() throws Exception { private List splitToRecords(String recordsSrc) { try { SAXBuilder saxBuilder = XMLUtils.getSAXBuilder(); + // To properly parse PubMed responses, we must allow DOCTYPEs overall. But, we can still apply all the + // other default XXE protections, including disabling external entities and entity expansion. + saxBuilder.setFeature("http://apache.org/xml/features/disallow-doctype-decl", false); Document document = saxBuilder.build(new StringReader(recordsSrc)); Element root = document.getRootElement(); From 2f4c5e8826cbe758b819e584eab8d9e53b457221 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 7 Jul 2025 02:52:43 +0000 Subject: [PATCH 793/979] Bump the spring group across 1 directory with 25 updates Bumps the spring group with 25 updates in the / directory: | Package | From | To | | --- | --- | --- | | [org.springframework:spring-orm](https://github.com/spring-projects/spring-framework) | `6.2.7` | `6.2.8` | | [org.springframework:spring-core](https://github.com/spring-projects/spring-framework) | `6.2.7` | `6.2.8` | | [org.springframework:spring-beans](https://github.com/spring-projects/spring-framework) | `6.2.7` | `6.2.8` | | [org.springframework:spring-aop](https://github.com/spring-projects/spring-framework) | `6.2.7` | `6.2.8` | | [org.springframework:spring-context](https://github.com/spring-projects/spring-framework) | `6.2.7` | `6.2.8` | | [org.springframework:spring-context-support](https://github.com/spring-projects/spring-framework) | `6.2.7` | `6.2.8` | | [org.springframework:spring-tx](https://github.com/spring-projects/spring-framework) | `6.2.7` | `6.2.8` | | [org.springframework:spring-jdbc](https://github.com/spring-projects/spring-framework) | `6.2.7` | `6.2.8` | | [org.springframework:spring-web](https://github.com/spring-projects/spring-framework) | `6.2.7` | `6.2.8` | | [org.springframework:spring-webmvc](https://github.com/spring-projects/spring-framework) | `6.2.7` | `6.2.8` | | [org.springframework:spring-expression](https://github.com/spring-projects/spring-framework) | `6.2.7` | `6.2.8` | | [org.springframework:spring-test](https://github.com/spring-projects/spring-framework) | `6.2.7` | `6.2.8` | | [org.springframework.boot:spring-boot-starter-test](https://github.com/spring-projects/spring-boot) | `3.5.0` | `3.5.3` | | [org.springframework.boot:spring-boot-starter-tomcat](https://github.com/spring-projects/spring-boot) | `3.5.0` | `3.5.3` | | [org.springframework.boot:spring-boot-maven-plugin](https://github.com/spring-projects/spring-boot) | `3.5.0` | `3.5.3` | | [org.springframework.boot:spring-boot-starter-cache](https://github.com/spring-projects/spring-boot) | `3.5.0` | `3.5.3` | | [org.springframework.boot:spring-boot-starter](https://github.com/spring-projects/spring-boot) | `3.5.0` | `3.5.3` | | [org.springframework.boot:spring-boot-starter-thymeleaf](https://github.com/spring-projects/spring-boot) | `3.5.0` | `3.5.3` | | [org.springframework.boot:spring-boot-starter-web](https://github.com/spring-projects/spring-boot) | `3.5.0` | `3.5.3` | | [org.springframework.boot:spring-boot-starter-data-rest](https://github.com/spring-projects/spring-boot) | `3.5.0` | `3.5.3` | | [org.springframework.boot:spring-boot-starter-security](https://github.com/spring-projects/spring-boot) | `3.5.0` | `3.5.3` | | [org.springframework.boot:spring-boot-starter-aop](https://github.com/spring-projects/spring-boot) | `3.5.0` | `3.5.3` | | [org.springframework.boot:spring-boot-starter-actuator](https://github.com/spring-projects/spring-boot) | `3.5.0` | `3.5.3` | | [org.springframework.boot:spring-boot-starter-log4j2](https://github.com/spring-projects/spring-boot) | `3.5.0` | `3.5.3` | | [org.springframework.security:spring-security-test](https://github.com/spring-projects/spring-security) | `6.5.0` | `6.5.1` | Updates `org.springframework:spring-orm` from 6.2.7 to 6.2.8 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.7...v6.2.8) Updates `org.springframework:spring-core` from 6.2.7 to 6.2.8 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.7...v6.2.8) Updates `org.springframework:spring-beans` from 6.2.7 to 6.2.8 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.7...v6.2.8) Updates `org.springframework:spring-aop` from 6.2.7 to 6.2.8 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.7...v6.2.8) Updates `org.springframework:spring-context` from 6.2.7 to 6.2.8 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.7...v6.2.8) Updates `org.springframework:spring-context-support` from 6.2.7 to 6.2.8 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.7...v6.2.8) Updates `org.springframework:spring-tx` from 6.2.7 to 6.2.8 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.7...v6.2.8) Updates `org.springframework:spring-jdbc` from 6.2.7 to 6.2.8 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.7...v6.2.8) Updates `org.springframework:spring-web` from 6.2.7 to 6.2.8 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.7...v6.2.8) Updates `org.springframework:spring-webmvc` from 6.2.7 to 6.2.8 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.7...v6.2.8) Updates `org.springframework:spring-expression` from 6.2.7 to 6.2.8 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.7...v6.2.8) Updates `org.springframework:spring-test` from 6.2.7 to 6.2.8 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.7...v6.2.8) Updates `org.springframework:spring-core` from 6.2.7 to 6.2.8 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.7...v6.2.8) Updates `org.springframework:spring-beans` from 6.2.7 to 6.2.8 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.7...v6.2.8) Updates `org.springframework:spring-aop` from 6.2.7 to 6.2.8 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.7...v6.2.8) Updates `org.springframework:spring-context` from 6.2.7 to 6.2.8 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.7...v6.2.8) Updates `org.springframework:spring-context-support` from 6.2.7 to 6.2.8 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.7...v6.2.8) Updates `org.springframework:spring-tx` from 6.2.7 to 6.2.8 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.7...v6.2.8) Updates `org.springframework:spring-jdbc` from 6.2.7 to 6.2.8 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.7...v6.2.8) Updates `org.springframework:spring-web` from 6.2.7 to 6.2.8 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.7...v6.2.8) Updates `org.springframework:spring-webmvc` from 6.2.7 to 6.2.8 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.7...v6.2.8) Updates `org.springframework:spring-expression` from 6.2.7 to 6.2.8 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.7...v6.2.8) Updates `org.springframework:spring-test` from 6.2.7 to 6.2.8 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.7...v6.2.8) Updates `org.springframework.boot:spring-boot-starter-test` from 3.5.0 to 3.5.3 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.5.0...v3.5.3) Updates `org.springframework.boot:spring-boot-starter-tomcat` from 3.5.0 to 3.5.3 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.5.0...v3.5.3) Updates `org.springframework.boot:spring-boot-maven-plugin` from 3.5.0 to 3.5.3 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.5.0...v3.5.3) Updates `org.springframework.boot:spring-boot-starter-cache` from 3.5.0 to 3.5.3 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.5.0...v3.5.3) Updates `org.springframework.boot:spring-boot-starter` from 3.5.0 to 3.5.3 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.5.0...v3.5.3) Updates `org.springframework.boot:spring-boot-starter-thymeleaf` from 3.5.0 to 3.5.3 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.5.0...v3.5.3) Updates `org.springframework.boot:spring-boot-starter-web` from 3.5.0 to 3.5.3 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.5.0...v3.5.3) Updates `org.springframework.boot:spring-boot-starter-data-rest` from 3.5.0 to 3.5.3 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.5.0...v3.5.3) Updates `org.springframework.boot:spring-boot-starter-security` from 3.5.0 to 3.5.3 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.5.0...v3.5.3) Updates `org.springframework.boot:spring-boot-starter-aop` from 3.5.0 to 3.5.3 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.5.0...v3.5.3) Updates `org.springframework.boot:spring-boot-starter-actuator` from 3.5.0 to 3.5.3 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.5.0...v3.5.3) Updates `org.springframework.boot:spring-boot-starter-log4j2` from 3.5.0 to 3.5.3 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.5.0...v3.5.3) Updates `org.springframework.boot:spring-boot-starter-tomcat` from 3.5.0 to 3.5.3 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.5.0...v3.5.3) Updates `org.springframework.security:spring-security-test` from 6.5.0 to 6.5.1 - [Release notes](https://github.com/spring-projects/spring-security/releases) - [Changelog](https://github.com/spring-projects/spring-security/blob/main/RELEASE.adoc) - [Commits](https://github.com/spring-projects/spring-security/compare/6.5.0...6.5.1) Updates `org.springframework.boot:spring-boot-maven-plugin` from 3.5.0 to 3.5.3 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.5.0...v3.5.3) Updates `org.springframework.boot:spring-boot-starter-cache` from 3.5.0 to 3.5.3 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.5.0...v3.5.3) Updates `org.springframework.boot:spring-boot-starter` from 3.5.0 to 3.5.3 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.5.0...v3.5.3) Updates `org.springframework.boot:spring-boot-starter-thymeleaf` from 3.5.0 to 3.5.3 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.5.0...v3.5.3) Updates `org.springframework.boot:spring-boot-starter-web` from 3.5.0 to 3.5.3 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.5.0...v3.5.3) Updates `org.springframework.boot:spring-boot-starter-data-rest` from 3.5.0 to 3.5.3 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.5.0...v3.5.3) Updates `org.springframework.boot:spring-boot-starter-security` from 3.5.0 to 3.5.3 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.5.0...v3.5.3) Updates `org.springframework.boot:spring-boot-starter-aop` from 3.5.0 to 3.5.3 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.5.0...v3.5.3) Updates `org.springframework.boot:spring-boot-starter-actuator` from 3.5.0 to 3.5.3 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.5.0...v3.5.3) Updates `org.springframework.boot:spring-boot-starter-log4j2` from 3.5.0 to 3.5.3 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.5.0...v3.5.3) --- updated-dependencies: - dependency-name: org.springframework:spring-orm dependency-version: 6.2.8 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-core dependency-version: 6.2.8 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-beans dependency-version: 6.2.8 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-aop dependency-version: 6.2.8 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-context dependency-version: 6.2.8 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-context-support dependency-version: 6.2.8 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-tx dependency-version: 6.2.8 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-jdbc dependency-version: 6.2.8 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-web dependency-version: 6.2.8 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-webmvc dependency-version: 6.2.8 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-expression dependency-version: 6.2.8 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-test dependency-version: 6.2.8 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-core dependency-version: 6.2.8 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-beans dependency-version: 6.2.8 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-aop dependency-version: 6.2.8 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-context dependency-version: 6.2.8 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-context-support dependency-version: 6.2.8 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-tx dependency-version: 6.2.8 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-jdbc dependency-version: 6.2.8 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-web dependency-version: 6.2.8 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-webmvc dependency-version: 6.2.8 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-expression dependency-version: 6.2.8 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-test dependency-version: 6.2.8 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter-test dependency-version: 3.5.3 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter-tomcat dependency-version: 3.5.3 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-maven-plugin dependency-version: 3.5.3 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter-cache dependency-version: 3.5.3 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter dependency-version: 3.5.3 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter-thymeleaf dependency-version: 3.5.3 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter-web dependency-version: 3.5.3 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter-data-rest dependency-version: 3.5.3 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter-security dependency-version: 3.5.3 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter-aop dependency-version: 3.5.3 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter-actuator dependency-version: 3.5.3 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter-log4j2 dependency-version: 3.5.3 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter-tomcat dependency-version: 3.5.3 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.security:spring-security-test dependency-version: 6.5.1 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-maven-plugin dependency-version: 3.5.3 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter-cache dependency-version: 3.5.3 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter dependency-version: 3.5.3 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter-thymeleaf dependency-version: 3.5.3 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter-web dependency-version: 3.5.3 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter-data-rest dependency-version: 3.5.3 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter-security dependency-version: 3.5.3 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter-aop dependency-version: 3.5.3 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter-actuator dependency-version: 3.5.3 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter-log4j2 dependency-version: 3.5.3 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring ... Signed-off-by: dependabot[bot] --- pom.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pom.xml b/pom.xml index 29b3d93836b9..e34a3c1bd509 100644 --- a/pom.xml +++ b/pom.xml @@ -19,9 +19,9 @@ 17 - 6.2.7 - 3.5.0 - 6.5.0 + 6.2.8 + 3.5.3 + 6.5.1 6.4.8.Final 8.0.2.Final 42.7.7 From d90065ee9a851d1ff651edc60872e688205d5806 Mon Sep 17 00:00:00 2001 From: Kim Shepherd Date: Thu, 10 Jul 2025 16:00:34 +0200 Subject: [PATCH 794/979] Allow trusted XML builder to enforce base path for entities --- .../org/dspace/app/util/DCInputsReader.java | 19 ++-- .../app/util/SubmissionConfigReader.java | 10 +- .../java/org/dspace/app/util/XMLUtils.java | 94 +++++++++++++++++-- 3 files changed, 99 insertions(+), 24 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/app/util/DCInputsReader.java b/dspace-api/src/main/java/org/dspace/app/util/DCInputsReader.java index d2f3ac0dc5d9..293ceaac47d7 100644 --- a/dspace-api/src/main/java/org/dspace/app/util/DCInputsReader.java +++ b/dspace-api/src/main/java/org/dspace/app/util/DCInputsReader.java @@ -8,6 +8,7 @@ package org.dspace.app.util; import java.io.File; +import java.nio.file.Path; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; @@ -118,19 +119,17 @@ private void buildInputs(String fileName) formDefns = new HashMap>>>(); valuePairs = new HashMap>(); - String uri = "file:" + new File(fileName).getAbsolutePath(); + File inputFile = new File(fileName); + String inputFileDir = inputFile.toPath().normalize().getParent().toString(); + + String uri = "file:" + inputFile.getAbsolutePath(); try { - // This document builder factory will *not* disable external + // This document builder will *not* disable external // entities as they can be useful in managing large forms, but - // it is up to site administrators to validate the XML they are - // storing - DocumentBuilderFactory factory = XMLUtils.getTrustedDocumentBuilderFactory(); - factory.setValidating(false); - factory.setIgnoringComments(true); - factory.setIgnoringElementContentWhitespace(true); - - DocumentBuilder db = factory.newDocumentBuilder(); + // it will restrict them to be within the directory that the + // current input form XML file exists (or a sub-directory) + DocumentBuilder db = XMLUtils.getTrustedDocumentBuilder(inputFileDir); Document doc = db.parse(uri); doNodes(doc); checkValues(); diff --git a/dspace-api/src/main/java/org/dspace/app/util/SubmissionConfigReader.java b/dspace-api/src/main/java/org/dspace/app/util/SubmissionConfigReader.java index 64e62317c515..5b487045a0d9 100644 --- a/dspace-api/src/main/java/org/dspace/app/util/SubmissionConfigReader.java +++ b/dspace-api/src/main/java/org/dspace/app/util/SubmissionConfigReader.java @@ -172,14 +172,8 @@ private void buildInputs(String fileName) throws SubmissionConfigReaderException try { // This document builder factory will *not* disable external // entities as they can be useful in managing large forms, but - // it is up to site administrators to validate the XML they are - // storing - DocumentBuilderFactory factory = XMLUtils.getTrustedDocumentBuilderFactory(); - factory.setValidating(false); - factory.setIgnoringComments(true); - factory.setIgnoringElementContentWhitespace(true); - - DocumentBuilder db = factory.newDocumentBuilder(); + // it will restrict them to the config dir containing submission definitions + DocumentBuilder db = XMLUtils.getTrustedDocumentBuilder(configDir); Document doc = db.parse(uri); doNodes(doc); } catch (FactoryConfigurationError fe) { diff --git a/dspace-api/src/main/java/org/dspace/app/util/XMLUtils.java b/dspace-api/src/main/java/org/dspace/app/util/XMLUtils.java index 389d53fe6da4..6b419a0485e8 100644 --- a/dspace-api/src/main/java/org/dspace/app/util/XMLUtils.java +++ b/dspace-api/src/main/java/org/dspace/app/util/XMLUtils.java @@ -7,7 +7,13 @@ */ package org.dspace.app.util; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.nio.file.Path; +import java.nio.file.Paths; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; @@ -18,6 +24,9 @@ import org.jdom2.input.SAXBuilder; import org.w3c.dom.Element; import org.w3c.dom.NodeList; +import org.xml.sax.EntityResolver; +import org.xml.sax.InputSource; +import org.xml.sax.SAXException; /** * Simple class to read information from small XML using DOM manipulation @@ -211,27 +220,36 @@ public static DocumentBuilderFactory getDocumentBuilderFactory() } /** - * Initialize and return a javax DocumentBuilder with NO security + * Initialize and return a javax DocumentBuilder with less security * applied. This is intended only for internal, administrative/configuration * use where external entities and other dangerous features are actually - * purposefully included. + * purposefully included, but are only allowed from specified paths, e.g. + * dspace.dir or some other path specified by the java caller. * The method here is tiny, but may be expanded with other features like * whitespace handling, and calling this method name helps to document * the fact that the caller knows it is trusting the XML source / builder + *

    + * If no allowedPaths are passed, then all external entities are rejected * * @return document builder with no security features set - * @throws ParserConfigurationException + * @throws ParserConfigurationException if the builder can not be configured */ - public static DocumentBuilder getTrustedDocumentBuilder() + public static DocumentBuilder getTrustedDocumentBuilder(String... allowedPaths) throws ParserConfigurationException { - return getTrustedDocumentBuilderFactory().newDocumentBuilder(); + DocumentBuilderFactory factory = getTrustedDocumentBuilderFactory(); + factory.setValidating(false); + factory.setIgnoringComments(true); + factory.setIgnoringElementContentWhitespace(true); + DocumentBuilder builder = factory.newDocumentBuilder(); + builder.setEntityResolver(new PathRestrictedEntityResolver(allowedPaths)); + return factory.newDocumentBuilder(); } /** * Initialize and return the javax DocumentBuilder with some basic security applied * to avoid XXE attacks and other unwanted content inclusion * @return document builder for use in XML parsing - * @throws ParserConfigurationException + * @throws ParserConfigurationException if the builder can not be configured */ public static DocumentBuilder getDocumentBuilder() throws ParserConfigurationException { @@ -284,4 +302,68 @@ public static XMLInputFactory getXMLInputFactory() { return xmlInputFactory; } + /** + * This entity resolver accepts one or more path strings in its + * constructor and throws a SAXException if the entity systemID + * is not within the allowed path (or a subdirectory). + * If no parameters are passed, then this effectively disallows + * any external entity resolution. + */ + public static class PathRestrictedEntityResolver implements EntityResolver { + private final List allowedBasePaths; + + public PathRestrictedEntityResolver(String... allowedBasePaths) { + this.allowedBasePaths = Arrays.asList(allowedBasePaths); + } + + @Override + public InputSource resolveEntity(String publicId, String systemId) + throws SAXException, IOException { + + if (systemId == null) { + return null; + } + + String filePath; + if (systemId.startsWith("file://")) { + filePath = systemId.substring(7); + } else if (systemId.startsWith("file:")) { + filePath = systemId.substring(5); + } else if (!systemId.contains("://")) { + filePath = systemId; + } else { + throw new SAXException("External resources not allowed: " + systemId + + ". Only local file paths are permitted."); + } + + Path resolvedPath; + try { + resolvedPath = Paths.get(filePath).toAbsolutePath().normalize(); + } catch (Exception e) { + throw new SAXException("Invalid path: " + systemId, e); + } + + boolean isAllowed = false; + for (String basePath : allowedBasePaths) { + Path allowedPath = Paths.get(basePath).toAbsolutePath().normalize(); + if (resolvedPath.startsWith(allowedPath)) { + isAllowed = true; + break; + } + } + + if (!isAllowed) { + throw new SAXException("Access denied to path: " + resolvedPath); + } + + File file = resolvedPath.toFile(); + if (!file.exists() || !file.canRead()) { + throw new SAXException("File not found or not readable: " + resolvedPath); + } + + return new InputSource(new FileInputStream(file)); + } + } + + } From 99b2a630a75db4eafbd7f44bb75a503f48e6170f Mon Sep 17 00:00:00 2001 From: Kim Shepherd Date: Thu, 10 Jul 2025 16:00:34 +0200 Subject: [PATCH 795/979] Allow trusted XML builder to enforce base path for entities --- .../org/dspace/app/util/DCInputsReader.java | 19 ++-- .../app/util/SubmissionConfigReader.java | 10 +- .../java/org/dspace/app/util/XMLUtils.java | 94 +++++++++++++++++-- 3 files changed, 99 insertions(+), 24 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/app/util/DCInputsReader.java b/dspace-api/src/main/java/org/dspace/app/util/DCInputsReader.java index d2f3ac0dc5d9..293ceaac47d7 100644 --- a/dspace-api/src/main/java/org/dspace/app/util/DCInputsReader.java +++ b/dspace-api/src/main/java/org/dspace/app/util/DCInputsReader.java @@ -8,6 +8,7 @@ package org.dspace.app.util; import java.io.File; +import java.nio.file.Path; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; @@ -118,19 +119,17 @@ private void buildInputs(String fileName) formDefns = new HashMap>>>(); valuePairs = new HashMap>(); - String uri = "file:" + new File(fileName).getAbsolutePath(); + File inputFile = new File(fileName); + String inputFileDir = inputFile.toPath().normalize().getParent().toString(); + + String uri = "file:" + inputFile.getAbsolutePath(); try { - // This document builder factory will *not* disable external + // This document builder will *not* disable external // entities as they can be useful in managing large forms, but - // it is up to site administrators to validate the XML they are - // storing - DocumentBuilderFactory factory = XMLUtils.getTrustedDocumentBuilderFactory(); - factory.setValidating(false); - factory.setIgnoringComments(true); - factory.setIgnoringElementContentWhitespace(true); - - DocumentBuilder db = factory.newDocumentBuilder(); + // it will restrict them to be within the directory that the + // current input form XML file exists (or a sub-directory) + DocumentBuilder db = XMLUtils.getTrustedDocumentBuilder(inputFileDir); Document doc = db.parse(uri); doNodes(doc); checkValues(); diff --git a/dspace-api/src/main/java/org/dspace/app/util/SubmissionConfigReader.java b/dspace-api/src/main/java/org/dspace/app/util/SubmissionConfigReader.java index 1914ded86b66..27138d1ba1ef 100644 --- a/dspace-api/src/main/java/org/dspace/app/util/SubmissionConfigReader.java +++ b/dspace-api/src/main/java/org/dspace/app/util/SubmissionConfigReader.java @@ -162,14 +162,8 @@ private void buildInputs(String fileName) throws SubmissionConfigReaderException try { // This document builder factory will *not* disable external // entities as they can be useful in managing large forms, but - // it is up to site administrators to validate the XML they are - // storing - DocumentBuilderFactory factory = XMLUtils.getTrustedDocumentBuilderFactory(); - factory.setValidating(false); - factory.setIgnoringComments(true); - factory.setIgnoringElementContentWhitespace(true); - - DocumentBuilder db = factory.newDocumentBuilder(); + // it will restrict them to the config dir containing submission definitions + DocumentBuilder db = XMLUtils.getTrustedDocumentBuilder(configDir); Document doc = db.parse(uri); doNodes(doc); } catch (FactoryConfigurationError fe) { diff --git a/dspace-api/src/main/java/org/dspace/app/util/XMLUtils.java b/dspace-api/src/main/java/org/dspace/app/util/XMLUtils.java index 389d53fe6da4..6b419a0485e8 100644 --- a/dspace-api/src/main/java/org/dspace/app/util/XMLUtils.java +++ b/dspace-api/src/main/java/org/dspace/app/util/XMLUtils.java @@ -7,7 +7,13 @@ */ package org.dspace.app.util; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.nio.file.Path; +import java.nio.file.Paths; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; @@ -18,6 +24,9 @@ import org.jdom2.input.SAXBuilder; import org.w3c.dom.Element; import org.w3c.dom.NodeList; +import org.xml.sax.EntityResolver; +import org.xml.sax.InputSource; +import org.xml.sax.SAXException; /** * Simple class to read information from small XML using DOM manipulation @@ -211,27 +220,36 @@ public static DocumentBuilderFactory getDocumentBuilderFactory() } /** - * Initialize and return a javax DocumentBuilder with NO security + * Initialize and return a javax DocumentBuilder with less security * applied. This is intended only for internal, administrative/configuration * use where external entities and other dangerous features are actually - * purposefully included. + * purposefully included, but are only allowed from specified paths, e.g. + * dspace.dir or some other path specified by the java caller. * The method here is tiny, but may be expanded with other features like * whitespace handling, and calling this method name helps to document * the fact that the caller knows it is trusting the XML source / builder + *

    + * If no allowedPaths are passed, then all external entities are rejected * * @return document builder with no security features set - * @throws ParserConfigurationException + * @throws ParserConfigurationException if the builder can not be configured */ - public static DocumentBuilder getTrustedDocumentBuilder() + public static DocumentBuilder getTrustedDocumentBuilder(String... allowedPaths) throws ParserConfigurationException { - return getTrustedDocumentBuilderFactory().newDocumentBuilder(); + DocumentBuilderFactory factory = getTrustedDocumentBuilderFactory(); + factory.setValidating(false); + factory.setIgnoringComments(true); + factory.setIgnoringElementContentWhitespace(true); + DocumentBuilder builder = factory.newDocumentBuilder(); + builder.setEntityResolver(new PathRestrictedEntityResolver(allowedPaths)); + return factory.newDocumentBuilder(); } /** * Initialize and return the javax DocumentBuilder with some basic security applied * to avoid XXE attacks and other unwanted content inclusion * @return document builder for use in XML parsing - * @throws ParserConfigurationException + * @throws ParserConfigurationException if the builder can not be configured */ public static DocumentBuilder getDocumentBuilder() throws ParserConfigurationException { @@ -284,4 +302,68 @@ public static XMLInputFactory getXMLInputFactory() { return xmlInputFactory; } + /** + * This entity resolver accepts one or more path strings in its + * constructor and throws a SAXException if the entity systemID + * is not within the allowed path (or a subdirectory). + * If no parameters are passed, then this effectively disallows + * any external entity resolution. + */ + public static class PathRestrictedEntityResolver implements EntityResolver { + private final List allowedBasePaths; + + public PathRestrictedEntityResolver(String... allowedBasePaths) { + this.allowedBasePaths = Arrays.asList(allowedBasePaths); + } + + @Override + public InputSource resolveEntity(String publicId, String systemId) + throws SAXException, IOException { + + if (systemId == null) { + return null; + } + + String filePath; + if (systemId.startsWith("file://")) { + filePath = systemId.substring(7); + } else if (systemId.startsWith("file:")) { + filePath = systemId.substring(5); + } else if (!systemId.contains("://")) { + filePath = systemId; + } else { + throw new SAXException("External resources not allowed: " + systemId + + ". Only local file paths are permitted."); + } + + Path resolvedPath; + try { + resolvedPath = Paths.get(filePath).toAbsolutePath().normalize(); + } catch (Exception e) { + throw new SAXException("Invalid path: " + systemId, e); + } + + boolean isAllowed = false; + for (String basePath : allowedBasePaths) { + Path allowedPath = Paths.get(basePath).toAbsolutePath().normalize(); + if (resolvedPath.startsWith(allowedPath)) { + isAllowed = true; + break; + } + } + + if (!isAllowed) { + throw new SAXException("Access denied to path: " + resolvedPath); + } + + File file = resolvedPath.toFile(); + if (!file.exists() || !file.canRead()) { + throw new SAXException("File not found or not readable: " + resolvedPath); + } + + return new InputSource(new FileInputStream(file)); + } + } + + } From 85dbfd22190c5f942f9fe04b5675c4291ea67840 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Fri, 11 Jul 2025 13:51:03 -0500 Subject: [PATCH 796/979] Update LICENSES_THIRD_PARTY to prepare for 8.2 release --- LICENSES_THIRD_PARTY | 299 ++++++++++++++++++++++--------------------- pom.xml | 4 +- 2 files changed, 157 insertions(+), 146 deletions(-) diff --git a/LICENSES_THIRD_PARTY b/LICENSES_THIRD_PARTY index 5049903ffc62..5d99bd7e426c 100644 --- a/LICENSES_THIRD_PARTY +++ b/LICENSES_THIRD_PARTY @@ -21,29 +21,29 @@ 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.780 - https://aws.amazon.com/sdkforjava) - * AWS Java SDK for AWS KMS (com.amazonaws:aws-java-sdk-kms:1.12.780 - https://aws.amazon.com/sdkforjava) - * AWS Java SDK for Amazon S3 (com.amazonaws:aws-java-sdk-s3:1.12.780 - https://aws.amazon.com/sdkforjava) - * JMES Path Query library (com.amazonaws:jmespath-java:1.12.780 - https://aws.amazon.com/sdkforjava) + * 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) * Titanium JSON-LD 1.1 (JRE11) (com.apicatalog:titanium-json-ld:1.3.2 - https://github.com/filip26/titanium-json-ld) * 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.18.2 - https://github.com/FasterXML/jackson) - * Jackson-core (com.fasterxml.jackson.core:jackson-core:2.18.2 - https://github.com/FasterXML/jackson-core) - * jackson-databind (com.fasterxml.jackson.core:jackson-databind:2.18.2 - https://github.com/FasterXML/jackson) + * 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) * Jackson dataformat: Smile (com.fasterxml.jackson.dataformat:jackson-dataformat-smile:2.15.2 - https://github.com/FasterXML/jackson-dataformats-binary) * Jackson-dataformat-TOML (com.fasterxml.jackson.dataformat:jackson-dataformat-toml:2.15.2 - https://github.com/FasterXML/jackson-dataformats-text) * Jackson-dataformat-YAML (com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:2.16.2 - https://github.com/FasterXML/jackson-dataformats-text) - * Jackson datatype: jdk8 (com.fasterxml.jackson.datatype:jackson-datatype-jdk8:2.18.2 - https://github.com/FasterXML/jackson-modules-java8/jackson-datatype-jdk8) - * Jackson datatype: JSR310 (com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.18.2 - https://github.com/FasterXML/jackson-modules-java8/jackson-datatype-jsr310) + * Jackson datatype: jdk8 (com.fasterxml.jackson.datatype:jackson-datatype-jdk8:2.19.1 - https://github.com/FasterXML/jackson-modules-java8/jackson-datatype-jdk8) + * Jackson datatype: JSR310 (com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.19.1 - https://github.com/FasterXML/jackson-modules-java8/jackson-datatype-jsr310) * Jackson Jakarta-RS: base (com.fasterxml.jackson.jakarta.rs:jackson-jakarta-rs-base:2.16.2 - https://github.com/FasterXML/jackson-jakarta-rs-providers/jackson-jakarta-rs-base) * Jackson Jakarta-RS: JSON (com.fasterxml.jackson.jakarta.rs:jackson-jakarta-rs-json-provider:2.16.2 - https://github.com/FasterXML/jackson-jakarta-rs-providers/jackson-jakarta-rs-json-provider) * Jackson module: Jakarta XML Bind Annotations (jakarta.xml.bind) (com.fasterxml.jackson.module:jackson-module-jakarta-xmlbind-annotations:2.16.2 - https://github.com/FasterXML/jackson-modules-base) - * Jackson-module-parameter-names (com.fasterxml.jackson.module:jackson-module-parameter-names:2.18.2 - https://github.com/FasterXML/jackson-modules-java8/jackson-module-parameter-names) + * Jackson-module-parameter-names (com.fasterxml.jackson.module:jackson-module-parameter-names:2.19.1 - https://github.com/FasterXML/jackson-modules-java8/jackson-module-parameter-names) * Java UUID Generator (com.fasterxml.uuid:java-uuid-generator:4.1.0 - https://github.com/cowtowncoder/java-uuid-generator) * Woodstox (com.fasterxml.woodstox:woodstox-core:6.5.1 - https://github.com/FasterXML/woodstox) * zjsonpatch (com.flipkart.zjsonpatch:zjsonpatch:0.4.16 - https://github.com/flipkart-incubator/zjsonpatch/) @@ -60,17 +60,16 @@ https://wiki.lyrasis.org/display/DSPACE/Code+Contribution+Guidelines * uri-template (com.github.java-json-tools:uri-template:0.10 - https://github.com/java-json-tools/uri-template) * JCIP Annotations under Apache License (com.github.stephenc.jcip:jcip-annotations:1.0-1 - http://stephenc.github.com/jcip-annotations) * 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.36.0 - https://errorprone.info/error_prone_annotations) + * Gson (com.google.code.gson:gson:2.13.1 - https://github.com/google/gson) + * error-prone annotations (com.google.errorprone:error_prone_annotations:2.38.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 ListenableFuture only (com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava - https://github.com/google/guava/listenablefuture) * 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/) - * 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.5 - https://jackcess.sourceforge.io) - * Jackcess Encrypt (com.healthmarketscience.jackcess:jackcess-encrypt:4.0.2 - http://jackcessencrypt.sf.net) + * Jackcess (com.healthmarketscience.jackcess:jackcess:4.0.8 - 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) * Disruptor Framework (com.lmax:disruptor:3.4.2 - http://lmax-exchange.github.com/disruptor) @@ -79,11 +78,15 @@ https://wiki.lyrasis.org/display/DSPACE/Code+Contribution+Guidelines * JsonSchemaValidator (com.networknt:json-schema-validator:1.0.76 - https://github.com/networknt/json-schema-validator) * Nimbus JOSE+JWT (com.nimbusds:nimbus-jose-jwt:9.28 - https://bitbucket.org/connect2id/nimbus-jose-jwt) * Nimbus JOSE+JWT (com.nimbusds:nimbus-jose-jwt:9.48 - https://bitbucket.org/connect2id/nimbus-jose-jwt) - * opencsv (com.opencsv:opencsv:5.10 - http://opencsv.sf.net) + * opencsv (com.opencsv:opencsv:5.11.1 - 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) * rome-utils (com.rometools:rome-utils:1.19.0 - http://rometools.com/rome-utils) + * mockwebserver (com.squareup.okhttp3:mockwebserver:4.12.0 - https://square.github.io/okhttp/) + * okhttp (com.squareup.okhttp3:okhttp:4.12.0 - https://square.github.io/okhttp/) + * 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) @@ -96,14 +99,14 @@ https://wiki.lyrasis.org/display/DSPACE/Code+Contribution+Guidelines * 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.10.0 - https://commons.apache.org/proper/commons-beanutils) + * 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.17.2 - https://commons.apache.org/proper/commons-codec/) + * Apache Commons Codec (commons-codec:commons-codec:1.18.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/) - * Apache Commons IO (commons-io:commons-io:2.18.0 - https://commons.apache.org/proper/commons-io/) + * Apache Commons IO (commons-io:commons-io:2.19.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.4 - https://commons.apache.org/proper/commons-logging/) + * 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/) * GeoJson POJOs for Jackson (de.grundid.opendatalab:geojson-jackson:1.14 - https://github.com/opendatalab-de/geojson-jackson) * broker-client (eu.openaire:broker-client:1.1.2 - http://api.openaire.eu/broker/broker-client) @@ -114,12 +117,10 @@ https://wiki.lyrasis.org/display/DSPACE/Code+Contribution+Guidelines * 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) * SWORD v2 Common Server Library (forked) (io.gdcc:sword2-server:2.0.0 - https://github.com/gdcc/sword2-server) - * micrometer-commons (io.micrometer:micrometer-commons:1.14.2 - https://github.com/micrometer-metrics/micrometer) - * micrometer-commons (io.micrometer:micrometer-commons:1.14.3 - https://github.com/micrometer-metrics/micrometer) - * micrometer-core (io.micrometer:micrometer-core:1.14.3 - https://github.com/micrometer-metrics/micrometer) - * micrometer-jakarta9 (io.micrometer:micrometer-jakarta9:1.14.3 - https://github.com/micrometer-metrics/micrometer) - * micrometer-observation (io.micrometer:micrometer-observation:1.14.2 - https://github.com/micrometer-metrics/micrometer) - * micrometer-observation (io.micrometer:micrometer-observation:1.14.3 - https://github.com/micrometer-metrics/micrometer) + * micrometer-commons (io.micrometer:micrometer-commons:1.14.8 - https://github.com/micrometer-metrics/micrometer) + * micrometer-core (io.micrometer:micrometer-core:1.15.1 - https://github.com/micrometer-metrics/micrometer) + * micrometer-jakarta9 (io.micrometer:micrometer-jakarta9:1.15.1 - https://github.com/micrometer-metrics/micrometer) + * micrometer-observation (io.micrometer:micrometer-observation:1.14.8 - https://github.com/micrometer-metrics/micrometer) * Netty/Buffer (io.netty:netty-buffer:4.1.99.Final - https://netty.io/netty-buffer/) * Netty/Codec (io.netty:netty-codec:4.1.99.Final - https://netty.io/netty-codec/) * Netty/Codec/HTTP (io.netty:netty-codec-http:4.1.86.Final - https://netty.io/netty-codec-http/) @@ -167,16 +168,17 @@ https://wiki.lyrasis.org/display/DSPACE/Code+Contribution+Guidelines * Jakarta Bean Validation API (jakarta.validation:jakarta.validation-api:3.0.2 - https://beanvalidation.org) * JSR107 API and SPI (javax.cache:cache-api:1.1.1 - https://github.com/jsr107/jsr107spec) * jdbm (jdbm:jdbm:1.0 - no url defined) - * Joda-Time (joda-time:joda-time:2.13.0 - https://www.joda.org/joda-time/) - * Byte Buddy (without dependencies) (net.bytebuddy:byte-buddy:1.16.1 - https://bytebuddy.net/byte-buddy) + * Joda-Time (joda-time:joda-time:2.12.7 - 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.14.11 - 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) * 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.1 - 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.1 - https://urielch.github.io/) + * JSON Small and Fast Parser (net.minidev:json-smart:2.5.2 - 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) * Abdera Parser (org.apache.abdera:abdera-parser:1.1.3 - http://abdera.apache.org/abdera-parser) @@ -187,18 +189,18 @@ https://wiki.lyrasis.org/display/DSPACE/Code+Contribution+Guidelines * 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.4 - https://commons.apache.org/proper/commons-collections/) + * 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.11.0 - https://commons.apache.org/proper/commons-configuration/) - * Apache Commons CSV (org.apache.commons:commons-csv:1.13.0 - https://commons.apache.org/proper/commons-csv/) + * 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 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 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.0 - https://commons.apache.org/proper/commons-pool/) - * Apache Commons Text (org.apache.commons:commons-text:1.13.0 - https://commons.apache.org/proper/commons-text) + * 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) * 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) @@ -212,13 +214,13 @@ https://wiki.lyrasis.org/display/DSPACE/Code+Contribution+Guidelines * Apache HttpCore (org.apache.httpcomponents:httpcore:4.4.16 - http://hc.apache.org/httpcomponents-core-ga) * Apache HttpClient Mime (org.apache.httpcomponents:httpmime:4.5.14 - http://hc.apache.org/httpcomponents-client-ga) * Apache HttpClient (org.apache.httpcomponents.client5:httpclient5:5.1.3 - https://hc.apache.org/httpcomponents-client-5.0.x/5.1.3/httpclient5/) - * Apache HttpClient (org.apache.httpcomponents.client5:httpclient5:5.4.1 - https://hc.apache.org/httpcomponents-client-5.4.x/5.4.1/httpclient5/) + * Apache HttpClient (org.apache.httpcomponents.client5:httpclient5:5.5 - https://hc.apache.org/httpcomponents-client-5.5.x/5.5/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/1.1 (org.apache.httpcomponents.core5:httpcore5:5.3.1 - https://hc.apache.org/httpcomponents-core-5.3.x/5.3.1/httpcore5/) + * Apache HttpComponents Core HTTP/1.1 (org.apache.httpcomponents.core5:httpcore5:5.3.4 - https://hc.apache.org/httpcomponents-core-5.3.x/5.3.4/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 HttpComponents Core HTTP/2 (org.apache.httpcomponents.core5:httpcore5-h2:5.3.1 - https://hc.apache.org/httpcomponents-core-5.3.x/5.3.1/httpcore5-h2/) + * Apache HttpComponents Core HTTP/2 (org.apache.httpcomponents.core5:httpcore5-h2:5.3.4 - https://hc.apache.org/httpcomponents-core-5.3.x/5.3.4/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.11 - http://james.apache.org/mime4j/apache-mime4j-dom) + * Apache James :: Mime4j :: DOM (org.apache.james:apache-mime4j-dom:0.8.12 - http://james.apache.org/mime4j/apache-mime4j-dom) * Apache Jena - Libraries POM (org.apache.jena:apache-jena-libs:4.10.0 - https://jena.apache.org/apache-jena-libs/) * Apache Jena - ARQ (org.apache.jena:jena-arq:4.10.0 - https://jena.apache.org/jena-arq/) * Apache Jena - Base (org.apache.jena:jena-base:4.10.0 - https://jena.apache.org/jena-base/) @@ -270,59 +272,60 @@ 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.33 - http://pdfbox.apache.org/) + * Apache FontBox (org.apache.pdfbox:fontbox:2.0.34 - 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.33 - https://www.apache.org/pdfbox-parent/pdfbox/) - * Apache PDFBox tools (org.apache.pdfbox:pdfbox-tools:2.0.31 - https://www.apache.org/pdfbox-parent/pdfbox-tools/) - * Apache XmpBox (org.apache.pdfbox:xmpbox:2.0.31 - https://www.apache.org/pdfbox-parent/xmpbox/) - * Apache POI - Common (org.apache.poi:poi:5.2.5 - https://poi.apache.org/) - * Apache POI - API based on OPC and OOXML schemas (org.apache.poi:poi-ooxml:5.2.5 - https://poi.apache.org/) - * Apache POI (org.apache.poi:poi-ooxml-lite:5.2.5 - https://poi.apache.org/) - * Apache POI (org.apache.poi:poi-scratchpad:5.2.5 - https://poi.apache.org/) + * 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 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.19.0 - http://thrift.apache.org) - * Apache Tika core (org.apache.tika:tika-core:2.9.2 - https://tika.apache.org/) - * Apache Tika Apple parser module (org.apache.tika:tika-parser-apple-module:2.9.2 - https://tika.apache.org/tika-parser-apple-module/) - * Apache Tika audiovideo parser module (org.apache.tika:tika-parser-audiovideo-module:2.9.2 - https://tika.apache.org/tika-parser-audiovideo-module/) - * Apache Tika cad parser module (org.apache.tika:tika-parser-cad-module:2.9.2 - https://tika.apache.org/tika-parser-cad-module/) - * Apache Tika code parser module (org.apache.tika:tika-parser-code-module:2.9.2 - https://tika.apache.org/tika-parser-code-module/) - * Apache Tika crypto parser module (org.apache.tika:tika-parser-crypto-module:2.9.2 - https://tika.apache.org/tika-parser-crypto-module/) - * Apache Tika digest commons (org.apache.tika:tika-parser-digest-commons:2.9.2 - https://tika.apache.org/tika-parser-digest-commons/) - * Apache Tika font parser module (org.apache.tika:tika-parser-font-module:2.9.2 - https://tika.apache.org/tika-parser-font-module/) - * Apache Tika html parser module (org.apache.tika:tika-parser-html-module:2.9.2 - https://tika.apache.org/tika-parser-html-module/) - * Apache Tika image parser module (org.apache.tika:tika-parser-image-module:2.9.2 - https://tika.apache.org/tika-parser-image-module/) - * Apache Tika mail commons (org.apache.tika:tika-parser-mail-commons:2.9.2 - https://tika.apache.org/tika-parser-mail-commons/) - * Apache Tika mail parser module (org.apache.tika:tika-parser-mail-module:2.9.2 - https://tika.apache.org/tika-parser-mail-module/) - * Apache Tika Microsoft parser module (org.apache.tika:tika-parser-microsoft-module:2.9.2 - https://tika.apache.org/tika-parser-microsoft-module/) - * Apache Tika miscellaneous office format parser module (org.apache.tika:tika-parser-miscoffice-module:2.9.2 - https://tika.apache.org/tika-parser-miscoffice-module/) - * Apache Tika news parser module (org.apache.tika:tika-parser-news-module:2.9.2 - https://tika.apache.org/tika-parser-news-module/) - * Apache Tika OCR parser module (org.apache.tika:tika-parser-ocr-module:2.9.2 - https://tika.apache.org/tika-parser-ocr-module/) - * Apache Tika PDF parser module (org.apache.tika:tika-parser-pdf-module:2.9.2 - https://tika.apache.org/tika-parser-pdf-module/) - * Apache Tika package parser module (org.apache.tika:tika-parser-pkg-module:2.9.2 - https://tika.apache.org/tika-parser-pkg-module/) - * Apache Tika text parser module (org.apache.tika:tika-parser-text-module:2.9.2 - https://tika.apache.org/tika-parser-text-module/) - * Apache Tika WARC parser module (org.apache.tika:tika-parser-webarchive-module:2.9.2 - https://tika.apache.org/tika-parser-webarchive-module/) - * Apache Tika XML parser module (org.apache.tika:tika-parser-xml-module:2.9.2 - https://tika.apache.org/tika-parser-xml-module/) - * Apache Tika XMP commons (org.apache.tika:tika-parser-xmp-commons:2.9.2 - https://tika.apache.org/tika-parser-xmp-commons/) - * Apache Tika ZIP commons (org.apache.tika:tika-parser-zip-commons:2.9.2 - https://tika.apache.org/tika-parser-zip-commons/) - * Apache Tika standard parser package (org.apache.tika:tika-parsers-standard-package:2.9.2 - https://tika.apache.org/tika-parsers/tika-parsers-standard/tika-parsers-standard-package/) - * tomcat-embed-core (org.apache.tomcat.embed:tomcat-embed-core:10.1.34 - https://tomcat.apache.org/) - * tomcat-embed-el (org.apache.tomcat.embed:tomcat-embed-el:10.1.34 - https://tomcat.apache.org/) - * tomcat-embed-websocket (org.apache.tomcat.embed:tomcat-embed-websocket:10.1.34 - https://tomcat.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/) + * tomcat-embed-core (org.apache.tomcat.embed:tomcat-embed-core:10.1.42 - https://tomcat.apache.org/) + * tomcat-embed-el (org.apache.tomcat.embed:tomcat-embed-el:10.1.42 - https://tomcat.apache.org/) + * tomcat-embed-websocket (org.apache.tomcat.embed:tomcat-embed-websocket:10.1.42 - https://tomcat.apache.org/) * Apache Velocity - Engine (org.apache.velocity:velocity-engine-core:2.4.1 - http://velocity.apache.org/engine/devel/velocity-engine-core/) * Apache Velocity - JSR 223 Scripting (org.apache.velocity:velocity-engine-scripting:2.3 - http://velocity.apache.org/engine/devel/velocity-engine-scripting/) * Apache Velocity Tools - Generic tools (org.apache.velocity.tools:velocity-tools-generic:3.1 - https://velocity.apache.org/tools/devel/velocity-tools-generic/) * Axiom API (org.apache.ws.commons.axiom:axiom-api:1.2.14 - http://ws.apache.org/axiom/) * Axiom Impl (org.apache.ws.commons.axiom:axiom-impl:1.2.14 - http://ws.apache.org/axiom/) - * XmlBeans (org.apache.xmlbeans:xmlbeans:5.2.0 - https://xmlbeans.apache.org/) + * XmlBeans (org.apache.xmlbeans:xmlbeans:5.3.0 - https://xmlbeans.apache.org/) * Apache ZooKeeper - Server (org.apache.zookeeper:zookeeper:3.6.2 - http://zookeeper.apache.org/zookeeper) * Apache ZooKeeper - Jute (org.apache.zookeeper:zookeeper-jute:3.6.2 - http://zookeeper.apache.org/zookeeper-jute) * org.apiguardian:apiguardian-api (org.apiguardian:apiguardian-api:1.1.2 - https://github.com/apiguardian-team/apiguardian) - * AssertJ Core (org.assertj:assertj-core:3.26.3 - https://assertj.github.io/doc/#assertj-core) + * AssertJ Core (org.assertj:assertj-core:3.27.3 - https://assertj.github.io/doc/#assertj-core) * Evo Inflector (org.atteo:evo-inflector:1.3 - http://atteo.org/static/evo-inflector) + * attoparser (org.attoparser:attoparser:2.0.7.RELEASE - https://www.attoparser.org) * Awaitility (org.awaitility:awaitility:4.2.2 - http://awaitility.org) * 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/) @@ -376,24 +379,28 @@ https://wiki.lyrasis.org/display/DSPACE/Code+Contribution+Guidelines * jersey-core-common (org.glassfish.jersey.core:jersey-common:3.1.10 - https://projects.eclipse.org/projects/ee4j.jersey/jersey-common) * jersey-inject-hk2 (org.glassfish.jersey.inject:jersey-hk2:3.1.10 - https://projects.eclipse.org/projects/ee4j.jersey/project/jersey-hk2) * jersey-media-multipart (org.glassfish.jersey.media:jersey-media-multipart:3.1.3 - https://projects.eclipse.org/projects/ee4j.jersey/project/jersey-media-multipart) - * Hibernate Validator Engine (org.hibernate.validator:hibernate-validator:8.0.1.Final - http://hibernate.org/validator/hibernate-validator) - * Hibernate Validator Portable Extension (org.hibernate.validator:hibernate-validator-cdi:8.0.1.Final - http://hibernate.org/validator/hibernate-validator-cdi) + * Hibernate Validator Engine (org.hibernate.validator:hibernate-validator:8.0.2.Final - http://hibernate.org/validator/hibernate-validator) + * Hibernate Validator Portable Extension (org.hibernate.validator:hibernate-validator-cdi:8.0.2.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/) * JBoss Logging 3 (org.jboss.logging:jboss-logging:3.6.1.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) + * 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/) * Proj4J (org.locationtech.proj4j:proj4j:1.1.5 - https://github.com/locationtech/proj4j) * Spatial4J (org.locationtech.spatial4j:spatial4j:0.7 - https://projects.eclipse.org/projects/locationtech.spatial4j) * MockServer Java Client (org.mock-server:mockserver-client-java:5.15.0 - https://www.mock-server.com) * MockServer Core (org.mock-server:mockserver-core:5.15.0 - https://www.mock-server.com) * MockServer JUnit 4 Integration (org.mock-server:mockserver-junit-rule:5.15.0 - https://www.mock-server.com) * MockServer & Proxy Netty (org.mock-server:mockserver-netty:5.15.0 - https://www.mock-server.com) - * jwarc (org.netpreserve:jwarc:0.29.0 - https://github.com/iipc/jwarc) + * jwarc (org.netpreserve:jwarc:0.31.1 - https://github.com/iipc/jwarc) * Objenesis (org.objenesis:objenesis:3.2 - http://objenesis.org/objenesis) - * parboiled-core (org.parboiled:parboiled-core:1.1.7 - http://parboiled.org) - * parboiled-java (org.parboiled:parboiled-java:1.1.7 - http://parboiled.org) + * org.opentest4j:opentest4j (org.opentest4j:opentest4j:1.3.0 - https://github.com/ota4j-team/opentest4j) * org.roaringbitmap:RoaringBitmap (org.roaringbitmap:RoaringBitmap:1.0.0 - https://github.com/RoaringBitmap/RoaringBitmap) * RRD4J (org.rrd4j:rrd4j:3.5 - https://github.com/rrd4j/rrd4j/) * Scala Library (org.scala-lang:scala-library:2.13.2 - https://www.scala-lang.org/) @@ -403,53 +410,56 @@ https://wiki.lyrasis.org/display/DSPACE/Code+Contribution+Guidelines * 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.3 - https://github.com/skyscreamer/JSONassert) - * JCL 1.2 implemented over SLF4J (org.slf4j:jcl-over-slf4j:2.0.16 - http://www.slf4j.org) - * Spring AOP (org.springframework:spring-aop:6.2.2 - https://github.com/spring-projects/spring-framework) - * Spring Beans (org.springframework:spring-beans:6.2.2 - https://github.com/spring-projects/spring-framework) - * Spring Context (org.springframework:spring-context:6.2.2 - https://github.com/spring-projects/spring-framework) - * Spring Context Support (org.springframework:spring-context-support:6.2.2 - https://github.com/spring-projects/spring-framework) - * Spring Core (org.springframework:spring-core:6.2.2 - https://github.com/spring-projects/spring-framework) - * Spring Expression Language (SpEL) (org.springframework:spring-expression:6.2.2 - https://github.com/spring-projects/spring-framework) - * Spring Commons Logging Bridge (org.springframework:spring-jcl:6.2.2 - https://github.com/spring-projects/spring-framework) - * Spring JDBC (org.springframework:spring-jdbc:6.2.2 - https://github.com/spring-projects/spring-framework) - * Spring Object/Relational Mapping (org.springframework:spring-orm:6.2.2 - https://github.com/spring-projects/spring-framework) - * Spring TestContext Framework (org.springframework:spring-test:6.2.2 - https://github.com/spring-projects/spring-framework) - * Spring Transaction (org.springframework:spring-tx:6.2.2 - https://github.com/spring-projects/spring-framework) - * Spring Web (org.springframework:spring-web:6.2.2 - https://github.com/spring-projects/spring-framework) - * Spring Web MVC (org.springframework:spring-webmvc:6.2.2 - https://github.com/spring-projects/spring-framework) - * spring-boot (org.springframework.boot:spring-boot:3.4.2 - https://spring.io/projects/spring-boot) - * spring-boot-actuator (org.springframework.boot:spring-boot-actuator:3.4.2 - https://spring.io/projects/spring-boot) - * spring-boot-actuator-autoconfigure (org.springframework.boot:spring-boot-actuator-autoconfigure:3.4.2 - https://spring.io/projects/spring-boot) - * spring-boot-autoconfigure (org.springframework.boot:spring-boot-autoconfigure:3.4.2 - https://spring.io/projects/spring-boot) - * Spring Boot Configuration Processor (org.springframework.boot:spring-boot-configuration-processor:2.0.0.RELEASE - https://projects.spring.io/spring-boot/#/spring-boot-parent/spring-boot-tools/spring-boot-configuration-processor) - * spring-boot-starter (org.springframework.boot:spring-boot-starter:3.4.2 - https://spring.io/projects/spring-boot) - * spring-boot-starter-actuator (org.springframework.boot:spring-boot-starter-actuator:3.4.2 - https://spring.io/projects/spring-boot) - * spring-boot-starter-aop (org.springframework.boot:spring-boot-starter-aop:3.4.2 - https://spring.io/projects/spring-boot) - * spring-boot-starter-cache (org.springframework.boot:spring-boot-starter-cache:3.4.2 - https://spring.io/projects/spring-boot) - * spring-boot-starter-data-rest (org.springframework.boot:spring-boot-starter-data-rest:3.4.2 - https://spring.io/projects/spring-boot) - * spring-boot-starter-json (org.springframework.boot:spring-boot-starter-json:3.4.2 - https://spring.io/projects/spring-boot) - * spring-boot-starter-log4j2 (org.springframework.boot:spring-boot-starter-log4j2:3.4.2 - https://spring.io/projects/spring-boot) - * spring-boot-starter-security (org.springframework.boot:spring-boot-starter-security:3.4.2 - https://spring.io/projects/spring-boot) - * spring-boot-starter-test (org.springframework.boot:spring-boot-starter-test:3.4.2 - https://spring.io/projects/spring-boot) - * spring-boot-starter-tomcat (org.springframework.boot:spring-boot-starter-tomcat:3.4.2 - https://spring.io/projects/spring-boot) - * spring-boot-starter-web (org.springframework.boot:spring-boot-starter-web:3.4.2 - https://spring.io/projects/spring-boot) - * spring-boot-test (org.springframework.boot:spring-boot-test:3.4.2 - https://spring.io/projects/spring-boot) - * spring-boot-test-autoconfigure (org.springframework.boot:spring-boot-test-autoconfigure:3.4.2 - https://spring.io/projects/spring-boot) - * Spring Data Core (org.springframework.data:spring-data-commons:3.4.2 - https://spring.io/projects/spring-data) - * Spring Data REST - Core (org.springframework.data:spring-data-rest-core:4.4.2 - https://www.spring.io/spring-data/spring-data-rest-parent/spring-data-rest-core) - * Spring Data REST - WebMVC (org.springframework.data:spring-data-rest-webmvc:4.4.2 - https://www.spring.io/spring-data/spring-data-rest-parent/spring-data-rest-webmvc) - * Spring HATEOAS (org.springframework.hateoas:spring-hateoas:2.4.1 - https://github.com/spring-projects/spring-hateoas) + * JCL 1.2 implemented over SLF4J (org.slf4j:jcl-over-slf4j:2.0.17 - http://www.slf4j.org) + * Spring AOP (org.springframework:spring-aop:6.2.8 - https://github.com/spring-projects/spring-framework) + * Spring Beans (org.springframework:spring-beans:6.2.8 - https://github.com/spring-projects/spring-framework) + * Spring Context (org.springframework:spring-context:6.2.8 - https://github.com/spring-projects/spring-framework) + * Spring Context Support (org.springframework:spring-context-support:6.2.8 - https://github.com/spring-projects/spring-framework) + * Spring Core (org.springframework:spring-core:6.2.8 - https://github.com/spring-projects/spring-framework) + * Spring Expression Language (SpEL) (org.springframework:spring-expression:6.2.8 - https://github.com/spring-projects/spring-framework) + * Spring Commons Logging Bridge (org.springframework:spring-jcl:6.2.8 - https://github.com/spring-projects/spring-framework) + * Spring JDBC (org.springframework:spring-jdbc:6.2.8 - https://github.com/spring-projects/spring-framework) + * Spring Object/Relational Mapping (org.springframework:spring-orm:6.2.8 - https://github.com/spring-projects/spring-framework) + * Spring TestContext Framework (org.springframework:spring-test:6.2.8 - https://github.com/spring-projects/spring-framework) + * Spring Transaction (org.springframework:spring-tx:6.2.8 - https://github.com/spring-projects/spring-framework) + * Spring Web (org.springframework:spring-web:6.2.8 - https://github.com/spring-projects/spring-framework) + * Spring Web MVC (org.springframework:spring-webmvc:6.2.8 - https://github.com/spring-projects/spring-framework) + * spring-boot (org.springframework.boot:spring-boot:3.5.3 - https://spring.io/projects/spring-boot) + * spring-boot-actuator (org.springframework.boot:spring-boot-actuator:3.5.3 - https://spring.io/projects/spring-boot) + * spring-boot-actuator-autoconfigure (org.springframework.boot:spring-boot-actuator-autoconfigure:3.5.3 - https://spring.io/projects/spring-boot) + * spring-boot-autoconfigure (org.springframework.boot:spring-boot-autoconfigure:3.5.3 - https://spring.io/projects/spring-boot) + * spring-boot-starter (org.springframework.boot:spring-boot-starter:3.5.3 - https://spring.io/projects/spring-boot) + * spring-boot-starter-actuator (org.springframework.boot:spring-boot-starter-actuator:3.5.3 - https://spring.io/projects/spring-boot) + * spring-boot-starter-aop (org.springframework.boot:spring-boot-starter-aop:3.5.3 - https://spring.io/projects/spring-boot) + * spring-boot-starter-cache (org.springframework.boot:spring-boot-starter-cache:3.5.3 - https://spring.io/projects/spring-boot) + * spring-boot-starter-data-rest (org.springframework.boot:spring-boot-starter-data-rest:3.5.3 - https://spring.io/projects/spring-boot) + * spring-boot-starter-json (org.springframework.boot:spring-boot-starter-json:3.5.3 - https://spring.io/projects/spring-boot) + * spring-boot-starter-log4j2 (org.springframework.boot:spring-boot-starter-log4j2:3.5.3 - https://spring.io/projects/spring-boot) + * spring-boot-starter-security (org.springframework.boot:spring-boot-starter-security:3.5.3 - https://spring.io/projects/spring-boot) + * spring-boot-starter-test (org.springframework.boot:spring-boot-starter-test:3.5.3 - https://spring.io/projects/spring-boot) + * spring-boot-starter-thymeleaf (org.springframework.boot:spring-boot-starter-thymeleaf:3.5.3 - https://spring.io/projects/spring-boot) + * spring-boot-starter-tomcat (org.springframework.boot:spring-boot-starter-tomcat:3.5.3 - https://spring.io/projects/spring-boot) + * spring-boot-starter-web (org.springframework.boot:spring-boot-starter-web:3.5.3 - https://spring.io/projects/spring-boot) + * spring-boot-test (org.springframework.boot:spring-boot-test:3.5.3 - https://spring.io/projects/spring-boot) + * spring-boot-test-autoconfigure (org.springframework.boot:spring-boot-test-autoconfigure:3.5.3 - https://spring.io/projects/spring-boot) + * Spring Data Core (org.springframework.data:spring-data-commons:3.5.1 - https://spring.io/projects/spring-data) + * Spring Data REST - Core (org.springframework.data:spring-data-rest-core:4.5.1 - https://www.spring.io/spring-data/spring-data-rest-parent/spring-data-rest-core) + * Spring Data REST - WebMVC (org.springframework.data:spring-data-rest-webmvc:4.5.1 - https://www.spring.io/spring-data/spring-data-rest-parent/spring-data-rest-webmvc) + * Spring HATEOAS (org.springframework.hateoas:spring-hateoas:2.5.1 - https://github.com/spring-projects/spring-hateoas) * Spring Plugin - Core (org.springframework.plugin:spring-plugin-core:3.0.0 - https://github.com/spring-projects/spring-plugin/spring-plugin-core) - * spring-security-config (org.springframework.security:spring-security-config:6.4.2 - https://spring.io/projects/spring-security) - * spring-security-core (org.springframework.security:spring-security-core:6.4.2 - https://spring.io/projects/spring-security) - * spring-security-crypto (org.springframework.security:spring-security-crypto:6.4.2 - https://spring.io/projects/spring-security) - * spring-security-test (org.springframework.security:spring-security-test:6.4.2 - https://spring.io/projects/spring-security) - * spring-security-web (org.springframework.security:spring-security-web:6.4.2 - https://spring.io/projects/spring-security) + * spring-security-config (org.springframework.security:spring-security-config:6.5.1 - https://spring.io/projects/spring-security) + * spring-security-core (org.springframework.security:spring-security-core:6.5.1 - https://spring.io/projects/spring-security) + * spring-security-crypto (org.springframework.security:spring-security-crypto:6.5.1 - https://spring.io/projects/spring-security) + * spring-security-test (org.springframework.security:spring-security-test:6.5.1 - https://spring.io/projects/spring-security) + * spring-security-web (org.springframework.security:spring-security-web:6.5.1 - https://spring.io/projects/spring-security) + * thymeleaf (org.thymeleaf:thymeleaf:3.1.3.RELEASE - http://www.thymeleaf.org/thymeleaf-lib/thymeleaf) + * thymeleaf-spring6 (org.thymeleaf:thymeleaf-spring6:3.1.3.RELEASE - http://www.thymeleaf.org/thymeleaf-lib/thymeleaf-spring6) + * unbescape (org.unbescape:unbescape:1.1.6.RELEASE - http://www.unbescape.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.0 - https://www.xmlunit.org/) + * org.xmlunit:xmlunit-core (org.xmlunit:xmlunit-core:2.10.2 - 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:2.3 - https://bitbucket.org/snakeyaml/snakeyaml) + * SnakeYAML (org.yaml:snakeyaml:2.4 - https://bitbucket.org/snakeyaml/snakeyaml) * Xerces2-j (xerces:xercesImpl:2.12.2 - https://xerces.apache.org/xerces2-j/) BSD License: @@ -463,7 +473,7 @@ https://wiki.lyrasis.org/display/DSPACE/Code+Contribution+Guidelines * Protocol Buffers [Core] (com.google.protobuf:protobuf-java:3.24.3 - 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.2 - https://github.com/dnsjava/dnsjava) + * dnsjava (dnsjava:dnsjava:3.6.3 - https://github.com/dnsjava/dnsjava) * jaxen (jaxen:jaxen:2.0.0 - http://www.cafeconleche.org/jaxen/jaxen) * ANTLR 4 Runtime (org.antlr:antlr4-runtime:4.13.2 - https://www.antlr.org/antlr4-runtime/) * commons-compiler (org.codehaus.janino:commons-compiler:3.1.8 - http://janino-compiler.github.io/commons-compiler/) @@ -481,10 +491,10 @@ https://wiki.lyrasis.org/display/DSPACE/Code+Contribution+Guidelines * asm-analysis (org.ow2.asm:asm-analysis:8.0.1 - http://asm.ow2.io/) * asm-commons (org.ow2.asm:asm-commons:8.0.1 - http://asm.ow2.io/) * asm-tree (org.ow2.asm:asm-tree:8.0.1 - http://asm.ow2.io/) - * ASM Util (org.ow2.asm:asm-util:5.0.3 - http://asm.objectweb.org/asm-util/) - * PostgreSQL JDBC Driver (org.postgresql:postgresql:42.7.5 - https://jdbc.postgresql.org) + * PostgreSQL JDBC Driver (org.postgresql:postgresql:42.7.7 - 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) * XMLUnit for Java (xmlunit:xmlunit:1.3 - http://xmlunit.sourceforge.net/) CC0: @@ -549,14 +559,13 @@ https://wiki.lyrasis.org/display/DSPACE/Code+Contribution+Guidelines * 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) * Jakarta Annotations API (jakarta.annotation:jakarta.annotation-api:2.1.1 - https://projects.eclipse.org/projects/ee4j.ca) - * Jakarta Expression Language API (jakarta.el:jakarta.el-api:5.0.1 - https://projects.eclipse.org/projects/ee4j.el) * Jakarta Mail API (jakarta.mail:jakarta.mail-api:2.1.3 - https://projects.eclipse.org/projects/ee4j/jakarta.mail-api) * Jakarta Persistence API (jakarta.persistence:jakarta.persistence-api:3.1.0 - https://github.com/eclipse-ee4j/jpa-api) * Jakarta Servlet (jakarta.servlet:jakarta.servlet-api:6.1.0 - https://projects.eclipse.org/projects/ee4j.servlet) * jakarta.transaction API (jakarta.transaction:jakarta.transaction-api:2.0.1 - https://projects.eclipse.org/projects/ee4j.jta) * Jakarta RESTful WS API (jakarta.ws.rs:jakarta.ws.rs-api:3.1.0 - https://github.com/eclipse-ee4j/jaxrs-api) * JUnit (junit:junit:4.13.2 - http://junit.org) - * AspectJ Weaver (org.aspectj:aspectjweaver:1.9.22.1 - https://www.eclipse.org/aspectj/) + * AspectJ Weaver (org.aspectj:aspectjweaver:1.9.24 - https://www.eclipse.org/aspectj/) * Angus Mail default provider (org.eclipse.angus:jakarta.mail:2.0.3 - http://eclipse-ee4j.github.io/angus-mail/jakarta.mail) * 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/) @@ -606,16 +615,19 @@ https://wiki.lyrasis.org/display/DSPACE/Code+Contribution+Guidelines * jersey-core-common (org.glassfish.jersey.core:jersey-common:3.1.10 - https://projects.eclipse.org/projects/ee4j.jersey/jersey-common) * jersey-inject-hk2 (org.glassfish.jersey.inject:jersey-hk2:3.1.10 - https://projects.eclipse.org/projects/ee4j.jersey/project/jersey-hk2) * jersey-media-multipart (org.glassfish.jersey.media:jersey-media-multipart:3.1.3 - https://projects.eclipse.org/projects/ee4j.jersey/project/jersey-media-multipart) + * 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) GENERAL PUBLIC LICENSE, version 3 (GPL-3.0): - * juniversalchardet (com.github.albfernandez:juniversalchardet:2.4.0 - https://github.com/albfernandez/juniversalchardet) + * juniversalchardet (com.github.albfernandez:juniversalchardet:2.5.0 - https://github.com/albfernandez/juniversalchardet) GNU LESSER GENERAL PUBLIC LICENSE, version 3 (LGPL-3.0): - * juniversalchardet (com.github.albfernandez:juniversalchardet:2.4.0 - https://github.com/albfernandez/juniversalchardet) + * juniversalchardet (com.github.albfernandez:juniversalchardet:2.5.0 - https://github.com/albfernandez/juniversalchardet) GNU Lesser General Public License (LGPL): @@ -641,9 +653,9 @@ https://wiki.lyrasis.org/display/DSPACE/Code+Contribution+Guidelines * RE2/J (com.google.re2j:re2j:1.2 - http://github.com/google/re2j) - Handle.Net Public License Agreement (Ver.2): + Handle.Net Public License Agreement (Ver.3): - * Handle Server (net.handle:handle:9.3.1 - https://www.handle.net) + * Handle Server (net.handle:handle:9.3.2 - https://www.handle.net) ISC License: @@ -659,18 +671,18 @@ https://wiki.lyrasis.org/display/DSPACE/Code+Contribution+Guidelines * s3mock (io.findify:s3mock_2.13:0.2.6 - https://github.com/findify/s3mock) * ClassGraph (io.github.classgraph:classgraph:4.8.165 - 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 S/MIME API (org.bouncycastle:bcmail-jdk18on:1.77 - https://www.bouncycastle.org/java.html) - * Bouncy Castle PKIX, CMS, EAC, TSP, PKCS, OCSP, CMP, and CRMF APIs (org.bouncycastle:bcpkix-jdk18on:1.80 - https://www.bouncycastle.org/download/bouncy-castle-java/) - * Bouncy Castle Provider (org.bouncycastle:bcprov-jdk18on:1.80 - https://www.bouncycastle.org/download/bouncy-castle-java/) - * Bouncy Castle ASN.1 Extension and Utility APIs (org.bouncycastle:bcutil-jdk18on:1.80 - https://www.bouncycastle.org/download/bouncy-castle-java/) + * Bouncy Castle JavaMail S/MIME APIs (org.bouncycastle:bcmail-jdk18on:1.80 - 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.48.4 - https://checkerframework.org/) + * Checker Qual (org.checkerframework:checker-qual:3.49.5 - https://checkerframework.org/) * jersey-core-client (org.glassfish.jersey.core:jersey-client:3.1.10 - https://projects.eclipse.org/projects/ee4j.jersey/jersey-client) * jersey-inject-hk2 (org.glassfish.jersey.inject:jersey-hk2:3.1.10 - https://projects.eclipse.org/projects/ee4j.jersey/project/jersey-hk2) * jersey-media-multipart (org.glassfish.jersey.media:jersey-media-multipart:3.1.3 - https://projects.eclipse.org/projects/ee4j.jersey/project/jersey-media-multipart) * 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) - * SLF4J API Module (org.slf4j:slf4j-api:2.0.16 - http://www.slf4j.org) + * SLF4J API Module (org.slf4j:slf4j-api:2.0.17 - http://www.slf4j.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) @@ -678,14 +690,14 @@ 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.40.0 - https://www.webjars.org) - * @json-editor/json-editor (org.webjars.npm:json-editor__json-editor:2.15.1 - https://www.webjars.org) + * core-js (org.webjars.npm:core-js:3.42.0 - https://www.webjars.org) + * @json-editor/json-editor (org.webjars.npm:json-editor__json-editor:2.15.2 - https://www.webjars.org) Mozilla Public License: - * juniversalchardet (com.github.albfernandez:juniversalchardet:2.4.0 - https://github.com/albfernandez/juniversalchardet) + * 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) - * Saxon-HE (net.sf.saxon:Saxon-HE:9.8.0-14 - http://www.saxonica.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) @@ -699,7 +711,6 @@ https://wiki.lyrasis.org/display/DSPACE/Code+Contribution+Guidelines * 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) - * XZ for Java (org.tukaani:xz:1.9 - https://tukaani.org/xz/java.html) UnRar License: diff --git a/pom.xml b/pom.xml index 9bddbe276164..e6f580426266 100644 --- a/pom.xml +++ b/pom.xml @@ -719,7 +719,7 @@ Apache Software License, Version 2.0|The SAX License|The W3C License Apache Software License, Version 2.0|Similar to Apache License but with the acknowledgment clause removed - BSD License|The BSD License|BSD licence|BSD license|BSD|BSD-style license|New BSD License|New BSD license|Revised BSD License|BSD 2-Clause license|3-Clause BSD License|BSD 2-Clause|BSD 3-clause New License|BSD Licence 3|BSD-2-Clause|BSD-3-Clause|Modified BSD|The New BSD License|The BSD 3-Clause License (BSD3)|BSD License 3|BSD License 2.0|The (New) BSD License + BSD License|The BSD License|BSD licence|BSD license|BSD|BSD-style license|New BSD License|New BSD license|Revised BSD License|BSD 2-Clause license|3-Clause BSD License|BSD 2-Clause|BSD 3-clause New License|BSD Licence 3|BSD-2-Clause|BSD-3-Clause|Modified BSD|The New BSD License|The BSD 3-Clause License (BSD3)|BSD License 3|BSD License 2.0|The (New) BSD License|0BSD BSD License|DSpace BSD License|DSpace Sourcecode License @@ -740,7 +740,7 @@ Common Development and Distribution License (CDDL)|GNU General Public License, Version 2 with the Classpath Exception Eclipse Distribution License, Version 1.0|Eclipse Distribution License (EDL), Version 1.0|Eclipse Distribution License - v 1.0|Eclipse Distribution License v. 1.0|EDL 1.0 - Eclipse Public License|Eclipse Public License - Version 1.0|Eclipse Public License - v 1.0|EPL 1.0 license|Eclipse Public License (EPL), Version 1.0|Eclipse Public License 1.0|Eclipse Public License v1.0|Eclipse Public License, Version 1.0|EPL 1.0|EPL 2.0|Eclipse Public License - v 2.0|EPL-2.0|Eclipse Public License 2.0|Eclipse Public License v. 2.0|Eclipse Public License, Version 2.0 + Eclipse Public License|Eclipse Public License - Version 1.0|Eclipse Public License - v 1.0|EPL 1.0 license|Eclipse Public License (EPL), Version 1.0|Eclipse Public License 1.0|Eclipse Public License v1.0|Eclipse Public License, Version 1.0|EPL 1.0|EPL 2.0|Eclipse Public License - v 2.0|EPL-2.0|Eclipse Public License 2.0|Eclipse Public License v. 2.0|Eclipse Public License, Version 2.0|Eclipse Public License v2.0 Eclipse Public License|Common Public License Version 1.0 From d48e22aff5a72f539cbf2a4a29981a9ee4a6cdc8 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Fri, 11 Jul 2025 14:01:37 -0500 Subject: [PATCH 797/979] Update LICENSES_THIRD_PARTY to prepare for 7.6.4 release. --- LICENSES_THIRD_PARTY | 245 +++++++++++++++++++++++-------------------- pom.xml | 4 +- 2 files changed, 133 insertions(+), 116 deletions(-) diff --git a/LICENSES_THIRD_PARTY b/LICENSES_THIRD_PARTY index e12c429121ac..304ee4e34302 100644 --- a/LICENSES_THIRD_PARTY +++ b/LICENSES_THIRD_PARTY @@ -21,18 +21,18 @@ 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.780 - https://aws.amazon.com/sdkforjava) - * AWS Java SDK for AWS KMS (com.amazonaws:aws-java-sdk-kms:1.12.780 - https://aws.amazon.com/sdkforjava) - * AWS Java SDK for Amazon S3 (com.amazonaws:aws-java-sdk-s3:1.12.780 - https://aws.amazon.com/sdkforjava) - * JMES Path Query library (com.amazonaws:jmespath-java:1.12.780 - https://aws.amazon.com/sdkforjava) + * 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.18.2 - https://github.com/FasterXML/jackson) - * Jackson-core (com.fasterxml.jackson.core:jackson-core:2.18.2 - https://github.com/FasterXML/jackson-core) - * jackson-databind (com.fasterxml.jackson.core:jackson-databind:2.18.2 - https://github.com/FasterXML/jackson) + * 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) * 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) @@ -57,22 +57,22 @@ https://wiki.lyrasis.org/display/DSPACE/Code+Contribution+Guidelines * Google APIs Client Library for Java (com.google.api-client:google-api-client:1.35.2 - https://github.com/googleapis/google-api-java-client/google-api-client) * 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.10.1 - https://github.com/google/gson/gson) + * 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) * 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 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.45.3 - 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.0 - 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.43.3 - 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.45.3 - 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.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) * 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/) - * Google OAuth Client Library for Java (com.google.oauth-client:google-oauth-client:1.37.0 - https://github.com/googleapis/google-oauth-java-client/google-oauth-client) + * 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.5 - https://jackcess.sourceforge.io) - * Jackcess Encrypt (com.healthmarketscience.jackcess:jackcess-encrypt:4.0.2 - http://jackcessencrypt.sf.net) + * Jackcess (com.healthmarketscience.jackcess:jackcess:4.0.8 - 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) * Disruptor Framework (com.lmax:disruptor:3.4.2 - http://lmax-exchange.github.com/disruptor) @@ -81,11 +81,15 @@ https://wiki.lyrasis.org/display/DSPACE/Code+Contribution+Guidelines * 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.10 - http://opencsv.sf.net) + * opencsv (com.opencsv:opencsv:5.11.1 - 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) * rome-utils (com.rometools:rome-utils:1.19.0 - http://rometools.com/rome-utils) + * mockwebserver (com.squareup.okhttp3:mockwebserver:4.12.0 - https://square.github.io/okhttp/) + * okhttp (com.squareup.okhttp3:okhttp:4.12.0 - https://square.github.io/okhttp/) + * 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) @@ -98,15 +102,15 @@ https://wiki.lyrasis.org/display/DSPACE/Code+Contribution+Guidelines * 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.10.0 - https://commons.apache.org/proper/commons-beanutils) + * 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.17.2 - https://commons.apache.org/proper/commons-codec/) + * Apache Commons Codec (commons-codec:commons-codec:1.18.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/) - * Apache Commons FileUpload (commons-fileupload:commons-fileupload:1.5 - https://commons.apache.org/proper/commons-fileupload/) - * Apache Commons IO (commons-io:commons-io:2.18.0 - https://commons.apache.org/proper/commons-io/) + * 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/) * Commons Lang (commons-lang:commons-lang:2.6 - http://commons.apache.org/lang/) - * Apache Commons Logging (commons-logging:commons-logging:1.3.4 - https://commons.apache.org/proper/commons-logging/) + * 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/) * 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) @@ -115,30 +119,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.69.0 - https://github.com/grpc/grpc-java) - * io.grpc:grpc-context (io.grpc:grpc-context:1.69.0 - https://github.com/grpc/grpc-java) + * 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) * micrometer-core (io.micrometer:micrometer-core:1.9.17 - https://github.com/micrometer-metrics/micrometer) - * Netty/Buffer (io.netty:netty-buffer:4.1.117.Final - https://netty.io/netty-buffer/) * Netty/Buffer (io.netty:netty-buffer:4.1.99.Final - https://netty.io/netty-buffer/) - * Netty/Codec (io.netty:netty-codec:4.1.117.Final - https://netty.io/netty-codec/) + * Netty/Buffer (io.netty:netty-buffer:4.2.2.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/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/Socks (io.netty:netty-codec-socks:4.1.86.Final - https://netty.io/netty-codec-socks/) - * Netty/Common (io.netty:netty-common:4.1.117.Final - https://netty.io/netty-common/) * Netty/Common (io.netty:netty-common:4.1.99.Final - https://netty.io/netty-common/) - * Netty/Handler (io.netty:netty-handler:4.1.117.Final - https://netty.io/netty-handler/) + * Netty/Common (io.netty:netty-common:4.2.2.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/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.117.Final - https://netty.io/netty-transport/) * 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/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.117.Final - https://netty.io/netty-transport-native-unix-common/) * 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/) * 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) @@ -167,16 +175,17 @@ 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.13.0 - https://www.joda.org/joda-time/) - * Byte Buddy (without dependencies) (net.bytebuddy:byte-buddy:1.16.1 - https://bytebuddy.net/byte-buddy) + * Joda-Time (joda-time:joda-time:2.14.0 - 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) * 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.1 - 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.1 - https://urielch.github.io/) + * JSON Small and Fast Parser (net.minidev:json-smart:2.5.2 - 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/) @@ -186,18 +195,18 @@ https://wiki.lyrasis.org/display/DSPACE/Code+Contribution+Guidelines * 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.4 - https://commons.apache.org/proper/commons-collections/) + * 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.11.0 - https://commons.apache.org/proper/commons-configuration/) - * Apache Commons CSV (org.apache.commons:commons-csv:1.10.0 - https://commons.apache.org/proper/commons-csv/) + * 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 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 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.0 - https://commons.apache.org/proper/commons-pool/) - * Apache Commons Text (org.apache.commons:commons-text:1.13.0 - https://commons.apache.org/proper/commons-text) + * 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) * 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) @@ -214,7 +223,7 @@ https://wiki.lyrasis.org/display/DSPACE/Code+Contribution+Guidelines * 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.11 - http://james.apache.org/mime4j/apache-mime4j-dom) + * Apache James :: Mime4j :: DOM (org.apache.james:apache-mime4j-dom:0.8.12 - 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/) @@ -224,9 +233,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.24.3 - https://logging.apache.org/log4j/2.x/log4j/log4j-1.2-api/) - * Apache Log4j API (org.apache.logging.log4j:log4j-api:2.24.3 - https://logging.apache.org/log4j/2.x/log4j/log4j-api/) - * Apache Log4j Core (org.apache.logging.log4j:log4j-core:2.24.3 - https://logging.apache.org/log4j/2.x/log4j/log4j-core/) + * Apache Log4j 1.x Compatibility API (org.apache.logging.log4j:log4j-1.2-api:2.25.0 - https://logging.apache.org/log4j/2.x/) + * Apache Log4j API (org.apache.logging.log4j:log4j-api:2.25.0 - https://logging.apache.org/log4j/2.x/) + * Apache Log4j Core (org.apache.logging.log4j:log4j-core:2.25.0 - 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/) @@ -254,45 +263,45 @@ 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.33 - http://pdfbox.apache.org/) + * Apache FontBox (org.apache.pdfbox:fontbox:2.0.34 - 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.33 - https://www.apache.org/pdfbox-parent/pdfbox/) - * Apache PDFBox tools (org.apache.pdfbox:pdfbox-tools:2.0.31 - https://www.apache.org/pdfbox-parent/pdfbox-tools/) - * Apache XmpBox (org.apache.pdfbox:xmpbox:2.0.31 - https://www.apache.org/pdfbox-parent/xmpbox/) - * Apache POI - Common (org.apache.poi:poi:5.2.5 - https://poi.apache.org/) - * Apache POI - API based on OPC and OOXML schemas (org.apache.poi:poi-ooxml:5.2.5 - https://poi.apache.org/) - * Apache POI (org.apache.poi:poi-ooxml-lite:5.2.5 - https://poi.apache.org/) - * Apache POI (org.apache.poi:poi-scratchpad:5.2.5 - https://poi.apache.org/) + * 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 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.2 - https://tika.apache.org/) - * Apache Tika Apple parser module (org.apache.tika:tika-parser-apple-module:2.9.2 - https://tika.apache.org/tika-parser-apple-module/) - * Apache Tika audiovideo parser module (org.apache.tika:tika-parser-audiovideo-module:2.9.2 - https://tika.apache.org/tika-parser-audiovideo-module/) - * Apache Tika cad parser module (org.apache.tika:tika-parser-cad-module:2.9.2 - https://tika.apache.org/tika-parser-cad-module/) - * Apache Tika code parser module (org.apache.tika:tika-parser-code-module:2.9.2 - https://tika.apache.org/tika-parser-code-module/) - * Apache Tika crypto parser module (org.apache.tika:tika-parser-crypto-module:2.9.2 - https://tika.apache.org/tika-parser-crypto-module/) - * Apache Tika digest commons (org.apache.tika:tika-parser-digest-commons:2.9.2 - https://tika.apache.org/tika-parser-digest-commons/) - * Apache Tika font parser module (org.apache.tika:tika-parser-font-module:2.9.2 - https://tika.apache.org/tika-parser-font-module/) - * Apache Tika html parser module (org.apache.tika:tika-parser-html-module:2.9.2 - https://tika.apache.org/tika-parser-html-module/) - * Apache Tika image parser module (org.apache.tika:tika-parser-image-module:2.9.2 - https://tika.apache.org/tika-parser-image-module/) - * Apache Tika mail commons (org.apache.tika:tika-parser-mail-commons:2.9.2 - https://tika.apache.org/tika-parser-mail-commons/) - * Apache Tika mail parser module (org.apache.tika:tika-parser-mail-module:2.9.2 - https://tika.apache.org/tika-parser-mail-module/) - * Apache Tika Microsoft parser module (org.apache.tika:tika-parser-microsoft-module:2.9.2 - https://tika.apache.org/tika-parser-microsoft-module/) - * Apache Tika miscellaneous office format parser module (org.apache.tika:tika-parser-miscoffice-module:2.9.2 - https://tika.apache.org/tika-parser-miscoffice-module/) - * Apache Tika news parser module (org.apache.tika:tika-parser-news-module:2.9.2 - https://tika.apache.org/tika-parser-news-module/) - * Apache Tika OCR parser module (org.apache.tika:tika-parser-ocr-module:2.9.2 - https://tika.apache.org/tika-parser-ocr-module/) - * Apache Tika PDF parser module (org.apache.tika:tika-parser-pdf-module:2.9.2 - https://tika.apache.org/tika-parser-pdf-module/) - * Apache Tika package parser module (org.apache.tika:tika-parser-pkg-module:2.9.2 - https://tika.apache.org/tika-parser-pkg-module/) - * Apache Tika text parser module (org.apache.tika:tika-parser-text-module:2.9.2 - https://tika.apache.org/tika-parser-text-module/) - * Apache Tika WARC parser module (org.apache.tika:tika-parser-webarchive-module:2.9.2 - https://tika.apache.org/tika-parser-webarchive-module/) - * Apache Tika XML parser module (org.apache.tika:tika-parser-xml-module:2.9.2 - https://tika.apache.org/tika-parser-xml-module/) - * Apache Tika XMP commons (org.apache.tika:tika-parser-xmp-commons:2.9.2 - https://tika.apache.org/tika-parser-xmp-commons/) - * Apache Tika ZIP commons (org.apache.tika:tika-parser-zip-commons:2.9.2 - https://tika.apache.org/tika-parser-zip-commons/) - * Apache Tika standard parser package (org.apache.tika:tika-parsers-standard-package:2.9.2 - https://tika.apache.org/tika-parsers/tika-parsers-standard/tika-parsers-standard-package/) + * 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/) * 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/) @@ -301,7 +310,7 @@ https://wiki.lyrasis.org/display/DSPACE/Code+Contribution+Guidelines * Apache Velocity Tools - Generic tools (org.apache.velocity.tools:velocity-tools-generic:3.1 - https://velocity.apache.org/tools/devel/velocity-tools-generic/) * Axiom API (org.apache.ws.commons.axiom:axiom-api:1.2.22 - http://ws.apache.org/axiom/) * Abdera Model (FOM) Implementation (org.apache.ws.commons.axiom:fom-impl:1.2.22 - http://ws.apache.org/axiom/implementations/fom-impl/) - * XmlBeans (org.apache.xmlbeans:xmlbeans:5.2.0 - https://xmlbeans.apache.org/) + * XmlBeans (org.apache.xmlbeans:xmlbeans:5.3.0 - https://xmlbeans.apache.org/) * Apache ZooKeeper - Server (org.apache.zookeeper:zookeeper:3.6.2 - http://zookeeper.apache.org/zookeeper) * Apache ZooKeeper - Jute (org.apache.zookeeper:zookeeper-jute:3.6.2 - http://zookeeper.apache.org/zookeeper-jute) * org.apiguardian:apiguardian-api (org.apiguardian:apiguardian-api:1.1.2 - https://github.com/apiguardian-team/apiguardian) @@ -348,9 +357,9 @@ https://wiki.lyrasis.org/display/DSPACE/Code+Contribution+Guidelines * 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.46 - https://projects.eclipse.org/projects/ee4j.jersey/jersey-client) - * jersey-core-common (org.glassfish.jersey.core:jersey-common:2.46 - https://projects.eclipse.org/projects/ee4j.jersey/jersey-common) - * jersey-inject-hk2 (org.glassfish.jersey.inject:jersey-hk2:2.46 - https://projects.eclipse.org/projects/ee4j.jersey/project/jersey-hk2) + * 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) * 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) @@ -360,6 +369,11 @@ https://wiki.lyrasis.org/display/DSPACE/Code+Contribution+Guidelines * 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) * JDOM (org.jdom:jdom2:2.0.6.1 - http://www.jdom.org) + * IntelliJ IDEA Annotations (org.jetbrains:annotations:13.0 - http://www.jetbrains.org) + * 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/) * 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) @@ -377,8 +391,9 @@ 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.29.0 - https://github.com/iipc/jwarc) + * jwarc (org.netpreserve:jwarc:0.31.1 - 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/) @@ -438,7 +453,7 @@ 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.0 - https://www.xmlunit.org/) + * org.xmlunit:xmlunit-core (org.xmlunit:xmlunit-core:2.10.2 - 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) @@ -456,15 +471,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.2 - https://github.com/dnsjava/dnsjava) + * dnsjava (dnsjava:dnsjava:3.6.3 - https://github.com/dnsjava/dnsjava) * jaxen (jaxen:jaxen:2.0.0 - http://www.cafeconleche.org/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.46 - https://projects.eclipse.org/projects/ee4j.jersey/jersey-client) - * jersey-inject-hk2 (org.glassfish.jersey.inject:jersey-hk2:2.46 - https://projects.eclipse.org/projects/ee4j.jersey/project/jersey-hk2) + * 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) * 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/) @@ -474,9 +489,10 @@ 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.5 - https://jdbc.postgresql.org) + * PostgreSQL JDBC Driver (org.postgresql:postgresql:42.7.7 - 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) * XMLUnit for Java (xmlunit:xmlunit:1.3 - http://xmlunit.sourceforge.net/) CC0: @@ -495,7 +511,6 @@ https://wiki.lyrasis.org/display/DSPACE/Code+Contribution+Guidelines * JavaBeans Activation Framework (JAF) (javax.activation:activation:1.1 - http://java.sun.com/products/javabeans/jaf/index.jsp) * JavaBeans Activation Framework API jar (javax.activation:javax.activation-api:1.2.0 - http://java.net/all/javax.activation-api/) * javax.annotation API (javax.annotation:javax.annotation-api:1.3.2 - http://jcp.org/en/jsr/detail?id=250) - * Expression Language 3.0 API (javax.el:javax.el-api:3.0.0 - http://uel-spec.java.net) * 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) @@ -506,8 +521,8 @@ 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.46 - https://projects.eclipse.org/projects/ee4j.jersey/jersey-client) - * jersey-inject-hk2 (org.glassfish.jersey.inject:jersey-hk2:2.46 - https://projects.eclipse.org/projects/ee4j.jersey/project/jersey-hk2) + * 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) * 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: @@ -528,8 +543,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.46 - https://projects.eclipse.org/projects/ee4j.jersey/jersey-client) - * jersey-inject-hk2 (org.glassfish.jersey.inject:jersey-hk2:2.46 - https://projects.eclipse.org/projects/ee4j.jersey/project/jersey-hk2) + * 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) * 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) @@ -584,10 +599,13 @@ 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.46 - https://projects.eclipse.org/projects/ee4j.jersey/jersey-client) - * jersey-core-common (org.glassfish.jersey.core:jersey-common:2.46 - https://projects.eclipse.org/projects/ee4j.jersey/jersey-common) - * jersey-inject-hk2 (org.glassfish.jersey.inject:jersey-hk2:2.46 - https://projects.eclipse.org/projects/ee4j.jersey/project/jersey-hk2) + * 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) * 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) @@ -596,11 +614,11 @@ https://wiki.lyrasis.org/display/DSPACE/Code+Contribution+Guidelines GENERAL PUBLIC LICENSE, version 3 (GPL-3.0): - * juniversalchardet (com.github.albfernandez:juniversalchardet:2.4.0 - https://github.com/albfernandez/juniversalchardet) + * juniversalchardet (com.github.albfernandez:juniversalchardet:2.5.0 - https://github.com/albfernandez/juniversalchardet) GNU LESSER GENERAL PUBLIC LICENSE, version 3 (LGPL-3.0): - * juniversalchardet (com.github.albfernandez:juniversalchardet:2.4.0 - https://github.com/albfernandez/juniversalchardet) + * juniversalchardet (com.github.albfernandez:juniversalchardet:2.5.0 - https://github.com/albfernandez/juniversalchardet) GNU Lesser General Public License (LGPL): @@ -626,9 +644,9 @@ https://wiki.lyrasis.org/display/DSPACE/Code+Contribution+Guidelines * RE2/J (com.google.re2j:re2j:1.2 - http://github.com/google/re2j) - Handle.Net Public License Agreement (Ver.2): + Handle.Net Public License Agreement (Ver.3): - * Handle Server (net.handle:handle:9.3.1 - https://www.handle.net) + * Handle Server (net.handle:handle:9.3.2 - https://www.handle.net) ISC License: @@ -643,15 +661,15 @@ https://wiki.lyrasis.org/display/DSPACE/Code+Contribution+Guidelines * 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 S/MIME API (org.bouncycastle:bcmail-jdk18on:1.77 - https://www.bouncycastle.org/java.html) - * Bouncy Castle PKIX, CMS, EAC, TSP, PKCS, OCSP, CMP, and CRMF APIs (org.bouncycastle:bcpkix-jdk18on:1.80 - https://www.bouncycastle.org/download/bouncy-castle-java/) - * Bouncy Castle Provider (org.bouncycastle:bcprov-jdk18on:1.80 - https://www.bouncycastle.org/download/bouncy-castle-java/) - * Bouncy Castle ASN.1 Extension and Utility APIs (org.bouncycastle:bcutil-jdk18on:1.80 - https://www.bouncycastle.org/download/bouncy-castle-java/) + * Bouncy Castle JavaMail S/MIME APIs (org.bouncycastle:bcmail-jdk18on:1.80 - 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.48.3 - https://checkerframework.org/) - * jersey-core-client (org.glassfish.jersey.core:jersey-client:2.46 - https://projects.eclipse.org/projects/ee4j.jersey/jersey-client) - * jersey-inject-hk2 (org.glassfish.jersey.inject:jersey-hk2:2.46 - https://projects.eclipse.org/projects/ee4j.jersey/project/jersey-hk2) + * 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) * 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) @@ -664,27 +682,26 @@ 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.40.0 - https://www.webjars.org) - * @json-editor/json-editor (org.webjars.npm:json-editor__json-editor:2.15.1 - https://www.webjars.org) + * core-js (org.webjars.npm:core-js:3.42.0 - https://www.webjars.org) + * @json-editor/json-editor (org.webjars.npm:json-editor__json-editor:2.15.2 - https://www.webjars.org) Mozilla Public License: - * juniversalchardet (com.github.albfernandez:juniversalchardet:2.4.0 - https://github.com/albfernandez/juniversalchardet) + * 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) - * Saxon-HE (net.sf.saxon:Saxon-HE:9.8.0-14 - http://www.saxonica.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.46 - https://projects.eclipse.org/projects/ee4j.jersey/jersey-client) - * jersey-core-common (org.glassfish.jersey.core:jersey-common:2.46 - https://projects.eclipse.org/projects/ee4j.jersey/jersey-common) - * jersey-inject-hk2 (org.glassfish.jersey.inject:jersey-hk2:2.46 - https://projects.eclipse.org/projects/ee4j.jersey/project/jersey-hk2) + * 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) * 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) - * XZ for Java (org.tukaani:xz:1.9 - https://tukaani.org/xz/java.html) UnRar License: @@ -696,10 +713,10 @@ https://wiki.lyrasis.org/display/DSPACE/Code+Contribution+Guidelines W3C license: - * jersey-core-client (org.glassfish.jersey.core:jersey-client:2.46 - https://projects.eclipse.org/projects/ee4j.jersey/jersey-client) - * jersey-inject-hk2 (org.glassfish.jersey.inject:jersey-hk2:2.46 - https://projects.eclipse.org/projects/ee4j.jersey/project/jersey-hk2) + * 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) jQuery license: - * jersey-core-client (org.glassfish.jersey.core:jersey-client:2.46 - https://projects.eclipse.org/projects/ee4j.jersey/jersey-client) - * jersey-inject-hk2 (org.glassfish.jersey.inject:jersey-hk2:2.46 - https://projects.eclipse.org/projects/ee4j.jersey/project/jersey-hk2) + * 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) diff --git a/pom.xml b/pom.xml index 85fb3ce6ccd7..8a5c266b84b0 100644 --- a/pom.xml +++ b/pom.xml @@ -706,7 +706,7 @@ Apache Software License, Version 2.0|The SAX License|The W3C License Apache Software License, Version 2.0|Similar to Apache License but with the acknowledgment clause removed - BSD License|The BSD License|BSD licence|BSD license|BSD|BSD-style license|New BSD License|New BSD license|Revised BSD License|BSD 2-Clause license|3-Clause BSD License|BSD 2-Clause|BSD 3-clause New License|BSD Licence 3|BSD-2-Clause|BSD-3-Clause|Modified BSD|The New BSD License|The BSD 3-Clause License (BSD3)|BSD License 3|BSD License 2.0|The (New) BSD License + BSD License|The BSD License|BSD licence|BSD license|BSD|BSD-style license|New BSD License|New BSD license|Revised BSD License|BSD 2-Clause license|3-Clause BSD License|BSD 2-Clause|BSD 3-clause New License|BSD Licence 3|BSD-2-Clause|BSD-3-Clause|Modified BSD|The New BSD License|The BSD 3-Clause License (BSD3)|BSD License 3|BSD License 2.0|The (New) BSD License|0BSD BSD License|DSpace BSD License|DSpace Sourcecode License @@ -727,7 +727,7 @@ Common Development and Distribution License (CDDL)|GNU General Public License, Version 2 with the Classpath Exception Eclipse Distribution License, Version 1.0|Eclipse Distribution License (EDL), Version 1.0|Eclipse Distribution License - v 1.0|Eclipse Distribution License v. 1.0|EDL 1.0 - Eclipse Public License|Eclipse Public License - Version 1.0|Eclipse Public License - v 1.0|EPL 1.0 license|Eclipse Public License (EPL), Version 1.0|Eclipse Public License 1.0|Eclipse Public License v1.0|Eclipse Public License, Version 1.0|EPL 1.0|EPL 2.0|Eclipse Public License - v 2.0|EPL-2.0|Eclipse Public License 2.0|Eclipse Public License v. 2.0|Eclipse Public License, Version 2.0 + Eclipse Public License|Eclipse Public License - Version 1.0|Eclipse Public License - v 1.0|EPL 1.0 license|Eclipse Public License (EPL), Version 1.0|Eclipse Public License 1.0|Eclipse Public License v1.0|Eclipse Public License, Version 1.0|EPL 1.0|EPL 2.0|Eclipse Public License - v 2.0|EPL-2.0|Eclipse Public License 2.0|Eclipse Public License v. 2.0|Eclipse Public License, Version 2.0|Eclipse Public License v2.0 Eclipse Public License|Common Public License Version 1.0 From 55c33ede6391ad2d6f2ca98af7994aad8b6b0326 Mon Sep 17 00:00:00 2001 From: Kim Shepherd Date: Sun, 13 Jul 2025 10:06:33 +0200 Subject: [PATCH 798/979] Enforce path traversal check on import subdir (pre-processing) --- .../app/itemimport/ItemImportServiceImpl.java | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/app/itemimport/ItemImportServiceImpl.java b/dspace-api/src/main/java/org/dspace/app/itemimport/ItemImportServiceImpl.java index b40ecae2f7e0..74fe4c461913 100644 --- a/dspace-api/src/main/java/org/dspace/app/itemimport/ItemImportServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/app/itemimport/ItemImportServiceImpl.java @@ -29,6 +29,7 @@ import java.io.OutputStream; import java.io.PrintWriter; import java.net.URL; +import java.nio.file.Path; import java.sql.SQLException; import java.text.SimpleDateFormat; import java.util.ArrayList; @@ -744,15 +745,20 @@ protected Item addItem(Context c, List mycollections, String path, myitem = wi.getItem(); } + // normalize and validate path to make sure itemname doesn't contain path traversal + Path itemPath = new File(path + File.separatorChar + itemname + File.separatorChar) + .toPath().normalize(); + if (!itemPath.startsWith(path)) { + throw new IOException("Illegal item metadata path: '" + itemPath); + } + // now fill out dublin core for item - loadMetadata(c, myitem, path + File.separatorChar + itemname - + File.separatorChar); + loadMetadata(c, myitem, itemPath.toString()); // and the bitstreams from the contents file // process contents file, add bistreams and bundles, return any // non-standard permissions - List options = processContentsFile(c, myitem, path - + File.separatorChar + itemname, "contents"); + List options = processContentsFile(c, myitem, itemPath.toString(), "contents"); if (useWorkflow) { // don't process handle file @@ -770,8 +776,7 @@ protected Item addItem(Context c, List mycollections, String path, } } else { // only process handle file if not using workflow system - String myhandle = processHandleFile(c, myitem, path - + File.separatorChar + itemname, "handle"); + String myhandle = processHandleFile(c, myitem, itemPath.toString(), "handle"); // put item in system if (!isTest) { From 259c3ddd370521bca4c5ebe77d02c1801c12b7dd Mon Sep 17 00:00:00 2001 From: Kim Shepherd Date: Sun, 13 Jul 2025 10:06:33 +0200 Subject: [PATCH 799/979] Enforce path traversal check on import subdir (pre-processing) --- .../app/itemimport/ItemImportServiceImpl.java | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/app/itemimport/ItemImportServiceImpl.java b/dspace-api/src/main/java/org/dspace/app/itemimport/ItemImportServiceImpl.java index 3af383b04caf..4c53dc6ee59f 100644 --- a/dspace-api/src/main/java/org/dspace/app/itemimport/ItemImportServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/app/itemimport/ItemImportServiceImpl.java @@ -29,6 +29,7 @@ import java.io.OutputStream; import java.io.PrintWriter; import java.net.URL; +import java.nio.file.Path; import java.sql.SQLException; import java.text.SimpleDateFormat; import java.util.ArrayList; @@ -744,15 +745,20 @@ protected Item addItem(Context c, List mycollections, String path, myitem = wi.getItem(); } + // normalize and validate path to make sure itemname doesn't contain path traversal + Path itemPath = new File(path + File.separatorChar + itemname + File.separatorChar) + .toPath().normalize(); + if (!itemPath.startsWith(path)) { + throw new IOException("Illegal item metadata path: '" + itemPath); + } + // now fill out dublin core for item - loadMetadata(c, myitem, path + File.separatorChar + itemname - + File.separatorChar); + loadMetadata(c, myitem, itemPath.toString()); // and the bitstreams from the contents file // process contents file, add bistreams and bundles, return any // non-standard permissions - List options = processContentsFile(c, myitem, path - + File.separatorChar + itemname, "contents"); + List options = processContentsFile(c, myitem, itemPath.toString(), "contents"); if (useWorkflow) { // don't process handle file @@ -770,8 +776,7 @@ protected Item addItem(Context c, List mycollections, String path, } } else { // only process handle file if not using workflow system - String myhandle = processHandleFile(c, myitem, path - + File.separatorChar + itemname, "handle"); + String myhandle = processHandleFile(c, myitem, itemPath.toString(), "handle"); // put item in system if (!isTest) { From 802a39fb77468bb9a0a30521579d00b15b9fed2c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 14 Jul 2025 03:33:08 +0000 Subject: [PATCH 800/979] Bump log4j.version from 2.24.3 to 2.25.1 Bumps `log4j.version` from 2.24.3 to 2.25.1. Updates `org.apache.logging.log4j:log4j-api` from 2.24.3 to 2.25.1 Updates `org.apache.logging.log4j:log4j-core` from 2.24.3 to 2.25.1 Updates `org.apache.logging.log4j:log4j-slf4j2-impl` from 2.24.3 to 2.25.1 --- updated-dependencies: - dependency-name: org.apache.logging.log4j:log4j-api dependency-version: 2.25.1 dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: org.apache.logging.log4j:log4j-core dependency-version: 2.25.1 dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: org.apache.logging.log4j:log4j-slf4j2-impl dependency-version: 2.25.1 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index e6f580426266..b0f3ae43199b 100644 --- a/pom.xml +++ b/pom.xml @@ -39,7 +39,7 @@ 1.1.1 9.4.57.v20241219 - 2.24.3 + 2.25.1 2.0.34 1.19.0 2.0.17 From a381611ebd040dc4f88bda6cb66efdfd66366f7c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 14 Jul 2025 03:45:03 +0000 Subject: [PATCH 801/979] Bump log4j.version from 2.25.0 to 2.25.1 Bumps `log4j.version` from 2.25.0 to 2.25.1. Updates `org.apache.logging.log4j:log4j-api` from 2.25.0 to 2.25.1 Updates `org.apache.logging.log4j:log4j-core` from 2.25.0 to 2.25.1 Updates `org.apache.logging.log4j:log4j-1.2-api` from 2.25.0 to 2.25.1 --- updated-dependencies: - dependency-name: org.apache.logging.log4j:log4j-api dependency-version: 2.25.1 dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.apache.logging.log4j:log4j-core dependency-version: 2.25.1 dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.apache.logging.log4j:log4j-1.2-api dependency-version: 2.25.1 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 8a5c266b84b0..d5d8b26225cf 100644 --- a/pom.xml +++ b/pom.xml @@ -38,7 +38,7 @@ 1.1.1 9.4.57.v20241219 - 2.25.0 + 2.25.1 2.0.34 1.19.0 1.7.36 From f473272a08d9ad1c846c5a013602566733694560 Mon Sep 17 00:00:00 2001 From: Kim Shepherd Date: Mon, 14 Jul 2025 12:48:55 +0200 Subject: [PATCH 802/979] Re-add file separator to normalized SAF item path --- .../org/dspace/app/itemimport/ItemImportServiceImpl.java | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/app/itemimport/ItemImportServiceImpl.java b/dspace-api/src/main/java/org/dspace/app/itemimport/ItemImportServiceImpl.java index 74fe4c461913..89fbb6d78cb0 100644 --- a/dspace-api/src/main/java/org/dspace/app/itemimport/ItemImportServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/app/itemimport/ItemImportServiceImpl.java @@ -751,14 +751,16 @@ protected Item addItem(Context c, List mycollections, String path, if (!itemPath.startsWith(path)) { throw new IOException("Illegal item metadata path: '" + itemPath); } + // Normalization chops off the last separator, and we need to put it back + String itemPathDir = itemPath.toString() + File.separatorChar; // now fill out dublin core for item - loadMetadata(c, myitem, itemPath.toString()); + loadMetadata(c, myitem, itemPathDir); // and the bitstreams from the contents file // process contents file, add bistreams and bundles, return any // non-standard permissions - List options = processContentsFile(c, myitem, itemPath.toString(), "contents"); + List options = processContentsFile(c, myitem, itemPathDir, "contents"); if (useWorkflow) { // don't process handle file @@ -776,7 +778,7 @@ protected Item addItem(Context c, List mycollections, String path, } } else { // only process handle file if not using workflow system - String myhandle = processHandleFile(c, myitem, itemPath.toString(), "handle"); + String myhandle = processHandleFile(c, myitem, itemPathDir, "handle"); // put item in system if (!isTest) { From 45a9f8b530d69a29de5c6e071ce76ebcb2837812 Mon Sep 17 00:00:00 2001 From: Kim Shepherd Date: Mon, 14 Jul 2025 12:48:55 +0200 Subject: [PATCH 803/979] Re-add file separator to normalized SAF item path --- .../org/dspace/app/itemimport/ItemImportServiceImpl.java | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/app/itemimport/ItemImportServiceImpl.java b/dspace-api/src/main/java/org/dspace/app/itemimport/ItemImportServiceImpl.java index 4c53dc6ee59f..6536af7139d4 100644 --- a/dspace-api/src/main/java/org/dspace/app/itemimport/ItemImportServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/app/itemimport/ItemImportServiceImpl.java @@ -751,14 +751,16 @@ protected Item addItem(Context c, List mycollections, String path, if (!itemPath.startsWith(path)) { throw new IOException("Illegal item metadata path: '" + itemPath); } + // Normalization chops off the last separator, and we need to put it back + String itemPathDir = itemPath.toString() + File.separatorChar; // now fill out dublin core for item - loadMetadata(c, myitem, itemPath.toString()); + loadMetadata(c, myitem, itemPathDir); // and the bitstreams from the contents file // process contents file, add bistreams and bundles, return any // non-standard permissions - List options = processContentsFile(c, myitem, itemPath.toString(), "contents"); + List options = processContentsFile(c, myitem, itemPathDir, "contents"); if (useWorkflow) { // don't process handle file @@ -776,7 +778,7 @@ protected Item addItem(Context c, List mycollections, String path, } } else { // only process handle file if not using workflow system - String myhandle = processHandleFile(c, myitem, itemPath.toString(), "handle"); + String myhandle = processHandleFile(c, myitem, itemPathDir, "handle"); // put item in system if (!isTest) { From 6b909126e65175d1b2c5ebe162372665e00e8c61 Mon Sep 17 00:00:00 2001 From: Kim Shepherd Date: Mon, 14 Jul 2025 13:06:08 +0200 Subject: [PATCH 804/979] Remove unused imports --- .../src/main/java/org/dspace/app/util/DCInputsReader.java | 2 -- .../main/java/org/dspace/app/util/SubmissionConfigReader.java | 1 - 2 files changed, 3 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/app/util/DCInputsReader.java b/dspace-api/src/main/java/org/dspace/app/util/DCInputsReader.java index 293ceaac47d7..7c0ad4830f13 100644 --- a/dspace-api/src/main/java/org/dspace/app/util/DCInputsReader.java +++ b/dspace-api/src/main/java/org/dspace/app/util/DCInputsReader.java @@ -8,7 +8,6 @@ package org.dspace.app.util; import java.io.File; -import java.nio.file.Path; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; @@ -16,7 +15,6 @@ import java.util.List; import java.util.Map; import javax.xml.parsers.DocumentBuilder; -import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.FactoryConfigurationError; import org.apache.commons.lang3.StringUtils; diff --git a/dspace-api/src/main/java/org/dspace/app/util/SubmissionConfigReader.java b/dspace-api/src/main/java/org/dspace/app/util/SubmissionConfigReader.java index 5b487045a0d9..2016fb2ce64e 100644 --- a/dspace-api/src/main/java/org/dspace/app/util/SubmissionConfigReader.java +++ b/dspace-api/src/main/java/org/dspace/app/util/SubmissionConfigReader.java @@ -16,7 +16,6 @@ import java.util.List; import java.util.Map; import javax.xml.parsers.DocumentBuilder; -import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.FactoryConfigurationError; import org.apache.commons.lang3.StringUtils; From dda6d9ec9ddf0697c3e51fa0bf70068eabb626e0 Mon Sep 17 00:00:00 2001 From: Kim Shepherd Date: Mon, 14 Jul 2025 13:06:08 +0200 Subject: [PATCH 805/979] Remove unused imports --- .../src/main/java/org/dspace/app/util/DCInputsReader.java | 2 -- .../main/java/org/dspace/app/util/SubmissionConfigReader.java | 1 - 2 files changed, 3 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/app/util/DCInputsReader.java b/dspace-api/src/main/java/org/dspace/app/util/DCInputsReader.java index 293ceaac47d7..7c0ad4830f13 100644 --- a/dspace-api/src/main/java/org/dspace/app/util/DCInputsReader.java +++ b/dspace-api/src/main/java/org/dspace/app/util/DCInputsReader.java @@ -8,7 +8,6 @@ package org.dspace.app.util; import java.io.File; -import java.nio.file.Path; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; @@ -16,7 +15,6 @@ import java.util.List; import java.util.Map; import javax.xml.parsers.DocumentBuilder; -import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.FactoryConfigurationError; import org.apache.commons.lang3.StringUtils; diff --git a/dspace-api/src/main/java/org/dspace/app/util/SubmissionConfigReader.java b/dspace-api/src/main/java/org/dspace/app/util/SubmissionConfigReader.java index 27138d1ba1ef..899702757adf 100644 --- a/dspace-api/src/main/java/org/dspace/app/util/SubmissionConfigReader.java +++ b/dspace-api/src/main/java/org/dspace/app/util/SubmissionConfigReader.java @@ -16,7 +16,6 @@ import java.util.List; import java.util.Map; import javax.xml.parsers.DocumentBuilder; -import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.FactoryConfigurationError; import org.apache.commons.lang3.StringUtils; From e9bc74cf6df686965d988c1737c6803fe308bd9d Mon Sep 17 00:00:00 2001 From: Kim Shepherd Date: Mon, 14 Jul 2025 13:09:43 +0200 Subject: [PATCH 806/979] Fix missing XMLUtils imports --- .../src/main/java/org/dspace/content/packager/RoleIngester.java | 1 + .../java/org/dspace/external/provider/orcid/xml/Converter.java | 1 + 2 files changed, 2 insertions(+) diff --git a/dspace-api/src/main/java/org/dspace/content/packager/RoleIngester.java b/dspace-api/src/main/java/org/dspace/content/packager/RoleIngester.java index d71012ff8356..5c4cf214445e 100644 --- a/dspace-api/src/main/java/org/dspace/content/packager/RoleIngester.java +++ b/dspace-api/src/main/java/org/dspace/content/packager/RoleIngester.java @@ -19,6 +19,7 @@ import javax.xml.parsers.ParserConfigurationException; import org.apache.commons.codec.DecoderException; +import org.dspace.app.util.XMLUtils; import org.dspace.authorize.AuthorizeException; import org.dspace.content.Collection; import org.dspace.content.Community; diff --git a/dspace-api/src/main/java/org/dspace/external/provider/orcid/xml/Converter.java b/dspace-api/src/main/java/org/dspace/external/provider/orcid/xml/Converter.java index af4e31e7fae2..0e156f7ab5f3 100644 --- a/dspace-api/src/main/java/org/dspace/external/provider/orcid/xml/Converter.java +++ b/dspace-api/src/main/java/org/dspace/external/provider/orcid/xml/Converter.java @@ -16,6 +16,7 @@ import javax.xml.stream.XMLStreamException; import javax.xml.stream.XMLStreamReader; +import org.dspace.app.util.XMLUtils; import org.xml.sax.SAXException; /** From 84e308c8f549b204eae89fbb36d6c2a3ef44a221 Mon Sep 17 00:00:00 2001 From: MMilosz Date: Fri, 4 Jul 2025 17:13:02 +0200 Subject: [PATCH 807/979] fix: prevent path traversal in SAF import (cherry picked from commit 596d8666f4b91c6af92f72f45da262d761b96607) --- .../app/itemimport/ItemImportServiceImpl.java | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/dspace-api/src/main/java/org/dspace/app/itemimport/ItemImportServiceImpl.java b/dspace-api/src/main/java/org/dspace/app/itemimport/ItemImportServiceImpl.java index 6536af7139d4..8f8e7438acbe 100644 --- a/dspace-api/src/main/java/org/dspace/app/itemimport/ItemImportServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/app/itemimport/ItemImportServiceImpl.java @@ -1010,6 +1010,34 @@ protected void addDCValue(Context c, Item i, String schema, Node n) } } + /** + * Ensures a file path does not attempt to access files outside the designated parent directory. + * + * @param parentDir The absolute path to the parent directory that should contain the file + * @param fileName The name or path of the file to validate + * @throws IOException If an error occurs while resolving canonical paths, or the file path attempts + * to access a location outside the parent directory + */ + private void validateFilePath(String parentDir, String fileName) throws IOException { + File parent = new File(parentDir); + File file = new File(fileName); + + // If the fileName is not an absolute path, we resolve it against the parentDir + if (!file.isAbsolute()) { + file = new File(parent, fileName); + } + + String parentCanonicalPath = parent.getCanonicalPath(); + String fileCanonicalPath = file.getCanonicalPath(); + + if (!fileCanonicalPath.startsWith(parentCanonicalPath)) { + log.error("File path outside of canonical root requested: fileCanonicalPath={} does not begin " + + "with parentCanonicalPath={}", fileCanonicalPath, parentCanonicalPath); + throw new IOException("Illegal file path '" + fileName + "' encountered. This references a path " + + "outside of the import package. Please see the system logs for more details."); + } + } + /** * Read the collections file inside the item directory. If there * is one and it is not empty return a list of collections in @@ -1210,6 +1238,7 @@ protected List processContentsFile(Context c, Item i, String path, sDescription = sDescription.replaceFirst("description:", ""); } + validateFilePath(path, sFilePath); registerBitstream(c, i, iAssetstore, sFilePath, sBundle, sDescription); logInfo("\tRegistering Bitstream: " + sFilePath + "\tAssetstore: " + iAssetstore @@ -1423,6 +1452,7 @@ protected void processContentFileEntry(Context c, Item i, String path, return; } + validateFilePath(path, fileName); String fullpath = path + File.separatorChar + fileName; // get an input stream From b0a4a3400f3285da033cc7f71b6ac80ec2e07a85 Mon Sep 17 00:00:00 2001 From: Kim Shepherd Date: Wed, 9 Jul 2025 13:59:37 +0200 Subject: [PATCH 808/979] Enforce bitstream path to be within (fs) bitstore base on get (cherry picked from commit 6799660a903bfea985c818654cca3891527780de) --- .../org/dspace/storage/bitstore/DSBitStoreService.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/dspace-api/src/main/java/org/dspace/storage/bitstore/DSBitStoreService.java b/dspace-api/src/main/java/org/dspace/storage/bitstore/DSBitStoreService.java index 6fef7365e482..3f92ccaac5f1 100644 --- a/dspace-api/src/main/java/org/dspace/storage/bitstore/DSBitStoreService.java +++ b/dspace-api/src/main/java/org/dspace/storage/bitstore/DSBitStoreService.java @@ -12,6 +12,7 @@ import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; +import java.nio.file.Path; import java.security.DigestInputStream; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; @@ -249,6 +250,12 @@ protected File getFile(Bitstream bitstream) throws IOException { log.debug("Local filename for " + sInternalId + " is " + bufFilename.toString()); } + File bitstreamFile = new File(bufFilename.toString()); + Path normalizedPath = bitstreamFile.toPath().normalize(); + if (!normalizedPath.startsWith(baseDir.getAbsolutePath())) { + log.error("Bitstream path outside of assetstore root requested: bitstream={}, path={}, assetstore={}", bitstream.getID(), normalizedPath, baseDir.getAbsolutePath()); + throw new IOException("Illegal bitstream path constructed"); + } return new File(bufFilename.toString()); } From 907b42c2a913ce748e71fbd15819e9b798909068 Mon Sep 17 00:00:00 2001 From: Kim Shepherd Date: Wed, 9 Jul 2025 19:07:32 +0200 Subject: [PATCH 809/979] return existing File constructed and validated for bitstream (cherry picked from commit 31b1c922b2cba42857679f291cb9320cf820db46) --- .../java/org/dspace/storage/bitstore/DSBitStoreService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dspace-api/src/main/java/org/dspace/storage/bitstore/DSBitStoreService.java b/dspace-api/src/main/java/org/dspace/storage/bitstore/DSBitStoreService.java index 3f92ccaac5f1..6589f99bc749 100644 --- a/dspace-api/src/main/java/org/dspace/storage/bitstore/DSBitStoreService.java +++ b/dspace-api/src/main/java/org/dspace/storage/bitstore/DSBitStoreService.java @@ -256,7 +256,7 @@ protected File getFile(Bitstream bitstream) throws IOException { log.error("Bitstream path outside of assetstore root requested: bitstream={}, path={}, assetstore={}", bitstream.getID(), normalizedPath, baseDir.getAbsolutePath()); throw new IOException("Illegal bitstream path constructed"); } - return new File(bufFilename.toString()); + return bitstreamFile; } public boolean isRegisteredBitstream(String internalId) { From bc17559162125252c4c759ac540dffa982748c05 Mon Sep 17 00:00:00 2001 From: Kim Shepherd Date: Mon, 14 Jul 2025 12:42:30 +0200 Subject: [PATCH 810/979] Fix line length in DSBitstore log (cherry picked from commit dbf524c1120261f407cc77574642e3ee0e4ffe33) --- .../java/org/dspace/storage/bitstore/DSBitStoreService.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/dspace-api/src/main/java/org/dspace/storage/bitstore/DSBitStoreService.java b/dspace-api/src/main/java/org/dspace/storage/bitstore/DSBitStoreService.java index 6589f99bc749..7743b93ca4ba 100644 --- a/dspace-api/src/main/java/org/dspace/storage/bitstore/DSBitStoreService.java +++ b/dspace-api/src/main/java/org/dspace/storage/bitstore/DSBitStoreService.java @@ -253,7 +253,9 @@ protected File getFile(Bitstream bitstream) throws IOException { File bitstreamFile = new File(bufFilename.toString()); Path normalizedPath = bitstreamFile.toPath().normalize(); if (!normalizedPath.startsWith(baseDir.getAbsolutePath())) { - log.error("Bitstream path outside of assetstore root requested: bitstream={}, path={}, assetstore={}", bitstream.getID(), normalizedPath, baseDir.getAbsolutePath()); + log.error("Bitstream path outside of assetstore root requested:" + + "bitstream={}, path={}, assetstore={}", + bitstream.getID(), normalizedPath, baseDir.getAbsolutePath()); throw new IOException("Illegal bitstream path constructed"); } return bitstreamFile; From 2c934dfbf71ca4257d54fea8f6eba4768f4128d3 Mon Sep 17 00:00:00 2001 From: MMilosz Date: Fri, 4 Jul 2025 17:13:02 +0200 Subject: [PATCH 811/979] fix: prevent path traversal in SAF import (cherry picked from commit 596d8666f4b91c6af92f72f45da262d761b96607) --- .../app/itemimport/ItemImportServiceImpl.java | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/dspace-api/src/main/java/org/dspace/app/itemimport/ItemImportServiceImpl.java b/dspace-api/src/main/java/org/dspace/app/itemimport/ItemImportServiceImpl.java index 087a33026151..e29a0b567095 100644 --- a/dspace-api/src/main/java/org/dspace/app/itemimport/ItemImportServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/app/itemimport/ItemImportServiceImpl.java @@ -1001,6 +1001,34 @@ protected void addDCValue(Context c, Item i, String schema, Node n) } } + /** + * Ensures a file path does not attempt to access files outside the designated parent directory. + * + * @param parentDir The absolute path to the parent directory that should contain the file + * @param fileName The name or path of the file to validate + * @throws IOException If an error occurs while resolving canonical paths, or the file path attempts + * to access a location outside the parent directory + */ + private void validateFilePath(String parentDir, String fileName) throws IOException { + File parent = new File(parentDir); + File file = new File(fileName); + + // If the fileName is not an absolute path, we resolve it against the parentDir + if (!file.isAbsolute()) { + file = new File(parent, fileName); + } + + String parentCanonicalPath = parent.getCanonicalPath(); + String fileCanonicalPath = file.getCanonicalPath(); + + if (!fileCanonicalPath.startsWith(parentCanonicalPath)) { + log.error("File path outside of canonical root requested: fileCanonicalPath={} does not begin " + + "with parentCanonicalPath={}", fileCanonicalPath, parentCanonicalPath); + throw new IOException("Illegal file path '" + fileName + "' encountered. This references a path " + + "outside of the import package. Please see the system logs for more details."); + } + } + /** * Read the collections file inside the item directory. If there * is one and it is not empty return a list of collections in @@ -1201,6 +1229,7 @@ protected List processContentsFile(Context c, Item i, String path, sDescription = sDescription.replaceFirst("description:", ""); } + validateFilePath(path, sFilePath); registerBitstream(c, i, iAssetstore, sFilePath, sBundle, sDescription); logInfo("\tRegistering Bitstream: " + sFilePath + "\tAssetstore: " + iAssetstore @@ -1414,6 +1443,7 @@ protected void processContentFileEntry(Context c, Item i, String path, return; } + validateFilePath(path, fileName); String fullpath = path + File.separatorChar + fileName; // get an input stream From daca9d8ec7c9451ae430b420248cb7c60c180e72 Mon Sep 17 00:00:00 2001 From: Kim Shepherd Date: Wed, 9 Jul 2025 13:59:37 +0200 Subject: [PATCH 812/979] Enforce bitstream path to be within (fs) bitstore base on get (cherry picked from commit 6799660a903bfea985c818654cca3891527780de) --- .../org/dspace/storage/bitstore/DSBitStoreService.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/dspace-api/src/main/java/org/dspace/storage/bitstore/DSBitStoreService.java b/dspace-api/src/main/java/org/dspace/storage/bitstore/DSBitStoreService.java index 6fef7365e482..3f92ccaac5f1 100644 --- a/dspace-api/src/main/java/org/dspace/storage/bitstore/DSBitStoreService.java +++ b/dspace-api/src/main/java/org/dspace/storage/bitstore/DSBitStoreService.java @@ -12,6 +12,7 @@ import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; +import java.nio.file.Path; import java.security.DigestInputStream; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; @@ -249,6 +250,12 @@ protected File getFile(Bitstream bitstream) throws IOException { log.debug("Local filename for " + sInternalId + " is " + bufFilename.toString()); } + File bitstreamFile = new File(bufFilename.toString()); + Path normalizedPath = bitstreamFile.toPath().normalize(); + if (!normalizedPath.startsWith(baseDir.getAbsolutePath())) { + log.error("Bitstream path outside of assetstore root requested: bitstream={}, path={}, assetstore={}", bitstream.getID(), normalizedPath, baseDir.getAbsolutePath()); + throw new IOException("Illegal bitstream path constructed"); + } return new File(bufFilename.toString()); } From 0e95ba80de86e3e0029a06165153aff9eb375a9d Mon Sep 17 00:00:00 2001 From: Kim Shepherd Date: Wed, 9 Jul 2025 19:07:32 +0200 Subject: [PATCH 813/979] return existing File constructed and validated for bitstream (cherry picked from commit 31b1c922b2cba42857679f291cb9320cf820db46) --- .../java/org/dspace/storage/bitstore/DSBitStoreService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dspace-api/src/main/java/org/dspace/storage/bitstore/DSBitStoreService.java b/dspace-api/src/main/java/org/dspace/storage/bitstore/DSBitStoreService.java index 3f92ccaac5f1..6589f99bc749 100644 --- a/dspace-api/src/main/java/org/dspace/storage/bitstore/DSBitStoreService.java +++ b/dspace-api/src/main/java/org/dspace/storage/bitstore/DSBitStoreService.java @@ -256,7 +256,7 @@ protected File getFile(Bitstream bitstream) throws IOException { log.error("Bitstream path outside of assetstore root requested: bitstream={}, path={}, assetstore={}", bitstream.getID(), normalizedPath, baseDir.getAbsolutePath()); throw new IOException("Illegal bitstream path constructed"); } - return new File(bufFilename.toString()); + return bitstreamFile; } public boolean isRegisteredBitstream(String internalId) { From d6c76bc63905ae13f70dc59121e97426ca9e95f0 Mon Sep 17 00:00:00 2001 From: Kim Shepherd Date: Mon, 14 Jul 2025 12:42:30 +0200 Subject: [PATCH 814/979] Fix line length in DSBitstore log (cherry picked from commit dbf524c1120261f407cc77574642e3ee0e4ffe33) --- .../java/org/dspace/storage/bitstore/DSBitStoreService.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/dspace-api/src/main/java/org/dspace/storage/bitstore/DSBitStoreService.java b/dspace-api/src/main/java/org/dspace/storage/bitstore/DSBitStoreService.java index 6589f99bc749..7743b93ca4ba 100644 --- a/dspace-api/src/main/java/org/dspace/storage/bitstore/DSBitStoreService.java +++ b/dspace-api/src/main/java/org/dspace/storage/bitstore/DSBitStoreService.java @@ -253,7 +253,9 @@ protected File getFile(Bitstream bitstream) throws IOException { File bitstreamFile = new File(bufFilename.toString()); Path normalizedPath = bitstreamFile.toPath().normalize(); if (!normalizedPath.startsWith(baseDir.getAbsolutePath())) { - log.error("Bitstream path outside of assetstore root requested: bitstream={}, path={}, assetstore={}", bitstream.getID(), normalizedPath, baseDir.getAbsolutePath()); + log.error("Bitstream path outside of assetstore root requested:" + + "bitstream={}, path={}, assetstore={}", + bitstream.getID(), normalizedPath, baseDir.getAbsolutePath()); throw new IOException("Illegal bitstream path constructed"); } return bitstreamFile; From a5f04f9c7731dbf088a00e28012367f24d76842b Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Mon, 14 Jul 2025 12:03:12 -0500 Subject: [PATCH 815/979] [maven-release-plugin] prepare release dspace-7.6.4 --- dspace-api/pom.xml | 2 +- dspace-iiif/pom.xml | 2 +- dspace-oai/pom.xml | 2 +- dspace-rdf/pom.xml | 2 +- dspace-rest/pom.xml | 4 ++-- dspace-server-webapp/pom.xml | 2 +- dspace-services/pom.xml | 2 +- dspace-sword/pom.xml | 2 +- dspace-swordv2/pom.xml | 2 +- dspace/modules/additions/pom.xml | 2 +- dspace/modules/pom.xml | 2 +- dspace/modules/rest/pom.xml | 2 +- dspace/modules/server/pom.xml | 2 +- dspace/pom.xml | 2 +- pom.xml | 32 ++++++++++++++++---------------- 15 files changed, 31 insertions(+), 31 deletions(-) diff --git a/dspace-api/pom.xml b/dspace-api/pom.xml index 7b3ab4a315e5..b6028cdb4241 100644 --- a/dspace-api/pom.xml +++ b/dspace-api/pom.xml @@ -12,7 +12,7 @@ org.dspace dspace-parent - 7.6.4-SNAPSHOT + 7.6.4 .. diff --git a/dspace-iiif/pom.xml b/dspace-iiif/pom.xml index 24c3d5f1642c..080962f67760 100644 --- a/dspace-iiif/pom.xml +++ b/dspace-iiif/pom.xml @@ -15,7 +15,7 @@ org.dspace dspace-parent - 7.6.4-SNAPSHOT + 7.6.4 .. diff --git a/dspace-oai/pom.xml b/dspace-oai/pom.xml index b6c419702d05..9060c6a02f11 100644 --- a/dspace-oai/pom.xml +++ b/dspace-oai/pom.xml @@ -8,7 +8,7 @@ dspace-parent org.dspace - 7.6.4-SNAPSHOT + 7.6.4 .. diff --git a/dspace-rdf/pom.xml b/dspace-rdf/pom.xml index ee06135cdce4..951793b5f7f9 100644 --- a/dspace-rdf/pom.xml +++ b/dspace-rdf/pom.xml @@ -9,7 +9,7 @@ org.dspace dspace-parent - 7.6.4-SNAPSHOT + 7.6.4 .. diff --git a/dspace-rest/pom.xml b/dspace-rest/pom.xml index e7f2c20ec8e2..663ed04c4d32 100644 --- a/dspace-rest/pom.xml +++ b/dspace-rest/pom.xml @@ -3,7 +3,7 @@ org.dspace dspace-rest war - 7.6.4-SNAPSHOT + 7.6.4 DSpace (Deprecated) REST Webapp DSpace RESTful Web Services API. NOTE: this REST API is DEPRECATED. Please consider using the REST API in the dspace-server-webapp instead! @@ -12,7 +12,7 @@ org.dspace dspace-parent - 7.6.4-SNAPSHOT + 7.6.4 .. diff --git a/dspace-server-webapp/pom.xml b/dspace-server-webapp/pom.xml index 15ee58e6e1bc..ce71d1cad2e3 100644 --- a/dspace-server-webapp/pom.xml +++ b/dspace-server-webapp/pom.xml @@ -15,7 +15,7 @@ org.dspace dspace-parent - 7.6.4-SNAPSHOT + 7.6.4 .. diff --git a/dspace-services/pom.xml b/dspace-services/pom.xml index 6d783349f165..3d55556936b2 100644 --- a/dspace-services/pom.xml +++ b/dspace-services/pom.xml @@ -9,7 +9,7 @@ org.dspace dspace-parent - 7.6.4-SNAPSHOT + 7.6.4 diff --git a/dspace-sword/pom.xml b/dspace-sword/pom.xml index fc436f668921..5e502e2b3fc2 100644 --- a/dspace-sword/pom.xml +++ b/dspace-sword/pom.xml @@ -15,7 +15,7 @@ org.dspace dspace-parent - 7.6.4-SNAPSHOT + 7.6.4 .. diff --git a/dspace-swordv2/pom.xml b/dspace-swordv2/pom.xml index 755dc30d696b..97cf8e235f31 100644 --- a/dspace-swordv2/pom.xml +++ b/dspace-swordv2/pom.xml @@ -13,7 +13,7 @@ org.dspace dspace-parent - 7.6.4-SNAPSHOT + 7.6.4 .. diff --git a/dspace/modules/additions/pom.xml b/dspace/modules/additions/pom.xml index dd38555808a2..7687c54b921a 100644 --- a/dspace/modules/additions/pom.xml +++ b/dspace/modules/additions/pom.xml @@ -17,7 +17,7 @@ org.dspace modules - 7.6.4-SNAPSHOT + 7.6.4 .. diff --git a/dspace/modules/pom.xml b/dspace/modules/pom.xml index bc902913e967..798fb1045d13 100644 --- a/dspace/modules/pom.xml +++ b/dspace/modules/pom.xml @@ -11,7 +11,7 @@ org.dspace dspace-parent - 7.6.4-SNAPSHOT + 7.6.4 ../../pom.xml diff --git a/dspace/modules/rest/pom.xml b/dspace/modules/rest/pom.xml index c424668c8980..bd2b9a2128ff 100644 --- a/dspace/modules/rest/pom.xml +++ b/dspace/modules/rest/pom.xml @@ -13,7 +13,7 @@ org.dspace modules - 7.6.4-SNAPSHOT + 7.6.4 .. diff --git a/dspace/modules/server/pom.xml b/dspace/modules/server/pom.xml index 318c01edd214..cbc2aee61ee1 100644 --- a/dspace/modules/server/pom.xml +++ b/dspace/modules/server/pom.xml @@ -13,7 +13,7 @@ just adding new jar in the classloader modules org.dspace - 7.6.4-SNAPSHOT + 7.6.4 .. diff --git a/dspace/pom.xml b/dspace/pom.xml index af6881303b96..d5ad247b3e24 100644 --- a/dspace/pom.xml +++ b/dspace/pom.xml @@ -16,7 +16,7 @@ org.dspace dspace-parent - 7.6.4-SNAPSHOT + 7.6.4 ../pom.xml diff --git a/pom.xml b/pom.xml index 8a5c266b84b0..a87d28e8f6e2 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ org.dspace dspace-parent pom - 7.6.4-SNAPSHOT + 7.6.4 DSpace Parent Project DSpace open source software is a turnkey institutional repository application. @@ -875,14 +875,14 @@ org.dspace dspace-rest - 7.6.4-SNAPSHOT + 7.6.4 jar classes org.dspace dspace-rest - 7.6.4-SNAPSHOT + 7.6.4 war @@ -1031,69 +1031,69 @@ org.dspace dspace-api - 7.6.4-SNAPSHOT + 7.6.4 org.dspace dspace-api test-jar - 7.6.4-SNAPSHOT + 7.6.4 test org.dspace.modules additions - 7.6.4-SNAPSHOT + 7.6.4 org.dspace dspace-sword - 7.6.4-SNAPSHOT + 7.6.4 org.dspace dspace-swordv2 - 7.6.4-SNAPSHOT + 7.6.4 org.dspace dspace-oai - 7.6.4-SNAPSHOT + 7.6.4 org.dspace dspace-services - 7.6.4-SNAPSHOT + 7.6.4 org.dspace dspace-server-webapp test-jar - 7.6.4-SNAPSHOT + 7.6.4 test org.dspace dspace-rdf - 7.6.4-SNAPSHOT + 7.6.4 org.dspace dspace-iiif - 7.6.4-SNAPSHOT + 7.6.4 org.dspace dspace-server-webapp - 7.6.4-SNAPSHOT + 7.6.4 jar classes org.dspace dspace-server-webapp - 7.6.4-SNAPSHOT + 7.6.4 war @@ -1939,7 +1939,7 @@ scm:git:git@github.com:DSpace/DSpace.git scm:git:git@github.com:DSpace/DSpace.git https://github.com/DSpace/DSpace - dspace-7_x + dspace-7.6.4 From 9424ccf4aa6d81072391018063500c8b0f0714c0 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Mon, 14 Jul 2025 12:03:15 -0500 Subject: [PATCH 816/979] [maven-release-plugin] prepare for next development iteration --- dspace-api/pom.xml | 2 +- dspace-iiif/pom.xml | 2 +- dspace-oai/pom.xml | 2 +- dspace-rdf/pom.xml | 2 +- dspace-rest/pom.xml | 4 ++-- dspace-server-webapp/pom.xml | 2 +- dspace-services/pom.xml | 2 +- dspace-sword/pom.xml | 2 +- dspace-swordv2/pom.xml | 2 +- dspace/modules/additions/pom.xml | 2 +- dspace/modules/pom.xml | 2 +- dspace/modules/rest/pom.xml | 2 +- dspace/modules/server/pom.xml | 2 +- dspace/pom.xml | 2 +- pom.xml | 32 ++++++++++++++++---------------- 15 files changed, 31 insertions(+), 31 deletions(-) diff --git a/dspace-api/pom.xml b/dspace-api/pom.xml index b6028cdb4241..78acb3d23ae1 100644 --- a/dspace-api/pom.xml +++ b/dspace-api/pom.xml @@ -12,7 +12,7 @@ org.dspace dspace-parent - 7.6.4 + 7.6.5-SNAPSHOT .. diff --git a/dspace-iiif/pom.xml b/dspace-iiif/pom.xml index 080962f67760..ff76244905d1 100644 --- a/dspace-iiif/pom.xml +++ b/dspace-iiif/pom.xml @@ -15,7 +15,7 @@ org.dspace dspace-parent - 7.6.4 + 7.6.5-SNAPSHOT .. diff --git a/dspace-oai/pom.xml b/dspace-oai/pom.xml index 9060c6a02f11..af0ffdf2c772 100644 --- a/dspace-oai/pom.xml +++ b/dspace-oai/pom.xml @@ -8,7 +8,7 @@ dspace-parent org.dspace - 7.6.4 + 7.6.5-SNAPSHOT .. diff --git a/dspace-rdf/pom.xml b/dspace-rdf/pom.xml index 951793b5f7f9..8a72bf049f30 100644 --- a/dspace-rdf/pom.xml +++ b/dspace-rdf/pom.xml @@ -9,7 +9,7 @@ org.dspace dspace-parent - 7.6.4 + 7.6.5-SNAPSHOT .. diff --git a/dspace-rest/pom.xml b/dspace-rest/pom.xml index 663ed04c4d32..bec3f96ee331 100644 --- a/dspace-rest/pom.xml +++ b/dspace-rest/pom.xml @@ -3,7 +3,7 @@ org.dspace dspace-rest war - 7.6.4 + 7.6.5-SNAPSHOT DSpace (Deprecated) REST Webapp DSpace RESTful Web Services API. NOTE: this REST API is DEPRECATED. Please consider using the REST API in the dspace-server-webapp instead! @@ -12,7 +12,7 @@ org.dspace dspace-parent - 7.6.4 + 7.6.5-SNAPSHOT .. diff --git a/dspace-server-webapp/pom.xml b/dspace-server-webapp/pom.xml index ce71d1cad2e3..4b7ba616a650 100644 --- a/dspace-server-webapp/pom.xml +++ b/dspace-server-webapp/pom.xml @@ -15,7 +15,7 @@ org.dspace dspace-parent - 7.6.4 + 7.6.5-SNAPSHOT .. diff --git a/dspace-services/pom.xml b/dspace-services/pom.xml index 3d55556936b2..2f4c4a5d3914 100644 --- a/dspace-services/pom.xml +++ b/dspace-services/pom.xml @@ -9,7 +9,7 @@ org.dspace dspace-parent - 7.6.4 + 7.6.5-SNAPSHOT diff --git a/dspace-sword/pom.xml b/dspace-sword/pom.xml index 5e502e2b3fc2..c93949e1de56 100644 --- a/dspace-sword/pom.xml +++ b/dspace-sword/pom.xml @@ -15,7 +15,7 @@ org.dspace dspace-parent - 7.6.4 + 7.6.5-SNAPSHOT .. diff --git a/dspace-swordv2/pom.xml b/dspace-swordv2/pom.xml index 97cf8e235f31..46a9c25890df 100644 --- a/dspace-swordv2/pom.xml +++ b/dspace-swordv2/pom.xml @@ -13,7 +13,7 @@ org.dspace dspace-parent - 7.6.4 + 7.6.5-SNAPSHOT .. diff --git a/dspace/modules/additions/pom.xml b/dspace/modules/additions/pom.xml index 7687c54b921a..008c4f15a9a2 100644 --- a/dspace/modules/additions/pom.xml +++ b/dspace/modules/additions/pom.xml @@ -17,7 +17,7 @@ org.dspace modules - 7.6.4 + 7.6.5-SNAPSHOT .. diff --git a/dspace/modules/pom.xml b/dspace/modules/pom.xml index 798fb1045d13..9da5e9d1059c 100644 --- a/dspace/modules/pom.xml +++ b/dspace/modules/pom.xml @@ -11,7 +11,7 @@ org.dspace dspace-parent - 7.6.4 + 7.6.5-SNAPSHOT ../../pom.xml diff --git a/dspace/modules/rest/pom.xml b/dspace/modules/rest/pom.xml index bd2b9a2128ff..a8bf8e6438c8 100644 --- a/dspace/modules/rest/pom.xml +++ b/dspace/modules/rest/pom.xml @@ -13,7 +13,7 @@ org.dspace modules - 7.6.4 + 7.6.5-SNAPSHOT .. diff --git a/dspace/modules/server/pom.xml b/dspace/modules/server/pom.xml index cbc2aee61ee1..18115c729743 100644 --- a/dspace/modules/server/pom.xml +++ b/dspace/modules/server/pom.xml @@ -13,7 +13,7 @@ just adding new jar in the classloader modules org.dspace - 7.6.4 + 7.6.5-SNAPSHOT .. diff --git a/dspace/pom.xml b/dspace/pom.xml index d5ad247b3e24..5be9b100ffc1 100644 --- a/dspace/pom.xml +++ b/dspace/pom.xml @@ -16,7 +16,7 @@ org.dspace dspace-parent - 7.6.4 + 7.6.5-SNAPSHOT ../pom.xml diff --git a/pom.xml b/pom.xml index a87d28e8f6e2..90703f43b1ad 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ org.dspace dspace-parent pom - 7.6.4 + 7.6.5-SNAPSHOT DSpace Parent Project DSpace open source software is a turnkey institutional repository application. @@ -875,14 +875,14 @@ org.dspace dspace-rest - 7.6.4 + 7.6.5-SNAPSHOT jar classes org.dspace dspace-rest - 7.6.4 + 7.6.5-SNAPSHOT war @@ -1031,69 +1031,69 @@ org.dspace dspace-api - 7.6.4 + 7.6.5-SNAPSHOT org.dspace dspace-api test-jar - 7.6.4 + 7.6.5-SNAPSHOT test org.dspace.modules additions - 7.6.4 + 7.6.5-SNAPSHOT org.dspace dspace-sword - 7.6.4 + 7.6.5-SNAPSHOT org.dspace dspace-swordv2 - 7.6.4 + 7.6.5-SNAPSHOT org.dspace dspace-oai - 7.6.4 + 7.6.5-SNAPSHOT org.dspace dspace-services - 7.6.4 + 7.6.5-SNAPSHOT org.dspace dspace-server-webapp test-jar - 7.6.4 + 7.6.5-SNAPSHOT test org.dspace dspace-rdf - 7.6.4 + 7.6.5-SNAPSHOT org.dspace dspace-iiif - 7.6.4 + 7.6.5-SNAPSHOT org.dspace dspace-server-webapp - 7.6.4 + 7.6.5-SNAPSHOT jar classes org.dspace dspace-server-webapp - 7.6.4 + 7.6.5-SNAPSHOT war @@ -1939,7 +1939,7 @@ scm:git:git@github.com:DSpace/DSpace.git scm:git:git@github.com:DSpace/DSpace.git https://github.com/DSpace/DSpace - dspace-7.6.4 + dspace-7_x From 1743314da4f38f0539c03e71c798f53ca158bf1a Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Mon, 14 Jul 2025 14:43:33 -0500 Subject: [PATCH 817/979] [maven-release-plugin] prepare release dspace-8.2 --- dspace-api/pom.xml | 2 +- dspace-iiif/pom.xml | 2 +- dspace-oai/pom.xml | 2 +- dspace-rdf/pom.xml | 2 +- dspace-server-webapp/pom.xml | 2 +- dspace-services/pom.xml | 2 +- dspace-sword/pom.xml | 2 +- dspace-swordv2/pom.xml | 2 +- dspace/modules/additions/pom.xml | 2 +- dspace/modules/pom.xml | 2 +- dspace/modules/server-boot/pom.xml | 2 +- dspace/modules/server/pom.xml | 2 +- dspace/pom.xml | 2 +- pom.xml | 28 ++++++++++++++-------------- 14 files changed, 27 insertions(+), 27 deletions(-) diff --git a/dspace-api/pom.xml b/dspace-api/pom.xml index 7303df1fcef0..882f0b037c73 100644 --- a/dspace-api/pom.xml +++ b/dspace-api/pom.xml @@ -12,7 +12,7 @@ org.dspace dspace-parent - 8.2-SNAPSHOT + 8.2 .. diff --git a/dspace-iiif/pom.xml b/dspace-iiif/pom.xml index 71b88d92ca9b..4a93ed41c006 100644 --- a/dspace-iiif/pom.xml +++ b/dspace-iiif/pom.xml @@ -15,7 +15,7 @@ org.dspace dspace-parent - 8.2-SNAPSHOT + 8.2 .. diff --git a/dspace-oai/pom.xml b/dspace-oai/pom.xml index 434e3fa2886a..8a9bd8aa4044 100644 --- a/dspace-oai/pom.xml +++ b/dspace-oai/pom.xml @@ -8,7 +8,7 @@ dspace-parent org.dspace - 8.2-SNAPSHOT + 8.2 .. diff --git a/dspace-rdf/pom.xml b/dspace-rdf/pom.xml index 4662433d49c1..f18177116b45 100644 --- a/dspace-rdf/pom.xml +++ b/dspace-rdf/pom.xml @@ -9,7 +9,7 @@ org.dspace dspace-parent - 8.2-SNAPSHOT + 8.2 .. diff --git a/dspace-server-webapp/pom.xml b/dspace-server-webapp/pom.xml index 47afbfd1374e..e38e99a30cb2 100644 --- a/dspace-server-webapp/pom.xml +++ b/dspace-server-webapp/pom.xml @@ -14,7 +14,7 @@ org.dspace dspace-parent - 8.2-SNAPSHOT + 8.2 .. diff --git a/dspace-services/pom.xml b/dspace-services/pom.xml index 973790ec435f..6d59839cdac8 100644 --- a/dspace-services/pom.xml +++ b/dspace-services/pom.xml @@ -9,7 +9,7 @@ org.dspace dspace-parent - 8.2-SNAPSHOT + 8.2 diff --git a/dspace-sword/pom.xml b/dspace-sword/pom.xml index bdab97fee787..1866cd4c3a7b 100644 --- a/dspace-sword/pom.xml +++ b/dspace-sword/pom.xml @@ -15,7 +15,7 @@ org.dspace dspace-parent - 8.2-SNAPSHOT + 8.2 .. diff --git a/dspace-swordv2/pom.xml b/dspace-swordv2/pom.xml index 84e1e412b945..29e81f2edb30 100644 --- a/dspace-swordv2/pom.xml +++ b/dspace-swordv2/pom.xml @@ -13,7 +13,7 @@ org.dspace dspace-parent - 8.2-SNAPSHOT + 8.2 .. diff --git a/dspace/modules/additions/pom.xml b/dspace/modules/additions/pom.xml index ec1153e1c9a5..e7c2073c73bf 100644 --- a/dspace/modules/additions/pom.xml +++ b/dspace/modules/additions/pom.xml @@ -17,7 +17,7 @@ org.dspace modules - 8.2-SNAPSHOT + 8.2 .. diff --git a/dspace/modules/pom.xml b/dspace/modules/pom.xml index 5394ccd3248a..1e9efb0401d1 100644 --- a/dspace/modules/pom.xml +++ b/dspace/modules/pom.xml @@ -11,7 +11,7 @@ org.dspace dspace-parent - 8.2-SNAPSHOT + 8.2 ../../pom.xml diff --git a/dspace/modules/server-boot/pom.xml b/dspace/modules/server-boot/pom.xml index 9d7251e78e2b..9aa952ec1290 100644 --- a/dspace/modules/server-boot/pom.xml +++ b/dspace/modules/server-boot/pom.xml @@ -11,7 +11,7 @@ modules org.dspace - 8.2-SNAPSHOT + 8.2 .. diff --git a/dspace/modules/server/pom.xml b/dspace/modules/server/pom.xml index 35c7f161e91b..a7399eee836a 100644 --- a/dspace/modules/server/pom.xml +++ b/dspace/modules/server/pom.xml @@ -7,7 +7,7 @@ modules org.dspace - 8.2-SNAPSHOT + 8.2 .. diff --git a/dspace/pom.xml b/dspace/pom.xml index 6690264d7c07..fd855be8b169 100644 --- a/dspace/pom.xml +++ b/dspace/pom.xml @@ -16,7 +16,7 @@ org.dspace dspace-parent - 8.2-SNAPSHOT + 8.2 ../pom.xml diff --git a/pom.xml b/pom.xml index e6f580426266..38980f32b111 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ org.dspace dspace-parent pom - 8.2-SNAPSHOT + 8.2 DSpace Parent Project DSpace open source software is a turnkey institutional repository application. @@ -1016,68 +1016,68 @@ org.dspace dspace-api - 8.2-SNAPSHOT + 8.2 org.dspace dspace-api test-jar - 8.2-SNAPSHOT + 8.2 test org.dspace.modules additions - 8.2-SNAPSHOT + 8.2 org.dspace.modules server classes - 8.2-SNAPSHOT + 8.2 org.dspace dspace-sword - 8.2-SNAPSHOT + 8.2 org.dspace dspace-swordv2 - 8.2-SNAPSHOT + 8.2 org.dspace dspace-oai - 8.2-SNAPSHOT + 8.2 org.dspace dspace-services - 8.2-SNAPSHOT + 8.2 org.dspace dspace-server-webapp test-jar - 8.2-SNAPSHOT + 8.2 test org.dspace dspace-rdf - 8.2-SNAPSHOT + 8.2 org.dspace dspace-iiif - 8.2-SNAPSHOT + 8.2 org.dspace dspace-server-webapp - 8.2-SNAPSHOT + 8.2 @@ -1910,7 +1910,7 @@ scm:git:git@github.com:DSpace/DSpace.git scm:git:git@github.com:DSpace/DSpace.git https://github.com/DSpace/DSpace - dspace-8_x + dspace-8.2 From 8f4e77170761c561b96a83051fadef1844fea624 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Mon, 14 Jul 2025 14:43:37 -0500 Subject: [PATCH 818/979] [maven-release-plugin] prepare for next development iteration --- dspace-api/pom.xml | 2 +- dspace-iiif/pom.xml | 2 +- dspace-oai/pom.xml | 2 +- dspace-rdf/pom.xml | 2 +- dspace-server-webapp/pom.xml | 2 +- dspace-services/pom.xml | 2 +- dspace-sword/pom.xml | 2 +- dspace-swordv2/pom.xml | 2 +- dspace/modules/additions/pom.xml | 2 +- dspace/modules/pom.xml | 2 +- dspace/modules/server-boot/pom.xml | 2 +- dspace/modules/server/pom.xml | 2 +- dspace/pom.xml | 2 +- pom.xml | 28 ++++++++++++++-------------- 14 files changed, 27 insertions(+), 27 deletions(-) diff --git a/dspace-api/pom.xml b/dspace-api/pom.xml index 882f0b037c73..76458d7bba82 100644 --- a/dspace-api/pom.xml +++ b/dspace-api/pom.xml @@ -12,7 +12,7 @@ org.dspace dspace-parent - 8.2 + 8.3-SNAPSHOT .. diff --git a/dspace-iiif/pom.xml b/dspace-iiif/pom.xml index 4a93ed41c006..6977ca4fd0ff 100644 --- a/dspace-iiif/pom.xml +++ b/dspace-iiif/pom.xml @@ -15,7 +15,7 @@ org.dspace dspace-parent - 8.2 + 8.3-SNAPSHOT .. diff --git a/dspace-oai/pom.xml b/dspace-oai/pom.xml index 8a9bd8aa4044..407711f680aa 100644 --- a/dspace-oai/pom.xml +++ b/dspace-oai/pom.xml @@ -8,7 +8,7 @@ dspace-parent org.dspace - 8.2 + 8.3-SNAPSHOT .. diff --git a/dspace-rdf/pom.xml b/dspace-rdf/pom.xml index f18177116b45..5bc1c402bd7d 100644 --- a/dspace-rdf/pom.xml +++ b/dspace-rdf/pom.xml @@ -9,7 +9,7 @@ org.dspace dspace-parent - 8.2 + 8.3-SNAPSHOT .. diff --git a/dspace-server-webapp/pom.xml b/dspace-server-webapp/pom.xml index e38e99a30cb2..588aa7696266 100644 --- a/dspace-server-webapp/pom.xml +++ b/dspace-server-webapp/pom.xml @@ -14,7 +14,7 @@ org.dspace dspace-parent - 8.2 + 8.3-SNAPSHOT .. diff --git a/dspace-services/pom.xml b/dspace-services/pom.xml index 6d59839cdac8..8baf523a3b19 100644 --- a/dspace-services/pom.xml +++ b/dspace-services/pom.xml @@ -9,7 +9,7 @@ org.dspace dspace-parent - 8.2 + 8.3-SNAPSHOT diff --git a/dspace-sword/pom.xml b/dspace-sword/pom.xml index 1866cd4c3a7b..00369160636e 100644 --- a/dspace-sword/pom.xml +++ b/dspace-sword/pom.xml @@ -15,7 +15,7 @@ org.dspace dspace-parent - 8.2 + 8.3-SNAPSHOT .. diff --git a/dspace-swordv2/pom.xml b/dspace-swordv2/pom.xml index 29e81f2edb30..dc48a2759a82 100644 --- a/dspace-swordv2/pom.xml +++ b/dspace-swordv2/pom.xml @@ -13,7 +13,7 @@ org.dspace dspace-parent - 8.2 + 8.3-SNAPSHOT .. diff --git a/dspace/modules/additions/pom.xml b/dspace/modules/additions/pom.xml index e7c2073c73bf..a82710f88c99 100644 --- a/dspace/modules/additions/pom.xml +++ b/dspace/modules/additions/pom.xml @@ -17,7 +17,7 @@ org.dspace modules - 8.2 + 8.3-SNAPSHOT .. diff --git a/dspace/modules/pom.xml b/dspace/modules/pom.xml index 1e9efb0401d1..7800d80026d9 100644 --- a/dspace/modules/pom.xml +++ b/dspace/modules/pom.xml @@ -11,7 +11,7 @@ org.dspace dspace-parent - 8.2 + 8.3-SNAPSHOT ../../pom.xml diff --git a/dspace/modules/server-boot/pom.xml b/dspace/modules/server-boot/pom.xml index 9aa952ec1290..6b73cc459dd3 100644 --- a/dspace/modules/server-boot/pom.xml +++ b/dspace/modules/server-boot/pom.xml @@ -11,7 +11,7 @@ modules org.dspace - 8.2 + 8.3-SNAPSHOT .. diff --git a/dspace/modules/server/pom.xml b/dspace/modules/server/pom.xml index a7399eee836a..48ecdfd0274b 100644 --- a/dspace/modules/server/pom.xml +++ b/dspace/modules/server/pom.xml @@ -7,7 +7,7 @@ modules org.dspace - 8.2 + 8.3-SNAPSHOT .. diff --git a/dspace/pom.xml b/dspace/pom.xml index fd855be8b169..571ff5e371f0 100644 --- a/dspace/pom.xml +++ b/dspace/pom.xml @@ -16,7 +16,7 @@ org.dspace dspace-parent - 8.2 + 8.3-SNAPSHOT ../pom.xml diff --git a/pom.xml b/pom.xml index 38980f32b111..5ad544ff64ad 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ org.dspace dspace-parent pom - 8.2 + 8.3-SNAPSHOT DSpace Parent Project DSpace open source software is a turnkey institutional repository application. @@ -1016,68 +1016,68 @@ org.dspace dspace-api - 8.2 + 8.3-SNAPSHOT org.dspace dspace-api test-jar - 8.2 + 8.3-SNAPSHOT test org.dspace.modules additions - 8.2 + 8.3-SNAPSHOT org.dspace.modules server classes - 8.2 + 8.3-SNAPSHOT org.dspace dspace-sword - 8.2 + 8.3-SNAPSHOT org.dspace dspace-swordv2 - 8.2 + 8.3-SNAPSHOT org.dspace dspace-oai - 8.2 + 8.3-SNAPSHOT org.dspace dspace-services - 8.2 + 8.3-SNAPSHOT org.dspace dspace-server-webapp test-jar - 8.2 + 8.3-SNAPSHOT test org.dspace dspace-rdf - 8.2 + 8.3-SNAPSHOT org.dspace dspace-iiif - 8.2 + 8.3-SNAPSHOT org.dspace dspace-server-webapp - 8.2 + 8.3-SNAPSHOT @@ -1910,7 +1910,7 @@ scm:git:git@github.com:DSpace/DSpace.git scm:git:git@github.com:DSpace/DSpace.git https://github.com/DSpace/DSpace - dspace-8.2 + dspace-8_x From 271f50b3771d906a33aa45f366917d8967a45e6f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 21 Jul 2025 03:08:17 +0000 Subject: [PATCH 819/979] Bump com.amazonaws:aws-java-sdk-s3 from 1.12.785 to 1.12.788 Bumps [com.amazonaws:aws-java-sdk-s3](https://github.com/aws/aws-sdk-java) from 1.12.785 to 1.12.788. - [Changelog](https://github.com/aws/aws-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java/compare/1.12.785...1.12.788) --- updated-dependencies: - dependency-name: com.amazonaws:aws-java-sdk-s3 dependency-version: 1.12.788 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- dspace-api/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dspace-api/pom.xml b/dspace-api/pom.xml index 76458d7bba82..6d1b0b8fc25d 100644 --- a/dspace-api/pom.xml +++ b/dspace-api/pom.xml @@ -731,7 +731,7 @@ com.amazonaws aws-java-sdk-s3 - 1.12.785 + 1.12.788 + test-argLine @@ -540,7 +540,7 @@ - -Xmx1024m + -Xmx1024m -Dfile.encoding=UTF-8 From 39fb2afba19b2b9b580dbac394a983958e8c5f27 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Wed, 16 Jul 2025 16:27:05 -0500 Subject: [PATCH 824/979] Update test to no longer assume 127.0.0.1 will always respond with "localhost" as the hostname. On my machine it does not. --- .../client/DSpaceHttpClientFactoryTest.java | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/dspace-api/src/test/java/org/dspace/app/client/DSpaceHttpClientFactoryTest.java b/dspace-api/src/test/java/org/dspace/app/client/DSpaceHttpClientFactoryTest.java index b518f19ff4d3..ca91bb5dc9ce 100644 --- a/dspace-api/src/test/java/org/dspace/app/client/DSpaceHttpClientFactoryTest.java +++ b/dspace-api/src/test/java/org/dspace/app/client/DSpaceHttpClientFactoryTest.java @@ -17,6 +17,7 @@ import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.when; +import java.net.InetAddress; import java.util.List; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReference; @@ -107,7 +108,14 @@ public void testBuildWithProxyConfiguredAndHostToIgnoreSet() throws Exception { @Test public void testBuildWithProxyConfiguredAndHostPrefixToIgnoreSet() throws Exception { - setHttpProxyOnConfigurationService("local*", "www.test.com"); + // Get hostname assigned to 127.0.0.1 (usually is "localhost", but not always) + InetAddress address = InetAddress.getByAddress(new byte[]{127, 0, 0, 1}); + String hostname = address.getHostName(); + // Take first 4 characters hostname as the prefix (e.g. "loca" in "localhost") + String hostnamePrefix = hostname.substring(0, 4); + // Save hostname prefix to our list of hosts to ignore, followed by an asterisk. + // (This should result in our Proxy ignoring our localhost) + setHttpProxyOnConfigurationService(hostnamePrefix + "*", "www.test.com"); CloseableHttpClient httpClient = httpClientFactory.build(); assertThat(mockProxy.getRequestCount(), is(0)); assertThat(mockServer.getRequestCount(), is(0)); @@ -122,7 +130,14 @@ public void testBuildWithProxyConfiguredAndHostPrefixToIgnoreSet() throws Except @Test public void testBuildWithProxyConfiguredAndHostSuffixToIgnoreSet() throws Exception { - setHttpProxyOnConfigurationService("www.test.com", "*host"); + // Get hostname assigned to 127.0.0.1 (usually is "localhost", but not always) + InetAddress address = InetAddress.getByAddress(new byte[]{127, 0, 0, 1}); + String hostname = address.getHostName(); + // Take last 4 characters hostname as the suffix (e.g. "host" in "localhost") + String hostnameSuffix = hostname.substring(hostname.length() - 4); + // Save hostname suffix to our list of hosts to ignore, preceded by an asterisk. + // (This should result in our Proxy ignoring our localhost) + setHttpProxyOnConfigurationService("www.test.com", "*" + hostnameSuffix); CloseableHttpClient httpClient = httpClientFactory.build(); assertThat(mockProxy.getRequestCount(), is(0)); assertThat(mockServer.getRequestCount(), is(0)); From d2c120bd8b0dd111281c70ab3dba7b51d2395330 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Fri, 18 Jul 2025 11:33:41 -0500 Subject: [PATCH 825/979] Fix broken tests on Windows by using Paths to split file path instead of regex. Also switch to in-memory s3mock because Windows cannot cleanup created files successfully. --- .../storage/bitstore/S3BitStoreServiceIT.java | 31 +++++++++++-------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/dspace-api/src/test/java/org/dspace/storage/bitstore/S3BitStoreServiceIT.java b/dspace-api/src/test/java/org/dspace/storage/bitstore/S3BitStoreServiceIT.java index 6ea21eac8d6d..63d88e950116 100644 --- a/dspace-api/src/test/java/org/dspace/storage/bitstore/S3BitStoreServiceIT.java +++ b/dspace-api/src/test/java/org/dspace/storage/bitstore/S3BitStoreServiceIT.java @@ -26,9 +26,11 @@ import java.io.File; import java.io.IOException; import java.io.InputStream; +import java.nio.file.Paths; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.sql.SQLException; +import java.util.ArrayList; import java.util.List; import java.util.Map; @@ -41,7 +43,6 @@ import com.amazonaws.services.s3.model.Bucket; import com.amazonaws.services.s3.model.ObjectMetadata; import io.findify.s3mock.S3Mock; -import org.apache.commons.io.FileUtils; import org.apache.commons.io.IOUtils; import org.apache.commons.lang.BooleanUtils; import org.dspace.AbstractIntegrationTestWithDatabase; @@ -80,8 +81,6 @@ public class S3BitStoreServiceIT extends AbstractIntegrationTestWithDatabase { private Collection collection; - private File s3Directory; - private ConfigurationService configurationService = DSpaceServicesFactory.getInstance().getConfigurationService(); @@ -89,9 +88,8 @@ public class S3BitStoreServiceIT extends AbstractIntegrationTestWithDatabase { public void setup() throws Exception { configurationService.setProperty("assetstore.s3.enabled", "true"); - s3Directory = new File(System.getProperty("java.io.tmpdir"), "s3"); - s3Mock = S3Mock.create(8001, s3Directory.getAbsolutePath()); + s3Mock = new S3Mock.Builder().withPort(8001).withInMemoryBackend().build(); s3Mock.start(); amazonS3Client = createAmazonS3Client(); @@ -112,8 +110,7 @@ public void setup() throws Exception { } @After - public void cleanUp() throws IOException { - FileUtils.deleteDirectory(s3Directory); + public void cleanUp() { s3Mock.shutdown(); } @@ -337,7 +334,7 @@ public void givenBitStreamIdentifierWhenIntermediatePathIsComputedThenNotEndingD String computedPath = this.s3BitStoreService.getIntermediatePath(path.toString()); int slashes = computeSlashes(path.toString()); assertThat(computedPath, Matchers.endsWith(File.separator)); - assertThat(computedPath.split(File.separator).length, Matchers.equalTo(slashes)); + assertThat(countPathElements(computedPath), Matchers.equalTo(slashes)); path.append("2"); computedPath = this.s3BitStoreService.getIntermediatePath(path.toString()); @@ -362,31 +359,31 @@ public void givenBitStreamIdentidierWhenIntermediatePathIsComputedThenMustBeSpli String computedPath = this.s3BitStoreService.getIntermediatePath(path.toString()); int slashes = computeSlashes(path.toString()); assertThat(computedPath, Matchers.endsWith(File.separator)); - assertThat(computedPath.split(File.separator).length, Matchers.equalTo(slashes)); + assertThat(countPathElements(computedPath), Matchers.equalTo(slashes)); path.append("2"); computedPath = this.s3BitStoreService.getIntermediatePath(path.toString()); slashes = computeSlashes(path.toString()); assertThat(computedPath, Matchers.endsWith(File.separator)); - assertThat(computedPath.split(File.separator).length, Matchers.equalTo(slashes)); + assertThat(countPathElements(computedPath), Matchers.equalTo(slashes)); path.append("3"); computedPath = this.s3BitStoreService.getIntermediatePath(path.toString()); slashes = computeSlashes(path.toString()); assertThat(computedPath, Matchers.endsWith(File.separator)); - assertThat(computedPath.split(File.separator).length, Matchers.equalTo(slashes)); + assertThat(countPathElements(computedPath), Matchers.equalTo(slashes)); path.append("4"); computedPath = this.s3BitStoreService.getIntermediatePath(path.toString()); slashes = computeSlashes(path.toString()); assertThat(computedPath, Matchers.endsWith(File.separator)); - assertThat(computedPath.split(File.separator).length, Matchers.equalTo(slashes)); + assertThat(countPathElements(computedPath), Matchers.equalTo(slashes)); path.append("56789"); computedPath = this.s3BitStoreService.getIntermediatePath(path.toString()); slashes = computeSlashes(path.toString()); assertThat(computedPath, Matchers.endsWith(File.separator)); - assertThat(computedPath.split(File.separator).length, Matchers.equalTo(slashes)); + assertThat(countPathElements(computedPath), Matchers.equalTo(slashes)); } @Test @@ -465,4 +462,12 @@ private int computeSlashes(String internalId) { return Math.min(slashes, S3BitStoreService.directoryLevels); } + // Count the number of elements in a Unix or Windows path. + // We use 'Paths' instead of splitting on slashes because these OSes use different path separators. + private int countPathElements(String stringPath) { + List pathElements = new ArrayList<>(); + Paths.get(stringPath).forEach(p -> pathElements.add(p.toString())); + return pathElements.size(); + } + } From 28686fc48adc8f6f188231c6531c27e58134548e Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Mon, 21 Jul 2025 15:57:34 -0500 Subject: [PATCH 826/979] Improve logging in AbstractLiveImportIntegrationTest (to make tests easier to debug). Replace obscure StringInputStream (from Ant) with IOUtils.toInputStream --- .../importer/external/metadatamapping/MetadatumDTO.java | 9 +++++++++ .../app/rest/AbstractLiveImportIntegrationTest.java | 7 ++++--- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/importer/external/metadatamapping/MetadatumDTO.java b/dspace-api/src/main/java/org/dspace/importer/external/metadatamapping/MetadatumDTO.java index 265dd55eb933..b8a7507eaa61 100644 --- a/dspace-api/src/main/java/org/dspace/importer/external/metadatamapping/MetadatumDTO.java +++ b/dspace-api/src/main/java/org/dspace/importer/external/metadatamapping/MetadatumDTO.java @@ -105,4 +105,13 @@ public String getValue() { public void setValue(String value) { this.value = value; } + + /** + * Return string representation of MetadatumDTO + * @return string representation of format "[schema].[element].[qualifier]=[value]" + */ + @Override + public String toString() { + return schema + "." + element + "." + qualifier + "=" + value; + } } diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/AbstractLiveImportIntegrationTest.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/AbstractLiveImportIntegrationTest.java index cf3e125cc531..94fbce4a5115 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/AbstractLiveImportIntegrationTest.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/AbstractLiveImportIntegrationTest.java @@ -16,12 +16,12 @@ import java.util.ArrayList; import java.util.List; +import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.StringUtils; import org.apache.http.ProtocolVersion; import org.apache.http.StatusLine; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.entity.BasicHttpEntity; -import org.apache.tools.ant.filters.StringInputStream; import org.dspace.app.rest.test.AbstractControllerIntegrationTest; import org.dspace.importer.external.datamodel.ImportRecord; import org.dspace.importer.external.metadatamapping.MetadatumDTO; @@ -43,7 +43,8 @@ protected void matchRecords(ArrayList recordsImported, ArrayList list, List list2) { assertEquals(list.size(), list2.size()); for (int i = 0; i < list.size(); i++) { - assertTrue(sameMetadatum(list.get(i), list2.get(i))); + assertTrue("'" + list.get(i).toString() + "' should be equal to '" + list2.get(i).toString() + "'", + sameMetadatum(list.get(i), list2.get(i))); } } @@ -70,7 +71,7 @@ protected CloseableHttpResponse mockResponse(String xmlExample, int statusCode, throws UnsupportedEncodingException { BasicHttpEntity basicHttpEntity = new BasicHttpEntity(); basicHttpEntity.setChunked(true); - basicHttpEntity.setContent(new StringInputStream(xmlExample)); + basicHttpEntity.setContent(IOUtils.toInputStream(xmlExample)); CloseableHttpResponse response = mock(CloseableHttpResponse.class); when(response.getStatusLine()).thenReturn(statusLine(statusCode, reason)); From 15b3f314a3bd19cd47d8964f24385e8a08bca4c2 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Mon, 21 Jul 2025 15:58:37 -0500 Subject: [PATCH 827/979] Force UTF-8 encoding in all tests. This fixes several test failures when running tests from Windows commandline because Windows doesn't default to using UTF-8. --- pom.xml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/pom.xml b/pom.xml index 90703f43b1ad..3df4347fe92a 100644 --- a/pom.xml +++ b/pom.xml @@ -515,10 +515,10 @@ - + test-argLine @@ -527,7 +527,7 @@ - -Xmx1024m + -Xmx1024m -Dfile.encoding=UTF-8 From 479cb7688527eea88a87423540ec1c11f81f5848 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Tue, 22 Jul 2025 14:42:47 -0500 Subject: [PATCH 828/979] Minor updates to LICENSES_THIRD_PARTY for 7.6.5 release --- LICENSES_THIRD_PARTY | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/LICENSES_THIRD_PARTY b/LICENSES_THIRD_PARTY index 304ee4e34302..92868467c989 100644 --- a/LICENSES_THIRD_PARTY +++ b/LICENSES_THIRD_PARTY @@ -233,9 +233,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.0 - https://logging.apache.org/log4j/2.x/) - * Apache Log4j API (org.apache.logging.log4j:log4j-api:2.25.0 - https://logging.apache.org/log4j/2.x/) - * Apache Log4j Core (org.apache.logging.log4j:log4j-core:2.25.0 - https://logging.apache.org/log4j/2.x/) + * 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 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/) From 6e8b6cc33acd3db77687995acbe3753d6ffdfc3a Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Tue, 22 Jul 2025 15:03:04 -0500 Subject: [PATCH 829/979] [maven-release-plugin] prepare release dspace-7.6.5 --- dspace-api/pom.xml | 2 +- dspace-iiif/pom.xml | 2 +- dspace-oai/pom.xml | 2 +- dspace-rdf/pom.xml | 2 +- dspace-rest/pom.xml | 4 ++-- dspace-server-webapp/pom.xml | 2 +- dspace-services/pom.xml | 2 +- dspace-sword/pom.xml | 2 +- dspace-swordv2/pom.xml | 2 +- dspace/modules/additions/pom.xml | 2 +- dspace/modules/pom.xml | 2 +- dspace/modules/rest/pom.xml | 2 +- dspace/modules/server/pom.xml | 2 +- dspace/pom.xml | 2 +- pom.xml | 32 ++++++++++++++++---------------- 15 files changed, 31 insertions(+), 31 deletions(-) diff --git a/dspace-api/pom.xml b/dspace-api/pom.xml index 78acb3d23ae1..6071f64c6eac 100644 --- a/dspace-api/pom.xml +++ b/dspace-api/pom.xml @@ -12,7 +12,7 @@ org.dspace dspace-parent - 7.6.5-SNAPSHOT + 7.6.5 .. diff --git a/dspace-iiif/pom.xml b/dspace-iiif/pom.xml index ff76244905d1..79d8412c17b1 100644 --- a/dspace-iiif/pom.xml +++ b/dspace-iiif/pom.xml @@ -15,7 +15,7 @@ org.dspace dspace-parent - 7.6.5-SNAPSHOT + 7.6.5 .. diff --git a/dspace-oai/pom.xml b/dspace-oai/pom.xml index af0ffdf2c772..a46ee7ba5116 100644 --- a/dspace-oai/pom.xml +++ b/dspace-oai/pom.xml @@ -8,7 +8,7 @@ dspace-parent org.dspace - 7.6.5-SNAPSHOT + 7.6.5 .. diff --git a/dspace-rdf/pom.xml b/dspace-rdf/pom.xml index 8a72bf049f30..d32a00bf6267 100644 --- a/dspace-rdf/pom.xml +++ b/dspace-rdf/pom.xml @@ -9,7 +9,7 @@ org.dspace dspace-parent - 7.6.5-SNAPSHOT + 7.6.5 .. diff --git a/dspace-rest/pom.xml b/dspace-rest/pom.xml index bec3f96ee331..64b798c58a12 100644 --- a/dspace-rest/pom.xml +++ b/dspace-rest/pom.xml @@ -3,7 +3,7 @@ org.dspace dspace-rest war - 7.6.5-SNAPSHOT + 7.6.5 DSpace (Deprecated) REST Webapp DSpace RESTful Web Services API. NOTE: this REST API is DEPRECATED. Please consider using the REST API in the dspace-server-webapp instead! @@ -12,7 +12,7 @@ org.dspace dspace-parent - 7.6.5-SNAPSHOT + 7.6.5 .. diff --git a/dspace-server-webapp/pom.xml b/dspace-server-webapp/pom.xml index 4b7ba616a650..193a9809e0dc 100644 --- a/dspace-server-webapp/pom.xml +++ b/dspace-server-webapp/pom.xml @@ -15,7 +15,7 @@ org.dspace dspace-parent - 7.6.5-SNAPSHOT + 7.6.5 .. diff --git a/dspace-services/pom.xml b/dspace-services/pom.xml index 2f4c4a5d3914..7849381edfcf 100644 --- a/dspace-services/pom.xml +++ b/dspace-services/pom.xml @@ -9,7 +9,7 @@ org.dspace dspace-parent - 7.6.5-SNAPSHOT + 7.6.5 diff --git a/dspace-sword/pom.xml b/dspace-sword/pom.xml index c93949e1de56..cd5b8a6af30d 100644 --- a/dspace-sword/pom.xml +++ b/dspace-sword/pom.xml @@ -15,7 +15,7 @@ org.dspace dspace-parent - 7.6.5-SNAPSHOT + 7.6.5 .. diff --git a/dspace-swordv2/pom.xml b/dspace-swordv2/pom.xml index 46a9c25890df..34a22026828a 100644 --- a/dspace-swordv2/pom.xml +++ b/dspace-swordv2/pom.xml @@ -13,7 +13,7 @@ org.dspace dspace-parent - 7.6.5-SNAPSHOT + 7.6.5 .. diff --git a/dspace/modules/additions/pom.xml b/dspace/modules/additions/pom.xml index 008c4f15a9a2..125f0e16cceb 100644 --- a/dspace/modules/additions/pom.xml +++ b/dspace/modules/additions/pom.xml @@ -17,7 +17,7 @@ org.dspace modules - 7.6.5-SNAPSHOT + 7.6.5 .. diff --git a/dspace/modules/pom.xml b/dspace/modules/pom.xml index 9da5e9d1059c..04ecf6c5efa7 100644 --- a/dspace/modules/pom.xml +++ b/dspace/modules/pom.xml @@ -11,7 +11,7 @@ org.dspace dspace-parent - 7.6.5-SNAPSHOT + 7.6.5 ../../pom.xml diff --git a/dspace/modules/rest/pom.xml b/dspace/modules/rest/pom.xml index a8bf8e6438c8..c182a7ab3b0a 100644 --- a/dspace/modules/rest/pom.xml +++ b/dspace/modules/rest/pom.xml @@ -13,7 +13,7 @@ org.dspace modules - 7.6.5-SNAPSHOT + 7.6.5 .. diff --git a/dspace/modules/server/pom.xml b/dspace/modules/server/pom.xml index 18115c729743..56a2ecdfdaff 100644 --- a/dspace/modules/server/pom.xml +++ b/dspace/modules/server/pom.xml @@ -13,7 +13,7 @@ just adding new jar in the classloader modules org.dspace - 7.6.5-SNAPSHOT + 7.6.5 .. diff --git a/dspace/pom.xml b/dspace/pom.xml index 5be9b100ffc1..8e1060a432cb 100644 --- a/dspace/pom.xml +++ b/dspace/pom.xml @@ -16,7 +16,7 @@ org.dspace dspace-parent - 7.6.5-SNAPSHOT + 7.6.5 ../pom.xml diff --git a/pom.xml b/pom.xml index 9f6c2ed42fbb..3526d2cf2e71 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ org.dspace dspace-parent pom - 7.6.5-SNAPSHOT + 7.6.5 DSpace Parent Project DSpace open source software is a turnkey institutional repository application. @@ -875,14 +875,14 @@ org.dspace dspace-rest - 7.6.5-SNAPSHOT + 7.6.5 jar classes org.dspace dspace-rest - 7.6.5-SNAPSHOT + 7.6.5 war @@ -1031,69 +1031,69 @@ org.dspace dspace-api - 7.6.5-SNAPSHOT + 7.6.5 org.dspace dspace-api test-jar - 7.6.5-SNAPSHOT + 7.6.5 test org.dspace.modules additions - 7.6.5-SNAPSHOT + 7.6.5 org.dspace dspace-sword - 7.6.5-SNAPSHOT + 7.6.5 org.dspace dspace-swordv2 - 7.6.5-SNAPSHOT + 7.6.5 org.dspace dspace-oai - 7.6.5-SNAPSHOT + 7.6.5 org.dspace dspace-services - 7.6.5-SNAPSHOT + 7.6.5 org.dspace dspace-server-webapp test-jar - 7.6.5-SNAPSHOT + 7.6.5 test org.dspace dspace-rdf - 7.6.5-SNAPSHOT + 7.6.5 org.dspace dspace-iiif - 7.6.5-SNAPSHOT + 7.6.5 org.dspace dspace-server-webapp - 7.6.5-SNAPSHOT + 7.6.5 jar classes org.dspace dspace-server-webapp - 7.6.5-SNAPSHOT + 7.6.5 war @@ -1939,7 +1939,7 @@ scm:git:git@github.com:DSpace/DSpace.git scm:git:git@github.com:DSpace/DSpace.git https://github.com/DSpace/DSpace - dspace-7_x + dspace-7.6.5 From 4ce1f730335368a9281a6d26237e5791a4861d27 Mon Sep 17 00:00:00 2001 From: Marie Verdonck Date: Thu, 28 Nov 2024 17:38:20 +0100 Subject: [PATCH 830/979] 109807: Live Import - PubMed - support optional apiKey config (cherry picked from commit c25433ee17c318b1054933662f42090f2736eabf) --- ...PubmedImportMetadataSourceServiceImpl.java | 23 +++++++++++++++++++ .../spring-dspace-addon-import-services.xml | 1 + dspace/config/modules/external-providers.cfg | 5 +++- 3 files changed, 28 insertions(+), 1 deletion(-) diff --git a/dspace-api/src/main/java/org/dspace/importer/external/pubmed/service/PubmedImportMetadataSourceServiceImpl.java b/dspace-api/src/main/java/org/dspace/importer/external/pubmed/service/PubmedImportMetadataSourceServiceImpl.java index c870161bf9bd..dc9954969394 100644 --- a/dspace-api/src/main/java/org/dspace/importer/external/pubmed/service/PubmedImportMetadataSourceServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/importer/external/pubmed/service/PubmedImportMetadataSourceServiceImpl.java @@ -55,6 +55,7 @@ public class PubmedImportMetadataSourceServiceImpl extends AbstractImportMetadat private String urlFetch; private String urlSearch; + private String apiKey; private int attempt = 3; @@ -210,6 +211,9 @@ public GetNbRecords(Query query) { @Override public Integer call() throws Exception { URIBuilder uriBuilder = new URIBuilder(urlSearch); + if (StringUtils.isNotBlank(apiKey)) { + uriBuilder.addParameter("api_key", apiKey); + } uriBuilder.addParameter("db", "pubmed"); uriBuilder.addParameter("term", query.getParameterAsClass("query", String.class)); Map> params = new HashMap>(); @@ -286,6 +290,9 @@ public Collection call() throws Exception { List records = new LinkedList(); URIBuilder uriBuilder = new URIBuilder(urlSearch); + if (StringUtils.isNotBlank(apiKey)) { + uriBuilder.addParameter("api_key", apiKey); + } uriBuilder.addParameter("db", "pubmed"); uriBuilder.addParameter("retstart", start.toString()); uriBuilder.addParameter("retmax", count.toString()); @@ -316,6 +323,9 @@ public Collection call() throws Exception { String webEnv = getSingleElementValue(response, "WebEnv"); URIBuilder uriBuilder2 = new URIBuilder(urlFetch); + if (StringUtils.isNotBlank(apiKey)) { + uriBuilder2.addParameter("api_key", apiKey); + } uriBuilder2.addParameter("db", "pubmed"); uriBuilder2.addParameter("retstart", start.toString()); uriBuilder2.addParameter("retmax", count.toString()); @@ -388,6 +398,9 @@ public GetRecord(Query q) { public ImportRecord call() throws Exception { URIBuilder uriBuilder = new URIBuilder(urlFetch); + if (StringUtils.isNotBlank(apiKey)) { + uriBuilder.addParameter("api_key", apiKey); + } uriBuilder.addParameter("db", "pubmed"); uriBuilder.addParameter("retmode", "xml"); uriBuilder.addParameter("id", query.getParameterAsClass("id", String.class)); @@ -428,6 +441,9 @@ public FindMatchingRecords(Query q) { public Collection call() throws Exception { URIBuilder uriBuilder = new URIBuilder(urlSearch); + if (StringUtils.isNotBlank(apiKey)) { + uriBuilder.addParameter("api_key", apiKey); + } uriBuilder.addParameter("db", "pubmed"); uriBuilder.addParameter("usehistory", "y"); uriBuilder.addParameter("term", query.getParameterAsClass("term", String.class)); @@ -457,6 +473,9 @@ public Collection call() throws Exception { String queryKey = getSingleElementValue(response, "QueryKey"); URIBuilder uriBuilder2 = new URIBuilder(urlFetch); + if (StringUtils.isNotBlank(apiKey)) { + uriBuilder.addParameter("api_key", apiKey); + } uriBuilder2.addParameter("db", "pubmed"); uriBuilder2.addParameter("retmode", "xml"); uriBuilder2.addParameter("WebEnv", webEnv); @@ -532,4 +551,8 @@ public void setUrlSearch(String urlSearch) { this.urlSearch = urlSearch; } + public void setApiKey(String apiKey) { + this.apiKey = apiKey; + } + } diff --git a/dspace-api/src/main/resources/spring/spring-dspace-addon-import-services.xml b/dspace-api/src/main/resources/spring/spring-dspace-addon-import-services.xml index e693d26e538e..5cfef3bd22fb 100644 --- a/dspace-api/src/main/resources/spring/spring-dspace-addon-import-services.xml +++ b/dspace-api/src/main/resources/spring/spring-dspace-addon-import-services.xml @@ -70,6 +70,7 @@ + diff --git a/dspace/config/modules/external-providers.cfg b/dspace/config/modules/external-providers.cfg index f210a0aa5163..04f5c54e9848 100644 --- a/dspace/config/modules/external-providers.cfg +++ b/dspace/config/modules/external-providers.cfg @@ -45,6 +45,9 @@ epo.searchUrl = https://ops.epo.org/rest-services/published-data/search ################################################################# #---------------------- PubMed -----------------------------# #---------------------------------------------------------------# +# If apiKey is set then it's used, if not set or blank then it's not +# Max amount of requests per ip per second with apiKey is 10; without 3 +pubmed.apiKey = pubmed.url.search = https://eutils.ncbi.nlm.nih.gov/entrez/eutils/esearch.fcgi pubmed.url.fetch = https://eutils.ncbi.nlm.nih.gov/entrez/eutils/efetch.fcgi ################################################################# @@ -96,4 +99,4 @@ datacite.timeout = 180000 #---------------------------------------------------------------# ror.orgunit-import.api-url = https://api.ror.org/organizations -################################################################# \ No newline at end of file +################################################################# From d396e4968bced33e0070aa215c533a7390b270dc Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 28 Jul 2025 03:09:34 +0000 Subject: [PATCH 831/979] Bump the fasterxml group with 4 updates Bumps the fasterxml group with 4 updates: [com.fasterxml.jackson.core:jackson-annotations](https://github.com/FasterXML/jackson), [com.fasterxml.jackson.core:jackson-core](https://github.com/FasterXML/jackson-core), [com.fasterxml.jackson.core:jackson-databind](https://github.com/FasterXML/jackson) and com.fasterxml.jackson.datatype:jackson-datatype-jsr310. Updates `com.fasterxml.jackson.core:jackson-annotations` from 2.19.1 to 2.19.2 - [Commits](https://github.com/FasterXML/jackson/commits) Updates `com.fasterxml.jackson.core:jackson-core` from 2.19.1 to 2.19.2 - [Commits](https://github.com/FasterXML/jackson-core/compare/jackson-core-2.19.1...jackson-core-2.19.2) Updates `com.fasterxml.jackson.core:jackson-core` from 2.19.1 to 2.19.2 - [Commits](https://github.com/FasterXML/jackson-core/compare/jackson-core-2.19.1...jackson-core-2.19.2) Updates `com.fasterxml.jackson.core:jackson-databind` from 2.19.1 to 2.19.2 - [Commits](https://github.com/FasterXML/jackson/commits) Updates `com.fasterxml.jackson.datatype:jackson-datatype-jsr310` from 2.19.1 to 2.19.2 Updates `com.fasterxml.jackson.datatype:jackson-datatype-jsr310` from 2.19.1 to 2.19.2 --- updated-dependencies: - dependency-name: com.fasterxml.jackson.core:jackson-annotations dependency-version: 2.19.2 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: fasterxml - dependency-name: com.fasterxml.jackson.core:jackson-core dependency-version: 2.19.2 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: fasterxml - dependency-name: com.fasterxml.jackson.core:jackson-core dependency-version: 2.19.2 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: fasterxml - dependency-name: com.fasterxml.jackson.core:jackson-databind dependency-version: 2.19.2 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: fasterxml - dependency-name: com.fasterxml.jackson.datatype:jackson-datatype-jsr310 dependency-version: 2.19.2 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: fasterxml - dependency-name: com.fasterxml.jackson.datatype:jackson-datatype-jsr310 dependency-version: 2.19.2 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: fasterxml ... Signed-off-by: dependabot[bot] --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 50c57d5b9476..bf9bcc9bd013 100644 --- a/pom.xml +++ b/pom.xml @@ -31,8 +31,8 @@ 3.10.8 2.38.0 - 2.19.1 - 2.19.1 + 2.19.2 + 2.19.2 2.1.1 4.0.2 4.0.5 From 59e37eb381fb068934bee848850cd7dc6f7cc2a4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 28 Jul 2025 03:18:35 +0000 Subject: [PATCH 832/979] Bump org.xmlunit:xmlunit-core in the test-tools group Bumps the test-tools group with 1 update: [org.xmlunit:xmlunit-core](https://github.com/xmlunit/xmlunit). Updates `org.xmlunit:xmlunit-core` from 2.10.2 to 2.10.3 - [Release notes](https://github.com/xmlunit/xmlunit/releases) - [Changelog](https://github.com/xmlunit/xmlunit/blob/main/RELEASE_NOTES.md) - [Commits](https://github.com/xmlunit/xmlunit/compare/v2.10.2...v2.10.3) --- updated-dependencies: - dependency-name: org.xmlunit:xmlunit-core dependency-version: 2.10.3 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: test-tools ... Signed-off-by: dependabot[bot] --- dspace-api/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dspace-api/pom.xml b/dspace-api/pom.xml index 76458d7bba82..d6c600ed45e5 100644 --- a/dspace-api/pom.xml +++ b/dspace-api/pom.xml @@ -785,7 +785,7 @@ org.xmlunit xmlunit-core - 2.10.2 + 2.10.3 test From c199df9d4f9f7278a8e88d5b408cb67dc0faf93c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 28 Jul 2025 03:47:20 +0000 Subject: [PATCH 833/979] Bump com.opencsv:opencsv from 5.11.1 to 5.12.0 Bumps com.opencsv:opencsv from 5.11.1 to 5.12.0. --- updated-dependencies: - dependency-name: com.opencsv:opencsv dependency-version: 5.12.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- dspace-api/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dspace-api/pom.xml b/dspace-api/pom.xml index 76458d7bba82..265f2e8cfc36 100644 --- a/dspace-api/pom.xml +++ b/dspace-api/pom.xml @@ -772,7 +772,7 @@ com.opencsv opencsv - 5.11.1 + 5.12.0 From db9c5e6599581b22571b513396d9d3c77a8ad249 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 28 Jul 2025 03:49:26 +0000 Subject: [PATCH 834/979] Bump org.apache.james:apache-mime4j-core from 0.8.12 to 0.8.13 Bumps org.apache.james:apache-mime4j-core from 0.8.12 to 0.8.13. --- updated-dependencies: - dependency-name: org.apache.james:apache-mime4j-core dependency-version: 0.8.13 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 50c57d5b9476..5e58905860d5 100644 --- a/pom.xml +++ b/pom.xml @@ -1320,7 +1320,7 @@ org.apache.james apache-mime4j-core - 0.8.12 + 0.8.13 From 0cd33e09fab453b039bb26f6639444dc54b0ef7b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 28 Jul 2025 04:04:36 +0000 Subject: [PATCH 835/979] Bump the spring group across 1 directory with 25 updates Bumps the spring group with 25 updates in the / directory: | Package | From | To | | --- | --- | --- | | [org.springframework:spring-orm](https://github.com/spring-projects/spring-framework) | `6.2.8` | `6.2.9` | | [org.springframework:spring-core](https://github.com/spring-projects/spring-framework) | `6.2.8` | `6.2.9` | | [org.springframework:spring-beans](https://github.com/spring-projects/spring-framework) | `6.2.8` | `6.2.9` | | [org.springframework:spring-aop](https://github.com/spring-projects/spring-framework) | `6.2.8` | `6.2.9` | | [org.springframework:spring-context](https://github.com/spring-projects/spring-framework) | `6.2.8` | `6.2.9` | | [org.springframework:spring-context-support](https://github.com/spring-projects/spring-framework) | `6.2.8` | `6.2.9` | | [org.springframework:spring-tx](https://github.com/spring-projects/spring-framework) | `6.2.8` | `6.2.9` | | [org.springframework:spring-jdbc](https://github.com/spring-projects/spring-framework) | `6.2.8` | `6.2.9` | | [org.springframework:spring-web](https://github.com/spring-projects/spring-framework) | `6.2.8` | `6.2.9` | | [org.springframework:spring-webmvc](https://github.com/spring-projects/spring-framework) | `6.2.8` | `6.2.9` | | [org.springframework:spring-expression](https://github.com/spring-projects/spring-framework) | `6.2.8` | `6.2.9` | | [org.springframework:spring-test](https://github.com/spring-projects/spring-framework) | `6.2.8` | `6.2.9` | | [org.springframework.boot:spring-boot-starter-test](https://github.com/spring-projects/spring-boot) | `3.5.3` | `3.5.4` | | [org.springframework.boot:spring-boot-starter-tomcat](https://github.com/spring-projects/spring-boot) | `3.5.3` | `3.5.4` | | [org.springframework.boot:spring-boot-maven-plugin](https://github.com/spring-projects/spring-boot) | `3.5.3` | `3.5.4` | | [org.springframework.boot:spring-boot-starter-cache](https://github.com/spring-projects/spring-boot) | `3.5.3` | `3.5.4` | | [org.springframework.boot:spring-boot-starter](https://github.com/spring-projects/spring-boot) | `3.5.3` | `3.5.4` | | [org.springframework.boot:spring-boot-starter-thymeleaf](https://github.com/spring-projects/spring-boot) | `3.5.3` | `3.5.4` | | [org.springframework.boot:spring-boot-starter-web](https://github.com/spring-projects/spring-boot) | `3.5.3` | `3.5.4` | | [org.springframework.boot:spring-boot-starter-data-rest](https://github.com/spring-projects/spring-boot) | `3.5.3` | `3.5.4` | | [org.springframework.boot:spring-boot-starter-security](https://github.com/spring-projects/spring-boot) | `3.5.3` | `3.5.4` | | [org.springframework.boot:spring-boot-starter-aop](https://github.com/spring-projects/spring-boot) | `3.5.3` | `3.5.4` | | [org.springframework.boot:spring-boot-starter-actuator](https://github.com/spring-projects/spring-boot) | `3.5.3` | `3.5.4` | | [org.springframework.boot:spring-boot-starter-log4j2](https://github.com/spring-projects/spring-boot) | `3.5.3` | `3.5.4` | | [org.springframework.security:spring-security-test](https://github.com/spring-projects/spring-security) | `6.5.1` | `6.5.2` | Updates `org.springframework:spring-orm` from 6.2.8 to 6.2.9 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.8...v6.2.9) Updates `org.springframework:spring-core` from 6.2.8 to 6.2.9 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.8...v6.2.9) Updates `org.springframework:spring-beans` from 6.2.8 to 6.2.9 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.8...v6.2.9) Updates `org.springframework:spring-aop` from 6.2.8 to 6.2.9 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.8...v6.2.9) Updates `org.springframework:spring-context` from 6.2.8 to 6.2.9 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.8...v6.2.9) Updates `org.springframework:spring-context-support` from 6.2.8 to 6.2.9 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.8...v6.2.9) Updates `org.springframework:spring-tx` from 6.2.8 to 6.2.9 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.8...v6.2.9) Updates `org.springframework:spring-jdbc` from 6.2.8 to 6.2.9 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.8...v6.2.9) Updates `org.springframework:spring-web` from 6.2.8 to 6.2.9 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.8...v6.2.9) Updates `org.springframework:spring-webmvc` from 6.2.8 to 6.2.9 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.8...v6.2.9) Updates `org.springframework:spring-expression` from 6.2.8 to 6.2.9 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.8...v6.2.9) Updates `org.springframework:spring-test` from 6.2.8 to 6.2.9 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.8...v6.2.9) Updates `org.springframework:spring-core` from 6.2.8 to 6.2.9 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.8...v6.2.9) Updates `org.springframework:spring-beans` from 6.2.8 to 6.2.9 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.8...v6.2.9) Updates `org.springframework:spring-aop` from 6.2.8 to 6.2.9 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.8...v6.2.9) Updates `org.springframework:spring-context` from 6.2.8 to 6.2.9 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.8...v6.2.9) Updates `org.springframework:spring-context-support` from 6.2.8 to 6.2.9 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.8...v6.2.9) Updates `org.springframework:spring-tx` from 6.2.8 to 6.2.9 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.8...v6.2.9) Updates `org.springframework:spring-jdbc` from 6.2.8 to 6.2.9 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.8...v6.2.9) Updates `org.springframework:spring-web` from 6.2.8 to 6.2.9 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.8...v6.2.9) Updates `org.springframework:spring-webmvc` from 6.2.8 to 6.2.9 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.8...v6.2.9) Updates `org.springframework:spring-expression` from 6.2.8 to 6.2.9 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.8...v6.2.9) Updates `org.springframework:spring-test` from 6.2.8 to 6.2.9 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.8...v6.2.9) Updates `org.springframework.boot:spring-boot-starter-test` from 3.5.3 to 3.5.4 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.5.3...v3.5.4) Updates `org.springframework.boot:spring-boot-starter-tomcat` from 3.5.3 to 3.5.4 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.5.3...v3.5.4) Updates `org.springframework.boot:spring-boot-maven-plugin` from 3.5.3 to 3.5.4 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.5.3...v3.5.4) Updates `org.springframework.boot:spring-boot-starter-cache` from 3.5.3 to 3.5.4 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.5.3...v3.5.4) Updates `org.springframework.boot:spring-boot-starter` from 3.5.3 to 3.5.4 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.5.3...v3.5.4) Updates `org.springframework.boot:spring-boot-starter-thymeleaf` from 3.5.3 to 3.5.4 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.5.3...v3.5.4) Updates `org.springframework.boot:spring-boot-starter-web` from 3.5.3 to 3.5.4 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.5.3...v3.5.4) Updates `org.springframework.boot:spring-boot-starter-data-rest` from 3.5.3 to 3.5.4 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.5.3...v3.5.4) Updates `org.springframework.boot:spring-boot-starter-security` from 3.5.3 to 3.5.4 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.5.3...v3.5.4) Updates `org.springframework.boot:spring-boot-starter-aop` from 3.5.3 to 3.5.4 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.5.3...v3.5.4) Updates `org.springframework.boot:spring-boot-starter-actuator` from 3.5.3 to 3.5.4 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.5.3...v3.5.4) Updates `org.springframework.boot:spring-boot-starter-log4j2` from 3.5.3 to 3.5.4 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.5.3...v3.5.4) Updates `org.springframework.boot:spring-boot-starter-tomcat` from 3.5.3 to 3.5.4 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.5.3...v3.5.4) Updates `org.springframework.security:spring-security-test` from 6.5.1 to 6.5.2 - [Release notes](https://github.com/spring-projects/spring-security/releases) - [Changelog](https://github.com/spring-projects/spring-security/blob/main/RELEASE.adoc) - [Commits](https://github.com/spring-projects/spring-security/compare/6.5.1...6.5.2) Updates `org.springframework.boot:spring-boot-maven-plugin` from 3.5.3 to 3.5.4 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.5.3...v3.5.4) Updates `org.springframework.boot:spring-boot-starter-cache` from 3.5.3 to 3.5.4 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.5.3...v3.5.4) Updates `org.springframework.boot:spring-boot-starter` from 3.5.3 to 3.5.4 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.5.3...v3.5.4) Updates `org.springframework.boot:spring-boot-starter-thymeleaf` from 3.5.3 to 3.5.4 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.5.3...v3.5.4) Updates `org.springframework.boot:spring-boot-starter-web` from 3.5.3 to 3.5.4 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.5.3...v3.5.4) Updates `org.springframework.boot:spring-boot-starter-data-rest` from 3.5.3 to 3.5.4 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.5.3...v3.5.4) Updates `org.springframework.boot:spring-boot-starter-security` from 3.5.3 to 3.5.4 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.5.3...v3.5.4) Updates `org.springframework.boot:spring-boot-starter-aop` from 3.5.3 to 3.5.4 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.5.3...v3.5.4) Updates `org.springframework.boot:spring-boot-starter-actuator` from 3.5.3 to 3.5.4 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.5.3...v3.5.4) Updates `org.springframework.boot:spring-boot-starter-log4j2` from 3.5.3 to 3.5.4 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.5.3...v3.5.4) --- updated-dependencies: - dependency-name: org.springframework:spring-orm dependency-version: 6.2.9 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-core dependency-version: 6.2.9 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-beans dependency-version: 6.2.9 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-aop dependency-version: 6.2.9 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-context dependency-version: 6.2.9 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-context-support dependency-version: 6.2.9 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-tx dependency-version: 6.2.9 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-jdbc dependency-version: 6.2.9 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-web dependency-version: 6.2.9 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-webmvc dependency-version: 6.2.9 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-expression dependency-version: 6.2.9 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-test dependency-version: 6.2.9 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-core dependency-version: 6.2.9 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-beans dependency-version: 6.2.9 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-aop dependency-version: 6.2.9 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-context dependency-version: 6.2.9 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-context-support dependency-version: 6.2.9 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-tx dependency-version: 6.2.9 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-jdbc dependency-version: 6.2.9 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-web dependency-version: 6.2.9 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-webmvc dependency-version: 6.2.9 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-expression dependency-version: 6.2.9 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-test dependency-version: 6.2.9 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter-test dependency-version: 3.5.4 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter-tomcat dependency-version: 3.5.4 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-maven-plugin dependency-version: 3.5.4 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter-cache dependency-version: 3.5.4 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter dependency-version: 3.5.4 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter-thymeleaf dependency-version: 3.5.4 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter-web dependency-version: 3.5.4 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter-data-rest dependency-version: 3.5.4 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter-security dependency-version: 3.5.4 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter-aop dependency-version: 3.5.4 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter-actuator dependency-version: 3.5.4 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter-log4j2 dependency-version: 3.5.4 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter-tomcat dependency-version: 3.5.4 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.security:spring-security-test dependency-version: 6.5.2 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-maven-plugin dependency-version: 3.5.4 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter-cache dependency-version: 3.5.4 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter dependency-version: 3.5.4 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter-thymeleaf dependency-version: 3.5.4 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter-web dependency-version: 3.5.4 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter-data-rest dependency-version: 3.5.4 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter-security dependency-version: 3.5.4 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter-aop dependency-version: 3.5.4 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter-actuator dependency-version: 3.5.4 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter-log4j2 dependency-version: 3.5.4 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring ... Signed-off-by: dependabot[bot] --- pom.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pom.xml b/pom.xml index 50c57d5b9476..99bdbf258e94 100644 --- a/pom.xml +++ b/pom.xml @@ -19,9 +19,9 @@ 17 - 6.2.8 - 3.5.3 - 6.5.1 + 6.2.9 + 3.5.4 + 6.5.2 6.4.8.Final 8.0.2.Final 42.7.7 From 68613d2c5ab1ee57e7ccd40c52523974585f1d8f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 28 Jul 2025 04:34:13 +0000 Subject: [PATCH 836/979] Bump the apache-commons group across 1 directory with 5 updates Bumps the apache-commons group with 5 updates in the / directory: | Package | From | To | | --- | --- | --- | | [commons-codec:commons-codec](https://github.com/apache/commons-codec) | `1.18.0` | `1.19.0` | | [commons-io:commons-io](https://github.com/apache/commons-io) | `2.19.0` | `2.20.0` | | org.apache.commons:commons-lang3 | `3.17.0` | `3.18.0` | | [org.apache.commons:commons-text](https://github.com/apache/commons-text) | `1.13.1` | `1.14.0` | | commons-validator:commons-validator | `1.9.0` | `1.10.0` | Updates `commons-codec:commons-codec` from 1.18.0 to 1.19.0 - [Changelog](https://github.com/apache/commons-codec/blob/master/RELEASE-NOTES.txt) - [Commits](https://github.com/apache/commons-codec/compare/rel/commons-codec-1.18.0...rel/commons-codec-1.19.0) Updates `commons-io:commons-io` from 2.19.0 to 2.20.0 - [Changelog](https://github.com/apache/commons-io/blob/master/RELEASE-NOTES.txt) - [Commits](https://github.com/apache/commons-io/compare/rel/commons-io-2.19.0...rel/commons-io-2.20.0) Updates `org.apache.commons:commons-lang3` from 3.17.0 to 3.18.0 Updates `org.apache.commons:commons-text` from 1.13.1 to 1.14.0 - [Changelog](https://github.com/apache/commons-text/blob/master/RELEASE-NOTES.txt) - [Commits](https://github.com/apache/commons-text/compare/rel/commons-text-1.13.1...rel/commons-text-1.14.0) Updates `commons-validator:commons-validator` from 1.9.0 to 1.10.0 --- updated-dependencies: - dependency-name: commons-codec:commons-codec dependency-version: 1.19.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: apache-commons - dependency-name: commons-io:commons-io dependency-version: 2.20.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: apache-commons - dependency-name: org.apache.commons:commons-lang3 dependency-version: 3.18.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: apache-commons - dependency-name: org.apache.commons:commons-text dependency-version: 1.14.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: apache-commons - dependency-name: commons-validator:commons-validator dependency-version: 1.10.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: apache-commons ... Signed-off-by: dependabot[bot] --- pom.xml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/pom.xml b/pom.xml index 50c57d5b9476..747d083f07b8 100644 --- a/pom.xml +++ b/pom.xml @@ -1476,7 +1476,7 @@ commons-codec commons-codec - 1.18.0 + 1.19.0 org.apache.commons @@ -1503,12 +1503,12 @@ commons-io commons-io - 2.19.0 + 2.20.0 org.apache.commons commons-lang3 - 3.17.0 + 3.18.0 @@ -1535,12 +1535,12 @@ org.apache.commons commons-text - 1.13.1 + 1.14.0 commons-validator commons-validator - 1.9.0 + 1.10.0 jakarta.activation From 6ef8ea0dab01e9760e3dd691d62f2a9119185981 Mon Sep 17 00:00:00 2001 From: Zeroday BYTE Date: Thu, 17 Jul 2025 02:06:29 +0700 Subject: [PATCH 837/979] Update DSpaceControlledVocabulary.java (cherry picked from commit e94f0a9cb3d43c1ed7af44683202737f598681d5) --- .../authority/DSpaceControlledVocabulary.java | 26 ++++++++++++++++--- 1 file changed, 22 insertions(+), 4 deletions(-) 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 444332df97d2..390efdb7c6b0 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 @@ -162,12 +162,21 @@ 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(); + 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 = 0; List choices = new ArrayList(); try { @@ -186,12 +195,21 @@ public Choices getMatches(String text, int start, int limit, String locale) { public Choices getBestMatch(String text, String locale) { init(); log.debug("Getting best 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(labelTemplate, textHierarchy[i].replaceAll("'", "'")); + xpathExpressionBuilder.append(String.format(labelTemplate, "$var" + i)); } + String xpathExpression = xpathExpressionBuilder.toString(); XPath xpath = XPathFactory.newInstance().newXPath(); + 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 = new ArrayList(); try { NodeList results = (NodeList) xpath.evaluate(xpathExpression, vocabulary, XPathConstants.NODESET); From b9a3ecf76484cdbed97529981516c98a3eb64106 Mon Sep 17 00:00:00 2001 From: "Mark H. Wood" Date: Thu, 24 Jul 2025 08:32:50 -0400 Subject: [PATCH 838/979] 'No match' should be test failure, not index error. (cherry picked from commit c781ba278093f7c80b8b68c601309fe90a9b85f0) --- .../content/authority/DSpaceControlledVocabularyTest.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/dspace-api/src/test/java/org/dspace/content/authority/DSpaceControlledVocabularyTest.java b/dspace-api/src/test/java/org/dspace/content/authority/DSpaceControlledVocabularyTest.java index 255b070e5eac..524c6407b7bd 100644 --- a/dspace-api/src/test/java/org/dspace/content/authority/DSpaceControlledVocabularyTest.java +++ b/dspace-api/src/test/java/org/dspace/content/authority/DSpaceControlledVocabularyTest.java @@ -8,6 +8,7 @@ package org.dspace.content.authority; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertNotNull; import java.io.IOException; @@ -86,6 +87,7 @@ public void testGetMatches() throws IOException, ClassNotFoundException { CoreServiceFactory.getInstance().getPluginService().getNamedPlugin(Class.forName(PLUGIN_INTERFACE), "farm"); assertNotNull(instance); Choices result = instance.getMatches(text, start, limit, locale); + assertNotEquals("At least one match expected", 0, result.values.length); assertEquals("north 40", result.values[0].value); } From 93c1d8f5aef8f56696307abc0dda89c339260364 Mon Sep 17 00:00:00 2001 From: "Mark H. Wood" Date: Thu, 24 Jul 2025 11:51:53 -0400 Subject: [PATCH 839/979] Variables in XPath expressions should not be quoted. Documentation cleanup. Clean up many IDE warnings. (cherry picked from commit 7deaf1cca5c35b721726be1fa7b8a0aa86647938) --- .../authority/DSpaceControlledVocabulary.java | 66 +++++++++---------- 1 file changed, 33 insertions(+), 33 deletions(-) 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 390efdb7c6b0..48d7c6999a96 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,23 +34,25 @@ * 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 @@ -58,12 +60,13 @@ public class DSpaceControlledVocabulary extends SelfNamedPlugin implements HierarchicalAuthority { - private static Logger log = org.apache.logging.log4j.LogManager.getLogger(DSpaceControlledVocabulary.class); + private static final Logger log = org.apache.logging.log4j.LogManager.getLogger(); 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']"; - protected static String idParentTemplate = "//node[@id = '%s']/parent::isComposedBy/parent::node"; + "'abcdefghijklmnopqrstuvwxyz'),%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 +109,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", "")); } @@ -177,8 +180,8 @@ public Choices getMatches(String text, int start, int limit, String locale) { } throw new IllegalArgumentException("Unexpected variable: " + varName); }); - int total = 0; - List choices = new ArrayList(); + int total; + List choices; try { NodeList results = (NodeList) xpath.evaluate(xpathExpression, vocabulary, XPathConstants.NODESET); total = results.getLength(); @@ -194,7 +197,7 @@ 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 + "'"); + 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++) { @@ -210,7 +213,7 @@ public Choices getBestMatch(String text, String locale) { } throw new IllegalArgumentException("Unexpected variable: " + varName); }); - List choices = new ArrayList(); + List choices; try { NodeList results = (NodeList) xpath.evaluate(xpathExpression, vocabulary, XPathConstants.NODESET); choices = getChoicesFromNodeList(results, 0, 1); @@ -282,15 +285,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; } @@ -302,7 +302,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; @@ -321,17 +321,17 @@ 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) { - extras.put("hasChildren", "true"); - } else { + if (childrenCurr.isEmpty()) { extras.put("hasChildren", "false"); + } else { + extras.put("hasChildren", "true"); } extras.put("id", authorityCurr); return extras; @@ -386,7 +386,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); @@ -409,7 +409,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; } @@ -436,7 +436,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); From 0f6f9b5fb79512f70e3291c7bc2c1e1aca8d14a1 Mon Sep 17 00:00:00 2001 From: "Mark H. Wood" Date: Thu, 24 Jul 2025 14:01:57 -0400 Subject: [PATCH 840/979] Correct some assumptions about what should be quoted. --- .../dspace/content/authority/DSpaceControlledVocabulary.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 48d7c6999a96..1bef8b389898 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 @@ -66,7 +66,7 @@ public class DSpaceControlledVocabulary extends SelfNamedPlugin implements Hiera 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 idParentTemplate = "//node[@id = '%s']/parent::isComposedBy/parent::node"; protected static String rootTemplate = "/node"; protected static String pluginNames[] = null; @@ -261,7 +261,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); } From e37a7ca74709565cfa5b117a9c8d667e349c12a1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 28 Jul 2025 19:41:53 +0000 Subject: [PATCH 841/979] Bump the build-tools group across 1 directory with 5 updates Bumps the build-tools group with 5 updates in the / directory: | Package | From | To | | --- | --- | --- | | [com.google.errorprone:error_prone_core](https://github.com/google/error-prone) | `2.38.0` | `2.39.0` | | [com.google.errorprone:error_prone_annotations](https://github.com/google/error-prone) | `2.38.0` | `2.39.0` | | [org.apache.maven.plugins:maven-enforcer-plugin](https://github.com/apache/maven-enforcer) | `3.5.0` | `3.6.0` | | [org.apache.maven.plugins:maven-gpg-plugin](https://github.com/apache/maven-gpg-plugin) | `3.2.7` | `3.2.8` | | [org.codehaus.mojo:license-maven-plugin](https://github.com/mojohaus/license-maven-plugin) | `2.5.0` | `2.6.0` | Updates `com.google.errorprone:error_prone_core` from 2.38.0 to 2.39.0 - [Release notes](https://github.com/google/error-prone/releases) - [Commits](https://github.com/google/error-prone/compare/v2.38.0...v2.39.0) Updates `com.google.errorprone:error_prone_annotations` from 2.38.0 to 2.39.0 - [Release notes](https://github.com/google/error-prone/releases) - [Commits](https://github.com/google/error-prone/compare/v2.38.0...v2.39.0) Updates `com.google.errorprone:error_prone_annotations` from 2.38.0 to 2.39.0 - [Release notes](https://github.com/google/error-prone/releases) - [Commits](https://github.com/google/error-prone/compare/v2.38.0...v2.39.0) Updates `org.apache.maven.plugins:maven-enforcer-plugin` from 3.5.0 to 3.6.0 - [Release notes](https://github.com/apache/maven-enforcer/releases) - [Commits](https://github.com/apache/maven-enforcer/compare/enforcer-3.5.0...enforcer-3.6.0) Updates `org.apache.maven.plugins:maven-gpg-plugin` from 3.2.7 to 3.2.8 - [Release notes](https://github.com/apache/maven-gpg-plugin/releases) - [Commits](https://github.com/apache/maven-gpg-plugin/compare/maven-gpg-plugin-3.2.7...maven-gpg-plugin-3.2.8) Updates `org.codehaus.mojo:license-maven-plugin` from 2.5.0 to 2.6.0 - [Release notes](https://github.com/mojohaus/license-maven-plugin/releases) - [Commits](https://github.com/mojohaus/license-maven-plugin/compare/2.5.0...2.6.0) --- updated-dependencies: - dependency-name: com.google.errorprone:error_prone_core dependency-version: 2.39.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: build-tools - dependency-name: com.google.errorprone:error_prone_annotations dependency-version: 2.39.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: build-tools - dependency-name: com.google.errorprone:error_prone_annotations dependency-version: 2.39.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: build-tools - dependency-name: org.apache.maven.plugins:maven-enforcer-plugin dependency-version: 3.6.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: build-tools - dependency-name: org.apache.maven.plugins:maven-gpg-plugin dependency-version: 3.2.8 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: build-tools - dependency-name: org.codehaus.mojo:license-maven-plugin dependency-version: 2.6.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: build-tools ... Signed-off-by: dependabot[bot] --- pom.xml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pom.xml b/pom.xml index be7a9dde64d7..434037771140 100644 --- a/pom.xml +++ b/pom.xml @@ -29,7 +29,7 @@ 8.11.4 3.10.8 - 2.38.0 + 2.41.0 2.19.2 2.19.2 @@ -89,7 +89,7 @@ org.apache.maven.plugins maven-enforcer-plugin - 3.5.0 + 3.6.1 enforce-java @@ -399,7 +399,7 @@ org.apache.maven.plugins maven-gpg-plugin - 3.2.7 + 3.2.8 @@ -693,7 +693,7 @@ org.codehaus.mojo license-maven-plugin - 2.5.0 + 2.6.0 false From 9eeec6ce95a71bf6f6c81e0760b8baa55f52d1ce Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Mon, 28 Jul 2025 15:29:32 -0500 Subject: [PATCH 842/979] Fix errorprone discovered issues. Many tests are missing @Test annotation. Once enabled, a broken test was found & fixed in WorkflowItemRestRepositoryIT. --- .../org/dspace/app/rest/CollectionRestRepositoryIT.java | 1 + .../org/dspace/app/rest/WorkflowItemRestRepositoryIT.java | 6 ++++-- .../org/dspace/app/rest/WorkspaceItemRestRepositoryIT.java | 3 +++ .../dspace/app/rest/authorization/RequestCopyFeatureIT.java | 5 +++++ 4 files changed, 13 insertions(+), 2 deletions(-) diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/CollectionRestRepositoryIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/CollectionRestRepositoryIT.java index 8ddcbd6ad26a..7a2f049b4e1d 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/CollectionRestRepositoryIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/CollectionRestRepositoryIT.java @@ -1473,6 +1473,7 @@ public void updateCollectionEpersonWithWriteRightsTest() throws Exception { authorizeService.removePoliciesActionFilter(context, eperson, Constants.WRITE); } + @Test public void patchCollectionMetadataAuthorized() throws Exception { runPatchMetadataTests(admin, 200); } diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/WorkflowItemRestRepositoryIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/WorkflowItemRestRepositoryIT.java index ef475353b93e..a16b20605be8 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/WorkflowItemRestRepositoryIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/WorkflowItemRestRepositoryIT.java @@ -913,6 +913,7 @@ public void unvalidCreateWorkflowItemTest() throws Exception { * * @throws Exception */ + @Test public void validationErrorsRequiredMetadataTest() throws Exception { context.turnOffAuthorisationSystem(); @@ -936,6 +937,7 @@ public void validationErrorsRequiredMetadataTest() throws Exception { XmlWorkflowItem witem = WorkflowItemBuilder.createWorkflowItem(context, col1) .withTitle("Workflow Item 1") .withIssueDate("2017-10-17") + .grantLicense() .build(); //4. a workflow item without the dateissued required field @@ -947,12 +949,12 @@ public void validationErrorsRequiredMetadataTest() throws Exception { String authToken = getAuthToken(eperson.getEmail(), password); - getClient(authToken).perform(get("/api/workflow/worfklowitems/" + witem.getID())) + getClient(authToken).perform(get("/api/workflow/workflowitems/" + witem.getID())) .andExpect(status().isOk()) .andExpect(jsonPath("$.errors").doesNotExist()) ; - getClient(authToken).perform(get("/api/workflow/worfklowitems/" + witemMissingFields.getID())) + getClient(authToken).perform(get("/api/workflow/workflowitems/" + witemMissingFields.getID())) .andExpect(status().isOk()) .andExpect(jsonPath("$.errors[?(@.message=='error.validation.required')]", Matchers.contains( diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/WorkspaceItemRestRepositoryIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/WorkspaceItemRestRepositoryIT.java index 542688ea2396..24a389dcaf69 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/WorkspaceItemRestRepositoryIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/WorkspaceItemRestRepositoryIT.java @@ -2618,6 +2618,7 @@ public void patchUpdateMetadataForbiddenTest() throws Exception { (witem, "Workspace Item 1", "2019-01-01", "ExtraEntry")))); } + @Test public void patchReplaceMetadataOnItemStillInSubmissionTest() throws Exception { context.turnOffAuthorisationSystem(); @@ -5929,6 +5930,7 @@ public void patchBitstreamWithAccessConditionLeaseMissingDate() throws Exception .andExpect(jsonPath("$.sections.upload.files[0].accessConditions", empty())); } + @Test public void deleteWorkspaceItemWithMinRelationshipsTest() throws Exception { context.turnOffAuthorisationSystem(); @@ -7983,6 +7985,7 @@ public void createItemWithoutEntityTypeIfCollectionHasBlankEntityType() throws E ))); } + @Test public void verifyBitstreamPolicyNotDuplicatedTest() throws Exception { context.turnOffAuthorisationSystem(); diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/authorization/RequestCopyFeatureIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/authorization/RequestCopyFeatureIT.java index 6fd5fad35c69..afae147515aa 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/authorization/RequestCopyFeatureIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/authorization/RequestCopyFeatureIT.java @@ -457,6 +457,7 @@ public void requestCopyOnBitstreamFromCollectionAsEperson() throws Exception { .andExpect(jsonPath("$._embedded").doesNotExist()); } + @Test public void requestACopyItemTypeLoggedAsAnonymous() throws Exception { configurationService.setProperty("request.item.type", "logged"); @@ -491,6 +492,7 @@ public void requestACopyItemTypeLoggedAsEperson() throws Exception { ); } + @Test public void requestACopyItemTypeEmptyAsAnonymous() throws Exception { configurationService.setProperty("request.item.type", ""); @@ -505,6 +507,7 @@ public void requestACopyItemTypeEmptyAsAnonymous() throws Exception { .andExpect(jsonPath("$._embedded").doesNotExist()); } + @Test public void requestACopyItemTypeEmptyAsEperson() throws Exception { configurationService.setProperty("request.item.type", ""); @@ -521,6 +524,7 @@ public void requestACopyItemTypeEmptyAsEperson() throws Exception { .andExpect(jsonPath("$._embedded").doesNotExist()); } + @Test public void requestACopyItemTypeBogusValueAsAnonymous() throws Exception { configurationService.setProperty("request.item.type", "invalid value"); @@ -535,6 +539,7 @@ public void requestACopyItemTypeBogusValueAsAnonymous() throws Exception { .andExpect(jsonPath("$._embedded").doesNotExist()); } + @Test public void requestACopyItemTypeBogusValueAsEperson() throws Exception { configurationService.setProperty("request.item.type", "invalid value"); From a4e1a0d57cce5d0263fe7deae04280a9696f954f Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Mon, 28 Jul 2025 15:31:09 -0500 Subject: [PATCH 843/979] Fix checkstyle indentation issue --- .../src/main/java/org/dspace/app/rest/LDNInboxController.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/LDNInboxController.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/LDNInboxController.java index f0ccbcf873c4..cff51520b3df 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/LDNInboxController.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/LDNInboxController.java @@ -120,7 +120,7 @@ private void validate(Context context, Notification notification, String sourceI } } catch (SQLException sqle) { throw new DSpaceBadRequestException("Notify Service [" + notification.getOrigin() - + "] unknown. LDN message can not be received."); + + "] unknown. LDN message can not be received."); } } if (configurationService.getBooleanProperty("ldn.notify.inbox.block-untrusted-ip", true)) { @@ -138,7 +138,7 @@ private void validate(Context context, Notification notification, String sourceI } } catch (SQLException sqle) { throw new DSpaceBadRequestException("Notify Service [" + notification.getOrigin() - + "] unknown. LDN message can not be received."); + + "] unknown. LDN message can not be received."); } } } From f32ea48d20a77d49dcd72495db6010c464730f97 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Fri, 25 Jul 2025 11:13:02 -0500 Subject: [PATCH 844/979] Add tests for backend logging to docker deployment tests --- .github/workflows/docker.yml | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 9d32cb119d41..aea0bb6478ff 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -220,6 +220,19 @@ jobs: result=$(wget -O- -q http://127.0.0.1:8080/server/api/core/collections) echo "$result" echo "$result" | grep -oE "\"Dog in Yard\"," + # Verify basic backend logging is working. + # 1. Access the top communities list. Verify that the "Before request" INFO statement is logged + # 2. Access an invalid endpoint (and ignore 404 response). Verify that a "status:404" WARN statement is logged + - name: Verify backend is logging properly + run: | + wget -O/dev/null -q http://127.0.0.1:8080/server/api/core/communities/search/top + logs=$(docker compose -f docker-compose.yml logs -n 5 dspace) + echo "$logs" + echo "$logs" | grep -o "Before request \[GET /server/api/core/communities/search/top\]" + wget -O/dev/null -q http://127.0.0.1:8080/server/api/does/not/exist || true + logs=$(docker compose -f docker-compose.yml logs -n 5 dspace) + echo "$logs" + echo "$logs" | grep -o "status:404 exception: The repository type does.not was not found" # Verify Handle Server can be stared and is working properly # 1. First generate the "[dspace]/handle-server" folder with the sitebndl.zip # 2. Start the Handle Server (and wait 20 seconds to let it start up) From e50317499541945cb7eaecedc1d19405cca35060 Mon Sep 17 00:00:00 2001 From: Kim Shepherd Date: Tue, 22 Jul 2025 17:27:13 +0200 Subject: [PATCH 845/979] Add simple log4j WebappLoggingIT test (cherry picked from commit 588c4ef4d259bccb927440058639a00ace67fb11) --- .../dspace/app/rest/test/WebappLoggingIT.java | 101 ++++++++++++++++++ 1 file changed, 101 insertions(+) create mode 100644 dspace-server-webapp/src/test/java/org/dspace/app/rest/test/WebappLoggingIT.java diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/test/WebappLoggingIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/test/WebappLoggingIT.java new file mode 100644 index 000000000000..fe746452c792 --- /dev/null +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/test/WebappLoggingIT.java @@ -0,0 +1,101 @@ +/** + * The contents of this file are subject to the license and copyright + * detailed in the LICENSE and NOTICE files at the root of the source + * tree and available online at + * + * http://www.dspace.org/license/ + */ +package org.dspace.app.rest.test; + +import static org.junit.Assert.assertTrue; + +import java.util.ArrayList; +import java.util.List; + +import org.apache.logging.log4j.Level; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.apache.logging.log4j.core.Appender; +import org.apache.logging.log4j.core.LogEvent; +import org.apache.logging.log4j.core.LoggerContext; +import org.apache.logging.log4j.core.appender.AbstractAppender; +import org.apache.logging.log4j.core.config.Configuration; +import org.apache.logging.log4j.core.config.LoggerConfig; +import org.apache.logging.log4j.core.config.Property; +import org.apache.logging.log4j.core.layout.PatternLayout; +import org.junit.After; +import org.junit.Test; + +/** + * Test basic log4j logging functionality, extending AbstractControllerIntegrationTest + * purely to make sure we are testing the *web application* and not just the kernel + * as that is where logging has broken in the past. + * + * @author Kim Shepherd + */ +public class WebappLoggingIT extends AbstractControllerIntegrationTest { + + private static final Logger logger = LogManager.getLogger(WebappLoggingIT.class); + private static final String APPENDER_NAME = "DSpaceTestAppender"; + + static class InMemoryAppender extends AbstractAppender { + private final List messages = new ArrayList<>(); + + protected InMemoryAppender(String name) { + super( + name, + null, + PatternLayout.newBuilder().withPattern("%m").build(), + false, + Property.EMPTY_ARRAY + ); + start(); + } + + @Override + public void append(LogEvent event) { + messages.add(event.getMessage().getFormattedMessage()); + } + + public List getMessages() { + return messages; + } + } + + @Test + public void testLogging() throws Exception { + LoggerContext context = (LoggerContext) LogManager.getContext(false); + Configuration config = context.getConfiguration(); + + InMemoryAppender appender = new InMemoryAppender(APPENDER_NAME); + config.addAppender(appender); + + LoggerConfig testLoggerConfig = new LoggerConfig(logger.getName(), Level.INFO, false); + testLoggerConfig.addAppender(appender, null, null); + config.addLogger(logger.getName(), testLoggerConfig); + context.updateLoggers(); + + logger.info("DSPACE TEST LOG ENTRY"); + + List messages = appender.getMessages(); + assertTrue(messages.stream().anyMatch(msg -> msg.contains("DSPACE TEST LOG ENTRY"))); + } + + @After + public void cleanupAppender() { + LoggerContext context = (LoggerContext) LogManager.getContext(false); + Configuration config = context.getConfiguration(); + + config.removeLogger(logger.getName()); + + Appender appender = config.getAppender(APPENDER_NAME); + if (appender != null) { + appender.stop(); + config.getAppenders().remove(APPENDER_NAME); + } + + context.updateLoggers(); +} + +} + From c8ee72c0f27e5b8fa6e821f577ca574e7897ed3f Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Mon, 24 Feb 2025 09:16:19 -0600 Subject: [PATCH 846/979] Ensure DSpace defaults to UTC time zone in all code / tests. This is necessary so that Spring / Hibernate don't auto-switch timezones when reading from database. --- .../app/bulkaccesscontrol/BulkAccessControl.java | 3 ++- .../src/main/java/org/dspace/util/DateMathParser.java | 3 ++- .../org/dspace/AbstractDSpaceIntegrationTest.java | 7 +++++-- .../src/test/java/org/dspace/AbstractDSpaceTest.java | 7 +++++-- .../main/java/org/dspace/app/rest/WebApplication.java | 11 +++++++++++ 5 files changed, 25 insertions(+), 6 deletions(-) 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 30f68efaf3cb..59e75059c94f 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; @@ -154,7 +155,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/util/DateMathParser.java b/dspace-api/src/main/java/org/dspace/util/DateMathParser.java index 9ff252e8ce3f..13f9216c9bdb 100644 --- a/dspace-api/src/main/java/org/dspace/util/DateMathParser.java +++ b/dspace-api/src/main/java/org/dspace/util/DateMathParser.java @@ -13,6 +13,7 @@ import java.time.LocalDateTime; import java.time.LocalTime; import java.time.ZoneId; +import java.time.ZoneOffset; import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatterBuilder; @@ -107,7 +108,7 @@ public class DateMathParser { private static final Logger LOG = LogManager.getLogger(); - public static final TimeZone UTC = TimeZone.getTimeZone("UTC"); + public static final TimeZone UTC = TimeZone.getTimeZone(ZoneOffset.UTC); /** * Default TimeZone for DateMath rounding (UTC) diff --git a/dspace-api/src/test/java/org/dspace/AbstractDSpaceIntegrationTest.java b/dspace-api/src/test/java/org/dspace/AbstractDSpaceIntegrationTest.java index 791fdbc66abc..2822cdcf6018 100644 --- a/dspace-api/src/test/java/org/dspace/AbstractDSpaceIntegrationTest.java +++ b/dspace-api/src/test/java/org/dspace/AbstractDSpaceIntegrationTest.java @@ -12,6 +12,7 @@ import java.io.IOException; import java.net.URL; import java.sql.SQLException; +import java.time.ZoneOffset; import java.util.Properties; import java.util.TimeZone; @@ -73,8 +74,10 @@ public static void initTestEnvironment() { //Stops System.exit(0) throws exception instead of exitting System.setSecurityManager(new NoExitSecurityManager()); - //set a standard time zone for the tests - TimeZone.setDefault(TimeZone.getTimeZone("Europe/Dublin")); + // All tests should assume UTC timezone by default (unless overridden in the test itself) + // This ensures that Spring doesn't attempt to change the timezone of dates that are read from the + // database (via Hibernate). We store all dates in the database as UTC. + TimeZone.setDefault(TimeZone.getTimeZone(ZoneOffset.UTC)); //load the properties of the tests testProps = new Properties(); diff --git a/dspace-api/src/test/java/org/dspace/AbstractDSpaceTest.java b/dspace-api/src/test/java/org/dspace/AbstractDSpaceTest.java index 136af83f076f..4452955a3b64 100644 --- a/dspace-api/src/test/java/org/dspace/AbstractDSpaceTest.java +++ b/dspace-api/src/test/java/org/dspace/AbstractDSpaceTest.java @@ -12,6 +12,7 @@ import java.io.IOException; import java.net.URL; import java.sql.SQLException; +import java.time.ZoneOffset; import java.util.Properties; import java.util.TimeZone; @@ -82,8 +83,10 @@ protected AbstractDSpaceTest() { } @BeforeClass public static void initKernel() { try { - //set a standard time zone for the tests - TimeZone.setDefault(TimeZone.getTimeZone("Europe/Dublin")); + // All tests should assume UTC timezone by default (unless overridden in the test itself) + // This ensures that Spring doesn't attempt to change the timezone of dates that are read from the + // database (via Hibernate). We store all dates in the database as UTC. + TimeZone.setDefault(TimeZone.getTimeZone(ZoneOffset.UTC)); //load the properties of the tests testProps = new Properties(); diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/WebApplication.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/WebApplication.java index 4c835d99183c..5494475b5241 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/WebApplication.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/WebApplication.java @@ -9,8 +9,11 @@ import java.io.IOException; import java.sql.SQLException; +import java.time.ZoneOffset; import java.util.List; +import java.util.TimeZone; +import jakarta.annotation.PostConstruct; import jakarta.servlet.Filter; import org.dspace.app.ldn.LDNQueueExtractor; import org.dspace.app.ldn.LDNQueueTimeoutChecker; @@ -246,4 +249,12 @@ public void addArgumentResolvers(@NonNull List ar } }; } + + @PostConstruct + public void setDefaultTimezone() { + // Set the default timezone in Spring Boot to UTC. + // This ensures that Spring Boot doesn't attempt to change the timezone of dates that are read from the + // database (via Hibernate). We store all dates in the database as UTC. + TimeZone.setDefault(TimeZone.getTimeZone(ZoneOffset.UTC)); + } } From ca8de732348fd90e751169b261bf3e2a8a91d9ee Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 30 Jul 2025 16:31:40 +0000 Subject: [PATCH 847/979] Bump the hibernate group with 3 updates Bumps the hibernate group with 3 updates: [org.hibernate.orm:hibernate-core](https://github.com/hibernate/hibernate-orm), [org.hibernate.orm:hibernate-jpamodelgen](https://github.com/hibernate/hibernate-orm) and [org.hibernate.orm:hibernate-jcache](https://github.com/hibernate/hibernate-orm). Updates `org.hibernate.orm:hibernate-core` from 6.4.8.Final to 6.4.10.Final - [Release notes](https://github.com/hibernate/hibernate-orm/releases) - [Changelog](https://github.com/hibernate/hibernate-orm/blob/6.4.10/changelog.txt) - [Commits](https://github.com/hibernate/hibernate-orm/compare/6.4.8...6.4.10) Updates `org.hibernate.orm:hibernate-jpamodelgen` from 6.4.8.Final to 6.4.10.Final - [Release notes](https://github.com/hibernate/hibernate-orm/releases) - [Changelog](https://github.com/hibernate/hibernate-orm/blob/6.4.10/changelog.txt) - [Commits](https://github.com/hibernate/hibernate-orm/compare/6.4.8...6.4.10) Updates `org.hibernate.orm:hibernate-jcache` from 6.4.8.Final to 6.4.10.Final - [Release notes](https://github.com/hibernate/hibernate-orm/releases) - [Changelog](https://github.com/hibernate/hibernate-orm/blob/6.4.10/changelog.txt) - [Commits](https://github.com/hibernate/hibernate-orm/compare/6.4.8...6.4.10) Updates `org.hibernate.orm:hibernate-jpamodelgen` from 6.4.8.Final to 6.4.10.Final - [Release notes](https://github.com/hibernate/hibernate-orm/releases) - [Changelog](https://github.com/hibernate/hibernate-orm/blob/6.4.10/changelog.txt) - [Commits](https://github.com/hibernate/hibernate-orm/compare/6.4.8...6.4.10) Updates `org.hibernate.orm:hibernate-jcache` from 6.4.8.Final to 6.4.10.Final - [Release notes](https://github.com/hibernate/hibernate-orm/releases) - [Changelog](https://github.com/hibernate/hibernate-orm/blob/6.4.10/changelog.txt) - [Commits](https://github.com/hibernate/hibernate-orm/compare/6.4.8...6.4.10) --- updated-dependencies: - dependency-name: org.hibernate.orm:hibernate-core dependency-version: 6.4.10.Final dependency-type: direct:production update-type: version-update:semver-patch dependency-group: hibernate - dependency-name: org.hibernate.orm:hibernate-jpamodelgen dependency-version: 6.4.10.Final dependency-type: direct:production update-type: version-update:semver-patch dependency-group: hibernate - dependency-name: org.hibernate.orm:hibernate-jcache dependency-version: 6.4.10.Final dependency-type: direct:production update-type: version-update:semver-patch dependency-group: hibernate - dependency-name: org.hibernate.orm:hibernate-jpamodelgen dependency-version: 6.4.10.Final dependency-type: direct:production update-type: version-update:semver-patch dependency-group: hibernate - dependency-name: org.hibernate.orm:hibernate-jcache dependency-version: 6.4.10.Final dependency-type: direct:production update-type: version-update:semver-patch dependency-group: hibernate ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 803d26cc9fa1..b6757304378e 100644 --- a/pom.xml +++ b/pom.xml @@ -22,7 +22,7 @@ 6.2.9 3.5.4 6.5.2 - 6.4.8.Final + 6.4.10.Final 8.0.2.Final 42.7.7 10.22.0 From 367b95f6242e31bdf35ad2df5db15993ad3362b0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 30 Jul 2025 18:40:29 +0000 Subject: [PATCH 848/979] Bump the apache-commons group with 2 updates Bumps the apache-commons group with 2 updates: [org.apache.commons:commons-compress](https://github.com/apache/commons-compress) and [org.apache.commons:commons-csv](https://github.com/apache/commons-csv). Updates `org.apache.commons:commons-compress` from 1.27.1 to 1.28.0 - [Changelog](https://github.com/apache/commons-compress/blob/master/RELEASE-NOTES.txt) - [Commits](https://github.com/apache/commons-compress/compare/rel/commons-compress-1.27.1...rel/commons-compress-1.28.0) Updates `org.apache.commons:commons-csv` from 1.14.0 to 1.14.1 - [Changelog](https://github.com/apache/commons-csv/blob/master/RELEASE-NOTES.txt) - [Commits](https://github.com/apache/commons-csv/compare/rel/commons-csv-1.14.0...rel/commons-csv-1.14.1) --- updated-dependencies: - dependency-name: org.apache.commons:commons-compress dependency-version: 1.28.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: apache-commons - dependency-name: org.apache.commons:commons-csv dependency-version: 1.14.1 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: apache-commons ... Signed-off-by: dependabot[bot] --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 803d26cc9fa1..74c2b00fea3d 100644 --- a/pom.xml +++ b/pom.xml @@ -1520,12 +1520,12 @@ org.apache.commons commons-compress - 1.27.1 + 1.28.0 org.apache.commons commons-csv - 1.14.0 + 1.14.1 org.apache.commons From 6145b74dda63442a05428c15ef43b8e27c33cbc5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 30 Jul 2025 18:41:42 +0000 Subject: [PATCH 849/979] Bump com.github.spotbugs:spotbugs-maven-plugin from 4.9.3.0 to 4.9.3.2 Bumps [com.github.spotbugs:spotbugs-maven-plugin](https://github.com/spotbugs/spotbugs-maven-plugin) from 4.9.3.0 to 4.9.3.2. - [Release notes](https://github.com/spotbugs/spotbugs-maven-plugin/releases) - [Commits](https://github.com/spotbugs/spotbugs-maven-plugin/compare/spotbugs-maven-plugin-4.9.3.0...spotbugs-maven-plugin-4.9.3.2) --- updated-dependencies: - dependency-name: com.github.spotbugs:spotbugs-maven-plugin dependency-version: 4.9.3.2 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 803d26cc9fa1..a5ae46d5b099 100644 --- a/pom.xml +++ b/pom.xml @@ -303,7 +303,7 @@ com.github.spotbugs spotbugs-maven-plugin - 4.9.3.0 + 4.9.3.2 Max Low From 79b39e2306e1fa02a918b9bc4de34a0d3001bced Mon Sep 17 00:00:00 2001 From: "David P. Steelman" Date: Thu, 13 Mar 2025 14:21:16 -0400 Subject: [PATCH 850/979] Fix checkpointing for bitstore migration Fixes the checkpointing for bitstore migration by actually committing the changes to the database. Replacing "dispatchEvents" with "commit" as testing has shown that this is necessary to ensure that the status of bitstreams is properly updated when the bitstore migration is interrupted. The provided integration tests fail if bitstreams successfully migrated before the interruption are not properly recorded in the database as being in the destination assetstore. (cherry picked from commit 3d7c45852d8b09fb15cddc1fb72c2a389e2b4d6a) --- .../bitstore/BitstreamStorageServiceImpl.java | 2 +- .../BitstreamStorageServiceImplIT.java | 262 ++++++++++++++++++ 2 files changed, 263 insertions(+), 1 deletion(-) create mode 100644 dspace-api/src/test/java/org/dspace/storage/bitstore/BitstreamStorageServiceImplIT.java diff --git a/dspace-api/src/main/java/org/dspace/storage/bitstore/BitstreamStorageServiceImpl.java b/dspace-api/src/main/java/org/dspace/storage/bitstore/BitstreamStorageServiceImpl.java index c89e5d7a54d1..9ebf3b29849c 100644 --- a/dspace-api/src/main/java/org/dspace/storage/bitstore/BitstreamStorageServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/storage/bitstore/BitstreamStorageServiceImpl.java @@ -423,7 +423,7 @@ public void migrate(Context context, Integer assetstoreSource, Integer assetstor //modulo if ((processedCounter % batchCommitSize) == 0) { log.info("Migration Commit Checkpoint: " + processedCounter); - context.dispatchEvents(); + context.commit(); } } diff --git a/dspace-api/src/test/java/org/dspace/storage/bitstore/BitstreamStorageServiceImplIT.java b/dspace-api/src/test/java/org/dspace/storage/bitstore/BitstreamStorageServiceImplIT.java new file mode 100644 index 000000000000..5b15cba1c4c1 --- /dev/null +++ b/dspace-api/src/test/java/org/dspace/storage/bitstore/BitstreamStorageServiceImplIT.java @@ -0,0 +1,262 @@ +/** + * 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.storage.bitstore; + +import static java.nio.charset.StandardCharsets.UTF_8; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.equalTo; +import static org.junit.Assert.fail; + +import java.io.IOException; +import java.io.InputStream; +import java.sql.SQLException; +import java.util.HashMap; +import java.util.Map; + +import org.apache.commons.io.IOUtils; +import org.dspace.AbstractIntegrationTestWithDatabase; +import org.dspace.authorize.AuthorizeException; +import org.dspace.builder.BitstreamBuilder; +import org.dspace.builder.CollectionBuilder; +import org.dspace.builder.CommunityBuilder; +import org.dspace.builder.ItemBuilder; +import org.dspace.content.Bitstream; +import org.dspace.content.Collection; +import org.dspace.content.Item; +import org.dspace.content.factory.ContentServiceFactory; +import org.dspace.content.service.BitstreamService; +import org.dspace.core.Context; +import org.dspace.storage.bitstore.factory.StorageServiceFactory; +import org.junit.After; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; + +public class BitstreamStorageServiceImplIT extends AbstractIntegrationTestWithDatabase { + private BitstreamService bitstreamService = ContentServiceFactory.getInstance().getBitstreamService(); + private BitstreamStorageServiceImpl bitstreamStorageService = + (BitstreamStorageServiceImpl) StorageServiceFactory.getInstance().getBitstreamStorageService(); + private Collection collection; + + private Map originalBitstores; + + private static final Integer SOURCE_STORE = 0; + private static final Integer DEST_STORE = 1; + + @Rule + public final TemporaryFolder tempStoreDir = new TemporaryFolder(); + + @Before + public void setup() throws Exception { + + context.turnOffAuthorisationSystem(); + + parentCommunity = CommunityBuilder.createCommunity(context) + .build(); + + collection = CollectionBuilder.createCollection(context, parentCommunity) + .build(); + + originalBitstores = bitstreamStorageService.getStores(); + Map stores = new HashMap<>(); + DSBitStoreService sourceStore = new DSBitStoreService(); + sourceStore.setBaseDir(tempStoreDir.newFolder("src")); + + stores.put(SOURCE_STORE, sourceStore); + bitstreamStorageService.setStores(stores); + + context.restoreAuthSystemState(); + } + + @After + public void cleanUp() throws IOException { + // Restore the bitstore storage stores + bitstreamStorageService.setStores(originalBitstores); + } + + /** + * Test batch commit checkpointing, using the default batch commit size of 1 + * + * @throws Exception if an exception occurs. + */ + @Test + public void testDefaultBatchCommitSize() throws Exception { + Context context = this.context; + + // Destination assetstore fails after two bitstreams have been migrated + DSBitStoreService destinationStore = new LimitedTempDSBitStoreService(tempStoreDir, 2); + Map stores = bitstreamStorageService.getStores(); + stores.put(DEST_STORE, destinationStore); + + // Create three bitstreams in the source assetstore + createBitstreams(context, 3); + + // Three bitstreams in source assetstore at the start + assertThat(bitstreamService.countByStoreNumber(context, SOURCE_STORE).intValue(), equalTo(3)); + + // No bitstreams in destination assetstore at the start + assertThat(bitstreamService.countByStoreNumber(context, DEST_STORE).intValue(), equalTo(0)); + + /// Commit any pending transaction to database + context.commit(); + + // Migrate bitstreams + context.turnOffAuthorisationSystem(); + + boolean deleteOld = false; + Integer batchCommitSize = 1; + try { + bitstreamStorageService.migrate( + context, SOURCE_STORE, DEST_STORE, deleteOld, + batchCommitSize + ); + fail("IOException should have been thrown"); + } catch (IOException ioe) { + // Rollback any pending transaction + context.rollback(); + } + + context.restoreAuthSystemState(); + + // One bitstream should still be in the source assetstore, due to the + // interrupted migration + assertThat(bitstreamService.countByStoreNumber(context, SOURCE_STORE).intValue(), equalTo(1)); + + // Two bitstreams should have migrated to the destination assetstore + assertThat(bitstreamService.countByStoreNumber(context, DEST_STORE).intValue(), equalTo(2)); + } + + /** + * Test batch commit checkpointing, using the default batch commit size of 3 + * + * @throws Exception if an exception occurs. + */ + @Test + public void testBatchCommitSizeThree() throws Exception { + Context context = this.context; + + // Destination assetstore fails after four bitstreams have been migrated + DSBitStoreService destinationStore = new LimitedTempDSBitStoreService(tempStoreDir, 4); + Map stores = bitstreamStorageService.getStores(); + stores.put(DEST_STORE, destinationStore); + + // Create five bitstreams in the source assetstore + createBitstreams(context, 5); + + // Five bitstreams in source assetstore at the start + assertThat(bitstreamService.countByStoreNumber(context, SOURCE_STORE).intValue(), equalTo(5)); + + // No bitstreams in destination assetstore at the start + assertThat(bitstreamService.countByStoreNumber(context, DEST_STORE).intValue(), equalTo(0)); + + // Commit any pending transaction to database + context.commit(); + + // Migrate bitstreams + context.turnOffAuthorisationSystem(); + + boolean deleteOld = false; + Integer batchCommitSize = 3; + try { + bitstreamStorageService.migrate( + context, SOURCE_STORE, DEST_STORE, deleteOld, + batchCommitSize + ); + fail("IOException should have been thrown"); + } catch (IOException ioe) { + // Rollback any pending transaction + context.rollback(); + } + + context.restoreAuthSystemState(); + + // Since the batch commit size is 3, only three bitstreams should be + // marked as migrated, so there should still be two bitstreams + // in the source assetstore, due to the interrupted migration + assertThat(bitstreamService.countByStoreNumber(context, SOURCE_STORE).intValue(), equalTo(2)); + + // Three bitstreams should have migrated to the destination assetstore + assertThat(bitstreamService.countByStoreNumber(context, DEST_STORE).intValue(), equalTo(3)); + } + + private void createBitstreams(Context context, int numBitstreams) + throws SQLException { + context.turnOffAuthorisationSystem(); + for (int i = 0; i < numBitstreams; i++) { + String content = "Test bitstream " + i; + createBitstream(content); + } + context.restoreAuthSystemState(); + context.commit(); + } + + private Bitstream createBitstream(String content) { + try { + return BitstreamBuilder + .createBitstream(context, createItem(), toInputStream(content)) + .build(); + } catch (SQLException | AuthorizeException | IOException e) { + throw new RuntimeException(e); + } + } + + private Item createItem() { + return ItemBuilder.createItem(context, collection) + .withTitle("Test item") + .build(); + } + + + private InputStream toInputStream(String content) { + return IOUtils.toInputStream(content, UTF_8); + } + + + /** + * DSBitStoreService variation that only allows a limited number of puts + * to the bit store before throwing an IOException, to test the + * error handling of the BitstreamStorageService.migrate() method. + */ + class LimitedTempDSBitStoreService extends DSBitStoreService { + // The number of put calls allowed before throwing an IOException + protected int maxPuts = Integer.MAX_VALUE; + + // The number of "put" method class seen so far. + protected int putCallCount = 0; + + /** + * Constructor. + * + * @param maxPuts the number of put calls to allow before throwing an + * IOException + */ + public LimitedTempDSBitStoreService(TemporaryFolder tempStoreDir, int maxPuts) throws IOException { + super(); + setBaseDir(tempStoreDir.newFolder()); + this.maxPuts = maxPuts; + } + + /** + * Store a stream of bits. + * + * After "maxPut" number of calls, this method throws an IOException. + * @param in The stream of bits to store + * @throws java.io.IOException If a problem occurs while storing the bits + */ + @Override + public void put(Bitstream bitstream, InputStream in) throws IOException { + putCallCount = putCallCount + 1; + if (putCallCount > maxPuts) { + throw new IOException("Max 'put' method calls exceeded"); + } else { + super.put(bitstream, in); + } + } + } +} From a9ff57dddc00d64cbd169cb1c1d570df296ee927 Mon Sep 17 00:00:00 2001 From: Kim Shepherd Date: Mon, 26 May 2025 10:15:40 +0200 Subject: [PATCH 851/979] [TLC-1117] Inherit custom, non-admin policies when creating new bundles Also shifted some resource policy methods from ItemService to AuthorizeService as they seemed better suited there. (cherry picked from commit 924678a092775124f56cc114126ea52a163550fa) --- .../authorize/AuthorizeServiceImpl.java | 117 +++++++++++++++++- .../authorize/service/AuthorizeService.java | 19 +++ .../org/dspace/content/ItemServiceImpl.java | 96 +------------- .../authorize/AuthorizeServiceTest.java | 97 +++++++++++++++ 4 files changed, 235 insertions(+), 94 deletions(-) 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 f2692cf394fc..60004bda9538 100644 --- a/dspace-api/src/main/java/org/dspace/authorize/AuthorizeServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/authorize/AuthorizeServiceImpl.java @@ -16,6 +16,7 @@ import java.util.Arrays; import java.util.Date; import java.util.List; +import java.util.Objects; import java.util.UUID; import org.apache.commons.collections4.CollectionUtils; @@ -48,6 +49,7 @@ import org.dspace.eperson.EPerson; import org.dspace.eperson.Group; import org.dspace.eperson.service.GroupService; +import org.dspace.services.ConfigurationService; import org.dspace.workflow.WorkflowItemService; import org.springframework.beans.factory.annotation.Autowired; @@ -84,6 +86,8 @@ public class AuthorizeServiceImpl implements AuthorizeService { protected WorkflowItemService workflowItemService; @Autowired(required = true) private SearchService searchService; + @Autowired(required = true) + private ConfigurationService configurationService; protected AuthorizeServiceImpl() { @@ -508,17 +512,26 @@ public List getPoliciesActionFilter(Context c, DSpaceObject o, return resourcePolicyService.find(c, o, actionID); } + @Override + public void inheritPolicies(Context c, DSpaceObject src, DSpaceObject dest) + throws SQLException, AuthorizeException { + inheritPolicies(c, src, dest, false); + } + @Override public void inheritPolicies(Context c, DSpaceObject src, - DSpaceObject dest) throws SQLException, AuthorizeException { + DSpaceObject dest, boolean includeCustom) throws SQLException, AuthorizeException { // find all policies for the source object List policies = getPolicies(c, src); - //Only inherit non-ADMIN policies (since ADMIN policies are automatically inherited) - //and non-custom policies as these are manually applied when appropriate + // Only inherit non-ADMIN policies (since ADMIN policies are automatically inherited) + // and non-custom policies (usually applied manually?) UNLESS specified otherwise with includCustom + // (for example, item.addBundle() will inherit custom policies to enforce access conditions) List nonAdminPolicies = new ArrayList<>(); for (ResourcePolicy rp : policies) { - if (rp.getAction() != Constants.ADMIN && !StringUtils.equals(rp.getRpType(), ResourcePolicy.TYPE_CUSTOM)) { + if (rp.getAction() != Constants.ADMIN && (!StringUtils.equals(rp.getRpType(), ResourcePolicy.TYPE_CUSTOM) + || (includeCustom && StringUtils.equals(rp.getRpType(), ResourcePolicy.TYPE_CUSTOM) + && isNotAlreadyACustomRPOfThisTypeOnDSO(c, dest)))) { nonAdminPolicies.add(rp); } } @@ -943,4 +956,100 @@ private String formatCustomQuery(String query) { return query + " AND "; } } + + /** + * Add the default policies, which have not been already added to the given DSpace object + * + * @param context The relevant DSpace Context. + * @param dso The DSpace Object to add policies to + * @param defaultCollectionPolicies list of policies + * @throws SQLException An exception that provides information on a database access error or other errors. + * @throws AuthorizeException Exception indicating the current user of the context does not have permission + * to perform a particular action. + */ + @Override + public void addDefaultPoliciesNotInPlace(Context context, DSpaceObject dso, + List defaultCollectionPolicies) throws SQLException, AuthorizeException { + boolean appendMode = configurationService + .getBooleanProperty("core.authorization.installitem.inheritance-read.append-mode", false); + for (ResourcePolicy defaultPolicy : defaultCollectionPolicies) { + if (!isAnIdenticalPolicyAlreadyInPlace(context, dso, defaultPolicy.getGroup(), Constants.READ, + defaultPolicy.getID()) && + (!appendMode && isNotAlreadyACustomRPOfThisTypeOnDSO(context, dso) || + appendMode && shouldBeAppended(context, dso, defaultPolicy))) { + ResourcePolicy newPolicy = resourcePolicyService.clone(context, defaultPolicy); + newPolicy.setdSpaceObject(dso); + newPolicy.setAction(Constants.READ); + newPolicy.setRpType(ResourcePolicy.TYPE_INHERITED); + resourcePolicyService.update(context, newPolicy); + } + } + } + + /** + * Add a list of custom policies if there are already NO custom policies in place + * + */ + @Override + public void addCustomPoliciesNotInPlace(Context context, DSpaceObject dso, List customPolicies) + throws SQLException, AuthorizeException { + boolean customPoliciesAlreadyInPlace = + findPoliciesByDSOAndType(context, dso, ResourcePolicy.TYPE_CUSTOM).size() > 0; + if (!customPoliciesAlreadyInPlace) { + addPolicies(context, customPolicies, dso); + } + } + + /** + * Check whether or not there is already an RP on the given dso, which has actionId={@link Constants.READ} and + * resourceTypeId={@link ResourcePolicy.TYPE_CUSTOM} + * + * @param context DSpace context + * @param dso DSpace object to check for custom read RP + * @return True if there is no RP on the item with custom read RP, otherwise false + * @throws SQLException If something goes wrong retrieving the RP on the DSO + */ + private boolean isNotAlreadyACustomRPOfThisTypeOnDSO(Context context, DSpaceObject dso) throws SQLException { + return isNotAlreadyACustomRPOfThisTypeOnDSO(context, dso, Constants.READ); + } + + private boolean isNotAlreadyACustomRPOfThisTypeOnDSO(Context context, DSpaceObject dso, int action) + throws SQLException { + List rps = resourcePolicyService.find(context, dso, action); + for (ResourcePolicy rp : rps) { + if (rp.getRpType() != null && rp.getRpType().equals(ResourcePolicy.TYPE_CUSTOM)) { + return false; + } + } + return true; + } + + /** + * Check if the provided default policy should be appended or not to the final + * item. If an item has at least one custom READ policy any anonymous READ + * policy with empty start/end date should be skipped + * + * @param context DSpace context + * @param dso DSpace object to check for custom read RP + * @param defaultPolicy The policy to check + * @return + * @throws SQLException If something goes wrong retrieving the RP on the DSO + */ + private boolean shouldBeAppended(Context context, DSpaceObject dso, ResourcePolicy defaultPolicy) + throws SQLException { + boolean hasCustomPolicy = resourcePolicyService.find(context, dso, Constants.READ) + .stream() + .filter(rp -> (Objects.nonNull(rp.getRpType()) && + Objects.equals(rp.getRpType(), ResourcePolicy.TYPE_CUSTOM))) + .findFirst() + .isPresent(); + + boolean isAnonymousGroup = Objects.nonNull(defaultPolicy.getGroup()) + && StringUtils.equals(defaultPolicy.getGroup().getName(), Group.ANONYMOUS); + + boolean datesAreNull = Objects.isNull(defaultPolicy.getStartDate()) + && Objects.isNull(defaultPolicy.getEndDate()); + + return !(hasCustomPolicy && isAnonymousGroup && datesAreNull); + } } 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 e0a94833d76c..0e542d98f6d4 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 @@ -322,6 +322,19 @@ public void addPolicy(Context c, DSpaceObject o, int actionID, Group g, String t */ public List getPoliciesActionFilterExceptRpType(Context c, DSpaceObject o, int actionID, String rpType) throws SQLException; + /** + * Add policies to an object to match those from a previous object + * + * @param c context + * @param src source of policies + * @param dest destination of inherited policies + * @param includeCustom whether TYPE_CUSTOM policies should be inherited + * @throws SQLException if there's a database problem + * @throws AuthorizeException if the current user is not authorized to add these policies + */ + public void inheritPolicies(Context c, DSpaceObject src, DSpaceObject dest, boolean includeCustom) + throws SQLException, AuthorizeException; + /** * Add policies to an object to match those from a previous object * @@ -604,4 +617,10 @@ long countAdminAuthorizedCollection(Context context, String query) public void replaceAllPolicies(Context context, DSpaceObject source, DSpaceObject dest) throws SQLException, AuthorizeException; + public void addDefaultPoliciesNotInPlace(Context context, DSpaceObject dso, + List defaultCollectionPolicies) throws SQLException, AuthorizeException; + + public void addCustomPoliciesNotInPlace(Context context, DSpaceObject dso, + List defaultCollectionPolicies) throws SQLException, AuthorizeException; + } 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 157b891486f0..99dc9459d84f 100644 --- a/dspace-api/src/main/java/org/dspace/content/ItemServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/content/ItemServiceImpl.java @@ -480,7 +480,7 @@ public void addBundle(Context context, Item item, Bundle bundle) throws SQLExcep // now add authorization policies from owning item // hmm, not very "multiple-inclusion" friendly - authorizeService.inheritPolicies(context, item, bundle); + authorizeService.inheritPolicies(context, item, bundle, true); // Add the bundle to in-memory list item.addBundle(bundle); @@ -1046,8 +1046,8 @@ public void adjustBundleBitstreamPolicies(Context context, Item item, Collection // if come from InstallItem: remove all submission/workflow policies authorizeService.removeAllPoliciesByDSOAndType(context, mybundle, ResourcePolicy.TYPE_SUBMISSION); authorizeService.removeAllPoliciesByDSOAndType(context, mybundle, ResourcePolicy.TYPE_WORKFLOW); - addCustomPoliciesNotInPlace(context, mybundle, defaultItemPolicies); - addDefaultPoliciesNotInPlace(context, mybundle, defaultCollectionBundlePolicies); + authorizeService.addCustomPoliciesNotInPlace(context, mybundle, defaultItemPolicies); + authorizeService.addDefaultPoliciesNotInPlace(context, mybundle, defaultCollectionBundlePolicies); for (Bitstream bitstream : mybundle.getBitstreams()) { // If collection has default READ policies, remove the bundle's READ policies. @@ -1093,8 +1093,8 @@ private void removeAllPoliciesAndAddDefault(Context context, Bitstream bitstream throws SQLException, AuthorizeException { authorizeService.removeAllPoliciesByDSOAndType(context, bitstream, ResourcePolicy.TYPE_SUBMISSION); authorizeService.removeAllPoliciesByDSOAndType(context, bitstream, ResourcePolicy.TYPE_WORKFLOW); - addCustomPoliciesNotInPlace(context, bitstream, defaultItemPolicies); - addDefaultPoliciesNotInPlace(context, bitstream, defaultCollectionPolicies); + authorizeService.addCustomPoliciesNotInPlace(context, bitstream, defaultItemPolicies); + authorizeService.addDefaultPoliciesNotInPlace(context, bitstream, defaultCollectionPolicies); } @Override @@ -1132,7 +1132,7 @@ public void adjustItemPolicies(Context context, Item item, Collection collection authorizeService.removeAllPoliciesByDSOAndType(context, item, ResourcePolicy.TYPE_WORKFLOW); // add default policies only if not already in place - addDefaultPoliciesNotInPlace(context, item, defaultCollectionPolicies); + authorizeService.addDefaultPoliciesNotInPlace(context, item, defaultCollectionPolicies); } finally { context.restoreAuthSystemState(); } @@ -1322,91 +1322,7 @@ public boolean isInProgressSubmission(Context context, Item item) throws SQLExce */ - /** - * Add the default policies, which have not been already added to the given DSpace object - * - * @param context The relevant DSpace Context. - * @param dso The DSpace Object to add policies to - * @param defaultCollectionPolicies list of policies - * @throws SQLException An exception that provides information on a database access error or other errors. - * @throws AuthorizeException Exception indicating the current user of the context does not have permission - * to perform a particular action. - */ - protected void addDefaultPoliciesNotInPlace(Context context, DSpaceObject dso, - List defaultCollectionPolicies) throws SQLException, AuthorizeException { - boolean appendMode = configurationService - .getBooleanProperty("core.authorization.installitem.inheritance-read.append-mode", false); - for (ResourcePolicy defaultPolicy : defaultCollectionPolicies) { - if (!authorizeService - .isAnIdenticalPolicyAlreadyInPlace(context, dso, defaultPolicy.getGroup(), Constants.READ, - defaultPolicy.getID()) && - (!appendMode && isNotAlreadyACustomRPOfThisTypeOnDSO(context, dso) || - appendMode && shouldBeAppended(context, dso, defaultPolicy))) { - ResourcePolicy newPolicy = resourcePolicyService.clone(context, defaultPolicy); - newPolicy.setdSpaceObject(dso); - newPolicy.setAction(Constants.READ); - newPolicy.setRpType(ResourcePolicy.TYPE_INHERITED); - resourcePolicyService.update(context, newPolicy); - } - } - } - private void addCustomPoliciesNotInPlace(Context context, DSpaceObject dso, List customPolicies) - throws SQLException, AuthorizeException { - boolean customPoliciesAlreadyInPlace = authorizeService - .findPoliciesByDSOAndType(context, dso, ResourcePolicy.TYPE_CUSTOM).size() > 0; - if (!customPoliciesAlreadyInPlace) { - authorizeService.addPolicies(context, customPolicies, dso); - } - } - - /** - * Check whether or not there is already an RP on the given dso, which has actionId={@link Constants.READ} and - * resourceTypeId={@link ResourcePolicy.TYPE_CUSTOM} - * - * @param context DSpace context - * @param dso DSpace object to check for custom read RP - * @return True if there is no RP on the item with custom read RP, otherwise false - * @throws SQLException If something goes wrong retrieving the RP on the DSO - */ - private boolean isNotAlreadyACustomRPOfThisTypeOnDSO(Context context, DSpaceObject dso) throws SQLException { - List readRPs = resourcePolicyService.find(context, dso, Constants.READ); - for (ResourcePolicy readRP : readRPs) { - if (readRP.getRpType() != null && readRP.getRpType().equals(ResourcePolicy.TYPE_CUSTOM)) { - return false; - } - } - return true; - } - - /** - * Check if the provided default policy should be appended or not to the final - * item. If an item has at least one custom READ policy any anonymous READ - * policy with empty start/end date should be skipped - * - * @param context DSpace context - * @param dso DSpace object to check for custom read RP - * @param defaultPolicy The policy to check - * @return - * @throws SQLException If something goes wrong retrieving the RP on the DSO - */ - private boolean shouldBeAppended(Context context, DSpaceObject dso, ResourcePolicy defaultPolicy) - throws SQLException { - boolean hasCustomPolicy = resourcePolicyService.find(context, dso, Constants.READ) - .stream() - .filter(rp -> (Objects.nonNull(rp.getRpType()) && - Objects.equals(rp.getRpType(), ResourcePolicy.TYPE_CUSTOM))) - .findFirst() - .isPresent(); - - boolean isAnonimousGroup = Objects.nonNull(defaultPolicy.getGroup()) - && StringUtils.equals(defaultPolicy.getGroup().getName(), Group.ANONYMOUS); - - boolean datesAreNull = Objects.isNull(defaultPolicy.getStartDate()) - && Objects.isNull(defaultPolicy.getEndDate()); - - return !(hasCustomPolicy && isAnonimousGroup && datesAreNull); - } /** * Returns an iterator of Items possessing the passed metadata field, or only diff --git a/dspace-api/src/test/java/org/dspace/authorize/AuthorizeServiceTest.java b/dspace-api/src/test/java/org/dspace/authorize/AuthorizeServiceTest.java index 70eaa2a0b909..e8bb428db53a 100644 --- a/dspace-api/src/test/java/org/dspace/authorize/AuthorizeServiceTest.java +++ b/dspace-api/src/test/java/org/dspace/authorize/AuthorizeServiceTest.java @@ -9,14 +9,24 @@ package org.dspace.authorize; import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; import org.dspace.AbstractUnitTest; import org.dspace.authorize.factory.AuthorizeServiceFactory; import org.dspace.authorize.service.ResourcePolicyService; +import org.dspace.content.Bundle; +import org.dspace.content.Collection; import org.dspace.content.Community; +import org.dspace.content.Item; +import org.dspace.content.WorkspaceItem; import org.dspace.content.factory.ContentServiceFactory; +import org.dspace.content.service.BundleService; import org.dspace.content.service.CollectionService; import org.dspace.content.service.CommunityService; +import org.dspace.content.service.InstallItemService; +import org.dspace.content.service.ItemService; +import org.dspace.content.service.WorkspaceItemService; import org.dspace.core.Constants; import org.dspace.eperson.EPerson; import org.dspace.eperson.Group; @@ -38,6 +48,10 @@ public class AuthorizeServiceTest extends AbstractUnitTest { .getResourcePolicyService(); protected CommunityService communityService = ContentServiceFactory.getInstance().getCommunityService(); protected CollectionService collectionService = ContentServiceFactory.getInstance().getCollectionService(); + protected ItemService itemService = ContentServiceFactory.getInstance().getItemService(); + protected BundleService bundleService = ContentServiceFactory.getInstance().getBundleService(); + protected WorkspaceItemService workspaceItemService = ContentServiceFactory.getInstance().getWorkspaceItemService(); + protected InstallItemService installItemService = ContentServiceFactory.getInstance().getInstallItemService(); public AuthorizeServiceTest() { } @@ -127,6 +141,89 @@ public void testauthorizeMethodRespectSpecialGroups() { throw new AssertionError(ex); } } + + /** + * When a bundle is created it should inherit custom policies (deduped) + * from the item, as otherwise bitstream bundles created via filter-media etc. + * will be created without READ policies + */ + @Test + public void testInheritanceOfCustomPolicies() { + try { + context.turnOffAuthorisationSystem(); + Community community = communityService.create(null, context); + Collection collection = collectionService.create(context, community); + WorkspaceItem wsItem = workspaceItemService.create(context, collection, false); + Item item = installItemService.installItem(context, wsItem); + // Simulate access conditions adding READ policy to the item + ResourcePolicy itemCustomRead = resourcePolicyService.create(context, eperson, null); + itemCustomRead.setAction(Constants.READ); + itemCustomRead.setRpType(ResourcePolicy.TYPE_CUSTOM); + // Simulate a random ADMIN action policy that might have been added manually + ResourcePolicy itemCustomAdmin = resourcePolicyService.create(context, eperson, null); + itemCustomAdmin.setAction(Constants.ADMIN); + itemCustomAdmin.setRpType(ResourcePolicy.TYPE_CUSTOM); + List customPolicies = new ArrayList<>(); + customPolicies.add(itemCustomRead); + customPolicies.add(itemCustomAdmin); + authorizeService.addPolicies(context, customPolicies, item); + // Create a bundle, this should call inheritPolicies via itemService.addBundle + Bundle bundle = bundleService.create(context, item, "THUMBNAIL"); + List newPolicies = authorizeService + .findPoliciesByDSOAndType(context, bundle, ResourcePolicy.TYPE_CUSTOM); + Assert.assertEquals("Bundle should inherit custom policy from item", 1, newPolicies.size()); + Assert.assertNotEquals("Bundle should ONLY inherit non-admin custom policy from item", + Constants.ADMIN, newPolicies.get(0).getAction()); + } catch (Exception e) { + throw new RuntimeException(e); + } finally { + context.restoreAuthSystemState(); + } + } + + /** + * For other DSOs (which pass false) and for a bundle explicitly calling + * inheritPolicies(..., false), the TYPE_CUSTOM policies should not be inherited + * but other non-admin policies should be inherited as usual + */ + @Test + public void testNonInheritanceOfCustomPolicies() { + try { + context.turnOffAuthorisationSystem(); + Community community = communityService.create(null, context); + Collection collection = collectionService.create(context, community); + WorkspaceItem wsItem = workspaceItemService.create(context, collection, false); + Item item = installItemService.installItem(context, wsItem); + Bundle bundle = bundleService.create(context, item, "THUMBNAIL"); + // Simulate a custom READ policy added by access conditions step + ResourcePolicy itemCustomRead = resourcePolicyService.create(context, eperson, null); + itemCustomRead.setAction(Constants.READ); + itemCustomRead.setRpType(ResourcePolicy.TYPE_CUSTOM); + // Simulate an ordinary default read item policy inherited from collection + ResourcePolicy itemDefaultRead = resourcePolicyService.create(context, eperson, null); + itemDefaultRead.setAction(Constants.READ); + itemDefaultRead.setRpType(ResourcePolicy.TYPE_INHERITED); + List customPolicies = new ArrayList<>(); + customPolicies.add(itemCustomRead); + customPolicies.add(itemDefaultRead); + authorizeService.addPolicies(context, customPolicies, item); + // Now, inherit policies for bundle with includeCustom=false (which is how other DSOs behave) + authorizeService.inheritPolicies(context, item, bundle, false); + List newCustomPolicies = authorizeService + .findPoliciesByDSOAndType(context, bundle, ResourcePolicy.TYPE_CUSTOM); + List newInheritedPolicies = authorizeService + .findPoliciesByDSOAndType(context, bundle, ResourcePolicy.TYPE_INHERITED); + Assert.assertEquals("Bundle should not inherit custom policy from item, if false passed", + 0, newCustomPolicies.size()); + Assert.assertEquals("Bundle should inherit non-custom, non-admin policies as usual", + ResourcePolicy.TYPE_INHERITED, newInheritedPolicies.get(0).getRpType()); + } catch (Exception e) { + throw new RuntimeException(e); + } finally { + context.restoreAuthSystemState(); + } + } + // // @Test // public void testIsCollectionAdmin() throws SQLException, AuthorizeException, IOException { From 53538cd3e8890f9f759535aa3d525649b5ff0a15 Mon Sep 17 00:00:00 2001 From: Kim Shepherd Date: Wed, 28 May 2025 17:26:13 +0200 Subject: [PATCH 852/979] [TLC-1097] ORCID external identifier sync fix Handle SELF and PART_OF identifiers properly based on configuration, work type, and identifier type (cherry picked from commit ad82b31c7466e71e5a3e8da1c5732fd3cf25f063) --- .../orcid/model/OrcidWorkFieldMapping.java | 10 ++ .../model/factory/impl/OrcidWorkFactory.java | 67 +++++++------ .../java/org/dspace/builder/ItemBuilder.java | 8 ++ .../service/OrcidEntityFactoryServiceIT.java | 99 ++++++++++++++++++- ...space-to-orcid-publication-type.properties | 3 +- dspace/config/modules/orcid.cfg | 10 +- dspace/config/spring/api/orcid-services.xml | 57 +++++++---- 7 files changed, 201 insertions(+), 53 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/orcid/model/OrcidWorkFieldMapping.java b/dspace-api/src/main/java/org/dspace/orcid/model/OrcidWorkFieldMapping.java index 781a9dcbd904..faefe798e92b 100644 --- a/dspace-api/src/main/java/org/dspace/orcid/model/OrcidWorkFieldMapping.java +++ b/dspace-api/src/main/java/org/dspace/orcid/model/OrcidWorkFieldMapping.java @@ -39,6 +39,7 @@ public class OrcidWorkFieldMapping { * The metadata fields related to the work external identifiers. */ private Map externalIdentifierFields = new HashMap<>(); + private Map> externalIdentifierPartOfMap = new HashMap<>(); /** * The metadata field related to the work publication date. @@ -129,6 +130,15 @@ public void setExternalIdentifierFields(String externalIdentifierFields) { this.externalIdentifierFields = parseConfigurations(externalIdentifierFields); } + public Map> getExternalIdentifierPartOfMap() { + return this.externalIdentifierPartOfMap; + } + + public void setExternalIdentifierPartOfMap( + HashMap> externalIdentifierPartOfMap) { + this.externalIdentifierPartOfMap = externalIdentifierPartOfMap; + } + public String getPublicationDateField() { return publicationDateField; } diff --git a/dspace-api/src/main/java/org/dspace/orcid/model/factory/impl/OrcidWorkFactory.java b/dspace-api/src/main/java/org/dspace/orcid/model/factory/impl/OrcidWorkFactory.java index 47619b3c1d63..66fd1a717d7b 100644 --- a/dspace-api/src/main/java/org/dspace/orcid/model/factory/impl/OrcidWorkFactory.java +++ b/dspace-api/src/main/java/org/dspace/orcid/model/factory/impl/OrcidWorkFactory.java @@ -9,6 +9,7 @@ import static org.apache.commons.lang3.StringUtils.isBlank; import static org.apache.commons.lang3.StringUtils.isNotBlank; +import static org.orcid.jaxb.model.common.Relationship.PART_OF; import static org.orcid.jaxb.model.common.Relationship.SELF; import java.util.ArrayList; @@ -73,12 +74,12 @@ public OrcidEntityType getEntityType() { @Override public Activity createOrcidObject(Context context, Item item) { Work work = new Work(); + work.setWorkType(getWorkType(context, item)); work.setJournalTitle(getJournalTitle(context, item)); work.setWorkContributors(getWorkContributors(context, item)); work.setWorkTitle(getWorkTitle(context, item)); work.setPublicationDate(getPublicationDate(context, item)); - work.setWorkExternalIdentifiers(getWorkExternalIds(context, item)); - work.setWorkType(getWorkType(context, item)); + work.setWorkExternalIdentifiers(getWorkExternalIds(context, item, work)); work.setShortDescription(getShortDescription(context, item)); work.setLanguageCode(getLanguageCode(context, item)); work.setUrl(getUrl(context, item)); @@ -148,58 +149,62 @@ private PublicationDate getPublicationDate(Context context, Item item) { .orElse(null); } - /** - * Creates an instance of ExternalIDs from the metadata values of the given - * item, using the orcid.mapping.funding.external-ids configuration. - */ - private ExternalIDs getWorkExternalIds(Context context, Item item) { - ExternalIDs externalIdentifiers = new ExternalIDs(); - externalIdentifiers.getExternalIdentifier().addAll(getWorkSelfExternalIds(context, item)); - return externalIdentifiers; + private ExternalIDs getWorkExternalIds(Context context, Item item, Work work) { + ExternalIDs externalIDs = new ExternalIDs(); + externalIDs.getExternalIdentifier().addAll(getWorkExternalIdList(context, item, work)); + return externalIDs; } /** * Creates a list of ExternalID, one for orcid.mapping.funding.external-ids * value, taking the values from the given item. */ - private List getWorkSelfExternalIds(Context context, Item item) { + private List getWorkExternalIdList(Context context, Item item, Work work) { - List selfExternalIds = new ArrayList<>(); + List externalIds = new ArrayList<>(); Map externalIdentifierFields = fieldMapping.getExternalIdentifierFields(); if (externalIdentifierFields.containsKey(SIMPLE_HANDLE_PLACEHOLDER)) { String handleType = externalIdentifierFields.get(SIMPLE_HANDLE_PLACEHOLDER); - selfExternalIds.add(getExternalId(handleType, item.getHandle(), SELF)); + ExternalID handle = new ExternalID(); + handle.setType(handleType); + handle.setValue(item.getHandle()); + handle.setRelationship(SELF); + externalIds.add(handle); } + // Resolve work type, used to determine identifier relationship type + // For version / funding relationships, we might want to use more complex + // business rules than just "work and id type" + final String workType = (work != null && work.getWorkType() != null) ? + work.getWorkType().value() : WorkType.OTHER.value(); getMetadataValues(context, item, externalIdentifierFields.keySet()).stream() - .map(this::getSelfExternalId) - .forEach(selfExternalIds::add); + .map(metadataValue -> this.getExternalId(metadataValue, workType)) + .forEach(externalIds::add); - return selfExternalIds; - } - - /** - * Creates an instance of ExternalID taking the value from the given - * metadataValue. The type of the ExternalID is calculated using the - * orcid.mapping.funding.external-ids configuration. The relationship of the - * ExternalID is SELF. - */ - private ExternalID getSelfExternalId(MetadataValue metadataValue) { - Map externalIdentifierFields = fieldMapping.getExternalIdentifierFields(); - String metadataField = metadataValue.getMetadataField().toString('.'); - return getExternalId(externalIdentifierFields.get(metadataField), metadataValue.getValue(), SELF); + return externalIds; } /** * Creates an instance of ExternalID with the given type, value and * relationship. */ - private ExternalID getExternalId(String type, String value, Relationship relationship) { + private ExternalID getExternalId(MetadataValue metadataValue, String workType) { + Map externalIdentifierFields = fieldMapping.getExternalIdentifierFields(); + Map> externalIdentifierPartOfMap = fieldMapping.getExternalIdentifierPartOfMap(); + String metadataField = metadataValue.getMetadataField().toString('.'); + String identifierType = externalIdentifierFields.get(metadataField); + // Default relationship type is SELF, configuration can + // override to PART_OF based on identifier and work type + Relationship relationship = SELF; + if (externalIdentifierPartOfMap.containsKey(identifierType) + && externalIdentifierPartOfMap.get(identifierType).contains(workType)) { + relationship = PART_OF; + } ExternalID externalID = new ExternalID(); - externalID.setType(type); - externalID.setValue(value); + externalID.setType(identifierType); + externalID.setValue(metadataValue.getValue()); externalID.setRelationship(relationship); return externalID; } diff --git a/dspace-api/src/test/java/org/dspace/builder/ItemBuilder.java b/dspace-api/src/test/java/org/dspace/builder/ItemBuilder.java index 5e9545fcafbd..d3e4d0ff8f78 100644 --- a/dspace-api/src/test/java/org/dspace/builder/ItemBuilder.java +++ b/dspace-api/src/test/java/org/dspace/builder/ItemBuilder.java @@ -113,6 +113,14 @@ public ItemBuilder withScopusIdentifier(String scopus) { return addMetadataValue(item, "dc", "identifier", "scopus", scopus); } + public ItemBuilder withISSN(String issn) { + return addMetadataValue(item, "dc", "identifier", "issn", issn); + } + + public ItemBuilder withISBN(String isbn) { + return addMetadataValue(item, "dc", "identifier", "isbn", isbn); + } + public ItemBuilder withRelationFunding(String funding) { return addMetadataValue(item, "dc", "relation", "funding", funding); } diff --git a/dspace-api/src/test/java/org/dspace/orcid/service/OrcidEntityFactoryServiceIT.java b/dspace-api/src/test/java/org/dspace/orcid/service/OrcidEntityFactoryServiceIT.java index 912efcfcf323..c73e7adecc41 100644 --- a/dspace-api/src/test/java/org/dspace/orcid/service/OrcidEntityFactoryServiceIT.java +++ b/dspace-api/src/test/java/org/dspace/orcid/service/OrcidEntityFactoryServiceIT.java @@ -73,6 +73,9 @@ public class OrcidEntityFactoryServiceIT extends AbstractIntegrationTestWithData private Collection projects; + private static final String isbn = "978-0-439-02348-1"; + private static final String issn = "1234-1234X"; + @Before public void setup() { @@ -117,6 +120,7 @@ public void testWorkCreation() { .withLanguage("en_US") .withType("Book") .withIsPartOf("Journal") + .withISBN(isbn) .withDoiIdentifier("doi-id") .withScopusIdentifier("scopus-id") .build(); @@ -149,11 +153,100 @@ public void testWorkCreation() { assertThat(work.getExternalIdentifiers(), notNullValue()); List externalIds = work.getExternalIdentifiers().getExternalIdentifier(); - assertThat(externalIds, hasSize(3)); + assertThat(externalIds, hasSize(4)); + assertThat(externalIds, has(selfExternalId("doi", "doi-id"))); + assertThat(externalIds, has(selfExternalId("eid", "scopus-id"))); + assertThat(externalIds, has(selfExternalId("handle", publication.getHandle()))); + // Book type should have SELF rel for ISBN + assertThat(externalIds, has(selfExternalId("isbn", isbn))); + + } + + @Test + public void testJournalArticleAndISSN() { + context.turnOffAuthorisationSystem(); + + Item publication = ItemBuilder.createItem(context, publications) + .withTitle("Test publication") + .withAuthor("Walter White") + .withAuthor("Jesse Pinkman") + .withEditor("Editor") + .withIssueDate("2021-04-30") + .withDescriptionAbstract("Publication description") + .withLanguage("en_US") + .withType("Article") + .withIsPartOf("Journal") + .withISSN(issn) + .withDoiIdentifier("doi-id") + .withScopusIdentifier("scopus-id") + .build(); + + context.restoreAuthSystemState(); + + Activity activity = entityFactoryService.createOrcidObject(context, publication); + assertThat(activity, instanceOf(Work.class)); + + Work work = (Work) activity; + assertThat(work.getJournalTitle(), notNullValue()); + assertThat(work.getJournalTitle().getContent(), is("Journal")); + assertThat(work.getLanguageCode(), is("en")); + assertThat(work.getPublicationDate(), matches(date("2021", "04", "30"))); + assertThat(work.getShortDescription(), is("Publication description")); + assertThat(work.getPutCode(), nullValue()); + assertThat(work.getWorkType(), is(WorkType.JOURNAL_ARTICLE)); + assertThat(work.getWorkTitle(), notNullValue()); + assertThat(work.getWorkTitle().getTitle(), notNullValue()); + assertThat(work.getWorkTitle().getTitle().getContent(), is("Test publication")); + assertThat(work.getWorkContributors(), notNullValue()); + assertThat(work.getUrl(), matches(urlEndsWith(publication.getHandle()))); + + List contributors = work.getWorkContributors().getContributor(); + assertThat(contributors, hasSize(3)); + assertThat(contributors, has(contributor("Walter White", AUTHOR, FIRST))); + assertThat(contributors, has(contributor("Editor", EDITOR, FIRST))); + assertThat(contributors, has(contributor("Jesse Pinkman", AUTHOR, ADDITIONAL))); + + assertThat(work.getExternalIdentifiers(), notNullValue()); + + List externalIds = work.getExternalIdentifiers().getExternalIdentifier(); + assertThat(externalIds, hasSize(4)); assertThat(externalIds, has(selfExternalId("doi", "doi-id"))); assertThat(externalIds, has(selfExternalId("eid", "scopus-id"))); assertThat(externalIds, has(selfExternalId("handle", publication.getHandle()))); + // journal-article should have PART_OF rel for ISSN + assertThat(externalIds, has(externalId("issn", issn, Relationship.PART_OF))); + } + @Test + public void testJournalWithISSN() { + context.turnOffAuthorisationSystem(); + + Item publication = ItemBuilder.createItem(context, publications) + .withTitle("Test journal") + .withEditor("Editor") + .withType("Journal") + .withISSN(issn) + .build(); + + context.restoreAuthSystemState(); + + Activity activity = entityFactoryService.createOrcidObject(context, publication); + assertThat(activity, instanceOf(Work.class)); + + Work work = (Work) activity; + assertThat(work.getWorkType(), is(WorkType.JOURNAL_ISSUE)); + assertThat(work.getWorkTitle(), notNullValue()); + assertThat(work.getWorkTitle().getTitle(), notNullValue()); + assertThat(work.getWorkTitle().getTitle().getContent(), is("Test journal")); + assertThat(work.getUrl(), matches(urlEndsWith(publication.getHandle()))); + + assertThat(work.getExternalIdentifiers(), notNullValue()); + + List externalIds = work.getExternalIdentifiers().getExternalIdentifier(); + assertThat(externalIds, hasSize(2)); + // journal-issue should have SELF rel for ISSN + assertThat(externalIds, has(selfExternalId("issn", issn))); + assertThat(externalIds, has(selfExternalId("handle", publication.getHandle()))); } @Test @@ -163,6 +256,7 @@ public void testEmptyWorkWithUnknownTypeCreation() { Item publication = ItemBuilder.createItem(context, publications) .withType("TYPE") + .withISSN(issn) .build(); context.restoreAuthSystemState(); @@ -183,8 +277,9 @@ public void testEmptyWorkWithUnknownTypeCreation() { assertThat(work.getExternalIdentifiers(), notNullValue()); List externalIds = work.getExternalIdentifiers().getExternalIdentifier(); - assertThat(externalIds, hasSize(1)); + assertThat(externalIds, hasSize(2)); assertThat(externalIds, has(selfExternalId("handle", publication.getHandle()))); + assertThat(externalIds, has(externalId("issn", issn, Relationship.PART_OF))); } @Test diff --git a/dspace/config/crosswalks/orcid/mapConverter-dspace-to-orcid-publication-type.properties b/dspace/config/crosswalks/orcid/mapConverter-dspace-to-orcid-publication-type.properties index 953ddc60eef6..6eac57092a10 100644 --- a/dspace/config/crosswalks/orcid/mapConverter-dspace-to-orcid-publication-type.properties +++ b/dspace/config/crosswalks/orcid/mapConverter-dspace-to-orcid-publication-type.properties @@ -7,6 +7,7 @@ Dataset = data-set Learning\ Object = other Image = other Image,\ 3-D = other +Journal = journal-issue Map = other Musical\ Score = other Plan\ or\ blueprint = other @@ -20,4 +21,4 @@ Technical\ Report = other Thesis = other Video = other Working\ Paper = working-paper -Other = other \ No newline at end of file +Other = other diff --git a/dspace/config/modules/orcid.cfg b/dspace/config/modules/orcid.cfg index ad31371cb890..e3c021f9c631 100644 --- a/dspace/config/modules/orcid.cfg +++ b/dspace/config/modules/orcid.cfg @@ -1,4 +1,3 @@ - #------------------------------------------------------------------# #--------------------ORCID GENERIC CONFIGURATIONS------------------# #------------------------------------------------------------------# @@ -61,12 +60,18 @@ orcid.mapping.work.contributors = dc.contributor.editor::editor ##orcid.mapping.work.external-ids syntax is :: or $simple-handle:: ##The full list of available external identifiers is available here https://pub.orcid.org/v3.0/identifiers +# The identifiers need to have a relationship of SELF, PART_OF, VERSION_OF or FUNDED_BY. +# The default for most identifiers is SELF. The default for identifiers more commonly +# associated with 'parent' publciations (ISSN, ISBN) is PART_OF. +# See the map in `orcid-services.xml` +# VERSION_OF and FUNDED_BY are not currently implemented. orcid.mapping.work.external-ids = dc.identifier.doi::doi orcid.mapping.work.external-ids = dc.identifier.scopus::eid orcid.mapping.work.external-ids = dc.identifier.pmid::pmid orcid.mapping.work.external-ids = $simple-handle::handle orcid.mapping.work.external-ids = dc.identifier.isi::wosuid orcid.mapping.work.external-ids = dc.identifier.issn::issn +orcid.mapping.work.external-ids = dc.identifier.isbn::isbn ### Funding mapping ### orcid.mapping.funding.title = dc.title @@ -146,6 +151,9 @@ orcid.bulk-synchronization.max-attempts = 5 #--------------------ORCID EXTERNAL DATA MAPPING-------------------# #------------------------------------------------------------------# +# Note - the below mapping is for ORCID->DSpace imports, not for +# DSpace->ORCID exports (see orcid.mapping.work.*) + ### Work (Publication) external-data.mapping ### orcid.external-data.mapping.publication.title = dc.title diff --git a/dspace/config/spring/api/orcid-services.xml b/dspace/config/spring/api/orcid-services.xml index 6ec9be9fdf5d..c7a131832de1 100644 --- a/dspace/config/spring/api/orcid-services.xml +++ b/dspace/config/spring/api/orcid-services.xml @@ -55,24 +55,45 @@ - - - - - - - - - - - - - - - - - - + + + + + journal-article + magazine-article + newspaper-article + data-set + learning-object + other + + + + + book-chapter + book-review + other + + + + + + + + + + + + + + + + + + + + + + From 5d58cb78ead2c60a62167cba3a694e328bdd34e7 Mon Sep 17 00:00:00 2001 From: Kim Shepherd Date: Mon, 2 Jun 2025 18:23:34 +0200 Subject: [PATCH 853/979] [TLC-1097] Additional javadoc for ORCID sync fix (cherry picked from commit 1b864e6ca2afab76131afa9ee39152c1802eb23b) --- .../dspace/orcid/model/factory/impl/OrcidWorkFactory.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/dspace-api/src/main/java/org/dspace/orcid/model/factory/impl/OrcidWorkFactory.java b/dspace-api/src/main/java/org/dspace/orcid/model/factory/impl/OrcidWorkFactory.java index 66fd1a717d7b..280a5ac2155f 100644 --- a/dspace-api/src/main/java/org/dspace/orcid/model/factory/impl/OrcidWorkFactory.java +++ b/dspace-api/src/main/java/org/dspace/orcid/model/factory/impl/OrcidWorkFactory.java @@ -149,6 +149,10 @@ private PublicationDate getPublicationDate(Context context, Item item) { .orElse(null); } + /** + * Returns a list of external work IDs constructed in the org.orcid.jaxb + * ExternalIDs object + */ private ExternalIDs getWorkExternalIds(Context context, Item item, Work work) { ExternalIDs externalIDs = new ExternalIDs(); externalIDs.getExternalIdentifier().addAll(getWorkExternalIdList(context, item, work)); @@ -157,7 +161,7 @@ private ExternalIDs getWorkExternalIds(Context context, Item item, Work work) { /** * Creates a list of ExternalID, one for orcid.mapping.funding.external-ids - * value, taking the values from the given item. + * value, taking the values from the given item and work type. */ private List getWorkExternalIdList(Context context, Item item, Work work) { From ec83eb612f4e383da4f5b560ba65200e88750471 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Wed, 6 Aug 2025 08:50:41 -0500 Subject: [PATCH 854/979] Add a deposit integration test for SWORDv1 based on the similar SWORDv2 test. (cherry picked from commit 0589011849cf4c7ac7a67d6dfc44839e11047980) --- .../java/org/dspace/app/sword/Swordv1IT.java | 98 +++++++++++++++++-- 1 file changed, 88 insertions(+), 10 deletions(-) diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/sword/Swordv1IT.java b/dspace-server-webapp/src/test/java/org/dspace/app/sword/Swordv1IT.java index 24244e1773e6..0b866659edd7 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/sword/Swordv1IT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/sword/Swordv1IT.java @@ -10,16 +10,30 @@ import static org.hamcrest.CoreMatchers.containsString; import static org.hamcrest.CoreMatchers.equalTo; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertThat; +import java.nio.file.Path; +import java.util.List; + import org.dspace.app.rest.test.AbstractWebClientIntegrationTest; +import org.dspace.builder.CollectionBuilder; +import org.dspace.builder.CommunityBuilder; +import org.dspace.content.Collection; import org.dspace.services.ConfigurationService; +import org.hamcrest.MatcherAssert; import org.junit.Assume; import org.junit.Before; -import org.junit.Ignore; import org.junit.Test; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.io.FileSystemResource; +import org.springframework.http.ContentDisposition; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.http.RequestEntity; import org.springframework.http.ResponseEntity; import org.springframework.test.context.TestPropertySource; @@ -45,6 +59,9 @@ public class Swordv1IT extends AbstractWebClientIntegrationTest { private final String DEPOSIT_PATH = "/sword/deposit"; private final String MEDIA_LINK_PATH = "/sword/media-link"; + // ATOM Content type returned by SWORDv1 + private final String ATOM_CONTENT_TYPE = "application/atom+xml;charset=UTF-8"; + @Before public void onlyRunIfConfigExists() { // These integration tests REQUIRE that SWORDWebConfig is found/available (as this class deploys SWORD) @@ -93,10 +110,76 @@ public void depositUnauthorizedTest() throws Exception { } @Test - @Ignore public void depositTest() throws Exception { - // TODO: Actually test a full deposit via SWORD. - // Currently, we are just ensuring the /deposit endpoint exists (see above) and isn't throwing a 404 + context.turnOffAuthorisationSystem(); + // Create a top level community and one Collection + parentCommunity = CommunityBuilder.createCommunity(context) + .withName("Parent Community") + .build(); + // Make sure our Collection allows the "eperson" user to submit into it + Collection collection = CollectionBuilder.createCollection(context, parentCommunity) + .withName("Test SWORDv1 Collection") + .withSubmitterGroup(eperson) + .build(); + // Above changes MUST be committed to the database for SWORDv2 to see them. + context.commit(); + context.restoreAuthSystemState(); + + // Specify zip file + // NOTE: We are using the same "example.zip" as SWORDv2IT because that same ZIP is valid for both v1 and v2 + FileSystemResource zipFile = new FileSystemResource(Path.of("src", "test", "resources", "org", + "dspace", "app", "sword2", "example.zip")); + + // Add required headers + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.valueOf("application/zip")); + headers.setContentDisposition(ContentDisposition.attachment().filename("example.zip").build()); + headers.set("X-Packaging", "http://purl.org/net/sword-types/METSDSpaceSIP"); + headers.setAccept(List.of(MediaType.APPLICATION_ATOM_XML)); + + //---- + // STEP 1: Verify upload/submit via SWORDv1 works + //---- + // Send POST to upload Zip file via SWORD + ResponseEntity response = postResponseAsString(DEPOSIT_PATH + "/" + collection.getHandle(), + eperson.getEmail(), password, + new HttpEntity<>(zipFile.getContentAsByteArray(), + headers)); + + // Expect a 201 CREATED response with ATOM content returned + assertEquals(HttpStatus.CREATED, response.getStatusCode()); + assertEquals(ATOM_CONTENT_TYPE, response.getHeaders().getContentType().toString()); + + // MUST return a "Location" header which is the "/sword/media-link/*" URI of the zip file bitstream within + // the created item (e.g. /sword/media-link/[handle-prefix]/[handle-suffix]/bitstream/[uuid]) + assertNotNull(response.getHeaders().getLocation()); + String mediaLink = response.getHeaders().getLocation().toString(); + + // Body should include the SWORD version in generator tag + MatcherAssert.assertThat(response.getBody(), + containsString("")); + // Verify Item title also is returned in the body + MatcherAssert.assertThat(response.getBody(), containsString("Attempts to detect retrotransposition")); + + //---- + // STEP 2: Verify /media-link access works + //---- + // Media-Link URI should work when requested by the EPerson who did the deposit + HttpHeaders authHeaders = new HttpHeaders(); + authHeaders.setBasicAuth(eperson.getEmail(), password); + RequestEntity request = RequestEntity.get(mediaLink) + .accept(MediaType.valueOf("application/atom+xml")) + .headers(authHeaders) + .build(); + response = responseAsString(request); + + // Expect a 200 response with ATOM feed content returned + assertEquals(HttpStatus.OK, response.getStatusCode()); + assertEquals(ATOM_CONTENT_TYPE, response.getHeaders().getContentType().toString()); + // Body should include a link to the zip bitstream in the newly created Item + // This just verifies "example.zip" exists in the body. + MatcherAssert.assertThat(response.getBody(), containsString("example.zip")); } @Test @@ -105,13 +188,8 @@ public void mediaLinkUnauthorizedTest() throws Exception { ResponseEntity response = getResponseAsString(MEDIA_LINK_PATH); // Expect a 401 response code assertThat(response.getStatusCode(), equalTo(HttpStatus.UNAUTHORIZED)); - } - @Test - @Ignore - public void mediaLinkTest() throws Exception { - // TODO: Actually test a /media-link request. - // Currently, we are just ensuring the /media-link endpoint exists (see above) and isn't throwing a 404 + //NOTE: An authorized /media-link test is performed in depositTest() above. } } From 4091b27370671ee4eebe722310646b9cac59fd56 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Wed, 6 Aug 2025 08:51:16 -0500 Subject: [PATCH 855/979] Fix WRITE permissions error when ingesting a new Item. Do not call "updateDSpaceObject" after calling "finishCreateItem" as the latter saves the object and removes submitter privileges from it. (cherry picked from commit c2d05891ab32596b326d78d2e4b5ff2372d94ca2) --- .../content/packager/AbstractMETSIngester.java | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/content/packager/AbstractMETSIngester.java b/dspace-api/src/main/java/org/dspace/content/packager/AbstractMETSIngester.java index 77236be9d525..0ab5ac71cda5 100644 --- a/dspace-api/src/main/java/org/dspace/content/packager/AbstractMETSIngester.java +++ b/dspace-api/src/main/java/org/dspace/content/packager/AbstractMETSIngester.java @@ -498,8 +498,11 @@ protected DSpaceObject ingestObject(Context context, DSpaceObject parent, // Finish creating the item. This actually assigns the handle, // and will either install item immediately or start a workflow, based on params PackageUtils.finishCreateItem(context, wsi, handle, params); + } else { + // We should have a workspace item during ingest, so this code is only here for safety. + // Update the object to make sure all changes are committed + PackageUtils.updateDSpaceObject(context, dso); } - } else if (type == Constants.COLLECTION || type == Constants.COMMUNITY) { // Add logo if one is referenced from manifest addContainerLogo(context, dso, manifest, pkgFile, params); @@ -513,6 +516,9 @@ protected DSpaceObject ingestObject(Context context, DSpaceObject parent, // (this allows subclasses to do some final validation / changes as // necessary) finishObject(context, dso, params); + + // Update the object to make sure all changes are committed + PackageUtils.updateDSpaceObject(context, dso); } else if (type == Constants.SITE) { // Do nothing by default -- Crosswalks will handle anything necessary to ingest at Site-level @@ -520,18 +526,15 @@ protected DSpaceObject ingestObject(Context context, DSpaceObject parent, // (this allows subclasses to do some final validation / changes as // necessary) finishObject(context, dso, params); + + // Update the object to make sure all changes are committed + PackageUtils.updateDSpaceObject(context, dso); } else { throw new PackageValidationException( "Unknown DSpace Object type in package, type=" + String.valueOf(type)); } - // -- Step 6 -- - // Finish things up! - - // Update the object to make sure all changes are committed - PackageUtils.updateDSpaceObject(context, dso); - return dso; } From a972ecba51d247a8cbb9eceb668c8e6021a361fb Mon Sep 17 00:00:00 2001 From: Vincenzo Mecca Date: Wed, 6 Aug 2025 13:00:11 +0200 Subject: [PATCH 856/979] fix: uses dc.source for bitstream originalName tag Refs: DURACOM-391, #1130 (cherry picked from commit f5c0c17c663c494709666e64d594598b19db876b) --- dspace-oai/src/main/java/org/dspace/xoai/util/ItemUtils.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dspace-oai/src/main/java/org/dspace/xoai/util/ItemUtils.java b/dspace-oai/src/main/java/org/dspace/xoai/util/ItemUtils.java index 40a193ea2905..c3364fb44259 100644 --- a/dspace-oai/src/main/java/org/dspace/xoai/util/ItemUtils.java +++ b/dspace-oai/src/main/java/org/dspace/xoai/util/ItemUtils.java @@ -143,7 +143,7 @@ private static Element createBundlesElement(Context context, Item item) throws S bitstream.getField().add(createValue("name", name)); } if (oname != null) { - bitstream.getField().add(createValue("originalName", name)); + bitstream.getField().add(createValue("originalName", oname)); } if (description != null) { bitstream.getField().add(createValue("description", description)); From e0cf6c717f623386eb016e5018bfd781936ed622 Mon Sep 17 00:00:00 2001 From: "David P. Steelman" Date: Fri, 14 Mar 2025 13:30:22 -0400 Subject: [PATCH 857/979] Fix checkpointing for checksum checking Fixes the checkpointing for checksum checking by actually committing the changes to the database. Replacing "uncacheEntity" with "commit", as testing has shown that this is necessary to ensure that the checksum status is properly updated in the most_recent_checksum" table when checksum process is interrupted. The provided integration test fails if the checksums of bitstreams that were checked before the interruption are not properly recorded in the database. (cherry picked from commit 502b655ef6041381eb15833a82f03054894c96bb) --- .../org/dspace/checker/CheckerCommand.java | 2 +- .../org/dspace/checker/ChecksumCheckerIT.java | 192 ++++++++++++++++++ 2 files changed, 193 insertions(+), 1 deletion(-) create mode 100644 dspace-api/src/test/java/org/dspace/checker/ChecksumCheckerIT.java 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 a12ac3b98a2e..9ad9f553b445 100644 --- a/dspace-api/src/main/java/org/dspace/checker/CheckerCommand.java +++ b/dspace-api/src/main/java/org/dspace/checker/CheckerCommand.java @@ -131,7 +131,7 @@ public void process() throws SQLException { collector.collect(context, info); } - context.uncacheEntity(bitstream); + context.commit(); bitstream = dispatcher.next(); } } diff --git a/dspace-api/src/test/java/org/dspace/checker/ChecksumCheckerIT.java b/dspace-api/src/test/java/org/dspace/checker/ChecksumCheckerIT.java new file mode 100644 index 000000000000..7653d518b196 --- /dev/null +++ b/dspace-api/src/test/java/org/dspace/checker/ChecksumCheckerIT.java @@ -0,0 +1,192 @@ +/** + * 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.checker; + +import static java.nio.charset.StandardCharsets.UTF_8; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import java.sql.SQLException; +import java.time.Instant; +import java.util.ArrayList; +import java.util.List; + +import org.apache.commons.io.IOUtils; +import org.dspace.AbstractIntegrationTestWithDatabase; +import org.dspace.builder.BitstreamBuilder; +import org.dspace.builder.CollectionBuilder; +import org.dspace.builder.CommunityBuilder; +import org.dspace.builder.ItemBuilder; +import org.dspace.checker.factory.CheckerServiceFactory; +import org.dspace.checker.service.ChecksumHistoryService; +import org.dspace.checker.service.MostRecentChecksumService; +import org.dspace.content.Bitstream; +import org.dspace.content.Collection; +import org.dspace.content.Community; +import org.dspace.content.Item; +import org.dspace.core.Context; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +public class ChecksumCheckerIT extends AbstractIntegrationTestWithDatabase { + protected List bitstreams; + protected MostRecentChecksumService checksumService = + CheckerServiceFactory.getInstance().getMostRecentChecksumService(); + + @Before + public void setup() throws Exception { + context.turnOffAuthorisationSystem(); + + Community parentCommunity = CommunityBuilder.createCommunity(context).build(); + Collection collection = CollectionBuilder.createCollection(context, parentCommunity) + .build(); + Item item = ItemBuilder.createItem(context, collection).withTitle("Test item") + .build(); + + int numBitstreams = 3; + bitstreams = new ArrayList<>(); + for (int i = 0; i < numBitstreams; i++) { + String content = "Test bitstream " + i; + bitstreams.add( + BitstreamBuilder.createBitstream( + context, item, IOUtils.toInputStream(content, UTF_8) + ).build() + ); + } + + context.restoreAuthSystemState(); + + // Call the "updateMissingBitstreams" method so that the test bitstreams + // already have checksums in the past when CheckerCommand runs. + // Otherwise, the CheckerCommand will simply update the test + // bitstreams without going through the BitstreamDispatcher. + checksumService = CheckerServiceFactory.getInstance().getMostRecentChecksumService(); + checksumService.updateMissingBitstreams(context); + + // The "updateMissingBitstreams" method updates the test bitstreams in + // a random order. To verify that the expected bitstreams were + // processed, reset the timestamps so that the bitstreams are + // checked in a specific order (oldest first). + Instant checksumInstant = Instant.ofEpochMilli(0); + for (Bitstream bitstream: bitstreams) { + MostRecentChecksum mrc = checksumService.findByBitstream(context, bitstream); + mrc.setProcessStartDate(checksumInstant); + mrc.setProcessEndDate(checksumInstant); + checksumInstant = checksumInstant.plusSeconds(10); + } + context.commit(); + } + + @After + public void cleanUp() throws SQLException { + // Need to clean up ChecksumHistory because of a referential integrity + // constraint violation between the most_recent_checksum table and + // bitstream tables + ChecksumHistoryService checksumHistoryService = CheckerServiceFactory.getInstance().getChecksumHistoryService(); + + for (Bitstream bitstream: bitstreams) { + checksumHistoryService.deleteByBitstream(context, bitstream); + } + } + + @Test + public void testChecksumsRecordedWhenProcesingIsInterrupted() throws SQLException { + CheckerCommand checker = new CheckerCommand(context); + + // The start date to use for the checker process + Instant checkerStartDate = Instant.now(); + + // Verify that all checksums are before the checker start date + for (Bitstream bitstream: bitstreams) { + MostRecentChecksum checksum = checksumService.findByBitstream(context, bitstream); + Instant lastChecksumDate = checksum.getProcessStartDate(); + assertTrue("lastChecksumDate (" + lastChecksumDate + ") <= checkerStartDate (" + checkerStartDate + ")", + lastChecksumDate.isBefore(checkerStartDate)); + } + + // Dispatcher that throws an exception when a third bitstream is + // retrieved. + BitstreamDispatcher dispatcher = new ExpectionThrowingDispatcher( + context, checkerStartDate, false, 2); + checker.setDispatcher(dispatcher); + + + // Run the checksum checker + checker.setProcessStartDate(checkerStartDate); + try { + checker.process(); + fail("SQLException should have been thrown"); + } catch (SQLException sqle) { + // Rollback any pending transaction + context.rollback(); + } + + // Verify that the checksums of the first two bitstreams (that were + // processed before the exception) have been successfully recorded in + // the database, while the third bitstream was not updated. + int bitstreamCount = 0; + for (Bitstream bitstream: bitstreams) { + MostRecentChecksum checksum = checksumService.findByBitstream(context, bitstream); + Instant lastChecksumDate = checksum.getProcessStartDate(); + + bitstreamCount = bitstreamCount + 1; + if (bitstreamCount <= 2) { + assertTrue("lastChecksumDate (" + lastChecksumDate + ") <= checkerStartDate (" + checkerStartDate + ")", + lastChecksumDate.isAfter(checkerStartDate)); + } else { + assertTrue("lastChecksumDate (" + lastChecksumDate + ") >= checkerStartDate (" + checkerStartDate + ")", + lastChecksumDate.isBefore(checkerStartDate)); + } + } + } + + /** + * Subclass of SimpleDispatcher that only allows a limited number of "next" + * class before throwing a SQLException. + */ + class ExpectionThrowingDispatcher extends SimpleDispatcher { + // The number of "next" calls to allow before throwing a SQLException + protected int maxNextCalls; + + // The number of "next" method calls seen so far. + protected int numNextCalls = 0; + + /** + * Constructor. + * + * @param context Context + * @param startTime timestamp for beginning of checker process + * @param looping indicates whether checker should loop infinitely + * through most_recent_checksum table + * @param maxNextCalls the number of "next" method calls to allow before + * throwing a SQLException. + */ + public ExpectionThrowingDispatcher(Context context, Instant startTime, boolean looping, int maxNextCalls) { + super(context, startTime, looping); + this.maxNextCalls = maxNextCalls; + } + + /** + * Selects the next candidate bitstream. + * + * After "maxNextClass" number of calls, this method throws a + * SQLException. + * + * @throws SQLException if database error + */ + @Override + public synchronized Bitstream next() throws SQLException { + numNextCalls = numNextCalls + 1; + if (numNextCalls > maxNextCalls) { + throw new SQLException("Max 'next' method calls exceeded"); + } + return super.next(); + } + } +} From ecdab82627310061d97f67a6d9546d80359d15b7 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Wed, 6 Aug 2025 15:09:41 -0500 Subject: [PATCH 858/979] Revert to using java.util.Date instead of Instant to support earlier versions of DSpace. --- .../org/dspace/checker/ChecksumCheckerIT.java | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/dspace-api/src/test/java/org/dspace/checker/ChecksumCheckerIT.java b/dspace-api/src/test/java/org/dspace/checker/ChecksumCheckerIT.java index 7653d518b196..34198ff1ebfe 100644 --- a/dspace-api/src/test/java/org/dspace/checker/ChecksumCheckerIT.java +++ b/dspace-api/src/test/java/org/dspace/checker/ChecksumCheckerIT.java @@ -14,6 +14,7 @@ import java.sql.SQLException; import java.time.Instant; import java.util.ArrayList; +import java.util.Date; import java.util.List; import org.apache.commons.io.IOUtils; @@ -76,8 +77,8 @@ public void setup() throws Exception { Instant checksumInstant = Instant.ofEpochMilli(0); for (Bitstream bitstream: bitstreams) { MostRecentChecksum mrc = checksumService.findByBitstream(context, bitstream); - mrc.setProcessStartDate(checksumInstant); - mrc.setProcessEndDate(checksumInstant); + mrc.setProcessStartDate(Date.from(checksumInstant)); + mrc.setProcessEndDate(Date.from(checksumInstant)); checksumInstant = checksumInstant.plusSeconds(10); } context.commit(); @@ -100,14 +101,14 @@ public void testChecksumsRecordedWhenProcesingIsInterrupted() throws SQLExceptio CheckerCommand checker = new CheckerCommand(context); // The start date to use for the checker process - Instant checkerStartDate = Instant.now(); + Date checkerStartDate = Date.from(Instant.now()); // Verify that all checksums are before the checker start date for (Bitstream bitstream: bitstreams) { MostRecentChecksum checksum = checksumService.findByBitstream(context, bitstream); - Instant lastChecksumDate = checksum.getProcessStartDate(); + Date lastChecksumDate = checksum.getProcessStartDate(); assertTrue("lastChecksumDate (" + lastChecksumDate + ") <= checkerStartDate (" + checkerStartDate + ")", - lastChecksumDate.isBefore(checkerStartDate)); + lastChecksumDate.before(checkerStartDate)); } // Dispatcher that throws an exception when a third bitstream is @@ -133,15 +134,15 @@ public void testChecksumsRecordedWhenProcesingIsInterrupted() throws SQLExceptio int bitstreamCount = 0; for (Bitstream bitstream: bitstreams) { MostRecentChecksum checksum = checksumService.findByBitstream(context, bitstream); - Instant lastChecksumDate = checksum.getProcessStartDate(); + Date lastChecksumDate = checksum.getProcessStartDate(); bitstreamCount = bitstreamCount + 1; if (bitstreamCount <= 2) { assertTrue("lastChecksumDate (" + lastChecksumDate + ") <= checkerStartDate (" + checkerStartDate + ")", - lastChecksumDate.isAfter(checkerStartDate)); + lastChecksumDate.after(checkerStartDate)); } else { assertTrue("lastChecksumDate (" + lastChecksumDate + ") >= checkerStartDate (" + checkerStartDate + ")", - lastChecksumDate.isBefore(checkerStartDate)); + lastChecksumDate.before(checkerStartDate)); } } } @@ -167,7 +168,7 @@ class ExpectionThrowingDispatcher extends SimpleDispatcher { * @param maxNextCalls the number of "next" method calls to allow before * throwing a SQLException. */ - public ExpectionThrowingDispatcher(Context context, Instant startTime, boolean looping, int maxNextCalls) { + public ExpectionThrowingDispatcher(Context context, Date startTime, boolean looping, int maxNextCalls) { super(context, startTime, looping); this.maxNextCalls = maxNextCalls; } From f79d012cbc1b2ad59cb11814d50219e1d6c91458 Mon Sep 17 00:00:00 2001 From: im-shubham-vish Date: Tue, 12 Aug 2025 16:55:53 +0530 Subject: [PATCH 859/979] Make parse protected to make it override in MetadataExportSearch Add Test for Double Quoted Search CSV Export (cherry picked from commit 39a45f7f3465df36fbf8db3f5a9ed541eab794d8) --- .../app/bulkedit/MetadataExportSearch.java | 12 +++++++ .../org/dspace/scripts/DSpaceRunnable.java | 2 +- .../app/bulkedit/MetadataExportSearchIT.java | 31 +++++++++++++++++++ 3 files changed, 44 insertions(+), 1 deletion(-) 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 e4bbe335d63e..689df4701a96 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/scripts/DSpaceRunnable.java b/dspace-api/src/main/java/org/dspace/scripts/DSpaceRunnable.java index 2ea0a52d6e34..8f905e01511e 100644 --- a/dspace-api/src/main/java/org/dspace/scripts/DSpaceRunnable.java +++ b/dspace-api/src/main/java/org/dspace/scripts/DSpaceRunnable.java @@ -117,7 +117,7 @@ private void handleHelpCommandLine() { * @param args The primitive array of Strings representing the parameters * @throws ParseException If something goes wrong */ - private StepResult parse(String[] args) throws ParseException { + protected StepResult parse(String[] args) throws ParseException { commandLine = new DefaultParser().parse(getScriptConfiguration().getOptions(), args); setup(); return StepResult.Continue; diff --git a/dspace-api/src/test/java/org/dspace/app/bulkedit/MetadataExportSearchIT.java b/dspace-api/src/test/java/org/dspace/app/bulkedit/MetadataExportSearchIT.java index 63a87a48f554..15a6371e920b 100644 --- a/dspace-api/src/test/java/org/dspace/app/bulkedit/MetadataExportSearchIT.java +++ b/dspace-api/src/test/java/org/dspace/app/bulkedit/MetadataExportSearchIT.java @@ -251,4 +251,35 @@ public void exportMetadataSearchNonExistinFacetsTest() throws Exception { assertNotNull(exception); assertEquals("nonExisting is not a valid search filter", exception.getMessage()); } + + @Test + public void exportMetadataSearchDoubleQuotedArgumentTest() throws Exception { + context.turnOffAuthorisationSystem(); + Item quotedItem1 = ItemBuilder.createItem(context, collection) + .withTitle("The Special Runnable Item") + .withSubject("quoted-subject") + .build(); + Item quotedItem2 = ItemBuilder.createItem(context, collection) + .withTitle("The Special Item") + .withSubject("quoted-subject") + .build(); + context.restoreAuthSystemState(); + + int result = runDSpaceScript( + "metadata-export-search", + "-q", "title:\"Special Runnable\"", + "-n", filename); + + assertEquals(0, result); + + Item[] expectedResult = new Item[] {quotedItem1}; + checkItemsPresentInFile(filename, expectedResult); + + File file = new File(filename); + try (Reader reader = Files.newReader(file, Charset.defaultCharset()); + CSVReader csvReader = new CSVReader(reader)) { + List lines = csvReader.readAll(); + assertEquals("Unexpected extra items in export", 2, lines.size()); + } + } } From d282f92427f340a7f8ca68ca8316114ce0bdd524 Mon Sep 17 00:00:00 2001 From: Martin Walk Date: Wed, 20 Aug 2025 15:45:14 +0200 Subject: [PATCH 860/979] Fix #11074 export simple archive format with no collection --- .../app/itemexport/ItemExportServiceImpl.java | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) 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 9eaabc20e862..d50b44fd8d4c 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()); } } } From d44a5ddc499102825cf44800e1f7af940f0d318c Mon Sep 17 00:00:00 2001 From: Joran De Braekeleer Date: Wed, 13 Aug 2025 13:32:33 +0200 Subject: [PATCH 861/979] 133268: Metadata import empty collection col check (cherry picked from commit b9513228d78ccb3aa8bcb8a94e2d0a8e1a37c30b) --- .../src/main/java/org/dspace/app/bulkedit/MetadataImport.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 e8cf42b47c1b..b12abbc46eb9 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 @@ -494,7 +494,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"); } From 3a160624b73e37fafd8e77a65cd41c38207b0b75 Mon Sep 17 00:00:00 2001 From: Alan Orth Date: Sun, 17 Aug 2025 11:56:13 +0300 Subject: [PATCH 862/979] .github/workflows/codescan.yml: use codeql-action v3 Version 2 was deprecated in January, 2024 after the release of v3. See: https://github.blog/changelog/2025-01-10-code-scanning-codeql-action-v2-is-now-deprecated/ (cherry picked from commit 19f3535dfdd57c46ad2b8a704a11fabd681fceb8) --- .github/workflows/codescan.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/codescan.yml b/.github/workflows/codescan.yml index 3a563c6fa39c..cbdb4b880cbb 100644 --- a/.github/workflows/codescan.yml +++ b/.github/workflows/codescan.yml @@ -47,7 +47,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@v3 with: # Codescan Javascript as well since a few JS files exist in REST API's interface languages: java, javascript @@ -56,8 +56,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@v3 # Perform GitHub Code Scanning. - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v2 + uses: github/codeql-action/analyze@v3 From dc768cd5ae059a71b882daeace6343635cf163c4 Mon Sep 17 00:00:00 2001 From: Miika Nurminen Date: Tue, 12 Aug 2025 19:57:57 +0300 Subject: [PATCH 863/979] Fix bean initialization error on Tomcat startup if citation-page.enabled_communities is set Arrays.asList produces non-mutable list but with communities, sub-collections are added dynamically on initializaion. --- .../org/dspace/disseminate/CitationDocumentServiceImpl.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/disseminate/CitationDocumentServiceImpl.java b/dspace-api/src/main/java/org/dspace/disseminate/CitationDocumentServiceImpl.java index c20961db7544..b1441867772f 100644 --- a/dspace-api/src/main/java/org/dspace/disseminate/CitationDocumentServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/disseminate/CitationDocumentServiceImpl.java @@ -139,8 +139,8 @@ public void afterPropertiesSet() throws Exception { //Load enabled collections String[] citationEnabledCollections = configurationService - .getArrayProperty("citation-page.enabled_collections"); - citationEnabledCollectionsList = Arrays.asList(citationEnabledCollections); + .getArrayProperty("citation-page.enabled_collections"); + citationEnabledCollectionsList = new ArrayList(Arrays.asList(citationEnabledCollections)); //Load enabled communities, and add to collection-list String[] citationEnabledCommunities = configurationService From d2f6140455de5a807c4ad8f6bbb5bdf7cf9f9a0c Mon Sep 17 00:00:00 2001 From: Alan Orth Date: Fri, 15 Aug 2025 09:57:51 +0300 Subject: [PATCH 864/979] dspace-api/pom.xml: remove build-helper-maven-plugin Remove the org.codehaus.mojo:build-helper-maven-plugin because the `maven-version` property has been included by maven itself since version 3.0.4. This fixes the following warning during build: [INFO] --- build-helper:3.6.1:maven-version (default) @ dspace-api --- [WARNING] Goal 'maven-version' is deprecated: Maven since version 3.0.4 has such property build in: MNG-4112 . So goal can be removed. (cherry picked from commit de3170d4ebbcc2168e639d681f0433b9dec293b5) --- dspace-api/pom.xml | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/dspace-api/pom.xml b/dspace-api/pom.xml index d0857bf4eed1..39944a08b593 100644 --- a/dspace-api/pom.xml +++ b/dspace-api/pom.xml @@ -99,20 +99,6 @@ - - org.codehaus.mojo - build-helper-maven-plugin - 3.6.1 - - - validate - - maven-version - - - - - org.codehaus.mojo buildnumber-maven-plugin From 8a326fd327120bcf63cba1a2c7c17f744bb4ddb6 Mon Sep 17 00:00:00 2001 From: JohnnyMendesC <177888064+JohnnyMendesC@users.noreply.github.com> Date: Thu, 14 Aug 2025 14:05:45 -0300 Subject: [PATCH 865/979] fix(#8852): Statistics event processor now uses oaiPrefix instead of getHost (cherry picked from commit c8fe80c22bdc67d8b4eae0575fb8a2cab31d2010) --- .../statistics/export/processor/ExportEventProcessor.java | 3 ++- .../statistics/export/ITIrusExportUsageEventListener.java | 1 + .../statistics/export/processor/ExportEventProcessorIT.java | 1 + 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/dspace-api/src/main/java/org/dspace/statistics/export/processor/ExportEventProcessor.java b/dspace-api/src/main/java/org/dspace/statistics/export/processor/ExportEventProcessor.java index 434de459bad9..44fc5f3dc9c5 100644 --- a/dspace-api/src/main/java/org/dspace/statistics/export/processor/ExportEventProcessor.java +++ b/dspace-api/src/main/java/org/dspace/statistics/export/processor/ExportEventProcessor.java @@ -136,9 +136,10 @@ protected String getBaseParameters(Item item) .append(URLEncoder.encode(clientUA, UTF_8)); String hostName = Utils.getHostName(configurationService.getProperty("dspace.ui.url")); + String oaiPrefix = configurationService.getProperty("oai.identifier.prefix"); data.append("&").append(URLEncoder.encode("rft.artnum", UTF_8)).append("="). - append(URLEncoder.encode("oai:" + hostName + ":" + item + append(URLEncoder.encode("oai:" + oaiPrefix + ":" + item .getHandle(), UTF_8)); data.append("&").append(URLEncoder.encode("rfr_dat", UTF_8)).append("=") .append(URLEncoder.encode(referer, UTF_8)); diff --git a/dspace-api/src/test/java/org/dspace/statistics/export/ITIrusExportUsageEventListener.java b/dspace-api/src/test/java/org/dspace/statistics/export/ITIrusExportUsageEventListener.java index 0c861a0d293d..48cf0b14b6bd 100644 --- a/dspace-api/src/test/java/org/dspace/statistics/export/ITIrusExportUsageEventListener.java +++ b/dspace-api/src/test/java/org/dspace/statistics/export/ITIrusExportUsageEventListener.java @@ -116,6 +116,7 @@ public void setUp() throws Exception { configurationService.setProperty("irus.statistics.tracker.enabled", true); configurationService.setProperty("irus.statistics.tracker.type-field", "dc.type"); configurationService.setProperty("irus.statistics.tracker.type-value", "Excluded type"); + configurationService.setProperty("oai.identifier.prefix", "localhost"); context.turnOffAuthorisationSystem(); diff --git a/dspace-api/src/test/java/org/dspace/statistics/export/processor/ExportEventProcessorIT.java b/dspace-api/src/test/java/org/dspace/statistics/export/processor/ExportEventProcessorIT.java index fb53d0c83c54..96909a9e3dbd 100644 --- a/dspace-api/src/test/java/org/dspace/statistics/export/processor/ExportEventProcessorIT.java +++ b/dspace-api/src/test/java/org/dspace/statistics/export/processor/ExportEventProcessorIT.java @@ -62,6 +62,7 @@ public void setUp() throws Exception { configurationService.setProperty("irus.statistics.tracker.enabled", true); configurationService.setProperty("irus.statistics.tracker.type-field", "dc.type"); configurationService.setProperty("irus.statistics.tracker.type-value", "Excluded type"); + configurationService.setProperty("oai.identifier.prefix", "localhost"); context.turnOffAuthorisationSystem(); publication = EntityTypeBuilder.createEntityTypeBuilder(context, "Publication").build(); From b6bacd01ac4098d96063e537c1ec603f0e7a5f92 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Sep 2025 05:43:19 +0000 Subject: [PATCH 866/979] Bump the build-tools group with 3 updates Bumps the build-tools group with 3 updates: [com.github.spotbugs:spotbugs](https://github.com/spotbugs/spotbugs), [com.github.spotbugs:spotbugs-maven-plugin](https://github.com/spotbugs/spotbugs-maven-plugin) and [org.apache.maven.plugins:maven-javadoc-plugin](https://github.com/apache/maven-javadoc-plugin). Updates `com.github.spotbugs:spotbugs` from 4.9.3 to 4.9.4 - [Release notes](https://github.com/spotbugs/spotbugs/releases) - [Changelog](https://github.com/spotbugs/spotbugs/blob/master/CHANGELOG.md) - [Commits](https://github.com/spotbugs/spotbugs/compare/4.9.3...4.9.4) Updates `com.github.spotbugs:spotbugs-maven-plugin` from 4.9.3.2 to 4.9.4.0 - [Release notes](https://github.com/spotbugs/spotbugs-maven-plugin/releases) - [Commits](https://github.com/spotbugs/spotbugs-maven-plugin/compare/spotbugs-maven-plugin-4.9.3.2...spotbugs-maven-plugin-4.9.4.0) Updates `org.apache.maven.plugins:maven-javadoc-plugin` from 3.11.2 to 3.11.3 - [Release notes](https://github.com/apache/maven-javadoc-plugin/releases) - [Commits](https://github.com/apache/maven-javadoc-plugin/compare/maven-javadoc-plugin-3.11.2...maven-javadoc-plugin-3.11.3) --- updated-dependencies: - dependency-name: com.github.spotbugs:spotbugs dependency-version: 4.9.4 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: build-tools - dependency-name: com.github.spotbugs:spotbugs-maven-plugin dependency-version: 4.9.4.0 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: build-tools - dependency-name: org.apache.maven.plugins:maven-javadoc-plugin dependency-version: 3.11.3 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: build-tools ... Signed-off-by: dependabot[bot] --- pom.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pom.xml b/pom.xml index ab300551b21d..c7a87870bc1a 100644 --- a/pom.xml +++ b/pom.xml @@ -303,7 +303,7 @@ com.github.spotbugs spotbugs-maven-plugin - 4.9.3.2 + 4.9.4.0 Max Low @@ -313,7 +313,7 @@ com.github.spotbugs spotbugs - 4.9.3 + 4.9.4 @@ -371,7 +371,7 @@ org.apache.maven.plugins maven-javadoc-plugin - 3.11.2 + 3.11.3 false From c8c6972fa0f154cbf2530cfd4baefdb780eb1b5a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Sep 2025 05:43:38 +0000 Subject: [PATCH 867/979] Bump the hibernate group with 2 updates Bumps the hibernate group with 2 updates: [org.hibernate.validator:hibernate-validator](https://github.com/hibernate/hibernate-validator) and [org.hibernate.validator:hibernate-validator-cdi](https://github.com/hibernate/hibernate-validator). Updates `org.hibernate.validator:hibernate-validator` from 8.0.2.Final to 8.0.3.Final - [Changelog](https://github.com/hibernate/hibernate-validator/blob/8.0.3.Final/changelog.txt) - [Commits](https://github.com/hibernate/hibernate-validator/compare/8.0.2.Final...8.0.3.Final) Updates `org.hibernate.validator:hibernate-validator-cdi` from 8.0.2.Final to 8.0.3.Final - [Changelog](https://github.com/hibernate/hibernate-validator/blob/8.0.3.Final/changelog.txt) - [Commits](https://github.com/hibernate/hibernate-validator/compare/8.0.2.Final...8.0.3.Final) Updates `org.hibernate.validator:hibernate-validator-cdi` from 8.0.2.Final to 8.0.3.Final - [Changelog](https://github.com/hibernate/hibernate-validator/blob/8.0.3.Final/changelog.txt) - [Commits](https://github.com/hibernate/hibernate-validator/compare/8.0.2.Final...8.0.3.Final) --- updated-dependencies: - dependency-name: org.hibernate.validator:hibernate-validator dependency-version: 8.0.3.Final dependency-type: direct:production update-type: version-update:semver-patch dependency-group: hibernate - dependency-name: org.hibernate.validator:hibernate-validator-cdi dependency-version: 8.0.3.Final dependency-type: direct:production update-type: version-update:semver-patch dependency-group: hibernate - dependency-name: org.hibernate.validator:hibernate-validator-cdi dependency-version: 8.0.3.Final dependency-type: direct:production update-type: version-update:semver-patch dependency-group: hibernate ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index ab300551b21d..af9a4d7b21a3 100644 --- a/pom.xml +++ b/pom.xml @@ -23,7 +23,7 @@ 3.5.4 6.5.2 6.4.10.Final - 8.0.2.Final + 8.0.3.Final 42.7.7 10.22.0 8.11.4 From 54752958a9f82ad5ec392fb366ff7710f022ac46 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Sep 2025 05:44:18 +0000 Subject: [PATCH 868/979] Bump commons-cli:commons-cli in the apache-commons group Bumps the apache-commons group with 1 update: [commons-cli:commons-cli](https://github.com/apache/commons-cli). Updates `commons-cli:commons-cli` from 1.9.0 to 1.10.0 - [Changelog](https://github.com/apache/commons-cli/blob/master/RELEASE-NOTES.txt) - [Commits](https://github.com/apache/commons-cli/compare/rel/commons-cli-1.9.0...rel/commons-cli-1.10.0) --- updated-dependencies: - dependency-name: commons-cli:commons-cli dependency-version: 1.10.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: apache-commons ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index ab300551b21d..a670498c5730 100644 --- a/pom.xml +++ b/pom.xml @@ -1471,7 +1471,7 @@ commons-cli commons-cli - 1.9.0 + 1.10.0 commons-codec From c07bf84e059d5662f7f2b3d4053e7a4199f1f117 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Sep 2025 05:46:45 +0000 Subject: [PATCH 869/979] Bump the jakarta group with 2 updates Bumps the jakarta group with 2 updates: [jakarta.mail:jakarta.mail-api](https://github.com/jakartaee/mail-api) and org.eclipse.angus:jakarta.mail. Updates `jakarta.mail:jakarta.mail-api` from 2.1.3 to 2.1.4 - [Release notes](https://github.com/jakartaee/mail-api/releases) - [Commits](https://github.com/jakartaee/mail-api/compare/2.1.3...2.1.4) Updates `org.eclipse.angus:jakarta.mail` from 2.0.3 to 2.0.4 --- updated-dependencies: - dependency-name: jakarta.mail:jakarta.mail-api dependency-version: 2.1.4 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: jakarta - dependency-name: org.eclipse.angus:jakarta.mail dependency-version: 2.0.4 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: jakarta ... Signed-off-by: dependabot[bot] --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index ab300551b21d..0d34e2c1319a 100644 --- a/pom.xml +++ b/pom.xml @@ -1557,14 +1557,14 @@ jakarta.mail jakarta.mail-api - 2.1.3 + 2.1.4 provided org.eclipse.angus jakarta.mail - 2.0.3 + 2.0.4 jakarta.servlet From 5fc9d9d8e018f7a4658e203ae196d16419c6c924 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Sep 2025 05:48:16 +0000 Subject: [PATCH 870/979] Bump the spring group with 25 updates Bumps the spring group with 25 updates: | Package | From | To | | --- | --- | --- | | [org.springframework:spring-orm](https://github.com/spring-projects/spring-framework) | `6.2.9` | `6.2.10` | | [org.springframework:spring-core](https://github.com/spring-projects/spring-framework) | `6.2.9` | `6.2.10` | | [org.springframework:spring-beans](https://github.com/spring-projects/spring-framework) | `6.2.9` | `6.2.10` | | [org.springframework:spring-aop](https://github.com/spring-projects/spring-framework) | `6.2.9` | `6.2.10` | | [org.springframework:spring-context](https://github.com/spring-projects/spring-framework) | `6.2.9` | `6.2.10` | | [org.springframework:spring-context-support](https://github.com/spring-projects/spring-framework) | `6.2.9` | `6.2.10` | | [org.springframework:spring-tx](https://github.com/spring-projects/spring-framework) | `6.2.9` | `6.2.10` | | [org.springframework:spring-jdbc](https://github.com/spring-projects/spring-framework) | `6.2.9` | `6.2.10` | | [org.springframework:spring-web](https://github.com/spring-projects/spring-framework) | `6.2.9` | `6.2.10` | | [org.springframework:spring-webmvc](https://github.com/spring-projects/spring-framework) | `6.2.9` | `6.2.10` | | [org.springframework:spring-expression](https://github.com/spring-projects/spring-framework) | `6.2.9` | `6.2.10` | | [org.springframework:spring-test](https://github.com/spring-projects/spring-framework) | `6.2.9` | `6.2.10` | | [org.springframework.boot:spring-boot-starter-test](https://github.com/spring-projects/spring-boot) | `3.5.4` | `3.5.5` | | [org.springframework.boot:spring-boot-starter-tomcat](https://github.com/spring-projects/spring-boot) | `3.5.4` | `3.5.5` | | [org.springframework.boot:spring-boot-maven-plugin](https://github.com/spring-projects/spring-boot) | `3.5.4` | `3.5.5` | | [org.springframework.boot:spring-boot-starter-cache](https://github.com/spring-projects/spring-boot) | `3.5.4` | `3.5.5` | | [org.springframework.boot:spring-boot-starter](https://github.com/spring-projects/spring-boot) | `3.5.4` | `3.5.5` | | [org.springframework.boot:spring-boot-starter-thymeleaf](https://github.com/spring-projects/spring-boot) | `3.5.4` | `3.5.5` | | [org.springframework.boot:spring-boot-starter-web](https://github.com/spring-projects/spring-boot) | `3.5.4` | `3.5.5` | | [org.springframework.boot:spring-boot-starter-data-rest](https://github.com/spring-projects/spring-boot) | `3.5.4` | `3.5.5` | | [org.springframework.boot:spring-boot-starter-security](https://github.com/spring-projects/spring-boot) | `3.5.4` | `3.5.5` | | [org.springframework.boot:spring-boot-starter-aop](https://github.com/spring-projects/spring-boot) | `3.5.4` | `3.5.5` | | [org.springframework.boot:spring-boot-starter-actuator](https://github.com/spring-projects/spring-boot) | `3.5.4` | `3.5.5` | | [org.springframework.boot:spring-boot-starter-log4j2](https://github.com/spring-projects/spring-boot) | `3.5.4` | `3.5.5` | | [org.springframework.security:spring-security-test](https://github.com/spring-projects/spring-security) | `6.5.2` | `6.5.3` | Updates `org.springframework:spring-orm` from 6.2.9 to 6.2.10 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.9...v6.2.10) Updates `org.springframework:spring-core` from 6.2.9 to 6.2.10 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.9...v6.2.10) Updates `org.springframework:spring-beans` from 6.2.9 to 6.2.10 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.9...v6.2.10) Updates `org.springframework:spring-aop` from 6.2.9 to 6.2.10 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.9...v6.2.10) Updates `org.springframework:spring-context` from 6.2.9 to 6.2.10 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.9...v6.2.10) Updates `org.springframework:spring-context-support` from 6.2.9 to 6.2.10 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.9...v6.2.10) Updates `org.springframework:spring-tx` from 6.2.9 to 6.2.10 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.9...v6.2.10) Updates `org.springframework:spring-jdbc` from 6.2.9 to 6.2.10 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.9...v6.2.10) Updates `org.springframework:spring-web` from 6.2.9 to 6.2.10 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.9...v6.2.10) Updates `org.springframework:spring-webmvc` from 6.2.9 to 6.2.10 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.9...v6.2.10) Updates `org.springframework:spring-expression` from 6.2.9 to 6.2.10 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.9...v6.2.10) Updates `org.springframework:spring-test` from 6.2.9 to 6.2.10 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.9...v6.2.10) Updates `org.springframework:spring-core` from 6.2.9 to 6.2.10 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.9...v6.2.10) Updates `org.springframework:spring-beans` from 6.2.9 to 6.2.10 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.9...v6.2.10) Updates `org.springframework:spring-aop` from 6.2.9 to 6.2.10 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.9...v6.2.10) Updates `org.springframework:spring-context` from 6.2.9 to 6.2.10 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.9...v6.2.10) Updates `org.springframework:spring-context-support` from 6.2.9 to 6.2.10 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.9...v6.2.10) Updates `org.springframework:spring-tx` from 6.2.9 to 6.2.10 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.9...v6.2.10) Updates `org.springframework:spring-jdbc` from 6.2.9 to 6.2.10 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.9...v6.2.10) Updates `org.springframework:spring-web` from 6.2.9 to 6.2.10 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.9...v6.2.10) Updates `org.springframework:spring-webmvc` from 6.2.9 to 6.2.10 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.9...v6.2.10) Updates `org.springframework:spring-expression` from 6.2.9 to 6.2.10 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.9...v6.2.10) Updates `org.springframework:spring-test` from 6.2.9 to 6.2.10 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.9...v6.2.10) Updates `org.springframework.boot:spring-boot-starter-test` from 3.5.4 to 3.5.5 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.5.4...v3.5.5) Updates `org.springframework.boot:spring-boot-starter-tomcat` from 3.5.4 to 3.5.5 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.5.4...v3.5.5) Updates `org.springframework.boot:spring-boot-maven-plugin` from 3.5.4 to 3.5.5 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.5.4...v3.5.5) Updates `org.springframework.boot:spring-boot-starter-cache` from 3.5.4 to 3.5.5 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.5.4...v3.5.5) Updates `org.springframework.boot:spring-boot-starter` from 3.5.4 to 3.5.5 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.5.4...v3.5.5) Updates `org.springframework.boot:spring-boot-starter-thymeleaf` from 3.5.4 to 3.5.5 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.5.4...v3.5.5) Updates `org.springframework.boot:spring-boot-starter-web` from 3.5.4 to 3.5.5 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.5.4...v3.5.5) Updates `org.springframework.boot:spring-boot-starter-data-rest` from 3.5.4 to 3.5.5 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.5.4...v3.5.5) Updates `org.springframework.boot:spring-boot-starter-security` from 3.5.4 to 3.5.5 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.5.4...v3.5.5) Updates `org.springframework.boot:spring-boot-starter-aop` from 3.5.4 to 3.5.5 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.5.4...v3.5.5) Updates `org.springframework.boot:spring-boot-starter-actuator` from 3.5.4 to 3.5.5 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.5.4...v3.5.5) Updates `org.springframework.boot:spring-boot-starter-log4j2` from 3.5.4 to 3.5.5 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.5.4...v3.5.5) Updates `org.springframework.boot:spring-boot-starter-tomcat` from 3.5.4 to 3.5.5 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.5.4...v3.5.5) Updates `org.springframework.security:spring-security-test` from 6.5.2 to 6.5.3 - [Release notes](https://github.com/spring-projects/spring-security/releases) - [Changelog](https://github.com/spring-projects/spring-security/blob/main/RELEASE.adoc) - [Commits](https://github.com/spring-projects/spring-security/compare/6.5.2...6.5.3) Updates `org.springframework.boot:spring-boot-maven-plugin` from 3.5.4 to 3.5.5 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.5.4...v3.5.5) Updates `org.springframework.boot:spring-boot-starter-cache` from 3.5.4 to 3.5.5 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.5.4...v3.5.5) Updates `org.springframework.boot:spring-boot-starter` from 3.5.4 to 3.5.5 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.5.4...v3.5.5) Updates `org.springframework.boot:spring-boot-starter-thymeleaf` from 3.5.4 to 3.5.5 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.5.4...v3.5.5) Updates `org.springframework.boot:spring-boot-starter-web` from 3.5.4 to 3.5.5 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.5.4...v3.5.5) Updates `org.springframework.boot:spring-boot-starter-data-rest` from 3.5.4 to 3.5.5 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.5.4...v3.5.5) Updates `org.springframework.boot:spring-boot-starter-security` from 3.5.4 to 3.5.5 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.5.4...v3.5.5) Updates `org.springframework.boot:spring-boot-starter-aop` from 3.5.4 to 3.5.5 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.5.4...v3.5.5) Updates `org.springframework.boot:spring-boot-starter-actuator` from 3.5.4 to 3.5.5 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.5.4...v3.5.5) Updates `org.springframework.boot:spring-boot-starter-log4j2` from 3.5.4 to 3.5.5 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.5.4...v3.5.5) --- updated-dependencies: - dependency-name: org.springframework:spring-orm dependency-version: 6.2.10 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-core dependency-version: 6.2.10 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-beans dependency-version: 6.2.10 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-aop dependency-version: 6.2.10 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-context dependency-version: 6.2.10 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-context-support dependency-version: 6.2.10 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-tx dependency-version: 6.2.10 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-jdbc dependency-version: 6.2.10 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-web dependency-version: 6.2.10 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-webmvc dependency-version: 6.2.10 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-expression dependency-version: 6.2.10 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-test dependency-version: 6.2.10 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-core dependency-version: 6.2.10 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-beans dependency-version: 6.2.10 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-aop dependency-version: 6.2.10 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-context dependency-version: 6.2.10 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-context-support dependency-version: 6.2.10 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-tx dependency-version: 6.2.10 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-jdbc dependency-version: 6.2.10 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-web dependency-version: 6.2.10 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-webmvc dependency-version: 6.2.10 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-expression dependency-version: 6.2.10 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-test dependency-version: 6.2.10 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter-test dependency-version: 3.5.5 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter-tomcat dependency-version: 3.5.5 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-maven-plugin dependency-version: 3.5.5 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter-cache dependency-version: 3.5.5 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter dependency-version: 3.5.5 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter-thymeleaf dependency-version: 3.5.5 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter-web dependency-version: 3.5.5 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter-data-rest dependency-version: 3.5.5 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter-security dependency-version: 3.5.5 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter-aop dependency-version: 3.5.5 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter-actuator dependency-version: 3.5.5 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter-log4j2 dependency-version: 3.5.5 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter-tomcat dependency-version: 3.5.5 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.security:spring-security-test dependency-version: 6.5.3 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-maven-plugin dependency-version: 3.5.5 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter-cache dependency-version: 3.5.5 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter dependency-version: 3.5.5 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter-thymeleaf dependency-version: 3.5.5 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter-web dependency-version: 3.5.5 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter-data-rest dependency-version: 3.5.5 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter-security dependency-version: 3.5.5 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter-aop dependency-version: 3.5.5 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter-actuator dependency-version: 3.5.5 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter-log4j2 dependency-version: 3.5.5 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring ... Signed-off-by: dependabot[bot] --- pom.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pom.xml b/pom.xml index ab300551b21d..2718aafa2e5c 100644 --- a/pom.xml +++ b/pom.xml @@ -19,9 +19,9 @@ 17 - 6.2.9 - 3.5.4 - 6.5.2 + 6.2.10 + 3.5.5 + 6.5.3 6.4.10.Final 8.0.2.Final 42.7.7 From 37ace6845ed0c6b111b21ef7c167167b8c8ef5fc Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Sep 2025 05:50:11 +0000 Subject: [PATCH 871/979] Bump jetty.version from 9.4.57.v20241219 to 9.4.58.v20250814 Bumps `jetty.version` from 9.4.57.v20241219 to 9.4.58.v20250814. Updates `org.eclipse.jetty:jetty-server` from 9.4.57.v20241219 to 9.4.58.v20250814 Updates `org.eclipse.jetty:jetty-deploy` from 9.4.57.v20241219 to 9.4.58.v20250814 Updates `org.eclipse.jetty:jetty-http` from 9.4.57.v20241219 to 9.4.58.v20250814 Updates `org.eclipse.jetty:jetty-io` from 9.4.57.v20241219 to 9.4.58.v20250814 Updates `org.eclipse.jetty:jetty-servlet` from 9.4.57.v20241219 to 9.4.58.v20250814 Updates `org.eclipse.jetty:jetty-util` from 9.4.57.v20241219 to 9.4.58.v20250814 Updates `org.eclipse.jetty:jetty-webapp` from 9.4.57.v20241219 to 9.4.58.v20250814 Updates `org.eclipse.jetty.http2:http2-common` from 9.4.57.v20241219 to 9.4.58.v20250814 --- updated-dependencies: - dependency-name: org.eclipse.jetty:jetty-server dependency-version: 9.4.58.v20250814 dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.eclipse.jetty:jetty-deploy dependency-version: 9.4.58.v20250814 dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.eclipse.jetty:jetty-http dependency-version: 9.4.58.v20250814 dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.eclipse.jetty:jetty-io dependency-version: 9.4.58.v20250814 dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.eclipse.jetty:jetty-servlet dependency-version: 9.4.58.v20250814 dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.eclipse.jetty:jetty-util dependency-version: 9.4.58.v20250814 dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.eclipse.jetty:jetty-webapp dependency-version: 9.4.58.v20250814 dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.eclipse.jetty.http2:http2-common dependency-version: 9.4.58.v20250814 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index ab300551b21d..d145cbd13339 100644 --- a/pom.xml +++ b/pom.xml @@ -38,7 +38,7 @@ 4.0.5 1.1.1 - 9.4.57.v20241219 + 9.4.58.v20250814 2.25.1 2.0.34 1.19.0 From b099da697649386a258a40f37b06d66b9312e37c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Sep 2025 06:00:34 +0000 Subject: [PATCH 872/979] Bump jersey.version from 3.1.10 to 3.1.11 Bumps `jersey.version` from 3.1.10 to 3.1.11. Updates `org.glassfish.jersey.core:jersey-client` from 3.1.10 to 3.1.11 Updates `org.glassfish.jersey.inject:jersey-hk2` from 3.1.10 to 3.1.11 --- updated-dependencies: - dependency-name: org.glassfish.jersey.core:jersey-client dependency-version: 3.1.11 dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.glassfish.jersey.inject:jersey-hk2 dependency-version: 3.1.11 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index ab300551b21d..12edd902c0d2 100644 --- a/pom.xml +++ b/pom.xml @@ -48,7 +48,7 @@ 1.81 8.0.1 - 3.1.10 + 3.1.11 2.9.0 From 58d26cf56b98068393aee8073ad8aa352450fc27 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Sep 2025 06:02:42 +0000 Subject: [PATCH 873/979] Bump org.checkerframework:checker-qual from 3.49.5 to 3.50.0 Bumps [org.checkerframework:checker-qual](https://github.com/typetools/checker-framework) from 3.49.5 to 3.50.0. - [Release notes](https://github.com/typetools/checker-framework/releases) - [Changelog](https://github.com/typetools/checker-framework/blob/master/docs/CHANGELOG.md) - [Commits](https://github.com/typetools/checker-framework/compare/checker-framework-3.49.5...checker-framework-3.50.0) --- updated-dependencies: - dependency-name: org.checkerframework:checker-qual dependency-version: 3.50.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index ab300551b21d..ca07d250c2b2 100644 --- a/pom.xml +++ b/pom.xml @@ -1348,7 +1348,7 @@ org.checkerframework checker-qual - 3.49.5 + 3.50.0 2.19.2 From 885c06b56f56dc305820432e3bfdca037a626104 Mon Sep 17 00:00:00 2001 From: Sascha Szott Date: Mon, 25 Aug 2025 14:16:56 +0200 Subject: [PATCH 875/979] Implement logging for index out-of-range Add logging for out-of-range index when removing metadata values. (cherry picked from commit 7692b02b12ddcdc50ec22e3ccd060240734202ed) --- .../impl/MetadataValueRemovePatchOperation.java | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/factory/impl/MetadataValueRemovePatchOperation.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/factory/impl/MetadataValueRemovePatchOperation.java index 1660a5455aea..18bc1df66c1c 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/factory/impl/MetadataValueRemovePatchOperation.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/factory/impl/MetadataValueRemovePatchOperation.java @@ -11,6 +11,8 @@ import java.util.Arrays; import java.util.List; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.dspace.app.rest.model.MetadataValueRest; import org.dspace.content.DSpaceObject; import org.dspace.content.Item; @@ -27,6 +29,8 @@ public abstract class MetadataValueRemovePatchOperation extends RemovePatchOperation { + private static final Logger log = LogManager.getLogger(); + @Override protected Class getArrayClassForEvaluation() { return MetadataValueRest[].class; @@ -42,7 +46,12 @@ protected void deleteValue(Context context, DSO source, String target, int index List mm = getDSpaceObjectService().getMetadata(source, metadata[0], metadata[1], metadata[2], Item.ANY); if (index != -1) { - getDSpaceObjectService().removeMetadataValues(context, source, Arrays.asList(mm.get(index))); + if (index < mm.size()) { + getDSpaceObjectService().removeMetadataValues(context, source, Arrays.asList(mm.get(index))); + } else { + log.warn("value of index ({}) is out of range of the metadata value list of size {} (target: {})", + index, mm.size(), target); + } } else { getDSpaceObjectService().clearMetadata(context, source, metadata[0], metadata[1], metadata[2], Item.ANY); } From 286202e8df24b43667361f9837f1f72a97037eb7 Mon Sep 17 00:00:00 2001 From: Alexandre Vryghem Date: Wed, 27 Aug 2025 15:01:35 +0200 Subject: [PATCH 876/979] 133421: Removed database connection leak on unsuccessful login --- .../app/rest/security/OrcidLoginFilter.java | 1 + .../rest/security/StatelessLoginFilter.java | 24 +++++++++++++++++++ 2 files changed, 25 insertions(+) diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/security/OrcidLoginFilter.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/security/OrcidLoginFilter.java index 70496b9dba23..63ba9a2de0eb 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/security/OrcidLoginFilter.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/security/OrcidLoginFilter.java @@ -89,6 +89,7 @@ protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServle String baseRediredirectUrl = configurationService.getProperty("dspace.ui.url"); String redirectUrl = baseRediredirectUrl + "/error?status=401&code=orcid.generic-error"; response.sendRedirect(redirectUrl); // lgtm [java/unvalidated-url-redirection] + this.closeOpenContext(request); } else { super.unsuccessfulAuthentication(request, response, failed); } diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/security/StatelessLoginFilter.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/security/StatelessLoginFilter.java index cfae6bfcb42b..caead66df25f 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/security/StatelessLoginFilter.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/security/StatelessLoginFilter.java @@ -8,6 +8,7 @@ package org.dspace.app.rest.security; import java.io.IOException; +import java.sql.SQLException; import jakarta.servlet.FilterChain; import jakarta.servlet.ServletException; @@ -15,6 +16,8 @@ import jakarta.servlet.http.HttpServletResponse; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import org.dspace.app.rest.utils.ContextUtil; +import org.dspace.core.Context; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.core.Authentication; import org.springframework.security.core.AuthenticationException; @@ -133,6 +136,27 @@ protected void unsuccessfulAuthentication(HttpServletRequest request, response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Authentication failed!"); log.error("Authentication failed (status:{})", HttpServletResponse.SC_UNAUTHORIZED, failed); + this.closeOpenContext(request); + } + + /** + * Manually closes the open {@link Context} if one exists. We need to do this manually because + * {@link #continueChainBeforeSuccessfulAuthentication} is {@code false} by default, which prevents the + * {@link org.dspace.app.rest.filter.DSpaceRequestContextFilter} from being called. Without this call, the request + * would leave an open database connection. + * + * @param request The current request. + */ + protected void closeOpenContext(HttpServletRequest request) { + if (ContextUtil.isContextAvailable(request)) { + try (Context context = ContextUtil.obtainContext(request)) { + if (context != null && context.isValid()) { + context.complete(); + } + } catch (SQLException e) { + throw new RuntimeException(e); + } + } } } From ba5b147889937d2d357c7f533deaccf50ecd971a Mon Sep 17 00:00:00 2001 From: Jesiel Viana Date: Fri, 9 May 2025 14:47:36 -0300 Subject: [PATCH 877/979] Update README.md (cherry picked from commit 7011556503f89fa2ea67370d15de4302e63ecd1f) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 1d93abe49948..c5e7e9277203 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ -# DSpace +# DSpace at LA Referencia [![Build Status](https://github.com/DSpace/DSpace/workflows/Build/badge.svg)](https://github.com/DSpace/DSpace/actions?query=workflow%3ABuild) From fc93298676b1d664bbdd27039b7ff39493225105 Mon Sep 17 00:00:00 2001 From: Jesiel Viana Date: Mon, 14 Jul 2025 13:47:49 -0300 Subject: [PATCH 878/979] Update DSpace integration to use ROR API v2 (cherry picked from commit 53713629a617b8d963652b504f213c3a6239c9fb) --- ...itionalArrayElementAttributeProcessor.java | 132 ++++++++++++++++++ dspace/config/modules/external-providers.cfg | 2 +- dspace/config/spring/api/ror-integration.xml | 56 +++++++- 3 files changed, 183 insertions(+), 7 deletions(-) create mode 100644 dspace-api/src/main/java/org/dspace/importer/external/metadatamapping/contributor/ConditionalArrayElementAttributeProcessor.java diff --git a/dspace-api/src/main/java/org/dspace/importer/external/metadatamapping/contributor/ConditionalArrayElementAttributeProcessor.java b/dspace-api/src/main/java/org/dspace/importer/external/metadatamapping/contributor/ConditionalArrayElementAttributeProcessor.java new file mode 100644 index 000000000000..af8af4d37ebf --- /dev/null +++ b/dspace-api/src/main/java/org/dspace/importer/external/metadatamapping/contributor/ConditionalArrayElementAttributeProcessor.java @@ -0,0 +1,132 @@ +/** + * The contents of this file are subject to the license and copyright + * detailed in the LICENSE and NOTICE files at the root of the source + * tree and available online at + * + * http://www.dspace.org/license/ + */ +package org.dspace.importer.external.metadatamapping.contributor; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Iterator; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.apache.commons.lang3.StringUtils; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +/** + * This Processor extracts values from a JSON array, but only when a condition + * on another attribute is met. For example, to extract all values of + * /names/value where /names/types contains "ror_display". + * + * Configurable via: + * pathToArray: e.g., /items/0/names + * elementAttribute: e.g., /value + * filterAttribute: e.g., /types + * requiredValueInFilter: e.g., ror_display + * + * Supports filtering when the filter attribute is either a JSON array or a single string. + * + * Example JSON: + * { + * "items": [{ + * "names": [ + * { "types": ["label", "ror_display"], "value": "Universidade Federal do Piauí" }, + * { "types": ["label"], "value": "UFPI" } + * ] + * }] + * } + * This processor can extract "Universidade Federal do Piauí" using proper configuration. + * + * Author: Jesiel (based on Mykhaylo Boychuk’s original processor) + */ +public class ConditionalArrayElementAttributeProcessor implements JsonPathMetadataProcessor { + + private static final Logger log = LogManager.getLogger(); + + private String pathToArray; + private String elementAttribute; + private String filterAttribute; + private String requiredValueInFilter; + + @Override + public Collection processMetadata(String json) { + System.out.println("pathToArray: " + pathToArray); + System.out.println("elementAttribute: " + elementAttribute); + System.out.println("filterAttribute: " + filterAttribute); + System.out.println("requiredValueInFilter: " + requiredValueInFilter); + JsonNode rootNode = convertStringJsonToJsonNode(json); + Collection results = new ArrayList<>(); + + if (rootNode == null) { + return results; + } + + Iterator array = rootNode.at(pathToArray).iterator(); + System.out.println("array: " + array.toString()); + while (array.hasNext()) { + JsonNode element = array.next(); + JsonNode filterNode = element.at(filterAttribute); + + boolean match = false; + + if (filterNode.isArray()) { + for (JsonNode filterValue : filterNode) { + if (requiredValueInFilter.equalsIgnoreCase(filterValue.textValue())) { + match = true; + break; + } + } + } else if (filterNode.isTextual()) { + if (requiredValueInFilter.equalsIgnoreCase(filterNode.textValue())) { + match = true; + } + } + + if (match) { + JsonNode valueNode = element.at(elementAttribute); + if (valueNode.isTextual()) { + results.add(valueNode.textValue()); + } else if (valueNode.isArray()) { + for (JsonNode item : valueNode) { + if (item.isTextual() && StringUtils.isNotBlank(item.textValue())) { + results.add(item.textValue()); + } + } + } + } + } + + return results; + } + + private JsonNode convertStringJsonToJsonNode(String json) { + ObjectMapper mapper = new ObjectMapper(); + try { + return mapper.readTree(json); + } catch (JsonProcessingException e) { + log.error("Unable to process JSON response.", e); + return null; + } + } + + public void setPathToArray(String pathToArray) { + this.pathToArray = pathToArray; + } + + public void setElementAttribute(String elementAttribute) { + this.elementAttribute = elementAttribute; + } + + public void setFilterAttribute(String filterAttribute) { + this.filterAttribute = filterAttribute; + } + + public void setRequiredValueInFilter(String requiredValueInFilter) { + this.requiredValueInFilter = requiredValueInFilter; + } +} \ No newline at end of file diff --git a/dspace/config/modules/external-providers.cfg b/dspace/config/modules/external-providers.cfg index 04f5c54e9848..254207febdd6 100644 --- a/dspace/config/modules/external-providers.cfg +++ b/dspace/config/modules/external-providers.cfg @@ -98,5 +98,5 @@ datacite.timeout = 180000 #--------------------------- ROR -------------------------------# #---------------------------------------------------------------# -ror.orgunit-import.api-url = https://api.ror.org/organizations +ror.orgunit-import.api-url = https://api.ror.org/v2/organizations ################################################################# diff --git a/dspace/config/spring/api/ror-integration.xml b/dspace/config/spring/api/ror-integration.xml index ff554612052e..8d19d4d10b29 100644 --- a/dspace/config/spring/api/ror-integration.xml +++ b/dspace/config/spring/api/ror-integration.xml @@ -26,8 +26,17 @@ - + + + + + + + + + + @@ -42,15 +51,31 @@ - + + + + + + + + + + - + + + + + + + + @@ -66,7 +91,12 @@ - + + + + + + @@ -82,7 +112,14 @@ - + + + + + + + + @@ -90,7 +127,14 @@ - + + + + + + + + From abbfde862308e474d33b36fbfcc934fc988cbb1b Mon Sep 17 00:00:00 2001 From: Jesiel Viana Date: Wed, 16 Jul 2025 09:28:58 -0300 Subject: [PATCH 879/979] refactoring and add addressLocality metadata (cherry picked from commit e89b00f8e64527f3b5faf2f54d699090657b1533) --- ...itionalArrayElementAttributeProcessor.java | 13 +++---- dspace/config/spring/api/ror-integration.xml | 35 +++++++++++++------ 2 files changed, 28 insertions(+), 20 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/importer/external/metadatamapping/contributor/ConditionalArrayElementAttributeProcessor.java b/dspace-api/src/main/java/org/dspace/importer/external/metadatamapping/contributor/ConditionalArrayElementAttributeProcessor.java index af8af4d37ebf..f813a34c89b5 100644 --- a/dspace-api/src/main/java/org/dspace/importer/external/metadatamapping/contributor/ConditionalArrayElementAttributeProcessor.java +++ b/dspace-api/src/main/java/org/dspace/importer/external/metadatamapping/contributor/ConditionalArrayElementAttributeProcessor.java @@ -24,7 +24,7 @@ * /names/value where /names/types contains "ror_display". * * Configurable via: - * pathToArray: e.g., /items/0/names + * pathToArray: e.g., /names * elementAttribute: e.g., /value * filterAttribute: e.g., /types * requiredValueInFilter: e.g., ror_display @@ -35,12 +35,12 @@ * { * "items": [{ * "names": [ - * { "types": ["label", "ror_display"], "value": "Universidade Federal do Piauí" }, - * { "types": ["label"], "value": "UFPI" } + * { "types": ["label", "ror_display"], "value": "Instituto Federal do Piauí" }, + * { "types": ["acronym"], "value": "IFPI" } * ] * }] * } - * This processor can extract "Universidade Federal do Piauí" using proper configuration. + * This processor can extract "Instituto Federal do Piauí" using proper configuration. * * Author: Jesiel (based on Mykhaylo Boychuk’s original processor) */ @@ -55,10 +55,6 @@ public class ConditionalArrayElementAttributeProcessor implements JsonPathMetada @Override public Collection processMetadata(String json) { - System.out.println("pathToArray: " + pathToArray); - System.out.println("elementAttribute: " + elementAttribute); - System.out.println("filterAttribute: " + filterAttribute); - System.out.println("requiredValueInFilter: " + requiredValueInFilter); JsonNode rootNode = convertStringJsonToJsonNode(json); Collection results = new ArrayList<>(); @@ -67,7 +63,6 @@ public Collection processMetadata(String json) { } Iterator array = rootNode.at(pathToArray).iterator(); - System.out.println("array: " + array.toString()); while (array.hasNext()) { JsonNode element = array.next(); JsonNode filterNode = element.at(filterAttribute); diff --git a/dspace/config/spring/api/ror-integration.xml b/dspace/config/spring/api/ror-integration.xml index 8d19d4d10b29..f205817e8c7b 100644 --- a/dspace/config/spring/api/ror-integration.xml +++ b/dspace/config/spring/api/ror-integration.xml @@ -18,29 +18,29 @@ + + - - - + @@ -49,26 +49,24 @@ + - - - + - @@ -81,6 +79,7 @@ + @@ -89,9 +88,9 @@ + - @@ -102,6 +101,20 @@ + + + + + + + + + + + + + + @@ -110,9 +123,9 @@ + - @@ -125,9 +138,9 @@ + - @@ -140,6 +153,7 @@ + @@ -147,7 +161,6 @@ - From 3266f7538e089d954ce25cad7b002bb406c3b718 Mon Sep 17 00:00:00 2001 From: Jesiel Viana Date: Wed, 16 Jul 2025 09:31:21 -0300 Subject: [PATCH 880/979] revert the README (cherry picked from commit d9e7b672e5b29ab54a8c2b35f0d4f966bab920b7) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index c5e7e9277203..1d93abe49948 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ -# DSpace at LA Referencia +# DSpace [![Build Status](https://github.com/DSpace/DSpace/workflows/Build/badge.svg)](https://github.com/DSpace/DSpace/actions?query=workflow%3ABuild) From 2d8e209d7c5c53c4ec67b54d793404416f69a00d Mon Sep 17 00:00:00 2001 From: Jesiel Viana Date: Wed, 16 Jul 2025 13:59:00 -0300 Subject: [PATCH 881/979] update integration tests to use ROR API v2 (cherry picked from commit afb4d2e54aaa9611f068b3db9d3a017cc3509bf6) --- .../RorImportMetadataSourceServiceIT.java | 61 +- .../org/dspace/app/rest/ror-record.json | 174 +- .../org/dspace/app/rest/ror-records.json | 3613 ++++++++--------- 3 files changed, 1718 insertions(+), 2130 deletions(-) diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/RorImportMetadataSourceServiceIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/RorImportMetadataSourceServiceIT.java index 9a8d14f3d658..84236fd58fe8 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/RorImportMetadataSourceServiceIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/RorImportMetadataSourceServiceIT.java @@ -45,6 +45,7 @@ public void tesGetRecords() throws Exception { CloseableHttpClient originalHttpClient = liveImportClient.getHttpClient(); CloseableHttpClient httpClient = Mockito.mock(CloseableHttpClient.class); + //ror-records.json is the result of a GET request to https://api.ror.org/v2/organizations at 16/07/2025. try (InputStream file = getClass().getResourceAsStream("ror-records.json")) { String jsonResponse = IOUtils.toString(file, Charset.defaultCharset()); @@ -59,21 +60,19 @@ public void tesGetRecords() throws Exception { ImportRecord record = recordsImported.iterator().next(); - assertThat(record.getValueList(), hasSize(11)); + assertThat(record.getValueList(), hasSize(9)); + + assertThat(record.getSingleValue("organization.legalName"), + is("University American College Skopje")); + assertThat(record.getSingleValue("organization.identifier.ror"), is("https://ror.org/05hknds03")); + assertThat(record.getSingleValue("organization.alternateName"), is("UACS")); + assertThat(record.getSingleValue("organization.url"), is("https://uacs.edu.mk")); + assertThat(record.getSingleValue("dc.type"), is("education")); + assertThat(record.getSingleValue("organization.address.addressCountry"), is("MK")); + assertThat(record.getSingleValue("organization.address.addressLocality"), is("Skopje")); + assertThat(record.getSingleValue("organization.foundingDate"), is("2005")); + assertThat(record.getSingleValue("organization.identifier.isni"), is("0000 0004 0446 4427")); - assertThat( - record.getSingleValue("organization.legalName"), - is("The University of Texas") - ); - assertThat(record.getSingleValue("organization.identifier.ror"), is("https://ror.org/02f6dcw23")); - assertThat(record.getSingleValue("organization.alternateName"), is("UTHSCSA")); - assertThat(record.getSingleValue("organization.url"), is("http://www.uthscsa.edu/")); - assertThat(record.getSingleValue("dc.type"), is("Education")); - assertThat(record.getSingleValue("organization.address.addressCountry"), is("US")); - assertThat(record.getSingleValue("organization.foundingDate"), is("1959")); - assertThat(record.getValue("organization", "identifier", "crossrefid"), hasSize(2)); - assertThat(record.getSingleValue("organization.identifier.isni"), is("0000 0001 0629 5880")); - assertThat(record.getSingleValue("organization.parentOrganization"), is("The University of Texas System")); } finally { liveImportClient.setHttpClient(originalHttpClient); @@ -96,7 +95,7 @@ public void tesCount() throws Exception { context.restoreAuthSystemState(); Integer count = rorServiceImpl.count("test"); - assertThat(count, equalTo(200)); + assertThat(count, equalTo(115409)); } finally { liveImportClient.setHttpClient(originalHttpClient); } @@ -110,6 +109,8 @@ public void tesGetRecord() throws Exception { try (InputStream file = getClass().getResourceAsStream("ror-record.json")) { + System.out.println("file = " + file.toString()); + String jsonResponse = IOUtils.toString(file, Charset.defaultCharset()); liveImportClient.setHttpClient(httpClient); @@ -117,22 +118,20 @@ public void tesGetRecord() throws Exception { when(httpClient.execute(ArgumentMatchers.any())).thenReturn(response); context.restoreAuthSystemState(); - ImportRecord record = rorServiceImpl.getRecord("https://ror.org/01sps7q28"); - assertThat(record.getValueList(), hasSize(9)); - assertThat( - record.getSingleValue("organization.legalName"), - is("The University of Texas Health Science Center at Tyler") - ); - assertThat(record.getSingleValue("organization.identifier.ror"), is("https://ror.org/01sps7q28")); - assertThat(record.getSingleValue("organization.alternateName"), is("UTHSCT")); - assertThat(record.getSingleValue("organization.url"), - is("https://www.utsystem.edu/institutions/university-texas-health-science-center-tyler")); - assertThat(record.getSingleValue("dc.type"), is("Healthcare")); + ImportRecord record = rorServiceImpl.getRecord("https://ror.org/02437s643"); + + assertThat(record.getValueList(), hasSize(10)); + assertThat(record.getSingleValue("organization.legalName"), + is("University of Illinois Chicago, Rockford campus")); + assertThat(record.getSingleValue("organization.identifier.ror"), is("https://ror.org/02437s643")); + assertThat(record.getSingleValue("organization.alternateName"), is("UICOMR")); + assertThat(record.getSingleValue("organization.url"), is("https://www.uillinois.edu")); + assertThat(record.getSingleValue("dc.type"), is("education")); assertThat(record.getSingleValue("organization.address.addressCountry"), is("US")); - assertThat(record.getSingleValue("organization.foundingDate"), is("1947")); - assertThat(record.getSingleValue("organization.identifier.isni"), is("0000 0000 9704 5790")); - assertThat(record.getSingleValue("organization.parentOrganization"), is("The University of Texas System")); - + assertThat(record.getSingleValue("organization.address.addressLocality"), is("Rockford")); + assertThat(record.getSingleValue("organization.foundingDate"), is("1972")); + assertThat(record.getSingleValue("organization.identifier.isni"), is("0000 0000 9018 7542")); + assertThat(record.getSingleValue("organization.parentOrganization"), is("University of Illinois Chicago")); } finally { liveImportClient.setHttpClient(originalHttpClient); } @@ -152,7 +151,7 @@ public void tesGetRecordsCount() throws Exception { context.restoreAuthSystemState(); int tot = rorServiceImpl.getRecordsCount("test query"); - assertEquals(200, tot); + assertEquals(115409, tot); } finally { liveImportClient.setHttpClient(originalHttpClient); } diff --git a/dspace-server-webapp/src/test/resources/org/dspace/app/rest/ror-record.json b/dspace-server-webapp/src/test/resources/org/dspace/app/rest/ror-record.json index 4d0cd97fd5b6..50df107f7d97 100644 --- a/dspace-server-webapp/src/test/resources/org/dspace/app/rest/ror-record.json +++ b/dspace-server-webapp/src/test/resources/org/dspace/app/rest/ror-record.json @@ -1,107 +1,93 @@ { - "id": "https://ror.org/01sps7q28", - "name": "The University of Texas Health Science Center at Tyler", - "email_address": null, - "ip_addresses": [ - - ], - "established": 1947, - "types": [ - "Healthcare" + "admin": { + "created": { + "date": "2018-11-14", + "schema_version": "1.0" + }, + "last_modified": { + "date": "2024-12-11", + "schema_version": "2.1" + } + }, + "domains": [], + "established": 1972, + "external_ids": [ + { + "all": [ + "grid.430864.d" + ], + "preferred": "grid.430864.d", + "type": "grid" + }, + { + "all": [ + "0000 0000 9018 7542" + ], + "preferred": null, + "type": "isni" + } ], - "relationships": [ + "id": "https://ror.org/02437s643", + "links": [ { - "label": "The University of Texas System", - "type": "Parent", - "id": "https://ror.org/01gek1696" + "type": "website", + "value": "https://www.uillinois.edu" } ], - "addresses": [ + "locations": [ { - "lat": 32.426014, - "lng": -95.212728, - "state": "Texas", - "state_code": "US-TX", - "city": "Tyler", - "geonames_city": { - "id": 4738214, - "city": "Tyler", - "geonames_admin1": { - "name": "Texas", - "id": 4736286, - "ascii_name": "Texas", - "code": "US.TX" - }, - "geonames_admin2": { - "name": "Smith County", - "id": 4729130, - "ascii_name": "Smith County", - "code": "US.TX.423" - }, - "license": { - "attribution": "Data from geonames.org under a CC-BY 4.0 license", - "license": "https://creativecommons.org/licenses/by/4.0/" - }, - "nuts_level1": { - "name": null, - "code": null - }, - "nuts_level2": { - "name": null, - "code": null - }, - "nuts_level3": { - "name": null, - "code": null - } + "geonames_details": { + "continent_code": "NA", + "continent_name": "North America", + "country_code": "US", + "country_name": "United States", + "country_subdivision_code": "IL", + "country_subdivision_name": "Illinois", + "lat": 42.27113, + "lng": -89.094, + "name": "Rockford" }, - "postcode": null, - "primary": false, - "line": null, - "country_geonames_id": 6252001 + "geonames_id": 4907959 } ], - "links": [ - "https://www.utsystem.edu/institutions/university-texas-health-science-center-tyler" - ], - "aliases": [ - "East Texas Tuberculosis Sanitarium", - "UT Health Northeast" - ], - "acronyms": [ - "UTHSCT" - ], - "status": "active", - "wikipedia_url": "https://en.wikipedia.org/wiki/University_of_Texas_Health_Science_Center_at_Tyler", - "labels": [ - - ], - "country": { - "country_name": "United States", - "country_code": "US" - }, - "external_ids": { - "ISNI": { - "preferred": null, - "all": [ - "0000 0000 9704 5790" - ] + "names": [ + { + "lang": null, + "types": [ + "acronym" + ], + "value": "UICOMR" }, - "OrgRef": { - "preferred": null, - "all": [ - "3446655" - ] + { + "lang": "en", + "types": [ + "ror_display", + "label" + ], + "value": "University of Illinois Chicago, Rockford campus" }, - "Wikidata": { - "preferred": null, - "all": [ - "Q7896437" - ] + { + "lang": "en", + "types": [ + "alias" + ], + "value": "University of Illinois at Rockford" + } + ], + "relationships": [ + { + "label": "University of Illinois Chicago", + "type": "parent", + "id": "https://ror.org/02mpq6x41" }, - "GRID": { - "preferred": "grid.267310.1", - "all": "grid.267310.1" + { + "label": "Swedish American Hospital", + "type": "related", + "id": "https://ror.org/05scd7d31" } - } -} + ], + "status": "active", + "types": [ + "education" + ] +} \ No newline at end of file diff --git a/dspace-server-webapp/src/test/resources/org/dspace/app/rest/ror-records.json b/dspace-server-webapp/src/test/resources/org/dspace/app/rest/ror-records.json index 5f93bb7d07a0..46ffbbe9b844 100644 --- a/dspace-server-webapp/src/test/resources/org/dspace/app/rest/ror-records.json +++ b/dspace-server-webapp/src/test/resources/org/dspace/app/rest/ror-records.json @@ -1,2383 +1,1986 @@ { - "number_of_results": 200, - "time_taken": 12, + "number_of_results": 115409, + "time_taken": 63, "items": [ { - "id": "https://ror.org/02f6dcw23", - "name": "The University of Texas", - "email_address": null, - "ip_addresses": [ - - ], - "established": 1959, - "types": [ - "Education" - ], - "relationships": [ - { - "label": "Audie L. Murphy Memorial VA Hospital", - "type": "Related", - "id": "https://ror.org/035xhk118" - }, - { - "label": "San Antonio Military Medical Center", - "type": "Related", - "id": "https://ror.org/00m1mwc36" - }, - { - "label": "The University of Texas System", - "type": "Parent", - "id": "https://ror.org/01gek1696" - } - ], - "addresses": [ - { - "lat": 29.508129, - "lng": -98.574025, - "state": "Texas", - "state_code": "US-TX", - "city": "San Antonio", - "geonames_city": { - "id": 4726206, - "city": "San Antonio", - "geonames_admin1": { - "name": "Texas", - "id": 4736286, - "ascii_name": "Texas", - "code": "US.TX" - }, - "geonames_admin2": { - "name": "Bexar County", - "id": 4674023, - "ascii_name": "Bexar County", - "code": "US.TX.029" - }, - "license": { - "attribution": "Data from geonames.org under a CC-BY 4.0 license", - "license": "https://creativecommons.org/licenses/by/4.0/" - }, - "nuts_level1": { - "name": null, - "code": null - }, - "nuts_level2": { - "name": null, - "code": null - }, - "nuts_level3": { - "name": null, - "code": null - } - }, - "postcode": null, - "primary": false, - "line": null, - "country_geonames_id": 6252001 + "admin": { + "created": { + "date": "2018-11-14", + "schema_version": "1.0" + }, + "last_modified": { + "date": "2024-12-11", + "schema_version": "2.1" } - ], - "links": [ - "http://www.uthscsa.edu/" - ], - "aliases": [ - - ], - "acronyms": [ - "UTHSCSA" - ], - "status": "active", - "wikipedia_url": "https://en.wikipedia.org/wiki/University_of_Texas_Health_Science_Center_at_San_Antonio", - "labels": [ - - ], - "country": { - "country_name": "United States", - "country_code": "US" }, - "external_ids": { - "ISNI": { - "preferred": null, + "domains": [ + "uacs.edu.mk" + ], + "established": 2005, + "external_ids": [ + { "all": [ - "0000 0001 0629 5880" - ] + "grid.445944.c" + ], + "preferred": "grid.445944.c", + "type": "grid" }, - "FundRef": { - "preferred": "100008635", + { "all": [ - "100008635", - "100008636" - ] + "0000 0004 0446 4427" + ], + "preferred": "0000 0004 0446 4427", + "type": "isni" }, - "OrgRef": { - "preferred": null, + { "all": [ - "1593427" - ] - }, - "Wikidata": { + "Q7894510" + ], "preferred": null, - "all": [ - "Q4005868" - ] - }, - "GRID": { - "preferred": "grid.267309.9", - "all": "grid.267309.9" + "type": "wikidata" } - } - }, - { - "id": "https://ror.org/01sps7q28", - "name": "The University of Texas Health Science Center at Tyler", - "email_address": null, - "ip_addresses": [ - ], - "established": 1947, - "types": [ - "Healthcare" + "id": "https://ror.org/05hknds03", + "links": [ + { + "type": "website", + "value": "https://uacs.edu.mk" + }, + { + "type": "wikipedia", + "value": "https://en.wikipedia.org/wiki/University_American_College_Skopje" + } ], - "relationships": [ + "locations": [ { - "label": "The University of Texas System", - "type": "Parent", - "id": "https://ror.org/01gek1696" - } - ], - "addresses": [ - { - "lat": 32.426014, - "lng": -95.212728, - "state": "Texas", - "state_code": "US-TX", - "city": "Tyler", - "geonames_city": { - "id": 4738214, - "city": "Tyler", - "geonames_admin1": { - "name": "Texas", - "id": 4736286, - "ascii_name": "Texas", - "code": "US.TX" - }, - "geonames_admin2": { - "name": "Smith County", - "id": 4729130, - "ascii_name": "Smith County", - "code": "US.TX.423" - }, - "license": { - "attribution": "Data from geonames.org under a CC-BY 4.0 license", - "license": "https://creativecommons.org/licenses/by/4.0/" - }, - "nuts_level1": { - "name": null, - "code": null - }, - "nuts_level2": { - "name": null, - "code": null - }, - "nuts_level3": { - "name": null, - "code": null - } + "geonames_details": { + "continent_code": "EU", + "continent_name": "Europe", + "country_code": "MK", + "country_name": "North Macedonia", + "country_subdivision_code": null, + "country_subdivision_name": "Grad Skopje", + "lat": 41.99646, + "lng": 21.43141, + "name": "Skopje" }, - "postcode": null, - "primary": false, - "line": null, - "country_geonames_id": 6252001 + "geonames_id": 785842 } ], - "links": [ - "https://www.utsystem.edu/institutions/university-texas-health-science-center-tyler" - ], - "aliases": [ - "East Texas Tuberculosis Sanitarium", - "UT Health Northeast" - ], - "acronyms": [ - "UTHSCT" - ], - "status": "active", - "wikipedia_url": "https://en.wikipedia.org/wiki/University_of_Texas_Health_Science_Center_at_Tyler", - "labels": [ - - ], - "country": { - "country_name": "United States", - "country_code": "US" - }, - "external_ids": { - "ISNI": { - "preferred": null, - "all": [ - "0000 0000 9704 5790" - ] - }, - "OrgRef": { - "preferred": null, - "all": [ - "3446655" - ] - }, - "Wikidata": { - "preferred": null, - "all": [ - "Q7896437" - ] + "names": [ + { + "lang": null, + "types": [ + "acronym" + ], + "value": "UACS" }, - "GRID": { - "preferred": "grid.267310.1", - "all": "grid.267310.1" + { + "lang": "en", + "types": [ + "ror_display", + "label" + ], + "value": "University American College Skopje" } - } - }, - { - "id": "https://ror.org/05byvp690", - "name": "The University of Texas Southwestern Medical Center", - "email_address": null, - "ip_addresses": [ - ], - "established": 1943, + "relationships": [], + "status": "active", "types": [ - "Healthcare" + "education" + ] + }, + { + "admin": { + "created": { + "date": "2018-11-14", + "schema_version": "1.0" + }, + "last_modified": { + "date": "2024-12-11", + "schema_version": "2.1" + } + }, + "domains": [ + "3-5lab.fr" ], - "relationships": [ + "established": 2004, + "external_ids": [ { - "label": "Children's Medical Center", - "type": "Related", - "id": "https://ror.org/02ndk3y82" - }, - { - "label": "Parkland Memorial Hospital", - "type": "Related", - "id": "https://ror.org/0208r0146" - }, - { - "label": "VA North Texas Health Care System", - "type": "Related", - "id": "https://ror.org/01nzxq896" - }, - { - "label": "The University of Texas System", - "type": "Parent", - "id": "https://ror.org/01gek1696" - }, - { - "label": "Institute for Exercise and Environmental Medicine", - "type": "Child", - "id": "https://ror.org/03gqc7y13" - }, - { - "label": "Texas Health Dallas", - "type": "Child", - "id": "https://ror.org/05k07p323" - } - ], - "addresses": [ - { - "lat": 32.812185, - "lng": -96.840174, - "state": "Texas", - "state_code": "US-TX", - "city": "Dallas", - "geonames_city": { - "id": 4684888, - "city": "Dallas", - "geonames_admin1": { - "name": "Texas", - "id": 4736286, - "ascii_name": "Texas", - "code": "US.TX" - }, - "geonames_admin2": { - "name": "Dallas County", - "id": 4684904, - "ascii_name": "Dallas County", - "code": "US.TX.113" - }, - "license": { - "attribution": "Data from geonames.org under a CC-BY 4.0 license", - "license": "https://creativecommons.org/licenses/by/4.0/" - }, - "nuts_level1": { - "name": null, - "code": null - }, - "nuts_level2": { - "name": null, - "code": null - }, - "nuts_level3": { - "name": null, - "code": null - } - }, - "postcode": null, - "primary": false, - "line": null, - "country_geonames_id": 6252001 + "all": [ + "grid.424877.a" + ], + "preferred": "grid.424877.a", + "type": "grid" } ], + "id": "https://ror.org/0509ggw88", "links": [ - "http://www.utsouthwestern.edu/" + { + "type": "website", + "value": "https://www.3-5lab.fr" + } ], - "aliases": [ - "UT Southwestern" + "locations": [ + { + "geonames_details": { + "continent_code": "EU", + "continent_name": "Europe", + "country_code": "FR", + "country_name": "France", + "country_subdivision_code": "IDF", + "country_subdivision_name": "Île-de-France", + "lat": 48.64026, + "lng": 2.23858, + "name": "Marcoussis" + }, + "geonames_id": 2995916 + } ], - "acronyms": [ - + "names": [ + { + "lang": "en", + "types": [ + "ror_display", + "label" + ], + "value": "III V Lab" + } ], + "relationships": [], "status": "active", - "wikipedia_url": "https://en.wikipedia.org/wiki/University_of_Texas_Southwestern_Medical_Center", - "labels": [ - - ], - "country": { - "country_name": "United States", - "country_code": "US" + "types": [ + "facility" + ] + }, + { + "admin": { + "created": { + "date": "2018-11-14", + "schema_version": "1.0" + }, + "last_modified": { + "date": "2024-12-11", + "schema_version": "2.1" + } }, - "external_ids": { - "ISNI": { - "preferred": null, + "domains": [ + "dnb.nl" + ], + "established": 1814, + "external_ids": [ + { "all": [ - "0000 0000 9482 7121" - ] + "501100014104" + ], + "preferred": "501100014104", + "type": "fundref" }, - "FundRef": { - "preferred": "100007914", + { "all": [ - "100007914", - "100010487", - "100008260" - ] + "grid.459463.9" + ], + "preferred": "grid.459463.9", + "type": "grid" }, - "OrgRef": { - "preferred": null, + { "all": [ - "617906" - ] - }, - "Wikidata": { + "0000 0004 0369 4300" + ], "preferred": null, - "all": [ - "Q2725999" - ] + "type": "isni" }, - "GRID": { - "preferred": "grid.267313.2", - "all": "grid.267313.2" + { + "all": [ + "Q1180205" + ], + "preferred": null, + "type": "wikidata" } - } - }, - { - "id": "https://ror.org/019kgqr73", - "name": "The University of Texas at Arlington", - "email_address": "", - "ip_addresses": [ - ], - "established": 1895, - "types": [ - "Education" + "id": "https://ror.org/02fabx761", + "links": [ + { + "type": "website", + "value": "https://www.dnb.nl" + }, + { + "type": "wikipedia", + "value": "https://en.wikipedia.org/wiki/De_Nederlandsche_Bank" + } ], - "relationships": [ + "locations": [ { - "label": "VA North Texas Health Care System", - "type": "Related", - "id": "https://ror.org/01nzxq896" - }, - { - "label": "The University of Texas System", - "type": "Parent", - "id": "https://ror.org/01gek1696" - } - ], - "addresses": [ - { - "lat": 32.731, - "lng": -97.115, - "state": "Texas", - "state_code": "US-TX", - "city": "Arlington", - "geonames_city": { - "id": 4671240, - "city": "Arlington", - "geonames_admin1": { - "name": "Texas", - "id": 4736286, - "ascii_name": "Texas", - "code": "US.TX" - }, - "geonames_admin2": { - "name": "Tarrant County", - "id": 4735638, - "ascii_name": "Tarrant County", - "code": "US.TX.439" - }, - "license": { - "attribution": "Data from geonames.org under a CC-BY 4.0 license", - "license": "https://creativecommons.org/licenses/by/4.0/" - }, - "nuts_level1": { - "name": null, - "code": null - }, - "nuts_level2": { - "name": null, - "code": null - }, - "nuts_level3": { - "name": null, - "code": null - } + "geonames_details": { + "continent_code": "EU", + "continent_name": "Europe", + "country_code": "NL", + "country_name": "The Netherlands", + "country_subdivision_code": "NH", + "country_subdivision_name": "North Holland", + "lat": 52.37403, + "lng": 4.88969, + "name": "Amsterdam" }, - "postcode": null, - "primary": false, - "line": null, - "country_geonames_id": 6252001 + "geonames_id": 2759794 } ], - "links": [ - "http://www.uta.edu/uta/" - ], - "aliases": [ - "UT Arlington" - ], - "acronyms": [ - "UTA" - ], - "status": "active", - "wikipedia_url": "http://en.wikipedia.org/wiki/University_of_Texas_at_Arlington", - "labels": [ + "names": [ + { + "lang": null, + "types": [ + "acronym" + ], + "value": "DNB" + }, + { + "lang": "nl", + "types": [ + "ror_display", + "label" + ], + "value": "De Nederlandsche Bank" + }, { - "label": "Université du Texas à Arlington", - "iso639": "fr" + "lang": "en", + "types": [ + "alias" + ], + "value": "Dutch Bank" } ], - "country": { - "country_name": "United States", - "country_code": "US" + "relationships": [], + "status": "active", + "types": [ + "funder", + "other" + ] + }, + { + "admin": { + "created": { + "date": "2018-11-14", + "schema_version": "1.0" + }, + "last_modified": { + "date": "2024-12-11", + "schema_version": "2.1" + } }, - "external_ids": { - "ISNI": { - "preferred": null, + "domains": [ + "kfupm.edu.sa" + ], + "established": 1963, + "external_ids": [ + { "all": [ - "0000 0001 2181 9515" - ] - }, - "FundRef": { + "501100004055" + ], "preferred": null, - "all": [ - "100009497" - ] + "type": "fundref" }, - "OrgRef": { - "preferred": null, + { "all": [ - "906409" - ] + "grid.412135.0" + ], + "preferred": "grid.412135.0", + "type": "grid" }, - "Wikidata": { - "preferred": null, + { "all": [ - "Q1230739" - ] + "0000 0001 1091 0356" + ], + "preferred": null, + "type": "isni" }, - "GRID": { - "preferred": "grid.267315.4", - "all": "grid.267315.4" + { + "all": [ + "Q4116241" + ], + "preferred": null, + "type": "wikidata" } - } - }, - { - "id": "https://ror.org/051smbs96", - "name": "The University of Texas of the Permian Basin", - "email_address": null, - "ip_addresses": [ - ], - "established": 1973, - "types": [ - "Education" + "id": "https://ror.org/03yez3163", + "links": [ + { + "type": "website", + "value": "https://www.kfupm.edu.sa" + }, + { + "type": "wikipedia", + "value": "http://en.wikipedia.org/wiki/King_Fahd_University_of_Petroleum_and_Minerals" + } ], - "relationships": [ + "locations": [ { - "label": "The University of Texas System", - "type": "Parent", - "id": "https://ror.org/01gek1696" - } - ], - "addresses": [ - { - "lat": 31.889444, - "lng": -102.329531, - "state": "Texas", - "state_code": "US-TX", - "city": "Odessa", - "geonames_city": { - "id": 5527554, - "city": "Odessa", - "geonames_admin1": { - "name": "Texas", - "id": 4736286, - "ascii_name": "Texas", - "code": "US.TX" - }, - "geonames_admin2": { - "name": "Ector County", - "id": 5520910, - "ascii_name": "Ector County", - "code": "US.TX.135" - }, - "license": { - "attribution": "Data from geonames.org under a CC-BY 4.0 license", - "license": "https://creativecommons.org/licenses/by/4.0/" - }, - "nuts_level1": { - "name": null, - "code": null - }, - "nuts_level2": { - "name": null, - "code": null - }, - "nuts_level3": { - "name": null, - "code": null - } + "geonames_details": { + "continent_code": "AS", + "continent_name": "Asia", + "country_code": "SA", + "country_name": "Saudi Arabia", + "country_subdivision_code": "04", + "country_subdivision_name": "Eastern Province", + "lat": 26.28864, + "lng": 50.11396, + "name": "Dhahran" }, - "postcode": null, - "primary": false, - "line": null, - "country_geonames_id": 6252001 + "geonames_id": 107797 } ], - "links": [ - "http://www.utpb.edu/" - ], - "aliases": [ - "UT Permian Basin" - ], - "acronyms": [ - "UTPB" + "names": [ + { + "lang": null, + "types": [ + "acronym" + ], + "value": "KFUPM" + }, + { + "lang": "en", + "types": [ + "ror_display", + "label" + ], + "value": "King Fahd University of Petroleum and Minerals" + }, + { + "lang": "ar", + "types": [ + "label" + ], + "value": "جامعة الملك فهد للبترول والمعادن" + } ], + "relationships": [], "status": "active", - "wikipedia_url": "http://en.wikipedia.org/wiki/University_of_Texas_of_the_Permian_Basin", - "labels": [ - - ], - "country": { - "country_name": "United States", - "country_code": "US" + "types": [ + "education", + "funder" + ] + }, + { + "admin": { + "created": { + "date": "2018-11-14", + "schema_version": "1.0" + }, + "last_modified": { + "date": "2024-12-11", + "schema_version": "2.1" + } }, - "external_ids": { - "ISNI": { - "preferred": null, + "domains": [], + "established": null, + "external_ids": [ + { "all": [ - "0000 0000 9140 1491" - ] - }, - "OrgRef": { + "100006445" + ], "preferred": null, - "all": [ - "1419441" - ] + "type": "fundref" }, - "Wikidata": { - "preferred": null, + { "all": [ - "Q2495935" - ] - }, - "GRID": { - "preferred": "grid.267328.a", - "all": "grid.267328.a" + "grid.457570.4" + ], + "preferred": "grid.457570.4", + "type": "grid" } - } - }, - { - "id": "https://ror.org/044vy1d05", - "name": "Tokushima University", - "email_address": "", - "ip_addresses": [ - ], - "established": 1949, - "types": [ - "Education" + "id": "https://ror.org/043trmd87", + "links": [ + { + "type": "website", + "value": "http://chm.pse.umass.edu/" + } ], - "relationships": [ + "locations": [ { - "label": "Tokushima University Hospital", - "type": "Related", - "id": "https://ror.org/021ph5e41" - } - ], - "addresses": [ - { - "lat": 34.07, - "lng": 134.56, - "state": null, - "state_code": null, - "city": "Tokushima", - "geonames_city": { - "id": 1850158, - "city": "Tokushima", - "geonames_admin1": { - "name": "Tokushima", - "id": 1850157, - "ascii_name": "Tokushima", - "code": "JP.39" - }, - "geonames_admin2": { - "name": "Tokushima Shi", - "id": 1850156, - "ascii_name": "Tokushima Shi", - "code": "JP.39.1850156" - }, - "license": { - "attribution": "Data from geonames.org under a CC-BY 4.0 license", - "license": "https://creativecommons.org/licenses/by/4.0/" - }, - "nuts_level1": { - "name": null, - "code": null - }, - "nuts_level2": { - "name": null, - "code": null - }, - "nuts_level3": { - "name": null, - "code": null - } + "geonames_details": { + "continent_code": "NA", + "continent_name": "North America", + "country_code": "US", + "country_name": "United States", + "country_subdivision_code": "MA", + "country_subdivision_name": "Massachusetts", + "lat": 42.37537, + "lng": -72.51925, + "name": "Amherst Center" }, - "postcode": null, - "primary": false, - "line": null, - "country_geonames_id": 1861060 + "geonames_id": 4929023 } ], - "links": [ - "https://www.tokushima-u.ac.jp/" - ], - "aliases": [ - "Tokushima Daigaku", - "University of Tokushima" - ], - "acronyms": [ - + "names": [ + { + "lang": null, + "types": [ + "acronym" + ], + "value": "CHM" + }, + { + "lang": "en", + "types": [ + "ror_display", + "label" + ], + "value": "Center for Hierarchical Manufacturing" + } ], - "status": "active", - "wikipedia_url": "https://en.wikipedia.org/wiki/University_of_Tokushima", - "labels": [ + "relationships": [ + { + "label": "U.S. National Science Foundation", + "type": "related", + "id": "https://ror.org/021nxhr62" + }, { - "label": "徳島大学", - "iso639": "ja" + "label": "University of Massachusetts Amherst", + "type": "related", + "id": "https://ror.org/0072zz521" } ], - "country": { - "country_name": "Japan", - "country_code": "JP" + "status": "active", + "types": [ + "funder", + "nonprofit" + ] + }, + { + "admin": { + "created": { + "date": "2018-11-14", + "schema_version": "1.0" + }, + "last_modified": { + "date": "2024-12-11", + "schema_version": "2.1" + } }, - "external_ids": { - "ISNI": { - "preferred": null, + "domains": [ + "musashino-u.ac.jp" + ], + "established": 1924, + "external_ids": [ + { "all": [ - "0000 0001 1092 3579" - ] + "100019640" + ], + "preferred": "100019640", + "type": "fundref" }, - "FundRef": { - "preferred": null, + { "all": [ - "501100005623" - ] + "grid.411867.d" + ], + "preferred": "grid.411867.d", + "type": "grid" }, - "OrgRef": { - "preferred": null, + { "all": [ - "15696836" - ] - }, - "Wikidata": { + "0000 0001 0356 8417" + ], "preferred": null, - "all": [ - "Q1150231" - ] + "type": "isni" }, - "GRID": { - "preferred": "grid.267335.6", - "all": "grid.267335.6" + { + "all": [ + "Q6940182" + ], + "preferred": null, + "type": "wikidata" } - } - }, - { - "id": "https://ror.org/03np13864", - "name": "University of Trinidad and Tobago", - "email_address": null, - "ip_addresses": [ - ], - "established": 2004, - "types": [ - "Education" + "id": "https://ror.org/04bcbax71", + "links": [ + { + "type": "website", + "value": "https://www.musashino-u.ac.jp" + }, + { + "type": "wikipedia", + "value": "http://en.wikipedia.org/wiki/Musashino_University" + } ], - "relationships": [ - - ], - "addresses": [ - { - "lat": 10.616667, - "lng": -61.216667, - "state": null, - "state_code": null, - "city": "Arima", - "geonames_city": { - "id": 3575051, - "city": "Arima", - "geonames_admin1": { - "name": "Borough of Arima", - "id": 3575052, - "ascii_name": "Borough of Arima", - "code": "TT.01" - }, - "geonames_admin2": { - "name": null, - "id": null, - "ascii_name": null, - "code": null - }, - "license": { - "attribution": "Data from geonames.org under a CC-BY 4.0 license", - "license": "https://creativecommons.org/licenses/by/4.0/" - }, - "nuts_level1": { - "name": null, - "code": null - }, - "nuts_level2": { - "name": null, - "code": null - }, - "nuts_level3": { - "name": null, - "code": null - } + "locations": [ + { + "geonames_details": { + "continent_code": "AS", + "continent_name": "Asia", + "country_code": "JP", + "country_name": "Japan", + "country_subdivision_code": "13", + "country_subdivision_name": "Tokyo", + "lat": 35.6895, + "lng": 139.69171, + "name": "Tokyo" }, - "postcode": null, - "primary": false, - "line": null, - "country_geonames_id": 3573591 + "geonames_id": 1850147 } ], - "links": [ - "https://utt.edu.tt/" - ], - "aliases": [ - - ], - "acronyms": [ - "UTT" - ], - "status": "active", - "wikipedia_url": "https://en.wikipedia.org/wiki/University_of_Trinidad_and_Tobago", - "labels": [ + "names": [ + { + "lang": null, + "types": [ + "alias" + ], + "value": "Musashino Daigaku" + }, + { + "lang": null, + "types": [ + "ror_display", + "label" + ], + "value": "Musashino University" + }, { - "label": "Universidad de Trinidad y Tobago", - "iso639": "es" + "lang": "ja", + "types": [ + "label" + ], + "value": "武蔵野大学" } ], - "country": { - "country_name": "Trinidad and Tobago", - "country_code": "TT" + "relationships": [], + "status": "active", + "types": [ + "education", + "funder" + ] + }, + { + "admin": { + "created": { + "date": "2018-11-14", + "schema_version": "1.0" + }, + "last_modified": { + "date": "2024-12-11", + "schema_version": "2.1" + } }, - "external_ids": { - "ISNI": { - "preferred": null, + "domains": [ + "hansung.ac.kr" + ], + "established": 1972, + "external_ids": [ + { "all": [ - "0000 0000 9490 0886" - ] - }, - "OrgRef": { + "501100002491" + ], "preferred": null, + "type": "fundref" + }, + { "all": [ - "8706288" - ] + "grid.444079.a" + ], + "preferred": "grid.444079.a", + "type": "grid" }, - "Wikidata": { - "preferred": null, + { "all": [ - "Q648244" - ] + "0000 0004 0532 678X" + ], + "preferred": null, + "type": "isni" }, - "GRID": { - "preferred": "grid.267355.0", - "all": "grid.267355.0" + { + "all": [ + "Q482765" + ], + "preferred": null, + "type": "wikidata" } - } - }, - { - "id": "https://ror.org/04wn28048", - "name": "University of Tulsa", - "email_address": "", - "ip_addresses": [ - ], - "established": 1894, - "types": [ - "Education" + "id": "https://ror.org/048m9x696", + "links": [ + { + "type": "website", + "value": "https://www.hansung.ac.kr" + }, + { + "type": "wikipedia", + "value": "https://en.wikipedia.org/wiki/Hansung_University" + } ], - "relationships": [ - - ], - "addresses": [ - { - "lat": 36.152222, - "lng": -95.946389, - "state": "Oklahoma", - "state_code": "US-OK", - "city": "Tulsa", - "geonames_city": { - "id": 4553433, - "city": "Tulsa", - "geonames_admin1": { - "name": "Oklahoma", - "id": 4544379, - "ascii_name": "Oklahoma", - "code": "US.OK" - }, - "geonames_admin2": { - "name": "Tulsa County", - "id": 4553440, - "ascii_name": "Tulsa County", - "code": "US.OK.143" - }, - "license": { - "attribution": "Data from geonames.org under a CC-BY 4.0 license", - "license": "https://creativecommons.org/licenses/by/4.0/" - }, - "nuts_level1": { - "name": null, - "code": null - }, - "nuts_level2": { - "name": null, - "code": null - }, - "nuts_level3": { - "name": null, - "code": null - } + "locations": [ + { + "geonames_details": { + "continent_code": "AS", + "continent_name": "Asia", + "country_code": "KR", + "country_name": "South Korea", + "country_subdivision_code": "11", + "country_subdivision_name": "Seoul", + "lat": 37.566, + "lng": 126.9784, + "name": "Seoul" }, - "postcode": null, - "primary": false, - "line": null, - "country_geonames_id": 6252001 + "geonames_id": 1835848 } ], - "links": [ - "http://utulsa.edu/" - ], - "aliases": [ - - ], - "acronyms": [ - "TU" - ], - "status": "active", - "wikipedia_url": "http://en.wikipedia.org/wiki/University_of_Tulsa", - "labels": [ + "names": [ + { + "lang": "en", + "types": [ + "ror_display", + "label" + ], + "value": "Hansung University" + }, + { + "lang": "en", + "types": [ + "alias" + ], + "value": "Hansung Woman's University" + }, { - "label": "Université de tulsa", - "iso639": "fr" + "lang": "ko", + "types": [ + "label" + ], + "value": "한성대학교" } ], - "country": { - "country_name": "United States", - "country_code": "US" + "relationships": [], + "status": "active", + "types": [ + "education", + "funder" + ] + }, + { + "admin": { + "created": { + "date": "2018-11-14", + "schema_version": "1.0" + }, + "last_modified": { + "date": "2024-12-11", + "schema_version": "2.1" + } }, - "external_ids": { - "ISNI": { - "preferred": null, + "domains": [ + "tuh.ie" + ], + "established": 1996, + "external_ids": [ + { "all": [ - "0000 0001 2160 264X" - ] + "grid.413305.0" + ], + "preferred": "grid.413305.0", + "type": "grid" }, - "FundRef": { - "preferred": "100007147", + { "all": [ - "100007147", - "100006455" - ] - }, - "OrgRef": { + "0000 0004 0617 5936" + ], "preferred": null, - "all": [ - "32043" - ] + "type": "isni" }, - "Wikidata": { - "preferred": null, + { "all": [ - "Q1848657" - ] - }, - "GRID": { - "preferred": "grid.267360.6", - "all": "grid.267360.6" + "Q7680014" + ], + "preferred": null, + "type": "wikidata" } - } - }, - { - "id": "https://ror.org/04scfb908", - "name": "Alfred Health", - "email_address": null, - "ip_addresses": [ - ], - "established": 1871, - "types": [ - "Healthcare" + "id": "https://ror.org/01fvmtt37", + "links": [ + { + "type": "website", + "value": "https://www.tuh.ie" + }, + { + "type": "wikipedia", + "value": "https://en.wikipedia.org/wiki/Tallaght_Hospital" + } ], - "relationships": [ + "locations": [ { - "label": "Caulfield Hospital", - "type": "Child", - "id": "https://ror.org/01fcxf261" - }, - { - "label": "Melbourne Sexual Health Centre", - "type": "Child", - "id": "https://ror.org/013fdz725" - }, - { - "label": "National Trauma Research Institute", - "type": "Child", - "id": "https://ror.org/048t93218" - }, - { - "label": "The Alfred Hospital", - "type": "Child", - "id": "https://ror.org/01wddqe20" - } - ], - "addresses": [ - { - "lat": -37.845542, - "lng": 144.981632, - "state": "Victoria", - "state_code": "AU-VIC", - "city": "Melbourne", - "geonames_city": { - "id": 2158177, - "city": "Melbourne", - "geonames_admin1": { - "name": "Victoria", - "id": 2145234, - "ascii_name": "Victoria", - "code": "AU.07" - }, - "geonames_admin2": { - "name": "Melbourne", - "id": 7839805, - "ascii_name": "Melbourne", - "code": "AU.07.24600" - }, - "license": { - "attribution": "Data from geonames.org under a CC-BY 4.0 license", - "license": "https://creativecommons.org/licenses/by/4.0/" - }, - "nuts_level1": { - "name": null, - "code": null - }, - "nuts_level2": { - "name": null, - "code": null - }, - "nuts_level3": { - "name": null, - "code": null - } + "geonames_details": { + "continent_code": "EU", + "continent_name": "Europe", + "country_code": "IE", + "country_name": "Ireland", + "country_subdivision_code": "L", + "country_subdivision_name": "Leinster", + "lat": 53.33306, + "lng": -6.24889, + "name": "Dublin" }, - "postcode": null, - "primary": false, - "line": null, - "country_geonames_id": 2077456 + "geonames_id": 2964574 } ], - "links": [ - "http://www.alfred.org.au/" - ], - "aliases": [ - - ], - "acronyms": [ - - ], - "status": "active", - "wikipedia_url": "", - "labels": [ - - ], - "country": { - "country_name": "Australia", - "country_code": "AU" - }, - "external_ids": { - "ISNI": { - "preferred": null, - "all": [ - "0000 0004 0432 5259" - ] + "names": [ + { + "lang": null, + "types": [ + "acronym" + ], + "value": "AMNCH" }, - "FundRef": { - "preferred": null, - "all": [ - "501100002716" - ] + { + "lang": "en", + "types": [ + "alias" + ], + "value": "Adelaide and Meath Hospital, Dublin, incorporating the National Children's Hospital" + }, + { + "lang": "ga", + "types": [ + "alias" + ], + "value": "Ospidéal Adelaide agus na Mí, Baile Átha Cliath, ina gcorpraítear Ospidéal Náisiúnta na Leanaí" }, - "GRID": { - "preferred": "grid.267362.4", - "all": "grid.267362.4" + { + "lang": "ga", + "types": [ + "label" + ], + "value": "Ospidéal Ollscoile Thamhlachta" + }, + { + "lang": "ga", + "types": [ + "alias" + ], + "value": "Ospidéal Thamhlachta" + }, + { + "lang": "en", + "types": [ + "alias" + ], + "value": "Tallaght Hospital" + }, + { + "lang": "en", + "types": [ + "ror_display", + "label" + ], + "value": "Tallaght University Hospital" } - } - }, - { - "id": "https://ror.org/02c2f8975", - "name": "University of Ulsan", - "email_address": null, - "ip_addresses": [ - - ], - "established": 1970, - "types": [ - "Education" ], "relationships": [ { - "label": "Ulsan University Hospital", - "type": "Related", - "id": "https://ror.org/03sab2a45" - } - ], - "addresses": [ - { - "lat": 35.542772, - "lng": 129.256725, - "state": null, - "state_code": null, - "city": "Ulsan", - "geonames_city": { - "id": 1833747, - "city": "Ulsan", - "geonames_admin1": { - "name": "Ulsan", - "id": 1833742, - "ascii_name": "Ulsan", - "code": "KR.21" - }, - "geonames_admin2": { - "name": null, - "id": null, - "ascii_name": null, - "code": null - }, - "license": { - "attribution": "Data from geonames.org under a CC-BY 4.0 license", - "license": "https://creativecommons.org/licenses/by/4.0/" - }, - "nuts_level1": { - "name": null, - "code": null - }, - "nuts_level2": { - "name": null, - "code": null - }, - "nuts_level3": { - "name": null, - "code": null - } - }, - "postcode": null, - "primary": false, - "line": null, - "country_geonames_id": 1835841 + "label": "Trinity College Dublin", + "type": "related", + "id": "https://ror.org/02tyrky19" } ], - "links": [ - "http://en.ulsan.ac.kr/contents/main/" - ], - "aliases": [ - - ], - "acronyms": [ - "UOU" - ], "status": "active", - "wikipedia_url": "http://en.wikipedia.org/wiki/University_of_Ulsan", - "labels": [ - { - "label": "울산대학교", - "iso639": "ko" + "types": [ + "healthcare" + ] + }, + { + "admin": { + "created": { + "date": "2018-11-14", + "schema_version": "1.0" + }, + "last_modified": { + "date": "2024-12-11", + "schema_version": "2.1" } - ], - "country": { - "country_name": "South Korea", - "country_code": "KR" }, - "external_ids": { - "ISNI": { - "preferred": null, + "domains": [ + "mseuf.edu.ph" + ], + "established": 1947, + "external_ids": [ + { "all": [ - "0000 0004 0533 4667" - ] + "grid.448687.1" + ], + "preferred": "grid.448687.1", + "type": "grid" }, - "FundRef": { - "preferred": null, + { "all": [ - "501100002568" - ] + "0000 0004 0639 6528" + ], + "preferred": null, + "type": "isni" }, - "OrgRef": { - "preferred": "10458246", + { "all": [ - "10458246", - "15162872" - ] - }, - "Wikidata": { + "Q3578221" + ], "preferred": null, - "all": [ - "Q491717" - ] - }, - "GRID": { - "preferred": "grid.267370.7", - "all": "grid.267370.7" + "type": "wikidata" } - } - }, - { - "id": "https://ror.org/010acrp16", - "name": "University of West Alabama", - "email_address": null, - "ip_addresses": [ - ], - "established": 1835, - "types": [ - "Education" + "id": "https://ror.org/02fhfq388", + "links": [ + { + "type": "website", + "value": "https://mseuf.edu.ph" + }, + { + "type": "wikipedia", + "value": "https://en.wikipedia.org/wiki/Enverga_University" + } ], - "relationships": [ - - ], - "addresses": [ - { - "lat": 32.59, - "lng": -88.186, - "state": "Alabama", - "state_code": "US-AL", - "city": "Livingston", - "geonames_city": { - "id": 4073383, - "city": "Livingston", - "geonames_admin1": { - "name": "Alabama", - "id": 4829764, - "ascii_name": "Alabama", - "code": "US.AL" - }, - "geonames_admin2": { - "name": "Sumter County", - "id": 4092386, - "ascii_name": "Sumter County", - "code": "US.AL.119" - }, - "license": { - "attribution": "Data from geonames.org under a CC-BY 4.0 license", - "license": "https://creativecommons.org/licenses/by/4.0/" - }, - "nuts_level1": { - "name": null, - "code": null - }, - "nuts_level2": { - "name": null, - "code": null - }, - "nuts_level3": { - "name": null, - "code": null - } + "locations": [ + { + "geonames_details": { + "continent_code": "AS", + "continent_name": "Asia", + "country_code": "PH", + "country_name": "Philippines", + "country_subdivision_code": "40", + "country_subdivision_name": "Calabarzon", + "lat": 13.93139, + "lng": 121.61722, + "name": "Lucena City" }, - "postcode": null, - "primary": false, - "line": null, - "country_geonames_id": 6252001 + "geonames_id": 1705357 } ], - "links": [ - "http://www.uwa.edu/" - ], - "aliases": [ - "Livingston Female Academy" - ], - "acronyms": [ - "UWA" + "names": [ + { + "lang": "en", + "types": [ + "ror_display", + "label" + ], + "value": "Enverga University" + }, + { + "lang": null, + "types": [ + "acronym" + ], + "value": "MSEUF" + }, + { + "lang": "en", + "types": [ + "alias" + ], + "value": "Manuel S. Enverga University Foundation" + } ], + "relationships": [], "status": "active", - "wikipedia_url": "http://en.wikipedia.org/wiki/University_of_West_Alabama", - "labels": [ - - ], - "country": { - "country_name": "United States", - "country_code": "US" + "types": [ + "education" + ] + }, + { + "admin": { + "created": { + "date": "2018-11-14", + "schema_version": "1.0" + }, + "last_modified": { + "date": "2024-12-11", + "schema_version": "2.1" + } }, - "external_ids": { - "ISNI": { - "preferred": null, + "domains": [ + "plus.ac.at" + ], + "established": 1622, + "external_ids": [ + { "all": [ - "0000 0000 9963 9197" - ] - }, - "OrgRef": { + "501100005644" + ], "preferred": null, + "type": "fundref" + }, + { "all": [ - "2425212" - ] + "grid.7039.d" + ], + "preferred": "grid.7039.d", + "type": "grid" }, - "Wikidata": { - "preferred": null, + { "all": [ - "Q637346" - ] + "0000 0001 1015 6330" + ], + "preferred": null, + "type": "isni" }, - "GRID": { - "preferred": "grid.267434.0", - "all": "grid.267434.0" + { + "all": [ + "Q27265" + ], + "preferred": null, + "type": "wikidata" } - } - }, - { - "id": "https://ror.org/002w4zy91", - "name": "University of West Florida", - "email_address": null, - "ip_addresses": [ - ], - "established": 1963, - "types": [ - "Education" + "id": "https://ror.org/05gs8cd61", + "links": [ + { + "type": "website", + "value": "https://www.plus.ac.at/" + }, + { + "type": "wikipedia", + "value": "http://en.wikipedia.org/wiki/University_of_Salzburg" + } ], - "relationships": [ + "locations": [ { - "label": "State University System of Florida", - "type": "Parent", - "id": "https://ror.org/05sqd3t97" - } - ], - "addresses": [ - { - "lat": 30.549493, - "lng": -87.21812, - "state": "Florida", - "state_code": "US-FL", - "city": "Pensacola", - "geonames_city": { - "id": 4168228, - "city": "Pensacola", - "geonames_admin1": { - "name": "Florida", - "id": 4155751, - "ascii_name": "Florida", - "code": "US.FL" - }, - "geonames_admin2": { - "name": "Escambia County", - "id": 4154550, - "ascii_name": "Escambia County", - "code": "US.FL.033" - }, - "license": { - "attribution": "Data from geonames.org under a CC-BY 4.0 license", - "license": "https://creativecommons.org/licenses/by/4.0/" - }, - "nuts_level1": { - "name": null, - "code": null - }, - "nuts_level2": { - "name": null, - "code": null - }, - "nuts_level3": { - "name": null, - "code": null - } + "geonames_details": { + "continent_code": "EU", + "continent_name": "Europe", + "country_code": "AT", + "country_name": "Austria", + "country_subdivision_code": "5", + "country_subdivision_name": "Salzburg", + "lat": 47.79941, + "lng": 13.04399, + "name": "Salzburg" }, - "postcode": null, - "primary": false, - "line": null, - "country_geonames_id": 6252001 + "geonames_id": 2766824 } ], - "links": [ - "http://uwf.edu/" - ], - "aliases": [ - - ], - "acronyms": [ - "UWF" - ], - "status": "active", - "wikipedia_url": "http://en.wikipedia.org/wiki/University_of_West_Florida", - "labels": [ - - ], - "country": { - "country_name": "United States", - "country_code": "US" - }, - "external_ids": { - "ISNI": { - "preferred": null, - "all": [ - "0000 0001 2112 2427" - ] - }, - "FundRef": { - "preferred": null, - "all": [ - "100009842" - ] + "names": [ + { + "lang": null, + "types": [ + "acronym" + ], + "value": "PLUS" }, - "OrgRef": { - "preferred": null, - "all": [ - "750756" - ] + { + "lang": "en", + "types": [ + "alias" + ], + "value": "Paris Lodron University of Salzburg" }, - "Wikidata": { - "preferred": null, - "all": [ - "Q659255" - ] + { + "lang": "de", + "types": [ + "label" + ], + "value": "Paris-Lodron-Universität Salzburg" }, - "GRID": { - "preferred": "grid.267436.2", - "all": "grid.267436.2" + { + "lang": "en", + "types": [ + "ror_display", + "label" + ], + "value": "University of Salzburg" } - } + ], + "relationships": [], + "status": "active", + "types": [ + "education", + "funder" + ] }, { - "id": "https://ror.org/01cqxk816", - "name": "University of West Georgia", - "email_address": null, - "ip_addresses": [ - + "admin": { + "created": { + "date": "2024-11-18", + "schema_version": "2.0" + }, + "last_modified": { + "date": "2024-12-11", + "schema_version": "2.1" + } + }, + "domains": [ + "hch.tums.ac.ir" ], - "established": 1906, - "types": [ - "Education" + "established": null, + "external_ids": [], + "id": "https://ror.org/04hgqjy83", + "links": [ + { + "type": "website", + "value": "https://hch.tums.ac.ir" + } ], - "relationships": [ + "locations": [ { - "label": "University System of Georgia", - "type": "Parent", - "id": "https://ror.org/017wcm924" - } - ], - "addresses": [ - { - "lat": 33.573357, - "lng": -85.099593, - "state": "Georgia", - "state_code": "US-GA", - "city": "Carrollton", - "geonames_city": { - "id": 4186416, - "city": "Carrollton", - "geonames_admin1": { - "name": "Georgia", - "id": 4197000, - "ascii_name": "Georgia", - "code": "US.GA" - }, - "geonames_admin2": { - "name": "Carroll County", - "id": 4186396, - "ascii_name": "Carroll County", - "code": "US.GA.045" - }, - "license": { - "attribution": "Data from geonames.org under a CC-BY 4.0 license", - "license": "https://creativecommons.org/licenses/by/4.0/" - }, - "nuts_level1": { - "name": null, - "code": null - }, - "nuts_level2": { - "name": null, - "code": null - }, - "nuts_level3": { - "name": null, - "code": null - } + "geonames_details": { + "continent_code": "AS", + "continent_name": "Asia", + "country_code": "IR", + "country_name": "Iran", + "country_subdivision_code": "23", + "country_subdivision_name": "Tehran", + "lat": 35.69439, + "lng": 51.42151, + "name": "Tehran" }, - "postcode": null, - "primary": false, - "line": null, - "country_geonames_id": 6252001 + "geonames_id": 112931 } ], - "links": [ - "http://www.westga.edu/" - ], - "aliases": [ - + "names": [ + { + "lang": "en", + "types": [ + "label", + "ror_display" + ], + "value": "Hakim Children Hospital" + }, + { + "lang": "en", + "types": [ + "alias" + ], + "value": "Hakim Children's Hospital" + }, + { + "lang": "fa", + "types": [ + "label" + ], + "value": "بیمارستان کودکان حکیم" + }, + { + "lang": "fa", + "types": [ + "alias" + ], + "value": "بیمارستان کودکان حکیم دانشگاه علوم پزشکی تهران" + } ], - "acronyms": [ - "UWG" + "relationships": [ + { + "label": "Tehran University of Medical Sciences", + "type": "related", + "id": "https://ror.org/01c4pz451" + } ], "status": "active", - "wikipedia_url": "http://en.wikipedia.org/wiki/University_of_West_Georgia", - "labels": [ - - ], - "country": { - "country_name": "United States", - "country_code": "US" + "types": [ + "healthcare" + ] + }, + { + "admin": { + "created": { + "date": "2018-11-14", + "schema_version": "1.0" + }, + "last_modified": { + "date": "2024-12-11", + "schema_version": "2.1" + } }, - "external_ids": { - "ISNI": { - "preferred": null, + "domains": [ + "pstu.ac.bd" + ], + "established": 2000, + "external_ids": [ + { "all": [ - "0000 0001 2223 6696" - ] + "501100014587" + ], + "preferred": "501100014587", + "type": "fundref" }, - "FundRef": { - "preferred": null, + { "all": [ - "100007922" - ] + "grid.443081.a" + ], + "preferred": "grid.443081.a", + "type": "grid" }, - "OrgRef": { - "preferred": null, + { "all": [ - "595315" - ] - }, - "Wikidata": { + "0000 0004 0489 3643" + ], "preferred": null, - "all": [ - "Q2495945" - ] + "type": "isni" }, - "GRID": { - "preferred": "grid.267437.3", - "all": "grid.267437.3" + { + "all": [ + "Q7148748" + ], + "preferred": null, + "type": "wikidata" } - } - }, - { - "id": "https://ror.org/03c8vvr84", - "name": "University of Western States", - "email_address": null, - "ip_addresses": [ - ], - "established": 1904, - "types": [ - "Education" + "id": "https://ror.org/03m50n726", + "links": [ + { + "type": "website", + "value": "https://www.pstu.ac.bd" + }, + { + "type": "wikipedia", + "value": "https://en.wikipedia.org/wiki/Patuakhali_Science_and_Technology_University" + } ], - "relationships": [ - - ], - "addresses": [ - { - "lat": 45.543351, - "lng": -122.523973, - "state": "Oregon", - "state_code": "US-OR", - "city": "Portland", - "geonames_city": { - "id": 5746545, - "city": "Portland", - "geonames_admin1": { - "name": "Oregon", - "id": 5744337, - "ascii_name": "Oregon", - "code": "US.OR" - }, - "geonames_admin2": { - "name": "Multnomah County", - "id": 5742126, - "ascii_name": "Multnomah County", - "code": "US.OR.051" - }, - "license": { - "attribution": "Data from geonames.org under a CC-BY 4.0 license", - "license": "https://creativecommons.org/licenses/by/4.0/" - }, - "nuts_level1": { - "name": null, - "code": null - }, - "nuts_level2": { - "name": null, - "code": null - }, - "nuts_level3": { - "name": null, - "code": null - } + "locations": [ + { + "geonames_details": { + "continent_code": "AS", + "continent_name": "Asia", + "country_code": "BD", + "country_name": "Bangladesh", + "country_subdivision_code": "A", + "country_subdivision_name": "Barisal Division", + "lat": 22.33333, + "lng": 90.33333, + "name": "Patuakhali" }, - "postcode": null, - "primary": false, - "line": null, - "country_geonames_id": 6252001 + "geonames_id": 1337216 } ], - "links": [ - "http://www.uws.edu/" - ], - "aliases": [ - "Western States Chiropractic College" - ], - "acronyms": [ - "UWS" + "names": [ + { + "lang": null, + "types": [ + "acronym" + ], + "value": "PSTU" + }, + { + "lang": "en", + "types": [ + "ror_display", + "label" + ], + "value": "Patuakhali Science and Technology University" + }, + { + "lang": "bn", + "types": [ + "label" + ], + "value": "পটুয়াখালী বিজ্ঞান ও প্রযুক্তি বিশ্ববিদ্যালয়" + } ], + "relationships": [], "status": "active", - "wikipedia_url": "http://en.wikipedia.org/wiki/University_of_Western_States", - "labels": [ - - ], - "country": { - "country_name": "United States", - "country_code": "US" + "types": [ + "education", + "funder" + ] + }, + { + "admin": { + "created": { + "date": "2018-11-14", + "schema_version": "1.0" + }, + "last_modified": { + "date": "2024-12-11", + "schema_version": "2.1" + } }, - "external_ids": { - "ISNI": { - "preferred": null, + "domains": [ + "aih-net.com" + ], + "established": 1918, + "external_ids": [ + { "all": [ - "0000 0004 0455 9493" - ] + "grid.413984.3" + ], + "preferred": "grid.413984.3", + "type": "grid" }, - "OrgRef": { - "preferred": null, + { "all": [ - "1655050" - ] - }, - "Wikidata": { + "Q11666229" + ], "preferred": null, - "all": [ - "Q7896612" - ] - }, - "GRID": { - "preferred": "grid.267451.3", - "all": "grid.267451.3" + "type": "wikidata" } - } - }, - { - "id": "https://ror.org/03fmjzx88", - "name": "University of Winchester", - "email_address": null, - "ip_addresses": [ - ], - "established": 1840, - "types": [ - "Education" + "id": "https://ror.org/04tg98e93", + "links": [ + { + "type": "website", + "value": "https://aih-net.com" + } ], - "relationships": [ - - ], - "addresses": [ - { - "lat": 51.060338, - "lng": -1.325418, - "state": null, - "state_code": null, - "city": "Winchester", - "geonames_city": { - "id": 2633858, - "city": "Winchester", - "geonames_admin1": { - "name": "England", - "id": 6269131, - "ascii_name": "England", - "code": "GB.ENG" - }, - "geonames_admin2": { - "name": "Hampshire", - "id": 2647554, - "ascii_name": "Hampshire", - "code": "GB.ENG.F2" - }, - "license": { - "attribution": "Data from geonames.org under a CC-BY 4.0 license", - "license": "https://creativecommons.org/licenses/by/4.0/" - }, - "nuts_level1": { - "name": "SOUTH EAST (ENGLAND)", - "code": "UKJ" - }, - "nuts_level2": { - "name": "Hampshire and Isle of Wight", - "code": "UKJ3" - }, - "nuts_level3": { - "name": "Central Hampshire", - "code": "UKJ36" - } + "locations": [ + { + "geonames_details": { + "continent_code": "AS", + "continent_name": "Asia", + "country_code": "JP", + "country_name": "Japan", + "country_subdivision_code": "40", + "country_subdivision_name": "Fukuoka", + "lat": 33.63654, + "lng": 130.68678, + "name": "Iizuka" }, - "postcode": null, - "primary": false, - "line": null, - "country_geonames_id": 2635167 + "geonames_id": 1861835 } ], - "links": [ - "http://www.winchester.ac.uk/pages/home.aspx" - ], - "aliases": [ - - ], - "acronyms": [ - + "names": [ + { + "lang": null, + "types": [ + "ror_display", + "label" + ], + "value": "Aso Iizuka Hospital" + }, + { + "lang": "ja", + "types": [ + "label" + ], + "value": "飯塚病院" + } ], + "relationships": [], "status": "active", - "wikipedia_url": "http://en.wikipedia.org/wiki/University_of_Winchester", - "labels": [ - - ], - "country": { - "country_name": "United Kingdom", - "country_code": "GB" - }, - "external_ids": { - "ISNI": { - "preferred": null, - "all": [ - "0000 0000 9422 2878" - ] + "types": [ + "healthcare" + ] + }, + { + "admin": { + "created": { + "date": "2018-11-14", + "schema_version": "1.0" }, - "FundRef": { - "preferred": null, + "last_modified": { + "date": "2024-12-11", + "schema_version": "2.1" + } + }, + "domains": [ + "imsciences.edu.pk" + ], + "established": 1995, + "external_ids": [ + { "all": [ - "100010057" - ] + "grid.444989.c" + ], + "preferred": "grid.444989.c", + "type": "grid" }, - "HESA": { - "preferred": null, + { "all": [ - "0021" - ] - }, - "UCAS": { + "0000 0004 0609 2495" + ], "preferred": null, - "all": [ - "W76" - ] + "type": "isni" }, - "UKPRN": { - "preferred": null, + { "all": [ - "10003614" - ] - }, - "OrgRef": { + "Q15983147" + ], "preferred": null, - "all": [ - "3140939" - ] + "type": "wikidata" + } + ], + "id": "https://ror.org/02m8e1r74", + "links": [ + { + "type": "website", + "value": "https://imsciences.edu.pk" }, - "Wikidata": { - "preferred": null, - "all": [ - "Q3551690" - ] + { + "type": "wikipedia", + "value": "https://en.wikipedia.org/wiki/Institute_of_Management_Sciences_(Peshawar)" + } + ], + "locations": [ + { + "geonames_details": { + "continent_code": "AS", + "continent_name": "Asia", + "country_code": "PK", + "country_name": "Pakistan", + "country_subdivision_code": "KP", + "country_subdivision_name": "Khyber Pakhtunkhwa", + "lat": 34.008, + "lng": 71.57849, + "name": "Peshawar" + }, + "geonames_id": 1168197 + } + ], + "names": [ + { + "lang": null, + "types": [ + "alias" + ], + "value": "IMSciences" }, - "GRID": { - "preferred": "grid.267454.6", - "all": "grid.267454.6" + { + "lang": "en", + "types": [ + "ror_display", + "label" + ], + "value": "Institute of Management Sciences Peshawar" } - } - }, - { - "id": "https://ror.org/01gw3d370", - "name": "University of Windsor", - "email_address": "", - "ip_addresses": [ - ], - "established": 1857, + "relationships": [], + "status": "active", "types": [ - "Education" + "education" + ] + }, + { + "admin": { + "created": { + "date": "2022-08-31", + "schema_version": "1.0" + }, + "last_modified": { + "date": "2024-12-11", + "schema_version": "2.1" + } + }, + "domains": [ + "galgotiacollege.edu" ], - "relationships": [ - - ], - "addresses": [ - { - "lat": 42.305196, - "lng": -83.067483, - "state": "Ontario", - "state_code": "CA-ON", - "city": "Windsor", - "geonames_city": { - "id": 6182962, - "city": "Windsor", - "geonames_admin1": { - "name": "Ontario", - "id": 6093943, - "ascii_name": "Ontario", - "code": "CA.08" - }, - "geonames_admin2": { - "name": null, - "id": null, - "ascii_name": null, - "code": null - }, - "license": { - "attribution": "Data from geonames.org under a CC-BY 4.0 license", - "license": "https://creativecommons.org/licenses/by/4.0/" - }, - "nuts_level1": { - "name": null, - "code": null - }, - "nuts_level2": { - "name": null, - "code": null - }, - "nuts_level3": { - "name": null, - "code": null - } - }, - "postcode": null, - "primary": false, - "line": null, - "country_geonames_id": 6251999 + "established": 1999, + "external_ids": [ + { + "all": [ + "0000 0004 1774 2078" + ], + "preferred": "0000 0004 1774 2078", + "type": "isni" } ], + "id": "https://ror.org/04a85ht85", "links": [ - "http://www.uwindsor.ca/" - ], - "aliases": [ - "UWindsor", - "Assumption University of Windsor" + { + "type": "website", + "value": "https://galgotiacollege.edu" + }, + { + "type": "wikipedia", + "value": "https://en.wikipedia.org/wiki/Galgotias_College" + } ], - "acronyms": [ - + "locations": [ + { + "geonames_details": { + "continent_code": "AS", + "continent_name": "Asia", + "country_code": "IN", + "country_name": "India", + "country_subdivision_code": "UP", + "country_subdivision_name": "Uttar Pradesh", + "lat": 28.49615, + "lng": 77.53601, + "name": "Greater Noida" + }, + "geonames_id": 6954929 + } ], - "status": "active", - "wikipedia_url": "http://en.wikipedia.org/wiki/University_of_Windsor", - "labels": [ + "names": [ + { + "lang": null, + "types": [ + "acronym" + ], + "value": "GCET" + }, + { + "lang": "en", + "types": [ + "ror_display", + "label" + ], + "value": "Galgotias College of Engineering & Technology" + }, { - "label": "Université de windsor", - "iso639": "fr" + "lang": "en", + "types": [ + "alias" + ], + "value": "Galgotias College of Engineering and Technology" } ], - "country": { - "country_name": "Canada", - "country_code": "CA" + "relationships": [], + "status": "active", + "types": [ + "education" + ] + }, + { + "admin": { + "created": { + "date": "2018-11-14", + "schema_version": "1.0" + }, + "last_modified": { + "date": "2024-12-11", + "schema_version": "2.1" + } }, - "external_ids": { - "ISNI": { - "preferred": null, + "domains": [ + "eli-beams.eu" + ], + "established": 2015, + "external_ids": [ + { "all": [ - "0000 0004 1936 9596" - ] + "grid.494603.c" + ], + "preferred": "grid.494603.c", + "type": "grid" }, - "FundRef": { - "preferred": "100009154", + { "all": [ - "100009154", - "501100000083" - ] + "0000 0004 7422 3856" + ], + "preferred": "0000 0004 7422 3856", + "type": "isni" }, - "OrgRef": { - "preferred": null, + { "all": [ - "342733" - ] - }, - "Wikidata": { + "Q39039051" + ], "preferred": null, - "all": [ - "Q2065769" - ] - }, - "GRID": { - "preferred": "grid.267455.7", - "all": "grid.267455.7" + "type": "wikidata" } - } - }, - { - "id": "https://ror.org/02gdzyx04", - "name": "University of Winnipeg", - "email_address": null, - "ip_addresses": [ - ], - "established": 1871, - "types": [ - "Education" + "id": "https://ror.org/00yzpcc69", + "links": [ + { + "type": "website", + "value": "https://www.eli-beams.eu" + } ], - "relationships": [ + "locations": [ { - "label": "Winnipeg Institute for Theoretical Physics", - "type": "Child", - "id": "https://ror.org/010tw2j24" - } - ], - "addresses": [ - { - "lat": 49.890122, - "lng": -97.153367, - "state": "Manitoba", - "state_code": "CA-MB", - "city": "Winnipeg", - "geonames_city": { - "id": 6183235, - "city": "Winnipeg", - "geonames_admin1": { - "name": "Manitoba", - "id": 6065171, - "ascii_name": "Manitoba", - "code": "CA.03" - }, - "geonames_admin2": { - "name": null, - "id": null, - "ascii_name": null, - "code": null - }, - "license": { - "attribution": "Data from geonames.org under a CC-BY 4.0 license", - "license": "https://creativecommons.org/licenses/by/4.0/" - }, - "nuts_level1": { - "name": null, - "code": null - }, - "nuts_level2": { - "name": null, - "code": null - }, - "nuts_level3": { - "name": null, - "code": null - } + "geonames_details": { + "continent_code": "EU", + "continent_name": "Europe", + "country_code": "CZ", + "country_name": "Czechia", + "country_subdivision_code": "20", + "country_subdivision_name": "Central Bohemia", + "lat": 49.96321, + "lng": 14.4585, + "name": "Dolní Břežany" }, - "postcode": null, - "primary": false, - "line": null, - "country_geonames_id": 6251999 + "geonames_id": 3076915 } ], - "links": [ - "http://www.uwinnipeg.ca/" - ], - "aliases": [ - - ], - "acronyms": [ - - ], - "status": "active", - "wikipedia_url": "http://en.wikipedia.org/wiki/University_of_Winnipeg", - "labels": [ + "names": [ + { + "lang": null, + "types": [ + "acronym" + ], + "value": "ELI-BL" + }, { - "label": "Université de winnipeg", - "iso639": "fr" + "lang": "en", + "types": [ + "ror_display", + "label" + ], + "value": "Extreme Light Infrastructure Beamlines" } ], - "country": { - "country_name": "Canada", - "country_code": "CA" - }, - "external_ids": { - "ISNI": { - "preferred": null, - "all": [ - "0000 0001 1703 4731" - ] + "relationships": [], + "status": "active", + "types": [ + "facility" + ] + }, + { + "admin": { + "created": { + "date": "2018-11-14", + "schema_version": "1.0" }, - "FundRef": { - "preferred": null, + "last_modified": { + "date": "2024-12-11", + "schema_version": "2.1" + } + }, + "domains": [], + "established": 1972, + "external_ids": [ + { "all": [ - "100009367" - ] + "grid.430864.d" + ], + "preferred": "grid.430864.d", + "type": "grid" }, - "OrgRef": { - "preferred": null, + { "all": [ - "587404" - ] - }, - "Wikidata": { + "0000 0000 9018 7542" + ], "preferred": null, - "all": [ - "Q472167" - ] - }, - "GRID": { - "preferred": "grid.267457.5", - "all": "grid.267457.5" + "type": "isni" } - } - }, - { - "id": "https://ror.org/03mnm0t94", - "name": "University of Wisconsin–Eau Claire", - "email_address": "", - "ip_addresses": [ - ], - "established": 1916, - "types": [ - "Education" + "id": "https://ror.org/02437s643", + "links": [ + { + "type": "website", + "value": "https://www.uillinois.edu" + } ], - "relationships": [ + "locations": [ { - "label": "University of Wisconsin System", - "type": "Parent", - "id": "https://ror.org/03ydkyb10" - } - ], - "addresses": [ - { - "lat": 44.79895, - "lng": -91.499346, - "state": "Wisconsin", - "state_code": "US-WI", - "city": "Eau Claire", - "geonames_city": { - "id": 5251436, - "city": "Eau Claire", - "geonames_admin1": { - "name": "Wisconsin", - "id": 5279468, - "ascii_name": "Wisconsin", - "code": "US.WI" - }, - "geonames_admin2": { - "name": "Eau Claire County", - "id": 5251439, - "ascii_name": "Eau Claire County", - "code": "US.WI.035" - }, - "license": { - "attribution": "Data from geonames.org under a CC-BY 4.0 license", - "license": "https://creativecommons.org/licenses/by/4.0/" - }, - "nuts_level1": { - "name": null, - "code": null - }, - "nuts_level2": { - "name": null, - "code": null - }, - "nuts_level3": { - "name": null, - "code": null - } + "geonames_details": { + "continent_code": "NA", + "continent_name": "North America", + "country_code": "US", + "country_name": "United States", + "country_subdivision_code": "IL", + "country_subdivision_name": "Illinois", + "lat": 42.27113, + "lng": -89.094, + "name": "Rockford" }, - "postcode": null, - "primary": false, - "line": null, - "country_geonames_id": 6252001 + "geonames_id": 4907959 } ], - "links": [ - "http://www.uwec.edu/" - ], - "aliases": [ - - ], - "acronyms": [ - "UWEC" + "names": [ + { + "lang": null, + "types": [ + "acronym" + ], + "value": "UICOMR" + }, + { + "lang": "en", + "types": [ + "ror_display", + "label" + ], + "value": "University of Illinois Chicago, Rockford campus" + }, + { + "lang": "en", + "types": [ + "alias" + ], + "value": "University of Illinois at Rockford" + } ], - "status": "active", - "wikipedia_url": "http://en.wikipedia.org/wiki/University_of_Wisconsin%E2%80%93Eau_Claire", - "labels": [ + "relationships": [ + { + "label": "University of Illinois Chicago", + "type": "parent", + "id": "https://ror.org/02mpq6x41" + }, { - "label": "Université du Wisconsin à Eau Claire", - "iso639": "fr" + "label": "Swedish American Hospital", + "type": "related", + "id": "https://ror.org/05scd7d31" } ], - "country": { - "country_name": "United States", - "country_code": "US" + "status": "active", + "types": [ + "education" + ] + }, + { + "admin": { + "created": { + "date": "2018-11-14", + "schema_version": "1.0" + }, + "last_modified": { + "date": "2024-12-11", + "schema_version": "2.1" + } }, - "external_ids": { - "ISNI": { - "preferred": null, + "domains": [ + "chatham.edu" + ], + "established": 1869, + "external_ids": [ + { "all": [ - "0000 0001 2227 2494" - ] + "grid.411264.4" + ], + "preferred": "grid.411264.4", + "type": "grid" }, - "FundRef": { - "preferred": null, + { "all": [ - "100010315" - ] - }, - "OrgRef": { + "0000 0000 9776 1631" + ], "preferred": null, - "all": [ - "496729" - ] + "type": "isni" }, - "Wikidata": { - "preferred": null, + { "all": [ - "Q3551771" - ] - }, - "GRID": { - "preferred": "grid.267460.1", - "all": "grid.267460.1" + "Q5087708" + ], + "preferred": null, + "type": "wikidata" } - } - }, - { - "id": "https://ror.org/05hbexn54", - "name": "University of Wisconsin–Green Bay", - "email_address": null, - "ip_addresses": [ - ], - "established": 1965, - "types": [ - "Education" + "id": "https://ror.org/05n2dnq32", + "links": [ + { + "type": "website", + "value": "https://www.chatham.edu" + }, + { + "type": "wikipedia", + "value": "http://en.wikipedia.org/wiki/Chatham_University" + } ], - "relationships": [ + "locations": [ { - "label": "University of Wisconsin System", - "type": "Parent", - "id": "https://ror.org/03ydkyb10" - } - ], - "addresses": [ - { - "lat": 44.533203, - "lng": -87.921521, - "state": "Wisconsin", - "state_code": "US-WI", - "city": "Green Bay", - "geonames_city": { - "id": 5254962, - "city": "Green Bay", - "geonames_admin1": { - "name": "Wisconsin", - "id": 5279468, - "ascii_name": "Wisconsin", - "code": "US.WI" - }, - "geonames_admin2": { - "name": "Brown County", - "id": 5246898, - "ascii_name": "Brown County", - "code": "US.WI.009" - }, - "license": { - "attribution": "Data from geonames.org under a CC-BY 4.0 license", - "license": "https://creativecommons.org/licenses/by/4.0/" - }, - "nuts_level1": { - "name": null, - "code": null - }, - "nuts_level2": { - "name": null, - "code": null - }, - "nuts_level3": { - "name": null, - "code": null - } + "geonames_details": { + "continent_code": "NA", + "continent_name": "North America", + "country_code": "US", + "country_name": "United States", + "country_subdivision_code": "PA", + "country_subdivision_name": "Pennsylvania", + "lat": 40.44062, + "lng": -79.99589, + "name": "Pittsburgh" }, - "postcode": null, - "primary": false, - "line": null, - "country_geonames_id": 6252001 + "geonames_id": 5206379 } ], - "links": [ - "http://www.uwgb.edu/" - ], - "aliases": [ - - ], - "acronyms": [ - "UWGB" - ], - "status": "active", - "wikipedia_url": "http://en.wikipedia.org/wiki/University_of_Wisconsin%E2%80%93Green_Bay", - "labels": [ + "names": [ { - "label": "Université du Wisconsin–Green Bay", - "iso639": "fr" + "lang": "en", + "types": [ + "ror_display", + "label" + ], + "value": "Chatham University" } ], - "country": { - "country_name": "United States", - "country_code": "US" + "relationships": [], + "status": "active", + "types": [ + "education" + ] + }, + { + "admin": { + "created": { + "date": "2018-11-14", + "schema_version": "1.0" + }, + "last_modified": { + "date": "2024-12-11", + "schema_version": "2.1" + } }, - "external_ids": { - "ISNI": { - "preferred": null, + "domains": [ + "lds.no" + ], + "established": 1992, + "external_ids": [ + { "all": [ - "0000 0001 0559 7692" - ] + "501100010678" + ], + "preferred": "501100010678", + "type": "fundref" }, - "OrgRef": { - "preferred": null, + { "all": [ - "1513886" - ] + "grid.416137.6" + ], + "preferred": "grid.416137.6", + "type": "grid" }, - "Wikidata": { - "preferred": null, + { "all": [ - "Q2378091" - ] - }, - "GRID": { - "preferred": "grid.267461.0", - "all": "grid.267461.0" + "0000 0004 0627 3157" + ], + "preferred": null, + "type": "isni" } - } - }, - { - "id": "https://ror.org/00x8ccz20", - "name": "University of Wisconsin–La Crosse", - "email_address": "", - "ip_addresses": [ - ], - "established": 1909, - "types": [ - "Education" + "id": "https://ror.org/03ym7ve89", + "links": [ + { + "type": "website", + "value": "https://www.lovisenbergsykehus.no" + } ], - "relationships": [ + "locations": [ { - "label": "University of Wisconsin System", - "type": "Parent", - "id": "https://ror.org/03ydkyb10" - } - ], - "addresses": [ - { - "lat": 43.815576, - "lng": -91.233517, - "state": "Wisconsin", - "state_code": "US-WI", - "city": "La Crosse", - "geonames_city": { - "id": 5258957, - "city": "La Crosse", - "geonames_admin1": { - "name": "Wisconsin", - "id": 5279468, - "ascii_name": "Wisconsin", - "code": "US.WI" - }, - "geonames_admin2": { - "name": "La Crosse County", - "id": 5258961, - "ascii_name": "La Crosse County", - "code": "US.WI.063" - }, - "license": { - "attribution": "Data from geonames.org under a CC-BY 4.0 license", - "license": "https://creativecommons.org/licenses/by/4.0/" - }, - "nuts_level1": { - "name": null, - "code": null - }, - "nuts_level2": { - "name": null, - "code": null - }, - "nuts_level3": { - "name": null, - "code": null - } + "geonames_details": { + "continent_code": "EU", + "continent_name": "Europe", + "country_code": "NO", + "country_name": "Norway", + "country_subdivision_code": "03", + "country_subdivision_name": "Oslo", + "lat": 59.91273, + "lng": 10.74609, + "name": "Oslo" }, - "postcode": null, - "primary": false, - "line": null, - "country_geonames_id": 6252001 + "geonames_id": 3143244 } ], - "links": [ - "http://www.uwlax.edu/Home/Future-Students/" - ], - "aliases": [ - - ], - "acronyms": [ - "UW–L" - ], - "status": "active", - "wikipedia_url": "http://en.wikipedia.org/wiki/University_of_Wisconsin%E2%80%93La_Crosse", - "labels": [ + "names": [ { - "label": "Université du Wisconsin–La Crosse", - "iso639": "fr" + "lang": "no", + "types": [ + "ror_display", + "label" + ], + "value": "Lovisenberg Diakonale Sykehus" } ], - "country": { - "country_name": "United States", - "country_code": "US" + "relationships": [], + "status": "active", + "types": [ + "funder", + "healthcare" + ] + }, + { + "admin": { + "created": { + "date": "2018-11-14", + "schema_version": "1.0" + }, + "last_modified": { + "date": "2024-12-11", + "schema_version": "2.1" + } }, - "external_ids": { - "ISNI": { - "preferred": null, + "domains": [ + "uim-makassar.ac.id" + ], + "established": 2000, + "external_ids": [ + { "all": [ - "0000 0001 2169 5137" - ] + "grid.443680.d" + ], + "preferred": "grid.443680.d", + "type": "grid" }, - "OrgRef": { - "preferred": null, + { "all": [ - "2422287" - ] - }, - "Wikidata": { + "0000 0001 0588 5299" + ], "preferred": null, + "type": "isni" + }, + { "all": [ - "Q2688358" - ] + "Q12523343" + ], + "preferred": null, + "type": "wikidata" + } + ], + "id": "https://ror.org/05baqgp89", + "links": [ + { + "type": "website", + "value": "https://uim-makassar.ac.id/" + } + ], + "locations": [ + { + "geonames_details": { + "continent_code": "AS", + "continent_name": "Asia", + "country_code": "ID", + "country_name": "Indonesia", + "country_subdivision_code": "SN", + "country_subdivision_name": "South Sulawesi", + "lat": -5.14861, + "lng": 119.43194, + "name": "Makassar" + }, + "geonames_id": 1622786 + } + ], + "names": [ + { + "lang": null, + "types": [ + "acronym" + ], + "value": "UIM" }, - "GRID": { - "preferred": "grid.267462.3", - "all": "grid.267462.3" + { + "lang": "id", + "types": [ + "ror_display", + "label" + ], + "value": "Universitas Islam Makassar" } - } + ], + "relationships": [], + "status": "active", + "types": [ + "education" + ] } ], "meta": { "types": [ { "id": "company", - "title": "Company", - "count": 29790 + "title": "company", + "count": 30791 }, { "id": "education", - "title": "Education", - "count": 20325 + "title": "education", + "count": 22599 + }, + { + "id": "funder", + "title": "funder", + "count": 17078 }, { "id": "nonprofit", - "title": "Nonprofit", - "count": 14187 + "title": "nonprofit", + "count": 15641 }, { "id": "healthcare", - "title": "Healthcare", - "count": 13107 + "title": "healthcare", + "count": 14062 }, { "id": "facility", - "title": "Facility", - "count": 10080 + "title": "facility", + "count": 12758 }, { "id": "other", - "title": "Other", - "count": 8369 + "title": "other", + "count": 9026 }, { "id": "government", - "title": "Government", - "count": 6511 + "title": "government", + "count": 7599 }, { "id": "archive", - "title": "Archive", - "count": 2967 + "title": "archive", + "count": 3104 } ], "countries": [ { "id": "us", "title": "United States", - "count": 31196 + "count": 32118 }, { "id": "gb", "title": "United Kingdom", - "count": 7410 + "count": 7581 }, { - "id": "de", - "title": "Germany", - "count": 5189 + "id": "jp", + "title": "Japan", + "count": 5754 }, { - "id": "cn", - "title": "China", - "count": 4846 + "id": "de", + "title": "Germany", + "count": 5372 }, { "id": "fr", "title": "France", - "count": 4344 + "count": 5110 }, { - "id": "jp", - "title": "Japan", - "count": 3940 + "id": "cn", + "title": "China", + "count": 5001 }, { "id": "ca", "title": "Canada", - "count": 3392 + "count": 3610 }, { "id": "in", "title": "India", - "count": 3075 + "count": 3399 }, { "id": "cz", "title": "Czech Republic", - "count": 2780 + "count": 2843 + }, + { + "id": "it", + "title": "Italy", + "count": 2196 + } + ], + "continents": [ + { + "id": "eu", + "title": "Europe", + "count": 45322 + }, + { + "id": "na", + "title": "North America", + "count": 37230 + }, + { + "id": "as", + "title": "Asia", + "count": 23498 + }, + { + "id": "af", + "title": "Africa", + "count": 3835 + }, + { + "id": "sa", + "title": "South America", + "count": 3583 + }, + { + "id": "oc", + "title": "Oceania", + "count": 1945 }, { - "id": "ru", - "title": "Russia", - "count": 2109 + "id": "an", + "title": "Antarctica", + "count": 2 } ], "statuses": [ { "id": "active", "title": "active", - "count": 105336 + "count": 115409 } ] } -} +} \ No newline at end of file From c11b4d5e6788b317213d4f1d2918f4987ba49348 Mon Sep 17 00:00:00 2001 From: Jesiel Viana Date: Wed, 6 Aug 2025 17:34:37 -0300 Subject: [PATCH 882/979] increase request timeout for ROR API (cherry picked from commit 7393ce023f9cce15effb78fb1cbd7bad7d84e4f3) --- .../ror/service/RorImportMetadataSourceServiceImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dspace-api/src/main/java/org/dspace/importer/external/ror/service/RorImportMetadataSourceServiceImpl.java b/dspace-api/src/main/java/org/dspace/importer/external/ror/service/RorImportMetadataSourceServiceImpl.java index 8298b6d6f011..2cc6c66c7ab3 100644 --- a/dspace-api/src/main/java/org/dspace/importer/external/ror/service/RorImportMetadataSourceServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/importer/external/ror/service/RorImportMetadataSourceServiceImpl.java @@ -46,7 +46,7 @@ public class RorImportMetadataSourceServiceImpl extends AbstractImportMetadataSo private String url; - private int timeout = 1000; + private int timeout = 5000; @Autowired private LiveImportClient liveImportClient; From 11141f34ab80161cd4fffa61301beaa561adcc3b Mon Sep 17 00:00:00 2001 From: nwoodward Date: Wed, 18 Dec 2024 14:25:41 -0600 Subject: [PATCH 883/979] make several usage statistics parameters configurable (cherry picked from commit cd5798593417e0501ae8fe881f9c82666dc02830) --- .../app/rest/utils/UsageReportUtils.java | 38 +++++++++++++------ dspace/config/modules/usage-statistics.cfg | 19 +++++++++- 2 files changed, 45 insertions(+), 12 deletions(-) diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/utils/UsageReportUtils.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/utils/UsageReportUtils.java index 4603569da84c..53f4317808a6 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/utils/UsageReportUtils.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/utils/UsageReportUtils.java @@ -27,6 +27,7 @@ import org.dspace.core.Constants; import org.dspace.core.Context; import org.dspace.handle.service.HandleService; +import org.dspace.services.ConfigurationService; import org.dspace.statistics.Dataset; import org.dspace.statistics.content.DatasetDSpaceObjectGenerator; import org.dspace.statistics.content.DatasetTimeGenerator; @@ -46,6 +47,9 @@ @Component public class UsageReportUtils { + @Autowired + private ConfigurationService configurationService; + @Autowired private HandleService handleService; @@ -135,13 +139,14 @@ public UsageReportRest createUsageReport(Context context, DSpaceObject dso, Stri */ private UsageReportRest resolveGlobalUsageReport(Context context) throws SQLException, IOException, ParseException, SolrServerException { + int topItemsLimit = configurationService.getIntProperty("usage-statistics.topItemsLimit", 10); + StatisticsListing statListing = new StatisticsListing( new StatisticsDataVisits()); - // Adding a new generator for our top 10 items without a name length delimiter + // Adding a new generator for our top n items without a name length delimiter DatasetDSpaceObjectGenerator dsoAxis = new DatasetDSpaceObjectGenerator(); - // TODO make max nr of top items (views wise)? Must be set - dsoAxis.addDsoChild(Constants.ITEM, 10, false, -1); + dsoAxis.addDsoChild(Constants.ITEM, topItemsLimit, false, -1); statListing.addDatasetGenerator(dsoAxis); Dataset dataset = statListing.getDataset(context, 1); @@ -182,7 +187,7 @@ private UsageReportRest resolveTotalVisits(Context context, DSpaceObject dso) UsageReportPointDsoTotalVisitsRest totalVisitPoint = new UsageReportPointDsoTotalVisitsRest(); totalVisitPoint.setType(StringUtils.substringAfterLast(dso.getClass().getName().toLowerCase(), ".")); totalVisitPoint.setId(dso.getID().toString()); - if (dataset.getColLabels().size() > 0) { + if (!dataset.getColLabels().isEmpty()) { totalVisitPoint.setLabel(dso.getName()); totalVisitPoint.addValue("views", Integer.valueOf(dataset.getMatrix()[0][0])); } else { @@ -205,10 +210,14 @@ private UsageReportRest resolveTotalVisits(Context context, DSpaceObject dso) */ private UsageReportRest resolveTotalVisitsPerMonth(Context context, DSpaceObject dso) throws SQLException, IOException, ParseException, SolrServerException { + String startDateInterval = + configurationService.getProperty("usage-statistics.startDateInterval", "-6"); + String endDateInterval = + configurationService.getProperty("usage-statistics.endDateInterval", "+1"); + StatisticsTable statisticsTable = new StatisticsTable(new StatisticsDataVisits(dso)); DatasetTimeGenerator timeAxis = new DatasetTimeGenerator(); - // TODO month start and end as request para? - timeAxis.setDateInterval("month", "-6", "+1"); + timeAxis.setDateInterval("month", startDateInterval, endDateInterval); statisticsTable.addDatasetGenerator(timeAxis); DatasetDSpaceObjectGenerator dsoAxis = new DatasetDSpaceObjectGenerator(); dsoAxis.addDsoChild(dso.getType(), 10, false, -1); @@ -275,7 +284,10 @@ private UsageReportRest resolveTotalDownloads(Context context, DSpaceObject dso) */ private UsageReportRest resolveTopCountries(Context context, DSpaceObject dso) throws SQLException, IOException, ParseException, SolrServerException { - Dataset dataset = this.getTypeStatsDataset(context, dso, "countryCode", 1); + int topCountriesLimit = + configurationService.getIntProperty("usage-statistics.topCountriesLimit", 100); + + Dataset dataset = this.getTypeStatsDataset(context, dso, "countryCode", topCountriesLimit, 1); UsageReportRest usageReportRest = new UsageReportRest(); for (int i = 0; i < dataset.getColLabels().size(); i++) { @@ -299,7 +311,10 @@ private UsageReportRest resolveTopCountries(Context context, DSpaceObject dso) */ private UsageReportRest resolveTopCities(Context context, DSpaceObject dso) throws SQLException, IOException, ParseException, SolrServerException { - Dataset dataset = this.getTypeStatsDataset(context, dso, "city", 1); + int topCitiesLimit = + configurationService.getIntProperty("usage-statistics.topCitiesLimit", 100); + + Dataset dataset = this.getTypeStatsDataset(context, dso, "city", topCitiesLimit, 1); UsageReportRest usageReportRest = new UsageReportRest(); for (int i = 0; i < dataset.getColLabels().size(); i++) { @@ -339,16 +354,17 @@ private Dataset getDSOStatsDataset(Context context, DSpaceObject dso, int facetM * @param dso DSO we want the stats dataset of * @param typeAxisString String of the type we want on the axis of the dataset (corresponds to solr field), * examples: countryCode, city + * @param typeAxisMax Maximum amount of results to return in the dataset * @param facetMinCount Minimum amount of results on a facet data point for it to be added to dataset * @return Stats dataset with the given type on the axis, of the given DSO and with given facetMinCount */ - private Dataset getTypeStatsDataset(Context context, DSpaceObject dso, String typeAxisString, int facetMinCount) + private Dataset getTypeStatsDataset(Context context, DSpaceObject dso, String typeAxisString, int typeAxisMax, + int facetMinCount) throws SQLException, IOException, ParseException, SolrServerException { StatisticsListing statListing = new StatisticsListing(new StatisticsDataVisits(dso)); DatasetTypeGenerator typeAxis = new DatasetTypeGenerator(); typeAxis.setType(typeAxisString); - // TODO make max nr of top countries/cities a request para? Must be set - typeAxis.setMax(100); + typeAxis.setMax(typeAxisMax); statListing.addDatasetGenerator(typeAxis); return statListing.getDataset(context, facetMinCount); } diff --git a/dspace/config/modules/usage-statistics.cfg b/dspace/config/modules/usage-statistics.cfg index c77bb1ca78a3..6d47a13dcfc4 100644 --- a/dspace/config/modules/usage-statistics.cfg +++ b/dspace/config/modules/usage-statistics.cfg @@ -60,4 +60,21 @@ usage-statistics.shardedByYear = false #anonymize_statistics.dns_mask = anonymized # Only anonymize statistics records older than this threshold (expressed in days) -#anonymize_statistics.time_threshold = 90 \ No newline at end of file +#anonymize_statistics.time_threshold = 90 + +# Maximum number of items to display in the usage statistics report for an entire repository +usage-statistics.topItemsLimit = 10 + +# Number of months to begin retrieving usage statistics for total visits per month of a DSpace object +# For example, -6 means include the previous six months +usage-statistics.startDateInterval = -6 + +# Number of months to end retrieving usage statistics for total visits per month of a DSpace object +# For example, +1 means include the current month +usage-statistics.endDateInterval = +1 + +# Maximum number of countries to display in the usage statistics reports +usage-statistics.topCountriesLimit = 100 + +# Maximum number of cities to display in the usage statistics reports +usage-statistics.topCitiesLimit = 100 From 9674bc05be5742ce19916904a71fd6f68e0fdba4 Mon Sep 17 00:00:00 2001 From: Kristof De Langhe Date: Wed, 3 Sep 2025 11:35:05 +0200 Subject: [PATCH 884/979] 133552: AIP packager bitstream url fix (cherry picked from commit 11da562ea40cf61790ee755a0069955a7a26586e) --- .../content/crosswalk/PREMISCrosswalk.java | 20 ++++--------------- 1 file changed, 4 insertions(+), 16 deletions(-) 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 39b6c8f29c80..1d38249e6b55 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 @@ -224,29 +224,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")); } From 6dff0b5221c3cef1106136b182371c9243e82002 Mon Sep 17 00:00:00 2001 From: Kristof De Langhe Date: Wed, 3 Sep 2025 11:56:42 +0200 Subject: [PATCH 885/979] 133552: unused import (cherry picked from commit 93240941d32b7a28533a666907d50387a7a1148a) --- .../main/java/org/dspace/content/crosswalk/PREMISCrosswalk.java | 2 -- 1 file changed, 2 deletions(-) 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 1d38249e6b55..6b6c0fd7c5a4 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; From d6c3ae1897f96aa1147fb607274027f2935ec834 Mon Sep 17 00:00:00 2001 From: JohnnyMendesC <177888064+JohnnyMendesC@users.noreply.github.com> Date: Thu, 4 Sep 2025 11:10:46 -0300 Subject: [PATCH 886/979] fix(#11191): Align Content-Disposition with RFC 5987/6266 --- .../rest/utils/HttpHeadersInitializer.java | 54 +++++++++++++++++-- .../app/rest/BitstreamRestControllerIT.java | 11 ++-- 2 files changed, 58 insertions(+), 7 deletions(-) diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/utils/HttpHeadersInitializer.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/utils/HttpHeadersInitializer.java index d1b80c36750b..2e9bc260f49f 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/utils/HttpHeadersInitializer.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/utils/HttpHeadersInitializer.java @@ -7,11 +7,13 @@ */ package org.dspace.app.rest.utils; -import static jakarta.mail.internet.MimeUtility.encodeText; import static java.util.Objects.isNull; import static java.util.Objects.nonNull; import java.io.IOException; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; +import java.text.Normalizer; import java.util.Arrays; import java.util.Collections; import java.util.Objects; @@ -171,9 +173,16 @@ public HttpHeaders initialiseHeaders() throws IOException { // distposition may be null here if contentType is null if (!isNullOrEmpty(disposition)) { - httpHeaders.put(CONTENT_DISPOSITION, Collections.singletonList(String.format(CONTENT_DISPOSITION_FORMAT, - disposition, - encodeText(fileName)))); + String fallbackAsciiName = createFallbackAsciiName(this.fileName); + String encodedUtf8Name = createEncodedUtf8Name(this.fileName); + + String headerValue = String.format( + "%s; filename=\"%s\"; filename*=UTF-8''%s", + disposition, + fallbackAsciiName, + encodedUtf8Name + ); + httpHeaders.put(CONTENT_DISPOSITION, Collections.singletonList(headerValue)); } log.debug("Content-Disposition : {}", disposition); @@ -261,4 +270,41 @@ private static boolean matches(String matchHeader, String toMatch) { return Arrays.binarySearch(matchValues, toMatch) > -1 || Arrays.binarySearch(matchValues, "*") > -1; } + /** + * Creates a safe ASCII-only fallback filename by removing diacritics (accents) + * and replacing any remaining non-ASCII characters. + * E.g., "ä-ö-é.pdf" becomes "a-o-e.pdf". + * @param originalFilename The original filename. + * @return A string containing only ASCII characters. + */ + private String createFallbackAsciiName(String originalFilename) { + if (originalFilename == null) { + return ""; + } + String normalized = Normalizer.normalize(originalFilename, Normalizer.Form.NFD); + String withoutAccents = normalized.replaceAll("\\p{InCombiningDiacriticalMarks}+", ""); + return withoutAccents.replaceAll("[^\\x00-\\x7F]", ""); + } + + /** + * Creates a percent-encoded UTF-8 filename according to RFC 5987. + * This is for the `filename*` parameter. + * E.g., "ä ö é.pdf" becomes "%C3%A4%20%C3%B6%20%C3%A9.pdf". + * @param originalFilename The original filename. + * @return A percent-encoded string. + */ + private String createEncodedUtf8Name(String originalFilename) { + if (originalFilename == null) { + return ""; + } + try { + String encoded = URLEncoder.encode(originalFilename, StandardCharsets.UTF_8.toString()); + return encoded.replace("+", "%20"); + } catch (java.io.UnsupportedEncodingException e) { + // Fallback to a simple ASCII name if encoding fails. + log.error("UTF-8 encoding not supported, which should not happen.", e); + return createFallbackAsciiName(originalFilename); + } + } + } diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/BitstreamRestControllerIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/BitstreamRestControllerIT.java index 691927c6e457..596e4e0c6b70 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/BitstreamRestControllerIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/BitstreamRestControllerIT.java @@ -7,7 +7,6 @@ */ package org.dspace.app.rest; -import static jakarta.mail.internet.MimeUtility.encodeText; import static java.util.UUID.randomUUID; import static org.apache.commons.codec.CharEncoding.UTF_8; import static org.apache.commons.collections.CollectionUtils.isEmpty; @@ -348,7 +347,11 @@ public void testBitstreamName() throws Exception { //2. A public item with a bitstream String bitstreamContent = "0123456789"; - String bitstreamName = "ภาษาไทย"; + String bitstreamName = "ภาษาไทย-com-acentuação.pdf"; + String expectedAscii = "-com-acentuacao.pdf"; + String expectedUtf8Encoded = + "%E0%B8%A0%E0%B8%B2%E0%B8%A9%E0%B8%B2%E0%B9%84%E0%B8%97%E0%B8%A2-" + + "com-acentua%C3%A7%C3%A3o.pdf"; try (InputStream is = IOUtils.toInputStream(bitstreamContent, CharEncoding.UTF_8)) { @@ -372,7 +375,9 @@ public void testBitstreamName() throws Exception { //We expect the content disposition to have the encoded bitstream name .andExpect(header().string( "Content-Disposition", - "attachment;filename=\"" + encodeText(bitstreamName) + "\"" + String.format("attachment; filename=\"%s\"; filename*=UTF-8''%s", + expectedAscii, + expectedUtf8Encoded) )); } From 3ea0befd28848c834852207edff06fec03ccad58 Mon Sep 17 00:00:00 2001 From: Stefano Maffei Date: Thu, 14 Aug 2025 12:02:08 +0200 Subject: [PATCH 887/979] [CST-21947] fix security fix --- .../org/dspace/storage/bitstore/DSBitStoreService.java | 7 ++++++- dspace/config/modules/assetstore.cfg | 9 ++++++--- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/storage/bitstore/DSBitStoreService.java b/dspace-api/src/main/java/org/dspace/storage/bitstore/DSBitStoreService.java index 7743b93ca4ba..fd4e23b82581 100644 --- a/dspace-api/src/main/java/org/dspace/storage/bitstore/DSBitStoreService.java +++ b/dspace-api/src/main/java/org/dspace/storage/bitstore/DSBitStoreService.java @@ -19,9 +19,11 @@ import java.util.List; import java.util.Map; +import org.apache.commons.lang3.StringUtils; import org.apache.logging.log4j.Logger; import org.dspace.content.Bitstream; import org.dspace.core.Utils; +import org.dspace.services.factory.DSpaceServicesFactory; /** * Native DSpace (or "Directory Scatter" if you prefer) asset store. @@ -252,7 +254,10 @@ protected File getFile(Bitstream bitstream) throws IOException { } File bitstreamFile = new File(bufFilename.toString()); Path normalizedPath = bitstreamFile.toPath().normalize(); - if (!normalizedPath.startsWith(baseDir.getAbsolutePath())) { + String[] allowedAssetstoreRoots = DSpaceServicesFactory.getInstance().getConfigurationService() + .getArrayProperty("assetstore.allowed.roots", new String[]{}); + if (!normalizedPath.startsWith(baseDir.getAbsolutePath()) + && !StringUtils.startsWithAny(normalizedPath.toString(), allowedAssetstoreRoots)) { log.error("Bitstream path outside of assetstore root requested:" + "bitstream={}, path={}, assetstore={}", bitstream.getID(), normalizedPath, baseDir.getAbsolutePath()); diff --git a/dspace/config/modules/assetstore.cfg b/dspace/config/modules/assetstore.cfg index cbee6bd2c3a4..5e625e0c8a1a 100644 --- a/dspace/config/modules/assetstore.cfg +++ b/dspace/config/modules/assetstore.cfg @@ -12,12 +12,15 @@ assetstore.dir = ${dspace.dir}/assetstore # This value will be used as `incoming` default store inside the `bitstore.xml` # Possible values are: # - 0: to use the `localStore`; -# - 1: to use the `s3Store`. +# - 1: to use the `s3Store`. # If you want to add additional assetstores, they must be added to that bitstore.xml # and new values should be provided as key-value pairs in the `stores` map of the -# `bitstore.xml` configuration. +# `bitstore.xml` configuration. assetstore.index.primary = 0 +#if the assetstore path is symbolic link, use this configuration to allow that path. +#assetstore.allowed.roots = /data/assetstore + #---------------------------------------------------------------# #-------------- Amazon S3 Specific Configurations --------------# #---------------------------------------------------------------# @@ -54,4 +57,4 @@ assetstore.s3.awsSecretKey = # If the credentials are left empty, # then this setting is ignored and the default AWS region will be used. -assetstore.s3.awsRegionName = \ No newline at end of file +assetstore.s3.awsRegionName = From 21ce7ddcf0aad12d4eddac2958b0e6732330c6d5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 8 Sep 2025 21:04:15 +0000 Subject: [PATCH 888/979] Bump com.amazonaws:aws-java-sdk-s3 from 1.12.788 to 1.12.791 Bumps [com.amazonaws:aws-java-sdk-s3](https://github.com/aws/aws-sdk-java) from 1.12.788 to 1.12.791. - [Changelog](https://github.com/aws/aws-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java/compare/1.12.788...1.12.791) --- updated-dependencies: - dependency-name: com.amazonaws:aws-java-sdk-s3 dependency-version: 1.12.791 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- dspace-api/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dspace-api/pom.xml b/dspace-api/pom.xml index 39944a08b593..108e5645cf31 100644 --- a/dspace-api/pom.xml +++ b/dspace-api/pom.xml @@ -717,7 +717,7 @@ com.amazonaws aws-java-sdk-s3 - 1.12.788 + 1.12.791 @@ -64,21 +62,14 @@ - - - - - - - + - - + From 667de82136477153b179ce85dec03b60eff7dcfe Mon Sep 17 00:00:00 2001 From: Sascha Szott Date: Tue, 23 Sep 2025 16:25:33 +0200 Subject: [PATCH 902/979] remove ratingreviewaction in test context (cherry picked from commit ddccb342d6aa2d43af67cf2d38dc9d2b25c98313) --- .../config/spring/api/workflow-actions.xml | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/dspace-api/src/test/data/dspaceFolder/config/spring/api/workflow-actions.xml b/dspace-api/src/test/data/dspaceFolder/config/spring/api/workflow-actions.xml index 0d074362279e..a7c725c524fe 100644 --- a/dspace-api/src/test/data/dspaceFolder/config/spring/api/workflow-actions.xml +++ b/dspace-api/src/test/data/dspaceFolder/config/spring/api/workflow-actions.xml @@ -23,7 +23,6 @@ - @@ -46,7 +45,6 @@ - @@ -66,21 +64,14 @@ - - - - - - - + - - + From e0c68eb5eaa295a57368fa31baf36f77dedc8e34 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Mon, 22 Sep 2025 16:57:20 -0500 Subject: [PATCH 903/979] Change to "mode=min" in order to minimize our cache size for Docker images (cherry picked from commit 1f8e290a1ec2b66fd217b0a6a3e533fb6adda714) --- .github/workflows/reusable-docker-build.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/reusable-docker-build.yml b/.github/workflows/reusable-docker-build.yml index 0c3261da95da..a3729c27f6ee 100644 --- a/.github/workflows/reusable-docker-build.yml +++ b/.github/workflows/reusable-docker-build.yml @@ -164,7 +164,7 @@ jobs: # 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=max + cache-to: type=gha,scope=${{ inputs.build_id }},mode=min # Export the digest of Docker build locally - name: Export Docker build digest @@ -216,7 +216,7 @@ jobs: # 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=max + cache-to: type=gha,scope=${{ inputs.build_id }},mode=min # Export image to a local TAR file outputs: type=docker,dest=/tmp/${{ inputs.build_id }}.tar From ef9b9898010a47922f67318daeaf4ac255d346b7 Mon Sep 17 00:00:00 2001 From: nwoodward Date: Fri, 19 Sep 2025 15:43:26 -0500 Subject: [PATCH 904/979] fix Hibernate bugs (cherry picked from commit 088463c94b108ec097ec659ecb111e27fa30b7d5) --- .../content/dao/impl/CollectionDAOImpl.java | 24 ++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) 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 841da319f0b2..b5a5919239b4 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 @@ -12,6 +12,7 @@ import java.util.ArrayList; import java.util.List; import java.util.Map; +import java.util.UUID; import jakarta.persistence.Query; import jakarta.persistence.criteria.CriteriaBuilder; @@ -19,6 +20,7 @@ import jakarta.persistence.criteria.Join; import jakarta.persistence.criteria.Predicate; import jakarta.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 +42,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(); } @@ -172,14 +179,25 @@ 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 list = query.getResultList(); List> returnList = new ArrayList<>(list.size()); for (Object[] o : list) { - returnList.add(new AbstractMap.SimpleEntry<>((Collection) o[0], (Long) o[1])); + CriteriaQuery criteriaQuery = criteriaBuilder.createQuery(Collection.class); + Root collectionRoot = criteriaQuery.from(Collection.class); + criteriaQuery.select(collectionRoot).where(criteriaBuilder.equal(collectionRoot.get("id"), (UUID) o[0])); + Query collectionQuery = createQuery(context, criteriaQuery); + Collection collection = (Collection) collectionQuery.getSingleResult(); + if (collection != null) { + returnList.add(new AbstractMap.SimpleEntry<>(collection, (Long) o[1])); + } else { + log.warn("Unable to find Collection with UUID: {}", o[0]); + } } return returnList; } From fdc33d709b6db25cdf526fa5a5d8c49c42a9c7bf Mon Sep 17 00:00:00 2001 From: nwoodward Date: Fri, 19 Sep 2025 15:43:44 -0500 Subject: [PATCH 905/979] fix Hibernate bug (cherry picked from commit 69c6d273224447334ac4848e63123c4af2349ae9) --- .../main/java/org/dspace/content/dao/impl/BitstreamDAOImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dspace-api/src/main/java/org/dspace/content/dao/impl/BitstreamDAOImpl.java b/dspace-api/src/main/java/org/dspace/content/dao/impl/BitstreamDAOImpl.java index 25f102f6def4..66a775e39d80 100644 --- a/dspace-api/src/main/java/org/dspace/content/dao/impl/BitstreamDAOImpl.java +++ b/dspace-api/src/main/java/org/dspace/content/dao/impl/BitstreamDAOImpl.java @@ -178,7 +178,7 @@ public int countDeleted(Context context) throws SQLException { @Override public int countWithNoPolicy(Context context) throws SQLException { Query query = createQuery(context, - "SELECT count(bit.id) from Bitstream bit where bit.deleted<>true and bit.id not in" + + "SELECT count(bit.id) from Bitstream bit where bit.deleted<>true and bit not in" + " (select res.dSpaceObject from ResourcePolicy res where res.resourceTypeId = " + ":typeId )"); query.setParameter("typeId", Constants.BITSTREAM); From ba41a8755ccd4d02eeb5e42150b36e6c70d716a6 Mon Sep 17 00:00:00 2001 From: nwoodward Date: Fri, 19 Sep 2025 14:06:09 -0500 Subject: [PATCH 906/979] fix hibernate syntax bug (cherry picked from commit d9cbb9665569431596e980d583bb7c802b899a47) --- .../java/org/dspace/content/dao/impl/CollectionDAOImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 b5a5919239b4..4530e6eeda40 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 @@ -166,7 +166,7 @@ public List findAuthorizedByGroup(Context context, EPerson ePerson, @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")); } From 6611466826469035834f68806fbb07a472b5b5b1 Mon Sep 17 00:00:00 2001 From: nwoodward Date: Fri, 19 Sep 2025 14:06:52 -0500 Subject: [PATCH 907/979] lint fixes (cherry picked from commit b8812aad458e41b7aeed78430c06dd4e6d282d23) --- .../src/main/java/org/dspace/health/UserCheck.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/health/UserCheck.java b/dspace-api/src/main/java/org/dspace/health/UserCheck.java index 19a2a9ced355..875bb1bb0b60 100644 --- a/dspace-api/src/main/java/org/dspace/health/UserCheck.java +++ b/dspace-api/src/main/java/org/dspace/health/UserCheck.java @@ -50,26 +50,26 @@ public String run(ReportInfo ri) { info.put("Self registered", 0); for (EPerson e : epersons) { - if (e.getEmail() != null && e.getEmail().length() > 0) { + if (e.getEmail() != null && !e.getEmail().isEmpty()) { info.put("Have email", info.get("Have email") + 1); } if (e.canLogIn()) { info.put("Can log in (password)", info.get("Can log in (password)") + 1); } - if (e.getFirstName() != null && e.getFirstName().length() > 0) { + if (e.getFirstName() != null && !e.getFirstName().isEmpty()) { info.put("Have 1st name", info.get("Have 1st name") + 1); } - if (e.getLastName() != null && e.getLastName().length() > 0) { + if (e.getLastName() != null && !e.getLastName().isEmpty()) { info.put("Have 2nd name", info.get("Have 2nd name") + 1); } - if (e.getLanguage() != null && e.getLanguage().length() > 0) { + if (e.getLanguage() != null && !e.getLanguage().isEmpty()) { info.put("Have lang", info.get("Have lang") + 1); } - if (e.getNetid() != null && e.getNetid().length() > 0) { + if (e.getNetid() != null && !e.getNetid().isEmpty()) { info.put("Have netid", info.get("Have netid") + 1); } - if (e.getNetid() != null && e.getNetid().length() > 0) { + if (e.getNetid() != null && !e.getNetid().isEmpty()) { info.put("Self registered", info.get("Self registered") + 1); } } From d56126fe007e5ba31c4b4bcf34c698a17710f19c Mon Sep 17 00:00:00 2001 From: JohnnyMendesC <177888064+JohnnyMendesC@users.noreply.github.com> Date: Fri, 19 Sep 2025 12:50:37 -0300 Subject: [PATCH 908/979] Fix (#9694): Change Solr dynamic field *.year to *_year (cherry picked from commit 9fc163fbdacd99c78518a5372881df1427c30275) --- dspace/solr/search/conf/schema.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dspace/solr/search/conf/schema.xml b/dspace/solr/search/conf/schema.xml index 66f2f176d2d6..5c79519159a4 100644 --- a/dspace/solr/search/conf/schema.xml +++ b/dspace/solr/search/conf/schema.xml @@ -324,7 +324,7 @@ - + From ae7e1387a6c2e2242dfe4b091a9b7c90f9d073ac Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 Oct 2025 02:18:26 +0000 Subject: [PATCH 909/979] Bump the build-tools group with 10 updates Bumps the build-tools group with 10 updates: | Package | From | To | | --- | --- | --- | | [com.google.errorprone:error_prone_core](https://github.com/google/error-prone) | `2.41.0` | `2.42.0` | | [com.google.errorprone:error_prone_annotations](https://github.com/google/error-prone) | `2.41.0` | `2.42.0` | | [com.github.spotbugs:spotbugs](https://github.com/spotbugs/spotbugs) | `4.9.4` | `4.9.6` | | [org.apache.maven.plugins:maven-compiler-plugin](https://github.com/apache/maven-compiler-plugin) | `3.14.0` | `3.14.1` | | [org.apache.maven.plugins:maven-surefire-plugin](https://github.com/apache/maven-surefire) | `3.5.3` | `3.5.4` | | [org.apache.maven.plugins:maven-failsafe-plugin](https://github.com/apache/maven-surefire) | `3.5.3` | `3.5.4` | | [com.github.spotbugs:spotbugs-maven-plugin](https://github.com/spotbugs/spotbugs-maven-plugin) | `4.9.4.2` | `4.9.6.0` | | [org.sonatype.central:central-publishing-maven-plugin](https://github.com/sonatype/central-publishing-maven-plugin) | `0.8.0` | `0.9.0` | | [org.apache.maven.plugins:maven-javadoc-plugin](https://github.com/apache/maven-javadoc-plugin) | `3.11.3` | `3.12.0` | | [org.codehaus.mojo:license-maven-plugin](https://github.com/mojohaus/license-maven-plugin) | `2.6.0` | `2.7.0` | Updates `com.google.errorprone:error_prone_core` from 2.41.0 to 2.42.0 - [Release notes](https://github.com/google/error-prone/releases) - [Commits](https://github.com/google/error-prone/compare/v2.41.0...v2.42.0) Updates `com.google.errorprone:error_prone_annotations` from 2.41.0 to 2.42.0 - [Release notes](https://github.com/google/error-prone/releases) - [Commits](https://github.com/google/error-prone/compare/v2.41.0...v2.42.0) Updates `com.github.spotbugs:spotbugs` from 4.9.4 to 4.9.6 - [Release notes](https://github.com/spotbugs/spotbugs/releases) - [Changelog](https://github.com/spotbugs/spotbugs/blob/master/CHANGELOG.md) - [Commits](https://github.com/spotbugs/spotbugs/compare/4.9.4...4.9.6) Updates `com.google.errorprone:error_prone_annotations` from 2.41.0 to 2.42.0 - [Release notes](https://github.com/google/error-prone/releases) - [Commits](https://github.com/google/error-prone/compare/v2.41.0...v2.42.0) Updates `org.apache.maven.plugins:maven-compiler-plugin` from 3.14.0 to 3.14.1 - [Release notes](https://github.com/apache/maven-compiler-plugin/releases) - [Commits](https://github.com/apache/maven-compiler-plugin/compare/maven-compiler-plugin-3.14.0...maven-compiler-plugin-3.14.1) Updates `org.apache.maven.plugins:maven-surefire-plugin` from 3.5.3 to 3.5.4 - [Release notes](https://github.com/apache/maven-surefire/releases) - [Commits](https://github.com/apache/maven-surefire/compare/surefire-3.5.3...surefire-3.5.4) Updates `org.apache.maven.plugins:maven-failsafe-plugin` from 3.5.3 to 3.5.4 - [Release notes](https://github.com/apache/maven-surefire/releases) - [Commits](https://github.com/apache/maven-surefire/compare/surefire-3.5.3...surefire-3.5.4) Updates `com.github.spotbugs:spotbugs-maven-plugin` from 4.9.4.2 to 4.9.6.0 - [Release notes](https://github.com/spotbugs/spotbugs-maven-plugin/releases) - [Commits](https://github.com/spotbugs/spotbugs-maven-plugin/compare/spotbugs-maven-plugin-4.9.4.2...spotbugs-maven-plugin-4.9.6.0) Updates `org.sonatype.central:central-publishing-maven-plugin` from 0.8.0 to 0.9.0 - [Commits](https://github.com/sonatype/central-publishing-maven-plugin/commits) Updates `org.apache.maven.plugins:maven-javadoc-plugin` from 3.11.3 to 3.12.0 - [Release notes](https://github.com/apache/maven-javadoc-plugin/releases) - [Commits](https://github.com/apache/maven-javadoc-plugin/compare/maven-javadoc-plugin-3.11.3...maven-javadoc-plugin-3.12.0) Updates `org.codehaus.mojo:license-maven-plugin` from 2.6.0 to 2.7.0 - [Release notes](https://github.com/mojohaus/license-maven-plugin/releases) - [Commits](https://github.com/mojohaus/license-maven-plugin/compare/2.6.0...2.7.0) --- updated-dependencies: - dependency-name: com.google.errorprone:error_prone_core dependency-version: 2.42.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: build-tools - dependency-name: com.google.errorprone:error_prone_annotations dependency-version: 2.42.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: build-tools - dependency-name: com.github.spotbugs:spotbugs dependency-version: 4.9.6 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: build-tools - dependency-name: com.google.errorprone:error_prone_annotations dependency-version: 2.42.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: build-tools - dependency-name: org.apache.maven.plugins:maven-compiler-plugin dependency-version: 3.14.1 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: build-tools - dependency-name: org.apache.maven.plugins:maven-surefire-plugin dependency-version: 3.5.4 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: build-tools - dependency-name: org.apache.maven.plugins:maven-failsafe-plugin dependency-version: 3.5.4 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: build-tools - dependency-name: com.github.spotbugs:spotbugs-maven-plugin dependency-version: 4.9.6.0 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: build-tools - dependency-name: org.sonatype.central:central-publishing-maven-plugin dependency-version: 0.9.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: build-tools - dependency-name: org.apache.maven.plugins:maven-javadoc-plugin dependency-version: 3.12.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: build-tools - dependency-name: org.codehaus.mojo:license-maven-plugin dependency-version: 2.7.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: build-tools ... Signed-off-by: dependabot[bot] --- pom.xml | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/pom.xml b/pom.xml index 5c12bb219acf..24cb4e51625f 100644 --- a/pom.xml +++ b/pom.xml @@ -29,7 +29,7 @@ 8.11.4 3.11.1 - 2.41.0 + 2.42.0 2.19.2 2.19.2 @@ -140,7 +140,7 @@ org.apache.maven.plugins maven-compiler-plugin - 3.14.0 + 3.14.1 ${java.version} @@ -208,7 +208,7 @@ org.apache.maven.plugins maven-surefire-plugin - 3.5.3 + 3.5.4 @@ -235,7 +235,7 @@ maven-failsafe-plugin - 3.5.3 + 3.5.4 @@ -303,7 +303,7 @@ com.github.spotbugs spotbugs-maven-plugin - 4.9.4.2 + 4.9.6.0 Max Low @@ -313,7 +313,7 @@ com.github.spotbugs spotbugs - 4.9.4 + 4.9.6 @@ -365,13 +365,13 @@ org.sonatype.central central-publishing-maven-plugin - 0.8.0 + 0.9.0 org.apache.maven.plugins maven-javadoc-plugin - 3.11.3 + 3.12.0 false @@ -693,7 +693,7 @@ org.codehaus.mojo license-maven-plugin - 2.6.0 + 2.7.0 false From 1642fa705fd26b00c3b655fb7144114ba58adeb8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 Oct 2025 02:18:46 +0000 Subject: [PATCH 910/979] Bump the test-tools group with 3 updates Bumps the test-tools group with 3 updates: [com.h2database:h2](https://github.com/h2database/h2database), [org.xmlunit:xmlunit-core](https://github.com/xmlunit/xmlunit) and [org.apache.httpcomponents.client5:httpclient5](https://github.com/apache/httpcomponents-client). Updates `com.h2database:h2` from 2.3.232 to 2.4.240 - [Release notes](https://github.com/h2database/h2database/releases) - [Commits](https://github.com/h2database/h2database/compare/version-2.3.232...version-2.4.240) Updates `org.xmlunit:xmlunit-core` from 2.10.3 to 2.10.4 - [Release notes](https://github.com/xmlunit/xmlunit/releases) - [Changelog](https://github.com/xmlunit/xmlunit/blob/main/RELEASE_NOTES.md) - [Commits](https://github.com/xmlunit/xmlunit/compare/v2.10.3...v2.10.4) Updates `org.apache.httpcomponents.client5:httpclient5` from 5.5 to 5.5.1 - [Changelog](https://github.com/apache/httpcomponents-client/blob/rel/v5.5.1/RELEASE_NOTES.txt) - [Commits](https://github.com/apache/httpcomponents-client/compare/rel/v5.5...rel/v5.5.1) --- updated-dependencies: - dependency-name: com.h2database:h2 dependency-version: 2.4.240 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: test-tools - dependency-name: org.xmlunit:xmlunit-core dependency-version: 2.10.4 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: test-tools - dependency-name: org.apache.httpcomponents.client5:httpclient5 dependency-version: 5.5.1 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: test-tools ... Signed-off-by: dependabot[bot] --- dspace-api/pom.xml | 2 +- dspace-server-webapp/pom.xml | 2 +- pom.xml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/dspace-api/pom.xml b/dspace-api/pom.xml index 108e5645cf31..1aa596458823 100644 --- a/dspace-api/pom.xml +++ b/dspace-api/pom.xml @@ -771,7 +771,7 @@ org.xmlunit xmlunit-core - 2.10.3 + 2.10.4 test diff --git a/dspace-server-webapp/pom.xml b/dspace-server-webapp/pom.xml index 456eaba0b374..aaf177d4d4a4 100644 --- a/dspace-server-webapp/pom.xml +++ b/dspace-server-webapp/pom.xml @@ -590,7 +590,7 @@ org.apache.httpcomponents.client5 httpclient5 - 5.5 + 5.5.1 test diff --git a/pom.xml b/pom.xml index 5c12bb219acf..8c9a3f360b56 100644 --- a/pom.xml +++ b/pom.xml @@ -1703,7 +1703,7 @@ com.h2database h2 - 2.3.232 + 2.4.240 test From efd35e3a308eb5283d0e9a3357b73d531af6a2ad Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 Oct 2025 02:19:07 +0000 Subject: [PATCH 911/979] Bump org.apache.commons:commons-lang3 in the apache-commons group Bumps the apache-commons group with 1 update: org.apache.commons:commons-lang3. Updates `org.apache.commons:commons-lang3` from 3.18.0 to 3.19.0 --- updated-dependencies: - dependency-name: org.apache.commons:commons-lang3 dependency-version: 3.19.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: apache-commons ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 5c12bb219acf..5cd690c5436f 100644 --- a/pom.xml +++ b/pom.xml @@ -1508,7 +1508,7 @@ org.apache.commons commons-lang3 - 3.18.0 + 3.19.0 From bb86ef931b19d4350a4f2f3ff26554e6fd978c10 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 Oct 2025 02:19:33 +0000 Subject: [PATCH 912/979] Bump the jakarta group with 5 updates Bumps the jakarta group with 5 updates: | Package | From | To | | --- | --- | --- | | [jakarta.activation:jakarta.activation-api](https://github.com/jakartaee/jaf-api) | `2.1.3` | `2.1.4` | | [jakarta.mail:jakarta.mail-api](https://github.com/jakartaee/mail-api) | `2.1.4` | `2.1.5` | | org.eclipse.angus:jakarta.mail | `2.0.4` | `2.0.5` | | [jakarta.xml.bind:jakarta.xml.bind-api](https://github.com/jakartaee/jaxb-api) | `4.0.2` | `4.0.4` | | org.glassfish.jaxb:jaxb-runtime | `4.0.5` | `4.0.6` | Updates `jakarta.activation:jakarta.activation-api` from 2.1.3 to 2.1.4 - [Release notes](https://github.com/jakartaee/jaf-api/releases) - [Commits](https://github.com/jakartaee/jaf-api/compare/2.1.3...2.1.4) Updates `jakarta.mail:jakarta.mail-api` from 2.1.4 to 2.1.5 - [Release notes](https://github.com/jakartaee/mail-api/releases) - [Commits](https://github.com/jakartaee/mail-api/compare/2.1.4...2.1.5) Updates `org.eclipse.angus:jakarta.mail` from 2.0.4 to 2.0.5 Updates `jakarta.xml.bind:jakarta.xml.bind-api` from 4.0.2 to 4.0.4 - [Release notes](https://github.com/jakartaee/jaxb-api/releases) - [Commits](https://github.com/jakartaee/jaxb-api/compare/4.0.2...4.0.4) Updates `org.glassfish.jaxb:jaxb-runtime` from 4.0.5 to 4.0.6 --- updated-dependencies: - dependency-name: jakarta.activation:jakarta.activation-api dependency-version: 2.1.4 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: jakarta - dependency-name: jakarta.mail:jakarta.mail-api dependency-version: 2.1.5 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: jakarta - dependency-name: org.eclipse.angus:jakarta.mail dependency-version: 2.0.5 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: jakarta - dependency-name: jakarta.xml.bind:jakarta.xml.bind-api dependency-version: 4.0.4 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: jakarta - dependency-name: org.glassfish.jaxb:jaxb-runtime dependency-version: 4.0.6 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: jakarta ... Signed-off-by: dependabot[bot] --- pom.xml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/pom.xml b/pom.xml index 5c12bb219acf..c86c53199459 100644 --- a/pom.xml +++ b/pom.xml @@ -34,8 +34,8 @@ 2.19.2 2.19.2 2.1.1 - 4.0.2 - 4.0.5 + 4.0.4 + 4.0.6 1.1.1 9.4.58.v20250814 @@ -1545,7 +1545,7 @@ jakarta.activation jakarta.activation-api - 2.1.3 + 2.1.4 @@ -1557,14 +1557,14 @@ jakarta.mail jakarta.mail-api - 2.1.4 + 2.1.5 provided org.eclipse.angus jakarta.mail - 2.0.4 + 2.0.5 jakarta.servlet From 0a5acdbe25b51ca482ae1d6c5e4dff21196a1bd1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 Oct 2025 02:22:01 +0000 Subject: [PATCH 913/979] Bump the spring group with 25 updates Bumps the spring group with 25 updates: | Package | From | To | | --- | --- | --- | | [org.springframework:spring-orm](https://github.com/spring-projects/spring-framework) | `6.2.10` | `6.2.11` | | [org.springframework:spring-core](https://github.com/spring-projects/spring-framework) | `6.2.10` | `6.2.11` | | [org.springframework:spring-beans](https://github.com/spring-projects/spring-framework) | `6.2.10` | `6.2.11` | | [org.springframework:spring-aop](https://github.com/spring-projects/spring-framework) | `6.2.10` | `6.2.11` | | [org.springframework:spring-context](https://github.com/spring-projects/spring-framework) | `6.2.10` | `6.2.11` | | [org.springframework:spring-context-support](https://github.com/spring-projects/spring-framework) | `6.2.10` | `6.2.11` | | [org.springframework:spring-tx](https://github.com/spring-projects/spring-framework) | `6.2.10` | `6.2.11` | | [org.springframework:spring-jdbc](https://github.com/spring-projects/spring-framework) | `6.2.10` | `6.2.11` | | [org.springframework:spring-web](https://github.com/spring-projects/spring-framework) | `6.2.10` | `6.2.11` | | [org.springframework:spring-webmvc](https://github.com/spring-projects/spring-framework) | `6.2.10` | `6.2.11` | | [org.springframework:spring-expression](https://github.com/spring-projects/spring-framework) | `6.2.10` | `6.2.11` | | [org.springframework:spring-test](https://github.com/spring-projects/spring-framework) | `6.2.10` | `6.2.11` | | [org.springframework.boot:spring-boot-starter-test](https://github.com/spring-projects/spring-boot) | `3.5.5` | `3.5.6` | | [org.springframework.boot:spring-boot-starter-tomcat](https://github.com/spring-projects/spring-boot) | `3.5.5` | `3.5.6` | | [org.springframework.boot:spring-boot-maven-plugin](https://github.com/spring-projects/spring-boot) | `3.5.5` | `3.5.6` | | [org.springframework.boot:spring-boot-starter-cache](https://github.com/spring-projects/spring-boot) | `3.5.5` | `3.5.6` | | [org.springframework.boot:spring-boot-starter](https://github.com/spring-projects/spring-boot) | `3.5.5` | `3.5.6` | | [org.springframework.boot:spring-boot-starter-thymeleaf](https://github.com/spring-projects/spring-boot) | `3.5.5` | `3.5.6` | | [org.springframework.boot:spring-boot-starter-web](https://github.com/spring-projects/spring-boot) | `3.5.5` | `3.5.6` | | [org.springframework.boot:spring-boot-starter-data-rest](https://github.com/spring-projects/spring-boot) | `3.5.5` | `3.5.6` | | [org.springframework.boot:spring-boot-starter-security](https://github.com/spring-projects/spring-boot) | `3.5.5` | `3.5.6` | | [org.springframework.boot:spring-boot-starter-aop](https://github.com/spring-projects/spring-boot) | `3.5.5` | `3.5.6` | | [org.springframework.boot:spring-boot-starter-actuator](https://github.com/spring-projects/spring-boot) | `3.5.5` | `3.5.6` | | [org.springframework.boot:spring-boot-starter-log4j2](https://github.com/spring-projects/spring-boot) | `3.5.5` | `3.5.6` | | [org.springframework.security:spring-security-test](https://github.com/spring-projects/spring-security) | `6.5.3` | `6.5.5` | Updates `org.springframework:spring-orm` from 6.2.10 to 6.2.11 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.10...v6.2.11) Updates `org.springframework:spring-core` from 6.2.10 to 6.2.11 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.10...v6.2.11) Updates `org.springframework:spring-beans` from 6.2.10 to 6.2.11 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.10...v6.2.11) Updates `org.springframework:spring-aop` from 6.2.10 to 6.2.11 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.10...v6.2.11) Updates `org.springframework:spring-context` from 6.2.10 to 6.2.11 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.10...v6.2.11) Updates `org.springframework:spring-context-support` from 6.2.10 to 6.2.11 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.10...v6.2.11) Updates `org.springframework:spring-tx` from 6.2.10 to 6.2.11 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.10...v6.2.11) Updates `org.springframework:spring-jdbc` from 6.2.10 to 6.2.11 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.10...v6.2.11) Updates `org.springframework:spring-web` from 6.2.10 to 6.2.11 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.10...v6.2.11) Updates `org.springframework:spring-webmvc` from 6.2.10 to 6.2.11 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.10...v6.2.11) Updates `org.springframework:spring-expression` from 6.2.10 to 6.2.11 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.10...v6.2.11) Updates `org.springframework:spring-test` from 6.2.10 to 6.2.11 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.10...v6.2.11) Updates `org.springframework:spring-core` from 6.2.10 to 6.2.11 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.10...v6.2.11) Updates `org.springframework:spring-beans` from 6.2.10 to 6.2.11 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.10...v6.2.11) Updates `org.springframework:spring-aop` from 6.2.10 to 6.2.11 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.10...v6.2.11) Updates `org.springframework:spring-context` from 6.2.10 to 6.2.11 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.10...v6.2.11) Updates `org.springframework:spring-context-support` from 6.2.10 to 6.2.11 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.10...v6.2.11) Updates `org.springframework:spring-tx` from 6.2.10 to 6.2.11 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.10...v6.2.11) Updates `org.springframework:spring-jdbc` from 6.2.10 to 6.2.11 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.10...v6.2.11) Updates `org.springframework:spring-web` from 6.2.10 to 6.2.11 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.10...v6.2.11) Updates `org.springframework:spring-webmvc` from 6.2.10 to 6.2.11 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.10...v6.2.11) Updates `org.springframework:spring-expression` from 6.2.10 to 6.2.11 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.10...v6.2.11) Updates `org.springframework:spring-test` from 6.2.10 to 6.2.11 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.10...v6.2.11) Updates `org.springframework.boot:spring-boot-starter-test` from 3.5.5 to 3.5.6 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.5.5...v3.5.6) Updates `org.springframework.boot:spring-boot-starter-tomcat` from 3.5.5 to 3.5.6 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.5.5...v3.5.6) Updates `org.springframework.boot:spring-boot-maven-plugin` from 3.5.5 to 3.5.6 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.5.5...v3.5.6) Updates `org.springframework.boot:spring-boot-starter-cache` from 3.5.5 to 3.5.6 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.5.5...v3.5.6) Updates `org.springframework.boot:spring-boot-starter` from 3.5.5 to 3.5.6 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.5.5...v3.5.6) Updates `org.springframework.boot:spring-boot-starter-thymeleaf` from 3.5.5 to 3.5.6 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.5.5...v3.5.6) Updates `org.springframework.boot:spring-boot-starter-web` from 3.5.5 to 3.5.6 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.5.5...v3.5.6) Updates `org.springframework.boot:spring-boot-starter-data-rest` from 3.5.5 to 3.5.6 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.5.5...v3.5.6) Updates `org.springframework.boot:spring-boot-starter-security` from 3.5.5 to 3.5.6 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.5.5...v3.5.6) Updates `org.springframework.boot:spring-boot-starter-aop` from 3.5.5 to 3.5.6 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.5.5...v3.5.6) Updates `org.springframework.boot:spring-boot-starter-actuator` from 3.5.5 to 3.5.6 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.5.5...v3.5.6) Updates `org.springframework.boot:spring-boot-starter-log4j2` from 3.5.5 to 3.5.6 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.5.5...v3.5.6) Updates `org.springframework.boot:spring-boot-starter-tomcat` from 3.5.5 to 3.5.6 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.5.5...v3.5.6) Updates `org.springframework.security:spring-security-test` from 6.5.3 to 6.5.5 - [Release notes](https://github.com/spring-projects/spring-security/releases) - [Changelog](https://github.com/spring-projects/spring-security/blob/main/RELEASE.adoc) - [Commits](https://github.com/spring-projects/spring-security/compare/6.5.3...6.5.5) Updates `org.springframework.boot:spring-boot-maven-plugin` from 3.5.5 to 3.5.6 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.5.5...v3.5.6) Updates `org.springframework.boot:spring-boot-starter-cache` from 3.5.5 to 3.5.6 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.5.5...v3.5.6) Updates `org.springframework.boot:spring-boot-starter` from 3.5.5 to 3.5.6 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.5.5...v3.5.6) Updates `org.springframework.boot:spring-boot-starter-thymeleaf` from 3.5.5 to 3.5.6 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.5.5...v3.5.6) Updates `org.springframework.boot:spring-boot-starter-web` from 3.5.5 to 3.5.6 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.5.5...v3.5.6) Updates `org.springframework.boot:spring-boot-starter-data-rest` from 3.5.5 to 3.5.6 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.5.5...v3.5.6) Updates `org.springframework.boot:spring-boot-starter-security` from 3.5.5 to 3.5.6 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.5.5...v3.5.6) Updates `org.springframework.boot:spring-boot-starter-aop` from 3.5.5 to 3.5.6 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.5.5...v3.5.6) Updates `org.springframework.boot:spring-boot-starter-actuator` from 3.5.5 to 3.5.6 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.5.5...v3.5.6) Updates `org.springframework.boot:spring-boot-starter-log4j2` from 3.5.5 to 3.5.6 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.5.5...v3.5.6) --- updated-dependencies: - dependency-name: org.springframework:spring-orm dependency-version: 6.2.11 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-core dependency-version: 6.2.11 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-beans dependency-version: 6.2.11 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-aop dependency-version: 6.2.11 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-context dependency-version: 6.2.11 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-context-support dependency-version: 6.2.11 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-tx dependency-version: 6.2.11 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-jdbc dependency-version: 6.2.11 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-web dependency-version: 6.2.11 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-webmvc dependency-version: 6.2.11 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-expression dependency-version: 6.2.11 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-test dependency-version: 6.2.11 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-core dependency-version: 6.2.11 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-beans dependency-version: 6.2.11 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-aop dependency-version: 6.2.11 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-context dependency-version: 6.2.11 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-context-support dependency-version: 6.2.11 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-tx dependency-version: 6.2.11 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-jdbc dependency-version: 6.2.11 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-web dependency-version: 6.2.11 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-webmvc dependency-version: 6.2.11 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-expression dependency-version: 6.2.11 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-test dependency-version: 6.2.11 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter-test dependency-version: 3.5.6 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter-tomcat dependency-version: 3.5.6 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-maven-plugin dependency-version: 3.5.6 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter-cache dependency-version: 3.5.6 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter dependency-version: 3.5.6 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter-thymeleaf dependency-version: 3.5.6 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter-web dependency-version: 3.5.6 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter-data-rest dependency-version: 3.5.6 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter-security dependency-version: 3.5.6 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter-aop dependency-version: 3.5.6 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter-actuator dependency-version: 3.5.6 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter-log4j2 dependency-version: 3.5.6 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter-tomcat dependency-version: 3.5.6 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.security:spring-security-test dependency-version: 6.5.5 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-maven-plugin dependency-version: 3.5.6 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter-cache dependency-version: 3.5.6 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter dependency-version: 3.5.6 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter-thymeleaf dependency-version: 3.5.6 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter-web dependency-version: 3.5.6 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter-data-rest dependency-version: 3.5.6 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter-security dependency-version: 3.5.6 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter-aop dependency-version: 3.5.6 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter-actuator dependency-version: 3.5.6 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter-log4j2 dependency-version: 3.5.6 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring ... Signed-off-by: dependabot[bot] --- pom.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pom.xml b/pom.xml index 5c12bb219acf..1c58a04306e7 100644 --- a/pom.xml +++ b/pom.xml @@ -19,9 +19,9 @@ 17 - 6.2.10 - 3.5.5 - 6.5.3 + 6.2.11 + 3.5.6 + 6.5.5 6.4.10.Final 8.0.3.Final 42.7.7 From a26fc94ee1f7b95e668d1b714a983aa1f7ec112f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 Oct 2025 02:22:22 +0000 Subject: [PATCH 914/979] Bump bouncycastle.version from 1.81 to 1.82 Bumps `bouncycastle.version` from 1.81 to 1.82. Updates `org.bouncycastle:bcpkix-jdk18on` from 1.81 to 1.82 - [Changelog](https://github.com/bcgit/bc-java/blob/main/docs/releasenotes.html) - [Commits](https://github.com/bcgit/bc-java/commits) Updates `org.bouncycastle:bcprov-jdk18on` from 1.81 to 1.82 - [Changelog](https://github.com/bcgit/bc-java/blob/main/docs/releasenotes.html) - [Commits](https://github.com/bcgit/bc-java/commits) Updates `org.bouncycastle:bcutil-jdk18on` from 1.81 to 1.82 - [Changelog](https://github.com/bcgit/bc-java/blob/main/docs/releasenotes.html) - [Commits](https://github.com/bcgit/bc-java/commits) --- updated-dependencies: - dependency-name: org.bouncycastle:bcpkix-jdk18on dependency-version: '1.82' dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: org.bouncycastle:bcprov-jdk18on dependency-version: '1.82' dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: org.bouncycastle:bcutil-jdk18on dependency-version: '1.82' dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 5c12bb219acf..396d8f8bb1e7 100644 --- a/pom.xml +++ b/pom.xml @@ -45,7 +45,7 @@ 2.0.17 2.9.4 - 1.81 + 1.82 8.0.1 3.1.11 From 9fddc2f23e401e17e63560df7a035fbf042c4f22 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 Oct 2025 02:22:46 +0000 Subject: [PATCH 915/979] Bump log4j.version from 2.25.1 to 2.25.2 Bumps `log4j.version` from 2.25.1 to 2.25.2. Updates `org.apache.logging.log4j:log4j-api` from 2.25.1 to 2.25.2 Updates `org.apache.logging.log4j:log4j-core` from 2.25.1 to 2.25.2 Updates `org.apache.logging.log4j:log4j-slf4j2-impl` from 2.25.1 to 2.25.2 --- updated-dependencies: - dependency-name: org.apache.logging.log4j:log4j-api dependency-version: 2.25.2 dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.apache.logging.log4j:log4j-core dependency-version: 2.25.2 dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.apache.logging.log4j:log4j-slf4j2-impl dependency-version: 2.25.2 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 5c12bb219acf..b953d0689c16 100644 --- a/pom.xml +++ b/pom.xml @@ -39,7 +39,7 @@ 1.1.1 9.4.58.v20250814 - 2.25.1 + 2.25.2 2.0.34 1.19.0 2.0.17 From 2c95468e2714fdb1c2152a4321f2c0de07147ab7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 Oct 2025 02:23:58 +0000 Subject: [PATCH 916/979] Bump com.google.code.gson:gson from 2.13.1 to 2.13.2 Bumps [com.google.code.gson:gson](https://github.com/google/gson) from 2.13.1 to 2.13.2. - [Release notes](https://github.com/google/gson/releases) - [Changelog](https://github.com/google/gson/blob/main/CHANGELOG.md) - [Commits](https://github.com/google/gson/compare/gson-parent-2.13.1...gson-parent-2.13.2) --- updated-dependencies: - dependency-name: com.google.code.gson:gson dependency-version: 2.13.2 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 5c12bb219acf..a39788f03c56 100644 --- a/pom.xml +++ b/pom.xml @@ -1356,7 +1356,7 @@ com.google.code.gson gson - 2.13.1 + 2.13.2 9.4.58.v20250814 2.25.2 - 2.0.34 + 2.0.35 1.19.0 2.0.17 2.9.4 From 245003658907c405e4f9f509dd1d14207c2feb84 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 6 Oct 2025 19:55:41 +0000 Subject: [PATCH 922/979] Bump com.amazonaws:aws-java-sdk-s3 from 1.12.791 to 1.12.792 Bumps [com.amazonaws:aws-java-sdk-s3](https://github.com/aws/aws-sdk-java) from 1.12.791 to 1.12.792. - [Changelog](https://github.com/aws/aws-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java/compare/1.12.791...1.12.792) --- updated-dependencies: - dependency-name: com.amazonaws:aws-java-sdk-s3 dependency-version: 1.12.792 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- dspace-api/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dspace-api/pom.xml b/dspace-api/pom.xml index 1aa596458823..2a14560d4f26 100644 --- a/dspace-api/pom.xml +++ b/dspace-api/pom.xml @@ -717,7 +717,7 @@ com.amazonaws aws-java-sdk-s3 - 1.12.791 + 1.12.792 6.4.10.Final 8.0.3.Final - 42.7.7 + 42.7.8 10.22.0 8.11.4 From f41c929cddfa99b1c93b5b362259ac0360957b47 Mon Sep 17 00:00:00 2001 From: Andreas Czerniak Date: Thu, 9 Oct 2025 16:41:28 +0200 Subject: [PATCH 925/979] =?UTF-8?q?Terminology=20update:=20=E2=80=98OpenAI?= =?UTF-8?q?RE=20Guidelines=20for=20Literature=20Repository=20Managers=20v4?= =?UTF-8?q?=E2=80=99=20=E2=86=92=20=E2=80=98OpenAIRE=20Guidelines=20for=20?= =?UTF-8?q?Institutional=20and=20Thematic=20Repository=20Managers=20v4?= =?UTF-8?q?=E2=80=99.=20Descriptions=20and=20links=20have=20been=20aligned?= =?UTF-8?q?=20with=20the=20current=20standard.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../oai/metadataFormats/oai_openaire.xsl | 70 +++++++++---------- .../crosswalks/oai/transformers/openaire4.xsl | 8 +-- dspace/config/crosswalks/oai/xoai.xml | 8 +-- .../entities/openaire4-relationships.xml | 12 ++-- dspace/config/registries/openaire4-types.xml | 8 +-- dspace/config/submission-forms.xml | 4 +- 6 files changed, 55 insertions(+), 55 deletions(-) diff --git a/dspace/config/crosswalks/oai/metadataFormats/oai_openaire.xsl b/dspace/config/crosswalks/oai/metadataFormats/oai_openaire.xsl index fdf93da5ae31..8042a8cf6fd9 100644 --- a/dspace/config/crosswalks/oai/metadataFormats/oai_openaire.xsl +++ b/dspace/config/crosswalks/oai/metadataFormats/oai_openaire.xsl @@ -101,7 +101,7 @@ - + @@ -137,7 +137,7 @@ - + @@ -206,7 +206,7 @@ - + @@ -406,7 +406,7 @@ - + @@ -489,7 +489,7 @@ - + @@ -599,7 +599,7 @@ - + @@ -618,7 +618,7 @@ - + @@ -640,7 +640,7 @@ - + @@ -670,7 +670,7 @@ - + @@ -704,7 +704,7 @@ - + @@ -724,8 +724,8 @@ - - + + @@ -736,7 +736,7 @@ - + @@ -746,7 +746,7 @@ @@ -779,7 +779,7 @@ - + @@ -791,7 +791,7 @@ - + @@ -802,7 +802,7 @@ - + @@ -827,7 +827,7 @@ - + @@ -840,7 +840,7 @@ - + @@ -856,7 +856,7 @@ - + @@ -875,7 +875,7 @@ - + @@ -945,7 +945,7 @@ - + @@ -954,7 +954,7 @@ - + @@ -963,7 +963,7 @@ - + @@ -972,7 +972,7 @@ - + @@ -981,7 +981,7 @@ - + @@ -990,7 +990,7 @@ - + @@ -999,7 +999,7 @@ - + @@ -1009,7 +1009,7 @@ - + @@ -1071,7 +1071,7 @@ - + @@ -1083,7 +1083,7 @@ - + Available @@ -1425,7 +1425,7 @@ @@ -1478,7 +1478,7 @@ This template will return the COAR Resource Type Vocabulary URI like http://purl.org/coar/resource_type/c_6501 based on a valued text like 'article' - https://openaire-guidelines-for-literature-repository-managers.readthedocs.io/en/v4.0.0/field_publicationtype.html#attribute-uri-m + https://openaire-guidelines-for-literature-repository-managers.readthedocs.io/en/4.0.1/field_publicationtype.html#attribute-uri-m --> @@ -1674,7 +1674,7 @@ like "open access" based on the values from DSpace Access Status mechanism like String 'open.access' please check class org.dspace.access.status.DefaultAccessStatusHelper for more information - https://openaire-guidelines-for-literature-repository-managers.readthedocs.io/en/v4.0.0/field_accessrights.html#definition-and-usage-instruction + https://openaire-guidelines-for-literature-repository-managers.readthedocs.io/en/4.0.1/field_accessrights.html#definition-and-usage-instruction --> @@ -1704,7 +1704,7 @@ This template will return the COAR Access Right Vocabulary URI like http://purl.org/coar/access_right/c_abf2 based on a value text like 'open access' - https://openaire-guidelines-for-literature-repository-managers.readthedocs.io/en/v4.0.0/field_accessrights.html#definition-and-usage-instruction + https://openaire-guidelines-for-literature-repository-managers.readthedocs.io/en/4.0.1/field_accessrights.html#definition-and-usage-instruction --> diff --git a/dspace/config/crosswalks/oai/transformers/openaire4.xsl b/dspace/config/crosswalks/oai/transformers/openaire4.xsl index 8ac703609d6f..7f26b81c344a 100644 --- a/dspace/config/crosswalks/oai/transformers/openaire4.xsl +++ b/dspace/config/crosswalks/oai/transformers/openaire4.xsl @@ -12,8 +12,8 @@ @@ -80,7 +80,7 @@ Normalizing dc.rights according to COAR Controlled Vocabulary for Access Rights (Version 1.0) (http://vocabularies.coar-repositories.org/documentation/access_rights/) available at - https://openaire-guidelines-for-literature-repository-managers.readthedocs.io/en/v4.0.0/field_accessrights.html#definition-and-usage-instruction + https://openaire-guidelines-for-literature-repository-managers.readthedocs.io/en/4.0.1/field_accessrights.html#definition-and-usage-instruction --> @@ -116,7 +116,7 @@ diff --git a/dspace/config/crosswalks/oai/xoai.xml b/dspace/config/crosswalks/oai/xoai.xml index 0f1cdf7d68cd..da51eb5eaf77 100644 --- a/dspace/config/crosswalks/oai/xoai.xml +++ b/dspace/config/crosswalks/oai/xoai.xml @@ -74,7 +74,7 @@ - This contexts complies with Openaire Guidelines for Literature Repositories v4.0. + This contexts complies with OpenAIRE Guidelines for Institutional and Thematic Repository Managers v4.0. oai_openaire diff --git a/dspace/config/entities/openaire4-relationships.xml b/dspace/config/entities/openaire4-relationships.xml index daa0e2c1da9c..d77371b603a4 100644 --- a/dspace/config/entities/openaire4-relationships.xml +++ b/dspace/config/entities/openaire4-relationships.xml @@ -3,7 +3,7 @@ - Publication @@ -17,7 +17,7 @@ 0 - Publication @@ -31,7 +31,7 @@ 0 - Publication @@ -45,7 +45,7 @@ 0 - Publication @@ -59,7 +59,7 @@ 0 - Publication @@ -73,7 +73,7 @@ 0 - Project diff --git a/dspace/config/registries/openaire4-types.xml b/dspace/config/registries/openaire4-types.xml index e47e06e0aebf..17dccb222dc8 100644 --- a/dspace/config/registries/openaire4-types.xml +++ b/dspace/config/registries/openaire4-types.xml @@ -2,13 +2,13 @@ - Openaire4 fields definition + OpenAIRE v4 fields definition @@ -102,4 +102,4 @@ The date when the conference took place. This property is considered to be part of the bibliographic citation. Recommended best practice for encoding the date value is defined in a profile of ISO 8601 [W3CDTF] and follows the YYYY-MM-DD format. - + \ No newline at end of file diff --git a/dspace/config/submission-forms.xml b/dspace/config/submission-forms.xml index c3e47a71b811..0c5d00e4aa62 100644 --- a/dspace/config/submission-forms.xml +++ b/dspace/config/submission-forms.xml @@ -1556,8 +1556,8 @@ - From 7bfe4265c8bf27c600d604272de7961bbf461751 Mon Sep 17 00:00:00 2001 From: Joran De Braekeleer Date: Mon, 8 Sep 2025 12:42:39 +0200 Subject: [PATCH 926/979] 133566: Add group-eperson logging (cherry picked from commit b6fefd46aabeb173694fe2e050e4b39abb131961) --- .../main/java/org/dspace/eperson/GroupServiceImpl.java | 8 ++++++++ dspace/config/dstat.map | 6 +++++- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/dspace-api/src/main/java/org/dspace/eperson/GroupServiceImpl.java b/dspace-api/src/main/java/org/dspace/eperson/GroupServiceImpl.java index 4cec4c9c0d93..44727d3e5fb9 100644 --- a/dspace-api/src/main/java/org/dspace/eperson/GroupServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/eperson/GroupServiceImpl.java @@ -142,6 +142,8 @@ public void addMember(Context context, Group group, EPerson e) { context.addEvent( new Event(Event.ADD, Constants.GROUP, group.getID(), Constants.EPERSON, e.getID(), e.getEmail(), getIdentifiers(context, group))); + log.info(LogHelper.getHeader(context, "add_group_eperson", + "group_id=" + group.getID() + ", eperson_id=" + e.getID())); } @Override @@ -157,6 +159,8 @@ public void addMember(Context context, Group groupParent, Group groupChild) thro context.addEvent(new Event(Event.ADD, Constants.GROUP, groupParent.getID(), Constants.GROUP, groupChild.getID(), groupChild.getName(), getIdentifiers(context, groupParent))); + log.info(LogHelper.getHeader(context, "add_group_subgroup", + "group_id=" + groupParent.getID() + ", subgroup_id=" + groupChild.getID())); } /** @@ -214,6 +218,8 @@ public void removeMember(Context context, Group group, EPerson ePerson) throws S if (group.remove(ePerson)) { context.addEvent(new Event(Event.REMOVE, Constants.GROUP, group.getID(), Constants.EPERSON, ePerson.getID(), ePerson.getEmail(), getIdentifiers(context, group))); + log.info(LogHelper.getHeader(context, "remove_group_eperson", + "group_id=" + group.getID() + ", eperson_id=" + ePerson.getID())); } } @@ -242,6 +248,8 @@ public void removeMember(Context context, Group groupParent, Group childGroup) t context.addEvent( new Event(Event.REMOVE, Constants.GROUP, groupParent.getID(), Constants.GROUP, childGroup.getID(), childGroup.getName(), getIdentifiers(context, groupParent))); + log.info(LogHelper.getHeader(context, "remove_group_subgroup", + "group_id=" + groupParent.getID() + ", subgroup_id=" + childGroup.getID())); } } diff --git a/dspace/config/dstat.map b/dspace/config/dstat.map index 140049ee13a1..bfd08ede3c56 100644 --- a/dspace/config/dstat.map +++ b/dspace/config/dstat.map @@ -100,4 +100,8 @@ show_feedback_form=Feedback Form Displayed create_dc_type=New Dublin Core Type Created remove_template_item=Item Template Removed withdraw_item=Item Withdrawn -download_export_archive = Download Export Archive \ No newline at end of file +download_export_archive = Download Export Archive +add_group_eperson = EPerson Added to Group +remove_group_eperson = EPerson Removed from Group +add_group_subgroup = Child Group Added to Group +remove_group_subgroup = Child Group Removed from Group \ No newline at end of file From d512533f6f1c5e85fc1a16daeb4c9ad786fd0359 Mon Sep 17 00:00:00 2001 From: fribeiro-fccn <65163266+fribeiro-fccn@users.noreply.github.com> Date: Tue, 21 Oct 2025 20:29:08 +0100 Subject: [PATCH 927/979] Fix 10740: Add more efficient Collection.findAuthorized() to improve performance of SWORDv2 servicedocument (#11333) * Changed Authentication for a user in Sword - Added new methods to handle the queries to get collections of the current user only * Changed to handle the community request if get communities is enabled in config * Added javadoc * Correct stylecheck * Corrected comments and changed AND to OR as the action_id should be an OR * Removed trailing space * Changed visibilty to getParentGroups to public and changed the DAO method to use Hibernate and bypass the WITH RECURSIVE sql query and the need the use o NativeQuery * Added Constants.ADMIN * Removed unused code * Refactor of DAO method to use CritriaBuilder * Changed return method * Changed findByEPersonGroupTypeIdAction it returns a List and maxim results was set to 1. Changed to -1 to get the multiple results * Changed the Service to get all collections when a user is admin of a Community * Minor Update to sword authenticator * Added some more tests --- .../dao/impl/ResourcePolicyDAOImpl.java | 2 +- .../dspace/content/CollectionServiceImpl.java | 84 +++++++++ .../org/dspace/content/dao/CollectionDAO.java | 12 ++ .../content/dao/impl/CollectionDAOImpl.java | 100 +++++++++- .../content/service/CollectionService.java | 12 ++ .../main/java/org/dspace/eperson/Group.java | 2 +- .../org/dspace/content/CollectionTest.java | 178 ++++++++++++++++++ .../org/dspace/sword2/SwordAuthenticator.java | 41 ++-- 8 files changed, 409 insertions(+), 22 deletions(-) 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 3b09f9cf300b..1f9e5ea26677 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 @@ -182,7 +182,7 @@ public List findByEPersonGroupTypeIdAction(Context context, EPer compareEpersonOrGroups ) ); - 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/content/CollectionServiceImpl.java b/dspace-api/src/main/java/org/dspace/content/CollectionServiceImpl.java index f5ef4f4b14a4..10f458b81975 100644 --- a/dspace-api/src/main/java/org/dspace/content/CollectionServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/content/CollectionServiceImpl.java @@ -13,13 +13,17 @@ 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; @@ -838,6 +842,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()) + .toList(); + + List communities = uuids.stream() + .map(uuid -> { + try { + return communityService.find(context, uuid); + } catch (SQLException e) { + return null; //ignore that uuid + } + }) + .filter(Objects::nonNull) + .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); 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 6bb65bbb46d8..13bcf5f52c02 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/CollectionDAOImpl.java b/dspace-api/src/main/java/org/dspace/content/dao/impl/CollectionDAOImpl.java index 4530e6eeda40..e47b1ed4a02b 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,12 @@ 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 jakarta.persistence.Query; @@ -164,6 +168,100 @@ 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 = " + @@ -201,4 +299,4 @@ public List> getCollectionsWithBitstreamSizesTotal(C } return returnList; } -} \ No newline at end of file +} diff --git a/dspace-api/src/main/java/org/dspace/content/service/CollectionService.java b/dspace-api/src/main/java/org/dspace/content/service/CollectionService.java index 3a865d9d63fd..c2b633821376 100644 --- a/dspace-api/src/main/java/org/dspace/content/service/CollectionService.java +++ b/dspace-api/src/main/java/org/dspace/content/service/CollectionService.java @@ -327,6 +327,18 @@ public void canEdit(Context context, Collection collection, boolean useInheritan public List findAuthorized(Context context, Community community, int actionID) throws java.sql.SQLException; + /** + * return an array of collections that user has a given permission on + * + * @param context DSpace Context + * @param community (optional) restrict search to a community, else null + * @param actions Listo of the of the action ADD, READ, ADMIN, etc. + * @return Collection [] of collections with matching permissions + * @throws SQLException if database error + */ + public List findAuthorized(Context context, Community community, List actions) + throws java.sql.SQLException; + /** * * @param context DSpace Context diff --git a/dspace-api/src/main/java/org/dspace/eperson/Group.java b/dspace-api/src/main/java/org/dspace/eperson/Group.java index 24b44b8149a4..8146bf702d0a 100644 --- a/dspace-api/src/main/java/org/dspace/eperson/Group.java +++ b/dspace-api/src/main/java/org/dspace/eperson/Group.java @@ -138,7 +138,7 @@ boolean contains(EPerson e) { return getMembers().contains(e); } - List getParentGroups() { + public List getParentGroups() { return parentGroups; } diff --git a/dspace-api/src/test/java/org/dspace/content/CollectionTest.java b/dspace-api/src/test/java/org/dspace/content/CollectionTest.java index a177571ffa46..d943a4a31613 100644 --- a/dspace-api/src/test/java/org/dspace/content/CollectionTest.java +++ b/dspace-api/src/test/java/org/dspace/content/CollectionTest.java @@ -1159,6 +1159,184 @@ public void testFindAuthorizedOptimized() throws Exception { assertFalse("testFindAuthorizeOptimized D.C", personDCollections.contains(collectionC)); } + /** + * Test of findAuthorizedEpersonAndGroups method, of class Collection. + * We create some collections and a user and groups and subgroups and add the user to one subgroup + * and one collection + * The parent group will be added to the other collection. + */ + @Test + public void testFindAuthorizedByEPerson() throws Exception { + context.turnOffAuthorisationSystem(); + Community com = communityService.create(null, context); + Collection collectionA = collectionService.create(context, com); + Collection collectionB = collectionService.create(context, com); + Collection collectionC = collectionService.create(context, com); + + com.addCollection(collectionA); + com.addCollection(collectionB); + com.addCollection(collectionC); + + Group groupParent = groupService.create(context); + Group groupChild = groupService.create(context); + + groupService.addMember(context, groupParent, groupChild); + + EPerson epersonA = ePersonService.create(context); + + //Add epersonA to the child group + groupService.addMember(context, groupChild, epersonA); + + //personA can submit to collectionA and collectionC + authorizeService.addPolicy(context, collectionA, Constants.ADD, epersonA); + authorizeService.addPolicy(context, collectionB, Constants.ADD, groupParent); + + context.restoreAuthSystemState(); + + context.setCurrentUser(epersonA); + List personACollections = + collectionService.findAuthorized(context, null, List.of(Constants.ADD, Constants.ADMIN)); + assertTrue("testFindAuthorizedByEPerson A", personACollections.size() == 2); + assertTrue("testFindAuthorizedByEPerson A.A", personACollections.contains(collectionA)); + assertTrue("testFindAuthorizedByEPerson A.B", personACollections.contains(collectionB)); + assertFalse("testFindAuthorizedByEPerson A.C", personACollections.contains(collectionC)); + } + + /** + * Test of testFindAuthorizedEPersonCommunityAdmin method, of class Collection. + * This will test what collections care retrieved if a user is a Com Administrator + * eperson A is Top of B (and by the caso of B,C and D) but not of E + * eperson E is Top of E nad of D so it can get THE E and D Collections + * + */ + @Test + public void testFindAuthorizedEPersonCommunityAdmin() throws Exception { + context.turnOffAuthorisationSystem(); + Community comA = communityService.create(null, context); + Community comB = communityService.create(null, context); + Community comC = communityService.create(null, context); + Community comD = communityService.create(null, context); + Community comE = communityService.create(null, context); + + Collection collectionA1 = collectionService.create(context, comA); + Collection collectionC1 = collectionService.create(context, comC); + Collection collectionC2 = collectionService.create(context, comC); + Collection collectionD1 = collectionService.create(context, comD); + Collection collectionE1 = collectionService.create(context, comE); + Collection collectionE2 = collectionService.create(context, comE); + + //Create Com hierarchies + comA.addSubCommunity(comB); + comA.addSubCommunity(comC); + comB.addSubCommunity(comD); + + comA.addCollection(collectionA1); + comC.addCollection(collectionC1); + comC.addCollection(collectionC2); + comD.addCollection(collectionD1); + comE.addCollection(collectionE1); + comE.addCollection(collectionE2); + + + Group groupA = groupService.create(context); + Group groupB = groupService.create(context); + Group groupC = groupService.create(context); + Group groupD = groupService.create(context); + Group groupE = groupService.create(context); + + EPerson epersonA = ePersonService.create(context); + EPerson epersonB = ePersonService.create(context); + + //Add epersonA to the child group + groupService.addMember(context, groupA, epersonA); + //Add epersonB to the child group + groupService.addMember(context, groupE, epersonB); + groupService.addMember(context, groupD, epersonB); + + //personA can submit to collectionA and collectionB + authorizeService.addPolicy(context, comA, Constants.ADMIN, groupA); + authorizeService.addPolicy(context, comD, Constants.ADMIN, groupD); + authorizeService.addPolicy(context, comE, Constants.ADMIN, groupE); + + context.restoreAuthSystemState(); + + //PersonA Can get AllCollection From Top to Bottom com ComA, but not from ComE + context.setCurrentUser(epersonA); + List personACollectionsAdminCommA = + collectionService.findAuthorized(context, null, List.of(Constants.ADD, Constants.ADMIN)); + assertTrue("testFindAuthorizedEPersonCommunityAdmin A", personACollectionsAdminCommA.size() == 4); + assertTrue("testFindAuthorizedEPersonCommunityAdmin A.A", personACollectionsAdminCommA + .containsAll(List.of(collectionA1, collectionD1, collectionC1, collectionC2))); + assertFalse("testFindAuthorizedEPersonCommunityAdmin A.B", personACollectionsAdminCommA + .containsAll(List.of(collectionE1, collectionE2))); + + //PersonB Can get AllCollection From Top to Bottom com ComE, but not from ComA + context.setCurrentUser(epersonB); + List personACollectionsAdminCommE = + collectionService.findAuthorized(context, null, List.of(Constants.ADD, Constants.ADMIN)); + assertTrue("testFindAuthorizedEPersonCommunityAdmin B", personACollectionsAdminCommE.size() == 3); + assertFalse("testFindAuthorizedEPersonCommunityAdmin B.A", personACollectionsAdminCommE + .containsAll(List.of(collectionA1, collectionC1, collectionC2))); + assertTrue("testFindAuthorizedEPersonCommunityAdmin B.B", personACollectionsAdminCommE + .containsAll(List.of(collectionD1, collectionE1, collectionE2))); + } + + /** + * Test of testFindNotAuthorizedEPersonDifferentActions method, of class Collection. + * We create some collections and a user and a group add the user as ADMIN by adding ti + * toa group and that group to a Collection and add the user as submitter to another + * we pass actions that shouldn't return collections if only those actions are passed + * And we test if only on collection is retrieved if we pass the Corresponding action + */ + @Test + public void testFindAuthorizedEPersonDifferentActions() throws Exception { + context.turnOffAuthorisationSystem(); + Community com = communityService.create(null, context); + Collection collectionA = collectionService.create(context, com); + Collection collectionB = collectionService.create(context, com); + + com.addCollection(collectionA); + com.addCollection(collectionB); + + Group group = groupService.create(context); + + EPerson epersonA = ePersonService.create(context); + + //Add epersonA to the child group + groupService.addMember(context, group, epersonA); + + //personA can submit to collectionA and collectionB + authorizeService.addPolicy(context, collectionA, Constants.ADD, epersonA); + authorizeService.addPolicy(context, collectionB, Constants.ADMIN, group); + + context.restoreAuthSystemState(); + + //Person does not Have other permission than ADD - So should not return a Colelction if we pass other + //Actions. In this case WRITE OR DELETE + context.setCurrentUser(epersonA); + List personACollectionsRD = + collectionService.findAuthorized(context, null, List.of(Constants.WRITE, Constants.DELETE)); + assertTrue("testFindAuthorizedEPersonDifferentActions A", personACollectionsRD.isEmpty()); + assertFalse("testFindAuthorizedEPersonDifferentActions A.A", personACollectionsRD.contains(collectionA)); + assertFalse("testFindAuthorizedEPersonDifferentActions A.B", personACollectionsRD.contains(collectionB)); + + //But It Should get Collection B if we pass the ADMIN Action too + List personACollectionsADD = + collectionService.findAuthorized(context, null, + List.of(Constants.WRITE, Constants.DELETE, Constants.ADD)); + assertTrue("testFindAuthorizedEPersonDifferentActions B", personACollectionsADD.size() == 1); + assertTrue("testFindAuthorizedEPersonDifferentActions B.A", personACollectionsADD.contains(collectionA)); + assertFalse("testFindAuthorizedEPersonDifferentActions B.B", personACollectionsADD.contains(collectionB)); + + //But It Should get Collection A if we pass the ADD Action too + List personACollections = + collectionService.findAuthorized(context, null, + List.of(Constants.WRITE, Constants.DELETE, Constants.ADMIN)); + assertTrue("testFindAuthorizedEPersonDifferentActions C", personACollections.size() == 1); + assertFalse("testFindAuthorizedEPersonDifferentActions C.A", personACollections.contains(collectionA)); + assertTrue("testFindAuthorizedEPersonDifferentActions C.B", personACollections.contains(collectionB)); + } + /** * Test of countItems method, of class Collection. */ diff --git a/dspace-swordv2/src/main/java/org/dspace/sword2/SwordAuthenticator.java b/dspace-swordv2/src/main/java/org/dspace/sword2/SwordAuthenticator.java index 54b769388c68..e47c0f076b98 100644 --- a/dspace-swordv2/src/main/java/org/dspace/sword2/SwordAuthenticator.java +++ b/dspace-swordv2/src/main/java/org/dspace/sword2/SwordAuthenticator.java @@ -9,6 +9,7 @@ import java.sql.SQLException; import java.util.ArrayList; +import java.util.Arrays; import java.util.Iterator; import java.util.List; @@ -618,31 +619,33 @@ public List getAllowedCollections( // short cut by obtaining the collections to which the authenticated user can submit List cols = collectionService.findAuthorized( - authContext, community, Constants.ADD); + authContext, community, Arrays.asList(Constants.ADD, Constants.ADMIN)); + List allowed = new ArrayList<>(); // now find out if the obo user is allowed to submit to any of these collections - for (Collection col : cols) { - boolean oboAllowed = false; - - // check for obo null - if (swordContext.getOnBehalfOf() == null) { - oboAllowed = true; - } - - // if we have not already determined that the obo user is ok to submit, look up the READ policy on the - // community. THis will include determining if the user is an administrator. - if (!oboAllowed) { - oboAllowed = authorizeService.authorizeActionBoolean( - swordContext.getOnBehalfOfContext(), col, - Constants.ADD); - } + if (swordContext.getOnBehalfOf() != null) { + for (Collection col : cols) { + boolean oboAllowed = false; + + //if we have not already determined that the obo user is ok to submit, + //look up the READ policy on the + // community. THis will include determining if the user is an administrator. + if (!oboAllowed) { + oboAllowed = authorizeService.authorizeActionBoolean( + swordContext.getOnBehalfOfContext(), col, + Constants.ADD); + } - // final check to see if we are allowed to READ - if (oboAllowed) { - allowed.add(col); + // final check to see if we are allowed to READ + if (oboAllowed) { + allowed.add(col); + } } + } else { + return cols; } + return allowed; } catch (SQLException e) { From dc338fcb501762f928eafe35cb8f5ad2f391f905 Mon Sep 17 00:00:00 2001 From: Alexandre Vryghem Date: Thu, 18 Sep 2025 15:23:06 +0200 Subject: [PATCH 928/979] 134319: Escape HTML tags from certain fields before applying hit highlights Co-authored-by: bram.maegerman@atmire.com --- .../org/dspace/discovery/SolrServiceImpl.java | 13 ++++ .../app/rest/DiscoveryRestControllerIT.java | 77 +++++++++++++++++++ dspace/config/modules/discovery.cfg | 8 +- dspace/solr/search/conf/solrconfig.xml | 6 ++ 4 files changed, 103 insertions(+), 1 deletion(-) diff --git a/dspace-api/src/main/java/org/dspace/discovery/SolrServiceImpl.java b/dspace-api/src/main/java/org/dspace/discovery/SolrServiceImpl.java index 0cf2aa50af67..3dafaf241bd1 100644 --- a/dspace-api/src/main/java/org/dspace/discovery/SolrServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/discovery/SolrServiceImpl.java @@ -18,6 +18,7 @@ import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.ArrayList; +import java.util.Arrays; import java.util.Calendar; import java.util.Collections; import java.util.Date; @@ -956,8 +957,20 @@ protected SolrQuery resolveToSolrQuery(Context context, DiscoverQuery discoveryQ if (0 < discoveryQuery.getHitHighlightingFields().size()) { solrQuery.setHighlight(true); solrQuery.add(HighlightParams.USE_PHRASE_HIGHLIGHTER, Boolean.TRUE.toString()); + boolean escapeHTML = configurationService.getBooleanProperty("discovery.highlights.escape-html", true); + String[] renderHTMLForFields = + configurationService.getArrayProperty("discovery.highlights.html-allowed-fields"); for (DiscoverHitHighlightingField highlightingField : discoveryQuery.getHitHighlightingFields()) { solrQuery.addHighlightField(highlightingField.getField() + "_hl"); + boolean allowHTMLInField = Arrays.stream(renderHTMLForFields) + .anyMatch(field -> highlightingField.getField().matches(field)); + if (!escapeHTML || allowHTMLInField) { + solrQuery.add("f." + highlightingField.getField() + "_hl." + HighlightParams.METHOD, "original"); + } else { + solrQuery.add("f." + highlightingField.getField() + "_hl." + HighlightParams.METHOD, "unified"); + solrQuery.add("f." + highlightingField.getField() + "_hl." + HighlightParams.ENCODER, "html"); + } + solrQuery.add("f." + highlightingField.getField() + "_hl." + HighlightParams.FRAGSIZE, String.valueOf(highlightingField.getMaxChars())); solrQuery.add("f." + highlightingField.getField() + "_hl." + HighlightParams.SNIPPETS, diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/DiscoveryRestControllerIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/DiscoveryRestControllerIT.java index a115c8aa2f15..bb29601be8ce 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/DiscoveryRestControllerIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/DiscoveryRestControllerIT.java @@ -85,6 +85,28 @@ public class DiscoveryRestControllerIT extends AbstractControllerIntegrationTest @Autowired ChoiceAuthorityService choiceAuthorityService; + /** + * Original value of the discovery.highlights.escape-html property, saved here to restore it after running the + * tests. + */ + boolean escapeHTML; + + @Override + public void setUp() throws Exception { + super.setUp(); + context.turnOffAuthorisationSystem(); + escapeHTML = configurationService.getBooleanProperty("discovery.highlights.escape-html"); + context.restoreAuthSystemState(); + } + + @Override + public void destroy() throws Exception { + context.turnOffAuthorisationSystem(); + configurationService.setProperty("discovery.highlights.escape-html", escapeHTML); + context.restoreAuthSystemState(); + super.destroy(); + } + @Test public void rootDiscoverTest() throws Exception { @@ -6805,4 +6827,59 @@ public void discoverSearchObjectsSupervisionConfigurationTest() throws Exception .andExpect(jsonPath("$._links.self.href", containsString("/api/discover/search/objects"))); } + @Test + public void discoverSearchObjectsFirstEscapeHTMLTagsBeforeApplyingHitHighlights() throws Exception { + context.turnOffAuthorisationSystem(); + configurationService.setProperty("discovery.highlights.escape-html", true); + parentCommunity = CommunityBuilder.createCommunity(context) + .withName("Parent Community") + .build(); + + Collection col1 = CollectionBuilder.createCollection(context, parentCommunity) + .withName("Collection") + .build(); + + ItemBuilder.createItem(context, col1) + .withTitle("This is a test title") + .build(); + context.restoreAuthSystemState(); + + // This test proves that the HTML tags that are in the original metadata, like test, + // are now escaped and should be returned like <a>test</a> + // Only after this happens should the hit highlights be applied + getClient().perform(get("/api/discover/search/objects") + .param("query", "title")) + .andExpect(status().isOk()) + .andExpect(jsonPath( + "$._embedded.searchResult._embedded.objects[0].hitHighlights['dc.title']", + contains("This is a <a>test</a> title"))); + } + + @Test + public void discoverSearchObjectsDontEscapeHTMLTagsBeforeApplyingHitHighlights() throws Exception { + context.turnOffAuthorisationSystem(); + configurationService.setProperty("discovery.highlights.escape-html", false); + parentCommunity = CommunityBuilder.createCommunity(context) + .withName("Parent Community") + .build(); + + Collection col1 = CollectionBuilder.createCollection(context, parentCommunity) + .withName("Collection") + .build(); + + ItemBuilder.createItem(context, col1) + .withTitle("This is a test title") + .build(); + context.restoreAuthSystemState(); + + // This test proves that the HTML tags that are in the original metadata, like test, + // are not escaped and should be returned like test + // Only after this happens should the hit highlights be applied + getClient().perform(get("/api/discover/search/objects") + .param("query", "title")) + .andExpect(status().isOk()) + .andExpect(jsonPath( + "$._embedded.searchResult._embedded.objects[0].hitHighlights['dc.title']", + contains("This is a test title"))); + } } diff --git a/dspace/config/modules/discovery.cfg b/dspace/config/modules/discovery.cfg index 72088ddc49fa..dcf2cd9e942b 100644 --- a/dspace/config/modules/discovery.cfg +++ b/dspace/config/modules/discovery.cfg @@ -48,4 +48,10 @@ discovery.facet.namedtype.workflow.pooled = 004workflow\n|||\nWaiting for Contro # Set the number of retry of a query when stale objects are found. # Set to -1 if stale objects should be ignored. Set to 0 if you want to avoid extra query but take the chance to cleanup # the index each time that stale objects are found. Default 3 -discovery.removestale.attempts = 3 \ No newline at end of file +discovery.removestale.attempts = 3 + +# Set to true to escape HTML tags in hit highlight results +discovery.highlights.escape-html = true +# Set the fields that should not escape HTML tags in hit highlight results when discovery.highlights.escape-html is true +# It is possible to provide multiple fields by separating them by commas like this: dc.description.abstract, dc.title +# discovery.highlights.html-allowed-fields = diff --git a/dspace/solr/search/conf/solrconfig.xml b/dspace/solr/search/conf/solrconfig.xml index 97b1d1ddbbf6..71c6c8846941 100644 --- a/dspace/solr/search/conf/solrconfig.xml +++ b/dspace/solr/search/conf/solrconfig.xml @@ -148,6 +148,12 @@ + + + + + + false From 5b7b0fb901d2cb1a0f8e075b9c5bf267de4c425b Mon Sep 17 00:00:00 2001 From: Zahraa Chreim Date: Wed, 29 Oct 2025 15:17:01 +0200 Subject: [PATCH 929/979] 135699: Fix deletion of request items referencing deleted bitstreams and add SQL cleanup script with corresponding test (cherry picked from commit 6fb3a700dea12080a114521a4c8568ed599ccfcd) --- .../dspace/content/BitstreamServiceImpl.java | 13 +++++++ ...-request-items-with-deleted-bitstreams.sql | 14 ++++++++ .../app/rest/RequestItemRepositoryIT.java | 36 +++++++++++++++++++ 3 files changed, 63 insertions(+) create mode 100644 dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/postgres/V7.6_2025.10.29__Fix-request-items-with-deleted-bitstreams.sql 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 bd56ad465163..7253761fd902 100644 --- a/dspace-api/src/main/java/org/dspace/content/BitstreamServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/content/BitstreamServiceImpl.java @@ -19,6 +19,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; @@ -63,6 +65,8 @@ public class BitstreamServiceImpl extends DSpaceObjectServiceImpl imp protected BundleService bundleService; @Autowired(required = true) protected BitstreamStorageService bitstreamStorageService; + @Autowired(required = true) + protected RequestItemService requestItemService; protected BitstreamServiceImpl() { super(); @@ -287,6 +291,15 @@ 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 + List requestItems = requestItemService.findAll(context); + for (RequestItem requestItem : requestItems) { + if (requestItem.getBitstream().equals(bitstream)) { + requestItemService.delete(context, requestItem); + } + } + // 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/resources/org/dspace/storage/rdbms/sqlmigration/postgres/V7.6_2025.10.29__Fix-request-items-with-deleted-bitstreams.sql b/dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/postgres/V7.6_2025.10.29__Fix-request-items-with-deleted-bitstreams.sql new file mode 100644 index 000000000000..4f0c54c975c6 --- /dev/null +++ b/dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/postgres/V7.6_2025.10.29__Fix-request-items-with-deleted-bitstreams.sql @@ -0,0 +1,14 @@ +-- +-- 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/ +-- + +DELETE +FROM requestitem +WHERE bitstream_id IN + (SELECT bs.uuid + FROM bitstream AS bs + WHERE bs.deleted IS TRUE) diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/RequestItemRepositoryIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/RequestItemRepositoryIT.java index 56409d18d738..3a7c805cacc6 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/RequestItemRepositoryIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/RequestItemRepositoryIT.java @@ -13,6 +13,7 @@ import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.is; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete; @@ -57,6 +58,7 @@ import org.dspace.content.Bitstream; import org.dspace.content.Collection; import org.dspace.content.Item; +import org.dspace.content.factory.ContentServiceFactory; import org.dspace.services.ConfigurationService; import org.hamcrest.Matchers; import org.junit.Before; @@ -639,4 +641,38 @@ public void testGetLinkTokenEmailWithoutSubPath() throws MalformedURLException, assertEquals(expectedUrl, generatedLink); configurationService.reloadConfig(); } + + /** + * Test that deleting a bitstream also removes any {@link RequestItem} entities associated with it. + */ + @Test + public void testDeleteBitstreamRemovesRequestItem() throws Exception { + // Fake up a request in REST form. + RequestItemRest rir = new RequestItemRest(); + rir.setAllfiles(false); + rir.setItemId(item.getID().toString()); + rir.setBitstreamId(bitstream.getID().toString()); + rir.setRequestEmail(eperson.getEmail()); + rir.setRequestName(eperson.getFullName()); + rir.setRequestMessage(RequestItemBuilder.REQ_MESSAGE); + + // Create it and see if it was created correctly. + ObjectMapper mapper = new ObjectMapper(); + String authToken = getAuthToken(eperson.getEmail(), password); + + getClient(authToken) + .perform(post(URI_ROOT) + .content(mapper.writeValueAsBytes(rir)) + .contentType(contentType)) + .andExpect(status().isCreated()) + // verify the body is empty + .andExpect(jsonPath("$").doesNotExist()); + + // Delete associated Bitstream + ContentServiceFactory.getInstance().getBitstreamService().delete(context, bitstream); + + // Verify that all RequestItems related to this bitstream have been removed + Iterator itemRequests = requestItemService.findByItem(context, item); + assertFalse(itemRequests.hasNext()); + } } From 28acaf7b4a2197259aa69e27b5f39d07ab1cce12 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 1 Nov 2025 02:16:21 +0000 Subject: [PATCH 930/979] Bump org.xmlunit:xmlunit-core in the test-tools group Bumps the test-tools group with 1 update: [org.xmlunit:xmlunit-core](https://github.com/xmlunit/xmlunit). Updates `org.xmlunit:xmlunit-core` from 2.10.4 to 2.11.0 - [Release notes](https://github.com/xmlunit/xmlunit/releases) - [Changelog](https://github.com/xmlunit/xmlunit/blob/main/RELEASE_NOTES.md) - [Commits](https://github.com/xmlunit/xmlunit/compare/v2.10.4...v2.11.0) --- updated-dependencies: - dependency-name: org.xmlunit:xmlunit-core dependency-version: 2.11.0 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: test-tools ... Signed-off-by: dependabot[bot] --- dspace-api/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dspace-api/pom.xml b/dspace-api/pom.xml index 2a14560d4f26..ac9d9a756897 100644 --- a/dspace-api/pom.xml +++ b/dspace-api/pom.xml @@ -771,7 +771,7 @@ org.xmlunit xmlunit-core - 2.10.4 + 2.11.0 test From 2a61e367ed53350b4b10b32ebea3fc3ca64e1d96 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 1 Nov 2025 02:19:54 +0000 Subject: [PATCH 931/979] Bump the spring group with 25 updates Bumps the spring group with 25 updates: | Package | From | To | | --- | --- | --- | | [org.springframework:spring-orm](https://github.com/spring-projects/spring-framework) | `6.2.11` | `6.2.12` | | [org.springframework:spring-core](https://github.com/spring-projects/spring-framework) | `6.2.11` | `6.2.12` | | [org.springframework:spring-beans](https://github.com/spring-projects/spring-framework) | `6.2.11` | `6.2.12` | | [org.springframework:spring-aop](https://github.com/spring-projects/spring-framework) | `6.2.11` | `6.2.12` | | [org.springframework:spring-context](https://github.com/spring-projects/spring-framework) | `6.2.11` | `6.2.12` | | [org.springframework:spring-context-support](https://github.com/spring-projects/spring-framework) | `6.2.11` | `6.2.12` | | [org.springframework:spring-tx](https://github.com/spring-projects/spring-framework) | `6.2.11` | `6.2.12` | | [org.springframework:spring-jdbc](https://github.com/spring-projects/spring-framework) | `6.2.11` | `6.2.12` | | [org.springframework:spring-web](https://github.com/spring-projects/spring-framework) | `6.2.11` | `6.2.12` | | [org.springframework:spring-webmvc](https://github.com/spring-projects/spring-framework) | `6.2.11` | `6.2.12` | | [org.springframework:spring-expression](https://github.com/spring-projects/spring-framework) | `6.2.11` | `6.2.12` | | [org.springframework:spring-test](https://github.com/spring-projects/spring-framework) | `6.2.11` | `6.2.12` | | [org.springframework.boot:spring-boot-starter-test](https://github.com/spring-projects/spring-boot) | `3.5.6` | `3.5.7` | | [org.springframework.boot:spring-boot-starter-tomcat](https://github.com/spring-projects/spring-boot) | `3.5.6` | `3.5.7` | | [org.springframework.boot:spring-boot-maven-plugin](https://github.com/spring-projects/spring-boot) | `3.5.6` | `3.5.7` | | [org.springframework.boot:spring-boot-starter-cache](https://github.com/spring-projects/spring-boot) | `3.5.6` | `3.5.7` | | [org.springframework.boot:spring-boot-starter](https://github.com/spring-projects/spring-boot) | `3.5.6` | `3.5.7` | | [org.springframework.boot:spring-boot-starter-thymeleaf](https://github.com/spring-projects/spring-boot) | `3.5.6` | `3.5.7` | | [org.springframework.boot:spring-boot-starter-web](https://github.com/spring-projects/spring-boot) | `3.5.6` | `3.5.7` | | [org.springframework.boot:spring-boot-starter-data-rest](https://github.com/spring-projects/spring-boot) | `3.5.6` | `3.5.7` | | [org.springframework.boot:spring-boot-starter-security](https://github.com/spring-projects/spring-boot) | `3.5.6` | `3.5.7` | | [org.springframework.boot:spring-boot-starter-aop](https://github.com/spring-projects/spring-boot) | `3.5.6` | `3.5.7` | | [org.springframework.boot:spring-boot-starter-actuator](https://github.com/spring-projects/spring-boot) | `3.5.6` | `3.5.7` | | [org.springframework.boot:spring-boot-starter-log4j2](https://github.com/spring-projects/spring-boot) | `3.5.6` | `3.5.7` | | [org.springframework.security:spring-security-test](https://github.com/spring-projects/spring-security) | `6.5.5` | `6.5.6` | Updates `org.springframework:spring-orm` from 6.2.11 to 6.2.12 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.11...v6.2.12) Updates `org.springframework:spring-core` from 6.2.11 to 6.2.12 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.11...v6.2.12) Updates `org.springframework:spring-beans` from 6.2.11 to 6.2.12 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.11...v6.2.12) Updates `org.springframework:spring-aop` from 6.2.11 to 6.2.12 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.11...v6.2.12) Updates `org.springframework:spring-context` from 6.2.11 to 6.2.12 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.11...v6.2.12) Updates `org.springframework:spring-context-support` from 6.2.11 to 6.2.12 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.11...v6.2.12) Updates `org.springframework:spring-tx` from 6.2.11 to 6.2.12 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.11...v6.2.12) Updates `org.springframework:spring-jdbc` from 6.2.11 to 6.2.12 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.11...v6.2.12) Updates `org.springframework:spring-web` from 6.2.11 to 6.2.12 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.11...v6.2.12) Updates `org.springframework:spring-webmvc` from 6.2.11 to 6.2.12 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.11...v6.2.12) Updates `org.springframework:spring-expression` from 6.2.11 to 6.2.12 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.11...v6.2.12) Updates `org.springframework:spring-test` from 6.2.11 to 6.2.12 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.11...v6.2.12) Updates `org.springframework:spring-core` from 6.2.11 to 6.2.12 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.11...v6.2.12) Updates `org.springframework:spring-beans` from 6.2.11 to 6.2.12 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.11...v6.2.12) Updates `org.springframework:spring-aop` from 6.2.11 to 6.2.12 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.11...v6.2.12) Updates `org.springframework:spring-context` from 6.2.11 to 6.2.12 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.11...v6.2.12) Updates `org.springframework:spring-context-support` from 6.2.11 to 6.2.12 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.11...v6.2.12) Updates `org.springframework:spring-tx` from 6.2.11 to 6.2.12 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.11...v6.2.12) Updates `org.springframework:spring-jdbc` from 6.2.11 to 6.2.12 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.11...v6.2.12) Updates `org.springframework:spring-web` from 6.2.11 to 6.2.12 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.11...v6.2.12) Updates `org.springframework:spring-webmvc` from 6.2.11 to 6.2.12 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.11...v6.2.12) Updates `org.springframework:spring-expression` from 6.2.11 to 6.2.12 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.11...v6.2.12) Updates `org.springframework:spring-test` from 6.2.11 to 6.2.12 - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.11...v6.2.12) Updates `org.springframework.boot:spring-boot-starter-test` from 3.5.6 to 3.5.7 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.5.6...v3.5.7) Updates `org.springframework.boot:spring-boot-starter-tomcat` from 3.5.6 to 3.5.7 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.5.6...v3.5.7) Updates `org.springframework.boot:spring-boot-maven-plugin` from 3.5.6 to 3.5.7 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.5.6...v3.5.7) Updates `org.springframework.boot:spring-boot-starter-cache` from 3.5.6 to 3.5.7 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.5.6...v3.5.7) Updates `org.springframework.boot:spring-boot-starter` from 3.5.6 to 3.5.7 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.5.6...v3.5.7) Updates `org.springframework.boot:spring-boot-starter-thymeleaf` from 3.5.6 to 3.5.7 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.5.6...v3.5.7) Updates `org.springframework.boot:spring-boot-starter-web` from 3.5.6 to 3.5.7 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.5.6...v3.5.7) Updates `org.springframework.boot:spring-boot-starter-data-rest` from 3.5.6 to 3.5.7 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.5.6...v3.5.7) Updates `org.springframework.boot:spring-boot-starter-security` from 3.5.6 to 3.5.7 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.5.6...v3.5.7) Updates `org.springframework.boot:spring-boot-starter-aop` from 3.5.6 to 3.5.7 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.5.6...v3.5.7) Updates `org.springframework.boot:spring-boot-starter-actuator` from 3.5.6 to 3.5.7 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.5.6...v3.5.7) Updates `org.springframework.boot:spring-boot-starter-log4j2` from 3.5.6 to 3.5.7 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.5.6...v3.5.7) Updates `org.springframework.boot:spring-boot-starter-tomcat` from 3.5.6 to 3.5.7 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.5.6...v3.5.7) Updates `org.springframework.security:spring-security-test` from 6.5.5 to 6.5.6 - [Release notes](https://github.com/spring-projects/spring-security/releases) - [Changelog](https://github.com/spring-projects/spring-security/blob/main/RELEASE.adoc) - [Commits](https://github.com/spring-projects/spring-security/compare/6.5.5...6.5.6) Updates `org.springframework.boot:spring-boot-maven-plugin` from 3.5.6 to 3.5.7 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.5.6...v3.5.7) Updates `org.springframework.boot:spring-boot-starter-cache` from 3.5.6 to 3.5.7 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.5.6...v3.5.7) Updates `org.springframework.boot:spring-boot-starter` from 3.5.6 to 3.5.7 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.5.6...v3.5.7) Updates `org.springframework.boot:spring-boot-starter-thymeleaf` from 3.5.6 to 3.5.7 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.5.6...v3.5.7) Updates `org.springframework.boot:spring-boot-starter-web` from 3.5.6 to 3.5.7 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.5.6...v3.5.7) Updates `org.springframework.boot:spring-boot-starter-data-rest` from 3.5.6 to 3.5.7 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.5.6...v3.5.7) Updates `org.springframework.boot:spring-boot-starter-security` from 3.5.6 to 3.5.7 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.5.6...v3.5.7) Updates `org.springframework.boot:spring-boot-starter-aop` from 3.5.6 to 3.5.7 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.5.6...v3.5.7) Updates `org.springframework.boot:spring-boot-starter-actuator` from 3.5.6 to 3.5.7 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.5.6...v3.5.7) Updates `org.springframework.boot:spring-boot-starter-log4j2` from 3.5.6 to 3.5.7 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v3.5.6...v3.5.7) --- updated-dependencies: - dependency-name: org.springframework:spring-orm dependency-version: 6.2.12 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-core dependency-version: 6.2.12 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-beans dependency-version: 6.2.12 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-aop dependency-version: 6.2.12 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-context dependency-version: 6.2.12 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-context-support dependency-version: 6.2.12 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-tx dependency-version: 6.2.12 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-jdbc dependency-version: 6.2.12 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-web dependency-version: 6.2.12 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-webmvc dependency-version: 6.2.12 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-expression dependency-version: 6.2.12 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-test dependency-version: 6.2.12 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-core dependency-version: 6.2.12 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-beans dependency-version: 6.2.12 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-aop dependency-version: 6.2.12 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-context dependency-version: 6.2.12 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-context-support dependency-version: 6.2.12 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-tx dependency-version: 6.2.12 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-jdbc dependency-version: 6.2.12 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-web dependency-version: 6.2.12 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-webmvc dependency-version: 6.2.12 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-expression dependency-version: 6.2.12 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework:spring-test dependency-version: 6.2.12 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter-test dependency-version: 3.5.7 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter-tomcat dependency-version: 3.5.7 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-maven-plugin dependency-version: 3.5.7 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter-cache dependency-version: 3.5.7 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter dependency-version: 3.5.7 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter-thymeleaf dependency-version: 3.5.7 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter-web dependency-version: 3.5.7 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter-data-rest dependency-version: 3.5.7 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter-security dependency-version: 3.5.7 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter-aop dependency-version: 3.5.7 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter-actuator dependency-version: 3.5.7 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter-log4j2 dependency-version: 3.5.7 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter-tomcat dependency-version: 3.5.7 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.security:spring-security-test dependency-version: 6.5.6 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-maven-plugin dependency-version: 3.5.7 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter-cache dependency-version: 3.5.7 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter dependency-version: 3.5.7 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter-thymeleaf dependency-version: 3.5.7 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter-web dependency-version: 3.5.7 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter-data-rest dependency-version: 3.5.7 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter-security dependency-version: 3.5.7 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter-aop dependency-version: 3.5.7 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter-actuator dependency-version: 3.5.7 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring - dependency-name: org.springframework.boot:spring-boot-starter-log4j2 dependency-version: 3.5.7 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: spring ... Signed-off-by: dependabot[bot] --- pom.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pom.xml b/pom.xml index 5e2e649504a2..dfde3b377910 100644 --- a/pom.xml +++ b/pom.xml @@ -19,9 +19,9 @@ 17 - 6.2.11 - 3.5.6 - 6.5.5 + 6.2.12 + 3.5.7 + 6.5.6 6.4.10.Final 8.0.3.Final 42.7.8 From 3b032e8d828b354784486076b4319a574c3cbd73 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 1 Nov 2025 02:20:26 +0000 Subject: [PATCH 932/979] Bump com.healthmarketscience.jackcess:jackcess from 4.0.8 to 4.0.10 Bumps [com.healthmarketscience.jackcess:jackcess](https://github.com/jahlborn/jackcess) from 4.0.8 to 4.0.10. - [Commits](https://github.com/jahlborn/jackcess/compare/jackcess-4.0.8...jackcess-4.0.10) --- updated-dependencies: - dependency-name: com.healthmarketscience.jackcess:jackcess dependency-version: 4.0.10 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 5e2e649504a2..418e7a7c14f7 100644 --- a/pom.xml +++ b/pom.xml @@ -1314,7 +1314,7 @@ com.healthmarketscience.jackcess jackcess - 4.0.8 + 4.0.10 From ca52eda42df02bcf44dcdd13ede56d52b9b3c352 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 1 Nov 2025 02:20:31 +0000 Subject: [PATCH 933/979] Bump org.apache.bcel:bcel from 6.10.0 to 6.11.0 Bumps [org.apache.bcel:bcel](https://github.com/apache/commons-bcel) from 6.10.0 to 6.11.0. - [Changelog](https://github.com/apache/commons-bcel/blob/master/RELEASE-NOTES.txt) - [Commits](https://github.com/apache/commons-bcel/compare/rel/commons-bcel-6.10.0...rel/commons-bcel-6.11.0) --- updated-dependencies: - dependency-name: org.apache.bcel:bcel dependency-version: 6.11.0 dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- dspace-api/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dspace-api/pom.xml b/dspace-api/pom.xml index 2a14560d4f26..592d0c29d58e 100644 --- a/dspace-api/pom.xml +++ b/dspace-api/pom.xml @@ -778,7 +778,7 @@ org.apache.bcel bcel - 6.10.0 + 6.11.0 test From d8d524045ec46f7f1bf27267fc7a41289f56843d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 1 Nov 2025 02:20:41 +0000 Subject: [PATCH 934/979] Bump json-path.version from 2.9.0 to 2.10.0 Bumps `json-path.version` from 2.9.0 to 2.10.0. Updates `com.jayway.jsonpath:json-path` from 2.9.0 to 2.10.0 - [Release notes](https://github.com/jayway/JsonPath/releases) - [Changelog](https://github.com/json-path/JsonPath/blob/master/changelog.md) - [Commits](https://github.com/jayway/JsonPath/compare/json-path-2.9.0...json-path-2.10.0) Updates `com.jayway.jsonpath:json-path-assert` from 2.9.0 to 2.10.0 - [Release notes](https://github.com/jayway/JsonPath/releases) - [Changelog](https://github.com/json-path/JsonPath/blob/master/changelog.md) - [Commits](https://github.com/jayway/JsonPath/compare/json-path-2.9.0...json-path-2.10.0) --- updated-dependencies: - dependency-name: com.jayway.jsonpath:json-path dependency-version: 2.10.0 dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: com.jayway.jsonpath:json-path-assert dependency-version: 2.10.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 5e2e649504a2..4135eeb52e4b 100644 --- a/pom.xml +++ b/pom.xml @@ -51,7 +51,7 @@ 3.1.11 - 2.9.0 + 2.10.0 9.48 From e69e9d5856ddd4dd1703a1ae9b4639a6df12c059 Mon Sep 17 00:00:00 2001 From: Alan Orth Date: Tue, 4 Nov 2025 10:16:26 +0300 Subject: [PATCH 935/979] pom.xml: adjust Jackson dependencies For some reason jackson-annotations no longer uses the patch version and jackson-databind now does. See: https://github.com/FasterXML/jackson/wiki/Jackson-Release-2.20 --- pom.xml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/pom.xml b/pom.xml index 5e2e649504a2..278f9ccf8f31 100644 --- a/pom.xml +++ b/pom.xml @@ -30,9 +30,9 @@ 3.11.1 2.42.0 - - 2.19.2 - 2.19.2 + + 2.20.1 + 2.20 2.1.1 4.0.4 4.0.6 @@ -1731,12 +1731,12 @@ com.fasterxml classmate - 1.7.0 + 1.7.1 com.fasterxml.jackson.core jackson-annotations - ${jackson.version} + ${jackson-annotations.version} com.fasterxml.jackson.core @@ -1746,12 +1746,12 @@ com.fasterxml.jackson.core jackson-databind - ${jackson-databind.version} + ${jackson.version} com.fasterxml.jackson.datatype jackson-datatype-jsr310 - ${jackson-databind.version} + ${jackson.version} com.google.guava From 9fee735faf9c8451b9cff68fad3e56160bf7c6d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Paulo=20Gra=C3=A7a?= Date: Tue, 21 Oct 2025 14:52:56 +0100 Subject: [PATCH 936/979] fix issue 10530 - latestForDiscovery (cherry picked from commit 95998638f9945aa3469cc2a8c4172363b9333c34) --- dspace/config/crosswalks/oai/metadataFormats/oai_openaire.xsl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dspace/config/crosswalks/oai/metadataFormats/oai_openaire.xsl b/dspace/config/crosswalks/oai/metadataFormats/oai_openaire.xsl index 8042a8cf6fd9..e9699d8e0160 100644 --- a/dspace/config/crosswalks/oai/metadataFormats/oai_openaire.xsl +++ b/dspace/config/crosswalks/oai/metadataFormats/oai_openaire.xsl @@ -5,7 +5,7 @@ detailed in the LICENSE and NOTICE files at the root of the source tree and available online at - Developed by Paulo Graça + Developed by paulo-graca > https://www.openaire.eu/schema/repo-lit/4.0/openaire.xsd @@ -303,7 +303,7 @@ - From 18ffbf1ffd34d37294afaa600ab2e6ea56c92dfd Mon Sep 17 00:00:00 2001 From: Toni Prieto Date: Mon, 10 Nov 2025 19:53:31 +0100 Subject: [PATCH 937/979] Fix missing assignment of Solr client in QAEventServiceImpl (cherry picked from commit 75a8cb7c7cafb6aa6f9e63ea3e47020f38b7b3ba) --- .../org/dspace/qaevent/service/impl/QAEventServiceImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dspace-api/src/main/java/org/dspace/qaevent/service/impl/QAEventServiceImpl.java b/dspace-api/src/main/java/org/dspace/qaevent/service/impl/QAEventServiceImpl.java index 98077a1c0c76..171cc4c31159 100644 --- a/dspace-api/src/main/java/org/dspace/qaevent/service/impl/QAEventServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/qaevent/service/impl/QAEventServiceImpl.java @@ -131,7 +131,7 @@ protected SolrClient getSolr() { if (solr == null) { String solrService = DSpaceServicesFactory.getInstance().getConfigurationService() .getProperty("qaevents.solr.server", "http://localhost:8983/solr/qaevent"); - return new HttpSolrClient.Builder(solrService).build(); + solr = new HttpSolrClient.Builder(solrService).build(); } return solr; } From 78e91bb7fc627181528a9a7b0ff9059d71754e33 Mon Sep 17 00:00:00 2001 From: im-shivamb Date: Thu, 18 Sep 2025 19:19:35 +0530 Subject: [PATCH 938/979] Fix for bitstreams with old style embargo lift date in metadata not rendering on simple item pages. (cherry picked from commit eeba9846217001e592c2f1a234ee83854485014b) --- .../src/main/java/org/dspace/embargo/DefaultEmbargoSetter.java | 1 - 1 file changed, 1 deletion(-) diff --git a/dspace-api/src/main/java/org/dspace/embargo/DefaultEmbargoSetter.java b/dspace-api/src/main/java/org/dspace/embargo/DefaultEmbargoSetter.java index 7857a45eb8d5..265ec213da60 100644 --- a/dspace-api/src/main/java/org/dspace/embargo/DefaultEmbargoSetter.java +++ b/dspace-api/src/main/java/org/dspace/embargo/DefaultEmbargoSetter.java @@ -94,7 +94,6 @@ public void setEmbargo(Context context, Item item) if (!(bnn.equals(Constants.LICENSE_BUNDLE_NAME) || bnn.equals(Constants.METADATA_BUNDLE_NAME) || bnn .equals(CreativeCommonsServiceImpl.CC_BUNDLE_NAME))) { //AuthorizeManager.removePoliciesActionFilter(context, bn, Constants.READ); - generatePolicies(context, liftDate.toDate(), null, bn, item.getOwningCollection()); for (Bitstream bs : bn.getBitstreams()) { //AuthorizeManager.removePoliciesActionFilter(context, bs, Constants.READ); generatePolicies(context, liftDate.toDate(), null, bs, item.getOwningCollection()); From dfbdf851eeadf0b06cfe8368ec21572229c1db56 Mon Sep 17 00:00:00 2001 From: Mark Patton Date: Tue, 11 Nov 2025 14:11:16 -0500 Subject: [PATCH 939/979] Port AWS SDK v2 update of S3 Bitstore Service to 8x --- dspace-api/pom.xml | 43 +- .../storage/bitstore/S3BitStoreService.java | 440 +++++++----------- .../storage/bitstore/S3BitStoreServiceIT.java | 113 ++--- dspace-server-webapp/pom.xml | 11 +- dspace/config/modules/assetstore.cfg | 17 + dspace/config/spring/api/bitstore.xml | 19 + 6 files changed, 298 insertions(+), 345 deletions(-) diff --git a/dspace-api/pom.xml b/dspace-api/pom.xml index 2a14560d4f26..483f1fc7efca 100644 --- a/dspace-api/pom.xml +++ b/dspace-api/pom.xml @@ -639,6 +639,7 @@ 1.1.1 + com.google.guava guava @@ -715,9 +716,25 @@ - com.amazonaws - aws-java-sdk-s3 - 1.12.792 + software.amazon.awssdk + s3 + 2.32.31 + + + software.amazon.awssdk + netty-nio-client + + + software.amazon.awssdk + apache-client + + + + + + software.amazon.awssdk.crt + aws-crt + 0.38.12 + + + + org.dspace + dspace-iiif + + org.dspace dspace-api @@ -500,10 +507,6 @@ - - org.dspace - dspace-iiif - org.dspace dspace-oai diff --git a/dspace/config/modules/assetstore.cfg b/dspace/config/modules/assetstore.cfg index 5e625e0c8a1a..74989aad0e5a 100644 --- a/dspace/config/modules/assetstore.cfg +++ b/dspace/config/modules/assetstore.cfg @@ -47,6 +47,9 @@ assetstore.s3.bucketName = # is shared. Optional, default is root level of bucket assetstore.s3.subfolder = +# Optional custom S3 endpoint URI. Leave this blank / commented to use the Amazon default +# assetstore.s3.endpoint = + # please don't use root credentials in production but rely on the aws credentials default # discovery mechanism to configure them (ENV VAR, EC2 Iam role, etc.) # The preferred approach for security reason is to use the IAM user credentials, but isn't always possible. @@ -58,3 +61,17 @@ assetstore.s3.awsSecretKey = # If the credentials are left empty, # then this setting is ignored and the default AWS region will be used. assetstore.s3.awsRegionName = + +# The target throughput for transfer requests in Gbps. Higher value means more connections will be established with S3. +assetstore.s3.targetThroughputGbps = 10.0 + +# Sets the minimum part size for transfer parts. Decreasing the minimum part size causes multipart transfer to be split +# into a larger number of smaller parts. +assetstore.s3.minPartSizeBytes = 8388608 + +# Specifies the maximum number of S3 connections that should be established during a transfer. +# If not provided, it will be based on targetThroughputGbps +assetstore.s3.maxConcurrency = + +# The algorithm the S3 client will use to create a checksum when doing putObject. +assetstore.s3.s3ChecksumAlgorithm = CRC32 diff --git a/dspace/config/spring/api/bitstore.xml b/dspace/config/spring/api/bitstore.xml index 15bb3ef1580b..af50a4c16fc3 100644 --- a/dspace/config/spring/api/bitstore.xml +++ b/dspace/config/spring/api/bitstore.xml @@ -27,6 +27,9 @@ + + + @@ -34,6 +37,22 @@ + + + + + + + + + + + + From b84a8a620f19c271b1d97291ca3d5a47d93959a0 Mon Sep 17 00:00:00 2001 From: igorbaptist4 Date: Wed, 29 Oct 2025 17:10:49 -0300 Subject: [PATCH 940/979] fix: #8443 - Remove the step 'complete' and fix the processing-class for step 'collection' (cherry picked from commit 677c7b5af6d070bd95f667a4cac24ed1f8b28644) --- dspace/config/migration/item-submissions.xsl | 61 +++++++++++--------- 1 file changed, 35 insertions(+), 26 deletions(-) diff --git a/dspace/config/migration/item-submissions.xsl b/dspace/config/migration/item-submissions.xsl index 9b1de738e1fd..aed95992ab17 100644 --- a/dspace/config/migration/item-submissions.xsl +++ b/dspace/config/migration/item-submissions.xsl @@ -34,35 +34,44 @@ configuration file into a DSpace 7.x (or above) item-submission.xml --> - - - - - - - - - - - + + + + + + + + + + + + org.dspace.app.rest.submit.step.CollectionStep + + + + + + + + + + + + + submission-form + + + - - + + submission + + + submission - - submission-form - - - - - submission - - - submission - - - + + From 7af27de2055f7bf3c96a2f3e5f0da1d995d972b6 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Wed, 12 Nov 2025 16:54:32 -0600 Subject: [PATCH 941/979] Switch to installing regctl manually instead of using a plugin (cherry picked from commit 8954575405fd9b77d719313addfe1bf997356147) --- .github/workflows/reusable-docker-build.yml | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/.github/workflows/reusable-docker-build.yml b/.github/workflows/reusable-docker-build.yml index a3729c27f6ee..de2831f8ff54 100644 --- a/.github/workflows/reusable-docker-build.yml +++ b/.github/workflows/reusable-docker-build.yml @@ -78,6 +78,8 @@ env: # Registry used during building of Docker images. (All images are later copied to docker.io registry) # We use GitHub's Container Registry to avoid aggressive rate limits at DockerHub. DOCKER_BUILD_REGISTRY: ghcr.io + # Version of 'regctl' to use for copying images to DOCKER_BUILD_REGISTRY + REGCTL_VERSION: 'v0.9.2' jobs: docker-build: @@ -298,9 +300,9 @@ jobs: # 'regctl' is used to more easily copy the image to DockerHub and obtain the digest from DockerHub # See https://github.com/regclient/regclient/blob/main/docs/regctl.md - name: Install regctl for Docker registry tools - uses: regclient/actions/regctl-installer@main - with: - release: 'v0.8.0' + run: | + curl -L https://github.com/regclient/regclient/releases/download/${REGCTL_VERSION}/regctl-linux-amd64 > /usr/bin/regctl + chmod 755 /usr/bin/regctl # This recreates Docker tags for DockerHub - name: Add Docker metadata for image From 12d56d7ef3f170f86aedabb2122f423691a8e6f2 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Wed, 12 Nov 2025 17:08:03 -0600 Subject: [PATCH 942/979] Fix bug in 11541 : Ensure regctl is added to GitHub path rather than installing in a directory that is access restricted --- .github/workflows/reusable-docker-build.yml | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/.github/workflows/reusable-docker-build.yml b/.github/workflows/reusable-docker-build.yml index de2831f8ff54..5e011c4668ed 100644 --- a/.github/workflows/reusable-docker-build.yml +++ b/.github/workflows/reusable-docker-build.yml @@ -78,8 +78,6 @@ env: # Registry used during building of Docker images. (All images are later copied to docker.io registry) # We use GitHub's Container Registry to avoid aggressive rate limits at DockerHub. DOCKER_BUILD_REGISTRY: ghcr.io - # Version of 'regctl' to use for copying images to DOCKER_BUILD_REGISTRY - REGCTL_VERSION: 'v0.9.2' jobs: docker-build: @@ -301,8 +299,11 @@ jobs: # See https://github.com/regclient/regclient/blob/main/docs/regctl.md - name: Install regctl for Docker registry tools run: | - curl -L https://github.com/regclient/regclient/releases/download/${REGCTL_VERSION}/regctl-linux-amd64 > /usr/bin/regctl - chmod 755 /usr/bin/regctl + export REGCTL_VERSION=v0.9.2 + mkdir -p bin + curl -sSLo bin/regctl https://github.com/regclient/regclient/releases/download/${REGCTL_VERSION}/regctl-linux-amd64 + chmod a+x bin/regctl + echo "$(pwd)/bin" >> $GITHUB_PATH # This recreates Docker tags for DockerHub - name: Add Docker metadata for image From 5b2ef7e01d87dab91c226cce8ad004333c0d2bd7 Mon Sep 17 00:00:00 2001 From: Marsa Haoua Date: Thu, 13 Nov 2025 18:42:52 +0100 Subject: [PATCH 943/979] Fix 11547: SWORDv2 - read, update and delete operations (cherry picked from commit 9514ded1cd813f442a95df429487c2bfbbd9a4cf) --- .../src/main/java/org/dspace/sword2/SwordUrlManager.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dspace-swordv2/src/main/java/org/dspace/sword2/SwordUrlManager.java b/dspace-swordv2/src/main/java/org/dspace/sword2/SwordUrlManager.java index eee3627c4045..1d49bc27977c 100644 --- a/dspace-swordv2/src/main/java/org/dspace/sword2/SwordUrlManager.java +++ b/dspace-swordv2/src/main/java/org/dspace/sword2/SwordUrlManager.java @@ -107,7 +107,7 @@ public String getSwordBaseUrl() "Unable to construct service document urls, due to missing/invalid " + "config in sword2.url and/or dspace.server.url"); } - sUrl = buildSWORDUrl("swordv2"); + sUrl = dspaceUrl + "/" + swordPath; } return sUrl; } From 6b0085f0b15eefb8fa0314d2ee33ca530cf8042d Mon Sep 17 00:00:00 2001 From: Vincenzo Mecca Date: Fri, 24 Oct 2025 17:24:50 +0200 Subject: [PATCH 944/979] rename(DuplicateDetectionTest): Renames DuplicateDetectionTest to DuplicateDetectionIT fix(DuplicateDetectionTest): Deactivates security policies while creating DSpaceObjects ref: UXP-307 (cherry picked from commit 2967a2ff13e5e7f1619de93d6bbe81b7a90d38d5) --- ...ionTest.java => DuplicateDetectionIT.java} | 20 +++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) rename dspace-api/src/test/java/org/dspace/content/{DuplicateDetectionTest.java => DuplicateDetectionIT.java} (97%) diff --git a/dspace-api/src/test/java/org/dspace/content/DuplicateDetectionTest.java b/dspace-api/src/test/java/org/dspace/content/DuplicateDetectionIT.java similarity index 97% rename from dspace-api/src/test/java/org/dspace/content/DuplicateDetectionTest.java rename to dspace-api/src/test/java/org/dspace/content/DuplicateDetectionIT.java index 0b6c909f03e8..4bc25fb731ac 100644 --- a/dspace-api/src/test/java/org/dspace/content/DuplicateDetectionTest.java +++ b/dspace-api/src/test/java/org/dspace/content/DuplicateDetectionIT.java @@ -31,7 +31,6 @@ import org.dspace.services.ConfigurationService; import org.dspace.services.factory.DSpaceServicesFactory; import org.dspace.xmlworkflow.storedcomponents.XmlWorkflowItem; -import org.junit.Before; import org.junit.Test; /** @@ -40,7 +39,7 @@ * * @author Kim Shepherd */ -public class DuplicateDetectionTest extends AbstractIntegrationTestWithDatabase { +public class DuplicateDetectionIT extends AbstractIntegrationTestWithDatabase { private DuplicateDetectionService duplicateDetectionService = ContentServiceFactory.getInstance() .getDuplicateDetectionService(); private ConfigurationService configurationService = DSpaceServicesFactory.getInstance().getConfigurationService(); @@ -56,7 +55,6 @@ public class DuplicateDetectionTest extends AbstractIntegrationTestWithDatabase private static final Logger log = LogManager.getLogger(); - @Before public void setUp() throws Exception { super.setUp(); // Temporarily enable duplicate detection and set comparison distance to 1 @@ -100,7 +98,7 @@ public void setUp() throws Exception { .withAuthor("Smith, Donald Y.") .withSubject("ExtraEntry 3") .build(); - + context.restoreAuthSystemState(); } @@ -210,7 +208,7 @@ public void testSearchDuplicates() throws Exception { public void testSearchDuplicatesWithReservedSolrCharacters() throws Exception { - + context.turnOffAuthorisationSystem(); Item item4 = ItemBuilder.createItem(context, col) .withTitle("Testing: An Important Development Step") .withIssueDate(item1IssueDate) @@ -223,6 +221,7 @@ public void testSearchDuplicatesWithReservedSolrCharacters() throws Exception { .withAuthor("Smith, Donald X.") .withSubject("ExtraEntry 2") .build(); + context.restoreAuthSystemState(); // Get potential duplicates of item 4 and make sure no exceptions are thrown List potentialDuplicates = new ArrayList<>(); @@ -254,6 +253,7 @@ public void testSearchDuplicatesWithReservedSolrCharacters() throws Exception { @Test public void testSearchDuplicatesWithVeryLongTitle() throws Exception { + context.turnOffAuthorisationSystem(); Item item6 = ItemBuilder.createItem(context, col) .withTitle("Testing: This title is over 200 characters long and should behave just the same as a " + "shorter title, with or without reserved characters. This integration test will prove that " + @@ -272,6 +272,8 @@ public void testSearchDuplicatesWithVeryLongTitle() throws Exception { .withSubject("ExtraEntry 2") .build(); + context.restoreAuthSystemState(); + // Get potential duplicates of item 4 and make sure no exceptions are thrown List potentialDuplicates = new ArrayList<>(); try { @@ -303,6 +305,8 @@ public void testSearchDuplicatesExactMatch() throws Exception { // Set distance to 0 manually configurationService.setProperty("duplicate.comparison.distance", 0); + context.turnOffAuthorisationSystem(); + Item item8 = ItemBuilder.createItem(context, col) .withTitle("This integration test will prove that the edit distance of 0 results in an exact match") .withIssueDate(item1IssueDate) @@ -323,7 +327,7 @@ public void testSearchDuplicatesExactMatch() throws Exception { .withAuthor("Smith, Donald X.") .withSubject("ExtraEntry") .build(); - + context.restoreAuthSystemState(); // Get potential duplicates of item 4 and make sure no exceptions are thrown List potentialDuplicates = new ArrayList<>(); try { @@ -387,6 +391,8 @@ public void testSearchDuplicatesWithMultipleFields() throws Exception { configurationService.setProperty("duplicate.comparison.metadata.field", new String[]{"dc.title", "dc.contributor.author"}); + context.turnOffAuthorisationSystem(); + Item item10 = ItemBuilder.createItem(context, col) .withTitle("Compare both title and author") .withIssueDate(item1IssueDate) @@ -407,6 +413,8 @@ public void testSearchDuplicatesWithMultipleFields() throws Exception { .withSubject("ExtraEntry 2") .build(); + context.restoreAuthSystemState(); + // Get potential duplicates of item 10 and make sure no exceptions are thrown List potentialDuplicates = new ArrayList<>(); try { From e39f6cddc4d888d608b54fbe5b0d2924d6ff8d3a Mon Sep 17 00:00:00 2001 From: Vincenzo Mecca Date: Thu, 6 Nov 2025 09:19:43 +0100 Subject: [PATCH 945/979] fix(DuplicateDetectionIT): Adds the Override annotation on the setup method. ref: DURACOM-408 (cherry picked from commit bb634cf19eab82b84a47532ce301669743003761) --- .../test/java/org/dspace/content/DuplicateDetectionIT.java | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/dspace-api/src/test/java/org/dspace/content/DuplicateDetectionIT.java b/dspace-api/src/test/java/org/dspace/content/DuplicateDetectionIT.java index 4bc25fb731ac..424233b47898 100644 --- a/dspace-api/src/test/java/org/dspace/content/DuplicateDetectionIT.java +++ b/dspace-api/src/test/java/org/dspace/content/DuplicateDetectionIT.java @@ -17,8 +17,6 @@ import java.util.List; import java.util.Optional; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; import org.dspace.AbstractIntegrationTestWithDatabase; import org.dspace.builder.CollectionBuilder; import org.dspace.builder.CommunityBuilder; @@ -53,8 +51,7 @@ public class DuplicateDetectionIT extends AbstractIntegrationTestWithDatabase { private final String item1Title = "Public item I"; private final String item1Author = "Smith, Donald"; - private static final Logger log = LogManager.getLogger(); - + @Override public void setUp() throws Exception { super.setUp(); // Temporarily enable duplicate detection and set comparison distance to 1 From 93405e0946e198a8114aad0dfb323699022f0af3 Mon Sep 17 00:00:00 2001 From: Elios Buzo Date: Wed, 28 May 2025 10:34:01 +0200 Subject: [PATCH 946/979] [DURACOM-360] Removed the initial article filter (cherry picked from commit a322c5093334343b9ff89efd2f4073dff0b45dd6) --- .../org/dspace/sort/OrderFormatTitle.java | 3 +- .../dspace/sort/OrderFormatTitleMarc21.java | 3 +- .../text/filter/InitialArticleWord.java | 172 --------- .../text/filter/MARC21InitialArticleWord.java | 329 ------------------ .../filter/StandardInitialArticleWord.java | 30 -- 5 files changed, 2 insertions(+), 535 deletions(-) delete mode 100644 dspace-api/src/main/java/org/dspace/text/filter/InitialArticleWord.java delete mode 100644 dspace-api/src/main/java/org/dspace/text/filter/MARC21InitialArticleWord.java delete mode 100644 dspace-api/src/main/java/org/dspace/text/filter/StandardInitialArticleWord.java diff --git a/dspace-api/src/main/java/org/dspace/sort/OrderFormatTitle.java b/dspace-api/src/main/java/org/dspace/sort/OrderFormatTitle.java index b745f0719cb7..f6f9aaa38e50 100644 --- a/dspace-api/src/main/java/org/dspace/sort/OrderFormatTitle.java +++ b/dspace-api/src/main/java/org/dspace/sort/OrderFormatTitle.java @@ -9,7 +9,6 @@ import org.dspace.text.filter.DecomposeDiactritics; import org.dspace.text.filter.LowerCaseAndTrim; -import org.dspace.text.filter.StandardInitialArticleWord; import org.dspace.text.filter.StripDiacritics; import org.dspace.text.filter.TextFilter; @@ -20,7 +19,7 @@ */ public class OrderFormatTitle extends AbstractTextFilterOFD { { - filters = new TextFilter[] {new StandardInitialArticleWord(), + filters = new TextFilter[] { new DecomposeDiactritics(), new StripDiacritics(), new LowerCaseAndTrim()}; diff --git a/dspace-api/src/main/java/org/dspace/sort/OrderFormatTitleMarc21.java b/dspace-api/src/main/java/org/dspace/sort/OrderFormatTitleMarc21.java index fa9ba297258a..9148ca2a988a 100644 --- a/dspace-api/src/main/java/org/dspace/sort/OrderFormatTitleMarc21.java +++ b/dspace-api/src/main/java/org/dspace/sort/OrderFormatTitleMarc21.java @@ -9,7 +9,6 @@ import org.dspace.text.filter.DecomposeDiactritics; import org.dspace.text.filter.LowerCaseAndTrim; -import org.dspace.text.filter.MARC21InitialArticleWord; import org.dspace.text.filter.StripDiacritics; import org.dspace.text.filter.StripLeadingNonAlphaNum; import org.dspace.text.filter.TextFilter; @@ -21,7 +20,7 @@ */ public class OrderFormatTitleMarc21 extends AbstractTextFilterOFD { { - filters = new TextFilter[] {new MARC21InitialArticleWord(), + filters = new TextFilter[] { new DecomposeDiactritics(), new StripDiacritics(), new StripLeadingNonAlphaNum(), diff --git a/dspace-api/src/main/java/org/dspace/text/filter/InitialArticleWord.java b/dspace-api/src/main/java/org/dspace/text/filter/InitialArticleWord.java deleted file mode 100644 index 167b201e0f7a..000000000000 --- a/dspace-api/src/main/java/org/dspace/text/filter/InitialArticleWord.java +++ /dev/null @@ -1,172 +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.text.filter; - -/** - * Abstract class for implementing initial article word filters - * Allows you to create new classes with their own rules for mapping - * languages to article word lists. - * - * @author Graham Triggs - */ -public abstract class InitialArticleWord implements TextFilter { - /** - * When no language is passed, use null and let implementation decide what to do - */ - @Override - public String filter(String str) { - return filter(str, null); - } - - /** - * Do an initial definite/indefinite article filter on the passed string. - * On matching an initial word, can strip or move to the end, depending on the - * configuration of the implementing class. - * - * @param str The string to parse - * @param lang The language of the passed string - * @return String The filtered string - */ - @Override - public String filter(String str, String lang) { - // Get the list of article words for this language - String[] articleWordArr = getArticleWords(lang); - - // If we have an article word array, process the string - if (articleWordArr != null && articleWordArr.length > 0) { - String initialArticleWord = null; - int curPos = 0; - int initialStart = -1; - int initialEnd = -1; - - // Iterate through the characters until we find something significant, or hit the end - while (initialEnd < 0 && curPos < str.length()) { - // Have we found a significant character - if (Character.isLetterOrDigit(str.charAt(curPos))) { - // Mark this as the cut point for the initial word - initialStart = curPos; - - // Loop through the article words looking for a match - for (int idx = 0; initialEnd < 0 && idx < articleWordArr.length; idx++) { - // Extract a fragment from the string to test - // Must be same length as the article word - if (idx > 1 && initialArticleWord != null) { - // Only need to do so if we haven't already got one - // of the right length - if (initialArticleWord.length() != articleWordArr[idx].length()) { - initialArticleWord = extractText(str, curPos, articleWordArr[idx].length()); - } - } else { - initialArticleWord = extractText(str, curPos, articleWordArr[idx].length()); - } - - // Does the fragment match an article word? - if (initialArticleWord != null && initialArticleWord.equalsIgnoreCase(articleWordArr[idx])) { - // Check to see if the next character in the source - // is a whitespace - boolean isNextWhitespace = Character.isWhitespace( - str.charAt(curPos + articleWordArr[idx].length()) - ); - - // Check to see if the last character of the article word is a letter or digit - boolean endsLetterOrDigit = Character - .isLetterOrDigit(initialArticleWord.charAt(initialArticleWord.length() - 1)); - - // If the last character of the article word is a letter or digit, - // then it must be followed by whitespace, if not, it can be anything - // Setting endPos signifies that we have found an article word - if (endsLetterOrDigit && isNextWhitespace) { - initialEnd = curPos + initialArticleWord.length(); - } else if (!endsLetterOrDigit) { - initialEnd = curPos + initialArticleWord.length(); - } - } - } - - // Quit the loop, as we have a significant character - break; - } - - // Keep going - curPos++; - } - - // If endPos is positive, then we've found an article word - if (initialEnd > 0) { - // Find a cut point in the source string, removing any whitespace after the article word - int cutPos = initialEnd; - while (cutPos < str.length() && Character.isWhitespace(str.charAt(cutPos))) { - cutPos++; - } - - // Are we stripping the article word? - if (stripInitialArticle) { - // Yes, simply return everything after the cut - return str.substring(cutPos); - } else { - // No - move the initial article word to the end - return new StringBuilder(str.substring(cutPos)) - .append(wordSeparator) - .append(str.substring(initialStart, initialEnd)) - .toString(); - } - } - } - - // Didn't do any processing, or didn't find an initial article word - // Return the original string - return str; - } - - protected InitialArticleWord(boolean stripWord) { - this.wordSeparator = ", "; - stripInitialArticle = stripWord; - } - - protected InitialArticleWord() { - this.wordSeparator = ", "; - stripInitialArticle = false; - } - - /** - * Abstract method to get the list of words to use in the initial word filter - * - * @param lang The language to retrieve article words for - * @return An array of definite/indefinite article words - */ - protected abstract String[] getArticleWords(String lang); - // Separator to use when appending article to end - private final String wordSeparator; - - // Flag to signify initial article word should be removed - // If false, then the initial article word is appended to the end - private boolean stripInitialArticle = false; - - /** - * Helper method to extract text from a string. - * Ensures that there is significant data (ie. non-whitespace) - * after the segment requested. - * - * @param str - * @param pos - * @param len - * @return - */ - private String extractText(String str, int pos, int len) { - int testPos = pos + len; - while (testPos < str.length() && Character.isWhitespace(str.charAt(testPos))) { - testPos++; - } - - if (testPos < str.length()) { - return str.substring(pos, pos + len); - } - - return null; - } -} diff --git a/dspace-api/src/main/java/org/dspace/text/filter/MARC21InitialArticleWord.java b/dspace-api/src/main/java/org/dspace/text/filter/MARC21InitialArticleWord.java deleted file mode 100644 index c82b9ccfcf83..000000000000 --- a/dspace-api/src/main/java/org/dspace/text/filter/MARC21InitialArticleWord.java +++ /dev/null @@ -1,329 +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.text.filter; - -import java.io.Serializable; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Comparator; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import org.apache.commons.lang3.ArrayUtils; -import org.apache.commons.lang3.StringUtils; -import org.dspace.services.factory.DSpaceServicesFactory; - -/** - * Implements MARC 21 standards to disregard initial - * definite or indefinite article in sorting. - * - * Note: This only works for languages defined with IANA code entries. - * - * @author Graham Triggs - */ -public class MARC21InitialArticleWord extends InitialArticleWord { - public MARC21InitialArticleWord() { - // Default behaviour is to strip the initial word completely - super(true); - } - - public MARC21InitialArticleWord(boolean stripWord) { - super(stripWord); - } - - /** - * Return the list of definite and indefinite article codes - * for this language. - */ - @Override - protected String[] getArticleWords(String lang) { - // No language - no words - if (StringUtils.isEmpty(lang)) { - return defaultWords; - } - - Language l = Language.getLanguage(lang); - - // Is the language in our map? - if (l != null && ianaArticleMap.containsKey(l.IANA)) { - // Get the list of words for this language - ArticlesForLang articles = ianaArticleMap.get(l.IANA); - - if (articles != null) { - return articles.words; - } - } - - return null; - } - - // Mapping of IANA codes to article word lists - private static Map ianaArticleMap = new HashMap(); - - private static String[] defaultWords = null; - - // Static initialisation - convert word -> languages map - // into language -> words map - static { - /* Define a mapping for article words to the languages that have them. - * Take from: http://www.loc.gov/marc/bibliographic/bdapp-e.html - */ - Object[][] articleWordArray = { - {"a", Language.ENGLISH, Language.GALICIAN, Language.HUNGARIAN, Language.PORTUGUESE, Language.ROMANIAN, - Language.SCOTS, Language.YIDDISH}, - {"a'", Language.SCOTTISH_GAELIC}, - {"al", Language.ROMANIAN}, - {"al-", Language.ARABIC, Language.BALUCHI, Language.BRAHUI, Language.PANJABI, Language.PERSIAN, - Language.TURKISH, Language.URDU}, - {"am", Language.SCOTTISH_GAELIC}, - {"an", Language.ENGLISH, Language.IRISH, Language.SCOTS, Language.SCOTTISH_GAELIC, Language.YIDDISH}, - {"an t-", Language.IRISH, Language.SCOTTISH_GAELIC}, - {"ane", Language.SCOTS}, - {"ang", Language.TAGALOG}, - {"ang mga", Language.TAGALOG}, - {"as", Language.GALICIAN, Language.PORTUGUESE}, - {"az", Language.HUNGARIAN}, - {"bat", Language.BASQUE}, - {"bir", Language.TURKISH}, - {"d'", Language.ENGLISH}, - {"da", Language.SHETLAND_ENGLISH}, - {"das", Language.GERMAN}, - {"de", Language.DANISH, Language.DUTCH, Language.ENGLISH, Language.FRISIAN, Language.NORWEGIAN, - Language.SWEDISH}, - {"dei", Language.NORWEGIAN}, - {"dem", Language.GERMAN}, - {"den", Language.DANISH, Language.GERMAN, Language.NORWEGIAN, Language.SWEDISH}, - {"der", Language.GERMAN, Language.YIDDISH}, - {"des", Language.GERMAN, Language.WALLOON}, - {"det", Language.DANISH, Language.NORWEGIAN, Language.SWEDISH}, - {"di", Language.YIDDISH}, - {"die", Language.AFRIKAANS, Language.GERMAN, Language.YIDDISH}, - {"dos", Language.YIDDISH}, - {"e", Language.NORWEGIAN}, - {"e", Language.FRISIAN}, // should be 'e - leading apostrophes are ignored - {"een", Language.DUTCH}, - {"eene", Language.DUTCH}, - {"egy", Language.HUNGARIAN}, - {"ei", Language.NORWEGIAN}, - {"ein", Language.GERMAN, Language.NORWEGIAN, Language.WALLOON}, - {"eine", Language.GERMAN}, - {"einem", Language.GERMAN}, - {"einen", Language.GERMAN}, - {"einer", Language.GERMAN}, - {"eines", Language.GERMAN}, - {"eit", Language.NORWEGIAN}, - {"el", Language.CATALAN, Language.SPANISH}, - {"el-", Language.ARABIC}, - {"els", Language.CATALAN}, - {"en", Language.CATALAN, Language.DANISH, Language.NORWEGIAN, Language.SWEDISH}, - {"enne", Language.WALLOON}, - {"et", Language.DANISH, Language.NORWEGIAN}, - {"ett", Language.SWEDISH}, - {"eyn", Language.YIDDISH}, - {"eyne", Language.YIDDISH}, - {"gl'", Language.ITALIAN}, - {"gli", Language.PROVENCAL}, - {"ha-", Language.HEBREW}, - {"hai", Language.CLASSICAL_GREEK, Language.GREEK}, - {"he", Language.HAWAIIAN}, - {"h\u0113", Language.CLASSICAL_GREEK, Language.GREEK}, // e macron - {"he-", Language.HEBREW}, - {"heis", Language.GREEK}, - {"hen", Language.GREEK}, - {"hena", Language.GREEK}, - {"henas", Language.GREEK}, - {"het", Language.DUTCH}, - {"hin", Language.ICELANDIC}, - {"hina", Language.ICELANDIC}, - {"hinar", Language.ICELANDIC}, - {"hinir", Language.ICELANDIC}, - {"hinn", Language.ICELANDIC}, - {"hinna", Language.ICELANDIC}, - {"hinnar", Language.ICELANDIC}, - {"hinni", Language.ICELANDIC}, - {"hins", Language.ICELANDIC}, - {"hinu", Language.ICELANDIC}, - {"hinum", Language.ICELANDIC}, - {"hi\u01d2", Language.ICELANDIC}, - {"ho", Language.CLASSICAL_GREEK, Language.GREEK}, - {"hoi", Language.CLASSICAL_GREEK, Language.GREEK}, - {"i", Language.ITALIAN}, - {"ih'", Language.PROVENCAL}, - {"il", Language.ITALIAN, Language.PROVENCAL_OCCITAN}, - {"il-", Language.MALTESE}, - {"in", Language.FRISIAN}, - {"it", Language.FRISIAN}, - {"ka", Language.HAWAIIAN}, - {"ke", Language.HAWAIIAN}, - {"l'", Language.CATALAN, Language.FRENCH, Language.ITALIAN, Language.PROVENCAL_OCCITAN, Language.WALLOON}, - {"l-", Language.MALTESE}, - {"la", Language.CATALAN, Language.ESPERANTO, Language.FRENCH, Language.ITALIAN, Language.PROVENCAL_OCCITAN, - Language.SPANISH}, - {"las", Language.PROVENCAL_OCCITAN, Language.SPANISH}, - {"le", Language.FRENCH, Language.ITALIAN, Language.PROVENCAL_OCCITAN}, - {"les", Language.CATALAN, Language.FRENCH, Language.PROVENCAL_OCCITAN, Language.WALLOON}, - {"lh", Language.PROVENCAL_OCCITAN}, - {"lhi", Language.PROVENCAL_OCCITAN}, - {"li", Language.PROVENCAL_OCCITAN}, - {"lis", Language.PROVENCAL_OCCITAN}, - {"lo", Language.ITALIAN, Language.PROVENCAL_OCCITAN, Language.SPANISH}, - {"los", Language.PROVENCAL_OCCITAN, Language.SPANISH}, - {"lou", Language.PROVENCAL_OCCITAN}, - {"lu", Language.PROVENCAL_OCCITAN}, - {"mga", Language.TAGALOG}, - {"m\u0303ga", Language.TAGALOG}, - {"mia", Language.GREEK}, - {"n", Language.AFRIKAANS, Language.DUTCH, Language.FRISIAN}, // should be 'n - leading - // apostrophes are ignored - {"na", Language.HAWAIIAN, Language.IRISH, Language.SCOTTISH_GAELIC}, - {"na h-", Language.IRISH, Language.SCOTTISH_GAELIC}, - {"nje", Language.ALBANIAN}, - {"ny", Language.MALAGASY}, - {"o", Language.NEAPOLITAN_ITALIAN}, // should be 'o - leading apostrophes are ignored - {"o", Language.GALICIAN, Language.HAWAIIAN, Language.PORTUGUESE, Language.ROMANIAN}, - {"os", Language.PORTUGUESE}, - {"r", Language.ICELANDIC}, // should be 'r - leading apostrophes are ignored - {"s", Language.GERMAN}, // should be 's - leading apostrophes are ignored - {"sa", Language.TAGALOG}, - {"sa mga", Language.TAGALOG}, - {"si", Language.TAGALOG}, - {"sin\u00e1", Language.TAGALOG}, - {"t", Language.DUTCH, Language.FRISIAN}, // should be 't - leading apostrophes are ignored - {"ta", Language.CLASSICAL_GREEK, Language.GREEK}, - {"tais", Language.CLASSICAL_GREEK}, - {"tas", Language.CLASSICAL_GREEK}, - {"t\u0113", Language.CLASSICAL_GREEK}, // e macron - {"t\u0113n", Language.CLASSICAL_GREEK, Language.GREEK}, // e macron - {"t\u0113s", Language.CLASSICAL_GREEK, Language.GREEK}, // e macron - {"the", Language.ENGLISH}, - {"t\u014d", Language.CLASSICAL_GREEK, Language.GREEK}, // o macron - {"tois", Language.CLASSICAL_GREEK}, - {"t\u014dn", Language.CLASSICAL_GREEK, Language.GREEK}, // o macron - {"tou", Language.CLASSICAL_GREEK, Language.GREEK}, - {"um", Language.PORTUGUESE}, - {"uma", Language.PORTUGUESE}, - {"un", Language.CATALAN, Language.FRENCH, Language.ITALIAN, Language.PROVENCAL_OCCITAN, Language.ROMANIAN, - Language.SPANISH}, - {"un'", Language.ITALIAN}, - {"una", Language.CATALAN, Language.ITALIAN, Language.PROVENCAL_OCCITAN, Language.SPANISH}, - {"une", Language.FRENCH}, - {"unei", Language.ROMANIAN}, - {"unha", Language.GALICIAN}, - {"uno", Language.ITALIAN, Language.PROVENCAL_OCCITAN}, - {"uns", Language.PROVENCAL_OCCITAN}, - {"unui", Language.ROMANIAN}, - {"us", Language.PROVENCAL_OCCITAN}, - {"y", Language.WELSH}, - {"ye", Language.ENGLISH}, - {"yr", Language.WELSH} - }; - - // Initialize the lang -> article map - ianaArticleMap = new HashMap(); - - int wordIdx = 0; - int langIdx = 0; - - // Iterate through word/language array - // Generate temporary language map - Map> langWordMap = new HashMap>(); - for (wordIdx = 0; wordIdx < articleWordArray.length; wordIdx++) { - for (langIdx = 1; langIdx < articleWordArray[wordIdx].length; langIdx++) { - Language lang = (Language) articleWordArray[wordIdx][langIdx]; - - if (lang != null && lang.IANA.length() > 0) { - List words = langWordMap.get(lang); - - if (words == null) { - words = new ArrayList(); - langWordMap.put(lang, words); - } - - // Add language to list if we haven't done so already - if (!words.contains(articleWordArray[wordIdx][0])) { - words.add((String) articleWordArray[wordIdx][0]); - } - } - } - } - - // Iterate through languages - for (Map.Entry> langToWord : langWordMap.entrySet()) { - Language lang = langToWord.getKey(); - List wordList = langToWord.getValue(); - - // Convert the list into an array of strings - String[] words = new String[wordList.size()]; - - for (int idx = 0; idx < wordList.size(); idx++) { - words[idx] = wordList.get(idx); - } - - // Sort the array into length order - longest to shortest - // This ensures maximal matching on the article words - Arrays.sort(words, new MARC21InitialArticleWord.InverseLengthComparator()); - - // Add language/article entry to map - ianaArticleMap.put(lang.IANA, new MARC21InitialArticleWord.ArticlesForLang(lang, words)); - } - - // Setup default stop words for null languages - String[] defaultLangs = DSpaceServicesFactory.getInstance().getConfigurationService() - .getArrayProperty("marc21wordfilter.defaultlang"); - if (ArrayUtils.isNotEmpty(defaultLangs)) { - int wordCount = 0; - ArticlesForLang[] afl = new ArticlesForLang[defaultLangs.length]; - - for (int idx = 0; idx < afl.length; idx++) { - Language l = Language.getLanguage(defaultLangs[idx]); - if (l != null && ianaArticleMap.containsKey(l.IANA)) { - afl[idx] = ianaArticleMap.get(l.IANA); - if (afl[idx] != null) { - wordCount += afl[idx].words.length; - } - } - } - - if (wordCount > 0) { - int destPos = 0; - defaultWords = new String[wordCount]; - for (int idx = 0; idx < afl.length; idx++) { - if (afl[idx] != null) { - System.arraycopy(afl[idx].words, 0, defaultWords, destPos, afl[idx].words.length); - destPos += afl[idx].words.length; - } - } - } - } - } - - // Wrapper class for inserting word arrays into a map - private static class ArticlesForLang { - final Language lang; - final String[] words; - - ArticlesForLang(Language lang, String[] words) { - this.lang = lang; - this.words = (String[]) ArrayUtils.clone(words); - } - } - - // Compare strings according to their length - longest to shortest - private static class InverseLengthComparator implements Comparator, Serializable { - @Override - public int compare(Object arg0, Object arg1) { - return ((String) arg1).length() - ((String) arg0).length(); - } - - ; - - } - - ; -} diff --git a/dspace-api/src/main/java/org/dspace/text/filter/StandardInitialArticleWord.java b/dspace-api/src/main/java/org/dspace/text/filter/StandardInitialArticleWord.java deleted file mode 100644 index ade72b150f5c..000000000000 --- a/dspace-api/src/main/java/org/dspace/text/filter/StandardInitialArticleWord.java +++ /dev/null @@ -1,30 +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.text.filter; - -/** - * Implements existing DSpace initial article word behaviour - * - * Note: This only works for languages defined with ISO code entries. - * - * @author Graham Triggs - */ -public class StandardInitialArticleWord extends InitialArticleWord { - private static final String[] articleWords = {"the", "an", "a"}; - - @Override - protected String[] getArticleWords(String lang) { - if (lang != null && lang.startsWith("en")) { - return articleWords; - } - - return null; - } - -} - From 4c2142076c83ec1a17326a176b1f7cd86d5ae1d3 Mon Sep 17 00:00:00 2001 From: Elios Buzo Date: Wed, 28 May 2025 11:46:19 +0200 Subject: [PATCH 947/979] [DURACOM-360] Removed unused class (cherry picked from commit 57ef735a51ff30673fce6ca04a7674f45755ed7d) --- .../java/org/dspace/text/filter/Language.java | 142 ------------------ 1 file changed, 142 deletions(-) delete mode 100644 dspace-api/src/main/java/org/dspace/text/filter/Language.java diff --git a/dspace-api/src/main/java/org/dspace/text/filter/Language.java b/dspace-api/src/main/java/org/dspace/text/filter/Language.java deleted file mode 100644 index 9be68d2ddfb9..000000000000 --- a/dspace-api/src/main/java/org/dspace/text/filter/Language.java +++ /dev/null @@ -1,142 +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.text.filter; - -import java.util.HashMap; -import java.util.Map; - -/** - * Define languages - both as IANA and ISO639-2 codes - * - * @author Graham Triggs - */ -public class Language { - public final String IANA; - public final String ISO639_1; - public final String ISO639_2; - - public static final Language AFRIKAANS = Language.create("af", "af", "afr"); - public static final Language ALBANIAN = Language.create("sq", "sq", "alb"); - public static final Language ARABIC = Language.create("ar", "ar", "ara"); - public static final Language BALUCHI = Language.create("bal", "", "bal"); - public static final Language BASQUE = Language.create("eu", "", "baq"); - public static final Language BRAHUI = Language.create("", "", ""); - public static final Language CATALAN = Language.create("ca", "ca", "cat"); - public static final Language CLASSICAL_GREEK = Language.create("grc", "", "grc"); - public static final Language DANISH = Language.create("da", "da", "dan"); - public static final Language DUTCH = Language.create("nl", "ni", "dut"); - public static final Language ENGLISH = Language.create("en", "en", "eng"); - public static final Language ESPERANTO = Language.create("eo", "eo", "epo"); - public static final Language FRENCH = Language.create("fr", "fr", "fre"); - public static final Language FRISIAN = Language.create("fy", "fy", "fri"); - public static final Language GALICIAN = Language.create("gl", "gl", "glg"); - public static final Language GERMAN = Language.create("de", "de", "ger"); - public static final Language GREEK = Language.create("el", "el", "gre"); - public static final Language HAWAIIAN = Language.create("haw", "", "haw"); - public static final Language HEBREW = Language.create("he", "he", "heb"); - public static final Language HUNGARIAN = Language.create("hu", "hu", "hun"); - public static final Language ICELANDIC = Language.create("is", "is", "ice"); - public static final Language IRISH = Language.create("ga", "ga", "gle"); - public static final Language ITALIAN = Language.create("it", "it", "ita"); - public static final Language MALAGASY = Language.create("mg", "mg", "mlg"); - public static final Language MALTESE = Language.create("mt", "mt", "mlt"); - public static final Language NEAPOLITAN_ITALIAN = Language.create("nap", "", "nap"); - public static final Language NORWEGIAN = Language.create("no", "no", "nor"); - public static final Language PORTUGUESE = Language.create("pt", "pt", "por"); - public static final Language PANJABI = Language.create("pa", "pa", "pan"); - public static final Language PERSIAN = Language.create("fa", "fa", "per"); - public static final Language PROVENCAL = Language.create("pro", "", "pro"); - public static final Language PROVENCAL_OCCITAN = Language.create("oc", "oc", "oci"); - public static final Language ROMANIAN = Language.create("ro", "ro", "rum"); - public static final Language SCOTS = Language.create("sco", "", "sco"); - public static final Language SCOTTISH_GAELIC = Language.create("gd", "gd", "gae"); - public static final Language SHETLAND_ENGLISH = Language.create("", "", ""); - public static final Language SPANISH = Language.create("es", "es", "spa"); - public static final Language SWEDISH = Language.create("sv", "sv", "swe"); - public static final Language TAGALOG = Language.create("tl", "tl", "tgl"); - public static final Language TURKISH = Language.create("tr", "tr", "tur"); - public static final Language URDU = Language.create("ur", "ur", "urd"); - public static final Language WALLOON = Language.create("wa", "wa", "wln"); - public static final Language WELSH = Language.create("cy", "cy", "wel"); - public static final Language YIDDISH = Language.create("yi", "yi", "yid"); - - public static Language getLanguage(String lang) { - return LanguageMaps.getLanguage(lang); - } - - public static Language getLanguageForIANA(String iana) { - return LanguageMaps.getLanguageForIANA(iana); - } - - public static Language getLanguageForISO639_2(String iso) { - return LanguageMaps.getLanguageForISO639_2(iso); - } - - private static synchronized Language create(String iana, String iso639_1, String iso639_2) { - Language lang = LanguageMaps.getLanguageForIANA(iana); - - lang = (lang != null ? lang : LanguageMaps.getLanguageForISO639_1(iso639_1)); - lang = (lang != null ? lang : LanguageMaps.getLanguageForISO639_2(iso639_2)); - - return (lang != null ? lang : new Language(iana, iso639_1, iso639_2)); - } - - private static class LanguageMaps { - private static final Map langMapIANA = new HashMap(); - private static final Map langMapISO639_1 = new HashMap(); - private static final Map langMapISO639_2 = new HashMap(); - - static void add(Language l) { - if (l.IANA != null && l.IANA.length() > 0 && !langMapIANA.containsKey(l.IANA)) { - langMapIANA.put(l.IANA, l); - } - - if (l.ISO639_1 != null && l.ISO639_1.length() > 0 && !langMapISO639_1.containsKey(l.ISO639_1)) { - langMapISO639_1.put(l.ISO639_1, l); - } - - if (l.ISO639_2 != null && l.ISO639_2.length() > 0 && !langMapISO639_2.containsKey(l.ISO639_2)) { - langMapISO639_2.put(l.ISO639_2, l); - } - } - - public static Language getLanguage(String lang) { - if (langMapIANA.containsKey(lang)) { - return langMapIANA.get(lang); - } - - return langMapISO639_2.get(lang); - } - - public static Language getLanguageForIANA(String iana) { - return langMapIANA.get(iana); - } - - public static Language getLanguageForISO639_1(String iso) { - return langMapISO639_1.get(iso); - } - - public static Language getLanguageForISO639_2(String iso) { - return langMapISO639_2.get(iso); - } - } - - private Language(String iana, String iso639_1, String iso639_2) { - IANA = iana; - ISO639_1 = iso639_1; - ISO639_2 = iso639_2; - - LanguageMaps.add(this); - } - - private Language() { - IANA = null; - ISO639_1 = null; - ISO639_2 = null; - } -} From 78fd9446d61692768459e9d2690dd4808f754b7f Mon Sep 17 00:00:00 2001 From: Miika Nurminen Date: Sat, 15 Feb 2025 11:56:57 +0200 Subject: [PATCH 948/979] Clarify command line options help and messages in checker and checker-emailer Based on discussion in #10008 and #8999 with arvoConsultores's original Messages patch. Adds error message if mutually exclusive checker options are attempted to be used in combination. Adds note to log on sent report emails and unifies email report formatting. (cherry picked from commit c5000903617ed10221297df9b00abfbced9b58a5) --- .../dspace/app/checker/ChecksumChecker.java | 53 +++++++++++++------ .../dspace/checker/DailyReportEmailer.java | 41 +++++++------- .../checker/SimpleReporterServiceImpl.java | 2 +- .../src/main/resources/Messages.properties | 10 ++-- 4 files changed, 64 insertions(+), 42 deletions(-) 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 ec024c345263..160d23e32204 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/checker/DailyReportEmailer.java b/dspace-api/src/main/java/org/dspace/checker/DailyReportEmailer.java index 50ef4baa98e3..10892b162831 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,18 @@ 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 bitstream email report"); + options.addOption("n", "not-processed", false, + "Send email report for all bitstreams set to longer be processed for today"); try { line = parser.parse(options, args); @@ -133,13 +134,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); } 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 ddefb28e1b57..6c69764fdc79 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/resources/Messages.properties b/dspace-api/src/main/resources/Messages.properties index efbbeedde053..9d15bd0621a8 100644 --- a/dspace-api/src/main/resources/Messages.properties +++ b/dspace-api/src/main/resources/Messages.properties @@ -72,20 +72,20 @@ org.dspace.checker.ResultsLogger.store-number org.dspace.checker.ResultsLogger.to-be-processed = To be processed org.dspace.checker.ResultsLogger.user-format-description = User format description org.dspace.checker.SimpleReporterImpl.bitstream-id = Bitstream Id -org.dspace.checker.SimpleReporterImpl.bitstream-not-found-report = The following is a BITSTREAM NOT FOUND report for -org.dspace.checker.SimpleReporterImpl.bitstream-will-no-longer-be-processed = The following is a BITSTREAM WILL NO LONGER BE PROCESSED report for +org.dspace.checker.SimpleReporterImpl.bitstream-not-found-report = The following is a BITSTREAM NOT FOUND report from +org.dspace.checker.SimpleReporterImpl.bitstream-will-no-longer-be-processed = The following is a BITSTREAM WILL NO LONGER BE PROCESSED report from org.dspace.checker.SimpleReporterImpl.check-id = Check Id org.dspace.checker.SimpleReporterImpl.checksum = Checksum org.dspace.checker.SimpleReporterImpl.checksum-algorithm = Checksum Algorithm org.dspace.checker.SimpleReporterImpl.checksum-calculated = Checksum Calculated -org.dspace.checker.SimpleReporterImpl.checksum-did-not-match = The following is a CHECKSUM DID NOT MATCH report for +org.dspace.checker.SimpleReporterImpl.checksum-did-not-match = The following is a CHECKSUM DID NOT MATCH report from org.dspace.checker.SimpleReporterImpl.checksum-expected = Checksum Expected org.dspace.checker.SimpleReporterImpl.date-range-to = to org.dspace.checker.SimpleReporterImpl.deleted = Deleted -org.dspace.checker.SimpleReporterImpl.deleted-bitstream-intro = The following is a BITSTREAM SET DELETED report for +org.dspace.checker.SimpleReporterImpl.deleted-bitstream-intro = The following is a BITSTREAM SET DELETED report from org.dspace.checker.SimpleReporterImpl.description = Description org.dspace.checker.SimpleReporterImpl.format-id = Format Id -org.dspace.checker.SimpleReporterImpl.howto-add-unchecked-bitstreams = To add these bitstreams to be checked run the checksum checker with the -u option +org.dspace.checker.SimpleReporterImpl.howto-add-unchecked-bitstreams = To add these bitstreams to be checked run the checksum checker again org.dspace.checker.SimpleReporterImpl.internal-id = Internal Id org.dspace.checker.SimpleReporterImpl.name = Name org.dspace.checker.SimpleReporterImpl.no-bitstreams-changed = There were no bitstreams found with changed checksums From 1e0b5a26e6353f0c456e81bd6067b7944c0d6b17 Mon Sep 17 00:00:00 2001 From: Miika Nurminen Date: Mon, 18 Aug 2025 12:12:49 +0300 Subject: [PATCH 949/979] Fix sum calculation for checker-emailer and clarify help on not to be processed report (cherry picked from commit 5ebdec62ed27d0c98430a8ff562a1b97da6f6e69) --- .../main/java/org/dspace/checker/DailyReportEmailer.java | 9 ++++++--- .../dspace/checker/service/SimpleReporterService.java | 3 ++- 2 files changed, 8 insertions(+), 4 deletions(-) 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 10892b162831..f353e0690b17 100644 --- a/dspace-api/src/main/java/org/dspace/checker/DailyReportEmailer.java +++ b/dspace-api/src/main/java/org/dspace/checker/DailyReportEmailer.java @@ -119,9 +119,10 @@ public static void main(String[] args) { options.addOption("a", "all", false, "Send all email reports (used by default)"); options.addOption("u", "unchecked", false, - "Send the Unchecked bitstream email report"); + "Send the unchecked (i.e. recently added) bitstream email report"); options.addOption("n", "not-processed", false, - "Send email report for all bitstreams set to longer be processed for today"); + "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); @@ -194,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 + // incude 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/service/SimpleReporterService.java b/dspace-api/src/main/java/org/dspace/checker/service/SimpleReporterService.java index 1dc56c20a3de..f3e0b43d8899 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. From 57b101b5d597e179890d113d4be4f3ed1e75d06e Mon Sep 17 00:00:00 2001 From: Miika Nurminen Date: Fri, 14 Nov 2025 10:04:56 +0200 Subject: [PATCH 950/979] Fix typo in comment (cherry picked from commit 2aa77e8f27cec22823debc0e57af6e9671843a30) --- .../src/main/java/org/dspace/checker/DailyReportEmailer.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 f353e0690b17..481b055fbb7d 100644 --- a/dspace-api/src/main/java/org/dspace/checker/DailyReportEmailer.java +++ b/dspace-api/src/main/java/org/dspace/checker/DailyReportEmailer.java @@ -196,7 +196,7 @@ public static void main(String[] args) { numBitstreams += reporter.getBitstreamNotFoundReport(context, yesterday, tomorrow, writer); writer.write("\n--------------------------------- Report Spacer ---------------------------\n\n"); // not to be processed report includes deleted and not found bitstreams so it is not necessary to - // incude the sum in the counter + // include the sum in the counter reporter.getNotToBeProcessedReport(context, yesterday, tomorrow, writer); writer.write("\n--------------------------------- Report Spacer ---------------------------\n\n"); numBitstreams += reporter.getUncheckedBitstreamsReport(context, writer); From ef8f4383b01f7110f5a91c18fb63a5eb260429d1 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Thu, 2 Oct 2025 16:12:51 -0500 Subject: [PATCH 951/979] Remove X509Authentication, its configuration, and all references to it within other files. --- .../authenticate/AuthenticationMethod.java | 8 +- .../AuthenticationServiceImpl.java | 8 +- .../authenticate/LDAPAuthentication.java | 2 +- .../authenticate/PasswordAuthentication.java | 4 +- .../authenticate/ShibAuthentication.java | 7 +- .../authenticate/X509Authentication.java | 616 ------------------ .../service/AuthenticationService.java | 12 +- .../org/dspace/eperson/EPersonCLITool.java | 2 +- dspace/config/dspace.cfg | 1 - dspace/config/local.cfg.EXAMPLE | 3 - dspace/config/modules/authentication-x509.cfg | 23 - dspace/config/modules/authentication.cfg | 6 - 12 files changed, 21 insertions(+), 671 deletions(-) delete mode 100644 dspace-api/src/main/java/org/dspace/authenticate/X509Authentication.java delete mode 100644 dspace/config/modules/authentication-x509.cfg 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 d316cb636f87..9f1c0827e979 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. */ @@ -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 2b07f73c489c..9387faeb738e 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 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 40b8f48078c9..9d7c09f1c314 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 */ 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 8e030305c957..035a235422a6 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 24d8266012d4..b5eed7e764b0 100644 --- a/dspace-api/src/main/java/org/dspace/authenticate/ShibAuthentication.java +++ b/dspace-api/src/main/java/org/dspace/authenticate/ShibAuthentication.java @@ -160,7 +160,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 @@ -417,8 +417,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. */ @@ -917,7 +916,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 55843c710760..000000000000 --- 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 jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; -import jakarta.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("jakarta.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 45ad8932daec..8409f1e27d05 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/eperson/EPersonCLITool.java b/dspace-api/src/main/java/org/dspace/eperson/EPersonCLITool.java index 343ddcccfa39..53451e07a9ec 100644 --- a/dspace-api/src/main/java/org/dspace/eperson/EPersonCLITool.java +++ b/dspace-api/src/main/java/org/dspace/eperson/EPersonCLITool.java @@ -50,7 +50,7 @@ public class EPersonCLITool { private static final Option OPT_PHONE = new Option("t", "telephone", true, "telephone number, empty for none"); private static final Option OPT_LANGUAGE = new Option("l", "language", true, "the person's preferred language"); private static final Option OPT_REQUIRE_CERTIFICATE = new Option("c", "requireCertificate", true, - "if 'true', an X.509 certificate will be " + + "if 'true', a certificate will be " + "required for login"); private static final Option OPT_CAN_LOGIN = new Option("C", "canLogIn", true, "'true' if the user can log in"); diff --git a/dspace/config/dspace.cfg b/dspace/config/dspace.cfg index 67490dac3e6d..01e168937a99 100644 --- a/dspace/config/dspace.cfg +++ b/dspace/config/dspace.cfg @@ -1655,7 +1655,6 @@ include = ${module_dir}/authentication-ldap.cfg include = ${module_dir}/authentication-oidc.cfg include = ${module_dir}/authentication-password.cfg include = ${module_dir}/authentication-shibboleth.cfg -include = ${module_dir}/authentication-x509.cfg include = ${module_dir}/authority.cfg include = ${module_dir}/bulkedit.cfg include = ${module_dir}/citation-page.cfg diff --git a/dspace/config/local.cfg.EXAMPLE b/dspace/config/local.cfg.EXAMPLE index 7454fcde06b4..15168a911336 100644 --- a/dspace/config/local.cfg.EXAMPLE +++ b/dspace/config/local.cfg.EXAMPLE @@ -200,9 +200,6 @@ db.password = dspace # ORCID certificate authentication. # plugin.sequence.org.dspace.authenticate.AuthenticationMethod = org.dspace.authenticate.OrcidAuthentication -# X.509 certificate authentication. See authentication-x509.cfg for default configuration. -#plugin.sequence.org.dspace.authenticate.AuthenticationMethod = org.dspace.authenticate.X509Authentication - # Authentication by Password (encrypted in DSpace's database). See authentication-password.cfg for default configuration. # Enabled by default in authentication.cfg #plugin.sequence.org.dspace.authenticate.AuthenticationMethod = org.dspace.authenticate.PasswordAuthentication diff --git a/dspace/config/modules/authentication-x509.cfg b/dspace/config/modules/authentication-x509.cfg deleted file mode 100644 index d3f05c7d17d5..000000000000 --- a/dspace/config/modules/authentication-x509.cfg +++ /dev/null @@ -1,23 +0,0 @@ -#---------------------------------------------------------------# -#------X.509 CERTIFICATE AUTHENTICATION CONFIGURATIONS----------# -#---------------------------------------------------------------# -# Configuration properties used by the X.509 Certificate # -# Authentication plugin, when it is enabled. # -#---------------------------------------------------------------# - -## method 1, using keystore -#authentication-x509.keystore.path = /tomcat/conf/keystore -#authentication-x509.keystore.password = changeit - -## method 2, using CA certificate -#authentication-x509.ca.cert = ${dspace.dir}/config/MyClientCA.pem - -## Create e-persons for unknown names in valid certificates? -#authentication-x509.autoregister = true - -## Allow Certificate auth to show as a choice in chooser -# Use Messages.properties key for title -#authentication-x509.chooser.title.key=org.dspace.eperson.X509Authentication.title -# -# Identify the location of the Certificate Login Servlet. -#authentication-x509.chooser.uri=/certificate-login diff --git a/dspace/config/modules/authentication.cfg b/dspace/config/modules/authentication.cfg index 568f871e3cd7..41c28df1d7c9 100644 --- a/dspace/config/modules/authentication.cfg +++ b/dspace/config/modules/authentication.cfg @@ -21,9 +21,6 @@ # * IP Address Authentication # Plugin class: org.dspace.authenticate.IPAuthentication # Configuration file: authentication-ip.cfg -# * X.509 Certificate Authentication -# Plugin class: org.dspace.authenticate.X509Authentication -# Configuration file: authentication-x509.cfg # * ORCID certificate authentication. # Plugin class: org.dspace.authenticate.OrcidAuthentication # Configuration file: orcid.cfg @@ -49,9 +46,6 @@ # Shibboleth authentication/authorization. See authentication-shibboleth.cfg for default configuration. #plugin.sequence.org.dspace.authenticate.AuthenticationMethod = org.dspace.authenticate.ShibAuthentication -# X.509 certificate authentication. See authentication-x509.cfg for default configuration. -#plugin.sequence.org.dspace.authenticate.AuthenticationMethod = org.dspace.authenticate.X509Authentication - # ORCID certificate authentication. # plugin.sequence.org.dspace.authenticate.AuthenticationMethod = org.dspace.authenticate.OrcidAuthentication From 13b0bbaaa92d82e69b76073588ebd090a8211fe8 Mon Sep 17 00:00:00 2001 From: Zahraa Chreim Date: Wed, 19 Nov 2025 08:42:33 +0200 Subject: [PATCH 952/979] 135699: Fix NPE in Bitstream deletion by reversing bitstream comparison (cherry picked from commit b2f862500a56fa585dd16509bffb47401c2f2419) --- .../src/main/java/org/dspace/content/BitstreamServiceImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 7253761fd902..3a2670395dbe 100644 --- a/dspace-api/src/main/java/org/dspace/content/BitstreamServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/content/BitstreamServiceImpl.java @@ -295,7 +295,7 @@ public void delete(Context context, Bitstream bitstream) throws SQLException, Au // a deleted bitstream List requestItems = requestItemService.findAll(context); for (RequestItem requestItem : requestItems) { - if (requestItem.getBitstream().equals(bitstream)) { + if (bitstream.equals(requestItem.getBitstream())) { requestItemService.delete(context, requestItem); } } From 3026942d99914f93de90059197625adad102ea21 Mon Sep 17 00:00:00 2001 From: "David P. Steelman" Date: Fri, 26 Sep 2025 10:29:43 -0400 Subject: [PATCH 953/979] Use canonical path for bitstream path/assetstore path comparisons Replaced `baseDir.getAbsolutePath()` with `baseDir.getCanonicalPath()` in the bitstream path check. This is necessary because the "normzalizedPath" is generated via the "getCanonicalFile" method. This method follows symbolic links, unlike the "getAbsolutePath" method, which does not. Therefore, when comparing the "normalizedPath" to the "baseDir", it is necessary to use "getCanonicalPath" so that any symbolic links are resolved in the same way. This issue was uncovered by running the "org.dspace.storage.bitstore.BitstreamStorageServiceImplIT" integration tests on macOS, in which a temporary file asset store is used. On macOS the temporary files are stored in "/var" which is a symbolic link to "/private/var". (cherry picked from commit 8f8cb52658954234c05e1ea71e43375f080b7d0f) --- .../java/org/dspace/storage/bitstore/DSBitStoreService.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/storage/bitstore/DSBitStoreService.java b/dspace-api/src/main/java/org/dspace/storage/bitstore/DSBitStoreService.java index fd4e23b82581..a08af8104ccd 100644 --- a/dspace-api/src/main/java/org/dspace/storage/bitstore/DSBitStoreService.java +++ b/dspace-api/src/main/java/org/dspace/storage/bitstore/DSBitStoreService.java @@ -256,11 +256,11 @@ protected File getFile(Bitstream bitstream) throws IOException { Path normalizedPath = bitstreamFile.toPath().normalize(); String[] allowedAssetstoreRoots = DSpaceServicesFactory.getInstance().getConfigurationService() .getArrayProperty("assetstore.allowed.roots", new String[]{}); - if (!normalizedPath.startsWith(baseDir.getAbsolutePath()) + if (!normalizedPath.startsWith(baseDir.getCanonicalPath()) && !StringUtils.startsWithAny(normalizedPath.toString(), allowedAssetstoreRoots)) { log.error("Bitstream path outside of assetstore root requested:" + "bitstream={}, path={}, assetstore={}", - bitstream.getID(), normalizedPath, baseDir.getAbsolutePath()); + bitstream.getID(), normalizedPath, baseDir.getCanonicalPath()); throw new IOException("Illegal bitstream path constructed"); } return bitstreamFile; From 128392eb9112998674ae001db9c9d024c29a337f Mon Sep 17 00:00:00 2001 From: JohnnyMendesC <177888064+JohnnyMendesC@users.noreply.github.com> Date: Fri, 14 Nov 2025 18:50:04 -0300 Subject: [PATCH 954/979] fix(#10176): Fix inverted parameters for Shibboleth metadata update (cherry picked from commit 779e929d51758acd6b03780b2b562877e53b3ce2) --- .../main/java/org/dspace/authenticate/ShibAuthentication.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 b5eed7e764b0..13a5ae6d0dfd 100644 --- a/dspace-api/src/main/java/org/dspace/authenticate/ShibAuthentication.java +++ b/dspace-api/src/main/java/org/dspace/authenticate/ShibAuthentication.java @@ -870,7 +870,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); } From 968d8bf9cd5ca2eda5cbf91c8a9fb226e8037042 Mon Sep 17 00:00:00 2001 From: JohnnyMendesC <177888064+JohnnyMendesC@users.noreply.github.com> Date: Tue, 18 Nov 2025 18:14:07 -0300 Subject: [PATCH 955/979] test(DSpace#10176): Add unit test for Shibboleth metadata update regression This class contains unit tests for the ShibAuthentication functionality, verifying metadata updates and initialization behavior. (cherry picked from commit 748a11fa20208017a78b1d97a0d9095a15064c83) --- .../authenticate/ShibAuthenticationTest.java | 128 ++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 dspace-api/src/test/java/org/dspace/authenticate/ShibAuthenticationTest.java diff --git a/dspace-api/src/test/java/org/dspace/authenticate/ShibAuthenticationTest.java b/dspace-api/src/test/java/org/dspace/authenticate/ShibAuthenticationTest.java new file mode 100644 index 000000000000..0f9daddf57a8 --- /dev/null +++ b/dspace-api/src/test/java/org/dspace/authenticate/ShibAuthenticationTest.java @@ -0,0 +1,128 @@ +package org.dspace.authenticate; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.ArgumentMatchers.isNull; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.util.Collections; +import jakarta.servlet.http.HttpServletRequest; +import org.dspace.AbstractUnitTest; +import org.dspace.content.service.MetadataFieldService; +import org.dspace.content.MetadataField; +import org.dspace.content.MetadataSchema; +import org.dspace.core.Context; +import org.dspace.eperson.EPerson; +import org.dspace.eperson.service.EPersonService; +import org.dspace.services.ConfigurationService; +import org.junit.Before; +import org.junit.Test; +import org.mockito.ArgumentCaptor; + +/** + * Unit tests for ShibAuthentication + */ +public class ShibAuthenticationTest extends AbstractUnitTest { + + private ShibAuthentication shibAuthentication; + private EPersonService ePersonService; + private ConfigurationService configurationService; + + @Before + public void setup() { + shibAuthentication = new ShibAuthentication(); + ePersonService = mock(EPersonService.class); + shibAuthentication.ePersonService = ePersonService; + configurationService = mock(ConfigurationService.class); + shibAuthentication.configurationService = configurationService; + when(configurationService.getProperty("authentication-shibboleth.netid-header")).thenReturn("SHIB-NETID"); + when(configurationService.getProperty("authentication-shibboleth.email-header")).thenReturn("SHIB-MAIL"); + when(configurationService.getArrayProperty("authentication-shibboleth.eperson.metadata")) + .thenReturn(new String[]{"SHIB-telephone => eperson.phone"}); + when(configurationService.getBooleanProperty("authentication-shibboleth.eperson.metadata.autocreate", true)).thenReturn(true); + MetadataFieldService metadataFieldService = mock(MetadataFieldService.class); + shibAuthentication.metadataFieldService = metadataFieldService; + + try { + when(metadataFieldService.findByElement(any(Context.class), any(String.class), any(String.class), any())) + .thenReturn(mock(MetadataField.class)); + } catch (Exception e) { + // ignore checked exceptions from mock + } + } + + @Test + public void testPhoneMetadataUpdateOrder() throws Exception { + Context context = mock(Context.class); + HttpServletRequest request = mock(HttpServletRequest.class); + EPerson eperson = mock(EPerson.class); + when(request.getAttribute("SHIB-NETID")).thenReturn("test-user"); + when(request.getAttribute("SHIB-MAIL")).thenReturn("test@example.com"); + String phoneValue = "555-1234"; + when(request.getAttribute("SHIB-telephone")).thenReturn(phoneValue); + shibAuthentication.initialize(context); + assertNotNull("metadataHeaderMap should be initialized", shibAuthentication.metadataHeaderMap); + assertTrue("metadataHeaderMap should contain SHIB-telephone", shibAuthentication.metadataHeaderMap.containsKey("SHIB-telephone")); + shibAuthentication.updateEPerson(context, request, eperson); + ArgumentCaptor languageCaptor = ArgumentCaptor.forClass(String.class); + ArgumentCaptor valueCaptor = ArgumentCaptor.forClass(String.class); + + verify(ePersonService, times(1)).setMetadataSingleValue( + any(Context.class), + eq(eperson), + eq("eperson"), + eq("phone"), + isNull(), + languageCaptor.capture(), + valueCaptor.capture() + ); + + assertNull("The language argument should be NULL.", languageCaptor.getValue()); + assertEquals("The value argument should be the phone number.", phoneValue, valueCaptor.getValue()); + } + + @Test + public void testInitializeLoadsMultipleMappings() throws Exception { + Context context = mock(Context.class); + when(configurationService.getArrayProperty("authentication-shibboleth.eperson.metadata")) + .thenReturn(new String[]{ + "SHIB-telephone => eperson.phone", + "SHIB-dept => eperson.department" + }); + shibAuthentication.initialize(context); + + assertNotNull("metadataHeaderMap should be initialized", shibAuthentication.metadataHeaderMap); + assertTrue("metadataHeaderMap should contain SHIB-telephone", shibAuthentication.metadataHeaderMap.containsKey("SHIB-telephone")); + assertTrue("metadataHeaderMap should contain SHIB-dept", shibAuthentication.metadataHeaderMap.containsKey("SHIB-dept")); + } + + @Test + public void testNoMetadataMappingNoUpdate() throws Exception { + Context context = mock(Context.class); + HttpServletRequest request = mock(HttpServletRequest.class); + EPerson eperson = mock(EPerson.class); + when(configurationService.getArrayProperty("authentication-shibboleth.eperson.metadata")) + .thenReturn(new String[0]); + shibAuthentication.initialize(context); + shibAuthentication.updateEPerson(context, request, eperson); + + verify(ePersonService, times(0)).setMetadataSingleValue( + any(Context.class), + any(EPerson.class), + anyString(), + anyString(), + any(), + any(), + any() + ); + } +} From 159b1f095e0eede5460377bc24a69a8cac17b8a3 Mon Sep 17 00:00:00 2001 From: JohnnyMendesC <177888064+JohnnyMendesC@users.noreply.github.com> Date: Wed, 19 Nov 2025 09:39:02 -0300 Subject: [PATCH 956/979] Added missing license header (cherry picked from commit f742549f72c71aebcc71a54fa35df643f617d741) --- .../org/dspace/authenticate/ShibAuthenticationTest.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/dspace-api/src/test/java/org/dspace/authenticate/ShibAuthenticationTest.java b/dspace-api/src/test/java/org/dspace/authenticate/ShibAuthenticationTest.java index 0f9daddf57a8..d3fe53f2b5bb 100644 --- a/dspace-api/src/test/java/org/dspace/authenticate/ShibAuthenticationTest.java +++ b/dspace-api/src/test/java/org/dspace/authenticate/ShibAuthenticationTest.java @@ -1,3 +1,10 @@ +/** + * 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 static org.junit.Assert.assertEquals; From 5aecee7a7f613a0647e181751e459abd2fa861e1 Mon Sep 17 00:00:00 2001 From: JohnnyMendesC <177888064+JohnnyMendesC@users.noreply.github.com> Date: Wed, 19 Nov 2025 10:55:16 -0300 Subject: [PATCH 957/979] Fixed checkstyle errors (cherry picked from commit deccd07d7d2821dc593e43cc0d2697d263a1905c) --- .../authenticate/ShibAuthenticationTest.java | 21 ++++++++++--------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/dspace-api/src/test/java/org/dspace/authenticate/ShibAuthenticationTest.java b/dspace-api/src/test/java/org/dspace/authenticate/ShibAuthenticationTest.java index d3fe53f2b5bb..e0f0fc57a9ea 100644 --- a/dspace-api/src/test/java/org/dspace/authenticate/ShibAuthenticationTest.java +++ b/dspace-api/src/test/java/org/dspace/authenticate/ShibAuthenticationTest.java @@ -8,25 +8,22 @@ package org.dspace.authenticate; import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNull; import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.isNull; import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.spy; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -import java.util.Collections; import jakarta.servlet.http.HttpServletRequest; import org.dspace.AbstractUnitTest; -import org.dspace.content.service.MetadataFieldService; import org.dspace.content.MetadataField; -import org.dspace.content.MetadataSchema; +import org.dspace.content.service.MetadataFieldService; import org.dspace.core.Context; import org.dspace.eperson.EPerson; import org.dspace.eperson.service.EPersonService; @@ -55,7 +52,8 @@ public void setup() { when(configurationService.getProperty("authentication-shibboleth.email-header")).thenReturn("SHIB-MAIL"); when(configurationService.getArrayProperty("authentication-shibboleth.eperson.metadata")) .thenReturn(new String[]{"SHIB-telephone => eperson.phone"}); - when(configurationService.getBooleanProperty("authentication-shibboleth.eperson.metadata.autocreate", true)).thenReturn(true); + when(configurationService.getBooleanProperty("authentication-shibboleth.eperson.metadata.autocreate", true)) + .thenReturn(true); MetadataFieldService metadataFieldService = mock(MetadataFieldService.class); shibAuthentication.metadataFieldService = metadataFieldService; @@ -78,7 +76,8 @@ public void testPhoneMetadataUpdateOrder() throws Exception { when(request.getAttribute("SHIB-telephone")).thenReturn(phoneValue); shibAuthentication.initialize(context); assertNotNull("metadataHeaderMap should be initialized", shibAuthentication.metadataHeaderMap); - assertTrue("metadataHeaderMap should contain SHIB-telephone", shibAuthentication.metadataHeaderMap.containsKey("SHIB-telephone")); + assertTrue("metadataHeaderMap should contain SHIB-telephone", shibAuthentication.metadataHeaderMap + .containsKey("SHIB-telephone")); shibAuthentication.updateEPerson(context, request, eperson); ArgumentCaptor languageCaptor = ArgumentCaptor.forClass(String.class); ArgumentCaptor valueCaptor = ArgumentCaptor.forClass(String.class); @@ -92,7 +91,7 @@ public void testPhoneMetadataUpdateOrder() throws Exception { languageCaptor.capture(), valueCaptor.capture() ); - + assertNull("The language argument should be NULL.", languageCaptor.getValue()); assertEquals("The value argument should be the phone number.", phoneValue, valueCaptor.getValue()); } @@ -108,8 +107,10 @@ public void testInitializeLoadsMultipleMappings() throws Exception { shibAuthentication.initialize(context); assertNotNull("metadataHeaderMap should be initialized", shibAuthentication.metadataHeaderMap); - assertTrue("metadataHeaderMap should contain SHIB-telephone", shibAuthentication.metadataHeaderMap.containsKey("SHIB-telephone")); - assertTrue("metadataHeaderMap should contain SHIB-dept", shibAuthentication.metadataHeaderMap.containsKey("SHIB-dept")); + assertTrue("metadataHeaderMap should contain SHIB-telephone", shibAuthentication.metadataHeaderMap + .containsKey("SHIB-telephone")); + assertTrue("metadataHeaderMap should contain SHIB-dept", shibAuthentication.metadataHeaderMap + .containsKey("SHIB-dept")); } @Test From 3d8ff72cbe7e49e40cd5d6b8904bda7fff9d4713 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 24 Nov 2025 16:31:06 +0000 Subject: [PATCH 958/979] build(deps): bump org.checkerframework:checker-qual Bumps [org.checkerframework:checker-qual](https://github.com/typetools/checker-framework) from 3.51.1 to 3.52.0. - [Release notes](https://github.com/typetools/checker-framework/releases) - [Changelog](https://github.com/typetools/checker-framework/blob/master/docs/CHANGELOG.md) - [Commits](https://github.com/typetools/checker-framework/compare/checker-framework-3.51.1...checker-framework-3.52.0) --- updated-dependencies: - dependency-name: org.checkerframework:checker-qual dependency-version: 3.52.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 61b167bd6a8d..f63927b34b15 100644 --- a/pom.xml +++ b/pom.xml @@ -1348,7 +1348,7 @@ org.checkerframework checker-qual - 3.51.1 + 3.52.0 17 - 6.2.12 - 3.5.7 - 6.5.6 + 6.2.14 + 3.5.8 + 6.5.7 6.4.10.Final 8.0.3.Final 42.7.8 From a548649bd21105598fec36376a0a3c4d0844985b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Dec 2025 18:50:05 +0000 Subject: [PATCH 964/979] build(deps): bump the apache-commons group across 1 directory with 6 updates Bumps the apache-commons group with 6 updates in the / directory: | Package | From | To | | --- | --- | --- | | [commons-cli:commons-cli](https://github.com/apache/commons-cli) | `1.10.0` | `1.11.0` | | [commons-codec:commons-codec](https://github.com/apache/commons-codec) | `1.19.0` | `1.20.0` | | org.apache.commons:commons-configuration2 | `2.12.0` | `2.13.0` | | [commons-io:commons-io](https://github.com/apache/commons-io) | `2.20.0` | `2.21.0` | | org.apache.commons:commons-lang3 | `3.19.0` | `3.20.0` | | [commons-validator:commons-validator](https://github.com/apache/commons-validator) | `1.10.0` | `1.10.1` | Updates `commons-cli:commons-cli` from 1.10.0 to 1.11.0 - [Changelog](https://github.com/apache/commons-cli/blob/master/RELEASE-NOTES.txt) - [Commits](https://github.com/apache/commons-cli/compare/rel/commons-cli-1.10.0...rel/commons-cli-1.11.0) Updates `commons-codec:commons-codec` from 1.19.0 to 1.20.0 - [Changelog](https://github.com/apache/commons-codec/blob/master/RELEASE-NOTES.txt) - [Commits](https://github.com/apache/commons-codec/compare/rel/commons-codec-1.19.0...rel/commons-codec-1.20.0) Updates `org.apache.commons:commons-configuration2` from 2.12.0 to 2.13.0 Updates `commons-io:commons-io` from 2.20.0 to 2.21.0 - [Changelog](https://github.com/apache/commons-io/blob/master/RELEASE-NOTES.txt) - [Commits](https://github.com/apache/commons-io/compare/rel/commons-io-2.20.0...rel/commons-io-2.21.0) Updates `org.apache.commons:commons-lang3` from 3.19.0 to 3.20.0 Updates `commons-validator:commons-validator` from 1.10.0 to 1.10.1 - [Changelog](https://github.com/apache/commons-validator/blob/master/RELEASE-NOTES.txt) - [Commits](https://github.com/apache/commons-validator/compare/rel/commons-validator-1.10.0...rel/commons-validator-1.10.1) --- updated-dependencies: - dependency-name: commons-cli:commons-cli dependency-version: 1.11.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: apache-commons - dependency-name: commons-codec:commons-codec dependency-version: 1.20.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: apache-commons - dependency-name: org.apache.commons:commons-configuration2 dependency-version: 2.13.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: apache-commons - dependency-name: commons-io:commons-io dependency-version: 2.21.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: apache-commons - dependency-name: org.apache.commons:commons-lang3 dependency-version: 3.20.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: apache-commons - dependency-name: commons-validator:commons-validator dependency-version: 1.10.1 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: apache-commons ... Signed-off-by: dependabot[bot] --- pom.xml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/pom.xml b/pom.xml index 61b167bd6a8d..ccebc256c8ff 100644 --- a/pom.xml +++ b/pom.xml @@ -1471,12 +1471,12 @@ commons-cli commons-cli - 1.10.0 + 1.11.0 commons-codec commons-codec - 1.19.0 + 1.20.0 org.apache.commons @@ -1486,7 +1486,7 @@ org.apache.commons commons-configuration2 - 2.12.0 + 2.13.0 org.apache.commons @@ -1503,12 +1503,12 @@ commons-io commons-io - 2.20.0 + 2.21.0 org.apache.commons commons-lang3 - 3.19.0 + 3.20.0 @@ -1540,7 +1540,7 @@ commons-validator commons-validator - 1.10.0 + 1.10.1 jakarta.activation From 6d59f247847b186d99e3d688579e550a16984732 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Dec 2025 18:52:37 +0000 Subject: [PATCH 965/979] build(deps): bump the build-tools group across 1 directory with 10 updates Bumps the build-tools group with 10 updates in the / directory: | Package | From | To | | --- | --- | --- | | [com.github.spotbugs:spotbugs](https://github.com/spotbugs/spotbugs) | `4.9.6` | `4.9.8` | | [org.apache.maven.plugins:maven-jar-plugin](https://github.com/apache/maven-jar-plugin) | `3.4.2` | `3.5.0` | | [org.apache.maven.plugins:maven-war-plugin](https://github.com/apache/maven-war-plugin) | `3.4.0` | `3.5.0` | | [com.github.spotbugs:spotbugs-maven-plugin](https://github.com/spotbugs/spotbugs-maven-plugin) | `4.9.6.0` | `4.9.8.2` | | [org.apache.maven.plugins:maven-assembly-plugin](https://github.com/apache/maven-assembly-plugin) | `3.7.1` | `3.8.0` | | [org.apache.maven.plugins:maven-resources-plugin](https://github.com/apache/maven-resources-plugin) | `3.3.1` | `3.4.0` | | [org.apache.maven.plugins:maven-source-plugin](https://github.com/apache/maven-source-plugin) | `3.3.1` | `3.4.0` | | [org.jacoco:jacoco-maven-plugin](https://github.com/jacoco/jacoco) | `0.8.13` | `0.8.14` | | [org.apache.maven.plugins:maven-release-plugin](https://github.com/apache/maven-release) | `3.1.1` | `3.2.0` | | [org.codehaus.mojo:xml-maven-plugin](https://github.com/mojohaus/xml-maven-plugin) | `1.1.0` | `1.2.0` | Updates `com.github.spotbugs:spotbugs` from 4.9.6 to 4.9.8 - [Release notes](https://github.com/spotbugs/spotbugs/releases) - [Changelog](https://github.com/spotbugs/spotbugs/blob/master/CHANGELOG.md) - [Commits](https://github.com/spotbugs/spotbugs/compare/4.9.6...4.9.8) Updates `org.apache.maven.plugins:maven-jar-plugin` from 3.4.2 to 3.5.0 - [Release notes](https://github.com/apache/maven-jar-plugin/releases) - [Commits](https://github.com/apache/maven-jar-plugin/compare/maven-jar-plugin-3.4.2...maven-jar-plugin-3.5.0) Updates `org.apache.maven.plugins:maven-war-plugin` from 3.4.0 to 3.5.0 - [Release notes](https://github.com/apache/maven-war-plugin/releases) - [Commits](https://github.com/apache/maven-war-plugin/compare/maven-war-plugin-3.4.0...maven-war-plugin-3.5.0) Updates `com.github.spotbugs:spotbugs-maven-plugin` from 4.9.6.0 to 4.9.8.2 - [Release notes](https://github.com/spotbugs/spotbugs-maven-plugin/releases) - [Commits](https://github.com/spotbugs/spotbugs-maven-plugin/compare/spotbugs-maven-plugin-4.9.6.0...spotbugs-maven-plugin-4.9.8.2) Updates `org.apache.maven.plugins:maven-assembly-plugin` from 3.7.1 to 3.8.0 - [Release notes](https://github.com/apache/maven-assembly-plugin/releases) - [Commits](https://github.com/apache/maven-assembly-plugin/compare/maven-assembly-plugin-3.7.1...v3.8.0) Updates `org.apache.maven.plugins:maven-resources-plugin` from 3.3.1 to 3.4.0 - [Release notes](https://github.com/apache/maven-resources-plugin/releases) - [Commits](https://github.com/apache/maven-resources-plugin/compare/maven-resources-plugin-3.3.1...v3.4.0) Updates `org.apache.maven.plugins:maven-source-plugin` from 3.3.1 to 3.4.0 - [Release notes](https://github.com/apache/maven-source-plugin/releases) - [Commits](https://github.com/apache/maven-source-plugin/compare/maven-source-plugin-3.3.1...maven-source-plugin-3.4.0) Updates `org.jacoco:jacoco-maven-plugin` from 0.8.13 to 0.8.14 - [Release notes](https://github.com/jacoco/jacoco/releases) - [Commits](https://github.com/jacoco/jacoco/compare/v0.8.13...v0.8.14) Updates `org.apache.maven.plugins:maven-release-plugin` from 3.1.1 to 3.2.0 - [Release notes](https://github.com/apache/maven-release/releases) - [Commits](https://github.com/apache/maven-release/compare/maven-release-3.1.1...maven-release-3.2.0) Updates `org.codehaus.mojo:xml-maven-plugin` from 1.1.0 to 1.2.0 - [Release notes](https://github.com/mojohaus/xml-maven-plugin/releases) - [Commits](https://github.com/mojohaus/xml-maven-plugin/compare/1.1.0...1.2.0) --- updated-dependencies: - dependency-name: com.github.spotbugs:spotbugs dependency-version: 4.9.8 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: build-tools - dependency-name: org.apache.maven.plugins:maven-jar-plugin dependency-version: 3.5.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: build-tools - dependency-name: org.apache.maven.plugins:maven-war-plugin dependency-version: 3.5.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: build-tools - dependency-name: com.github.spotbugs:spotbugs-maven-plugin dependency-version: 4.9.8.2 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: build-tools - dependency-name: org.apache.maven.plugins:maven-assembly-plugin dependency-version: 3.8.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: build-tools - dependency-name: org.apache.maven.plugins:maven-resources-plugin dependency-version: 3.4.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: build-tools - dependency-name: org.apache.maven.plugins:maven-source-plugin dependency-version: 3.4.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: build-tools - dependency-name: org.jacoco:jacoco-maven-plugin dependency-version: 0.8.14 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: build-tools - dependency-name: org.apache.maven.plugins:maven-release-plugin dependency-version: 3.2.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: build-tools - dependency-name: org.codehaus.mojo:xml-maven-plugin dependency-version: 1.2.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: build-tools ... Signed-off-by: dependabot[bot] --- pom.xml | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/pom.xml b/pom.xml index 61b167bd6a8d..65c829051034 100644 --- a/pom.xml +++ b/pom.xml @@ -177,7 +177,7 @@ org.apache.maven.plugins maven-jar-plugin - 3.4.2 + 3.5.0 @@ -191,7 +191,7 @@ org.apache.maven.plugins maven-war-plugin - 3.4.0 + 3.5.1 false @@ -303,7 +303,7 @@ com.github.spotbugs spotbugs-maven-plugin - 4.9.6.0 + 4.9.8.2 Max Low @@ -313,7 +313,7 @@ com.github.spotbugs spotbugs - 4.9.6 + 4.9.8 @@ -343,7 +343,7 @@ maven-assembly-plugin - 3.7.1 + 3.8.0 org.apache.maven.plugins @@ -353,7 +353,7 @@ org.apache.maven.plugins maven-resources-plugin - 3.3.1 + 3.4.0 @@ -386,7 +386,7 @@ org.apache.maven.plugins maven-source-plugin - 3.3.1 + 3.4.0 @@ -405,7 +405,7 @@ org.jacoco jacoco-maven-plugin - 0.8.13 + 0.8.14 @@ -417,7 +417,7 @@ org.apache.maven.plugins maven-release-plugin - 3.1.1 + 3.2.0 @@ -485,7 +485,7 @@ org.codehaus.mojo xml-maven-plugin - 1.1.0 + 1.2.0 validate-ALL-xml-and-xsl From 97ddacccfcb65bd86f7f14ab9f1cf2fe5f279810 Mon Sep 17 00:00:00 2001 From: Alan Orth Date: Thu, 7 Aug 2025 11:20:18 +0300 Subject: [PATCH 966/979] pom.xml: upgrade pdfbox and tika Use latest pdfbox 3.0.5 and tika 3.2.2. See: https://pdfbox.apache.org/3.0/migration.html See: https://dist.apache.org/repos/dist/release/tika/3.2.2/CHANGES-3.2.2.txt --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 966af2edb2dd..401a83b744d6 100644 --- a/pom.xml +++ b/pom.xml @@ -40,10 +40,10 @@ 9.4.58.v20250814 2.25.2 - 2.0.35 + 3.0.5 1.19.0 2.0.17 - 2.9.4 + 3.2.2 1.82 8.0.1 From ab7288e75b18520de6a85955dc5369b93c96be85 Mon Sep 17 00:00:00 2001 From: Alan Orth Date: Thu, 7 Aug 2025 11:39:09 +0300 Subject: [PATCH 967/979] dspace-api: Update syntax for pdfbox 3.0.x Conflicts resolved with CitationDocumentServiceImpl to satisfy the older implementation. --- .../ImageMagickThumbnailFilter.java | 6 ++--- .../app/mediafilter/PDFBoxThumbnail.java | 4 +++- .../dspace/content/packager/PDFPackager.java | 23 +++++++++++-------- .../EpoImportMetadataSourceServiceImpl.java | 2 +- .../app/rest/BitstreamRestControllerIT.java | 6 +++-- 5 files changed, 25 insertions(+), 16 deletions(-) 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 408982d157e5..7543410a7968 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 @@ -14,7 +14,7 @@ import java.util.regex.Pattern; import java.util.regex.PatternSyntaxException; -import org.apache.pdfbox.pdmodel.PDDocument; +import org.apache.pdfbox.Loader; import org.apache.pdfbox.pdmodel.PDPage; import org.apache.pdfbox.pdmodel.common.PDRectangle; import org.dspace.content.Bitstream; @@ -153,8 +153,8 @@ public File getImageFile(File f, boolean verbose) // the CropBox is missing or empty because pdfbox will set it to the // same size as the MediaBox if it doesn't exist. Also note that we // only need to check the first page, since that's what we use for - // generating the thumbnail (PDDocument uses a zero-based index). - PDPage pdfPage = PDDocument.load(f).getPage(0); + // generating the thumbnail (PDPage uses a zero-based index). + PDPage pdfPage = Loader.loadPDF(f).getPage(0); PDRectangle pdfPageMediaBox = pdfPage.getMediaBox(); PDRectangle pdfPageCropBox = pdfPage.getCropBox(); 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 577f1dec4a18..eb23e9daa085 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 @@ -11,6 +11,8 @@ import java.io.InputStream; import org.apache.logging.log4j.Logger; +import org.apache.pdfbox.Loader; +import org.apache.pdfbox.io.RandomAccessReadBuffer; import org.apache.pdfbox.pdmodel.PDDocument; import org.apache.pdfbox.pdmodel.encryption.InvalidPasswordException; import org.apache.pdfbox.rendering.PDFRenderer; @@ -71,7 +73,7 @@ public InputStream getDestinationStream(Item currentItem, InputStream source, bo BufferedImage buf; // Render the page image. - try ( PDDocument doc = PDDocument.load(source); ) { + try ( PDDocument doc = Loader.loadPDF(new RandomAccessReadBuffer(source)); ) { PDFRenderer renderer = new PDFRenderer(doc); buf = renderer.renderImage(0); } catch (InvalidPasswordException ex) { diff --git a/dspace-api/src/main/java/org/dspace/content/packager/PDFPackager.java b/dspace-api/src/main/java/org/dspace/content/packager/PDFPackager.java index 6c7baad45497..f63585f3c498 100644 --- a/dspace-api/src/main/java/org/dspace/content/packager/PDFPackager.java +++ b/dspace-api/src/main/java/org/dspace/content/packager/PDFPackager.java @@ -18,11 +18,11 @@ import org.apache.commons.lang3.ArrayUtils; import org.apache.logging.log4j.Logger; +import org.apache.pdfbox.Loader; import org.apache.pdfbox.cos.COSDocument; import org.apache.pdfbox.io.MemoryUsageSetting; -import org.apache.pdfbox.io.RandomAccessBufferedFileInputStream; +import org.apache.pdfbox.io.RandomAccessReadBuffer; import org.apache.pdfbox.io.ScratchFile; -import org.apache.pdfbox.pdfparser.PDFParser; import org.apache.pdfbox.pdmodel.PDDocument; import org.apache.pdfbox.pdmodel.PDDocumentInformation; import org.dspace.authorize.AuthorizeException; @@ -330,19 +330,24 @@ private void crosswalkPDF(Context context, Item item, InputStream metadata) COSDocument cos = null; try { - ScratchFile scratchFile = null; + PDDocument document = null; + try { - long useRAM = Runtime.getRuntime().freeMemory() * 80 / 100; // use up to 80% of JVM free memory - scratchFile = new ScratchFile( - MemoryUsageSetting.setupMixed(useRAM)); // then fallback to temp file (unlimited size) + // Use up to 80% of JVM free memory and fall back to a temp file (unlimited size) + long useRAM = Runtime.getRuntime().freeMemory() * 80 / 100; + document = Loader.loadPDF( + new RandomAccessReadBuffer(metadata), + () -> new ScratchFile(MemoryUsageSetting.setupMixed(useRAM))); } catch (IOException ioe) { log.warn("Error initializing scratch file: " + ioe.getMessage()); } - PDFParser parser = new PDFParser(new RandomAccessBufferedFileInputStream(metadata), scratchFile); - parser.parse(); - cos = parser.getDocument(); + // sanity check: loaded PDF document must not be null. + if (document == null) { + throw new MetadataValidationException("The provided stream could not be parsed into a PDF document."); + } + cos = document.getDocument(); // sanity check: PDFBox breaks on encrypted documents, so give up. if (cos.getEncryptionDictionary() != null) { throw new MetadataValidationException("This packager cannot accept an encrypted PDF document."); diff --git a/dspace-api/src/main/java/org/dspace/importer/external/epo/service/EpoImportMetadataSourceServiceImpl.java b/dspace-api/src/main/java/org/dspace/importer/external/epo/service/EpoImportMetadataSourceServiceImpl.java index 552f607827a8..4ec1f4db39e7 100644 --- a/dspace-api/src/main/java/org/dspace/importer/external/epo/service/EpoImportMetadataSourceServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/importer/external/epo/service/EpoImportMetadataSourceServiceImpl.java @@ -29,9 +29,9 @@ import org.apache.commons.lang3.StringUtils; import org.apache.http.HttpException; import org.apache.http.client.utils.URIBuilder; +import org.apache.jena.ext.xerces.impl.dv.util.Base64; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import org.apache.xerces.impl.dv.util.Base64; import org.dspace.app.util.XMLUtils; import org.dspace.content.Item; import org.dspace.importer.external.datamodel.ImportRecord; diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/BitstreamRestControllerIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/BitstreamRestControllerIT.java index 596e4e0c6b70..fe75c9fc518c 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/BitstreamRestControllerIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/BitstreamRestControllerIT.java @@ -57,6 +57,8 @@ import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.CharEncoding; import org.apache.commons.lang3.StringUtils; +import org.apache.pdfbox.Loader; +import org.apache.pdfbox.io.RandomAccessReadBuffer; import org.apache.pdfbox.pdmodel.PDDocument; import org.apache.pdfbox.text.PDFTextStripper; import org.apache.solr.client.solrj.SolrServerException; @@ -994,7 +996,7 @@ private String extractPDFText(byte[] content) throws IOException { try (ByteArrayInputStream source = new ByteArrayInputStream(content); Writer writer = new StringWriter(); - PDDocument pdfDoc = PDDocument.load(source)) { + PDDocument pdfDoc = Loader.loadPDF(new RandomAccessReadBuffer(source))) { pts.writeText(pdfDoc, writer); return writer.toString(); @@ -1003,7 +1005,7 @@ private String extractPDFText(byte[] content) throws IOException { private int getNumberOfPdfPages(byte[] content) throws IOException { try (ByteArrayInputStream source = new ByteArrayInputStream(content); - PDDocument pdfDoc = PDDocument.load(source)) { + PDDocument pdfDoc = Loader.loadPDF(new RandomAccessReadBuffer(source))) { return pdfDoc.getNumberOfPages(); } } From ea21c0f558e342ed01c92e17dfbb5378f67babd0 Mon Sep 17 00:00:00 2001 From: Kim Shepherd Date: Wed, 10 Dec 2025 17:56:36 +0100 Subject: [PATCH 968/979] Apply PDFBox 3.x changes to CitationDocumentServiceImpl --- .../disseminate/CitationDocumentServiceImpl.java | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/disseminate/CitationDocumentServiceImpl.java b/dspace-api/src/main/java/org/dspace/disseminate/CitationDocumentServiceImpl.java index b1441867772f..bf9f7b9d6a01 100644 --- a/dspace-api/src/main/java/org/dspace/disseminate/CitationDocumentServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/disseminate/CitationDocumentServiceImpl.java @@ -23,6 +23,8 @@ import org.apache.commons.lang3.tuple.Pair; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import org.apache.pdfbox.Loader; +import org.apache.pdfbox.io.RandomAccessReadBuffer; import org.apache.pdfbox.pdmodel.PDDocument; import org.apache.pdfbox.pdmodel.PDPage; import org.apache.pdfbox.pdmodel.PDPageContentStream; @@ -30,6 +32,7 @@ import org.apache.pdfbox.pdmodel.common.PDRectangle; import org.apache.pdfbox.pdmodel.font.PDFont; import org.apache.pdfbox.pdmodel.font.PDType1Font; +import org.apache.pdfbox.pdmodel.font.Standard14Fonts; import org.dspace.authorize.AuthorizeException; import org.dspace.authorize.service.AuthorizeService; import org.dspace.content.Bitstream; @@ -304,7 +307,7 @@ public Pair makeCitedDocument(Context context, Bitstream bitstream Item item = (Item) bitstreamService.getParentObject(context, bitstream); final InputStream inputStream = bitstreamService.retrieve(context, bitstream); try { - sourceDocument = sourceDocument.load(inputStream); + sourceDocument = Loader.loadPDF(new RandomAccessReadBuffer(inputStream)); } finally { inputStream.close(); } @@ -335,9 +338,10 @@ protected void generateCoverPage(Context context, PDDocument document, PDPage co int xwidth = 550; int ygap = 20; - PDFont fontHelvetica = PDType1Font.HELVETICA; - PDFont fontHelveticaBold = PDType1Font.HELVETICA_BOLD; - PDFont fontHelveticaOblique = PDType1Font.HELVETICA_OBLIQUE; + PDFont fontHelvetica = new PDType1Font(Standard14Fonts.FontName.HELVETICA); + PDFont fontHelveticaBold = new PDType1Font(Standard14Fonts.FontName.HELVETICA_BOLD); + PDFont fontHelveticaOblique = new PDType1Font(Standard14Fonts.FontName.HELVETICA_OBLIQUE); + contentStream.setNonStrokingColor(Color.BLACK); String[][] content = {header1}; From c3de82d3732c1ece3d61017310aee8dc8c5939f7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 6 Oct 2025 19:56:59 +0000 Subject: [PATCH 969/979] Bump tika.version from 3.2.2 to 3.2.3 Bumps `tika.version` from 3.2.2 to 3.2.3. Updates `org.apache.tika:tika-core` from 3.2.2 to 3.2.3 - [Changelog](https://github.com/apache/tika/blob/main/CHANGES.txt) - [Commits](https://github.com/apache/tika/compare/3.2.2...3.2.3) Updates `org.apache.tika:tika-parsers-standard-package` from 3.2.2 to 3.2.3 --- updated-dependencies: - dependency-name: org.apache.tika:tika-core dependency-version: 3.2.3 dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.apache.tika:tika-parsers-standard-package dependency-version: 3.2.3 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 401a83b744d6..d4e45b05cde5 100644 --- a/pom.xml +++ b/pom.xml @@ -43,7 +43,7 @@ 3.0.5 1.19.0 2.0.17 - 3.2.2 + 3.2.3 1.82 8.0.1 From 69797d83d4b27ccfc66d8554fbe69a9572ca0ca1 Mon Sep 17 00:00:00 2001 From: Mykhaylo Boychuk Date: Wed, 10 Dec 2025 15:04:16 +0100 Subject: [PATCH 970/979] [DURACOM-428] fix closed http client before reading entity (cherry picked from commit 5ac1c4a863b02bcdf7cf034d73c7aaa56952de52) --- .../impl/OrcidV3AuthorDataProvider.java | 3 + .../model/factory/OrcidFactoryUtils.java | 106 ++++++++++++------ 2 files changed, 73 insertions(+), 36 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/external/provider/impl/OrcidV3AuthorDataProvider.java b/dspace-api/src/main/java/org/dspace/external/provider/impl/OrcidV3AuthorDataProvider.java index a9e10f92948d..fad0e75b4fb7 100644 --- a/dspace-api/src/main/java/org/dspace/external/provider/impl/OrcidV3AuthorDataProvider.java +++ b/dspace-api/src/main/java/org/dspace/external/provider/impl/OrcidV3AuthorDataProvider.java @@ -89,6 +89,9 @@ public void init() throws IOException { public void initializeAccessToken() { // If we have reaches max retries or the access token is already set, return immediately if (maxClientRetries <= 0 || StringUtils.isNotBlank(accessToken)) { + if (maxClientRetries <= 0) { + log.warn("Maximum retry attempts reached for ORCID token retrieval"); + } return; } try { diff --git a/dspace-api/src/main/java/org/dspace/orcid/model/factory/OrcidFactoryUtils.java b/dspace-api/src/main/java/org/dspace/orcid/model/factory/OrcidFactoryUtils.java index ce68ab47c26e..f08aff740580 100644 --- a/dspace-api/src/main/java/org/dspace/orcid/model/factory/OrcidFactoryUtils.java +++ b/dspace-api/src/main/java/org/dspace/orcid/model/factory/OrcidFactoryUtils.java @@ -7,21 +7,29 @@ */ package org.dspace.orcid.model.factory; -import java.io.BufferedReader; +import static java.nio.charset.StandardCharsets.UTF_8; + import java.io.IOException; import java.io.InputStream; -import java.io.InputStreamReader; -import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.Base64; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.Optional; import org.apache.commons.lang3.StringUtils; import org.apache.http.HttpResponse; +import org.apache.http.NameValuePair; +import org.apache.http.client.entity.UrlEncodedFormEntity; import org.apache.http.client.methods.HttpPost; import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.message.BasicNameValuePair; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.dspace.app.client.DSpaceHttpClientFactory; import org.json.JSONObject; +import org.json.JSONTokener; /** * Utility class for Orcid factory classes. This is used to parse the @@ -29,13 +37,12 @@ * contributors and external ids configuration). * * @author Luca Giamminonni (luca.giamminonni at 4science.it) - * */ public final class OrcidFactoryUtils { - private OrcidFactoryUtils() { + private static final Logger log = LogManager.getLogger(OrcidFactoryUtils.class); - } + private OrcidFactoryUtils() { } /** * Parse the given configurations value and returns a map with metadata fields @@ -46,7 +53,7 @@ private OrcidFactoryUtils() { * @return the configurations parsing result as map */ public static Map parseConfigurations(String configurations) { - Map configurationMap = new HashMap(); + Map configurationMap = new HashMap<>(); if (StringUtils.isBlank(configurations)) { return configurationMap; } @@ -55,7 +62,6 @@ public static Map parseConfigurations(String configurations) { String[] configurationSections = parseConfiguration(configuration); configurationMap.put(configurationSections[0], configurationSections[1]); } - return configurationMap; } @@ -87,37 +93,65 @@ private static String[] parseConfiguration(String configuration) { */ public static Optional retrieveAccessToken(String clientId, String clientSecret, String oauthUrl) throws IOException { - if (StringUtils.isNotBlank(clientSecret) && StringUtils.isNotBlank(clientId) - && StringUtils.isNotBlank(oauthUrl)) { - String authenticationParameters = "?client_id=" + clientId + - "&client_secret=" + clientSecret + - "&scope=/read-public&grant_type=client_credentials"; - HttpPost httpPost = new HttpPost(oauthUrl + authenticationParameters); - httpPost.addHeader("Accept", "application/json"); - httpPost.addHeader("Content-Type", "application/x-www-form-urlencoded"); - - HttpResponse response; - try (CloseableHttpClient httpClient = DSpaceHttpClientFactory.getInstance().build()) { - response = httpClient.execute(httpPost); + if (StringUtils.isBlank(clientSecret) || StringUtils.isBlank(clientId) || StringUtils.isBlank(oauthUrl)) { + String missingParams = (StringUtils.isBlank(clientId) ? "clientId " : "") + + (StringUtils.isBlank(clientSecret) ? "clientSecret " : "") + + (StringUtils.isBlank(oauthUrl) ? "oauthUrl" : ""); + log.error("Cannot retrieve ORCID access token: missing required parameters:{} ", missingParams.trim()); + return Optional.empty(); + } + + HttpPost httpPost = new HttpPost(oauthUrl); + + String auth = clientId + ":" + clientSecret; + String encodedAuth = Base64.getEncoder().encodeToString(auth.getBytes(UTF_8)); + addHeaders(httpPost, encodedAuth); + + List params = new ArrayList<>(); + params.add(new BasicNameValuePair("grant_type", "client_credentials")); + params.add(new BasicNameValuePair("scope", "/read-public")); + httpPost.setEntity(new UrlEncodedFormEntity(params, UTF_8)); + + try (CloseableHttpClient httpClient = DSpaceHttpClientFactory.getInstance().build()) { + log.debug("Sending ORCID token request to {}", oauthUrl); + HttpResponse response = httpClient.execute(httpPost); + if (!isSuccessful(response)) { + log.error("Failed to retrieve ORCID access token"); + return Optional.empty(); } - JSONObject responseObject = null; - if (response != null && response.getStatusLine().getStatusCode() == 200) { - try (InputStream is = response.getEntity().getContent(); - BufferedReader streamReader = new BufferedReader(new InputStreamReader(is, - StandardCharsets.UTF_8))) { - String inputStr; - while ((inputStr = streamReader.readLine()) != null && responseObject == null) { - if (inputStr.startsWith("{") && inputStr.endsWith("}") && inputStr.contains("access_token")) { - responseObject = new JSONObject(inputStr); - } - } + // Parsing JSON response + try (InputStream is = response.getEntity().getContent()) { + JSONObject responseObject = new JSONObject(new JSONTokener(is)); + if (responseObject.has("access_token")) { + String token = responseObject.getString("access_token"); + log.debug("Successfully retrieved ORCID access token"); + return Optional.of(token); + } else { + log.error("ORCID response missing access_token field:{} ", responseObject); + return Optional.empty(); } } - if (responseObject != null && responseObject.has("access_token")) { - return Optional.of((String) responseObject.get("access_token")); - } } - // Return empty by default - return Optional.empty(); } + + private static void addHeaders(HttpPost httpPost, String encodedAuth) { + httpPost.addHeader("Authorization", "Basic " + encodedAuth); + httpPost.addHeader("Accept", "application/json"); + httpPost.addHeader("Content-Type", "application/x-www-form-urlencoded"); + } + + private static boolean isSuccessful(HttpResponse response) { + if (response == null) { + log.error("ORCID API request failed: null response received"); + return false; + } + int statusCode = response.getStatusLine().getStatusCode(); + if (statusCode != 200) { + var errorMsg = "ORCID API request failed with status code {}: {}"; + log.error(errorMsg, statusCode, response.getStatusLine().getReasonPhrase()); + return false; + } + return true; + } + } From 00e2d95f06d8eb7b21c7fd344d0bc1ddbd6e11ce Mon Sep 17 00:00:00 2001 From: Mykhaylo Boychuk Date: Wed, 10 Dec 2025 15:05:00 +0100 Subject: [PATCH 971/979] [DURACOM-428] improve ORCID connector (cherry picked from commit 768c669985ed106d819074c8f336f4c69cd7e568) --- .../dspace/external/OrcidRestConnector.java | 47 ++++++++++--------- 1 file changed, 24 insertions(+), 23 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/external/OrcidRestConnector.java b/dspace-api/src/main/java/org/dspace/external/OrcidRestConnector.java index aa16af7a524d..1e9688aa4ce6 100644 --- a/dspace-api/src/main/java/org/dspace/external/OrcidRestConnector.java +++ b/dspace-api/src/main/java/org/dspace/external/OrcidRestConnector.java @@ -9,13 +9,13 @@ import java.io.ByteArrayInputStream; import java.io.InputStream; -import java.nio.charset.StandardCharsets; -import java.util.Scanner; import org.apache.commons.lang3.StringUtils; +import org.apache.http.HttpResponse; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpGet; import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.util.EntityUtils; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.dspace.app.client.DSpaceHttpClientFactory; @@ -28,9 +28,6 @@ */ public class OrcidRestConnector { - /** - * log4j logger - */ private static final Logger log = LogManager.getLogger(OrcidRestConnector.class); private final String url; @@ -40,32 +37,31 @@ public OrcidRestConnector(String url) { } public InputStream get(String path, String accessToken) { - CloseableHttpResponse getResponse = null; - InputStream result = null; - path = trimSlashes(path); - - String fullPath = url + '/' + path; + String fullPath = url + '/' + trimSlashes(path); HttpGet httpGet = new HttpGet(fullPath); if (StringUtils.isNotBlank(accessToken)) { httpGet.addHeader("Content-Type", "application/vnd.orcid+xml"); httpGet.addHeader("Authorization","Bearer " + accessToken); } try (CloseableHttpClient httpClient = DSpaceHttpClientFactory.getInstance().build()) { - getResponse = httpClient.execute(httpGet); - try (InputStream responseStream = getResponse.getEntity().getContent()) { + CloseableHttpResponse httpResponse = httpClient.execute(httpGet); + if (!isSuccessful(httpResponse)) { + // Consume entity to avoid memory leak + EntityUtils.consumeQuietly(httpResponse.getEntity()); + var statusCode = getStatusCode(httpResponse); + var reason = httpResponse.getStatusLine().getReasonPhrase(); + var error = String.format("The request failed with:%d code, reason:%s ", statusCode, reason); + throw new RuntimeException(error); + } + try (InputStream responseStream = httpResponse.getEntity().getContent()) { // Read all the content of the response stream into a byte array to prevent TruncatedChunkException byte[] content = responseStream.readAllBytes(); - result = new ByteArrayInputStream(content); + return new ByteArrayInputStream(content); } } catch (Exception e) { - getGotError(e, fullPath); + log.error("Error in rest connector for path: " + fullPath, e); + throw new RuntimeException("Failed to execute ORCID request: " + fullPath, e); } - - return result; - } - - protected void getGotError(Exception e, String fullPath) { - log.error("Error in rest connector for path: " + fullPath, e); } public static String trimSlashes(String path) { @@ -78,8 +74,13 @@ public static String trimSlashes(String path) { return path; } - public static String convertStreamToString(InputStream is) { - Scanner s = new Scanner(is, StandardCharsets.UTF_8).useDelimiter("\\A"); - return s.hasNext() ? s.next() : ""; + private boolean isSuccessful(HttpResponse response) { + int statusCode = getStatusCode(response); + return statusCode >= 200 || statusCode <= 299; + } + + private int getStatusCode(HttpResponse response) { + return response.getStatusLine().getStatusCode(); } + } From 4564b8446917e9d0057cda907fb9f2e59a4a3a48 Mon Sep 17 00:00:00 2001 From: Mykhaylo Boychuk Date: Wed, 10 Dec 2025 17:30:23 +0100 Subject: [PATCH 972/979] [DURACOM-428] implemented custom exception for orcid connector (cherry picked from commit e3187bf201b82bec7ab1506ea10f6444c75e9c83) --- .../orcid/Orcidv3SolrAuthorityImpl.java | 58 ++++++++++++------- .../external/OrcidConnectionException.java | 33 +++++++++++ .../dspace/external/OrcidRestConnector.java | 34 +++++------ .../impl/OrcidV3AuthorDataProvider.java | 51 ++++++++++------ .../org/dspace/authority/orcid/MockOrcid.java | 7 ++- 5 files changed, 124 insertions(+), 59 deletions(-) create mode 100644 dspace-api/src/main/java/org/dspace/external/OrcidConnectionException.java 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 494daa97734a..312a00c146af 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/external/OrcidConnectionException.java b/dspace-api/src/main/java/org/dspace/external/OrcidConnectionException.java new file mode 100644 index 000000000000..3574045aab2b --- /dev/null +++ b/dspace-api/src/main/java/org/dspace/external/OrcidConnectionException.java @@ -0,0 +1,33 @@ +/** + * The contents of this file are subject to the license and copyright + * detailed in the LICENSE and NOTICE files at the root of the source + * tree and available online at + * + * http://www.dspace.org/license/ + */ +package org.dspace.external; + +/** + * Exception thrown when there are issues with ORCID service connections. + * + * @author Boychuk Mykhaylo (mykhaylo.boychuk@4science.com) + */ +public class OrcidConnectionException extends Exception { + + private final int statusCode; + + public OrcidConnectionException(String message, int statusCode) { + super(message); + this.statusCode = statusCode; + } + + public OrcidConnectionException(String message, int statusCode, Throwable cause) { + super(message, cause); + this.statusCode = statusCode; + } + + public int getStatusCode() { + return statusCode; + } + +} diff --git a/dspace-api/src/main/java/org/dspace/external/OrcidRestConnector.java b/dspace-api/src/main/java/org/dspace/external/OrcidRestConnector.java index 1e9688aa4ce6..3d61462cc354 100644 --- a/dspace-api/src/main/java/org/dspace/external/OrcidRestConnector.java +++ b/dspace-api/src/main/java/org/dspace/external/OrcidRestConnector.java @@ -15,7 +15,6 @@ import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpGet; import org.apache.http.impl.client.CloseableHttpClient; -import org.apache.http.util.EntityUtils; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.dspace.app.client.DSpaceHttpClientFactory; @@ -36,7 +35,7 @@ public OrcidRestConnector(String url) { this.url = url; } - public InputStream get(String path, String accessToken) { + public InputStream get(String path, String accessToken) throws OrcidConnectionException { String fullPath = url + '/' + trimSlashes(path); HttpGet httpGet = new HttpGet(fullPath); if (StringUtils.isNotBlank(accessToken)) { @@ -44,23 +43,24 @@ public InputStream get(String path, String accessToken) { httpGet.addHeader("Authorization","Bearer " + accessToken); } try (CloseableHttpClient httpClient = DSpaceHttpClientFactory.getInstance().build()) { - CloseableHttpResponse httpResponse = httpClient.execute(httpGet); - if (!isSuccessful(httpResponse)) { - // Consume entity to avoid memory leak - EntityUtils.consumeQuietly(httpResponse.getEntity()); - var statusCode = getStatusCode(httpResponse); - var reason = httpResponse.getStatusLine().getReasonPhrase(); - var error = String.format("The request failed with:%d code, reason:%s ", statusCode, reason); - throw new RuntimeException(error); - } - try (InputStream responseStream = httpResponse.getEntity().getContent()) { - // Read all the content of the response stream into a byte array to prevent TruncatedChunkException - byte[] content = responseStream.readAllBytes(); - return new ByteArrayInputStream(content); + try (CloseableHttpResponse httpResponse = httpClient.execute(httpGet)) { + if (!isSuccessful(httpResponse)) { + var statusCode = getStatusCode(httpResponse); + var reason = httpResponse.getStatusLine().getReasonPhrase(); + var error = String.format("The request failed with:%d code, reason:%s ", statusCode, reason); + throw new OrcidConnectionException(error, statusCode); + } + try (InputStream responseStream = httpResponse.getEntity().getContent()) { + // Read all the content of the response stream into a byte array to prevent TruncatedChunkException + byte[] content = responseStream.readAllBytes(); + return new ByteArrayInputStream(content); + } } + } catch (OrcidConnectionException e) { + throw e; } catch (Exception e) { log.error("Error in rest connector for path: " + fullPath, e); - throw new RuntimeException("Failed to execute ORCID request: " + fullPath, e); + throw new OrcidConnectionException("Failed to execute ORCID request: " + fullPath, 0, e); } } @@ -83,4 +83,4 @@ private int getStatusCode(HttpResponse response) { return response.getStatusLine().getStatusCode(); } -} +} \ No newline at end of file diff --git a/dspace-api/src/main/java/org/dspace/external/provider/impl/OrcidV3AuthorDataProvider.java b/dspace-api/src/main/java/org/dspace/external/provider/impl/OrcidV3AuthorDataProvider.java index fad0e75b4fb7..fe2fdfa95381 100644 --- a/dspace-api/src/main/java/org/dspace/external/provider/impl/OrcidV3AuthorDataProvider.java +++ b/dspace-api/src/main/java/org/dspace/external/provider/impl/OrcidV3AuthorDataProvider.java @@ -21,6 +21,7 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.dspace.content.dto.MetadataValueDTO; +import org.dspace.external.OrcidConnectionException; import org.dspace.external.OrcidRestConnector; import org.dspace.external.model.ExternalDataObject; import org.dspace.external.provider.AbstractExternalDataProvider; @@ -171,8 +172,14 @@ public Person getBio(String id) { return null; } initializeAccessToken(); - InputStream bioDocument = orcidRestConnector.get(id + ((id.endsWith("/person")) ? "" : "/person"), accessToken); - return converter.convertSinglePerson(bioDocument); + try { + InputStream bioDocument = orcidRestConnector.get(id + ((id.endsWith("/person")) ? "" : "/person"), + accessToken); + return converter.convertSinglePerson(bioDocument); + } catch (OrcidConnectionException e) { + log.error("Error retrieving ORCID bio for ID=" + id, e); + return null; + } } /** @@ -203,21 +210,26 @@ public List searchExternalDataObjects(String query, int star + "&start=" + start + "&rows=" + limit; log.debug("queryBio searchPath=" + searchPath + " accessToken=" + accessToken); - InputStream bioDocument = orcidRestConnector.get(searchPath, accessToken); - 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.getPath()); - String orcid = orcidIdentifier.getPath(); - Person bio = getBio(orcid); - if (bio != null) { - bios.add(bio); + try { + InputStream bioDocument = orcidRestConnector.get(searchPath, accessToken); + 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.getPath()); + String orcid = orcidIdentifier.getPath(); + Person bio = getBio(orcid); + if (bio != null) { + bios.add(bio); + } } } + return bios.stream().map(bio -> convertToExternalDataObject(bio)).collect(Collectors.toList()); + } catch (OrcidConnectionException e) { + log.error("Error searching ORCID for query=" + query, e); + return Collections.emptyList(); } - return bios.stream().map(bio -> convertToExternalDataObject(bio)).collect(Collectors.toList()); } @Override @@ -236,8 +248,13 @@ public int getNumberOfResults(String query) { + "&start=" + 0 + "&rows=" + 0; log.debug("queryBio searchPath=" + searchPath + " accessToken=" + accessToken); - InputStream bioDocument = orcidRestConnector.get(searchPath, accessToken); - return Math.min(converter.getNumberOfResultsFromXml(bioDocument), MAX_INDEX); + try { + InputStream bioDocument = orcidRestConnector.get(searchPath, accessToken); + return Math.min(converter.getNumberOfResultsFromXml(bioDocument), MAX_INDEX); + } catch (OrcidConnectionException e) { + log.error("Error getting number of results from ORCID for query=" + query, e); + return 0; + } } @@ -299,4 +316,4 @@ public void setOrcidRestConnector(OrcidRestConnector orcidRestConnector) { this.orcidRestConnector = orcidRestConnector; } -} +} \ No newline at end of file diff --git a/dspace-api/src/test/java/org/dspace/authority/orcid/MockOrcid.java b/dspace-api/src/test/java/org/dspace/authority/orcid/MockOrcid.java index 511df79f1e50..b6be6d1f3ac2 100644 --- a/dspace-api/src/test/java/org/dspace/authority/orcid/MockOrcid.java +++ b/dspace-api/src/test/java/org/dspace/authority/orcid/MockOrcid.java @@ -11,6 +11,7 @@ import java.io.InputStream; +import org.dspace.external.OrcidConnectionException; import org.dspace.external.OrcidRestConnector; import org.mockito.ArgumentMatchers; import org.mockito.Mockito; @@ -38,7 +39,7 @@ public void init() { * Call this to set up mocking for any test classes that need it. We don't set it in init() * or other AbstractIntegrationTest implementations will complain of unnecessary Mockito stubbing */ - public void setupNoResultsSearch() { + public void setupNoResultsSearch() throws OrcidConnectionException { when(orcidRestConnector.get(ArgumentMatchers.startsWith("search?"), ArgumentMatchers.any())) .thenAnswer(new Answer() { @Override @@ -51,7 +52,7 @@ public InputStream answer(InvocationOnMock invocation) { * Call this to set up mocking for any test classes that need it. We don't set it in init() * or other AbstractIntegrationTest implementations will complain of unnecessary Mockito stubbing */ - public void setupSingleSearch() { + public void setupSingleSearch() throws OrcidConnectionException { when(orcidRestConnector.get(ArgumentMatchers.startsWith("search?q=Bollini"), ArgumentMatchers.any())) .thenAnswer(new Answer() { @Override @@ -64,7 +65,7 @@ public InputStream answer(InvocationOnMock invocation) { * Call this to set up mocking for any test classes that need it. We don't set it in init() * or other AbstractIntegrationTest implementations will complain of unnecessary Mockito stubbing */ - public void setupSearchWithResults() { + public void setupSearchWithResults() throws OrcidConnectionException { when(orcidRestConnector.get(ArgumentMatchers.endsWith("/person"), ArgumentMatchers.any())) .thenAnswer(new Answer() { @Override From 95a1ec30ac45bc6db64df4bf92b695b1865d4e39 Mon Sep 17 00:00:00 2001 From: Vincenzo Mecca Date: Fri, 28 Nov 2025 17:45:01 +0100 Subject: [PATCH 973/979] fix(authentication-method): Sets the authentication method just after the login has been concluded successfully ref: DURACOM-401 (cherry picked from commit 008998bee45e76ea5e3180dd8ec6c339b88ede51) --- .../java/org/dspace/authenticate/AuthenticationServiceImpl.java | 1 + 1 file changed, 1 insertion(+) 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 9387faeb738e..c0dde49b13a3 100644 --- a/dspace-api/src/main/java/org/dspace/authenticate/AuthenticationServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/authenticate/AuthenticationServiceImpl.java @@ -110,6 +110,7 @@ protected int authenticateInternal(Context context, } if (ret == AuthenticationMethod.SUCCESS) { updateLastActiveDate(context); + context.setAuthenticationMethod(aMethodStack.getName()); return ret; } if (ret < bestRet) { From 2b70d3a56e7268c9bc45f0a02fdfec9553e28931 Mon Sep 17 00:00:00 2001 From: Vincenzo Mecca Date: Fri, 28 Nov 2025 17:51:54 +0100 Subject: [PATCH 974/979] fix(shib-auth-groups): Fixes special-groups check inside AuthenticationMethod.java Adds some IT to verify the correctness of the authentication. ref: DURACOM-401 (cherry picked from commit cf8e7f3c43697b82a45c05d3cfc9c0c8d50ec678) --- .../authenticate/AuthenticationMethod.java | 2 +- .../rest/AuthenticationRestControllerIT.java | 96 +++++++++++++++++++ 2 files changed, 97 insertions(+), 1 deletion(-) 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 9f1c0827e979..e83d20aae890 100644 --- a/dspace-api/src/main/java/org/dspace/authenticate/AuthenticationMethod.java +++ b/dspace-api/src/main/java/org/dspace/authenticate/AuthenticationMethod.java @@ -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); } /** diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/AuthenticationRestControllerIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/AuthenticationRestControllerIT.java index 9edb0a2a9f40..b4c3e720cfc6 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/AuthenticationRestControllerIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/AuthenticationRestControllerIT.java @@ -1803,6 +1803,102 @@ private boolean tokenClaimsEqual(String token1, String token2) { } } + @Test + public void testShibbolethStaffMappedToStaffAndMembers() throws Exception { + context.turnOffAuthorisationSystem(); + + GroupBuilder.createGroup(context) + .withName("Staff") + .build(); + GroupBuilder.createGroup(context) + .withName("Member") + .build(); + + configurationService.setProperty("plugin.sequence.org.dspace.authenticate.AuthenticationMethod", SHIB_ONLY); + configurationService.setProperty("authentication-shibboleth.role.staff", "Staff, Member"); + configurationService.setProperty("authentication-shibboleth.default-roles", "staff"); + configurationService.setProperty("authentication-shibboleth.netid-header", "mail"); + configurationService.setProperty("authentication-shibboleth.email-header", "mail"); + + context.restoreAuthSystemState(); + + String shibToken = getClient().perform(post("/api/authn/login") + .requestAttr("mail", eperson.getEmail()) + .requestAttr("SHIB-SCOPED-AFFILIATION", "staff")) + .andExpect(status().isOk()) + .andReturn().getResponse().getHeader(AUTHORIZATION_HEADER).replace(AUTHORIZATION_TYPE, ""); + + getClient(shibToken).perform(get("/api/authn/status").param("projection", "full")) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.okay", is(true))) + .andExpect(jsonPath("$.authenticated", is(true))) + .andExpect(jsonPath("$.authenticationMethod", is("shibboleth"))) + .andExpect(jsonPath("$._embedded.specialGroups._embedded.specialGroups", + Matchers.containsInAnyOrder( + matchGroupWithName("Staff"), + matchGroupWithName("Member") + ) + )); + + getClient(shibToken).perform(get("/api/authn/status/specialGroups").param("projection", "full")) + .andExpect(status().isOk()) + .andExpect(jsonPath("$._embedded.specialGroups", + Matchers.containsInAnyOrder( + matchGroupWithName("Staff"), + matchGroupWithName("Member") + ) + )); + } + + @Test + public void testPasswordLoginNotMappedToStaffAndMembers() throws Exception { + context.turnOffAuthorisationSystem(); + + GroupBuilder.createGroup(context) + .withName("Staff") + .build(); + GroupBuilder.createGroup(context) + .withName("Member") + .build(); + GroupBuilder.createGroup(context) + .withName("specialGroupPwd") + .build(); + + + configurationService.setProperty("plugin.sequence.org.dspace.authenticate.AuthenticationMethod", + "org.dspace.authenticate.PasswordAuthentication, org.dspace.authenticate.ShibAuthentication"); + configurationService.setProperty("authentication-shibboleth.role.staff", "Staff, Member"); + configurationService.setProperty("authentication-shibboleth.default-roles", "staff"); + configurationService.setProperty("authentication-shibboleth.netid-header", "mail"); + configurationService.setProperty("authentication-shibboleth.email-header", "mail"); + configurationService.setProperty("authentication-password.login.specialgroup", "specialGroupPwd"); + + context.restoreAuthSystemState(); + + String passwordToken = getAuthToken(eperson.getEmail(), password); + + getClient(passwordToken).perform(get("/api/authn/status").param("projection", "full")) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.okay", is(true))) + .andExpect(jsonPath("$.authenticated", is(true))) + .andExpect(jsonPath("$.authenticationMethod", is("password"))) + .andExpect(jsonPath("$._embedded.specialGroups._embedded.specialGroups", + Matchers.containsInAnyOrder( + matchGroupWithName("specialGroupPwd") + ) + )); + + getClient(passwordToken).perform(get("/api/authn/status/specialGroups").param("projection", "full")) + .andExpect(status().isOk()) + .andExpect(jsonPath("$._embedded.specialGroups", + Matchers.containsInAnyOrder( + matchGroupWithName("specialGroupPwd") + ) + )); + } + + + private OrcidTokenResponseDTO buildOrcidTokenResponse(String orcid, String accessToken) { OrcidTokenResponseDTO token = new OrcidTokenResponseDTO(); token.setAccessToken(accessToken); From 0ba5a3389024cb310608148805ac42112136b797 Mon Sep 17 00:00:00 2001 From: Vincenzo Mecca Date: Mon, 15 Dec 2025 13:06:52 +0100 Subject: [PATCH 975/979] fix(style): Adds missing space between parameters of isUsed method ref: DURACOM-401 (cherry picked from commit 40cc61b493e0bec624416fe8aa6f9744d142b6ff) --- .../main/java/org/dspace/authenticate/AuthenticationMethod.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 e83d20aae890..a7140f244dfa 100644 --- a/dspace-api/src/main/java/org/dspace/authenticate/AuthenticationMethod.java +++ b/dspace-api/src/main/java/org/dspace/authenticate/AuthenticationMethod.java @@ -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()) || isUsed(context,request); + return getName().equals(context.getAuthenticationMethod()) || isUsed(context, request); } /** From c4a1a6378af9497de22bfdf0d165c760896fec74 Mon Sep 17 00:00:00 2001 From: Daniel Coelho Date: Fri, 19 Sep 2025 15:53:03 -0300 Subject: [PATCH 976/979] Trigger object reindex when updating eperson or eperson group of an associated resource policy (fixes #11325) (cherry picked from commit fbddb06ae2377b8f4475df27c6b371fa5358fb45) --- .../app/rest/ResourcePolicyEPersonReplaceRestController.java | 1 + .../app/rest/ResourcePolicyGroupReplaceRestController.java | 1 + 2 files changed, 2 insertions(+) diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/ResourcePolicyEPersonReplaceRestController.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/ResourcePolicyEPersonReplaceRestController.java index 3311f303ade6..b8b638c82ae4 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/ResourcePolicyEPersonReplaceRestController.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/ResourcePolicyEPersonReplaceRestController.java @@ -75,6 +75,7 @@ public ResponseEntity> replaceEPersonOfResourcePolicy(@Pa } EPerson newEPerson = (EPerson) dsoList.get(0); resourcePolicy.setEPerson(newEPerson); + resourcePolicyService.update(context, resourcePolicy); context.commit(); return ControllerUtils.toEmptyResponse(HttpStatus.NO_CONTENT); } diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/ResourcePolicyGroupReplaceRestController.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/ResourcePolicyGroupReplaceRestController.java index 2a041aba3a0a..6f08cf2cf99a 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/ResourcePolicyGroupReplaceRestController.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/ResourcePolicyGroupReplaceRestController.java @@ -75,6 +75,7 @@ public ResponseEntity> replaceGroupOfResourcePolicy(@Path Group newGroup = (Group) dsoList.get(0); resourcePolicy.setGroup(newGroup); + resourcePolicyService.update(context, resourcePolicy); context.commit(); return ControllerUtils.toEmptyResponse(HttpStatus.NO_CONTENT); } From bcf38e11840efc7835e4881bc260983aa7555a20 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Tue, 16 Dec 2025 15:51:03 -0600 Subject: [PATCH 977/979] Update LICENSES_THIRD_PARTY for 8.3 release --- LICENSES_THIRD_PARTY | 456 ++++++++++++++++++++++--------------------- 1 file changed, 237 insertions(+), 219 deletions(-) diff --git a/LICENSES_THIRD_PARTY b/LICENSES_THIRD_PARTY index 5d99bd7e426c..d6b30f3fd1f8 100644 --- a/LICENSES_THIRD_PARTY +++ b/LICENSES_THIRD_PARTY @@ -21,35 +21,34 @@ 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) + * S3Mock - Testsupport - Testcontainers (com.adobe.testing:s3mock-testcontainers:4.10.0 - https://www.github.com/adobe/S3Mock/s3mock-testsupport-reactor/s3mock-testcontainers) * Titanium JSON-LD 1.1 (JRE11) (com.apicatalog:titanium-json-ld:1.3.2 - https://github.com/filip26/titanium-json-ld) * 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.1 - https://github.com/FasterXML/java-classmate) + * Jackson-annotations (com.fasterxml.jackson.core:jackson-annotations:2.20 - https://github.com/FasterXML/jackson) + * Jackson-core (com.fasterxml.jackson.core:jackson-core:2.20.1 - https://github.com/FasterXML/jackson-core) + * jackson-databind (com.fasterxml.jackson.core:jackson-databind:2.20.1 - 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-TOML (com.fasterxml.jackson.dataformat:jackson-dataformat-toml:2.15.2 - https://github.com/FasterXML/jackson-dataformats-text) * Jackson-dataformat-YAML (com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:2.16.2 - https://github.com/FasterXML/jackson-dataformats-text) - * Jackson datatype: jdk8 (com.fasterxml.jackson.datatype:jackson-datatype-jdk8:2.19.1 - https://github.com/FasterXML/jackson-modules-java8/jackson-datatype-jdk8) - * Jackson datatype: JSR310 (com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.19.1 - https://github.com/FasterXML/jackson-modules-java8/jackson-datatype-jsr310) + * Jackson datatype: jdk8 (com.fasterxml.jackson.datatype:jackson-datatype-jdk8:2.19.4 - https://github.com/FasterXML/jackson-modules-java8/jackson-datatype-jdk8) + * Jackson datatype: JSR310 (com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.20.1 - https://github.com/FasterXML/jackson-modules-java8/jackson-datatype-jsr310) * Jackson Jakarta-RS: base (com.fasterxml.jackson.jakarta.rs:jackson-jakarta-rs-base:2.16.2 - https://github.com/FasterXML/jackson-jakarta-rs-providers/jackson-jakarta-rs-base) * Jackson Jakarta-RS: JSON (com.fasterxml.jackson.jakarta.rs:jackson-jakarta-rs-json-provider:2.16.2 - https://github.com/FasterXML/jackson-jakarta-rs-providers/jackson-jakarta-rs-json-provider) * Jackson module: Jakarta XML Bind Annotations (jakarta.xml.bind) (com.fasterxml.jackson.module:jackson-module-jakarta-xmlbind-annotations:2.16.2 - https://github.com/FasterXML/jackson-modules-base) - * Jackson-module-parameter-names (com.fasterxml.jackson.module:jackson-module-parameter-names:2.19.1 - https://github.com/FasterXML/jackson-modules-java8/jackson-module-parameter-names) + * Jackson-module-parameter-names (com.fasterxml.jackson.module:jackson-module-parameter-names:2.19.4 - https://github.com/FasterXML/jackson-modules-java8/jackson-module-parameter-names) * Java UUID Generator (com.fasterxml.uuid:java-uuid-generator:4.1.0 - https://github.com/cowtowncoder/java-uuid-generator) * Woodstox (com.fasterxml.woodstox:woodstox-core:6.5.1 - https://github.com/FasterXML/woodstox) * 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) * Caffeine cache (com.github.ben-manes.caffeine:caffeine:3.1.8 - 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.4.2 - https://github.com/docker-java/docker-java) + * docker-java-transport (com.github.docker-java:docker-java-transport:3.4.2 - https://github.com/docker-java/docker-java) + * docker-java-transport-zerodep (com.github.docker-java:docker-java-transport-zerodep:3.4.2 - 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) @@ -60,25 +59,25 @@ https://wiki.lyrasis.org/display/DSPACE/Code+Contribution+Guidelines * uri-template (com.github.java-json-tools:uri-template:0.10 - https://github.com/java-json-tools/uri-template) * JCIP Annotations under Apache License (com.github.stephenc.jcip:jcip-annotations:1.0-1 - http://stephenc.github.com/jcip-annotations) * FindBugs-jsr305 (com.google.code.findbugs:jsr305:3.0.2 - http://findbugs.sourceforge.net/) - * Gson (com.google.code.gson:gson:2.13.1 - https://github.com/google/gson) - * error-prone annotations (com.google.errorprone:error_prone_annotations:2.38.0 - https://errorprone.info/error_prone_annotations) + * Gson (com.google.code.gson:gson:2.13.2 - https://github.com/google/gson) + * error-prone annotations (com.google.errorprone:error_prone_annotations:2.42.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 ListenableFuture only (com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava - https://github.com/google/guava/listenablefuture) * 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/) * 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:9.28 - https://bitbucket.org/connect2id/nimbus-jose-jwt) * Nimbus JOSE+JWT (com.nimbusds:nimbus-jose-jwt:9.48 - 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) @@ -88,26 +87,17 @@ 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.20.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/) - * 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.21.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 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) * broker-client (eu.openaire:broker-client:1.1.2 - http://api.openaire.eu/broker/broker-client) * OpenAIRE Funders Model (eu.openaire:funders-model:2.0.0 - https://api.openaire.eu) @@ -117,10 +107,10 @@ https://wiki.lyrasis.org/display/DSPACE/Code+Contribution+Guidelines * 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) * SWORD v2 Common Server Library (forked) (io.gdcc:sword2-server:2.0.0 - https://github.com/gdcc/sword2-server) - * micrometer-commons (io.micrometer:micrometer-commons:1.14.8 - https://github.com/micrometer-metrics/micrometer) - * micrometer-core (io.micrometer:micrometer-core:1.15.1 - https://github.com/micrometer-metrics/micrometer) - * micrometer-jakarta9 (io.micrometer:micrometer-jakarta9:1.15.1 - https://github.com/micrometer-metrics/micrometer) - * micrometer-observation (io.micrometer:micrometer-observation:1.14.8 - https://github.com/micrometer-metrics/micrometer) + * micrometer-commons (io.micrometer:micrometer-commons:1.14.13 - https://github.com/micrometer-metrics/micrometer) + * micrometer-core (io.micrometer:micrometer-core:1.15.6 - https://github.com/micrometer-metrics/micrometer) + * micrometer-jakarta9 (io.micrometer:micrometer-jakarta9:1.15.6 - https://github.com/micrometer-metrics/micrometer) + * micrometer-observation (io.micrometer:micrometer-observation:1.14.13 - https://github.com/micrometer-metrics/micrometer) * Netty/Buffer (io.netty:netty-buffer:4.1.99.Final - https://netty.io/netty-buffer/) * Netty/Codec (io.netty:netty-codec:4.1.99.Final - https://netty.io/netty-codec/) * Netty/Codec/HTTP (io.netty:netty-codec-http:4.1.86.Final - https://netty.io/netty-codec-http/) @@ -168,39 +158,38 @@ https://wiki.lyrasis.org/display/DSPACE/Code+Contribution+Guidelines * Jakarta Bean Validation API (jakarta.validation:jakarta.validation-api:3.0.2 - https://beanvalidation.org) * JSR107 API and SPI (javax.cache:cache-api:1.1.1 - https://github.com/jsr107/jsr107spec) * jdbm (jdbm:jdbm:1.0 - no url defined) - * Joda-Time (joda-time:joda-time:2.12.7 - https://www.joda.org/joda-time/) + * Joda-Time (joda-time:joda-time:2.10.5 - 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.14.11 - 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.13.0 - 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) * Abdera Parser (org.apache.abdera:abdera-parser:1.1.3 - http://abdera.apache.org/abdera-parser) * 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 Commons BCEL (org.apache.bcel:bcel:6.11.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 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.13.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.13.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.5.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 Text (org.apache.commons:commons-text:1.14.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) @@ -214,13 +203,13 @@ https://wiki.lyrasis.org/display/DSPACE/Code+Contribution+Guidelines * Apache HttpCore (org.apache.httpcomponents:httpcore:4.4.16 - http://hc.apache.org/httpcomponents-core-ga) * Apache HttpClient Mime (org.apache.httpcomponents:httpmime:4.5.14 - http://hc.apache.org/httpcomponents-client-ga) * Apache HttpClient (org.apache.httpcomponents.client5:httpclient5:5.1.3 - https://hc.apache.org/httpcomponents-client-5.0.x/5.1.3/httpclient5/) - * Apache HttpClient (org.apache.httpcomponents.client5:httpclient5:5.5 - https://hc.apache.org/httpcomponents-client-5.5.x/5.5/httpclient5/) + * Apache HttpClient (org.apache.httpcomponents.client5:httpclient5:5.5.1 - https://hc.apache.org/httpcomponents-client-5.5.x/5.5.1/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/1.1 (org.apache.httpcomponents.core5:httpcore5:5.3.4 - https://hc.apache.org/httpcomponents-core-5.3.x/5.3.4/httpcore5/) + * Apache HttpComponents Core HTTP/1.1 (org.apache.httpcomponents.core5:httpcore5:5.3.6 - https://hc.apache.org/httpcomponents-core-5.3.x/5.3.6/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 HttpComponents Core HTTP/2 (org.apache.httpcomponents.core5:httpcore5-h2:5.3.4 - https://hc.apache.org/httpcomponents-core-5.3.x/5.3.4/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 HttpComponents Core HTTP/2 (org.apache.httpcomponents.core5:httpcore5-h2:5.3.6 - https://hc.apache.org/httpcomponents-core-5.3.x/5.3.6/httpcore5-h2/) + * Apache James :: Mime4j :: Core (org.apache.james:apache-mime4j-core:0.8.13 - 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:4.10.0 - https://jena.apache.org/apache-jena-libs/) * Apache Jena - ARQ (org.apache.jena:jena-arq:4.10.0 - https://jena.apache.org/jena-arq/) * Apache Jena - Base (org.apache.jena:jena-base:4.10.0 - https://jena.apache.org/jena-base/) @@ -242,12 +231,12 @@ https://wiki.lyrasis.org/display/DSPACE/Code+Contribution+Guidelines * 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.17.2 - https://logging.apache.org/log4j/2.x/log4j-1.2-api/) - * Apache Log4j API (org.apache.logging.log4j:log4j-api:2.24.3 - https://logging.apache.org/log4j/2.x/log4j/log4j-api/) - * Apache Log4j Core (org.apache.logging.log4j:log4j-core:2.24.3 - https://logging.apache.org/log4j/2.x/log4j/log4j-core/) + * Apache Log4j API (org.apache.logging.log4j:log4j-api:2.25.2 - https://logging.apache.org/log4j/2.x/) + * Apache Log4j Core (org.apache.logging.log4j:log4j-core:2.25.2 - https://logging.apache.org/log4j/2.x/) * Apache Log4j JUL Adapter (org.apache.logging.log4j:log4j-jul:2.24.3 - https://logging.apache.org/log4j/2.x/log4j/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/) - * SLF4J 2 Provider for Log4j API (org.apache.logging.log4j:log4j-slf4j2-impl:2.24.3 - https://logging.apache.org/log4j/2.x/log4j/log4j-slf4j2-impl/) + * SLF4J 2 Provider for Log4j API (org.apache.logging.log4j:log4j-slf4j2-impl:2.25.2 - https://logging.apache.org/log4j/2.x/) * Apache Log4j Web (org.apache.logging.log4j:log4j-web:2.17.2 - https://logging.apache.org/log4j/2.x/log4j-web/) * Lucene Common Analyzers (org.apache.lucene:lucene-analyzers-common:8.11.4 - https://lucene.apache.org/lucene-parent/lucene-analyzers-common) * Lucene ICU Analysis Components (org.apache.lucene:lucene-analyzers-icu:8.11.4 - https://lucene.apache.org/lucene-parent/lucene-analyzers-icu) @@ -272,12 +261,13 @@ 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.5 - 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 PDFBox (org.apache.pdfbox:pdfbox:3.0.5 - https://www.apache.org/pdfbox-parent/pdfbox/) + * Apache PDFBox io (org.apache.pdfbox:pdfbox-io:3.0.5 - https://www.apache.org/pdfbox-parent/pdfbox-io/) + * Apache PDFBox tools (org.apache.pdfbox:pdfbox-tools:3.0.5 - https://www.apache.org/pdfbox-parent/pdfbox-tools/) + * Apache XmpBox (org.apache.pdfbox:xmpbox:3.0.5 - 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/) @@ -287,33 +277,33 @@ https://wiki.lyrasis.org/display/DSPACE/Code+Contribution+Guidelines * 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.19.0 - 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/) - * tomcat-embed-core (org.apache.tomcat.embed:tomcat-embed-core:10.1.42 - https://tomcat.apache.org/) - * tomcat-embed-el (org.apache.tomcat.embed:tomcat-embed-el:10.1.42 - https://tomcat.apache.org/) - * tomcat-embed-websocket (org.apache.tomcat.embed:tomcat-embed-websocket:10.1.42 - https://tomcat.apache.org/) + * Apache Tika core (org.apache.tika:tika-core:3.2.3 - https://tika.apache.org/) + * Apache Tika Apple parser module (org.apache.tika:tika-parser-apple-module:3.2.3 - https://tika.apache.org/tika-parser-apple-module/) + * Apache Tika audiovideo parser module (org.apache.tika:tika-parser-audiovideo-module:3.2.3 - https://tika.apache.org/tika-parser-audiovideo-module/) + * Apache Tika cad parser module (org.apache.tika:tika-parser-cad-module:3.2.3 - https://tika.apache.org/tika-parser-cad-module/) + * Apache Tika code parser module (org.apache.tika:tika-parser-code-module:3.2.3 - https://tika.apache.org/tika-parser-code-module/) + * Apache Tika crypto parser module (org.apache.tika:tika-parser-crypto-module:3.2.3 - https://tika.apache.org/tika-parser-crypto-module/) + * Apache Tika digest commons (org.apache.tika:tika-parser-digest-commons:3.2.3 - https://tika.apache.org/tika-parser-digest-commons/) + * Apache Tika font parser module (org.apache.tika:tika-parser-font-module:3.2.3 - https://tika.apache.org/tika-parser-font-module/) + * Apache Tika html parser module (org.apache.tika:tika-parser-html-module:3.2.3 - https://tika.apache.org/tika-parser-html-module/) + * Apache Tika image parser module (org.apache.tika:tika-parser-image-module:3.2.3 - https://tika.apache.org/tika-parser-image-module/) + * Apache Tika mail commons (org.apache.tika:tika-parser-mail-commons:3.2.3 - https://tika.apache.org/tika-parser-mail-commons/) + * Apache Tika mail parser module (org.apache.tika:tika-parser-mail-module:3.2.3 - https://tika.apache.org/tika-parser-mail-module/) + * Apache Tika Microsoft parser module (org.apache.tika:tika-parser-microsoft-module:3.2.3 - https://tika.apache.org/tika-parser-microsoft-module/) + * Apache Tika miscellaneous office format parser module (org.apache.tika:tika-parser-miscoffice-module:3.2.3 - https://tika.apache.org/tika-parser-miscoffice-module/) + * Apache Tika news parser module (org.apache.tika:tika-parser-news-module:3.2.3 - https://tika.apache.org/tika-parser-news-module/) + * Apache Tika OCR parser module (org.apache.tika:tika-parser-ocr-module:3.2.3 - https://tika.apache.org/tika-parser-ocr-module/) + * Apache Tika PDF parser module (org.apache.tika:tika-parser-pdf-module:3.2.3 - https://tika.apache.org/tika-parser-pdf-module/) + * Apache Tika package parser module (org.apache.tika:tika-parser-pkg-module:3.2.3 - https://tika.apache.org/tika-parser-pkg-module/) + * Apache Tika text parser module (org.apache.tika:tika-parser-text-module:3.2.3 - https://tika.apache.org/tika-parser-text-module/) + * Apache Tika WARC parser module (org.apache.tika:tika-parser-webarchive-module:3.2.3 - https://tika.apache.org/tika-parser-webarchive-module/) + * Apache Tika XML parser module (org.apache.tika:tika-parser-xml-module:3.2.3 - https://tika.apache.org/tika-parser-xml-module/) + * Apache Tika XMP commons (org.apache.tika:tika-parser-xmp-commons:3.2.3 - https://tika.apache.org/tika-parser-xmp-commons/) + * Apache Tika ZIP commons (org.apache.tika:tika-parser-zip-commons:3.2.3 - https://tika.apache.org/tika-parser-zip-commons/) + * Apache Tika standard parser package (org.apache.tika:tika-parsers-standard-package:3.2.3 - https://tika.apache.org/tika-parsers/tika-parsers-standard/tika-parsers-standard-package/) + * tomcat-embed-core (org.apache.tomcat.embed:tomcat-embed-core:10.1.49 - https://tomcat.apache.org/) + * tomcat-embed-el (org.apache.tomcat.embed:tomcat-embed-el:10.1.49 - https://tomcat.apache.org/) + * tomcat-embed-websocket (org.apache.tomcat.embed:tomcat-embed-websocket:10.1.49 - https://tomcat.apache.org/) * Apache Velocity - Engine (org.apache.velocity:velocity-engine-core:2.4.1 - http://velocity.apache.org/engine/devel/velocity-engine-core/) * Apache Velocity - JSR 223 Scripting (org.apache.velocity:velocity-engine-scripting:2.3 - http://velocity.apache.org/engine/devel/velocity-engine-scripting/) * Apache Velocity Tools - Generic tools (org.apache.velocity.tools:velocity-tools-generic:3.1 - https://velocity.apache.org/tools/devel/velocity-tools-generic/) @@ -323,12 +313,11 @@ https://wiki.lyrasis.org/display/DSPACE/Code+Contribution+Guidelines * Apache ZooKeeper - Server (org.apache.zookeeper:zookeeper:3.6.2 - http://zookeeper.apache.org/zookeeper) * Apache ZooKeeper - Jute (org.apache.zookeeper:zookeeper-jute:3.6.2 - http://zookeeper.apache.org/zookeeper-jute) * org.apiguardian:apiguardian-api (org.apiguardian:apiguardian-api:1.1.2 - https://github.com/apiguardian-team/apiguardian) - * AssertJ Core (org.assertj:assertj-core:3.27.3 - https://assertj.github.io/doc/#assertj-core) + * AssertJ Core (org.assertj:assertj-core:3.27.6 - https://assertj.github.io/doc/#assertj-core) * Evo Inflector (org.atteo:evo-inflector:1.3 - http://atteo.org/static/evo-inflector) * attoparser (org.attoparser:attoparser:2.0.7.RELEASE - https://www.attoparser.org) * Awaitility (org.awaitility:awaitility:4.2.2 - http://awaitility.org) * 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/) * Woodstox (org.codehaus.woodstox:wstx-asl:3.2.6 - http://woodstox.codehaus.org) * 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) @@ -344,122 +333,145 @@ https://wiki.lyrasis.org/display/DSPACE/Code+Contribution+Guidelines * 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.15.v20190215 - http://www.eclipse.org/jetty) * Jetty :: Continuation (org.eclipse.jetty:jetty-continuation:9.4.53.v20231009 - https://eclipse.org/jetty/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 :: 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 :: 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.15.v20190215 - http://www.eclipse.org/jetty) * Jetty :: Utility Servlets and Filters (org.eclipse.jetty:jetty-servlets:9.4.53.v20231009 - https://eclipse.org/jetty/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 :: 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.53.v20231009 - https://eclipse.org/jetty/jetty-xml) - * Jetty :: XML utilities (org.eclipse.jetty:jetty-xml:9.4.57.v20241219 - https://jetty.org/jetty-xml/) + * Jetty :: XML utilities (org.eclipse.jetty:jetty-xml:9.4.58.v20250814 - https://jetty.org/jetty-xml/) * Jetty :: ALPN :: API (org.eclipse.jetty.alpn:alpn-api:1.1.3.v20160715 - http://www.eclipse.org/jetty/alpn-api) * 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.15.v20190215 - https://eclipse.org/jetty/http2-parent/http2-server) * Jetty :: HTTP2 :: Server (org.eclipse.jetty.http2:http2-server:9.4.53.v20231009 - https://eclipse.org/jetty/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:10.22.0 - https://flywaydb.org/flyway-core) * flyway-database-postgresql (org.flywaydb:flyway-database-postgresql:10.22.0 - https://flywaydb.org/flyway-database-postgresql) * 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:3.1.10 - https://projects.eclipse.org/projects/ee4j.jersey/jersey-client) - * jersey-core-common (org.glassfish.jersey.core:jersey-common:3.1.10 - https://projects.eclipse.org/projects/ee4j.jersey/jersey-common) - * jersey-inject-hk2 (org.glassfish.jersey.inject:jersey-hk2:3.1.10 - https://projects.eclipse.org/projects/ee4j.jersey/project/jersey-hk2) + * jersey-core-client (org.glassfish.jersey.core:jersey-client:3.1.11 - https://projects.eclipse.org/projects/ee4j.jersey/jersey-client) + * jersey-core-common (org.glassfish.jersey.core:jersey-common:3.1.11 - https://projects.eclipse.org/projects/ee4j.jersey/jersey-common) + * jersey-inject-hk2 (org.glassfish.jersey.inject:jersey-hk2:3.1.11 - https://projects.eclipse.org/projects/ee4j.jersey/project/jersey-hk2) * jersey-media-multipart (org.glassfish.jersey.media:jersey-media-multipart:3.1.3 - https://projects.eclipse.org/projects/ee4j.jersey/project/jersey-media-multipart) - * Hibernate Validator Engine (org.hibernate.validator:hibernate-validator:8.0.2.Final - http://hibernate.org/validator/hibernate-validator) - * Hibernate Validator Portable Extension (org.hibernate.validator:hibernate-validator-cdi:8.0.2.Final - http://hibernate.org/validator/hibernate-validator-cdi) + * Hibernate Validator Engine (org.hibernate.validator:hibernate-validator:8.0.3.Final - https://hibernate.org/validator) + * Hibernate Validator Portable Extension (org.hibernate.validator:hibernate-validator-cdi:8.0.3.Final - https://hibernate.org/validator) * 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/) * JBoss Logging 3 (org.jboss.logging:jboss-logging:3.6.1.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/) * Proj4J (org.locationtech.proj4j:proj4j:1.1.5 - https://github.com/locationtech/proj4j) * Spatial4J (org.locationtech.spatial4j:spatial4j:0.7 - https://projects.eclipse.org/projects/locationtech.spatial4j) * MockServer Java Client (org.mock-server:mockserver-client-java:5.15.0 - https://www.mock-server.com) * MockServer Core (org.mock-server:mockserver-core:5.15.0 - https://www.mock-server.com) * MockServer JUnit 4 Integration (org.mock-server:mockserver-junit-rule:5.15.0 - https://www.mock-server.com) * MockServer & Proxy Netty (org.mock-server:mockserver-netty:5.15.0 - https://www.mock-server.com) - * jwarc (org.netpreserve:jwarc:0.31.1 - https://github.com/iipc/jwarc) + * jwarc (org.netpreserve:jwarc:0.32.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) * org.roaringbitmap:RoaringBitmap (org.roaringbitmap:RoaringBitmap:1.0.0 - https://github.com/RoaringBitmap/RoaringBitmap) * RRD4J (org.rrd4j:rrd4j:3.5 - https://github.com/rrd4j/rrd4j/) - * Scala Library (org.scala-lang:scala-library:2.13.2 - 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.3 - https://github.com/skyscreamer/JSONassert) * JCL 1.2 implemented over SLF4J (org.slf4j:jcl-over-slf4j:2.0.17 - http://www.slf4j.org) - * Spring AOP (org.springframework:spring-aop:6.2.8 - https://github.com/spring-projects/spring-framework) - * Spring Beans (org.springframework:spring-beans:6.2.8 - https://github.com/spring-projects/spring-framework) - * Spring Context (org.springframework:spring-context:6.2.8 - https://github.com/spring-projects/spring-framework) - * Spring Context Support (org.springframework:spring-context-support:6.2.8 - https://github.com/spring-projects/spring-framework) - * Spring Core (org.springframework:spring-core:6.2.8 - https://github.com/spring-projects/spring-framework) - * Spring Expression Language (SpEL) (org.springframework:spring-expression:6.2.8 - https://github.com/spring-projects/spring-framework) - * Spring Commons Logging Bridge (org.springframework:spring-jcl:6.2.8 - https://github.com/spring-projects/spring-framework) - * Spring JDBC (org.springframework:spring-jdbc:6.2.8 - https://github.com/spring-projects/spring-framework) - * Spring Object/Relational Mapping (org.springframework:spring-orm:6.2.8 - https://github.com/spring-projects/spring-framework) - * Spring TestContext Framework (org.springframework:spring-test:6.2.8 - https://github.com/spring-projects/spring-framework) - * Spring Transaction (org.springframework:spring-tx:6.2.8 - https://github.com/spring-projects/spring-framework) - * Spring Web (org.springframework:spring-web:6.2.8 - https://github.com/spring-projects/spring-framework) - * Spring Web MVC (org.springframework:spring-webmvc:6.2.8 - https://github.com/spring-projects/spring-framework) - * spring-boot (org.springframework.boot:spring-boot:3.5.3 - https://spring.io/projects/spring-boot) - * spring-boot-actuator (org.springframework.boot:spring-boot-actuator:3.5.3 - https://spring.io/projects/spring-boot) - * spring-boot-actuator-autoconfigure (org.springframework.boot:spring-boot-actuator-autoconfigure:3.5.3 - https://spring.io/projects/spring-boot) - * spring-boot-autoconfigure (org.springframework.boot:spring-boot-autoconfigure:3.5.3 - https://spring.io/projects/spring-boot) - * spring-boot-starter (org.springframework.boot:spring-boot-starter:3.5.3 - https://spring.io/projects/spring-boot) - * spring-boot-starter-actuator (org.springframework.boot:spring-boot-starter-actuator:3.5.3 - https://spring.io/projects/spring-boot) - * spring-boot-starter-aop (org.springframework.boot:spring-boot-starter-aop:3.5.3 - https://spring.io/projects/spring-boot) - * spring-boot-starter-cache (org.springframework.boot:spring-boot-starter-cache:3.5.3 - https://spring.io/projects/spring-boot) - * spring-boot-starter-data-rest (org.springframework.boot:spring-boot-starter-data-rest:3.5.3 - https://spring.io/projects/spring-boot) - * spring-boot-starter-json (org.springframework.boot:spring-boot-starter-json:3.5.3 - https://spring.io/projects/spring-boot) - * spring-boot-starter-log4j2 (org.springframework.boot:spring-boot-starter-log4j2:3.5.3 - https://spring.io/projects/spring-boot) - * spring-boot-starter-security (org.springframework.boot:spring-boot-starter-security:3.5.3 - https://spring.io/projects/spring-boot) - * spring-boot-starter-test (org.springframework.boot:spring-boot-starter-test:3.5.3 - https://spring.io/projects/spring-boot) - * spring-boot-starter-thymeleaf (org.springframework.boot:spring-boot-starter-thymeleaf:3.5.3 - https://spring.io/projects/spring-boot) - * spring-boot-starter-tomcat (org.springframework.boot:spring-boot-starter-tomcat:3.5.3 - https://spring.io/projects/spring-boot) - * spring-boot-starter-web (org.springframework.boot:spring-boot-starter-web:3.5.3 - https://spring.io/projects/spring-boot) - * spring-boot-test (org.springframework.boot:spring-boot-test:3.5.3 - https://spring.io/projects/spring-boot) - * spring-boot-test-autoconfigure (org.springframework.boot:spring-boot-test-autoconfigure:3.5.3 - https://spring.io/projects/spring-boot) - * Spring Data Core (org.springframework.data:spring-data-commons:3.5.1 - https://spring.io/projects/spring-data) - * Spring Data REST - Core (org.springframework.data:spring-data-rest-core:4.5.1 - https://www.spring.io/spring-data/spring-data-rest-parent/spring-data-rest-core) - * Spring Data REST - WebMVC (org.springframework.data:spring-data-rest-webmvc:4.5.1 - https://www.spring.io/spring-data/spring-data-rest-parent/spring-data-rest-webmvc) + * Spring AOP (org.springframework:spring-aop:6.2.14 - https://github.com/spring-projects/spring-framework) + * Spring Beans (org.springframework:spring-beans:6.2.14 - https://github.com/spring-projects/spring-framework) + * Spring Context (org.springframework:spring-context:6.2.14 - https://github.com/spring-projects/spring-framework) + * Spring Context Support (org.springframework:spring-context-support:6.2.14 - https://github.com/spring-projects/spring-framework) + * Spring Core (org.springframework:spring-core:6.2.14 - https://github.com/spring-projects/spring-framework) + * Spring Expression Language (SpEL) (org.springframework:spring-expression:6.2.14 - https://github.com/spring-projects/spring-framework) + * Spring Commons Logging Bridge (org.springframework:spring-jcl:6.2.14 - https://github.com/spring-projects/spring-framework) + * Spring JDBC (org.springframework:spring-jdbc:6.2.14 - https://github.com/spring-projects/spring-framework) + * Spring Object/Relational Mapping (org.springframework:spring-orm:6.2.14 - https://github.com/spring-projects/spring-framework) + * Spring TestContext Framework (org.springframework:spring-test:6.2.14 - https://github.com/spring-projects/spring-framework) + * Spring Transaction (org.springframework:spring-tx:6.2.14 - https://github.com/spring-projects/spring-framework) + * Spring Web (org.springframework:spring-web:6.2.14 - https://github.com/spring-projects/spring-framework) + * Spring Web MVC (org.springframework:spring-webmvc:6.2.14 - https://github.com/spring-projects/spring-framework) + * spring-boot (org.springframework.boot:spring-boot:3.5.8 - https://spring.io/projects/spring-boot) + * spring-boot-actuator (org.springframework.boot:spring-boot-actuator:3.5.8 - https://spring.io/projects/spring-boot) + * spring-boot-actuator-autoconfigure (org.springframework.boot:spring-boot-actuator-autoconfigure:3.5.8 - https://spring.io/projects/spring-boot) + * spring-boot-autoconfigure (org.springframework.boot:spring-boot-autoconfigure:3.5.8 - https://spring.io/projects/spring-boot) + * spring-boot-starter (org.springframework.boot:spring-boot-starter:3.5.8 - https://spring.io/projects/spring-boot) + * spring-boot-starter-actuator (org.springframework.boot:spring-boot-starter-actuator:3.5.8 - https://spring.io/projects/spring-boot) + * spring-boot-starter-aop (org.springframework.boot:spring-boot-starter-aop:3.5.8 - https://spring.io/projects/spring-boot) + * spring-boot-starter-cache (org.springframework.boot:spring-boot-starter-cache:3.5.8 - https://spring.io/projects/spring-boot) + * spring-boot-starter-data-rest (org.springframework.boot:spring-boot-starter-data-rest:3.5.8 - https://spring.io/projects/spring-boot) + * spring-boot-starter-json (org.springframework.boot:spring-boot-starter-json:3.5.8 - https://spring.io/projects/spring-boot) + * spring-boot-starter-log4j2 (org.springframework.boot:spring-boot-starter-log4j2:3.5.8 - https://spring.io/projects/spring-boot) + * spring-boot-starter-security (org.springframework.boot:spring-boot-starter-security:3.5.8 - https://spring.io/projects/spring-boot) + * spring-boot-starter-test (org.springframework.boot:spring-boot-starter-test:3.5.8 - https://spring.io/projects/spring-boot) + * spring-boot-starter-thymeleaf (org.springframework.boot:spring-boot-starter-thymeleaf:3.5.8 - https://spring.io/projects/spring-boot) + * spring-boot-starter-tomcat (org.springframework.boot:spring-boot-starter-tomcat:3.5.8 - https://spring.io/projects/spring-boot) + * spring-boot-starter-web (org.springframework.boot:spring-boot-starter-web:3.5.8 - https://spring.io/projects/spring-boot) + * spring-boot-test (org.springframework.boot:spring-boot-test:3.5.8 - https://spring.io/projects/spring-boot) + * spring-boot-test-autoconfigure (org.springframework.boot:spring-boot-test-autoconfigure:3.5.8 - https://spring.io/projects/spring-boot) + * Spring Data Core (org.springframework.data:spring-data-commons:3.5.6 - https://spring.io/projects/spring-data) + * Spring Data REST - Core (org.springframework.data:spring-data-rest-core:4.5.6 - https://www.spring.io/spring-data/spring-data-rest-parent/spring-data-rest-core) + * Spring Data REST - WebMVC (org.springframework.data:spring-data-rest-webmvc:4.5.6 - https://www.spring.io/spring-data/spring-data-rest-parent/spring-data-rest-webmvc) * Spring HATEOAS (org.springframework.hateoas:spring-hateoas:2.5.1 - https://github.com/spring-projects/spring-hateoas) * Spring Plugin - Core (org.springframework.plugin:spring-plugin-core:3.0.0 - https://github.com/spring-projects/spring-plugin/spring-plugin-core) - * spring-security-config (org.springframework.security:spring-security-config:6.5.1 - https://spring.io/projects/spring-security) - * spring-security-core (org.springframework.security:spring-security-core:6.5.1 - https://spring.io/projects/spring-security) - * spring-security-crypto (org.springframework.security:spring-security-crypto:6.5.1 - https://spring.io/projects/spring-security) - * spring-security-test (org.springframework.security:spring-security-test:6.5.1 - https://spring.io/projects/spring-security) - * spring-security-web (org.springframework.security:spring-security-web:6.5.1 - https://spring.io/projects/spring-security) + * spring-security-config (org.springframework.security:spring-security-config:6.5.7 - https://spring.io/projects/spring-security) + * spring-security-core (org.springframework.security:spring-security-core:6.5.7 - https://spring.io/projects/spring-security) + * spring-security-crypto (org.springframework.security:spring-security-crypto:6.5.7 - https://spring.io/projects/spring-security) + * spring-security-test (org.springframework.security:spring-security-test:6.5.7 - https://spring.io/projects/spring-security) + * spring-security-web (org.springframework.security:spring-security-web:6.5.7 - https://spring.io/projects/spring-security) * thymeleaf (org.thymeleaf:thymeleaf:3.1.3.RELEASE - http://www.thymeleaf.org/thymeleaf-lib/thymeleaf) * thymeleaf-spring6 (org.thymeleaf:thymeleaf-spring6:3.1.3.RELEASE - http://www.thymeleaf.org/thymeleaf-lib/thymeleaf-spring6) * unbescape (org.unbescape:unbescape:1.1.6.RELEASE - http://www.unbescape.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.10.4 - https://www.xmlunit.org/) + * org.xmlunit:xmlunit-core (org.xmlunit:xmlunit-core:2.11.0 - 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:2.4 - https://bitbucket.org/snakeyaml/snakeyaml) + * AWS Java SDK :: Annotations (software.amazon.awssdk:annotations:2.38.8 - https://aws.amazon.com/sdkforjava/core/annotations) + * AWS Java SDK :: Arns (software.amazon.awssdk:arns:2.38.8 - https://aws.amazon.com/sdkforjava) + * AWS Java SDK :: Auth (software.amazon.awssdk:auth:2.38.8 - https://aws.amazon.com/sdkforjava) + * AWS Java SDK :: AWS Core (software.amazon.awssdk:aws-core:2.38.8 - https://aws.amazon.com/sdkforjava) + * AWS Java SDK :: Core :: Protocols :: AWS Query Protocol (software.amazon.awssdk:aws-query-protocol:2.38.8 - https://aws.amazon.com/sdkforjava) + * AWS Java SDK :: Core :: Protocols :: AWS Xml Protocol (software.amazon.awssdk:aws-xml-protocol:2.38.8 - https://aws.amazon.com/sdkforjava) + * AWS Java SDK :: Checksums (software.amazon.awssdk:checksums:2.38.8 - https://aws.amazon.com/sdkforjava) + * AWS Java SDK :: Checksums SPI (software.amazon.awssdk:checksums-spi:2.38.8 - https://aws.amazon.com/sdkforjava) + * AWS Java SDK :: AWS CRT Core (software.amazon.awssdk:crt-core:2.38.8 - https://aws.amazon.com/sdkforjava) + * AWS Java SDK :: Endpoints SPI (software.amazon.awssdk:endpoints-spi:2.38.8 - https://aws.amazon.com/sdkforjava/core/endpoints-spi) + * AWS Java SDK :: HTTP Auth (software.amazon.awssdk:http-auth:2.38.8 - https://aws.amazon.com/sdkforjava) + * AWS Java SDK :: HTTP Auth AWS (software.amazon.awssdk:http-auth-aws:2.38.8 - https://aws.amazon.com/sdkforjava) + * AWS Java SDK :: HTTP Auth Event Stream (software.amazon.awssdk:http-auth-aws-eventstream:2.38.8 - https://aws.amazon.com/sdkforjava) + * AWS Java SDK :: HTTP Auth SPI (software.amazon.awssdk:http-auth-spi:2.38.8 - https://aws.amazon.com/sdkforjava) + * AWS Java SDK :: HTTP Client Interface (software.amazon.awssdk:http-client-spi:2.38.8 - https://aws.amazon.com/sdkforjava/http-client-spi) + * AWS Java SDK :: Identity SPI (software.amazon.awssdk:identity-spi:2.38.8 - https://aws.amazon.com/sdkforjava) + * AWS Java SDK :: Core :: Protocols :: Json Utils (software.amazon.awssdk:json-utils:2.38.8 - https://aws.amazon.com/sdkforjava) + * AWS Java SDK :: Metrics SPI (software.amazon.awssdk:metrics-spi:2.38.8 - https://aws.amazon.com/sdkforjava/core/metrics-spi) + * AWS Java SDK :: Profiles (software.amazon.awssdk:profiles:2.38.8 - https://aws.amazon.com/sdkforjava) + * AWS Java SDK :: Core :: Protocols :: Protocol Core (software.amazon.awssdk:protocol-core:2.38.8 - https://aws.amazon.com/sdkforjava) + * AWS Java SDK :: Regions (software.amazon.awssdk:regions:2.38.8 - https://aws.amazon.com/sdkforjava/core/regions) + * AWS Java SDK :: Retries (software.amazon.awssdk:retries:2.38.8 - https://aws.amazon.com/sdkforjava/core/retries) + * AWS Java SDK :: Retries API (software.amazon.awssdk:retries-spi:2.38.8 - https://aws.amazon.com/sdkforjava/core/retries-spi) + * AWS Java SDK :: Services :: Amazon S3 (software.amazon.awssdk:s3:2.38.8 - https://aws.amazon.com/sdkforjava) + * AWS Java SDK :: SDK Core (software.amazon.awssdk:sdk-core:2.38.8 - https://aws.amazon.com/sdkforjava) + * AWS Java SDK :: Third Party :: Jackson-core (software.amazon.awssdk:third-party-jackson-core:2.38.8 - https://aws.amazon.com/sdkforjava) + * AWS Java SDK :: Utilities (software.amazon.awssdk:utils:2.38.8 - https://aws.amazon.com/sdkforjava/utils) + * AWS Java SDK :: Utils Lite (software.amazon.awssdk:utils-lite:2.38.8 - https://aws.amazon.com/sdkforjava) + * software.amazon.awssdk.crt:aws-crt (software.amazon.awssdk.crt:aws-crt:0.39.4 - 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/) BSD License: @@ -480,8 +492,8 @@ https://wiki.lyrasis.org/display/DSPACE/Code+Contribution+Guidelines * 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:3.1.10 - https://projects.eclipse.org/projects/ee4j.jersey/jersey-client) - * jersey-inject-hk2 (org.glassfish.jersey.inject:jersey-hk2:3.1.10 - https://projects.eclipse.org/projects/ee4j.jersey/project/jersey-hk2) + * jersey-core-client (org.glassfish.jersey.core:jersey-client:3.1.11 - https://projects.eclipse.org/projects/ee4j.jersey/jersey-client) + * jersey-inject-hk2 (org.glassfish.jersey.inject:jersey-hk2:3.1.11 - https://projects.eclipse.org/projects/ee4j.jersey/project/jersey-hk2) * jersey-media-multipart (org.glassfish.jersey.media:jersey-media-multipart:3.1.3 - https://projects.eclipse.org/projects/ee4j.jersey/project/jersey-media-multipart) * Hamcrest (org.hamcrest:hamcrest:2.2 - http://hamcrest.org/JavaHamcrest/) * Hamcrest Core (org.hamcrest:hamcrest-core:2.2 - http://hamcrest.org/JavaHamcrest/) @@ -491,23 +503,19 @@ https://wiki.lyrasis.org/display/DSPACE/Code+Contribution+Guidelines * asm-analysis (org.ow2.asm:asm-analysis:8.0.1 - http://asm.ow2.io/) * asm-commons (org.ow2.asm:asm-commons:8.0.1 - http://asm.ow2.io/) * asm-tree (org.ow2.asm:asm-tree:8.0.1 - http://asm.ow2.io/) - * PostgreSQL JDBC Driver (org.postgresql:postgresql:42.7.7 - https://jdbc.postgresql.org) + * PostgreSQL JDBC Driver (org.postgresql:postgresql:42.7.8 - 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) * 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 (no providers) (com.sun.mail:mailapi:1.6.2 - http://javaee.github.io/javamail/mailapi) * Old JAXB Core (com.sun.xml.bind:jaxb-core:2.3.0.1 - http://jaxb.java.net/jaxb-bundles/jaxb-core) * Old JAXB Runtime (com.sun.xml.bind:jaxb-impl:2.3.1 - http://jaxb.java.net/jaxb-bundles/jaxb-impl) * Jakarta Annotations API (jakarta.annotation:jakarta.annotation-api:2.1.1 - https://projects.eclipse.org/projects/ee4j.ca) - * Jakarta Mail API (jakarta.mail:jakarta.mail-api:2.1.3 - https://projects.eclipse.org/projects/ee4j/jakarta.mail-api) + * Jakarta Mail API (jakarta.mail:jakarta.mail-api:2.1.5 - https://projects.eclipse.org/projects/ee4j/jakarta.mail-api) * Jakarta Servlet (jakarta.servlet:jakarta.servlet-api:6.1.0 - https://projects.eclipse.org/projects/ee4j.servlet) * jakarta.transaction API (jakarta.transaction:jakarta.transaction-api:2.0.1 - https://projects.eclipse.org/projects/ee4j.jta) * JavaBeans Activation Framework API jar (javax.activation:javax.activation-api:1.2.0 - http://java.net/all/javax.activation-api/) @@ -516,14 +524,14 @@ https://wiki.lyrasis.org/display/DSPACE/Code+Contribution+Guidelines * 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) - * Angus Mail default provider (org.eclipse.angus:jakarta.mail:2.0.3 - http://eclipse-ee4j.github.io/angus-mail/jakarta.mail) + * Angus Mail default provider (org.eclipse.angus:jakarta.mail:2.0.5 - http://eclipse-ee4j.github.io/angus-mail/jakarta.mail) * HK2 API module (org.glassfish.hk2:hk2-api:3.0.6 - https://github.com/eclipse-ee4j/glassfish-hk2/hk2-api) * ServiceLocator Default Implementation (org.glassfish.hk2:hk2-locator:3.0.6 - https://github.com/eclipse-ee4j/glassfish-hk2/hk2-locator) * HK2 Implementation Utilities (org.glassfish.hk2:hk2-utils:3.0.6 - 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:3.0.6 - https://github.com/eclipse-ee4j/glassfish-hk2/external/aopalliance-repackaged) - * jersey-core-client (org.glassfish.jersey.core:jersey-client:3.1.10 - https://projects.eclipse.org/projects/ee4j.jersey/jersey-client) - * jersey-inject-hk2 (org.glassfish.jersey.inject:jersey-hk2:3.1.10 - https://projects.eclipse.org/projects/ee4j.jersey/project/jersey-hk2) + * jersey-core-client (org.glassfish.jersey.core:jersey-client:3.1.11 - https://projects.eclipse.org/projects/ee4j.jersey/jersey-client) + * jersey-inject-hk2 (org.glassfish.jersey.inject:jersey-hk2:3.1.11 - https://projects.eclipse.org/projects/ee4j.jersey/project/jersey-hk2) * jersey-media-multipart (org.glassfish.jersey.media:jersey-media-multipart:3.1.3 - https://projects.eclipse.org/projects/ee4j.jersey/project/jersey-media-multipart) Cordra (Version 2) License Agreement: @@ -538,17 +546,17 @@ https://wiki.lyrasis.org/display/DSPACE/Code+Contribution+Guidelines Eclipse Distribution License, Version 1.0: * istack common utility code runtime (com.sun.istack:istack-commons-runtime:4.1.2 - https://projects.eclipse.org/projects/ee4j/istack-commons/istack-commons-runtime) - * Jakarta Activation API (jakarta.activation:jakarta.activation-api:2.1.3 - https://github.com/jakartaee/jaf-api) - * Jakarta Mail API (jakarta.mail:jakarta.mail-api:2.1.3 - https://projects.eclipse.org/projects/ee4j/jakarta.mail-api) + * Jakarta Activation API (jakarta.activation:jakarta.activation-api:2.1.4 - https://github.com/jakartaee/jaf-api) + * Jakarta Mail API (jakarta.mail:jakarta.mail-api:2.1.5 - https://projects.eclipse.org/projects/ee4j/jakarta.mail-api) * Jakarta Persistence API (jakarta.persistence:jakarta.persistence-api:3.1.0 - https://github.com/eclipse-ee4j/jpa-api) - * Jakarta XML Binding API (jakarta.xml.bind:jakarta.xml.bind-api:4.0.2 - https://github.com/jakartaee/jaxb-api/jakarta.xml.bind-api) - * Angus Activation Registries (org.eclipse.angus:angus-activation:2.0.2 - https://github.com/eclipse-ee4j/angus-activation/angus-activation) - * Angus Mail default provider (org.eclipse.angus:jakarta.mail:2.0.3 - http://eclipse-ee4j.github.io/angus-mail/jakarta.mail) - * JAXB Core (org.glassfish.jaxb:jaxb-core:4.0.5 - https://eclipse-ee4j.github.io/jaxb-ri/) - * JAXB Runtime (org.glassfish.jaxb:jaxb-runtime:4.0.5 - https://eclipse-ee4j.github.io/jaxb-ri/) - * TXW2 Runtime (org.glassfish.jaxb:txw2:4.0.5 - https://eclipse-ee4j.github.io/jaxb-ri/) - * jersey-core-client (org.glassfish.jersey.core:jersey-client:3.1.10 - https://projects.eclipse.org/projects/ee4j.jersey/jersey-client) - * jersey-inject-hk2 (org.glassfish.jersey.inject:jersey-hk2:3.1.10 - https://projects.eclipse.org/projects/ee4j.jersey/project/jersey-hk2) + * Jakarta XML Binding API (jakarta.xml.bind:jakarta.xml.bind-api:4.0.4 - https://github.com/jakartaee/jaxb-api/jakarta.xml.bind-api) + * Angus Activation Registries (org.eclipse.angus:angus-activation:2.0.3 - https://github.com/eclipse-ee4j/angus-activation/angus-activation) + * Angus Mail default provider (org.eclipse.angus:jakarta.mail:2.0.5 - http://eclipse-ee4j.github.io/angus-mail/jakarta.mail) + * JAXB Core (org.glassfish.jaxb:jaxb-core:4.0.6 - https://eclipse-ee4j.github.io/jaxb-ri/) + * JAXB Runtime (org.glassfish.jaxb:jaxb-runtime:4.0.6 - https://eclipse-ee4j.github.io/jaxb-ri/) + * TXW2 Runtime (org.glassfish.jaxb:txw2:4.0.6 - https://eclipse-ee4j.github.io/jaxb-ri/) + * jersey-core-client (org.glassfish.jersey.core:jersey-client:3.1.11 - https://projects.eclipse.org/projects/ee4j.jersey/jersey-client) + * jersey-inject-hk2 (org.glassfish.jersey.inject:jersey-hk2:3.1.11 - https://projects.eclipse.org/projects/ee4j.jersey/project/jersey-hk2) * jersey-media-multipart (org.glassfish.jersey.media:jersey-media-multipart:3.1.3 - https://projects.eclipse.org/projects/ee4j.jersey/project/jersey-media-multipart) * MIME streaming extension (org.jvnet.mimepull:mimepull:1.9.15 - https://github.com/eclipse-ee4j/metro-mimepull) * org.locationtech.jts:jts-core (org.locationtech.jts:jts-core:1.19.0 - https://www.locationtech.org/projects/technology.jts/jts-modules/jts-core) @@ -557,16 +565,16 @@ 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:2.1.1 - https://projects.eclipse.org/projects/ee4j.ca) - * Jakarta Mail API (jakarta.mail:jakarta.mail-api:2.1.3 - https://projects.eclipse.org/projects/ee4j/jakarta.mail-api) + * Jakarta Mail API (jakarta.mail:jakarta.mail-api:2.1.5 - https://projects.eclipse.org/projects/ee4j/jakarta.mail-api) * Jakarta Persistence API (jakarta.persistence:jakarta.persistence-api:3.1.0 - https://github.com/eclipse-ee4j/jpa-api) * Jakarta Servlet (jakarta.servlet:jakarta.servlet-api:6.1.0 - https://projects.eclipse.org/projects/ee4j.servlet) * jakarta.transaction API (jakarta.transaction:jakarta.transaction-api:2.0.1 - https://projects.eclipse.org/projects/ee4j.jta) * Jakarta RESTful WS API (jakarta.ws.rs:jakarta.ws.rs-api:3.1.0 - https://github.com/eclipse-ee4j/jaxrs-api) * JUnit (junit:junit:4.13.2 - http://junit.org) - * AspectJ Weaver (org.aspectj:aspectjweaver:1.9.24 - https://www.eclipse.org/aspectj/) - * Angus Mail default provider (org.eclipse.angus:jakarta.mail:2.0.3 - http://eclipse-ee4j.github.io/angus-mail/jakarta.mail) + * AspectJ Weaver (org.aspectj:aspectjweaver:1.9.25 - https://www.eclipse.org/aspectj/) + * Angus Mail default provider (org.eclipse.angus:jakarta.mail:2.0.5 - http://eclipse-ee4j.github.io/angus-mail/jakarta.mail) * 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) @@ -579,27 +587,27 @@ https://wiki.lyrasis.org/display/DSPACE/Code+Contribution+Guidelines * 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.15.v20190215 - http://www.eclipse.org/jetty) * Jetty :: Continuation (org.eclipse.jetty:jetty-continuation:9.4.53.v20231009 - https://eclipse.org/jetty/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 :: 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 :: 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.15.v20190215 - http://www.eclipse.org/jetty) * Jetty :: Utility Servlets and Filters (org.eclipse.jetty:jetty-servlets:9.4.53.v20231009 - https://eclipse.org/jetty/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 :: 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.53.v20231009 - https://eclipse.org/jetty/jetty-xml) - * Jetty :: XML utilities (org.eclipse.jetty:jetty-xml:9.4.57.v20241219 - https://jetty.org/jetty-xml/) + * Jetty :: XML utilities (org.eclipse.jetty:jetty-xml:9.4.58.v20250814 - https://jetty.org/jetty-xml/) * Jetty :: ALPN :: API (org.eclipse.jetty.alpn:alpn-api:1.1.3.v20160715 - http://www.eclipse.org/jetty/alpn-api) * 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.15.v20190215 - https://eclipse.org/jetty/http2-parent/http2-server) @@ -611,13 +619,10 @@ https://wiki.lyrasis.org/display/DSPACE/Code+Contribution+Guidelines * HK2 Implementation Utilities (org.glassfish.hk2:hk2-utils:3.0.6 - 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:3.0.6 - https://github.com/eclipse-ee4j/glassfish-hk2/external/aopalliance-repackaged) - * jersey-core-client (org.glassfish.jersey.core:jersey-client:3.1.10 - https://projects.eclipse.org/projects/ee4j.jersey/jersey-client) - * jersey-core-common (org.glassfish.jersey.core:jersey-common:3.1.10 - https://projects.eclipse.org/projects/ee4j.jersey/jersey-common) - * jersey-inject-hk2 (org.glassfish.jersey.inject:jersey-hk2:3.1.10 - https://projects.eclipse.org/projects/ee4j.jersey/project/jersey-hk2) + * jersey-core-client (org.glassfish.jersey.core:jersey-client:3.1.11 - https://projects.eclipse.org/projects/ee4j.jersey/jersey-client) + * jersey-core-common (org.glassfish.jersey.core:jersey-common:3.1.11 - https://projects.eclipse.org/projects/ee4j.jersey/jersey-common) + * jersey-inject-hk2 (org.glassfish.jersey.inject:jersey-hk2:3.1.11 - https://projects.eclipse.org/projects/ee4j.jersey/project/jersey-hk2) * jersey-media-multipart (org.glassfish.jersey.media:jersey-media-multipart:3.1.3 - https://projects.eclipse.org/projects/ee4j.jersey/project/jersey-media-multipart) - * 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) @@ -642,9 +647,9 @@ https://wiki.lyrasis.org/display/DSPACE/Code+Contribution+Guidelines * 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) * Hibernate Commons Annotations (org.hibernate.common:hibernate-commons-annotations:6.0.6.Final - http://hibernate.org) - * Hibernate ORM - hibernate-core (org.hibernate.orm:hibernate-core:6.4.8.Final - https://hibernate.org/orm) - * Hibernate ORM - hibernate-jcache (org.hibernate.orm:hibernate-jcache:6.4.8.Final - https://hibernate.org/orm) - * Hibernate ORM - hibernate-jpamodelgen (org.hibernate.orm:hibernate-jpamodelgen:6.4.8.Final - https://hibernate.org/orm) + * Hibernate ORM - hibernate-core (org.hibernate.orm:hibernate-core:6.4.10.Final - https://hibernate.org/orm) + * Hibernate ORM - hibernate-jcache (org.hibernate.orm:hibernate-jcache:6.4.10.Final - https://hibernate.org/orm) + * Hibernate ORM - hibernate-jpamodelgen (org.hibernate.orm:hibernate-jpamodelgen:6.4.10.Final - https://hibernate.org/orm) * 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) @@ -661,28 +666,33 @@ 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.13.0 - https://github.com/java-native-access/jna) + MIT License: * dexx (com.github.andrewoma.dexx:collection:0.7 - https://github.com/andrewoma/dexx) - * 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) * 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.165 - 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 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/) + * Bouncy Castle JavaMail Jakarta S/MIME APIs (org.bouncycastle:bcjmail-jdk18on:1.81 - 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.82 - https://www.bouncycastle.org/download/bouncy-castle-java/) + * Bouncy Castle Provider (org.bouncycastle:bcprov-jdk18on:1.82 - https://www.bouncycastle.org/download/bouncy-castle-java/) + * Bouncy Castle ASN.1 Extension and Utility APIs (org.bouncycastle:bcutil-jdk18on:1.82 - 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.49.5 - https://checkerframework.org/) - * jersey-core-client (org.glassfish.jersey.core:jersey-client:3.1.10 - https://projects.eclipse.org/projects/ee4j.jersey/jersey-client) - * jersey-inject-hk2 (org.glassfish.jersey.inject:jersey-hk2:3.1.10 - https://projects.eclipse.org/projects/ee4j.jersey/project/jersey-hk2) + * Checker Qual (org.checkerframework:checker-qual:3.52.0 - https://checkerframework.org/) + * jersey-core-client (org.glassfish.jersey.core:jersey-client:3.1.11 - https://projects.eclipse.org/projects/ee4j.jersey/jersey-client) + * jersey-inject-hk2 (org.glassfish.jersey.inject:jersey-hk2:3.1.11 - https://projects.eclipse.org/projects/ee4j.jersey/project/jersey-hk2) * jersey-media-multipart (org.glassfish.jersey.media:jersey-media-multipart:3.1.3 - https://projects.eclipse.org/projects/ee4j.jersey/project/jersey-media-multipart) + * jsoup Java HTML Parser (org.jsoup:jsoup:1.21.2 - 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) + * Duct Tape (org.rnorth.duct-tape:duct-tape:1.0.8 - https://github.com/rnorth/duct-tape) * SLF4J API Module (org.slf4j:slf4j-api:2.0.17 - http://www.slf4j.org) + * Testcontainers Core (org.testcontainers:testcontainers:1.21.3 - 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) @@ -690,28 +700,36 @@ 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.46.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:3.1.10 - https://projects.eclipse.org/projects/ee4j.jersey/jersey-client) - * jersey-core-common (org.glassfish.jersey.core:jersey-common:3.1.10 - https://projects.eclipse.org/projects/ee4j.jersey/jersey-common) - * jersey-inject-hk2 (org.glassfish.jersey.inject:jersey-hk2:3.1.10 - https://projects.eclipse.org/projects/ee4j.jersey/project/jersey-hk2) + * jersey-core-client (org.glassfish.jersey.core:jersey-client:3.1.11 - https://projects.eclipse.org/projects/ee4j.jersey/jersey-client) + * jersey-core-common (org.glassfish.jersey.core:jersey-common:3.1.11 - https://projects.eclipse.org/projects/ee4j.jersey/jersey-common) + * jersey-inject-hk2 (org.glassfish.jersey.inject:jersey-hk2:3.1.11 - https://projects.eclipse.org/projects/ee4j.jersey/project/jersey-hk2) * jersey-media-multipart (org.glassfish.jersey.media:jersey-media-multipart:3.1.3 - https://projects.eclipse.org/projects/ee4j.jersey/project/jersey-media-multipart) * HdrHistogram (org.hdrhistogram:HdrHistogram:2.2.2 - 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.6 - https://picocli.info) + UnRar License: * Java Unrar (com.github.junrar:junrar:7.5.5 - https://github.com/junrar/junrar) @@ -722,12 +740,12 @@ https://wiki.lyrasis.org/display/DSPACE/Code+Contribution+Guidelines W3C license: - * jersey-core-client (org.glassfish.jersey.core:jersey-client:3.1.10 - https://projects.eclipse.org/projects/ee4j.jersey/jersey-client) - * jersey-inject-hk2 (org.glassfish.jersey.inject:jersey-hk2:3.1.10 - https://projects.eclipse.org/projects/ee4j.jersey/project/jersey-hk2) + * jersey-core-client (org.glassfish.jersey.core:jersey-client:3.1.11 - https://projects.eclipse.org/projects/ee4j.jersey/jersey-client) + * jersey-inject-hk2 (org.glassfish.jersey.inject:jersey-hk2:3.1.11 - https://projects.eclipse.org/projects/ee4j.jersey/project/jersey-hk2) * jersey-media-multipart (org.glassfish.jersey.media:jersey-media-multipart:3.1.3 - https://projects.eclipse.org/projects/ee4j.jersey/project/jersey-media-multipart) jQuery license: - * jersey-core-client (org.glassfish.jersey.core:jersey-client:3.1.10 - https://projects.eclipse.org/projects/ee4j.jersey/jersey-client) - * jersey-inject-hk2 (org.glassfish.jersey.inject:jersey-hk2:3.1.10 - https://projects.eclipse.org/projects/ee4j.jersey/project/jersey-hk2) + * jersey-core-client (org.glassfish.jersey.core:jersey-client:3.1.11 - https://projects.eclipse.org/projects/ee4j.jersey/jersey-client) + * jersey-inject-hk2 (org.glassfish.jersey.inject:jersey-hk2:3.1.11 - https://projects.eclipse.org/projects/ee4j.jersey/project/jersey-hk2) * jersey-media-multipart (org.glassfish.jersey.media:jersey-media-multipart:3.1.3 - https://projects.eclipse.org/projects/ee4j.jersey/project/jersey-media-multipart) From 2f3fcaa3d7eb7df5c9f6d39cd3782e6b3e09b583 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Wed, 17 Dec 2025 11:55:27 -0600 Subject: [PATCH 978/979] Revert bouncycastle to version that is compatible with maven-gpg-plugin. --- pom.xml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index d4e45b05cde5..0d8fef4d2db5 100644 --- a/pom.xml +++ b/pom.xml @@ -45,7 +45,9 @@ 2.0.17 3.2.3 - 1.82 + + 1.81 8.0.1 3.1.11 From b9396131728bbac686b0cf950e0a863ae3a0ea2e Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Wed, 17 Dec 2025 13:57:10 -0600 Subject: [PATCH 979/979] [maven-release-plugin] prepare release dspace-8.3 --- dspace-api/pom.xml | 2 +- dspace-iiif/pom.xml | 2 +- dspace-oai/pom.xml | 2 +- dspace-rdf/pom.xml | 2 +- dspace-server-webapp/pom.xml | 2 +- dspace-services/pom.xml | 2 +- dspace-sword/pom.xml | 2 +- dspace-swordv2/pom.xml | 2 +- dspace/modules/additions/pom.xml | 6 +++++- dspace/modules/pom.xml | 2 +- dspace/modules/server-boot/pom.xml | 6 +++++- dspace/modules/server/pom.xml | 6 +++++- dspace/pom.xml | 2 +- pom.xml | 28 ++++++++++++++-------------- 14 files changed, 39 insertions(+), 27 deletions(-) diff --git a/dspace-api/pom.xml b/dspace-api/pom.xml index 09cbd88da277..6e061919362b 100644 --- a/dspace-api/pom.xml +++ b/dspace-api/pom.xml @@ -12,7 +12,7 @@ org.dspace dspace-parent - 8.3-SNAPSHOT + 8.3 .. diff --git a/dspace-iiif/pom.xml b/dspace-iiif/pom.xml index 6977ca4fd0ff..968c2678b7bc 100644 --- a/dspace-iiif/pom.xml +++ b/dspace-iiif/pom.xml @@ -15,7 +15,7 @@ org.dspace dspace-parent - 8.3-SNAPSHOT + 8.3 .. diff --git a/dspace-oai/pom.xml b/dspace-oai/pom.xml index 407711f680aa..01bdb90c2285 100644 --- a/dspace-oai/pom.xml +++ b/dspace-oai/pom.xml @@ -8,7 +8,7 @@ dspace-parent org.dspace - 8.3-SNAPSHOT + 8.3 .. diff --git a/dspace-rdf/pom.xml b/dspace-rdf/pom.xml index 5bc1c402bd7d..180b7844c210 100644 --- a/dspace-rdf/pom.xml +++ b/dspace-rdf/pom.xml @@ -9,7 +9,7 @@ org.dspace dspace-parent - 8.3-SNAPSHOT + 8.3 .. diff --git a/dspace-server-webapp/pom.xml b/dspace-server-webapp/pom.xml index 52b68c89bdaa..adba3efc08d0 100644 --- a/dspace-server-webapp/pom.xml +++ b/dspace-server-webapp/pom.xml @@ -14,7 +14,7 @@ org.dspace dspace-parent - 8.3-SNAPSHOT + 8.3 .. diff --git a/dspace-services/pom.xml b/dspace-services/pom.xml index 8baf523a3b19..2c129e02b1f2 100644 --- a/dspace-services/pom.xml +++ b/dspace-services/pom.xml @@ -9,7 +9,7 @@ org.dspace dspace-parent - 8.3-SNAPSHOT + 8.3 diff --git a/dspace-sword/pom.xml b/dspace-sword/pom.xml index 00369160636e..efed58a13cee 100644 --- a/dspace-sword/pom.xml +++ b/dspace-sword/pom.xml @@ -15,7 +15,7 @@ org.dspace dspace-parent - 8.3-SNAPSHOT + 8.3 .. diff --git a/dspace-swordv2/pom.xml b/dspace-swordv2/pom.xml index dc48a2759a82..d0a7df571e36 100644 --- a/dspace-swordv2/pom.xml +++ b/dspace-swordv2/pom.xml @@ -13,7 +13,7 @@ org.dspace dspace-parent - 8.3-SNAPSHOT + 8.3 .. diff --git a/dspace/modules/additions/pom.xml b/dspace/modules/additions/pom.xml index a82710f88c99..d2e2b775addf 100644 --- a/dspace/modules/additions/pom.xml +++ b/dspace/modules/additions/pom.xml @@ -17,7 +17,7 @@ org.dspace modules - 8.3-SNAPSHOT + 8.3 .. @@ -296,4 +296,8 @@ + + + dspace-8.3 + diff --git a/dspace/modules/pom.xml b/dspace/modules/pom.xml index 7800d80026d9..df8ffceb3f03 100644 --- a/dspace/modules/pom.xml +++ b/dspace/modules/pom.xml @@ -11,7 +11,7 @@ org.dspace dspace-parent - 8.3-SNAPSHOT + 8.3 ../../pom.xml diff --git a/dspace/modules/server-boot/pom.xml b/dspace/modules/server-boot/pom.xml index 6b73cc459dd3..24dc21ae0f2a 100644 --- a/dspace/modules/server-boot/pom.xml +++ b/dspace/modules/server-boot/pom.xml @@ -11,7 +11,7 @@ modules org.dspace - 8.3-SNAPSHOT + 8.3 .. @@ -121,4 +121,8 @@ + + + dspace-8.3 + diff --git a/dspace/modules/server/pom.xml b/dspace/modules/server/pom.xml index 48ecdfd0274b..48a5acfdc10b 100644 --- a/dspace/modules/server/pom.xml +++ b/dspace/modules/server/pom.xml @@ -7,7 +7,7 @@ modules org.dspace - 8.3-SNAPSHOT + 8.3 .. @@ -341,4 +341,8 @@ + + + dspace-8.3 + diff --git a/dspace/pom.xml b/dspace/pom.xml index 571ff5e371f0..1ab977bb4f1b 100644 --- a/dspace/pom.xml +++ b/dspace/pom.xml @@ -16,7 +16,7 @@ org.dspace dspace-parent - 8.3-SNAPSHOT + 8.3 ../pom.xml diff --git a/pom.xml b/pom.xml index 0d8fef4d2db5..f62b10b11588 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ org.dspace dspace-parent pom - 8.3-SNAPSHOT + 8.3 DSpace Parent Project DSpace open source software is a turnkey institutional repository application. @@ -1018,68 +1018,68 @@ org.dspace dspace-api - 8.3-SNAPSHOT + 8.3 org.dspace dspace-api test-jar - 8.3-SNAPSHOT + 8.3 test org.dspace.modules additions - 8.3-SNAPSHOT + 8.3 org.dspace.modules server classes - 8.3-SNAPSHOT + 8.3 org.dspace dspace-sword - 8.3-SNAPSHOT + 8.3 org.dspace dspace-swordv2 - 8.3-SNAPSHOT + 8.3 org.dspace dspace-oai - 8.3-SNAPSHOT + 8.3 org.dspace dspace-services - 8.3-SNAPSHOT + 8.3 org.dspace dspace-server-webapp test-jar - 8.3-SNAPSHOT + 8.3 test org.dspace dspace-rdf - 8.3-SNAPSHOT + 8.3 org.dspace dspace-iiif - 8.3-SNAPSHOT + 8.3 org.dspace dspace-server-webapp - 8.3-SNAPSHOT + 8.3 @@ -1912,7 +1912,7 @@ scm:git:git@github.com:DSpace/DSpace.git scm:git:git@github.com:DSpace/DSpace.git https://github.com/DSpace/DSpace - dspace-8_x + dspace-8.3