Skip to content
This repository was archived by the owner on Sep 16, 2024. It is now read-only.

Commit 95cc9f6

Browse files
committed
#244 Construction of a RestTemplate can now be customized
1 parent 3ebcf5a commit 95cc9f6

File tree

7 files changed

+201
-71
lines changed

7 files changed

+201
-71
lines changed
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package com.marklogic.rest.util;
2+
3+
import org.apache.http.impl.client.HttpClientBuilder;
4+
5+
public interface HttpClientBuilderConfigurer {
6+
7+
HttpClientBuilder configureHttpClientBuilder(RestConfig restConfig, HttpClientBuilder httpClientBuilder);
8+
9+
}
Lines changed: 46 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -1,102 +1,77 @@
11
package com.marklogic.rest.util;
22

3-
import org.apache.http.auth.AuthScope;
4-
import org.apache.http.auth.UsernamePasswordCredentials;
3+
import com.marklogic.rest.util.configurer.BasicAuthConfigurer;
4+
import com.marklogic.rest.util.configurer.NoConnectionReuseConfigurer;
5+
import com.marklogic.rest.util.configurer.SslConfigurer;
6+
import com.marklogic.rest.util.configurer.UseSystemPropertiesConfigurer;
57
import org.apache.http.client.HttpClient;
6-
import org.apache.http.conn.ssl.SSLContextBuilder;
7-
import org.apache.http.conn.ssl.TrustStrategy;
8-
import org.apache.http.conn.ssl.X509HostnameVerifier;
9-
import org.apache.http.impl.NoConnectionReuseStrategy;
10-
import org.apache.http.impl.client.BasicCredentialsProvider;
118
import org.apache.http.impl.client.HttpClientBuilder;
129
import org.slf4j.Logger;
1310
import org.slf4j.LoggerFactory;
1411
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
1512
import org.springframework.web.client.RestTemplate;
1613

