Add @CloudScan annotation that registers services and application instance info beans

This commit is contained in:
Ramnivas Laddad
2014-07-31 14:04:24 -07:00
parent 3545ad0549
commit d005a06d24
6 changed files with 164 additions and 7 deletions

View File

@@ -10,6 +10,7 @@ import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.cloud.Cloud;
import org.springframework.cloud.CloudFactory;
import org.springframework.cloud.app.ApplicationInstanceInfo;
import org.springframework.cloud.config.java.ServiceScan;
import org.springframework.cloud.service.GenericCloudServiceConnectorFactory;
import org.springframework.cloud.service.ServiceInfo;
@@ -21,7 +22,7 @@ import org.springframework.cloud.service.ServiceInfo;
*
* Usage:
* Most applications should use either the Java config using {@link ServiceScan} annotation
* or XML config using <cloud:service-scan/> that introduce a bean of this type lgically
* or XML config using <cloud:service-scan/> that introduce a bean of this type logically
* equivalent to:
* <pre>
* &lt;bean class="org.cloudfoundry.runtime.service.CloudServicesScanner"/&gt;
@@ -44,9 +45,9 @@ import org.springframework.cloud.service.ServiceInfo;
* @author Ramnivas Laddad
*
*/
public class ServiceScanHelper {
public class CloudScanHelper {
private static final String CLOUD_FACTORY_BEAN_NAME = "__cloud_factory__";
private static Logger logger = Logger.getLogger(ServiceScanHelper.class.getName());
private static Logger logger = Logger.getLogger(CloudScanHelper.class.getName());
private Cloud cloud;
@@ -58,6 +59,17 @@ public class ServiceScanHelper {
registerServiceBean(registry, serviceInfo);
}
}
public void registerApplicationInstanceBean(BeanDefinitionRegistry registry) {
initializeCloud(registry);
BeanDefinitionBuilder definitionBuilder =
BeanDefinitionBuilder.genericBeanDefinition(ApplicationInstanceInfoWrapper.class);
definitionBuilder.addConstructorArgValue(cloud);
definitionBuilder.getRawBeanDefinition().setAttribute(
"factoryBeanObjectType", ApplicationInstanceInfo.class);
registry.registerBeanDefinition("spring.cloud.appplicationInstanceInfo", definitionBuilder.getBeanDefinition());
}
private void initializeCloud(BeanDefinitionRegistry registry) {
if (cloud != null) {
@@ -113,5 +125,27 @@ public class ServiceScanHelper {
}
}
public static class ApplicationInstanceInfoWrapper implements FactoryBean<ApplicationInstanceInfo> {
private Cloud cloud;
public ApplicationInstanceInfoWrapper(Cloud cloud) {
this.cloud = cloud;
}
@Override
public ApplicationInstanceInfo getObject() throws Exception {
return cloud.getApplicationInstanceInfo();
}
@Override
public Class<?> getObjectType() {
return ApplicationInstanceInfo.class;
}
@Override
public boolean isSingleton() {
return true;
}
}
}

View File

@@ -0,0 +1,63 @@
package org.springframework.cloud.config.java;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.cloud.app.ApplicationInstanceInfo;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
/**
* Add this annotation to an &#64;{@link Configuration} class to have a bean for each
* service bound to the app as well as one for {@link ApplicationInstanceInfo} added to
* the application context.
*
* <pre class="code">
* &#064;Configuration
* &#064;CloudScan
* public class CloudConfiguration {
* // may (optionally) extend AbstractCloudConfiguration
* }
* </pre>
*
* This annotation is similar to &#064;ComponentScan in Spring, which scans for classes
* with the &#064;Component classes and creates a bean for each. &#064;CloudScan, in the same
* spirit, scans services bound to the app and creates a bean for each. It also creates a bean
* of {@link ApplicationInstanceInfo} class to expose information about the application instance.
*
* Upon service scanning, if there is a unique bean of for service type, you can inject it
* using the following code (shows Redis, but the same scheme works for all services):
* <pre>
* &#64;Autowired RedisConnectionFactory redisConnectionFactory;
* </pre>
*
* If there are more than one services of a type, you can use the &#064;Qualifier annotation
* as in the following code:
* <pre>
* &#64;Autowired &#64;Qualifier("service-name1") RedisConnectionFactory redisConnectionFactory;
* &#64;Autowired &#64;Qualifier("service-name2") RedisConnectionFactory redisConnectionFactory;
* </pre>
*
* Similarly, application can have {@link ApplicationInstanceInfo} injected as follows:
* <pre>
* &#64;Autowired ApplicationInstanceInfo applicationInstanceInfo;
* </pre>
*
* Note the difference between &#64;{@link ServiceScan} and this annotation. While the former only
* adds beans for each bound service, the &#64;{@link CloudScan} annotation also adds a
* bean for {@link ApplicationInstanceInfo}.
*
* @see ServiceScan
* @author Ramnivas Laddad
*
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(CloudScanConfiguration.class)
public @interface CloudScan {
}

View File

@@ -0,0 +1,25 @@
package org.springframework.cloud.config.java;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.cloud.app.ApplicationInstanceInfo;
import org.springframework.cloud.config.CloudScanHelper;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.type.AnnotationMetadata;
/**
* Introduces beans for each bound service and one for {@link ApplicationInstanceInfo}
*
* @author Ramnivas Laddad
* @see CloudScan
*/
@Configuration
public class CloudScanConfiguration extends ServiceScanConfiguration {
private CloudScanHelper helper = new CloudScanHelper();
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) throws BeansException {
super.registerBeanDefinitions(importingClassMetadata, registry);
helper.registerApplicationInstanceBean(registry);
}
}

