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 **Note:** If you are using spring-cloud in a Spring application, you should consider using the
-[Java config](../spring-cloud-spring-service-connector#the-java-config) or the
+> **Note:** If you are using spring-cloud in a Spring application, you should consider using the
+[Java config](../spring-cloud-spring-service-connector#the-java-config) or the
[XML namespace support](../spring-cloud-spring-service-connector#the-cloud-namespace) instead.
-* Create a [`CloudFactory`](src/main/java/org/springframework/cloud/CloudFactory.java) instance.
+* Create a [`CloudFactory`](src/main/java/org/springframework/cloud/CloudFactory.java) instance.
Creation of a `CloudFactory` instance is a bit expensive, so caching such an instance is recommended.
If you are using a dependency injection frameworks such as Spring, creating a bean for `CloudFactory`
will achieve the caching effect.
@@ -25,45 +27,45 @@ Usage pattern: Application Developers
```java
CloudFactory cloudFactory = new CloudFactory();
```
-* Obtain a suitable [`Cloud`](src/main/java/org/springframework/cloud/Cloud.java) for the environment
+* Obtain a suitable [`Cloud`](src/main/java/org/springframework/cloud/Cloud.java) for the environment
in which the application is running.
-
+
```java
Cloud cloud = cloudFactory.getCloud();
```
Note that you must have a `CloudConnector` implementation suitable
for the environment in which the application is being deployed in your classpath. For example, if you are
- deploying the application in Cloud Foundry, you must add [cloudfoundry-connector](../spring-cloud-cloudfoundry-connector)
+ deploying the application in Cloud Foundry, you must add [cloudfoundry-connector](../spring-cloud-cloudfoundry-connector)
in your classpath. If no suitable `CloudConnctor` is found, the `getCloud()` method will throw a `CloudException`.
-* Use the `Cloud` instance to get access to application info, service infos, and create service
+* Use the `Cloud` instance to get access to application info, service infos, and create service
connectors.
```java
// ServiceInfo has all the information necessary to connect to the underlying service
cloud.getServiceInfos();
```
-
+
```java
// Alternatively, let the cloud create a service connector for you
DataSource ds = cloud.getServiceConnector("inventory-db", DataSource.class, null /* default config */);
```
-
+
Usage pattern: Cloud and Service Providers
==========================================
A cloud provider may extends the functionality in two ways:
-1. Add new [`CloudConnector`](src/main/java/org/springframework/cloud/CloudConnector.java)s to make
- spring-cloud related libraries work with a new cloud.
- See [cloudfoundry-connector](../spring-cloud-cloudfoundry-connector)
- or [heroku-connector](../spring-cloud-heroku-connector) for an example.
+1. Add new [`CloudConnector`](src/main/java/org/springframework/cloud/CloudConnector.java)s to make
+ spring-cloud related libraries work with a new cloud.
+ See [cloudfoundry-connector](../spring-cloud-cloudfoundry-connector)
+ or [heroku-connector](../spring-cloud-heroku-connector) for an example.
This is done declaratively by adding connector classes to:
```
META-INF/services/org.springframework.cloud.CloudConnector
```
-2. Add new [`ServiceConnectorCreator`](src/main/java/org/springframework/cloud/service/ServiceConnectorCreator.java)s
- to allow creation of service connector objects.
- See [spring-service-connector](../spring-cloud-spring-service-connector) for an example.
- This is done declaratively by adding creator classes to:
+2. Add new [`ServiceConnectorCreator`](src/main/java/org/springframework/cloud/service/ServiceConnectorCreator.java)s
+ to allow creation of service connector objects.
+ See [spring-service-connector](../spring-cloud-spring-service-connector) for an example.
+ This is done declaratively by adding creator classes to:
```
META-INF/services/org.springframework.cloud.service.ServiceConnectorCreator
```
\ No newline at end of file
diff --git a/spring-cloud-core/src/main/java/org/springframework/cloud/AbstractCloudConnector.java b/spring-cloud-core/src/main/java/org/springframework/cloud/AbstractCloudConnector.java
index 6a73afa..eb8291d 100644
--- a/spring-cloud-core/src/main/java/org/springframework/cloud/AbstractCloudConnector.java
+++ b/spring-cloud-core/src/main/java/org/springframework/cloud/AbstractCloudConnector.java
@@ -10,25 +10,25 @@ import org.springframework.cloud.service.ServiceInfo;
/**
* Helper abstract class to simplify {@link CloudConnector} implementations.
- *
+ *
* User the {@link ServiceLoader} approach to looks for file name matching the class passed in constructor
* and registers {@link ServiceInfoCreator} found there.
- *
+ *
* Implementation of {@link CloudConnector}s that wish to support the recommended service scanning approach
* should extends this approach to gain that functionality automatically.
- *
+ *
* @author Ramnivas Laddad
*
*/
public abstract class AbstractCloudConnector implements CloudConnector {
private static Logger logger = Logger.getLogger(AbstractCloudConnector.class.getName());
-
+
protected List> serviceInfoCreators = new ArrayList>();
protected abstract List getServicesData();
protected abstract FallbackServiceInfoCreator,SD> getFallbackServiceInfoCreator();
-
+
public AbstractCloudConnector(Class extends ServiceInfoCreator extends ServiceInfo, ?>> 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 extends ServiceInfo,SD> 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 extends ServiceInfo, UriBasedServiceData> 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;