package org.springframework.cloud.config.xml;
import java.lang.reflect.Constructor;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanCreationException;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.cloud.service.AbstractCloudServiceConnectorFactory;
import org.springframework.cloud.service.CloudServiceConnectorFactory;
import org.springframework.cloud.service.ServiceConnectorConfig;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import org.w3c.dom.Element;
/**
* Common class to support <cloud> namespace.
*
* @author Ramnivas Laddad
*
* Implementation notes:
* This parser adds a {@link CloudServiceIntroducer} bean supplying it the {@link CloudServiceConnectorFactory}
* class that was passed as the constructor parameter. The added {@link CloudServiceIntroducer} bean introduces
* the service connector factory, which in turn creates the required service connector.
* This level of indirection allows attaching a proper 'id' to the service connector object.
*/
class AbstractCloudServiceFactoryParser extends AbstractSingleBeanDefinitionParser {
private final Class<? extends CloudServiceConnectorFactory<?>> serviceConnectorFactoryType;
public AbstractCloudServiceFactoryParser(Class<? extends CloudServiceConnectorFactory<?>> serviceConnectorFactoryType) {
Assert.notNull(serviceConnectorFactoryType, "serviceConnectorFactoryType must not be null");
this.serviceConnectorFactoryType = serviceConnectorFactoryType;
}
@Override
protected boolean shouldGenerateId() {
return true;
}
@Override
protected final Class<?> getBeanClass(Element element) {
return CloudServiceIntroducer.class;
}
@Override
protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {
String serviceId = element.getAttribute("service-name");
builder.addConstructorArgValue(serviceConnectorFactoryType);
builder.addConstructorArgValue(element.getAttribute(ID_ATTRIBUTE));
builder.addConstructorArgValue(serviceId);
// subclasses should add one more constructor parameter for ServiceConnectorConfig
}
}
class CloudServiceIntroducer implements BeanFactoryPostProcessor {
private Class<? extends CloudServiceConnectorFactory<?>> serviceConnectorFactoryType;
private String beanId;
private String serviceId;
private ServiceConnectorConfig serviceConnectorConfig;
private Class<?> serviceConnectorType;
public CloudServiceIntroducer(Class<? extends CloudServiceConnectorFactory<?>> serviceConnectorFactoryType,
String beanId, String serviceId, ServiceConnectorConfig serviceConnectorConfig) {
this.serviceConnectorFactoryType = serviceConnectorFactoryType;
this.beanId = beanId;
this.serviceId = serviceId;
this.serviceConnectorConfig = serviceConnectorConfig;
}
@SuppressWarnings({ "unchecked", "rawtypes" })
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
try {
Constructor<?> ctor = serviceConnectorFactoryType.getConstructor(String.class, ServiceConnectorConfig.class);
AbstractCloudServiceConnectorFactory<?> serviceFactory = (AbstractCloudServiceConnectorFactory<?>)ctor.newInstance(serviceId, serviceConnectorConfig);
serviceFactory.setServiceConnectorType((Class)serviceConnectorType);
serviceFactory.setBeanFactory(beanFactory);
serviceFactory.afterPropertiesSet();
// id is the beanId if specified, otherwise the serviceId
if (StringUtils.hasText(beanId)) {
beanFactory.registerSingleton(beanId, serviceFactory);
} else {
beanFactory.registerSingleton(serviceFactory.getServiceId(), serviceFactory);
}
} catch (Exception ex) {
throw new BeanCreationException("Error registering service factory", ex);
}
}
/**
* To set connector type that is more specific that the factory is meant to produce.
*
* @param serviceConnectorType
*/
public void setServiceConnectorType(Class<?> serviceConnectorType) {
this.serviceConnectorType = serviceConnectorType;
}
}