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