Merge branch 'chrylis-local-connector'
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -7,3 +7,4 @@ build
|
||||
Servers
|
||||
.gradle
|
||||
_site
|
||||
/bin
|
||||
|
||||
@@ -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"
|
||||
include "${rootProject.name}-heroku-connector"
|
||||
include "${rootProject.name}-localconfig-connector"
|
||||
|
||||
@@ -5,20 +5,20 @@ import java.util.Map;
|
||||
import org.springframework.cloud.service.common.AmqpServiceInfo;
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* @author Ramnivas Laddad
|
||||
*
|
||||
*/
|
||||
public class AmqpServiceInfoCreator extends CloudFoundryServiceInfoCreator<AmqpServiceInfo> {
|
||||
|
||||
public AmqpServiceInfoCreator() {
|
||||
super(new Tags("rabbitmq"), "amqp");
|
||||
super(new Tags("rabbitmq"), AmqpServiceInfo.URI_SCHEME);
|
||||
}
|
||||
|
||||
public AmqpServiceInfo createServiceInfo(Map<String,Object> serviceData) {
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<String,Object> credentials = (Map<String, Object>) serviceData.get("credentials");
|
||||
|
||||
|
||||
String id = (String) serviceData.get("name");
|
||||
|
||||
String uri = getStringFromCredentials(credentials, "uri", "url");
|
||||
|
||||
@@ -12,8 +12,8 @@ import org.springframework.cloud.service.common.MongoServiceInfo;
|
||||
public class MongoServiceInfoCreator extends CloudFoundryServiceInfoCreator<MongoServiceInfo> {
|
||||
|
||||
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<String,Object> serviceData) {
|
||||
|
||||
@@ -3,14 +3,15 @@ package org.springframework.cloud.cloudfoundry;
|
||||
import org.springframework.cloud.service.common.MysqlServiceInfo;
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* @author Ramnivas Laddad
|
||||
*
|
||||
*/
|
||||
public class MysqlServiceInfoCreator extends RelationalServiceInfoCreator<MysqlServiceInfo> {
|
||||
|
||||
public MysqlServiceInfoCreator() {
|
||||
super(new Tags("mysql"), "mysql");
|
||||
// the literal in the tag is CloudFoundry-specific
|
||||
super(new Tags("mysql"), MysqlServiceInfo.URI_SCHEME);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -4,7 +4,7 @@ import org.springframework.cloud.service.common.OracleServiceInfo;
|
||||
|
||||
public class OracleServiceInfoCreator extends RelationalServiceInfoCreator<OracleServiceInfo> {
|
||||
public OracleServiceInfoCreator() {
|
||||
super(new Tags(), "oracle");
|
||||
super(new Tags(), OracleServiceInfo.URI_SCHEME);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -3,14 +3,14 @@ package org.springframework.cloud.cloudfoundry;
|
||||
import org.springframework.cloud.service.common.PostgresqlServiceInfo;
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* @author Ramnivas Laddad
|
||||
*
|
||||
*/
|
||||
public class PostgresqlServiceInfoCreator extends RelationalServiceInfoCreator<PostgresqlServiceInfo> {
|
||||
|
||||
public PostgresqlServiceInfoCreator() {
|
||||
super(new Tags("postgresql"), "postgres");
|
||||
super(new Tags("postgresql"), PostgresqlServiceInfo.URI_SCHEME);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -12,7 +12,8 @@ import org.springframework.cloud.service.common.RedisServiceInfo;
|
||||
public class RedisServiceInfoCreator extends CloudFoundryServiceInfoCreator<RedisServiceInfo> {
|
||||
|
||||
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<String,Object> serviceData) {
|
||||
|
||||
@@ -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<SmtpS
|
||||
private static final int DEFAULT_SMTP_PORT = 587;
|
||||
|
||||
public SmtpServiceInfoCreator() {
|
||||
super(new Tags("smtp"), "smtp");
|
||||
// the literal in the tag is CloudFoundry-specific
|
||||
super(new Tags("smtp"), SmtpServiceInfo.URI_SCHEME);
|
||||
}
|
||||
|
||||
public SmtpServiceInfo createServiceInfo(Map<String,Object> serviceData) {
|
||||
String id = (String) serviceData.get("name");
|
||||
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<String,Object> credentials = (Map<String, Object>) 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<SmtpS
|
||||
String username = (String) credentials.get("username");
|
||||
String password = (String) credentials.get("password");
|
||||
|
||||
String uri = new UriInfo("smtp", host, port, username, password).toString();
|
||||
|
||||
String uri = new UriInfo(SmtpServiceInfo.URI_SCHEME, host, port, username, password).toString();
|
||||
|
||||
return new SmtpServiceInfo(id, uri);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package org.springframework.cloud.cloudfoundry;
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* @author Ramnivas Laddad
|
||||
*
|
||||
*/
|
||||
@@ -20,12 +20,13 @@ public abstract class AbstractCloudFoundryConnectorRelationalServiceTest extends
|
||||
}
|
||||
|
||||
protected static String getJdbcUrl(String databaseType, String name) {
|
||||
// this should be cleaned up more broadly; pull into RelationalServiceInfo interface?
|
||||
String jdbcUrlDatabaseType = databaseType;
|
||||
if (databaseType.equals("postgres")) {
|
||||
jdbcUrlDatabaseType = "postgresql";
|
||||
}
|
||||
|
||||
return "jdbc:" + jdbcUrlDatabaseType + "://" + hostname + ":" + port + "/" + name +
|
||||
return "jdbc:" + jdbcUrlDatabaseType + "://" + hostname + ":" + port + "/" + name +
|
||||
"?user=" + username + "&password=" + password;
|
||||
}
|
||||
|
||||
|
||||
@@ -2,10 +2,12 @@ Spring Cloud Core Library
|
||||
=========================
|
||||
|
||||
The core library to let cloud applications access application information and services.
|
||||
While Spring applications is one of the main target for this library, it may be used in
|
||||
While Spring applications is one of the main target for this library, it may be used in
|
||||
non-Spring projects as well. In fact, **this library doesn't even depend on Spring**.
|
||||
|
||||
This library is cloud-agnostic. Through connectors, it supports multiple clouds
|
||||
This library requires Java 6,
|
||||
|
||||
This library is cloud-agnostic. Through connectors, it supports multiple clouds
|
||||
(with Cloud Foundry and Heroku as the example clouds).
|
||||
|
||||
This library also supports an extension to create services connectors of user-desired types.
|
||||
@@ -13,11 +15,11 @@ This library also supports an extension to create services connectors of user-de
|
||||
Usage pattern: Application Developers
|
||||
=====================================
|
||||
|
||||
> **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
|
||||
```
|
||||
@@ -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<SD> implements CloudConnector {
|
||||
|
||||
private static Logger logger = Logger.getLogger(AbstractCloudConnector.class.getName());
|
||||
|
||||
|
||||
protected List<ServiceInfoCreator<?,SD>> serviceInfoCreators = new ArrayList<ServiceInfoCreator<?,SD>>();
|
||||
|
||||
protected abstract List<SD> getServicesData();
|
||||
protected abstract FallbackServiceInfoCreator<?,SD> getFallbackServiceInfoCreator();
|
||||
|
||||
|
||||
public AbstractCloudConnector(Class<? extends ServiceInfoCreator<? extends ServiceInfo, ?>> serviceInfoCreatorClass) {
|
||||
scanServiceInfoCreators(serviceInfoCreatorClass);
|
||||
}
|
||||
@@ -39,7 +39,7 @@ public abstract class AbstractCloudConnector<SD> implements CloudConnector {
|
||||
for (SD serviceData : getServicesData()) {
|
||||
serviceInfos.add(getServiceInfo(serviceData));
|
||||
}
|
||||
|
||||
|
||||
return serviceInfos;
|
||||
}
|
||||
|
||||
@@ -54,14 +54,14 @@ public abstract class AbstractCloudConnector<SD> 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()
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
package org.springframework.cloud.service;
|
||||
|
||||
import org.springframework.cloud.FallbackServiceInfoCreator;
|
||||
|
||||
public class FallbackBaseServiceInfoCreator extends FallbackServiceInfoCreator<BaseServiceInfo, UriBasedServiceData> {
|
||||
@Override
|
||||
public BaseServiceInfo createServiceInfo(UriBasedServiceData serviceData) {
|
||||
return new BaseServiceInfo(serviceData.getKey());
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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() + "]";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
package org.springframework.cloud.service;
|
||||
|
||||
import org.springframework.cloud.ServiceInfoCreator;
|
||||
|
||||
public abstract class UriBasedServiceInfoCreator<SI extends ServiceInfo> implements
|
||||
ServiceInfoCreator<ServiceInfo, UriBasedServiceData> {
|
||||
|
||||
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());
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -3,14 +3,14 @@ package org.springframework.cloud.heroku;
|
||||
import org.springframework.cloud.service.common.AmqpServiceInfo;
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* @author Ramnivas Laddad
|
||||
*
|
||||
*/
|
||||
public class AmqpServiceInfoCreator extends HerokuServiceInfoCreator<AmqpServiceInfo> {
|
||||
|
||||
public AmqpServiceInfoCreator() {
|
||||
super("amqp");
|
||||
super(AmqpServiceInfo.URI_SCHEME);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -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<HerokuConnector.KeyValuePair> {
|
||||
public class HerokuConnector extends AbstractCloudConnector<UriBasedServiceData> {
|
||||
|
||||
private EnvironmentAccessor environment = new EnvironmentAccessor();
|
||||
private ApplicationInstanceInfoCreator applicationInstanceInfoCreator
|
||||
private ApplicationInstanceInfoCreator applicationInstanceInfoCreator
|
||||
= new ApplicationInstanceInfoCreator(environment);
|
||||
|
||||
|
||||
private List<String> serviceEnvPrefixes;
|
||||
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||
@@ -41,28 +42,28 @@ public class HerokuConnector extends AbstractCloudConnector<HerokuConnector.KeyV
|
||||
public boolean isInMatchingCloud() {
|
||||
return environment.getEnvValue("DYNO") != null;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public ApplicationInstanceInfo getApplicationInstanceInfo() {
|
||||
try {
|
||||
return applicationInstanceInfoCreator.createApplicationInstanceInfo();
|
||||
} catch (Exception e) {
|
||||
throw new CloudException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* package for testing purpose */
|
||||
void setCloudEnvironment(EnvironmentAccessor environment) {
|
||||
this.environment = environment;
|
||||
this.applicationInstanceInfoCreator = new ApplicationInstanceInfoCreator(environment);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected void registerServiceInfoCreator(ServiceInfoCreator<? extends ServiceInfo, HerokuConnector.KeyValuePair> 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<String>();
|
||||
@@ -75,17 +76,17 @@ public class HerokuConnector extends AbstractCloudConnector<HerokuConnector.KeyV
|
||||
* <p>
|
||||
* Returns map whose key is the env key and value is the associated url
|
||||
* </p>
|
||||
* @return information about services bound to the app
|
||||
* @return information about services bound to the app
|
||||
*/
|
||||
protected List<KeyValuePair> getServicesData() {
|
||||
List<KeyValuePair> serviceData = new ArrayList<KeyValuePair>();
|
||||
|
||||
protected List<UriBasedServiceData> getServicesData() {
|
||||
List<UriBasedServiceData> serviceData = new ArrayList<UriBasedServiceData>();
|
||||
|
||||
Map<String,String> env = environment.getEnv();
|
||||
|
||||
|
||||
for (Map.Entry<String, String> 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<HerokuConnector.KeyV
|
||||
}
|
||||
|
||||
@Override
|
||||
protected FallbackServiceInfoCreator<BaseServiceInfo,KeyValuePair> getFallbackServiceInfoCreator() {
|
||||
return new HerokuFallbackServiceInfoCreator();
|
||||
protected FallbackServiceInfoCreator<BaseServiceInfo,UriBasedServiceData> 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<BaseServiceInfo,KeyValuePair> {
|
||||
@Override
|
||||
public BaseServiceInfo createServiceInfo(KeyValuePair serviceData) {
|
||||
return new BaseServiceInfo(serviceData.getKey());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<SI extends ServiceInfo> implements ServiceInfoCreator<SI,KeyValuePair> {
|
||||
public abstract class HerokuServiceInfoCreator<SI extends ServiceInfo> extends UriBasedServiceInfoCreator<SI> {
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
@@ -3,14 +3,14 @@ package org.springframework.cloud.heroku;
|
||||
import org.springframework.cloud.service.common.MongoServiceInfo;
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* @author Ramnivas Laddad
|
||||
*
|
||||
*/
|
||||
public class MongoServiceInfoCreator extends HerokuServiceInfoCreator<MongoServiceInfo> {
|
||||
|
||||
public MongoServiceInfoCreator() {
|
||||
super("mongodb");
|
||||
super(MongoServiceInfo.URI_SCHEME);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -3,14 +3,14 @@ package org.springframework.cloud.heroku;
|
||||
import org.springframework.cloud.service.common.MysqlServiceInfo;
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* @author Ramnivas Laddad
|
||||
*
|
||||
*/
|
||||
public class MysqlServiceInfoCreator extends RelationalServiceInfoCreator<MysqlServiceInfo> {
|
||||
|
||||
public MysqlServiceInfoCreator() {
|
||||
super("mysql");
|
||||
super(MysqlServiceInfo.URI_SCHEME);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -3,14 +3,14 @@ package org.springframework.cloud.heroku;
|
||||
import org.springframework.cloud.service.common.PostgresqlServiceInfo;
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* @author Ramnivas Laddad
|
||||
*
|
||||
*/
|
||||
public class PostgresqlServiceInfoCreator extends RelationalServiceInfoCreator<PostgresqlServiceInfo> {
|
||||
|
||||
public PostgresqlServiceInfoCreator() {
|
||||
super("postgres");
|
||||
super(PostgresqlServiceInfo.URI_SCHEME);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -3,14 +3,14 @@ package org.springframework.cloud.heroku;
|
||||
import org.springframework.cloud.service.common.RedisServiceInfo;
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* @author Ramnivas Laddad
|
||||
*
|
||||
*/
|
||||
public class RedisServiceInfoCreator extends HerokuServiceInfoCreator<RedisServiceInfo> {
|
||||
|
||||
public RedisServiceInfoCreator() {
|
||||
super("redis");
|
||||
super(RedisServiceInfo.URI_SCHEME);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -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<ServiceInfo> serviceInfos, String serviceId) {
|
||||
for (ServiceInfo serviceInfo : serviceInfos) {
|
||||
if (serviceInfo.getId().equals(serviceId)) {
|
||||
|
||||
@@ -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<String, String> env = new HashMap<String, String>();
|
||||
|
||||
@@ -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<String, String> env = new HashMap<String, String>();
|
||||
|
||||
1
spring-cloud-localconfig-connector/.gitignore
vendored
Normal file
1
spring-cloud-localconfig-connector/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
/bin
|
||||
80
spring-cloud-localconfig-connector/README.md
Normal file
80
spring-cloud-localconfig-connector/README.md
Normal file
@@ -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.
|
||||
6
spring-cloud-localconfig-connector/build.gradle
Normal file
6
spring-cloud-localconfig-connector/build.gradle
Normal file
@@ -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'
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package org.springframework.cloud.localconfig;
|
||||
|
||||
import org.springframework.cloud.service.common.AmqpServiceInfo;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Christopher Smith
|
||||
*
|
||||
*/
|
||||
public class AmqpServiceInfoCreator extends LocalConfigServiceInfoCreator<AmqpServiceInfo>{
|
||||
|
||||
public AmqpServiceInfoCreator() {
|
||||
super(AmqpServiceInfo.URI_SCHEME);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AmqpServiceInfo createServiceInfo(String id, String uri) {
|
||||
return new AmqpServiceInfo(id, uri);
|
||||
}
|
||||
}
|
||||
@@ -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<UriBasedServiceData> {
|
||||
|
||||
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<String> 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.<String, Object> emptyMap());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<UriBasedServiceData> getServicesData() {
|
||||
if(fileProperties == null)
|
||||
throw new IllegalStateException("isInMatchingCloud() must be called first to initialize connector");
|
||||
|
||||
LinkedHashMap<String, Properties> propertySources = new LinkedHashMap<String, Properties>();
|
||||
|
||||
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<BaseServiceInfo, UriBasedServiceData> 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);
|
||||
}
|
||||
}
|
||||
@@ -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<SI extends ServiceInfo> extends UriBasedServiceInfoCreator<SI> {
|
||||
|
||||
protected LocalConfigServiceInfoCreator(String uriScheme) {
|
||||
super(uriScheme);
|
||||
}
|
||||
}
|
||||
@@ -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<UriBasedServiceData> readServicesData(LinkedHashMap<String, Properties> propertySources) {
|
||||
// we'll turn this into KVPs to return but need to eliminate duplicates first
|
||||
Map<String, String> collectedServices = new HashMap<String, String>();
|
||||
|
||||
// iterate over the property sources in order, extracting matching properties
|
||||
for (Map.Entry<String, Properties> propertySource : propertySources.entrySet()) {
|
||||
if(propertySource.getValue().isEmpty()) {
|
||||
logger.info("no " + propertySource.getKey());
|
||||
continue;
|
||||
}
|
||||
logger.info("reading services from " + propertySource.getKey());
|
||||
Map<String, String> services = readServices(propertySource.getValue());
|
||||
|
||||
// add each of the found services to the list, warning about duplicates
|
||||
for (Map.Entry<String, String> 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<UriBasedServiceData> serviceData = new ArrayList<UriBasedServiceData>(collectedServices.size());
|
||||
for (Map.Entry<String, String> 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<String, String> readServices(Properties properties) {
|
||||
Map<String, String> services = new HashMap<String, String>();
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package org.springframework.cloud.localconfig;
|
||||
|
||||
import org.springframework.cloud.service.common.MongoServiceInfo;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Christopher Smith
|
||||
*
|
||||
*/
|
||||
public class MongoServiceInfoCreator extends LocalConfigServiceInfoCreator<MongoServiceInfo>{
|
||||
|
||||
public MongoServiceInfoCreator() {
|
||||
super(MongoServiceInfo.URI_SCHEME);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MongoServiceInfo createServiceInfo(String id, String uri) {
|
||||
return new MongoServiceInfo(id, uri);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package org.springframework.cloud.localconfig;
|
||||
|
||||
import org.springframework.cloud.service.common.MysqlServiceInfo;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Christopher Smith
|
||||
*
|
||||
*/
|
||||
public class MysqlServiceInfoCreator extends LocalConfigServiceInfoCreator<MysqlServiceInfo>{
|
||||
|
||||
public MysqlServiceInfoCreator() {
|
||||
super(MysqlServiceInfo.URI_SCHEME);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MysqlServiceInfo createServiceInfo(String id, String uri) {
|
||||
return new MysqlServiceInfo(id, uri);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package org.springframework.cloud.localconfig;
|
||||
|
||||
import org.springframework.cloud.service.common.PostgresqlServiceInfo;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Christopher Smith
|
||||
*
|
||||
*/
|
||||
public class PostgresqlServiceInfoCreator extends LocalConfigServiceInfoCreator<PostgresqlServiceInfo>{
|
||||
|
||||
public PostgresqlServiceInfoCreator() {
|
||||
super(PostgresqlServiceInfo.URI_SCHEME);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PostgresqlServiceInfo createServiceInfo(String id, String uri) {
|
||||
return new PostgresqlServiceInfo(id, uri);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package org.springframework.cloud.localconfig;
|
||||
|
||||
import org.springframework.cloud.service.common.RedisServiceInfo;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Christopher Smith
|
||||
*
|
||||
*/
|
||||
public class RedisServiceInfoCreator extends LocalConfigServiceInfoCreator<RedisServiceInfo>{
|
||||
|
||||
public RedisServiceInfoCreator() {
|
||||
super(RedisServiceInfo.URI_SCHEME);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RedisServiceInfo createServiceInfo(String id, String uri) {
|
||||
return new RedisServiceInfo(id, uri);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
org.springframework.cloud.localconfig.LocalConfigConnector
|
||||
@@ -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
|
||||
@@ -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<ServiceInfo> 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());
|
||||
}
|
||||
}
|
||||
@@ -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<ServiceInfo> services = connector.getServiceInfos();
|
||||
ServiceInfo service = getServiceInfo(services, "rabbit");
|
||||
assertNotNull(service);
|
||||
assertTrue(service instanceof AmqpServiceInfo);
|
||||
assertUriParameters((AmqpServiceInfo) service);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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<ServiceInfo> services = connector.getServiceInfos();
|
||||
ServiceInfo service = getServiceInfo(services, "candygram");
|
||||
assertNotNull(service);
|
||||
assertTrue(service instanceof MongoServiceInfo);
|
||||
assertUriParameters((MongoServiceInfo) service);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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<ServiceInfo> services = connector.getServiceInfos();
|
||||
ServiceInfo service = getServiceInfo(services, "maria");
|
||||
assertNotNull(service);
|
||||
assertTrue(service instanceof MysqlServiceInfo);
|
||||
assertUriParameters((MysqlServiceInfo) service);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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<ServiceInfo> services = connector.getServiceInfos();
|
||||
ServiceInfo service = getServiceInfo(services, "ingres");
|
||||
assertNotNull(service);
|
||||
assertTrue(service instanceof PostgresqlServiceInfo);
|
||||
assertUriParameters((PostgresqlServiceInfo) service);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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<ServiceInfo> services = connector.getServiceInfos();
|
||||
ServiceInfo service = getServiceInfo(services, "blue");
|
||||
assertNotNull(service);
|
||||
assertTrue(service instanceof RedisServiceInfo);
|
||||
assertUriParameters((RedisServiceInfo) service);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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<UriBasedServiceData> 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<UriBasedServiceData> services = connector.getServicesData();
|
||||
assertEquals(2, services.size());
|
||||
for(UriBasedServiceData service: services)
|
||||
if("baz".equals(service.getKey()))
|
||||
assertEquals("inline!", service.getUri());
|
||||
}
|
||||
}
|
||||
@@ -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<ServiceInfo> 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());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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<String, Properties> propertySources;
|
||||
|
||||
@Before
|
||||
public void initProperties(){
|
||||
first = new Properties();
|
||||
second = new Properties();
|
||||
|
||||
propertySources = new LinkedHashMap<String, Properties>();
|
||||
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<String, String> 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<UriBasedServiceData> 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<UriBasedServiceData> serviceData = LocalConfigUtil.readServicesData(propertySources);
|
||||
assertEquals(1, serviceData.size());
|
||||
UriBasedServiceData kvp = serviceData.get(0);
|
||||
assertEquals("duplicate", kvp.getKey());
|
||||
assertEquals("secondUri", kvp.getUri());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
spring.cloud.appId: testApp
|
||||
spring.cloud.foo: bar
|
||||
spring.cloud.baz: quux
|
||||
@@ -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
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user