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