6 | Apache License
7 | Version 2.0, January 2004
8 | http://www.apache.org/licenses/
9 |
10 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
11 |
12 | 1. Definitions.
13 |
14 | "License" shall mean the terms and conditions for use, reproduction,
15 | and distribution as defined by Sections 1 through 9 of this document.
16 |
17 | "Licensor" shall mean the copyright owner or entity authorized by
18 | the copyright owner that is granting the License.
19 |
20 | "Legal Entity" shall mean the union of the acting entity and all
21 | other entities that control, are controlled by, or are under common
22 | control with that entity. For the purposes of this definition,
23 | "control" means (i) the power, direct or indirect, to cause the
24 | direction or management of such entity, whether by contract or
25 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
26 | outstanding shares, or (iii) beneficial ownership of such entity.
27 |
28 | "You" (or "Your") shall mean an individual or Legal Entity
29 | exercising permissions granted by this License.
30 |
31 | "Source" form shall mean the preferred form for making modifications,
32 | including but not limited to software source code, documentation
33 | source, and configuration files.
34 |
35 | "Object" form shall mean any form resulting from mechanical
36 | transformation or translation of a Source form, including but
37 | not limited to compiled object code, generated documentation,
38 | and conversions to other media types.
39 |
40 | "Work" shall mean the work of authorship, whether in Source or
41 | Object form, made available under the License, as indicated by a
42 | copyright notice that is included in or attached to the work
43 | (an example is provided in the Appendix below).
44 |
45 | "Derivative Works" shall mean any work, whether in Source or Object
46 | form, that is based on (or derived from) the Work and for which the
47 | editorial revisions, annotations, elaborations, or other modifications
48 | represent, as a whole, an original work of authorship. For the purposes
49 | of this License, Derivative Works shall not include works that remain
50 | separable from, or merely link (or bind by name) to the interfaces of,
51 | the Work and Derivative Works thereof.
52 |
53 | "Contribution" shall mean any work of authorship, including
54 | the original version of the Work and any modifications or additions
55 | to that Work or Derivative Works thereof, that is intentionally
56 | submitted to Licensor for inclusion in the Work by the copyright owner
57 | or by an individual or Legal Entity authorized to submit on behalf of
58 | the copyright owner. For the purposes of this definition, "submitted"
59 | means any form of electronic, verbal, or written communication sent
60 | to the Licensor or its representatives, including but not limited to
61 | communication on electronic mailing lists, source code control systems,
62 | and issue tracking systems that are managed by, or on behalf of, the
63 | Licensor for the purpose of discussing and improving the Work, but
64 | excluding communication that is conspicuously marked or otherwise
65 | designated in writing by the copyright owner as "Not a Contribution."
66 |
67 | "Contributor" shall mean Licensor and any individual or Legal Entity
68 | on behalf of whom a Contribution has been received by Licensor and
69 | subsequently incorporated within the Work.
70 |
71 | 2. Grant of Copyright License. Subject to the terms and conditions of
72 | this License, each Contributor hereby grants to You a perpetual,
73 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
74 | copyright license to reproduce, prepare Derivative Works of,
75 | publicly display, publicly perform, sublicense, and distribute the
76 | Work and such Derivative Works in Source or Object form.
77 |
78 | 3. Grant of Patent License. Subject to the terms and conditions of
79 | this License, each Contributor hereby grants to You a perpetual,
80 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
81 | (except as stated in this section) patent license to make, have made,
82 | use, offer to sell, sell, import, and otherwise transfer the Work,
83 | where such license applies only to those patent claims licensable
84 | by such Contributor that are necessarily infringed by their
85 | Contribution(s) alone or by combination of their Contribution(s)
86 | with the Work to which such Contribution(s) was submitted. If You
87 | institute patent litigation against any entity (including a
88 | cross-claim or counterclaim in a lawsuit) alleging that the Work
89 | or a Contribution incorporated within the Work constitutes direct
90 | or contributory patent infringement, then any patent licenses
91 | granted to You under this License for that Work shall terminate
92 | as of the date such litigation is filed.
93 |
94 | 4. Redistribution. You may reproduce and distribute copies of the
95 | Work or Derivative Works thereof in any medium, with or without
96 | modifications, and in Source or Object form, provided that You
97 | meet the following conditions:
98 |
99 | (a) You must give any other recipients of the Work or
100 | Derivative Works a copy of this License; and
101 |
102 | (b) You must cause any modified files to carry prominent notices
103 | stating that You changed the files; and
104 |
105 | (c) You must retain, in the Source form of any Derivative Works
106 | that You distribute, all copyright, patent, trademark, and
107 | attribution notices from the Source form of the Work,
108 | excluding those notices that do not pertain to any part of
109 | the Derivative Works; and
110 |
111 | (d) If the Work includes a "NOTICE" text file as part of its
112 | distribution, then any Derivative Works that You distribute must
113 | include a readable copy of the attribution notices contained
114 | within such NOTICE file, excluding those notices that do not
115 | pertain to any part of the Derivative Works, in at least one
116 | of the following places: within a NOTICE text file distributed
117 | as part of the Derivative Works; within the Source form or
118 | documentation, if provided along with the Derivative Works; or,
119 | within a display generated by the Derivative Works, if and
120 | wherever such third-party notices normally appear. The contents
121 | of the NOTICE file are for informational purposes only and
122 | do not modify the License. You may add Your own attribution
123 | notices within Derivative Works that You distribute, alongside
124 | or as an addendum to the NOTICE text from the Work, provided
125 | that such additional attribution notices cannot be construed
126 | as modifying the License.
127 |
128 | You may add Your own copyright statement to Your modifications and
129 | may provide additional or different license terms and conditions
130 | for use, reproduction, or distribution of Your modifications, or
131 | for any such Derivative Works as a whole, provided Your use,
132 | reproduction, and distribution of the Work otherwise complies with
133 | the conditions stated in this License.
134 |
135 | 5. Submission of Contributions. Unless You explicitly state otherwise,
136 | any Contribution intentionally submitted for inclusion in the Work
137 | by You to the Licensor shall be under the terms and conditions of
138 | this License, without any additional terms or conditions.
139 | Notwithstanding the above, nothing herein shall supersede or modify
140 | the terms of any separate license agreement you may have executed
141 | with Licensor regarding such Contributions.
142 |
143 | 6. Trademarks. This License does not grant permission to use the trade
144 | names, trademarks, service marks, or product names of the Licensor,
145 | except as required for reasonable and customary use in describing the
146 | origin of the Work and reproducing the content of the NOTICE file.
147 |
148 | 7. Disclaimer of Warranty. Unless required by applicable law or
149 | agreed to in writing, Licensor provides the Work (and each
150 | Contributor provides its Contributions) on an "AS IS" BASIS,
151 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
152 | implied, including, without limitation, any warranties or conditions
153 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
154 | PARTICULAR PURPOSE. You are solely responsible for determining the
155 | appropriateness of using or redistributing the Work and assume any
156 | risks associated with Your exercise of permissions under this License.
157 |
158 | 8. Limitation of Liability. In no event and under no legal theory,
159 | whether in tort (including negligence), contract, or otherwise,
160 | unless required by applicable law (such as deliberate and grossly
161 | negligent acts) or agreed to in writing, shall any Contributor be
162 | liable to You for damages, including any direct, indirect, special,
163 | incidental, or consequential damages of any character arising as a
164 | result of this License or out of the use or inability to use the
165 | Work (including but not limited to damages for loss of goodwill,
166 | work stoppage, computer failure or malfunction, or any and all
167 | other commercial damages or losses), even if such Contributor
168 | has been advised of the possibility of such damages.
169 |
170 | 9. Accepting Warranty or Additional Liability. While redistributing
171 | the Work or Derivative Works thereof, You may choose to offer,
172 | and charge a fee for, acceptance of support, warranty, indemnity,
173 | or other liability obligations and/or rights consistent with this
174 | License. However, in accepting such obligations, You may act only
175 | on Your own behalf and on Your sole responsibility, not on behalf
176 | of any other Contributor, and only if You agree to indemnify,
177 | defend, and hold each Contributor harmless for any liability
178 | incurred by, or claims asserted against, such Contributor by reason
179 | of your accepting any such warranty or additional liability.
180 |
181 | END OF TERMS AND CONDITIONS
182 |
183 | APPENDIX: How to apply the Apache License to your work.
184 |
185 | To apply the Apache License to your work, attach the following
186 | boilerplate notice, with the fields enclosed by brackets "[]"
187 | replaced with your own identifying information. (Don't include
188 | the brackets!) The text should be enclosed in the appropriate
189 | comment syntax for the file format. We also recommend that a
190 | file or class name and description of purpose be included on the
191 | same "printed page" as the copyright notice for easier
192 | identification within third-party archives.
193 |
194 | Copyright [yyyy] [name of copyright owner]
195 |
196 | Licensed under the Apache License, Version 2.0 (the "License");
197 | you may not use this file except in compliance with the License.
198 | You may obtain a copy of the License at
199 |
200 | http://www.apache.org/licenses/LICENSE-2.0
201 |
202 | Unless required by applicable law or agreed to in writing, software
203 | distributed under the License is distributed on an "AS IS" BASIS,
204 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
205 | See the License for the specific language governing permissions and
206 | limitations under the License.
207 |
208 |
209 |
210 |
211 |
212 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | [](https://travis-ci.org/paypal/resteasy-spring-boot)
2 | [](https://www.codacy.com/app/fabiocarvalho777/resteasy-spring-boot)
3 | [](https://www.codacy.com/app/fabiocarvalho777/resteasy-spring-boot)
4 | [](http://search.maven.org/#search|ga|1|g:com.paypal.springboot)
5 | [](http://www.apache.org/licenses/LICENSE-2.0.txt)
6 |
7 | # RESTEasy Spring Boot Starter
8 |
9 | ---
10 |
11 | **Important: this project has been transfered from PayPal team to RESTEasy team.**
12 |
13 | **All future versions are going to be managed and released from https://github.com/resteasy/resteasy-spring-boot.**
14 |
15 | **This Git repo will be kept here for historical purposes only.**
16 |
17 | ---
18 |
19 | There was no RESTEasy Spring Boot starter out there, so PayPal team decided to create one and share it with the community.
20 |
21 | This Spring Boot starter is fully functional, has ZERO PayPal specific code on it, and can be used normally by any regular Spring Boot application that wants to have REST endpoints and prefers RESTEasy as the JAX-RS implementation.
22 |
23 | Also, this RESTEasy Spring Boot starter integrates 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.
24 |
25 | ## Features
26 | * Enables RESTEasy for Spring Boot applications
27 | * Supports JAX-RS providers, resources and sub-resources as Spring beans
28 | * 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
29 | * 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
30 | * Leverages and supports RESTEasy configuration
31 | * Supports RESTEasy Asynchronous Job Service
32 |
33 | ## Quick start
34 |
35 | ### Adding POM dependency
36 | Add the Maven dependency below to your Spring Boot application pom file.
37 |
38 | ``` xml
39 |
40 | com.paypal.springboot
41 | resteasy-spring-boot-starter
42 | 2.3.4-RELEASE
43 | runtime
44 |
45 | ```
46 |
47 | ### Registering JAX-RS application classes
48 | 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.
49 | See section _JAX-RS application registration methods_ in [How to use RESTEasy Spring Boot Starter](mds/USAGE.md) for further information.
50 |
51 | ``` java
52 | package com.sample.app;
53 |
54 | import org.springframework.stereotype.Component;
55 | import javax.ws.rs.ApplicationPath;
56 | import javax.ws.rs.core.Application;
57 |
58 | @Component
59 | @ApplicationPath("/sample-app/")
60 | public class JaxrsApplication extends Application {
61 | }
62 | ```
63 |
64 | ### Registering JAX-RS resources and providers
65 | Just define them as Spring beans, and they will be automatically registered.
66 | Notice that JAX-RS resources can be singleton or request scoped, while JAX-RS providers must be singletons.
67 |
68 | ### Further information
69 | See [How to use RESTEasy Spring Boot Starter](mds/USAGE.md).
70 |
71 | ## Release notes
72 | See [RESTEasy Spring Boot starter release notes](mds/RELEASE_NOTES.md).
73 |
74 | ## Projects
75 |
76 | - **sample-app**: A simple Spring Boot application that exposes JAX-RS endpoints as Spring beans using RESTEasy via this RESTEasy Spring Boot starter.
77 | - **resteasy-spring-boot-starter**: The RESTEasy Spring Boot Starter project.
78 | - **resteasy-spring-boot-starter-test**: Integration tests for the RESTEasy Spring Boot Starter project.
79 |
80 | ## Reporting an issue
81 | Please open an issue using our [GitHub issues](https://github.com/paypal/resteasy-spring-boot/issues) page.
82 |
83 | ## Contributing
84 | You are very welcome to contribute to RESTEasy Spring Boot starter! Read our [Contribution guidelines](mds/CONTRIBUTING.md).
85 |
86 | ## Contacting us
87 | To contact us, please send an email to fabiocarvalho777@gmail.com.
88 |
89 | ## License
90 | This project is licensed under the [Apache 2 License](License.html).
91 |
--------------------------------------------------------------------------------
/mds/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contribution guidelines
2 |
3 | ## Basic
4 | - If there is not one open already, open an issue in [GitHub issues](https://github.com/paypal/resteasy-spring-boot/issues)
5 | - Fork this repo
6 | - Checkout `develop` branch
7 | - Apply your changes
8 | - Make sure all projects build and all unit tests pass
9 | - Make sure code coverage doesn't drop (add extra unit tests if necessary)
10 | - If fixing a bug, make sure you add an unit or integration test to expose the issue
11 | - If adding a new feature, make sure you add an unit or integration test to test the feature
12 | - If adding a new feature, add end user documentation as well
13 | - Add comments to the code explaining your changes if necessary
14 | - Create a pull request to the correct development branch (mention the issue id in the PR comments)
15 |
16 | ## Code style
17 | Make sure to follow the code style of the existing code. That means for example four spaces for indentation.
18 |
19 | ## Commit messages
20 | When committing, make sure the commit message is describing what is changed and why. See the example below.
21 |
22 | [issue id] a short description
23 | A more detailed description
24 |
25 | ## Communication
26 | Please send an email to facarvalho@paypal.com, or fabiocarvalho777@gmail.com.
27 |
28 | ## More information
29 | Read more about best practices in [this github guide](https://guides.github.com/activities/contributing-to-open-source/).
30 |
--------------------------------------------------------------------------------
/mds/RELEASE_NOTES.md:
--------------------------------------------------------------------------------
1 | # Release notes
2 |
3 | ## 2.3.4-RELEASE
4 |
5 | #### Release date
6 | November 6th, 2017.
7 |
8 | #### Third-party versions
9 | - RESTEasy: 3.1.4.Final
10 | - Spring Boot: 1.5.8.RELEASE
11 |
12 | #### New features and enhancements
13 | 1. [83 - Upgrade to Spring Boot version 1.5.8](https://github.com/paypal/resteasy-spring-boot/issues/83)
14 |
15 | #### Bug fixes
16 | None
17 |
18 | ## 2.3.3-RELEASE
19 |
20 | #### Release date
21 | August 1st, 2017.
22 |
23 | #### Third-party versions
24 | - RESTEasy: 3.1.4.Final
25 | - Spring Boot: 1.5.6.RELEASE
26 |
27 | #### New features and enhancements
28 | 1. [76 - Upgrade to Spring Boot version 1.5.6](https://github.com/paypal/resteasy-spring-boot/issues/76)
29 | 1. [77 - Upgrade to RESTEasy version 3.1.4.Final](https://github.com/paypal/resteasy-spring-boot/issues/77)
30 |
31 | #### Bug fixes
32 | None
33 |
34 | ## 2.3.2-RELEASE
35 |
36 | #### Release date
37 | June 5th, 2017.
38 |
39 | #### Third-party versions
40 | - RESTEasy: 3.1.3.Final
41 | - Spring Boot: 1.5.3.RELEASE
42 |
43 | #### New features and enhancements
44 | 1. [61 - Adding support for RESTEasy Asynchronous Job Service](https://github.com/paypal/resteasy-spring-boot/issues/61)
45 | 1. [65 - Use Spring Framework scanning facility in JaxrsApplicationScanner](https://github.com/paypal/resteasy-spring-boot/issues/65)
46 | 1. [72 - Upgrade RESTEasy to version 3.1.3.Final](https://github.com/paypal/resteasy-spring-boot/issues/72)
47 |
48 | #### Bug fixes
49 | None
50 |
51 | #### Important notes
52 | 1. 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.
53 |
54 | ## 2.3.1-RELEASE
55 |
56 | #### Release date
57 | May 12th, 2017.
58 |
59 | #### Third-party versions
60 | - RESTEasy: 3.1.0.Final
61 | - Spring Boot: 1.5.3.RELEASE
62 |
63 | #### New features and enhancements
64 | 1. [66 - Upgrade to Spring Boot 1.5.3.RELEASE](https://github.com/paypal/resteasy-spring-boot/issues/66)
65 | 1. [64 - Renaming `ResteasySpringBootConfig` to `RestEasyAutoConfiguration`](https://github.com/paypal/resteasy-spring-boot/issues/64)
66 | 1. [54 - Add a test case to integration tests to reproduce issue with multiple applications](https://github.com/paypal/resteasy-spring-boot/issues/54)
67 | 1. [57 - Add a test case to check for warning or error messages](https://github.com/paypal/resteasy-spring-boot/issues/57)
68 |
69 | #### Bug fixes
70 | 1. [67 - Remove all warnings during build](https://github.com/paypal/resteasy-spring-boot/issues/67)
71 |
72 | ## 2.3.0-RELEASE
73 |
74 | #### Release date
75 | February 22nd, 2017.
76 |
77 | #### Third-party versions
78 | - RESTEasy: 3.1.0.Final
79 | - Spring Boot: 1.5.1.RELEASE
80 |
81 | #### New features and enhancements
82 | 1. [49 - Upgrade to RESTEasy version 3.1.0](https://github.com/paypal/resteasy-spring-boot/issues/49)
83 | 1. [50 - Upgrade to Spring Boot version 1.5.1](https://github.com/paypal/resteasy-spring-boot/issues/50)
84 |
85 | #### Bug fixes
86 | 1. [56 - Warning messages during startup](https://github.com/paypal/resteasy-spring-boot/issues/56)
87 |
88 | ## 2.2.2-RELEASE
89 |
90 | #### Release date
91 | February 1st, 2017.
92 |
93 | #### Third-party versions
94 | - RESTEasy: 3.0.19.Final
95 | - Spring Boot: 1.4.2.RELEASE
96 |
97 | #### New features and enhancements
98 | 1. [13 - Add automated integration tests](https://github.com/paypal/resteasy-spring-boot/issues/13)
99 |
100 | #### Bug fixes
101 | 1. [51 - Do not share ResteasyProviderFactory and Registry among applications](https://github.com/paypal/resteasy-spring-boot/issues/51)
102 | 1. [44 - Fix CI travis jpm usage](https://github.com/paypal/resteasy-spring-boot/issues/44)
103 |
104 | ## 2.2.1-RELEASE
105 |
106 | #### Release date
107 | November 20th, 2016.
108 |
109 | #### Third-party versions
110 | - RESTEasy: 3.0.19.Final
111 | - Spring Boot: 1.4.2.RELEASE
112 |
113 | #### New features and enhancements
114 | 1. [43 - Upgrade Spring Boot to version 1.4.2](https://github.com/paypal/resteasy-spring-boot/issues/43)
115 | 1. [40 - Spring 4.1 is not supported although it easily can be](https://github.com/paypal/resteasy-spring-boot/issues/40)
116 |
117 | #### Bug fixes
118 | 1. [42 - Remove deprecated code](https://github.com/paypal/resteasy-spring-boot/issues/42)
119 |
120 | ## 2.2.0-RELEASE
121 |
122 | #### Release date
123 | August 31st, 2016.
124 |
125 | #### Third-party versions
126 | - RESTEasy: 3.0.19.Final
127 | - Spring Boot: 1.4.0.RELEASE
128 |
129 | #### New features and enhancements
130 | 1. [33 - Upgrade to the latest Spring Boot and the RESTEasy versions](https://github.com/paypal/resteasy-spring-boot/issues/33)
131 |
132 | #### Bug fixes
133 | 1. [35 - Not possible to use application property in application.yml](https://github.com/paypal/resteasy-spring-boot/issues/35)
134 | 1. [34 - Mockito should be set to test scope](https://github.com/paypal/resteasy-spring-boot/issues/34)
135 | 1. [32 - Documentation has a contradiction about registering Application subclasses](https://github.com/paypal/resteasy-spring-boot/issues/32)
136 |
137 | ## 2.1.1-RELEASE
138 |
139 | #### Release date
140 | July 22nd, 2016.
141 |
142 | #### Third-party versions
143 | - RESTEasy: 3.0.18.Final
144 | - Spring Boot: 1.3.6.RELEASE
145 |
146 | #### New features and enhancements
147 | None
148 |
149 | #### Bug fixes
150 |
151 | 1. [23 - JAX-RS application fails to register after upgrade to 2.1.0](https://github.com/paypal/resteasy-spring-boot/issues/30)
152 |
153 | ## 2.1.0-RELEASE
154 |
155 | #### Release date
156 | July 21st, 2016.
157 |
158 | #### Third-party versions
159 | - RESTEasy: 3.0.18.Final
160 | - Spring Boot: 1.3.6.RELEASE
161 |
162 | #### New features and enhancements
163 |
164 | 1. [22 - Add configuration to turn OFF scanning for JAX-RS Application sub-classes](https://github.com/paypal/resteasy-spring-boot/issues/22)
165 | 1. [10 - Support RESTEasy configuration](https://github.com/paypal/resteasy-spring-boot/issues/10)
166 | 1. [20 - Add documentation](https://github.com/paypal/resteasy-spring-boot/issues/20)
167 | 1. [25 - Add Cobertura code coverage](https://github.com/paypal/resteasy-spring-boot/issues/25)
168 | 1. [24 - Automate Travis CI](https://github.com/paypal/resteasy-spring-boot/issues/24)
169 |
170 | #### Bug fixes
171 |
172 | 1. [23 - Make sure section 2.3.2 in JAX-RS 2.0 spec is followed strictly in regard to the absence of JAX-RS Application classes](https://github.com/paypal/resteasy-spring-boot/issues/23)
173 |
174 | ## 2.0.1-RELEASE
175 |
176 | #### Release date
177 | June 9th, 2016.
178 |
179 | #### Third-party versions
180 | - RESTEasy: 3.0.17.Final
181 | - Spring Boot: 1.3.5.RELEASE
182 |
183 | #### New features and enhancements
184 |
185 | 1. [18 - Component annotation removal from ResteasyEmbeddedServletInitializer](https://github.com/paypal/resteasy-spring-boot/issues/18)
186 | 1. [17 - Upgrade Sprig Boot to version 1.3.5](https://github.com/paypal/resteasy-spring-boot/issues/17)
187 | 1. [16 - Upgrade RESTEasy to version 3.0.17.Final](https://github.com/paypal/resteasy-spring-boot/issues/16)
188 | 1. [7 - Remove custom RESTEasy classes as soon as RESTEasy 3.0.17.Final is released](https://github.com/paypal/resteasy-spring-boot/issues/7)
189 |
190 | #### Bug fixes
191 |
192 | 1. [19 - JAX-RS resources resolution should be targeted towards Spring beans only](https://github.com/paypal/resteasy-spring-boot/issues/19)
193 |
194 | ## 2.0.0-RELEASE
195 |
196 | #### Important note
197 | The starter artifact id is being renamed to **resteasy-spring-boot-starter**, due to [issue 2](https://github.com/paypal/resteasy-spring-boot/issues/2).
198 |
199 | #### Release date
200 | April 27th, 2016.
201 |
202 | #### Third-party versions
203 | - RESTEasy: 3.0.16.Final
204 | - Spring Boot: 1.3.3.RELEASE
205 |
206 | #### New features and enhancements
207 |
208 | 1. [2 - Renaming artifact id due to Spring team request](https://github.com/paypal/resteasy-spring-boot/issues/2)
209 | 1. [4 - Replace ComponentScan by Import annotations](https://github.com/paypal/resteasy-spring-boot/issues/4)
210 |
211 | #### Bug fixes
212 |
213 | 1. [5 - Classpath scanner prints warning when a non jar file is scanned](https://github.com/paypal/resteasy-spring-boot/issues/5)
214 | 1. [6 - JAX-RS application and endpoints don't get registered when running mvn spring-boot:run](https://github.com/paypal/resteasy-spring-boot/issues/6)
215 | 1. [8 - Don't skip all applications if one has no path bug ](https://github.com/paypal/resteasy-spring-boot/issues/8)
216 |
217 | ## 1.0.0-RELEASE
218 |
219 | #### Release date
220 | February 26th, 2016.
221 |
222 | #### Third-party versions
223 | - RESTEasy: 3.0.16.Final
224 | - Spring Boot: 1.3.2.RELEASE
225 |
226 | #### New features and enhancements
227 |
228 | 1. Enable RESTEasy and Spring integration for Spring Boot applications
229 | 1. Support JAX-RS sub-resources
230 | 1. Support automatic discovery of JAX-RS Application classes
231 |
232 | #### Bug fixes
233 | None
--------------------------------------------------------------------------------
/mds/USAGE.md:
--------------------------------------------------------------------------------
1 | # How to use RESTEasy Spring Boot Starter
2 |
3 | #### Adding POM dependency
4 | Add the Maven dependency below to your Spring Boot application pom file.
5 |
6 | ``` xml
7 |
8 | com.paypal.springboot
9 | resteasy-spring-boot-starter
10 | 2.3.4-RELEASE
11 | runtime
12 |
13 | ```
14 |
15 | #### Registering JAX-RS application classes
16 | 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.
17 | See section [JAX-RS application registration methods](#jax-rs-application-registration-methods) for further information.
18 |
19 | ``` java
20 | package com.sample.app;
21 |
22 | import org.springframework.stereotype.Component;
23 | import javax.ws.rs.ApplicationPath;
24 | import javax.ws.rs.core.Application;
25 |
26 | @Component
27 | @ApplicationPath("/sample-app/")
28 | public class JaxrsApplication extends Application {
29 | }
30 | ```
31 |
32 | #### Registering JAX-RS resources and providers
33 | Just define them as Spring beans, and they will be automatically registered.
34 | Notice that JAX-RS resources can be singleton or request scoped, while JAX-RS providers must be singletons.
35 |
36 | ## Advanced topics
37 |
38 | #### JAX-RS application registration methods
39 |
40 | JAX-RS applications are defined via sub-classes of [Application](http://docs.oracle.com/javaee/7/api/javax/ws/rs/core/Application.html). One or more JAX-RS applications can be registered, and there are three different methods to do so:
41 |
42 | 1. By having them defined as Spring beans.
43 | 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.
44 | 3. Automatically by classpath scanning (looking for `javax.ws.rs.core.Application` sub-classes). **See important note number 6 about this method**.
45 |
46 | 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:
47 |
48 | 1. `beans`
49 | 1. `property`
50 | 1. `scanning`
51 | 1. `auto` (default)
52 |
53 | 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_).
54 |
55 | __Important notes__
56 |
57 | 1. 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.
58 | 1. It is recommended to always have at least one JAX-RS application class.
59 | 1. A JAX-RS application class with no `javax.ws.rs.ApplicationPath` annotation will not be registered.
60 | 1. Avoid setting the JAX-RS application base URI to simply `/` to prevent URI conflicts, as explained in item 1.
61 | 1. Property `resteasy.jaxrs.app` has been deprecated and replaced by `resteasy.jaxrs.app.classes` since version *2.2.0-RELEASE* (see [issue 35](https://github.com/paypal/resteasy-spring-boot/issues/35)). Property `resteasy.jaxrs.app` is going to be finally removed in version *3.0.0-RELEASE*.
62 | 1. 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.
63 |
64 | #### RESTEasy configuration
65 | RESTEasy offers a few configuration switches, [as seen here](http://docs.jboss.org/resteasy/docs/3.1.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.context-parameters.*` (search for it in [Spring Boot reference guide](http://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/)).
66 |
67 | As an example, to set RESTEasy property `resteasy.role.based.security` to `true`, just add the property bellow to Spring Boot `application.properties` file.
68 |
69 | ```
70 | server.context-parameters.resteasy.role.based.security=true
71 | ```
72 |
73 | It is important to mention that the following RESTEasy configuration options are NOT applicable to an application using RESTEasy Spring Boot starter.
74 | All other RESTEasy configuration options are supported normally.
75 |
76 | | Configuration option | Why it is not applicable |
77 | |---|---|
78 | |`javax.ws.rs.Application`|JAX-RS application classes are registered as explained in section _"JAX-RS application registration methods"_ above|
79 | |`resteasy.servlet.mapping.prefix`|The url-pattern for the Resteasy servlet-mapping is always based on the `ApplicationPath` annotation in the JAX-RS application class|
80 | |`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|
81 |
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 | 4.0.0
7 |
8 | com.paypal.springboot
9 | resteasy-spring-boot-starter-parent
10 | 2.3.4-RELEASE
11 | pom
12 |
13 |
14 | resteasy-spring-boot-starter
15 | sample-app
16 | resteasy-spring-boot-starter-test
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/release_steps.txt:
--------------------------------------------------------------------------------
1 | changes in develop branch
2 | rev all 4 poms to release
3 | update /README
4 | set the new RELEASE version in Quick Start
5 | update /mds/USAGE
6 | set the new RELEASE version
7 | update /mds/RELEASE_NOTES
8 | check if it has all contributions (improvements and fixes)
9 | set delivery date
10 | set 3rd party versions
11 | build everything to make sure it is Ok
12 | check in changes to develop branch (Revving up to version 2.1.0-RELEASE)
13 | merge all changes from develop branch to master (Bringing in 2.1.0 changes to master)
14 |
15 | checkout master branch
16 | git pull
17 | build everything to make sure it is Ok
18 | release from master
19 | comment out cobertura plugin and cobertura reporting plugin in resteasy-spring-boot-starter/pom.xml (BUT NOT CHECK IT IN!)
20 | deploy to MavenCentral(mvn clean install deploy -Possrh)
21 | remove comment marks for cobertura plugin and cobertura reporting plugin
22 | manual sonatype release
23 | Staging Repositories
24 | Close (the one with sources and everything)
25 | Release
26 | tag new release from master
27 | git tag -a 2.1.0-RELEASE
28 | git push origin 2.1.0-RELEASE
29 |
30 | Close GitHub Milestone that has just been released
31 |
32 | git checkout develop
33 | rev develop branch to next snapshot version
34 | rev all 4 poms in to next snapshot version
35 | build everything to make sure it is Ok
36 | update /README
37 | set the new SNAPSHOT version in Quick Start
38 | update /mds/USAGE
39 | set the new SNAPSHOT version
40 | update /mds/RELEASE NOTES
41 | create new version section by copying latest one
42 | set delivery date to TBD
43 | set third-parties to TBD
44 | set contributions (improvements and fixes) to TBD
45 | check in changes to develop branch (Preparing for version 2.1.1-SNAPSHOT)
46 |
47 | Create a new GitHub Milestone and assign issues to it (if any)
48 |
--------------------------------------------------------------------------------
/resteasy-spring-boot-starter-test/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 | 4.0.0
5 |
6 | com.paypal.springboot
7 | resteasy-spring-boot-starter-test
8 | 2.3.4-RELEASE
9 | ${project.artifactId}
10 |
11 | Integration test project for RESTEasy Spring Boot starter
12 | https://github.com/paypal/resteasy-spring-boot
13 |
14 |
15 |
16 | The Apache License, Version 2.0
17 | http://www.apache.org/licenses/LICENSE-2.0.txt
18 |
19 |
20 |
21 |
22 | Fabio Carvalho
23 | facarvalho@paypal.com
24 | PayPal
25 | http://www.paypal.com
26 |
27 |
28 |
29 | scm:git:git@github.com:paypal/resteasy-spring-boot.git
30 | scm:git:git@github.com:paypal/resteasy-spring-boot.git
31 | git@github.com:paypal/resteasy-spring-boot.git
32 |
33 |
34 |
35 | ossrh
36 | https://oss.sonatype.org/content/repositories/snapshots
37 |
38 |
39 | ossrh
40 | https://oss.sonatype.org/service/local/staging/deploy/maven2/
41 |
42 |
43 |
44 |
45 | UTF-8
46 | UTF-8
47 | 1.8
48 |
49 |
50 |
51 |
52 | javax.ws.rs
53 | javax.ws.rs-api
54 | 2.0
55 | test
56 |
57 |
58 | com.test
59 | sample-app
60 | ${project.version}
61 | test
62 |
63 |
64 | io.rest-assured
65 | rest-assured
66 | 3.0.1
67 | test
68 |
69 |
70 | org.testng
71 | testng
72 | 6.1.1
73 | test
74 |
75 |
76 | org.yaml
77 | snakeyaml
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 | fortify
87 |
88 |
89 |
90 |
91 | com.fortify.ps.maven.plugin
92 | sca-maven-plugin
93 | 4.22
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 | com.fortify.ps.maven.plugin
106 | sca-maven-plugin
107 |
108 | 1.8
109 | 1G
110 |
111 |
112 |
113 | sca-clean
114 |
115 | clean
116 |
117 |
118 |
119 | sca-translate-exec
120 |
121 | translate
122 |
123 |
124 |
125 | 4.22
126 |
127 |
128 |
129 |
130 |
131 |
132 | org.apache.maven.plugins
133 | maven-failsafe-plugin
134 | 2.20
135 |
136 |
137 | failsafe-integration-tests
138 | integration-test
139 |
140 | integration-test
141 | verify
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
--------------------------------------------------------------------------------
/resteasy-spring-boot-starter-test/src/test/java/com/paypal/springboot/resteasy/AsyncJobIT.java:
--------------------------------------------------------------------------------
1 | package com.paypal.springboot.resteasy;
2 |
3 | import com.sample.app.Application;
4 | import io.restassured.RestAssured;
5 | import io.restassured.response.Response;
6 | import org.springframework.boot.SpringApplication;
7 | import org.springframework.util.SocketUtils;
8 | import org.testng.annotations.AfterClass;
9 | import org.testng.annotations.BeforeClass;
10 | import org.testng.annotations.Test;
11 |
12 | import java.util.Properties;
13 |
14 | import static io.restassured.RestAssured.given;
15 | import static org.hamcrest.CoreMatchers.notNullValue;
16 | import static org.hamcrest.Matchers.empty;
17 | import static org.hamcrest.Matchers.equalTo;
18 | import static org.hamcrest.Matchers.isEmptyString;
19 |
20 | /**
21 | * Integration tests for RESTEasy Asynchronous Job Service
22 | *
23 | * @author facarvalho
24 | */
25 | public class AsyncJobIT {
26 |
27 | @BeforeClass
28 | public void setUp() {
29 | int appPort = SocketUtils.findAvailableTcpPort();
30 |
31 | RestAssured.basePath = "sample-app";
32 | RestAssured.port = appPort;
33 |
34 | Properties properties = new Properties();
35 | properties.put("server.context-parameters.resteasy.async.job.service.enabled", true);
36 |
37 | SpringApplication app = new SpringApplication(Application.class);
38 | app.setDefaultProperties(properties);
39 | app.addListeners(new LogbackTestApplicationListener());
40 | app.run("--server.port=" + appPort).registerShutdownHook();
41 | }
42 |
43 | @Test
44 | public void regularRequestTest() {
45 | Response response = given().body("is there anybody out there?").post("/echo");
46 | response.then().statusCode(200).body("timestamp", notNullValue()).body("echoText", equalTo("is there anybody out there?"));
47 | }
48 |
49 | @Test
50 | public void asyncRequestTest() {
51 | Response response = given().body("is there anybody out there?").post("/echo?asynch=true");
52 | response.then().statusCode(202).body(isEmptyString());
53 |
54 | String location = response.getHeader("Location");
55 | response = given().get(location + "?wait=1000");
56 | response.then().statusCode(200).body("timestamp", notNullValue()).body("echoText", equalTo("is there anybody out there?"));
57 | }
58 |
59 | @Test
60 | public void fireAndForgetRequestTest() {
61 | Response response = given().body("is there anybody out there?").post("/echo?oneway=true");
62 | response.then().statusCode(202).body(isEmptyString());
63 | }
64 |
65 | @AfterClass
66 | public void shuttingDownApplication() {
67 | Response response = given().basePath("/").post("/shutdown");
68 | response.then().statusCode(200).body("message", equalTo("Shutting down, bye..."));
69 | }
70 |
71 | }
72 |
--------------------------------------------------------------------------------
/resteasy-spring-boot-starter-test/src/test/java/com/paypal/springboot/resteasy/CommonUseCasesIT.java:
--------------------------------------------------------------------------------
1 | package com.paypal.springboot.resteasy;
2 |
3 | import com.sample.app.Application;
4 | import io.restassured.RestAssured;
5 | import io.restassured.response.Response;
6 | import org.springframework.boot.SpringApplication;
7 | import org.springframework.util.SocketUtils;
8 | import org.testng.annotations.AfterClass;
9 | import org.testng.annotations.BeforeClass;
10 | import org.testng.annotations.Test;
11 |
12 | import static io.restassured.RestAssured.given;
13 | import static org.hamcrest.CoreMatchers.equalTo;
14 | import static org.hamcrest.CoreMatchers.notNullValue;
15 |
16 | /**
17 | * This is an integration test based on a simple sample application and
18 | * very common use cases (see sample-app project)
19 | *
20 | * @author facarvalho
21 | */
22 | public class CommonUseCasesIT {
23 |
24 | @BeforeClass
25 | public void startingApplicationUp() {
26 | RestAssured.basePath = "sample-app";
27 | int port = SocketUtils.findAvailableTcpPort();
28 | RestAssured.port = port;
29 |
30 | SpringApplication springApplication = new SpringApplication(Application.class);
31 | springApplication.addListeners(new LogbackTestApplicationListener());
32 | springApplication.run("--server.port=" + port).registerShutdownHook();
33 | }
34 |
35 | @AfterClass
36 | public void shuttingDownApplication() {
37 | Response response = given().basePath("/").post("/shutdown");
38 | response.then().statusCode(200).body("message", equalTo("Shutting down, bye..."));
39 | }
40 |
41 | @Test
42 | public void happyPathTest() {
43 | Response response = given().body("is there anybody out there?").post("/echo");
44 | response.then().statusCode(200).body("timestamp", notNullValue()).body("echoText", equalTo("is there anybody out there?"));
45 | }
46 |
47 | @Test
48 | public void filterTest() {
49 | Response response = given().body("is there anybody out there?").header("ping", "ping").post("/echo");
50 | response.then().statusCode(200).body("timestamp", notNullValue()).body("echoText", equalTo("is there anybody out there?")).header("pong", equalTo("pong"));
51 | }
52 |
53 | @Test
54 | public void invalidUriPathTest() {
55 | // Notice "eco" is supposed to result in 404
56 | Response response = given().body("is there anybody out there?").post("/eco");
57 | response.then().statusCode(404).body(equalTo("The resource you've requested, has not been found!"));
58 | }
59 |
60 | @Test
61 | public void invalidBaseUrlTest() {
62 | // Notice "sampl-ap" is supposed to result in 404
63 | Response response = given().basePath("sampl-app").body("is there anybody out there?").post("/echo");
64 | response.then().statusCode(404).body("status", equalTo(404)).body("error", equalTo("Not Found"));
65 | }
66 |
67 | @Test
68 | public void invalidNoPayloadTest() {
69 | // Notice that the endpoint we are sending a request to uses Bean Validations to assure
70 | // the request message payload is valid. If that is not the case (a blank payload for example),
71 | // then the server is expected to return a 400 response message
72 | Response response = given().body("").post("/echo");
73 | response.then().statusCode(400).body(equalTo("[PARAMETER]\r[echo.arg0]\r[may not be empty]\r[]\r\r"));
74 | }
75 |
76 | @Test
77 | public void actuatorTest() throws InterruptedException {
78 | Response response = given().basePath("/").get("/health");
79 | response.then().statusCode(200).body("status", equalTo("UP"));
80 | }
81 |
82 | }
83 |
--------------------------------------------------------------------------------
/resteasy-spring-boot-starter-test/src/test/java/com/paypal/springboot/resteasy/ConfigurationIT.java:
--------------------------------------------------------------------------------
1 | package com.paypal.springboot.resteasy;
2 |
3 | import com.sample.app.Application;
4 | import io.restassured.response.Response;
5 | import org.springframework.beans.BeansException;
6 | import org.springframework.boot.SpringApplication;
7 | import org.springframework.util.SocketUtils;
8 | import org.testng.Assert;
9 | import org.testng.annotations.Test;
10 |
11 | import java.util.Properties;
12 |
13 | import static io.restassured.RestAssured.given;
14 | import static org.hamcrest.CoreMatchers.equalTo;
15 | import static org.hamcrest.CoreMatchers.notNullValue;
16 |
17 | /**
18 | * This is an integration test based on a simple sample application (see sample-app project).
19 | * This class test possible configurations to register JAX-RS application classes.
20 | *
21 | * @author facarvalho
22 | */
23 | public class ConfigurationIT {
24 |
25 | private int configureAndStartApp(Properties properties) {
26 | return configureAndStartApp(properties, true);
27 | }
28 |
29 | private int configureAndStartApp(Properties properties, boolean assertPerfectLog) {
30 | SpringApplication springApplication = new SpringApplication(Application.class);
31 | if (assertPerfectLog) {
32 | springApplication.addListeners(new LogbackTestApplicationListener());
33 | }
34 | if (properties != null) {
35 | springApplication.setDefaultProperties(properties);
36 | }
37 |
38 | int port = SocketUtils.findAvailableTcpPort();
39 | springApplication.run("--server.port=" + port).registerShutdownHook();
40 |
41 | return port;
42 | }
43 |
44 | private void appShutdown(int port) {
45 | Response response = given().basePath("/").port(port).post("/shutdown");
46 | response.then().statusCode(200).body("message", equalTo("Shutting down, bye..."));
47 | }
48 |
49 | private void assertResourceFound(int port, String basePath) {
50 | Response response = given().basePath(basePath).port(port).body("is there anybody out there?").post("/echo");
51 | response.then().statusCode(200).body("timestamp", notNullValue()).body("echoText", equalTo("is there anybody out there?"));
52 | }
53 |
54 | private void assertResourceNotFound(int port, String basePath) {
55 | Response response = given().basePath(basePath).port(port).body("is there anybody out there?").post("/echo");
56 | response.then().statusCode(404).body("status", equalTo(404)).body("error", equalTo("Not Found"));
57 | }
58 |
59 | @Test
60 | public void implicitAutoTest() {
61 | int port = configureAndStartApp(null);
62 |
63 | assertResourceFound(port, "sample-app");
64 | assertResourceNotFound(port, "sample-app-test");
65 | assertResourceNotFound(port, "/");
66 |
67 | appShutdown(port);
68 | }
69 |
70 | @Test
71 | public void explicitAutoTest() {
72 | Properties properties = new Properties();
73 | properties.put("resteasy.jaxrs.app.registration", "auto");
74 |
75 | int port = configureAndStartApp(properties);
76 |
77 | assertResourceFound(port, "sample-app");
78 | assertResourceNotFound(port, "sample-app-test");
79 | assertResourceNotFound(port, "/");
80 |
81 | appShutdown(port);
82 | }
83 |
84 | @Test
85 | public void beansTest() {
86 | Properties properties = new Properties();
87 | properties.put("resteasy.jaxrs.app.registration", "beans");
88 |
89 | int port = configureAndStartApp(properties);
90 |
91 | assertResourceFound(port, "sample-app");
92 | assertResourceNotFound(port, "sample-app-test");
93 | assertResourceNotFound(port, "/");
94 |
95 | appShutdown(port);
96 | }
97 |
98 | @Test
99 | public void propertySpringBeanClassTest() {
100 | Properties properties = new Properties();
101 | properties.put("resteasy.jaxrs.app.registration", "property");
102 | properties.put("resteasy.jaxrs.app.classes", "com.sample.app.JaxrsApplication");
103 |
104 | int port = configureAndStartApp(properties);
105 |
106 | assertResourceFound(port, "sample-app");
107 | assertResourceNotFound(port, "sample-app-test");
108 | assertResourceNotFound(port, "/");
109 |
110 | appShutdown(port);
111 | }
112 |
113 | @Test
114 | public void propertyNonSpringBeanClassTest() {
115 | Properties properties = new Properties();
116 | properties.put("resteasy.jaxrs.app.registration", "property");
117 | properties.put("resteasy.jaxrs.app.classes", "com.test.NonSpringBeanJaxrsApplication");
118 |
119 | int port = configureAndStartApp(properties);
120 |
121 | assertResourceNotFound(port, "sample-app");
122 | assertResourceFound(port, "sample-app-test");
123 | assertResourceNotFound(port, "/");
124 |
125 | appShutdown(port);
126 | }
127 |
128 | @Test
129 | public void invalidClassTest() {
130 | Properties properties = new Properties();
131 | properties.put("resteasy.jaxrs.app.registration", "property");
132 | properties.put("resteasy.jaxrs.app.classes", "com.foor.bar.NonExistentApplicationClass");
133 |
134 | try {
135 | configureAndStartApp(properties, false);
136 |
137 | Assert.fail("Expected exception, due to class not found, has not been thrown");
138 | } catch (BeansException ex) {
139 | Assert.assertEquals(ex.getCause().getClass(), ClassNotFoundException.class);
140 | Assert.assertEquals(ex.getCause().getMessage(), "com.foor.bar.NonExistentApplicationClass");
141 | }
142 | }
143 |
144 | @Test
145 | public void scanningTest() {
146 | Properties properties = new Properties();
147 | properties.put("resteasy.jaxrs.app.registration", "scanning");
148 |
149 | int port = configureAndStartApp(properties);
150 |
151 | assertResourceFound(port, "sample-app");
152 | assertResourceFound(port, "sample-app-test");
153 | assertResourceFound(port, "sample-app-test-two");
154 | assertResourceNotFound(port, "/");
155 |
156 | appShutdown(port);
157 | }
158 |
159 | }
160 |
--------------------------------------------------------------------------------
/resteasy-spring-boot-starter-test/src/test/java/com/paypal/springboot/resteasy/LogbackTestApplicationListener.java:
--------------------------------------------------------------------------------
1 | package com.paypal.springboot.resteasy;
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 | * @author facarvalho
23 | */
24 | public class LogbackTestApplicationListener implements SmartApplicationListener {
25 |
26 | private boolean warningOrErrorFound = false;
27 |
28 | private Appender appender = new AppenderBase() {
29 |
30 | // TODO
31 | // Remove this after implementing https://github.com/paypal/resteasy-spring-boot/issues/69
32 | private static final java.lang.String SCANNING_WARNING = "\n-------------\nStarting on version 3.0.0, the behavior of the `scanning`";
33 |
34 | @Override
35 | protected void append(ILoggingEvent event) {
36 | if (event == null || warningOrErrorFound) {
37 | return;
38 | }
39 | Level level = event.getLevel();
40 | if ((level.equals(Level.WARN) || level.equals(Level.ERROR)) && !event.getMessage().startsWith(SCANNING_WARNING)) {
41 | warningOrErrorFound = true;
42 | Assert.fail(event.getFormattedMessage());
43 | }
44 | }
45 | };
46 |
47 | private void addTestAppender() {
48 | LoggerContext loggerContext = (LoggerContext) LoggerFactory.getILoggerFactory();
49 | appender.setContext(loggerContext);
50 | appender.start();
51 | Logger rootLogger = loggerContext.getLogger(Logger.ROOT_LOGGER_NAME);
52 | rootLogger.addAppender(appender);
53 | }
54 |
55 | private void detachTestAppender() {
56 | if (appender != null) {
57 | appender.stop();
58 | LoggerContext loggerContext = (LoggerContext) LoggerFactory.getILoggerFactory();
59 | Logger rootLogger = loggerContext.getLogger(Logger.ROOT_LOGGER_NAME);
60 | rootLogger.detachAppender(appender);
61 | }
62 | }
63 |
64 | public boolean supportsEventType(Class extends ApplicationEvent> eventType) {
65 | return ApplicationEnvironmentPreparedEvent.class.isAssignableFrom(eventType);
66 | }
67 |
68 | public boolean supportsSourceType(Class> sourceType) {
69 | return true;
70 | }
71 |
72 | public void onApplicationEvent(ApplicationEvent event) {
73 | if(event instanceof ApplicationEnvironmentPreparedEvent) {
74 | addTestAppender();
75 | } else if(event instanceof ContextClosedEvent && ((ContextClosedEvent)event).getApplicationContext().getParent() == null) {
76 | detachTestAppender();
77 | }
78 | }
79 |
80 | public int getOrder() {
81 | return Ordered.HIGHEST_PRECEDENCE - 12;
82 | }
83 |
84 | }
85 |
--------------------------------------------------------------------------------
/resteasy-spring-boot-starter-test/src/test/java/com/paypal/springboot/resteasy/MultipleContextsIT.java:
--------------------------------------------------------------------------------
1 | package com.paypal.springboot.resteasy;
2 |
3 | import com.sample.app.Application;
4 | import com.test.multicontexttest.MultiContextTestApp;
5 | import io.restassured.RestAssured;
6 | import io.restassured.response.Response;
7 | import org.springframework.boot.SpringApplication;
8 | import org.springframework.util.SocketUtils;
9 | import org.testng.annotations.AfterClass;
10 | import org.testng.annotations.BeforeClass;
11 | import org.testng.annotations.Test;
12 |
13 | import java.util.Properties;
14 |
15 | import static io.restassured.RestAssured.given;
16 | import static org.hamcrest.CoreMatchers.notNullValue;
17 | import static org.hamcrest.Matchers.equalTo;
18 |
19 | /**
20 | * This test assures that the RESTEasy and Spring integration, promoted by the starter,
21 | * does not cause any conflicts when there is more than one application context
22 | * registered, as reported in the past by this bug (already fixed):
23 | * https://github.com/paypal/resteasy-spring-boot/issues/51
24 | *
25 | * @author facarvalho
26 | */
27 | public class MultipleContextsIT {
28 |
29 | private int app1Port, app2Port;
30 |
31 | @BeforeClass
32 | public void setUp() {
33 | app1Port = SocketUtils.findAvailableTcpPort();
34 | app2Port = SocketUtils.findAvailableTcpPort();
35 |
36 | RestAssured.basePath = "sample-app";
37 | }
38 |
39 | @Test
40 | public void test() {
41 | Properties properties = new Properties();
42 | properties.put("spring.jmx.enabled", false);
43 |
44 | SpringApplication app1 = new SpringApplication(Application.class);
45 | app1.setDefaultProperties(properties);
46 | app1.addListeners(new LogbackTestApplicationListener());
47 | app1.run("--server.port=" + app1Port).registerShutdownHook();
48 |
49 | SpringApplication app2 = new SpringApplication(MultiContextTestApp.class);
50 | app2.setDefaultProperties(properties);
51 | app2.addListeners(new LogbackTestApplicationListener());
52 | app2.run("--server.port=" + app2Port).registerShutdownHook();
53 |
54 | Response response;
55 |
56 | response = given().port(app1Port).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 | response = given().port(app2Port).body("is there anybody out there?").post("/echo");
60 | response.then().statusCode(200).body("timestamp", notNullValue()).body("echoText", equalTo("I don't want to echo anything today"));
61 | }
62 |
63 | @AfterClass
64 | public void shuttingDownApplication() {
65 | Response response;
66 |
67 | response = given().port(app1Port).basePath("/").post("/shutdown");
68 | response.then().statusCode(200).body("message", equalTo("Shutting down, bye..."));
69 |
70 | response = given().port(app2Port).basePath("/").post("/shutdown");
71 | response.then().statusCode(200).body("message", equalTo("Shutting down, bye..."));
72 | }
73 |
74 | }
75 |
--------------------------------------------------------------------------------
/resteasy-spring-boot-starter-test/src/test/java/com/sample/app/test/NonSpringBeanJaxrsApplication.java:
--------------------------------------------------------------------------------
1 | package com.sample.app.test;
2 |
3 | import javax.ws.rs.ApplicationPath;
4 | import javax.ws.rs.core.Application;
5 |
6 | @ApplicationPath("sample-app-test-two")
7 | public class NonSpringBeanJaxrsApplication extends Application {
8 | }
--------------------------------------------------------------------------------
/resteasy-spring-boot-starter-test/src/test/java/com/test/NonSpringBeanJaxrsApplication.java:
--------------------------------------------------------------------------------
1 | package com.test;
2 |
3 | import javax.ws.rs.ApplicationPath;
4 | import javax.ws.rs.core.Application;
5 |
6 | @ApplicationPath("sample-app-test")
7 | public class NonSpringBeanJaxrsApplication extends Application {
8 | }
--------------------------------------------------------------------------------
/resteasy-spring-boot-starter-test/src/test/java/com/test/multicontexttest/Echo.java:
--------------------------------------------------------------------------------
1 | package com.test.multicontexttest;
2 |
3 | import org.springframework.beans.factory.annotation.Autowired;
4 | import org.springframework.stereotype.Component;
5 |
6 | import javax.ws.rs.Consumes;
7 | import javax.ws.rs.POST;
8 | import javax.ws.rs.Path;
9 | import javax.ws.rs.Produces;
10 | import javax.ws.rs.core.MediaType;
11 |
12 | /**
13 | * Echo REST endpoint class
14 | *
15 | * Created by facarvalho on 12/7/15.
16 | */
17 | @Path("/echo")
18 | @Component
19 | public class Echo {
20 |
21 | @Autowired
22 | private EchoMessageCreator echoer;
23 |
24 | /**
25 | * Receives a simple POST request message containing as payload
26 | * a text, in text plain format, to be echoed by the service.
27 | * It returns as response, in JSON, the text to be echoed plus a timestamp of the
28 | * moment the echo response was created on the server side
29 | *
30 | * @param echoText
31 | * @return
32 | */
33 | @POST
34 | @Consumes({ MediaType.TEXT_PLAIN })
35 | @Produces({ MediaType.APPLICATION_JSON })
36 | public EchoMessage echo(String echoText) {
37 | return echoer.createEchoMessage(echoText);
38 | }
39 |
40 | }
41 |
--------------------------------------------------------------------------------
/resteasy-spring-boot-starter-test/src/test/java/com/test/multicontexttest/EchoMessage.java:
--------------------------------------------------------------------------------
1 | package com.test.multicontexttest;
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 facarvalho
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 | }
--------------------------------------------------------------------------------
/resteasy-spring-boot-starter-test/src/test/java/com/test/multicontexttest/EchoMessageCreator.java:
--------------------------------------------------------------------------------
1 | package com.test.multicontexttest;
2 |
3 | import org.springframework.stereotype.Component;
4 |
5 | /**
6 | * This bean creates {@link EchoMessage} objects but,
7 | * different than the original application it was copied from,
8 | * ignoring echo texts received as input
9 | *
10 | * @author facarvalho
11 | */
12 | @Component
13 | public class EchoMessageCreator {
14 |
15 | public EchoMessage createEchoMessage(String echoText) {
16 | return new EchoMessage("I don't want to echo anything today");
17 | }
18 |
19 | }
20 |
--------------------------------------------------------------------------------
/resteasy-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 javax.ws.rs.ApplicationPath;
6 | import javax.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 |
--------------------------------------------------------------------------------
/resteasy-spring-boot-starter-test/src/test/java/com/test/multicontexttest/MultiContextTestApp.java:
--------------------------------------------------------------------------------
1 | package com.test.multicontexttest;
2 |
3 | import org.springframework.boot.SpringApplication;
4 | import org.springframework.boot.autoconfigure.SpringBootApplication;
5 | import org.springframework.boot.web.support.SpringBootServletInitializer;
6 |
7 | /**
8 | * SpringBoot entry point application
9 | *
10 | * Created by facarvalho on 12/7/15.
11 | */
12 | @SpringBootApplication
13 | public class MultiContextTestApp extends SpringBootServletInitializer {
14 |
15 | public static void main(String[] args) {
16 | SpringApplication.run(MultiContextTestApp.class, args);
17 | }
18 |
19 | }
20 |
--------------------------------------------------------------------------------
/resteasy-spring-boot-starter-test/src/test/resources/application.yaml:
--------------------------------------------------------------------------------
1 | endpoints:
2 | shutdown:
3 | enabled: true
4 | management:
5 | security:
6 | enabled: false
--------------------------------------------------------------------------------
/resteasy-spring-boot-starter/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 | 4.0.0
5 |
6 | com.paypal.springboot
7 | resteasy-spring-boot-starter
8 | 2.3.4-RELEASE
9 | ${project.artifactId}
10 |
11 | A Spring Boot starter for RESTEasy
12 | https://github.com/paypal/resteasy-spring-boot
13 |
14 |
15 |
16 | The Apache License, Version 2.0
17 | http://www.apache.org/licenses/LICENSE-2.0.txt
18 |
19 |
20 |
21 |
22 | Fabio Carvalho
23 | facarvalho@paypal.com
24 | PayPal
25 | http://www.paypal.com
26 |
27 |
28 |
29 | scm:git:git@github.com:paypal/resteasy-spring-boot.git
30 | scm:git:git@github.com:paypal/resteasy-spring-boot.git
31 | git@github.com:paypal/resteasy-spring-boot.git
32 |
33 |
34 |
35 | ossrh
36 | https://oss.sonatype.org/content/repositories/snapshots
37 |
38 |
39 | ossrh
40 | https://oss.sonatype.org/service/local/staging/deploy/maven2/
41 |
42 |
43 |
44 |
45 | UTF-8
46 | UTF-8
47 | 1.5.8.RELEASE
48 | 3.1.4.Final
49 | 80
50 | 80
51 |
52 |
53 |
54 |
55 | org.springframework.boot
56 | spring-boot-starter
57 | ${springboot.version}
58 |
59 |
60 | org.springframework.boot
61 | spring-boot-starter-web
62 | ${springboot.version}
63 |
64 |
65 | org.jboss.resteasy
66 | resteasy-jaxrs
67 | ${resteasy.version}
68 |
69 |
70 | org.jboss.resteasy
71 | resteasy-servlet-initializer
72 | ${resteasy.version}
73 |
74 |
75 | org.jboss.resteasy
76 | resteasy-spring
77 | ${resteasy.version}
78 |
79 |
80 | org.jboss.resteasy
81 | resteasy-jettison-provider
82 |
83 |
84 |
85 |
86 | org.jboss.resteasy
87 | resteasy-jackson2-provider
88 | ${resteasy.version}
89 |
90 |
91 | javax.servlet
92 | javax.servlet-api
93 | 3.1.0
94 |
95 |
96 |
97 |
98 |
99 | org.springframework
100 | spring-test
101 | 4.1.0.RELEASE
102 | test
103 |
104 |
105 | org.testng
106 | testng
107 | 6.1.1
108 | test
109 |
110 |
111 | org.mockito
112 | mockito-core
113 | 1.10.19
114 | test
115 |
116 |
117 | org.powermock
118 | powermock-core
119 | 1.6.6
120 | test
121 |
122 |
123 | org.powermock
124 | powermock-api-mockito
125 | 1.6.5
126 | test
127 |
128 |
129 | org.powermock
130 | powermock-module-testng-common
131 | 1.6.5
132 | test
133 |
134 |
135 | org.powermock
136 | powermock-module-testng
137 | 1.6.5
138 | test
139 |
140 |
141 | org.powermock
142 | powermock-reflect
143 | 1.6.5
144 | test
145 |
146 |
147 |
148 |
149 |
150 |
151 | fortify
152 |
153 |
154 |
155 |
156 | com.fortify.ps.maven.plugin
157 | sca-maven-plugin
158 | 4.22
159 |
160 |
161 |
162 |
163 |
164 | ossrh
165 |
166 |
167 |
168 | org.apache.maven.plugins
169 | maven-source-plugin
170 |
171 |
172 | attach-sources
173 |
174 | jar
175 |
176 |
177 |
178 |
179 |
180 | org.apache.maven.plugins
181 | maven-javadoc-plugin
182 |
183 |
184 | attach-javadocs
185 |
186 | jar
187 |
188 |
189 |
190 |
191 |
192 | org.apache.maven.plugins
193 | maven-gpg-plugin
194 | 1.6
195 |
196 |
197 | sign-artifacts
198 | verify
199 |
200 | sign
201 |
202 |
203 | ${gpg.keyname}
204 | ${gpg.passphrase}
205 |
206 |
207 |
208 |
209 |
210 |
211 |
212 |
213 |
214 |
215 |
216 |
217 |
218 |
219 | com.fortify.ps.maven.plugin
220 | sca-maven-plugin
221 |
222 | 1.8
223 | 1G
224 |
225 |
226 |
227 | sca-clean
228 |
229 | clean
230 |
231 |
232 |
233 | sca-translate-exec
234 |
235 | translate
236 |
237 |
238 |
239 | 4.22
240 |
241 |
242 |
243 |
244 |
245 |
246 | org.codehaus.mojo
247 | cobertura-maven-plugin
248 | 2.7
249 |
250 |
251 | ${coverage.line}
252 | ${coverage.line}
253 | ${coverage.line}
254 | ${coverage.branch}
255 | ${coverage.branch}
256 | ${coverage.branch}
257 | true
258 |
259 |
260 | com.paypal.springboot.resteasy.JaxrsApplicationScanner
261 | 80
262 | 65
263 |
264 |
265 |
266 |
267 | html
268 | xml
269 |
270 |
271 |
272 |
273 | package
274 |
275 | clean
276 | cobertura
277 | check
278 |
279 |
280 |
281 |
282 |
283 |
284 |
285 |
286 |
287 |
288 | org.codehaus.mojo
289 | cobertura-maven-plugin
290 | 2.7
291 |
292 |
293 |
294 |
295 |
296 |
--------------------------------------------------------------------------------
/resteasy-spring-boot-starter/settings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | ossrh
6 | ${env.SONATYPE_USER}
7 | ${env.SONATYPE_PASSWORD}
8 |
9 |
10 |
--------------------------------------------------------------------------------
/resteasy-spring-boot-starter/src/main/java/com/paypal/springboot/resteasy/JaxrsApplicationScanner.java:
--------------------------------------------------------------------------------
1 | package com.paypal.springboot.resteasy;
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 javax.ws.rs.core.Application;
11 | import java.util.HashSet;
12 | import java.util.List;
13 | import java.util.Set;
14 |
15 | /**
16 | * Helper class to scan the classpath under the specified packages
17 | * searching for JAX-RS Application sub-classes
18 | *
19 | * @author Fabio Carvalho (facarvalho@paypal.com or fabiocarvalho777@gmail.com)
20 | */
21 | public abstract class JaxrsApplicationScanner {
22 |
23 | private static final Logger logger = LoggerFactory.getLogger(JaxrsApplicationScanner.class);
24 |
25 | private static Set> applications;
26 |
27 | public static Set> getApplications(List packagesToBeScanned) {
28 | if(applications == null) {
29 | applications = findJaxrsApplicationClasses(packagesToBeScanned);
30 | }
31 |
32 | return applications;
33 | }
34 |
35 | /*
36 | * Scan the classpath under the specified packages looking for JAX-RS Application sub-classes
37 | */
38 | private static Set> findJaxrsApplicationClasses(List packagesToBeScanned) {
39 | logger.info("Scanning classpath to find JAX-RS Application classes");
40 |
41 | ClassPathScanningCandidateComponentProvider scanner = new ClassPathScanningCandidateComponentProvider(false);
42 | scanner.addIncludeFilter(new AssignableTypeFilter(Application.class));
43 |
44 | Set candidates = new HashSet();
45 | Set candidatesSubSet;
46 |
47 | for (String packageToScan : packagesToBeScanned) {
48 | candidatesSubSet = scanner.findCandidateComponents(packageToScan);
49 | candidates.addAll(candidatesSubSet);
50 | }
51 |
52 | Set> classes = new HashSet>();
53 | ClassLoader classLoader = JaxrsApplicationScanner.class.getClassLoader();
54 | Class extends Application> type;
55 | for (BeanDefinition candidate : candidates) {
56 | try {
57 | type = (Class extends Application>) ClassUtils.forName(candidate.getBeanClassName(), classLoader);
58 | classes.add(type);
59 | } catch (ClassNotFoundException e) {
60 | logger.error("JAX-RS Application subclass could not be loaded", e);
61 | }
62 | }
63 |
64 | // We don't want the JAX-RS Application class itself in there
65 | classes.remove(Application.class);
66 |
67 | return classes;
68 | }
69 |
70 | }
71 |
--------------------------------------------------------------------------------
/resteasy-spring-boot-starter/src/main/java/com/paypal/springboot/resteasy/ResteasyApplicationBuilder.java:
--------------------------------------------------------------------------------
1 | package com.paypal.springboot.resteasy;
2 |
3 | import java.util.Set;
4 |
5 | import javax.servlet.Servlet;
6 | import javax.servlet.ServletContainerInitializer;
7 |
8 | import org.jboss.resteasy.plugins.server.servlet.HttpServlet30Dispatcher;
9 | import org.jboss.resteasy.plugins.server.servlet.ResteasyContextParameters;
10 | import org.jboss.resteasy.plugins.servlet.ResteasyServletInitializer;
11 | import org.slf4j.Logger;
12 | import org.slf4j.LoggerFactory;
13 | import org.springframework.boot.web.servlet.ServletRegistrationBean;
14 |
15 | /**
16 | * This class is the Spring Boot equivalent of {@link ResteasyServletInitializer},
17 | * which implements the Servlet API {@link ServletContainerInitializer} interface
18 | * to find all JAX-RS Application, Provider and Path classes in the classpath.
19 | *
20 | * As we all know, in Spring Boot we use an embedded servlet container. However,
21 | * the Servlet spec does not support embedded containers, and many portions of it
22 | * do not apply to embedded containers, and ServletContainerInitializer is one of them.
23 | *
24 | * This class fills in this gap.
25 | *
26 | * Notice that the JAX-RS Application classes are found in this RESTEasy starter by class
27 | * ResteasyEmbeddedServletInitializer, and that is done by scanning the classpath.
28 | *
29 | * The Path and Provider annotated classes are found by using Spring framework (instead of
30 | * scanning the classpath), since it is assumed those classes are ALWAYS necessarily
31 | * Spring beans (this starter is meant for Spring Boot applications that use RESTEasy
32 | * as the JAX-RS implementation)
33 | *
34 | * @author Fabio Carvalho (facarvalho@paypal.com or fabiocarvalho777@gmail.com)
35 | */
36 | public class ResteasyApplicationBuilder {
37 |
38 | public static final String BEAN_NAME = "JaxrsApplicationServletBuilder";
39 |
40 | private static final Logger logger = LoggerFactory.getLogger(ResteasyApplicationBuilder.class);
41 |
42 | public ServletRegistrationBean build(String applicationClassName, String path, Set> resources, Set> providers) {
43 | Servlet servlet = new HttpServlet30Dispatcher();
44 |
45 | ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean(servlet);
46 |
47 | servletRegistrationBean.setName(applicationClassName);
48 | servletRegistrationBean.setLoadOnStartup(1);
49 | servletRegistrationBean.setAsyncSupported(true);
50 | servletRegistrationBean.addInitParameter("javax.ws.rs.Application", applicationClassName);
51 |
52 | if (path != null) {
53 | String mapping = path;
54 | if (!mapping.startsWith("/"))
55 | mapping = "/" + mapping;
56 | String prefix = mapping;
57 | if (!"/".equals(prefix) && prefix.endsWith("/"))
58 | prefix = prefix.substring(0, prefix.length() - 1);
59 | if (mapping.endsWith("/"))
60 | mapping += "*";
61 | else
62 | mapping += "/*";
63 | // resteasy.servlet.mapping.prefix
64 | servletRegistrationBean.addInitParameter("resteasy.servlet.mapping.prefix", prefix);
65 | servletRegistrationBean.addUrlMappings(mapping);
66 | }
67 |
68 | if (resources.size() > 0) {
69 | StringBuilder builder = new StringBuilder();
70 | boolean first = true;
71 | for (Class> resource : resources) {
72 | if (first) {
73 | first = false;
74 | } else {
75 | builder.append(",");
76 | }
77 |
78 | builder.append(resource.getName());
79 | }
80 | servletRegistrationBean.addInitParameter(ResteasyContextParameters.RESTEASY_SCANNED_RESOURCES, builder.toString());
81 | }
82 | if (providers.size() > 0) {
83 | StringBuilder builder = new StringBuilder();
84 | boolean first = true;
85 | for (Class> provider : providers) {
86 | if (first) {
87 | first = false;
88 | } else {
89 | builder.append(",");
90 | }
91 | builder.append(provider.getName());
92 | }
93 | servletRegistrationBean.addInitParameter(ResteasyContextParameters.RESTEASY_SCANNED_PROVIDERS, builder.toString());
94 | }
95 |
96 | logger.debug("ServletRegistrationBean has just bean created for JAX-RS class " + applicationClassName);
97 |
98 | return servletRegistrationBean;
99 | }
100 |
101 | }
102 |
--------------------------------------------------------------------------------
/resteasy-spring-boot-starter/src/main/java/com/paypal/springboot/resteasy/ResteasyAutoConfiguration.java:
--------------------------------------------------------------------------------
1 | package com.paypal.springboot.resteasy;
2 |
3 | import org.jboss.resteasy.core.AsynchronousDispatcher;
4 | import org.jboss.resteasy.core.Dispatcher;
5 | import org.jboss.resteasy.core.ResourceMethodRegistry;
6 | import org.jboss.resteasy.core.SynchronousDispatcher;
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.Registry;
11 | import org.jboss.resteasy.spi.ResteasyDeployment;
12 | import org.jboss.resteasy.spi.ResteasyProviderFactory;
13 | import org.slf4j.Logger;
14 | import org.slf4j.LoggerFactory;
15 | import org.springframework.beans.factory.annotation.Qualifier;
16 | import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
17 | import org.springframework.boot.autoconfigure.AutoConfigureAfter;
18 | import org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration;
19 | import org.springframework.boot.context.properties.EnableConfigurationProperties;
20 | import org.springframework.context.annotation.Bean;
21 | import org.springframework.context.annotation.Configuration;
22 |
23 | import javax.servlet.ServletContext;
24 | import javax.servlet.ServletContextEvent;
25 | import javax.servlet.ServletContextListener;
26 |
27 | /**
28 | * This is the main class that configures this Resteasy Sring Boot starter
29 | *
30 | * @author Fabio Carvalho (facarvalho@paypal.com or fabiocarvalho777@gmail.com)
31 | */
32 | @Configuration
33 | @AutoConfigureAfter(WebMvcAutoConfiguration.class)
34 | @EnableConfigurationProperties
35 | public class ResteasyAutoConfiguration {
36 |
37 | private static Logger logger = LoggerFactory.getLogger(ResteasyAutoConfiguration.class);
38 |
39 | @Bean
40 | @Qualifier("ResteasyProviderFactory")
41 | public static BeanFactoryPostProcessor springBeanProcessor() {
42 | ResteasyProviderFactory resteasyProviderFactory = new ResteasyProviderFactory();
43 | ResourceMethodRegistry resourceMethodRegistry = new ResourceMethodRegistry(resteasyProviderFactory);
44 |
45 | SpringBeanProcessor springBeanProcessor = new SpringBeanProcessor();
46 | springBeanProcessor.setProviderFactory(resteasyProviderFactory);
47 | springBeanProcessor.setRegistry(resourceMethodRegistry);
48 |
49 | logger.debug("SpringBeanProcessor has been created");
50 |
51 | return springBeanProcessor;
52 | }
53 |
54 | /**
55 | * This is a modified version of {@link ResteasyBootstrap}
56 | *
57 | * @return a ServletContextListener object that configures and start a ResteasyDeployment
58 | */
59 | @Bean
60 | public ServletContextListener resteasyBootstrapListener(@Qualifier("ResteasyProviderFactory") final BeanFactoryPostProcessor beanFactoryPostProcessor) {
61 | ServletContextListener servletContextListener = new ServletContextListener() {
62 |
63 | private SpringBeanProcessor springBeanProcessor = (SpringBeanProcessor) beanFactoryPostProcessor;
64 |
65 | protected ResteasyDeployment deployment;
66 |
67 | public void contextInitialized(ServletContextEvent sce) {
68 | ServletContext servletContext = sce.getServletContext();
69 |
70 | ListenerBootstrap config = new ListenerBootstrap(servletContext);
71 |
72 | ResteasyProviderFactory resteasyProviderFactory = springBeanProcessor.getProviderFactory();
73 | ResourceMethodRegistry resourceMethodRegistry = (ResourceMethodRegistry) springBeanProcessor.getRegistry();
74 |
75 | deployment = config.createDeployment();
76 |
77 | deployment.setProviderFactory(resteasyProviderFactory);
78 | deployment.setRegistry(resourceMethodRegistry);
79 |
80 | if (deployment.isAsyncJobServiceEnabled()) {
81 | AsynchronousDispatcher dispatcher = new AsynchronousDispatcher(resteasyProviderFactory, resourceMethodRegistry);
82 | deployment.setDispatcher(dispatcher);
83 | } else {
84 | SynchronousDispatcher dispatcher = new SynchronousDispatcher(resteasyProviderFactory, resourceMethodRegistry);
85 | deployment.setDispatcher(dispatcher);
86 | }
87 |
88 | deployment.start();
89 |
90 | servletContext.setAttribute(ResteasyProviderFactory.class.getName(), deployment.getProviderFactory());
91 | servletContext.setAttribute(Dispatcher.class.getName(), deployment.getDispatcher());
92 | servletContext.setAttribute(Registry.class.getName(), deployment.getRegistry());
93 | }
94 |
95 | public void contextDestroyed(ServletContextEvent sce) {
96 | if (deployment != null) {
97 | deployment.stop();
98 | }
99 | }
100 | };
101 |
102 | logger.debug("ServletContextListener has been created");
103 |
104 | return servletContextListener;
105 | }
106 |
107 | @Bean(name = ResteasyApplicationBuilder.BEAN_NAME)
108 | public ResteasyApplicationBuilder resteasyApplicationBuilder() {
109 | return new ResteasyApplicationBuilder();
110 | }
111 |
112 | @Bean
113 | public static ResteasyEmbeddedServletInitializer resteasyEmbeddedServletInitializer() {
114 | return new ResteasyEmbeddedServletInitializer();
115 | }
116 |
117 | }
118 |
--------------------------------------------------------------------------------
/resteasy-spring-boot-starter/src/main/java/com/paypal/springboot/resteasy/ResteasyEmbeddedServletInitializer.java:
--------------------------------------------------------------------------------
1 | package com.paypal.springboot.resteasy;
2 |
3 | import org.jboss.resteasy.plugins.servlet.ResteasyServletInitializer;
4 | import org.slf4j.Logger;
5 | import org.slf4j.LoggerFactory;
6 | import org.springframework.beans.BeansException;
7 | import org.springframework.beans.factory.BeanFactory;
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 javax.ws.rs.ApplicationPath;
17 | import javax.ws.rs.Path;
18 | import javax.ws.rs.core.Application;
19 | import javax.ws.rs.ext.Provider;
20 | import java.util.*;
21 |
22 | /**
23 | * This is a Spring version of {@link ResteasyServletInitializer}.
24 | * It does not register the servlets though, that is done by {@link ResteasyApplicationBuilder}
25 | * It only finds the JAX-RS Application classes (by scanning the classpath), and
26 | * the JAX-RS Path and Provider annotated Spring beans, and then register the
27 | * Spring bean definitions that represent each servlet registration.
28 | *
29 | * @author Fabio Carvalho (facarvalho@paypal.com or fabiocarvalho777@gmail.com)
30 | */
31 | public class ResteasyEmbeddedServletInitializer implements BeanFactoryPostProcessor {
32 |
33 | private static final String JAXRS_APP_CLASSES_DEFINITION_PROPERTY = "resteasy.jaxrs.app.registration";
34 | private static final String JAXRS_APP_CLASSES_PROPERTY = "resteasy.jaxrs.app.classes";
35 |
36 | // This is how JAXRS_APP_CLASSES_PROPERTY was named originally. It conflicted with "resteasy.jaxrs.app.registration"
37 | // in case of YAML files, since registration was a child of app from an YAML perspective, which is not allowed.
38 | // Because of that its name was changed (the ".classes" suffix was added).
39 | // This legacy property has not been removed though, to keep backward compatibility, but it is marked as deprecated. It will be
40 | // available only for .properties files, but not for YAML files. It should be finally removed in a future major release.
41 | private static final String JAXRS_APP_CLASSES_PROPERTY_LEGACY = "resteasy.jaxrs.app";
42 |
43 | private Set> applications = new HashSet>();
44 | private Set> allResources = new HashSet>();
45 | private Set> providers = new HashSet>();
46 |
47 | private static final Logger logger = LoggerFactory.getLogger(ResteasyEmbeddedServletInitializer.class);
48 |
49 | private enum JaxrsAppClassesRegistration {
50 | BEANS, PROPERTY, SCANNING, AUTO
51 | }
52 |
53 | /*
54 | * Find the JAX-RS application classes.
55 | * This is done by one of these three options in this order:
56 | *
57 | * 1- By having them defined as Spring beans
58 | * 2- By setting property {@code resteasy.jaxrs.app.classes} via Spring Boot application properties file.
59 | * This property should contain a comma separated list of JAX-RS sub-classes
60 | * 3- Via classpath scanning (looking for javax.ws.rs.core.Application sub-classes)
61 | *
62 | * First try to find JAX-RS Application sub-classes defined as Spring beans. If that is existent,
63 | * the search stops, and those are the only JAX-RS applications to be registered.
64 | * If no JAX-RS application Spring beans are found, then see if Spring Boot property {@code resteasy.jaxrs.app.classes}
65 | * has been set. If it has, the search stops, and those are the only JAX-RS applications to be registered.
66 | * If not, then scan the classpath searching for JAX-RS applications.
67 | *
68 | * There is a way though to force one of the options above, which is by setting property
69 | * {@code resteasy.jaxrs.app.registration} via Spring Boot application properties file. The possible valid
70 | * values are {@code beans}, {@code property}, {@code scanning} or {@code auto}. If this property is not
71 | * present, the default value is {@code auto}, which means every approach will be tried in the order and way
72 | * explained earlier.
73 | *
74 | * @param beanFactory
75 | */
76 | private void findJaxrsApplications(ConfigurableListableBeanFactory beanFactory) {
77 | logger.info("Finding JAX-RS Application classes");
78 |
79 | JaxrsAppClassesRegistration registration = getJaxrsAppClassesRegistration(beanFactory);
80 |
81 | switch (registration) {
82 | case AUTO:
83 | findJaxrsApplicationBeans(beanFactory);
84 | if(applications.size() == 0) findJaxrsApplicationProperty(beanFactory);
85 | if(applications.size() == 0) findJaxrsApplicationScanning(beanFactory);
86 | break;
87 | case BEANS:
88 | findJaxrsApplicationBeans(beanFactory);
89 | break;
90 | case PROPERTY:
91 | findJaxrsApplicationProperty(beanFactory);
92 | break;
93 | case SCANNING:
94 | // TODO
95 | // Remove this warning after implementing
96 | // https://github.com/paypal/resteasy-spring-boot/issues/69
97 | logger.warn("\n-------------\nStarting on version 3.0.0, the behavior of the `scanning` JAX-RS Application subclass registration method will change, being more restrictive.\nInstead 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.\nHaving 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.\n-------------");
98 | findJaxrsApplicationScanning(beanFactory);
99 | break;
100 | default:
101 | logger.error("JAX-RS application registration method (%s) not known, no application will be registered", registration.name());
102 | break;
103 | }
104 |
105 | for (Object appClass : applications.toArray()) {
106 | logger.info("JAX-RS Application class found: {}", ((Class) appClass).getName());
107 | }
108 | }
109 |
110 | private JaxrsAppClassesRegistration getJaxrsAppClassesRegistration(ConfigurableListableBeanFactory beanFactory) {
111 | ConfigurableEnvironment configurableEnvironment = beanFactory.getBean(ConfigurableEnvironment.class);
112 | String jaxrsAppClassesRegistration = configurableEnvironment.getProperty(JAXRS_APP_CLASSES_DEFINITION_PROPERTY);
113 | JaxrsAppClassesRegistration registration = JaxrsAppClassesRegistration.AUTO;
114 |
115 | if(jaxrsAppClassesRegistration == null) {
116 | logger.info("Property {} has not been set, JAX-RS Application classes registration is being set to AUTO", JAXRS_APP_CLASSES_DEFINITION_PROPERTY);
117 | } else {
118 | logger.info("Property {} has been set to {}", JAXRS_APP_CLASSES_DEFINITION_PROPERTY, jaxrsAppClassesRegistration);
119 | try {
120 | registration = JaxrsAppClassesRegistration.valueOf(jaxrsAppClassesRegistration.toUpperCase());
121 | } catch(IllegalArgumentException ex) {
122 | String errorMesage = String.format("Property %s has not been properly set, value %s is invalid. JAX-RS Application classes registration is being set to AUTO.", JAXRS_APP_CLASSES_DEFINITION_PROPERTY, jaxrsAppClassesRegistration);
123 | logger.error(errorMesage);
124 | throw new IllegalArgumentException(errorMesage, ex);
125 | }
126 | }
127 |
128 | return registration;
129 | }
130 |
131 | /*
132 | * Find JAX-RS application classes by searching for their related
133 | * Spring beans
134 | *
135 | * @param beanFactory
136 | */
137 | private void findJaxrsApplicationBeans(ConfigurableListableBeanFactory beanFactory) {
138 | logger.info("Searching for JAX-RS Application Spring beans");
139 |
140 | Map applicationBeans = beanFactory.getBeansOfType(Application.class, true, false);
141 | if(applicationBeans == null || applicationBeans.size() == 0) {
142 | logger.info("No JAX-RS Application Spring beans found");
143 | return;
144 | }
145 |
146 | for (Application application : applicationBeans.values()) {
147 | applications.add(application.getClass());
148 | }
149 | }
150 |
151 | /*
152 | * Find JAX-RS application classes via property {@code resteasy.jaxrs.app.classes}
153 | */
154 | private void findJaxrsApplicationProperty(ConfigurableListableBeanFactory beanFactory) {
155 | ConfigurableEnvironment configurableEnvironment = beanFactory.getBean(ConfigurableEnvironment.class);
156 | String jaxrsAppsProperty = configurableEnvironment.getProperty(JAXRS_APP_CLASSES_PROPERTY);
157 | if(jaxrsAppsProperty == null) {
158 | jaxrsAppsProperty = configurableEnvironment.getProperty(JAXRS_APP_CLASSES_PROPERTY_LEGACY);
159 | if(jaxrsAppsProperty == null) {
160 | logger.info("No JAX-RS Application set via property {}", JAXRS_APP_CLASSES_PROPERTY);
161 | return;
162 | }
163 | logger.warn("Property {} has been set. Notice that this property has been deprecated and will be removed soon. Please replace it by property {}", JAXRS_APP_CLASSES_PROPERTY_LEGACY, JAXRS_APP_CLASSES_PROPERTY);
164 | } else {
165 | logger.info("Property {} has been set to {}", JAXRS_APP_CLASSES_PROPERTY, jaxrsAppsProperty);
166 | }
167 |
168 | String[] jaxrsClassNames = jaxrsAppsProperty.split(",");
169 |
170 | for(String jaxrsClassName : jaxrsClassNames) {
171 | Class extends Application> jaxrsClass = null;
172 | try {
173 | jaxrsClass = (Class extends Application>) Class.forName(jaxrsClassName.trim());
174 | } catch (ClassNotFoundException e) {
175 | String exceptionMessage = String.format("JAX-RS Application class %s has not been found", jaxrsClassName.trim());
176 | logger.error(exceptionMessage, e);
177 | throw new BeansException(exceptionMessage, e){};
178 | }
179 | applications.add(jaxrsClass);
180 | }
181 | }
182 |
183 | /*
184 | * Find JAX-RS application classes by scanning the classpath under
185 | * packages already marked to be scanned by Spring framework
186 | */
187 | private void findJaxrsApplicationScanning(BeanFactory beanFactory) {
188 | List packagesToBeScanned = getSpringApplicationPackages(beanFactory);
189 |
190 | Set> applications = JaxrsApplicationScanner.getApplications(packagesToBeScanned);
191 | if(applications == null || applications.size() == 0) {
192 | return;
193 | }
194 | this.applications.addAll(applications);
195 | }
196 |
197 | /*
198 | * Return the name of the packages to be scanned by Spring framework
199 | */
200 | private List getSpringApplicationPackages(BeanFactory beanFactory) {
201 | // TODO
202 | // See https://github.com/paypal/resteasy-spring-boot/issues/69
203 |
204 | List packages = new ArrayList();
205 | packages.add("");
206 | return packages;
207 | }
208 |
209 | /*
210 | * Search for JAX-RS resource and provider Spring beans,
211 | * which are the ones whose classes are annotated with
212 | * {@link Path} or {@link Provider} respectively
213 | *
214 | * @param beanFactory
215 | */
216 | private void findJaxrsResourcesAndProviderClasses(ConfigurableListableBeanFactory beanFactory) {
217 | logger.debug("Finding JAX-RS resources and providers Spring bean classes");
218 |
219 | String[] resourceBeans = beanFactory.getBeanNamesForAnnotation(Path.class);
220 | String[] providerBeans = beanFactory.getBeanNamesForAnnotation(Provider.class);
221 |
222 | if(resourceBeans != null) {
223 | for(String resourceBean : resourceBeans) {
224 | allResources.add(beanFactory.getType(resourceBean));
225 | }
226 | }
227 |
228 | if(providerBeans != null) {
229 | for(String providerBean : providerBeans) {
230 | providers.add(beanFactory.getType(providerBean));
231 | }
232 | }
233 |
234 | if(logger.isDebugEnabled()) {
235 | for (Object resourceClass : allResources.toArray()) {
236 | logger.debug("JAX-RS resource class found: {}", ((Class) resourceClass).getName());
237 | }
238 | }
239 | if(logger.isDebugEnabled()) {
240 | for (Object providerClass: providers.toArray()) {
241 | logger.debug("JAX-RS provider class found: {}", ((Class) providerClass).getName());
242 | }
243 | }
244 | }
245 |
246 | public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
247 | logger.debug("Post process bean factory has been called");
248 |
249 | findJaxrsApplications(beanFactory);
250 |
251 | // This is done by finding their related Spring beans
252 | findJaxrsResourcesAndProviderClasses(beanFactory);
253 |
254 | if (allResources.size() == 0) {
255 | logger.warn("No JAX-RS resource Spring beans have been found");
256 | }
257 | if (applications.size() == 0) {
258 | logger.info("No JAX-RS Application classes have been found. A default, one mapped to '/', will be registered.");
259 | registerDefaultJaxrsApp(beanFactory);
260 | return;
261 | }
262 |
263 | BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;
264 |
265 | for (Class extends Application> applicationClass : applications) {
266 | ApplicationPath path = AnnotationUtils.findAnnotation(applicationClass, ApplicationPath.class);
267 | if (path == null) {
268 | logger.warn("JAX-RS Application class {} has no ApplicationPath annotation, so it will not be registered", applicationClass.getName());
269 | continue;
270 | }
271 |
272 | logger.debug("registering JAX-RS application class " + applicationClass.getName());
273 |
274 | GenericBeanDefinition applicationServletBean = createApplicationServlet(applicationClass, path.value());
275 | registry.registerBeanDefinition(applicationClass.getName(), applicationServletBean);
276 | }
277 |
278 | }
279 |
280 | /**
281 | * Register a default JAX-RS application, in case no other is present in the application.
282 | * Read section 2.3.2 in JAX-RS 2.0 specification.
283 | *
284 | * @param beanFactory
285 | */
286 | private void registerDefaultJaxrsApp(ConfigurableListableBeanFactory beanFactory) {
287 | BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;
288 | GenericBeanDefinition applicationServletBean = createApplicationServlet(Application.class, "/");
289 | registry.registerBeanDefinition(Application.class.getName(), applicationServletBean);
290 | }
291 |
292 | /**
293 | * Creates a Servlet bean definition for the given JAX-RS application
294 | *
295 | * @param applicationClass
296 | * @param path
297 | * @return a Servlet bean definition for the given JAX-RS application
298 | */
299 | private GenericBeanDefinition createApplicationServlet(Class extends Application> applicationClass, String path) {
300 | GenericBeanDefinition applicationServletBean = new GenericBeanDefinition();
301 | applicationServletBean.setFactoryBeanName(ResteasyApplicationBuilder.BEAN_NAME);
302 | applicationServletBean.setFactoryMethodName("build");
303 |
304 | Set> resources = allResources;
305 |
306 | ConstructorArgumentValues values = new ConstructorArgumentValues();
307 | values.addIndexedArgumentValue(0, applicationClass.getName());
308 | values.addIndexedArgumentValue(1, path);
309 | values.addIndexedArgumentValue(2, resources);
310 | values.addIndexedArgumentValue(3, providers);
311 | applicationServletBean.setConstructorArgumentValues(values);
312 |
313 | applicationServletBean.setAutowireCandidate(false);
314 | applicationServletBean.setScope("singleton");
315 |
316 | return applicationServletBean;
317 | }
318 |
319 | }
320 |
--------------------------------------------------------------------------------
/resteasy-spring-boot-starter/src/main/resources/META-INF/spring.factories:
--------------------------------------------------------------------------------
1 | org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
2 | com.paypal.springboot.resteasy.ResteasyAutoConfiguration
--------------------------------------------------------------------------------
/resteasy-spring-boot-starter/src/test/java/com/paypal/springboot/resteasy/JaxrsAppRegistrationTest.java:
--------------------------------------------------------------------------------
1 | package com.paypal.springboot.resteasy;
2 |
3 | import com.paypal.springboot.resteasy.sample.*;
4 | import org.mockito.internal.verification.VerificationModeFactory;
5 | import org.powermock.api.mockito.PowerMockito;
6 | import org.powermock.api.mockito.mockpolicies.Slf4jMockPolicy;
7 | import org.powermock.core.classloader.annotations.MockPolicy;
8 | import org.powermock.core.classloader.annotations.PrepareForTest;
9 | import org.powermock.modules.testng.PowerMockTestCase;
10 | import org.springframework.beans.BeansException;
11 | import org.springframework.beans.factory.BeanFactory;
12 | import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
13 | import org.springframework.beans.factory.support.BeanDefinitionRegistry;
14 | import org.springframework.beans.factory.support.GenericBeanDefinition;
15 | import org.springframework.boot.autoconfigure.AutoConfigurationPackages;
16 | import org.springframework.core.env.ConfigurableEnvironment;
17 | import org.testng.annotations.BeforeMethod;
18 | import org.testng.annotations.Test;
19 |
20 | import javax.ws.rs.Path;
21 | import javax.ws.rs.core.Application;
22 | import javax.ws.rs.ext.Provider;
23 | import java.util.*;
24 |
25 | import static org.mockito.Mockito.*;
26 |
27 | /**
28 | * Created by facarvalho on 7/19/16.
29 | * @author Fabio Carvalho (facarvalho@paypal.com or fabiocarvalho777@gmail.com)
30 | */
31 | @PrepareForTest(AutoConfigurationPackages.class)
32 | @MockPolicy(Slf4jMockPolicy.class)
33 | public class JaxrsAppRegistrationTest extends PowerMockTestCase {
34 |
35 | private static final String DEFINITION_PROPERTY = "resteasy.jaxrs.app.registration";
36 | private static final String APP_CLASSES_PROPERTY = "resteasy.jaxrs.app.classes";
37 | private static final String APP_CLASSES_PROPERTY_LEGACY = "resteasy.jaxrs.app";
38 |
39 | private static Set allPossibleAppClasses;
40 |
41 | static {
42 | Set _allPossibleAppClasses = new HashSet();
43 |
44 | _allPossibleAppClasses.add(TestApplication1.class);
45 | _allPossibleAppClasses.add(TestApplication2.class);
46 | _allPossibleAppClasses.add(TestApplication3.class);
47 | _allPossibleAppClasses.add(TestApplication4.class);
48 | _allPossibleAppClasses.add(TestApplication5.class);
49 | _allPossibleAppClasses.add(Application.class);
50 |
51 | allPossibleAppClasses = Collections.unmodifiableSet(_allPossibleAppClasses);
52 | }
53 |
54 | @BeforeMethod
55 | public void beforeTest() {
56 | PowerMockito.mockStatic(AutoConfigurationPackages.class);
57 | List packages = new ArrayList();
58 | packages.add("com.paypal.springboot.resteasy.sample");
59 | PowerMockito.when(AutoConfigurationPackages.get(any(BeanFactory.class))).thenReturn(packages);
60 | }
61 |
62 | @Test
63 | public void nullTest() {
64 | ConfigurableEnvironment configurableEnvironmentMock = mock(ConfigurableEnvironment.class);
65 | when(configurableEnvironmentMock.getProperty(DEFINITION_PROPERTY)).thenReturn(null);
66 |
67 | Set expectedRegisteredAppClasses = new HashSet();
68 | expectedRegisteredAppClasses.add(TestApplication1.class);
69 | expectedRegisteredAppClasses.add(TestApplication2.class);
70 | expectedRegisteredAppClasses.add(TestApplication4.class);
71 | expectedRegisteredAppClasses.add(TestApplication5.class);
72 |
73 | test(configurableEnvironmentMock, expectedRegisteredAppClasses);
74 | }
75 |
76 | @Test
77 | public void autoTest() {
78 | ConfigurableEnvironment configurableEnvironmentMock = mock(ConfigurableEnvironment.class);
79 | when(configurableEnvironmentMock.getProperty(DEFINITION_PROPERTY)).thenReturn("auto");
80 |
81 | Set expectedRegisteredAppClasses = new HashSet();
82 | expectedRegisteredAppClasses.add(TestApplication1.class);
83 | expectedRegisteredAppClasses.add(TestApplication2.class);
84 | expectedRegisteredAppClasses.add(TestApplication4.class);
85 | expectedRegisteredAppClasses.add(TestApplication5.class);
86 |
87 | test(configurableEnvironmentMock, expectedRegisteredAppClasses);
88 | }
89 |
90 | @Test
91 | public void beansTest() {
92 | ConfigurableEnvironment configurableEnvironmentMock = mock(ConfigurableEnvironment.class);
93 | when(configurableEnvironmentMock.getProperty(DEFINITION_PROPERTY)).thenReturn("beans");
94 |
95 | Set expectedRegisteredAppClasses = new HashSet();
96 | expectedRegisteredAppClasses.add(TestApplication1.class);
97 | expectedRegisteredAppClasses.add(TestApplication4.class);
98 |
99 | test(configurableEnvironmentMock, expectedRegisteredAppClasses);
100 | }
101 |
102 | @Test
103 | public void propertyTest() {
104 | ConfigurableEnvironment configurableEnvironmentMock = mock(ConfigurableEnvironment.class);
105 | when(configurableEnvironmentMock.getProperty(DEFINITION_PROPERTY)).thenReturn("property");
106 | when(configurableEnvironmentMock.getProperty(APP_CLASSES_PROPERTY)).thenReturn("com.paypal.springboot.resteasy.sample.TestApplication3, com.paypal.springboot.resteasy.sample.TestApplication4,com.paypal.springboot.resteasy.sample.TestApplication2");
107 |
108 | Set expectedRegisteredAppClasses = new HashSet();
109 | expectedRegisteredAppClasses.add(TestApplication2.class);
110 | expectedRegisteredAppClasses.add(TestApplication4.class);
111 |
112 | test(configurableEnvironmentMock, expectedRegisteredAppClasses);
113 | }
114 |
115 | @Test
116 | public void legacyPropertyTest() {
117 | ConfigurableEnvironment configurableEnvironmentMock = mock(ConfigurableEnvironment.class);
118 | when(configurableEnvironmentMock.getProperty(DEFINITION_PROPERTY)).thenReturn("property");
119 | when(configurableEnvironmentMock.getProperty(APP_CLASSES_PROPERTY_LEGACY)).thenReturn("com.paypal.springboot.resteasy.sample.TestApplication3, com.paypal.springboot.resteasy.sample.TestApplication4,com.paypal.springboot.resteasy.sample.TestApplication2");
120 |
121 | Set expectedRegisteredAppClasses = new HashSet();
122 | expectedRegisteredAppClasses.add(TestApplication2.class);
123 | expectedRegisteredAppClasses.add(TestApplication4.class);
124 |
125 | test(configurableEnvironmentMock, expectedRegisteredAppClasses);
126 | }
127 |
128 | @Test
129 | public void scanningTest() {
130 | ConfigurableEnvironment configurableEnvironmentMock = mock(ConfigurableEnvironment.class);
131 | when(configurableEnvironmentMock.getProperty(DEFINITION_PROPERTY)).thenReturn("scanning");
132 |
133 | Set expectedRegisteredAppClasses = new HashSet();
134 | expectedRegisteredAppClasses.add(TestApplication1.class);
135 | expectedRegisteredAppClasses.add(TestApplication2.class);
136 | expectedRegisteredAppClasses.add(TestApplication4.class);
137 | expectedRegisteredAppClasses.add(TestApplication5.class);
138 |
139 | test(configurableEnvironmentMock, expectedRegisteredAppClasses);
140 | }
141 |
142 | @Test(expectedExceptions = IllegalArgumentException.class,
143 | expectedExceptionsMessageRegExp = "Property " + DEFINITION_PROPERTY +
144 | " has not been properly set, value blah is invalid. JAX-RS Application classes registration is being set to AUTO.")
145 | public void invalidRegistrationTest() {
146 | ConfigurableEnvironment configurableEnvironmentMock = mock(ConfigurableEnvironment.class);
147 | when(configurableEnvironmentMock.getProperty(DEFINITION_PROPERTY)).thenReturn("blah");
148 |
149 | ConfigurableListableBeanFactory beanFactory = mock(ConfigurableListableBeanFactory.class);
150 | when(beanFactory.getBean(ConfigurableEnvironment.class)).thenReturn(configurableEnvironmentMock);
151 |
152 | ResteasyEmbeddedServletInitializer resteasyEmbeddedServletInitializer = new ResteasyEmbeddedServletInitializer();
153 | resteasyEmbeddedServletInitializer.postProcessBeanFactory(beanFactory);
154 | }
155 |
156 | @Test(expectedExceptions = BeansException.class)
157 | public void classNotFoundTest() {
158 | ConfigurableEnvironment configurableEnvironmentMock = mock(ConfigurableEnvironment.class);
159 | when(configurableEnvironmentMock.getProperty(DEFINITION_PROPERTY)).thenReturn("property");
160 | when(configurableEnvironmentMock.getProperty(APP_CLASSES_PROPERTY)).thenReturn("com.paypal.springboot.resteasy.sample.TestApplication3, com.paypal.springboot.resteasy.sample.TestApplication4,com.paypal.springboot.resteasy.sample.TestApplication9");
161 |
162 | ConfigurableListableBeanFactory beanFactory = mock(ConfigurableListableBeanFactory.class);
163 | when(beanFactory.getBean(ConfigurableEnvironment.class)).thenReturn(configurableEnvironmentMock);
164 |
165 | ResteasyEmbeddedServletInitializer resteasyEmbeddedServletInitializer = new ResteasyEmbeddedServletInitializer();
166 | resteasyEmbeddedServletInitializer.postProcessBeanFactory(beanFactory);
167 | }
168 |
169 | @Test
170 | public void testPropertyNoApps() {
171 | ConfigurableEnvironment configurableEnvironmentMock = mock(ConfigurableEnvironment.class);
172 | when(configurableEnvironmentMock.getProperty(DEFINITION_PROPERTY)).thenReturn("property");
173 |
174 | Set expectedRegisteredAppClasses = new HashSet();
175 | expectedRegisteredAppClasses.add(Application.class);
176 |
177 | test(configurableEnvironmentMock, expectedRegisteredAppClasses);
178 | }
179 |
180 | private void test(ConfigurableEnvironment envMock, Set expectedRegisteredAppClasses) {
181 | ConfigurableListableBeanFactory beanFactory = prepareTest(envMock);
182 | performTest(envMock, beanFactory, expectedRegisteredAppClasses);
183 | }
184 |
185 | private ConfigurableListableBeanFactory prepareTest(ConfigurableEnvironment envMock) {
186 | ConfigurableListableBeanFactory beanFactory = mock(
187 | ConfigurableListableBeanFactory.class,
188 | withSettings().extraInterfaces(BeanDefinitionRegistry.class)
189 | );
190 |
191 | when(beanFactory.getBean(ConfigurableEnvironment.class)).thenReturn(envMock);
192 | when(beanFactory.getBeanNamesForAnnotation(Path.class)).thenReturn(new String[]{"testResource1", "testResource2"});
193 | when(beanFactory.getType("testResource1")).thenReturn((Class) TestResource1.class);
194 | when(beanFactory.getType("testResource2")).thenReturn((Class) TestResource2.class);
195 |
196 | String definition = envMock.getProperty(DEFINITION_PROPERTY);
197 |
198 | if((definition != null && definition.equals("beans"))) {
199 | // Although TestApplication1 and TestApplication4 are not really Spring beans, here we are simulating
200 | // they are to see how the JAX-RS Application registration behaves
201 | Map applicationsMap = new HashMap();
202 | applicationsMap.put("testApplication1", new TestApplication1());
203 | applicationsMap.put("testApplication4", new TestApplication4());
204 | when(beanFactory.getBeansOfType(Application.class, true, false)).thenReturn(applicationsMap);
205 | }
206 |
207 | return beanFactory;
208 | }
209 |
210 | private void performTest(ConfigurableEnvironment envMock, ConfigurableListableBeanFactory beanFactory, Set expectedRegisteredAppClasses) {
211 | String definition = envMock.getProperty(DEFINITION_PROPERTY);
212 | boolean findSpringBeans = (definition == null || definition.equals("auto") || definition.equals("beans"));
213 | boolean getAppsProperty = (definition == null || definition.equals("auto") || definition.equals("property"));
214 |
215 | ResteasyEmbeddedServletInitializer resteasyEmbeddedServletInitializer = new ResteasyEmbeddedServletInitializer();
216 | resteasyEmbeddedServletInitializer.postProcessBeanFactory(beanFactory);
217 |
218 | verify(beanFactory, VerificationModeFactory.times(getAppsProperty ? 2 : 1)).getBean(ConfigurableEnvironment.class);
219 | verify(beanFactory, VerificationModeFactory.times(findSpringBeans ? 1 : 0)).getBeansOfType(Application.class, true, false);
220 | verify(beanFactory, VerificationModeFactory.times(1)).getBeanNamesForAnnotation(Path.class);
221 | verify(beanFactory, VerificationModeFactory.times(1)).getBeanNamesForAnnotation(Provider.class);
222 | verify(beanFactory, VerificationModeFactory.times(2)).getType(anyString());
223 |
224 | BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;
225 |
226 | Set expectedNotRegisteredAppClassess = new HashSet(allPossibleAppClasses);
227 | for(Class applicationClass : expectedRegisteredAppClasses) {
228 | verify(registry, VerificationModeFactory.times(1)).registerBeanDefinition(eq(applicationClass.getName()), any(GenericBeanDefinition.class));
229 | expectedNotRegisteredAppClassess.remove(applicationClass);
230 | }
231 | for(Class applicationClass : expectedNotRegisteredAppClassess) {
232 | verify(registry, VerificationModeFactory.times(0)).registerBeanDefinition(eq(applicationClass.getName()), any(GenericBeanDefinition.class));
233 | }
234 | }
235 |
236 | }
237 |
--------------------------------------------------------------------------------
/resteasy-spring-boot-starter/src/test/java/com/paypal/springboot/resteasy/ResteasyAutoConfigurationTest.java:
--------------------------------------------------------------------------------
1 | package com.paypal.springboot.resteasy;
2 |
3 | import org.jboss.resteasy.core.Dispatcher;
4 | import org.jboss.resteasy.core.ResourceMethodRegistry;
5 | import org.jboss.resteasy.plugins.spring.SpringBeanProcessor;
6 | import org.jboss.resteasy.spi.Registry;
7 | import org.jboss.resteasy.spi.ResteasyProviderFactory;
8 | import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
9 | import org.springframework.mock.web.MockServletContext;
10 | import org.testng.Assert;
11 | import org.testng.annotations.Test;
12 |
13 | import javax.servlet.ServletContext;
14 | import javax.servlet.ServletContextEvent;
15 | import javax.servlet.ServletContextListener;
16 |
17 | /**
18 | * Created by facarvalho on 11/24/15.
19 | * @author Fabio Carvalho (facarvalho@paypal.com or fabiocarvalho777@gmail.com)
20 | */
21 | public class ResteasyAutoConfigurationTest {
22 |
23 | @Test
24 | public void springBeanProcessor() {
25 | BeanFactoryPostProcessor beanFactoryPostProcessor = new ResteasyAutoConfiguration().springBeanProcessor();
26 |
27 | Assert.assertNotNull(beanFactoryPostProcessor);
28 | Assert.assertEquals(SpringBeanProcessor.class, beanFactoryPostProcessor.getClass());
29 |
30 | SpringBeanProcessor springBeanProcessor = (SpringBeanProcessor) beanFactoryPostProcessor;
31 | Registry springBeanProcessorRegistry = springBeanProcessor.getRegistry();
32 | ResteasyProviderFactory providerFactory = springBeanProcessor.getProviderFactory();
33 |
34 | Assert.assertNotNull(springBeanProcessorRegistry);
35 | Assert.assertNotNull(providerFactory);
36 | Assert.assertEquals(ResourceMethodRegistry.class, springBeanProcessorRegistry.getClass());
37 | }
38 |
39 | @Test
40 | public void syncDispatcherServletContextListenerTest() throws Exception {
41 | ServletContext servletContext = new MockServletContext();
42 | testServletContextListener(servletContext);
43 | }
44 |
45 | @Test
46 | public void asyncDispatcherServletContextListenerTest() throws Exception {
47 | ServletContext servletContext = new MockServletContext();
48 | servletContext.setInitParameter("resteasy.async.job.service.enabled", "true");
49 | testServletContextListener(servletContext);
50 | }
51 |
52 | private void testServletContextListener(ServletContext servletContext) throws Exception {
53 | ResteasyAutoConfiguration resteasyAutoConfiguration = new ResteasyAutoConfiguration();
54 | BeanFactoryPostProcessor beanFactoryPostProcessor = ResteasyAutoConfiguration.springBeanProcessor();
55 | ServletContextListener servletContextListener = resteasyAutoConfiguration.resteasyBootstrapListener(beanFactoryPostProcessor);
56 | Assert.assertNotNull(servletContextListener);
57 |
58 | ServletContextEvent sce = new ServletContextEvent(servletContext);
59 | servletContextListener.contextInitialized(sce);
60 |
61 | ResteasyProviderFactory servletContextProviderFactory = (ResteasyProviderFactory) servletContext.getAttribute(ResteasyProviderFactory.class.getName());
62 | Dispatcher servletContextDispatcher = (Dispatcher) servletContext.getAttribute(Dispatcher.class.getName());
63 | Registry servletContextRegistry = (Registry) servletContext.getAttribute(Registry.class.getName());
64 |
65 | Assert.assertNotNull(servletContextProviderFactory);
66 | Assert.assertNotNull(servletContextDispatcher);
67 | Assert.assertNotNull(servletContextRegistry);
68 |
69 | // Exercising fully cobertura branch coverage
70 | servletContextListener.contextDestroyed(sce);
71 | ServletContextListener servletContextListener2 = resteasyAutoConfiguration.resteasyBootstrapListener(beanFactoryPostProcessor);
72 | servletContextListener2.contextDestroyed(sce);
73 | }
74 |
75 |
76 | }
77 |
--------------------------------------------------------------------------------
/resteasy-spring-boot-starter/src/test/java/com/paypal/springboot/resteasy/ResteasyEmbeddedServletInitializerTest.java:
--------------------------------------------------------------------------------
1 | package com.paypal.springboot.resteasy;
2 |
3 | import org.springframework.beans.factory.annotation.Autowired;
4 | import org.springframework.boot.web.servlet.ServletRegistrationBean;
5 | import org.springframework.context.ApplicationContext;
6 | import org.springframework.test.context.ContextConfiguration;
7 | import org.springframework.test.context.testng.AbstractTestNGSpringContextTests;
8 | import org.testng.Assert;
9 | import org.testng.annotations.Test;
10 |
11 | import java.util.Map;
12 |
13 | /**
14 | * Created by facarvalho on 11/25/15.
15 | * @author Fabio Carvalho (facarvalho@paypal.com or fabiocarvalho777@gmail.com)
16 | */
17 | @ContextConfiguration("classpath:test-config.xml")
18 | public class ResteasyEmbeddedServletInitializerTest extends AbstractTestNGSpringContextTests {
19 |
20 | @Autowired
21 | private ApplicationContext applicationContext;
22 |
23 | @Test
24 | public void postProcessBeanFactory() {
25 | Map servletRegistrationBeans = applicationContext.getBeansOfType(ServletRegistrationBean.class);
26 | Assert.assertNotNull(servletRegistrationBeans);
27 |
28 | // Although there are 5 sample JAX-RS Application classes, one of them is not annotated with the ApplicationPath annotation!
29 | Assert.assertEquals(servletRegistrationBeans.size(), 4);
30 |
31 | for(String applicationClassName : servletRegistrationBeans.keySet()) {
32 | testApplicaton(applicationClassName, servletRegistrationBeans.get(applicationClassName));
33 | }
34 | }
35 |
36 | private void testApplicaton(String applicationClassName, ServletRegistrationBean servletRegistrationBean) {
37 | Assert.assertEquals(applicationClassName, servletRegistrationBean.getServletName());
38 | Assert.assertTrue(servletRegistrationBean.isAsyncSupported());
39 | Assert.assertEquals(applicationClassName, servletRegistrationBean.getInitParameters().get("javax.ws.rs.Application"));
40 | Assert.assertTrue(servletRegistrationBean.isAsyncSupported());
41 | }
42 |
43 | }
44 |
--------------------------------------------------------------------------------
/resteasy-spring-boot-starter/src/test/java/com/paypal/springboot/resteasy/sample/SampleApp.java:
--------------------------------------------------------------------------------
1 | package com.paypal.springboot.resteasy.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 |
--------------------------------------------------------------------------------
/resteasy-spring-boot-starter/src/test/java/com/paypal/springboot/resteasy/sample/TestApplication1.java:
--------------------------------------------------------------------------------
1 | package com.paypal.springboot.resteasy.sample;
2 |
3 | import javax.ws.rs.ApplicationPath;
4 | import javax.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 |
--------------------------------------------------------------------------------
/resteasy-spring-boot-starter/src/test/java/com/paypal/springboot/resteasy/sample/TestApplication2.java:
--------------------------------------------------------------------------------
1 | package com.paypal.springboot.resteasy.sample;
2 |
3 | import javax.ws.rs.ApplicationPath;
4 | import javax.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 |
--------------------------------------------------------------------------------
/resteasy-spring-boot-starter/src/test/java/com/paypal/springboot/resteasy/sample/TestApplication3.java:
--------------------------------------------------------------------------------
1 | package com.paypal.springboot.resteasy.sample;
2 |
3 | import javax.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 |
--------------------------------------------------------------------------------
/resteasy-spring-boot-starter/src/test/java/com/paypal/springboot/resteasy/sample/TestApplication4.java:
--------------------------------------------------------------------------------
1 | package com.paypal.springboot.resteasy.sample;
2 |
3 | import javax.ws.rs.ApplicationPath;
4 | import javax.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 |
--------------------------------------------------------------------------------
/resteasy-spring-boot-starter/src/test/java/com/paypal/springboot/resteasy/sample/TestApplication5.java:
--------------------------------------------------------------------------------
1 | package com.paypal.springboot.resteasy.sample;
2 |
3 | import javax.ws.rs.ApplicationPath;
4 | import javax.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 |
--------------------------------------------------------------------------------
/resteasy-spring-boot-starter/src/test/java/com/paypal/springboot/resteasy/sample/TestProvider1.java:
--------------------------------------------------------------------------------
1 | package com.paypal.springboot.resteasy.sample;
2 |
3 | import org.springframework.stereotype.Component;
4 |
5 | import javax.ws.rs.core.Response;
6 | import javax.ws.rs.ext.ExceptionMapper;
7 | import javax.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 |
--------------------------------------------------------------------------------
/resteasy-spring-boot-starter/src/test/java/com/paypal/springboot/resteasy/sample/TestResource1.java:
--------------------------------------------------------------------------------
1 | package com.paypal.springboot.resteasy.sample;
2 |
3 | import org.springframework.stereotype.Component;
4 |
5 | import javax.ws.rs.GET;
6 | import javax.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 |
--------------------------------------------------------------------------------
/resteasy-spring-boot-starter/src/test/java/com/paypal/springboot/resteasy/sample/TestResource2.java:
--------------------------------------------------------------------------------
1 | package com.paypal.springboot.resteasy.sample;
2 |
3 | import org.springframework.stereotype.Component;
4 |
5 | import javax.ws.rs.GET;
6 | import javax.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 |
--------------------------------------------------------------------------------
/resteasy-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 |
--------------------------------------------------------------------------------
/resteasy-spring-boot-starter/src/test/resources/test-config.xml:
--------------------------------------------------------------------------------
1 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/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 should receive a response message with a payload similar to this as result:
21 |
22 | ``` json
23 | {
24 | "timestamp": "1484775122357",
25 | "echoText": "is there anybody out there?"
26 | }
27 | ```
28 |
29 | The request message payload can be anything as plain text.
30 | 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.
31 |
--------------------------------------------------------------------------------
/sample-app/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 | 4.0.0
5 |
6 | com.test
7 | sample-app
8 | 2.3.4-RELEASE
9 | jar
10 |
11 | Simple test application for the Resteasy Spring Boot starter
12 |
13 |
14 | org.springframework.boot
15 | spring-boot-starter-parent
16 | 1.5.8.RELEASE
17 |
18 |
19 |
20 |
21 | UTF-8
22 | 1.8
23 | 3.1.4.Final
24 |
25 |
26 |
27 |
28 | org.springframework.boot
29 | spring-boot-starter-actuator
30 | runtime
31 |
32 |
33 | org.springframework.boot
34 | spring-boot-starter-web
35 |
36 |
37 | javax.ws.rs
38 | javax.ws.rs-api
39 | 2.0
40 |
41 |
42 | com.paypal.springboot
43 | resteasy-spring-boot-starter
44 | ${project.version}
45 | runtime
46 |
47 |
48 |
49 |
50 | org.jboss.resteasy
51 | resteasy-validator-provider-11
52 | ${resteasy.version}
53 |
54 |
55 |
56 |
57 |
58 |
59 | org.springframework.boot
60 | spring-boot-maven-plugin
61 |
62 |
66 |
67 |
68 |
69 | repackage
70 |
71 |
72 | exec
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
--------------------------------------------------------------------------------
/sample-app/src/main/java/com/sample/app/Application.java:
--------------------------------------------------------------------------------
1 | package com.sample.app;
2 |
3 | import org.springframework.boot.SpringApplication;
4 | import org.springframework.boot.autoconfigure.SpringBootApplication;
5 | import org.springframework.boot.web.support.SpringBootServletInitializer;
6 |
7 | /**
8 | * SpringBoot entry point application
9 | *
10 | * @author Fabio Carvalho (facarvalho@paypal.com or fabiocarvalho777@gmail.com)
11 | */
12 | @SpringBootApplication
13 | public class Application extends SpringBootServletInitializer {
14 |
15 | public static void main(String[] args) {
16 | SpringApplication.run(Application.class, args);
17 | }
18 |
19 | }
20 |
--------------------------------------------------------------------------------
/sample-app/src/main/java/com/sample/app/CustomContainerResponseFilter.java:
--------------------------------------------------------------------------------
1 | package com.sample.app;
2 |
3 | import org.springframework.beans.factory.annotation.Autowired;
4 | import org.springframework.stereotype.Component;
5 |
6 | import javax.ws.rs.container.ContainerRequestContext;
7 | import javax.ws.rs.container.ContainerResponseContext;
8 | import javax.ws.rs.container.ContainerResponseFilter;
9 | import javax.ws.rs.ext.Provider;
10 | import java.io.IOException;
11 |
12 | /**
13 | * Custom container request filter just to
14 | * exercise Spring beans as providers and,
15 | * specifically, in this case, as a filter.
16 | *
17 | * @author Fabio Carvalho (facarvalho@paypal.com or fabiocarvalho777@gmail.com)
18 | */
19 | @Component
20 | @Provider
21 | public class CustomContainerResponseFilter implements ContainerResponseFilter {
22 |
23 | @Autowired
24 | private CustomSingletonBean customSingletonBean;
25 |
26 | @Override
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 |
--------------------------------------------------------------------------------
/sample-app/src/main/java/com/sample/app/CustomExceptionMapper.java:
--------------------------------------------------------------------------------
1 | package com.sample.app;
2 |
3 | import org.springframework.beans.factory.annotation.Autowired;
4 | import org.springframework.stereotype.Component;
5 |
6 | import javax.ws.rs.NotFoundException;
7 | import javax.ws.rs.core.MediaType;
8 | import javax.ws.rs.core.Response;
9 | import javax.ws.rs.ext.ExceptionMapper;
10 | import javax.ws.rs.ext.Provider;
11 |
12 | /**
13 | * Custom exception mapper for 404 cases.
14 | *
15 | * @author Fabio Carvalho (facarvalho@paypal.com or fabiocarvalho777@gmail.com)
16 | */
17 | @Component
18 | @Provider
19 | public class CustomExceptionMapper implements ExceptionMapper {
20 |
21 | @Autowired
22 | private CustomSingletonBean customSingletonBean;
23 |
24 | @Override
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 |
--------------------------------------------------------------------------------
/sample-app/src/main/java/com/sample/app/CustomSingletonBean.java:
--------------------------------------------------------------------------------
1 | package com.sample.app;
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 |
--------------------------------------------------------------------------------
/sample-app/src/main/java/com/sample/app/Echo.java:
--------------------------------------------------------------------------------
1 | package com.sample.app;
2 |
3 | import org.springframework.beans.factory.annotation.Autowired;
4 | import org.springframework.stereotype.Component;
5 |
6 | import javax.ws.rs.Consumes;
7 | import javax.ws.rs.POST;
8 | import javax.ws.rs.Path;
9 | import javax.ws.rs.Produces;
10 | import javax.ws.rs.core.MediaType;
11 |
12 | import org.hibernate.validator.constraints.NotEmpty;
13 |
14 | /**
15 | * Echo REST endpoint class
16 | *
17 | * @author Fabio Carvalho (facarvalho@paypal.com or fabiocarvalho777@gmail.com)
18 | */
19 | @Path("/echo")
20 | @Component
21 | public class Echo {
22 |
23 | @Autowired
24 | private EchoMessageCreator echoer;
25 |
26 | /**
27 | * Receives a simple POST request message containing as payload
28 | * a text, in text plain format, to be echoed by the service.
29 | * It returns as response, in JSON, the text to be echoed plus a timestamp of the
30 | * moment the echo response was created on the server side
31 | *
32 | * @param echoText
33 | * @return
34 | */
35 | @POST
36 | @Consumes({ MediaType.TEXT_PLAIN })
37 | @Produces({ MediaType.APPLICATION_JSON })
38 | public EchoMessage echo(@NotEmpty String echoText) {
39 | return echoer.createEchoMessage(echoText);
40 | }
41 |
42 | }
43 |
--------------------------------------------------------------------------------
/sample-app/src/main/java/com/sample/app/EchoMessage.java:
--------------------------------------------------------------------------------
1 | package com.sample.app;
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 | }
--------------------------------------------------------------------------------
/sample-app/src/main/java/com/sample/app/EchoMessageCreator.java:
--------------------------------------------------------------------------------
1 | package com.sample.app;
2 |
3 | import org.springframework.stereotype.Component;
4 |
5 | /**
6 | * This bean creates {@link EchoMessage} objects based on
7 | * echo texts received as input
8 | *
9 | * @author Fabio Carvalho (facarvalho@paypal.com or fabiocarvalho777@gmail.com)
10 | */
11 | @Component
12 | public class EchoMessageCreator {
13 |
14 | public EchoMessage createEchoMessage(String echoText) {
15 | return new EchoMessage(echoText);
16 | }
17 |
18 | }
19 |
--------------------------------------------------------------------------------
/sample-app/src/main/java/com/sample/app/JaxrsApplication.java:
--------------------------------------------------------------------------------
1 | package com.sample.app;
2 |
3 | import org.springframework.stereotype.Component;
4 |
5 | import javax.ws.rs.ApplicationPath;
6 | import javax.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 |
--------------------------------------------------------------------------------
/sample-app/src/main/resources/application.yaml:
--------------------------------------------------------------------------------
1 | resteasy:
2 | jaxrs:
3 | app:
4 | registration: property
5 | classes: com.sample.app.JaxrsApplication
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------