package org.springframework.cloud.service; import java.util.List; import org.springframework.beans.factory.FactoryBean; import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.config.AbstractFactoryBean; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.cloud.Cloud; import org.springframework.cloud.CloudException; import org.springframework.cloud.CloudFactory; 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. * * @author Ramnivas Laddad * * @param <S> * The service type */ public abstract class AbstractCloudServiceConnectorFactory<S> extends AbstractFactoryBean<S> implements CloudServiceConnectorFactory<S> { private static final String CLOUD_FACTORY_BEAN_NAME = "__cloud_factory__"; private Cloud cloud; protected String serviceId; private Class<? extends S> serviceConnectorType; private ServiceConnectorConfig serviceConnectorConfiguration; private S serviceInstance; /** * * @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; } public AbstractCloudServiceConnectorFactory(Class<S> serviceConnectorType, ServiceConnectorConfig serviceConnectorConfiguration) { this(null, serviceConnectorType, 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 * the {@link Cloud} instance describing the discovered runtime environment */ 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(); } @SuppressWarnings("unchecked") public S createService() { if (serviceInstance == null && cloud != null) { serviceInstance = cloud.getServiceConnector(serviceId, serviceConnectorType, serviceConnectorConfiguration); if (serviceInstance != null && FactoryBean.class.isAssignableFrom(serviceInstance.getClass())) { try { serviceInstance = (S) ((FactoryBean) serviceInstance).getObject(); } catch (Exception e) { return null; } } } return serviceInstance; } @Override @SuppressWarnings("unchecked") 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; } } }