2 * ============LICENSE_START=======================================================
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, 2023 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
13 * http://www.apache.org/licenses/LICENSE-2.0
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=========================================================
23 package org.onap.policy.common.endpoints.http.client.internal;
25 import com.google.re2j.Pattern;
26 import jakarta.ws.rs.client.Client;
27 import jakarta.ws.rs.client.ClientBuilder;
28 import jakarta.ws.rs.client.Entity;
29 import jakarta.ws.rs.client.Invocation.Builder;
30 import jakarta.ws.rs.client.InvocationCallback;
31 import jakarta.ws.rs.client.WebTarget;
32 import jakarta.ws.rs.core.Response;
33 import java.security.KeyManagementException;
34 import java.security.NoSuchAlgorithmException;
35 import java.security.SecureRandom;
36 import java.util.Collections;
38 import java.util.Map.Entry;
39 import java.util.concurrent.Future;
40 import javax.net.ssl.SSLContext;
42 import lombok.ToString;
43 import org.apache.commons.lang3.StringUtils;
44 import org.glassfish.jersey.client.ClientProperties;
45 import org.glassfish.jersey.client.authentication.HttpAuthenticationFeature;
46 import org.onap.policy.common.endpoints.event.comm.bus.internal.BusTopicParams;
47 import org.onap.policy.common.endpoints.http.client.HttpClient;
48 import org.onap.policy.common.utils.network.NetworkUtil;
49 import org.slf4j.Logger;
50 import org.slf4j.LoggerFactory;
53 * Http Client implementation using a Jersey Client.
57 public class JerseyClient implements HttpClient {
58 private static final Pattern COMMA_PAT = Pattern.compile(",");
63 private static final Logger logger = LoggerFactory.getLogger(JerseyClient.class);
65 protected static final String JERSEY_DEFAULT_SERIALIZATION_PROVIDER =
66 "org.onap.policy.common.gson.GsonMessageBodyHandler";
68 protected final String name;
69 protected final boolean https;
70 protected final boolean selfSignedCerts;
71 protected final String hostname;
72 protected final int port;
73 protected final String basePath;
74 protected final String userName;
75 protected final String password;
77 protected final Client client;
78 protected final String baseUrl;
80 protected boolean alive = true;
86 * https - is it https or not
87 * selfSignedCerts - are there self-signed certs
88 * hostname - the hostname
89 * port - port being used
90 * basePath - base context
91 * userName - user credentials
92 * password - password credentials
94 * @param busTopicParams Input parameters object
95 * @throws KeyManagementException key exception
96 * @throws NoSuchAlgorithmException no algorithm exception
97 * @throws ClassNotFoundException if the serialization provider cannot be found
99 public JerseyClient(BusTopicParams busTopicParams)
100 throws KeyManagementException, NoSuchAlgorithmException, ClassNotFoundException {
102 if (busTopicParams.isClientNameInvalid()) {
103 throw new IllegalArgumentException("Name must be provided");
106 if (busTopicParams.isHostnameInvalid()) {
107 throw new IllegalArgumentException("Hostname must be provided");
110 if (busTopicParams.isPortInvalid()) {
111 throw new IllegalArgumentException("Invalid Port provided: " + busTopicParams.getPort());
114 this.name = busTopicParams.getClientName();
115 this.https = busTopicParams.isUseHttps();
116 this.hostname = busTopicParams.getHostname();
117 this.port = busTopicParams.getPort();
118 this.basePath = busTopicParams.getBasePath();
119 this.userName = busTopicParams.getUserName();
120 this.password = busTopicParams.getPassword();
121 this.selfSignedCerts = busTopicParams.isAllowSelfSignedCerts();
122 this.client = detmClient();
124 if (!StringUtils.isBlank(this.userName) && !StringUtils.isBlank(this.password)) {
125 var authFeature = HttpAuthenticationFeature.basic(userName, password);
126 this.client.register(authFeature);
129 this.client.property(ClientProperties.METAINF_SERVICES_LOOKUP_DISABLE, "true");
131 registerSerProviders(busTopicParams.getSerializationProvider());
133 this.baseUrl = (this.https ? "https://" : "http://") + this.hostname + ":" + this.port + "/"
134 + (this.basePath == null ? "" : this.basePath);
137 private Client detmClient() throws NoSuchAlgorithmException, KeyManagementException {
139 ClientBuilder clientBuilder;
140 var sslContext = SSLContext.getInstance("TLSv1.2");
141 if (this.selfSignedCerts) {
142 sslContext.init(null, NetworkUtil.getAlwaysTrustingManager(), new SecureRandom());
144 // This falls under self-signed certs which is used for non-production testing environments where
145 // the hostname in the cert is unlikely to be crafted properly. We always return true for the
146 // hostname verifier. This causes a sonar vuln, but we ignore it as it could cause problems in some
147 // testing environments.
149 ClientBuilder.newBuilder().sslContext(sslContext).hostnameVerifier(
150 (host, session) -> true); //NOSONAR
152 sslContext.init(null, null, null);
153 clientBuilder = ClientBuilder.newBuilder().sslContext(sslContext);
155 return clientBuilder.build();
158 return ClientBuilder.newClient();
163 * Registers the serialization provider(s) with the client.
165 * @param serializationProvider comma-separated list of serialization providers
166 * @throws ClassNotFoundException if the serialization provider cannot be found
168 private void registerSerProviders(String serializationProvider) throws ClassNotFoundException {
169 String providers = (StringUtils.isBlank(serializationProvider)
170 ? JERSEY_DEFAULT_SERIALIZATION_PROVIDER : serializationProvider);
171 for (String prov : COMMA_PAT.split(providers)) {
172 this.client.register(Class.forName(prov));
177 public WebTarget getWebTarget() {
178 return this.client.target(this.baseUrl);
182 public Response get(String path) {
183 if (!StringUtils.isBlank(path)) {
184 return getWebTarget().path(path).request().get();
186 return getWebTarget().request().get();
191 public Response get() {
192 return getWebTarget().request().get();
196 public Future<Response> get(InvocationCallback<Response> callback, String path, Map<String, Object> headers) {
197 Map<String, Object> headers2 = (headers != null ? headers : Collections.emptyMap());
199 if (!StringUtils.isBlank(path)) {
200 return getBuilder(path, headers2).async().get(callback);
202 return get(callback, headers2);
207 public Future<Response> get(InvocationCallback<Response> callback, Map<String, Object> headers) {
208 var builder = getWebTarget().request();
209 if (headers != null) {
210 headers.forEach(builder::header);
212 return builder.async().get(callback);
216 public Response put(String path, Entity<?> entity, Map<String, Object> headers) {
217 return getBuilder(path, headers).put(entity);
221 public Future<Response> put(InvocationCallback<Response> callback, String path, Entity<?> entity,
222 Map<String, Object> headers) {
223 return getBuilder(path, headers).async().put(entity, callback);
227 public Response post(String path, Entity<?> entity, Map<String, Object> headers) {
228 return getBuilder(path, headers).post(entity);
232 public Future<Response> post(InvocationCallback<Response> callback, String path, Entity<?> entity,
233 Map<String, Object> headers) {
234 return getBuilder(path, headers).async().post(entity, callback);
238 public Response delete(String path, Map<String, Object> headers) {
239 return getBuilder(path, headers).delete();
243 public Future<Response> delete(InvocationCallback<Response> callback, String path, Map<String, Object> headers) {
244 return getBuilder(path, headers).async().delete(callback);
248 public boolean start() {
253 public boolean stop() {
258 public void shutdown() {
259 synchronized (this) {
265 } catch (Exception e) {
266 logger.warn("{}: cannot close because of {}", this, e.getMessage(), e);
271 public synchronized boolean isAlive() {
275 private Builder getBuilder(String path, Map<String, Object> headers) {
276 var builder = getWebTarget().path(path).request();
277 for (Entry<String, Object> header : headers.entrySet()) {
278 builder.header(header.getKey(), header.getValue());