Skip to content

Commit 152b53d

Browse files
authored
Merge pull request #1519 from marklogic/feature/164-properties-function
DEVEXP-164: Added new factory method for DatabaseClient
2 parents 8b9d274 + 275d3eb commit 152b53d

File tree

19 files changed

+932
-149
lines changed

19 files changed

+932
-149
lines changed

marklogic-client-api-functionaltests/src/test/java/com/marklogic/client/fastfunctest/AbstractFunctionalTest.java

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -56,9 +56,8 @@ public static void initializeClients() throws Exception {
5656
System.out.println("ML version: " + version.getVersionString());
5757
isML11OrHigher = version.getMajor() >= 11;
5858

59-
client = newClientAsUser(OPTIC_USER, OPTIC_USER_PASSWORD);
60-
schemasClient = newClient(getRestServerHostName(), getRestServerPort(), "java-functest-schemas",
61-
newSecurityContext(OPTIC_USER, OPTIC_USER_PASSWORD), null);
59+
client = newDatabaseClientBuilder().build();
60+
schemasClient = newClientForDatabase("java-functest-schemas");
6261
adminModulesClient = newAdminModulesClient();
6362

6463
// Required to ensure that tests using the "/ext/" prefix work reliably. Expand to other directories as needed.

marklogic-client-api-functionaltests/src/test/java/com/marklogic/client/functionaltest/ConnectedRESTQA.java

Lines changed: 58 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,8 @@
2323
import com.fasterxml.jackson.databind.node.ObjectNode;
2424
import com.marklogic.client.DatabaseClient;
2525
import com.marklogic.client.DatabaseClient.ConnectionType;
26+
import com.marklogic.client.DatabaseClientBuilder;
2627
import com.marklogic.client.DatabaseClientFactory;
27-
import com.marklogic.client.DatabaseClientFactory.SecurityContext;
2828
import com.marklogic.client.admin.ServerConfigurationManager;
2929
import com.marklogic.client.impl.OkHttpServices;
3030
import com.marklogic.client.impl.RESTServices;
@@ -48,6 +48,8 @@
4848

4949
public abstract class ConnectedRESTQA {
5050

51+
protected static Properties testProperties = null;
52+
5153
protected static String securityContextType;
5254
protected static String restServerName = null;
5355
private static String restSslServerName = null;
@@ -2019,45 +2021,64 @@ else if (getSslEnabled().trim().equalsIgnoreCase("false") || getSslEnabled() ==
20192021
return bSecurityEnabled;
20202022
}
20212023

2024+
public static DatabaseClientBuilder newDatabaseClientBuilder() {
2025+
Map<String, Object> props = new HashMap<>();
2026+
testProperties.entrySet().forEach(entry -> props.put((String) entry.getKey(), entry.getValue()));
2027+
return new DatabaseClientBuilder(props);
2028+
}
2029+
2030+
public static DatabaseClient newBasicAuthClient(String username, String password) {
2031+
return newDatabaseClientBuilder()
2032+
.withUsername(username)
2033+
.withPassword(password)
2034+
.withSecurityContextType("basic")
2035+
.build();
2036+
}
2037+
20222038
public static DatabaseClient newClientAsUser(String username, String password) {
2023-
return newClient(getRestServerHostName(), getRestServerPort(), null, newSecurityContext(username, password), null);
2039+
return newDatabaseClientBuilder()
2040+
.withUsername(username)
2041+
.withPassword(password)
2042+
.build();
20242043
}
20252044

2026-
public static DatabaseClient newAdminModulesClient() {
2027-
return newClient(getRestServerHostName(), getRestServerPort(), "java-unittest-modules",
2028-
newSecurityContext(getAdminUser(), getAdminPassword()), null);
2045+
public static DatabaseClient newClientForDatabase(String database) {
2046+
return newDatabaseClientBuilder()
2047+
.withDatabase(database)
2048+
.build();
20292049
}
20302050

2031-
public static DatabaseClient newBasicAuthClient(String username, String password) {
2032-
return newClient(getRestServerHostName(), getRestServerPort(), null,
2033-
new DatabaseClientFactory.BasicAuthContext(username, password), null);
2051+
public static DatabaseClient newAdminModulesClient() {
2052+
return newDatabaseClientBuilder()
2053+
.withUsername(getAdminUser())
2054+
.withPassword(getAdminPassword())
2055+
.withDatabase("java-unittest-modules")
2056+
.build();
20342057
}
20352058

20362059
public static DatabaseClient getDatabaseClient(String user, String password, ConnectionType connType)
20372060
throws KeyManagementException, NoSuchAlgorithmException, IOException {
2038-
return newClient(getRestServerHostName(), getRestServerPort(), null, newSecurityContext(user, password), connType);
2061+
return newDatabaseClientBuilder()
2062+
.withUsername(user)
2063+
.withPassword(password)
2064+
.withConnectionType(connType)
2065+
.build();
20392066
}
20402067

20412068
/**
2042-
* Intent is for every functional test to create a client ultimately via this method so that basePath can be
2043-
* applied in one place.
2044-
*
2045-
* @param host
2046-
* @param port
2047-
* @param database
2048-
* @param securityContext
2049-
* @param connectionType
2050-
* @return
2069+
* Only use this in "slow" functional tests until they're converted over to fast.
20512070
*/
2052-
public static DatabaseClient newClient(String host, int port, String database,
2053-
SecurityContext securityContext, ConnectionType connectionType) {
2054-
connectionType = connectionType != null ? connectionType : getConnType();
2055-
return DatabaseClientFactory.newClient(host, port, basePath, database, securityContext, connectionType);
2056-
}
2057-
2071+
@Deprecated
20582072
public static DatabaseClient getDatabaseClientOnDatabase(String hostName, int port, String databaseName,
2059-
String user, String password, ConnectionType connType) {
2060-
return newClient(hostName, port, databaseName, newSecurityContext(user, password), connType);
2073+
String user, String password, ConnectionType connType) {
2074+
return newDatabaseClientBuilder()
2075+
.withHost(hostName)
2076+
.withPort(port)
2077+
.withUsername(user)
2078+
.withPassword(password)
2079+
.withDatabase(databaseName)
2080+
.withConnectionType(connType)
2081+
.build();
20612082
}
20622083

20632084
//Return a Server name. For SSL runs returns value in restSslServerName For
@@ -2090,9 +2111,9 @@ private static void overrideTestPropertiesWithSystemProperties(Properties testPr
20902111
if ("true".equals(System.getProperty("TEST_USE_REVERSE_PROXY_SERVER"))) {
20912112
System.out.println("TEST_USE_REVERSE_PROXY_SERVER is true, so overriding properties to use reverse proxy server");
20922113
testProperties.setProperty("httpPort", "8020");
2093-
testProperties.setProperty("fastHttpPort", "8020");
2094-
testProperties.setProperty("basePath", "testFunctional");
2095-
testProperties.setProperty("securityContextType", "basic");
2114+
testProperties.setProperty("marklogic.client.port", "8020");
2115+
testProperties.setProperty("marklogic.client.basePath", "testFunctional");
2116+
testProperties.setProperty("marklogic.client.securityContextType", "basic");
20962117
}
20972118
}
20982119

@@ -2110,18 +2131,18 @@ public static void loadGradleProperties() {
21102131

21112132
overrideTestPropertiesWithSystemProperties(properties);
21122133

2113-
securityContextType = properties.getProperty("securityContextType");
2134+
securityContextType = properties.getProperty("marklogic.client.securityContextType");
21142135
restServerName = properties.getProperty("mlAppServerName");
21152136
restSslServerName = properties.getProperty("mlAppServerSSLName");
21162137

21172138
https_port = properties.getProperty("httpsPort");
21182139
http_port = properties.getProperty("httpPort");
2119-
fast_http_port = properties.getProperty("fastHttpPort");
2120-
admin_port = properties.getProperty("adminPort");
2121-
basePath = properties.getProperty("basePath");
2140+
fast_http_port = properties.getProperty("marklogic.client.port");
2141+
admin_port = "8002"; // No need yet for a property for this
2142+
basePath = properties.getProperty("marklogic.client.basePath");
21222143

21232144
// Machine names where ML Server runs
2124-
host_name = properties.getProperty("restHost");
2145+
host_name = properties.getProperty("marklogic.client.host");
21252146
ssl_host_name = properties.getProperty("restSSLHost");
21262147

21272148
// Users
@@ -2136,9 +2157,11 @@ public static void loadGradleProperties() {
21362157
ml_certificate_password = properties.getProperty("ml_certificate_password");
21372158
ml_certificate_file = properties.getProperty("ml_certificate_file");
21382159
mlDataConfigDirPath = properties.getProperty("mlDataConfigDirPath");
2139-
isLBHost = Boolean.parseBoolean(properties.getProperty("lbHost"));
2160+
isLBHost = "gateway".equalsIgnoreCase(properties.getProperty("marklogic.client.connectionType"));
21402161
PROPERTY_WAIT = Integer.parseInt(isLBHost ? "15000" : "0");
21412162

2163+
testProperties = properties;
2164+
21422165
System.out.println("For 'slow' tests, will connect to: " + host_name + ":" + http_port + "; basePath: " + basePath +
21432166
"; auth: " + securityContextType);
21442167
System.out.println("For 'fast' tests, will connect to: " + host_name + ":" + fast_http_port + "; basePath: " + basePath +

marklogic-client-api-functionaltests/src/test/resources/test.properties

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,26 @@
1-
securityContextType=digest
1+
# Standard properties for constructing a DatabaseClient
2+
marklogic.client.host=localhost
3+
marklogic.client.port=8014
4+
marklogic.client.securityContextType=digest
5+
marklogic.client.username=opticUser
6+
marklogic.client.password=0pt1c
7+
marklogic.client.basePath=
8+
marklogic.client.type=direct
9+
10+
# Custom properties used by ConnectedRESTQA
211
restSSLset=false
3-
restHost=localhost
412
restSSLHost=localhost
513
mlAdminUser=admin
614
mlAdminPassword=admin
715
mlRestReadUser=rest-reader
816
mlRestReadPassword=x
917
mlAppServerName=REST-Java-Client-API-Server
1018
mlAppServerSSLName=REST-Java-Client-API-SSL-Server
19+
20+
# Used by "slow" functional tests that don't use the test-app's application
1121
httpPort=8011
12-
# For fastfunctest tests
13-
fastHttpPort=8014
1422
httpsPort=8013
15-
adminPort=8002
16-
basePath=
17-
lbHost=false
23+
1824
ml_certificate_password=welcome
1925
ml_certificate_file=user.p12
2026

Lines changed: 173 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
1+
/*
2+
* Copyright (c) 2022 MarkLogic Corporation
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package com.marklogic.client;
17+
18+
import com.marklogic.client.impl.DatabaseClientPropertySource;
19+
20+
import javax.net.ssl.SSLContext;
21+
import javax.net.ssl.X509TrustManager;
22+
import java.util.HashMap;
23+
import java.util.Map;
24+
import java.util.function.Function;
25+
26+
/**
27+
* Intended to support programmatically building a {@code DatabaseClient} via chained "with" methods for setting
28+
* each possible input allowed for connecting to and authenticating with MarkLogic. While the
29+
* {@code DatabaseClientFactory.Bean} class is intended for use in a context such as a Spring container, it requires
30+
* that a user have already assembled the appropriate {@code DatabaseClientFactory.SecurityContext}. This builder
31+
* instead is intended for a more dynamic environment - in particular, one where the desired authentication strategy is
32+
* not known until runtime. A client can then collect inputs from a user at runtime and call the appropriate methods on
33+
* this builder. The builder will handle constructing the correct {@code DatabaseClientFactory.SecurityContext} and
34+
* using that to construct a {@code DatabaseClient}.
35+
*
36+
* @since 6.1.0
37+
*/
38+
public class DatabaseClientBuilder {
39+
40+
public final static String PREFIX = "marklogic.client.";
41+
private final Map<String, Object> props;
42+
43+
public DatabaseClientBuilder() {
44+
this.props = new HashMap<>();
45+
}
46+
47+
/**
48+
* Initialize the builder with the given set of properties.
49+
*
50+
* @param props
51+
*/
52+
public DatabaseClientBuilder(Map<String, Object> props) {
53+
this();
54+
this.props.putAll(props);
55+
}
56+
57+
/**
58+
* @return a {@code DatabaseClient} based on the inputs that have been provided via the "with" builder methods
59+
* and any inputs provided via this instance's constructor
60+
*/
61+
public DatabaseClient build() {
62+
return DatabaseClientFactory.newClient(getPropertySource());
63+
}
64+
65+
/**
66+
* @return an instance of {@code DatabaseClientFactory.Bean} based on the inputs that have been provided via
67+
* the "with" builder methods and any inputs provided via this instance's constructor
68+
*/
69+
public DatabaseClientFactory.Bean buildBean() {
70+
return new DatabaseClientPropertySource(getPropertySource()).newClientBean();
71+
}
72+
73+
/**
74+
* @return a function that acts as a property source, specifically for use with the
75+
* {@code DatabaseClientFactory.newClient} method that accepts this type of function.
76+
*/
77+
private Function<String, Object> getPropertySource() {
78+
return propertyName -> props.get(propertyName);
79+
}
80+
81+
public DatabaseClientBuilder withHost(String host) {
82+
props.put(PREFIX + "host", host);
83+
return this;
84+
}
85+
86+
public DatabaseClientBuilder withPort(int port) {
87+
props.put(PREFIX + "port", port);
88+
return this;
89+
}
90+
91+
public DatabaseClientBuilder withBasePath(String basePath) {
92+
props.put(PREFIX + "basePath", basePath);
93+
return this;
94+
}
95+
96+
public DatabaseClientBuilder withDatabase(String database) {
97+
props.put(PREFIX + "database", database);
98+
return this;
99+
}
100+
101+
public DatabaseClientBuilder withUsername(String username) {
102+
props.put(PREFIX + "username", username);
103+
return this;
104+
}
105+
106+
public DatabaseClientBuilder withPassword(String password) {
107+
props.put(PREFIX + "password", password);
108+
return this;
109+
}
110+
111+
public DatabaseClientBuilder withSecurityContext(DatabaseClientFactory.SecurityContext securityContext) {
112+
props.put(PREFIX + "securityContext", securityContext);
113+
return this;
114+
}
115+
116+
public DatabaseClientBuilder withSecurityContextType(String type) {
117+
props.put(PREFIX + "securityContextType", type);
118+
return this;
119+
}
120+
121+
public DatabaseClientBuilder withConnectionType(DatabaseClient.ConnectionType type) {
122+
props.put(PREFIX + "connectionType", type);
123+
return this;
124+
}
125+
126+
public DatabaseClientBuilder withCloudApiKey(String cloudApiKey) {
127+
props.put(PREFIX + "cloud.apiKey", cloudApiKey);
128+
return this;
129+
}
130+
131+
public DatabaseClientBuilder withCertificateFile(String file) {
132+
props.put(PREFIX + "certificate.file", file);
133+
return this;
134+
}
135+
136+
public DatabaseClientBuilder withCertificatePassword(String password) {
137+
props.put(PREFIX + "certificate.password", password);
138+
return this;
139+
}
140+
141+
public DatabaseClientBuilder withKerberosPrincipal(String principal) {
142+
props.put(PREFIX + "kerberos.principal", principal);
143+
return this;
144+
}
145+
146+
public DatabaseClientBuilder withSAMLToken(String token) {
147+
props.put(PREFIX + "saml.token", token);
148+
return this;
149+
}
150+
151+
public DatabaseClientBuilder withSSLContext(SSLContext sslContext) {
152+
props.put(PREFIX + "sslContext", sslContext);
153+
return this;
154+
}
155+
156+
public DatabaseClientBuilder withSSLProtocol(String sslProtocol) {
157+
props.put(PREFIX + "sslProtocol", sslProtocol);
158+
return this;
159+
}
160+
161+
public DatabaseClientBuilder withTrustManager(X509TrustManager trustManager) {
162+
props.put(PREFIX + "trustManager", trustManager);
163+
return this;
164+
}
165+
166+
public DatabaseClientBuilder withSSLHostnameVerifier(DatabaseClientFactory.SSLHostnameVerifier sslHostnameVerifier) {
167+
props.put(PREFIX + "sslHostnameVerifier", sslHostnameVerifier);
168+
return this;
169+
}
170+
}
171+
172+
173+

0 commit comments

Comments
 (0)