diff --git a/columnar-java-client/src/main/java/com/couchbase/columnar/client/java/Cluster.java b/columnar-java-client/src/main/java/com/couchbase/columnar/client/java/Cluster.java index 1fbe9c652..045b5b07d 100644 --- a/columnar-java-client/src/main/java/com/couchbase/columnar/client/java/Cluster.java +++ b/columnar-java-client/src/main/java/com/couchbase/columnar/client/java/Cluster.java @@ -32,7 +32,6 @@ import java.io.Closeable; import java.time.Duration; import java.util.Collections; -import java.util.LinkedHashMap; import java.util.Map; import java.util.Optional; import java.util.concurrent.atomic.AtomicBoolean; @@ -121,11 +120,13 @@ public static Cluster newInstance( throw new IllegalArgumentException("Invalid connection string; must start with secure scheme \"couchbases://\" (note the final 's') but got: " + redactUser(cs.original())); } + checkParameterNamesAreLowercase(cs); + ClusterOptions builder = new ClusterOptions(); optionsCustomizer.accept(builder); - BuilderPropertySetter propertySetter = new BuilderPropertySetter("", Collections.emptyMap()); - propertySetter.set(builder, translateTlsVerify(cs.params())); + BuilderPropertySetter propertySetter = new BuilderPropertySetter("", Collections.emptyMap(), Cluster::lowerSnakeCaseToLowerCamelCase); + propertySetter.set(builder, cs.params()); // do we really want to allow a system property to disable server certificate verification? //propertySetter.set(builder, systemPropertyMap(SYSTEM_PROPERTY_PREFIX)); @@ -175,27 +176,38 @@ public static Cluster newInstance( return new Cluster(cs, credential.toInternalAuthenticator(), env); } - private static Map translateTlsVerify(Map connectionStringProperties) { - Map properties = new LinkedHashMap<>(connectionStringProperties); - String tlsVerify = properties.remove("tls_verify"); - if (tlsVerify == null) { - return properties; - } + private static void checkParameterNamesAreLowercase(ConnectionString cs) { + cs.params().keySet().stream() + .filter(Cluster::hasUppercase) + .findFirst() + .ifPresent(badName -> { + throw new IllegalArgumentException("Invalid connection string parameter '" + badName + "'. Please use lower_snake_case in connection string parameter names."); + }); + } + + private static boolean hasUppercase(String s) { + return s.codePoints().anyMatch(Character::isUpperCase); + } - String javaName = "security.verifyServerCertificate"; - switch (tlsVerify) { - case "none": - properties.put(javaName, "false"); - break; - case "peer": - properties.put(javaName, "true"); - break; - default: - throw new IllegalArgumentException( - "Unexpected value for connection string parameter 'tls_verify'; expected 'none' or 'peer', but got: '" + tlsVerify + "'"); + private static String lowerSnakeCaseToLowerCamelCase(String s) { + StringBuilder sb = new StringBuilder(); + int[] codePoints = s.codePoints().toArray(); + + boolean prevWasUnderscore = false; + for (int i : codePoints) { + if (i == '_') { + prevWasUnderscore = true; + continue; + } + + if (prevWasUnderscore) { + i = Character.toUpperCase(i); + } + sb.appendCodePoint(i); + prevWasUnderscore = false; } - return properties; + return sb.toString(); } private static CoreTransactionsConfig disableTransactionsCleanup() { diff --git a/columnar-java-client/src/test/java/com/couchbase/columnar/client/java/sandbox/Sandbox.java b/columnar-java-client/src/test/java/com/couchbase/columnar/client/java/sandbox/Sandbox.java index a5463ceea..eca5731c4 100644 --- a/columnar-java-client/src/test/java/com/couchbase/columnar/client/java/sandbox/Sandbox.java +++ b/columnar-java-client/src/test/java/com/couchbase/columnar/client/java/sandbox/Sandbox.java @@ -35,7 +35,7 @@ public class Sandbox { public static void main(String[] args) throws Exception { - String connectionString = "couchbases://127.0.0.1?security.disableServerCertificateVerification=true"; + String connectionString = "couchbases://127.0.0.1?security.disable_server_certificate_verification=true&srv=0"; String username = "Administrator"; String password = "password"; diff --git a/core-io/src/main/java/com/couchbase/client/core/env/BuilderPropertySetter.java b/core-io/src/main/java/com/couchbase/client/core/env/BuilderPropertySetter.java index 81d0f37f3..f3f9bdd0e 100644 --- a/core-io/src/main/java/com/couchbase/client/core/env/BuilderPropertySetter.java +++ b/core-io/src/main/java/com/couchbase/client/core/env/BuilderPropertySetter.java @@ -44,11 +44,11 @@ import java.util.concurrent.atomic.AtomicReference; import java.util.function.Consumer; import java.util.function.Function; -import java.util.stream.Collectors; import static com.couchbase.client.core.util.CbCollections.mapCopyOf; import static com.couchbase.client.core.util.CbCollections.mapOf; import static java.util.Objects.requireNonNull; +import static java.util.stream.Collectors.toList; @SuppressWarnings("rawtypes") @Stability.Internal @@ -60,16 +60,22 @@ public class BuilderPropertySetter { // Escape hatch in case some accessors don't follow the convention. private final Map irregularChildBuilderAccessors; + // Converts an input path component to match the Java method name, + // for translating case conventions. + private final Function pathComponentTransformer; + public BuilderPropertySetter() { - this("Config", mapOf("ioEnvironment", "ioEnvironment")); + this("Config", mapOf("ioEnvironment", "ioEnvironment"), name -> name); } public BuilderPropertySetter( String childBuilderAccessorSuffix, - Map irregularChildBuilderAccessors + Map irregularChildBuilderAccessors, + Function pathComponentTransformer ) { this.childBuilderAccessorSuffix = requireNonNull(childBuilderAccessorSuffix); this.irregularChildBuilderAccessors = mapCopyOf(irregularChildBuilderAccessors); + this.pathComponentTransformer = requireNonNull(pathComponentTransformer); } /** @@ -83,10 +89,11 @@ public void set(Object builder, Map properties) { * @throws InvalidPropertyException if the property could not be applied to the builder */ public void set(Object builder, String propertyName, String propertyValue) { - - try { - final List propertyComponents = Arrays.asList(propertyName.split("\\.", -1)); + final List propertyComponents = Arrays.stream(propertyName.split("\\.", -1)) + .map(pathComponentTransformer) + .collect(toList()); + final List pathToBuilder = propertyComponents.subList(0, propertyComponents.size() - 1); final String setterName = propertyComponents.get(propertyComponents.size() - 1); @@ -111,7 +118,7 @@ public void set(Object builder, String propertyName, String propertyValue) { final List candidates = Arrays.stream(builder.getClass().getMethods()) .filter(m -> m.getName().equals(setterName)) .filter(m -> m.getParameterCount() == 1) - .collect(Collectors.toList()); + .collect(toList()); if (candidates.isEmpty()) { throw InvalidArgumentException.fromMessage("No one-arg setter for property \"" + propertyName + "\" in " + builder.getClass()); @@ -256,7 +263,7 @@ private static E convertEnum(Class enumClass, String value) try { return (E) Enum.valueOf(enumClass, value); } catch (IllegalArgumentException e) { - List enumValueNames = Arrays.stream(enumClass.getEnumConstants()).map(Enum::name).collect(Collectors.toList()); + List enumValueNames = Arrays.stream(enumClass.getEnumConstants()).map(Enum::name).collect(toList()); throw InvalidArgumentException.fromMessage("Expected one of " + enumValueNames + " but got \"" + value + "\""); } }