Skip to content

Commit bb8a318

Browse files
committed
JCO-19 Support standard connection string parameter names
Motivation ---------- Standardize connection string parameter names across Columnar SDKs Modifications ------------- Throw if connection string parameter has uppercase letter. BuilderPropertySetter now applies a function that transforms each path component (for example, from lower_snake_case to lowerCamelCase). Remove the `tls_verify` alias in favor of standardizing on `security.disable_server_certificate_verification`. Change-Id: Id66d5bcae951898915638cfab60aede120f72b69 Reviewed-on: https://review.couchbase.org/c/couchbase-jvm-clients/+/217176 Tested-by: Build Bot <build@couchbase.com> Reviewed-by: David Nault <david.nault@couchbase.com>
1 parent fbc926d commit bb8a318

File tree

3 files changed

+49
-30
lines changed

3 files changed

+49
-30
lines changed

columnar-java-client/src/main/java/com/couchbase/columnar/client/java/Cluster.java

Lines changed: 33 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,6 @@
3232
import java.io.Closeable;
3333
import java.time.Duration;
3434
import java.util.Collections;
35-
import java.util.LinkedHashMap;
3635
import java.util.Map;
3736
import java.util.Optional;
3837
import java.util.concurrent.atomic.AtomicBoolean;
@@ -121,11 +120,13 @@ public static Cluster newInstance(
121120
throw new IllegalArgumentException("Invalid connection string; must start with secure scheme \"couchbases://\" (note the final 's') but got: " + redactUser(cs.original()));
122121
}
123122

123+
checkParameterNamesAreLowercase(cs);
124+
124125
ClusterOptions builder = new ClusterOptions();
125126
optionsCustomizer.accept(builder);
126127

127-
BuilderPropertySetter propertySetter = new BuilderPropertySetter("", Collections.emptyMap());
128-
propertySetter.set(builder, translateTlsVerify(cs.params()));
128+
BuilderPropertySetter propertySetter = new BuilderPropertySetter("", Collections.emptyMap(), Cluster::lowerSnakeCaseToLowerCamelCase);
129+
propertySetter.set(builder, cs.params());
129130

130131
// do we really want to allow a system property to disable server certificate verification?
131132
//propertySetter.set(builder, systemPropertyMap(SYSTEM_PROPERTY_PREFIX));
@@ -175,27 +176,38 @@ public static Cluster newInstance(
175176
return new Cluster(cs, credential.toInternalAuthenticator(), env);
176177
}
177178

