├── .github ├── dependabot.yml └── workflows │ └── ci.yml ├── .gitignore ├── LICENSE ├── README.md ├── common ├── pom.xml ├── resteasy-spring-boot-starter-common │ ├── pom.xml │ └── src │ │ └── main │ │ └── java │ │ └── org │ │ └── jboss │ │ └── resteasy │ │ └── springboot │ │ └── common │ │ ├── DeploymentCustomizer.java │ │ ├── JaxrsApplicationScanner.java │ │ ├── ResteasyBeanProcessorFactory.java │ │ └── ResteasyResourcesFinder.java └── resteasy-spring-boot-starter-sample-app-common │ ├── pom.xml │ └── src │ └── main │ └── java │ └── org │ └── jboss │ └── resteasy │ └── springboot │ └── common │ ├── sample │ ├── bean │ │ └── CustomSingletonBean.java │ ├── configuration │ │ ├── CustomExceptionMapper.java │ │ └── SampleSSLContextFactory.java │ ├── filter │ │ └── CustomContainerResponseFilter.java │ └── resources │ │ ├── Echo.java │ │ ├── EchoMessage.java │ │ ├── Foo.java │ │ └── IEchoMessageCreator.java │ └── utils │ └── SocketUtils.java ├── mds └── USAGE.md ├── pom.xml ├── reactor-netty ├── pom.xml ├── resteasy-reactor-netty-spring-boot-sample-app-no-jaxrs │ ├── README.md │ ├── pom.xml │ └── src │ │ └── main │ │ └── java │ │ └── com │ │ └── sample │ │ └── app2 │ │ └── Application.java ├── resteasy-reactor-netty-spring-boot-sample-app │ ├── README.md │ ├── pom.xml │ └── src │ │ └── main │ │ ├── java │ │ └── com │ │ │ └── sample │ │ │ └── app │ │ │ ├── Application.java │ │ │ └── configuration │ │ │ └── JaxrsApplication.java │ │ └── resources │ │ ├── application.yaml │ │ ├── logback.xml │ │ └── test.keystore ├── resteasy-reactor-netty-spring-boot-starter-test │ ├── pom.xml │ └── src │ │ └── test │ │ ├── java │ │ └── com │ │ │ └── sample │ │ │ └── app │ │ │ ├── NonSpringBeanJaxrsApplication1.java │ │ │ ├── NonSpringBeanJaxrsApplication2.java │ │ │ └── test │ │ │ ├── AsyncJobIT.java │ │ │ ├── CommonUseCasesIT.java │ │ │ ├── ConfigurationIT.java │ │ │ └── LogbackTestApplicationListener.java │ │ └── resources │ │ └── application.yaml └── resteasy-reactor-netty-spring-boot-starter │ ├── pom.xml │ ├── settings.xml │ └── src │ ├── main │ ├── java │ │ └── org │ │ │ └── jboss │ │ │ └── resteasy │ │ │ └── springboot │ │ │ └── reactor │ │ │ ├── ReactorNettyServerConfig.java │ │ │ ├── ResteasyAutoConfiguration.java │ │ │ └── ResteasyBeanProcessorReactorNetty.java │ └── resources │ │ └── META-INF │ │ └── spring │ │ └── org.springframework.boot.autoconfigure.AutoConfiguration.imports │ └── test │ ├── java │ └── org │ │ └── jboss │ │ └── resteasy │ │ └── springboot │ │ ├── JaxrsAppRegistrationTest.java │ │ ├── ResteasyBeansProcessorTest.java │ │ ├── sample │ │ ├── SampleApp.java │ │ ├── TestApplication1.java │ │ ├── TestApplication2.java │ │ ├── TestApplication3.java │ │ ├── TestApplication4.java │ │ ├── TestApplication5.java │ │ ├── TestProvider1.java │ │ ├── TestResource1.java │ │ └── TestResource2.java │ │ └── utilities │ │ ├── CustomPropertySource.java │ │ └── TestUtils.java │ └── resources │ ├── logback.xml │ ├── test-config-no-resources.xml │ └── test-config.xml ├── resteasy-spring-boot-bom └── pom.xml └── servlet ├── pom.xml ├── resteasy-servlet-spring-boot-sample-app-no-jaxrs ├── README.md ├── pom.xml └── src │ └── main │ └── java │ └── com │ └── sample │ └── app2 │ └── Application.java ├── resteasy-servlet-spring-boot-sample-app ├── README.md ├── pom.xml └── src │ └── main │ ├── java │ └── com │ │ └── sample │ │ └── app │ │ ├── Application.java │ │ └── configuration │ │ └── JaxrsApplication.java │ └── resources │ ├── application.yaml │ └── logback.xml ├── resteasy-servlet-spring-boot-starter-test ├── pom.xml └── src │ └── test │ ├── java │ └── com │ │ ├── sample │ │ └── app │ │ │ ├── NonSpringBeanJaxrsApplication1.java │ │ │ ├── NonSpringBeanJaxrsApplication2.java │ │ │ └── test │ │ │ ├── AsyncJobIT.java │ │ │ ├── CommonUseCasesIT.java │ │ │ ├── ConfigurationIT.java │ │ │ ├── InvalidClassTest.java │ │ │ ├── LogbackTestApplicationListener.java │ │ │ └── MultipleContextsIT.java │ │ └── test │ │ └── multicontexttest │ │ ├── JaxrsApplication.java │ │ └── MultiContextTestApp.java │ └── resources │ └── application.yaml ├── resteasy-servlet-spring-boot-starter ├── cacerts ├── pom.xml ├── settings.xml └── src │ ├── main │ ├── java │ │ └── org │ │ │ └── jboss │ │ │ └── resteasy │ │ │ └── springboot │ │ │ ├── ResteasyApplicationBuilder.java │ │ │ ├── ResteasyAutoConfiguration.java │ │ │ └── ResteasyBeanProcessorTomcat.java │ └── resources │ │ └── META-INF │ │ └── spring │ │ └── org.springframework.boot.autoconfigure.AutoConfiguration.imports │ └── test │ ├── java │ └── org │ │ └── jboss │ │ └── resteasy │ │ └── springboot │ │ ├── JaxrsAppRegistrationTest.java │ │ ├── ResteasyAutoConfigurationTest.java │ │ ├── ResteasyBeanProcessorTest.java │ │ └── sample │ │ ├── SampleApp.java │ │ ├── TestApplication1.java │ │ ├── TestApplication2.java │ │ ├── TestApplication3.java │ │ ├── TestApplication4.java │ │ ├── TestApplication5.java │ │ ├── TestProvider1.java │ │ ├── TestProviderNoBean.java │ │ ├── TestResource1.java │ │ ├── TestResource2.java │ │ └── TestResourceNoBean.java │ └── resources │ ├── logback.xml │ └── test-config.xml └── resteasy-wildfly-spring-boot-sample-app ├── README.md ├── pom.xml └── src └── main ├── java └── com │ └── sample │ └── app │ ├── EchoBean.java │ ├── HelloResource.java │ ├── RestApp.java │ └── SpringApp.java └── resources └── application.yaml /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # To get started with Dependabot version updates, you'll need to specify which 2 | # package ecosystems to update and where the package manifests are located. 3 | # Please see the documentation for all configuration options: 4 | # https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates 5 | 6 | version: 2 7 | updates: 8 | - package-ecosystem: "maven" 9 | directory: "/" 10 | schedule: 11 | interval: "daily" 12 | rebase-strategy: disabled 13 | - package-ecosystem: "github-actions" 14 | # Workflow files stored in the 15 | # default location of `.github/workflows` 16 | directory: "/" 17 | schedule: 18 | interval: "daily" 19 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: Java CI with Maven 2 | 3 | on: 4 | push: 5 | branches: [ main, '**' ] 6 | pull_request: 7 | branches: [ main, '**' ] 8 | 9 | jobs: 10 | build: 11 | 12 | runs-on: ${{ matrix.os }} 13 | strategy: 14 | matrix: 15 | os: [ ubuntu-latest ] 16 | java: [ '17', '21' ] 17 | steps: 18 | - uses: actions/checkout@v4 19 | - name: Set up JDK ${{ matrix.java }} 20 | uses: actions/setup-java@v4 21 | with: 22 | java-version: ${{ matrix.java }} 23 | distribution: 'temurin' 24 | - name: Build With Maven 25 | run: mvn -B -fae install --file pom.xml 26 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.class 2 | target/ 3 | test-output/ 4 | 5 | # Package Files # 6 | *.jar 7 | *.war 8 | *.ear 9 | 10 | bin/ 11 | 12 | # IDE Files # 13 | .classpath 14 | .project 15 | .settings/ 16 | .idea/ 17 | *.iml 18 | 19 | .DS_Store 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![License](http://img.shields.io/:license-Apache%202-green.svg)](http://www.apache.org/licenses/LICENSE-2.0.txt) 2 | [![Github CI](https://github.com/resteasy/resteasy-spring-boot/actions/workflows/ci.yml/badge.svg)](https://github.com/resteasy/resteasy-spring-boot/actions) 3 | 4 | # RESTEasy Spring Boot Starter 5 | 6 | The RESTEasy Spring Boot starters can be used by any regular Spring Boot application that wants to have REST endpoints and prefers RESTEasy as the JAX-RS implementation. They integrate with Spring as expected, which means every JAX-RS REST resource that is also a Spring bean will be automatically auto-scanned, integrated, and available. There are two flavors of starters available that provide integration with the following types of application servers: 7 | - Servlet based (e.g. Tomcat) 8 | - Reactor Netty 9 | 10 | ## Features 11 | * Enables RESTEasy for Spring Boot applications 12 | * Supports JAX-RS providers, resources and sub-resources as Spring beans 13 | * Supports automatic discovery and registration of multiple [JAX-RS Application](http://docs.oracle.com/javaee/7/api/javax/ws/rs/core/Application.html) classes as Spring beans 14 | * Supports optional registration of [JAX-RS Application](http://docs.oracle.com/javaee/7/api/javax/ws/rs/core/Application.html) classes via class-path scanning, or manually, via configuration properties (or YAML) file 15 | * Leverages and supports RESTEasy configuration 16 | * Supports RESTEasy Asynchronous Job Service 17 | * Servlet based server integration 18 | * Reactor Netty integration 19 | 20 | *This project has been kindly donated by PayPal. Please refer to https://github.com/paypal/resteasy-spring-boot for old versions.* 21 | 22 | ## Quick start 23 | 24 | ### Adding POM dependency 25 | Add the Maven dependency below to your Spring Boot application pom file.
26 | 27 | **Servlet** 28 | 29 | ``` xml 30 | 31 | org.jboss.resteasy 32 | resteasy-servlet-spring-boot-starter 33 | 6.3.1-SNAPSHOT 34 | runtime 35 | 36 | ``` 37 | 38 | **Reactor Netty** 39 | 40 | ``` xml 41 | 42 | org.jboss.resteasy 43 | resteasy-reactor-netty-spring-boot-starter 44 | 6.3.1-SNAPSHOT 45 | runtime 46 | 47 | ``` 48 | 49 | ### Registering JAX-RS application classes 50 | Just define your JAX-RS application class (a subclass of [Application](http://docs.oracle.com/javaee/7/api/javax/ws/rs/core/Application.html)) as a Spring bean, and it will be automatically registered. See the example below. 51 | See section _JAX-RS application registration methods_ in [How to use RESTEasy Spring Boot Starter](mds/USAGE.md) for further information. 52 | 53 | ``` java 54 | package com.sample.app; 55 | 56 | import org.springframework.stereotype.Component; 57 | import jakarta.ws.rs.ApplicationPath; 58 | import jakarta.ws.rs.core.Application; 59 | 60 | @Component 61 | @ApplicationPath("/sample-app/") 62 | public class JaxrsApplication extends Application { 63 | } 64 | ``` 65 | 66 | ### Registering JAX-RS resources and providers 67 | Just define them as Spring beans, and they will be automatically registered. 68 | Notice that JAX-RS resources can be singleton or request scoped, while JAX-RS providers must be singletons. 69 | 70 | ### Further information 71 | See [How to use RESTEasy Spring Boot Starter](mds/USAGE.md). 72 | 73 | ## Core Projects 74 | 75 | **Servlet** 76 | 77 | - **resteasy-servlet-spring-boot-starter**: The RESTEasy Spring Boot Starter project for servlet based application servers. 78 | - **resteasy-servlet-spring-boot-sample-app**: A simple Spring Boot application that exposes JAX-RS endpoints as Spring beans using RESTEasy via the RESTEasy Spring Boot servlet starter. 79 | - **resteasy-servlet-spring-boot-starter-test**: Integration tests for the RESTEasy Spring Boot servlet starter. 80 | 81 | 82 | **Reactor Netty** 83 | - **resteasy-reactor-netty-spring-boot-starter**: The RESTEasy Spring Boot Starter project using Reactor Netty as application server. 84 | - **resteasy-reactor-netty-spring-boot-sample-app**: A simple Spring Boot application that exposes JAX-RS endpoints as Spring beans using RESTEasy via the RESTEasy Spring Boot starter using Reactor Netty. 85 | - **resteasy-reactor-netty-spring-boot-starter-test**: Integration tests for the RESTEasy Spring Boot Starter with Reactor Netty as application server. 86 | 87 | 88 | ## Reporting an issue 89 | Please open an issue using [JIRA](https://issues.jboss.org/browse/RESTEASY) (be sure to set *Spring / Spring Boot* in the *Component/s* field). 90 | 91 | ## Contacting us 92 | To contact us, please use RESTEasy [mailing lists](http://resteasy.jboss.org/mailinglists). 93 | 94 | ## License 95 | This project is licensed under the [Apache 2 License](License.html). 96 | -------------------------------------------------------------------------------- /common/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 4.0.0 7 | 8 | 9 | org.jboss.resteasy 10 | resteasy-spring-boot-starter-parent 11 | 6.3.1-SNAPSHOT 12 | ../pom.xml 13 | 14 | 15 | resteasy-spring-boot-starter-common-parent 16 | pom 17 | 18 | 19 | resteasy-spring-boot-starter-common 20 | resteasy-spring-boot-starter-sample-app-common 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /common/resteasy-spring-boot-starter-common/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 4.0.0 7 | 8 | 9 | org.jboss.resteasy 10 | resteasy-spring-boot-starter-common-parent 11 | 6.3.1-SNAPSHOT 12 | ../pom.xml 13 | 14 | 15 | resteasy-spring-boot-starter-common 16 | jar 17 | 18 | 19 | 20 | 21 | org.springframework.boot 22 | spring-boot-starter-parent 23 | ${springboot.version} 24 | pom 25 | import 26 | 27 | 28 | ${project.groupId} 29 | resteasy-spring-boot-bom 30 | ${project.version} 31 | pom 32 | import 33 | 34 | 35 | 36 | 37 | 38 | 39 | org.springframework.boot 40 | spring-boot-autoconfigure 41 | 42 | 43 | org.jboss.resteasy.spring 44 | resteasy-spring 45 | 46 | 47 | org.jboss.resteasy 48 | resteasy-core 49 | 50 | 51 | org.jboss.resteasy 52 | resteasy-core-spi 53 | 54 | 55 | org.slf4j 56 | slf4j-api 57 | 58 | 59 | 60 | 61 | 62 | 63 | org.apache.maven.plugins 64 | maven-deploy-plugin 65 | 66 | false 67 | 68 | 69 | 70 | org.apache.maven.plugins 71 | maven-enforcer-plugin 72 | 73 | 74 | 75 | 76 | -------------------------------------------------------------------------------- /common/resteasy-spring-boot-starter-common/src/main/java/org/jboss/resteasy/springboot/common/DeploymentCustomizer.java: -------------------------------------------------------------------------------- 1 | package org.jboss.resteasy.springboot.common; 2 | 3 | import java.util.Objects; 4 | import org.jboss.resteasy.core.AsynchronousDispatcher; 5 | import org.jboss.resteasy.core.ResourceMethodRegistry; 6 | import org.jboss.resteasy.core.SynchronousDispatcher; 7 | import org.jboss.resteasy.plugins.spring.SpringBeanProcessor; 8 | import org.jboss.resteasy.spi.ResteasyDeployment; 9 | import org.jboss.resteasy.spi.ResteasyProviderFactory; 10 | 11 | /** 12 | * The main function of this class is to prepare, configure and initialize the core components of a RESTEasy deployment. 13 | */ 14 | public class DeploymentCustomizer { 15 | 16 | /** 17 | * Configures and initializes a resteasy deployment with:
18 | * - A {@code org.jboss.resteasy.spi.Dispatcher}
19 | * - A {@code org.jboss.resteasy.spi.ResteasyProviderFactory}
20 | * - A {@code org.jboss.resteasy.core.ResourceMethodRegistry} 21 | * 22 | * @param resteasySpringBeanProcessor 23 | * - The spring bean processor to acquire the provider and resource factories from. 24 | * @param deployment 25 | * - The deployment to customize. 26 | * @param enableAsyncJob 27 | * - Indicates whether the async job service should be enabled. 28 | */ 29 | public static void customizeRestEasyDeployment(SpringBeanProcessor resteasySpringBeanProcessor, ResteasyDeployment deployment, boolean enableAsyncJob) { 30 | 31 | Objects.requireNonNull(resteasySpringBeanProcessor); 32 | Objects.requireNonNull(deployment); 33 | 34 | final ResteasyProviderFactory resteasyProviderFactory = resteasySpringBeanProcessor.getProviderFactory(); 35 | final ResourceMethodRegistry resourceMethodRegistry = (ResourceMethodRegistry) resteasySpringBeanProcessor.getRegistry(); 36 | 37 | deployment.setProviderFactory(resteasyProviderFactory); 38 | deployment.setRegistry(resourceMethodRegistry); 39 | 40 | if(enableAsyncJob) { 41 | deployment.setAsyncJobServiceEnabled(true); 42 | final AsynchronousDispatcher dispatcher = new AsynchronousDispatcher(resteasyProviderFactory, resourceMethodRegistry); 43 | deployment.setDispatcher(dispatcher); 44 | } else { 45 | final SynchronousDispatcher dispatcher = new SynchronousDispatcher(resteasyProviderFactory, resourceMethodRegistry); 46 | deployment.setDispatcher(dispatcher); 47 | } 48 | 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /common/resteasy-spring-boot-starter-common/src/main/java/org/jboss/resteasy/springboot/common/JaxrsApplicationScanner.java: -------------------------------------------------------------------------------- 1 | package org.jboss.resteasy.springboot.common; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | import org.springframework.beans.factory.config.BeanDefinition; 6 | import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider; 7 | import org.springframework.core.type.filter.AssignableTypeFilter; 8 | import org.springframework.util.ClassUtils; 9 | 10 | import jakarta.ws.rs.core.Application; 11 | import java.util.*; 12 | 13 | /** 14 | * Helper class to scan the classpath under the specified packages 15 | * searching for JAX-RS Application sub-classes 16 | * 17 | * @author Fabio Carvalho (facarvalho@paypal.com or fabiocarvalho777@gmail.com) 18 | */ 19 | public abstract class JaxrsApplicationScanner { 20 | 21 | private static final Logger logger = LoggerFactory.getLogger(JaxrsApplicationScanner.class); 22 | 23 | private static Map>> packagesToClassesMap = new HashMap<>(); 24 | 25 | public static Set> getApplications(List packagesToBeScanned) { 26 | final String packagesKey = createPackagesKey(packagesToBeScanned); 27 | if(!packagesToClassesMap.containsKey(packagesKey)) { 28 | packagesToClassesMap.put(packagesKey, findJaxrsApplicationClasses(packagesToBeScanned)); 29 | } 30 | 31 | return packagesToClassesMap.get(packagesKey); 32 | } 33 | 34 | private static String createPackagesKey(List packagesToBeScanned) { 35 | return String.join(",", packagesToBeScanned); 36 | } 37 | 38 | /* 39 | * Scan the classpath under the specified packages looking for JAX-RS Application sub-classes 40 | */ 41 | private static Set> findJaxrsApplicationClasses(List packagesToBeScanned) { 42 | logger.info("Scanning classpath to find JAX-RS Application classes"); 43 | 44 | ClassPathScanningCandidateComponentProvider scanner = new ClassPathScanningCandidateComponentProvider(false); 45 | scanner.addIncludeFilter(new AssignableTypeFilter(Application.class)); 46 | 47 | Set candidates = new HashSet(); 48 | Set candidatesSubSet; 49 | 50 | for (String packageToScan : packagesToBeScanned) { 51 | candidatesSubSet = scanner.findCandidateComponents(packageToScan); 52 | candidates.addAll(candidatesSubSet); 53 | } 54 | 55 | Set> classes = new HashSet>(); 56 | ClassLoader classLoader = JaxrsApplicationScanner.class.getClassLoader(); 57 | Class type; 58 | for (BeanDefinition candidate : candidates) { 59 | try { 60 | type = (Class) ClassUtils.forName(candidate.getBeanClassName(), classLoader); 61 | classes.add(type); 62 | } catch (ClassNotFoundException e) { 63 | logger.error("JAX-RS Application subclass could not be loaded", e); 64 | } 65 | } 66 | 67 | // We don't want the JAX-RS Application class itself in there 68 | classes.remove(Application.class); 69 | 70 | return classes; 71 | } 72 | 73 | } 74 | -------------------------------------------------------------------------------- /common/resteasy-spring-boot-starter-common/src/main/java/org/jboss/resteasy/springboot/common/ResteasyBeanProcessorFactory.java: -------------------------------------------------------------------------------- 1 | package org.jboss.resteasy.springboot.common; 2 | 3 | import org.jboss.resteasy.core.ResourceMethodRegistry; 4 | import org.jboss.resteasy.core.providerfactory.ResteasyProviderFactoryImpl; 5 | import org.jboss.resteasy.plugins.spring.SpringBeanProcessor; 6 | import org.jboss.resteasy.spi.ResteasyProviderFactory; 7 | import org.slf4j.Logger; 8 | import org.slf4j.LoggerFactory; 9 | 10 | 11 | /** 12 | * Class that creates a spring bean processor for Resteasy. See 13 | * {@link org.jboss.resteasy.plugins.spring.SpringBeanProcessor}. 14 | */ 15 | public class ResteasyBeanProcessorFactory { 16 | 17 | private static final Logger logger = LoggerFactory.getLogger(ResteasyBeanProcessorFactory.class); 18 | 19 | public static SpringBeanProcessor resteasySpringBeanProcessor() { 20 | ResteasyProviderFactory resteasyProviderFactory = new ResteasyProviderFactoryImpl(); 21 | ResourceMethodRegistry resourceMethodRegistry = new ResourceMethodRegistry(resteasyProviderFactory); 22 | 23 | SpringBeanProcessor resteasySpringBeanProcessor = new SpringBeanProcessor(); 24 | resteasySpringBeanProcessor.setProviderFactory(resteasyProviderFactory); 25 | resteasySpringBeanProcessor.setRegistry(resourceMethodRegistry); 26 | 27 | logger.debug("Resteasy Spring Bean Processor has been created"); 28 | 29 | return resteasySpringBeanProcessor; 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /common/resteasy-spring-boot-starter-sample-app-common/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 4.0.0 7 | 8 | 9 | org.jboss.resteasy 10 | resteasy-spring-boot-starter-common-parent 11 | 6.3.1-SNAPSHOT 12 | ../pom.xml 13 | 14 | 15 | resteasy-spring-boot-starter-sample-app-common 16 | jar 17 | 18 | 19 | 20 | 21 | org.springframework.boot 22 | spring-boot-starter-parent 23 | ${springboot.version} 24 | pom 25 | import 26 | 27 | 28 | ${project.groupId} 29 | resteasy-spring-boot-bom 30 | ${project.version} 31 | pom 32 | import 33 | 34 | 35 | 36 | 37 | 38 | 39 | org.springframework 40 | spring-context 41 | 42 | 43 | org.jboss.resteasy.spring 44 | resteasy-spring 45 | 46 | 47 | org.jboss.resteasy 48 | resteasy-core 49 | 50 | 51 | org.jboss.resteasy 52 | resteasy-core-spi 53 | 54 | 55 | org.jboss.resteasy 56 | resteasy-client 57 | 58 | 59 | org.slf4j 60 | slf4j-api 61 | 62 | 63 | jakarta.inject 64 | jakarta.inject-api 65 | ${version.jakarta.inject.api} 66 | 67 | 68 | 69 | 70 | 71 | 72 | org.apache.maven.plugins 73 | maven-deploy-plugin 74 | 75 | false 76 | 77 | 78 | 79 | org.apache.maven.plugins 80 | maven-enforcer-plugin 81 | 82 | 83 | 84 | 85 | -------------------------------------------------------------------------------- /common/resteasy-spring-boot-starter-sample-app-common/src/main/java/org/jboss/resteasy/springboot/common/sample/bean/CustomSingletonBean.java: -------------------------------------------------------------------------------- 1 | package org.jboss.resteasy.springboot.common.sample.bean; 2 | 3 | import org.springframework.stereotype.Component; 4 | 5 | /** 6 | * This singleton Spring bean just exists to 7 | * test that injections work normally in 8 | * JAX-RS provider beans 9 | * 10 | * @author Fabio Carvalho (facarvalho@paypal.com or fabiocarvalho777@gmail.com) 11 | */ 12 | @Component 13 | public class CustomSingletonBean { 14 | 15 | public boolean amIAlive() { 16 | return true; 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /common/resteasy-spring-boot-starter-sample-app-common/src/main/java/org/jboss/resteasy/springboot/common/sample/configuration/CustomExceptionMapper.java: -------------------------------------------------------------------------------- 1 | package org.jboss.resteasy.springboot.common.sample.configuration; 2 | 3 | import org.jboss.resteasy.springboot.common.sample.bean.CustomSingletonBean; 4 | import org.springframework.beans.factory.annotation.Autowired; 5 | import org.springframework.stereotype.Component; 6 | 7 | import jakarta.ws.rs.NotFoundException; 8 | import jakarta.ws.rs.core.MediaType; 9 | import jakarta.ws.rs.core.Response; 10 | import jakarta.ws.rs.ext.ExceptionMapper; 11 | import jakarta.ws.rs.ext.Provider; 12 | 13 | /** 14 | * Custom exception mapper for 404 cases. 15 | * 16 | * @author Fabio Carvalho (facarvalho@paypal.com or fabiocarvalho777@gmail.com) 17 | */ 18 | @Component 19 | @Provider 20 | public class CustomExceptionMapper implements ExceptionMapper { 21 | 22 | @Autowired 23 | private CustomSingletonBean customSingletonBean; 24 | 25 | public Response toResponse(NotFoundException exception) { 26 | 27 | // This will cause a NPE if this bean couldn't be injected, 28 | // and that is all we want to check. No need for assertions here 29 | customSingletonBean.amIAlive(); 30 | 31 | Response.ResponseBuilder responseBuilder = Response.status(Response.Status.NOT_FOUND).entity("The resource you've requested, has not been found!"); 32 | responseBuilder.type(MediaType.TEXT_PLAIN); 33 | return responseBuilder.build(); 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /common/resteasy-spring-boot-starter-sample-app-common/src/main/java/org/jboss/resteasy/springboot/common/sample/configuration/SampleSSLContextFactory.java: -------------------------------------------------------------------------------- 1 | package org.jboss.resteasy.springboot.common.sample.configuration; 2 | 3 | import java.io.InputStream; 4 | import java.security.KeyStore; 5 | import javax.net.ssl.SSLContext; 6 | import org.apache.http.ssl.SSLContextBuilder; 7 | 8 | public class SampleSSLContextFactory { 9 | 10 | /** 11 | * Loads and initialize an SSLContext 12 | * @param keystoreFileName - A file name for the keystore. 13 | * @param keystoreSecret - Secret to use for reading the keystore file. 14 | * @return - The SSLContext created. 15 | */ 16 | public static SSLContext sslContext(final String keystoreFileName, final String keystoreSecret) { 17 | 18 | final char[] password = keystoreSecret.toCharArray(); 19 | 20 | try { 21 | final KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType()); 22 | final InputStream stream = ClassLoader.getSystemResourceAsStream(keystoreFileName); 23 | 24 | keyStore.load(stream, password); 25 | 26 | return SSLContextBuilder.create() 27 | .loadKeyMaterial(keyStore, password) 28 | .build(); 29 | 30 | } catch (Exception e) { 31 | throw new RuntimeException(e); 32 | } 33 | 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /common/resteasy-spring-boot-starter-sample-app-common/src/main/java/org/jboss/resteasy/springboot/common/sample/filter/CustomContainerResponseFilter.java: -------------------------------------------------------------------------------- 1 | package org.jboss.resteasy.springboot.common.sample.filter; 2 | 3 | import org.jboss.resteasy.springboot.common.sample.bean.CustomSingletonBean; 4 | import org.springframework.beans.factory.annotation.Autowired; 5 | import org.springframework.stereotype.Component; 6 | 7 | import jakarta.ws.rs.container.ContainerRequestContext; 8 | import jakarta.ws.rs.container.ContainerResponseContext; 9 | import jakarta.ws.rs.container.ContainerResponseFilter; 10 | import jakarta.ws.rs.ext.Provider; 11 | import java.io.IOException; 12 | 13 | /** 14 | * Custom container request filter just to 15 | * exercise Spring beans as providers and, 16 | * specifically, in this case, as a filter. 17 | * 18 | * @author Fabio Carvalho (facarvalho@paypal.com or fabiocarvalho777@gmail.com) 19 | */ 20 | @Component 21 | @Provider 22 | public class CustomContainerResponseFilter implements ContainerResponseFilter { 23 | 24 | @Autowired 25 | private CustomSingletonBean customSingletonBean; 26 | 27 | public void filter(ContainerRequestContext requestContext, ContainerResponseContext responseContext) throws IOException { 28 | 29 | // This will cause a NPE if this bean couldn't be injected, 30 | // and that is all we want to check. No need for assertions here 31 | customSingletonBean.amIAlive(); 32 | 33 | // Checks if request has a HTTP header named "ping". 34 | // If it does, adds an HTTP header named "pong" to the response. 35 | // The header value is irrelevant. 36 | if(requestContext.getHeaderString("ping") != null) { 37 | responseContext.getHeaders().add("pong", "pong"); 38 | } 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /common/resteasy-spring-boot-starter-sample-app-common/src/main/java/org/jboss/resteasy/springboot/common/sample/resources/Echo.java: -------------------------------------------------------------------------------- 1 | package org.jboss.resteasy.springboot.common.sample.resources; 2 | 3 | import org.springframework.beans.factory.annotation.Autowired; 4 | import org.springframework.stereotype.Component; 5 | 6 | import jakarta.validation.constraints.NotEmpty; 7 | import jakarta.ws.rs.Consumes; 8 | import jakarta.ws.rs.POST; 9 | import jakarta.ws.rs.Path; 10 | import jakarta.ws.rs.Produces; 11 | import jakarta.ws.rs.core.MediaType; 12 | 13 | /** 14 | * Echo REST endpoint class 15 | * 16 | * @author Fabio Carvalho (facarvalho@paypal.com or fabiocarvalho777@gmail.com) 17 | */ 18 | @Path("/echo") 19 | @Component 20 | public class Echo { 21 | 22 | @Autowired 23 | private IEchoMessageCreator echoer; 24 | 25 | /** 26 | * Receives a simple POST request message containing as payload 27 | * a text, in text plain format, to be echoed by the service. 28 | * It returns as response, in JSON, the text to be echoed plus a timestamp of the 29 | * moment the echo response was created on the server side 30 | * 31 | * @param echoText 32 | * @return 33 | */ 34 | @POST 35 | @Consumes({MediaType.TEXT_PLAIN}) 36 | @Produces({MediaType.APPLICATION_JSON}) 37 | public EchoMessage echo(@NotEmpty(message = "must not be empty") String echoText) { 38 | return echoer.createEchoMessage(echoText); 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /common/resteasy-spring-boot-starter-sample-app-common/src/main/java/org/jboss/resteasy/springboot/common/sample/resources/EchoMessage.java: -------------------------------------------------------------------------------- 1 | package org.jboss.resteasy.springboot.common.sample.resources; 2 | 3 | /** 4 | * A simple echo message, containing the text to be echoed 5 | * and timestamp of the moment the message was created 6 | * 7 | * @author Fabio Carvalho (facarvalho@paypal.com or fabiocarvalho777@gmail.com) 8 | */ 9 | public class EchoMessage { 10 | 11 | private long timestamp; 12 | private String echoText; 13 | 14 | public EchoMessage(String echoText) { 15 | timestamp = System.currentTimeMillis(); 16 | this.echoText = echoText; 17 | } 18 | 19 | public long getTimestamp() { 20 | return timestamp; 21 | } 22 | 23 | public String getEchoText() { 24 | return echoText; 25 | } 26 | 27 | } -------------------------------------------------------------------------------- /common/resteasy-spring-boot-starter-sample-app-common/src/main/java/org/jboss/resteasy/springboot/common/sample/resources/Foo.java: -------------------------------------------------------------------------------- 1 | package org.jboss.resteasy.springboot.common.sample.resources; 2 | 3 | 4 | import org.springframework.context.annotation.Bean; 5 | import org.springframework.context.annotation.Configuration; 6 | import org.springframework.stereotype.Component; 7 | 8 | import jakarta.inject.Inject; 9 | import jakarta.ws.rs.GET; 10 | import jakarta.ws.rs.Path; 11 | import jakarta.ws.rs.Produces; 12 | import jakarta.ws.rs.core.MediaType; 13 | 14 | @Component 15 | @Path("foo") 16 | public class Foo { 17 | 18 | private Random random; 19 | 20 | @Inject 21 | private IEchoMessageCreator echoer; 22 | 23 | 24 | public Foo(Random random) { 25 | this.random = random; 26 | } 27 | 28 | @GET 29 | @Produces({ MediaType.APPLICATION_JSON }) 30 | public EchoMessage echoRandom() { 31 | return echoer.createEchoMessage(Integer.toString(random.random)); 32 | } 33 | 34 | } 35 | 36 | class Random { 37 | public int random = new java.util.Random().nextInt(); 38 | } 39 | 40 | @Configuration 41 | class Config { 42 | @Bean 43 | public static Random mkRandom() { 44 | return new Random(); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /common/resteasy-spring-boot-starter-sample-app-common/src/main/java/org/jboss/resteasy/springboot/common/sample/resources/IEchoMessageCreator.java: -------------------------------------------------------------------------------- 1 | package org.jboss.resteasy.springboot.common.sample.resources; 2 | 3 | /** 4 | * This bean creates {@link EchoMessage} objects based on 5 | * echo texts received as input 6 | * 7 | * @author Fabio Carvalho (facarvalho@paypal.com or fabiocarvalho777@gmail.com) 8 | */ 9 | public interface IEchoMessageCreator { 10 | 11 | default public EchoMessage createEchoMessage(final String echoText) { 12 | return new EchoMessage(echoText); 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /common/resteasy-spring-boot-starter-sample-app-common/src/main/java/org/jboss/resteasy/springboot/common/utils/SocketUtils.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2022-2022 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.jboss.resteasy.springboot.common.utils; 17 | 18 | 19 | import java.net.InetAddress; 20 | import java.net.ServerSocket; 21 | import java.util.Random; 22 | 23 | import javax.net.ServerSocketFactory; 24 | 25 | 26 | // Spring has removed SockUtils: https://github.com/onobc/spring-cloud-function/blob/2fb90d5a35f6db2178cdb8be4ad9815cacb7cd84/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/web/TestSocketUtils.java 27 | // So we use a copy here: 28 | /** 29 | * Simple test utility to find a random available TCP port. 30 | *

Inspired by the now removed {@code org.springframework.util.SocketUtils} and is only used in a testing capacity. 31 | * 32 | * @author Chris Bono 33 | * @deprecated will soon be removed or consolidated - do not use further 34 | */ 35 | @Deprecated 36 | public final class SocketUtils { 37 | 38 | private static final Random random = new Random(System.nanoTime()); 39 | 40 | /** 41 | * Find an available TCP port randomly selected from the range {@code 1024-65535}. 42 | * 43 | * @return an available TCP port number 44 | * @throws IllegalStateException if no available port could be found 45 | */ 46 | public static int findAvailableTcpPort() { 47 | int minPort = 1024; 48 | int maxPort = 65535; 49 | int portRange = maxPort - minPort; 50 | int candidatePort; 51 | int searchCounter = 0; 52 | do { 53 | if (searchCounter > portRange) { 54 | throw new IllegalStateException(String.format( 55 | "Could not find an available TCP port after %d attempts", searchCounter)); 56 | } 57 | candidatePort = minPort + random.nextInt(portRange + 1); 58 | searchCounter++; 59 | } 60 | while (!isPortAvailable(candidatePort)); 61 | 62 | return candidatePort; 63 | } 64 | 65 | private static boolean isPortAvailable(int port) { 66 | try { 67 | ServerSocket serverSocket = ServerSocketFactory.getDefault().createServerSocket( 68 | port, 1, InetAddress.getByName("localhost")); 69 | serverSocket.close(); 70 | return true; 71 | } catch (Exception ex) { 72 | return false; 73 | } 74 | } 75 | } -------------------------------------------------------------------------------- /mds/USAGE.md: -------------------------------------------------------------------------------- 1 | # How to use RESTEasy Spring Boot Starter 2 | 3 | #### Adding POM dependency 4 | 5 | Add one of the following Maven dependencies to your Spring Boot application pom file.
6 | 7 | **Servlet** 8 | 9 | ``` xml 10 | 11 | org.jboss.resteasy 12 | resteasy-servlet-spring-boot-starter 13 | 6.3.1-SNAPSHOT 14 | runtime 15 | 16 | ``` 17 | 18 | **Reactor Netty** 19 | 20 | ``` xml 21 | 22 | org.jboss.resteasy 23 | resteasy-reactor-netty-spring-boot-starter 24 | 6.3.1-SNAPSHOT 25 | runtime 26 | 27 | ``` 28 | 29 | #### Registering JAX-RS application classes 30 | 31 | Just define your JAX-RS application class (a subclass of [Application](https://github.com/eclipse-ee4j/jaxrs-api/blob/master/jaxrs-api/src/main/java/jakarta/ws/rs/core/Application.java)) as a Spring bean, and it will be automatically registered. See the example below. 32 | See section [JAX-RS Application, Resources and Sub-Resources](https://eclipse-ee4j.github.io/jersey.github.io/documentation/latest/jaxrs-resources.html) for further information. 33 | 34 | ``` java 35 | package com.sample.app; 36 | 37 | import org.springframework.stereotype.Component; 38 | import jakarta.ws.rs.ApplicationPath; 39 | import jakarta.ws.rs.core.Application; 40 | 41 | @Component 42 | @ApplicationPath("/sample-app/") 43 | public class JaxrsApplication extends Application { 44 | } 45 | ``` 46 | 47 | #### Registering JAX-RS resources and providers 48 | 49 | Just define them as Spring beans, and they will be automatically registered. 50 | Notice that JAX-RS resources can be singleton or request scoped, while JAX-RS providers must be singletons. 51 | 52 | ## Advanced topics 53 | 54 | #### JAX-RS application registration methods 55 | 56 | JAX-RS applications are defined via sub-classes of [Application](https://github.com/eclipse-ee4j/jaxrs-api/blob/master/jaxrs-api/src/main/java/jakarta/ws/rs/core/Application.java). One or more JAX-RS applications can be registered, and there are three different methods to do so: 57 | 58 | 1. By having them defined as Spring beans. 59 | 2. By setting property `resteasy.jaxrs.app.classes` via Spring Boot configuration file (properties or YAML). This property should contain a comma separated list of JAX-RS sub-classes. 60 | 3. Automatically by classpath scanning (looking for `jakarta.ws.rs.core.Application` sub-classes). **See important note number 6 about this method**. 61 | 62 | You can define the method you prefer by setting property `resteasy.jaxrs.app.registration` (via Spring Boot configuration file), although you don't have to, in that case the `auto` method is the default. The possible values are: 63 | 64 | - `beans` 65 | - `property` 66 | - `scanning` 67 | - `auto` (default) 68 | 69 | The first three values refer respectively to each one of the three methods described earlier. The last one, `auto`, when set (or when property `resteasy.jaxrs.app.registration` is not present), attempts first to find JAX-RS application classes by searching them as Spring beans. If any is found, the search stops, and those are the only JAX-RS applications to be registered. If no JAX-RS application Spring beans are found, then the `property` approach is tried. If still no JAX-RS application classes could be found, then the last method, `scanning`, is attempted. If after that still no JAX-RS application class could be registered, then a default one will be automatically created mapping to `/*` (_according to section 2.3.2 in the JAX-RS 2.0 specification_). 70 | 71 | __Important notes__ 72 | 73 | - If no JAX-RS application classes are found, a default one will be automatically created mapping to `/*` (_according to section 2.3.2 in the JAX-RS 2.0 specification_). Notice that, in this case, if you have any other Servlet in your application, their URL matching might conflict. For example, if you have Spring Boot actuator and its mapped to `/`, its endpoints might not be reachable. 74 | - It is recommended to always have at least one JAX-RS application class. 75 | - A JAX-RS application class with no `jakarta.ws.rs.ApplicationPath` annotation will not be registered, unless `resteasy.servlet.mapping.prefix` is specified. 76 | - Avoid setting the JAX-RS application base URI to simply `/` to prevent URI conflicts, as explained in item 1. 77 | - Property `resteasy.jaxrs.app` was deprecated in version *2.2.0-RELEASE* (see [issue 35](https://github.com/paypal/resteasy-spring-boot/issues/35)) 78 | and replaced `resteasy.jaxrs.app.classes`. Property `resteasy.jaxrs.app` has been fully removed from version 4.0.1.Final. 79 | - Starting on version 3.0.0, the behavior of the `scanning` JAX-RS Application subclass registration method will change, being more restrictive. Instead of scanning the whole classpath, it will scan only packages registered to be scanned by Spring framework (regardless of the JAX-RS Application subclass being a Spring bean or not). The reason is to improve application startup performance. Having said that, it is recommended that every application use any method, other than `scanning`. Or, if using `scanning`, make sure your JAX-RS Application subclass is under a package to be scanned by Spring framework. If not, starting on version 3.0.0,it won't be found. 80 | - When no JAX-RS Application is configured, the property `resteasy.jaxrs.defaultPath` can be used to define the base path. It defaults to `/` if not set 81 | - `resteasy.servlet.mapping.prefix` will override `jakarta.ws.rs.ApplicationPath` if both are specified. 82 | 83 | #### RESTEasy Servlet Configuration 84 | 85 | RESTEasy offers a few configuration switches, [as seen here](https://docs.jboss.org/resteasy/docs/4.7.0.Final/userguide/html_single/index.html#configuration_switches), and they are set as Servlet context init parameters. In Spring Boot, Servlet context init parameters are defined via Spring Boot `application.properties` file, using the property prefix `server.servlet.context-parameters.*` (search for it in [Spring Boot reference guide](http://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/)).
86 | 87 | As an example, to set RESTEasy property `resteasy.role.based.security` to `true`, just add the property bellow to Spring Boot `application.properties` file. 88 | 89 | ``` 90 | server.servlet.context-parameters.resteasy.role.based.security=true 91 | ``` 92 | 93 | It is important to mention that the following RESTEasy configuration options are NOT applicable to an application using RESTEasy Spring Boot starter. 94 | All other RESTEasy configuration options are supported normally. 95 | 96 | | Configuration option | Why it is not applicable | 97 | |---|---| 98 | |`jakarta.ws.rs.Application`|JAX-RS application classes are registered as explained in section _"JAX-RS application registration methods"_ above| 99 | |`resteasy.scan`
`resteasy.scan.providers`
`resteasy.scan.resources`
`resteasy.providers`
`resteasy.use.builtin.providers`
`resteasy.resources`
`resteasy.jndi.resources`|All JAX-RS resources and providers are always supposed to be Spring beans, and they are automatically discovered| 100 | 101 | 102 | #### Reactor Netty Configuration 103 | 104 | The following parameters can be set in `application.properties` to configure Reactor Netty: 105 | 106 | | Parameter | Description | 107 | |---|---| 108 | |`server.port` | Port to use for running reactor netty server. Default is `8080`. Set to `0` to let Reactor Netty choose any available| 109 | 110 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 4.0.0 7 | 8 | 9 | org.jboss 10 | jboss-parent 11 | 49 12 | 13 | 14 | org.jboss.resteasy 15 | resteasy-spring-boot-starter-parent 16 | 6.3.1-SNAPSHOT 17 | pom 18 | 19 | 20 | 17 21 | 17 22 | 17 23 | 24 | 25 | UTF-8 26 | UTF-8 27 | 3.2.0.Final 28 | 6.2.12.Final 29 | 3.4.0 30 | 7.11.0 31 | 5.1.3.Final 32 | 34.0.0.Final 33 | 4.2.1.Final 34 | 2.0.1.MR 35 | 4.0.2 36 | 6.2.1 37 | 5.17.0 38 | 5.2.0 39 | 40 | 41 | 42 | common 43 | servlet 44 | reactor-netty 45 | resteasy-spring-boot-bom 46 | 47 | 48 | 49 | 50 | jboss-releases-repository 51 | JBoss Releases Repository 52 | https://repository.jboss.org/nexus/service/local/staging/deploy/maven2/ 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | true 61 | never 62 | 63 | 64 | true 65 | never 66 | 67 | jboss-public-repository-group 68 | JBoss Public Repository Group 69 | https://repository.jboss.org/nexus/content/groups/public/ 70 | default 71 | 72 | 73 | spring-repo 74 | Spring Repository 75 | https://repo.spring.io/milestone 76 | 77 | 78 | 79 | 80 | 81 | spring-repo 82 | Spring Repository 83 | https://repo.spring.io/milestone 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | org.apache.maven.plugins 92 | maven-enforcer-plugin 93 | 94 | 95 | dep-convergence 96 | 97 | enforce 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | org.apache.maven.plugins 109 | maven-deploy-plugin 110 | 111 | false 112 | 113 | 114 | 115 | 116 | 117 | 118 | org.apache.maven.plugins 119 | maven-install-plugin 120 | 121 | false 122 | 123 | 124 | 125 | org.apache.maven.plugins 126 | maven-deploy-plugin 127 | 128 | 129 | org.apache.maven.plugins 130 | maven-compiler-plugin 131 | 132 | true 133 | true 134 | 135 | -parameters 136 | 137 | 138 | 139 | 140 | 141 | 142 | -------------------------------------------------------------------------------- /reactor-netty/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 4.0.0 7 | 8 | 9 | org.jboss.resteasy 10 | resteasy-spring-boot-starter-parent 11 | 6.3.1-SNAPSHOT 12 | ../pom.xml 13 | 14 | 15 | resteasy-reactor-netty-spring-boot-starter-parent 16 | pom 17 | 18 | 19 | resteasy-reactor-netty-spring-boot-starter 20 | resteasy-reactor-netty-spring-boot-starter-test 21 | resteasy-reactor-netty-spring-boot-sample-app 22 | resteasy-reactor-netty-spring-boot-sample-app-no-jaxrs 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /reactor-netty/resteasy-reactor-netty-spring-boot-sample-app-no-jaxrs/README.md: -------------------------------------------------------------------------------- 1 | ## Usage 2 | 3 | ### Start the server 4 | 5 | ```bash 6 | $ mvn spring-boot:run 7 | ... 8 | 2022-02-22 20:44:30.299 INFO 61839 --- [ main] com.sample.app2.MyApp : Started MyApp in 1.219 seconds (JVM running for 1.401) 9 | ``` 10 | 11 | ## Access the service 12 | 13 | 14 | Use the following command to access the service: 15 | 16 | ```bash 17 | curl --location --request POST 'localhost:8080/echo' \ 18 | --header 'Content-Type: text/plain' \ 19 | --data-raw 'foo' 20 | ``` 21 | 22 | And here is the output: 23 | 24 | ```bash 25 | {"timestamp":1645534458892,"echoText":"foo"} 26 | ``` 27 | 28 | -------------------------------------------------------------------------------- /reactor-netty/resteasy-reactor-netty-spring-boot-sample-app-no-jaxrs/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | 8 | org.jboss.resteasy 9 | resteasy-reactor-netty-spring-boot-starter-parent 10 | 6.3.1-SNAPSHOT 11 | ../pom.xml 12 | 13 | 14 | resteasy-reactor-netty-spring-boot-sample-app-no-jaxrs 15 | jar 16 | 17 | Simple test application for the Resteasy Spring Boot Reactor Netty starter that contains no JAX-RS application class 18 | 19 | 20 | 21 | 22 | org.springframework.boot 23 | spring-boot-starter-parent 24 | ${springboot.version} 25 | pom 26 | import 27 | 28 | 29 | 30 | 31 | 32 | 33 | org.springframework.boot 34 | spring-boot-autoconfigure 35 | 36 | 37 | org.jboss.resteasy 38 | resteasy-spring-boot-starter-sample-app-common 39 | ${project.version} 40 | 41 | 42 | org.jboss.resteasy 43 | resteasy-reactor-netty-spring-boot-starter 44 | ${project.version} 45 | runtime 46 | 47 | 48 | 49 | 50 | 51 | 52 | org.apache.maven.plugins 53 | maven-deploy-plugin 54 | 55 | false 56 | 57 | 58 | 59 | org.springframework.boot 60 | spring-boot-maven-plugin 61 | ${springboot.version} 62 | 63 | 64 | 65 | repackage 66 | 67 | 68 | exec 69 | 70 | 71 | 72 | 73 | 74 | org.apache.maven.plugins 75 | maven-enforcer-plugin 76 | 77 | 78 | 79 | 80 | -------------------------------------------------------------------------------- /reactor-netty/resteasy-reactor-netty-spring-boot-sample-app-no-jaxrs/src/main/java/com/sample/app2/Application.java: -------------------------------------------------------------------------------- 1 | package com.sample.app2; 2 | 3 | import org.jboss.resteasy.springboot.common.sample.resources.IEchoMessageCreator; 4 | import org.springframework.boot.SpringApplication; 5 | import org.springframework.boot.autoconfigure.SpringBootApplication; 6 | import org.springframework.context.annotation.Bean; 7 | 8 | @SpringBootApplication(scanBasePackages = {"org.jboss.resteasy.springboot" }) 9 | public class Application { 10 | 11 | public static void main(String[] args) { 12 | SpringApplication.run(Application.class, args); 13 | } 14 | 15 | @Bean 16 | public IEchoMessageCreator echoMessageCreator() { 17 | return new IEchoMessageCreator() {}; 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /reactor-netty/resteasy-reactor-netty-spring-boot-sample-app/README.md: -------------------------------------------------------------------------------- 1 | # Sample application 2 | 3 | This is a super simple JAX-RS RESTEasy Spring Boot application just to exercise RESTEasy Spring Boot starter.
4 | 5 | ## Starting the application 6 | 7 | You can start the application as you for any other regular Spring Boot application. For example: 8 | 9 | 1. From the command line, under the sample application project, run `mvn spring-boot:run` 10 | 1. From your favorite IDE, run class `com.test.Application` 11 | 12 | ## Testing it 13 | 14 | Send a `POST` request message, containing the payload below, to [http://localhost:8080/sample-app/echo](http://localhost:8080/sample-app/echo). 15 | 16 | ``` 17 | is there anybody out there? 18 | ``` 19 | 20 | You can use the following command to send the request: 21 | 22 | ```bash 23 | $ curl -k --location --request POST 'https://localhost:8443/sample-app/echo' \ 24 | --header 'Content-Type: text/plain' \ 25 | --data-raw 'is there anybody out there?' 26 | ``` 27 | 28 | You should receive a response message with a payload similar to this as result: 29 | 30 | ``` json 31 | { 32 | "timestamp": "1484775122357", 33 | "echoText": "is there anybody out there?" 34 | } 35 | ``` 36 | 37 | The request message payload can be anything as plain text. 38 | The response message is supposed to echo that, plus a timestamp of the moment the echo response was created on the server side. The response message will be in JSON format. 39 | -------------------------------------------------------------------------------- /reactor-netty/resteasy-reactor-netty-spring-boot-sample-app/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | 7 | org.jboss.resteasy 8 | resteasy-reactor-netty-spring-boot-starter-parent 9 | 6.3.1-SNAPSHOT 10 | ../pom.xml 11 | 12 | 13 | resteasy-reactor-netty-spring-boot-sample-app 14 | jar 15 | 16 | Sample test application for the Resteasy Spring Boot Reactor Netty starter 17 | 18 | 19 | 20 | 21 | org.springframework.boot 22 | spring-boot-starter-parent 23 | ${springboot.version} 24 | pom 25 | import 26 | 27 | 28 | ${project.groupId} 29 | resteasy-spring-boot-bom 30 | ${project.version} 31 | pom 32 | import 33 | 34 | 35 | 36 | 37 | 38 | 39 | org.springframework.boot 40 | spring-boot-autoconfigure 41 | 42 | 43 | org.jboss.resteasy 44 | resteasy-spring-boot-starter-sample-app-common 45 | ${project.version} 46 | 47 | 48 | org.jboss.resteasy 49 | resteasy-reactor-netty-spring-boot-starter 50 | ${project.version} 51 | 52 | 53 | 54 | 55 | 56 | 57 | org.springframework.boot 58 | spring-boot-maven-plugin 59 | ${springboot.version} 60 | 61 | 62 | 63 | repackage 64 | 65 | 66 | exec 67 | 68 | 69 | 70 | 71 | 72 | org.apache.maven.plugins 73 | maven-deploy-plugin 74 | 75 | false 76 | 77 | 78 | 79 | org.apache.maven.plugins 80 | maven-enforcer-plugin 81 | 82 | 83 | 84 | 85 | 86 | -------------------------------------------------------------------------------- /reactor-netty/resteasy-reactor-netty-spring-boot-sample-app/src/main/java/com/sample/app/Application.java: -------------------------------------------------------------------------------- 1 | package com.sample.app; 2 | 3 | import org.jboss.resteasy.springboot.reactor.ReactorNettyServerConfig; 4 | import org.jboss.resteasy.springboot.common.sample.configuration.SampleSSLContextFactory; 5 | import org.jboss.resteasy.springboot.common.sample.resources.IEchoMessageCreator; 6 | import org.springframework.boot.SpringApplication; 7 | import org.springframework.boot.autoconfigure.SpringBootApplication; 8 | import org.springframework.context.annotation.Bean; 9 | 10 | import io.netty.handler.ssl.ClientAuth; 11 | /** 12 | * SpringBoot entry point application 13 | */ 14 | @SpringBootApplication(scanBasePackages = { "com.sample.app", "org.jboss.resteasy.springboot"}) 15 | public class Application { 16 | 17 | public static void main(String[] args) { 18 | SpringApplication.run(Application.class, args); 19 | } 20 | 21 | @Bean 22 | public ReactorNettyServerConfig reactorNettyServerConfig() { 23 | return new ReactorNettyServerConfig.Builder() 24 | .withSSLContext(SampleSSLContextFactory 25 | .sslContext("test.keystore", "test123")) 26 | .withClientAuth(ClientAuth.NONE) 27 | .build(); 28 | } 29 | 30 | @Bean 31 | public IEchoMessageCreator echoMessageCreator() { 32 | return new IEchoMessageCreator() {}; 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /reactor-netty/resteasy-reactor-netty-spring-boot-sample-app/src/main/java/com/sample/app/configuration/JaxrsApplication.java: -------------------------------------------------------------------------------- 1 | package com.sample.app.configuration; 2 | 3 | import org.springframework.stereotype.Component; 4 | 5 | import jakarta.ws.rs.ApplicationPath; 6 | import jakarta.ws.rs.core.Application; 7 | 8 | /** 9 | * JAX-RS application 10 | */ 11 | @Component 12 | @ApplicationPath("/sample-app/") 13 | public class JaxrsApplication extends Application {} 14 | -------------------------------------------------------------------------------- /reactor-netty/resteasy-reactor-netty-spring-boot-sample-app/src/main/resources/application.yaml: -------------------------------------------------------------------------------- 1 | resteasy: 2 | jaxrs: 3 | app: 4 | registration: property 5 | classes: com.sample.app.configuration.JaxrsApplication 6 | management: 7 | endpoints: 8 | web: 9 | exposure: 10 | include: 11 | - health 12 | - shutdown 13 | endpoint: 14 | shutdown: 15 | enabled: true 16 | logging: 17 | level: 18 | org: 19 | springframework: info 20 | -------------------------------------------------------------------------------- /reactor-netty/resteasy-reactor-netty-spring-boot-sample-app/src/main/resources/logback.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | %d{HH:mm:ss.SSS} [%thread] %level %logger %msg%n 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /reactor-netty/resteasy-reactor-netty-spring-boot-sample-app/src/main/resources/test.keystore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resteasy/resteasy-spring-boot/1a61abe02dd04c50038b54d433ecb080754f7e94/reactor-netty/resteasy-reactor-netty-spring-boot-sample-app/src/main/resources/test.keystore -------------------------------------------------------------------------------- /reactor-netty/resteasy-reactor-netty-spring-boot-starter-test/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | 7 | org.jboss.resteasy 8 | resteasy-reactor-netty-spring-boot-starter-parent 9 | 6.3.1-SNAPSHOT 10 | ../pom.xml 11 | 12 | 13 | resteasy-reactor-netty-spring-boot-starter-test 14 | ${project.artifactId} 15 | 16 | Integration test project for RESTEasy Spring Boot servlet starter 17 | https://github.com/resteasy/resteasy-spring-boot 18 | 19 | 20 | 21 | The Apache License, Version 2.0 22 | http://www.apache.org/licenses/LICENSE-2.0.txt 23 | 24 | 25 | 26 | 27 | scm:git:git@github.com:resteasy/resteasy-spring-boot.git 28 | scm:git:git@github.com:resteasy/resteasy-spring-boot.git 29 | git@github.com:resteasy/resteasy-spring-boot.git 30 | 31 | 32 | 33 | JIRA 34 | http://jira.jboss.com/jira/browse/RESTEASY 35 | 36 | 37 | 38 | 39 | 40 | org.springframework.boot 41 | spring-boot-starter-parent 42 | ${springboot.version} 43 | pom 44 | import 45 | 46 | 47 | 48 | 49 | 50 | 51 | jakarta.ws.rs 52 | jakarta.ws.rs-api 53 | test 54 | 55 | 56 | org.jboss.resteasy 57 | resteasy-reactor-netty-spring-boot-sample-app 58 | ${project.version} 59 | test 60 | 61 | 62 | org.jboss.resteasy 63 | resteasy-reactor-netty-spring-boot-sample-app-no-jaxrs 64 | ${project.version} 65 | test 66 | 67 | 68 | io.rest-assured 69 | rest-assured 70 | test 71 | 72 | 73 | 74 | org.testng 75 | testng 76 | ${ver.testng} 77 | test 78 | 79 | 80 | org.yaml 81 | snakeyaml 82 | 83 | 84 | 85 | 86 | 87 | jakarta.xml.bind 88 | jakarta.xml.bind-api 89 | 90 | 91 | 92 | 93 | 94 | 95 | org.apache.maven.plugins 96 | maven-enforcer-plugin 97 | 98 | true 99 | 100 | 101 | 102 | org.apache.maven.plugins 103 | maven-deploy-plugin 104 | 105 | 106 | 107 | 108 | -------------------------------------------------------------------------------- /reactor-netty/resteasy-reactor-netty-spring-boot-starter-test/src/test/java/com/sample/app/NonSpringBeanJaxrsApplication1.java: -------------------------------------------------------------------------------- 1 | package com.sample.app; 2 | 3 | import jakarta.ws.rs.ApplicationPath; 4 | import jakarta.ws.rs.core.Application; 5 | 6 | @ApplicationPath("sample-app-test-two") 7 | public class NonSpringBeanJaxrsApplication1 extends Application {} -------------------------------------------------------------------------------- /reactor-netty/resteasy-reactor-netty-spring-boot-starter-test/src/test/java/com/sample/app/NonSpringBeanJaxrsApplication2.java: -------------------------------------------------------------------------------- 1 | package com.sample.app; 2 | 3 | import jakarta.ws.rs.ApplicationPath; 4 | import jakarta.ws.rs.core.Application; 5 | 6 | @ApplicationPath("sample-app-test") 7 | public class NonSpringBeanJaxrsApplication2 extends Application {} -------------------------------------------------------------------------------- /reactor-netty/resteasy-reactor-netty-spring-boot-starter-test/src/test/java/com/sample/app/test/AsyncJobIT.java: -------------------------------------------------------------------------------- 1 | package com.sample.app.test; 2 | 3 | import static io.restassured.RestAssured.given; 4 | import static org.hamcrest.CoreMatchers.notNullValue; 5 | import static org.hamcrest.Matchers.equalTo; 6 | import static org.hamcrest.Matchers.emptyString; 7 | import static org.hamcrest.Matchers.is; 8 | import java.util.Properties; 9 | import org.jboss.resteasy.springboot.reactor.ResteasyAutoConfiguration.ResteasyReactorNettyServerBean; 10 | import org.springframework.boot.SpringApplication; 11 | import org.springframework.context.ConfigurableApplicationContext; 12 | import org.testng.Assert; 13 | import org.testng.annotations.BeforeClass; 14 | import org.testng.annotations.Test; 15 | import com.sample.app.Application; 16 | import io.restassured.RestAssured; 17 | import io.restassured.response.Response; 18 | 19 | /** 20 | * Integration tests for RESTEasy Asynchronous Job Service 21 | */ 22 | public class AsyncJobIT { 23 | 24 | private static final String JAXRS_APP_ASYNC_JOB_ENABLE_PROPERTY = "resteasy.async.job.service.enabled"; 25 | 26 | @BeforeClass 27 | public void setUp() throws InterruptedException { 28 | 29 | // Start app 30 | final Properties properties = new Properties(); 31 | properties.put("server.servlet.context-parameters." + JAXRS_APP_ASYNC_JOB_ENABLE_PROPERTY, true); 32 | System.setProperty(JAXRS_APP_ASYNC_JOB_ENABLE_PROPERTY, "true"); 33 | 34 | final SpringApplication app = new SpringApplication(Application.class); 35 | app.setDefaultProperties(properties); 36 | app.addListeners(new LogbackTestApplicationListener()); 37 | 38 | final ConfigurableApplicationContext appContext = app.run("--server.port=" + 0); 39 | appContext.registerShutdownHook(); 40 | 41 | // Find running port and configure RestAssured 42 | ResteasyReactorNettyServerBean serverBean = appContext.getBean(ResteasyReactorNettyServerBean.class); 43 | 44 | int appPort = 0; 45 | int maxWaitInSec = 5; 46 | 47 | while(appPort == 0 && maxWaitInSec > 0) { 48 | Thread.sleep(1000);//Allow for server to start 49 | maxWaitInSec--; 50 | appPort = serverBean.getServer().getPort(); 51 | } 52 | if(appPort == 0) { 53 | Assert.fail("Coudn't find server configured port within " + maxWaitInSec + " seconds."); 54 | } 55 | RestAssured.basePath = "sample-app"; 56 | RestAssured.port = appPort; 57 | RestAssured.baseURI = "https://localhost"; 58 | RestAssured.useRelaxedHTTPSValidation(); 59 | 60 | } 61 | 62 | @Test 63 | public void regularRequestTest() { 64 | final Response response = given().body("is there anybody out there?").post("/echo"); 65 | response.then().statusCode(200).body("timestamp", notNullValue()).body("echoText", equalTo("is there anybody out there?")); 66 | } 67 | 68 | @Test 69 | public void asyncRequestTest() { 70 | final Response response = given().body("is there anybody out there?").post("/echo?asynch=true"); 71 | response.then().statusCode(202).body(is(emptyString())); 72 | 73 | final String location = response.getHeader("Location"); 74 | final Response response1 = given().basePath("/").get(location + "?wait=1000"); 75 | response1.then().statusCode(200).body("timestamp", notNullValue()).body("echoText", equalTo("is there anybody out there?")); 76 | } 77 | 78 | @Test 79 | public void fireAndForgetRequestTest() { 80 | final Response response = given().body("is there anybody out there?").post("/echo?oneway=true"); 81 | response.then().statusCode(202).body(is(emptyString())); 82 | } 83 | 84 | } 85 | -------------------------------------------------------------------------------- /reactor-netty/resteasy-reactor-netty-spring-boot-starter-test/src/test/java/com/sample/app/test/CommonUseCasesIT.java: -------------------------------------------------------------------------------- 1 | package com.sample.app.test; 2 | 3 | import static io.restassured.RestAssured.given; 4 | import static org.hamcrest.CoreMatchers.any; 5 | import static org.hamcrest.CoreMatchers.equalTo; 6 | import static org.hamcrest.CoreMatchers.hasItems; 7 | import static org.hamcrest.CoreMatchers.notNullValue; 8 | import org.jboss.resteasy.springboot.reactor.ResteasyAutoConfiguration.ResteasyReactorNettyServerBean; 9 | import org.springframework.boot.SpringApplication; 10 | import org.springframework.context.ConfigurableApplicationContext; 11 | import org.testng.Assert; 12 | import org.testng.annotations.BeforeClass; 13 | import org.testng.annotations.Test; 14 | import com.sample.app.Application; 15 | import io.restassured.RestAssured; 16 | import io.restassured.response.Response; 17 | 18 | /** 19 | * This is an integration test based on a simple sample application and 20 | * very common use cases (see sample-app project) 21 | * 22 | */ 23 | public class CommonUseCasesIT { 24 | 25 | @BeforeClass 26 | public void startingApplicationUp() throws InterruptedException { 27 | 28 | // Start application 29 | final SpringApplication springApplication = new SpringApplication(Application.class); 30 | springApplication.addListeners(new LogbackTestApplicationListener()); 31 | final ConfigurableApplicationContext appContext = springApplication.run("--server.port=" + 0); 32 | appContext.registerShutdownHook(); 33 | 34 | // Find running port and configure RestAssured 35 | ResteasyReactorNettyServerBean serverBean = appContext.getBean(ResteasyReactorNettyServerBean.class); 36 | 37 | int appPort = 0; 38 | int maxWaitInSec = 5; 39 | 40 | while(appPort == 0 && maxWaitInSec > 0) { 41 | Thread.sleep(1000);//Allow for server to start 42 | maxWaitInSec--; 43 | appPort = serverBean.getServer().getPort(); 44 | } 45 | if(appPort == 0) { 46 | Assert.fail("Coudn't find server configured port within " + maxWaitInSec + " seconds."); 47 | } 48 | RestAssured.basePath = "sample-app"; 49 | RestAssured.port = appPort; 50 | RestAssured.baseURI = "https://localhost"; 51 | RestAssured.useRelaxedHTTPSValidation(); 52 | } 53 | 54 | @Test 55 | public void happyPathTest() { 56 | final Response response = given().body("is there anybody out there?").post("/echo"); 57 | response.then().statusCode(200).body("timestamp", notNullValue()).body("echoText", equalTo("is there anybody out there?")); 58 | } 59 | 60 | @Test 61 | public void fieldBasedInjectionResourceTest() { 62 | final Response response = given().get("/foo"); 63 | response.then().statusCode(200).body("timestamp", notNullValue()).body("echoText", any(String.class)); 64 | } 65 | 66 | @Test 67 | public void filterTest() { 68 | final Response response = given().body("is there anybody out there?").header("ping", "ping").post("/echo"); 69 | response.then().statusCode(200).body("timestamp", notNullValue()).body("echoText", equalTo("is there anybody out there?")).header("pong", equalTo("pong")); 70 | } 71 | 72 | @Test 73 | public void invalidUriPathTest() { 74 | // Notice "eco" is supposed to result in 404 75 | final Response response = given().body("is there anybody out there?").post("/eco"); 76 | response.then().statusCode(404).body(equalTo("The resource you've requested, has not been found!")); 77 | } 78 | 79 | @Test 80 | public void invalidBaseUrlTest() { 81 | // Notice "sampl-ap" is missing both 'e' and 'p' and is supposed to result in 404 82 | final Response response = given().basePath("sampl-app").body("is there anybody out there?").post("/echo"); 83 | response.then().statusCode(404).statusLine("HTTP/1.1 404 Not Found"); 84 | } 85 | 86 | @Test 87 | public void invalidNoPayloadTest() { 88 | // Notice that the endpoint we are sending a request to uses Bean Validations to assure 89 | // the request message payload is valid. If that is not the case (a blank payload for example), 90 | // then the server is expected to return a 400 response message 91 | final Response response = given().body("").accept("application/json").post("/echo"); 92 | response.then().statusCode(400).body("parameterViolations.message", hasItems("must not be empty")); 93 | } 94 | 95 | 96 | } 97 | -------------------------------------------------------------------------------- /reactor-netty/resteasy-reactor-netty-spring-boot-starter-test/src/test/java/com/sample/app/test/LogbackTestApplicationListener.java: -------------------------------------------------------------------------------- 1 | package com.sample.app.test; 2 | 3 | import ch.qos.logback.classic.Level; 4 | import ch.qos.logback.classic.Logger; 5 | import ch.qos.logback.classic.LoggerContext; 6 | import ch.qos.logback.classic.spi.ILoggingEvent; 7 | import ch.qos.logback.core.Appender; 8 | import ch.qos.logback.core.AppenderBase; 9 | import org.slf4j.LoggerFactory; 10 | import org.springframework.boot.context.event.ApplicationEnvironmentPreparedEvent; 11 | import org.springframework.context.ApplicationEvent; 12 | import org.springframework.context.event.ContextClosedEvent; 13 | import org.springframework.context.event.SmartApplicationListener; 14 | import org.springframework.core.Ordered; 15 | import org.testng.Assert; 16 | 17 | /** 18 | * The Spring application listener registers a Logback appender 19 | * which allows inspecting every log statement looking for warning 20 | * or error messages. If any is found, the test will fail. 21 | */ 22 | public class LogbackTestApplicationListener implements SmartApplicationListener { 23 | 24 | private boolean warningOrErrorFound = false; 25 | 26 | private Appender appender = new AppenderBase() { 27 | 28 | // TODO 29 | // Remove this after implementing https://github.com/paypal/resteasy-spring-boot/issues/69 30 | private static final java.lang.String SCANNING_WARNING = "\n-------------\nStarting on version 3.0.0, the behavior of the `scanning`"; 31 | 32 | @Override 33 | protected void append(ILoggingEvent event) { 34 | if (event == null || warningOrErrorFound) { 35 | return; 36 | } 37 | Level level = event.getLevel(); 38 | if ((level.equals(Level.WARN) || level.equals(Level.ERROR)) 39 | && !event.getMessage().startsWith(SCANNING_WARNING) 40 | && !event.getMessage().startsWith("InetAddress.getLocalHost") // On MacOS Java 11 it sometimes generate relative warning so we ignore it. 41 | && !event.getLoggerName().equals("org.apache.tomcat.util.modeler.Registry")) { // Tomcat generate relative warnings when the servers are started/stoped multiple times during tests. 42 | warningOrErrorFound = true; 43 | Assert.fail(event.getFormattedMessage()); 44 | } 45 | } 46 | }; 47 | 48 | private void addTestAppender() { 49 | LoggerContext loggerContext = (LoggerContext) LoggerFactory.getILoggerFactory(); 50 | appender.setContext(loggerContext); 51 | appender.start(); 52 | Logger rootLogger = loggerContext.getLogger(Logger.ROOT_LOGGER_NAME); 53 | rootLogger.addAppender(appender); 54 | } 55 | 56 | private void detachTestAppender() { 57 | if (appender != null) { 58 | appender.stop(); 59 | LoggerContext loggerContext = (LoggerContext) LoggerFactory.getILoggerFactory(); 60 | Logger rootLogger = loggerContext.getLogger(Logger.ROOT_LOGGER_NAME); 61 | rootLogger.detachAppender(appender); 62 | } 63 | } 64 | 65 | public boolean supportsEventType(Class eventType) { 66 | return ApplicationEnvironmentPreparedEvent.class.isAssignableFrom(eventType); 67 | } 68 | 69 | public boolean supportsSourceType(Class sourceType) { 70 | return true; 71 | } 72 | 73 | @Override 74 | public void onApplicationEvent(ApplicationEvent event) { 75 | if(event instanceof ApplicationEnvironmentPreparedEvent) { 76 | addTestAppender(); 77 | } else if(event instanceof ContextClosedEvent && ((ContextClosedEvent)event).getApplicationContext().getParent() == null) { 78 | detachTestAppender(); 79 | } 80 | } 81 | 82 | public int getOrder() { 83 | return Ordered.HIGHEST_PRECEDENCE - 12; 84 | } 85 | 86 | } 87 | 88 | -------------------------------------------------------------------------------- /reactor-netty/resteasy-reactor-netty-spring-boot-starter-test/src/test/resources/application.yaml: -------------------------------------------------------------------------------- 1 | management: 2 | endpoints: 3 | web: 4 | exposure: 5 | include: 6 | - health 7 | - shutdown 8 | endpoint: 9 | shutdown: 10 | enabled: true 11 | logging: 12 | level: 13 | org: 14 | springframework: info 15 | -------------------------------------------------------------------------------- /reactor-netty/resteasy-reactor-netty-spring-boot-starter/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | 7 | org.jboss.resteasy 8 | resteasy-reactor-netty-spring-boot-starter-parent 9 | 6.3.1-SNAPSHOT 10 | ../pom.xml 11 | 12 | 13 | resteasy-reactor-netty-spring-boot-starter 14 | ${project.artifactId} 15 | 16 | A Spring Boot starter for RESTEasy using Reactor Netty as server 17 | https://github.com/resteasy/resteasy-spring-boot 18 | 19 | 20 | 21 | The Apache License, Version 2.0 22 | http://www.apache.org/licenses/LICENSE-2.0.txt 23 | 24 | 25 | 26 | 27 | scm:git:git@github.com:resteasy/resteasy-spring-boot.git 28 | scm:git:git@github.com:resteasy/resteasy-spring-boot.git 29 | git@github.com:resteasy/resteasy-spring-boot.git 30 | 31 | 32 | 33 | JIRA 34 | http://jira.jboss.com/jira/browse/RESTEASY 35 | 36 | 37 | 38 | 39 | 40 | org.springframework.boot 41 | spring-boot-starter-parent 42 | ${springboot.version} 43 | pom 44 | import 45 | 46 | 47 | ${project.groupId} 48 | resteasy-spring-boot-bom 49 | ${project.version} 50 | pom 51 | import 52 | 53 | 54 | 55 | 56 | 57 | org.jboss.resteasy 58 | resteasy-spring-boot-starter-common 59 | ${project.version} 60 | 61 | 62 | org.springframework.boot 63 | spring-boot-starter 64 | runtime 65 | 66 | 67 | org.jboss.resteasy.spring 68 | resteasy-spring 69 | 70 | 71 | org.slf4j 72 | slf4j-api 73 | 74 | 75 | org.jboss.resteasy 76 | resteasy-jackson2-provider 77 | runtime 78 | 79 | 80 | org.jboss.resteasy 81 | resteasy-reactor-netty 82 | 83 | 84 | io.netty 85 | netty-all 86 | ${version.io.netty.netty4} 87 | test 88 | 89 | 90 | org.jboss.resteasy 91 | resteasy-core-spi 92 | 93 | 94 | jakarta.annotation 95 | jakarta.annotation-api 96 | 97 | 98 | org.springframework.boot 99 | spring-boot-starter-test 100 | test 101 | 102 | 103 | 104 | jakarta.ws.rs 105 | jakarta.ws.rs-api 106 | 107 | 108 | 109 | jakarta.validation 110 | jakarta.validation-api 111 | runtime 112 | 113 | 114 | 115 | org.hibernate.validator 116 | hibernate-validator 117 | runtime 118 | 119 | 120 | 121 | 122 | org.glassfish 123 | jakarta.el 124 | ${version.jakarta.el-api} 125 | test 126 | 127 | 128 | 129 | 130 | 131 | org.jboss.resteasy 132 | resteasy-validator-provider 133 | runtime 134 | 135 | 136 | 137 | org.testng 138 | testng 139 | ${ver.testng} 140 | test 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | org.apache.maven.plugins 149 | maven-deploy-plugin 150 | 151 | false 152 | 153 | 154 | 155 | org.apache.maven.plugins 156 | maven-enforcer-plugin 157 | 158 | 159 | 160 | 161 | -------------------------------------------------------------------------------- /reactor-netty/resteasy-reactor-netty-spring-boot-starter/settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | ossrh 6 | ${env.SONATYPE_USER} 7 | ${env.SONATYPE_PASSWORD} 8 | 9 | 10 | -------------------------------------------------------------------------------- /reactor-netty/resteasy-reactor-netty-spring-boot-starter/src/main/java/org/jboss/resteasy/springboot/reactor/ReactorNettyServerConfig.java: -------------------------------------------------------------------------------- 1 | package org.jboss.resteasy.springboot.reactor; 2 | 3 | import java.time.Duration; 4 | import java.util.Arrays; 5 | import java.util.List; 6 | import java.util.Objects; 7 | import java.util.Optional; 8 | import javax.net.ssl.SSLContext; 9 | import org.jboss.resteasy.plugins.server.embedded.SecurityDomain; 10 | import org.springframework.core.env.Environment; 11 | import io.netty.handler.ssl.ClientAuth; 12 | 13 | public class ReactorNettyServerConfig { 14 | 15 | private static final String REACTOR_NETTY_SERVER_PORT_PROPERTY = "server.port"; 16 | private static final int REACTOR_NETTY_SERVER_PORT_HTTP_DEFAULT = 8080; 17 | private static final int REACTOR_NETTY_SERVER_PORT_HTTPS_DEFAULT = 8443; 18 | 19 | private final Integer port; 20 | private final Duration idleTimeout; 21 | private final SSLContext sslContext; 22 | private final ClientAuth clientAuth; 23 | private final SecurityDomain securityDomain; 24 | /** 25 | * Clean up tasks which are expected to run after servicing every request. All given tasks will 26 | * be run for every request 27 | */ 28 | private final List cleanupTasks; 29 | 30 | public ReactorNettyServerConfig(final int port, final Duration idleTimeout, final SSLContext sslContext, 31 | final ClientAuth clientAuth, final SecurityDomain securityDomain, final List cleanupTasks) { 32 | this.port = port; 33 | this.idleTimeout = idleTimeout; 34 | this.sslContext = sslContext; 35 | this.clientAuth = clientAuth; 36 | this.securityDomain = securityDomain; 37 | this.cleanupTasks = cleanupTasks; 38 | } 39 | 40 | public Integer getPort() { 41 | return port; 42 | } 43 | 44 | public Duration getIdleTimeout() { 45 | return idleTimeout; 46 | } 47 | 48 | public SSLContext getSslContext() { 49 | return sslContext; 50 | } 51 | 52 | public ClientAuth getClientAuth() { 53 | return clientAuth; 54 | } 55 | 56 | public SecurityDomain getSecurityDomain() { 57 | return securityDomain; 58 | } 59 | 60 | public List getCleanupTasks() { 61 | return cleanupTasks; 62 | } 63 | 64 | public static class Builder { 65 | 66 | private Integer port; 67 | private Duration idleTimeout; 68 | private SSLContext sslContext; 69 | private ClientAuth clientAuth = ClientAuth.REQUIRE; 70 | private SecurityDomain securityDomain; 71 | private List cleanupTasks; 72 | 73 | public Builder() {} 74 | 75 | public Builder withPort(final int port) { 76 | this.port = port; 77 | return this; 78 | } 79 | 80 | public Builder withIdleTimeout(final Duration idleTimeout) { 81 | this.idleTimeout = idleTimeout; 82 | return this; 83 | } 84 | 85 | public Builder withSSLContext(final SSLContext sslContext) { 86 | this.sslContext = sslContext; 87 | return this; 88 | } 89 | 90 | /** 91 | * Applies the setting to indicate if/how client authentication is performed. 92 | * @param clientAuth - See {@link io.netty.handler.ssl.ClientAuth}. Null values not allowed. 93 | * @return - A modified instance of the builder with the new {@code clientAuth} setting. 94 | */ 95 | public Builder withClientAuth(final ClientAuth clientAuth) { 96 | Objects.requireNonNull(clientAuth); 97 | this.clientAuth = clientAuth; 98 | return this; 99 | } 100 | 101 | public Builder withSecurityDomain(final SecurityDomain securityDomain) { 102 | this.securityDomain = securityDomain; 103 | return this; 104 | } 105 | 106 | /** 107 | * Tasks to be run after servicing of request. All tasks will run 108 | * after every request. Given tasks will be run on netty event loop thread. 109 | * Hence tasks need to be non-blocking and finish quickly. Long running tasks 110 | * or blocking runnables will slow down netty server. 111 | * 112 | * @param tasks tasks to run after service request 113 | * @return A modified instance of the builder with the clean up tasks added. 114 | * @see org.jboss.resteasy.plugins.server.reactor.netty.ReactorNettyJaxrsServer 115 | */ 116 | public Builder withCleanupTasks(final Runnable... tasks){ 117 | Objects.requireNonNull(tasks); 118 | this.cleanupTasks = Arrays.asList(tasks); 119 | return this; 120 | } 121 | 122 | public ReactorNettyServerConfig build(){ 123 | 124 | return new ReactorNettyServerConfig( 125 | Optional.ofNullable(port) 126 | .orElseGet(() -> 127 | Optional.ofNullable(sslContext) 128 | .map(c -> REACTOR_NETTY_SERVER_PORT_HTTPS_DEFAULT) 129 | .orElseGet(() -> REACTOR_NETTY_SERVER_PORT_HTTP_DEFAULT)), 130 | idleTimeout, 131 | sslContext, 132 | clientAuth, 133 | securityDomain, 134 | cleanupTasks); 135 | } 136 | 137 | } 138 | 139 | 140 | public static ReactorNettyServerConfig defaultConfig(final Environment env) { 141 | 142 | Objects.requireNonNull(env); 143 | 144 | return new ReactorNettyServerConfig.Builder().withPort(Integer.parseInt( 145 | env.getProperty(REACTOR_NETTY_SERVER_PORT_PROPERTY, 146 | String.valueOf(REACTOR_NETTY_SERVER_PORT_HTTP_DEFAULT)))) 147 | .build(); 148 | 149 | } 150 | } 151 | -------------------------------------------------------------------------------- /reactor-netty/resteasy-reactor-netty-spring-boot-starter/src/main/java/org/jboss/resteasy/springboot/reactor/ResteasyAutoConfiguration.java: -------------------------------------------------------------------------------- 1 | package org.jboss.resteasy.springboot.reactor; 2 | 3 | import java.lang.invoke.MethodHandles; 4 | import java.util.Optional; 5 | import java.util.Set; 6 | import java.util.concurrent.CountDownLatch; 7 | import org.jboss.resteasy.core.ResteasyDeploymentImpl; 8 | import org.jboss.resteasy.plugins.server.reactor.netty.ReactorNettyJaxrsServer; 9 | import org.jboss.resteasy.plugins.spring.SpringBeanProcessor; 10 | import org.jboss.resteasy.spi.ResteasyDeployment; 11 | import org.jboss.resteasy.springboot.common.DeploymentCustomizer; 12 | import org.jboss.resteasy.springboot.common.ResteasyBeanProcessorFactory; 13 | import org.slf4j.Logger; 14 | import org.slf4j.LoggerFactory; 15 | import org.springframework.beans.factory.config.BeanFactoryPostProcessor; 16 | import org.springframework.boot.autoconfigure.AutoConfiguration; 17 | import org.springframework.boot.context.properties.EnableConfigurationProperties; 18 | import org.springframework.context.annotation.Bean; 19 | import org.springframework.core.annotation.AnnotationUtils; 20 | import org.springframework.core.env.Environment; 21 | 22 | import jakarta.annotation.PostConstruct; 23 | import jakarta.annotation.PreDestroy; 24 | import jakarta.ws.rs.ApplicationPath; 25 | import jakarta.ws.rs.core.Application; 26 | 27 | 28 | /** 29 | * This is the main class that prepares a Resteasy deployment and starts a Reactor Netty server. 30 | */ 31 | @AutoConfiguration 32 | @EnableConfigurationProperties 33 | public class ResteasyAutoConfiguration { 34 | 35 | private static final Logger logger = LoggerFactory.getLogger(ResteasyAutoConfiguration.class); 36 | 37 | private static final String JAXRS_APP_ASYNC_JOB_ENABLE_PROPERTY = "resteasy.async.job.service.enabled"; 38 | private static final String JAXRS_APP_ASYNC_JOB_ENABLE_PROPERTY_DEFAULT = "false"; 39 | private static final String DEFAULT_BASE_APP_PATH = "/"; 40 | 41 | @Bean 42 | public static ResteasyBeanProcessorReactorNetty resteasyBeansProcessor() { 43 | return new ResteasyBeanProcessorReactorNetty(); 44 | } 45 | 46 | @Bean 47 | public static BeanFactoryPostProcessor resteasySpringBeanProcessor() { 48 | return ResteasyBeanProcessorFactory.resteasySpringBeanProcessor(); 49 | } 50 | 51 | @Bean 52 | public ResteasyReactorNettyServerBean resteasyReactorNettyServerBean( 53 | final BeanFactoryPostProcessor resteasySpringBeanProcessor, 54 | final ResteasyBeanProcessorReactorNetty resteasyBeansProcessor, 55 | final Environment environment, 56 | final Optional maybeServerConfig) throws InterruptedException { 57 | 58 | final ReactorNettyJaxrsServer server = new ReactorNettyJaxrsServer(); 59 | server.setDeployment(new ResteasyDeploymentImpl()); 60 | 61 | final ReactorNettyServerConfig serverConfig = maybeServerConfig 62 | .orElseGet(() -> ReactorNettyServerConfig.defaultConfig(environment)); 63 | 64 | configureServerAndDeployment(server, resteasyBeansProcessor.getApplications(), 65 | (SpringBeanProcessor) resteasySpringBeanProcessor, environment, 66 | serverConfig); 67 | 68 | server.getDeployment().start(); 69 | 70 | return new ResteasyReactorNettyServerBean(server); 71 | } 72 | 73 | 74 | public class ResteasyReactorNettyServerBean { 75 | 76 | private final ReactorNettyJaxrsServer server; 77 | private final CountDownLatch shutdownLatch = new CountDownLatch(1); 78 | 79 | public ResteasyReactorNettyServerBean(ReactorNettyJaxrsServer server) throws InterruptedException { 80 | this.server = server; 81 | } 82 | 83 | /** 84 | * Starts the server a non daemon thread to prevent it from shutting down prematurely.
85 | * Similar to: 86 | * https://github.com/spring-projects/spring-boot/blob/master/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/netty/NettyWebServer.java 87 | * 88 | * @param server - The Reactor Netty server to start. 89 | * @param shutdownLatch - The countdown latch to use in order to wait for the server to shutdown. 90 | * @throws InterruptedException - If thread is interrupted while waiting for the server to start. 91 | */ 92 | @PostConstruct 93 | private void startServer() throws InterruptedException { 94 | final CountDownLatch startupLatch = new CountDownLatch(1); 95 | final Thread awaitThread = new Thread("server") { 96 | @Override 97 | public void run() { 98 | server.start(); 99 | logger.info("Reactor Netty server started on port: {}", server.getPort()); 100 | startupLatch.countDown(); 101 | try { 102 | shutdownLatch.await(); 103 | } catch (final InterruptedException ie) { 104 | logger.error("Exception caught while waiting for the Reactor Netty server to stop", ie); 105 | Thread.currentThread().interrupt(); 106 | } 107 | } 108 | }; 109 | awaitThread.setContextClassLoader(MethodHandles.lookup().lookupClass().getClassLoader()); 110 | awaitThread.setDaemon(false); 111 | awaitThread.start(); 112 | startupLatch.await(); 113 | } 114 | 115 | @PreDestroy 116 | private void stopServer() { 117 | logger.info("Stopping the JAX-RS+Reactor-Netty server."); 118 | server.stop(); 119 | shutdownLatch.countDown(); 120 | } 121 | 122 | public ReactorNettyJaxrsServer getServer() { 123 | return server; 124 | } 125 | 126 | } 127 | 128 | 129 | private void configureServerAndDeployment( 130 | final ReactorNettyJaxrsServer server, 131 | final Set> applications, 132 | final SpringBeanProcessor resteasySpringBeanProcessor, 133 | final Environment env, 134 | final ReactorNettyServerConfig config) { 135 | 136 | final ResteasyDeployment deployment = server.getDeployment(); 137 | 138 | if (applications.isEmpty()) { 139 | logger.info( 140 | "No JAX-RS Application classes with proper path have been found. A default app mapped to '{}', will be configured.", 141 | DEFAULT_BASE_APP_PATH); 142 | deployment.setApplicationClass(Application.class.getName()); 143 | } else { 144 | 145 | final Class application = (Class) applications.iterator().next(); 146 | final String path = AnnotationUtils.findAnnotation(application, ApplicationPath.class).value(); 147 | 148 | if (applications.size() > 1) { 149 | logger.info( 150 | "Multiple application classes found. Will only configure one. Application name: {}, Base path: {}", 151 | application.getName(), path); 152 | } 153 | 154 | logger.info("Configuring JAX-RS application class {}. Base path: {}", application.getName(), path); 155 | deployment.setApplicationClass(application.getName()); 156 | } 157 | 158 | final boolean enableAsyncJob = Boolean.valueOf(env.getProperty(JAXRS_APP_ASYNC_JOB_ENABLE_PROPERTY, 159 | JAXRS_APP_ASYNC_JOB_ENABLE_PROPERTY_DEFAULT)); 160 | 161 | DeploymentCustomizer.customizeRestEasyDeployment(resteasySpringBeanProcessor, deployment, enableAsyncJob); 162 | 163 | Optional.ofNullable(config.getSslContext()) 164 | .map(c -> server.setSSLContext(c)) 165 | .orElse(server) 166 | .setPort(config.getPort()) 167 | .setIdleTimeout(config.getIdleTimeout()) 168 | .setClientAuth(config.getClientAuth()) 169 | .setSecurityDomain(config.getSecurityDomain()) 170 | .setCleanUpTasks(config.getCleanupTasks()) 171 | .setDeployment(deployment); 172 | 173 | } 174 | 175 | 176 | } 177 | -------------------------------------------------------------------------------- /reactor-netty/resteasy-reactor-netty-spring-boot-starter/src/main/java/org/jboss/resteasy/springboot/reactor/ResteasyBeanProcessorReactorNetty.java: -------------------------------------------------------------------------------- 1 | package org.jboss.resteasy.springboot.reactor; 2 | 3 | import org.jboss.resteasy.springboot.common.ResteasyResourcesFinder; 4 | import org.slf4j.Logger; 5 | import org.slf4j.LoggerFactory; 6 | import org.springframework.beans.BeansException; 7 | import org.springframework.beans.factory.config.BeanFactoryPostProcessor; 8 | import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; 9 | 10 | /** 11 | * This class finds JAX-RS Application, Resource and Provider classes. 12 | */ 13 | public class ResteasyBeanProcessorReactorNetty extends ResteasyResourcesFinder implements BeanFactoryPostProcessor { 14 | 15 | private static final Logger logger = LoggerFactory.getLogger(ResteasyBeanProcessorReactorNetty.class); 16 | 17 | public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { 18 | 19 | logger.debug("Post process bean factory has been called"); 20 | 21 | findJaxrsApplications(beanFactory); 22 | 23 | // This is done by finding their related Spring beans 24 | findJaxrsResourcesAndProviderClasses(beanFactory); 25 | 26 | } 27 | 28 | 29 | } 30 | -------------------------------------------------------------------------------- /reactor-netty/resteasy-reactor-netty-spring-boot-starter/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports: -------------------------------------------------------------------------------- 1 | org.jboss.resteasy.springboot.reactor.ResteasyAutoConfiguration -------------------------------------------------------------------------------- /reactor-netty/resteasy-reactor-netty-spring-boot-starter/src/test/java/org/jboss/resteasy/springboot/JaxrsAppRegistrationTest.java: -------------------------------------------------------------------------------- 1 | package org.jboss.resteasy.springboot; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | import java.util.Optional; 6 | import java.util.stream.Collectors; 7 | import java.util.stream.Stream; 8 | 9 | import org.jboss.resteasy.springboot.reactor.ResteasyBeanProcessorReactorNetty; 10 | import org.jboss.resteasy.springboot.sample.TestApplication1; 11 | import org.jboss.resteasy.springboot.sample.TestApplication2; 12 | import org.jboss.resteasy.springboot.sample.TestApplication3; 13 | import org.jboss.resteasy.springboot.sample.TestApplication4; 14 | import org.jboss.resteasy.springboot.sample.TestApplication5; 15 | import org.jboss.resteasy.springboot.utilities.CustomPropertySource; 16 | import org.jboss.resteasy.springboot.utilities.TestUtils; 17 | import org.springframework.beans.BeansException; 18 | import org.springframework.context.ApplicationContext; 19 | import org.springframework.context.ConfigurableApplicationContext; 20 | import org.springframework.test.context.testng.AbstractTestNGSpringContextTests; 21 | import org.testng.Assert; 22 | import org.testng.annotations.Test; 23 | 24 | import jakarta.ws.rs.core.Application; 25 | 26 | public class JaxrsAppRegistrationTest extends AbstractTestNGSpringContextTests { 27 | 28 | private final String SPRING_CONTEXT_FILE_LOCATION = "classpath:test-config.xml"; 29 | 30 | private enum JaxrsAppClassesRegistration { 31 | BEANS, PROPERTY, SCANNING, AUTO 32 | } 33 | 34 | private final List> expectedAppsAll = Stream.of(TestApplication1.class, 35 | TestApplication2.class, TestApplication3.class, TestApplication4.class, TestApplication5.class) 36 | .collect(Collectors.toList()); 37 | 38 | @Test 39 | public void nullTest() { 40 | verifyExpectedApps(expectedAppsAll, new CustomPropertySource()); 41 | } 42 | 43 | @Test 44 | public void autoTest() { 45 | final CustomPropertySource customPropertySource = new CustomPropertySource() 46 | .setAppClassesDefinition(JaxrsAppClassesRegistration.AUTO.name()); 47 | verifyExpectedApps(expectedAppsAll, customPropertySource); 48 | } 49 | 50 | @Test 51 | public void scanningTest() { 52 | final CustomPropertySource customPropertySource = new CustomPropertySource() 53 | .setAppClassesDefinition(JaxrsAppClassesRegistration.SCANNING.name()); 54 | verifyExpectedApps(expectedAppsAll, customPropertySource); 55 | } 56 | 57 | @Test 58 | public void autoClassesSetTest() { 59 | 60 | final CustomPropertySource customPropertySource = new CustomPropertySource() 61 | .setAppClassesDefinition(JaxrsAppClassesRegistration.AUTO.name()) 62 | .setAppClasses(TestApplication3.class.getTypeName()); 63 | 64 | final List> expectedApps = Stream.of(TestApplication3.class) 65 | .collect(Collectors.toList()); 66 | 67 | verifyExpectedApps(expectedApps, customPropertySource); 68 | } 69 | 70 | @Test 71 | public void beansTest() { 72 | final CustomPropertySource customPropertySource = new CustomPropertySource() 73 | .setAppClassesDefinition(JaxrsAppClassesRegistration.BEANS.name()); 74 | verifyExpectedApps(new ArrayList<>(), customPropertySource); 75 | } 76 | 77 | @Test 78 | public void propertyTest() { 79 | 80 | final CustomPropertySource customPropertySource = new CustomPropertySource() 81 | .setAppClassesDefinition(JaxrsAppClassesRegistration.PROPERTY.name()) 82 | .setAppClasses(TestApplication1.class.getTypeName() + "," + TestApplication2.class.getTypeName()); 83 | 84 | final List> expectedApps = Stream 85 | .of(TestApplication1.class, TestApplication2.class).collect(Collectors.toList()); 86 | 87 | verifyExpectedApps(expectedApps, customPropertySource); 88 | 89 | } 90 | 91 | @Test 92 | public void propertyClassesNotSetTest() { 93 | final CustomPropertySource customPropertySource = new CustomPropertySource() 94 | .setAppClassesDefinition(JaxrsAppClassesRegistration.PROPERTY.name()); 95 | verifyExpectedApps(expectedAppsAll, customPropertySource); 96 | 97 | } 98 | 99 | @Test(expectedExceptions = { BeansException.class }) 100 | public void propertyInvalidClassTest() { 101 | final CustomPropertySource customPropertySource = new CustomPropertySource() 102 | .setAppClassesDefinition(JaxrsAppClassesRegistration.PROPERTY.name()) 103 | .setAppClasses("does.not.exist.Com"); 104 | verifyExpectedApps(null, customPropertySource); 105 | 106 | } 107 | 108 | @Test(expectedExceptions = IllegalArgumentException.class) 109 | public void invalidRegistrationTest() { 110 | final CustomPropertySource customPropertySource = new CustomPropertySource().setAppClassesDefinition("turkey"); 111 | verifyExpectedApps(null, customPropertySource); 112 | } 113 | 114 | @Test 115 | public void legacyPropertyTest() { 116 | final CustomPropertySource customPropertySource = new CustomPropertySource() 117 | .setAppClassesDefinition(JaxrsAppClassesRegistration.PROPERTY.name()) 118 | .setAppClasses(TestApplication3.class.getTypeName() + "," + TestApplication5.class.getTypeName()); 119 | 120 | final List> expectedApps = Stream 121 | .of(TestApplication3.class, TestApplication5.class).collect(Collectors.toList()); 122 | 123 | verifyExpectedApps(expectedApps, customPropertySource); 124 | 125 | } 126 | 127 | private void verifyExpectedApps(List> expectedApps, 128 | CustomPropertySource customPropertySource) { 129 | 130 | final ApplicationContext appContext = TestUtils.configureAndCreateAppContext(SPRING_CONTEXT_FILE_LOCATION, 131 | customPropertySource, Optional.empty()); 132 | 133 | final ResteasyBeanProcessorReactorNetty beanProcessor = appContext 134 | .getBean(ResteasyBeanProcessorReactorNetty.class); 135 | 136 | if (expectedApps == null) { 137 | Assert.assertEquals(beanProcessor.getApplications(), 0); 138 | } else { 139 | Assert.assertNotNull(beanProcessor.getApplications()); 140 | expectedApps.forEach(app -> { 141 | beanProcessor.getApplications().contains(app); 142 | }); 143 | } 144 | 145 | ((ConfigurableApplicationContext) appContext).close(); 146 | 147 | } 148 | 149 | } 150 | -------------------------------------------------------------------------------- /reactor-netty/resteasy-reactor-netty-spring-boot-starter/src/test/java/org/jboss/resteasy/springboot/sample/SampleApp.java: -------------------------------------------------------------------------------- 1 | package org.jboss.resteasy.springboot.sample; 2 | 3 | import org.springframework.boot.autoconfigure.EnableAutoConfiguration; 4 | import org.springframework.stereotype.Component; 5 | 6 | /** 7 | * Sample app for test purposes 8 | */ 9 | @EnableAutoConfiguration 10 | @Component 11 | public class SampleApp {} 12 | -------------------------------------------------------------------------------- /reactor-netty/resteasy-reactor-netty-spring-boot-starter/src/test/java/org/jboss/resteasy/springboot/sample/TestApplication1.java: -------------------------------------------------------------------------------- 1 | package org.jboss.resteasy.springboot.sample; 2 | 3 | import jakarta.ws.rs.ApplicationPath; 4 | import jakarta.ws.rs.core.Application; 5 | 6 | /** 7 | * Sample test JAX-RS application. 8 | */ 9 | @ApplicationPath("/myapp1") 10 | public class TestApplication1 extends Application {} 11 | -------------------------------------------------------------------------------- /reactor-netty/resteasy-reactor-netty-spring-boot-starter/src/test/java/org/jboss/resteasy/springboot/sample/TestApplication2.java: -------------------------------------------------------------------------------- 1 | package org.jboss.resteasy.springboot.sample; 2 | 3 | import jakarta.ws.rs.ApplicationPath; 4 | import jakarta.ws.rs.core.Application; 5 | 6 | /** 7 | * Sample test JAX-RS application. 8 | */ 9 | @ApplicationPath("myapp2") 10 | public class TestApplication2 extends Application {} 11 | -------------------------------------------------------------------------------- /reactor-netty/resteasy-reactor-netty-spring-boot-starter/src/test/java/org/jboss/resteasy/springboot/sample/TestApplication3.java: -------------------------------------------------------------------------------- 1 | package org.jboss.resteasy.springboot.sample; 2 | 3 | import jakarta.ws.rs.core.Application; 4 | 5 | /** 6 | * This application, although extending Application class, 7 | * is NOT annotated with ApplicationPath annotation, which 8 | * should prevent its registration 9 | */ 10 | public class TestApplication3 extends Application {} 11 | -------------------------------------------------------------------------------- /reactor-netty/resteasy-reactor-netty-spring-boot-starter/src/test/java/org/jboss/resteasy/springboot/sample/TestApplication4.java: -------------------------------------------------------------------------------- 1 | package org.jboss.resteasy.springboot.sample; 2 | 3 | import jakarta.ws.rs.ApplicationPath; 4 | import jakarta.ws.rs.core.Application; 5 | 6 | /** 7 | * Sample test JAX-RS application. 8 | */ 9 | @ApplicationPath("/") 10 | public class TestApplication4 extends Application {} 11 | -------------------------------------------------------------------------------- /reactor-netty/resteasy-reactor-netty-spring-boot-starter/src/test/java/org/jboss/resteasy/springboot/sample/TestApplication5.java: -------------------------------------------------------------------------------- 1 | package org.jboss.resteasy.springboot.sample; 2 | 3 | import jakarta.ws.rs.ApplicationPath; 4 | import jakarta.ws.rs.core.Application; 5 | 6 | /** 7 | * Sample test JAX-RS application. 8 | */ 9 | @ApplicationPath("myapp5/") 10 | public class TestApplication5 extends Application {} 11 | -------------------------------------------------------------------------------- /reactor-netty/resteasy-reactor-netty-spring-boot-starter/src/test/java/org/jboss/resteasy/springboot/sample/TestProvider1.java: -------------------------------------------------------------------------------- 1 | package org.jboss.resteasy.springboot.sample; 2 | 3 | import org.springframework.stereotype.Component; 4 | 5 | import jakarta.ws.rs.core.Response; 6 | import jakarta.ws.rs.ext.ExceptionMapper; 7 | import jakarta.ws.rs.ext.Provider; 8 | 9 | @Component 10 | @Provider 11 | public class TestProvider1 implements ExceptionMapper { 12 | 13 | public Response toResponse(Exception exception) { 14 | return null; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /reactor-netty/resteasy-reactor-netty-spring-boot-starter/src/test/java/org/jboss/resteasy/springboot/sample/TestResource1.java: -------------------------------------------------------------------------------- 1 | package org.jboss.resteasy.springboot.sample; 2 | 3 | import org.springframework.stereotype.Component; 4 | 5 | import jakarta.ws.rs.GET; 6 | import jakarta.ws.rs.Path; 7 | 8 | @Path("resource1") 9 | @Component 10 | public class TestResource1 { 11 | 12 | @GET 13 | public void get() { 14 | // Test get method 15 | } 16 | 17 | } 18 | -------------------------------------------------------------------------------- /reactor-netty/resteasy-reactor-netty-spring-boot-starter/src/test/java/org/jboss/resteasy/springboot/sample/TestResource2.java: -------------------------------------------------------------------------------- 1 | package org.jboss.resteasy.springboot.sample; 2 | 3 | import org.springframework.stereotype.Component; 4 | 5 | import jakarta.ws.rs.GET; 6 | import jakarta.ws.rs.Path; 7 | 8 | @Path("resource2") 9 | @Component 10 | public class TestResource2 { 11 | 12 | @GET 13 | public void get() { 14 | // Test get method 15 | } 16 | 17 | } 18 | -------------------------------------------------------------------------------- /reactor-netty/resteasy-reactor-netty-spring-boot-starter/src/test/java/org/jboss/resteasy/springboot/utilities/CustomPropertySource.java: -------------------------------------------------------------------------------- 1 | package org.jboss.resteasy.springboot.utilities; 2 | 3 | import org.springframework.core.env.PropertySource; 4 | 5 | public class CustomPropertySource extends PropertySource { 6 | 7 | private static final String JAXRS_APP_CLASSES_PROPERTY = "resteasy.jaxrs.app.classes"; 8 | private static final String JAXRS_APP_CLASSES_DEFINITION_PROPERTY = "resteasy.jaxrs.app.registration"; 9 | private static final String JAXRS_APP_ASYNC_JOB_ENABLE_PROPERTY = "resteasy.async.job.service.enabled"; 10 | private static final String REACTOR_NETTY_SERVER_PORT_PROPERTY = "server.port"; 11 | 12 | private String appClasses; 13 | private String appClassesDefinition; 14 | private int port = 0; 15 | private boolean isAsyncEnabled = false; 16 | 17 | public CustomPropertySource() { 18 | super("custom"); 19 | } 20 | 21 | @Override 22 | public String getProperty(String name) { 23 | 24 | String value = null; 25 | 26 | switch (name) { 27 | case JAXRS_APP_CLASSES_DEFINITION_PROPERTY: 28 | value = appClassesDefinition; 29 | break; 30 | case JAXRS_APP_CLASSES_PROPERTY: 31 | value = appClasses; 32 | break; 33 | case REACTOR_NETTY_SERVER_PORT_PROPERTY: 34 | value = String.valueOf(port); 35 | break; 36 | case JAXRS_APP_ASYNC_JOB_ENABLE_PROPERTY: 37 | value = String.valueOf(isAsyncEnabled); 38 | break; 39 | default: 40 | break; 41 | } 42 | 43 | return value; 44 | 45 | } 46 | 47 | public CustomPropertySource setReactorNettyPort(int port) { 48 | this.port = port; 49 | return this; 50 | } 51 | 52 | public CustomPropertySource setAppClassesDefinition(String appClassesDefinition) { 53 | this.appClassesDefinition = appClassesDefinition; 54 | return this; 55 | } 56 | 57 | public CustomPropertySource setIsAsyncEnabled(boolean isAsyncEnabled) { 58 | this.isAsyncEnabled = isAsyncEnabled; 59 | return this; 60 | } 61 | 62 | public CustomPropertySource setAppClasses(String appClasses) { 63 | this.appClasses = appClasses; 64 | return this; 65 | } 66 | 67 | } 68 | -------------------------------------------------------------------------------- /reactor-netty/resteasy-reactor-netty-spring-boot-starter/src/test/java/org/jboss/resteasy/springboot/utilities/TestUtils.java: -------------------------------------------------------------------------------- 1 | package org.jboss.resteasy.springboot.utilities; 2 | 3 | import java.util.Optional; 4 | import org.jboss.resteasy.springboot.reactor.ReactorNettyServerConfig; 5 | import org.springframework.beans.factory.support.DefaultListableBeanFactory; 6 | import org.springframework.context.ApplicationContext; 7 | import org.springframework.context.support.AbstractApplicationContext; 8 | import org.springframework.context.support.ClassPathXmlApplicationContext; 9 | import org.springframework.context.support.GenericApplicationContext; 10 | 11 | public class TestUtils { 12 | 13 | /** 14 | * Creates an application context from a context file and customizes it with passed properties. 15 | * @param contextFileLocation - The xml context file to use for creating the application context. 16 | * @param customPropertySource - Properties to apply before loading the context. 17 | * @param maybeServerConfig - An optional ReactorNettyServerConfig to use. 18 | * @return - The created applicationContext. 19 | */ 20 | public static ApplicationContext configureAndCreateAppContext(final String contextFileLocation, 21 | final CustomPropertySource customPropertySource, Optional maybeServerConfig) { 22 | 23 | final DefaultListableBeanFactory parentBeanFactory = new DefaultListableBeanFactory(); 24 | final GenericApplicationContext parentContext = new GenericApplicationContext(parentBeanFactory); 25 | maybeServerConfig.ifPresent(config -> { 26 | parentBeanFactory.registerSingleton("reactorNettyServerConfig", config); 27 | }); 28 | parentContext.refresh(); 29 | 30 | final AbstractApplicationContext appContext = new ClassPathXmlApplicationContext(new String[] { contextFileLocation }, 31 | false, parentContext); 32 | appContext.getEnvironment().getPropertySources().addLast(customPropertySource); 33 | appContext.refresh(); 34 | 35 | appContext.registerShutdownHook(); 36 | return appContext; 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /reactor-netty/resteasy-reactor-netty-spring-boot-starter/src/test/resources/logback.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | %d{HH:mm:ss.SSS} [%thread] %level %logger %msg%n 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /reactor-netty/resteasy-reactor-netty-spring-boot-starter/src/test/resources/test-config-no-resources.xml: -------------------------------------------------------------------------------- 1 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /reactor-netty/resteasy-reactor-netty-spring-boot-starter/src/test/resources/test-config.xml: -------------------------------------------------------------------------------- 1 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /resteasy-spring-boot-bom/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | org.jboss.resteasy 7 | resteasy-spring-boot-starter-parent 8 | 6.3.1-SNAPSHOT 9 | ../pom.xml 10 | 11 | 4.0.0 12 | 13 | resteasy-spring-boot-bom 14 | pom 15 | RESTEasy Spring Boot Modules: Production BOM 16 | 17 | 18 | 19 | 20 | org.jboss.resteasy.spring 21 | resteasy-spring 22 | ${resteasy.spring.version} 23 | 24 | 25 | org.jboss.resteasy 26 | resteasy-core 27 | 28 | 29 | org.jboss.resteasy 30 | resteasy-core-spi 31 | 32 | 33 | org.jboss.resteasy 34 | resteasy-client 35 | 36 | 37 | 38 | 39 | org.jboss.resteasy.spring 40 | resteasy-spring-web 41 | ${resteasy.spring.version} 42 | 43 | 44 | org.jboss.resteasy 45 | resteasy-core 46 | ${version.org.jboss.resteasy} 47 | 48 | 49 | org.jboss.resteasy 50 | resteasy-core-spi 51 | ${version.org.jboss.resteasy} 52 | 53 | 54 | org.jboss.resteasy 55 | resteasy-client 56 | ${version.org.jboss.resteasy} 57 | 58 | 59 | org.jboss.resteasy 60 | resteasy-servlet-spring-boot-starter 61 | ${project.version} 62 | 63 | 64 | ch.qos.logback 65 | logback-classic 66 | 67 | 68 | jakarta.annotation 69 | jakarta.annotation-api 70 | 71 | 72 | jakarta.servlet 73 | jakarta.servlet-api 74 | 75 | 76 | jakarta.validation 77 | jakarta.validation-api 78 | 79 | 80 | jakarta.ws.rs 81 | jakarta.ws.rs-api 82 | 83 | 84 | org.springframework.boot 85 | spring-boot-starter-logging 86 | 87 | 88 | org.jboss.logging 89 | jboss-logging 90 | 91 | 92 | org.jboss.resteasy 93 | * 94 | 95 | 96 | 97 | 98 | org.springframework.boot 99 | spring-boot-starter-web 100 | 101 | 102 | com.fasterxml.jackson.core 103 | * 104 | 105 | 106 | com.fasterxml.jackson.datatype 107 | * 108 | 109 | 110 | com.fasterxml.jackson.module 111 | * 112 | 113 | 114 | org.springframework.boot 115 | spring-boot-starter-tomcat 116 | 117 | 118 | 119 | 120 | org.springframework.boot 121 | spring-boot-starter-test 122 | test 123 | 124 | 125 | org.springframework.boot 126 | spring-boot-starter-actuator 127 | 128 | 129 | com.fasterxml.jackson.core 130 | * 131 | 132 | 133 | com.fasterxml.jackson.datatype 134 | * 135 | 136 | 137 | 138 | 139 | org.jboss.resteasy 140 | resteasy-jackson2-provider 141 | ${version.org.jboss.resteasy} 142 | runtime 143 | 144 | 145 | org.jboss.resteasy 146 | resteasy-reactor-netty 147 | ${version.org.jboss.resteasy} 148 | 149 | 150 | org.jboss.resteasy 151 | resteasy-validator-provider 152 | ${version.org.jboss.resteasy} 153 | runtime 154 | 155 | 156 | org.jboss.resteasy 157 | resteasy-servlet-initializer 158 | ${version.org.jboss.resteasy} 159 | provided 160 | 161 | 162 | org.mockito 163 | mockito-core 164 | ${version.mockito} 165 | test 166 | 167 | 168 | org.mockito 169 | mockito-inline 170 | ${version.mockito.inline} 171 | test 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | org.apache.maven.plugins 180 | maven-deploy-plugin 181 | 182 | false 183 | 184 | 185 | 186 | 187 | 188 | -------------------------------------------------------------------------------- /servlet/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 4.0.0 7 | 8 | 9 | org.jboss.resteasy 10 | resteasy-spring-boot-starter-parent 11 | 6.3.1-SNAPSHOT 12 | ../pom.xml 13 | 14 | 15 | resteasy-servlet-spring-boot-starter-parent 16 | pom 17 | 18 | 19 | resteasy-servlet-spring-boot-starter 20 | resteasy-servlet-spring-boot-starter-test 21 | resteasy-servlet-spring-boot-sample-app 22 | resteasy-servlet-spring-boot-sample-app-no-jaxrs 23 | resteasy-wildfly-spring-boot-sample-app 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /servlet/resteasy-servlet-spring-boot-sample-app-no-jaxrs/README.md: -------------------------------------------------------------------------------- 1 | ## Usage 2 | 3 | ### Start the server 4 | 5 | ```bash 6 | $ mvn spring-boot:run 7 | ... 8 | 2022-02-22 20:44:30.299 INFO 61839 --- [ main] com.sample.app2.MyApp : Started MyApp in 1.219 seconds (JVM running for 1.401) 9 | ``` 10 | 11 | ## Access the service 12 | 13 | 14 | Use the following command to access the service: 15 | 16 | ```bash 17 | curl --location --request POST 'localhost:8080/echo' \ 18 | --header 'Content-Type: text/plain' \ 19 | --data-raw 'foo' 20 | ``` 21 | 22 | And here is the output: 23 | 24 | ```bash 25 | {"timestamp":1645534458892,"echoText":"foo"} 26 | ``` 27 | 28 | -------------------------------------------------------------------------------- /servlet/resteasy-servlet-spring-boot-sample-app-no-jaxrs/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | 8 | org.jboss.resteasy 9 | resteasy-servlet-spring-boot-starter-parent 10 | 6.3.1-SNAPSHOT 11 | ../pom.xml 12 | 13 | 14 | resteasy-servlet-spring-boot-sample-app-no-jaxrs 15 | jar 16 | 17 | Simple test application for the Resteasy Spring Boot Servlet starter that contains no JAX-RS application class 18 | 19 | 20 | 21 | 22 | org.springframework.boot 23 | spring-boot-starter-parent 24 | ${springboot.version} 25 | pom 26 | import 27 | 28 | 29 | ${project.groupId} 30 | resteasy-spring-boot-bom 31 | ${project.version} 32 | pom 33 | import 34 | 35 | 36 | 37 | 38 | 39 | 40 | org.jboss.resteasy 41 | resteasy-spring-boot-starter-sample-app-common 42 | ${project.version} 43 | 44 | 45 | org.springframework.boot 46 | spring-boot-starter-web 47 | 48 | 49 | org.springframework.boot 50 | spring-boot-starter-actuator 51 | runtime 52 | 53 | 54 | org.springframework.boot 55 | spring-boot-starter-test 56 | test 57 | 58 | 59 | jakarta.ws.rs 60 | jakarta.ws.rs-api 61 | 62 | 63 | org.jboss.resteasy 64 | resteasy-servlet-spring-boot-starter 65 | ${project.version} 66 | runtime 67 | 68 | 69 | org.eclipse 70 | yasson 71 | 72 | 73 | 74 | jakarta.validation 75 | jakarta.validation-api 76 | 77 | 78 | org.hibernate.validator 79 | hibernate-validator 80 | 81 | 82 | 83 | 84 | org.jboss.resteasy 85 | resteasy-validator-provider 86 | 87 | 88 | 89 | 90 | 91 | 92 | org.apache.maven.plugins 93 | maven-deploy-plugin 94 | 95 | false 96 | 97 | 98 | 99 | org.springframework.boot 100 | spring-boot-maven-plugin 101 | ${springboot.version} 102 | 103 | 107 | 108 | 109 | 110 | repackage 111 | 112 | 113 | exec 114 | 115 | 116 | 117 | 118 | 119 | org.apache.maven.plugins 120 | maven-enforcer-plugin 121 | 122 | 123 | 124 | 125 | -------------------------------------------------------------------------------- /servlet/resteasy-servlet-spring-boot-sample-app-no-jaxrs/src/main/java/com/sample/app2/Application.java: -------------------------------------------------------------------------------- 1 | package com.sample.app2; 2 | 3 | import org.jboss.resteasy.springboot.common.sample.resources.IEchoMessageCreator; 4 | import org.springframework.boot.SpringApplication; 5 | import org.springframework.boot.autoconfigure.SpringBootApplication; 6 | import org.springframework.context.annotation.Bean; 7 | 8 | @SpringBootApplication(scanBasePackages = { "org.jboss.resteasy.springboot"}) 9 | public class Application { 10 | 11 | public static void main(String[] args) { 12 | SpringApplication.run(Application.class, args); 13 | } 14 | 15 | @Bean 16 | public IEchoMessageCreator echoMessageCreator() { 17 | return new IEchoMessageCreator() {}; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /servlet/resteasy-servlet-spring-boot-sample-app/README.md: -------------------------------------------------------------------------------- 1 | # Sample application 2 | 3 | This is a super simple JAX-RS RESTEasy Spring Boot application just to exercise RESTEasy Spring Boot starter.
4 | 5 | ## Starting the application 6 | 7 | You can start the application as you for any other regular Spring Boot application. For example: 8 | 9 | 1. From the command line, under the sample application project, run `mvn spring-boot:run` 10 | 1. From your favorite IDE, run class `com.test.Application` 11 | 12 | ## Testing it 13 | 14 | Send a `POST` request message, containing the payload below, to [http://localhost:8080/sample-app/echo](http://localhost:8080/sample-app/echo). 15 | 16 | ``` 17 | is there anybody out there? 18 | ``` 19 | 20 | You can use the following command to send the request: 21 | 22 | ```bash 23 | $ curl --location --request POST 'http://localhost:8080/sample-app/echo' \ 24 | --header 'Content-Type: text/plain' \ 25 | --data-raw 'is there anybody out there?' 26 | ``` 27 | 28 | You should receive a response message with a payload similar to this as result: 29 | 30 | ``` json 31 | { 32 | "timestamp": "1484775122357", 33 | "echoText": "is there anybody out there?" 34 | } 35 | ``` 36 | 37 | The request message payload can be anything as plain text. 38 | The response message is supposed to echo that, plus a timestamp of the moment the echo response was created on the server side. The response message will be in JSON format. 39 | -------------------------------------------------------------------------------- /servlet/resteasy-servlet-spring-boot-sample-app/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | 7 | org.jboss.resteasy 8 | resteasy-servlet-spring-boot-starter-parent 9 | 6.3.1-SNAPSHOT 10 | ../pom.xml 11 | 12 | 13 | resteasy-servlet-spring-boot-sample-app 14 | jar 15 | 16 | Sample test application for the Resteasy Spring Boot Servlet starter 17 | 18 | 19 | 20 | 21 | org.springframework.boot 22 | spring-boot-starter-parent 23 | ${springboot.version} 24 | pom 25 | import 26 | 27 | 28 | 29 | 30 | 31 | 32 | org.springframework.boot 33 | spring-boot-starter-web 34 | 35 | 36 | org.springframework.boot 37 | spring-boot-starter-actuator 38 | runtime 39 | 40 | 41 | org.springframework.boot 42 | spring-boot-starter-test 43 | test 44 | 45 | 46 | jakarta.ws.rs 47 | jakarta.ws.rs-api 48 | 49 | 50 | org.jboss.resteasy 51 | resteasy-spring-boot-starter-sample-app-common 52 | ${project.version} 53 | 54 | 55 | org.jboss.resteasy 56 | resteasy-servlet-spring-boot-starter 57 | ${project.version} 58 | runtime 59 | 60 | 61 | jakarta.validation 62 | jakarta.validation-api 63 | 64 | 65 | org.hibernate.validator 66 | hibernate-validator 67 | runtime 68 | 69 | 70 | org.jboss.resteasy 71 | resteasy-jackson2-provider 72 | ${version.org.jboss.resteasy} 73 | 74 | 75 | 76 | 77 | org.jboss.resteasy 78 | resteasy-validator-provider 79 | ${version.org.jboss.resteasy} 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | org.springframework.boot 89 | spring-boot-maven-plugin 90 | ${springboot.version} 91 | 92 | 96 | 97 | 98 | 99 | repackage 100 | 101 | 102 | exec 103 | 104 | 105 | 106 | 107 | 108 | org.apache.maven.plugins 109 | maven-deploy-plugin 110 | 111 | false 112 | 113 | 114 | 115 | org.apache.maven.plugins 116 | maven-enforcer-plugin 117 | 118 | 119 | 120 | 121 | 122 | -------------------------------------------------------------------------------- /servlet/resteasy-servlet-spring-boot-sample-app/src/main/java/com/sample/app/Application.java: -------------------------------------------------------------------------------- 1 | package com.sample.app; 2 | 3 | import org.jboss.resteasy.springboot.common.sample.resources.IEchoMessageCreator; 4 | import org.springframework.boot.SpringApplication; 5 | import org.springframework.boot.autoconfigure.SpringBootApplication; 6 | import org.springframework.context.annotation.Bean; 7 | 8 | /** 9 | * SpringBoot entry point application 10 | * 11 | * @author Fabio Carvalho (facarvalho@paypal.com or fabiocarvalho777@gmail.com) 12 | */ 13 | @SpringBootApplication(scanBasePackages = { "com.sample.app", "org.jboss.resteasy.springboot"}) 14 | public class Application { 15 | 16 | public static void main(String[] args) { 17 | SpringApplication.run(Application.class, args); 18 | } 19 | 20 | @Bean 21 | public IEchoMessageCreator echoMessageCreator() { 22 | return new IEchoMessageCreator() {}; 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /servlet/resteasy-servlet-spring-boot-sample-app/src/main/java/com/sample/app/configuration/JaxrsApplication.java: -------------------------------------------------------------------------------- 1 | package com.sample.app.configuration; 2 | 3 | import org.springframework.stereotype.Component; 4 | 5 | import jakarta.ws.rs.ApplicationPath; 6 | import jakarta.ws.rs.core.Application; 7 | 8 | /** 9 | * JAX-RS application 10 | * 11 | * @author Fabio Carvalho (facarvalho@paypal.com or fabiocarvalho777@gmail.com) 12 | */ 13 | @Component 14 | @ApplicationPath("/sample-app/") 15 | public class JaxrsApplication extends Application { 16 | } 17 | -------------------------------------------------------------------------------- /servlet/resteasy-servlet-spring-boot-sample-app/src/main/resources/application.yaml: -------------------------------------------------------------------------------- 1 | resteasy: 2 | jaxrs: 3 | app: 4 | registration: property 5 | classes: com.sample.app.configuration.JaxrsApplication 6 | management: 7 | endpoints: 8 | web: 9 | exposure: 10 | include: 11 | - health 12 | - shutdown 13 | endpoint: 14 | shutdown: 15 | enabled: true 16 | logging: 17 | level: 18 | org: 19 | springframework: info 20 | -------------------------------------------------------------------------------- /servlet/resteasy-servlet-spring-boot-sample-app/src/main/resources/logback.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | %d{HH:mm:ss.SSS} [%thread] %level %logger %msg%n 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /servlet/resteasy-servlet-spring-boot-starter-test/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | 7 | org.jboss.resteasy 8 | resteasy-servlet-spring-boot-starter-parent 9 | 6.3.1-SNAPSHOT 10 | ../pom.xml 11 | 12 | 13 | resteasy-servlet-spring-boot-starter-test 14 | ${project.artifactId} 15 | 16 | Integration test project for RESTEasy Spring Boot servlet starter 17 | https://github.com/resteasy/resteasy-spring-boot 18 | 19 | 20 | 21 | The Apache License, Version 2.0 22 | http://www.apache.org/licenses/LICENSE-2.0.txt 23 | 24 | 25 | 26 | 27 | Fabio Carvalho 28 | facarvalho@paypal.com 29 | PayPal 30 | http://www.paypal.com 31 | 32 | 33 | Alessio Soldano 34 | alessio.soldano@jboss.com 35 | Red Hat 36 | http://www.redhat.com 37 | 38 | 39 | 40 | scm:git:git@github.com:resteasy/resteasy-spring-boot.git 41 | scm:git:git@github.com:resteasy/resteasy-spring-boot.git 42 | git@github.com:resteasy/resteasy-spring-boot.git 43 | 44 | 45 | 46 | JIRA 47 | http://jira.jboss.com/jira/browse/RESTEASY 48 | 49 | 50 | 51 | 52 | 53 | org.springframework.boot 54 | spring-boot-starter-parent 55 | ${springboot.version} 56 | pom 57 | import 58 | 59 | 60 | 61 | 62 | 63 | 64 | jakarta.ws.rs 65 | jakarta.ws.rs-api 66 | test 67 | 68 | 69 | org.jboss.resteasy 70 | resteasy-servlet-spring-boot-sample-app 71 | ${project.version} 72 | test 73 | 74 | 75 | org.jboss.resteasy 76 | resteasy-servlet-spring-boot-sample-app-no-jaxrs 77 | ${project.version} 78 | test 79 | 80 | 81 | io.rest-assured 82 | rest-assured 83 | test 84 | 85 | 86 | org.testng 87 | testng 88 | ${ver.testng} 89 | test 90 | 91 | 92 | org.springframework.boot 93 | spring-boot-starter-web 94 | ${springboot.version} 95 | 96 | 97 | jakarta.xml.bind 98 | jakarta.xml.bind-api 99 | 100 | 101 | 102 | 103 | 104 | 105 | org.apache.maven.plugins 106 | maven-failsafe-plugin 107 | 108 | ${modular.jdk.args} ${modular.jdk.props} 109 | always 110 | 111 | en 112 | GB 113 | 114 | 115 | 116 | 117 | failsafe-integration-tests 118 | integration-test 119 | 120 | integration-test 121 | verify 122 | 123 | 124 | 125 | 126 | 127 | org.apache.maven.plugins 128 | maven-enforcer-plugin 129 | 130 | true 131 | 132 | 133 | 134 | org.apache.maven.plugins 135 | maven-deploy-plugin 136 | 137 | false 138 | 139 | 140 | 141 | org.apache.maven.plugins 142 | maven-surefire-plugin 143 | 144 | 1 145 | 146 | 147 | 148 | 149 | 150 | -------------------------------------------------------------------------------- /servlet/resteasy-servlet-spring-boot-starter-test/src/test/java/com/sample/app/NonSpringBeanJaxrsApplication1.java: -------------------------------------------------------------------------------- 1 | package com.sample.app; 2 | 3 | import jakarta.ws.rs.ApplicationPath; 4 | import jakarta.ws.rs.core.Application; 5 | 6 | /** 7 | * @author facarvalho 8 | */ 9 | @ApplicationPath("sample-app-test-two") 10 | public class NonSpringBeanJaxrsApplication1 extends Application {} -------------------------------------------------------------------------------- /servlet/resteasy-servlet-spring-boot-starter-test/src/test/java/com/sample/app/NonSpringBeanJaxrsApplication2.java: -------------------------------------------------------------------------------- 1 | package com.sample.app; 2 | 3 | import jakarta.ws.rs.ApplicationPath; 4 | import jakarta.ws.rs.core.Application; 5 | 6 | /** 7 | * @author facarvalho 8 | */ 9 | @ApplicationPath("sample-app-test") 10 | public class NonSpringBeanJaxrsApplication2 extends Application {} -------------------------------------------------------------------------------- /servlet/resteasy-servlet-spring-boot-starter-test/src/test/java/com/sample/app/test/AsyncJobIT.java: -------------------------------------------------------------------------------- 1 | package com.sample.app.test; 2 | 3 | import static io.restassured.RestAssured.given; 4 | import static org.hamcrest.CoreMatchers.notNullValue; 5 | import static org.hamcrest.Matchers.emptyString; 6 | import static org.hamcrest.Matchers.equalTo; 7 | import static org.hamcrest.Matchers.is; 8 | 9 | import java.util.Properties; 10 | 11 | import org.jboss.resteasy.springboot.common.utils.SocketUtils; 12 | import org.springframework.boot.SpringApplication; 13 | import org.testng.annotations.AfterClass; 14 | import org.testng.annotations.BeforeClass; 15 | import org.testng.annotations.Test; 16 | 17 | import com.sample.app.Application; 18 | 19 | import io.restassured.RestAssured; 20 | import io.restassured.response.Response; 21 | 22 | /** 23 | * Integration tests for RESTEasy Asynchronous Job Service 24 | * 25 | * @author facarvalho 26 | */ 27 | public class AsyncJobIT { 28 | 29 | private final String JAXRS_APP_ASYNC_JOB_ENABLE_PROPERTY = "resteasy.async.job.service.enabled"; 30 | 31 | @BeforeClass 32 | public void setUp() { 33 | int appPort = SocketUtils.findAvailableTcpPort(); 34 | 35 | RestAssured.basePath = "sample-app"; 36 | RestAssured.port = appPort; 37 | 38 | Properties properties = new Properties(); 39 | properties.put("server.servlet.context-parameters." + JAXRS_APP_ASYNC_JOB_ENABLE_PROPERTY, true); 40 | 41 | System.setProperty(JAXRS_APP_ASYNC_JOB_ENABLE_PROPERTY, "true"); 42 | 43 | SpringApplication app = new SpringApplication(Application.class); 44 | app.setDefaultProperties(properties); 45 | app.addListeners(new LogbackTestApplicationListener()); 46 | app.run("--server.port=" + appPort).registerShutdownHook(); 47 | } 48 | 49 | @Test 50 | public void regularRequestTest() { 51 | Response response = given().body("is there anybody out there?").post("/echo"); 52 | response.then().statusCode(200).body("timestamp", notNullValue()).body("echoText", equalTo("is there anybody out there?")); 53 | } 54 | 55 | @Test 56 | public void asyncRequestTest() { 57 | Response response = given().body("is there anybody out there?").post("/echo?asynch=true"); 58 | response.then().statusCode(202).body(is(emptyString())); 59 | 60 | String location = response.getHeader("Location"); 61 | response = given().get(location + "?wait=1000"); 62 | response.then().statusCode(200).body("timestamp", notNullValue()).body("echoText", equalTo("is there anybody out there?")); 63 | } 64 | 65 | @Test 66 | public void fireAndForgetRequestTest() { 67 | Response response = given().body("is there anybody out there?").post("/echo?oneway=true"); 68 | response.then().statusCode(202).body(is(emptyString())); 69 | } 70 | 71 | @AfterClass 72 | public void shuttingDownApplication() { 73 | Response response = given().basePath("/").contentType("application/json").post("/actuator/shutdown"); 74 | response.then().statusCode(200).body("message", equalTo("Shutting down, bye...")); 75 | } 76 | 77 | } 78 | -------------------------------------------------------------------------------- /servlet/resteasy-servlet-spring-boot-starter-test/src/test/java/com/sample/app/test/CommonUseCasesIT.java: -------------------------------------------------------------------------------- 1 | package com.sample.app.test; 2 | 3 | import static io.restassured.RestAssured.given; 4 | import static org.hamcrest.CoreMatchers.any; 5 | import static org.hamcrest.CoreMatchers.equalTo; 6 | import static org.hamcrest.CoreMatchers.hasItems; 7 | import static org.hamcrest.CoreMatchers.notNullValue; 8 | 9 | import org.jboss.resteasy.springboot.common.utils.SocketUtils; 10 | import org.springframework.boot.SpringApplication; 11 | import org.testng.annotations.AfterClass; 12 | import org.testng.annotations.BeforeClass; 13 | import org.testng.annotations.Test; 14 | 15 | import com.sample.app.Application; 16 | 17 | import io.restassured.RestAssured; 18 | import io.restassured.response.Response; 19 | 20 | /** 21 | * This is an integration test based on a simple sample application and 22 | * very common use cases (see sample-app project) 23 | * 24 | * @author facarvalho 25 | */ 26 | public class CommonUseCasesIT { 27 | 28 | @BeforeClass 29 | public void startingApplicationUp() { 30 | RestAssured.basePath = "sample-app"; 31 | int port = SocketUtils.findAvailableTcpPort(); 32 | RestAssured.port = port; 33 | 34 | SpringApplication springApplication = new SpringApplication(Application.class); 35 | springApplication.addListeners(new LogbackTestApplicationListener()); 36 | springApplication.run("--server.port=" + port).registerShutdownHook(); 37 | } 38 | 39 | @AfterClass 40 | public void shuttingDownApplication() { 41 | Response response = given().basePath("/").contentType("application/json").post("/actuator/shutdown"); 42 | response.then().statusCode(200).body("message", equalTo("Shutting down, bye...")); 43 | } 44 | 45 | @Test 46 | public void happyPathTest() { 47 | Response response = given().body("is there anybody out there?").post("/echo"); 48 | response.then().statusCode(200).body("timestamp", notNullValue()).body("echoText", equalTo("is there anybody out there?")); 49 | } 50 | 51 | @Test 52 | public void fieldBasedInjectionResourceTest() { 53 | Response response = given().get("/foo"); 54 | response.then().statusCode(200).body("timestamp", notNullValue()).body("echoText", any(String.class)); 55 | } 56 | 57 | @Test 58 | public void filterTest() { 59 | Response response = given().body("is there anybody out there?").header("ping", "ping").post("/echo"); 60 | response.then().statusCode(200).body("timestamp", notNullValue()).body("echoText", equalTo("is there anybody out there?")).header("pong", equalTo("pong")); 61 | } 62 | 63 | @Test 64 | public void invalidUriPathTest() { 65 | // Notice "eco" is supposed to result in 404 66 | Response response = given().body("is there anybody out there?").post("/eco"); 67 | response.then().statusCode(404).body(equalTo("The resource you've requested, has not been found!")); 68 | } 69 | 70 | @Test 71 | public void invalidBaseUrlTest() { 72 | // Notice "sampl-ap" is supposed to result in 404 73 | Response response = given().basePath("sampl-app").body("is there anybody out there?").post("/echo"); 74 | response.then().statusCode(404).body("status", equalTo(404)).body("error", equalTo("Not Found")); 75 | } 76 | 77 | @Test 78 | public void invalidNoPayloadTest() { 79 | // Notice that the endpoint we are sending a request to uses Bean Validations to assure 80 | // the request message payload is valid. If that is not the case (a blank payload for example), 81 | // then the server is expected to return a 400 response message 82 | Response response = given().body("").accept("application/json").post("/echo"); 83 | response.then().statusCode(400).body("parameterViolations.message", hasItems("must not be empty")); 84 | } 85 | 86 | @Test 87 | public void actuatorTest() throws InterruptedException { 88 | Response response = given().basePath("/").get("/actuator/health"); 89 | response.then().statusCode(200).body("status", equalTo("UP")); 90 | } 91 | 92 | } 93 | -------------------------------------------------------------------------------- /servlet/resteasy-servlet-spring-boot-starter-test/src/test/java/com/sample/app/test/ConfigurationIT.java: -------------------------------------------------------------------------------- 1 | package com.sample.app.test; 2 | 3 | import io.restassured.response.Response; 4 | 5 | import org.jboss.resteasy.springboot.common.utils.SocketUtils; 6 | import org.springframework.boot.WebApplicationType; 7 | import org.springframework.boot.builder.SpringApplicationBuilder; 8 | import org.springframework.context.ConfigurableApplicationContext; 9 | import org.testng.Assert; 10 | import org.testng.annotations.Test; 11 | 12 | import com.sample.app.NonSpringBeanJaxrsApplication2; 13 | 14 | import jakarta.ws.rs.core.MediaType; 15 | import java.util.Properties; 16 | 17 | import static io.restassured.RestAssured.given; 18 | import static org.hamcrest.CoreMatchers.equalTo; 19 | import static org.hamcrest.CoreMatchers.notNullValue; 20 | 21 | /** 22 | * Integration test that tests a couple sample application (see sample-app and sample-app-no-jaxrs-application). 23 | * This class test possible configurations to register JAX-RS application classes. 24 | * 25 | * @author facarvalho 26 | */ 27 | public class ConfigurationIT { 28 | 29 | private CtxAndPort configureAndStartApp(Properties properties, Class springBootApplicationClass) { 30 | return configureAndStartApp(properties, true, springBootApplicationClass); 31 | } 32 | 33 | CtxAndPort configureAndStartApp(Properties properties, boolean assertPerfectLog, 34 | Class springBootApplicationClass) { 35 | 36 | final SpringApplicationBuilder builder = new SpringApplicationBuilder(springBootApplicationClass); 37 | 38 | builder.web(WebApplicationType.SERVLET); 39 | if (assertPerfectLog) { 40 | builder.listeners(new LogbackTestApplicationListener()); 41 | } 42 | if (properties != null) { 43 | builder.properties(properties); 44 | } 45 | 46 | final int port = SocketUtils.findAvailableTcpPort(); 47 | 48 | // give the SpringApplicationBuilder some time to throw exception firstly. 49 | try { 50 | Thread.sleep(1000); 51 | } catch (InterruptedException e) { 52 | throw new RuntimeException(e); 53 | } 54 | // NOTE: this async operation causes random `invalidClassTest` test failure. 55 | // InvalidClassTest -> java.lang.AssertionError: Application run failed 56 | // Which comes from: spring-boot-project/spring-boot/src/main/java/org/springframework/boot/SpringApplication.java:818: logger.error("Application run failed", failure); 57 | final ConfigurableApplicationContext ctx = builder.run("--debug", "--server.port=" + port); 58 | 59 | try { 60 | Thread.sleep(1000); 61 | } catch (InterruptedException e) { 62 | throw new RuntimeException(e); 63 | } 64 | 65 | ctx.registerShutdownHook(); 66 | 67 | return new CtxAndPort(ctx, port); 68 | } 69 | 70 | private void assertResourceFound(int port, String basePath) { 71 | Response response = given().basePath(basePath).port(port).body("is there anybody out there?").contentType(MediaType.TEXT_PLAIN).post("/echo"); 72 | response.then().statusCode(200).body("timestamp", notNullValue()).body("echoText", equalTo("is there anybody out there?")); 73 | } 74 | 75 | private void assertResourceNotFound(int port, String basePath) { 76 | Response response = given().basePath(basePath).port(port).body("is there anybody out there?").post("/echo"); 77 | response.then().statusCode(404).body("status", equalTo(404)).body("error", equalTo("Not Found")); 78 | } 79 | 80 | @Test 81 | public void implicitAutoTest() { 82 | final CtxAndPort ctxAndPort = configureAndStartApp(null, com.sample.app.Application.class); 83 | 84 | assertResourceFound(ctxAndPort.port, "sample-app"); 85 | assertResourceNotFound(ctxAndPort.port, "sample-app-test"); 86 | assertResourceNotFound(ctxAndPort.port, "/"); 87 | 88 | shutdownCtx(ctxAndPort); 89 | } 90 | 91 | @Test 92 | public void explicitAutoTest() { 93 | final Properties properties = new Properties(); 94 | properties.put("resteasy.jaxrs.app.registration", "auto"); 95 | 96 | final CtxAndPort ctxAndPort = configureAndStartApp(properties, com.sample.app.Application.class); 97 | 98 | assertResourceFound(ctxAndPort.port, "sample-app"); 99 | assertResourceNotFound(ctxAndPort.port, "sample-app-test"); 100 | assertResourceNotFound(ctxAndPort.port, "/"); 101 | 102 | shutdownCtx(ctxAndPort); 103 | } 104 | 105 | private void shutdownCtx(CtxAndPort ctxAndPort) { 106 | Response response = given().port(ctxAndPort.port).basePath("/").contentType("application/json").post("/actuator/shutdown"); 107 | response.then().statusCode(200).body("message", equalTo("Shutting down, bye...")); 108 | } 109 | 110 | @Test 111 | public void beansTest() { 112 | final Properties properties = new Properties(); 113 | properties.put("resteasy.jaxrs.app.registration", "beans"); 114 | 115 | final CtxAndPort ctxAndPort = configureAndStartApp(properties, com.sample.app.Application.class); 116 | 117 | assertResourceFound(ctxAndPort.port, "sample-app"); 118 | assertResourceNotFound(ctxAndPort.port, "sample-app-test"); 119 | assertResourceNotFound(ctxAndPort.port, "/"); 120 | 121 | shutdownCtx(ctxAndPort); 122 | } 123 | 124 | @Test 125 | public void propertySpringBeanClassTest() { 126 | final Properties properties = new Properties(); 127 | properties.put("resteasy.jaxrs.app.registration", "property"); 128 | properties.put("resteasy.jaxrs.app.classes", "com.sample.app.configuration.JaxrsApplication"); 129 | 130 | final CtxAndPort ctxAndPort = configureAndStartApp(properties, com.sample.app.Application.class); 131 | 132 | assertResourceFound(ctxAndPort.port, "sample-app"); 133 | assertResourceNotFound(ctxAndPort.port, "sample-app-test"); 134 | assertResourceNotFound(ctxAndPort.port, "/"); 135 | 136 | shutdownCtx(ctxAndPort); 137 | } 138 | 139 | @Test 140 | public void propertyNonSpringBeanClassTest() { 141 | final Properties properties = new Properties(); 142 | properties.put("resteasy.jaxrs.app.registration", "property"); 143 | properties.put("resteasy.jaxrs.app.classes", NonSpringBeanJaxrsApplication2.class.getTypeName()); 144 | 145 | final CtxAndPort ctxAndPort = configureAndStartApp(properties, com.sample.app.Application.class); 146 | 147 | assertResourceNotFound(ctxAndPort.port, "sample-app"); 148 | assertResourceFound(ctxAndPort.port, "sample-app-test"); 149 | assertResourceNotFound(ctxAndPort.port, "/"); 150 | 151 | shutdownCtx(ctxAndPort); 152 | } 153 | 154 | 155 | 156 | @Test 157 | public void scanningTest() { 158 | final Properties properties = new Properties(); 159 | properties.put("resteasy.jaxrs.app.registration", "scanning"); 160 | 161 | final CtxAndPort ctxAndPort = configureAndStartApp(properties, com.sample.app.Application.class); 162 | 163 | // we expect that the scanning will only find jax-rs application classes that are located 164 | // under the package that the main Spring Boot application class is found 165 | assertResourceFound(ctxAndPort.port, "sample-app"); 166 | assertResourceFound(ctxAndPort.port, "sample-app-test-two"); 167 | assertResourceNotFound(ctxAndPort.port, "/"); 168 | 169 | shutdownCtx(ctxAndPort); 170 | } 171 | 172 | @Test 173 | public void noJaxrsApplicationAndImplicitAutoTest() { 174 | final CtxAndPort ctxAndPort = configureAndStartApp(null, com.sample.app2.Application.class); 175 | 176 | // since there is no jax-rs application class, we expect the app to respond on the root path 177 | assertResourceFound(ctxAndPort.port, "/"); 178 | 179 | // this test is special so we manually shutdown the server here 180 | ctxAndPort.ctx.stop(); 181 | } 182 | 183 | @Test 184 | public void noJaxrsApplicationAndImplicitAutoWithDefaultPathTest() { 185 | final Properties properties = new Properties(); 186 | properties.put("resteasy.jaxrs.defaultPath", "/testpath"); 187 | final CtxAndPort ctxAndPort = configureAndStartApp(properties, com.sample.app2.Application.class); 188 | 189 | // since there is no jax-rs application class, we expect the app to respond on the root path 190 | assertResourceFound(ctxAndPort.port, "/testpath"); 191 | 192 | shutdownCtx(ctxAndPort); 193 | } 194 | 195 | private static class CtxAndPort { 196 | final ConfigurableApplicationContext ctx; 197 | final int port; 198 | 199 | 200 | CtxAndPort(ConfigurableApplicationContext ctx, int port) { 201 | this.port = port; 202 | this.ctx = ctx; 203 | } 204 | } 205 | } 206 | -------------------------------------------------------------------------------- /servlet/resteasy-servlet-spring-boot-starter-test/src/test/java/com/sample/app/test/InvalidClassTest.java: -------------------------------------------------------------------------------- 1 | package com.sample.app.test; 2 | 3 | import org.testng.Assert; 4 | import org.testng.annotations.Test; 5 | 6 | import java.util.Properties; 7 | 8 | public class InvalidClassTest extends ConfigurationIT { 9 | @Test 10 | public void invalidClassTest() { 11 | final Properties properties = new Properties(); 12 | properties.put("resteasy.jaxrs.app.registration", "property"); 13 | properties.put("resteasy.jaxrs.app.classes", "com.foor.bar.NonExistentApplicationClass"); 14 | 15 | try { 16 | configureAndStartApp(properties, false, com.sample.app.Application.class); 17 | } catch (Throwable ex) { 18 | Assert.assertEquals(ex.getCause().getClass(), ClassNotFoundException.class); 19 | Assert.assertEquals(ex.getCause().getMessage(), "com.foor.bar.NonExistentApplicationClass"); 20 | return; 21 | } 22 | 23 | Assert.fail("Expected exception, due to class not found, has not been thrown"); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /servlet/resteasy-servlet-spring-boot-starter-test/src/test/java/com/sample/app/test/LogbackTestApplicationListener.java: -------------------------------------------------------------------------------- 1 | package com.sample.app.test; 2 | 3 | import ch.qos.logback.classic.Level; 4 | import ch.qos.logback.classic.Logger; 5 | import ch.qos.logback.classic.LoggerContext; 6 | import ch.qos.logback.classic.spi.ILoggingEvent; 7 | import ch.qos.logback.core.Appender; 8 | import ch.qos.logback.core.AppenderBase; 9 | import org.slf4j.LoggerFactory; 10 | import org.springframework.boot.context.event.ApplicationEnvironmentPreparedEvent; 11 | import org.springframework.context.ApplicationEvent; 12 | import org.springframework.context.event.ContextClosedEvent; 13 | import org.springframework.context.event.SmartApplicationListener; 14 | import org.springframework.core.Ordered; 15 | import org.testng.Assert; 16 | 17 | import java.net.InetAddress; 18 | 19 | /** 20 | * The Spring application listener registers a Logback appender 21 | * which allows inspecting every log statement looking for warning 22 | * or error messages. If any is found, the test will fail. 23 | * 24 | * @author facarvalho 25 | */ 26 | public class LogbackTestApplicationListener implements SmartApplicationListener { 27 | 28 | private boolean warningOrErrorFound = false; 29 | 30 | private Appender appender = new AppenderBase() { 31 | 32 | // TODO 33 | // Remove this after implementing https://github.com/paypal/resteasy-spring-boot/issues/69 34 | private static final java.lang.String SCANNING_WARNING = "\n-------------\nStarting on version 3.0.0, the behavior of the `scanning`"; 35 | 36 | @Override 37 | protected void append(ILoggingEvent event) { 38 | if (event == null || warningOrErrorFound) { 39 | return; 40 | } 41 | Level level = event.getLevel(); 42 | if ((level.equals(Level.WARN) || level.equals(Level.ERROR)) 43 | && !event.getMessage().startsWith(SCANNING_WARNING) 44 | && !event.getMessage().startsWith("InetAddress.getLocalHost") // On MacOS Java 11 it sometimes generate relative warning so we ignore it. 45 | && !event.getLoggerName().equals("org.apache.tomcat.util.modeler.Registry")) // Tomcat generate relative warnings when the servers are started/stoped multiple times during tests. 46 | { 47 | warningOrErrorFound = true; 48 | Assert.fail(event.getFormattedMessage()); 49 | } 50 | } 51 | }; 52 | 53 | private void addTestAppender() { 54 | LoggerContext loggerContext = (LoggerContext) LoggerFactory.getILoggerFactory(); 55 | appender.setContext(loggerContext); 56 | appender.start(); 57 | Logger rootLogger = loggerContext.getLogger(Logger.ROOT_LOGGER_NAME); 58 | rootLogger.addAppender(appender); 59 | } 60 | 61 | private void detachTestAppender() { 62 | if (appender != null) { 63 | appender.stop(); 64 | LoggerContext loggerContext = (LoggerContext) LoggerFactory.getILoggerFactory(); 65 | Logger rootLogger = loggerContext.getLogger(Logger.ROOT_LOGGER_NAME); 66 | rootLogger.detachAppender(appender); 67 | } 68 | } 69 | 70 | public boolean supportsEventType(Class eventType) { 71 | return ApplicationEnvironmentPreparedEvent.class.isAssignableFrom(eventType); 72 | } 73 | 74 | public boolean supportsSourceType(Class sourceType) { 75 | return true; 76 | } 77 | 78 | @Override 79 | public void onApplicationEvent(ApplicationEvent event) { 80 | if (event instanceof ApplicationEnvironmentPreparedEvent) { 81 | addTestAppender(); 82 | } else if (event instanceof ContextClosedEvent && ((ContextClosedEvent) event).getApplicationContext().getParent() == null) { 83 | detachTestAppender(); 84 | } 85 | } 86 | 87 | public int getOrder() { 88 | return Ordered.HIGHEST_PRECEDENCE - 12; 89 | } 90 | 91 | } 92 | -------------------------------------------------------------------------------- /servlet/resteasy-servlet-spring-boot-starter-test/src/test/java/com/sample/app/test/MultipleContextsIT.java: -------------------------------------------------------------------------------- 1 | package com.sample.app.test; 2 | 3 | import static io.restassured.RestAssured.given; 4 | import static org.hamcrest.CoreMatchers.notNullValue; 5 | import static org.hamcrest.Matchers.equalTo; 6 | 7 | import java.util.Properties; 8 | 9 | import org.jboss.resteasy.springboot.common.utils.SocketUtils; 10 | import org.springframework.boot.SpringApplication; 11 | import org.testng.annotations.AfterClass; 12 | import org.testng.annotations.BeforeClass; 13 | import org.testng.annotations.Test; 14 | 15 | import com.sample.app.Application; 16 | import com.test.multicontexttest.MultiContextTestApp; 17 | 18 | import io.restassured.RestAssured; 19 | import io.restassured.response.Response; 20 | 21 | /** 22 | * This test assures that the RESTEasy and Spring integration, promoted by the starter, 23 | * does not cause any conflicts when there is more than one application context 24 | * registered, as reported in the past by this bug (already fixed): 25 | * https://github.com/paypal/resteasy-spring-boot/issues/51 26 | * 27 | * @author facarvalho 28 | */ 29 | public class MultipleContextsIT { 30 | 31 | private int app1Port, app2Port; 32 | 33 | @BeforeClass 34 | public void setUp() { 35 | app1Port = SocketUtils.findAvailableTcpPort(); 36 | app2Port = SocketUtils.findAvailableTcpPort(); 37 | 38 | RestAssured.basePath = "sample-app"; 39 | } 40 | 41 | @Test 42 | public void test() { 43 | Properties properties = new Properties(); 44 | properties.put("spring.jmx.enabled", false); 45 | 46 | SpringApplication app1 = new SpringApplication(Application.class); 47 | app1.setDefaultProperties(properties); 48 | app1.addListeners(new LogbackTestApplicationListener()); 49 | app1.run("--server.port=" + app1Port).registerShutdownHook(); 50 | 51 | SpringApplication app2 = new SpringApplication(MultiContextTestApp.class); 52 | app2.setDefaultProperties(properties); 53 | app2.addListeners(new LogbackTestApplicationListener()); 54 | app2.run("--server.port=" + app2Port).registerShutdownHook(); 55 | 56 | Response response; 57 | 58 | response = given().port(app1Port).body("is there anybody out there?").post("/echo"); 59 | response.then().statusCode(200).body("timestamp", notNullValue()).body("echoText", equalTo("is there anybody out there?")); 60 | 61 | response = given().port(app2Port).body("is there anybody out there?").post("/echo"); 62 | response.then().statusCode(200).body("timestamp", notNullValue()).body("echoText", equalTo("I don't want to echo anything today")); 63 | } 64 | 65 | @AfterClass 66 | public void shuttingDownApplication() { 67 | Response response; 68 | 69 | response = given().port(app1Port).basePath("/").contentType("application/json").post("/actuator/shutdown"); 70 | response.then().statusCode(200).body("message", equalTo("Shutting down, bye...")); 71 | 72 | response = given().port(app2Port).basePath("/").contentType("application/json").post("/actuator/shutdown"); 73 | response.then().statusCode(200).body("message", equalTo("Shutting down, bye...")); 74 | } 75 | 76 | } 77 | -------------------------------------------------------------------------------- /servlet/resteasy-servlet-spring-boot-starter-test/src/test/java/com/test/multicontexttest/JaxrsApplication.java: -------------------------------------------------------------------------------- 1 | package com.test.multicontexttest; 2 | 3 | import org.springframework.stereotype.Component; 4 | 5 | import jakarta.ws.rs.ApplicationPath; 6 | import jakarta.ws.rs.core.Application; 7 | 8 | /** 9 | * JAX-RS application 10 | * 11 | * Created by facarvalho on 12/7/15. 12 | */ 13 | @Component 14 | @ApplicationPath("/sample-app/") 15 | public class JaxrsApplication extends Application { 16 | } 17 | -------------------------------------------------------------------------------- /servlet/resteasy-servlet-spring-boot-starter-test/src/test/java/com/test/multicontexttest/MultiContextTestApp.java: -------------------------------------------------------------------------------- 1 | package com.test.multicontexttest; 2 | 3 | import org.jboss.resteasy.springboot.common.sample.resources.EchoMessage; 4 | import org.jboss.resteasy.springboot.common.sample.resources.IEchoMessageCreator; 5 | import org.springframework.boot.SpringApplication; 6 | import org.springframework.boot.autoconfigure.SpringBootApplication; 7 | import org.springframework.boot.web.servlet.support.SpringBootServletInitializer; 8 | import org.springframework.context.annotation.Bean; 9 | 10 | /** 11 | * SpringBoot entry point application 12 | * 13 | * Created by facarvalho on 12/7/15. 14 | */ 15 | @SpringBootApplication(scanBasePackages = { "org.jboss.resteasy.springboot", "com.test.multicontexttest"}) 16 | public class MultiContextTestApp extends SpringBootServletInitializer { 17 | 18 | public static void main(String[] args) { 19 | SpringApplication.run(MultiContextTestApp.class, args); 20 | } 21 | 22 | @Bean 23 | public IEchoMessageCreator echoMessageCreator() { 24 | return new IEchoMessageCreator() { 25 | public EchoMessage createEchoMessage(final String echoText) { 26 | return new EchoMessage("I don't want to echo anything today"); 27 | } 28 | }; 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /servlet/resteasy-servlet-spring-boot-starter-test/src/test/resources/application.yaml: -------------------------------------------------------------------------------- 1 | management: 2 | endpoints: 3 | web: 4 | exposure: 5 | include: 6 | - health 7 | - shutdown 8 | endpoint: 9 | shutdown: 10 | enabled: true 11 | logging: 12 | level: 13 | org: 14 | springframework: info 15 | -------------------------------------------------------------------------------- /servlet/resteasy-servlet-spring-boot-starter/cacerts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resteasy/resteasy-spring-boot/1a61abe02dd04c50038b54d433ecb080754f7e94/servlet/resteasy-servlet-spring-boot-starter/cacerts -------------------------------------------------------------------------------- /servlet/resteasy-servlet-spring-boot-starter/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | 7 | org.jboss.resteasy 8 | resteasy-servlet-spring-boot-starter-parent 9 | 6.3.1-SNAPSHOT 10 | ../pom.xml 11 | 12 | 13 | resteasy-servlet-spring-boot-starter 14 | ${project.artifactId} 15 | 16 | A Spring Boot starter for RESTEasy applications that are Servlet based 17 | https://github.com/resteasy/resteasy-spring-boot 18 | 19 | 20 | 21 | The Apache License, Version 2.0 22 | http://www.apache.org/licenses/LICENSE-2.0.txt 23 | 24 | 25 | 26 | 27 | Fabio Carvalho 28 | facarvalho@paypal.com 29 | PayPal 30 | http://www.paypal.com 31 | 32 | 33 | Alessio Soldano 34 | alessio.soldano@jboss.com 35 | Red Hat 36 | http://www.redhat.com 37 | 38 | 39 | 40 | 41 | scm:git:git@github.com:resteasy/resteasy-spring-boot.git 42 | scm:git:git@github.com:resteasy/resteasy-spring-boot.git 43 | git@github.com:resteasy/resteasy-spring-boot.git 44 | 45 | 46 | 47 | JIRA 48 | http://jira.jboss.com/jira/browse/RESTEASY 49 | 50 | 51 | 52 | 53 | 54 | org.springframework.boot 55 | spring-boot-starter-parent 56 | ${springboot.version} 57 | pom 58 | import 59 | 60 | 61 | ${project.groupId} 62 | resteasy-spring-boot-bom 63 | ${project.version} 64 | pom 65 | import 66 | 67 | 68 | 69 | 70 | 71 | 72 | org.jboss.resteasy 73 | resteasy-spring-boot-starter-common 74 | ${project.version} 75 | 76 | 77 | org.springframework.boot 78 | spring-boot-starter 79 | 80 | 81 | org.mockito 82 | mockito-core 83 | 84 | 85 | 86 | 87 | org.mockito 88 | mockito-core 89 | test 90 | 91 | 92 | org.mockito 93 | mockito-inline 94 | test 95 | 96 | 97 | org.springframework.boot 98 | spring-boot-starter-web 99 | 100 | 101 | org.springframework.boot 102 | spring-boot-starter-tomcat 103 | 104 | 105 | 106 | 107 | jakarta.servlet 108 | jakarta.servlet-api 109 | provided 110 | 111 | 112 | org.jboss.resteasy 113 | resteasy-core 114 | 115 | 116 | org.jboss.resteasy 117 | resteasy-core-spi 118 | 119 | 120 | org.jboss.resteasy 121 | resteasy-servlet-initializer 122 | provided 123 | 124 | 125 | org.jboss.resteasy.spring 126 | resteasy-spring 127 | 128 | 129 | org.jboss.resteasy 130 | resteasy-jackson2-provider 131 | 132 | 133 | 134 | 135 | org.springframework.boot 136 | spring-boot-starter-test 137 | test 138 | 139 | 140 | org.testng 141 | testng 142 | ${ver.testng} 143 | test 144 | 145 | 146 | jakarta.validation 147 | jakarta.validation-api 148 | 149 | 150 | commons-codec 151 | commons-codec 152 | 153 | 154 | 155 | 156 | 157 | 158 | org.apache.maven.plugins 159 | maven-deploy-plugin 160 | 161 | false 162 | 163 | 164 | 165 | org.apache.maven.plugins 166 | maven-surefire-plugin 167 | 168 | ${modular.jdk.args} ${modular.jdk.props} --add-opens java.base/java.lang=ALL-UNNAMED 169 | -Dillegal-access=permit 170 | 171 | 172 | 173 | 174 | org.apache.maven.plugins 175 | maven-enforcer-plugin 176 | 177 | 178 | 179 | 180 | -------------------------------------------------------------------------------- /servlet/resteasy-servlet-spring-boot-starter/settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | ossrh 6 | ${env.SONATYPE_USER} 7 | ${env.SONATYPE_PASSWORD} 8 | 9 | 10 | -------------------------------------------------------------------------------- /servlet/resteasy-servlet-spring-boot-starter/src/main/java/org/jboss/resteasy/springboot/ResteasyApplicationBuilder.java: -------------------------------------------------------------------------------- 1 | package org.jboss.resteasy.springboot; 2 | 3 | import java.util.Set; 4 | 5 | import jakarta.servlet.Servlet; 6 | import jakarta.servlet.ServletContainerInitializer; 7 | import jakarta.ws.rs.core.Application; 8 | 9 | import org.jboss.resteasy.plugins.server.servlet.HttpServlet30Dispatcher; 10 | import org.jboss.resteasy.plugins.server.servlet.ResteasyContextParameters; 11 | import org.jboss.resteasy.plugins.servlet.ResteasyServletInitializer; 12 | import org.slf4j.Logger; 13 | import org.slf4j.LoggerFactory; 14 | import org.springframework.boot.web.servlet.ServletRegistrationBean; 15 | 16 | /** 17 | * This class is the Spring Boot equivalent of {@link ResteasyServletInitializer}, 18 | * which implements the Servlet API {@link ServletContainerInitializer} interface 19 | * to find all JAX-RS Application, Provider and Path classes in the classpath. 20 | * 21 | * As we all know, in Spring Boot we use an embedded servlet container. However, 22 | * the Servlet spec does not support embedded containers, and many portions of it 23 | * do not apply to embedded containers, and ServletContainerInitializer is one of them. 24 | * 25 | * This class fills in this gap. 26 | * 27 | * Notice that the JAX-RS Application classes are found in this RESTEasy starter by class 28 | * ResteasyEmbeddedServletInitializer, and that is done by scanning the classpath. 29 | * 30 | * The Path and Provider annotated classes are found by using Spring framework (instead of 31 | * scanning the classpath), since it is assumed those classes are ALWAYS necessarily 32 | * Spring beans (this starter is meant for Spring Boot applications that use RESTEasy 33 | * as the JAX-RS implementation) 34 | * 35 | * @author Fabio Carvalho (facarvalho@paypal.com or fabiocarvalho777@gmail.com) 36 | */ 37 | public class ResteasyApplicationBuilder { 38 | 39 | public static final String BEAN_NAME = "JaxrsApplicationServletBuilder"; 40 | 41 | private static final Logger logger = LoggerFactory.getLogger(ResteasyApplicationBuilder.class); 42 | 43 | public ServletRegistrationBean build(String applicationClassName, String path, Set> resources, Set> providers) { 44 | Servlet servlet = new HttpServlet30Dispatcher(); 45 | 46 | ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean(servlet); 47 | 48 | servletRegistrationBean.setName(applicationClassName); 49 | servletRegistrationBean.setLoadOnStartup(1); 50 | servletRegistrationBean.setAsyncSupported(true); 51 | // servletRegistrationBean.addInitParameter(Application.class.getTypeName(), applicationClassName); 52 | servletRegistrationBean.addInitParameter("jakarta.ws.rs.Application", applicationClassName); 53 | 54 | if (path != null) { 55 | String mapping = path; 56 | if (!mapping.startsWith("/")) 57 | mapping = "/" + mapping; 58 | String prefix = mapping; 59 | if (!"/".equals(prefix) && prefix.endsWith("/")) 60 | prefix = prefix.substring(0, prefix.length() - 1); 61 | if (mapping.endsWith("/")) 62 | mapping += "*"; 63 | else 64 | mapping += "/*"; 65 | servletRegistrationBean.addInitParameter("resteasy.servlet.mapping.prefix", prefix); 66 | servletRegistrationBean.addUrlMappings(mapping); 67 | } 68 | 69 | if (resources.size() > 0) { 70 | StringBuilder builder = new StringBuilder(); 71 | boolean first = true; 72 | for (Class resource : resources) { 73 | if (first) { 74 | first = false; 75 | } else { 76 | builder.append(","); 77 | } 78 | 79 | builder.append(resource.getName()); 80 | } 81 | servletRegistrationBean.addInitParameter(ResteasyContextParameters.RESTEASY_SCANNED_RESOURCES, builder.toString()); 82 | } 83 | if (providers.size() > 0) { 84 | StringBuilder builder = new StringBuilder(); 85 | boolean first = true; 86 | for (Class provider : providers) { 87 | if (first) { 88 | first = false; 89 | } else { 90 | builder.append(","); 91 | } 92 | builder.append(provider.getName()); 93 | } 94 | servletRegistrationBean.addInitParameter(ResteasyContextParameters.RESTEASY_SCANNED_PROVIDERS, builder.toString()); 95 | } 96 | 97 | logger.debug("ServletRegistrationBean has just bean created for JAX-RS class " + applicationClassName); 98 | 99 | return servletRegistrationBean; 100 | } 101 | 102 | } 103 | -------------------------------------------------------------------------------- /servlet/resteasy-servlet-spring-boot-starter/src/main/java/org/jboss/resteasy/springboot/ResteasyAutoConfiguration.java: -------------------------------------------------------------------------------- 1 | package org.jboss.resteasy.springboot; 2 | 3 | import jakarta.servlet.ServletContext; 4 | import jakarta.servlet.ServletContextEvent; 5 | import jakarta.servlet.ServletContextListener; 6 | 7 | import org.jboss.resteasy.plugins.server.servlet.ListenerBootstrap; 8 | import org.jboss.resteasy.plugins.server.servlet.ResteasyBootstrap; 9 | import org.jboss.resteasy.plugins.spring.SpringBeanProcessor; 10 | import org.jboss.resteasy.spi.Dispatcher; 11 | import org.jboss.resteasy.spi.Registry; 12 | import org.jboss.resteasy.spi.ResteasyDeployment; 13 | import org.jboss.resteasy.spi.ResteasyProviderFactory; 14 | import org.jboss.resteasy.springboot.common.DeploymentCustomizer; 15 | import org.jboss.resteasy.springboot.common.ResteasyBeanProcessorFactory; 16 | import org.slf4j.Logger; 17 | import org.slf4j.LoggerFactory; 18 | import org.springframework.beans.factory.annotation.Qualifier; 19 | import org.springframework.boot.autoconfigure.AutoConfiguration; 20 | import org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration; 21 | import org.springframework.boot.context.properties.EnableConfigurationProperties; 22 | import org.springframework.context.annotation.Bean; 23 | 24 | /** 25 | * This is the main class that configures this Resteasy Sring Boot starter 26 | * 27 | * @author Fabio Carvalho (facarvalho@paypal.com or fabiocarvalho777@gmail.com) 28 | */ 29 | @AutoConfiguration(after = WebMvcAutoConfiguration.class) 30 | @EnableConfigurationProperties 31 | public class ResteasyAutoConfiguration { 32 | 33 | private static Logger logger = LoggerFactory.getLogger(ResteasyAutoConfiguration.class); 34 | 35 | 36 | /** 37 | * This is a modified version of {@link ResteasyBootstrap} 38 | * @param resteasySpringBeanProcessor - A bean processor for Resteasy. 39 | * 40 | * @return a ServletContextListener object that configures and start a ResteasyDeployment 41 | */ 42 | @Bean 43 | public ServletContextListener resteasyBootstrapListener( 44 | final @Qualifier("resteasySpringBeanProcessor") SpringBeanProcessor resteasySpringBeanProcessor) { 45 | 46 | ServletContextListener servletContextListener = new ServletContextListener() { 47 | 48 | protected ResteasyDeployment deployment; 49 | 50 | public void contextInitialized(ServletContextEvent sce) { 51 | ServletContext servletContext = sce.getServletContext(); 52 | 53 | deployment = new ListenerBootstrap(servletContext).createDeployment(); 54 | DeploymentCustomizer.customizeRestEasyDeployment(resteasySpringBeanProcessor, deployment, 55 | deployment.isAsyncJobServiceEnabled()); 56 | deployment.start(); 57 | 58 | servletContext.setAttribute(ResteasyProviderFactory.class.getName(), deployment.getProviderFactory()); 59 | servletContext.setAttribute(Dispatcher.class.getName(), deployment.getDispatcher()); 60 | servletContext.setAttribute(Registry.class.getName(), deployment.getRegistry()); 61 | } 62 | 63 | public void contextDestroyed(ServletContextEvent sce) { 64 | if (deployment != null) { 65 | deployment.stop(); 66 | } 67 | } 68 | }; 69 | 70 | logger.debug("ServletContextListener has been created"); 71 | 72 | return servletContextListener; 73 | } 74 | 75 | @Bean(name = ResteasyApplicationBuilder.BEAN_NAME) 76 | public ResteasyApplicationBuilder resteasyApplicationBuilder() { 77 | return new ResteasyApplicationBuilder(); 78 | } 79 | 80 | @Bean 81 | public static ResteasyBeanProcessorTomcat resteasyBeanProcessorTomcat() { 82 | return new ResteasyBeanProcessorTomcat(); 83 | } 84 | 85 | @Bean("resteasySpringBeanProcessor") 86 | public static SpringBeanProcessor resteasySpringBeanProcessor() { 87 | return ResteasyBeanProcessorFactory.resteasySpringBeanProcessor(); 88 | } 89 | 90 | } 91 | -------------------------------------------------------------------------------- /servlet/resteasy-servlet-spring-boot-starter/src/main/java/org/jboss/resteasy/springboot/ResteasyBeanProcessorTomcat.java: -------------------------------------------------------------------------------- 1 | package org.jboss.resteasy.springboot; 2 | 3 | import org.jboss.resteasy.plugins.servlet.ResteasyServletInitializer; 4 | import org.jboss.resteasy.springboot.common.ResteasyResourcesFinder; 5 | import org.slf4j.Logger; 6 | import org.slf4j.LoggerFactory; 7 | import org.springframework.beans.BeansException; 8 | import org.springframework.beans.factory.config.BeanFactoryPostProcessor; 9 | import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; 10 | import org.springframework.beans.factory.config.ConstructorArgumentValues; 11 | import org.springframework.beans.factory.support.BeanDefinitionRegistry; 12 | import org.springframework.beans.factory.support.GenericBeanDefinition; 13 | import org.springframework.core.annotation.AnnotationUtils; 14 | import org.springframework.core.env.ConfigurableEnvironment; 15 | 16 | import jakarta.ws.rs.ApplicationPath; 17 | import jakarta.ws.rs.core.Application; 18 | import java.util.*; 19 | 20 | /** 21 | * This is a Spring version of {@link ResteasyServletInitializer}. 22 | * It does not register the servlets though, that is done by {@link ResteasyApplicationBuilder} 23 | * It only finds the JAX-RS Application classes (by scanning the classpath), and 24 | * the JAX-RS Path and Provider annotated Spring beans, and then register the 25 | * Spring bean definitions that represent each servlet registration. 26 | * 27 | * @author Fabio Carvalho (facarvalho@paypal.com or fabiocarvalho777@gmail.com) 28 | */ 29 | public class ResteasyBeanProcessorTomcat extends ResteasyResourcesFinder implements BeanFactoryPostProcessor { 30 | 31 | private static final Logger logger = LoggerFactory.getLogger(ResteasyBeanProcessorTomcat.class); 32 | 33 | private static final String JAXRS_DEFAULT_PATH = "resteasy.jaxrs.defaultPath"; 34 | private static final String DEFAULT_BASE_APP_PATH = "/"; 35 | 36 | public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { 37 | 38 | logger.debug("Post process bean factory has been called"); 39 | 40 | findJaxrsApplications(beanFactory); 41 | 42 | // This is done by finding their related Spring beans 43 | findJaxrsResourcesAndProviderClasses(beanFactory); 44 | 45 | if (getApplications().size() == 0) { 46 | registerDefaultJaxrsApp(beanFactory); 47 | return; 48 | } 49 | 50 | BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory; 51 | 52 | for (Class applicationClass : getApplications()) { 53 | 54 | ApplicationPath path = AnnotationUtils.findAnnotation(applicationClass, ApplicationPath.class); 55 | logger.debug("registering JAX-RS application class " + applicationClass.getName()); 56 | GenericBeanDefinition applicationServletBean = createApplicationServlet(applicationClass, path.value()); 57 | registry.registerBeanDefinition(applicationClass.getName(), applicationServletBean); 58 | 59 | } 60 | 61 | } 62 | 63 | /** 64 | * Register a default JAX-RS application, in case no other is present in the application. 65 | * Read section 2.3.2 in JAX-RS 2.0 specification. 66 | * 67 | * @param beanFactory 68 | */ 69 | private void registerDefaultJaxrsApp(ConfigurableListableBeanFactory beanFactory) { 70 | BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory; 71 | ConfigurableEnvironment configurableEnvironment = beanFactory.getBean(ConfigurableEnvironment.class); 72 | String path = configurableEnvironment.getProperty(JAXRS_DEFAULT_PATH, DEFAULT_BASE_APP_PATH); 73 | GenericBeanDefinition applicationServletBean = 74 | createApplicationServlet(Application.class, path); 75 | 76 | logger.info("No JAX-RS Application classes have been found. A default, one mapped to '{}', will be registered.", path); 77 | registry.registerBeanDefinition(Application.class.getName(), applicationServletBean); 78 | } 79 | 80 | /** 81 | * Creates a Servlet bean definition for the given JAX-RS application 82 | * 83 | * @param applicationClass 84 | * @param path 85 | * @return a Servlet bean definition for the given JAX-RS application 86 | */ 87 | private GenericBeanDefinition createApplicationServlet(Class applicationClass, String path) { 88 | GenericBeanDefinition applicationServletBean = new GenericBeanDefinition(); 89 | applicationServletBean.setFactoryBeanName(ResteasyApplicationBuilder.BEAN_NAME); 90 | applicationServletBean.setFactoryMethodName("build"); 91 | 92 | Set> resources = getAllResources(); 93 | 94 | ConstructorArgumentValues values = new ConstructorArgumentValues(); 95 | values.addIndexedArgumentValue(0, applicationClass.getName()); 96 | values.addIndexedArgumentValue(1, path); 97 | values.addIndexedArgumentValue(2, resources); 98 | values.addIndexedArgumentValue(3, getProviders()); 99 | applicationServletBean.setConstructorArgumentValues(values); 100 | 101 | applicationServletBean.setAutowireCandidate(false); 102 | applicationServletBean.setScope("singleton"); 103 | 104 | return applicationServletBean; 105 | } 106 | 107 | } 108 | -------------------------------------------------------------------------------- /servlet/resteasy-servlet-spring-boot-starter/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports: -------------------------------------------------------------------------------- 1 | org.jboss.resteasy.springboot.ResteasyAutoConfiguration -------------------------------------------------------------------------------- /servlet/resteasy-servlet-spring-boot-starter/src/test/java/org/jboss/resteasy/springboot/ResteasyAutoConfigurationTest.java: -------------------------------------------------------------------------------- 1 | package org.jboss.resteasy.springboot; 2 | 3 | import org.jboss.resteasy.core.ResourceMethodRegistry; 4 | import org.jboss.resteasy.plugins.spring.SpringBeanProcessor; 5 | import org.jboss.resteasy.spi.Dispatcher; 6 | import org.jboss.resteasy.spi.Registry; 7 | import org.jboss.resteasy.spi.ResteasyProviderFactory; 8 | import org.jboss.resteasy.springboot.common.ResteasyBeanProcessorFactory; 9 | import org.springframework.beans.factory.config.BeanFactoryPostProcessor; 10 | import org.springframework.mock.web.MockServletContext; 11 | import org.testng.Assert; 12 | import org.testng.annotations.Test; 13 | 14 | import jakarta.servlet.ServletContext; 15 | import jakarta.servlet.ServletContextEvent; 16 | import jakarta.servlet.ServletContextListener; 17 | 18 | /** 19 | * Created by facarvalho on 11/24/15. 20 | * @author Fabio Carvalho (facarvalho@paypal.com or fabiocarvalho777@gmail.com) 21 | */ 22 | public class ResteasyAutoConfigurationTest { 23 | 24 | private static final String JAXRS_APP_ASYNC_JOB_ENABLE_PROPERTY = "resteasy.async.job.service.enabled"; 25 | 26 | @Test 27 | public void springBeanProcessor() { 28 | BeanFactoryPostProcessor beanFactoryPostProcessor = ResteasyBeanProcessorFactory.resteasySpringBeanProcessor(); 29 | 30 | Assert.assertNotNull(beanFactoryPostProcessor); 31 | Assert.assertEquals(SpringBeanProcessor.class, beanFactoryPostProcessor.getClass()); 32 | 33 | SpringBeanProcessor springBeanProcessor = (SpringBeanProcessor) beanFactoryPostProcessor; 34 | Registry springBeanProcessorRegistry = springBeanProcessor.getRegistry(); 35 | ResteasyProviderFactory providerFactory = springBeanProcessor.getProviderFactory(); 36 | 37 | Assert.assertNotNull(springBeanProcessorRegistry); 38 | Assert.assertNotNull(providerFactory); 39 | Assert.assertEquals(ResourceMethodRegistry.class, springBeanProcessorRegistry.getClass()); 40 | } 41 | 42 | @Test 43 | public void syncDispatcherServletContextListenerTest() throws Exception { 44 | System.setProperty(JAXRS_APP_ASYNC_JOB_ENABLE_PROPERTY, "false"); 45 | ServletContext servletContext = new MockServletContext(); 46 | testServletContextListener(servletContext); 47 | } 48 | 49 | @Test 50 | public void asyncDispatcherServletContextListenerTest() throws Exception { 51 | ServletContext servletContext = new MockServletContext(); 52 | servletContext.setInitParameter(JAXRS_APP_ASYNC_JOB_ENABLE_PROPERTY, "true"); 53 | System.setProperty(JAXRS_APP_ASYNC_JOB_ENABLE_PROPERTY, "true"); 54 | testServletContextListener(servletContext); 55 | } 56 | 57 | private void testServletContextListener(ServletContext servletContext) throws Exception { 58 | ResteasyAutoConfiguration resteasyAutoConfiguration = new ResteasyAutoConfiguration(); 59 | BeanFactoryPostProcessor beanFactoryPostProcessor = ResteasyBeanProcessorFactory.resteasySpringBeanProcessor(); 60 | ServletContextListener servletContextListener = resteasyAutoConfiguration.resteasyBootstrapListener((SpringBeanProcessor)beanFactoryPostProcessor); 61 | Assert.assertNotNull(servletContextListener); 62 | 63 | ServletContextEvent sce = new ServletContextEvent(servletContext); 64 | servletContextListener.contextInitialized(sce); 65 | 66 | ResteasyProviderFactory servletContextProviderFactory = (ResteasyProviderFactory) servletContext.getAttribute(ResteasyProviderFactory.class.getName()); 67 | Dispatcher servletContextDispatcher = (Dispatcher) servletContext.getAttribute(Dispatcher.class.getName()); 68 | Registry servletContextRegistry = (Registry) servletContext.getAttribute(Registry.class.getName()); 69 | 70 | Assert.assertNotNull(servletContextProviderFactory); 71 | Assert.assertNotNull(servletContextDispatcher); 72 | Assert.assertNotNull(servletContextRegistry); 73 | 74 | servletContextListener.contextDestroyed(sce); 75 | ServletContextListener servletContextListener2 = resteasyAutoConfiguration.resteasyBootstrapListener((SpringBeanProcessor)beanFactoryPostProcessor); 76 | servletContextListener2.contextDestroyed(sce); 77 | } 78 | 79 | 80 | } 81 | -------------------------------------------------------------------------------- /servlet/resteasy-servlet-spring-boot-starter/src/test/java/org/jboss/resteasy/springboot/ResteasyBeanProcessorTest.java: -------------------------------------------------------------------------------- 1 | package org.jboss.resteasy.springboot; 2 | 3 | import java.util.Map; 4 | 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.boot.web.servlet.ServletRegistrationBean; 7 | import org.springframework.context.ApplicationContext; 8 | import org.springframework.test.context.ContextConfiguration; 9 | import org.springframework.test.context.testng.AbstractTestNGSpringContextTests; 10 | import org.testng.Assert; 11 | import org.testng.annotations.Test; 12 | 13 | import jakarta.ws.rs.core.Application; 14 | 15 | /** 16 | * Created by facarvalho on 11/25/15. 17 | * @author Fabio Carvalho (facarvalho@paypal.com or fabiocarvalho777@gmail.com) 18 | */ 19 | @ContextConfiguration("classpath:test-config.xml") 20 | public class ResteasyBeanProcessorTest extends AbstractTestNGSpringContextTests { 21 | 22 | @Autowired 23 | private ApplicationContext applicationContext; 24 | 25 | @Test 26 | public void postProcessBeanFactoryTest() { 27 | Map servletRegistrationBeans = applicationContext.getBeansOfType(ServletRegistrationBean.class); 28 | Assert.assertNotNull(servletRegistrationBeans); 29 | 30 | // Although there are 5 sample JAX-RS Application classes, one of them is not annotated with the ApplicationPath annotation! 31 | Assert.assertEquals(servletRegistrationBeans.size(), 4); 32 | 33 | for(String applicationClassName : servletRegistrationBeans.keySet()) { 34 | testApplication(applicationClassName, servletRegistrationBeans.get(applicationClassName)); 35 | } 36 | } 37 | 38 | 39 | private void testApplication(String applicationClassName, ServletRegistrationBean servletRegistrationBean) { 40 | Assert.assertEquals(applicationClassName, servletRegistrationBean.getServletName()); 41 | Assert.assertEquals(applicationClassName, servletRegistrationBean.getInitParameters().get("jakarta.ws.rs.Application")); 42 | Assert.assertTrue(servletRegistrationBean.isAsyncSupported()); 43 | } 44 | 45 | } -------------------------------------------------------------------------------- /servlet/resteasy-servlet-spring-boot-starter/src/test/java/org/jboss/resteasy/springboot/sample/SampleApp.java: -------------------------------------------------------------------------------- 1 | package org.jboss.resteasy.springboot.sample; 2 | 3 | import org.springframework.boot.autoconfigure.EnableAutoConfiguration; 4 | import org.springframework.stereotype.Component; 5 | 6 | /** 7 | * Sample app for test purposes 8 | * 9 | * Created by facarvalho on 5/17/17. 10 | */ 11 | @EnableAutoConfiguration 12 | @Component 13 | public class SampleApp { 14 | } 15 | -------------------------------------------------------------------------------- /servlet/resteasy-servlet-spring-boot-starter/src/test/java/org/jboss/resteasy/springboot/sample/TestApplication1.java: -------------------------------------------------------------------------------- 1 | package org.jboss.resteasy.springboot.sample; 2 | 3 | import jakarta.ws.rs.ApplicationPath; 4 | import jakarta.ws.rs.core.Application; 5 | 6 | /** 7 | * Sample test JAX-RS application. 8 | * 9 | * Created by facarvalho on 11/25/15. 10 | */ 11 | @ApplicationPath("/myapp1") 12 | public class TestApplication1 extends Application { 13 | } 14 | -------------------------------------------------------------------------------- /servlet/resteasy-servlet-spring-boot-starter/src/test/java/org/jboss/resteasy/springboot/sample/TestApplication2.java: -------------------------------------------------------------------------------- 1 | package org.jboss.resteasy.springboot.sample; 2 | 3 | import jakarta.ws.rs.ApplicationPath; 4 | import jakarta.ws.rs.core.Application; 5 | 6 | /** 7 | * Sample test JAX-RS application. 8 | * 9 | * Created by facarvalho on 11/25/15. 10 | */ 11 | @ApplicationPath("myapp2") 12 | public class TestApplication2 extends Application { 13 | } 14 | -------------------------------------------------------------------------------- /servlet/resteasy-servlet-spring-boot-starter/src/test/java/org/jboss/resteasy/springboot/sample/TestApplication3.java: -------------------------------------------------------------------------------- 1 | package org.jboss.resteasy.springboot.sample; 2 | 3 | import jakarta.ws.rs.core.Application; 4 | 5 | /** 6 | * This application, although extending Application class, 7 | * is NOT annotated with ApplicationPath annotation, which 8 | * should prevent its registration 9 | * 10 | * Created by facarvalho on 11/25/15. 11 | */ 12 | public class TestApplication3 extends Application { 13 | } 14 | -------------------------------------------------------------------------------- /servlet/resteasy-servlet-spring-boot-starter/src/test/java/org/jboss/resteasy/springboot/sample/TestApplication4.java: -------------------------------------------------------------------------------- 1 | package org.jboss.resteasy.springboot.sample; 2 | 3 | import jakarta.ws.rs.ApplicationPath; 4 | import jakarta.ws.rs.core.Application; 5 | 6 | /** 7 | * Sample test JAX-RS application. 8 | * 9 | * Created by facarvalho on 11/25/15. 10 | */ 11 | @ApplicationPath("/") 12 | public class TestApplication4 extends Application { 13 | } 14 | -------------------------------------------------------------------------------- /servlet/resteasy-servlet-spring-boot-starter/src/test/java/org/jboss/resteasy/springboot/sample/TestApplication5.java: -------------------------------------------------------------------------------- 1 | package org.jboss.resteasy.springboot.sample; 2 | 3 | import jakarta.ws.rs.ApplicationPath; 4 | import jakarta.ws.rs.core.Application; 5 | 6 | /** 7 | * Sample test JAX-RS application. 8 | * 9 | * Created by facarvalho on 11/25/15. 10 | */ 11 | @ApplicationPath("myapp5/") 12 | public class TestApplication5 extends Application { 13 | } 14 | -------------------------------------------------------------------------------- /servlet/resteasy-servlet-spring-boot-starter/src/test/java/org/jboss/resteasy/springboot/sample/TestProvider1.java: -------------------------------------------------------------------------------- 1 | package org.jboss.resteasy.springboot.sample; 2 | 3 | import org.springframework.stereotype.Component; 4 | 5 | import jakarta.ws.rs.core.Response; 6 | import jakarta.ws.rs.ext.ExceptionMapper; 7 | import jakarta.ws.rs.ext.Provider; 8 | 9 | /** 10 | * Created by facarvalho on 6/9/16. 11 | */ 12 | @Component 13 | @Provider 14 | public class TestProvider1 implements ExceptionMapper { 15 | 16 | public Response toResponse(Exception exception) { 17 | return null; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /servlet/resteasy-servlet-spring-boot-starter/src/test/java/org/jboss/resteasy/springboot/sample/TestProviderNoBean.java: -------------------------------------------------------------------------------- 1 | package org.jboss.resteasy.springboot.sample; 2 | 3 | import jakarta.ws.rs.core.Response; 4 | import jakarta.ws.rs.ext.ExceptionMapper; 5 | import jakarta.ws.rs.ext.Provider; 6 | 7 | @Provider 8 | public class TestProviderNoBean implements ExceptionMapper { 9 | 10 | public Response toResponse(Exception exception) { 11 | return null; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /servlet/resteasy-servlet-spring-boot-starter/src/test/java/org/jboss/resteasy/springboot/sample/TestResource1.java: -------------------------------------------------------------------------------- 1 | package org.jboss.resteasy.springboot.sample; 2 | 3 | import org.springframework.stereotype.Component; 4 | 5 | import jakarta.ws.rs.GET; 6 | import jakarta.ws.rs.Path; 7 | 8 | /** 9 | * Created by facarvalho on 6/9/16. 10 | */ 11 | @Path("resource1") 12 | @Component 13 | public class TestResource1 { 14 | 15 | @GET 16 | public void get() { 17 | // Test get method 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /servlet/resteasy-servlet-spring-boot-starter/src/test/java/org/jboss/resteasy/springboot/sample/TestResource2.java: -------------------------------------------------------------------------------- 1 | package org.jboss.resteasy.springboot.sample; 2 | 3 | import org.springframework.stereotype.Component; 4 | 5 | import jakarta.ws.rs.GET; 6 | import jakarta.ws.rs.Path; 7 | 8 | /** 9 | * Created by facarvalho on 6/9/16. 10 | */ 11 | @Path("resource2") 12 | @Component 13 | public class TestResource2 { 14 | 15 | @GET 16 | public void get() { 17 | // Test get method 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /servlet/resteasy-servlet-spring-boot-starter/src/test/java/org/jboss/resteasy/springboot/sample/TestResourceNoBean.java: -------------------------------------------------------------------------------- 1 | package org.jboss.resteasy.springboot.sample; 2 | 3 | import jakarta.ws.rs.GET; 4 | import jakarta.ws.rs.Path; 5 | 6 | @Path("resourceNoBean") 7 | public class TestResourceNoBean { 8 | 9 | @GET 10 | public void get() { 11 | // Test get method 12 | } 13 | 14 | } 15 | -------------------------------------------------------------------------------- /servlet/resteasy-servlet-spring-boot-starter/src/test/resources/logback.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | %d{HH:mm:ss.SSS} [%thread] %level %logger %msg%n 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /servlet/resteasy-servlet-spring-boot-starter/src/test/resources/test-config.xml: -------------------------------------------------------------------------------- 1 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /servlet/resteasy-wildfly-spring-boot-sample-app/README.md: -------------------------------------------------------------------------------- 1 | # Sample application for Wildfly Deployment 2 | 3 | This example is for deploying to Wildfly Java EE Full & Web Distribution. 4 | 5 | ## Running the Example 6 | 7 | This example has embedded `maven-wildfly-plugin` to download and run WildFly server automatically, and it will deploy the sample project into the server. 8 | 9 | Using the following Maven command to run the example: 10 | 11 | ```bash 12 | $ mvn wildfly:run 13 | ``` 14 | 15 | The above command will download WildFly server, start it, and then deploy the sample project. 16 | 17 | ## Testing it 18 | 19 | Here is the command to access the server: 20 | 21 | ```bash 22 | $ curl localhost:8080/rest/hello 23 | Hello, world! 24 | ``` 25 | 26 | From above, we can see the `HelloResource` served the request and output response generated from `EchoBean`, which is a spring bean wired into `HelloResource`. 27 | 28 | 29 | ## Play with the Springdoc integration 30 | 31 | The example includes the `springdoc` integration, and you can access the swagger-ui by it: 32 | 33 | - http://localhost:8080/swagger-ui/index.html 34 | 35 | And you can send the request to the `hello` method provided by the service. 36 | 37 | To access the generated OpenAPI doc, you should use the following URL: 38 | 39 | - http://localhost:8080/openapi.json 40 | 41 | Or: 42 | 43 | - http://localhost:8080/openapi.yaml 44 | 45 | Here's the sample output: 46 | 47 | ```bash 48 | ➤ curl localhost:8080/openapi.json 49 | { 50 | "openapi" : "3.0.1", 51 | "info" : { 52 | "title" : "my-rest-app", 53 | "description" : "My Rest App", 54 | "version" : "1.0.0" 55 | }, 56 | "paths" : { 57 | "/rest/hello" : { 58 | "get" : { 59 | "operationId" : "get", 60 | "responses" : { 61 | "default" : { 62 | "description" : "return hello world", 63 | "content" : { 64 | "*/*" : { 65 | "schema" : { 66 | "type" : "string" 67 | } 68 | } 69 | } 70 | } 71 | } 72 | } 73 | } 74 | } 75 | } 76 | ``` -------------------------------------------------------------------------------- /servlet/resteasy-wildfly-spring-boot-sample-app/src/main/java/com/sample/app/EchoBean.java: -------------------------------------------------------------------------------- 1 | package com.sample.app; 2 | 3 | import org.springframework.stereotype.Component; 4 | 5 | @Component 6 | public class EchoBean { 7 | public String echo(String val) { 8 | return val; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /servlet/resteasy-wildfly-spring-boot-sample-app/src/main/java/com/sample/app/HelloResource.java: -------------------------------------------------------------------------------- 1 | package com.sample.app; 2 | 3 | import io.swagger.v3.oas.annotations.Operation; 4 | import jakarta.ws.rs.Produces; 5 | import jakarta.ws.rs.core.MediaType; 6 | import org.springframework.beans.factory.annotation.Autowired; 7 | import org.springframework.stereotype.Component; 8 | import io.swagger.v3.oas.annotations.responses.ApiResponse; 9 | import jakarta.ws.rs.GET; 10 | import jakarta.ws.rs.Path; 11 | 12 | @Component 13 | @Path("/hello") 14 | public class HelloResource { 15 | 16 | @Autowired 17 | EchoBean bean; 18 | 19 | @Operation 20 | @ApiResponse(description = "return hello world") 21 | @GET 22 | @Produces(MediaType.TEXT_PLAIN) 23 | public String get() { 24 | return bean.echo("Hello, world!"); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /servlet/resteasy-wildfly-spring-boot-sample-app/src/main/java/com/sample/app/RestApp.java: -------------------------------------------------------------------------------- 1 | package com.sample.app; 2 | 3 | import io.swagger.v3.oas.annotations.OpenAPIDefinition; 4 | import io.swagger.v3.oas.annotations.info.Info; 5 | import jakarta.ws.rs.ApplicationPath; 6 | import jakarta.ws.rs.core.Application; 7 | 8 | @OpenAPIDefinition(info = @Info(title = "my-rest-app", description = "My Rest App", version = "1.0.0")) 9 | @ApplicationPath("/rest") 10 | public class RestApp extends Application { 11 | } 12 | -------------------------------------------------------------------------------- /servlet/resteasy-wildfly-spring-boot-sample-app/src/main/java/com/sample/app/SpringApp.java: -------------------------------------------------------------------------------- 1 | package com.sample.app; 2 | 3 | import io.swagger.v3.oas.annotations.OpenAPIDefinition; 4 | import io.swagger.v3.oas.annotations.info.Info; 5 | import org.springframework.boot.SpringApplication; 6 | import org.springframework.boot.autoconfigure.SpringBootApplication; 7 | import org.springframework.boot.web.servlet.support.SpringBootServletInitializer; 8 | 9 | 10 | /** 11 | * SpringBoot entry point application 12 | * 13 | * To deploy to Wildfly, this class must extends SpringBootServletInitializer. 14 | */ 15 | @SpringBootApplication 16 | public class SpringApp extends SpringBootServletInitializer { 17 | 18 | public static void main(String[] args) { 19 | SpringApplication.run(SpringApp.class, args); 20 | } 21 | 22 | } 23 | -------------------------------------------------------------------------------- /servlet/resteasy-wildfly-spring-boot-sample-app/src/main/resources/application.yaml: -------------------------------------------------------------------------------- 1 | resteasy: 2 | jaxrs: 3 | app: 4 | registration: property 5 | classes: com.sample.app.RestApp 6 | springdoc: 7 | swagger-ui: 8 | url: "/openapi.json" 9 | enabled: false 10 | 11 | --------------------------------------------------------------------------------