Skip to content

Commit

Permalink
HttpClientFactory: Expose a method to customize the underlying
Browse files Browse the repository at this point in the history
HttpClient

Cleanup javadoc
This closes #3205
  • Loading branch information
kwin committed Nov 10, 2023
1 parent e5b4df5 commit 40551e0
Show file tree
Hide file tree
Showing 4 changed files with 54 additions and 12 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ The format is based on [Keep a Changelog](http://keepachangelog.com)

## Unreleased ([details][unreleased changes details])

## Added

- #3205 - HttpClientFactory: Expose a method to customize the underlying HttpClient

## 6.3.0 - 2023-10-25

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,21 +17,35 @@
*/
package com.adobe.acs.commons.http;

import java.util.function.Consumer;

import org.apache.http.client.HttpClient;
import org.apache.http.client.fluent.Executor;
import org.apache.http.client.fluent.Request;
import org.apache.http.impl.client.HttpClientBuilder;

/**
* Factory for building pre-configured HttpClient Fluent Executor and Request objects
* based a configure host, port and (optionally) username/password.
* Factory for building pre-configured HttpClient Fluent {@link Executor} and {@link Request} objects
* based on a configured host, port and (optionally) username/password.
*
* Factories will generally be accessed by service lookup using the factory.name property.
* Each OSGi configuration with factory PID {@code com.adobe.acs.commons.http.impl.HttpClientFactoryImpl} exposes one service of this kind.
* The individual factories will generally be accessed by service lookup using the {@code factory.name} property.
* Despite the name each instance of this interface only holds a single {@link Executor} instance (bound to a single {@code HttpClient}).
*/
public interface HttpClientFactory {

/**
* Get the configured Executor object from this factory.
* Customizes the underlying {@link HttpClientBuilder} which is used to create the singleton http client and executor
* @param builderCustomizer a {@link Consumer} taking the {@link HttpClientBuilder} initialized with the configured basic options.
* @throws IllegalStateException in case {@link #getExecutor()} has been called already
* @since 2.1.0 (Bundle version 6.4.0)
*/
void customize(Consumer<HttpClientBuilder> builderCustomizer);

/**
* Get the singleton {@link Executor} object.
*
* @return an Executor object
* @return the executor
*/
Executor getExecutor();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@
import org.apache.felix.scr.annotations.Reference;
import org.apache.felix.scr.annotations.Service;
import org.apache.http.HttpHost;
import org.apache.http.auth.Credentials;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.fluent.Executor;
import org.apache.http.client.fluent.Request;
Expand All @@ -39,7 +41,9 @@
import org.apache.sling.commons.osgi.PropertiesUtil;

import java.io.IOException;
import java.security.Principal;
import java.util.Map;
import java.util.function.Consumer;

@Component(
label = "ACS AEM Commons - Http Components Fluent Executor Factory",
Expand Down Expand Up @@ -87,9 +91,12 @@ public class HttpClientFactoryImpl implements HttpClientFactory {
@Reference
private HttpClientBuilderFactory httpClientBuilderFactory;

private HttpClientBuilder builder;
private Executor executor;
private String baseUrl;
private CloseableHttpClient httpClient;
private HttpHost httpHost;
private Credentials credentials;

@Activate
protected void activate(Map<String, Object> config) throws Exception {
Expand All @@ -108,7 +115,7 @@ protected void activate(Map<String, Object> config) throws Exception {
int connectTimeout = PropertiesUtil.toInteger(config.get(PROP_CONNECT_TIMEOUT), DEFAULT_CONNECT_TIMEOUT);
int soTimeout = PropertiesUtil.toInteger(config.get(PROP_SO_TIMEOUT), DEFAULT_SOCKET_TIMEOUT);

HttpClientBuilder builder = httpClientBuilderFactory.newBuilder();
builder = httpClientBuilderFactory.newBuilder();

RequestConfig requestConfig = RequestConfig.custom()
.setConnectTimeout(connectTimeout)
Expand All @@ -126,15 +133,22 @@ protected void activate(Map<String, Object> config) throws Exception {
sslbuilder.build(), NoopHostnameVerifier.INSTANCE);
builder.setSSLSocketFactory(sslsf);
}
httpClient = builder.build();
executor = Executor.newInstance(httpClient);

httpHost = new HttpHost(hostname, port, useSSL ? "https" : "http");
String username = PropertiesUtil.toString(config.get(PROP_USERNAME), null);
String password = PropertiesUtil.toString(config.get(PROP_PASSWORD), null);
if (username != null && password != null) {
HttpHost httpHost = new HttpHost(hostname, port, useSSL ? "https" : "http");
executor.auth(httpHost, username, password).authPreemptive(httpHost);
credentials = new UsernamePasswordCredentials(username, password);
}
}

synchronized Executor createExecutor() {
httpClient = builder.build();
executor = Executor.newInstance(httpClient);

if (credentials != null) {
executor.auth(httpHost, credentials).authPreemptive(httpHost);
}
return executor;
}

@Deactivate
Expand All @@ -148,8 +162,19 @@ protected void deactivate() {
}
}

@Override
public void customize(Consumer<HttpClientBuilder> builderCustomizer) {
if (httpClient != null) {
throw new IllegalStateException("The underlying http client has already been created through a call of getExecutor() and can no longer be customized");
}
builderCustomizer.accept(builder);
}

@Override
public Executor getExecutor() {
if (executor == null) {
executor = createExecutor();
}
return executor;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,5 @@
/**
* Http Injectors.
*/
@org.osgi.annotation.versioning.Version("2.0.0")
@org.osgi.annotation.versioning.Version("2.1.0")
package com.adobe.acs.commons.http;

0 comments on commit 40551e0

Please sign in to comment.