22fc9ac3c5b49e39958b4a561457ad711cbed91f
[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  * <p>Also note: the serialization provider will be ignored if the maven artifact,
41  * <i>jersey-media-json-jackson</i>, is included, regardless of whether it's included
42  * directly or indirectly.
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 Jackson Classes Init Param Value.
68      */
69     protected static final String JERSEY_JACKSON_INIT_CLASSNAMES_PARAM_VALUE =
70             "com.fasterxml.jackson.jaxrs.json.JacksonJaxbJsonProvider";
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      * Logger.
79      */
80     protected static Logger logger = LoggerFactory.getLogger(JettyJerseyServer.class);
81
82     /**
83      * Container for servlets.
84      */
85     protected HashMap<String, ServletHolder> servlets = new HashMap<>();
86
87     /**
88      * Swagger ID.
89      */
90     protected String swaggerId = null;
91
92     /**
93      * The serialization provider to be used when classes are added to the service.
94      */
95     private String classProvider = JERSEY_JACKSON_INIT_CLASSNAMES_PARAM_VALUE;
96
97     /**
98      * Constructor.
99      *
100      * @param name name
101      * @param https enable https?
102      * @param host host server host
103      * @param port port server port
104      * @param swagger support swagger?
105      * @param contextPath context path
106      *
107      * @throws IllegalArgumentException in invalid arguments are provided
108      */
109     public JettyJerseyServer(String name, boolean https, String host, int port, String contextPath, boolean swagger) {
110
111         super(name, https, host, port, contextPath);
112         if (swagger) {
113             this.swaggerId = "swagger-" + this.port;
114             attachSwaggerServlet(https);
115         }
116     }
117
118     /**
119      * Attaches a swagger initialization servlet.
120      */
121     protected void attachSwaggerServlet(boolean https) {
122
123         ServletHolder swaggerServlet = context.addServlet(JerseyJaxrsConfig.class, "/");
124
125         String hostname = this.connector.getHost();
126         if (hostname == null || hostname.isEmpty() || hostname.equals(NetworkUtil.IPv4_WILDCARD_ADDRESS)) {
127             hostname = NetworkUtil.getHostname();
128         }
129
130         swaggerServlet.setInitParameter(SWAGGER_API_BASEPATH,
131                 ((https) ? "https://" : "http://") + hostname + ":" + this.connector.getPort() + "/");
132         swaggerServlet.setInitParameter(SWAGGER_CONTEXT_ID, swaggerId);
133         swaggerServlet.setInitParameter(SWAGGER_SCANNER_ID, swaggerId);
134         swaggerServlet.setInitParameter(SWAGGER_PRETTY_PRINT, "true");
135         swaggerServlet.setInitOrder(2);
136
137         if (logger.isDebugEnabled()) {
138             logger.debug("{}: Swagger Servlet has been attached: {}", this, swaggerServlet.dump());
139         }
140     }
141
142     /**
143      * Retrieves cached server based on servlet path.
144      *
145      * @param servletPath servlet path
146      * @return the jetty servlet holder
147      *
148      * @throws IllegalArgumentException if invalid arguments are provided
149      */
150     protected synchronized ServletHolder getServlet(String servletPath) {
151
152         ServletHolder jerseyServlet = servlets.get(servletPath);
153         if (jerseyServlet == null) {
154             jerseyServlet = context.addServlet(org.glassfish.jersey.servlet.ServletContainer.class, servletPath);
155             jerseyServlet.setInitOrder(0);
156             servlets.put(servletPath, jerseyServlet);
157         }
158
159         return jerseyServlet;
160     }
161
162     @Override
163     public synchronized void addServletPackage(String servletPath, String restPackage) {
164         String servPath = servletPath;
165         if (restPackage == null || restPackage.isEmpty()) {
166             throw new IllegalArgumentException("No discoverable REST package provided");
167         }
168
169         if (servPath == null || servPath.isEmpty()) {
170             servPath = "/*";
171         }
172
173         ServletHolder jerseyServlet = this.getServlet(servPath);
174
175         initStandardParams(jerseyServlet);
176
177         String initPackages = jerseyServlet.getInitParameter(ServerProperties.PROVIDER_PACKAGES);
178         if (initPackages == null) {
179             initPackages = restPackage;
180
181         } else {
182             initPackages += "," + restPackage;
183         }
184
185         jerseyServlet.setInitParameter(ServerProperties.PROVIDER_PACKAGES, initPackages);
186
187         if (logger.isDebugEnabled()) {
188             logger.debug("{}: added REST package: {}", this, jerseyServlet.dump());
189         }
190     }
191
192     @Override
193     public synchronized void addServletClass(String servletPath, String restClass) {
194
195         if (restClass == null || restClass.isEmpty()) {
196             throw new IllegalArgumentException("No discoverable REST class provided");
197         }
198
199         if (servletPath == null || servletPath.isEmpty()) {
200             servletPath = "/*";
201         }
202
203         ServletHolder jerseyServlet = this.getServlet(servletPath);
204
205         initStandardParams(jerseyServlet);
206
207         String initClasses = jerseyServlet.getInitParameter(ServerProperties.PROVIDER_CLASSNAMES);
208         if (initClasses == null) {
209             initClasses = restClass;
210
211         } else {
212             initClasses += "," + restClass;
213         }
214
215         jerseyServlet.setInitParameter(ServerProperties.PROVIDER_CLASSNAMES, initClasses);
216
217         if (logger.isDebugEnabled()) {
218             logger.debug("{}: added REST class: {}", this, jerseyServlet.dump());
219         }
220     }
221
222     /**
223      * Adds "standard" parameters to the initParameter set. Sets swagger parameters, if
224      * specified, and sets the class provider property. This can be invoked multiple
225      * times, but only the first actually causes any changes to the parameter set.
226      *
227      * @param jerseyServlet servlet into which parameters should be added
228      */
229     private void initStandardParams(ServletHolder jerseyServlet) {
230         String initClasses = jerseyServlet.getInitParameter(ServerProperties.PROVIDER_CLASSNAMES);
231         if (initClasses != null) {
232             return;
233         }
234
235         initClasses = classProvider;
236
237         if (this.swaggerId != null) {
238             initClasses += "," + SWAGGER_INIT_CLASSNAMES_PARAM_VALUE;
239
240             jerseyServlet.setInitParameter(SWAGGER_CONTEXT_ID, swaggerId);
241             jerseyServlet.setInitParameter(SWAGGER_SCANNER_ID, swaggerId);
242         }
243
244         jerseyServlet.setInitParameter(ServerProperties.PROVIDER_CLASSNAMES, initClasses);
245         
246         jerseyServlet.setInitParameter(ServerProperties.METAINF_SERVICES_LOOKUP_DISABLE, "true");
247     }
248
249     /**
250      * Note: this must be invoked <i>before</i> {@link #addServletClass(String, String)}
251      * or {@link #addServletPackage(String, String)}.
252      */
253     @Override
254     public void setSerializationProvider(String provider) {
255         classProvider = provider;
256     }
257
258     @Override
259     public String toString() {
260         StringBuilder builder = new StringBuilder();
261         builder.append("JettyJerseyServer [servlets=").append(servlets).append(", swaggerId=").append(swaggerId)
262                 .append(", toString()=").append(super.toString()).append("]");
263         return builder.toString();
264     }
265 }