clean up Javadocs to make Java 8 happy

This commit is contained in:
Christopher Smith
2014-07-22 03:52:48 -05:00
parent 2f76b86c91
commit fd1677851f
22 changed files with 1041 additions and 937 deletions

View File

@@ -22,320 +22,346 @@ import org.springframework.cloud.service.ServiceInfo.ServiceProperty;
/**
* The main user-level API access to application and services for the app in which this instance is embedded in.
*
* The class connects application and the underlying cloud. Besides passing along information about the application
* instance, it allows simple access for using services. It uses {@link ServiceInfo}s obtained using the
*
* The class connects application and the underlying cloud. Besides passing along information about the application
* instance, it allows simple access for using services. It uses {@link ServiceInfo}s obtained using the
* underlying {@link CloudConnector} and allows translating those to service connector such as a {@link DataSource}.
*
*
* It also passes along information obtained through {@link CloudConnector} to let application take control on how
* to use bound services.
*
* <p>NOTE: Users or cloud providers shouldn't need to instantiate an instance of this class
* (constructor has package-access only for unit-testing purpose). Instead, they can obtain an appropriate
* instance through {@link CloudFactory}</p>
*
*
* <p>
* NOTE: Users or cloud providers shouldn't need to instantiate an instance of this class (constructor has package-access only for
* unit-testing purpose). Instead, they can obtain an appropriate instance through {@link CloudFactory}
* </p>
*
* @author Ramnivas Laddad
*
*/
public class Cloud {
private CloudConnector cloudConnector;
private ServiceConnectorCreatorRegistry serviceConnectorCreatorRegistry = new ServiceConnectorCreatorRegistry();
/**
* Package-access constructor.
*
* @param cloudConnector the underlying connector
* @param serviceConnectorCreators service connector creators
*/
Cloud(CloudConnector cloudConnector, List<ServiceConnectorCreator<?, ? extends ServiceInfo>> serviceConnectorCreators) {
this.cloudConnector = cloudConnector;
for (ServiceConnectorCreator<?, ? extends ServiceInfo> serviceCreator: serviceConnectorCreators) {
registerServiceConnectorCreator(serviceCreator);
}
}
/**
* @see CloudConnector#getApplicationInstanceInfo()
*
* @return information about the application instance
*/
public ApplicationInstanceInfo getApplicationInstanceInfo() {
return cloudConnector.getApplicationInstanceInfo();
}
/**
* Get {@link ServiceInfo} for the given service id
*
* @param serviceId service id
* @return info for the serviceId
*/
public ServiceInfo getServiceInfo(String serviceId) {
for (ServiceInfo serviceInfo : getServiceInfos()) {
if (serviceInfo.getId().equals(serviceId)) {
return serviceInfo;
}
}
throw new CloudException("No service with id " + serviceId + " found");
}
/**
* @see CloudConnector#getServiceInfos()
* @return information about all services bound to the application
*/
public List<ServiceInfo> getServiceInfos() {
return cloudConnector.getServiceInfos();
}
/**
* Get {@link ServiceInfo}s for the bound services that could be mapped to the given service connector type.
*
* <p>
* For example, if the connector type is {@link DataSource}, then the method will return
* all {@link ServiceInfo} objects matching bound relational database services.
* <p>
*
* @param serviceConnectorType service connector type.
* Passing null returns all {@link ServiceInfo}s (matching that of {@link Cloud#getServiceInfos()}
* @return information about services bound to the application that could be transformed into the given connector type
*/
public <T> List<ServiceInfo> getServiceInfos(Class<T> serviceConnectorType) {
List<ServiceInfo> allServiceInfos = getServiceInfos();
List<ServiceInfo> matchingServiceInfos = new ArrayList<ServiceInfo>();
for (ServiceInfo serviceInfo: allServiceInfos) {
if (serviceConnectorCreatorRegistry.canCreate(serviceConnectorType, serviceInfo)) {
matchingServiceInfos.add(serviceInfo);
}
}
return matchingServiceInfos;
}
/**
* Get a service connector for the given service id, the connector type, configured with the given config
*
* @param serviceId the service id
* @param serviceConnectorType The expected class of service connector such as, DataSource.class.
* @param serviceConnectorConfig service connector configuration (such as pooling parameters).
*
*/
public <SC> SC getServiceConnector(String serviceId, Class<SC> serviceConnectorType, ServiceConnectorConfig serviceConnectorConfig) {
ServiceInfo serviceInfo = getServiceInfo(serviceId);
return getServiceConnector(serviceInfo, serviceConnectorType, serviceConnectorConfig);
}
private CloudConnector cloudConnector;
private ServiceConnectorCreatorRegistry serviceConnectorCreatorRegistry = new ServiceConnectorCreatorRegistry();
/**
* Get the singleton service connector for the given connector type, configured with the given config
*
* @param serviceConnectorType The expected class of service connector such as, DataSource.class.
* @param serviceConnectorConfig service connector configuration (such as pooling parameters).
*
*/
public <SC> SC getSingletonServiceConnector(Class<SC> serviceConnectorType, ServiceConnectorConfig serviceConnectorConfig) {
List<ServiceInfo> matchingServiceInfos = getServiceInfos(serviceConnectorType);
if (matchingServiceInfos.size() != 1) {
throw new CloudException("No unique service matching " + serviceConnectorType + " found. Expected 1, found " + matchingServiceInfos.size());
}
ServiceInfo matchingServiceInfo = matchingServiceInfos.get(0);
return getServiceConnector(matchingServiceInfo, serviceConnectorType, serviceConnectorConfig);
}
/**
* Package-access constructor.
*
* @param cloudConnector
* the underlying connector
* @param serviceConnectorCreators
* service connector creators
*/
Cloud(CloudConnector cloudConnector, List<ServiceConnectorCreator<?, ? extends ServiceInfo>> serviceConnectorCreators) {
this.cloudConnector = cloudConnector;
/**
* Register a new service connector creator
*
* @param serviceConnectorCreator
*/
public void registerServiceConnectorCreator(ServiceConnectorCreator<?, ? extends ServiceInfo> serviceConnectorCreator) {
serviceConnectorCreatorRegistry.registerCreator(serviceConnectorCreator);
}
for (ServiceConnectorCreator<?, ? extends ServiceInfo> serviceCreator : serviceConnectorCreators) {
registerServiceConnectorCreator(serviceCreator);
}
}
/**
* Get properties for app and services.
*
*
*
* <p>Application properties always include <code>cloud.application.app-id</code>
* and <code>cloud.application.instance-id</code> with values bound to application id and
* instance id. The rest of the properties are cloud-provider specific, but take the
* <code>cloud.application.&lt;property-name&gt;</code> form.
* <code><pre>
* cloud.application.app-id = helloworld
* cloud.application.instance-id = instance-0-0fab098f
* cloud.application.&lt;property-name&gt; = &lt;property-value&gt;
* </pre></code>
*
* <p>Service specific properties are exposed for each bound service, with each key starting
* in <code>cloud.services</code>. Like application properties, these too are cloud and service specific.
* Each key for a specific service starts with <code>cloud.services.&lt;service-id&gt;</code>
* <code><pre>
* cloud.services.customerDb.type = mysql-5.1
* cloud.services.customerDb.plan = free
* cloud.services.customerDb.connection.hostname = ...
* cloud.services.customerDb.connection.port = ...
* etc...
* </pre></code>
*
* <p>If a there is only a single service of a given type, that service is
* aliased to the service type. Keys for such properties start in <code>cloud.services.&lt;service-type&gt;</code>.
* For example, if there is only a single MySQL service bound to the application, the service properties will also be
* exposed starting with '<code>cloud.services.mysql</code>' key:
* <code><pre>
* cloud.services.mysql.type = mysql-5.1
* cloud.services.mysql.plan = free
* cloud.services.mysql.connection.hostname = ...
* cloud.services.mysql.connection.port = ...
* etc...
* </pre></code>
*
* @return the properties object
*/
public Properties getCloudProperties() {
Map<String, List<ServiceInfo>> mappedServiceInfos = new HashMap<String, List<ServiceInfo>>();
for (ServiceInfo serviceInfo : getServiceInfos()) {
String key = getServiceLabel(serviceInfo);
List<ServiceInfo> serviceInfosForLabel = mappedServiceInfos.get(key);
if (serviceInfosForLabel == null) {
serviceInfosForLabel = new ArrayList<ServiceInfo>();
mappedServiceInfos.put(key, serviceInfosForLabel);
}
serviceInfosForLabel.add(serviceInfo);
}
final String servicePropKeyLead = "cloud.services.";
Properties cloudProperties = new Properties();
for (Entry<String, List<ServiceInfo>> mappedServiceInfo : mappedServiceInfos.entrySet()) {
List<ServiceInfo> serviceInfos = mappedServiceInfo.getValue();
for (ServiceInfo serviceInfo : serviceInfos) {
String idBasedKey = servicePropKeyLead + serviceInfo.getId();
cloudProperties.putAll(getServiceProperties(idBasedKey, serviceInfo));
// If there is only one service for a given label, put props with that label instead of just id
if (serviceInfos.size() == 1) {
String labelBasedKey = servicePropKeyLead + mappedServiceInfo.getKey();
cloudProperties.putAll(getServiceProperties(labelBasedKey, serviceInfo));
}
}
}
cloudProperties.putAll(getAppProperties());
return cloudProperties;
}
private <SC> SC getServiceConnector(ServiceInfo serviceInfo, Class<SC> serviceConnectorType, ServiceConnectorConfig serviceConnectorConfig) {
ServiceConnectorCreator<SC, ServiceInfo> serviceConnectorCreator = serviceConnectorCreatorRegistry.getServiceCreator(serviceConnectorType, serviceInfo);
return serviceConnectorCreator.create(serviceInfo, serviceConnectorConfig);
}
private Properties getAppProperties() {
final String appPropLeadKey = "cloud.application.";
Properties appProperties = new Properties();
appProperties.put(appPropLeadKey + "instance-id", getApplicationInstanceInfo().getInstanceId());
appProperties.put(appPropLeadKey + "app-id", getApplicationInstanceInfo().getAppId());
for (Map.Entry<String, Object> entry : getApplicationInstanceInfo().getProperties().entrySet()) {
if (entry.getValue() != null) {
appProperties.put(appPropLeadKey + entry.getKey(), entry.getValue());
}
}
/**
* @see CloudConnector#getApplicationInstanceInfo()
*
* @return information about the application instance
*/
public ApplicationInstanceInfo getApplicationInstanceInfo() {
return cloudConnector.getApplicationInstanceInfo();
}
return appProperties;
}
private Properties getServiceProperties(String keyLead, ServiceInfo serviceInfo) {
Properties cloudProperties = new Properties();
try {
BeanInfo beanInfo = Introspector.getBeanInfo(serviceInfo.getClass());
PropertyDescriptor[] propDescriptors = beanInfo.getPropertyDescriptors();
for (PropertyDescriptor propDescriptor : propDescriptors) {
ServiceProperty propAnnotation = propDescriptor.getReadMethod().getAnnotation(ServiceProperty.class);
String key = keyLead;
if (propAnnotation != null) {
if (!propAnnotation.category().isEmpty()) {
key = key + "." + propAnnotation.category();
}
if (!propAnnotation.name().isEmpty()) {
key = key + "." + propAnnotation.name();
} else {
key = key + "." + propDescriptor.getName().toLowerCase();
}
Object value = propDescriptor.getReadMethod().invoke(serviceInfo);
if (value != null) {
cloudProperties.put(key, value);
}
}
}
} catch (Exception e) {
throw new CloudException(e);
}
return cloudProperties;
}
private static String getServiceLabel(ServiceInfo serviceInfo) {
Class<? extends ServiceInfo> serviceInfoClass = serviceInfo.getClass();
ServiceLabel labelAnnotation = serviceInfoClass.getAnnotation(ServiceInfo.ServiceLabel.class);
if (labelAnnotation == null) {
return null;
} else {
return labelAnnotation.value();
}
}
/**
* Get {@link ServiceInfo} for the given service id
*
* @param serviceId
* service id
* @return info for the serviceId
*/
public ServiceInfo getServiceInfo(String serviceId) {
for (ServiceInfo serviceInfo : getServiceInfos()) {
if (serviceInfo.getId().equals(serviceId)) {
return serviceInfo;
}
}
throw new CloudException("No service with id " + serviceId + " found");
}
/**
* @see CloudConnector#getServiceInfos()
* @return information about all services bound to the application
*/
public List<ServiceInfo> getServiceInfos() {
return cloudConnector.getServiceInfos();
}
/**
* Get {@link ServiceInfo}s for the bound services that could be mapped to the given service connector type.
*
* <p>
* For example, if the connector type is {@link DataSource}, then the method will return all {@link ServiceInfo} objects
* matching bound relational database services.
* <p>
*
* @param <T>
* The class of the connector to find services for.
* @param serviceConnectorType
* service connector type.
* Passing null returns all {@link ServiceInfo}s (matching that of {@link Cloud#getServiceInfos()}
* @return information about services bound to the application that could be transformed into the given connector type
*/
public <T> List<ServiceInfo> getServiceInfos(Class<T> serviceConnectorType) {
List<ServiceInfo> allServiceInfos = getServiceInfos();
List<ServiceInfo> matchingServiceInfos = new ArrayList<ServiceInfo>();
for (ServiceInfo serviceInfo : allServiceInfos) {
if (serviceConnectorCreatorRegistry.canCreate(serviceConnectorType, serviceInfo)) {
matchingServiceInfos.add(serviceInfo);
}
}
return matchingServiceInfos;
}
/**
* Get a service connector for the given service id, the connector type, configured with the given config
*
*
* @param <SC>
* The class of the service connector to return.
* @param serviceId
* the service id
* @param serviceConnectorType
* The expected class of service connector such as, DataSource.class.
* @param serviceConnectorConfig
* service connector configuration (such as pooling parameters).
* @return a service connector of the specified type with the given configuration applied
*
*/
public <SC> SC getServiceConnector(String serviceId, Class<SC> serviceConnectorType,
ServiceConnectorConfig serviceConnectorConfig) {
ServiceInfo serviceInfo = getServiceInfo(serviceId);
return getServiceConnector(serviceInfo, serviceConnectorType, serviceConnectorConfig);
}
/**
* Get the singleton service connector for the given connector type, configured with the given config
*
* @param <SC>
* The class of the service connector to return.
* @param serviceConnectorType
* The expected class of service connector such as, DataSource.class.
* @param serviceConnectorConfig
* service connector configuration (such as pooling parameters).
* @return the single service connector of the specified type with the given configuration applied
*
*/
public <SC> SC getSingletonServiceConnector(Class<SC> serviceConnectorType, ServiceConnectorConfig serviceConnectorConfig) {
List<ServiceInfo> matchingServiceInfos = getServiceInfos(serviceConnectorType);
if (matchingServiceInfos.size() != 1) {
throw new CloudException("No unique service matching " + serviceConnectorType + " found. Expected 1, found "
+ matchingServiceInfos.size());
}
ServiceInfo matchingServiceInfo = matchingServiceInfos.get(0);
return getServiceConnector(matchingServiceInfo, serviceConnectorType, serviceConnectorConfig);
}
/**
* Register a new service connector creator
*
* @param serviceConnectorCreator the service connector to register
*/
public void registerServiceConnectorCreator(ServiceConnectorCreator<?, ? extends ServiceInfo> serviceConnectorCreator) {
serviceConnectorCreatorRegistry.registerCreator(serviceConnectorCreator);
}
/**
* Get properties for app and services.
*
*
*
* <p>
* Application properties always include <code>cloud.application.app-id</code> and <code>cloud.application.instance-id</code>
* with values bound to application id and instance id. The rest of the properties are cloud-provider specific, but take the
* <code>cloud.application.&lt;property-name&gt;</code> form. <pre>
* cloud.application.app-id = helloworld
* cloud.application.instance-id = instance-0-0fab098f
* cloud.application.&lt;property-name&gt; = &lt;property-value&gt;
* </pre>
*
* <p>
* Service specific properties are exposed for each bound service, with each key starting in <code>cloud.services</code>. Like
* application properties, these too are cloud and service specific. Each key for a specific service starts with
* <code>cloud.services.&lt;service-id&gt;</code> <pre>
* cloud.services.customerDb.type = mysql-5.1
* cloud.services.customerDb.plan = free
* cloud.services.customerDb.connection.hostname = ...
* cloud.services.customerDb.connection.port = ...
* etc...
* </pre>
*
* <p>
* If a there is only a single service of a given type, that service is aliased to the service type. Keys for such properties
* start in <code>cloud.services.&lt;service-type&gt;</code>. For example, if there is only a single MySQL service bound to the
* application, the service properties will also be exposed starting with '<code>cloud.services.mysql</code>' key: <pre>
* cloud.services.mysql.type = mysql-5.1
* cloud.services.mysql.plan = free
* cloud.services.mysql.connection.hostname = ...
* cloud.services.mysql.connection.port = ...
* etc...
* </pre>
*
* @return the properties object
*/
public Properties getCloudProperties() {
Map<String, List<ServiceInfo>> mappedServiceInfos = new HashMap<String, List<ServiceInfo>>();
for (ServiceInfo serviceInfo : getServiceInfos()) {
String key = getServiceLabel(serviceInfo);
List<ServiceInfo> serviceInfosForLabel = mappedServiceInfos.get(key);
if (serviceInfosForLabel == null) {
serviceInfosForLabel = new ArrayList<ServiceInfo>();
mappedServiceInfos.put(key, serviceInfosForLabel);
}
serviceInfosForLabel.add(serviceInfo);
}
final String servicePropKeyLead = "cloud.services.";
Properties cloudProperties = new Properties();
for (Entry<String, List<ServiceInfo>> mappedServiceInfo : mappedServiceInfos.entrySet()) {
List<ServiceInfo> serviceInfos = mappedServiceInfo.getValue();
for (ServiceInfo serviceInfo : serviceInfos) {
String idBasedKey = servicePropKeyLead + serviceInfo.getId();
cloudProperties.putAll(getServiceProperties(idBasedKey, serviceInfo));
// If there is only one service for a given label, put props with that label instead of just id
if (serviceInfos.size() == 1) {
String labelBasedKey = servicePropKeyLead + mappedServiceInfo.getKey();
cloudProperties.putAll(getServiceProperties(labelBasedKey, serviceInfo));
}
}
}
cloudProperties.putAll(getAppProperties());
return cloudProperties;
}
private <SC> SC getServiceConnector(ServiceInfo serviceInfo, Class<SC> serviceConnectorType,
ServiceConnectorConfig serviceConnectorConfig) {
ServiceConnectorCreator<SC, ServiceInfo> serviceConnectorCreator = serviceConnectorCreatorRegistry.getServiceCreator(
serviceConnectorType, serviceInfo);
return serviceConnectorCreator.create(serviceInfo, serviceConnectorConfig);
}
private Properties getAppProperties() {
final String appPropLeadKey = "cloud.application.";
Properties appProperties = new Properties();
appProperties.put(appPropLeadKey + "instance-id", getApplicationInstanceInfo().getInstanceId());
appProperties.put(appPropLeadKey + "app-id", getApplicationInstanceInfo().getAppId());
for (Map.Entry<String, Object> entry : getApplicationInstanceInfo().getProperties().entrySet()) {
if (entry.getValue() != null) {
appProperties.put(appPropLeadKey + entry.getKey(), entry.getValue());
}
}
return appProperties;
}
private Properties getServiceProperties(String keyLead, ServiceInfo serviceInfo) {
Properties cloudProperties = new Properties();
try {
BeanInfo beanInfo = Introspector.getBeanInfo(serviceInfo.getClass());
PropertyDescriptor[] propDescriptors = beanInfo.getPropertyDescriptors();
for (PropertyDescriptor propDescriptor : propDescriptors) {
ServiceProperty propAnnotation = propDescriptor.getReadMethod().getAnnotation(ServiceProperty.class);
String key = keyLead;
if (propAnnotation != null) {
if (!propAnnotation.category().isEmpty()) {
key = key + "." + propAnnotation.category();
}
if (!propAnnotation.name().isEmpty()) {
key = key + "." + propAnnotation.name();
} else {
key = key + "." + propDescriptor.getName().toLowerCase();
}
Object value = propDescriptor.getReadMethod().invoke(serviceInfo);
if (value != null) {
cloudProperties.put(key, value);
}
}
}
} catch (Exception e) {
throw new CloudException(e);
}
return cloudProperties;
}
private static String getServiceLabel(ServiceInfo serviceInfo) {
Class<? extends ServiceInfo> serviceInfoClass = serviceInfo.getClass();
ServiceLabel labelAnnotation = serviceInfoClass.getAnnotation(ServiceInfo.ServiceLabel.class);
if (labelAnnotation == null) {
return null;
} else {
return labelAnnotation.value();
}
}
}
class ServiceConnectorCreatorRegistry {
private static Logger logger = Logger.getLogger(Cloud.class.getName());
private static Logger logger = Logger.getLogger(Cloud.class.getName());
private List<ServiceConnectorCreator<?, ? extends ServiceInfo>> serviceConnectorCreators = new ArrayList<ServiceConnectorCreator<?, ? extends ServiceInfo>>();
public void registerCreator(ServiceConnectorCreator<?, ? extends ServiceInfo> serviceConnectorCreator) {
serviceConnectorCreators.add(serviceConnectorCreator);
}
public <SC, SI extends ServiceInfo> ServiceConnectorCreator<SC, SI> getServiceCreator(Class<SC> serviceConnectorType, SI serviceInfo) {
ServiceConnectorCreator<SC, SI> serviceConnectorCreator = getServiceCreatorOrNull(serviceConnectorType, serviceInfo);
private List<ServiceConnectorCreator<?, ? extends ServiceInfo>> serviceConnectorCreators = new ArrayList<ServiceConnectorCreator<?, ? extends ServiceInfo>>();
if (serviceConnectorCreator != null) {
return serviceConnectorCreator;
} else {
throw new CloudException("No suitable ServiceConnectorCreator found: "
+ "service id=" + serviceInfo.getId() + ", "
+ "service info type=" + serviceInfo.getClass().getName() + ", "
+ "connector type=" + serviceConnectorType);
}
}
public <SC, SI extends ServiceInfo> boolean canCreate(Class<SC> serviceConnectorType, SI serviceInfo) {
return getServiceCreatorOrNull(serviceConnectorType, serviceInfo) != null;
}
public void registerCreator(ServiceConnectorCreator<?, ? extends ServiceInfo> serviceConnectorCreator) {
serviceConnectorCreators.add(serviceConnectorCreator);
}
public boolean accept(ServiceConnectorCreator<?, ? extends ServiceInfo> creator, Class<?> serviceConnectorType, ServiceInfo serviceInfo) {
boolean typeBasedAccept = serviceConnectorType == null ? true : serviceConnectorType.isAssignableFrom(creator.getServiceConnectorType());
boolean infoBasedAccept = serviceInfo == null ? true : creator.getServiceInfoType().isAssignableFrom(serviceInfo.getClass());
return typeBasedAccept && infoBasedAccept;
}
public <SC, SI extends ServiceInfo> ServiceConnectorCreator<SC, SI> getServiceCreator(Class<SC> serviceConnectorType,
SI serviceInfo) {
ServiceConnectorCreator<SC, SI> serviceConnectorCreator = getServiceCreatorOrNull(serviceConnectorType, serviceInfo);
@SuppressWarnings("unchecked")
private <SC, SI extends ServiceInfo> ServiceConnectorCreator<SC, SI> getServiceCreatorOrNull(Class<SC> serviceConnectorType, SI serviceInfo) {
for (ServiceConnectorCreator<?, ? extends ServiceInfo> serviceConnectorCreator : serviceConnectorCreators) {
logger.info("Trying connector creator type " + serviceConnectorCreator);
if (accept(serviceConnectorCreator, serviceConnectorType, serviceInfo)) {
return (ServiceConnectorCreator<SC, SI>) serviceConnectorCreator;
}
}
return null;
}
if (serviceConnectorCreator != null) {
return serviceConnectorCreator;
} else {
throw new CloudException("No suitable ServiceConnectorCreator found: "
+ "service id=" + serviceInfo.getId() + ", "
+ "service info type=" + serviceInfo.getClass().getName() + ", "
+ "connector type=" + serviceConnectorType);
}
}
public <SC, SI extends ServiceInfo> boolean canCreate(Class<SC> serviceConnectorType, SI serviceInfo) {
return getServiceCreatorOrNull(serviceConnectorType, serviceInfo) != null;
}
public boolean accept(ServiceConnectorCreator<?, ? extends ServiceInfo> creator, Class<?> serviceConnectorType,
ServiceInfo serviceInfo) {
boolean typeBasedAccept = serviceConnectorType == null ? true : serviceConnectorType.isAssignableFrom(creator
.getServiceConnectorType());
boolean infoBasedAccept = serviceInfo == null ? true : creator.getServiceInfoType()
.isAssignableFrom(serviceInfo.getClass());
return typeBasedAccept && infoBasedAccept;
}
@SuppressWarnings("unchecked")
private <SC, SI extends ServiceInfo> ServiceConnectorCreator<SC, SI> getServiceCreatorOrNull(Class<SC> serviceConnectorType,
SI serviceInfo) {
for (ServiceConnectorCreator<?, ? extends ServiceInfo> serviceConnectorCreator : serviceConnectorCreators) {
logger.info("Trying connector creator type " + serviceConnectorCreator);
if (accept(serviceConnectorCreator, serviceConnectorType, serviceInfo)) {
return (ServiceConnectorCreator<SC, SI>) serviceConnectorCreator;
}
}
return null;
}
}

