Skip to content

Commit

Permalink
Merge "Cloud service-scan should skip service creation for unavailabl…
Browse files Browse the repository at this point in the history
…e dependencies"
  • Loading branch information
Jennifer Hickey authored and testazuretrain committed Sep 23, 2011
2 parents a84db20 + da35474 commit a8c9441
Show file tree
Hide file tree
Showing 17 changed files with 323 additions and 130 deletions.
@@ -1,49 +1,81 @@
package org.cloudfoundry.runtime.service;

import java.util.List;

import org.cloudfoundry.runtime.env.AbstractServiceInfo;
import org.cloudfoundry.runtime.env.CloudEnvironment;
import org.cloudfoundry.runtime.env.CloudServiceException;
import org.cloudfoundry.runtime.service.AbstractServiceCreator.ServiceNameTuple;
import org.springframework.beans.factory.config.AbstractFactoryBean;

/**
* 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 <SI> The service info type matching the service to be created
* @param <S>
* The service type
* @param <SI>
* The service info type matching the service to be created
*/
public abstract class AbstractCloudServiceFactory<S, SI extends AbstractServiceInfo>
extends AbstractFactoryBean<S> {

private AbstractServiceCreator<S, SI> serviceCreator;
public abstract class AbstractCloudServiceFactory<S, SI extends AbstractServiceInfo> extends AbstractFactoryBean<S> {

private Class<SI> serviceInfoClass;

protected String serviceName;

public AbstractCloudServiceFactory(AbstractServiceCreator<S, SI> creationHelper) {
this.serviceCreator = creationHelper;
private CloudEnvironment cloudEnvironment;

private AbstractServiceCreator<S, SI> serviceCreator;

public AbstractCloudServiceFactory(AbstractServiceCreator<S, SI> serviceCreator, Class<SI> serviceInfoClass,
CloudEnvironment cloudEnvironment) {
this.serviceCreator = serviceCreator;
this.serviceInfoClass = serviceInfoClass;
this.cloudEnvironment = cloudEnvironment;
}

/**
* Optional service name property. If this property isn't set or set to null,
* unique service of the expected type (redis, for example) needs to be bound
* to the application.
*
* Optional service name property. If this property isn't set or set to
* null, unique service of the expected type (redis, for example) needs to
* be bound to the application.
*
* @param serviceName
*/
public void setServiceName(String serviceName) {
this.serviceName = serviceName;
}

@Override
protected S createInstance() throws Exception {
if (serviceName != null) {
return serviceCreator.createService(serviceName);
SI serviceInfo = cloudEnvironment.getServiceInfo(serviceName, serviceInfoClass);
if (serviceInfo == null) {
return null;
}
return serviceCreator.createSingletonService(serviceInfo).service;
} else {
return serviceCreator.createSingletonService().service;
List<SI> serviceInfos = cloudEnvironment.getServiceInfos(serviceInfoClass);
if (serviceInfos.size() != 1) {
throw new CloudServiceException("Expected 1 service of " + serviceInfoClass + " type, but found"
+ serviceInfos.size());
}
return serviceCreator.createSingletonService(serviceInfos.get(0)).service;
}
}

/**
*
* @return A list of {@link ServiceNameTuple}s containing beans for each
* service of the expected type (redis, for example) bound to the
* application
*/
public List<ServiceNameTuple<S>> createInstances() {
return serviceCreator.createServices(cloudEnvironment.getServiceInfos(serviceInfoClass));
}
}
Expand Up @@ -5,7 +5,6 @@
import javax.sql.DataSource;

import org.cloudfoundry.runtime.env.AbstractDataSourceServiceInfo;
import org.cloudfoundry.runtime.env.CloudEnvironment;
import org.cloudfoundry.runtime.env.CloudServiceException;
import org.springframework.jdbc.datasource.SimpleDriverDataSource;

Expand All @@ -16,11 +15,6 @@
*/
abstract public class AbstractDataSourceCreator<SI extends AbstractDataSourceServiceInfo>
extends AbstractServiceCreator<DataSource, SI> {

public AbstractDataSourceCreator(CloudEnvironment cloudEnvironment,
Class<SI> serviceInfoClass) {
super(cloudEnvironment, serviceInfoClass);
}

abstract public String getDriverClassName();
abstract public String getValidationQuery();
Expand Down
Expand Up @@ -4,8 +4,6 @@
import java.util.List;

import org.cloudfoundry.runtime.env.AbstractServiceInfo;
import org.cloudfoundry.runtime.env.CloudEnvironment;
import org.cloudfoundry.runtime.env.CloudServiceException;

/**
* Base service creator
Expand All @@ -18,20 +16,13 @@
* </ul>
*
* @author Ramnivas Laddad
* @author Jennifer Hickey
*
* @param <S> Service type to be create
* @param <SI> ServiceInfo type
*/
public abstract class AbstractServiceCreator<S, SI extends AbstractServiceInfo> {

private CloudEnvironment cloudEnvironment;
private Class<SI> serviceInfoClass;

public AbstractServiceCreator(CloudEnvironment cloudEnvironment, Class<SI> serviceInfoClass) {
this.cloudEnvironment = cloudEnvironment;
this.serviceInfoClass = serviceInfoClass;
}

/**
* Implementation of this method must create service based on the service info object passed.
*
Expand All @@ -43,41 +34,20 @@ public AbstractServiceCreator(CloudEnvironment cloudEnvironment, Class<SI> servi
/**
* Create service based on the unique service of the required type.
*
* @param singletonServiceInfo
* @return service object along with the name of the matching service
* @throws ServiceAccessException if unique service of the expected type isn't bound to the application
*/
public ServiceNameTuple<S> createSingletonService() {
List<SI> serviceInfos = cloudEnvironment.getServiceInfos(serviceInfoClass);

if (serviceInfos.size() != 1) {
throw new CloudServiceException("Expected 1 service of " + serviceInfoClass + " type, but found " + serviceInfos.size());
}

SI singletonServiceInfo = serviceInfos.get(0);
public ServiceNameTuple<S> createSingletonService(SI singletonServiceInfo) {
return new ServiceNameTuple<S>(createService(singletonServiceInfo), singletonServiceInfo.getServiceName());
}

/**
* Create service object for the given name service bound to the application.
*
* @param serviceName
* @return service object
*/
public S createService(String serviceName) {
SI serviceInfo = cloudEnvironment.getServiceInfo(serviceName, serviceInfoClass);
if (serviceInfo != null) {
return createService(serviceInfo);
}
return null;
}

/**
* Create service objects for all services of the matching type bound to the application.
* Create service objects for all specified services
*
* @param serviceInfos
* @return service objects along with the name of the matching services
*/
public List<ServiceNameTuple<S>> createServices() {
List<SI> serviceInfos = cloudEnvironment.getServiceInfos(serviceInfoClass);
public List<ServiceNameTuple<S>> createServices(List<SI> serviceInfos) {
List<ServiceNameTuple<S>> services = new ArrayList<ServiceNameTuple<S>>();
for (SI serviceInfo : serviceInfos) {
services.add(new ServiceNameTuple<S>(createService(serviceInfo), serviceInfo.getServiceName()));
Expand Down
@@ -1,17 +1,20 @@
package org.cloudfoundry.runtime.service;

import java.util.ArrayList;
import java.util.List;
import java.util.logging.Logger;

import org.cloudfoundry.runtime.env.AbstractServiceInfo;
import org.cloudfoundry.runtime.env.CloudEnvironment;
import org.cloudfoundry.runtime.env.CloudServiceException;
import org.cloudfoundry.runtime.env.MongoServiceInfo;
import org.cloudfoundry.runtime.env.RabbitServiceInfo;
import org.cloudfoundry.runtime.env.RdbmsServiceInfo;
import org.cloudfoundry.runtime.env.RedisServiceInfo;
import org.cloudfoundry.runtime.service.AbstractServiceCreator.ServiceNameTuple;
import org.cloudfoundry.runtime.service.document.MongoServiceCreator;
import org.cloudfoundry.runtime.service.keyvalue.RedisServiceCreator;
import org.cloudfoundry.runtime.service.messaging.RabbitServiceCreator;
import org.cloudfoundry.runtime.service.relational.MysqlServiceCreator;
import org.cloudfoundry.runtime.service.relational.PostgresqlServiceCreator;
import org.cloudfoundry.runtime.service.relational.RdbmsServiceCreator;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
Expand Down Expand Up @@ -47,14 +50,14 @@
* You may, of course, use XML-based configuration.
*
* @author Ramnivas Laddad
* @author Jennifer Hickey
*
*/
public class CloudServicesScanner implements BeanFactoryPostProcessor {

Logger logger = Logger.getLogger(CloudServicesScanner.class.getName());

@Override
@SuppressWarnings({ "rawtypes", "unchecked" })
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
CloudEnvironment cloudEnvironment;

Expand All @@ -68,24 +71,58 @@ public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory)
throw new CloudServiceException("CloudServicesScanner expects 0 or 1 bean of CloudEnvironment, found "
+ envBeans.length);
}
createCloudServiceBeans(beanFactory, cloudEnvironment);
}

List<AbstractServiceCreator> serviceCreators = new ArrayList<AbstractServiceCreator>();
serviceCreators.add(new MysqlServiceCreator(cloudEnvironment));
serviceCreators.add(new PostgresqlServiceCreator(cloudEnvironment));
serviceCreators.add(new RedisServiceCreator(cloudEnvironment));
serviceCreators.add(new RabbitServiceCreator(cloudEnvironment));
serviceCreators.add(new MongoServiceCreator(cloudEnvironment));

/**
* Create and register beans for each cloud service bound to the application
* @param beanFactory
* @param cloudEnvironment
*/
protected void createCloudServiceBeans(ConfigurableListableBeanFactory beanFactory, CloudEnvironment cloudEnvironment) {
logger.info("Auto-creating service beans");

for (AbstractServiceCreator serviceCreator : serviceCreators) {
List<ServiceNameTuple<Object>> serviceNamePairs = serviceCreator.createServices();
for (ServiceNameTuple<Object> serviceNamePair : serviceNamePairs) {
logger.info("Auto-creating service bean for " + serviceNamePair.name);
beanFactory.registerSingleton(serviceNamePair.name, serviceNamePair.service);
}
if(hasServicesOfType(cloudEnvironment, MongoServiceInfo.class)) {
registerServiceBeans(beanFactory, cloudEnvironment, new MongoServiceCreator(), MongoServiceInfo.class);
}
if(hasServicesOfType(cloudEnvironment, RedisServiceInfo.class)) {
registerServiceBeans(beanFactory, cloudEnvironment, new RedisServiceCreator(), RedisServiceInfo.class);
}
if(hasServicesOfType(cloudEnvironment, RabbitServiceInfo.class)) {
registerServiceBeans(beanFactory, cloudEnvironment, new RabbitServiceCreator(), RabbitServiceInfo.class);
}
if(hasServicesOfType(cloudEnvironment, RdbmsServiceInfo.class)) {
registerServiceBeans(beanFactory, cloudEnvironment, new RdbmsServiceCreator(), RdbmsServiceInfo.class);
}
}

/**
* Verifies that there are services of a specified type bound to the application. It is very important to
* check for bound services before instantiating the {@link AbstractServiceCreator}, as the service creators may have
* third party dependencies that are not present on the classpath
* @param cloudEnvironment
* @param serviceInfoClass
* @return If there are services of the specified type bound to the application
*/
protected boolean hasServicesOfType(CloudEnvironment cloudEnvironment, Class<? extends AbstractServiceInfo> serviceInfoClass) {
if(cloudEnvironment.getServiceInfos(serviceInfoClass).isEmpty()) {
return false;
}
return true;
}

/**
* Registers specified service beans
*
* @param beanFactory
* @param serviceNamePairs
*/
protected <S, T extends AbstractServiceInfo> void registerServiceBeans(ConfigurableListableBeanFactory beanFactory,
CloudEnvironment cloudEnvironment, AbstractServiceCreator<S, T> serviceCreator, Class<T> serviceInfoType) {
List<ServiceNameTuple<S>> serviceNamePairs = serviceCreator.createServices(cloudEnvironment
.getServiceInfos(serviceInfoType));
for (ServiceNameTuple<S> serviceNamePair : serviceNamePairs) {
logger.info("Auto-creating service bean for " + serviceNamePair.name);
beanFactory.registerSingleton(serviceNamePair.name, serviceNamePair.service);
}
}
}
Expand Up @@ -13,11 +13,11 @@
*/
public class CloudMongoDbFactoryBean extends AbstractCloudServiceFactory<MongoDbFactory, MongoServiceInfo> {
public CloudMongoDbFactoryBean(CloudEnvironment cloudEnvironment) {
super(new MongoServiceCreator(cloudEnvironment));
super(new MongoServiceCreator(),MongoServiceInfo.class,cloudEnvironment);
}

public CloudMongoDbFactoryBean() {
super(new MongoServiceCreator(new CloudEnvironment()));
this(new CloudEnvironment());
}

@Override
Expand Down
Expand Up @@ -2,7 +2,6 @@

import java.net.UnknownHostException;

import org.cloudfoundry.runtime.env.CloudEnvironment;
import org.cloudfoundry.runtime.env.CloudServiceException;
import org.cloudfoundry.runtime.env.MongoServiceInfo;
import org.cloudfoundry.runtime.service.AbstractServiceCreator;
Expand All @@ -20,9 +19,6 @@
*
*/
public class MongoServiceCreator extends AbstractServiceCreator<MongoDbFactory, MongoServiceInfo> {
public MongoServiceCreator(CloudEnvironment cloudEnvironment) {
super(cloudEnvironment, MongoServiceInfo.class);
}

public MongoDbFactory createService(MongoServiceInfo serviceInfo) {
try {
Expand All @@ -35,4 +31,5 @@ public MongoDbFactory createService(MongoServiceInfo serviceInfo) {
throw new CloudServiceException(e);
}
}

}
Expand Up @@ -13,11 +13,11 @@
*/
public class CloudRedisConnectionFactoryBean extends AbstractCloudServiceFactory<RedisConnectionFactory, RedisServiceInfo> {
public CloudRedisConnectionFactoryBean(CloudEnvironment cloudEnvironment) {
super(new RedisServiceCreator(cloudEnvironment));
super(new RedisServiceCreator(),RedisServiceInfo.class,cloudEnvironment);
}

public CloudRedisConnectionFactoryBean() {
super(new RedisServiceCreator(new CloudEnvironment()));
this(new CloudEnvironment());
}

@Override
Expand Down
@@ -1,6 +1,5 @@
package org.cloudfoundry.runtime.service.keyvalue;

import org.cloudfoundry.runtime.env.CloudEnvironment;
import org.cloudfoundry.runtime.env.CloudServiceException;
import org.cloudfoundry.runtime.env.RedisServiceInfo;
import org.cloudfoundry.runtime.service.AbstractServiceCreator;
Expand All @@ -18,10 +17,6 @@ public class RedisServiceCreator extends AbstractServiceCreator<RedisConnectionF

private static final String REDIS_CLIENT_CLASS_NAME = "redis.clients.jedis.Jedis";

public RedisServiceCreator(CloudEnvironment cloudEnvironment) {
super(cloudEnvironment, RedisServiceInfo.class);
}

public RedisConnectionFactory createService(RedisServiceInfo serviceInfo) {
if (hasClass(REDIS_CLIENT_CLASS_NAME)) {
JedisConnectionFactory connectionFactory = new JedisConnectionFactory();
Expand Down
Expand Up @@ -14,11 +14,11 @@
public class CloudRabbitConnectionFactoryBean extends AbstractCloudServiceFactory<ConnectionFactory, RabbitServiceInfo> {

public CloudRabbitConnectionFactoryBean(CloudEnvironment cloudEnvironment) {
super(new RabbitServiceCreator(cloudEnvironment));
super(new RabbitServiceCreator(), RabbitServiceInfo.class,cloudEnvironment);
}

public CloudRabbitConnectionFactoryBean() {
super(new RabbitServiceCreator(new CloudEnvironment()));
this(new CloudEnvironment());
}

@Override
Expand Down

0 comments on commit a8c9441

Please sign in to comment.