17-
import javax.net.ssl.SSLContext;
18-
import javax.net.ssl.SSLSession;
19-
import javax.net.ssl.SSLSocket;
20-
import java.security.cert.X509Certificate;
21-
14+
import java.util.ArrayList;
15+
import java.util.List;
16+
17+
/**
18+
* Factory class for constructing a Spring RestTemplate for communicating with the MarkLogic Manage API.
19+
* <p>
20+
* This class uses an org.apache.http.impl.client.HttpClientBuilder to construct an
21+
* org.apache.http.client.HttpClient that is then used to construct a RestTemplate. Instances of the interface
22+
* HttpClientBuilderConfigurer can be passed in to customize how the HttpClient is constructed. If no instances are passed
23+
* in, then the configurers defined by DEFAULT_CONFIGURERS are used.
24+
* </p>
25+
* <p>
26+
* The DEFAULT_CONFIGURERS variable is public and static but not final so that clients of ml-app-deployer can
27+
* fiddle with it to customize how classes within ml-app-deployer construct a RestTemplate.
28+
* </p>
29+
*/
2230
public class RestTemplateUtil {
2331

2432
private final static Logger logger = LoggerFactory.getLogger(RestTemplateUtil.class);
2533

34+
public static List<HttpClientBuilderConfigurer> DEFAULT_CONFIGURERS = new ArrayList<>();
35+
36+
static {
37+
DEFAULT_CONFIGURERS.add(new BasicAuthConfigurer());
38+
DEFAULT_CONFIGURERS.add(new SslConfigurer());
39+
DEFAULT_CONFIGURERS.add(new NoConnectionReuseConfigurer());
40+
DEFAULT_CONFIGURERS.add(new UseSystemPropertiesConfigurer());
41+
}
42+
2643
public static RestTemplate newRestTemplate(String host, int port, String username, String password) {
2744
return newRestTemplate(new RestConfig(host, port, username, password));
2845
}
2946

30-
public static RestTemplate newRestTemplate(RestConfig config) {
31-
HttpClientBuilder httpClientBuilder = HttpClientBuilder.create();
47+
public static RestTemplate newRestTemplate(String host, int port, String username, String password, HttpClientBuilderConfigurer... configurers) {
48+
return newRestTemplate(new RestConfig(host, port, username, password), configurers);
49+
}
3250

33-
String username = config.getUsername();
34-
if (username != null) {
35-
BasicCredentialsProvider prov = new BasicCredentialsProvider();
36-
prov.setCredentials(new AuthScope(config.getHost(), config.getPort(), AuthScope.ANY_REALM),
37-
new UsernamePasswordCredentials(username, config.getPassword()));
38-
httpClientBuilder = httpClientBuilder.setDefaultCredentialsProvider(prov);
39-
}
51+
public static RestTemplate newRestTemplate(RestConfig config) {
52+
return newRestTemplate(config, DEFAULT_CONFIGURERS);
53+
}
4054

41-
if (config.isConfigureSimpleSsl()) {
42-
if (logger.isInfoEnabled()) {
43-
logger.info("Configuring simple SSL approach for connecting to: " + config.getBaseUrl());
44-
}
45-
configureSimpleSsl(httpClientBuilder);
46-
}
55+
public static RestTemplate newRestTemplate(RestConfig config, List<HttpClientBuilderConfigurer> configurers) {
56+
return newRestTemplate(config, configurers.toArray(new HttpClientBuilderConfigurer[]{}));
57+
}
4758

48-
if (config.getSslContext() != null) {
49-
if (logger.isInfoEnabled()) {
50-
logger.info("Using custom SSLContext for connecting to: " + config.getBaseUrl());
51-
}
52-
httpClientBuilder.setSslcontext(config.getSslContext());
53-
}
59+
public static RestTemplate newRestTemplate(RestConfig config, HttpClientBuilderConfigurer... configurers) {
60+
HttpClientBuilder httpClientBuilder = HttpClientBuilder.create();
5461

55-
if (config.getHostnameVerifier() != null) {
56-
if (logger.isInfoEnabled()) {
57-
logger.info("Using custom X509HostnameVerifier for connecting to: " + config.getBaseUrl());
62+
if (configurers != null) {
63+
for (HttpClientBuilderConfigurer configurer : configurers) {
64+
if (logger.isDebugEnabled()) {
65+
logger.debug("Applying HttpClientBuilderConfigurer: " + configurer);
66+
}
67+
httpClientBuilder = configurer.configureHttpClientBuilder(config, httpClientBuilder);
5868
}
59-
httpClientBuilder.setHostnameVerifier(config.getHostnameVerifier());
6069
}
6170

62-
httpClientBuilder.setConnectionReuseStrategy(new NoConnectionReuseStrategy());
6371
HttpClient client = httpClientBuilder.build();
64-
6572
RestTemplate rt = new RestTemplate(new HttpComponentsClientHttpRequestFactory(client));
6673
rt.setErrorHandler(new MgmtResponseErrorHandler());
6774
return rt;
6875
}
6976

70-
private static void configureSimpleSsl(HttpClientBuilder httpClientBuilder) {
71-
try {
72-
SSLContext sslContext = new SSLContextBuilder().loadTrustMaterial(null, new TrustStrategy() {
73-
@Override
74-
public boolean isTrusted(X509Certificate[] chain, String authType) {
75-
return true;
76-
}
77-
}).build();
78-
httpClientBuilder.setSslcontext(sslContext);
79-
} catch (Exception ex) {
80-
throw new RuntimeException("Unable to configure simple SSLContext, cause: " + ex.getMessage(), ex);
81-
}
82-
83-
httpClientBuilder.setHostnameVerifier(new X509HostnameVerifier() {
84-
@Override
85-
public void verify(String host, SSLSocket ssl) {
86-
}
87-
88-
@Override
89-
public void verify(String host, X509Certificate cert) {
90-
}
91-
92-
@Override
93-
public void verify(String host, String[] cns, String[] subjectAlts) {
94-
}
95-
96-
@Override
97-
public boolean verify(String s, SSLSession sslSession) {
98-
return false;
99-
}
100-
});
101-
}
10277
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package com.marklogic.rest.util.configurer;
2+
3+
import com.marklogic.rest.util.HttpClientBuilderConfigurer;
4+
import com.marklogic.rest.util.RestConfig;
5+
import org.apache.http.auth.AuthScope;
6+
import org.apache.http.auth.UsernamePasswordCredentials;
7+
import org.apache.http.impl.client.BasicCredentialsProvider;
8+
import org.apache.http.impl.client.HttpClientBuilder;
9+
10+
public class BasicAuthConfigurer implements HttpClientBuilderConfigurer {
11+
12+
@Override
13+
public HttpClientBuilder configureHttpClientBuilder(RestConfig config, HttpClientBuilder httpClientBuilder) {
14+
String username = config.getUsername();
15+
if (username != null) {
16+
BasicCredentialsProvider prov = new BasicCredentialsProvider();
17+
prov.setCredentials(new AuthScope(config.getHost(), config.getPort(), AuthScope.ANY_REALM),
18+
new UsernamePasswordCredentials(username, config.getPassword()));
19+
return httpClientBuilder.setDefaultCredentialsProvider(prov);
20+
}
21+
return httpClientBuilder;
22+
}
23+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package com.marklogic.rest.util.configurer;
2+
3+
import com.marklogic.rest.util.HttpClientBuilderConfigurer;
4+
import com.marklogic.rest.util.RestConfig;
5+
import org.apache.http.impl.NoConnectionReuseStrategy;
6+
import org.apache.http.impl.client.HttpClientBuilder;
7+
8+
public class NoConnectionReuseConfigurer implements HttpClientBuilderConfigurer {
9+
10+
@Override
11+
public HttpClientBuilder configureHttpClientBuilder(RestConfig restConfig, HttpClientBuilder httpClientBuilder) {
12+
return httpClientBuilder.setConnectionReuseStrategy(new NoConnectionReuseStrategy());
13+
}
14+
15+
}
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
package com.marklogic.rest.util.configurer;
2+
3+
import com.marklogic.client.ext.helper.LoggingObject;
4+
import com.marklogic.rest.util.HttpClientBuilderConfigurer;
5+
import com.marklogic.rest.util.RestConfig;
6+
import org.apache.http.conn.ssl.SSLContextBuilder;
7+
import org.apache.http.conn.ssl.TrustStrategy;
8+
import org.apache.http.conn.ssl.X509HostnameVerifier;
9+
import org.apache.http.impl.client.HttpClientBuilder;
10+
11+
import javax.net.ssl.SSLContext;
12+
import javax.net.ssl.SSLSession;
13+
import javax.net.ssl.SSLSocket;
14+
import java.security.cert.X509Certificate;
15+
16+
public class SslConfigurer extends LoggingObject implements HttpClientBuilderConfigurer {
17+
18+
@Override
19+
public HttpClientBuilder configureHttpClientBuilder(RestConfig config, HttpClientBuilder httpClientBuilder) {
20+
if (config.isConfigureSimpleSsl()) {
21+
if (logger.isInfoEnabled()) {
22+
logger.info("Configuring simple SSL approach for connecting to: " + config.getBaseUrl());
23+
}
24+
configureSimpleSsl(httpClientBuilder);
25+
}
26+
27+
if (config.getSslContext() != null) {
28+
if (logger.isInfoEnabled()) {
29+
logger.info("Using custom SSLContext for connecting to: " + config.getBaseUrl());
30+
}
31+
httpClientBuilder.setSslcontext(config.getSslContext());
32+
}
33+
34+
if (config.getHostnameVerifier() != null) {
35+
if (logger.isInfoEnabled()) {
36+
logger.info("Using custom X509HostnameVerifier for connecting to: " + config.getBaseUrl());
37+
}
38+
httpClientBuilder.setHostnameVerifier(config.getHostnameVerifier());
39+
}
40+
41+
return httpClientBuilder;
42+
}
43+
44+
protected void configureSimpleSsl(HttpClientBuilder httpClientBuilder) {
45+
try {
46+
SSLContext sslContext = new SSLContextBuilder().loadTrustMaterial(null, new TrustStrategy() {
47+
@Override
48+
public boolean isTrusted(X509Certificate[] chain, String authType) {
49+
return true;
50+
}
51+
}).build();
52+
httpClientBuilder.setSslcontext(sslContext);
53+
} catch (Exception ex) {
54+
throw new RuntimeException("Unable to configure simple SSLContext, cause: " + ex.getMessage(), ex);
55+
}
56+
57+
httpClientBuilder.setHostnameVerifier(new X509HostnameVerifier() {
58+
@Override
59+
public void verify(String host, SSLSocket ssl) {
60+
}
61+
62+
@Override
63+
public void verify(String host, X509Certificate cert) {
64+
}
65+
66+
@Override
67+
public void verify(String host, String[] cns, String[] subjectAlts) {
68+
}
69+
70+
@Override
71+
public boolean verify(String s, SSLSession sslSession) {
72+
return false;
73+
}
74+
});
75+
}
76+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package com.marklogic.rest.util.configurer;
2+
3+
import com.marklogic.rest.util.HttpClientBuilderConfigurer;
4+
import com.marklogic.rest.util.RestConfig;
5+
import org.apache.http.impl.client.HttpClientBuilder;
6+
7+
public class UseSystemPropertiesConfigurer implements HttpClientBuilderConfigurer {
8+
9+
@Override
10+
public HttpClientBuilder configureHttpClientBuilder(RestConfig restConfig, HttpClientBuilder httpClientBuilder) {
11+
return httpClientBuilder.useSystemProperties();
12+
}
13+
14+
}

src/test/java/com/marklogic/mgmt/rest/util/RestTemplateUtilTest.java

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import com.marklogic.mgmt.AbstractMgmtTest;
44
import com.marklogic.mgmt.ManageClient;
5+
import com.marklogic.rest.util.RestTemplateUtil;
56
import org.apache.http.conn.ssl.SSLContextBuilder;
67
import org.apache.http.conn.ssl.TrustStrategy;
78
import org.apache.http.conn.ssl.X509HostnameVerifier;
@@ -14,6 +15,23 @@
1415

1516
public class RestTemplateUtilTest extends AbstractMgmtTest {
1617

18+
private boolean configurerInvoked = false;
19+
20+
@Test
21+
public void configurerList() {
22+
assertEquals(4, RestTemplateUtil.DEFAULT_CONFIGURERS.size());
23+
24+
assertFalse(configurerInvoked);
25+
RestTemplateUtil.DEFAULT_CONFIGURERS.add((restConfig, builder) -> {
26+
logger.info("Just a test of adding a configurer");
27+
configurerInvoked = true;
28+
return builder;
29+
});
30+
31+
new ManageClient(manageConfig);
32+
assertTrue(configurerInvoked);
33+
}
34+
1735
/**
1836
* Just a smoke test to inspect the logging.
1937
*/

0 commit comments

Comments
 (0)