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