178-
private static Map<String, String> translateTlsVerify(Map<String, String> connectionStringProperties) {
179-
Map<String, String> properties = new LinkedHashMap<>(connectionStringProperties);
180-
String tlsVerify = properties.remove("tls_verify");
181-
if (tlsVerify == null) {
182-
return properties;
183-
}
179+
private static void checkParameterNamesAreLowercase(ConnectionString cs) {
180+
cs.params().keySet().stream()
181+
.filter(Cluster::hasUppercase)
182+
.findFirst()
183+
.ifPresent(badName -> {
184+
throw new IllegalArgumentException("Invalid connection string parameter '" + badName + "'. Please use lower_snake_case in connection string parameter names.");
185+
});
186+
}
187+
188+
private static boolean hasUppercase(String s) {
189+
return s.codePoints().anyMatch(Character::isUpperCase);
190+
}
184191

185-
String javaName = "security.verifyServerCertificate";
186-
switch (tlsVerify) {
187-
case "none":
188-
properties.put(javaName, "false");
189-
break;
190-
case "peer":
191-
properties.put(javaName, "true");
192-
break;
193-
default:
194-
throw new IllegalArgumentException(
195-
"Unexpected value for connection string parameter 'tls_verify'; expected 'none' or 'peer', but got: '" + tlsVerify + "'");
192+
private static String lowerSnakeCaseToLowerCamelCase(String s) {
193+
StringBuilder sb = new StringBuilder();
194+
int[] codePoints = s.codePoints().toArray();
195+
196+
boolean prevWasUnderscore = false;
197+
for (int i : codePoints) {
198+
if (i == '_') {
199+
prevWasUnderscore = true;
200+
continue;
201+
}
202+
203+
if (prevWasUnderscore) {
204+
i = Character.toUpperCase(i);
205+
}
206+
sb.appendCodePoint(i);
207+
prevWasUnderscore = false;
196208
}
197209

198-
return properties;
210+
return sb.toString();
199211
}
200212

201213
private static CoreTransactionsConfig disableTransactionsCleanup() {

columnar-java-client/src/test/java/com/couchbase/columnar/client/java/sandbox/Sandbox.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@
3535
public class Sandbox {
3636

3737
public static void main(String[] args) throws Exception {
38-
String connectionString = "couchbases://127.0.0.1?security.disableServerCertificateVerification=true";
38+
String connectionString = "couchbases://127.0.0.1?security.disable_server_certificate_verification=true&srv=0";
3939
String username = "Administrator";
4040
String password = "password";
4141

core-io/src/main/java/com/couchbase/client/core/env/BuilderPropertySetter.java

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -44,11 +44,11 @@
4444
import java.util.concurrent.atomic.AtomicReference;
4545
import java.util.function.Consumer;
4646
import java.util.function.Function;
47-
import java.util.stream.Collectors;
4847

4948
import static com.couchbase.client.core.util.CbCollections.mapCopyOf;
5049
import static com.couchbase.client.core.util.CbCollections.mapOf;
5150
import static java.util.Objects.requireNonNull;
51+
import static java.util.stream.Collectors.toList;
5252

5353
@SuppressWarnings("rawtypes")
5454
@Stability.Internal
@@ -60,16 +60,22 @@ public class BuilderPropertySetter {
6060
// Escape hatch in case some accessors don't follow the convention.
6161
private final Map<String, String> irregularChildBuilderAccessors;
6262

63+
// Converts an input path component to match the Java method name,
64+
// for translating case conventions.
65+
private final Function<String, String> pathComponentTransformer;
66+
6367
public BuilderPropertySetter() {
64-
this("Config", mapOf("ioEnvironment", "ioEnvironment"));
68+
this("Config", mapOf("ioEnvironment", "ioEnvironment"), name -> name);
6569
}
6670

6771
public BuilderPropertySetter(
6872
String childBuilderAccessorSuffix,
69-
Map<String, String> irregularChildBuilderAccessors
73+
Map<String, String> irregularChildBuilderAccessors,
74+
Function<String, String> pathComponentTransformer
7075
) {
7176
this.childBuilderAccessorSuffix = requireNonNull(childBuilderAccessorSuffix);
7277
this.irregularChildBuilderAccessors = mapCopyOf(irregularChildBuilderAccessors);
78+
this.pathComponentTransformer = requireNonNull(pathComponentTransformer);
7379
}
7480

7581
/**
@@ -83,10 +89,11 @@ public void set(Object builder, Map<String, String> properties) {
8389
* @throws InvalidPropertyException if the property could not be applied to the builder
8490
*/
8591
public void set(Object builder, String propertyName, String propertyValue) {
86-
87-
8892
try {
89-
final List<String> propertyComponents = Arrays.asList(propertyName.split("\\.", -1));
93+
final List<String> propertyComponents = Arrays.stream(propertyName.split("\\.", -1))
94+
.map(pathComponentTransformer)
95+
.collect(toList());
96+
9097
final List<String> pathToBuilder = propertyComponents.subList(0, propertyComponents.size() - 1);
9198
final String setterName = propertyComponents.get(propertyComponents.size() - 1);
9299

@@ -111,7 +118,7 @@ public void set(Object builder, String propertyName, String propertyValue) {
111118
final List<Method> candidates = Arrays.stream(builder.getClass().getMethods())
112119
.filter(m -> m.getName().equals(setterName))
113120
.filter(m -> m.getParameterCount() == 1)
114-
.collect(Collectors.toList());
121+
.collect(toList());
115122

116123
if (candidates.isEmpty()) {
117124
throw InvalidArgumentException.fromMessage("No one-arg setter for property \"" + propertyName + "\" in " + builder.getClass());
@@ -256,7 +263,7 @@ private static <E extends Enum> E convertEnum(Class<E> enumClass, String value)
256263
try {
257264
return (E) Enum.valueOf(enumClass, value);
258265
} catch (IllegalArgumentException e) {
259-
List<String> enumValueNames = Arrays.stream(enumClass.getEnumConstants()).map(Enum::name).collect(Collectors.toList());
266+
List<String> enumValueNames = Arrays.stream(enumClass.getEnumConstants()).map(Enum::name).collect(toList());
260267
throw InvalidArgumentException.fromMessage("Expected one of " + enumValueNames + " but got \"" + value + "\"");
261268
}
262269
}

0 commit comments

Comments
 (0)