From 121629e41bc99b10b395e53c3e8cbb3a873a9115 Mon Sep 17 00:00:00 2001
From: Udara Pathum <46132469+hwupathum@users.noreply.github.com>
Date: Wed, 3 Jul 2024 13:34:37 +0530
Subject: [PATCH] Add Httpclient5 as an OSGI service
---
core/org.wso2.carbon.http.client/pom.xml | 111 ++++++++++++++++++
.../wso2/carbon/http/client/ClientUtils.java | 41 +++++++
.../http/client/HttpClientConstants.java | 31 +++++
.../http/client/cache/ClientBaseCache.java | 73 ++++++++++++
.../http/client/cache/HttpClientCache.java | 61 ++++++++++
.../client/exception/HttpClientException.java | 51 ++++++++
.../handler/AbstractResponseHandler.java | 23 ++++
.../client/handler/JsonResponseHandler.java | 50 ++++++++
.../internal/HttpClientServiceComponent.java | 34 ++++++
.../http/client/request/HttpPostRequest.java | 39 ++++++
.../client/services/HttpClientService.java | 36 ++++++
.../services/HttpClientServiceImpl.java | 54 +++++++++
.../src/main/resources/testing.xml | 28 +++++
.../client/cache/HttpClientCacheTest.java | 49 ++++++++
core/pom.xml | 1 +
.../pom.xml | 2 +
.../pom.xml | 2 +
parent/pom.xml | 12 ++
18 files changed, 698 insertions(+)
create mode 100644 core/org.wso2.carbon.http.client/pom.xml
create mode 100644 core/org.wso2.carbon.http.client/src/main/java/org/wso2/carbon/http/client/ClientUtils.java
create mode 100644 core/org.wso2.carbon.http.client/src/main/java/org/wso2/carbon/http/client/HttpClientConstants.java
create mode 100644 core/org.wso2.carbon.http.client/src/main/java/org/wso2/carbon/http/client/cache/ClientBaseCache.java
create mode 100644 core/org.wso2.carbon.http.client/src/main/java/org/wso2/carbon/http/client/cache/HttpClientCache.java
create mode 100644 core/org.wso2.carbon.http.client/src/main/java/org/wso2/carbon/http/client/exception/HttpClientException.java
create mode 100644 core/org.wso2.carbon.http.client/src/main/java/org/wso2/carbon/http/client/handler/AbstractResponseHandler.java
create mode 100644 core/org.wso2.carbon.http.client/src/main/java/org/wso2/carbon/http/client/handler/JsonResponseHandler.java
create mode 100644 core/org.wso2.carbon.http.client/src/main/java/org/wso2/carbon/http/client/internal/HttpClientServiceComponent.java
create mode 100644 core/org.wso2.carbon.http.client/src/main/java/org/wso2/carbon/http/client/request/HttpPostRequest.java
create mode 100644 core/org.wso2.carbon.http.client/src/main/java/org/wso2/carbon/http/client/services/HttpClientService.java
create mode 100644 core/org.wso2.carbon.http.client/src/main/java/org/wso2/carbon/http/client/services/HttpClientServiceImpl.java
create mode 100644 core/org.wso2.carbon.http.client/src/main/resources/testing.xml
create mode 100644 core/org.wso2.carbon.http.client/src/test/java/org/wso2/carbon/http/client/cache/HttpClientCacheTest.java
diff --git a/core/org.wso2.carbon.http.client/pom.xml b/core/org.wso2.carbon.http.client/pom.xml
new file mode 100644
index 00000000000..b3cd3dea4d5
--- /dev/null
+++ b/core/org.wso2.carbon.http.client/pom.xml
@@ -0,0 +1,111 @@
+
+
+
+
+
+
+ org.wso2.carbon
+ carbon-kernel
+ 4.10.17-SNAPSHOT
+ ../pom.xml
+
+
+ 4.0.0
+ org.wso2.carbon.http.client
+ bundle
+ WSO2 Carbon - Http Client
+ OSGi Bundle for Carbon Http Client
+ http://wso2.org
+
+
+
+ org.osgi
+ org.osgi.service.component.annotations
+
+
+ org.osgi
+ org.osgi.annotation
+
+
+ org.osgi
+ org.osgi.service.metatype.annotations
+
+
+ org.osgi
+ org.osgi.compendium
+ provided
+
+
+ org.wso2.org.ops4j.pax.logging
+ pax-logging-api
+
+
+ org.wso2.orbit.org.apache.httpcomponents
+ httpclient5
+
+
+ com.google.guava
+ guava
+
+
+ com.google.code.gson
+ gson
+
+
+
+ org.wso2.orbit.org.apache.httpcomponents
+ httpcore5
+ test
+
+
+ org.testng
+ testng
+ test
+
+
+
+
+
+
+ org.apache.felix
+ maven-bundle-plugin
+ true
+
+
+ ${project.artifactId}
+ ${project.artifactId}
+ ${project.artifactId}-${project.version}
+
+ org.wso2.carbon.http.client.internal,
+
+
+ org.osgi.framework.*,
+ org.apache.commons.logging.*; version="${import.package.version.commons.logging}",
+ org.wso2.carbon.caching.impl.*,
+ *;resolution:=optional
+
+
+ !org.wso2.carbon.http.client.internal,
+ org.wso2.carbon.http.client.*,
+
+
+
+
+
+
+
+
diff --git a/core/org.wso2.carbon.http.client/src/main/java/org/wso2/carbon/http/client/ClientUtils.java b/core/org.wso2.carbon.http.client/src/main/java/org/wso2/carbon/http/client/ClientUtils.java
new file mode 100644
index 00000000000..588546a0c05
--- /dev/null
+++ b/core/org.wso2.carbon.http.client/src/main/java/org/wso2/carbon/http/client/ClientUtils.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com).
+ *
+ * WSO2 LLC. licenses this file to you under the Apache License,
+ * Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.wso2.carbon.http.client;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
+import org.apache.hc.client5.http.impl.classic.HttpClientBuilder;
+
+public class ClientUtils {
+
+ private static final Log log = LogFactory.getLog(ClientUtils.class);
+
+ private ClientUtils() {
+ }
+
+ public static CloseableHttpClient createClient() {
+
+ HttpClientBuilder httpClientBuilder = HttpClientBuilder.create().useSystemProperties();
+ if (log.isDebugEnabled()) {
+ log.debug("Creating a new HttpClient instance");
+ }
+ return httpClientBuilder.build();
+
+ }
+}
diff --git a/core/org.wso2.carbon.http.client/src/main/java/org/wso2/carbon/http/client/HttpClientConstants.java b/core/org.wso2.carbon.http.client/src/main/java/org/wso2/carbon/http/client/HttpClientConstants.java
new file mode 100644
index 00000000000..75b56217028
--- /dev/null
+++ b/core/org.wso2.carbon.http.client/src/main/java/org/wso2/carbon/http/client/HttpClientConstants.java
@@ -0,0 +1,31 @@
+package org.wso2.carbon.http.client;
+
+public class HttpClientConstants {
+
+ public enum Error {
+
+ RESPONSE_ENTITY_EMPTY("12001", "Response entity is empty"),
+ RESPONSE_PARSE_ERROR("12002", "Error occurred while parsing the response");
+
+ private final String code;
+ private final String message;
+ private static final String API_ERROR_CODE_PREFIX = "HTC-";
+
+ Error(String code, String message) {
+
+ this.code = code;
+ this.message = message;
+ }
+
+ public String getCode() {
+
+ return API_ERROR_CODE_PREFIX + code;
+ }
+
+ public String getMessage() {
+
+ return message;
+ }
+ }
+
+}
diff --git a/core/org.wso2.carbon.http.client/src/main/java/org/wso2/carbon/http/client/cache/ClientBaseCache.java b/core/org.wso2.carbon.http.client/src/main/java/org/wso2/carbon/http/client/cache/ClientBaseCache.java
new file mode 100644
index 00000000000..896cafc064b
--- /dev/null
+++ b/core/org.wso2.carbon.http.client/src/main/java/org/wso2/carbon/http/client/cache/ClientBaseCache.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com).
+ *
+ * WSO2 LLC. licenses this file to you under the Apache License,
+ * Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.wso2.carbon.http.client.cache;
+
+import com.google.common.cache.Cache;
+import com.google.common.cache.CacheBuilder;
+import com.google.common.cache.RemovalListener;
+
+import java.io.Serializable;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * A base class for all cache implementations in Identity modules. This maintains caches in the tenanted space.
+ * A copy of this class is maintained at org.wso2.carbon.identity.organization.management.service.cache component.
+ *
+ * @param cache key type.
+ * @param cache value type.
+ */
+public abstract class ClientBaseCache {
+
+ private final Cache cache;
+
+ protected ClientBaseCache(int cacheSize, int expireAfterAccess, RemovalListener removalListener) {
+
+ cache = CacheBuilder.newBuilder()
+ .maximumSize(cacheSize)
+ .expireAfterAccess(expireAfterAccess, TimeUnit.MILLISECONDS)
+ .removalListener(removalListener)
+ .build();
+ }
+
+ public void put(K key, V value) {
+
+ cache.put(key, value);
+ }
+
+ public V get(K key, Callable loader) {
+
+ try {
+ return cache.get(key, loader);
+ } catch (ExecutionException e) {
+ // TODO: handle exception
+ return null;
+ }
+ }
+
+ public V get(K key) {
+
+ return cache.getIfPresent(key);
+ }
+
+ public void cleanUp() {
+
+ cache.cleanUp();
+ }
+}
diff --git a/core/org.wso2.carbon.http.client/src/main/java/org/wso2/carbon/http/client/cache/HttpClientCache.java b/core/org.wso2.carbon.http.client/src/main/java/org/wso2/carbon/http/client/cache/HttpClientCache.java
new file mode 100644
index 00000000000..60446b4e771
--- /dev/null
+++ b/core/org.wso2.carbon.http.client/src/main/java/org/wso2/carbon/http/client/cache/HttpClientCache.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com).
+ *
+ * WSO2 LLC. licenses this file to you under the Apache License,
+ * Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.wso2.carbon.http.client.cache;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
+
+import java.io.IOException;
+
+public class HttpClientCache extends ClientBaseCache {
+
+ private static final Log log = LogFactory.getLog(HttpClientCache.class);
+ private static HttpClientCache instance;
+
+
+ protected HttpClientCache(int cacheSize, int expireAfterAccess) {
+
+ super(cacheSize, expireAfterAccess, httpClientCacheEntry -> {
+ // This is not called at expire
+ // https://stackoverflow.com/questions/21986551/guava-cachebuilder-doesnt-call-removal-listener
+ try {
+ if (httpClientCacheEntry.getValue() != null) {
+ CloseableHttpClient client = httpClientCacheEntry.getValue();
+ client.close();
+ if (log.isDebugEnabled()) {
+ log.debug("Http Client - " + httpClientCacheEntry.getKey() + " removed due to " +
+ httpClientCacheEntry.getCause());
+ }
+ }
+ } catch (IOException e) {
+ log.error("Error occurred while closing the http client for key: " + httpClientCacheEntry.getKey(), e);
+ }
+ });
+ }
+
+ public static HttpClientCache getInstance() {
+
+ if (instance == null) {
+ int ttlMinutes = 1;
+ int cacheTimeoutMinutes = 5;
+ instance = new HttpClientCache(100, (ttlMinutes + cacheTimeoutMinutes) * 60 * 1000);
+ }
+ return instance;
+ }
+}
diff --git a/core/org.wso2.carbon.http.client/src/main/java/org/wso2/carbon/http/client/exception/HttpClientException.java b/core/org.wso2.carbon.http.client/src/main/java/org/wso2/carbon/http/client/exception/HttpClientException.java
new file mode 100644
index 00000000000..311972cf5aa
--- /dev/null
+++ b/core/org.wso2.carbon.http.client/src/main/java/org/wso2/carbon/http/client/exception/HttpClientException.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com).
+ *
+ * WSO2 LLC. licenses this file to you under the Apache License,
+ * Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.wso2.carbon.http.client.exception;
+
+import java.io.IOException;
+
+public class HttpClientException extends IOException {
+
+ private String errorCode;
+
+ public HttpClientException(String message) {
+ super(message);
+ }
+
+ public HttpClientException(String errorCode, String message) {
+
+ super(message);
+ this.errorCode = errorCode;
+ }
+
+ public HttpClientException(String message, Throwable cause) {
+
+ super(message, cause);
+ }
+
+ public HttpClientException(String errorCode, String message, Throwable cause) {
+
+ super(message, cause);
+ this.errorCode = errorCode;
+ }
+
+ public String getErrorCode() {
+
+ return errorCode;
+ }
+}
diff --git a/core/org.wso2.carbon.http.client/src/main/java/org/wso2/carbon/http/client/handler/AbstractResponseHandler.java b/core/org.wso2.carbon.http.client/src/main/java/org/wso2/carbon/http/client/handler/AbstractResponseHandler.java
new file mode 100644
index 00000000000..04fd726ce9e
--- /dev/null
+++ b/core/org.wso2.carbon.http.client/src/main/java/org/wso2/carbon/http/client/handler/AbstractResponseHandler.java
@@ -0,0 +1,23 @@
+package org.wso2.carbon.http.client.handler;
+
+import org.apache.hc.core5.http.ClassicHttpResponse;
+import org.apache.hc.core5.http.HttpEntity;
+import org.apache.hc.core5.http.io.HttpClientResponseHandler;
+import org.wso2.carbon.http.client.HttpClientConstants;
+import org.wso2.carbon.http.client.exception.HttpClientException;
+
+public abstract class AbstractResponseHandler implements HttpClientResponseHandler {
+
+ public T handleResponse(ClassicHttpResponse response) throws HttpClientException {
+
+ // TODO: handle response status codes
+ HttpEntity entity = response.getEntity();
+ if (entity == null) {
+ throw new HttpClientException(HttpClientConstants.Error.RESPONSE_ENTITY_EMPTY.getCode(),
+ HttpClientConstants.Error.RESPONSE_ENTITY_EMPTY.getMessage());
+ }
+ return this.handleEntity(entity);
+ }
+
+ protected abstract T handleEntity(HttpEntity httpEntity) throws HttpClientException;
+}
diff --git a/core/org.wso2.carbon.http.client/src/main/java/org/wso2/carbon/http/client/handler/JsonResponseHandler.java b/core/org.wso2.carbon.http.client/src/main/java/org/wso2/carbon/http/client/handler/JsonResponseHandler.java
new file mode 100644
index 00000000000..8a22583094e
--- /dev/null
+++ b/core/org.wso2.carbon.http.client/src/main/java/org/wso2/carbon/http/client/handler/JsonResponseHandler.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com).
+ *
+ * WSO2 LLC. licenses this file to you under the Apache License,
+ * Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.wso2.carbon.http.client.handler;
+
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonParser;
+import org.apache.hc.core5.http.ClassicHttpResponse;
+import org.apache.hc.core5.http.HttpEntity;
+import org.apache.hc.core5.http.io.HttpClientResponseHandler;
+import org.wso2.carbon.http.client.HttpClientConstants;
+import org.wso2.carbon.http.client.exception.HttpClientException;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.nio.charset.StandardCharsets;
+
+public class JsonResponseHandler extends AbstractResponseHandler {
+
+ protected JsonObject handleEntity(HttpEntity entity) throws HttpClientException {
+
+ try (final InputStream inputStream = entity.getContent()) {
+ if (inputStream == null) {
+ return null;
+ }
+ JsonElement jsonElement =
+ JsonParser.parseReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8));
+ return jsonElement.getAsJsonObject();
+ } catch (IOException e) {
+ throw new HttpClientException(HttpClientConstants.Error.RESPONSE_PARSE_ERROR.getCode(),
+ HttpClientConstants.Error.RESPONSE_PARSE_ERROR.getMessage(), e);
+ }
+ }
+}
diff --git a/core/org.wso2.carbon.http.client/src/main/java/org/wso2/carbon/http/client/internal/HttpClientServiceComponent.java b/core/org.wso2.carbon.http.client/src/main/java/org/wso2/carbon/http/client/internal/HttpClientServiceComponent.java
new file mode 100644
index 00000000000..34f8f889225
--- /dev/null
+++ b/core/org.wso2.carbon.http.client/src/main/java/org/wso2/carbon/http/client/internal/HttpClientServiceComponent.java
@@ -0,0 +1,34 @@
+package org.wso2.carbon.http.client.internal;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.osgi.service.component.ComponentContext;
+import org.osgi.service.component.annotations.Activate;
+import org.osgi.service.component.annotations.Component;
+import org.osgi.service.component.annotations.Deactivate;
+import org.wso2.carbon.http.client.services.HttpClientService;
+import org.wso2.carbon.http.client.services.HttpClientServiceImpl;
+
+@Component(
+ name = "http.client.component",
+ immediate = true
+)
+public class HttpClientServiceComponent {
+
+ private static final Log LOGGER = LogFactory.getLog(HttpClientServiceComponent.class);
+
+ @Activate
+ protected void activate(ComponentContext context) {
+ context.getBundleContext().registerService(HttpClientService.class,
+ new HttpClientServiceImpl(), null);
+ LOGGER.info("HttpClient bundle is activated");
+ }
+
+ @Deactivate
+ protected void deactivate(ComponentContext context) {
+
+ LOGGER.debug("HttpClient service component deactivated");
+ }
+
+
+}
diff --git a/core/org.wso2.carbon.http.client/src/main/java/org/wso2/carbon/http/client/request/HttpPostRequest.java b/core/org.wso2.carbon.http.client/src/main/java/org/wso2/carbon/http/client/request/HttpPostRequest.java
new file mode 100644
index 00000000000..0d2fc0640e3
--- /dev/null
+++ b/core/org.wso2.carbon.http.client/src/main/java/org/wso2/carbon/http/client/request/HttpPostRequest.java
@@ -0,0 +1,39 @@
+package org.wso2.carbon.http.client.request;
+
+import org.apache.hc.client5.http.classic.methods.HttpPost;
+import org.apache.hc.client5.http.entity.UrlEncodedFormEntity;
+import org.apache.hc.core5.http.ContentType;
+import org.apache.hc.core5.http.io.entity.StringEntity;
+import org.apache.hc.core5.http.message.BasicNameValuePair;
+
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+public class HttpPostRequest {
+
+ private HttpPostRequest() {
+ }
+
+ public static HttpPost createUrlEncodedRequest(String url, Map paramsMap) {
+
+ HttpPost httpPost = new HttpPost(url);
+ List params = new ArrayList<>();
+ for (Map.Entry entry : paramsMap.entrySet()) {
+ params.add(new BasicNameValuePair(entry.getKey(), entry.getValue()));
+ }
+ httpPost.setEntity(new UrlEncodedFormEntity(params, StandardCharsets.UTF_8));
+
+ return httpPost;
+ }
+
+ public static HttpPost createJsonRequest(String url, String jsonPayload) {
+
+ HttpPost httpPost = new HttpPost(url);
+ httpPost.setEntity(new StringEntity(jsonPayload, ContentType.APPLICATION_JSON));
+
+ return httpPost;
+ }
+
+}
diff --git a/core/org.wso2.carbon.http.client/src/main/java/org/wso2/carbon/http/client/services/HttpClientService.java b/core/org.wso2.carbon.http.client/src/main/java/org/wso2/carbon/http/client/services/HttpClientService.java
new file mode 100644
index 00000000000..dcf49c69e31
--- /dev/null
+++ b/core/org.wso2.carbon.http.client/src/main/java/org/wso2/carbon/http/client/services/HttpClientService.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com).
+ *
+ * WSO2 LLC. licenses this file to you under the Apache License,
+ * Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.wso2.carbon.http.client.services;
+
+import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
+
+import java.io.IOException;
+import java.net.URL;
+import java.util.concurrent.Callable;
+
+import javax.net.ssl.HttpsURLConnection;
+
+public interface HttpClientService {
+
+ CloseableHttpClient getClosableHttpClient(String key, Callable loader);
+
+ CloseableHttpClient getClosableHttpClient(String key);
+
+ HttpsURLConnection getHttpsURLConnection(URL url, String httpMethod)
+ throws IOException;
+}
diff --git a/core/org.wso2.carbon.http.client/src/main/java/org/wso2/carbon/http/client/services/HttpClientServiceImpl.java b/core/org.wso2.carbon.http.client/src/main/java/org/wso2/carbon/http/client/services/HttpClientServiceImpl.java
new file mode 100644
index 00000000000..9725de85ea3
--- /dev/null
+++ b/core/org.wso2.carbon.http.client/src/main/java/org/wso2/carbon/http/client/services/HttpClientServiceImpl.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com).
+ *
+ * WSO2 LLC. licenses this file to you under the Apache License,
+ * Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.wso2.carbon.http.client.services;
+
+import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
+import org.wso2.carbon.http.client.ClientUtils;
+import org.wso2.carbon.http.client.cache.HttpClientCache;
+
+import java.io.IOException;
+import java.net.URL;
+import java.util.concurrent.Callable;
+
+import javax.net.ssl.HttpsURLConnection;
+
+public class HttpClientServiceImpl implements HttpClientService {
+
+ @Override
+ public CloseableHttpClient getClosableHttpClient(String key, Callable loader) {
+
+ return HttpClientCache.getInstance().get(key, loader);
+ }
+
+ @Override
+ public CloseableHttpClient getClosableHttpClient(String key) {
+
+ return HttpClientCache.getInstance().get(key, ClientUtils::createClient);
+ }
+
+ @Override
+ public HttpsURLConnection getHttpsURLConnection(URL url, String httpMethod)
+ throws IOException {
+
+ HttpsURLConnection connection = (HttpsURLConnection) url.openConnection();
+ connection.setDoInput(true);
+ connection.setDoOutput(true);
+ connection.setRequestMethod(httpMethod);
+ return connection;
+ }
+}
diff --git a/core/org.wso2.carbon.http.client/src/main/resources/testing.xml b/core/org.wso2.carbon.http.client/src/main/resources/testing.xml
new file mode 100644
index 00000000000..c37030f0439
--- /dev/null
+++ b/core/org.wso2.carbon.http.client/src/main/resources/testing.xml
@@ -0,0 +1,28 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/core/org.wso2.carbon.http.client/src/test/java/org/wso2/carbon/http/client/cache/HttpClientCacheTest.java b/core/org.wso2.carbon.http.client/src/test/java/org/wso2/carbon/http/client/cache/HttpClientCacheTest.java
new file mode 100644
index 00000000000..d0c03add046
--- /dev/null
+++ b/core/org.wso2.carbon.http.client/src/test/java/org/wso2/carbon/http/client/cache/HttpClientCacheTest.java
@@ -0,0 +1,49 @@
+package org.wso2.carbon.http.client.cache;
+
+import org.testng.Assert;
+import org.testng.annotations.Test;
+import org.wso2.carbon.http.client.ClientUtils;
+
+public class HttpClientCacheTest {
+
+ private static final int ACCESS_TIMEOUT = 1000;
+
+ @Test(description = " Test case for cache put and get")
+ public void cachePutAndGetTest() {
+ HttpClientCache cache = new HttpClientCache(5, ACCESS_TIMEOUT);
+
+ cache.put("key", ClientUtils.createClient());
+ Assert.assertNotNull(cache.get("key"));
+ }
+
+ @Test(description = " Test case for cache put and get with loader")
+ public void cachePutAndGetWithLoaderTest() {
+ HttpClientCache cache = new HttpClientCache(5, ACCESS_TIMEOUT);
+
+ Assert.assertNotNull(cache.get("key", ClientUtils::createClient));
+ }
+
+ @Test(description = " Test case for cache eviction")
+ public void cacheEvictionTest() throws InterruptedException {
+ HttpClientCache cache = new HttpClientCache(5, ACCESS_TIMEOUT);
+
+ cache.put("key", ClientUtils.createClient());
+ Thread.sleep(ACCESS_TIMEOUT / 2);
+ Assert.assertNotNull(cache.get("key"));
+ Thread.sleep(ACCESS_TIMEOUT * 5);
+// cache.cleanUp();
+ Assert.assertNull(cache.get("key")); //return null
+ }
+
+ @Test(description = " Test case for cache eviction with loader")
+ public void cacheEvictionLoaderTest() throws InterruptedException {
+ HttpClientCache cache = new HttpClientCache(5, ACCESS_TIMEOUT);
+
+ cache.put("key", ClientUtils.createClient());
+ Thread.sleep(ACCESS_TIMEOUT / 2);
+ Assert.assertNotNull(cache.get("key"));
+ Thread.sleep(ACCESS_TIMEOUT * 5);
+ Assert.assertNotNull(cache.get("key", ClientUtils::createClient)); //return null
+ }
+
+}
diff --git a/core/pom.xml b/core/pom.xml
index 3e55a543b6a..0ab3bc312a4 100644
--- a/core/pom.xml
+++ b/core/pom.xml
@@ -122,6 +122,7 @@
org.wso2.carbon.application.deployer
org.wso2.carbon.core
org.wso2.carbon.bridge
+ org.wso2.carbon.http.client
org.wso2.carbon.http.bridge
org.wso2.carbon.servletbridge
org.wso2.carbon.core.common
diff --git a/features/org.wso2.carbon.core.common.feature/pom.xml b/features/org.wso2.carbon.core.common.feature/pom.xml
index 5903b88b0e8..3debc01e964 100644
--- a/features/org.wso2.carbon.core.common.feature/pom.xml
+++ b/features/org.wso2.carbon.core.common.feature/pom.xml
@@ -100,6 +100,8 @@
org.wso2.orbit.commons-collections:commons-collections:${orbit.version.commons.collection}
org.apache.httpcomponents.wso2:httpcore:${orbit.version.httpcore}
+ org.wso2.orbit.org.apache.httpcomponents:httpcore5:${orbit.version.httpcore5}
+ org.wso2.orbit.org.apache.httpcomponents:httpclient5:${orbit.version.httpclient5}
org.compass-project.wso2:compass:${version.compass}
commons-lang.wso2:commons-lang:${orbit.version.commons.lang}
org.wso2.orbit.org.apache.poi:poi:${orbit.version.poi}
diff --git a/features/org.wso2.carbon.core.server.feature/pom.xml b/features/org.wso2.carbon.core.server.feature/pom.xml
index ab60be8ce35..a966a89cbd9 100644
--- a/features/org.wso2.carbon.core.server.feature/pom.xml
+++ b/features/org.wso2.carbon.core.server.feature/pom.xml
@@ -67,6 +67,8 @@
org.wso2.carbon:org.wso2.carbon.admin.advisory.mgt:${carbon.kernel.version}
+ org.wso2.carbon:org.wso2.carbon.http.client:${carbon.kernel.version}
+
org.wso2.orbit.org.bouncycastle:bcprov-jdk18on:${bouncycastle.version}
org.wso2.orbit.org.bouncycastle:bcpkix-jdk18on:${bouncycastle.version}
diff --git a/parent/pom.xml b/parent/pom.xml
index fd02488607f..2c628d1e20f 100644
--- a/parent/pom.xml
+++ b/parent/pom.xml
@@ -547,6 +547,8 @@
1.8.wso2v1
4.4.14.wso2v1
+ 5.2.1.wso2v1
+ 5.2.1.wso2v1
1.6.2.wso2v4
3.1.0.wso2v6
1.5.6.wso2v1
@@ -1176,6 +1178,16 @@
httpcore
${orbit.version.httpcore}
+
+ org.wso2.orbit.org.apache.httpcomponents
+ httpcore5
+ ${orbit.version.httpcore5}
+
+
+ org.wso2.orbit.org.apache.httpcomponents
+ httpclient5
+ ${orbit.version.httpclient5}
+
net.sf.ehcache.wso2
ehcache