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