diff options
Diffstat (limited to 'policy-endpoints/src/main/java/org/onap/policy/common/endpoints/http/client')
5 files changed, 176 insertions, 121 deletions
diff --git a/policy-endpoints/src/main/java/org/onap/policy/common/endpoints/http/client/HttpClient.java b/policy-endpoints/src/main/java/org/onap/policy/common/endpoints/http/client/HttpClient.java index 2fe46fb3..01deaa2d 100644 --- a/policy-endpoints/src/main/java/org/onap/policy/common/endpoints/http/client/HttpClient.java +++ b/policy-endpoints/src/main/java/org/onap/policy/common/endpoints/http/client/HttpClient.java @@ -2,7 +2,8 @@ * ============LICENSE_START======================================================= * ONAP * ================================================================================ - * Copyright (C) 2017-2019 AT&T Intellectual Property. All rights reserved. + * Copyright (C) 2017-2020 AT&T Intellectual Property. All rights reserved. + * Modifications Copyright (C) 2023 Nordix Foundation. * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,15 +21,17 @@ package org.onap.policy.common.endpoints.http.client; +import jakarta.ws.rs.client.Entity; +import jakarta.ws.rs.client.InvocationCallback; +import jakarta.ws.rs.client.WebTarget; +import jakarta.ws.rs.core.Response; import java.util.Map; - -import javax.ws.rs.client.Entity; -import javax.ws.rs.core.Response; - +import java.util.concurrent.Future; import org.onap.policy.common.capabilities.Startable; /** - * Http Client interface. + * Http Client interface. Supports both synchronous and asynchronous operations. + * */ public interface HttpClient extends Startable { @@ -48,6 +51,26 @@ public interface HttpClient extends Startable { Response get(); /** + * Asynchronous GET request. + * + * @param callback callback to be invoked, asynchronously, when the request completes + * @param path context uri path + * @param headers request headers + * + * @return future that can be used to cancel the request or await the response + */ + Future<Response> get(InvocationCallback<Response> callback, String path, Map<String, Object> headers); + + /** + * Asynchronous GET request. + * + * @param callback callback to be invoked, asynchronously, when the request completes + * @param headers request headers + * @return future that can be used to cancel the request or await the response + */ + Future<Response> get(InvocationCallback<Response> callback, Map<String, Object> headers); + + /** * PUT request. * * @param path context uri path @@ -59,6 +82,19 @@ public interface HttpClient extends Startable { Response put(String path, Entity<?> entity, Map<String, Object> headers); /** + * Asynchronous PUT request. + * + * @param callback callback to be invoked, asynchronously, when the request completes + * @param path context uri path + * @param entity body + * @param headers headers + * + * @return future that can be used to cancel the request or await the response + */ + Future<Response> put(InvocationCallback<Response> callback, String path, Entity<?> entity, + Map<String, Object> headers); + + /** * POST request. * * @param path context uri path @@ -70,6 +106,19 @@ public interface HttpClient extends Startable { Response post(String path, Entity<?> entity, Map<String, Object> headers); /** + * Asynchronous POST request. + * + * @param callback callback to be invoked, asynchronously, when the request completes + * @param path context uri path + * @param entity body + * @param headers headers + * + * @return future that can be used to cancel the request or await the response + */ + Future<Response> post(InvocationCallback<Response> callback, String path, Entity<?> entity, + Map<String, Object> headers); + + /** * DELETE request. * * @param path context uri path @@ -80,6 +129,17 @@ public interface HttpClient extends Startable { Response delete(String path, Map<String, Object> headers); /** + * Asynchronous DELETE request. + * + * @param callback callback to be invoked, asynchronously, when the request completes + * @param path context uri path + * @param headers headers + * + * @return future that can be used to cancel the request or await the response + */ + Future<Response> delete(InvocationCallback<Response> callback, String path, Map<String, Object> headers); + + /** * Retrieve the body from the HTTP transaction. * * @param response response. @@ -134,9 +194,9 @@ public interface HttpClient extends Startable { String getBasePath(); /** - * Get the user name. + * Get the username. * - * @return the user name + * @return the username */ String getUserName(); @@ -154,4 +214,10 @@ public interface HttpClient extends Startable { */ String getBaseUrl(); + /** + * Gets a web target associated with the base URL. + * + * @return a webtarget + */ + WebTarget getWebTarget(); } diff --git a/policy-endpoints/src/main/java/org/onap/policy/common/endpoints/http/client/HttpClientConfigException.java b/policy-endpoints/src/main/java/org/onap/policy/common/endpoints/http/client/HttpClientConfigException.java index 98ec576f..bb871fa0 100644 --- a/policy-endpoints/src/main/java/org/onap/policy/common/endpoints/http/client/HttpClientConfigException.java +++ b/policy-endpoints/src/main/java/org/onap/policy/common/endpoints/http/client/HttpClientConfigException.java @@ -3,6 +3,7 @@ * ONAP * ================================================================================ * Copyright (C) 2019 AT&T Intellectual Property. All rights reserved. + * Modifications Copyright (C) 2023 Nordix Foundation. * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,10 +21,13 @@ package org.onap.policy.common.endpoints.http.client; +import java.io.Serial; + /** * Exception generated by HttpClient builder. */ public class HttpClientConfigException extends Exception { + @Serial private static final long serialVersionUID = 1L; public HttpClientConfigException() { diff --git a/policy-endpoints/src/main/java/org/onap/policy/common/endpoints/http/client/HttpClientFactoryInstance.java b/policy-endpoints/src/main/java/org/onap/policy/common/endpoints/http/client/HttpClientFactoryInstance.java index c2921640..f64be30e 100644 --- a/policy-endpoints/src/main/java/org/onap/policy/common/endpoints/http/client/HttpClientFactoryInstance.java +++ b/policy-endpoints/src/main/java/org/onap/policy/common/endpoints/http/client/HttpClientFactoryInstance.java @@ -2,7 +2,7 @@ * ============LICENSE_START======================================================= * ONAP * ================================================================================ - * Copyright (C) 2019 AT&T Intellectual Property. All rights reserved. + * Copyright (C) 2019, 2021 AT&T Intellectual Property. All rights reserved. * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,8 +20,11 @@ package org.onap.policy.common.endpoints.http.client; +import lombok.AccessLevel; import lombok.Getter; +import lombok.NoArgsConstructor; +@NoArgsConstructor(access = AccessLevel.PRIVATE) public class HttpClientFactoryInstance { /** @@ -29,9 +32,4 @@ public class HttpClientFactoryInstance { */ @Getter private static final HttpClientFactory clientFactory = new IndexedHttpClientFactory(); - - - private HttpClientFactoryInstance() { - // do nothing - } } diff --git a/policy-endpoints/src/main/java/org/onap/policy/common/endpoints/http/client/IndexedHttpClientFactory.java b/policy-endpoints/src/main/java/org/onap/policy/common/endpoints/http/client/IndexedHttpClientFactory.java index edf8ff6f..5f0b1d6e 100644 --- a/policy-endpoints/src/main/java/org/onap/policy/common/endpoints/http/client/IndexedHttpClientFactory.java +++ b/policy-endpoints/src/main/java/org/onap/policy/common/endpoints/http/client/IndexedHttpClientFactory.java @@ -2,7 +2,8 @@ * ============LICENSE_START======================================================= * ONAP * ================================================================================ - * Copyright (C) 2017-2019 AT&T Intellectual Property. All rights reserved. + * Copyright (C) 2017-2019, 2021 AT&T Intellectual Property. All rights reserved. + * Modifications Copyright (C) 2023 Nordix Foundation. * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,6 +21,7 @@ package org.onap.policy.common.endpoints.http.client; +import com.google.re2j.Pattern; import java.security.KeyManagementException; import java.security.NoSuchAlgorithmException; import java.util.ArrayList; @@ -38,11 +40,12 @@ import org.slf4j.LoggerFactory; * HTTP client factory implementation indexed by name. */ class IndexedHttpClientFactory implements HttpClientFactory { + private static final Pattern COMMA_SPACE_PAT = Pattern.compile("\\s*,\\s*"); /** * Logger. */ - private static Logger logger = LoggerFactory.getLogger(IndexedHttpClientFactory.class); + private static final Logger logger = LoggerFactory.getLogger(IndexedHttpClientFactory.class); protected HashMap<String, HttpClient> clients = new HashMap<>(); @@ -75,7 +78,7 @@ class IndexedHttpClientFactory implements HttpClientFactory { return clientList; } - for (String clientName : clientNames.split("\\s*,\\s*")) { + for (String clientName : COMMA_SPACE_PAT.split(clientNames)) { addClient(clientList, clientName, properties); } @@ -85,23 +88,22 @@ class IndexedHttpClientFactory implements HttpClientFactory { private void addClient(ArrayList<HttpClient> clientList, String clientName, Properties properties) { String clientPrefix = PolicyEndPointProperties.PROPERTY_HTTP_CLIENT_SERVICES + "." + clientName; - PropertyUtils props = new PropertyUtils(properties, clientPrefix, + var props = new PropertyUtils(properties, clientPrefix, (name, value, ex) -> logger.warn("{}: {} {} is in invalid format for http client {} ", this, name, value, clientName)); - int port = props.getInteger(PolicyEndPointProperties.PROPERTY_HTTP_PORT_SUFFIX, -1); + var port = props.getInteger(PolicyEndPointProperties.PROPERTY_HTTP_PORT_SUFFIX, -1); if (port < 0) { logger.warn("No HTTP port for client in {}", clientName); return; } - boolean https = props.getBoolean(PolicyEndPointProperties.PROPERTY_HTTP_HTTPS_SUFFIX, false); - try { HttpClient client = this.build(BusTopicParams.builder() .clientName(clientName) - .useHttps(https) - .allowSelfSignedCerts(https) + .useHttps(props.getBoolean(PolicyEndPointProperties.PROPERTY_HTTP_HTTPS_SUFFIX, false)) + .allowSelfSignedCerts( + props.getBoolean(PolicyEndPointProperties.PROPERTY_ALLOW_SELF_SIGNED_CERTIFICATES_SUFFIX, false)) .hostname(props.getString(PolicyEndPointProperties.PROPERTY_HTTP_HOST_SUFFIX, null)) .port(port) .basePath(props.getString(PolicyEndPointProperties.PROPERTY_HTTP_URL_SUFFIX, null)) diff --git a/policy-endpoints/src/main/java/org/onap/policy/common/endpoints/http/client/internal/JerseyClient.java b/policy-endpoints/src/main/java/org/onap/policy/common/endpoints/http/client/internal/JerseyClient.java index 8a717712..130b6c15 100644 --- a/policy-endpoints/src/main/java/org/onap/policy/common/endpoints/http/client/internal/JerseyClient.java +++ b/policy-endpoints/src/main/java/org/onap/policy/common/endpoints/http/client/internal/JerseyClient.java @@ -2,9 +2,9 @@ * ============LICENSE_START======================================================= * ONAP * ================================================================================ - * Copyright (C) 2017-2019 AT&T Intellectual Property. All rights reserved. + * Copyright (C) 2017-2021 AT&T Intellectual Property. All rights reserved. * Modifications Copyright (C) 2018 Samsung Electronics Co., Ltd. - * Modifications Copyright (C) 2019 Nordix Foundation. + * Modifications Copyright (C) 2019, 2023 Nordix Foundation. * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -22,24 +22,29 @@ package org.onap.policy.common.endpoints.http.client.internal; -import com.fasterxml.jackson.annotation.JsonIgnore; +import com.google.re2j.Pattern; +import jakarta.ws.rs.client.Client; +import jakarta.ws.rs.client.ClientBuilder; +import jakarta.ws.rs.client.Entity; +import jakarta.ws.rs.client.Invocation.Builder; +import jakarta.ws.rs.client.InvocationCallback; +import jakarta.ws.rs.client.WebTarget; +import jakarta.ws.rs.core.Response; import java.security.KeyManagementException; import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; +import java.util.Collections; import java.util.Map; import java.util.Map.Entry; +import java.util.concurrent.Future; import javax.net.ssl.SSLContext; -import javax.ws.rs.client.Client; -import javax.ws.rs.client.ClientBuilder; -import javax.ws.rs.client.Entity; -import javax.ws.rs.client.Invocation.Builder; -import javax.ws.rs.core.Response; +import lombok.Getter; +import lombok.ToString; import org.apache.commons.lang3.StringUtils; import org.glassfish.jersey.client.ClientProperties; import org.glassfish.jersey.client.authentication.HttpAuthenticationFeature; import org.onap.policy.common.endpoints.event.comm.bus.internal.BusTopicParams; import org.onap.policy.common.endpoints.http.client.HttpClient; -import org.onap.policy.common.gson.annotation.GsonJsonIgnore; import org.onap.policy.common.utils.network.NetworkUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -47,15 +52,18 @@ import org.slf4j.LoggerFactory; /** * Http Client implementation using a Jersey Client. */ +@Getter +@ToString public class JerseyClient implements HttpClient { + private static final Pattern COMMA_PAT = Pattern.compile(","); /** * Logger. */ - private static Logger logger = LoggerFactory.getLogger(JerseyClient.class); + private static final Logger logger = LoggerFactory.getLogger(JerseyClient.class); protected static final String JERSEY_DEFAULT_SERIALIZATION_PROVIDER = - "org.glassfish.jersey.jackson.internal.jackson.jaxrs.json.JacksonJsonProvider"; + "org.onap.policy.common.gson.GsonMessageBodyHandler"; protected final String name; protected final boolean https; @@ -74,9 +82,14 @@ public class JerseyClient implements HttpClient { /** * Constructor. * - * <p>name the name https is it https or not selfSignedCerts are there self signed certs - * hostname the hostname port port being used basePath base context userName user - * password password + * <p>name - the name + * https - is it https or not + * selfSignedCerts - are there self-signed certs + * hostname - the hostname + * port - port being used + * basePath - base context + * userName - user credentials + * password - password credentials * * @param busTopicParams Input parameters object * @throws KeyManagementException key exception @@ -109,7 +122,7 @@ public class JerseyClient implements HttpClient { this.client = detmClient(); if (!StringUtils.isBlank(this.userName) && !StringUtils.isBlank(this.password)) { - HttpAuthenticationFeature authFeature = HttpAuthenticationFeature.basic(userName, password); + var authFeature = HttpAuthenticationFeature.basic(userName, password); this.client.register(authFeature); } @@ -124,11 +137,17 @@ public class JerseyClient implements HttpClient { private Client detmClient() throws NoSuchAlgorithmException, KeyManagementException { if (this.https) { ClientBuilder clientBuilder; - SSLContext sslContext = SSLContext.getInstance("TLSv1.2"); + var sslContext = SSLContext.getInstance("TLSv1.2"); if (this.selfSignedCerts) { sslContext.init(null, NetworkUtil.getAlwaysTrustingManager(), new SecureRandom()); + + // This falls under self-signed certs which is used for non-production testing environments where + // the hostname in the cert is unlikely to be crafted properly. We always return true for the + // hostname verifier. This causes a sonar vuln, but we ignore it as it could cause problems in some + // testing environments. clientBuilder = - ClientBuilder.newBuilder().sslContext(sslContext).hostnameVerifier((host, session) -> true); + ClientBuilder.newBuilder().sslContext(sslContext).hostnameVerifier( + (host, session) -> true); //NOSONAR } else { sslContext.init(null, null, null); clientBuilder = ClientBuilder.newBuilder().sslContext(sslContext); @@ -149,23 +168,48 @@ public class JerseyClient implements HttpClient { private void registerSerProviders(String serializationProvider) throws ClassNotFoundException { String providers = (StringUtils.isBlank(serializationProvider) ? JERSEY_DEFAULT_SERIALIZATION_PROVIDER : serializationProvider); - for (String prov : providers.split(",")) { + for (String prov : COMMA_PAT.split(providers)) { this.client.register(Class.forName(prov)); } } @Override + public WebTarget getWebTarget() { + return this.client.target(this.baseUrl); + } + + @Override public Response get(String path) { if (!StringUtils.isBlank(path)) { - return this.client.target(this.baseUrl).path(path).request().get(); + return getWebTarget().path(path).request().get(); } else { - return this.client.target(this.baseUrl).request().get(); + return getWebTarget().request().get(); } } @Override public Response get() { - return this.client.target(this.baseUrl).request().get(); + return getWebTarget().request().get(); + } + + @Override + public Future<Response> get(InvocationCallback<Response> callback, String path, Map<String, Object> headers) { + Map<String, Object> headers2 = (headers != null ? headers : Collections.emptyMap()); + + if (!StringUtils.isBlank(path)) { + return getBuilder(path, headers2).async().get(callback); + } else { + return get(callback, headers2); + } + } + + @Override + public Future<Response> get(InvocationCallback<Response> callback, Map<String, Object> headers) { + var builder = getWebTarget().request(); + if (headers != null) { + headers.forEach(builder::header); + } + return builder.async().get(callback); } @Override @@ -174,16 +218,33 @@ public class JerseyClient implements HttpClient { } @Override + public Future<Response> put(InvocationCallback<Response> callback, String path, Entity<?> entity, + Map<String, Object> headers) { + return getBuilder(path, headers).async().put(entity, callback); + } + + @Override public Response post(String path, Entity<?> entity, Map<String, Object> headers) { return getBuilder(path, headers).post(entity); } @Override + public Future<Response> post(InvocationCallback<Response> callback, String path, Entity<?> entity, + Map<String, Object> headers) { + return getBuilder(path, headers).async().post(entity, callback); + } + + @Override public Response delete(String path, Map<String, Object> headers) { return getBuilder(path, headers).delete(); } @Override + public Future<Response> delete(InvocationCallback<Response> callback, String path, Map<String, Object> headers) { + return getBuilder(path, headers).async().delete(callback); + } + + @Override public boolean start() { return alive; } @@ -211,84 +272,8 @@ public class JerseyClient implements HttpClient { return this.alive; } - @Override - public String getName() { - return name; - } - - @Override - public boolean isHttps() { - return https; - } - - @Override - public boolean isSelfSignedCerts() { - return selfSignedCerts; - } - - @Override - public String getHostname() { - return hostname; - } - - @Override - public int getPort() { - return port; - } - - @Override - public String getBasePath() { - return basePath; - } - - @Override - public String getUserName() { - return userName; - } - - @JsonIgnore - @GsonJsonIgnore - @Override - public String getPassword() { - return password; - } - - @Override - public String getBaseUrl() { - return baseUrl; - } - - @Override - public String toString() { - StringBuilder builder = new StringBuilder(); - builder.append("JerseyClient [name="); - builder.append(name); - builder.append(", https="); - builder.append(https); - builder.append(", selfSignedCerts="); - builder.append(selfSignedCerts); - builder.append(", hostname="); - builder.append(hostname); - builder.append(", port="); - builder.append(port); - builder.append(", basePath="); - builder.append(basePath); - builder.append(", userName="); - builder.append(userName); - builder.append(", password="); - builder.append(password); - builder.append(", client="); - builder.append(client); - builder.append(", baseUrl="); - builder.append(baseUrl); - builder.append(", alive="); - builder.append(alive); - builder.append("]"); - return builder.toString(); - } - private Builder getBuilder(String path, Map<String, Object> headers) { - Builder builder = this.client.target(this.baseUrl).path(path).request(); + var builder = getWebTarget().path(path).request(); for (Entry<String, Object> header : headers.entrySet()) { builder.header(header.getKey(), header.getValue()); } |