From 7673f2f21851998e991370fe533a07b85e794752 Mon Sep 17 00:00:00 2001 From: Anthony Dahanne Date: Mon, 12 Aug 2024 10:15:58 -0400 Subject: [PATCH] Pr #224 - somehow I broke it (#271) * Add MariaDB service detection and replace MySQL JDBC prefix Update MySqlJdbcUrlCreator to add MariaDB schemes, tags and labels Update MySqlJdbcUrlCreator to override createJdbcUrl from AbstractJdbcUrlCreator Update MySqlJdbcUrlCreator to add detection of MariaDB service based on CF environment tags, labels and MariaDB uri Conditionally update the JDBC URL that was either generated or obtained from the CF Service if the current driver class name matches the MariaDB driver and any of the specific MariaDB detection cases Overload existsByLabelStartsWith method in CfService to allow checking if one of many labels matches the CF service label * Add test cases for MariaDB database CF Service * Fix Formatting * Include mariadb services with label that includes * not just starts with --------- Co-authored-by: David Duarte <102606398+daviddmd@users.noreply.github.com> --- .../cfenv/jdbc/MySqlJdbcUrlCreator.java | 58 +++++-- .../io/pivotal/cfenv/jdbc/MySqlJdbcTests.java | 150 +++++++++++++++++- .../jdbc/test-mariadb-info-jdbc-url-only.json | 6 + .../jdbc/test-mariadb-info-jdbc-url.json | 7 + .../cfenv/jdbc/test-mariadb-info-uri.json | 8 + .../test-mariadb-info-with-label-no-tags.json | 9 ++ .../pivotal/cfenv/jdbc/test-mariadb-info.json | 12 ++ .../java/io/pivotal/cfenv/core/CfService.java | 28 ++++ 8 files changed, 258 insertions(+), 20 deletions(-) create mode 100644 java-cfenv-jdbc/src/test/resources/io/pivotal/cfenv/jdbc/test-mariadb-info-jdbc-url-only.json create mode 100644 java-cfenv-jdbc/src/test/resources/io/pivotal/cfenv/jdbc/test-mariadb-info-jdbc-url.json create mode 100644 java-cfenv-jdbc/src/test/resources/io/pivotal/cfenv/jdbc/test-mariadb-info-uri.json create mode 100644 java-cfenv-jdbc/src/test/resources/io/pivotal/cfenv/jdbc/test-mariadb-info-with-label-no-tags.json create mode 100644 java-cfenv-jdbc/src/test/resources/io/pivotal/cfenv/jdbc/test-mariadb-info.json diff --git a/java-cfenv-jdbc/src/main/java/io/pivotal/cfenv/jdbc/MySqlJdbcUrlCreator.java b/java-cfenv-jdbc/src/main/java/io/pivotal/cfenv/jdbc/MySqlJdbcUrlCreator.java index d54acfa3..d0229a35 100644 --- a/java-cfenv-jdbc/src/main/java/io/pivotal/cfenv/jdbc/MySqlJdbcUrlCreator.java +++ b/java-cfenv-jdbc/src/main/java/io/pivotal/cfenv/jdbc/MySqlJdbcUrlCreator.java @@ -23,42 +23,67 @@ * @author Mark Pollack */ public class MySqlJdbcUrlCreator extends AbstractJdbcUrlCreator { - public static final String MYSQL_SCHEME = "mysql"; - + public static final String MARIADB_SCHEME = "mariadb"; + public static final String[] MYSQL_SCHEMES = new String[]{MYSQL_SCHEME, MARIADB_SCHEME}; public static final String MYSQL_TAG = "mysql"; - + public static final String MARIADB_TAG = "mariadb"; + public static final String[] MYSQL_TAGS = new String[]{MYSQL_TAG, MARIADB_TAG}; public static final String MYSQL_LABEL = "mysql"; + public static final String MARIADB_LABEL = "mariadb"; + public static final String[] MYSQL_LABELS = new String[]{MYSQL_LABEL, MARIADB_LABEL}; + public static final String MYSQL_DRIVER_CLASS_NAME = "com.mysql.cj.jdbc.Driver"; + public static final String MARIADB_DRIVER_CLASS_NAME = "org.mariadb.jdbc.Driver"; @Override public boolean isDatabaseService(CfService cfService) { - if (jdbcUrlMatchesScheme(cfService, MYSQL_SCHEME) - || cfService.existsByTagIgnoreCase(MYSQL_TAG) - || cfService.existsByLabelStartsWith(MYSQL_LABEL) - || cfService.existsByUriSchemeStartsWith(MYSQL_SCHEME) - || cfService.existsByCredentialsContainsUriField(MYSQL_SCHEME)) { - return true; - } - return false; + return jdbcUrlMatchesScheme(cfService, MYSQL_SCHEMES) + || cfService.existsByTagIgnoreCase(MYSQL_TAGS) + || cfService.existsByLabelStartsWith(MYSQL_LABELS) + || cfService.existsByUriSchemeStartsWith(MYSQL_SCHEMES) + || cfService.existsByCredentialsContainsUriField(MYSQL_SCHEMES); + } + + private boolean isMariaDbService(CfService cfService) { + return jdbcUrlMatchesScheme(cfService, MARIADB_SCHEME) + || cfService.existsByTagIgnoreCase(MARIADB_TAG) + || cfService.existsByLabelContains(MARIADB_LABEL) + || cfService.existsByUriSchemeStartsWith(MARIADB_SCHEME) + || cfService.existsByCredentialsContainsUriField(MARIADB_SCHEME); } @Override public String getDriverClassName() { - String driverClassNameToUse = null; + String driverClassNameToUse; try { - driverClassNameToUse = "org.mariadb.jdbc.Driver"; + driverClassNameToUse = MARIADB_DRIVER_CLASS_NAME; Class.forName(driverClassNameToUse, false, getClass().getClassLoader()); - } catch (ClassNotFoundException e) { + } + catch (ClassNotFoundException e) { try { - driverClassNameToUse = "com.mysql.cj.jdbc.Driver"; + driverClassNameToUse = MYSQL_DRIVER_CLASS_NAME; Class.forName(driverClassNameToUse, false, getClass().getClassLoader()); - } catch (ClassNotFoundException e2) { + } + catch (ClassNotFoundException e2) { return null; } } return driverClassNameToUse; } + @Override + public String createJdbcUrl(CfService cfService) { + CfCredentials cfCredentials = cfService.getCredentials(); + String jdbcUrl = (String) cfCredentials.getMap().get("jdbcUrl"); + if (jdbcUrl == null) { + jdbcUrl = buildJdbcUrlFromUriField(cfCredentials); + } + if (isMariaDbService(cfService) && getDriverClassName().equals(MARIADB_DRIVER_CLASS_NAME)) { + jdbcUrl = jdbcUrl.replaceFirst("^(jdbc:mysql)", "jdbc:mariadb"); + } + return jdbcUrl; + } + @Override public String buildJdbcUrlFromUriField(CfCredentials cfCredentials) { UriInfo uriInfo = cfCredentials.getUriInfo(MYSQL_SCHEME); @@ -66,5 +91,4 @@ public String buildJdbcUrlFromUriField(CfCredentials cfCredentials) { uriInfo.getHost(), uriInfo.formatPort(), uriInfo.getPath(), uriInfo.formatUserNameAndPasswordQuery(), uriInfo.formatQuery()); } - } diff --git a/java-cfenv-jdbc/src/test/java/io/pivotal/cfenv/jdbc/MySqlJdbcTests.java b/java-cfenv-jdbc/src/test/java/io/pivotal/cfenv/jdbc/MySqlJdbcTests.java index b71deaab..96433354 100644 --- a/java-cfenv-jdbc/src/test/java/io/pivotal/cfenv/jdbc/MySqlJdbcTests.java +++ b/java-cfenv-jdbc/src/test/java/io/pivotal/cfenv/jdbc/MySqlJdbcTests.java @@ -48,7 +48,7 @@ public void mysqlServiceCreation() { assertThat(cfJdbcService.getUsername()).isEqualTo(username); assertThat(cfJdbcService.getPassword()).isEqualTo(password); assertThat(cfJdbcService.getDriverClassName()) - .isEqualTo("org.mariadb.jdbc.Driver"); + .isEqualTo(MySqlJdbcUrlCreator.MARIADB_DRIVER_CLASS_NAME); assertThatThrownBy(() -> { cfJdbcEnv.findJdbcServiceByName("mysql.*"); @@ -160,7 +160,7 @@ public void mysqlServiceCreationWithJdbcUrlOnly() { assertThat(cfJdbcService.getUsername()).isNull(); assertThat(cfJdbcService.getPassword()).isNull(); assertThat(cfJdbcService.getDriverClassName()) - .isEqualTo("org.mariadb.jdbc.Driver"); + .isEqualTo(MySqlJdbcUrlCreator.MARIADB_DRIVER_CLASS_NAME); } @Test @@ -178,6 +178,91 @@ public void mysqlServiceCreationWithJdbcUrlOnlyWithSpecialChars() { passwordWithSpecialChars)).isEqualTo(jdbcUrlMysql); } + + @Test + public void mariaDbServiceCreation() { + String name1 = "database-1"; + String name2 = "database-2"; + + mockVcapServices(getServicesPayload( + getMariaDbServicePayload("mariadb-1", hostname, port, username, password, + name1), + getMariaDbServicePayload("mariadb-2", hostname, port, username, password, + name2))); + + assertJdbcServiceValuesMariaDb(name1, name2); + + CfJdbcEnv cfJdbcEnv = new CfJdbcEnv(); + CfJdbcService cfJdbcService = cfJdbcEnv.findJdbcServiceByName("mariadb-1"); + assertThat(cfJdbcService.getUsername()).isEqualTo(username); + assertThat(cfJdbcService.getPassword()).isEqualTo(password); + assertThat(cfJdbcService.getDriverClassName()) + .isEqualTo(MySqlJdbcUrlCreator.MARIADB_DRIVER_CLASS_NAME); + } + @Test + public void mariaDbServiceCreationWithJdbcUrl() { + String name1 = "database-1"; + String name2 = "database-2"; + mockVcapServices(getServicesPayload( + getMariaDbServicePayloadWithJdbcUrl("mariadb-1", hostname, port, username, + password, name1), + getMariaDbServicePayloadWithJdbcUrl("mariadb-2", hostname, port, username, + password, name2))); + + assertJdbcServiceValuesMariaDb(name1, name2); + } + + @Test + public void mariaDbServiceCreationWithLabelNoTags() { + String name1 = "database-1"; + String name2 = "database-2"; + mockVcapServices(getServicesPayload( + getMariaDbServicePayloadWithLabelNoTags("mariadb-1", hostname, port, username, + password, name1), + getMariaDbServicePayloadWithLabelNoTags("mariadb-2", hostname, port, username, + password, name2))); + assertJdbcServiceValuesMariaDb(name1, name2); + + } + + @Test + public void mariaDbServiceCreationWithUriOnly() { + String name1 = "database-1"; + String name2 = "database-2"; + mockVcapServices(getServicesPayload( + getMariaDbServicePayloadUri("mariadb-1", hostname, port, username, + password, name1), + getMariaDbServicePayloadUri("mariadb-2", hostname, port, username, + password, name2))); + + assertJdbcServiceValuesMariaDb(name1, name2); + + CfJdbcEnv cfJdbcEnv = new CfJdbcEnv(); + CfJdbcService cfJdbcService = cfJdbcEnv.findJdbcServiceByName("mariadb-1"); + assertThat(cfJdbcService.getDriverClassName()) + .isEqualTo(MySqlJdbcUrlCreator.MARIADB_DRIVER_CLASS_NAME); + } + + @Test + public void mariaDbServiceCreationWithJdbcUrlOnly() { + String name1 = "database-1"; + String name2 = "database-2"; + mockVcapServices(getServicesPayload( + getMariaDbServicePayloadWithJdbcUrlOnly("mariadb-1", hostname, port, username, + password, name1), + getMariaDbServicePayloadWithJdbcUrlOnly("mariadb-2", hostname, port, username, + password, name2))); + + assertJdbcServiceValuesMariaDb(name1, name2); + + CfJdbcEnv cfJdbcEnv = new CfJdbcEnv(); + CfJdbcService cfJdbcService = cfJdbcEnv.findJdbcServiceByName("mariadb-1"); + assertThat(cfJdbcService.getUsername()).isNull(); + assertThat(cfJdbcService.getPassword()).isNull(); + assertThat(cfJdbcService.getDriverClassName()) + .isEqualTo(MySqlJdbcUrlCreator.MARIADB_DRIVER_CLASS_NAME); + } + // Utility methods private void assertJdbcServiceValues(String name1, String name2) { @@ -209,7 +294,40 @@ private void assertJdbcServiceValues(String name1, String name2) { CfJdbcService cfJdbcService = cfJdbcEnv.findJdbcServiceByName("mysql-1"); assertThat(cfJdbcService.getDriverClassName()) - .isEqualTo("org.mariadb.jdbc.Driver"); + .isEqualTo(MySqlJdbcUrlCreator.MARIADB_DRIVER_CLASS_NAME); + + } + + private void assertJdbcServiceValuesMariaDb(String name1, String name2) { + CfJdbcEnv cfJdbcEnv = new CfJdbcEnv(); + String jdbcUrlMysql1 = cfJdbcEnv.findJdbcServiceByName("mariadb-1").getJdbcUrl(); + String jdbcUrlMysql2 = cfJdbcEnv.findJdbcServiceByName("mariadb-2").getJdbcUrl(); + + assertThat(getExpectedJdbcUrl(MySqlJdbcUrlCreator.MARIADB_SCHEME, name1)) + .isEqualTo(jdbcUrlMysql1); + assertThat(getExpectedJdbcUrl(MySqlJdbcUrlCreator.MARIADB_SCHEME, name2)) + .isEqualTo(jdbcUrlMysql2); + + CfJdbcService cfJdbcService1 = cfJdbcEnv.findJdbcServiceByName("mariadb-1"); + CfJdbcService cfJdbcService2 = cfJdbcEnv.findJdbcServiceByName("mariadb-2"); + jdbcUrlMysql1 = cfJdbcService1.getJdbcUrl(); + jdbcUrlMysql2 = cfJdbcService2.getJdbcUrl(); + assertThat(getExpectedJdbcUrl(MySqlJdbcUrlCreator.MARIADB_SCHEME, name1)) + .isEqualTo(jdbcUrlMysql1); + assertThat(getExpectedJdbcUrl(MySqlJdbcUrlCreator.MARIADB_SCHEME, name2)) + .isEqualTo(jdbcUrlMysql2); + + List cfJdbcServices = cfJdbcEnv.findJdbcServices(); + assertThat(cfJdbcServices.size()).isEqualTo(2); + + assertThatThrownBy(() -> { + cfJdbcEnv.findJdbcService().getJdbcUrl(); + }).isInstanceOf(IllegalArgumentException.class).hasMessage( + "No unique database service found. Found database service names [mariadb-1, mariadb-2]"); + + CfJdbcService cfJdbcService = cfJdbcEnv.findJdbcServiceByName("mariadb-1"); + assertThat(cfJdbcService.getDriverClassName()) + .isEqualTo(MySqlJdbcUrlCreator.MARIADB_DRIVER_CLASS_NAME); } @@ -255,6 +373,32 @@ private String getMysqlServicePayloadWithJdbcUrlOnly(String serviceName, hostname, port, user, password, name); } + private String getMariaDbServicePayloadWithJdbcUrl(String serviceName, String hostname, int port, String user, String password, String name) { + //Test by MariaDB JDBC url with DB uri + return getTemplatedPayload("test-mariadb-info-jdbc-url.json", serviceName, hostname, port, user, password, name); + } + + private String getMariaDbServicePayloadWithLabelNoTags(String serviceName, String hostname, int port, String user, String password, String name) { + //Test by presence of mariadb string in beginning of service label + return getTemplatedPayload("test-mariadb-info-with-label-no-tags.json", serviceName, hostname, port, user, password, name); + } + + private String getMariaDbServicePayload(String serviceName, String hostname, int port, String user, String password, String name) { + //Test by MariaDB tag (alongside mysql) + return getTemplatedPayload("test-mariadb-info.json", serviceName, hostname, port, user, password, name); + } + + private String getMariaDbServicePayloadUri(String serviceName, String hostname, int port, String user, String password, String name) { + //Test by MariaDB uri + return getTemplatedPayload("test-mariadb-info-uri.json", serviceName, hostname, port, user, password, name); + } + + private String getMariaDbServicePayloadWithJdbcUrlOnly(String serviceName, String hostname, int port, String user, String password, String name) { + //Test by MariaDB jdbc url + return getTemplatedPayload("test-mariadb-info-jdbc-url-only.json", serviceName, + hostname, port, user, password, name); + } + @Override protected String getExpectedJdbcUrl(String databaseType, String name) { return "jdbc:" + databaseType + "://" + hostname + ":" + port + "/" + name diff --git a/java-cfenv-jdbc/src/test/resources/io/pivotal/cfenv/jdbc/test-mariadb-info-jdbc-url-only.json b/java-cfenv-jdbc/src/test/resources/io/pivotal/cfenv/jdbc/test-mariadb-info-jdbc-url-only.json new file mode 100644 index 00000000..b09efcdf --- /dev/null +++ b/java-cfenv-jdbc/src/test/resources/io/pivotal/cfenv/jdbc/test-mariadb-info-jdbc-url-only.json @@ -0,0 +1,6 @@ +{ + "name": "$serviceName", + "credentials": { + "jdbcUrl": "jdbc:mariadb://$hostname:$port/$name?user=$user&password=$password" + } +} \ No newline at end of file diff --git a/java-cfenv-jdbc/src/test/resources/io/pivotal/cfenv/jdbc/test-mariadb-info-jdbc-url.json b/java-cfenv-jdbc/src/test/resources/io/pivotal/cfenv/jdbc/test-mariadb-info-jdbc-url.json new file mode 100644 index 00000000..161c0320 --- /dev/null +++ b/java-cfenv-jdbc/src/test/resources/io/pivotal/cfenv/jdbc/test-mariadb-info-jdbc-url.json @@ -0,0 +1,7 @@ +{ + "name": "$serviceName", + "credentials": { + "jdbcUrl": "jdbc:mariadb://$hostname:$port/$name?user=$user&password=$password", + "uri": "mariadb://$user:$password@$hostname:$port/$name" + } +} \ No newline at end of file diff --git a/java-cfenv-jdbc/src/test/resources/io/pivotal/cfenv/jdbc/test-mariadb-info-uri.json b/java-cfenv-jdbc/src/test/resources/io/pivotal/cfenv/jdbc/test-mariadb-info-uri.json new file mode 100644 index 00000000..ec12bc44 --- /dev/null +++ b/java-cfenv-jdbc/src/test/resources/io/pivotal/cfenv/jdbc/test-mariadb-info-uri.json @@ -0,0 +1,8 @@ +{ + "name": "$serviceName", + "label": "cleardb", + "plan": "free", + "credentials": { + "uri": "mariadb://$user:$password@$hostname:$port/$name" + } +} \ No newline at end of file diff --git a/java-cfenv-jdbc/src/test/resources/io/pivotal/cfenv/jdbc/test-mariadb-info-with-label-no-tags.json b/java-cfenv-jdbc/src/test/resources/io/pivotal/cfenv/jdbc/test-mariadb-info-with-label-no-tags.json new file mode 100644 index 00000000..4ae0bf68 --- /dev/null +++ b/java-cfenv-jdbc/src/test/resources/io/pivotal/cfenv/jdbc/test-mariadb-info-with-label-no-tags.json @@ -0,0 +1,9 @@ +{ + "name": "$serviceName", + "label": "a9s-mariadb106", + "tags": [], + "plan": "free", + "credentials": { + "uri": "mysql://$user:$password@$hostname:$port/$name" + } +} \ No newline at end of file diff --git a/java-cfenv-jdbc/src/test/resources/io/pivotal/cfenv/jdbc/test-mariadb-info.json b/java-cfenv-jdbc/src/test/resources/io/pivotal/cfenv/jdbc/test-mariadb-info.json new file mode 100644 index 00000000..f9ee8443 --- /dev/null +++ b/java-cfenv-jdbc/src/test/resources/io/pivotal/cfenv/jdbc/test-mariadb-info.json @@ -0,0 +1,12 @@ +{ + "name": "$serviceName", + "label": "cleardb", + "tags": [ + "mariadb", + "mysql" + ], + "plan": "free", + "credentials": { + "uri": "mysql://$user:$password@$hostname:$port/$name" + } +} \ No newline at end of file diff --git a/java-cfenv/src/main/java/io/pivotal/cfenv/core/CfService.java b/java-cfenv/src/main/java/io/pivotal/cfenv/core/CfService.java index 5db0d913..130720ec 100644 --- a/java-cfenv/src/main/java/io/pivotal/cfenv/core/CfService.java +++ b/java-cfenv/src/main/java/io/pivotal/cfenv/core/CfService.java @@ -185,4 +185,32 @@ public boolean existsByLabelStartsWith(String label) { return false; } + /** + * @param label string to search for in the service label + * @return whether the label includes the provided string + */ + public boolean existsByLabelContains(String label) { + String cfLabel = this.getLabel(); + if (cfLabel != null && cfLabel.length() > 0) { + return cfLabel.contains(label); + } + return false; + } + + /** + * @param labels Strings to search for as a prefix in the service label + * @return whether any of the provided labels starts with the service label + */ + public boolean existsByLabelStartsWith(String... labels) { + String cfLabel = this.getLabel(); + if (labels != null && cfLabel != null && !cfLabel.isEmpty()){ + for (String labelToMatch : labels){ + if (labelToMatch.startsWith(cfLabel)){ + return true; + } + } + } + return false; + } + }