├── .gitignore
├── Jenkinsfile
├── LICENSE
├── README.md
├── pom.xml
└── src
├── main
└── java
│ └── com
│ └── roamsys
│ └── swagger
│ ├── SwaggerAPIAuthorizationHandler.java
│ ├── SwaggerAPIConfig.java
│ ├── SwaggerAPICustomRequestHandler.java
│ ├── SwaggerAPIDefaultAuthorizationHandler.java
│ ├── SwaggerAPIListener.java
│ ├── SwaggerAPIModel.java
│ ├── SwaggerAPIServlet.java
│ ├── annotations
│ ├── SwaggerApi.java
│ ├── SwaggerModel.java
│ └── SwaggerParameter.java
│ ├── data
│ ├── ContentType.java
│ ├── SwaggerAPIContext.java
│ ├── SwaggerAPIModelData.java
│ ├── SwaggerAPIParameterData.java
│ └── SwaggerExceptionHandler.java
│ └── documentation
│ ├── AbstractApiSpecPart.java
│ ├── ApiSpecBuilder.java
│ ├── InfoApiSpec.java
│ ├── OperationApiSpec.java
│ ├── ParameterApiSpec.java
│ ├── ResponseApiSpec.java
│ ├── SwaggerApiSpec.java
│ └── TagApiSpec.java
└── test
├── java
└── com
│ └── roamsys
│ └── swagger
│ ├── TestsAPI.java
│ ├── data
│ └── SwaggerAPIModelDataTest.java
│ └── documentation
│ └── ApiSpecBuilderTest.java
└── resources
└── swagger.json
/.gitignore:
--------------------------------------------------------------------------------
1 | nb-configuration.xml
2 | nbactions*.xml
3 | target/
4 | .settings
5 | .classpath
6 | .project
--------------------------------------------------------------------------------
/Jenkinsfile:
--------------------------------------------------------------------------------
1 | buildAndDeploy([
2 | git: 'https://github.com/ROAMSYS/swaggerapi'
3 | ])
4 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2014 Roamsys S.A.
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
23 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Swagger Java Client
2 |
3 | * [Configuration](#configuration)
4 | * [Register the Swagger API components](#register-the-swagger-api-components)
5 | * [Start Up listener](#start-up-listener)
6 | * [Servlet for API calls](#servlet-for-api-calls)
7 | * [Example API declaration](#example-api-declaration)
8 | * [API class annotation](#api-class-annotation)
9 | * [API method annotations](#api-method-annotations)
10 | * [Method parameters](#method-parameters)
11 | * [License](#license)
12 |
13 | ## Configuration
14 |
15 | Create a class that extends the **SwaggerAPIListener** class. Here you can do all your configuration using the instance of SwaggerAPIConfig. Use the **registerModel** method to add your API services.
16 | ````java
17 | public class SwaggerListener extends SwaggerAPIListener {
18 |
19 | @Override
20 | public void initialize(final SwaggerAPIConfig config) {
21 | config.allowCrossOriginAccess();
22 | config.setAPIVersion("0.1.0");
23 | config.setBasePath("http://example.roamsys.lan:8080/api/");
24 | config.setSwaggerVersion("1.1");
25 | config.setDefaultContentType(SwaggerAPIConfig.CONTENT_TYPE_JSON_UTF8);
26 | config.registerModel(new TestModel());
27 | }
28 | }
29 | ````
30 | ## Register the Swagger API components
31 |
32 | ### Start Up listener
33 | Register the start up listener class you've created in your **web.xml**. This will initiate the Swagger API services at the start up of your application.
34 | ````xml
35 |
36 | ...
37 |
38 | ...
39 | com.roamsys.example.gwt.server.swagger.SwaggerListener
40 |
41 |
42 | ````
43 |
44 | ### Servlet for API calls
45 | Edit your **web.xml** and add the Swagger API servlet. Thats all.
46 | ````xml
47 |
48 | ...
49 |
50 | SwaggerAPI
51 | com.roamsys.swagger.SwaggerAPIServlet
52 |
53 |
54 | SwaggerAPI
55 | /api/*
56 |
57 |
58 | ````
59 |
60 | ## Example API declaration
61 | ````java
62 | @SwaggerModel (path = "/metadata")
63 | public class MetadataAPI implements SwaggerAPIModel {
64 |
65 | @SwaggerApi (
66 | notes = "Returns a list of all documents",
67 | method = HTTPMethod.GET,
68 | path = "/all",
69 | summary = "Get document list")
70 | public void all(final SwaggerAPIContext context) throws IOException {
71 | context.getResponse().getWriter().println("[ { \"name\" : \"document 1\", \"hash\" : \"abc\"}, { \"name\": \"another document\", \"hash\" : \"rrr\"} ]");
72 | }
73 |
74 | @SwaggerApi (
75 | notes = "Returns detailed information a specific document",
76 | method = HTTPMethod.GET,
77 | path = "/details/{hash}",
78 | summary = "Get document details")
79 | public void allForTypeAndFormat(final SwaggerAPIContext context,
80 | @SwaggerParameter (
81 | name = "hash",
82 | description = "The document hash",
83 | required = true,
84 | paramType = ParamType.PATH,
85 | dataType = DataType.STRING
86 | ) final String hash) {
87 | if (hash.equals("abc")) {
88 | context.getResponse().getWriter().println("{ \"name\" : \"document 1\", \"hash\" : \"abc\", , \"size\" : 1232, , \"extension\" : \"odt\"}");
89 | } else if (hash.equals("rrr")) {
90 | context.getResponse().getWriter().println("{ \"name\" : \"another document\", \"hash\" : \"rrr\", , \"size\" : 3532, , \"extension\" : \"zip\"}");
91 | }
92 | }
93 | }
94 | ````
95 | ### API class annotation
96 |
97 | Each class defining an API has to be annotated with @SwaggerModel(path = "pathToAPIs"). All APIs defined in the class will have that path as prefix.
98 | The following example defines two APIs. Both APIs will be available thru the base path of your application followed by */metadata*.
99 |
100 | ### API method annotations
101 |
102 | To make an method available as public API annotate it with @SwaggerApi and provide the usual Swagger specifications:
103 |
104 | * **notes** - A description for the API
105 | * **method** - The HTTP method GET, PUT, POST, DELETE
106 | * **path** - The URL pattern containing placeholders for parameters
107 | * **summary** - A short description or name for the API
108 |
109 | ### Method parameters
110 |
111 | The parameters used in the URL must annotated with the @SwaggerParameter annotation, which uses the following properties:
112 |
113 | * **name** - The name of the parameter, should be the same as the method argument
114 | * **description** - A short description for the parameter
115 | * **required** - Set this to *true* for mandatory parameters, optional parameters should be placed at the end of the URL, if paramType is PATH
116 | * **paramType** - The type/kind of the parameter
117 | * *PATH* - For parameters placed in a REST-full URL seperated by slashes
118 | * *QUERY* - For parameters in a query string append to the URL
119 | * *BODY* - For parameters in a the body (the data) of a PUT or POST request
120 | * *HEADER* - For parameters in the request header
121 | * *FORM* - For parameters in a request body encoded with *multipart/form-data*
122 | * **dataType** - The data type of the parameter
123 | * *STRING*
124 | * *INTEGER*
125 | * *LONG*
126 | * *BOOLEAN*
127 | * *DATE*
128 | * *DATETIME*
129 |
130 | ## Documentation support
131 |
132 | The support for Swagger UI has been changed since Release 5:
133 | * Former versions support the Swagger UI version 1.x (aka resource.json).
134 | * The current version now supports Swagger UI version 2.0 which is also known as *OpenAPI* framework.
135 |
136 | The following URL may be used for getting the OpenAPI specification based on the annotation information defined on class and method level:
137 | `https:////swagger.json`
138 |
139 | The API key has to be specified as URL parameter 'api_key' or as header field 'X-Api-Key'.
140 |
141 | ## License
142 |
143 | The MIT License (MIT)
144 |
145 | Copyright (c) 2014 Roamsys S.A.
146 |
147 | Permission is hereby granted, free of charge, to any person obtaining a copy
148 | of this software and associated documentation files (the "Software"), to deal
149 | in the Software without restriction, including without limitation the rights
150 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
151 | copies of the Software, and to permit persons to whom the Software is
152 | furnished to do so, subject to the following conditions:
153 |
154 | The above copyright notice and this permission notice shall be included in all
155 | copies or substantial portions of the Software.
156 |
157 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
158 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
159 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
160 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
161 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
162 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
163 | SOFTWARE.
164 |
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 | 4.0.0
7 | com.roamsys.opensource
8 | swaggerapi
9 | jar
10 | 11.0.0-SNAPSHOT
11 |
12 | ROAMSYS S.A.
13 | http://www.roamsys.com
14 |
15 | SwaggerAPI
16 | Swagger API support for server with REST APIs written in Java
17 |
18 |
19 | jakarta.servlet
20 | jakarta.servlet-api
21 | 6.1.0
22 | provided
23 |
24 |
25 | org.apache.commons
26 | commons-lang3
27 | 3.17.0
28 |
29 |
30 | commons-io
31 | commons-io
32 | 2.18.0
33 |
34 |
35 | com.google.code.gson
36 | gson
37 | 2.12.1
38 |
39 |
40 | junit
41 | junit
42 | 4.13.2
43 | test
44 |
45 |
46 |
47 | UTF-8
48 |
49 |
50 |
51 |
52 | org.apache.maven.plugins
53 | maven-compiler-plugin
54 | 3.14.0
55 |
56 | 17
57 |
58 |
59 |
60 |
61 |
62 | org.apache.maven.wagon
63 | wagon-ssh
64 | 3.3.3
65 |
66 |
67 |
68 |
69 |
70 | Roamsys Repository
71 | roamsys-repository
72 | scp://37.157.154.101/home/mvnuser/repository
73 |
74 |
75 |
76 |
77 | roamsys-repository
78 | Roamsys Repository
79 | scp://37.157.154.101/home/mvnuser/repository
80 |
81 |
82 |
83 |
84 | Jörg Adams
85 | adams@roamsys-next.com
86 | ROAMSYS S.A.
87 | http://www.roamsys.com
88 |
89 |
90 | Johanna Heib
91 | heib@roamsys-next.com
92 | ROAMSYS S.A.
93 | http://www.roamsys.com
94 |
95 |
96 | Norbert Becker
97 | becker@roamsys-next.com
98 | ROAMSYS S.A.
99 | http://www.roamsys.com
100 |
101 |
102 |
103 | scm:git:git@github.com:ROAMSYS/swaggerapi.git
104 | scm:git:git@github.com:ROAMSYS/swaggerapi.git
105 | git@github.com:ROAMSYS/swaggerapi.git
106 |
107 |
108 |
109 | MIT License
110 | http://www.opensource.org/licenses/mit-license.php
111 |
112 |
113 |
114 |
--------------------------------------------------------------------------------
/src/main/java/com/roamsys/swagger/SwaggerAPIAuthorizationHandler.java:
--------------------------------------------------------------------------------
1 | package com.roamsys.swagger;
2 |
3 | import java.io.IOException;
4 | import jakarta.servlet.ServletException;
5 | import jakarta.servlet.http.HttpServletRequest;
6 | import jakarta.servlet.http.HttpServletResponse;
7 |
8 | /**
9 | * Interface for authentication handler.
10 | *
11 | * @author mbartel
12 | */
13 | public interface SwaggerAPIAuthorizationHandler {
14 |
15 | /**
16 | * Handles a Swagger API authentication request.
17 | *
18 | * @param request the request
19 | * @param response the response
20 | * @return true, if the request is authorized
21 | * @throws ServletException
22 | * @throws IOException
23 | */
24 | boolean isRequestAuthorized(final HttpServletRequest request, final HttpServletResponse response) throws ServletException, IOException;
25 |
26 | /**
27 | * Default implementation for resolving API key from request.
28 | * The following strategy is used:
29 | *
30 | *
Check for URL parameter api_key
31 | *
Check for header field X-Api-Key
32 | *
33 | *
34 | * @param request the request
35 | * @return the resolved API key or null
36 | */
37 | default String resolveApiKey(final HttpServletRequest request) {
38 | final String requestAPIKeyParameter = request.getParameter("api_key");
39 | if (requestAPIKeyParameter == null) {
40 | return request.getHeader("x-api-key");
41 | } else {
42 | return requestAPIKeyParameter;
43 | }
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/src/main/java/com/roamsys/swagger/SwaggerAPIConfig.java:
--------------------------------------------------------------------------------
1 | package com.roamsys.swagger;
2 |
3 | import com.roamsys.swagger.annotations.SwaggerApi;
4 | import com.roamsys.swagger.annotations.SwaggerModel;
5 | import com.roamsys.swagger.annotations.SwaggerParameter;
6 | import com.roamsys.swagger.data.SwaggerAPIModelData;
7 | import com.roamsys.swagger.data.SwaggerAPIParameterData;
8 | import com.roamsys.swagger.data.SwaggerExceptionHandler;
9 | import com.roamsys.swagger.documentation.ApiSpecBuilder;
10 | import com.roamsys.swagger.documentation.SwaggerApiSpec;
11 | import java.lang.annotation.Annotation;
12 | import java.lang.reflect.Method;
13 | import java.net.URL;
14 | import java.util.ArrayList;
15 | import java.util.Collections;
16 | import java.util.HashMap;
17 | import java.util.List;
18 | import java.util.Map;
19 | import java.util.stream.Collectors;
20 | import jakarta.servlet.ServletContext;
21 |
22 | /**
23 | * The swagger configuration.
24 | *
25 | * @author johanna
26 | */
27 | public class SwaggerAPIConfig {
28 |
29 | /**
30 | * Content type for JavaScript
31 | */
32 | public final static String CONTENT_TYPE_JAVASCRIPT = "text/javascript";
33 |
34 | /**
35 | * The swagger API servlet context attribute name
36 | */
37 | public final static String SERVLET_ATTRIBUTE_NAME = "SwaggerAPIModel";
38 |
39 | /**
40 | * The map that holds all swagger API data with their path as identifier
41 | */
42 | private final Map> swaggerAPIs = new HashMap<>();
43 |
44 | /**
45 | * The builder for collecting API spec information
46 | */
47 | private final ApiSpecBuilder apiSpecBuilder;
48 |
49 | /**
50 | * Defines if cross origin access is allowed
51 | */
52 | private boolean allowOriginAccess = false;
53 |
54 | /**
55 | * The default content type for the HTTP response
56 | */
57 | private String defaultContentType;
58 |
59 | /**
60 | * The exception handler
61 | */
62 | private SwaggerExceptionHandler exceptionHandler = SwaggerExceptionHandler.DEFAULT;
63 |
64 | /**
65 | * The handler used to authenticate the API calls
66 | */
67 | private SwaggerAPIAuthorizationHandler authorizationHandler;
68 |
69 | /**
70 | * The handler that is executed before each request after the authentication
71 | */
72 | private SwaggerAPICustomRequestHandler preRequestHandler;
73 |
74 | /**
75 | * The handler that is executed after each request
76 | */
77 | private SwaggerAPICustomRequestHandler postRequestHandler;
78 |
79 | /**
80 | * The context of the servlet session
81 | */
82 | private final ServletContext servletContext;
83 |
84 | /**
85 | * Constructor to initialize values
86 | * @param servletContext the servlet context
87 | */
88 | public SwaggerAPIConfig(final ServletContext servletContext) {
89 | this.servletContext = servletContext;
90 | apiSpecBuilder = new ApiSpecBuilder();
91 | }
92 |
93 | /**
94 | * Registers a new API model
95 | *
96 | * @param model new Swagger API model
97 | */
98 | public void registerModel(final SwaggerAPIModel model) {
99 | // check if current model is annotated with {@link SwaggerModel}
100 | final SwaggerModel modelAnnotation = model.getClass().getAnnotation(SwaggerModel.class);
101 | if (modelAnnotation != null) {
102 | final String modelPath = modelAnnotation.path() + "." + modelAnnotation.format();
103 |
104 | for (final Method method : model.getClass().getMethods()) {
105 | if (method.isAnnotationPresent(SwaggerApi.class)) {
106 |
107 | // fetch Swagger annotations for the method and it's parameters and prepare the data structures for them
108 | method.setAccessible(true);
109 | final SwaggerApi annotation = method.getAnnotation(SwaggerApi.class);
110 | final Annotation[][] annotations = method.getParameterAnnotations();
111 | final List paramAnnotations = new ArrayList<>(annotations.length);
112 |
113 | // collect the parameter annotations
114 | for (final Annotation[] param : annotations) {
115 | for (final Annotation currentParamAnnotation : param) {
116 | if (currentParamAnnotation.annotationType().equals(SwaggerParameter.class)) {
117 | final SwaggerParameter paramAnnotaion = (SwaggerParameter) currentParamAnnotation;
118 | paramAnnotations.add(paramAnnotaion);
119 | }
120 | }
121 | }
122 |
123 | // add the data structure with the collected information to the list of APIs for the current base path
124 | final String path = modelPath + annotation.path();
125 | final List parameters = paramAnnotations.stream().map(a -> new SwaggerAPIParameterData(a.name(), a.paramType(), a.dataType())).collect(Collectors.toList());
126 | swaggerAPIs.computeIfAbsent(modelPath, p -> new ArrayList<>()).add(new SwaggerAPIModelData(model, method, annotation.method(), path, parameters));
127 |
128 | // add the API operation to spec
129 | apiSpecBuilder.addOperation(modelAnnotation, annotation, paramAnnotations);
130 | }
131 | }
132 | } else {
133 | throw new IllegalArgumentException(SwaggerAPIModel.class.getSimpleName() + " annotation must be present on model class");
134 | }
135 | }
136 |
137 | /**
138 | * Get swagger API list for current base path
139 | *
140 | * @param path the base path to the API model
141 | * @return list of swagger API data that belong to the base path
142 | */
143 | public List getAPIsFor(final String path) {
144 | return swaggerAPIs.get(path);
145 | }
146 |
147 | /**
148 | * Get the OpenAPI spec for the configured models.
149 | *
150 | * @return the API spec
151 | */
152 | public SwaggerApiSpec getApiSpec() {
153 | return apiSpecBuilder.getApiSpec();
154 | }
155 |
156 | /**
157 | * Sets the URL serving the API. This field is important for completing the OpenAPI specification.
158 | * Declarations on the server providing the APIs themselves, it is not a requirement.
159 | *
160 | * @param apiUrl The API URL
161 | */
162 | public void setURL(final URL apiUrl) {
163 | apiSpecBuilder.setSchemes(Collections.singletonList(apiUrl.getProtocol()));
164 | if (apiUrl.getPort() != -1) {
165 | apiSpecBuilder.setHost(String.format("%s:%d", apiUrl.getHost(), apiUrl.getPort()));
166 | } else {
167 | apiSpecBuilder.setHost(apiUrl.getHost());
168 | }
169 | apiSpecBuilder.setBasePath(apiUrl.getPath());
170 | }
171 |
172 | /**
173 | * Provides the version of the application API.
174 | *
175 | * @param apiVersion the API version as string
176 | */
177 | public void setAPIVersion(final String apiVersion) {
178 | apiSpecBuilder.setVersion(apiVersion);
179 | }
180 |
181 | /**
182 | * Provides title metadata about the API. The title can be used by the clients if needed, and can be presented in the
183 | * Swagger-UI for convenience.
184 | *
185 | * @param info the swagger metadata info as string
186 | */
187 | public void setTitle(final String title) {
188 | apiSpecBuilder.setTitle(title);
189 | }
190 |
191 | /**
192 | * Provides description metadata about the API. This field is important for completing the OpenAPI specification.
193 | *
194 | * @param info the swagger metadata info as string
195 | */
196 | public void setDescription(final String description) {
197 | apiSpecBuilder.setDescription(description);
198 | }
199 |
200 | /**
201 | * Returns the context of the Swagger API servlet
202 | * @return the servlet context
203 | */
204 | public ServletContext getServletContext() {
205 | return servletContext;
206 | }
207 |
208 | /**
209 | * Returns if current path belongs to an API model definition
210 | *
211 | * @param path the current API path
212 | * @return true if current path defines an API model
213 | */
214 | public boolean isAPIModelPath(final String path) {
215 | return swaggerAPIs.containsKey(path);
216 | }
217 |
218 | /**
219 | * Allows cross origin access to the API. Is not allowed by default.
220 | */
221 | public void allowCrossOriginAccess() {
222 | allowOriginAccess = true;
223 | }
224 |
225 | /**
226 | * Returns if cross origin access to the API is allowed.
227 | *
228 | * @return true if cross origin access is enabled
229 | */
230 | public boolean isCrossOriginAccessAllowed() {
231 | return allowOriginAccess;
232 | }
233 |
234 | /**
235 | * Sets the default content type for the HTTP response
236 | *
237 | * @param defaultContentType
238 | */
239 | public void setDefaultContentType(final String defaultContentType) {
240 | this.defaultContentType = defaultContentType;
241 | }
242 |
243 | /**
244 | * Returns default content type for the HTTP response
245 | *
246 | * @return default content type for the HTTP response
247 | */
248 | public String getDefaultContentType() {
249 | return defaultContentType;
250 | }
251 |
252 | /**
253 | * Returns the exception handler for handling the exceptions thrown while method invocations
254 | * @return the exception handler or null if none is set
255 | */
256 | public SwaggerExceptionHandler getExceptionHandler() {
257 | return exceptionHandler;
258 | }
259 |
260 | /**
261 | * Sets an exception handler for handling exceptions thrown while method invocations
262 | * @param exceptionHandler the exception handler
263 | */
264 | public void setExceptionHandler(final SwaggerExceptionHandler exceptionHandler) {
265 | this.exceptionHandler = exceptionHandler;
266 | }
267 |
268 | /**
269 | * Sets the authorization handler to authenticate the API calls
270 | * @param authorizationHandler the authorization handler (set to null to disable authorization)
271 | */
272 | public void setAuthorizationHandler(final SwaggerAPIAuthorizationHandler authorizationHandler) {
273 | this.authorizationHandler = authorizationHandler;
274 | }
275 |
276 | /**
277 | * Returns the authentication handler, which is called before every time before a request is handled
278 | * @return the authentication handler
279 | */
280 | public SwaggerAPIAuthorizationHandler getAuthorizationHandler() {
281 | return authorizationHandler;
282 | }
283 |
284 | /**
285 | * Returns the pre-request handler, that is executed before each API request (after the authentication)
286 | * @return the pre request handler
287 | */
288 | public SwaggerAPICustomRequestHandler getPreRequestHandler() {
289 | return preRequestHandler;
290 | }
291 |
292 | /**
293 | * Sets the pre-request handler, that is executed before each API request (after the authentication)
294 | * @param preRequestHandler the pre request hander (set to null to disable)
295 | */
296 | public void setPreRequestHandler(final SwaggerAPICustomRequestHandler preRequestHandler) {
297 | this.preRequestHandler = preRequestHandler;
298 | }
299 |
300 | /**
301 | * Returns the post-request handler, that is executed after each API request
302 | * @return the post request handler
303 | */
304 | public SwaggerAPICustomRequestHandler getPostRequestHandler() {
305 | return postRequestHandler;
306 | }
307 |
308 | /**
309 | * Sets the post-request handler, that is executed after each API request
310 | * @param postRequestHandler the post request hander (set to null to disable)
311 | */
312 | public void setPostRequestHandler(final SwaggerAPICustomRequestHandler postRequestHandler) {
313 | this.postRequestHandler = postRequestHandler;
314 | }
315 |
316 | }
317 |
--------------------------------------------------------------------------------
/src/main/java/com/roamsys/swagger/SwaggerAPICustomRequestHandler.java:
--------------------------------------------------------------------------------
1 | package com.roamsys.swagger;
2 |
3 | import java.io.IOException;
4 | import jakarta.servlet.ServletException;
5 | import jakarta.servlet.http.HttpServletRequest;
6 | import jakarta.servlet.http.HttpServletResponse;
7 |
8 | /**
9 | * Interface for pre- and post-request handlers
10 | * @author mbartel
11 | */
12 | public interface SwaggerAPICustomRequestHandler {
13 |
14 | /**
15 | * Handles a Swagger API request
16 | * @param request the request
17 | * @param response the response
18 | * @throws ServletException
19 | * @throws IOException
20 | */
21 | public void handle(final HttpServletRequest request, final HttpServletResponse response) throws ServletException, IOException;
22 | }
23 |
--------------------------------------------------------------------------------
/src/main/java/com/roamsys/swagger/SwaggerAPIDefaultAuthorizationHandler.java:
--------------------------------------------------------------------------------
1 | package com.roamsys.swagger;
2 |
3 | import java.io.IOException;
4 | import jakarta.servlet.ServletException;
5 | import jakarta.servlet.http.HttpServletRequest;
6 | import jakarta.servlet.http.HttpServletResponse;
7 |
8 | /**
9 | * Check for a valid API key
10 | * @author mbartel
11 | */
12 | public class SwaggerAPIDefaultAuthorizationHandler implements SwaggerAPIAuthorizationHandler {
13 |
14 | /**
15 | * The API key
16 | */
17 | private final String apiKey;
18 |
19 | /**
20 | * Creates a new authentication handler that only checks the API key
21 | * @param apiKey the API key
22 | */
23 | public SwaggerAPIDefaultAuthorizationHandler(final String apiKey) {
24 | this.apiKey = apiKey;
25 | }
26 |
27 | @Override
28 | public boolean isRequestAuthorized(final HttpServletRequest request, final HttpServletResponse response) throws ServletException, IOException {
29 | final String requestAPIKey = resolveApiKey(request);
30 | return requestAPIKey != null && requestAPIKey.equals(apiKey);
31 | }
32 |
33 | }
34 |
--------------------------------------------------------------------------------
/src/main/java/com/roamsys/swagger/SwaggerAPIListener.java:
--------------------------------------------------------------------------------
1 | package com.roamsys.swagger;
2 |
3 | import jakarta.servlet.ServletContext;
4 | import jakarta.servlet.ServletContextEvent;
5 | import jakarta.servlet.ServletContextListener;
6 |
7 | /**
8 | * The swagger API listener to be executed on startup
9 | *
10 | * @author johanna
11 | */
12 | public abstract class SwaggerAPIListener implements ServletContextListener {
13 |
14 | @Override
15 | public void contextInitialized(final ServletContextEvent sce) {
16 | final ServletContext servletContext = sce.getServletContext();
17 |
18 | final SwaggerAPIConfig model = new SwaggerAPIConfig(servletContext);
19 | servletContext.setAttribute(SwaggerAPIConfig.SERVLET_ATTRIBUTE_NAME, model);
20 |
21 | System.out.print("Initializing Swagger API components ... ");
22 | initialize(model);
23 | System.out.println("done. => " + model.getApiSpec().toString());
24 | }
25 |
26 | @Override
27 | public void contextDestroyed(final ServletContextEvent sce) {
28 | sce.getServletContext().removeAttribute(SwaggerAPIConfig.SERVLET_ATTRIBUTE_NAME);
29 | }
30 |
31 | /**
32 | * Initializes the swagger API startup definitions
33 | *
34 | * @param model the swagger API model
35 | */
36 | public abstract void initialize(final SwaggerAPIConfig model);
37 |
38 | }
39 |
--------------------------------------------------------------------------------
/src/main/java/com/roamsys/swagger/SwaggerAPIModel.java:
--------------------------------------------------------------------------------
1 | package com.roamsys.swagger;
2 |
3 | /**
4 | * A marker interface for swagger API models
5 | *
6 | * @author johanna
7 | */
8 | public interface SwaggerAPIModel {
9 |
10 | }
11 |
--------------------------------------------------------------------------------
/src/main/java/com/roamsys/swagger/SwaggerAPIServlet.java:
--------------------------------------------------------------------------------
1 | package com.roamsys.swagger;
2 |
3 | import com.google.gson.GsonBuilder;
4 | import com.roamsys.swagger.annotations.SwaggerApi.HTTPMethod;
5 | import com.roamsys.swagger.annotations.SwaggerParameter.DataType;
6 | import com.roamsys.swagger.data.ContentType;
7 | import com.roamsys.swagger.data.SwaggerAPIContext;
8 | import com.roamsys.swagger.data.SwaggerAPIModelData;
9 | import com.roamsys.swagger.data.SwaggerAPIParameterData;
10 | import com.roamsys.swagger.data.SwaggerExceptionHandler;
11 | import java.io.IOException;
12 | import java.lang.reflect.InvocationTargetException;
13 | import java.text.ParseException;
14 | import java.text.SimpleDateFormat;
15 | import java.util.List;
16 | import java.util.regex.Matcher;
17 | import jakarta.servlet.ServletException;
18 | import jakarta.servlet.http.HttpServlet;
19 | import jakarta.servlet.http.HttpServletRequest;
20 | import jakarta.servlet.http.HttpServletResponse;
21 | import org.apache.commons.lang3.StringUtils;
22 |
23 | /**
24 | * The Swagger API servlet.
25 | *
26 | * @author johanna
27 | */
28 | public class SwaggerAPIServlet extends HttpServlet {
29 |
30 | private static final long serialVersionUID = 1L;
31 |
32 | /**
33 | * Date format for API calls
34 | */
35 | private static final String DATE_FORMAT = "yyyy-MM-dd";
36 |
37 | /**
38 | * Date-Time format for API calls
39 | */
40 | private static final String DATE_TIME_FORMAT = "yyyy-MM-dd'T'HH:mm:ssXXX";
41 |
42 | /**
43 | * Processes requests for both HTTP GET and POST methods.
44 | *
45 | * @param request servlet request
46 | * @param response servlet response
47 | * @param method the HTTP method call type
48 | * @throws ServletException if a servlet-specific error occurs
49 | * @throws IOException if an I/O error occurs
50 | */
51 | protected void processRequest(final HttpServletRequest request, final HttpServletResponse response, final HTTPMethod method) throws ServletException, IOException {
52 | final SwaggerAPIConfig config = (SwaggerAPIConfig) request.getSession().getServletContext().getAttribute(SwaggerAPIConfig.SERVLET_ATTRIBUTE_NAME);
53 |
54 | // register exception handler for API
55 | final String exceptionHandlerClass = getServletConfig().getInitParameter("exceptionHandler");
56 | SwaggerExceptionHandler exceptionHandler = config.getExceptionHandler();
57 | if (!StringUtils.isEmpty(exceptionHandlerClass)) {
58 | try {
59 | final Class> clazz = Class.forName(exceptionHandlerClass);
60 | exceptionHandler = (SwaggerExceptionHandler) clazz.getConstructor().newInstance();
61 | config.setExceptionHandler(exceptionHandler);
62 | } catch (final Exception ex) {
63 | // log error with default exception handler
64 | exceptionHandler.handleException(response, HttpServletResponse.SC_BAD_REQUEST, "Could not instantiate Swagger exception handler [" + exceptionHandlerClass + "]. Default exception handler will be used.", ex);
65 | }
66 | }
67 |
68 | // get the URL decoded path to be called
69 | final String path = request.getPathInfo();
70 | if (path == null) {
71 | exceptionHandler.handleException(response, HttpServletResponse.SC_BAD_REQUEST, "Invalid empty path", null);
72 | return;
73 | }
74 |
75 | // enables cross-origin-access, if allowed in config
76 | if (config.isCrossOriginAccessAllowed()) {
77 | response.addHeader("Access-Control-Allow-Origin", "*");
78 | response.addHeader("Access-Control-Allow-Methods", "POST, GET, PUT, DELETE, UPDATE, OPTIONS");
79 | response.addHeader("Access-Control-Allow-Headers", "Content-Type, Accept, X-Requested-With, X-Api-Key");
80 | }
81 |
82 | // sets the default content type, if defined in config
83 | if (config.getDefaultContentType() != null) {
84 | response.setContentType(config.getDefaultContentType());
85 | }
86 |
87 | // try to authenticate the API call
88 | if (config.getAuthorizationHandler() != null && !config.getAuthorizationHandler().isRequestAuthorized(request, response)) {
89 | exceptionHandler.handleException(response, HttpServletResponse.SC_UNAUTHORIZED, "Invalid authorization key", null);
90 | return;
91 | }
92 |
93 | // execute pre-request handler
94 | if (config.getPreRequestHandler() != null) {
95 | config.getPreRequestHandler().handle(request, response);
96 | }
97 |
98 | // OpenAPI swagger.json request e.g. from Swagger UI
99 | if (path.equals("/swagger.json")) {
100 | response.setStatus(HttpServletResponse.SC_OK);
101 | response.setContentType(ContentType.JSON_UTF8);
102 | final GsonBuilder gsonBuilder = new GsonBuilder();
103 | gsonBuilder.excludeFieldsWithoutExposeAnnotation();
104 | gsonBuilder.create().toJson(config.getApiSpec(), response.getWriter());
105 | } else {
106 | // API method calls
107 | final int basePathEndPos = path.indexOf("/", 1);
108 | if (basePathEndPos == -1) {
109 | exceptionHandler.handleException(response, HttpServletResponse.SC_METHOD_NOT_ALLOWED, "Invalid base URL", null);
110 | return;
111 | }
112 |
113 | // initialize for maybe no match
114 | boolean matchFound = false;
115 |
116 | final String basePath = path.substring(0, path.indexOf("/", 1));
117 | if (config.isAPIModelPath(basePath)) {
118 | for (final SwaggerAPIModelData api : config.getAPIsFor(basePath)) {
119 | // Find matching API model and method
120 | if (api.getHTTPMethod() == method) {
121 | final Matcher pathParamMatcher = api.matchPath(path);
122 | if (pathParamMatcher.matches()) {
123 | matchFound = true;
124 | response.setStatus(HttpServletResponse.SC_OK);
125 |
126 | // Set up variables for parameter collection
127 | final List paramsData = api.getParameters();
128 | final int parameterCount = paramsData.size();
129 | final Object[] arguments = new Object[parameterCount + 1];
130 | arguments[0] = new SwaggerAPIContext(this, request, response, exceptionHandler);
131 |
132 | // Collect parameters
133 | int getParamIndex = 0;
134 | for (int i = 1; i <= parameterCount; i++) {
135 | final SwaggerAPIParameterData paramData = paramsData.get(i - 1);
136 |
137 | // Fetch and convert argument value
138 | try {
139 | switch (paramData.getParamType()) {
140 | case PATH:
141 | if (pathParamMatcher.groupCount() >= getParamIndex + 1) {
142 | arguments[i] = convertParamToArgument(paramData.getDataType(), pathParamMatcher.group(1 + getParamIndex++));
143 | }
144 | break;
145 |
146 | case QUERY:
147 | arguments[i] = convertParamToArgument(paramData.getDataType(), request.getParameter(paramData.getName()));
148 | break;
149 |
150 | case FORM:
151 | case BODY:
152 | arguments[i] = request.getInputStream();
153 | break;
154 |
155 | case HEADER:
156 | arguments[i] = convertParamToArgument(paramData.getDataType(), request.getHeader(paramData.getName()));
157 | break;
158 |
159 | default:
160 | throw new IllegalArgumentException("Handling for parameter type \"" + paramData.getParamType().name() + "\" not yet implemented.");
161 | }
162 | } catch (final ParseException ex) {
163 | exceptionHandler.handleException(response, HttpServletResponse.SC_BAD_REQUEST, "Invalid value for parameter " + paramData.getName(), ex);
164 | return;
165 | }
166 | }
167 |
168 | // Try to invoke method
169 | try {
170 | api.getMethod().invoke(api.getAPIModelClass(), arguments);
171 | } catch (final IllegalAccessException ex) {
172 | exceptionHandler.handleException(response, HttpServletResponse.SC_BAD_REQUEST, "Called method is not accessable", ex);
173 | } catch (final IllegalArgumentException ex) {
174 | exceptionHandler.handleException(response, HttpServletResponse.SC_NOT_ACCEPTABLE, "Illegal parameters for called method. See server error log for details.", ex);
175 | } catch (final InvocationTargetException ex) {
176 | exceptionHandler.handleException(response, HttpServletResponse.SC_BAD_REQUEST, "Error calling method. See server error log for details.", ex.getTargetException());
177 | } catch (final Throwable ex) {
178 | exceptionHandler.handleException(response, HttpServletResponse.SC_BAD_REQUEST, "Internal server error for called method. See server error log for details.", ex);
179 | }
180 | break;
181 | }
182 | }
183 | }
184 | }
185 | if (!matchFound) {
186 | exceptionHandler.handleException(response, HttpServletResponse.SC_NOT_IMPLEMENTED, "Called method does not exist", null);
187 | }
188 | }
189 |
190 | // execute post-request handler
191 | if (config.getPostRequestHandler() != null) {
192 | config.getPostRequestHandler().handle(request, response);
193 | }
194 |
195 | response.flushBuffer();
196 | }
197 |
198 | /**
199 | * Convert the swagger API parameter to a method argument class type depending on the swagger API data type.
200 | *
201 | * @param dataType the swagger API parameter data type
202 | * @param paramValue the value of the parameter as string
203 | * @return the argument
204 | */
205 | private Object convertParamToArgument(final DataType dataType, final String paramValue) throws ParseException, IOException {
206 | if (StringUtils.isEmpty(paramValue)) {
207 | return null;
208 | } else {
209 | switch (dataType) {
210 | case STRING:
211 | return paramValue;
212 | case INTEGER:
213 | return Integer.parseInt(paramValue);
214 | case LONG:
215 | return Long.parseLong(paramValue);
216 | case BOOLEAN:
217 | return Boolean.valueOf(paramValue);
218 | case DATE:
219 | return new SimpleDateFormat(DATE_FORMAT).parse(paramValue);
220 | case DATETIME:
221 | return new SimpleDateFormat(DATE_TIME_FORMAT).parse(paramValue);
222 | default:
223 | throw new IllegalArgumentException("Handling for data type \"" + dataType.name() + "\" not yet implemented.");
224 | }
225 | }
226 | }
227 |
228 | @Override
229 | protected void doGet(final HttpServletRequest request, final HttpServletResponse response) throws ServletException, IOException {
230 | processRequest(request, response, HTTPMethod.GET);
231 | }
232 |
233 | @Override
234 | protected void doPost(final HttpServletRequest request, final HttpServletResponse response) throws ServletException, IOException {
235 | processRequest(request, response, HTTPMethod.POST);
236 | }
237 |
238 | @Override
239 | protected void doDelete(final HttpServletRequest request, final HttpServletResponse response) throws ServletException, IOException {
240 | processRequest(request, response, HTTPMethod.DELETE);
241 | }
242 |
243 | @Override
244 | protected void doPut(final HttpServletRequest request, final HttpServletResponse response) throws ServletException, IOException {
245 | processRequest(request, response, HTTPMethod.PUT);
246 | }
247 |
248 | @Override
249 | protected void doOptions(final HttpServletRequest request, final HttpServletResponse response) throws ServletException, IOException {
250 | final SwaggerAPIConfig config = (SwaggerAPIConfig) request.getSession().getServletContext().getAttribute(SwaggerAPIConfig.SERVLET_ATTRIBUTE_NAME);
251 |
252 | if (config.isCrossOriginAccessAllowed()) {
253 | response.addHeader("Access-Control-Allow-Origin", "*");
254 | response.addHeader("Access-Control-Allow-Methods", "POST, GET, PUT, DELETE, UPDATE, OPTIONS");
255 | response.addHeader("Access-Control-Allow-Headers", "Content-Type, Accept, X-Requested-With, X-Api-Key");
256 | }
257 |
258 | if (config.getDefaultContentType() != null) {
259 | response.setContentType(config.getDefaultContentType());
260 | }
261 |
262 | response.setStatus(HttpServletResponse.SC_OK);
263 | response.flushBuffer();
264 | }
265 |
266 | @Override
267 | protected void service(final HttpServletRequest request, final HttpServletResponse response) throws ServletException, IOException {
268 | try {
269 | super.service(request, response);
270 | } finally {
271 | // because Swagger REST calls are stateless the session should be invalidated immediately
272 | request.getSession().invalidate();
273 | }
274 | }
275 | }
276 |
--------------------------------------------------------------------------------
/src/main/java/com/roamsys/swagger/annotations/SwaggerApi.java:
--------------------------------------------------------------------------------
1 | package com.roamsys.swagger.annotations;
2 |
3 | import java.lang.annotation.ElementType;
4 | import java.lang.annotation.Retention;
5 | import java.lang.annotation.RetentionPolicy;
6 | import java.lang.annotation.Target;
7 |
8 | /**
9 | * The swagger API annotation
10 | *
11 | * @author johanna
12 | */
13 | @Target ({ElementType.METHOD})
14 | @Retention (RetentionPolicy.RUNTIME)
15 | public @interface SwaggerApi {
16 |
17 | /**
18 | * Enum constants for HTTP methods
19 | */
20 | public enum HTTPMethod {
21 |
22 | GET("get"),
23 | POST("post"),
24 | PUT("put"),
25 | DELETE("delete");
26 |
27 | private final String name;
28 |
29 | private HTTPMethod(final String name) {
30 | this.name = name;
31 | }
32 |
33 | @Override
34 | public String toString() {
35 | return name;
36 | }
37 |
38 | }
39 |
40 | /**
41 | * The HTTP method of the current service
42 | *
43 | * @return HTTP
44 | */
45 | HTTPMethod method() default HTTPMethod.GET;
46 |
47 | /**
48 | * The URL pattern with placeholder for parameters
49 | *
50 | * @return URL as string
51 | */
52 | String path();
53 |
54 | /**
55 | * The summary of the current service
56 | *
57 | * @return summary as string
58 | */
59 | String summary();
60 |
61 | /**
62 | * A verbose explanation of the operation behavior.
63 | *
64 | * @return implementation notes as string
65 | */
66 | String notes();
67 |
68 | /**
69 | * The description for the implementation notes
70 | *
71 | * @return description as string
72 | */
73 | String description() default "";
74 | }
75 |
--------------------------------------------------------------------------------
/src/main/java/com/roamsys/swagger/annotations/SwaggerModel.java:
--------------------------------------------------------------------------------
1 | package com.roamsys.swagger.annotations;
2 |
3 | import java.lang.annotation.ElementType;
4 | import java.lang.annotation.Inherited;
5 | import java.lang.annotation.Retention;
6 | import java.lang.annotation.RetentionPolicy;
7 | import java.lang.annotation.Target;
8 |
9 | /**
10 | * The swagger API model
11 | *
12 | * @author johanna
13 | */
14 | @Target ({ElementType.TYPE})
15 | @Retention (RetentionPolicy.RUNTIME)
16 | @Inherited
17 | public @interface SwaggerModel {
18 |
19 | /**
20 | * The base URL pattern to the model
21 | *
22 | * @return URL as string
23 | */
24 | String path();
25 |
26 | /**
27 | * The description of the model
28 | *
29 | * @return model description as string
30 | */
31 | String description() default "";
32 |
33 | /**
34 | * The result format of the model.
35 | *
36 | * @return result format
37 | */
38 | String format() default "json";
39 |
40 | }
41 |
--------------------------------------------------------------------------------
/src/main/java/com/roamsys/swagger/annotations/SwaggerParameter.java:
--------------------------------------------------------------------------------
1 | package com.roamsys.swagger.annotations;
2 |
3 | import java.lang.annotation.ElementType;
4 | import java.lang.annotation.Retention;
5 | import java.lang.annotation.RetentionPolicy;
6 | import java.lang.annotation.Target;
7 |
8 | /**
9 | * The swagger API parameter annotation
10 | *
11 | * @author johanna
12 | */
13 | @Target ({ElementType.PARAMETER})
14 | @Retention (RetentionPolicy.RUNTIME)
15 | public @interface SwaggerParameter {
16 |
17 | /**
18 | * Enum constants for parameter types
19 | */
20 | public enum ParamType {
21 |
22 | PATH("path"),
23 | QUERY("query"),
24 | BODY("body"),
25 | HEADER("header"),
26 | FORM("form");
27 |
28 | private final String name;
29 |
30 | private ParamType(final String name) {
31 | this.name = name;
32 | }
33 |
34 | @Override
35 | public String toString() {
36 | return name;
37 | }
38 | }
39 |
40 | /**
41 | * Enum constants for parameter date types
42 | */
43 | public enum DataType {
44 | STRING("string"),
45 | INTEGER("integer"),
46 | LONG("long"),
47 | BOOLEAN("boolean"),
48 | DATE("date"),
49 | DATETIME("dateTime");
50 |
51 | private final String name;
52 |
53 | private DataType(final String name) {
54 | this.name = name;
55 | }
56 |
57 | @Override
58 | public String toString() {
59 | return name;
60 | }
61 | }
62 |
63 | /**
64 | * The parameter name as it appears in the URL pattern.
65 | *
66 | * @return name
67 | */
68 | String name();
69 |
70 | /**
71 | * Defines if parameter is mandatory
72 | *
73 | * @return boolean if parameter is required
74 | */
75 | boolean required();
76 |
77 | /**
78 | * Another way to allow multiple values for a "query" parameter. If used, the query parameter may accept comma-separated values.
79 | *
80 | * @return true if multiple allowed
81 | */
82 | boolean allowMultiple() default false;
83 |
84 | /**
85 | * The parameter description.
86 | *
87 | * @return description as string
88 | */
89 | String description();
90 |
91 | /**
92 | * The type of the parameter (that is, the location of the parameter in the request).
93 | *
94 | * @return parameter type (default is PATH)
95 | */
96 | ParamType paramType() default ParamType.PATH;
97 |
98 | /**
99 | * The data type of the parameter
100 | * @return the data type (default is STRING)
101 | */
102 | DataType dataType() default DataType.STRING;
103 | }
104 |
--------------------------------------------------------------------------------
/src/main/java/com/roamsys/swagger/data/ContentType.java:
--------------------------------------------------------------------------------
1 | package com.roamsys.swagger.data;
2 |
3 | /**
4 | * List of MIME content type constants
5 | * @author mbartel
6 | */
7 | public class ContentType {
8 | /**
9 | * The standard content type for JSON UTF-8 encoded responses
10 | */
11 | final public static String JSON_UTF8 = "application/json;charset=UTF-8";
12 | /**
13 | * Content type for plain text UTF-8 encoded
14 | */
15 | final public static String TEXT = "text/plain";
16 | /**
17 | * Content type for RSS feeds
18 | */
19 | final public static String RSS = "application/rss+xml";
20 | /**
21 | * Content type for XML content
22 | */
23 | final public static String XML = "application/xml";
24 | /**
25 | * Content type for PDFs
26 | */
27 | final public static String PDF = "application/pdf";
28 | /**
29 | * Content type for ZIP archives
30 | */
31 | final public static String ZIP = "application/zip";
32 | /**
33 | * Content type for comma separated values
34 | */
35 | final public static String CSV = "text/csv";
36 | /**
37 | * Content type for HTML content
38 | */
39 | final public static String HTML = "text/html";
40 | /**
41 | * Content type for cascading style sheets
42 | */
43 | final public static String CSS = "text/css";
44 | }
45 |
--------------------------------------------------------------------------------
/src/main/java/com/roamsys/swagger/data/SwaggerAPIContext.java:
--------------------------------------------------------------------------------
1 | package com.roamsys.swagger.data;
2 |
3 | import jakarta.servlet.http.HttpServlet;
4 | import jakarta.servlet.http.HttpServletRequest;
5 | import jakarta.servlet.http.HttpServletResponse;
6 |
7 | /**
8 | * Context for an swagger API call.
9 | *
10 | * @author mbartel
11 | */
12 | public class SwaggerAPIContext {
13 |
14 | /**
15 | * The Swagger API request
16 | */
17 | private final HttpServletRequest request;
18 | /**
19 | * The Swagger API response
20 | */
21 | private final HttpServletResponse response;
22 | /**
23 | * The Swagger API servlet
24 | */
25 | private final HttpServlet servlet;
26 | /**
27 | * The Swagger API exception handler
28 | */
29 | private final SwaggerExceptionHandler exceptionHandler;
30 |
31 | /**
32 | * Create a new Swagger API request context.
33 | *
34 | * @param servlet the servlet
35 | * @param request the request
36 | * @param response the response
37 | */
38 | public SwaggerAPIContext(final HttpServlet servlet, final HttpServletRequest request, final HttpServletResponse response, final SwaggerExceptionHandler exceptionHandler) {
39 | this.servlet = servlet;
40 | this.request = request;
41 | this.response = response;
42 | this.exceptionHandler = exceptionHandler;
43 | }
44 |
45 | /**
46 | * The HTTP request.
47 | *
48 | * @return the HTTP request
49 | */
50 | public HttpServletRequest getRequest() {
51 | return request;
52 | }
53 |
54 | /**
55 | * The HTTP response.
56 | *
57 | * @return the HTTP response
58 | */
59 | public HttpServletResponse getResponse() {
60 | return response;
61 | }
62 |
63 | /**
64 | * The HTTP servlet.
65 | *
66 | * @return the servlet
67 | */
68 | public HttpServlet getServlet() {
69 | return servlet;
70 | }
71 |
72 | /**
73 | * Gets the exception handler.
74 | *
75 | * @return the exception handler
76 | */
77 | public SwaggerExceptionHandler getExceptionHandler() {
78 | return exceptionHandler;
79 | }
80 |
81 |
82 | }
83 |
--------------------------------------------------------------------------------
/src/main/java/com/roamsys/swagger/data/SwaggerAPIModelData.java:
--------------------------------------------------------------------------------
1 | package com.roamsys.swagger.data;
2 |
3 | import com.roamsys.swagger.SwaggerAPIModel;
4 | import com.roamsys.swagger.annotations.SwaggerApi.HTTPMethod;
5 | import java.lang.reflect.Method;
6 | import java.util.List;
7 | import java.util.regex.Matcher;
8 | import java.util.regex.Pattern;
9 |
10 | /**
11 | * Holds the data for a swagger API
12 | *
13 | * @author johanna
14 | */
15 | public class SwaggerAPIModelData {
16 |
17 | /**
18 | * Instance of the swagger API model
19 | */
20 | private final SwaggerAPIModel modelClass;
21 | /**
22 | * The reflection method
23 | */
24 | private final Method method;
25 | /**
26 | * The path pattern
27 | */
28 | private final Pattern pathPattern;
29 |
30 | /**
31 | * HTTP method type
32 | */
33 | private final HTTPMethod httpMethod;
34 |
35 | /**
36 | * Parameter data
37 | */
38 | private final List parameters;
39 |
40 | /**
41 | * The standard parameter pattern
42 | */
43 | private static final String PATTERN = "\\{[0-9a-zA-Z]+\\}";
44 |
45 | /**
46 | * Creates a new wrapper for swagger API
47 | *
48 | * @param modelClass the class of the API model
49 | * @param method the method
50 | * @param httpMethod the HTTP method
51 | * @param path the entire path of the API method
52 | * @param parameters the detailed data for the method parameters
53 | */
54 | public SwaggerAPIModelData(final SwaggerAPIModel modelClass, final Method method, final HTTPMethod httpMethod, final String path, final List parameters) {
55 | this.modelClass = modelClass;
56 | this.method = method;
57 | this.httpMethod = httpMethod;
58 | this.pathPattern = Pattern.compile(path.replaceAll(PATTERN, "(\\[^/\\]+)"));
59 | this.parameters = parameters;
60 | }
61 |
62 | /**
63 | * Returns the matcher for the given path with the current swagger API path
64 | *
65 | * @param path full swagger API path as string
66 | * @return matcher
67 | */
68 | public Matcher matchPath(final String path) {
69 | return this.pathPattern.matcher(path);
70 | }
71 |
72 | /**
73 | * Returns the HTTP method of current swagger API
74 | *
75 | * @return HTTP method
76 | */
77 | public HTTPMethod getHTTPMethod() {
78 | return httpMethod;
79 | }
80 |
81 | /**
82 | * Returns the reflection method of current swagger API
83 | *
84 | * @return reflection method
85 | */
86 | public Method getMethod() {
87 | return method;
88 | }
89 |
90 | /**
91 | * Returns the class of the parent swagger API model
92 | *
93 | * @return instance of swagger API model
94 | */
95 | public SwaggerAPIModel getAPIModelClass() {
96 | return modelClass;
97 | }
98 |
99 | /**
100 | * Returns detailed data about parameters
101 | *
102 | * @return parameter details
103 | */
104 | public List getParameters() {
105 | return parameters;
106 | }
107 |
108 | }
109 |
--------------------------------------------------------------------------------
/src/main/java/com/roamsys/swagger/data/SwaggerAPIParameterData.java:
--------------------------------------------------------------------------------
1 | package com.roamsys.swagger.data;
2 |
3 | import com.roamsys.swagger.annotations.SwaggerParameter.DataType;
4 | import com.roamsys.swagger.annotations.SwaggerParameter.ParamType;
5 |
6 | /**
7 | * Data class for method parameters
8 | * @author mbartel
9 | */
10 | public class SwaggerAPIParameterData {
11 |
12 | /**
13 | * The data type of the parameter
14 | */
15 | private final DataType dataType;
16 |
17 | /**
18 | * The HTTP parameter type of the parameter
19 | */
20 | private final ParamType paramType;
21 |
22 | /**
23 | * The parameters name
24 | */
25 | private final String name;
26 |
27 | /**
28 | * Creates a new data object for method parameter data
29 | * @param name the name of the parameter
30 | * @param paramType the type of parameter
31 | * @param dataType the data type of the parameter
32 | */
33 | public SwaggerAPIParameterData(final String name, final ParamType paramType, final DataType dataType) {
34 | this.name = name;
35 | this.paramType = paramType;
36 | this.dataType = dataType;
37 | }
38 |
39 | /**
40 | * Returns the data type of the parameter
41 | * @return the data type
42 | */
43 | public DataType getDataType() {
44 | return dataType;
45 | }
46 |
47 | /**
48 | * Returns the type of parameter
49 | * @return the parameter type
50 | */
51 | public ParamType getParamType() {
52 | return paramType;
53 | }
54 |
55 | /**
56 | * Returns the name of the parameter
57 | * @return the parameters name
58 | */
59 | public String getName() {
60 | return name;
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/src/main/java/com/roamsys/swagger/data/SwaggerExceptionHandler.java:
--------------------------------------------------------------------------------
1 | package com.roamsys.swagger.data;
2 |
3 | import com.google.gson.GsonBuilder;
4 | import java.io.IOException;
5 | import java.io.PrintWriter;
6 | import java.util.Map;
7 | import jakarta.servlet.http.HttpServletResponse;
8 |
9 | /**
10 | * Handle exceptions thrown while invoking the Swagger API method.
11 | *
12 | * @author mbartel
13 | */
14 | public interface SwaggerExceptionHandler {
15 |
16 | /**
17 | * Default implementation that uses {@link System#err}, sets the status and returns error JSON.
18 | */
19 | public static final SwaggerExceptionHandler DEFAULT = new SwaggerExceptionHandler() {
20 |
21 | @Override
22 | public void handleException(final HttpServletResponse response, final int code, final String message, final Throwable ex) {
23 | System.err.println("Error: " + message + ", status code: " + code);
24 | if (ex != null) {
25 | ex.printStackTrace(System.err);
26 | }
27 | response.setStatus(code);
28 | // write error JSON
29 | try {
30 | final PrintWriter writer = response.getWriter();
31 | response.setContentType(ContentType.JSON_UTF8);
32 | new GsonBuilder().create().toJson(Map.of("code", code, "reason", message), writer);
33 | writer.flush();
34 | } catch (final IOException nested) {
35 | nested.printStackTrace(System.err);
36 | }
37 | }
38 | };
39 |
40 |
41 | /**
42 | * Handles an exception.
43 | *
44 | * @param response the response
45 | * @param code the HTTP response code
46 | * @param message the exception message
47 | * @param ex the exception
48 | */
49 | public void handleException(final HttpServletResponse response, final int code, final String message, final Throwable ex);
50 |
51 | }
52 |
--------------------------------------------------------------------------------
/src/main/java/com/roamsys/swagger/documentation/AbstractApiSpecPart.java:
--------------------------------------------------------------------------------
1 | package com.roamsys.swagger.documentation;
2 |
3 | import com.google.gson.annotations.Expose;
4 |
5 | /**
6 | * Base class of an OpenAPI specification block.
7 | * Every block should have at least a description property.
8 | *
9 | * @author nbecker
10 | */
11 | public class AbstractApiSpecPart {
12 |
13 | @Expose
14 | String description;
15 | }
16 |
--------------------------------------------------------------------------------
/src/main/java/com/roamsys/swagger/documentation/ApiSpecBuilder.java:
--------------------------------------------------------------------------------
1 | package com.roamsys.swagger.documentation;
2 |
3 | import com.roamsys.swagger.annotations.SwaggerApi;
4 | import com.roamsys.swagger.annotations.SwaggerModel;
5 | import com.roamsys.swagger.annotations.SwaggerParameter;
6 | import java.util.ArrayList;
7 | import java.util.Collections;
8 | import java.util.HashMap;
9 | import java.util.List;
10 | import java.util.Map;
11 | import java.util.TreeMap;
12 |
13 | /**
14 | * Builder for creating OpenAPI Swagger documentation/specification based on metadata/code annotations.
15 | *
16 | * @author nbecker
17 | */
18 | public class ApiSpecBuilder {
19 |
20 | /**
21 | * The currently used OpenAPI version: swagger: "2.0"
22 | */
23 | private static final String SWAGGER_VERSION = "2.0";
24 |
25 | // defaults for several structures that are declared static in context of this framework
26 | private static final String DEFAULT_TITLE = "Swagger API";
27 | private static final List DEFAULT_SCHEMES = Collections.singletonList("https");
28 | private static final Map> DEFAULT_SECURITY_DEFINITION = Collections.singletonMap("api_key", Map.of("type", "apiKey", "name", "x-api-key", "in", "header"));
29 | private static final List