This is an automated email from the ASF dual-hosted git repository. ahuber pushed a commit to branch spring6 in repository https://gitbox.apache.org/repos/asf/causeway.git
The following commit(s) were added to refs/heads/spring6 by this push: new 97766d3590 Bump resteasy-spring 3.1.0.Final -> 3.1.1.Final (also workaround regressions) 97766d3590 is described below commit 97766d3590c1aaadf9d3b69b3f7bd34a01fd014d Author: Andi Huber <ahu...@apache.org> AuthorDate: Wed Jan 24 15:07:05 2024 +0100 Bump resteasy-spring 3.1.0.Final -> 3.1.1.Final (also workaround regressions) --- bom/pom.xml | 2 +- .../integtests/OutboxRestClient_IntegTest.java | 63 ++++---- .../SpringBeanProcessorRegressionWorkaround.java | 167 +++++++++++++++++++++ 3 files changed, 202 insertions(+), 30 deletions(-) diff --git a/bom/pom.xml b/bom/pom.xml index 0b5bd0bc45..efef3135ca 100644 --- a/bom/pom.xml +++ b/bom/pom.xml @@ -162,7 +162,7 @@ It is therefore a copy of org.apache:apache, with customisations clearly identif <quartz-scheduler.version>2.3.2</quartz-scheduler.version> <resteasy-spring-boot.version>6.0.0.Alpha2</resteasy-spring-boot.version> - <resteasy-spring.version>3.1.0.Final</resteasy-spring.version> + <resteasy-spring.version>3.1.1.Final</resteasy-spring.version> <resteasy.version>6.2.7.Final</resteasy.version> <!-- be awre of potential clashes with https://mvnrepository.com/artifact/org.jboss.resteasy/resteasy-spring-boot-starter --> <resteasy-jaxb-provider.version>6.2.2.Final</resteasy-jaxb-provider.version> diff --git a/extensions/core/executionoutbox/restclient/src/test/java/org/apache/causeway/extensions/executionoutbox/restclient/integtests/OutboxRestClient_IntegTest.java b/extensions/core/executionoutbox/restclient/src/test/java/org/apache/causeway/extensions/executionoutbox/restclient/integtests/OutboxRestClient_IntegTest.java index c0d8cf07e5..d3133be5db 100644 --- a/extensions/core/executionoutbox/restclient/src/test/java/org/apache/causeway/extensions/executionoutbox/restclient/integtests/OutboxRestClient_IntegTest.java +++ b/extensions/core/executionoutbox/restclient/src/test/java/org/apache/causeway/extensions/executionoutbox/restclient/integtests/OutboxRestClient_IntegTest.java @@ -32,7 +32,6 @@ import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.web.server.LocalServerPort; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.Import; import org.springframework.context.annotation.PropertySource; import org.springframework.context.annotation.PropertySources; import org.springframework.test.context.ActiveProfiles; @@ -55,14 +54,35 @@ import org.apache.causeway.extensions.executionoutbox.applib.integtest.model.Cou import org.apache.causeway.extensions.executionoutbox.jpa.CausewayModuleExtExecutionOutboxPersistenceJpa; import org.apache.causeway.extensions.executionoutbox.jpa.integtests.model.Counter; import org.apache.causeway.extensions.executionoutbox.restclient.api.OutboxClient; -import org.apache.causeway.persistence.jpa.eclipselink.CausewayModulePersistenceJpaEclipselink; import org.apache.causeway.schema.ixn.v2.InteractionDto; import org.apache.causeway.security.bypass.CausewayModuleSecurityBypass; import org.apache.causeway.testing.fixtures.applib.CausewayModuleTestingFixturesApplib; -import org.apache.causeway.viewer.restfulobjects.jaxrsresteasy.CausewayModuleViewerRestfulObjectsJaxrsResteasy; +import org.apache.causeway.viewer.restfulobjects.jaxrsresteasy.conneg.RestfulObjectsJaxbWriterForXml; +import org.apache.causeway.viewer.restfulobjects.jaxrsresteasy.webmodule.WebModuleJaxrsResteasy; +import org.apache.causeway.viewer.restfulobjects.viewer.CausewayModuleViewerRestfulObjectsViewer; @SpringBootTest( - classes = OutboxRestClient_IntegTest.AppManifest.class, + classes = { + OutboxRestClient_IntegTest.TestManifest.class, + CausewayModuleCoreRuntimeServices.class, + CausewayModuleSecurityBypass.class, + + CausewayModuleTestingFixturesApplib.class, + CausewayModuleExtExecutionOutboxPersistenceJpa.class, + + // RESTEASY013015: could not find the type for bean named jpaSharedEM_entityManagerFactory + //CausewayModuleViewerRestfulObjectsJaxrsResteasy.class, // replaced + SpringBeanProcessorRegressionWorkaround.class, + CausewayModuleViewerRestfulObjectsViewer.class, + WebModuleJaxrsResteasy.class, + RestfulObjectsJaxbWriterForXml.class, + // --- + + CausewayModuleCoreWebapp.class, + // mixins + Counter_bumpUsingMixin.class, + Counter_bumpUsingMixinWithExecutionPublishingDisabled.class, + }, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT ) @ActiveProfiles("test") @@ -70,27 +90,22 @@ public class OutboxRestClient_IntegTest { @EnableAutoConfiguration @Configuration - @Import({ - CausewayModuleCoreRuntimeServices.class, - CausewayModuleSecurityBypass.class, - CausewayModulePersistenceJpaEclipselink.class, - CausewayModuleTestingFixturesApplib.class, - CausewayModuleExtExecutionOutboxPersistenceJpa.class, - CausewayModuleViewerRestfulObjectsJaxrsResteasy.class, - CausewayModuleCoreWebapp.class, - - // mixins - Counter_bumpUsingMixin.class, - Counter_bumpUsingMixinWithExecutionPublishingDisabled.class, - }) @PropertySources({ @PropertySource(CausewayPresets.UseLog4j2Test) }) @EntityScan(basePackageClasses = {Counter.class}) - @ComponentScan(basePackageClasses = {AppManifest.class, Counter.class}) - public static class AppManifest { + @ComponentScan(basePackageClasses = {TestManifest.class, Counter.class}) + public static class TestManifest { } + @Inject ExecutionOutboxEntryRepository executionOutboxEntryRepository; + @Inject InteractionService interactionService; + @Inject RepositoryService repositoryService; + @Inject CounterRepository<Counter> counterRepository; + @Inject WrapperFactory wrapperFactory; + @Inject TransactionService transactionService; + @Inject RestEndpointService restEndpointService; + @LocalServerPort protected int port; @@ -243,14 +258,4 @@ public class OutboxRestClient_IntegTest { }); } - - @Inject RestEndpointService restEndpointService; - - @Inject ExecutionOutboxEntryRepository executionOutboxEntryRepository; - @Inject InteractionService interactionService; - @Inject RepositoryService repositoryService; - @Inject CounterRepository<Counter> counterRepository; - @Inject WrapperFactory wrapperFactory; - @Inject TransactionService transactionService; - } diff --git a/extensions/core/executionoutbox/restclient/src/test/java/org/apache/causeway/extensions/executionoutbox/restclient/integtests/SpringBeanProcessorRegressionWorkaround.java b/extensions/core/executionoutbox/restclient/src/test/java/org/apache/causeway/extensions/executionoutbox/restclient/integtests/SpringBeanProcessorRegressionWorkaround.java new file mode 100644 index 0000000000..3aa897dc71 --- /dev/null +++ b/extensions/core/executionoutbox/restclient/src/test/java/org/apache/causeway/extensions/executionoutbox/restclient/integtests/SpringBeanProcessorRegressionWorkaround.java @@ -0,0 +1,167 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.causeway.extensions.executionoutbox.restclient.integtests; + +import java.util.List; + +import jakarta.persistence.EntityManagerFactory; +import jakarta.servlet.ServletContext; +import jakarta.servlet.ServletContextEvent; +import jakarta.servlet.ServletContextListener; + +import org.jboss.resteasy.core.AsynchronousDispatcher; +import org.jboss.resteasy.core.ResourceMethodRegistry; +import org.jboss.resteasy.core.ResteasyContext; +import org.jboss.resteasy.core.SynchronousDispatcher; +import org.jboss.resteasy.plugins.server.servlet.ListenerBootstrap; +import org.jboss.resteasy.plugins.server.servlet.ResteasyBootstrap; +import org.jboss.resteasy.plugins.spring.SpringBeanProcessor; +import org.jboss.resteasy.spi.Dispatcher; +import org.jboss.resteasy.spi.Registry; +import org.jboss.resteasy.spi.ResteasyDeployment; +import org.jboss.resteasy.spi.ResteasyProviderFactory; +import org.jboss.resteasy.springboot.JAXRSResourcesAndProvidersScannerPostProcessor; +import org.jboss.resteasy.springboot.ResteasyApplicationBuilder; +import org.jboss.resteasy.springboot.ResteasyEmbeddedServletInitializer; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.beans.factory.config.BeanFactoryPostProcessor; +import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Primary; + +import org.apache.causeway.viewer.restfulobjects.jaxrsresteasy.CausewayModuleViewerRestfulObjectsJaxrsResteasy; + +/** + * RESTEASY013015: could not find the type for bean named jpaSharedEM_entityManagerFactory + * <p> + * Overrides {@link org.jboss.resteasy.springboot.ResteasyAutoConfiguration} + * and fixes regression with {@link SpringBeanProcessor}. + * <p> + * If seeing this in production, we need to propagate the workaround to + * {@link CausewayModuleViewerRestfulObjectsJaxrsResteasy}. + */ +@Configuration +class SpringBeanProcessorRegressionWorkaround { + + @Bean @Primary + @Qualifier("ResteasyProviderFactory") + public static BeanFactoryPostProcessor springBeanProcessor() { + var blackListed = List.of("jpaSharedEM_entityManagerFactory", "jpaSharedEM_AWC_entityManagerFactory"); + + ResteasyProviderFactory resteasyProviderFactory = ResteasyProviderFactory.newInstance(); + ResourceMethodRegistry resourceMethodRegistry = new ResourceMethodRegistry(resteasyProviderFactory); + + SpringBeanProcessor springBeanProcessor = new SpringBeanProcessor() { + + @Override + protected Class<?> processBean(final ConfigurableListableBeanFactory beanFactory, + final List<String> dependsOnProviders, final String name, final BeanDefinition beanDef) { + + if(blackListed.contains(name)) { + return EntityManagerFactory.class; + } + + return super.processBean(beanFactory, dependsOnProviders, name, beanDef); + } + + }; + springBeanProcessor.setProviderFactory(resteasyProviderFactory); + springBeanProcessor.setRegistry(resourceMethodRegistry); + + return springBeanProcessor; + } + + @Bean + @ConditionalOnProperty(name="resteasy.jaxrs.scan-packages") + public static JAXRSResourcesAndProvidersScannerPostProcessor providerScannerPostProcessor() { + return new JAXRSResourcesAndProvidersScannerPostProcessor(); + } + + /** + * This is a modified version of {@link ResteasyBootstrap} + * + * @return a ServletContextListener object that configures and start a ResteasyDeployment + */ + @Bean + public ServletContextListener resteasyBootstrapListener(@Qualifier("ResteasyProviderFactory") final BeanFactoryPostProcessor beanFactoryPostProcessor) { + ServletContextListener servletContextListener = new ServletContextListener() { + + private SpringBeanProcessor springBeanProcessor = (SpringBeanProcessor) beanFactoryPostProcessor; + + protected ResteasyDeployment deployment; + + @Override + public void contextInitialized(final ServletContextEvent sce) { + ServletContext servletContext = sce.getServletContext(); + ResteasyContext.pushContext(ServletContext.class, servletContext); + ListenerBootstrap config = new ListenerBootstrap(servletContext); + + ResteasyProviderFactory resteasyProviderFactory = springBeanProcessor.getProviderFactory(); + ResourceMethodRegistry resourceMethodRegistry = (ResourceMethodRegistry) springBeanProcessor.getRegistry(); + + deployment = config.createDeployment(); + + deployment.setProviderFactory(resteasyProviderFactory); + deployment.setRegistry(resourceMethodRegistry); + + if (deployment.isAsyncJobServiceEnabled()) { + AsynchronousDispatcher dispatcher = new AsynchronousDispatcher(resteasyProviderFactory, resourceMethodRegistry); + deployment.setDispatcher(dispatcher); + } else { + SynchronousDispatcher dispatcher = new SynchronousDispatcher(resteasyProviderFactory, resourceMethodRegistry); + deployment.setDispatcher(dispatcher); + } + + deployment.start(); + + servletContext.setAttribute(ResteasyProviderFactory.class.getName(), deployment.getProviderFactory()); + servletContext.setAttribute(Dispatcher.class.getName(), deployment.getDispatcher()); + servletContext.setAttribute(Registry.class.getName(), deployment.getRegistry()); + } + + @Override + public void contextDestroyed(final ServletContextEvent sce) { + try { + if (deployment != null) { + deployment.stop(); + } + } finally { + ResteasyContext.popContextData(ServletContext.class); + } + } + }; + + return servletContextListener; + } + + @Bean(name = ResteasyApplicationBuilder.BEAN_NAME) + public ResteasyApplicationBuilder resteasyApplicationBuilder() { + return new ResteasyApplicationBuilder(); + } + + @Bean + public static ResteasyEmbeddedServletInitializer resteasyEmbeddedServletInitializer() { + return new ResteasyEmbeddedServletInitializer(); + } + + +}