View File

@@ -10,95 +10,96 @@ import org.springframework.cloud.util.ServiceLoaderWithExceptionControl;
/**
* Factory for the cloud.
*
*
* <p>
* The main entry point for user code. Typical code directly using this class (as opposed to using a dependency-injection
* framework) will look like:
* The main entry point for user code. Typical code directly using this class (as opposed to using a dependency-injection framework)
* will look like:
*
* <pre>
* CloudFactory cloudFactory = new CloudFactory();
* Cloud cloud = cloudFoundry.getCloud();
* </pre>
*
* Creating object of this class is somewhat expensive (due to scanning of various files to load connectors),
* so consider caching such an object if you create it manually. If you use a dependency-injection framework such as
* Spring, simply creating a bean for either CloudFactory or using this class as a factory-bean for {@link Cloud} will
* suffice.
*
*
* Creating object of this class is somewhat expensive (due to scanning of various files to load connectors), so consider caching
* such an object if you create it manually. If you use a dependency-injection framework such as Spring, simply creating a bean for
* either CloudFactory or using this class as a factory-bean for {@link Cloud} will suffice.
*
* @author Ramnivas Laddad
*
*/
public class CloudFactory {
private List<CloudConnector> cloudConnectors = new ArrayList<CloudConnector>();
private List<ServiceConnectorCreator<?, ? extends ServiceInfo>> serviceCreators = new ArrayList<ServiceConnectorCreator<?, ? extends ServiceInfo>>();
private List<CloudConnector> cloudConnectors = new ArrayList<CloudConnector>();
private List<ServiceConnectorCreator<?, ? extends ServiceInfo>> serviceCreators = new ArrayList<ServiceConnectorCreator<?, ? extends ServiceInfo>>();
public CloudFactory() {
scanCloudConnectors();
scanServiceConnectorCreators();
}
public CloudFactory() {
scanCloudConnectors();
scanServiceConnectorCreators();
}
/**
*
* @return a cloud suitable for the current environment
* @throws CloudException if no suitable cloud found
*/
public Cloud getCloud() {
CloudConnector suitableCloudConnector = null;
for (CloudConnector cloudConnector : cloudConnectors) {
if (cloudConnector.isInMatchingCloud()) {
suitableCloudConnector = cloudConnector;
break;
}
}
/**
*
* @return a cloud suitable for the current environment
* @throws CloudException
* if no suitable cloud found
*/
public Cloud getCloud() {
CloudConnector suitableCloudConnector = null;
for (CloudConnector cloudConnector : cloudConnectors) {
if (cloudConnector.isInMatchingCloud()) {
suitableCloudConnector = cloudConnector;
break;
}
}
if (suitableCloudConnector == null) {
throw new CloudException("No suitable cloud connector found");
}
if (suitableCloudConnector == null) {
throw new CloudException("No suitable cloud connector found");
}
return new Cloud(suitableCloudConnector, serviceCreators);
}
return new Cloud(suitableCloudConnector, serviceCreators);
}
/**
* Register a cloud connector.
*
* <p>
* CloudConnector developers should prefer the declarative mechanism described in README.MD
* instead of calling this method.
* <p>
*
* @param cloudConnector
*/
public void registerCloudConnector(CloudConnector cloudConnector) {
cloudConnectors.add(cloudConnector);
}
/* package access for testing */
List<CloudConnector> getCloudConnectors() {
return cloudConnectors;
}
/**
* Register a cloud connector.
*
* <p>
* CloudConnector developers should prefer the declarative mechanism described in README.MD instead of calling this method.
* </p>
*
* @param cloudConnector
* the cloud connector to register for discovery
*/
public void registerCloudConnector(CloudConnector cloudConnector) {
cloudConnectors.add(cloudConnector);
}
/* package access for testing */
List<ServiceConnectorCreator<?, ? extends ServiceInfo>> getServiceCreators() {
return serviceCreators;
}
/* package access for testing */
List<CloudConnector> getCloudConnectors() {
return cloudConnectors;
}
private void registerServiceCreator(ServiceConnectorCreator<?, ? extends ServiceInfo> serviceConnectorCreator) {
serviceCreators.add(serviceConnectorCreator);
}
/* package access for testing */
List<ServiceConnectorCreator<?, ? extends ServiceInfo>> getServiceCreators() {
return serviceCreators;
}
private void scanCloudConnectors() {
Iterable<CloudConnector> loader = ServiceLoader.load(CloudConnector.class);
for (CloudConnector cloudConnector: loader) {
registerCloudConnector(cloudConnector);
}
}
@SuppressWarnings({ "rawtypes", "unchecked" })
private void scanServiceConnectorCreators() {
Iterable<ServiceConnectorCreator> loader = ServiceLoaderWithExceptionControl.load(ServiceConnectorCreator.class);
for (ServiceConnectorCreator serviceConnectorCreator: loader) {
if (serviceConnectorCreator != null) {
registerServiceCreator(serviceConnectorCreator);
}
}
}
private void registerServiceCreator(ServiceConnectorCreator<?, ? extends ServiceInfo> serviceConnectorCreator) {
serviceCreators.add(serviceConnectorCreator);
}
private void scanCloudConnectors() {
Iterable<CloudConnector> loader = ServiceLoader.load(CloudConnector.class);
for (CloudConnector cloudConnector : loader) {
registerCloudConnector(cloudConnector);
}
}
@SuppressWarnings({ "rawtypes", "unchecked" })
private void scanServiceConnectorCreators() {
Iterable<ServiceConnectorCreator> loader = ServiceLoaderWithExceptionControl.load(ServiceConnectorCreator.class);
for (ServiceConnectorCreator serviceConnectorCreator : loader) {
if (serviceConnectorCreator != null) {
registerServiceCreator(serviceConnectorCreator);
}
}
}
}

