ea76c590c9238fbac0d865bba8d01f7fa5f54944
[policy/common.git] /
1 /*
2  * ============LICENSE_START=======================================================
3  * ONAP
4  * ================================================================================
5  * Copyright (C) 2017-2021 AT&T Intellectual Property. All rights reserved.
6  * Modifications Copyright (C) 2018 Samsung Electronics Co., Ltd.
7  * Modifications Copyright (C) 2019 Nordix Foundation.
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.client.internal;
24
25 import java.security.KeyManagementException;
26 import java.security.NoSuchAlgorithmException;
27 import java.security.SecureRandom;
28 import java.util.Collections;
29 import java.util.Map;
30 import java.util.Map.Entry;
31 import java.util.concurrent.Future;
32 import java.util.regex.Pattern;
33 import javax.net.ssl.SSLContext;
34 import javax.ws.rs.client.Client;
35 import javax.ws.rs.client.ClientBuilder;
36 import javax.ws.rs.client.Entity;
37 import javax.ws.rs.client.Invocation.Builder;
38 import javax.ws.rs.client.InvocationCallback;
39 import javax.ws.rs.client.WebTarget;
40 import javax.ws.rs.core.Response;
41 import org.apache.commons.lang3.StringUtils;
42 import org.glassfish.jersey.client.ClientProperties;
43 import org.glassfish.jersey.client.authentication.HttpAuthenticationFeature;
44 import org.onap.policy.common.endpoints.event.comm.bus.internal.BusTopicParams;
45 import org.onap.policy.common.endpoints.http.client.HttpClient;
46 import org.onap.policy.common.gson.annotation.GsonJsonIgnore;
47 import org.onap.policy.common.utils.network.NetworkUtil;
48 import org.slf4j.Logger;
49 import org.slf4j.LoggerFactory;
50
51 /**
52  * Http Client implementation using a Jersey Client.
53  */
54 public class JerseyClient implements HttpClient {
55     private static final Pattern COMMA_PAT = Pattern.compile(",");
56
57     /**
58      * Logger.
59      */
60     private static Logger logger = LoggerFactory.getLogger(JerseyClient.class);
61
62     protected static final String JERSEY_DEFAULT_SERIALIZATION_PROVIDER =
63                     "org.onap.policy.common.gson.GsonMessageBodyHandler";
64
65     protected final String name;
66     protected final boolean https;
67     protected final boolean selfSignedCerts;
68     protected final String hostname;
69     protected final int port;
70     protected final String basePath;
71     protected final String userName;
72     protected final String password;
73
74     protected final Client client;
75     protected final String baseUrl;
76
77     protected boolean alive = true;
78
79     /**
80      * Constructor.
81      *
82      * <p>name the name https is it https or not selfSignedCerts are there self signed certs
83      * hostname the hostname port port being used basePath base context userName user
84      * password password
85      *
86      * @param busTopicParams Input parameters object
87      * @throws KeyManagementException key exception
88      * @throws NoSuchAlgorithmException no algorithm exception
89      * @throws ClassNotFoundException if the serialization provider cannot be found
90      */
91     public JerseyClient(BusTopicParams busTopicParams)
92                     throws KeyManagementException, NoSuchAlgorithmException, ClassNotFoundException {
93
94         if (busTopicParams.isClientNameInvalid()) {
95             throw new IllegalArgumentException("Name must be provided");
96         }
97
98         if (busTopicParams.isHostnameInvalid()) {
99             throw new IllegalArgumentException("Hostname must be provided");
100         }
101
102         if (busTopicParams.isPortInvalid()) {
103             throw new IllegalArgumentException("Invalid Port provided: " + busTopicParams.getPort());
104         }
105
106         this.name = busTopicParams.getClientName();
107         this.https = busTopicParams.isUseHttps();
108         this.hostname = busTopicParams.getHostname();
109         this.port = busTopicParams.getPort();
110         this.basePath = busTopicParams.getBasePath();
111         this.userName = busTopicParams.getUserName();
112         this.password = busTopicParams.getPassword();
113         this.selfSignedCerts = busTopicParams.isAllowSelfSignedCerts();
114         this.client = detmClient();
115
116         if (!StringUtils.isBlank(this.userName) && !StringUtils.isBlank(this.password)) {
117             var authFeature = HttpAuthenticationFeature.basic(userName, password);
118             this.client.register(authFeature);
119         }
120
121         this.client.property(ClientProperties.METAINF_SERVICES_LOOKUP_DISABLE, "true");
122
123         registerSerProviders(busTopicParams.getSerializationProvider());
124
125         this.baseUrl = (this.https ? "https://" : "http://") + this.hostname + ":" + this.port + "/"
126                         + (this.basePath == null ? "" : this.basePath);
127     }
128
129     private Client detmClient() throws NoSuchAlgorithmException, KeyManagementException {
130         if (this.https) {
131             ClientBuilder clientBuilder;
132             var sslContext = SSLContext.getInstance("TLSv1.2");
133             if (this.selfSignedCerts) {
134                 sslContext.init(null, NetworkUtil.getAlwaysTrustingManager(), new SecureRandom());
135
136                 // This falls under self signed certs which is used for non-production testing environments where
137                 // the hostname in the cert is unlikely to be crafted properly.  We always return true for the
138                 // hostname verifier.  This causes a sonar vuln but we ignore it as it could cause problems in some
139                 // testing environments.
140                 clientBuilder =
141                         ClientBuilder.newBuilder().sslContext(sslContext).hostnameVerifier(
142                             (host, session) -> true); //NOSONAR
143             } else {
144                 sslContext.init(null, null, null);
145                 clientBuilder = ClientBuilder.newBuilder().sslContext(sslContext);
146             }
147             return clientBuilder.build();
148
149         } else {
150             return ClientBuilder.newClient();
151         }
152     }
153
154     /**
155      * Registers the serialization provider(s) with the client.
156      *
157      * @param serializationProvider comma-separated list of serialization providers
158      * @throws ClassNotFoundException if the serialization provider cannot be found
159      */
160     private void registerSerProviders(String serializationProvider) throws ClassNotFoundException {
161         String providers = (StringUtils.isBlank(serializationProvider)
162                         ? JERSEY_DEFAULT_SERIALIZATION_PROVIDER : serializationProvider);
163         for (String prov : COMMA_PAT.split(providers)) {
164             this.client.register(Class.forName(prov));
165         }
166     }
167
168     @Override
169     public WebTarget getWebTarget() {
170         return this.client.target(this.baseUrl);
171     }
172
173     @Override
174     public Response get(String path) {
175         if (!StringUtils.isBlank(path)) {
176             return getWebTarget().path(path).request().get();
177         } else {
178             return getWebTarget().request().get();
179         }
180     }
181
182     @Override
183     public Response get() {
184         return getWebTarget().request().get();
185     }
186
187     @Override
188     public Future<Response> get(InvocationCallback<Response> callback, String path, Map<String, Object> headers) {
189         Map<String, Object> headers2 = (headers != null ? headers : Collections.emptyMap());
190
191         if (!StringUtils.isBlank(path)) {
192             return getBuilder(path, headers2).async().get(callback);
193         } else {
194             return get(callback, headers2);
195         }
196     }
197
198     @Override
199     public Future<Response> get(InvocationCallback<Response> callback, Map<String, Object> headers) {
200         var builder = getWebTarget().request();
201         if (headers != null) {
202             headers.forEach(builder::header);
203         }
204         return builder.async().get(callback);
205     }
206
207     @Override
208     public Response put(String path, Entity<?> entity, Map<String, Object> headers) {
209         return getBuilder(path, headers).put(entity);
210     }
211
212     @Override
213     public Future<Response> put(InvocationCallback<Response> callback, String path, Entity<?> entity,
214                     Map<String, Object> headers) {
215         return getBuilder(path, headers).async().put(entity, callback);
216     }
217
218     @Override
219     public Response post(String path, Entity<?> entity, Map<String, Object> headers) {
220         return getBuilder(path, headers).post(entity);
221     }
222
223     @Override
224     public Future<Response> post(InvocationCallback<Response> callback, String path, Entity<?> entity,
225                     Map<String, Object> headers) {
226         return getBuilder(path, headers).async().post(entity, callback);
227     }
228
229     @Override
230     public Response delete(String path, Map<String, Object> headers) {
231         return getBuilder(path, headers).delete();
232     }
233
234     @Override
235     public Future<Response> delete(InvocationCallback<Response> callback, String path, Map<String, Object> headers) {
236         return getBuilder(path, headers).async().delete(callback);
237     }
238
239     @Override
240     public boolean start() {
241         return alive;
242     }
243
244     @Override
245     public boolean stop() {
246         return !alive;
247     }
248
249     @Override
250     public void shutdown() {
251         synchronized (this) {
252             alive = false;
253         }
254
255         try {
256             this.client.close();
257         } catch (Exception e) {
258             logger.warn("{}: cannot close because of {}", this, e.getMessage(), e);
259         }
260     }
261
262     @Override
263     public synchronized boolean isAlive() {
264         return this.alive;
265     }
266
267     @Override
268     public String getName() {
269         return name;
270     }
271
272     @Override
273     public boolean isHttps() {
274         return https;
275     }
276
277     @Override
278     public boolean isSelfSignedCerts() {
279         return selfSignedCerts;
280     }
281
282     @Override
283     public String getHostname() {
284         return hostname;
285     }
286
287     @Override
288     public int getPort() {
289         return port;
290     }
291
292     @Override
293     public String getBasePath() {
294         return basePath;
295     }
296
297     @Override
298     public String getUserName() {
299         return userName;
300     }
301
302     @GsonJsonIgnore
303     @Override
304     public String getPassword() {
305         return password;
306     }
307
308     @Override
309     public String getBaseUrl() {
310         return baseUrl;
311     }
312
313     @Override
314     public String toString() {
315         var builder = new StringBuilder();
316         builder.append("JerseyClient [name=");
317         builder.append(name);
318         builder.append(", https=");
319         builder.append(https);
320         builder.append(", selfSignedCerts=");
321         builder.append(selfSignedCerts);
322         builder.append(", hostname=");
323         builder.append(hostname);
324         builder.append(", port=");
325         builder.append(port);
326         builder.append(", basePath=");
327         builder.append(basePath);
328         builder.append(", userName=");
329         builder.append(userName);
330         builder.append(", password=");
331         builder.append(password);
332         builder.append(", client=");
333         builder.append(client);
334         builder.append(", baseUrl=");
335         builder.append(baseUrl);
336         builder.append(", alive=");
337         builder.append(alive);
338         builder.append("]");
339         return builder.toString();
340     }
341
342     private Builder getBuilder(String path, Map<String, Object> headers) {
343         var builder = getWebTarget().path(path).request();
344         for (Entry<String, Object> header : headers.entrySet()) {
345             builder.header(header.getKey(), header.getValue());
346         }
347         return builder;
348     }
349
350
351 }