From d28851568c0b40fa9df8ed6d6547ff7999bbad3d Mon Sep 17 00:00:00 2001 From: Michael Ritter Date: Mon, 14 Sep 2026 11:29:27 -0600 Subject: [PATCH 1/3] Add resteasy-servlet-initializer dependency --- services/JaxRsServiceProvider/pom.xml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/services/JaxRsServiceProvider/pom.xml b/services/JaxRsServiceProvider/pom.xml index 43db2b47b7..0bc96eb846 100644 --- a/services/JaxRsServiceProvider/pom.xml +++ b/services/JaxRsServiceProvider/pom.xml @@ -489,6 +489,10 @@ org.jboss.resteasy resteasy-multipart-provider + + org.jboss.resteasy + resteasy-servlet-initializer + junit junit From f72b6b62f3595af9cf64f4dcc890bc9efafc6b3d Mon Sep 17 00:00:00 2001 From: Michael Ritter Date: Mon, 14 Sep 2026 11:32:45 -0600 Subject: [PATCH 2/3] Prefer ApplicationPath over servlet-mapping --- .../jaxrs/CollectionSpaceJaxRsApplication.java | 2 ++ .../src/main/webapp/WEB-INF/web.xml | 12 +----------- 2 files changed, 3 insertions(+), 11 deletions(-) diff --git a/services/JaxRsServiceProvider/src/main/java/org/collectionspace/services/jaxrs/CollectionSpaceJaxRsApplication.java b/services/JaxRsServiceProvider/src/main/java/org/collectionspace/services/jaxrs/CollectionSpaceJaxRsApplication.java index 105569ff8a..7092690039 100644 --- a/services/JaxRsServiceProvider/src/main/java/org/collectionspace/services/jaxrs/CollectionSpaceJaxRsApplication.java +++ b/services/JaxRsServiceProvider/src/main/java/org/collectionspace/services/jaxrs/CollectionSpaceJaxRsApplication.java @@ -85,6 +85,7 @@ import org.collectionspace.services.logout.LogoutResource; import javax.servlet.ServletContext; +import javax.ws.rs.ApplicationPath; import javax.ws.rs.core.Application; import java.util.HashSet; @@ -109,6 +110,7 @@ * $LastChangedRevision$ * $LastChangedDate$ */ +@ApplicationPath("/") public class CollectionSpaceJaxRsApplication extends Application implements ResourceMapHolder { private Set singletons = new HashSet<>(); diff --git a/services/JaxRsServiceProvider/src/main/webapp/WEB-INF/web.xml b/services/JaxRsServiceProvider/src/main/webapp/WEB-INF/web.xml index f1749217ae..ef703deb6e 100644 --- a/services/JaxRsServiceProvider/src/main/webapp/WEB-INF/web.xml +++ b/services/JaxRsServiceProvider/src/main/webapp/WEB-INF/web.xml @@ -17,11 +17,6 @@ CSpaceLoggingContext - - javax.ws.rs.Application - org.collectionspace.services.jaxrs.CollectionSpaceJaxRsApplication - - resteasy.servlet.mapping.prefix / @@ -157,16 +152,11 @@ org.collectionspace.services.jaxrs.CSpaceResteasyBootstrap - + Resteasy org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher - - Resteasy - /* - - From ce3ee5e50b55fc1c156d56d947ac83c74d588da8 Mon Sep 17 00:00:00 2001 From: Michael Ritter Date: Mon, 14 Sep 2026 11:33:15 -0600 Subject: [PATCH 3/3] Implement jaxrs Feature instead of ResteasyBoostrap This allows us to perform the tasks done by CSpaceResteasyBoostrap which are a mix of updating the Resteasy context and post-init tasks. --- .../jaxrs/CSpaceResteasyBootstrap.java | 92 +++++++++---------- .../CollectionSpaceJaxRsApplication.java | 3 +- .../src/main/webapp/WEB-INF/web.xml | 7 -- 3 files changed, 47 insertions(+), 55 deletions(-) diff --git a/services/JaxRsServiceProvider/src/main/java/org/collectionspace/services/jaxrs/CSpaceResteasyBootstrap.java b/services/JaxRsServiceProvider/src/main/java/org/collectionspace/services/jaxrs/CSpaceResteasyBootstrap.java index 6b1b6b6653..b1d8093083 100644 --- a/services/JaxRsServiceProvider/src/main/java/org/collectionspace/services/jaxrs/CSpaceResteasyBootstrap.java +++ b/services/JaxRsServiceProvider/src/main/java/org/collectionspace/services/jaxrs/CSpaceResteasyBootstrap.java @@ -2,13 +2,24 @@ import static org.nuxeo.elasticsearch.ElasticSearchConstants.ES_ENABLED_PROPERTY; -import javax.servlet.ServletContextEvent; +import java.io.File; +import java.io.InputStream; +import java.lang.reflect.Constructor; +import java.net.URI; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Hashtable; +import java.util.List; +import java.util.Set; +import javax.ws.rs.core.Feature; +import javax.ws.rs.core.FeatureContext; import javax.ws.rs.core.PathSegment; import javax.ws.rs.core.Response; import javax.ws.rs.core.UriInfo; -import org.jboss.resteasy.plugins.server.servlet.ResteasyBootstrap; -import org.jboss.resteasy.specimpl.PathSegmentImpl; import org.apache.commons.io.IOUtils; import org.collectionspace.authentication.AuthN; import org.collectionspace.authentication.CSpaceTenant; @@ -32,7 +43,6 @@ import org.collectionspace.services.common.config.TenantBindingConfigReaderImpl; import org.collectionspace.services.common.query.UriInfoImpl; import org.collectionspace.services.common.vocabulary.AuthorityResource; - import org.collectionspace.services.config.service.AuthorityInstanceType; import org.collectionspace.services.config.service.ServiceBindingType; import org.collectionspace.services.config.service.ServiceBindingType.AuthorityInstanceList; @@ -45,27 +55,22 @@ import org.collectionspace.services.jaxb.AbstractCommonList.ListItem; import org.collectionspace.services.nuxeo.util.NuxeoUtils; import org.collectionspace.services.report.ReportResource; +import org.jboss.resteasy.core.ResteasyContext; +import org.jboss.resteasy.specimpl.PathSegmentImpl; import org.jboss.resteasy.spi.Dispatcher; +import org.jboss.resteasy.spi.ResteasyDeployment; import org.nuxeo.elasticsearch.ElasticSearchComponent; import org.nuxeo.elasticsearch.api.ElasticSearchService; import org.nuxeo.runtime.api.Framework; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.io.File; -import java.io.InputStream; -import java.lang.reflect.Constructor; -import java.net.URI; -import java.net.URLEncoder; -import java.nio.charset.StandardCharsets; -import java.nio.file.Files; -import java.util.Arrays; -import java.util.HashSet; -import java.util.Hashtable; -import java.util.List; -import java.util.Set; - -public class CSpaceResteasyBootstrap extends ResteasyBootstrap { +/** + * Note: this isn't really a proper {@link Feature}, but is just being used as a way to do work once the + * RESTEasyDeployment is available. Previously, this extended ResteasyBootstrap which was causing multiple instances of + * our JaxRsApplication to be created. + */ +public class CSpaceResteasyBootstrap implements Feature { private static final Logger logger = LoggerFactory.getLogger(CSpaceResteasyBootstrap.class); private static final String RESET_AUTHORITIES_PROPERTY = "org.collectionspace.services.authorities.reset"; @@ -77,40 +82,40 @@ public class CSpaceResteasyBootstrap extends ResteasyBootstrap { private static final String BATCH_PROPERTY = "batch"; @Override - public void contextInitialized(ServletContextEvent event) { + public boolean configure(FeatureContext featureContext) { try { // // This call to super instantiates and initializes our JAX-RS application class. // The application class is org.collectionspace.services.jaxrs.CollectionSpaceJaxRsApplication. // logger.info("Starting up the CollectionSpace Services JAX-RS application."); - super.contextInitialized(event); - CollectionSpaceJaxRsApplication app = (CollectionSpaceJaxRsApplication)deployment.getApplication(); + ResteasyDeployment deployment = ResteasyContext.getContextData(ResteasyDeployment.class); + CollectionSpaceJaxRsApplication app = (CollectionSpaceJaxRsApplication) deployment.getApplication(); Dispatcher disp = deployment.getDispatcher(); disp.getDefaultContextObjects().put(ResourceMap.class, app.getResourceMap()); // Property can be set in the tomcat/bin/setenv.sh (or setenv.bat) file String quickBoot = System.getProperty(QUICK_BOOT_PROPERTY, Boolean.FALSE.toString()); - if (Boolean.valueOf(quickBoot) == false) { + if (!Boolean.parseBoolean(quickBoot)) { // The below properties can be set in the tomcat/bin/setenv.sh (or setenv.bat) file. String resetAuthsString = System.getProperty(RESET_AUTHORITIES_PROPERTY, Boolean.FALSE.toString()); String resetElasticsearchIndexString = System.getProperty(RESET_ELASTICSEARCH_INDEX_PROPERTY, Boolean.FALSE.toString()); String resetBatchJobsString = System.getProperty(RESET_BATCH_JOBS_PROPERTY, Boolean.TRUE.toString()); String resetReportsString = System.getProperty(RESET_REPORTS_PROPERTY, Boolean.TRUE.toString()); - initializeAuthorities(app.getResourceMap(), Boolean.valueOf(resetAuthsString)); + initializeAuthorities(app.getResourceMap(), Boolean.parseBoolean(resetAuthsString)); - if (Boolean.valueOf(resetElasticsearchIndexString) == true) { + if (Boolean.parseBoolean(resetElasticsearchIndexString)) { resetElasticSearchIndex(); } - if (Boolean.valueOf(resetReportsString) == true) { - resetReports(); + if (Boolean.parseBoolean(resetReportsString)) { + resetReports(app); } - if (Boolean.valueOf(resetBatchJobsString) == true) { - resetBatchJobs(); + if (Boolean.parseBoolean(resetBatchJobsString)) { + resetBatchJobs(app); } } @@ -119,17 +124,11 @@ public void contextInitialized(ServletContextEvent event) { e.printStackTrace(); throw new RuntimeException(e); } - } - - @Override - public void contextDestroyed(ServletContextEvent event) { - logger.info("Shutting down the CollectionSpace Services JAX-RS application."); - //Do something if needed. - logger.info("CollectionSpace Services JAX-RS application stopped."); + return true; } - public void resetReports() throws Exception { + public void resetReports(CollectionSpaceJaxRsApplication app) throws Exception { logger.info("Resetting reports"); TenantBindingConfigReaderImpl tenantBindingConfigReader = ServiceMain.getInstance().getTenantBindingConfigReader(); @@ -161,17 +160,17 @@ public void resetReports() throws Exception { if (reportNames.size() > 0) { CSpaceTenant tenant = new CSpaceTenant(tenantBinding.getId(), tenantBinding.getName()); - resetTenantReports(tenant, reportNames); + resetTenantReports(tenant, reportNames, app); } } } - private void resetTenantReports(CSpaceTenant tenant, Set reportNames) throws Exception { + private void resetTenantReports(CSpaceTenant tenant, Set reportNames, CollectionSpaceJaxRsApplication app) + throws Exception { logger.info("Resetting reports for tenant {}", tenant.getId()); AuthZ.get().login(tenant); - CollectionSpaceJaxRsApplication app = (CollectionSpaceJaxRsApplication) deployment.getApplication(); ResourceMap resourceMap = app.getResourceMap(); ReportResource reportResource = (ReportResource) resourceMap.get(ReportClient.SERVICE_NAME); @@ -245,7 +244,7 @@ private void resetTenantReports(CSpaceTenant tenant, Set reportNames) th } } - public void resetBatchJobs() throws Exception { + public void resetBatchJobs(CollectionSpaceJaxRsApplication app) throws Exception { logger.info("Resetting batch jobs"); TenantBindingConfigReaderImpl tenantBindingConfigReader = ServiceMain.getInstance().getTenantBindingConfigReader(); @@ -277,17 +276,17 @@ public void resetBatchJobs() throws Exception { if (batchNames.size() > 0) { CSpaceTenant tenant = new CSpaceTenant(tenantBinding.getId(), tenantBinding.getName()); - resetTenantBatchJobs(tenant, batchNames); + resetTenantBatchJobs(tenant, batchNames, app); } } } - private void resetTenantBatchJobs(CSpaceTenant tenant, Set batchNames) throws Exception { + private void resetTenantBatchJobs(CSpaceTenant tenant, Set batchNames, CollectionSpaceJaxRsApplication app) + throws Exception { logger.info("Resetting batch jobs for tenant {}", tenant.getId()); AuthZ.get().login(tenant); - CollectionSpaceJaxRsApplication app = (CollectionSpaceJaxRsApplication) deployment.getApplication(); ResourceMap resourceMap = app.getResourceMap(); BatchResource batchResource = (BatchResource) resourceMap.get(BatchClient.SERVICE_NAME); @@ -388,7 +387,8 @@ public void resetElasticSearchIndex() throws Exception { Boolean isElasticsearchIndexed = serviceBinding.isElasticsearchIndexed(); String servicesRepoDomainName = serviceBinding.getRepositoryDomain(); - if (isElasticsearchIndexed && servicesRepoDomainName != null && servicesRepoDomainName.trim().isEmpty() == false) { + if (isElasticsearchIndexed && servicesRepoDomainName != null && !servicesRepoDomainName.trim() + .isEmpty()) { String repositoryName = ConfigUtils.getRepositoryName(tenantBinding, servicesRepoDomainName); String docType = NuxeoUtils.getTenantQualifiedDocType(tenantBinding.getId(), serviceBinding.getObject().getName()); @@ -410,7 +410,7 @@ public void initializeAuthorities(ResourceMap resourceMap, boolean reset) throws Hashtable tenantBindingsTable = tenantBindingConfigReader.getTenantBindings(false); for (TenantBindingType tenantBindings : tenantBindingsTable.values()) { CSpaceTenant tenant = new CSpaceTenant(tenantBindings.getId(), tenantBindings.getName()); - if (shouldInitializeAuthorities(tenant, reset) == true) { + if (shouldInitializeAuthorities(tenant, reset)) { logger.info("Initializing vocabularies and authorities of tenant '{}'.", tenant.getId()); for (ServiceBindingType serviceBinding : tenantBindings.getServiceBindings()) { AuthorityInstanceList element = serviceBinding.getAuthorityInstanceList(); @@ -453,7 +453,7 @@ private boolean shouldInitializeAuthorities(CSpaceTenant cspaceTenant, boolean r // we're not being asked to reset them, we'll return 'false' // making any changes // - return tenantState.isAuthoritiesInitialized() == false || reset == true; + return !tenantState.isAuthoritiesInitialized() || reset; } private void setAuthoritiesInitialized(CSpaceTenant cspaceTenant, boolean initState) { diff --git a/services/JaxRsServiceProvider/src/main/java/org/collectionspace/services/jaxrs/CollectionSpaceJaxRsApplication.java b/services/JaxRsServiceProvider/src/main/java/org/collectionspace/services/jaxrs/CollectionSpaceJaxRsApplication.java index 7092690039..d2f4d9ca0e 100644 --- a/services/JaxRsServiceProvider/src/main/java/org/collectionspace/services/jaxrs/CollectionSpaceJaxRsApplication.java +++ b/services/JaxRsServiceProvider/src/main/java/org/collectionspace/services/jaxrs/CollectionSpaceJaxRsApplication.java @@ -114,7 +114,6 @@ public class CollectionSpaceJaxRsApplication extends Application implements ResourceMapHolder { private Set singletons = new HashSet<>(); - private Set> empty = new HashSet<>(); private ResourceMap resourceMap = new ResourceMapImpl(); private ServletContext servletContext = null; @@ -205,7 +204,7 @@ private void addResourceToMapAndSingletons(NuxeoBasedResource resource) { @Override public Set> getClasses() { - return empty; + return Set.of(CSpaceResteasyBootstrap.class); } @Override diff --git a/services/JaxRsServiceProvider/src/main/webapp/WEB-INF/web.xml b/services/JaxRsServiceProvider/src/main/webapp/WEB-INF/web.xml index ef703deb6e..c5310c499d 100644 --- a/services/JaxRsServiceProvider/src/main/webapp/WEB-INF/web.xml +++ b/services/JaxRsServiceProvider/src/main/webapp/WEB-INF/web.xml @@ -146,13 +146,6 @@ - - - - org.collectionspace.services.jaxrs.CSpaceResteasyBootstrap - - - Resteasy