diff --git a/.gitignore b/.gitignore index ae08494..1eb96eb 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,4 @@ build Servers .gradle _site +/bin diff --git a/settings.gradle b/settings.gradle index c22faa4..bc6b88f 100644 --- a/settings.gradle +++ b/settings.gradle @@ -3,4 +3,5 @@ rootProject.name = "spring-cloud" include "${rootProject.name}-core" include "${rootProject.name}-cloudfoundry-connector" include "${rootProject.name}-spring-service-connector" -include "${rootProject.name}-heroku-connector" \ No newline at end of file +include "${rootProject.name}-heroku-connector" +include "${rootProject.name}-localconfig-connector" diff --git a/spring-cloud-cloudfoundry-connector/src/main/java/org/springframework/cloud/cloudfoundry/AmqpServiceInfoCreator.java b/spring-cloud-cloudfoundry-connector/src/main/java/org/springframework/cloud/cloudfoundry/AmqpServiceInfoCreator.java index 1753c11..af68eee 100644 --- a/spring-cloud-cloudfoundry-connector/src/main/java/org/springframework/cloud/cloudfoundry/AmqpServiceInfoCreator.java +++ b/spring-cloud-cloudfoundry-connector/src/main/java/org/springframework/cloud/cloudfoundry/AmqpServiceInfoCreator.java @@ -5,20 +5,20 @@ import java.util.Map; import org.springframework.cloud.service.common.AmqpServiceInfo; /** - * + * * @author Ramnivas Laddad * */ public class AmqpServiceInfoCreator extends CloudFoundryServiceInfoCreator { public AmqpServiceInfoCreator() { - super(new Tags("rabbitmq"), "amqp"); + super(new Tags("rabbitmq"), AmqpServiceInfo.URI_SCHEME); } public AmqpServiceInfo createServiceInfo(Map serviceData) { @SuppressWarnings("unchecked") Map credentials = (Map) serviceData.get("credentials"); - + String id = (String) serviceData.get("name"); String uri = getStringFromCredentials(credentials, "uri", "url"); diff --git a/spring-cloud-cloudfoundry-connector/src/main/java/org/springframework/cloud/cloudfoundry/MongoServiceInfoCreator.java b/spring-cloud-cloudfoundry-connector/src/main/java/org/springframework/cloud/cloudfoundry/MongoServiceInfoCreator.java index 5aeace5..ac963c5 100644 --- a/spring-cloud-cloudfoundry-connector/src/main/java/org/springframework/cloud/cloudfoundry/MongoServiceInfoCreator.java +++ b/spring-cloud-cloudfoundry-connector/src/main/java/org/springframework/cloud/cloudfoundry/MongoServiceInfoCreator.java @@ -12,8 +12,8 @@ import org.springframework.cloud.service.common.MongoServiceInfo; public class MongoServiceInfoCreator extends CloudFoundryServiceInfoCreator { public MongoServiceInfoCreator() { - super(new Tags("mongodb"), "mongodb"); - + // the literal in the tag is CloudFoundry-specific + super(new Tags("mongodb"), MongoServiceInfo.URI_SCHEME); } public MongoServiceInfo createServiceInfo(Map serviceData) { diff --git a/spring-cloud-cloudfoundry-connector/src/main/java/org/springframework/cloud/cloudfoundry/MysqlServiceInfoCreator.java b/spring-cloud-cloudfoundry-connector/src/main/java/org/springframework/cloud/cloudfoundry/MysqlServiceInfoCreator.java index aca803e..b5c6e15 100644 --- a/spring-cloud-cloudfoundry-connector/src/main/java/org/springframework/cloud/cloudfoundry/MysqlServiceInfoCreator.java +++ b/spring-cloud-cloudfoundry-connector/src/main/java/org/springframework/cloud/cloudfoundry/MysqlServiceInfoCreator.java @@ -3,14 +3,15 @@ package org.springframework.cloud.cloudfoundry; import org.springframework.cloud.service.common.MysqlServiceInfo; /** - * + * * @author Ramnivas Laddad * */ public class MysqlServiceInfoCreator extends RelationalServiceInfoCreator { public MysqlServiceInfoCreator() { - super(new Tags("mysql"), "mysql"); + // the literal in the tag is CloudFoundry-specific + super(new Tags("mysql"), MysqlServiceInfo.URI_SCHEME); } @Override diff --git a/spring-cloud-cloudfoundry-connector/src/main/java/org/springframework/cloud/cloudfoundry/OracleServiceInfoCreator.java b/spring-cloud-cloudfoundry-connector/src/main/java/org/springframework/cloud/cloudfoundry/OracleServiceInfoCreator.java index 1b8159f..1d82ab3 100644 --- a/spring-cloud-cloudfoundry-connector/src/main/java/org/springframework/cloud/cloudfoundry/OracleServiceInfoCreator.java +++ b/spring-cloud-cloudfoundry-connector/src/main/java/org/springframework/cloud/cloudfoundry/OracleServiceInfoCreator.java @@ -4,7 +4,7 @@ import org.springframework.cloud.service.common.OracleServiceInfo; public class OracleServiceInfoCreator extends RelationalServiceInfoCreator { public OracleServiceInfoCreator() { - super(new Tags(), "oracle"); + super(new Tags(), OracleServiceInfo.URI_SCHEME); } @Override diff --git a/spring-cloud-cloudfoundry-connector/src/main/java/org/springframework/cloud/cloudfoundry/PostgresqlServiceInfoCreator.java b/spring-cloud-cloudfoundry-connector/src/main/java/org/springframework/cloud/cloudfoundry/PostgresqlServiceInfoCreator.java index da8be3e..abfbde1 100644 --- a/spring-cloud-cloudfoundry-connector/src/main/java/org/springframework/cloud/cloudfoundry/PostgresqlServiceInfoCreator.java +++ b/spring-cloud-cloudfoundry-connector/src/main/java/org/springframework/cloud/cloudfoundry/PostgresqlServiceInfoCreator.java @@ -3,14 +3,14 @@ package org.springframework.cloud.cloudfoundry; import org.springframework.cloud.service.common.PostgresqlServiceInfo; /** - * + * * @author Ramnivas Laddad * */ public class PostgresqlServiceInfoCreator extends RelationalServiceInfoCreator { public PostgresqlServiceInfoCreator() { - super(new Tags("postgresql"), "postgres"); + super(new Tags("postgresql"), PostgresqlServiceInfo.URI_SCHEME); } @Override diff --git a/spring-cloud-cloudfoundry-connector/src/main/java/org/springframework/cloud/cloudfoundry/RedisServiceInfoCreator.java b/spring-cloud-cloudfoundry-connector/src/main/java/org/springframework/cloud/cloudfoundry/RedisServiceInfoCreator.java index 75e6df1..f8fcfa3 100644 --- a/spring-cloud-cloudfoundry-connector/src/main/java/org/springframework/cloud/cloudfoundry/RedisServiceInfoCreator.java +++ b/spring-cloud-cloudfoundry-connector/src/main/java/org/springframework/cloud/cloudfoundry/RedisServiceInfoCreator.java @@ -12,7 +12,8 @@ import org.springframework.cloud.service.common.RedisServiceInfo; public class RedisServiceInfoCreator extends CloudFoundryServiceInfoCreator { public RedisServiceInfoCreator() { - super(new Tags("redis"), "redis"); + // the literal in the tag is CloudFoundry-specific + super(new Tags("redis"), RedisServiceInfo.URI_SCHEME); } public RedisServiceInfo createServiceInfo(Map serviceData) { diff --git a/spring-cloud-cloudfoundry-connector/src/main/java/org/springframework/cloud/cloudfoundry/SmtpServiceInfoCreator.java b/spring-cloud-cloudfoundry-connector/src/main/java/org/springframework/cloud/cloudfoundry/SmtpServiceInfoCreator.java index ccf7560..10bd6e5 100644 --- a/spring-cloud-cloudfoundry-connector/src/main/java/org/springframework/cloud/cloudfoundry/SmtpServiceInfoCreator.java +++ b/spring-cloud-cloudfoundry-connector/src/main/java/org/springframework/cloud/cloudfoundry/SmtpServiceInfoCreator.java @@ -6,7 +6,7 @@ import org.springframework.cloud.service.common.SmtpServiceInfo; import org.springframework.cloud.util.UriInfo; /** - * + * * @author Ramnivas Laddad * */ @@ -15,16 +15,17 @@ public class SmtpServiceInfoCreator extends CloudFoundryServiceInfoCreator serviceData) { String id = (String) serviceData.get("name"); - + @SuppressWarnings("unchecked") Map credentials = (Map) serviceData.get("credentials"); String host = (String) credentials.get("hostname"); - + int port = DEFAULT_SMTP_PORT; if (credentials.containsKey("port")) { port = Integer.parseInt(credentials.get("port").toString()); @@ -33,8 +34,8 @@ public class SmtpServiceInfoCreator extends CloudFoundryServiceInfoCreator implements CloudConnector { private static Logger logger = Logger.getLogger(AbstractCloudConnector.class.getName()); - + protected List> serviceInfoCreators = new ArrayList>(); protected abstract List getServicesData(); protected abstract FallbackServiceInfoCreator getFallbackServiceInfoCreator(); - + public AbstractCloudConnector(Class> serviceInfoCreatorClass) { scanServiceInfoCreators(serviceInfoCreatorClass); } @@ -39,7 +39,7 @@ public abstract class AbstractCloudConnector implements CloudConnector { for (SD serviceData : getServicesData()) { serviceInfos.add(getServiceInfo(serviceData)); } - + return serviceInfos; } @@ -54,14 +54,14 @@ public abstract class AbstractCloudConnector implements CloudConnector { registerServiceInfoCreator(serviceInfoCreator); } } - + private ServiceInfo getServiceInfo(SD serviceData) { for (ServiceInfoCreator serviceInfoCreator : serviceInfoCreators) { if (serviceInfoCreator.accept(serviceData)) { return serviceInfoCreator.createServiceInfo(serviceData); } } - + // Fallback with a warning ServiceInfo fallackServiceInfo = getFallbackServiceInfoCreator().createServiceInfo(serviceData); logger.warning("No suitable service info creator found for service " + fallackServiceInfo.getId() diff --git a/spring-cloud-core/src/main/java/org/springframework/cloud/Cloud.java b/spring-cloud-core/src/main/java/org/springframework/cloud/Cloud.java index 808b903..41fd035 100644 --- a/spring-cloud-core/src/main/java/org/springframework/cloud/Cloud.java +++ b/spring-cloud-core/src/main/java/org/springframework/cloud/Cloud.java @@ -38,8 +38,6 @@ import org.springframework.cloud.service.ServiceInfo.ServiceProperty; * */ public class Cloud { - private static Logger logger = Logger.getLogger(Cloud.class.getName()); - private CloudConnector cloudConnector; private ServiceConnectorCreatorRegistry serviceConnectorCreatorRegistry = new ServiceConnectorCreatorRegistry(); diff --git a/spring-cloud-core/src/main/java/org/springframework/cloud/service/FallbackBaseServiceInfoCreator.java b/spring-cloud-core/src/main/java/org/springframework/cloud/service/FallbackBaseServiceInfoCreator.java new file mode 100644 index 0000000..e819b76 --- /dev/null +++ b/spring-cloud-core/src/main/java/org/springframework/cloud/service/FallbackBaseServiceInfoCreator.java @@ -0,0 +1,10 @@ +package org.springframework.cloud.service; + +import org.springframework.cloud.FallbackServiceInfoCreator; + +public class FallbackBaseServiceInfoCreator extends FallbackServiceInfoCreator { + @Override + public BaseServiceInfo createServiceInfo(UriBasedServiceData serviceData) { + return new BaseServiceInfo(serviceData.getKey()); + } +} diff --git a/spring-cloud-core/src/main/java/org/springframework/cloud/service/UriBasedServiceData.java b/spring-cloud-core/src/main/java/org/springframework/cloud/service/UriBasedServiceData.java new file mode 100644 index 0000000..67d02a2 --- /dev/null +++ b/spring-cloud-core/src/main/java/org/springframework/cloud/service/UriBasedServiceData.java @@ -0,0 +1,19 @@ +package org.springframework.cloud.service; + +public class UriBasedServiceData { + private final String key; + private final String uri; + + public UriBasedServiceData(String key, String uri) { + this.key = key; + this.uri = uri; + } + + public String getKey() { + return key; + } + + public String getUri() { + return uri; + } +} \ No newline at end of file diff --git a/spring-cloud-core/src/main/java/org/springframework/cloud/service/UriBasedServiceInfo.java b/spring-cloud-core/src/main/java/org/springframework/cloud/service/UriBasedServiceInfo.java index 78828de..fda552c 100644 --- a/spring-cloud-core/src/main/java/org/springframework/cloud/service/UriBasedServiceInfo.java +++ b/spring-cloud-core/src/main/java/org/springframework/cloud/service/UriBasedServiceInfo.java @@ -6,84 +6,96 @@ import org.springframework.cloud.util.UriInfoFactory; /** * Common class for all {@link ServiceInfo}s - * + * * @author Ramnivas Laddad * */ public abstract class UriBasedServiceInfo extends BaseServiceInfo { - private UriInfo uriInfo; + private UriInfo uriInfo; - private static UriInfoFactory uriFactory = new StandardUriInfoFactory(); + private static UriInfoFactory uriFactory = new StandardUriInfoFactory(); - public UriBasedServiceInfo(String id, String scheme, String host, int port, String username, String password, String path) { - super(id); - this.uriInfo = getUriInfoFactory().createUri(scheme, host, port, username, password, path); - this.uriInfo = validateAndCleanUriInfo(uriInfo); - } - - public UriBasedServiceInfo(String id, String uriString) { - super(id); - this.uriInfo = getUriInfoFactory().createUri(uriString); - this.uriInfo = validateAndCleanUriInfo(uriInfo); - } + public UriBasedServiceInfo(String id, String scheme, String host, int port, String username, String password, String path) { + super(id); + this.uriInfo = getUriInfoFactory().createUri(scheme, host, port, username, password, path); + this.uriInfo = validateAndCleanUriInfo(uriInfo); + } - /** - * For URI-based (@link ServiceInfo}s which don't conform to the standard URI - * format, override this method in your own ServiceInfo class to return a - * {@link UriInfoFactory} which will create the appropriate URIs. - * - * @return your special UriInfoFactory - */ - public UriInfoFactory getUriInfoFactory() { - return uriFactory; - } + public UriBasedServiceInfo(String id, String uriString) { + super(id); + this.uriInfo = getUriInfoFactory().createUri(uriString); + this.uriInfo = validateAndCleanUriInfo(uriInfo); + } - @ServiceProperty(category="connection") - public String getUri() { - return uriInfo.getUri().toString(); - } - - @ServiceProperty(category="connection") - public String getUserName() { - return uriInfo.getUserName(); - } - - @ServiceProperty(category="connection") - public String getPassword() { - return uriInfo.getPassword(); - } + /** + * For URI-based (@link ServiceInfo}s which don't conform to the standard URI + * format, override this method in your own ServiceInfo class to return a {@link UriInfoFactory} which will create the + * appropriate URIs. + * + * @return your special UriInfoFactory + */ + public UriInfoFactory getUriInfoFactory() { + return uriFactory; + } - @ServiceProperty(category="connection") - public String getHost() { - return uriInfo.getHost(); - } + @ServiceProperty(category = "connection") + public String getUri() { + return uriInfo.getUri().toString(); + } - @ServiceProperty(category="connection") - public int getPort() { - return uriInfo.getPort(); - } + @ServiceProperty(category = "connection") + public String getUserName() { + return uriInfo.getUserName(); + } - @ServiceProperty(category="connection") - public String getPath() { - return uriInfo.getPath(); - } + @ServiceProperty(category = "connection") + public String getPassword() { + return uriInfo.getPassword(); + } - @ServiceProperty(category="connection") - public String getQuery() { - return uriInfo.getQuery(); - } + @ServiceProperty(category = "connection") + public String getHost() { + return uriInfo.getHost(); + } - /** - * Validate the URI and clean it up by using defaults for any missing information, if possible. - * - * @param uriInfo uri info based on parsed payload - * @return cleaned up uri info - */ - protected UriInfo validateAndCleanUriInfo(UriInfo uriInfo) { - return uriInfo; - } - - protected UriInfo getUriInfo() { - return uriInfo; - } + @ServiceProperty(category = "connection") + public int getPort() { + return uriInfo.getPort(); + } + + @ServiceProperty(category = "connection") + public String getPath() { + return uriInfo.getPath(); + } + + @ServiceProperty(category = "connection") + public String getQuery() { + return uriInfo.getQuery(); + } + + @ServiceProperty(category = "connection") + public String getScheme() { + return uriInfo.getScheme(); + } + + /** + * Validate the URI and clean it up by using defaults for any missing information, if possible. + * + * @param uriInfo + * uri info based on parsed payload + * @return cleaned up uri info + */ + protected UriInfo validateAndCleanUriInfo(UriInfo uriInfo) { + return uriInfo; + } + + protected UriInfo getUriInfo() { + return uriInfo; + } + + @Override + public String toString() { + return getClass().getSimpleName() + "[" + getScheme() + "://" + getUserName() + ":****@" + getHost() + ":" + getPort() + + "/" + getPath() + "]"; + } } diff --git a/spring-cloud-core/src/main/java/org/springframework/cloud/service/UriBasedServiceInfoCreator.java b/spring-cloud-core/src/main/java/org/springframework/cloud/service/UriBasedServiceInfoCreator.java new file mode 100644 index 0000000..a76ab3b --- /dev/null +++ b/spring-cloud-core/src/main/java/org/springframework/cloud/service/UriBasedServiceInfoCreator.java @@ -0,0 +1,25 @@ +package org.springframework.cloud.service; + +import org.springframework.cloud.ServiceInfoCreator; + +public abstract class UriBasedServiceInfoCreator implements + ServiceInfoCreator { + + private final String uriScheme; + + public UriBasedServiceInfoCreator(String uriScheme) { + this.uriScheme = uriScheme; + } + + @Override + public boolean accept(UriBasedServiceData serviceData) { + return serviceData.getUri().toString().startsWith(uriScheme + "://"); + } + + public abstract SI createServiceInfo(String id, String uri); + + @Override + public SI createServiceInfo(UriBasedServiceData serviceData) { + return createServiceInfo(serviceData.getKey(), serviceData.getUri()); + } +} diff --git a/spring-cloud-core/src/main/java/org/springframework/cloud/service/common/AmqpServiceInfo.java b/spring-cloud-core/src/main/java/org/springframework/cloud/service/common/AmqpServiceInfo.java index 5ae72bb..aa7bb98 100644 --- a/spring-cloud-core/src/main/java/org/springframework/cloud/service/common/AmqpServiceInfo.java +++ b/spring-cloud-core/src/main/java/org/springframework/cloud/service/common/AmqpServiceInfo.java @@ -1,8 +1,8 @@ package org.springframework.cloud.service.common; import org.springframework.cloud.CloudException; -import org.springframework.cloud.service.UriBasedServiceInfo; import org.springframework.cloud.service.ServiceInfo.ServiceLabel; +import org.springframework.cloud.service.UriBasedServiceInfo; import org.springframework.cloud.util.UriInfo; /** @@ -13,29 +13,32 @@ import org.springframework.cloud.util.UriInfo; */ @ServiceLabel("rabbitmq") public class AmqpServiceInfo extends UriBasedServiceInfo { + + public static final String URI_SCHEME = "amqp"; + public AmqpServiceInfo(String id, String host, int port, String username, String password, String virtualHost) { - super(id, "amqp", host, port, username, password, virtualHost); + super(id, URI_SCHEME, host, port, username, password, virtualHost); } - + public AmqpServiceInfo(String id, String uri) throws CloudException { super(id, uri); } - + @ServiceProperty(category="connection") public String getVirtualHost() { return getUriInfo().getPath(); } - + @Override protected UriInfo validateAndCleanUriInfo(UriInfo uriInfo) { - if (!"amqp".equals(uriInfo.getScheme())) { + if (!URI_SCHEME.equals(uriInfo.getScheme())) { throw new IllegalArgumentException("wrong scheme in amqp URI: " + uriInfo); } if (uriInfo.getHost() == null) { throw new IllegalArgumentException("missing authority in amqp URI: " + uriInfo); } - + int port = uriInfo.getPort(); if (port == -1) { port = 5672; @@ -43,7 +46,7 @@ public class AmqpServiceInfo extends UriBasedServiceInfo { String userName = uriInfo.getUserName(); String password = uriInfo.getPassword(); - + if (userName == null || password == null) { throw new IllegalArgumentException("missing userinfo in amqp URI: " + uriInfo); } diff --git a/spring-cloud-core/src/main/java/org/springframework/cloud/service/common/MongoServiceInfo.java b/spring-cloud-core/src/main/java/org/springframework/cloud/service/common/MongoServiceInfo.java index b45800f..a332b65 100644 --- a/spring-cloud-core/src/main/java/org/springframework/cloud/service/common/MongoServiceInfo.java +++ b/spring-cloud-core/src/main/java/org/springframework/cloud/service/common/MongoServiceInfo.java @@ -4,23 +4,26 @@ import org.springframework.cloud.service.UriBasedServiceInfo; import org.springframework.cloud.service.ServiceInfo.ServiceLabel; /** - * + * * @author Ramnivas Laddad * */ @ServiceLabel("mongo") public class MongoServiceInfo extends UriBasedServiceInfo { + + public static final String URI_SCHEME = "mongodb"; + public MongoServiceInfo(String id, String host, int port, String username, String password, String db) { - super(id, "mongodb", host, port, username, password, db); + super(id, URI_SCHEME, host, port, username, password, db); } public MongoServiceInfo(String id, String uri) { super(id, uri); } - + @ServiceProperty(category="connection") public String getDatabase() { return getUriInfo().getPath(); } - + } diff --git a/spring-cloud-core/src/main/java/org/springframework/cloud/service/common/MysqlServiceInfo.java b/spring-cloud-core/src/main/java/org/springframework/cloud/service/common/MysqlServiceInfo.java index 467a59c..f4609eb 100644 --- a/spring-cloud-core/src/main/java/org/springframework/cloud/service/common/MysqlServiceInfo.java +++ b/spring-cloud-core/src/main/java/org/springframework/cloud/service/common/MysqlServiceInfo.java @@ -3,14 +3,18 @@ package org.springframework.cloud.service.common; import org.springframework.cloud.service.ServiceInfo.ServiceLabel; /** - * + * * @author Ramnivas Laddad * */ @ServiceLabel("mysql") public class MysqlServiceInfo extends RelationalServiceInfo { - public MysqlServiceInfo(String id, String url) { - super(id, url, "mysql"); - } + public static final String JDBC_URL_TYPE = "mysql"; + + public static final String URI_SCHEME = JDBC_URL_TYPE; + + public MysqlServiceInfo(String id, String url) { + super(id, url, URI_SCHEME); + } } diff --git a/spring-cloud-core/src/main/java/org/springframework/cloud/service/common/OracleServiceInfo.java b/spring-cloud-core/src/main/java/org/springframework/cloud/service/common/OracleServiceInfo.java index 13e0f49..6d2289d 100644 --- a/spring-cloud-core/src/main/java/org/springframework/cloud/service/common/OracleServiceInfo.java +++ b/spring-cloud-core/src/main/java/org/springframework/cloud/service/common/OracleServiceInfo.java @@ -5,8 +5,12 @@ import org.springframework.cloud.service.ServiceInfo; @ServiceInfo.ServiceLabel("oracle") public class OracleServiceInfo extends RelationalServiceInfo { + public static final String JDBC_URL_TYPE = "oracle"; + + public static final String URI_SCHEME = JDBC_URL_TYPE; + public OracleServiceInfo(String id, String url) { - super(id, url, "oracle"); + super(id, url, JDBC_URL_TYPE); } @Override diff --git a/spring-cloud-core/src/main/java/org/springframework/cloud/service/common/PostgresqlServiceInfo.java b/spring-cloud-core/src/main/java/org/springframework/cloud/service/common/PostgresqlServiceInfo.java index 98e478c..edcace5 100644 --- a/spring-cloud-core/src/main/java/org/springframework/cloud/service/common/PostgresqlServiceInfo.java +++ b/spring-cloud-core/src/main/java/org/springframework/cloud/service/common/PostgresqlServiceInfo.java @@ -4,13 +4,18 @@ import org.springframework.cloud.service.ServiceInfo.ServiceLabel; /** - * + * * @author Ramnivas Laddad * */ @ServiceLabel("postgresql") public class PostgresqlServiceInfo extends RelationalServiceInfo { + + public static final String JDBC_URL_TYPE = "postgresql"; + + public static final String URI_SCHEME = "postgres"; + public PostgresqlServiceInfo(String id, String url) { - super(id, url, "postgresql"); + super(id, url, JDBC_URL_TYPE); } } diff --git a/spring-cloud-core/src/main/java/org/springframework/cloud/service/common/RedisServiceInfo.java b/spring-cloud-core/src/main/java/org/springframework/cloud/service/common/RedisServiceInfo.java index d31be25..ab35a8e 100644 --- a/spring-cloud-core/src/main/java/org/springframework/cloud/service/common/RedisServiceInfo.java +++ b/spring-cloud-core/src/main/java/org/springframework/cloud/service/common/RedisServiceInfo.java @@ -4,16 +4,19 @@ import org.springframework.cloud.service.UriBasedServiceInfo; import org.springframework.cloud.service.ServiceInfo.ServiceLabel; /** - * + * * @author Ramnivas Laddad * */ @ServiceLabel("redis") public class RedisServiceInfo extends UriBasedServiceInfo { + + public static final String URI_SCHEME = "redis"; + public RedisServiceInfo(String id, String host, int port, String password) { - super(id, "redis", host, port, null, password, null); + super(id, URI_SCHEME, host, port, null, password, null); } - + public RedisServiceInfo(String id, String uri) { super(id, uri); } diff --git a/spring-cloud-core/src/main/java/org/springframework/cloud/service/common/RelationalServiceInfo.java b/spring-cloud-core/src/main/java/org/springframework/cloud/service/common/RelationalServiceInfo.java index 49fe00d..bac6236 100644 --- a/spring-cloud-core/src/main/java/org/springframework/cloud/service/common/RelationalServiceInfo.java +++ b/spring-cloud-core/src/main/java/org/springframework/cloud/service/common/RelationalServiceInfo.java @@ -7,7 +7,7 @@ import org.springframework.cloud.service.UriBasedServiceInfo; */ public abstract class RelationalServiceInfo extends UriBasedServiceInfo { - protected String jdbcUrlDatabaseType; + protected final String jdbcUrlDatabaseType; public RelationalServiceInfo(String id, String uriString, String jdbcUrlDatabaseType) { super(id, uriString); diff --git a/spring-cloud-core/src/main/java/org/springframework/cloud/service/common/SmtpServiceInfo.java b/spring-cloud-core/src/main/java/org/springframework/cloud/service/common/SmtpServiceInfo.java index a7f87fa..f973626 100644 --- a/spring-cloud-core/src/main/java/org/springframework/cloud/service/common/SmtpServiceInfo.java +++ b/spring-cloud-core/src/main/java/org/springframework/cloud/service/common/SmtpServiceInfo.java @@ -4,10 +4,12 @@ import org.springframework.cloud.service.UriBasedServiceInfo; public class SmtpServiceInfo extends UriBasedServiceInfo { + public static final String URI_SCHEME = "smtp"; + public SmtpServiceInfo(String id, String host, int port, String username, String password) { - super(id, "smtp", host, port, username, password, ""); + super(id, URI_SCHEME, host, port, username, password, ""); } - + public SmtpServiceInfo(String id, String url) { super(id, url); } diff --git a/spring-cloud-heroku-connector/src/main/java/org/springframework/cloud/heroku/AmqpServiceInfoCreator.java b/spring-cloud-heroku-connector/src/main/java/org/springframework/cloud/heroku/AmqpServiceInfoCreator.java index a139bd6..86621f8 100644 --- a/spring-cloud-heroku-connector/src/main/java/org/springframework/cloud/heroku/AmqpServiceInfoCreator.java +++ b/spring-cloud-heroku-connector/src/main/java/org/springframework/cloud/heroku/AmqpServiceInfoCreator.java @@ -3,14 +3,14 @@ package org.springframework.cloud.heroku; import org.springframework.cloud.service.common.AmqpServiceInfo; /** - * + * * @author Ramnivas Laddad * */ public class AmqpServiceInfoCreator extends HerokuServiceInfoCreator { public AmqpServiceInfoCreator() { - super("amqp"); + super(AmqpServiceInfo.URI_SCHEME); } @Override diff --git a/spring-cloud-heroku-connector/src/main/java/org/springframework/cloud/heroku/HerokuConnector.java b/spring-cloud-heroku-connector/src/main/java/org/springframework/cloud/heroku/HerokuConnector.java index 374b7f8..55d1cba 100644 --- a/spring-cloud-heroku-connector/src/main/java/org/springframework/cloud/heroku/HerokuConnector.java +++ b/spring-cloud-heroku-connector/src/main/java/org/springframework/cloud/heroku/HerokuConnector.java @@ -10,26 +10,27 @@ import org.springframework.cloud.CloudException; import org.springframework.cloud.FallbackServiceInfoCreator; import org.springframework.cloud.ServiceInfoCreator; import org.springframework.cloud.app.ApplicationInstanceInfo; -import org.springframework.cloud.heroku.HerokuConnector.KeyValuePair; import org.springframework.cloud.service.BaseServiceInfo; +import org.springframework.cloud.service.FallbackBaseServiceInfoCreator; import org.springframework.cloud.service.ServiceInfo; +import org.springframework.cloud.service.UriBasedServiceData; import org.springframework.cloud.util.EnvironmentAccessor; /** * Implementation of CloudConnector for Heroku - * + * * Currently support Postgres (default provided), Mysql (Cleardb), MongoDb (MongoLab, MongoHQ, MongoSoup), * Redis (RedisToGo, RedisCloud, OpenRedis, RedisGreen), and AMQP (CloudAmqp). - * + * * @author Ramnivas Laddad * */ -public class HerokuConnector extends AbstractCloudConnector { +public class HerokuConnector extends AbstractCloudConnector { private EnvironmentAccessor environment = new EnvironmentAccessor(); - private ApplicationInstanceInfoCreator applicationInstanceInfoCreator + private ApplicationInstanceInfoCreator applicationInstanceInfoCreator = new ApplicationInstanceInfoCreator(environment); - + private List serviceEnvPrefixes; @SuppressWarnings({ "unchecked", "rawtypes" }) @@ -41,28 +42,28 @@ public class HerokuConnector extends AbstractCloudConnector serviceInfoCreator) { + protected void registerServiceInfoCreator(ServiceInfoCreator serviceInfoCreator) { super.registerServiceInfoCreator(serviceInfoCreator); HerokuServiceInfoCreator herokuServiceInfoCreator = (HerokuServiceInfoCreator)serviceInfoCreator; String[] envPrefixes = herokuServiceInfoCreator.getEnvPrefixes(); - + // need to do this since this method gets called during construction and we cannot initialize serviceEnvPrefixes before this if (serviceEnvPrefixes == null) { serviceEnvPrefixes = new ArrayList(); @@ -75,17 +76,17 @@ public class HerokuConnector extends AbstractCloudConnector * Returns map whose key is the env key and value is the associated url *

- * @return information about services bound to the app + * @return information about services bound to the app */ - protected List getServicesData() { - List serviceData = new ArrayList(); - + protected List getServicesData() { + List serviceData = new ArrayList(); + Map env = environment.getEnv(); - + for (Map.Entry envEntry : env.entrySet()) { for (String envPrefix : serviceEnvPrefixes) { if (envEntry.getKey().startsWith(envPrefix)) { - serviceData.add(new KeyValuePair(envEntry.getKey(), envEntry.getValue())); + serviceData.add(new UriBasedServiceData(envEntry.getKey(), envEntry.getValue())); } } } @@ -94,32 +95,7 @@ public class HerokuConnector extends AbstractCloudConnector getFallbackServiceInfoCreator() { - return new HerokuFallbackServiceInfoCreator(); + protected FallbackServiceInfoCreator getFallbackServiceInfoCreator() { + return new FallbackBaseServiceInfoCreator(); } - - public static class KeyValuePair { - private String key; - private String value; - - public KeyValuePair(String key, String value) { - this.key = key; - this.value = value; - } - - public String getKey() { - return key; - } - - public String getValue() { - return value; - } - } -} - -class HerokuFallbackServiceInfoCreator extends FallbackServiceInfoCreator { - @Override - public BaseServiceInfo createServiceInfo(KeyValuePair serviceData) { - return new BaseServiceInfo(serviceData.getKey()); - } -} +} \ No newline at end of file diff --git a/spring-cloud-heroku-connector/src/main/java/org/springframework/cloud/heroku/HerokuServiceInfoCreator.java b/spring-cloud-heroku-connector/src/main/java/org/springframework/cloud/heroku/HerokuServiceInfoCreator.java index 15ed4aa..81094eb 100644 --- a/spring-cloud-heroku-connector/src/main/java/org/springframework/cloud/heroku/HerokuServiceInfoCreator.java +++ b/spring-cloud-heroku-connector/src/main/java/org/springframework/cloud/heroku/HerokuServiceInfoCreator.java @@ -1,40 +1,27 @@ package org.springframework.cloud.heroku; -import org.springframework.cloud.ServiceInfoCreator; -import org.springframework.cloud.heroku.HerokuConnector.KeyValuePair; import org.springframework.cloud.service.ServiceInfo; +import org.springframework.cloud.service.UriBasedServiceInfoCreator; /** - * + * * @author Ramnivas Laddad * */ -public abstract class HerokuServiceInfoCreator implements ServiceInfoCreator { +public abstract class HerokuServiceInfoCreator extends UriBasedServiceInfoCreator { - private String urlProtocol; + public HerokuServiceInfoCreator(String uriScheme) { + super(uriScheme); + } - public HerokuServiceInfoCreator(String urlProtocol) { - this.urlProtocol = urlProtocol; - } - - public boolean accept(KeyValuePair serviceData) { - return serviceData.getValue().toString().startsWith(urlProtocol + "://"); - } - - public abstract SI createServiceInfo(String id, String uri); - - public SI createServiceInfo(KeyValuePair serviceData) { - return createServiceInfo(serviceData.getKey(), serviceData.getValue()); - } - /** * Get prefixes for env variable with which the associated {@link ServiceInfo} may be created. - * + * * Unlike CloudFoundry which exposes VCAP_SERVICES as a single environment to encompass all services bound * to the app, Heroku expose one environment variable per app. This method allows each info creator to declare - * appropriate env variables. - * - * @return prefixes for the relevant environment variables + * appropriate env variables. + * + * @return prefixes for the relevant environment variables */ public abstract String[] getEnvPrefixes(); } diff --git a/spring-cloud-heroku-connector/src/main/java/org/springframework/cloud/heroku/MongoServiceInfoCreator.java b/spring-cloud-heroku-connector/src/main/java/org/springframework/cloud/heroku/MongoServiceInfoCreator.java index bb1e11e..d75299e 100644 --- a/spring-cloud-heroku-connector/src/main/java/org/springframework/cloud/heroku/MongoServiceInfoCreator.java +++ b/spring-cloud-heroku-connector/src/main/java/org/springframework/cloud/heroku/MongoServiceInfoCreator.java @@ -3,14 +3,14 @@ package org.springframework.cloud.heroku; import org.springframework.cloud.service.common.MongoServiceInfo; /** - * + * * @author Ramnivas Laddad * */ public class MongoServiceInfoCreator extends HerokuServiceInfoCreator { public MongoServiceInfoCreator() { - super("mongodb"); + super(MongoServiceInfo.URI_SCHEME); } @Override diff --git a/spring-cloud-heroku-connector/src/main/java/org/springframework/cloud/heroku/MysqlServiceInfoCreator.java b/spring-cloud-heroku-connector/src/main/java/org/springframework/cloud/heroku/MysqlServiceInfoCreator.java index 4318cb0..73bb8eb 100644 --- a/spring-cloud-heroku-connector/src/main/java/org/springframework/cloud/heroku/MysqlServiceInfoCreator.java +++ b/spring-cloud-heroku-connector/src/main/java/org/springframework/cloud/heroku/MysqlServiceInfoCreator.java @@ -3,14 +3,14 @@ package org.springframework.cloud.heroku; import org.springframework.cloud.service.common.MysqlServiceInfo; /** - * + * * @author Ramnivas Laddad * */ public class MysqlServiceInfoCreator extends RelationalServiceInfoCreator { public MysqlServiceInfoCreator() { - super("mysql"); + super(MysqlServiceInfo.URI_SCHEME); } @Override diff --git a/spring-cloud-heroku-connector/src/main/java/org/springframework/cloud/heroku/PostgresqlServiceInfoCreator.java b/spring-cloud-heroku-connector/src/main/java/org/springframework/cloud/heroku/PostgresqlServiceInfoCreator.java index 57a4e67..70f47de 100644 --- a/spring-cloud-heroku-connector/src/main/java/org/springframework/cloud/heroku/PostgresqlServiceInfoCreator.java +++ b/spring-cloud-heroku-connector/src/main/java/org/springframework/cloud/heroku/PostgresqlServiceInfoCreator.java @@ -3,14 +3,14 @@ package org.springframework.cloud.heroku; import org.springframework.cloud.service.common.PostgresqlServiceInfo; /** - * + * * @author Ramnivas Laddad * */ public class PostgresqlServiceInfoCreator extends RelationalServiceInfoCreator { public PostgresqlServiceInfoCreator() { - super("postgres"); + super(PostgresqlServiceInfo.URI_SCHEME); } @Override diff --git a/spring-cloud-heroku-connector/src/main/java/org/springframework/cloud/heroku/RedisServiceInfoCreator.java b/spring-cloud-heroku-connector/src/main/java/org/springframework/cloud/heroku/RedisServiceInfoCreator.java index cfa88ad..1757bd7 100644 --- a/spring-cloud-heroku-connector/src/main/java/org/springframework/cloud/heroku/RedisServiceInfoCreator.java +++ b/spring-cloud-heroku-connector/src/main/java/org/springframework/cloud/heroku/RedisServiceInfoCreator.java @@ -3,14 +3,14 @@ package org.springframework.cloud.heroku; import org.springframework.cloud.service.common.RedisServiceInfo; /** - * + * * @author Ramnivas Laddad * */ public class RedisServiceInfoCreator extends HerokuServiceInfoCreator { public RedisServiceInfoCreator() { - super("redis"); + super(RedisServiceInfo.URI_SCHEME); } @Override diff --git a/spring-cloud-heroku-connector/src/test/java/org/springframework/cloud/heroku/AbstractHerokuConnectorTest.java b/spring-cloud-heroku-connector/src/test/java/org/springframework/cloud/heroku/AbstractHerokuConnectorTest.java index 7b88bf9..576dcfc 100644 --- a/spring-cloud-heroku-connector/src/test/java/org/springframework/cloud/heroku/AbstractHerokuConnectorTest.java +++ b/spring-cloud-heroku-connector/src/test/java/org/springframework/cloud/heroku/AbstractHerokuConnectorTest.java @@ -10,7 +10,7 @@ import org.springframework.cloud.util.EnvironmentAccessor; /** * Base test class that provides setup and utility methods to generate test payload - * + * * @author Ramnivas Laddad * */ @@ -20,7 +20,7 @@ public abstract class AbstractHerokuConnectorTest { protected static final String hostname = "10.20.30.40"; protected static final int port = 1234; - protected static String username = "myuser"; + protected static final String username = "myuser"; protected static final String password = "mypass"; @Before @@ -28,7 +28,7 @@ public abstract class AbstractHerokuConnectorTest { MockitoAnnotations.initMocks(this); testCloudConnector.setCloudEnvironment(mockEnvironment); } - + protected static ServiceInfo getServiceInfo(List serviceInfos, String serviceId) { for (ServiceInfo serviceInfo : serviceInfos) { if (serviceInfo.getId().equals(serviceId)) { diff --git a/spring-cloud-heroku-connector/src/test/java/org/springframework/cloud/heroku/HerokuConnectorMysqlServiceTest.java b/spring-cloud-heroku-connector/src/test/java/org/springframework/cloud/heroku/HerokuConnectorMysqlServiceTest.java index 349c738..439a854 100644 --- a/spring-cloud-heroku-connector/src/test/java/org/springframework/cloud/heroku/HerokuConnectorMysqlServiceTest.java +++ b/spring-cloud-heroku-connector/src/test/java/org/springframework/cloud/heroku/HerokuConnectorMysqlServiceTest.java @@ -13,15 +13,15 @@ import org.springframework.cloud.service.ServiceInfo; import org.springframework.cloud.service.common.MysqlServiceInfo; /** - * + * * @author Ramnivas Laddad * */ public class HerokuConnectorMysqlServiceTest extends AbstractHerokuConnectorRelationalServiceTest { public HerokuConnectorMysqlServiceTest() { - super("mysql"); + super(MysqlServiceInfo.URI_SCHEME); } - + @Test public void mysqlServiceCreation() { Map env = new HashMap(); diff --git a/spring-cloud-heroku-connector/src/test/java/org/springframework/cloud/heroku/HerokuConnectorPostgresqlServiceTest.java b/spring-cloud-heroku-connector/src/test/java/org/springframework/cloud/heroku/HerokuConnectorPostgresqlServiceTest.java index 35bf83b..52d7398 100644 --- a/spring-cloud-heroku-connector/src/test/java/org/springframework/cloud/heroku/HerokuConnectorPostgresqlServiceTest.java +++ b/spring-cloud-heroku-connector/src/test/java/org/springframework/cloud/heroku/HerokuConnectorPostgresqlServiceTest.java @@ -13,15 +13,15 @@ import org.springframework.cloud.service.ServiceInfo; import org.springframework.cloud.service.common.PostgresqlServiceInfo; /** - * + * * @author Ramnivas Laddad * */ public class HerokuConnectorPostgresqlServiceTest extends AbstractHerokuConnectorRelationalServiceTest { public HerokuConnectorPostgresqlServiceTest() { - super("postgres"); + super(PostgresqlServiceInfo.URI_SCHEME); } - + @Test public void postgresqlServiceCreation() { Map env = new HashMap(); diff --git a/spring-cloud-localconfig-connector/.gitignore b/spring-cloud-localconfig-connector/.gitignore new file mode 100644 index 0000000..5e56e04 --- /dev/null +++ b/spring-cloud-localconfig-connector/.gitignore @@ -0,0 +1 @@ +/bin diff --git a/spring-cloud-localconfig-connector/README.md b/spring-cloud-localconfig-connector/README.md new file mode 100644 index 0000000..128d2c3 --- /dev/null +++ b/spring-cloud-localconfig-connector/README.md @@ -0,0 +1,80 @@ +Local-configuration connector for Spring Cloud +======================================= + +Provides the ability to configure Spring Cloud services locally for development or testing. +The current implementation reads from Java properties only; in order to prevent dependencies +on the Spring Framework, the placeholder functionality is unavailable in the connector. +Pull requests for also inspecting environment variables are welcome. + +Quick start +----------- +Since service URIs contain passwords and should not be stored in code, this connector does not +attempt to read properties out of the classpath. You can provide a filename with service definitions +by setting the `spring.cloud.propertiesFile` property or by passing in an open `InputStream`: + +````java +InputStream propertyStream = new FileInputStream("/path/to/spring-cloud.properties"); +LocalConfigConnector.supplyProperties(propertyStream); +Cloud cloud = new CloudFactory().getCloud(); +```` + +The property file should contain an application ID and the desired services in this format: + +````properties +spring.cloud.appId: myApp +; spring.cloud.{id}: URI +spring.cloud.database: mysql://user:pass@host:1234/dbname +```` + +Service type is determined by the URI scheme. + +Property sources +---------------- +This connector first attempts to read the system properties generally and a system property named +`spring.cloud.propertiesFile` specifically. If the system properties are not readable +(the security manager denies `checkPropertiesAccess`), then they will be treated as empty. +If a system property named `spring.cloud.propertiesFile` is found, that file will be loaded +as a property list. + +###Programmatically supplying properties +You can programmatically supply a property source by calling the static method +`LocalConfigConnector.supplyProperties(InputStream)` before invoking `getCloud()`. +Calling this method will cause the connector to read the stream as a property list +and then close the stream. Calling this method after invoking `getCloud()` will +still read the stream, but the properties will have no effect on the connector +service configuration. Calling this method multiple times will load the supplied +streams onto the same `Properties` object, overwriting duplicates. + +###Property order +To provide the maximum configuration flexibility, the connector will scan the available +property sources in this order: + +- programmatically-supplied properties +- properties read from `spring.cloud.propertiesFile` +- system properties + +The last definition of a specific service ID wins. The connector will log a message at +`WARN` if you override a service ID. + +Activating the connector +------------------------ +The Spring Cloud core expects exactly one cloud connector to return `true` for +`isInMatchingCloud()`. This connector identifies the "local cloud" by the presence of +a property named `spring.cloud.appId`, which will be used in the `ApplicationInstanceInfo`. + +Service definitions +------------------- +If the connector is activated, it will iterate through all the available properties +for keys matching the pattern `spring.cloud.{serviceId}`. Each value is interpreted as a URI +to the services, and the type of service is determined from the scheme. All of the standard +`UriBasedServiceInfo`s are supported. + +Supporting additional services +------------------------------ +Please see the documentation for [cloudfoundry-connector](../spring-cloud-cloudfoundry-connector), since the same +mechanism applies to any cloud connector. + +Instance ID +----------- +This connector will create a UUID for use as the instance ID, as Java does not provide +any portable mechanism for reliably determining hostnames or PIDs. \ No newline at end of file diff --git a/spring-cloud-localconfig-connector/build.gradle b/spring-cloud-localconfig-connector/build.gradle new file mode 100644 index 0000000..d020bde --- /dev/null +++ b/spring-cloud-localconfig-connector/build.gradle @@ -0,0 +1,6 @@ +description = 'Spring Cloud local-configuration connector' + +dependencies { + compile project(':spring-cloud-core') + testCompile 'com.github.stefanbirkner:system-rules:1.5.0' +} diff --git a/spring-cloud-localconfig-connector/src/main/java/org/springframework/cloud/localconfig/AmqpServiceInfoCreator.java b/spring-cloud-localconfig-connector/src/main/java/org/springframework/cloud/localconfig/AmqpServiceInfoCreator.java new file mode 100644 index 0000000..b9dc631 --- /dev/null +++ b/spring-cloud-localconfig-connector/src/main/java/org/springframework/cloud/localconfig/AmqpServiceInfoCreator.java @@ -0,0 +1,20 @@ +package org.springframework.cloud.localconfig; + +import org.springframework.cloud.service.common.AmqpServiceInfo; + +/** + * + * @author Christopher Smith + * + */ +public class AmqpServiceInfoCreator extends LocalConfigServiceInfoCreator{ + + public AmqpServiceInfoCreator() { + super(AmqpServiceInfo.URI_SCHEME); + } + + @Override + public AmqpServiceInfo createServiceInfo(String id, String uri) { + return new AmqpServiceInfo(id, uri); + } +} diff --git a/spring-cloud-localconfig-connector/src/main/java/org/springframework/cloud/localconfig/LocalConfigConnector.java b/spring-cloud-localconfig-connector/src/main/java/org/springframework/cloud/localconfig/LocalConfigConnector.java new file mode 100644 index 0000000..3a8251a --- /dev/null +++ b/spring-cloud-localconfig-connector/src/main/java/org/springframework/cloud/localconfig/LocalConfigConnector.java @@ -0,0 +1,190 @@ +package org.springframework.cloud.localconfig; + +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.Arrays; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Properties; +import java.util.UUID; +import java.util.logging.Level; +import java.util.logging.Logger; +import java.util.regex.Pattern; + +import org.springframework.cloud.AbstractCloudConnector; +import org.springframework.cloud.FallbackServiceInfoCreator; +import org.springframework.cloud.app.ApplicationInstanceInfo; +import org.springframework.cloud.app.BasicApplicationInstanceInfo; +import org.springframework.cloud.service.BaseServiceInfo; +import org.springframework.cloud.service.FallbackBaseServiceInfoCreator; +import org.springframework.cloud.service.UriBasedServiceData; + +/** + * + * @author Christopher Smith + * + */ +public class LocalConfigConnector extends AbstractCloudConnector { + + private static final Logger logger = Logger.getLogger(LocalConfigConnector.class.getName()); + + /*--------------- String constants for property keys ---------------*/ + + public static final String PROPERTY_PREFIX = "spring.cloud."; + + public static final Pattern SERVICE_PROPERTY_PATTERN = Pattern.compile("\\A" + Pattern.quote(PROPERTY_PREFIX) + "(.+)" + "\\Z"); + + public static final String APP_ID_PROPERTY = PROPERTY_PREFIX + "appId"; + + public static final String PROPERTIES_FILE_PROPERTY = PROPERTY_PREFIX + "propertiesFile"; + + /** + * These properties configure the connector itself and aren't service definitions. + */ + public static final List META_PROPERTIES = Collections.unmodifiableList( + Arrays.asList(new String[] { APP_ID_PROPERTY, PROPERTIES_FILE_PROPERTY })); + + /*--------------- sources for service-definition properties ---------------*/ + + static Properties programmaticProperties = new Properties(); + + private Properties fileProperties = null; + + /*--------------- API implementation ---------------*/ + + @SuppressWarnings({ "unchecked", "rawtypes" }) + public LocalConfigConnector() { + super((Class) LocalConfigServiceInfoCreator.class); + } + + /** + * Returns {@code true} if a property named {@code spring.cloud.appId} is present in any of the property sources. + * On the first call, attempts to load properties from a file specified in {@code spring.cloud.propertiesFile}. + */ + @Override + public boolean isInMatchingCloud() { + if (fileProperties == null) + readFileProperties(); + + return findProperty(APP_ID_PROPERTY) != null; + } + + @Override + public ApplicationInstanceInfo getApplicationInstanceInfo() { + return new BasicApplicationInstanceInfo(UUID.randomUUID().toString(), findProperty(APP_ID_PROPERTY), + Collections. emptyMap()); + } + + @Override + protected List getServicesData() { + if(fileProperties == null) + throw new IllegalStateException("isInMatchingCloud() must be called first to initialize connector"); + + LinkedHashMap propertySources = new LinkedHashMap(); + + propertySources.put("programmatic properties", programmaticProperties); + propertySources.put("properties from file", fileProperties); + try { + propertySources.put("system properties", System.getProperties()); + } catch (SecurityException e) { + logger.log(Level.WARNING, + "couldn't read system properties; no service definitions from system properties will be applied", e); + } + + return LocalConfigUtil.readServicesData(propertySources); + } + + @Override + protected FallbackServiceInfoCreator getFallbackServiceInfoCreator() { + return new FallbackBaseServiceInfoCreator(); + } + + /*--------------- methods for manipulating properties and sources ---------------*/ + + /** + * Adds properties to be scanned from the supplied {@link InputStream}, overwriting + * existing properties with the same name. Closes the stream after loading. + * + * @param propertiesInputStream + * a property list + * @throws IOException + * if the underlying load operation throws an exception + */ + public static void supplyProperties(final InputStream propertiesInputStream) throws IOException { + programmaticProperties.load(propertiesInputStream); + propertiesInputStream.close(); + } + + /** + * Checks for the presence of a supplied or system property named {@code spring.cloud.propertiesFile}. If the property + * is present, load its contents into {@link #fileProperties}. If there's a problem, log but continue. + */ + private void readFileProperties() { + fileProperties = new Properties(); + logger.fine("looking for a properties file"); + + String filename = null; + + filename = programmaticProperties.getProperty(PROPERTIES_FILE_PROPERTY); + + try { + filename = System.getProperty(PROPERTIES_FILE_PROPERTY, filename); + } catch (SecurityException e) { + logSystemReadException(PROPERTIES_FILE_PROPERTY, e); + return; + } + + if (filename == null) { + logger.info("did not find a system property " + PROPERTIES_FILE_PROPERTY); + return; + } + + logger.info("loading properties from file " + filename); + + try { + InputStream fis = openFile(filename); + fileProperties.load(fis); + } catch (IOException e) { + logger.log(Level.SEVERE, "exception while loading properties from file " + filename, e); + return; + } + + logger.info("properties loaded successfully"); + } + + /** + * Broken out into a separate method for mocking the filesystem. + * @param filename the file to open + * @return a {@code FileInputStream} to the file + * @throws IOException if opening the file throws + */ + InputStream openFile(String filename) throws IOException { + return new FileInputStream(filename); + } + + /** + * Look for a specific property in programmatically-supplied properties, properties from a file, + * or the system properties. Last source wins. + * + * @param key + * the property to look for + * @return the highest-priority value for the key, or {@code null} if the key is not found + */ + private String findProperty(String key) { + String value = programmaticProperties.getProperty(key); + value = fileProperties.getProperty(key, value); + try { + value = System.getProperty(key, value); + } catch (SecurityException e) { + logSystemReadException(key, e); + } + + return value; + } + + private static void logSystemReadException(String key, SecurityException e) { + logger.log(Level.WARNING, "couldn't read system property " + key, e); + } +} diff --git a/spring-cloud-localconfig-connector/src/main/java/org/springframework/cloud/localconfig/LocalConfigServiceInfoCreator.java b/spring-cloud-localconfig-connector/src/main/java/org/springframework/cloud/localconfig/LocalConfigServiceInfoCreator.java new file mode 100644 index 0000000..98a8669 --- /dev/null +++ b/spring-cloud-localconfig-connector/src/main/java/org/springframework/cloud/localconfig/LocalConfigServiceInfoCreator.java @@ -0,0 +1,11 @@ +package org.springframework.cloud.localconfig; + +import org.springframework.cloud.service.ServiceInfo; +import org.springframework.cloud.service.UriBasedServiceInfoCreator; + +public abstract class LocalConfigServiceInfoCreator extends UriBasedServiceInfoCreator { + + protected LocalConfigServiceInfoCreator(String uriScheme) { + super(uriScheme); + } +} diff --git a/spring-cloud-localconfig-connector/src/main/java/org/springframework/cloud/localconfig/LocalConfigUtil.java b/spring-cloud-localconfig-connector/src/main/java/org/springframework/cloud/localconfig/LocalConfigUtil.java new file mode 100644 index 0000000..3dbc38f --- /dev/null +++ b/spring-cloud-localconfig-connector/src/main/java/org/springframework/cloud/localconfig/LocalConfigUtil.java @@ -0,0 +1,85 @@ +package org.springframework.cloud.localconfig; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Properties; +import java.util.logging.Logger; +import java.util.regex.Matcher; + +import org.springframework.cloud.service.UriBasedServiceData; + +public final class LocalConfigUtil { + private static final Logger logger = Logger.getLogger(LocalConfigConnector.class.getName()); + + private LocalConfigUtil() { + } + + static List readServicesData(LinkedHashMap propertySources) { + // we'll turn this into KVPs to return but need to eliminate duplicates first + Map collectedServices = new HashMap(); + + // iterate over the property sources in order, extracting matching properties + for (Map.Entry propertySource : propertySources.entrySet()) { + if(propertySource.getValue().isEmpty()) { + logger.info("no " + propertySource.getKey()); + continue; + } + logger.info("reading services from " + propertySource.getKey()); + Map services = readServices(propertySource.getValue()); + + // add each of the found services to the list, warning about duplicates + for (Map.Entry service : services.entrySet()) { + String oldUri = collectedServices.put(service.getKey(), service.getValue()); + if (oldUri == null) + logger.info("added service '" + service.getKey() + "' from " + propertySource.getKey()); + else + logger.warning("replaced service '" + service.getKey() + "' with new URI from " + propertySource.getKey()); + } + } + + // now we have a collated set of service IDs and URIs + List serviceData = new ArrayList(collectedServices.size()); + for (Map.Entry serviceInfo : collectedServices.entrySet()) { + serviceData.add(new UriBasedServiceData(serviceInfo.getKey(), serviceInfo.getValue())); + } + + return serviceData; + } + + /** + * Goes through a {@code Properties} object, finding all service definitions (properties + * prefixed with {@code spring.cloud.} but not in {@code META_PROPERTIES}) and collects {@code (id,URI)} pairs. + * + * @param properties + * the {@code Properties} object to read + * @return all of the service definitions found + */ + static Map readServices(Properties properties) { + Map services = new HashMap(); + + for (String propertyName : properties.stringPropertyNames()) { + if (LocalConfigConnector.META_PROPERTIES.contains(propertyName)) { + logger.finer("skipping meta property " + propertyName); + continue; + } + + Matcher m = LocalConfigConnector.SERVICE_PROPERTY_PATTERN.matcher(propertyName); + if (!m.matches()) { + logger.finest("skipping non-Spring-Cloud property " + propertyName); + continue; + } + + String serviceId = m.group(1); + String serviceUri = properties.getProperty(propertyName); + + // no URI here because they will contain passwords + logger.fine("found service URI for service " + serviceId); + services.put(serviceId, serviceUri); + } + + return services; + } +} diff --git a/spring-cloud-localconfig-connector/src/main/java/org/springframework/cloud/localconfig/MongoServiceInfoCreator.java b/spring-cloud-localconfig-connector/src/main/java/org/springframework/cloud/localconfig/MongoServiceInfoCreator.java new file mode 100644 index 0000000..68671ac --- /dev/null +++ b/spring-cloud-localconfig-connector/src/main/java/org/springframework/cloud/localconfig/MongoServiceInfoCreator.java @@ -0,0 +1,20 @@ +package org.springframework.cloud.localconfig; + +import org.springframework.cloud.service.common.MongoServiceInfo; + +/** + * + * @author Christopher Smith + * + */ +public class MongoServiceInfoCreator extends LocalConfigServiceInfoCreator{ + + public MongoServiceInfoCreator() { + super(MongoServiceInfo.URI_SCHEME); + } + + @Override + public MongoServiceInfo createServiceInfo(String id, String uri) { + return new MongoServiceInfo(id, uri); + } +} diff --git a/spring-cloud-localconfig-connector/src/main/java/org/springframework/cloud/localconfig/MysqlServiceInfoCreator.java b/spring-cloud-localconfig-connector/src/main/java/org/springframework/cloud/localconfig/MysqlServiceInfoCreator.java new file mode 100644 index 0000000..0d39262 --- /dev/null +++ b/spring-cloud-localconfig-connector/src/main/java/org/springframework/cloud/localconfig/MysqlServiceInfoCreator.java @@ -0,0 +1,20 @@ +package org.springframework.cloud.localconfig; + +import org.springframework.cloud.service.common.MysqlServiceInfo; + +/** + * + * @author Christopher Smith + * + */ +public class MysqlServiceInfoCreator extends LocalConfigServiceInfoCreator{ + + public MysqlServiceInfoCreator() { + super(MysqlServiceInfo.URI_SCHEME); + } + + @Override + public MysqlServiceInfo createServiceInfo(String id, String uri) { + return new MysqlServiceInfo(id, uri); + } +} diff --git a/spring-cloud-localconfig-connector/src/main/java/org/springframework/cloud/localconfig/PostgresqlServiceInfoCreator.java b/spring-cloud-localconfig-connector/src/main/java/org/springframework/cloud/localconfig/PostgresqlServiceInfoCreator.java new file mode 100644 index 0000000..282da2f --- /dev/null +++ b/spring-cloud-localconfig-connector/src/main/java/org/springframework/cloud/localconfig/PostgresqlServiceInfoCreator.java @@ -0,0 +1,20 @@ +package org.springframework.cloud.localconfig; + +import org.springframework.cloud.service.common.PostgresqlServiceInfo; + +/** + * + * @author Christopher Smith + * + */ +public class PostgresqlServiceInfoCreator extends LocalConfigServiceInfoCreator{ + + public PostgresqlServiceInfoCreator() { + super(PostgresqlServiceInfo.URI_SCHEME); + } + + @Override + public PostgresqlServiceInfo createServiceInfo(String id, String uri) { + return new PostgresqlServiceInfo(id, uri); + } +} diff --git a/spring-cloud-localconfig-connector/src/main/java/org/springframework/cloud/localconfig/RedisServiceInfoCreator.java b/spring-cloud-localconfig-connector/src/main/java/org/springframework/cloud/localconfig/RedisServiceInfoCreator.java new file mode 100644 index 0000000..7a9e06f --- /dev/null +++ b/spring-cloud-localconfig-connector/src/main/java/org/springframework/cloud/localconfig/RedisServiceInfoCreator.java @@ -0,0 +1,20 @@ +package org.springframework.cloud.localconfig; + +import org.springframework.cloud.service.common.RedisServiceInfo; + +/** + * + * @author Christopher Smith + * + */ +public class RedisServiceInfoCreator extends LocalConfigServiceInfoCreator{ + + public RedisServiceInfoCreator() { + super(RedisServiceInfo.URI_SCHEME); + } + + @Override + public RedisServiceInfo createServiceInfo(String id, String uri) { + return new RedisServiceInfo(id, uri); + } +} diff --git a/spring-cloud-localconfig-connector/src/main/resources/META-INF/services/org.springframework.cloud.CloudConnector b/spring-cloud-localconfig-connector/src/main/resources/META-INF/services/org.springframework.cloud.CloudConnector new file mode 100644 index 0000000..2a7adb6 --- /dev/null +++ b/spring-cloud-localconfig-connector/src/main/resources/META-INF/services/org.springframework.cloud.CloudConnector @@ -0,0 +1 @@ +org.springframework.cloud.localconfig.LocalConfigConnector \ No newline at end of file diff --git a/spring-cloud-localconfig-connector/src/main/resources/META-INF/services/org.springframework.cloud.localconfig.LocalConfigServiceInfoCreator b/spring-cloud-localconfig-connector/src/main/resources/META-INF/services/org.springframework.cloud.localconfig.LocalConfigServiceInfoCreator new file mode 100644 index 0000000..f8a14d4 --- /dev/null +++ b/spring-cloud-localconfig-connector/src/main/resources/META-INF/services/org.springframework.cloud.localconfig.LocalConfigServiceInfoCreator @@ -0,0 +1,5 @@ +org.springframework.cloud.localconfig.AmqpServiceInfoCreator +org.springframework.cloud.localconfig.MongoServiceInfoCreator +org.springframework.cloud.localconfig.MysqlServiceInfoCreator +org.springframework.cloud.localconfig.PostgresqlServiceInfoCreator +org.springframework.cloud.localconfig.RedisServiceInfoCreator \ No newline at end of file diff --git a/spring-cloud-localconfig-connector/src/test/java/org/springframework/cloud/localconfig/AbstractLocalConfigConnectorTest.java b/spring-cloud-localconfig-connector/src/test/java/org/springframework/cloud/localconfig/AbstractLocalConfigConnectorTest.java new file mode 100644 index 0000000..9313717 --- /dev/null +++ b/spring-cloud-localconfig-connector/src/test/java/org/springframework/cloud/localconfig/AbstractLocalConfigConnectorTest.java @@ -0,0 +1,54 @@ +package org.springframework.cloud.localconfig; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import java.io.IOException; +import java.io.InputStream; +import java.util.List; +import java.util.Properties; + +import org.junit.After; +import org.junit.Before; +import org.springframework.cloud.service.ServiceInfo; +import org.springframework.cloud.service.UriBasedServiceInfo; + +public class AbstractLocalConfigConnectorTest { + + public static final String PROPERTIES_FILE = "localconfig.testuris.properties"; + + protected LocalConfigConnector connector = new LocalConfigConnector(); + + protected static final String HOSTNAME = "10.20.30.40"; + protected static final int PORT = 1234; + protected static final String USERNAME = "myuser"; + protected static final String PASSWORD = "mypass"; + + @Before + public void init() throws IOException { + InputStream propertiesFile = getClass().getClassLoader().getResourceAsStream(PROPERTIES_FILE); + LocalConfigConnector.supplyProperties(propertiesFile); + assertTrue(connector.isInMatchingCloud()); + } + + @After + public void clearProperties() { + LocalConfigConnector.programmaticProperties = new Properties(); + } + + protected static ServiceInfo getServiceInfo(List serviceInfos, String serviceId) { + for (ServiceInfo serviceInfo : serviceInfos) { + if (serviceInfo.getId().equals(serviceId)) { + return serviceInfo; + } + } + return null; + } + + protected static void assertUriParameters(UriBasedServiceInfo serviceInfo) { + assertEquals(HOSTNAME, serviceInfo.getHost()); + assertEquals(PORT, serviceInfo.getPort()); + assertEquals(USERNAME, serviceInfo.getUserName()); + assertEquals(PASSWORD, serviceInfo.getPassword()); + } +} diff --git a/spring-cloud-localconfig-connector/src/test/java/org/springframework/cloud/localconfig/LocalConfigConnectorAmqpServiceTest.java b/spring-cloud-localconfig-connector/src/test/java/org/springframework/cloud/localconfig/LocalConfigConnectorAmqpServiceTest.java new file mode 100644 index 0000000..a515741 --- /dev/null +++ b/spring-cloud-localconfig-connector/src/test/java/org/springframework/cloud/localconfig/LocalConfigConnectorAmqpServiceTest.java @@ -0,0 +1,23 @@ +package org.springframework.cloud.localconfig; + +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +import java.util.List; + +import org.junit.Test; +import org.springframework.cloud.service.ServiceInfo; +import org.springframework.cloud.service.common.AmqpServiceInfo; + +public class LocalConfigConnectorAmqpServiceTest extends AbstractLocalConfigConnectorTest { + + @Test + public void serviceCreation() { + List services = connector.getServiceInfos(); + ServiceInfo service = getServiceInfo(services, "rabbit"); + assertNotNull(service); + assertTrue(service instanceof AmqpServiceInfo); + assertUriParameters((AmqpServiceInfo) service); + } + +} diff --git a/spring-cloud-localconfig-connector/src/test/java/org/springframework/cloud/localconfig/LocalConfigConnectorMongoServiceTest.java b/spring-cloud-localconfig-connector/src/test/java/org/springframework/cloud/localconfig/LocalConfigConnectorMongoServiceTest.java new file mode 100644 index 0000000..5fec86f --- /dev/null +++ b/spring-cloud-localconfig-connector/src/test/java/org/springframework/cloud/localconfig/LocalConfigConnectorMongoServiceTest.java @@ -0,0 +1,23 @@ +package org.springframework.cloud.localconfig; + +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +import java.util.List; + +import org.junit.Test; +import org.springframework.cloud.service.ServiceInfo; +import org.springframework.cloud.service.common.MongoServiceInfo; + +public class LocalConfigConnectorMongoServiceTest extends AbstractLocalConfigConnectorTest { + + @Test + public void serviceCreation() { + List services = connector.getServiceInfos(); + ServiceInfo service = getServiceInfo(services, "candygram"); + assertNotNull(service); + assertTrue(service instanceof MongoServiceInfo); + assertUriParameters((MongoServiceInfo) service); + } + +} diff --git a/spring-cloud-localconfig-connector/src/test/java/org/springframework/cloud/localconfig/LocalConfigConnectorMysqlServiceTest.java b/spring-cloud-localconfig-connector/src/test/java/org/springframework/cloud/localconfig/LocalConfigConnectorMysqlServiceTest.java new file mode 100644 index 0000000..4e305dd --- /dev/null +++ b/spring-cloud-localconfig-connector/src/test/java/org/springframework/cloud/localconfig/LocalConfigConnectorMysqlServiceTest.java @@ -0,0 +1,23 @@ +package org.springframework.cloud.localconfig; + +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +import java.util.List; + +import org.junit.Test; +import org.springframework.cloud.service.ServiceInfo; +import org.springframework.cloud.service.common.MysqlServiceInfo; + +public class LocalConfigConnectorMysqlServiceTest extends AbstractLocalConfigConnectorTest { + + @Test + public void serviceCreation() { + List services = connector.getServiceInfos(); + ServiceInfo service = getServiceInfo(services, "maria"); + assertNotNull(service); + assertTrue(service instanceof MysqlServiceInfo); + assertUriParameters((MysqlServiceInfo) service); + } + +} diff --git a/spring-cloud-localconfig-connector/src/test/java/org/springframework/cloud/localconfig/LocalConfigConnectorPostgresqlServiceTest.java b/spring-cloud-localconfig-connector/src/test/java/org/springframework/cloud/localconfig/LocalConfigConnectorPostgresqlServiceTest.java new file mode 100644 index 0000000..13123a5 --- /dev/null +++ b/spring-cloud-localconfig-connector/src/test/java/org/springframework/cloud/localconfig/LocalConfigConnectorPostgresqlServiceTest.java @@ -0,0 +1,23 @@ +package org.springframework.cloud.localconfig; + +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +import java.util.List; + +import org.junit.Test; +import org.springframework.cloud.service.ServiceInfo; +import org.springframework.cloud.service.common.PostgresqlServiceInfo; + +public class LocalConfigConnectorPostgresqlServiceTest extends AbstractLocalConfigConnectorTest { + + @Test + public void serviceCreation() { + List services = connector.getServiceInfos(); + ServiceInfo service = getServiceInfo(services, "ingres"); + assertNotNull(service); + assertTrue(service instanceof PostgresqlServiceInfo); + assertUriParameters((PostgresqlServiceInfo) service); + } + +} diff --git a/spring-cloud-localconfig-connector/src/test/java/org/springframework/cloud/localconfig/LocalConfigConnectorRedisServiceTest.java b/spring-cloud-localconfig-connector/src/test/java/org/springframework/cloud/localconfig/LocalConfigConnectorRedisServiceTest.java new file mode 100644 index 0000000..29dd550 --- /dev/null +++ b/spring-cloud-localconfig-connector/src/test/java/org/springframework/cloud/localconfig/LocalConfigConnectorRedisServiceTest.java @@ -0,0 +1,23 @@ +package org.springframework.cloud.localconfig; + +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +import java.util.List; + +import org.junit.Test; +import org.springframework.cloud.service.ServiceInfo; +import org.springframework.cloud.service.common.RedisServiceInfo; + +public class LocalConfigConnectorRedisServiceTest extends AbstractLocalConfigConnectorTest { + + @Test + public void serviceCreation() { + List services = connector.getServiceInfos(); + ServiceInfo service = getServiceInfo(services, "blue"); + assertNotNull(service); + assertTrue(service instanceof RedisServiceInfo); + assertUriParameters((RedisServiceInfo) service); + } + +} diff --git a/spring-cloud-localconfig-connector/src/test/java/org/springframework/cloud/localconfig/LocalConfigConnectorTest.java b/spring-cloud-localconfig-connector/src/test/java/org/springframework/cloud/localconfig/LocalConfigConnectorTest.java new file mode 100644 index 0000000..9bf3f1d --- /dev/null +++ b/spring-cloud-localconfig-connector/src/test/java/org/springframework/cloud/localconfig/LocalConfigConnectorTest.java @@ -0,0 +1,145 @@ +package org.springframework.cloud.localconfig; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.Charset; +import java.util.List; +import java.util.Properties; + +import org.junit.After; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.contrib.java.lang.system.ClearSystemProperties; +import org.junit.contrib.java.lang.system.ProvideSystemProperty; +import org.springframework.cloud.service.UriBasedServiceData; + +public class LocalConfigConnectorTest { + + static final Charset UTF_8 = Charset.forName("UTF-8"); + + public static final String APP_ID_1 = "appId1"; + public static final String APP_ID_1_PROPERTY = LocalConfigConnector.APP_ID_PROPERTY + ": " + APP_ID_1; + + public static final String APP_ID_2 = "appId2"; + public static final String APP_ID_2_PROPERTY = LocalConfigConnector.APP_ID_PROPERTY + ": " + APP_ID_2; + + public static final String PROPERTY_FILE_NAME = "localconfig.nonsense.properties"; + public static final String PROPERTY_FILE_PROPERTY = LocalConfigConnector.PROPERTIES_FILE_PROPERTY + ": " + PROPERTY_FILE_NAME; + + public static class DetectAppIdTest { + + private LocalConfigConnector connector; + + @Before + public void setup() { + connector = new LocalConfigConnector(); + } + + @After + public void clearProperties() { + LocalConfigConnector.programmaticProperties = new Properties(); + } + + @Rule + public final ClearSystemProperties NO_APP_ID_PROPERTY = new ClearSystemProperties(LocalConfigConnector.APP_ID_PROPERTY); + + @Test + public void testNoAppIdAnywhere() { + assertFalse(connector.isInMatchingCloud()); + } + + @Test + public void testProgrammaticAppId() throws IOException { + LocalConfigConnector.supplyProperties(new ByteArrayInputStream(APP_ID_1_PROPERTY.getBytes(UTF_8))); + assertTrue(connector.isInMatchingCloud()); + assertEquals(APP_ID_1, connector.getApplicationInstanceInfo().getAppId()); + } + + @Test + public void testProgrammaticAndFileAppIds() throws IOException { + LocalConfigConnector.supplyProperties(new ByteArrayInputStream(APP_ID_1_PROPERTY.getBytes(UTF_8))); + LocalConfigConnector.supplyProperties(new ByteArrayInputStream(PROPERTY_FILE_PROPERTY.getBytes(UTF_8))); + + LocalConfigConnector stubConnector = new LocalConfigConnector() { + @Override + InputStream openFile(String filename) throws IOException { + assertEquals(PROPERTY_FILE_NAME, filename); + return new ByteArrayInputStream(APP_ID_2_PROPERTY.getBytes(UTF_8)); + }; + }; + + assertTrue(stubConnector.isInMatchingCloud()); + assertEquals(APP_ID_2, stubConnector.getApplicationInstanceInfo().getAppId()); + } + + @Test + public void testProgrammaticFilenamePlusSystemAppId() throws IOException { + LocalConfigConnector.supplyProperties(new ByteArrayInputStream(PROPERTY_FILE_PROPERTY.getBytes(UTF_8))); + + LocalConfigConnector stubConnector = new LocalConfigConnector() { + @Override + InputStream openFile(String filename) throws IOException { + assertEquals(PROPERTY_FILE_NAME, filename); + return new ByteArrayInputStream(APP_ID_2_PROPERTY.getBytes(UTF_8)); + }; + }; + + System.setProperty(LocalConfigConnector.APP_ID_PROPERTY, "helloApp"); + assertTrue(stubConnector.isInMatchingCloud()); + assertEquals("helloApp", stubConnector.getApplicationInstanceInfo().getAppId()); + } + } + + private LocalConfigConnector connector; + + InputStream propertiesFile; + + @Before + public void setup() { + connector = new LocalConfigConnector(); + propertiesFile = LocalConfigConnectorTest.class.getClassLoader().getResourceAsStream(PROPERTY_FILE_NAME); + } + + @After + public void cleanup() throws IOException { + LocalConfigConnector.programmaticProperties = new Properties(); + propertiesFile.close(); + } + + @Test + public void testLoadFromFile() throws IOException { + LocalConfigConnector.supplyProperties(propertiesFile); + + assertTrue(connector.isInMatchingCloud()); + assertEquals("testApp", connector.getApplicationInstanceInfo().getAppId()); + + List services = connector.getServicesData(); + assertEquals(2, services.size()); + for (UriBasedServiceData service : services) + if ("foo".equals(service.getKey())) + assertEquals("bar", service.getUri()); + } + + @Rule + public ProvideSystemProperty BAZ_PROPERTY = new ProvideSystemProperty("spring.cloud.baz", "inline!"); + + @Test + public void testLoadFromInputStreamWithOverride() throws IOException { + LocalConfigConnector.supplyProperties(propertiesFile); + + assertTrue(connector.isInMatchingCloud()); + assertEquals("testApp", connector.getApplicationInstanceInfo().getAppId()); + + List services = connector.getServicesData(); + assertEquals(2, services.size()); + for(UriBasedServiceData service: services) + if("baz".equals(service.getKey())) + assertEquals("inline!", service.getUri()); + } +} diff --git a/spring-cloud-localconfig-connector/src/test/java/org/springframework/cloud/localconfig/LocalConfigServiceOverrideTest.java b/spring-cloud-localconfig-connector/src/test/java/org/springframework/cloud/localconfig/LocalConfigServiceOverrideTest.java new file mode 100644 index 0000000..980067a --- /dev/null +++ b/spring-cloud-localconfig-connector/src/test/java/org/springframework/cloud/localconfig/LocalConfigServiceOverrideTest.java @@ -0,0 +1,34 @@ +package org.springframework.cloud.localconfig; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +import java.util.List; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.contrib.java.lang.system.ProvideSystemProperty; +import org.springframework.cloud.service.ServiceInfo; +import org.springframework.cloud.service.common.MongoServiceInfo; + +public class LocalConfigServiceOverrideTest extends AbstractLocalConfigConnectorTest { + + @Rule + public final ProvideSystemProperty OVERRIDE_MYSQL = + new ProvideSystemProperty( + "spring.cloud.candygram", + "mongodb://youruser:yourpass@40.30.20.10:4321/dbname"); + + @Test + public void serviceOverride() { + List services = connector.getServiceInfos(); + ServiceInfo service = getServiceInfo(services, "candygram"); + assertNotNull(service); + assertTrue(service instanceof MongoServiceInfo); + MongoServiceInfo mongo = (MongoServiceInfo) service; + assertEquals("youruser", mongo.getUserName()); + assertEquals(4321, mongo.getPort()); + } + +} diff --git a/spring-cloud-localconfig-connector/src/test/java/org/springframework/cloud/localconfig/LocalConfigUtilTest.java b/spring-cloud-localconfig-connector/src/test/java/org/springframework/cloud/localconfig/LocalConfigUtilTest.java new file mode 100644 index 0000000..306e5db --- /dev/null +++ b/spring-cloud-localconfig-connector/src/test/java/org/springframework/cloud/localconfig/LocalConfigUtilTest.java @@ -0,0 +1,74 @@ +package org.springframework.cloud.localconfig; + +import static org.junit.Assert.*; + +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Properties; + +import org.junit.Before; +import org.junit.Test; +import org.springframework.cloud.service.UriBasedServiceData; + +public class LocalConfigUtilTest { + + private Properties first, second; + + private LinkedHashMap propertySources; + + @Before + public void initProperties(){ + first = new Properties(); + second = new Properties(); + + propertySources = new LinkedHashMap(); + propertySources.put("first", first); + propertySources.put("second", second); + } + + @Test + public void testPropertyParsing() { + first.setProperty("spring.cloud.appId", "should skip me because I'm meta"); + first.setProperty("spring.cloud.service1", "one"); + first.setProperty("spring.cloud.", "should skip me because I don't have an ID"); + first.setProperty("spring.cloud.service.two", "two"); + first.setProperty("foobar", "should skip me because I don't match the prefix"); + + Map services = LocalConfigUtil.readServices(first); + assertEquals(2, services.size()); + assertEquals("one", services.get("service1")); + assertEquals("two", services.get("service.two")); + } + + @Test + public void testCollation() { + first.setProperty("spring.cloud.first", "firstUri"); + second.setProperty("spring.cloud.second", "secondUri"); + + List serviceData = LocalConfigUtil.readServicesData(propertySources); + assertEquals(2, serviceData.size()); + boolean foundFirst = false; + + for(UriBasedServiceData kvp : serviceData) { + if(kvp.getKey().equals("first")) { + assertEquals("firstUri", kvp.getUri()); + foundFirst = true; + } + } + + assertTrue(foundFirst); + } + + @Test + public void testOverride() { + first.setProperty("spring.cloud.duplicate", "firstUri"); + second.setProperty("spring.cloud.duplicate", "secondUri"); + + List serviceData = LocalConfigUtil.readServicesData(propertySources); + assertEquals(1, serviceData.size()); + UriBasedServiceData kvp = serviceData.get(0); + assertEquals("duplicate", kvp.getKey()); + assertEquals("secondUri", kvp.getUri()); + } +} diff --git a/spring-cloud-localconfig-connector/src/test/resources/localconfig.nonsense.properties b/spring-cloud-localconfig-connector/src/test/resources/localconfig.nonsense.properties new file mode 100644 index 0000000..bcc84c3 --- /dev/null +++ b/spring-cloud-localconfig-connector/src/test/resources/localconfig.nonsense.properties @@ -0,0 +1,3 @@ +spring.cloud.appId: testApp +spring.cloud.foo: bar +spring.cloud.baz: quux \ No newline at end of file diff --git a/spring-cloud-localconfig-connector/src/test/resources/localconfig.testuris.properties b/spring-cloud-localconfig-connector/src/test/resources/localconfig.testuris.properties new file mode 100644 index 0000000..94bb5d9 --- /dev/null +++ b/spring-cloud-localconfig-connector/src/test/resources/localconfig.testuris.properties @@ -0,0 +1,6 @@ +spring.cloud.appId: testAppWithUris +spring.cloud.rabbit: amqp://myuser:mypass@10.20.30.40:1234/queue +spring.cloud.maria: mysql://myuser:mypass@10.20.30.40:1234/dbname +spring.cloud.candygram: mongodb://myuser:mypass@10.20.30.40:1234/dbname +spring.cloud.ingres: postgres://myuser:mypass@10.20.30.40:1234/dbname +spring.cloud.blue: redis://myuser:mypass@10.20.30.40:1234/dbname \ No newline at end of file diff --git a/spring-cloud-spring-service-connector/src/main/java/org/springframework/cloud/config/ServiceScanHelper.java b/spring-cloud-spring-service-connector/src/main/java/org/springframework/cloud/config/ServiceScanHelper.java index bf162c9..deefb6f 100644 --- a/spring-cloud-spring-service-connector/src/main/java/org/springframework/cloud/config/ServiceScanHelper.java +++ b/spring-cloud-spring-service-connector/src/main/java/org/springframework/cloud/config/ServiceScanHelper.java @@ -9,7 +9,6 @@ import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.beans.factory.support.BeanDefinitionBuilder; import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.cloud.Cloud; -import org.springframework.cloud.CloudException; import org.springframework.cloud.CloudFactory; import org.springframework.cloud.config.java.ServiceScan; import org.springframework.cloud.service.GenericCloudServiceConnectorFactory;