View File

@@ -3,25 +3,28 @@ package org.springframework.cloud;
import org.springframework.cloud.service.ServiceInfo;
/**
* Fallback service info creator to deal with situations there is no {@link ServiceInfoCreator} that can
* Fallback service info creator to deal with situations there is no {@link ServiceInfoCreator} that can
* accept service data.
*
* A fallback mechanism is needed, for example, when a services is bound to an app, but there is no
* {@link ServiceInfoCreator} implemented to handle it or the {@link ServiceInfoCreator} implementation
* for it isn't placed properly to be picked up by scanning. By having a fallback mechanism, we can
* extract as much info from the service data as possible (minimally, the service id) and issue a warning.
*
*
* A fallback mechanism is needed, for example, when a services is bound to an app, but there is no {@link ServiceInfoCreator}
* implemented to handle it or the {@link ServiceInfoCreator} implementation
* for it isn't placed properly to be picked up by scanning. By having a fallback mechanism, we can
* extract as much info from the service data as possible (minimally, the service id) and issue a warning.
*
* @author Ramnivas Laddad
*
* @param <SI>
* the type of {@link ServiceInfo} that this fallback creator will return
* @param <SD>
* the type of service definition data that this creator will consume
*/
public abstract class FallbackServiceInfoCreator<SI extends ServiceInfo, SD> implements ServiceInfoCreator<SI, SD>{
public abstract class FallbackServiceInfoCreator<SI extends ServiceInfo, SD> implements ServiceInfoCreator<SI, SD> {
/*
* Override to ensure that it accepts all service data
*/
@Override
public final boolean accept(SD serviceData) {
return true;
}
/*
* Override to ensure that it accepts all service data
*/
@Override
public final boolean accept(SD serviceData) {
return true;
}
}

View File

@@ -1,24 +1,28 @@
package org.springframework.cloud.service;
/**
*
*
* @author Ramnivas Laddad
*
* @param <SC> service connector type
* @param <SI> service info type
* @param <SC>
* service connector type
* @param <SI>
* service info type
*/
public interface ServiceConnectorCreator<SC, SI extends ServiceInfo> {
/**
* Create service for the given service info and configured with the given
* configuration
*
* @param serviceInfo
* @param serviceConnectorConfig
* @return service connector
*/
SC create(SI serviceInfo, ServiceConnectorConfig serviceConnectorConfig);
Class<SC> getServiceConnectorType();
/**
* Create service for the given service info and configured with the given
* configuration
*
* @param serviceInfo
* the {@link ServiceInfo} object containing the information necessary to connect to the service
* @param serviceConnectorConfig
* configuration information to be applied to the connection
* @return service connector
*/
SC create(SI serviceInfo, ServiceConnectorConfig serviceConnectorConfig);
Class<?> getServiceInfoType();
Class<SC> getServiceConnectorType();
Class<?> getServiceInfoType();
}

View File

@@ -10,66 +10,66 @@ import org.springframework.cloud.service.ServiceConnectorCreator;
/**
* A variation of {@link ServiceLoader} that ignores exception encountered when loading services.
*
*
* <p>
* The {@link ServiceLoader} class throws an error when it encounters a service that it cannot load.
* For {@link ServiceConnectorCreator}, this can be a common occurrence due to missing dependencies.
* For example, if an app isn't using MongoDB, thus not declaring dependencies on relevant classes,
* will cause {@link ServiceConnectorCreator} for MongoDB to fail. We don't want to abandon other service
* connector creators in such cases.
* The {@link ServiceLoader} class throws an error when it encounters a service that it cannot load. For
* {@link ServiceConnectorCreator}, this can be a common occurrence due to missing dependencies. For example, if an app isn't using
* MongoDB, thus not declaring dependencies on relevant classes, will cause {@link ServiceConnectorCreator} for MongoDB to fail. We
* don't want to abandon other service connector creators in such cases.
* </p>
*
*
* @author Ramnivas Laddad
*
* @param <T>
* the type of the service being loaded
*/
public class ServiceLoaderWithExceptionControl<T> implements Iterable<T> {
private Iterable<T> underlying;
private static Logger logger = Logger.getLogger(ServiceLoaderWithExceptionControl.class.getName());
private Iterable<T> underlying;
public static <T> Iterable<T> load(Class<T> serviceType) {
ServiceLoader<T> loader = ServiceLoader.load(serviceType);
return new ServiceLoaderWithExceptionControl<T>(loader);
}
private static Logger logger = Logger.getLogger(ServiceLoaderWithExceptionControl.class.getName());
private ServiceLoaderWithExceptionControl(Iterable<T> underlying) {
this.underlying = underlying;
}
public static <T> Iterable<T> load(Class<T> serviceType) {
ServiceLoader<T> loader = ServiceLoader.load(serviceType);
return new ServiceLoaderWithExceptionControl<T>(loader);
}
@Override
public Iterator<T> iterator() {
return new ServiceLoaderIterator(underlying.iterator());
}
private class ServiceLoaderIterator implements Iterator<T> {
private Iterator<T> underlying;
public ServiceLoaderIterator(Iterator<T> underlying) {
this.underlying = underlying;
}
@Override
public boolean hasNext() {
return underlying.hasNext();
}
@Override
public T next() {
try {
return underlying.next();
} catch (ServiceConfigurationError e) {
logger.log(Level.CONFIG, "Failed to load " + e);
if (hasNext()) {
return next();
}
}
return null;
}
@Override
public void remove() {
underlying.remove();
}
}
private ServiceLoaderWithExceptionControl(Iterable<T> underlying) {
this.underlying = underlying;
}
@Override
public Iterator<T> iterator() {
return new ServiceLoaderIterator(underlying.iterator());
}
private class ServiceLoaderIterator implements Iterator<T> {
private Iterator<T> underlying;
public ServiceLoaderIterator(Iterator<T> underlying) {
this.underlying = underlying;
}
@Override
public boolean hasNext() {
return underlying.hasNext();
}
@Override
public T next() {
try {
return underlying.next();
} catch (ServiceConfigurationError e) {
logger.log(Level.CONFIG, "Failed to load " + e);
if (hasNext()) {
return next();
}
}
return null;
}
@Override
public void remove() {
underlying.remove();
}
}
}

View File

@@ -5,9 +5,9 @@ import java.net.URISyntaxException;
import java.net.URLDecoder;
/**
* Factory for standard Cloud Foundry URIs which all conform to the format:
* <p/>
* [jdbc:]scheme://[user:pass]@authority[:port]/path
* Factory for standard Cloud Foundry URIs, which all conform to the format:
* <p>
* {@code [jdbc:]scheme://[user:pass]@authority[:port]/path}
*/
public class StandardUriInfoFactory implements UriInfoFactory {

View File

@@ -2,31 +2,40 @@ package org.springframework.cloud.util;
/**
* An interface implemented in order to create {@link UriInfo}s.
*
*
* If your {@link org.springframework.cloud.service.ServiceInfo} needs to deal with special URIs and you are extending
* {@link org.springframework.cloud.service.UriBasedServiceInfo} then you can implement this factory interface in order
* to return a custom factory from your ServiceInfo by overriding
* {@link org.springframework.cloud.service.UriBasedServiceInfo#getUriInfoFactory()}.
*
*
* @author Jens Deppe
*/
public interface UriInfoFactory {
/**
* Create a {@link UriInfo} based on a URI string
* @param uriString the URI string to parse
*
* @param uriString
* the URI string to parse
* @return a {@link UriInfo}
*/
public UriInfo createUri(String uriString);
/**
* Create a {@link UriInfo} based on explicit components of the URI
*
* @param scheme
* the URI scheme for this service
* @param host
* the host for this service
* @param port
* the port for this service
* @param username
* the authentication username for this service
* @param password
* the authentication password for this service
* @param path
* the path to this service resource on the server
* @return a {@link UriInfo}
*/
public UriInfo createUri(String scheme, String host, int port, String username, String password, String path);

View File

@@ -24,350 +24,400 @@ import org.springframework.data.redis.connection.RedisConnectionFactory;
/**
* JavaConfig base class for simplified access to the bound services.
*
*
* Note that this class doesn't directly expose low-level methods such as getServiceInfo()
* to keep focus on typical needs of a Spring application. But, since it exposes the cloud object itself,
* to keep focus on typical needs of a Spring application. But, since it exposes the cloud object itself,
* you can always call methods on it to get that kind of information.
*
*
* @author Ramnivas Laddad
*
*/
@Configuration
public abstract class AbstractCloudConfig implements BeanFactoryAware {
private static final String CLOUD_FACTORY_BEAN_NAME = "__cloud_factory__";
private CloudFactory cloudFactory;
private static final String CLOUD_FACTORY_BEAN_NAME = "__cloud_factory__";
private Cloud cloud;
private CloudFactory cloudFactory;
private ServiceConnectionFactory connectionFactory;
private Cloud cloud;
/**
* Get the cloud factory.
*
* Most applications will never need this method, but provided here to cover corner cases.
*
* @return cloud factory
*/
protected CloudFactory cloudFactory() {
return cloudFactory;
}
private ServiceConnectionFactory connectionFactory;
/**
* Get the underlying cloud object.
*
* @return the cloud object appropriate for the current environment
*/
@Bean
public Cloud cloud() {
return cloud;
}
public ServiceConnectionFactory connectionFactory() {
return connectionFactory;
}
/**
* Get the object containing service and app properties
*
*/
public Properties properties() {
return cloud().getCloudProperties();
}
/**
* Get the cloud factory.
*
* Most applications will never need this method, but provided here to cover corner cases.
*
* @return cloud factory
*/
protected CloudFactory cloudFactory() {
return cloudFactory;
}
/**
/**
* Get the underlying cloud object.
*
* @return the cloud object appropriate for the current environment
*/
@Bean
public Cloud cloud() {
return cloud;
}
public ServiceConnectionFactory connectionFactory() {
return connectionFactory;
}
/**
* Get the object containing service and app properties
*
* @return the properties of the discovered runtime environment
*/
public Properties properties() {
return cloud().getCloudProperties();
}
/**
* Implementation note: This roundabout way of implementation is required to ensure that
* a {@link CloudFactory} bean if created in some other configuration is available, we should use
* that.
*/
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
if (cloudFactory == null) {
try {
cloudFactory = beanFactory.getBean(CloudFactory.class);
} catch (NoSuchBeanDefinitionException ex) {
cloudFactory = new CloudFactory();
((SingletonBeanRegistry)beanFactory).registerSingleton(CLOUD_FACTORY_BEAN_NAME, cloudFactory);
}
}
this.cloud = cloudFactory.getCloud();
this.connectionFactory = new ServiceConnectionFactory();
}
*/
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
if (cloudFactory == null) {
try {
cloudFactory = beanFactory.getBean(CloudFactory.class);
} catch (NoSuchBeanDefinitionException ex) {
cloudFactory = new CloudFactory();
((SingletonBeanRegistry) beanFactory).registerSingleton(CLOUD_FACTORY_BEAN_NAME, cloudFactory);
}
}
this.cloud = cloudFactory.getCloud();
this.connectionFactory = new ServiceConnectionFactory();
}
public class ServiceConnectionFactory {
// Relational database
/**
* Get the {@link DataSource} object associated with the only relational database service bound to the app.
*
* This is equivalent to the <cloud:data-source/> element.
*
* @return data source
* @throws CloudException if there are either 0 or more than 1 relational database services.
*/
public DataSource dataSource() {
return dataSource((DataSourceConfig)null);
}
/**
* Get the {@link DataSource} object associated with the only relational database service bound to the app
* configured as specified.
*
* This is equivalent to the <cloud:data-source> element with a nested <cloud:connection>
* and/or <cloud:pool> elements.
*
* @param dataSourceConfig configuration for the data source created
* @return data source
* @throws CloudException if there are either 0 or more than 1 relational database services.
*/
public DataSource dataSource(DataSourceConfig dataSourceConfig) {
return cloud.getSingletonServiceConnector(DataSource.class, dataSourceConfig);
}
/**
* Get the {@link DataSource} object for the specified relational database service.
*
* This is equivalent to the <cloud:data-source service-id="serviceId"/>
*
* @param serviceId the name of the service
* @return data source
* @throws CloudException if the specified service doesn't exist
*/
public DataSource dataSource(String serviceId) {
return dataSource(serviceId, null);
}
/**
* Get the {@link DataSource} object for the specified relational database service configured as specified.
*
* This is equivalent to the <cloud:data-source service-id="serviceId"/> element with a
* nested <cloud:connection> and/or <cloud:pool> elements.
*
* @param serviceId the name of the service
* @param dataSourceConfig configuration for the data source created
* @return data source
* @throws CloudException if the specified service doesn't exist
*/
public DataSource dataSource(String serviceId, DataSourceConfig dataSourceConfig) {
return cloud.getServiceConnector(serviceId, DataSource.class, dataSourceConfig);
}
// Mongodb
/**
* Get the {@link MongoDbFactory} object associated with the only MongoDB service bound to the app.
*
* This is equivalent to the <cloud:mongo-db-factory/> element.
*
* @return mongo db factory
* @throws CloudException if there are either 0 or more than 1 mongodb services.
*/
public MongoDbFactory mongoDbFactory() {
return mongoDbFactory((MongoDbFactoryConfig)null);
}
/**
* Get the {@link MongoDbFactory} object associated with the only MongoDB service bound to the app
* configured as specified.
*
* This is equivalent to the <cloud:mongo-db-factory> element with a nested <cloud:mongo-options> element.
*
* @param mongoDbFactoryConfig configuration for the mondo db factory created
* @return mongo db factory
* @throws CloudException if there are either 0 or more than 1 mongodb services.
*/
public MongoDbFactory mongoDbFactory(MongoDbFactoryConfig mongoDbFactoryConfig) {
return cloud.getSingletonServiceConnector(MongoDbFactory.class, mongoDbFactoryConfig);
}
/**
* Get the {@link MongoDbFactory} object for the specified MongoDB service.
*
* This is equivalent to the <cloud:mongo-db-factory service-id="serviceId"> element.
*
* @param serviceId the name of the service
* @return mongo db factory
* @throws CloudException if the specified service doesn't exist
*/
public MongoDbFactory mongoDbFactory(String serviceId) {
return mongoDbFactory(serviceId, null);
}
/**
* Get the {@link MongoDbFactory} object for the specified MongoDB service configured as specified.
*
* This is equivalent to the <cloud:mongo-db-factory service-id="serviceId"> element
* with a nested <cloud:mongo-options> element.
*
* @param serviceId the name of the service
* @param mongoDbFactoryConfig configuration for the mongo db factory created
* @return mongo db factory
* @throws CloudException if the specified service doesn't exist
*/
public MongoDbFactory mongoDbFactory(String serviceId, MongoDbFactoryConfig mongoDbFactoryConfig) {
return cloud.getServiceConnector(serviceId, MongoDbFactory.class, mongoDbFactoryConfig);
}
// RabbitMQ
/**
* Get the {@link ConnectionFactory} object associated with the only RabbitMQ service bound to the app.
*
* This is equivalent to the <cloud:rabbit-connection-factory> element.
*
* @return rabbit connection factory
* @throws CloudException if there are either 0 or more than 1 RabbitMQ services.
*/
public ConnectionFactory rabbitConnectionFactory() {
return rabbitConnectionFactory((RabbitConnectionFactoryConfig)null);
}
/**
* Get the {@link ConnectionFactory} object associated with the only RabbitMQ service bound to the app
* configured as specified.
*
* This is equivalent to the <cloud:rabbit-connection-factory> element
* with a nested <cloud:rabbit-options> element.
*
* @param rabbitConnectionFactoryConfig configuration for the rabbit connection factory created
* @return rabbit connection factory
* @throws CloudException if there are either 0 or more than 1 RabbitMQ services.
*/
public ConnectionFactory rabbitConnectionFactory(RabbitConnectionFactoryConfig rabbitConnectionFactoryConfig) {
return cloud.getSingletonServiceConnector(ConnectionFactory.class, rabbitConnectionFactoryConfig);
}
/**
* Get the {@link ConnectionFactory} object for the specified RabbitMQ service.
*
* This is equivalent to the <cloud:rabbit-connection-factory service-id="serviceId"> element.
*
* @param serviceId the name of the service
* @return rabbit connection factory
* @throws CloudException if the specified service doesn't exist
*/
public ConnectionFactory rabbitConnectionFactory(String serviceId) {
return rabbitConnectionFactory(serviceId, null);
}
/**
* Get the {@link ConnectionFactory} object for the specified RabbitMQ service configured as specified.
*
* This is equivalent to the <cloud:rabbit-connection-factory service-id="serviceId"> element
* with a nested <cloud:rabbit-options> element.
*
* @param serviceId the name of the service
* @param rabbitConnectionFactoryConfig configuration for the {@link ConnectionFactory} created
* @return rabbit connection factory
* @throws CloudException if the specified service doesn't exist
*/
public ConnectionFactory rabbitConnectionFactory(String serviceId, RabbitConnectionFactoryConfig rabbitConnectionFactoryConfig) {
return cloud.getServiceConnector(serviceId, ConnectionFactory.class, rabbitConnectionFactoryConfig);
}
// Redis
/**
* Get the {@link RedisConnectionFactory} object associated with the only Redis service bound to the app.
*
* This is equivalent to the <cloud:redis-connection-factory/> element
*
* @return redis connection factory
* @throws CloudException if there are either 0 or more than 1 redis services.
*/
public RedisConnectionFactory redisConnectionFactory() {
return redisConnectionFactory((PooledServiceConnectorConfig)null);
}
/**
* Get the {@link RedisConnectionFactory} object associated with the only Redis service bound to the app
* configured as specified.
*
* This is equivalent to the <cloud:redis-connection-factory service-id="serviceId"> element
* with a nested <cloud:pool> element.
*
* @param redisConnectionFactoryConfig configuration for the {@link RedisConnectionFactory} created
* @return redis connection factory
* @throws CloudException if there are either 0 or more than 1 redis services.
*/
public RedisConnectionFactory redisConnectionFactory(PooledServiceConnectorConfig redisConnectionFactoryConfig) {
return cloud.getSingletonServiceConnector(RedisConnectionFactory.class, redisConnectionFactoryConfig);
}
/**
* Get the {@link RedisConnectionFactory} object for the specified Redis service.
*
* This is equivalent to the <cloud:redis-connection-factory service-id="serviceId"> element.
*
* @param serviceId the name of the service
* @return redis connection factory
* @throws CloudException if the specified service doesn't exist
*/
public RedisConnectionFactory redisConnectionFactory(String serviceId) {
return redisConnectionFactory(serviceId, null);
}
/**
* Get the {@link RedisConnectionFactory} object for the specified Redis service configured as specified.
*
* This is equivalent to the <cloud:redis-connection-factory service-id="serviceId"> element
* with a nested <cloud:pool> element.
*
* @param serviceId the name of the service
* @param redisConnectionFactoryConfig configuration for the {@link RedisConnectionFactory} created
* @return redis connection factory
* @throws CloudException if the specified service doesn't exist
*/
public RedisConnectionFactory redisConnectionFactory(String serviceId, PooledServiceConnectorConfig redisConnectionFactoryConfig) {
return cloud.getServiceConnector(serviceId, RedisConnectionFactory.class, redisConnectionFactoryConfig);
}
// Generic service
/**
* Get the service connector object associated with the only service bound to the app.
*
* This is equivalent to the <cloud:service/> element.
*
* @return service connector object
* @throws CloudException if there are either 0 or more than 1 services.
*/
public Object service() {
return service(Object.class);
}
/**
* Get the service connector object of the specified type if there is only one such candidate service
*
* This is equivalent to the <cloud:service connector-type="T.class"/> element.
*
* @return service connector object
* @throws CloudException if there are either 0 or more than 1 candidate services.
*/
public <T> T service(Class<T> serviceConnectorType) {
return cloud.getSingletonServiceConnector(serviceConnectorType, null);
}
/**
* Get the service connector object for the specified service.
*
* This is equivalent to the <cloud:service service-id="serviceId"/> element.
*
* @return service connector object
* @throws CloudException if the specified service doesn't exist
*/
public Object service(String serviceId) {
return service(serviceId, Object.class);
}
/**
* Get the service connector object of the specified type and service id
*
* This is equivalent to the <cloud:service service-id="serviceId" connector-type="T.class"/> element.
*
* @return service connector object
* @throws CloudException if the specified service doesn't exist
*/
public <T> T service(String serviceId, Class<T> serviceConnectorType) {
return cloud.getServiceConnector(serviceId, serviceConnectorType, null);
}
}
public class ServiceConnectionFactory {
// Relational database
/**
* Get the {@link DataSource} object associated with the only relational database service bound to the app.
*
* This is equivalent to the {@code <cloud:data-source/>} element.
*
* @return data source
* @throws CloudException
* if there are either 0 or more than 1 relational database services.
*/
public DataSource dataSource() {
return dataSource((DataSourceConfig) null);
}
/**
* Get the {@link DataSource} object associated with the only relational database service bound to the app
* configured as specified.
*
* This is equivalent to the {@code <cloud:data-source>} element with nested {@code <cloud:connection>} and/or
* {@code <cloud:pool>} elements.
*
* @param dataSourceConfig
* configuration for the data source created
* @return data source
* @throws CloudException
* if there are either 0 or more than 1 relational database services.
*/
public DataSource dataSource(DataSourceConfig dataSourceConfig) {
return cloud.getSingletonServiceConnector(DataSource.class, dataSourceConfig);
}
/**
* Get the {@link DataSource} object for the specified relational database service.
*
* This is equivalent to the {@code <cloud:data-source service-id="serviceId"/>}
*
* @param serviceId
* the name of the service
* @return data source
* @throws CloudException
* if the specified service doesn't exist
*/
public DataSource dataSource(String serviceId) {
return dataSource(serviceId, null);
}
/**
* Get the {@link DataSource} object for the specified relational database service configured as specified.
*
* This is equivalent to the {@code <cloud:data-source service-id="serviceId"/>} element with
* nested {@code <cloud:connection>} and/or {@code <cloud:pool>} elements.
*
* @param serviceId
* the name of the service
* @param dataSourceConfig
* configuration for the data source created
* @return data source
* @throws CloudException
* if the specified service doesn't exist
*/
public DataSource dataSource(String serviceId, DataSourceConfig dataSourceConfig) {
return cloud.getServiceConnector(serviceId, DataSource.class, dataSourceConfig);
}
// Mongodb
/**
* Get the {@link MongoDbFactory} object associated with the only MongoDB service bound to the app.
*
* This is equivalent to the {@code <cloud:mongo-db-factory/>} element.
*
* @return mongo db factory
* @throws CloudException
* if there are either 0 or more than 1 mongodb services.
*/
public MongoDbFactory mongoDbFactory() {
return mongoDbFactory((MongoDbFactoryConfig) null);
}
/**
* Get the {@link MongoDbFactory} object associated with the only MongoDB service bound to the app
* configured as specified.
*
* This is equivalent to the {@code <cloud:mongo-db-factory>} element with a nested {@code <cloud:mongo-options>} element.
*
* @param mongoDbFactoryConfig
* configuration for the mondo db factory created
* @return mongo db factory
* @throws CloudException
* if there are either 0 or more than 1 mongodb services.
*/
public MongoDbFactory mongoDbFactory(MongoDbFactoryConfig mongoDbFactoryConfig) {
return cloud.getSingletonServiceConnector(MongoDbFactory.class, mongoDbFactoryConfig);
}
/**
* Get the {@link MongoDbFactory} object for the specified MongoDB service.
*
* This is equivalent to the {@code <cloud:mongo-db-factory service-id="serviceId">} element.
*
* @param serviceId
* the name of the service
* @return mongo db factory
* @throws CloudException
* if the specified service doesn't exist
*/
public MongoDbFactory mongoDbFactory(String serviceId) {
return mongoDbFactory(serviceId, null);
}
/**
* Get the {@link MongoDbFactory} object for the specified MongoDB service configured as specified.
*
* This is equivalent to the {@code <cloud:mongo-db-factory service-id="serviceId">} element
* with a nested {@code <cloud:mongo-options>} element.
*
* @param serviceId
* the name of the service
* @param mongoDbFactoryConfig
* configuration for the mongo db factory created
* @return mongo db factory
* @throws CloudException
* if the specified service doesn't exist
*/
public MongoDbFactory mongoDbFactory(String serviceId, MongoDbFactoryConfig mongoDbFactoryConfig) {
return cloud.getServiceConnector(serviceId, MongoDbFactory.class, mongoDbFactoryConfig);
}
// RabbitMQ
/**
* Get the {@link ConnectionFactory} object associated with the only RabbitMQ service bound to the app.
*
* This is equivalent to the {@code <cloud:rabbit-connection-factory>} element.
*
* @return rabbit connection factory
* @throws CloudException
* if there are either 0 or more than 1 RabbitMQ services.
*/
public ConnectionFactory rabbitConnectionFactory() {
return rabbitConnectionFactory((RabbitConnectionFactoryConfig) null);
}
/**
* Get the {@link ConnectionFactory} object associated with the only RabbitMQ service bound to the app
* configured as specified.
*
* This is equivalent to the {@code <cloud:rabbit-connection-factory>} element
* with a nested {@code <cloud:rabbit-options>} element.
*
* @param rabbitConnectionFactoryConfig
* configuration for the rabbit connection factory created
* @return rabbit connection factory
* @throws CloudException
* if there are either 0 or more than 1 RabbitMQ services.
*/
public ConnectionFactory rabbitConnectionFactory(RabbitConnectionFactoryConfig rabbitConnectionFactoryConfig) {
return cloud.getSingletonServiceConnector(ConnectionFactory.class, rabbitConnectionFactoryConfig);
}
/**
* Get the {@link ConnectionFactory} object for the specified RabbitMQ service.
*
* This is equivalent to the {@code <cloud:rabbit-connection-factory service-id="serviceId">} element.
*
* @param serviceId
* the name of the service
* @return rabbit connection factory
* @throws CloudException
* if the specified service doesn't exist
*/
public ConnectionFactory rabbitConnectionFactory(String serviceId) {
return rabbitConnectionFactory(serviceId, null);
}
/**
* Get the {@link ConnectionFactory} object for the specified RabbitMQ service configured as specified.
*
* This is equivalent to the {@code <cloud:rabbit-connection-factory service-id="serviceId">} element
* with a nested {@code <cloud:rabbit-options>} element.
*
* @param serviceId
* the name of the service
* @param rabbitConnectionFactoryConfig
* configuration for the {@link ConnectionFactory} created
* @return rabbit connection factory
* @throws CloudException
* if the specified service doesn't exist
*/
public ConnectionFactory rabbitConnectionFactory(String serviceId,
RabbitConnectionFactoryConfig rabbitConnectionFactoryConfig) {
return cloud.getServiceConnector(serviceId, ConnectionFactory.class, rabbitConnectionFactoryConfig);
}
// Redis
/**
* Get the {@link RedisConnectionFactory} object associated with the only Redis service bound to the app.
*
* This is equivalent to the {@code <cloud:redis-connection-factory/>} element
*
* @return redis connection factory
* @throws CloudException
* if there are either 0 or more than 1 redis services.
*/
public RedisConnectionFactory redisConnectionFactory() {
return redisConnectionFactory((PooledServiceConnectorConfig) null);
}
/**
* Get the {@link RedisConnectionFactory} object associated with the only Redis service bound to the app
* configured as specified.
*
* This is equivalent to the {@code <cloud:redis-connection-factory service-id="serviceId">} element
* with a nested {@code <cloud:pool>} element.
*
* @param redisConnectionFactoryConfig
* configuration for the {@link RedisConnectionFactory} created
* @return redis connection factory
* @throws CloudException
* if there are either 0 or more than 1 redis services.
*/
public RedisConnectionFactory redisConnectionFactory(PooledServiceConnectorConfig redisConnectionFactoryConfig) {
return cloud.getSingletonServiceConnector(RedisConnectionFactory.class, redisConnectionFactoryConfig);
}
/**
* Get the {@link RedisConnectionFactory} object for the specified Redis service.
*
* This is equivalent to the {@code <cloud:redis-connection-factory service-id="serviceId">} element.
*
* @param serviceId
* the name of the service
* @return redis connection factory
* @throws CloudException
* if the specified service doesn't exist
*/
public RedisConnectionFactory redisConnectionFactory(String serviceId) {
return redisConnectionFactory(serviceId, null);
}
/**
* Get the {@link RedisConnectionFactory} object for the specified Redis service configured as specified.
*
* This is equivalent to the {@code <cloud:redis-connection-factory service-id="serviceId">} element
* with a nested {@code <cloud:pool>} element.
*
* @param serviceId
* the name of the service
* @param redisConnectionFactoryConfig
* configuration for the {@link RedisConnectionFactory} created
* @return redis connection factory
* @throws CloudException
* if the specified service doesn't exist
*/
public RedisConnectionFactory redisConnectionFactory(String serviceId,
PooledServiceConnectorConfig redisConnectionFactoryConfig) {
return cloud.getServiceConnector(serviceId, RedisConnectionFactory.class, redisConnectionFactoryConfig);
}
// Generic service
/**
* Get the service connector object associated with the only service bound to the app.
*
* This is equivalent to the {@code <cloud:service/>} element.
*
* @return service connector object
* @throws CloudException
* if there are either 0 or more than 1 services.
*/
public Object service() {
return service(Object.class);
}
/**
* Get the service connector object of the specified type if there is only one such candidate service
*
* This is equivalent to the {@code <cloud:service connector-type="T.class"/>} element.
*
* @param <T>
* the type of the service connector to be returned
* @param serviceConnectorType
* the class of the service connector to be returned
* @return service connector object
* @throws CloudException
* if there are either 0 or more than 1 candidate services.
*/
public <T> T service(Class<T> serviceConnectorType) {
return cloud.getSingletonServiceConnector(serviceConnectorType, null);
}
/**
* Get the service connector object for the specified service.
*
* This is equivalent to the {@code <cloud:service service-id="serviceId"/>} element.
*
* @param serviceId
* the service ID of the service to be returned
* @return service connector object
* @throws CloudException
* if the specified service doesn't exist
*/
public Object service(String serviceId) {
return service(serviceId, Object.class);
}
/**
* Get the service connector object of the specified type and service ID
*
* This is equivalent to the {@code <cloud:service service-id="serviceId" connector-type="T.class"/>} element.
*
* @param serviceId
* the service ID of the service to be returned
* @param <T>
* the type of the service connector to be returned
* @param serviceConnectorType
* the class of the service connector to be returned
* @return service connector object
* @throws CloudException
* if the specified service doesn't exist
*/
public <T> T service(String serviceId, Class<T> serviceConnectorType) {
return cloud.getServiceConnector(serviceId, serviceConnectorType, null);
}
}
}

View File

@@ -10,7 +10,7 @@ import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
/**
* Parser for the <cloud:data-source> namespace element
* Parser for the {@code <cloud:data-source>} namespace element
*
* @author Ramnivas Laddad
* @author Thomas Risberg
@@ -38,7 +38,7 @@ public class CloudDataSourceFactoryParser extends AbstractPoolingCloudServiceFac
cloudPoolConfiguration = parsePoolElement((Element) child, parserContext);
}
}
BeanDefinitionBuilder dataSourceConfigBeanBuilder =
BeanDefinitionBuilder.genericBeanDefinition("org.springframework.cloud.service.relational.DataSourceConfig");
dataSourceConfigBeanBuilder.addConstructorArgValue(cloudPoolConfiguration);

View File

@@ -12,7 +12,7 @@ import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
/**
* Parser for the <cloud:mongo-db-factory> namespace element
* Parser for the {@code <cloud:mongo-db-factory>} namespace element
*
* @author Thomas Risberg
* @author Ramnivas Laddad
@@ -31,7 +31,7 @@ public class CloudMongoDbFactoryParser extends AbstractNestedElementCloudService
@Override
protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {
super.doParse(element, parserContext, builder);
Map<String, String> attributeMap = new HashMap<String, String>();
parseWriteConcern(element, attributeMap);
parseMongoOptionsElement(element, parserContext, attributeMap);
@@ -40,9 +40,9 @@ public class CloudMongoDbFactoryParser extends AbstractNestedElementCloudService
BeanDefinitionBuilder.genericBeanDefinition("org.springframework.cloud.service.document.MongoDbFactoryConfig");
for (String key : new String[]{WRITE_CONCERN, CONNECTIONS_PER_HOST, MAX_WAIT_TIME}) {
String value = attributeMap.get(key);
cloudMongoConfigurationBeanBuilder.addConstructorArgValue(value);
cloudMongoConfigurationBeanBuilder.addConstructorArgValue(value);
}
builder.addConstructorArgValue(cloudMongoConfigurationBeanBuilder.getBeanDefinition());
}
@@ -55,7 +55,7 @@ public class CloudMongoDbFactoryParser extends AbstractNestedElementCloudService
private void parseMongoOptionsElement(Element element, ParserContext parserContext, Map<String, String> attributeMap) {
NodeList childNodes = element.getChildNodes();
for (int i = 0; i < childNodes.getLength(); i++) {
Node child = childNodes.item(i);
if (isElement(child, parserContext, ELEMENT_MONGO_OPTIONS)) {

View File

@@ -9,7 +9,7 @@ import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
/**
* Parser for the <cloud:rabbit-connection-factory> namespace element
* Parser for the {@code <cloud:rabbit-connection-factory>} namespace element
*
* @author Thomas Risberg
* @author Ramnivas Laddad

View File

@@ -9,8 +9,8 @@ import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
/**
* Parser for the <cloud:redis-connection-factory> namespace element
*
* Parser for the {@code <cloud:redis-connection-factory>} namespace element
*
* @author Ramnivas Laddad
*
*/
@@ -33,11 +33,11 @@ public class CloudRedisConnectionFactoryParser extends AbstractPoolingCloudServi
cloudPoolConfiguration = parsePoolElement((Element) child, parserContext);
}
}
BeanDefinitionBuilder redisConfigBeanBuilder =
BeanDefinitionBuilder.genericBeanDefinition("org.springframework.cloud.service.PooledServiceConnectorConfig");
redisConfigBeanBuilder.addConstructorArgValue(cloudPoolConfiguration);
builder.addConstructorArgValue(redisConfigBeanBuilder.getBeanDefinition());
}
}
}

