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