467fd8642a10aa8acb4cd60e6ed84a5dbdc1c2d7
[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 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 java.util.Map;
27 import org.apache.commons.lang3.StringUtils;
28 import org.eclipse.jetty.servlet.ServletHolder;
29 import org.glassfish.jersey.server.ServerProperties;
30 import org.onap.policy.common.endpoints.http.server.JsonExceptionMapper;
31 import org.onap.policy.common.gson.GsonMessageBodyHandler;
32 import org.onap.policy.common.utils.network.NetworkUtil;
33 import org.slf4j.Logger;
34 import org.slf4j.LoggerFactory;
35
36 /**
37  * REST Jetty Server that uses Jersey Servlets to support JAX-RS Web Services.
38  *
39  * <p>Note: the serialization provider will always be added to the server's class providers, as will the swagger
40  * providers (assuming swagger has been enabled). This happens whether {@link #addServletClass(String, String)} is used
41  * or {@link #addServletPackage(String, String)} is used. Thus it's possible to have both the server's class provider
42  * property and the server's package provider property populated.
43  */
44 public class JettyJerseyServer extends JettyServletServer {
45
46     /**
47      * Swagger API Base Path.
48      */
49     protected static final String SWAGGER_API_BASEPATH = "swagger.api.basepath";
50
51     /**
52      * Swagger Context ID.
53      */
54     protected static final String SWAGGER_CONTEXT_ID = "swagger.context.id";
55
56     /**
57      * Swagger Scanner ID.
58      */
59     protected static final String SWAGGER_SCANNER_ID = "swagger.scanner.id";
60
61     /**
62      * Swagger Pretty Print.
63      */
64     protected static final String SWAGGER_PRETTY_PRINT = "swagger.pretty.print";
65
66     /**
67      * Jersey GSON Classes Init Param Value.
68      */
69     protected static final String JERSEY_GSON_INIT_CLASSNAMES_PARAM_VALUE =
70                     String.join(",", GsonMessageBodyHandler.class.getName(), JsonExceptionMapper.class.getName());
71
72     /**
73      * Jersey Swagger Classes Init Param Value.
74      */
75     protected static final String SWAGGER_INIT_CLASSNAMES_PARAM_VALUE =
76             "io.swagger.jaxrs.listing.ApiListingResource," + "io.swagger.jaxrs.listing.SwaggerSerializers";
77
78     /**
79      * Logger.
80      */
81     protected static Logger logger = LoggerFactory.getLogger(JettyJerseyServer.class);
82
83     /**
84      * Container for servlets.
85      */
86     protected final Map<String, ServletHolder> servlets = new HashMap<>();
87
88     /**
89      * Swagger ID.
90      */
91     protected String swaggerId = null;
92
93     /**
94      * The serialization provider to be used when classes are added to the service.
95      */
96     private String classProvider = JERSEY_GSON_INIT_CLASSNAMES_PARAM_VALUE;
97
98     /**
99      * Constructor.
100      *
101      * @param name name
102      * @param https enable https?
103      * @param host host server host
104      * @param port port server port
105      * @param swagger support swagger?
106      * @param contextPath context path
107      *
108      * @throws IllegalArgumentException in invalid arguments are provided
109      */
110     public JettyJerseyServer(String name, boolean https, String host, int port, String contextPath, boolean swagger) {
111
112         super(name, https, host, port, contextPath);
113         if (swagger) {
114             this.swaggerId = "swagger-" + this.port;
115             attachSwaggerServlet(https);
116         }
117     }
118
119     /**
120      * Attaches a swagger initialization servlet.
121      */
122     protected void attachSwaggerServlet(boolean https) {
123
124         ServletHolder swaggerServlet = context.addServlet(JerseyJaxrsConfig.class, "/");
125
126         String hostname = this.connector.getHost();
127         if (StringUtils.isBlank(hostname) || hostname.equals(NetworkUtil.IPV4_WILDCARD_ADDRESS)) {
128             hostname = NetworkUtil.getHostname();
129         }
130
131         swaggerServlet.setInitParameter(SWAGGER_API_BASEPATH,
132                 ((https) ? "https://" : "http://") + hostname + ":" + this.connector.getPort() + "/");
133         swaggerServlet.setInitParameter(SWAGGER_CONTEXT_ID, swaggerId);
134         swaggerServlet.setInitParameter(SWAGGER_SCANNER_ID, swaggerId);
135         swaggerServlet.setInitParameter(SWAGGER_PRETTY_PRINT, "true");
136         swaggerServlet.setInitOrder(2);
137
138         if (logger.isDebugEnabled()) {
139             logger.debug("{}: Swagger Servlet has been attached: {}", this, swaggerServlet.dump());
140         }
141     }
142
143     /**
144      * Retrieves cached server based on servlet path.
145      *
146      * @param servletPath servlet path
147      * @return the jetty servlet holder
148      *
149      * @throws IllegalArgumentException if invalid arguments are provided
150      */
151     protected synchronized ServletHolder getServlet(String servletPath) {
152
153         return servlets.computeIfAbsent(servletPath, key -> {
154
155             ServletHolder jerseyServlet =
156                     context.addServlet(org.glassfish.jersey.servlet.ServletContainer.class, servletPath);
157             jerseyServlet.setInitOrder(0);
158
159             return jerseyServlet;
160         });
161     }
162
163     @Override
164     public synchronized void addServletPackage(String servletPath, String restPackage) {
165         String servPath = servletPath;
166         if (StringUtils.isBlank(restPackage)) {
167             throw new IllegalArgumentException("No discoverable REST package provided");
168         }
169
170         if (servPath == null || servPath.isEmpty()) {
171             servPath = "/*";
172         }
173
174         ServletHolder jerseyServlet = this.getServlet(servPath);
175
176         initStandardParams(jerseyServlet);
177
178         String initPackages = jerseyServlet.getInitParameter(ServerProperties.PROVIDER_PACKAGES);
179         if (initPackages == null) {
180             initPackages = restPackage;
181
182         } else {
183             initPackages += "," + restPackage;
184         }
185
186         jerseyServlet.setInitParameter(ServerProperties.PROVIDER_PACKAGES, initPackages);
187
188         if (logger.isDebugEnabled()) {
189             logger.debug("{}: added REST package: {}", this, jerseyServlet.dump());
190         }
191     }
192
193     @Override
194     public synchronized void addServletClass(String servletPath, String restClass) {
195
196         if (StringUtils.isBlank(restClass)) {
197             throw new IllegalArgumentException("No discoverable REST class provided");
198         }
199
200         if (servletPath == null || servletPath.isEmpty()) {
201             servletPath = "/*";
202         }
203
204         ServletHolder jerseyServlet = this.getServlet(servletPath);
205
206         initStandardParams(jerseyServlet);
207
208         String initClasses = jerseyServlet.getInitParameter(ServerProperties.PROVIDER_CLASSNAMES);
209         if (initClasses == null) {
210             initClasses = restClass;
211
212         } else {
213             initClasses += "," + restClass;
214         }
215
216         jerseyServlet.setInitParameter(ServerProperties.PROVIDER_CLASSNAMES, initClasses);
217
218         if (logger.isDebugEnabled()) {
219             logger.debug("{}: added REST class: {}", this, jerseyServlet.dump());
220         }
221     }
222
223     /**
224      * Adds "standard" parameters to the initParameter set. Sets swagger parameters, if specified, and sets the class
225      * provider property. This can be invoked multiple times, but only the first actually causes any changes to the
226      * parameter set.
227      *
228      * @param jerseyServlet servlet into which parameters should be added
229      */
230     private void initStandardParams(ServletHolder jerseyServlet) {
231         String initClasses = jerseyServlet.getInitParameter(ServerProperties.PROVIDER_CLASSNAMES);
232         if (initClasses != null) {
233             return;
234         }
235
236         initClasses = classProvider;
237
238         if (this.swaggerId != null) {
239             initClasses += "," + SWAGGER_INIT_CLASSNAMES_PARAM_VALUE;
240
241             jerseyServlet.setInitParameter(SWAGGER_CONTEXT_ID, swaggerId);
242             jerseyServlet.setInitParameter(SWAGGER_SCANNER_ID, swaggerId);
243         }
244
245         jerseyServlet.setInitParameter(ServerProperties.PROVIDER_CLASSNAMES, initClasses);
246
247         jerseyServlet.setInitParameter(ServerProperties.METAINF_SERVICES_LOOKUP_DISABLE, "true");
248     }
249
250     /**
251      * Note: this must be invoked <i>before</i> {@link #addServletClass(String, String)} or
252      * {@link #addServletPackage(String, String)}.
253      */
254     @Override
255     public void setSerializationProvider(String provider) {
256         classProvider = provider;
257     }
258
259     @Override
260     public String toString() {
261         return "JettyJerseyServer [Jerseyservlets=" + servlets
262             + ", swaggerId=" + swaggerId
263             + ", toString()=" + super.toString()
264             + "]";
265     }
266 }