View File

@@ -9,8 +9,8 @@ import org.w3c.dom.Element;
/**
* Support for the catch-all <cloud:service> namespace
*
* Support for the catch-all {@code <cloud:service>} namespace
*
* @author Ramnivas Laddad
*
*/
@@ -26,7 +26,7 @@ public class GenericCloudServiceFactoryParser extends AbstractCloudServiceFactor
@Override
protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {
super.doParse(element, parserContext, builder);
String connectorTypeName = element.getAttribute(CONNECTOR_TYPE);
if (StringUtils.hasText(connectorTypeName)) {
try {
@@ -36,7 +36,7 @@ public class GenericCloudServiceFactoryParser extends AbstractCloudServiceFactor
throw new CloudException("Failed to load " + connectorTypeName, ex);
}
}
// TBD: Support generic (map-based?) service config
builder.addConstructorArgValue(null);
}

View File

@@ -13,111 +13,118 @@ import org.springframework.util.StringUtils;
/**
* Abstract base factory class.
* <p>
* This factory uses the service creator provided through the constructor to
* create services. If the service name is provided it creates a service object
* based on the service bound to that name. Otherwise, it creates a singleton
* service and fails if it doesn't find a unique service of the expected type.
* This factory uses the service creator provided through the constructor to create services. If the service name is provided it
* creates a service object based on the service bound to that name. Otherwise, it creates a singleton service and fails if it
* doesn't find a unique service of the expected type.
*
* @author Ramnivas Laddad
*
* @param <S> The service type
* @param <S>
* The service type
*/
public abstract class AbstractCloudServiceConnectorFactory<S> extends AbstractFactoryBean<S> implements CloudServiceConnectorFactory<S> {
public abstract class AbstractCloudServiceConnectorFactory<S> extends AbstractFactoryBean<S> implements
CloudServiceConnectorFactory<S> {
private static final String CLOUD_FACTORY_BEAN_NAME = "__cloud_factory__";
private static final String CLOUD_FACTORY_BEAN_NAME = "__cloud_factory__";
private Cloud cloud;
private Cloud cloud;
protected String serviceId;
private Class<? extends S> serviceConnectorType;
private ServiceConnectorConfig serviceConnectorConfiguration;
private S serviceInstance;
protected String serviceId;
private Class<? extends S> serviceConnectorType;
private ServiceConnectorConfig serviceConnectorConfiguration;
/**
*
* @param serviceId Optional service name property. If this property is null, a unique service of the expected type
* (redis, for example) needs to be bound to the application.
* @param serviceConnectorType
* @param serviceConnectorConfiguration
*/
public AbstractCloudServiceConnectorFactory(String serviceId, Class<S> serviceConnectorType, ServiceConnectorConfig serviceConnectorConfiguration) {
this.serviceId = serviceId;
this.serviceConnectorType = serviceConnectorType;
this.serviceConnectorConfiguration = serviceConnectorConfiguration;
}
private S serviceInstance;
public AbstractCloudServiceConnectorFactory(Class<S> serviceConnectorType, ServiceConnectorConfig serviceConnectorConfiguration) {
this(null, serviceConnectorType, serviceConnectorConfiguration);
}
/**
*
* @param serviceId
* Optional service name property. If this property is null, a unique service of the expected type
* (redis, for example) needs to be bound to the application.
* @param serviceConnectorType
* the class of the service connector that will be returned
* @param serviceConnectorConfiguration
* configuration to be applied to the service connector
*/
public AbstractCloudServiceConnectorFactory(String serviceId, Class<S> serviceConnectorType,
ServiceConnectorConfig serviceConnectorConfiguration) {
this.serviceId = serviceId;
this.serviceConnectorType = serviceConnectorType;
this.serviceConnectorConfiguration = serviceConnectorConfiguration;
}
/**
* Set the cloud, for internal testing purpose only.
*
* <p>
* For normal usage, the {@link InitializingBean} approach will create (if needed) a {@link CloudFactory} and obtain a {@link Cloud} from it.
*
* @param cloud
*/
public void setCloud(Cloud cloud) {
this.cloud = cloud;
}
@Override
public void afterPropertiesSet() throws Exception {
ConfigurableListableBeanFactory beanFactory = (ConfigurableListableBeanFactory) getBeanFactory();
if (cloud == null) {
if(beanFactory.getBeansOfType(CloudFactory.class).isEmpty()) {
beanFactory.registerSingleton(CLOUD_FACTORY_BEAN_NAME, new CloudFactory());
}
CloudFactory cloudFactory = beanFactory.getBeansOfType(CloudFactory.class).values().iterator().next();
cloud = cloudFactory.getCloud();
}
if (!StringUtils.hasText(serviceId)) {
List<? extends ServiceInfo> infos = cloud.getServiceInfos(serviceConnectorType);
if (infos.size() != 1) {
throw new CloudException("Expected 1 service matching " + serviceConnectorType.getName() + " type, but found " + infos.size());
}
serviceId = infos.get(0).getId();
}
super.afterPropertiesSet();
}
@Override
protected S createInstance() throws Exception {
return createService();
}
public S createService() {
if (serviceInstance == null && cloud != null) {
serviceInstance = cloud.getServiceConnector(serviceId, serviceConnectorType, serviceConnectorConfiguration);
}
return serviceInstance;
}
@SuppressWarnings("unchecked")
@Override
public Class<?> getObjectType() {
if (serviceConnectorType == null) {
try {
serviceConnectorType = (Class<? extends S>) createService().getClass();
}
catch (Exception e) {
return null;
}
}
return serviceConnectorType;
}
public AbstractCloudServiceConnectorFactory(Class<S> serviceConnectorType, ServiceConnectorConfig serviceConnectorConfiguration) {
this(null, serviceConnectorType, serviceConnectorConfiguration);
}
public String getServiceId() {
return serviceId;
}
/**
* Set the cloud, for internal testing purpose only.
*
* <p>
* For normal usage, the {@link InitializingBean} approach will create (if needed) a {@link CloudFactory} and obtain a
* {@link Cloud} from it.
*
* @param cloud
* the {@link Cloud} instance describing the discovered runtime environment
*/
public void setCloud(Cloud cloud) {
this.cloud = cloud;
}
public void setServiceConnectorType(Class<? extends S> serviceConnectorType) {
if (serviceConnectorType != null) {
this.serviceConnectorType = serviceConnectorType;
}
}
@Override
public void afterPropertiesSet() throws Exception {
ConfigurableListableBeanFactory beanFactory = (ConfigurableListableBeanFactory) getBeanFactory();
if (cloud == null) {
if (beanFactory.getBeansOfType(CloudFactory.class).isEmpty()) {
beanFactory.registerSingleton(CLOUD_FACTORY_BEAN_NAME, new CloudFactory());
}
CloudFactory cloudFactory = beanFactory.getBeansOfType(CloudFactory.class).values().iterator().next();
cloud = cloudFactory.getCloud();
}
if (!StringUtils.hasText(serviceId)) {
List<? extends ServiceInfo> infos = cloud.getServiceInfos(serviceConnectorType);
if (infos.size() != 1) {
throw new CloudException("Expected 1 service matching " + serviceConnectorType.getName() + " type, but found "
+ infos.size());
}
serviceId = infos.get(0).getId();
}
super.afterPropertiesSet();
}
@Override
protected S createInstance() throws Exception {
return createService();
}
public S createService() {
if (serviceInstance == null && cloud != null) {
serviceInstance = cloud.getServiceConnector(serviceId, serviceConnectorType, serviceConnectorConfiguration);
}
return serviceInstance;
}
@SuppressWarnings("unchecked")
@Override
public Class<?> getObjectType() {
if (serviceConnectorType == null) {
try {
serviceConnectorType = (Class<? extends S>) createService().getClass();
} catch (Exception e) {
return null;
}
}
return serviceConnectorType;
}
public String getServiceId() {
return serviceId;
}
public void setServiceConnectorType(Class<? extends S> serviceConnectorType) {
if (serviceConnectorType != null) {
this.serviceConnectorType = serviceConnectorType;
}
}
}

View File

@@ -4,7 +4,7 @@ import org.springframework.util.StringUtils;
/**
* Configuration for pooling
*
*
* @author Ramnivas Laddad
* @author Mark Fisher
* @author Thomas Risberg
@@ -20,7 +20,7 @@ public class PooledServiceConnectorConfig implements ServiceConnectorConfig {
public PoolConfig getPoolConfig() {
return poolConfig;
}
public static class PoolConfig {
private int minPoolSize;
private int maxPoolSize;
@@ -31,54 +31,60 @@ public class PooledServiceConnectorConfig implements ServiceConnectorConfig {
this.maxPoolSize = maxPoolSize;
this.maxWaitTime = maxWaitTime;
}
public PoolConfig(int maxPoolSize, int maxWaitTime) {
this(0, maxPoolSize, maxWaitTime);
}
}
public PoolConfig(String poolSize, int maxWaitTime) {
determinePoolSizeRange(poolSize);
this.maxWaitTime = maxWaitTime;
}
/**
* Getter corresponding to the DBCP initialSize property
* @return property corresponding to DBCP {@code initialSize}
*/
public int getInitialSize() {
return minPoolSize;
}
/**
* Getter corresponding to the DBCP minIdle property
* @return property corresponding to DBCP {@code minIdle}
*/
public int getMinIdle() {
return minPoolSize;
}
/**
* Getter corresponding to the DBCP maxActive property
* @return property corresponding to DBCP {@code maxActive}
*/
public int getMaxActive() {
return maxPoolSize;
}
// For commons-pool2
/**
* @return property corresponding to commons-pool {@code maxTotal}
*/
public int getMaxTotal() {
return maxPoolSize;
}
/**
* Getter corresponding to the DBCP maxWait property
* @return property corresponding to DBCP {@code maxWait}
*/
public int getMaxWait() {
return maxWaitTime;
}
// For commons-pool2
// For commons-pool2
/**
* @return property corresponding to commons-pool {@code maxWaitMillis}
*/
public int getMaxWaitMillis() {
return maxWaitTime;
}
private void determinePoolSizeRange(String poolSize) {
if (StringUtils.hasText(poolSize)) {
try {

View File

@@ -12,7 +12,7 @@ public class MongoDbFactoryConfig implements ServiceConnectorConfig {
private String writeConcern;
private Integer connectionsPerHost;
private Integer maxWaitTime;
public MongoDbFactoryConfig(String writeConcern, Integer connectionsPerHost, Integer maxWaitTime) {
this.writeConcern = writeConcern;
this.connectionsPerHost = connectionsPerHost;
@@ -24,14 +24,14 @@ public class MongoDbFactoryConfig implements ServiceConnectorConfig {
}
/**
* Getter corresponding to the MongoOptions connectionsPerHost field
* @return property corresponding to MongoOptions {@code connectionsPerHost}
*/
public Integer getConnectionsPerHost() {
return connectionsPerHost;
}
/**
* Getter corresponding to the MongoOptions maxWaitTime field
* @return property corresponding to the MongoOptions {@code maxWaitTime}
*/
public Integer getMaxWaitTime () {
return maxWaitTime ;

View File

@@ -8,10 +8,10 @@ import org.springframework.cloud.service.ServiceConnectorConfig;
import org.springframework.cloud.service.common.RelationalServiceInfo;
/**
*
*
* @author Ramnivas Laddad
*
* @param <SI>
* @param <SI> the {@link RelationalServiceInfo} type for the underlying database service
*/
public class BasicDbcpPooledDataSourceCreator<SI extends RelationalServiceInfo> extends DbcpLikePooledDataSourceCreator<SI> {

View File

@@ -15,10 +15,10 @@ import org.springframework.cloud.service.common.RelationalServiceInfo;
import org.springframework.jdbc.datasource.SimpleDriverDataSource;
/**
*
*
* @author Ramnivas Laddad
*
* @param <SI>
* @param <SI> the {@link RelationalServiceInfo} for the backing database service
*/
public abstract class DataSourceCreator<SI extends RelationalServiceInfo> extends AbstractServiceConnectorCreator<DataSource, SI> {
@@ -34,20 +34,20 @@ public abstract class DataSourceCreator<SI extends RelationalServiceInfo> extend
this.driverSystemPropKey = driverSystemPropKey;
this.driverClasses = driverClasses;
this.validationQuery = validationQuery;
if (pooledDataSourceCreators.size() == 0) {
pooledDataSourceCreators.add(new BasicDbcpPooledDataSourceCreator<SI>());
pooledDataSourceCreators.add(new TomcatDbcpPooledDataSourceCreator<SI>());
pooledDataSourceCreators.add(new TomcatHighPerformancePooledDataSourceCreator<SI>());
}
}
@Override
public DataSource create(SI serviceInfo, ServiceConnectorConfig serviceConnectorConfig) {
try {
for (PooledDataSourceCreator<SI> delegate: pooledDataSourceCreators) {
DataSource ds = delegate.create(serviceInfo, serviceConnectorConfig, getDriverClassName(serviceInfo), validationQuery);
if (ds != null) {
return ds;
}
@@ -61,10 +61,10 @@ public abstract class DataSourceCreator<SI extends RelationalServiceInfo> extend
+ serviceInfo.getId() + " service", e);
}
}
public String getDriverClassName(SI serviceInfo) {
String userSpecifiedDriver = System.getProperty(driverSystemPropKey);
if (userSpecifiedDriver != null && !userSpecifiedDriver.isEmpty()) {
return userSpecifiedDriver;
} else {

View File

@@ -12,13 +12,13 @@ import org.springframework.cloud.service.common.RelationalServiceInfo;
/**
* Common implementation that assumes DBCP connection pool properties.
*
*
* @author Ramnivas Laddad
*
* @param <SI>
* @param <SI> the {@link RelationalServiceInfo} type for the underlying database service
*/
public abstract class DbcpLikePooledDataSourceCreator<SI extends RelationalServiceInfo> implements PooledDataSourceCreator<SI> {
protected static Logger logger = Logger.getLogger(PooledDataSourceCreator.class.getName());
private DataSourceConfigurer configurer = new DataSourceConfigurer();
@@ -33,9 +33,9 @@ public abstract class DbcpLikePooledDataSourceCreator<SI extends RelationalServi
target.setPropertyValue("validationQuery", validationQuery);
target.setPropertyValue("testOnBorrow", true);
}
if (serviceConnectorConfig == null) {
// choose sensible values so that we set max connection pool size to what
// choose sensible values so that we set max connection pool size to what
// free tier services on Cloud Foundry and Heroku allow
serviceConnectorConfig = new DataSourceConfig(new PoolConfig(4, 30000), null);
}

View File

@@ -7,10 +7,10 @@ import org.springframework.cloud.service.common.RelationalServiceInfo;
/**
* DataSource creator that produces a pooled connection
*
*
* @author Ramnivas Laddad
*
* @param <SI>
* @param <SI> the {@link RelationalServiceInfo} type for the underlying database service
*/
public interface PooledDataSourceCreator<SI extends RelationalServiceInfo> {
public abstract DataSource create(RelationalServiceInfo serviceInfo, ServiceConnectorConfig serviceConnectorConfig,

View File

@@ -8,10 +8,10 @@ import org.springframework.cloud.service.ServiceConnectorConfig;
import org.springframework.cloud.service.common.RelationalServiceInfo;
/**
*
*
* @author Ramnivas Laddad
*
* @param <SI>
* @param <SI> the {@link RelationalServiceInfo} type for the underlying database service
*/
public class TomcatDbcpPooledDataSourceCreator<SI extends RelationalServiceInfo> extends DbcpLikePooledDataSourceCreator<SI> {

View File

@@ -8,12 +8,10 @@ import org.springframework.cloud.service.ServiceConnectorConfig;
import org.springframework.cloud.service.common.RelationalServiceInfo;
/**
*
* @author Ramnivas Laddad
*
* @param <SI>
* @author Ramnivas Laddad
*/
public class TomcatHighPerformancePooledDataSourceCreator<SI extends RelationalServiceInfo>
public class TomcatHighPerformancePooledDataSourceCreator<SI extends RelationalServiceInfo>
extends DbcpLikePooledDataSourceCreator<SI> {
@Override