diff --git a/core/src/main/java/org/springframework/cloud/service/UriBasedServiceInfo.java b/core/src/main/java/org/springframework/cloud/service/UriBasedServiceInfo.java index 79fa82f..78828de 100644 --- a/core/src/main/java/org/springframework/cloud/service/UriBasedServiceInfo.java +++ b/core/src/main/java/org/springframework/cloud/service/UriBasedServiceInfo.java @@ -67,7 +67,12 @@ public abstract class UriBasedServiceInfo extends BaseServiceInfo { public String getPath() { return uriInfo.getPath(); } - + + @ServiceProperty(category="connection") + public String getQuery() { + return uriInfo.getQuery(); + } + /** * Validate the URI and clean it up by using defaults for any missing information, if possible. * diff --git a/core/src/main/java/org/springframework/cloud/service/common/RelationalServiceInfo.java b/core/src/main/java/org/springframework/cloud/service/common/RelationalServiceInfo.java index a1aa681..49fe00d 100644 --- a/core/src/main/java/org/springframework/cloud/service/common/RelationalServiceInfo.java +++ b/core/src/main/java/org/springframework/cloud/service/common/RelationalServiceInfo.java @@ -3,29 +3,48 @@ package org.springframework.cloud.service.common; import org.springframework.cloud.service.UriBasedServiceInfo; /** - * * @author Ramnivas Laddad - * */ public abstract class RelationalServiceInfo extends UriBasedServiceInfo { - protected String jdbcUrlDatabaseType; + protected String jdbcUrlDatabaseType; - public RelationalServiceInfo(String id, String uriString, String jdbcUrlDatabaseType) { - super(id, uriString); - this.jdbcUrlDatabaseType = jdbcUrlDatabaseType; - } + public RelationalServiceInfo(String id, String uriString, String jdbcUrlDatabaseType) { + super(id, uriString); + this.jdbcUrlDatabaseType = jdbcUrlDatabaseType; + } - @ServiceProperty(category="connection") - public String getJdbcUrl() { - if (getPort() != -1) { - return String.format("jdbc:%s://%s:%d/%s?user=%s&password=%s", - jdbcUrlDatabaseType, getHost(), getPort(), getPath(), - getUserName(), getPassword()); - } else { - return String.format("jdbc:%s://%s/%s?user=%s&password=%s", - jdbcUrlDatabaseType, getHost(), getPath(), - getUserName(), getPassword()); - } - } + @ServiceProperty(category = "connection") + public String getJdbcUrl() { + return String.format("jdbc:%s://%s%s/%s%s%s", jdbcUrlDatabaseType, getHost(), formatPort(), + getPath(), formatUserinfo(), formatQuery()); + } + + private String formatPort() { + if (getPort() != -1) { + return String.format(":%d", getPort()); + } + return ""; + } + + private String formatUserinfo() { + if (getUserName() != null && getPassword() != null) { + return String.format("?user=%s&password=%s", getUserName(), getPassword()); + } + if (getUserName() != null) { + return String.format("?user=%s", getUserName()); + } + return ""; + } + + private String formatQuery() { + if (getQuery() != null) { + if (getUserName() == null && getPassword() == null) { + return String.format("?%s", getQuery()); + } else { + return String.format("&%s", getQuery()); + } + } + return ""; + } } diff --git a/core/src/main/java/org/springframework/cloud/util/StandardUriInfoFactory.java b/core/src/main/java/org/springframework/cloud/util/StandardUriInfoFactory.java index 9bc3819..398ca88 100644 --- a/core/src/main/java/org/springframework/cloud/util/StandardUriInfoFactory.java +++ b/core/src/main/java/org/springframework/cloud/util/StandardUriInfoFactory.java @@ -6,64 +6,83 @@ import java.net.URLDecoder; /** * Factory for standard Cloud Foundry URIs which all conform to the format: - * - * [jdbc:]scheme://[user:pass]@authority/path + *

+ * [jdbc:]scheme://[user:pass]@authority[:port]/path */ public class StandardUriInfoFactory implements UriInfoFactory { - @Override - public UriInfo createUri(String uriString) { - String userName = null; - String password = null; - String path; - URI tmpUri; - if (uriString.startsWith("jdbc:")) { - int idx = uriString.indexOf(":"); - uriString = uriString.substring(idx + 1); - } + public static final String JDBC_PREFIX = "jdbc:"; - try { - tmpUri = new URI(uriString); - } - catch (URISyntaxException e) { - throw new IllegalArgumentException(e); - } + @Override + public UriInfo createUri(String scheme, String host, int port, String username, String password, String path) { + return new UriInfo(scheme, host, port, username, password, path); + } - String userInfo = tmpUri.getRawUserInfo(); - if (userInfo != null) { - String userPass[] = userInfo.split(":"); - if (userPass.length != 2) { - throw new IllegalArgumentException("bad user info in URI: " + tmpUri); - } + @Override + public UriInfo createUri(String uriString) { - userName = uriDecode(userPass[0]); - password = uriDecode(userPass[1]); - } + uriString = trimJdbcScheme(uriString); - String rawPath = tmpUri.getRawPath(); - if (rawPath != null && rawPath.length() > 1) { - path = rawPath.substring(1); - } - else { - path = null; - } - return new UriInfo(tmpUri.getScheme(), tmpUri.getHost(), tmpUri.getPort(), userName, password, path); - } + URI tmpUri = createTmpUri(uriString); - @Override - public UriInfo createUri(String scheme, String host, int port, String username, String password, String path) { - return new UriInfo(scheme, host, port, username, password, path); - } + String[] userInfo = parseUserinfo(tmpUri); + String userName = uriDecode(userInfo[0]); + String password = uriDecode(userInfo[1]); - private static String uriDecode(String s) { - try { - // URLDecode decodes '+' to a space, as for - // form encoding. So protect plus signs. - return URLDecoder.decode(s.replace("+", "%2B"), "US-ASCII"); - } - catch (java.io.UnsupportedEncodingException e) { - // US-ASCII is always supported - throw new RuntimeException(e); - } - } + return new UriInfo(tmpUri.getScheme(), tmpUri.getHost(), tmpUri.getPort(), + userName, password, + parsePath(tmpUri), tmpUri.getRawQuery()); + } + + private URI createTmpUri(String uriString) { + try { + return new URI(uriString); + } catch (URISyntaxException e) { + throw new IllegalArgumentException(e); + } + } + + private String trimJdbcScheme(String uriString) { + if (uriString.startsWith(JDBC_PREFIX)) { + uriString = uriString.substring(JDBC_PREFIX.length()); + } + return uriString; + } + + private String[] parseUserinfo(URI uri) { + String userInfo = uri.getRawUserInfo(); + + if (userInfo != null) { + String[] userPass = userInfo.split(":"); + if (userPass.length != 2) { + throw new IllegalArgumentException("Bad userinfo in URI: " + uri); + } + return userPass; + } + + return new String[]{null, null}; + } + + private String parsePath(URI uri) { + String rawPath = uri.getRawPath(); + if (rawPath != null && rawPath.length() > 1) { + return rawPath.substring(1); + } + return null; + } + + private static String uriDecode(String s) { + if (s == null) { + return null; + } + + try { + // URLDecode decodes '+' to a space, as for + // form encoding. So protect plus signs. + return URLDecoder.decode(s.replace("+", "%2B"), "US-ASCII"); + } catch (java.io.UnsupportedEncodingException e) { + // US-ASCII is always supported + throw new RuntimeException(e); + } + } } diff --git a/core/src/main/java/org/springframework/cloud/util/UriInfo.java b/core/src/main/java/org/springframework/cloud/util/UriInfo.java index 8aa2754..6e22e11 100644 --- a/core/src/main/java/org/springframework/cloud/util/UriInfo.java +++ b/core/src/main/java/org/springframework/cloud/util/UriInfo.java @@ -17,22 +17,28 @@ public class UriInfo { private String password; private String path; private URI uri; + private String query; + + public UriInfo(String scheme, String host, int port, String username, String password) { + this(scheme, host, port, username, password, ""); + } public UriInfo(String scheme, String host, int port, String username, String password, String path) { + this(scheme, host, port, username, password, path, null); + } + + public UriInfo(String scheme, String host, int port, String username, String password, String path, String query) { this.scheme = scheme; this.host = host; this.port = port; this.userName = username; this.password = password; this.path = path; + this.query = query; this.uri = buildUri(); } - public UriInfo(String scheme, String host, int port, String username, String password) { - this(scheme, host, port, username, password, ""); - } - public String getScheme() { return scheme; } @@ -57,11 +63,15 @@ public class UriInfo { return path; } + public String getQuery() { + return query; + } + public URI getUri() { return uri; } - public URI buildUri() throws IllegalArgumentException { + private URI buildUri() { String userInfo = null; if (userName != null && password != null) { @@ -71,7 +81,7 @@ public class UriInfo { String cleanedPath = path == null || path.startsWith("/") ? path : "/" + path; try { - return new URI(scheme, userInfo, host, port, cleanedPath, null, null); + return new URI(scheme, userInfo, host, port, cleanedPath, query, null); } catch (URISyntaxException e) { throw new IllegalArgumentException(e); diff --git a/core/src/test/java/org/springframework/cloud/StandardUriInfoFactoryTest.java b/core/src/test/java/org/springframework/cloud/StandardUriInfoFactoryTest.java index 0226816..7afdf17 100644 --- a/core/src/test/java/org/springframework/cloud/StandardUriInfoFactoryTest.java +++ b/core/src/test/java/org/springframework/cloud/StandardUriInfoFactoryTest.java @@ -5,86 +5,72 @@ import org.springframework.cloud.util.StandardUriInfoFactory; import org.springframework.cloud.util.UriInfo; import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNull; /** * @author Jens Deppe */ public class StandardUriInfoFactoryTest { - private static StandardUriInfoFactory factory = new StandardUriInfoFactory(); + private static StandardUriInfoFactory factory = new StandardUriInfoFactory(); - /** - * Basic sanity - */ - @Test - public void createUri1() { - String uri = "mysql://joe:joes_password@localhost:1527/big_db"; - UriInfo result = factory.createUri(uri); + @Test + public void createUri() { + String uri = "mysql://joe:joes_password@localhost:1527/big_db"; + UriInfo result = factory.createUri(uri); - assertEquals("localhost", result.getHost()); - assertEquals(1527, result.getPort()); - assertEquals("joe", result.getUserName()); - assertEquals("joes_password", result.getPassword()); - assertEquals("big_db", result.getPath()); - assertEquals(uri, result.buildUri().toString()); - } + assertUriInfoEquals(result, "localhost", 1527, "joe", "joes_password", "big_db", null); + assertEquals(uri, result.getUri().toString()); + } - /** - * Test with a 'jdbc:...' URI - */ - @Test - public void createUri2() { - String uri = "mysql://joe:joes_password@localhost:1527/big_db"; - String jdbcUri = "jdbc:" + uri; - UriInfo result = factory.createUri(jdbcUri); + @Test + public void createUriWithQuery() { + String uri = "mysql://joe:joes_password@localhost:1527/big_db?p1=v1&p2=v2"; + UriInfo result = factory.createUri(uri); - assertEquals("localhost", result.getHost()); - assertEquals(1527, result.getPort()); - assertEquals("joe", result.getUserName()); - assertEquals("joes_password", result.getPassword()); - assertEquals("big_db", result.getPath()); - assertEquals(uri, result.buildUri().toString()); - } + assertUriInfoEquals(result, "localhost", 1527, "joe", "joes_password", "big_db", "p1=v1&p2=v2"); + assertEquals(uri, result.getUri().toString()); + } - /** - * Test without user/password - */ - @Test - public void createUri3() { - String uri = "mysql://localhost:1527/big_db"; - UriInfo result = factory.createUri(uri); + @Test + public void createWithJdbcUri() { + String uri = "mysql://joe:joes_password@localhost:1527/big_db"; + UriInfo result = factory.createUri("jdbc:" + uri); - assertEquals("localhost", result.getHost()); - assertEquals(1527, result.getPort()); - assertNull(result.getUserName()); - assertNull(result.getPassword()); - assertEquals("big_db", result.getPath()); - assertEquals(uri, result.buildUri().toString()); - } + assertUriInfoEquals(result, "localhost", 1527, "joe", "joes_password", "big_db", null); + assertEquals(uri, result.getUri().toString()); + } - /** - * Test with just a user and no password - */ - @Test(expected = IllegalArgumentException.class) - public void createUri4() { - String uri = "mysql://joe@localhost:1527/big_db"; - factory.createUri(uri); - } + @Test + public void createNoUsernamePassword() { + String uri = "mysql://localhost:1527/big_db"; + UriInfo result = factory.createUri(uri); - /** - * Test when creating URI with explicit components - */ - @Test - public void createUri5() { - String uri = "mysql://joe:joes_password@localhost:1527/big_db"; - UriInfo result = factory.createUri("mysql", "localhost", 1527, "joe", "joes_password", "big_db"); + assertUriInfoEquals(result, "localhost", 1527, null, null, "big_db", null); + assertEquals(uri, result.getUri().toString()); + } - assertEquals("localhost", result.getHost()); - assertEquals(1527, result.getPort()); - assertEquals("joe", result.getUserName()); - assertEquals("joes_password", result.getPassword()); - assertEquals("big_db", result.getPath()); - assertEquals(uri, result.buildUri().toString()); - } + @Test(expected = IllegalArgumentException.class) + public void createWithUsernameNoPassword() { + String uri = "mysql://joe@localhost:1527/big_db"; + factory.createUri(uri); + } + + @Test + public void createWithExplicitParameters() { + String uri = "mysql://joe:joes_password@localhost:1527/big_db"; + UriInfo result = factory.createUri("mysql", "localhost", 1527, "joe", "joes_password", "big_db"); + + assertUriInfoEquals(result, "localhost", 1527, "joe", "joes_password", "big_db", null); + assertEquals(uri, result.getUri().toString()); + } + + private void assertUriInfoEquals(UriInfo result, String host, int port, + String username, String password, String path, String query) { + assertEquals(host, result.getHost()); + assertEquals(port, result.getPort()); + assertEquals(username, result.getUserName()); + assertEquals(password, result.getPassword()); + assertEquals(path, result.getPath()); + assertEquals(query, result.getQuery()); + } } diff --git a/core/src/test/java/org/springframework/cloud/service/common/RelationalServiceInfoTest.java b/core/src/test/java/org/springframework/cloud/service/common/RelationalServiceInfoTest.java new file mode 100644 index 0000000..41532a0 --- /dev/null +++ b/core/src/test/java/org/springframework/cloud/service/common/RelationalServiceInfoTest.java @@ -0,0 +1,54 @@ +package org.springframework.cloud.service.common; + +import org.junit.Test; + +import static org.junit.Assert.assertEquals; + +public class RelationalServiceInfoTest { + + @Test + public void jdbcFullUrl() { + RelationalServiceInfo serviceInfo = createServiceInfo("dbtype://username:password@hostname:1234/database"); + + assertEquals("jdbc:jdbcdbtype://hostname:1234/database?user=username&password=password", serviceInfo.getJdbcUrl()); + } + + @Test + public void jdbcUrlNoPort() { + RelationalServiceInfo serviceInfo = createServiceInfo("dbtype://username:password@hostname/database"); + + assertEquals("jdbc:jdbcdbtype://hostname/database?user=username&password=password", serviceInfo.getJdbcUrl()); + } + + @Test + public void jdbcUrlNoUsernamePassword() { + RelationalServiceInfo serviceInfo = createServiceInfo("dbtype://hostname:1234/database"); + + assertEquals("jdbc:jdbcdbtype://hostname:1234/database", serviceInfo.getJdbcUrl()); + } + + @Test(expected = java.lang.IllegalArgumentException.class) + public void jdbcUrlNoPassword() { + createServiceInfo("dbtype://username@hostname/database"); + } + + @Test + public void jdbcUrlWithQuery() { + RelationalServiceInfo serviceInfo = createServiceInfo("dbtype://username:password@hostname:1234/database?reconnect=true"); + + assertEquals("jdbc:jdbcdbtype://hostname:1234/database?user=username&password=password&reconnect=true", serviceInfo.getJdbcUrl()); + } + + @Test + public void jdbcUrlWithQueryNoUsernamePassword() { + RelationalServiceInfo serviceInfo = createServiceInfo("dbtype://hostname:1234/database?reconnect=true"); + + assertEquals("jdbc:jdbcdbtype://hostname:1234/database?reconnect=true", serviceInfo.getJdbcUrl()); + } + + private RelationalServiceInfo createServiceInfo(final String uri) { + return new RelationalServiceInfo("test", uri, "jdbcdbtype") { + }; + } + +} \ No newline at end of file