Skip to content

Commit 2a7f157

Browse files
Add HttpClientConfiguration to configuration and refactor HttpClient creation and usage (#2842)
* Add HttpClientConfiguration to `configuration` * Refactor HTTP client handling to improve configuration management. * Refactor HTTPClientInterceptor initialization and fix Transport dependency handling. * Refactor `HttpClientConfiguration` handling and improve `Transport` initialization logic. * Initialize `name` and set `appliedFlow` in HTTPClientInterceptor constructor. * refactor(core): rename `hc` to `httpClient` for improved code readability and consistency * Update annotation from `@MCAttribute` to `@MCChildElement` for `setWsdlHttpClientConfig`. * add legacy setter * fix tests * Pass `HttpClientFactory` to `ResolverMap` and `HTTPSchemaResolver` constructors to improve dependency management. * Refactor `HTTPClientInterceptor` initialization for improved `HttpClientConfiguration` handling. * Simplify constructor dependencies by removing redundant `HttpClientFactory` parameter from `ResolverMap` and `HTTPSchemaResolver`. * fix(core): correct `combine` argument order in `ResolverMap` for proper factory handling * refactor(core): initialize `DefaultRouter` in tests and simplify `ResolverMap` constructor * Move `name` and `appliedFlow` initialization from constructor to `init` method in `HTTPClientInterceptor`. * Refactor `SOAPProxy` resolver initialization and add config update method in `HTTPClientInterceptor`. * add default resolvermap * fix tests --------- Co-authored-by: Thomas Bayer <bayer@predic8.de>
1 parent 61fb2e2 commit 2a7f157

20 files changed

Lines changed: 198 additions & 140 deletions

File tree

annot/src/main/java/com/predic8/membrane/annot/generator/kubernetes/model/Schema.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,17 @@
1+
/* Copyright 2026 predic8 GmbH, www.predic8.com
2+
3+
Licensed under the Apache License, Version 2.0 (the "License");
4+
you may not use this file except in compliance with the License.
5+
You may obtain a copy of the License at
6+
7+
http://www.apache.org/licenses/LICENSE-2.0
8+
9+
Unless required by applicable law or agreed to in writing, software
10+
distributed under the License is distributed on an "AS IS" BASIS,
11+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
See the License for the specific language governing permissions and
13+
limitations under the License. */
14+
115
package com.predic8.membrane.annot.generator.kubernetes.model;
216

317
import com.fasterxml.jackson.databind.node.ObjectNode;

core/src/main/java/com/predic8/membrane/core/cli/RouterCLI.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -221,11 +221,12 @@ private static Router initRouterByYAML(String location) throws Exception {
221221

222222
var grammar = new GrammarAutoGenerated();
223223
var reg = new BeanRegistryImplementation(grammar);
224+
224225
router.setRegistry(reg);
225226
reg.register("router", router);
226227

227228
getConfigDefinition(reg.parseYamlBeanDefinitions(router.getResolverMap().resolve(location), grammar))
228-
.ifPresent(beanDefinition -> router.applyConfiguration((Configuration) reg.resolve(beanDefinition.getName())));
229+
.ifPresent(configBd -> router.applyConfiguration((Configuration) reg.resolve(configBd.getName())));
229230

230231
reg.finishStaticConfiguration();
231232
reg.start();

core/src/main/java/com/predic8/membrane/core/interceptor/HTTPClientInterceptor.java

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -45,33 +45,42 @@ public class HTTPClientInterceptor extends AbstractInterceptor {
4545
// null => inherit from HttpClientConfiguration unless explicitly set here
4646
private Boolean failOverOn5XX;
4747

48-
private HttpClientConfiguration httpClientConfig = new HttpClientConfiguration();
48+
private HttpClientConfiguration httpClientConfig;
4949

50-
private HttpClient hc;
50+
private HttpClient httpClient;
5151

52-
public HTTPClientInterceptor() {
53-
name = "http client";
54-
setAppliedFlow(REQUEST_FLOW);
52+
public HTTPClientInterceptor() {}
53+
54+
public HTTPClientInterceptor(HttpClient httpClient) {
55+
this.httpClient = httpClient;
5556
}
5657

5758
@Override
5859
public void init() {
60+
name = "http client";
61+
setAppliedFlow(REQUEST_FLOW);
62+
5963
super.init();
6064

65+
if (httpClientConfig == null) httpClientConfig = router.getHttpClientConfig();
66+
6167
// Overwrite httpClientConfiguration with local value
6268
if (failOverOn5XX != null) {
6369
httpClientConfig.getRetryHandler().setFailOverOn5XX(failOverOn5XX);
6470
}
6571

66-
hc = router.getHttpClientFactory().createClient(httpClientConfig);
67-
hc.setStreamPumpStats(getRouter().getStatistics().getStreamPumpStats());
72+
if (httpClient == null) {
73+
httpClient = router.getHttpClientFactory().createClient(httpClientConfig);
74+
}
75+
76+
httpClient.setStreamPumpStats(getRouter().getStatistics().getStreamPumpStats());
6877
}
6978

7079
@Override
7180
public Outcome handleRequest(Exchange exc) {
7281
try {
7382
applyTargetModifications(exc);
74-
hc.call(exc);
83+
httpClient.call(exc);
7584
return RETURN;
7685
} catch (ConnectException e) {
7786
String msg = "Target %s is not reachable.".formatted(getDestination(exc));
@@ -131,6 +140,13 @@ public Outcome handleRequest(Exchange exc) {
131140
}
132141
}
133142

143+
// Used to update the httpClientConfig in tests
144+
public void updateHttpClientConfig(HttpClientConfiguration httpClientConfig) {
145+
this.httpClient = null;
146+
this.httpClientConfig = httpClientConfig;
147+
init();
148+
}
149+
134150
/**
135151
* Manipulates the target URL according to the target (change of method, URL expression)
136152
*

core/src/main/java/com/predic8/membrane/core/interceptor/jwt/Jwks.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -135,8 +135,7 @@ public String getJwk(Router router, ObjectMapper mapper) throws IOException {
135135
ResolverMap rm = router.getResolverMap();
136136

137137
if (httpClientConfig != null) {
138-
HTTPSchemaResolver httpSR = new HTTPSchemaResolver(router.getHttpClientFactory());
139-
httpSR.setHttpClientConfig(httpClientConfig);
138+
HTTPSchemaResolver httpSR = new HTTPSchemaResolver(router.getHttpClientFactory().createClient(httpClientConfig));
140139

141140
rm = rm.clone();
142141
rm.addSchemaResolver(httpSR);

core/src/main/java/com/predic8/membrane/core/proxies/SOAPProxy.java

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -78,10 +78,8 @@ public SOAPProxy() {
7878
public void init() {
7979
resolverMap = router.getResolverMap();
8080
if (httpClientConfig != null) {
81-
HTTPSchemaResolver httpSR = new HTTPSchemaResolver(router.getHttpClientFactory());
82-
httpSR.setHttpClientConfig(httpClientConfig);
8381
resolverMap = resolverMap.clone();
84-
resolverMap.addSchemaResolver(httpSR);
82+
resolverMap.addSchemaResolver(new HTTPSchemaResolver(router.getHttpClientFactory().createClient(httpClientConfig)));
8583
}
8684
configureFromWSDL();
8785
super.init(); // Must be called last! Otherwise, SSL will not be configured!
@@ -293,7 +291,7 @@ public HttpClientConfiguration getWsdlHttpClientConfig() {
293291
return httpClientConfig;
294292
}
295293

296-
@MCAttribute
294+
@MCChildElement
297295
public void setWsdlHttpClientConfig(HttpClientConfiguration httpClientConfig) {
298296
this.httpClientConfig = httpClientConfig;
299297
}

core/src/main/java/com/predic8/membrane/core/resolver/HTTPSchemaResolver.java

Lines changed: 29 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -14,28 +14,32 @@
1414

1515
package com.predic8.membrane.core.resolver;
1616

17-
import com.google.common.collect.*;
18-
import com.predic8.membrane.annot.*;
19-
import com.predic8.membrane.core.exchange.*;
20-
import com.predic8.membrane.core.http.*;
21-
import com.predic8.membrane.core.transport.http.*;
22-
import com.predic8.membrane.core.transport.http.client.*;
23-
import com.predic8.membrane.core.util.*;
24-
import com.predic8.membrane.core.util.functionalInterfaces.*;
25-
26-
import javax.annotation.*;
27-
import java.io.*;
28-
import java.net.*;
29-
import java.security.*;
30-
import java.util.*;
31-
import java.util.concurrent.*;
32-
33-
import static com.predic8.membrane.annot.Constants.*;
34-
import static com.predic8.membrane.core.http.Header.*;
17+
import com.google.common.collect.Lists;
18+
import com.predic8.membrane.annot.MCElement;
19+
import com.predic8.membrane.core.exchange.Exchange;
20+
import com.predic8.membrane.core.http.Response;
21+
import com.predic8.membrane.core.transport.http.HttpClient;
22+
import com.predic8.membrane.core.transport.http.HttpClientFactory;
23+
import com.predic8.membrane.core.transport.http.client.HttpClientConfiguration;
24+
import com.predic8.membrane.core.util.URIFactory;
25+
import com.predic8.membrane.core.util.functionalInterfaces.ExceptionThrowingConsumer;
26+
27+
import javax.annotation.Nullable;
28+
import java.io.ByteArrayInputStream;
29+
import java.io.InputStream;
30+
import java.net.URISyntaxException;
31+
import java.security.MessageDigest;
32+
import java.security.NoSuchAlgorithmException;
33+
import java.util.Arrays;
34+
import java.util.List;
35+
import java.util.concurrent.ConcurrentHashMap;
36+
37+
import static com.predic8.membrane.annot.Constants.PRODUCT_NAME;
38+
import static com.predic8.membrane.core.http.Header.USER_AGENT;
3539
import static com.predic8.membrane.core.http.Request.Builder;
36-
import static com.predic8.membrane.core.http.Request.*;
37-
import static java.lang.Thread.*;
38-
import static java.nio.charset.StandardCharsets.*;
40+
import static com.predic8.membrane.core.http.Request.METHOD_GET;
41+
import static java.lang.Thread.sleep;
42+
import static java.nio.charset.StandardCharsets.UTF_8;
3943

4044
@MCElement(name = "httpSchemaResolver")
4145
public class HTTPSchemaResolver implements SchemaResolver {
@@ -48,9 +52,7 @@ public class HTTPSchemaResolver implements SchemaResolver {
4852

4953
private static final byte[] NO_HASH = "NO_HASH".getBytes(UTF_8);
5054

51-
private HttpClientConfiguration httpClientConfig = new HttpClientConfiguration();
52-
53-
private HttpClient httpClient;
55+
private final HttpClient httpClient;
5456
private final URIFactory uriFactory = new URIFactory(false);
5557

5658
private final Runnable httpWatchJob = new Runnable() {
@@ -104,17 +106,8 @@ private Exchange createResolveExchange(String url) throws URISyntaxException {
104106
return new Builder().method(METHOD_GET).url(uriFactory, url).header(USER_AGENT, PRODUCT_NAME).buildExchange();
105107
}
106108

107-
public HTTPSchemaResolver(@Nullable HttpClientFactory httpClientFactory) {
108-
this.httpClientFactory = httpClientFactory;
109-
}
110-
111-
private synchronized HttpClient getHttpClient() {
112-
if (httpClient == null) {
113-
if (httpClientFactory == null)
114-
httpClientFactory = new HttpClientFactory(null);
115-
httpClient = httpClientFactory.createClient(httpClientConfig);
116-
}
117-
return httpClient;
109+
public HTTPSchemaResolver(@Nullable HttpClient httpClient) {
110+
this.httpClient = httpClient;
118111
}
119112

120113
@Override
@@ -125,7 +118,7 @@ public List<String> getSchemas() {
125118
public InputStream resolve(String url) throws ResourceRetrievalException {
126119
try {
127120
var resolveExchange = createResolveExchange(url);
128-
getHttpClient().call(resolveExchange);
121+
httpClient.call(resolveExchange);
129122
Response response = resolveExchange.getResponse();
130123
response.readBody();
131124

@@ -163,12 +156,4 @@ public long getTimestamp(String url) {
163156
return 0;
164157
}
165158

166-
public synchronized HttpClientConfiguration getHttpClientConfig() {
167-
return httpClientConfig;
168-
}
169-
170-
public synchronized void setHttpClientConfig(HttpClientConfiguration httpClientConfig) {
171-
this.httpClientConfig = httpClientConfig;
172-
httpClient = null;
173-
}
174159
}

core/src/main/java/com/predic8/membrane/core/resolver/ResolverMap.java

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import com.predic8.membrane.core.kubernetes.client.*;
2121
import com.predic8.membrane.core.router.*;
2222
import com.predic8.membrane.core.transport.http.*;
23+
import com.predic8.membrane.core.transport.http.client.HttpClientConfiguration;
2324
import com.predic8.membrane.core.util.*;
2425
import com.predic8.membrane.core.util.functionalInterfaces.*;
2526
import com.predic8.xml.util.*;
@@ -54,17 +55,18 @@ public class ResolverMap implements Cloneable, Resolver {
5455
private String[] schemas;
5556
private SchemaResolver[] resolvers;
5657

58+
5759
public ResolverMap() {
58-
this(null, null);
60+
this(new HttpClientFactory(null).createClient(new HttpClientConfiguration()), null);
5961
}
6062

61-
public ResolverMap(HttpClientFactory httpClientFactory, KubernetesClientFactory kubernetesClientFactory) {
63+
public ResolverMap(HttpClient httpClient, KubernetesClientFactory kubernetesClientFactory) {
6264
schemas = new String[10];
6365
resolvers = new SchemaResolver[10];
6466

6567
// the default config
6668
addSchemaResolver(new ClasspathSchemaResolver());
67-
addSchemaResolver(new HTTPSchemaResolver(httpClientFactory));
69+
addSchemaResolver(new HTTPSchemaResolver(httpClient));
6870
addSchemaResolver(new KubernetesSchemaResolver(kubernetesClientFactory));
6971
addSchemaResolver(new FileSchemaResolver());
7072
}
@@ -104,7 +106,7 @@ private static String combineInternal(URIFactory factory,String... locations) {
104106
// lfold
105107
String[] l = new String[locations.length - 1];
106108
System.arraycopy(locations, 0, l, 0, locations.length - 1);
107-
return combine(factory,combine(l), locations[locations.length - 1]);
109+
return combine(factory,combine(factory,l), locations[locations.length - 1]);
108110
}
109111

110112
return combineInternal2( factory, locations,locations[1], locations[0]);

core/src/main/java/com/predic8/membrane/core/router/Configuration.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
import com.predic8.membrane.annot.*;
1818
import com.predic8.membrane.core.interceptor.administration.*;
1919
import com.predic8.membrane.core.proxies.*;
20+
import com.predic8.membrane.core.transport.http.client.HttpClientConfiguration;
2021
import com.predic8.membrane.core.util.*;
2122

2223
/**
@@ -44,6 +45,8 @@ public class Configuration {
4445

4546
private String baseLocation;
4647

48+
private HttpClientConfiguration httpClientConfig = new HttpClientConfiguration();
49+
4750
/**
4851
* @param hotDeploy If true the hot deploy feature will be activated during init of the Router.
4952
* @description <p>Whether changes to the router's configuration file should automatically trigger a restart.
@@ -144,6 +147,20 @@ public URIFactory getUriFactory() {
144147
return uriFactory;
145148
}
146149

150+
/**
151+
* @description A global &lt;httpClientConfig&gt;. This instance is used everywhere
152+
* a HTTP Client is used. In every specific place, you should still be able to configure a local
153+
* &lt;httpClientConfig&gt; (with higher precedence compared to this global instance).
154+
*/
155+
@MCChildElement
156+
public void setHttpClientConfig(HttpClientConfiguration httpClientConfig) {
157+
this.httpClientConfig = httpClientConfig;
158+
}
159+
160+
public HttpClientConfiguration getHttpClientConfig() {
161+
return httpClientConfig;
162+
}
163+
147164
/**
148165
* @description Base location for the router's configuration file.
149166
* @param baseLocation The base directory path used to resolve relative file paths in configuration

core/src/main/java/com/predic8/membrane/core/router/DefaultMainComponents.java

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,11 @@ public class DefaultMainComponents implements MainComponents {
4646

4747
private final TimerManager timerManager = new TimerManager();
4848
private final HttpClientFactory httpClientFactory = new HttpClientFactory(timerManager);
49+
50+
private HttpClient httpClient = new HttpClient();
51+
4952
private final KubernetesClientFactory kubernetesClientFactory = new KubernetesClientFactory(httpClientFactory);
50-
private final ResolverMap resolverMap;
53+
private ResolverMap resolverMap = new ResolverMap();
5154

5255
private final FlowController flowController;
5356
private final RuleManager ruleManager;
@@ -58,22 +61,24 @@ public class DefaultMainComponents implements MainComponents {
5861
public DefaultMainComponents(DefaultRouter router) {
5962
log.debug("Creating new router.");
6063
this.router = router;
61-
resolverMap = new ResolverMap(httpClientFactory, kubernetesClientFactory);
62-
resolverMap.addRuleResolver(router);
6364
flowController = new FlowController(router);
6465
ruleManager= new RuleManager();
6566
ruleManager.setRouter(router);
6667
}
6768

6869
public void init() {
70+
httpClient = httpClientFactory.createClient(getHttpClientConfig());
71+
resolverMap = new ResolverMap(httpClient, kubernetesClientFactory);
72+
resolverMap.addRuleResolver(router);
73+
6974
log.debug("Initializing.");
7075

7176
if (registry == null) {
7277
registry = new BeanRegistryImplementation(null);
7378
registry.register("router", router);
7479
}
7580

76-
registry.registerIfAbsent(HttpClientConfiguration.class, HttpClientConfiguration::new);
81+
registry.registerIfAbsent(HttpClientConfiguration.class, () -> router.getConfiguration().getHttpClientConfig());
7782
registry.registerIfAbsent(ExchangeStore.class, LimitedMemoryExchangeStore::new);
7883
registry.registerIfAbsent(DNSCache.class, DNSCache::new);
7984

@@ -126,14 +131,6 @@ public void setTransport(Transport transport) {
126131
this.transport = transport;
127132
}
128133

129-
public HttpClientConfiguration getHttpClientConfig() {
130-
return getResolverMap().getHTTPSchemaResolver().getHttpClientConfig();
131-
}
132-
133-
public void setHttpClientConfig(HttpClientConfiguration httpClientConfig) {
134-
getResolverMap().getHTTPSchemaResolver().setHttpClientConfig(httpClientConfig);
135-
}
136-
137134
@Override
138135
public DNSCache getDnsCache() {
139136
return getRegistry().getBean(DNSCache.class).orElseThrow(); // TODO
@@ -167,6 +164,15 @@ public HttpClientFactory getHttpClientFactory() {
167164
return httpClientFactory;
168165
}
169166

167+
@Override
168+
public HttpClientConfiguration getHttpClientConfig() {
169+
return router.getConfiguration().getHttpClientConfig();
170+
}
171+
172+
public HttpClient getHttpClient() {
173+
return httpClient;
174+
}
175+
170176
public FlowController getFlowController() {
171177
return flowController;
172178
}

0 commit comments

Comments
 (0)