42ef1c9e6ce2538ff650f76aafb41aaa4d079ab4
[policy/common.git] /
1 /*-
2  * ============LICENSE_START=======================================================
3  * policy-endpoints
4  * ================================================================================
5  * Copyright (C) 2017-2021 AT&T Intellectual Property. All rights reserved.
6  * Modifications Copyright (C) 2019-2020,2023 Nordix Foundation.
7  * Modifications Copyright (C) 2021 Bell Canada. All rights reserved.
8  * ================================================================================
9  * Licensed under the Apache License, Version 2.0 (the "License");
10  * you may not use this file except in compliance with the License.
11  * You may obtain a copy of the License at
12  *
13  *      http://www.apache.org/licenses/LICENSE-2.0
14  *
15  * Unless required by applicable law or agreed to in writing, software
16  * distributed under the License is distributed on an "AS IS" BASIS,
17  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18  * See the License for the specific language governing permissions and
19  * limitations under the License.
20  * ============LICENSE_END=========================================================
21  */
22
23 package org.onap.policy.common.endpoints.http.server.internal;
24
25 import io.swagger.jersey.config.JerseyJaxrsConfig;
26 import org.apache.commons.lang3.StringUtils;
27 import org.eclipse.jetty.servlet.ServletHolder;
28 import org.glassfish.jersey.server.ServerProperties;
29 import org.onap.policy.common.endpoints.http.server.JsonExceptionMapper;
30 import org.onap.policy.common.gson.GsonMessageBodyHandler;
31 import org.onap.policy.common.utils.network.NetworkUtil;
32 import org.slf4j.Logger;
33 import org.slf4j.LoggerFactory;
34
35 /**
36  * REST Jetty Server that uses Jersey Servlets to support JAX-RS Web Services.
37  *
38  * <p>Note: the serialization provider will always be added to the server's class providers, as will the swagger
39  * providers (assuming swagger has been enabled). This happens whether {@link #addServletClass(String, String)} is used
40  * or {@link #addServletPackage(String, String)} is used. Thus it's possible to have both the server's class provider
41  * property and the server's package provider property populated.
42  */
43 public class JettyJerseyServer extends JettyServletServer {
44
45     /**
46      * Swagger API Base Path.
47      */
48     protected static final String SWAGGER_API_BASEPATH = "swagger.api.basepath";
49
50     /**
51      * Swagger Context ID.
52      */
53     protected static final String SWAGGER_CONTEXT_ID = "swagger.context.id";
54
55     /**
56      * Swagger Scanner ID.
57      */
58     protected static final String SWAGGER_SCANNER_ID = "swagger.scanner.id";
59
60     /**
61      * Swagger Pretty Print.
62      */
63     protected static final String SWAGGER_PRETTY_PRINT = "swagger.pretty.print";
64
65     /**
66      * Jersey GSON Classes Init Param Value.
67      */
68     protected static final String JERSEY_GSON_INIT_CLASSNAMES_PARAM_VALUE =
69         String.join(",", GsonMessageBodyHandler.class.getName(), JsonExceptionMapper.class.getName());
70
71     /**
72      * Jersey Swagger Classes Init Param Value.
73      */
74     protected static final String SWAGGER_INIT_CLASSNAMES_PARAM_VALUE =
75         "io.swagger.jaxrs.listing.ApiListingResource," + "io.swagger.jaxrs.listing.SwaggerSerializers";
76
77     /**
78      * Logger.
79      */
80     protected static Logger logger = LoggerFactory.getLogger(JettyJerseyServer.class);
81
82     /**
83      * Swagger ID.
84      */
85     protected String swaggerId = null;
86
87     /**
88      * The serialization provider to be used when classes are added to the service.
89      */
90     private String classProvider = JERSEY_GSON_INIT_CLASSNAMES_PARAM_VALUE;
91
92     /**
93      * Constructor.
94      *
95      * @param name name
96      * @param https enable https?
97      * @param host host server host
98      * @param port port server port
99      * @param sniHostCheck SNI Host checking flag
100      * @param swagger support swagger?
101      * @param contextPath context path
102      * @throws IllegalArgumentException in invalid arguments are provided
103      */
104     public JettyJerseyServer(String name, boolean https, String host, int port, boolean sniHostCheck,
105         String contextPath, boolean swagger) {
106
107         super(name, https, host, port, sniHostCheck, contextPath);
108         if (swagger) {
109             this.swaggerId = "swagger-" + this.port;
110             attachSwaggerServlet(https);
111         }
112     }
113
114     /**
115      * Attaches a swagger initialization servlet.
116      */
117     protected void attachSwaggerServlet(boolean https) {
118
119         ServletHolder swaggerServlet = getServlet(JerseyJaxrsConfig.class, "/");
120
121         String hostname = this.connector.getHost();
122         if (StringUtils.isBlank(hostname) || hostname.equals(NetworkUtil.IPV4_WILDCARD_ADDRESS)) {
123             hostname = NetworkUtil.getHostname();
124         }
125
126         swaggerServlet.setInitParameter(SWAGGER_API_BASEPATH,
127             ((https) ? "https://" : "http://") + hostname + ":" + this.connector.getPort() + "/");
128         swaggerServlet.setInitParameter(SWAGGER_CONTEXT_ID, swaggerId);
129         swaggerServlet.setInitParameter(SWAGGER_SCANNER_ID, swaggerId);
130         swaggerServlet.setInitParameter(SWAGGER_PRETTY_PRINT, "true");
131         swaggerServlet.setInitOrder(2);
132
133         if (logger.isDebugEnabled()) {
134             logger.debug("{}: Swagger Servlet has been attached: {}", this, swaggerServlet.dump());
135         }
136     }
137
138     /**
139      * Retrieves cached server based on servlet path.
140      *
141      * @param servletPath servlet path
142      * @return the jetty servlet holder
143      *
144      * @throws IllegalArgumentException if invalid arguments are provided
145      */
146     protected synchronized ServletHolder getServlet(String servletPath) {
147         ServletHolder jerseyServlet =
148             super.getServlet(org.glassfish.jersey.servlet.ServletContainer.class, servletPath);
149         jerseyServlet.setInitOrder(0);
150         return jerseyServlet;
151     }
152
153     @Override
154     public synchronized void addServletPackage(String servletPath, String restPackage) {
155         String servPath = servletPath;
156         if (StringUtils.isBlank(restPackage)) {
157             throw new IllegalArgumentException("No discoverable REST package provided");
158         }
159
160         if (servPath == null || servPath.isEmpty()) {
161             servPath = "/*";
162         }
163
164         ServletHolder jerseyServlet = this.getServlet(servPath);
165
166         initStandardParams(jerseyServlet);
167
168         String initPackages = jerseyServlet.getInitParameter(ServerProperties.PROVIDER_PACKAGES);
169         if (initPackages == null) {
170             initPackages = restPackage;
171
172         } else {
173             initPackages += "," + restPackage;
174         }
175
176         jerseyServlet.setInitParameter(ServerProperties.PROVIDER_PACKAGES, initPackages);
177
178         if (logger.isDebugEnabled()) {
179             logger.debug("{}: added REST package: {}", this, jerseyServlet.dump());
180         }
181     }
182
183     @Override
184     public synchronized void addServletClass(String servletPath, String restClass) {
185
186         if (StringUtils.isBlank(restClass)) {
187             throw new IllegalArgumentException("No discoverable REST class provided");
188         }
189
190         if (servletPath == null || servletPath.isEmpty()) {
191             servletPath = "/*";
192         }
193
194         ServletHolder jerseyServlet = this.getServlet(servletPath);
195
196         initStandardParams(jerseyServlet);
197
198         String initClasses = jerseyServlet.getInitParameter(ServerProperties.PROVIDER_CLASSNAMES);
199         if (initClasses == null) {
200             initClasses = restClass;
201
202         } else {
203             initClasses += "," + restClass;
204         }
205
206         jerseyServlet.setInitParameter(ServerProperties.PROVIDER_CLASSNAMES, initClasses);
207
208         if (logger.isDebugEnabled()) {
209             logger.debug("{}: added REST class: {}", this, jerseyServlet.dump());
210         }
211     }
212
213     /**
214      * Adds "standard" parameters to the initParameter set. Sets swagger parameters, if specified, and sets the class
215      * provider property. This can be invoked multiple times, but only the first actually causes any changes to the
216      * parameter set.
217      *
218      * @param jerseyServlet servlet into which parameters should be added
219      */
220     private void initStandardParams(ServletHolder jerseyServlet) {
221         String initClasses = jerseyServlet.getInitParameter(ServerProperties.PROVIDER_CLASSNAMES);
222         if (initClasses != null) {
223             return;
224         }
225
226         initClasses = classProvider;
227
228         if (this.swaggerId != null) {
229             initClasses += "," + SWAGGER_INIT_CLASSNAMES_PARAM_VALUE;
230
231             jerseyServlet.setInitParameter(SWAGGER_CONTEXT_ID, swaggerId);
232             jerseyServlet.setInitParameter(SWAGGER_SCANNER_ID, swaggerId);
233         }
234
235         jerseyServlet.setInitParameter(ServerProperties.PROVIDER_CLASSNAMES, initClasses);
236
237         jerseyServlet.setInitParameter(ServerProperties.METAINF_SERVICES_LOOKUP_DISABLE, "true");
238     }
239
240     /**
241      * Note: this must be invoked <i>before</i> {@link #addServletClass(String, String)} or
242      * {@link #addServletPackage(String, String)}.
243      */
244     @Override
245     public void setSerializationProvider(String provider) {
246         classProvider = provider;
247     }
248
249     @Override
250     public String toString() {
251         return "JettyJerseyServer [Jerseyservlets=" + servlets
252             + ", swaggerId=" + swaggerId
253             + ", toString()=" + super.toString()
254             + "]";
255     }
256 }