Skip to content

Commit

Permalink
Greg main - replaces Support for R2DBC connections in DataSource proc…
Browse files Browse the repository at this point in the history
…essor. #262  (#272)

* Support for R2DBC connections in DataSource processor.

* Cleaning up some formatting.

* Resolved style formatting issues.

* Remove Greg MariadDB code
* it's now handled directly in MySqlJdbcUrlCreator

---------

Co-authored-by: meyerg <meyerg@vmware.com>
  • Loading branch information
anthonydahanne and meyerg authored Aug 13, 2024
1 parent 3466053 commit 68dfa18
Show file tree
Hide file tree
Showing 3 changed files with 154 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,11 @@
*/
package io.pivotal.cfenv.spring.boot;

import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.cloud.CloudPlatform;
Expand All @@ -38,6 +39,7 @@
/**
* @author Mark Pollack
* @author David Turanski
* @author Greg Meyer
*/
public class CfDataSourceEnvironmentPostProcessor implements CfServiceEnablingEnvironmentPostProcessor,
Ordered, ApplicationListener<ApplicationEvent> {
Expand Down Expand Up @@ -72,7 +74,7 @@ public void postProcessEnvironment(ConfigurableEnvironment environment,

List<CfJdbcService> jdbcServices = cfJdbcEnv.findJdbcServices().stream()
.filter(service -> this.isEnabled(service, environment))
.collect(Collectors.toList());
.toList();

if (jdbcServices.size() > 1) {
if (invocationCount == 1) {
Expand All @@ -96,6 +98,36 @@ public void postProcessEnvironment(ConfigurableEnvironment environment,
properties.put("spring.datasource.driver-class-name", driverClassName);
}

/* R2DBC processing
* Split query param options and URL into two string
* and move options to spring.r2dbc.properties.<option>
*/

String[] splitJDBCUrl = cfJdbcService.getJdbcUrl().split("\\?");

String r2dbcUrl = splitJDBCUrl[0].replaceFirst("jdbc:", "r2dbc:");

properties.put("spring.r2dbc.url", r2dbcUrl);
properties.put("spring.r2dbc.username", cfJdbcService.getUsername());
properties.put("spring.r2dbc.password", cfJdbcService.getPassword());

if (splitJDBCUrl.length == 2) {
Map<String, String> queryOptions = parseQueryString(splitJDBCUrl[1]);

if (queryOptions.size() > 0) {
queryOptions.forEach((key, value) -> {

switch (key) {
case "enabledTLSProtocols":
properties.put("spring.r2dbc.properties.tlsVersion", value);
break;
default:
properties.put(String.format("spring.r2dbc.properties.%s", key), value);
}
});
}
}

MutablePropertySources propertySources = environment.getPropertySources();
if (propertySources.contains(
CommandLinePropertySource.COMMAND_LINE_PROPERTY_SOURCE_NAME)) {
Expand All @@ -120,6 +152,27 @@ public void postProcessEnvironment(ConfigurableEnvironment environment,
}
}

private Map<String, String> parseQueryString(String queryParams) {

if (queryParams == null || queryParams.equals(""))
return Collections.emptyMap();

Map<String, String> retVal = new HashMap<>();

String[] options = queryParams.split("&");
for (String option : options) {

String[] keyValue = option.split("=");
if (keyValue.length != 2 || keyValue[0].length() == 0 || keyValue[1].length() == 0) {
continue;
}

retVal.put(keyValue[0], keyValue[1]);
}

return retVal;
}

@Override
public void onApplicationEvent(ApplicationEvent event) {
if (event instanceof ApplicationPreparedEvent) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,14 @@

/**
* @author Mark Pollack
* @author Greg Meyer
*/
public class DataSourceTests extends AbstractCfEnvTests {

private static final String mysqlJdbcUrl = "jdbc:mysql://10.0.4.35:3306/cf_2e23d10a_8738_8c3c_66cf_13e44422698c?user=mysql_username&password=mysql_password";

private static final String tlsMysqlJdbcUrl = "jdbc:mysql://10.0.4.35:3306/service_instance_db?permitMysqlScheme&user=mysql_username&password=mysql_password&sslMode=VERIFY_IDENTITY&useSSL=true&requireSSL=true&enabledTLSProtocols=TLSv1.2&serverSslCert=/etc/ssl/certs/ca-certificates.crt";

private final CfDataSourceEnvironmentPostProcessor environmentPostProcessor = new CfDataSourceEnvironmentPostProcessor();

private final ConfigurableApplicationContext context = new AnnotationConfigApplicationContext();
Expand Down Expand Up @@ -62,9 +65,80 @@ public void testDataSource() throws Exception {
assertThat(
this.context.getEnvironment().getProperty("spring.datasource.password"))
.isEqualTo("mysql_password");

assertThat(this.context.getEnvironment().getProperty("spring.r2dbc.url"))
.isEqualTo(mysqlJdbcUrl.replace("jdbc:", "r2dbc:").substring(0, mysqlJdbcUrl.indexOf("?") + 1));
assertThat(
this.context.getEnvironment().getProperty("spring.r2dbc.username"))
.isEqualTo("mysql_username");
assertThat(
this.context.getEnvironment().getProperty("spring.r2dbc.password"))
.isEqualTo("mysql_password");

} finally {
System.clearProperty("VCAP_APPLICATION");
}
}

@Test
public void testDataSource_r2dbcOptions() throws Exception {

// To make CloudPlatform test pass
try {
System.setProperty("VCAP_APPLICATION", "yes");

// To setup values used by CfEnv
File file = ResourceUtils.getFile("classpath:vcap-services-tls.json");
String fileContents = new String(Files.readAllBytes(file.toPath()));
mockVcapServices(fileContents);


environmentPostProcessor.postProcessEnvironment(this.context.getEnvironment(),
null);
assertThat(this.context.getEnvironment().getProperty("spring.datasource.url"))
.isEqualTo(tlsMysqlJdbcUrl);
assertThat(
this.context.getEnvironment().getProperty("spring.datasource.username"))
.isEqualTo("mysql_username");
assertThat(
this.context.getEnvironment().getProperty("spring.datasource.password"))
.isEqualTo("mysql_password");

assertThat(this.context.getEnvironment().getProperty("spring.r2dbc.url"))
.isEqualTo(tlsMysqlJdbcUrl.replace("jdbc:", "r2dbc:").substring(0, tlsMysqlJdbcUrl.indexOf("?") + 1));
assertThat(
this.context.getEnvironment().getProperty("spring.r2dbc.username"))
.isEqualTo("mysql_username");
assertThat(
this.context.getEnvironment().getProperty("spring.r2dbc.password"))
.isEqualTo("mysql_password");
assertThat(
this.context.getEnvironment().getProperty("spring.r2dbc.properties.user"))
.isEqualTo("mysql_username");
assertThat(
this.context.getEnvironment().getProperty("spring.r2dbc.properties.password"))
.isEqualTo("mysql_password");
assertThat(
this.context.getEnvironment().getProperty("spring.r2dbc.properties.sslMode"))
.isEqualTo("VERIFY_IDENTITY");
assertThat(
this.context.getEnvironment().getProperty("spring.r2dbc.properties.useSSL"))
.isEqualTo("true");
assertThat(
this.context.getEnvironment().getProperty("spring.r2dbc.properties.requireSSL"))
.isEqualTo("true");
assertThat(
this.context.getEnvironment().getProperty("spring.r2dbc.properties.tlsVersion"))
.isEqualTo("TLSv1.2");
assertThat(
this.context.getEnvironment().getProperty("spring.r2dbc.properties.serverSslCert"))
.isEqualTo("/etc/ssl/certs/ca-certificates.crt");
assertThat(
this.context.getEnvironment().getProperty("spring.r2dbc.properties.serverSslCert"))
.isEqualTo("/etc/ssl/certs/ca-certificates.crt");

} finally {
System.clearProperty("VCAP_APPLICATION");
}
}
}
25 changes: 25 additions & 0 deletions java-cfenv-boot/src/test/resources/vcap-services-tls.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
{
"p-mysql": [
{
"credentials": {
"hostname": "10.0.4.35",
"port": 3306,
"name": "mysql_name",
"username": "mysql_username",
"password": "mysql_password",
"uri": "mysql://mysql_username:mysql_password@10.0.4.35:3306/service_instance_db?reconnect=true",
"jdbcUrl": "jdbc:mysql://10.0.4.35:3306/service_instance_db?permitMysqlScheme&user=mysql_username&password=mysql_password&sslMode=VERIFY_IDENTITY&useSSL=true&requireSSL=true&enabledTLSProtocols=TLSv1.2&serverSslCert=/etc/ssl/certs/ca-certificates.crt"
},
"syslog_drain_url": null,
"volume_mounts": [],
"label": "p-mysql",
"provider": null,
"plan": "100mb",
"name": "mysql",
"tags": [
"mysql",
"relational"
]
}
]
}

0 comments on commit 68dfa18

Please sign in to comment.