View File

@@ -2,7 +2,7 @@ package org.springframework.cloud.config.java;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.cloud.config.ServiceScanHelper;
import org.springframework.cloud.config.CloudScanHelper;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.type.AnnotationMetadata;
@@ -15,7 +15,7 @@ import org.springframework.core.type.AnnotationMetadata;
*/
@Configuration
public class ServiceScanConfiguration implements ImportBeanDefinitionRegistrar {
private ServiceScanHelper helper = new ServiceScanHelper();
private CloudScanHelper helper = new CloudScanHelper();
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) throws BeansException {

View File

@@ -6,7 +6,7 @@ import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.xml.AbstractSimpleBeanDefinitionParser;
import org.springframework.beans.factory.xml.NamespaceHandlerSupport;
import org.springframework.cloud.config.ServiceScanHelper;
import org.springframework.cloud.config.CloudScanHelper;
import org.w3c.dom.Element;
/**
@@ -47,7 +47,7 @@ public class CloudNamespaceHandler extends NamespaceHandlerSupport {
}
public static class ServiceScanBeanFactoryProcessor implements BeanFactoryPostProcessor {
private ServiceScanHelper helper = new ServiceScanHelper();
private CloudScanHelper helper = new CloudScanHelper();
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {

View File

@@ -0,0 +1,35 @@
package org.springframework.cloud.config.java;
import static org.junit.Assert.assertNotNull;
import org.junit.Test;
import org.springframework.cloud.app.ApplicationInstanceInfo;
import org.springframework.cloud.config.AbstractCloudConfigServiceScanTest;
import org.springframework.cloud.service.ServiceInfo;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Configuration;
/**
* Test for the {@link CloudScan} annotation
*
* @author Ramnivas Laddad
*
*/
public class CloudScanJavaConfigTest extends AbstractCloudConfigServiceScanTest {
protected ApplicationContext getTestApplicationContext(ServiceInfo... serviceInfos) {
return getTestApplicationContext(CloudScanJavaConfigTestConfig.class, serviceInfos);
}
@Test
public void cloudScanIntroducesApplicationInstanceInfo() {
ApplicationContext testContext = getTestApplicationContext();
assertNotNull(testContext.getBean(ApplicationInstanceInfo.class));
}
}
@Configuration
@CloudScan
class CloudScanJavaConfigTestConfig {
}