Search code examples
spring-dataspring-java-configgemfirespring-data-gemfire

Java Config for Spring Gemfire xml


I've defined my Gemfire's "client-cache.xml" as below:

<beans xmlns="http://www.springframework.org/schema/beans"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xmlns:gfe="http://www.springframework.org/schema/gemfire"
   xsi:schemaLocation="http://www.springframework.org/schema/gemfire http://www.springframework.org/schema/gemfire/spring-gemfire.xsd
                       http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <gfe:pool id="client" subscription-enabled="true">
    <gfe:locator host="localhost" port="41114"/>
    </gfe:pool>

    <gfe:client-cache pool-name="client"/>

    <gfe:client-region id="dataRegion" name="dataRegion" pool-name="client" shortcut="PROXY"/>

</beans>

I want to create a corresponding Java Configuration for the same.

I've tried below configuration:

@Resource
private Cache gemfireCache;

@Resource
private Pool client;

@Bean
public PoolFactoryBean client() {
    PoolFactoryBean client = new PoolFactoryBean();
    client.setSubscriptionEnabled(true);
    client.addLocators(new ConnectionEndpoint("localhost", 41114));
    return client;
}

@Bean
public ClientRegionFactoryBean<String, AbstractContent> dataRegion() {
    ClientRegionFactoryBean<String, AbstractContent> dataRegionFactory = new ClientRegionFactoryBean<>();
    dataRegionFactory.setPoolName("client");
    dataRegionFactory.setName("dataRegion");
    dataRegionFactory.setShortcut(ClientRegionShortcut.PROXY);
    return dataRegionFactory;
}

@Bean
public ClientCacheFactoryBean gemfireCache() {
    ClientCacheFactoryBean clientCacheFactory = new ClientCacheFactoryBean();
    clientCacheFactory.setPoolName("client");
    clientCacheFactory.setPdxSerializer(mappingPdxSerializer());
    return clientCacheFactory;
}

@Bean
public PlatformTransactionManager gemfireTransactionManager() throws Exception {
    return new GemfireTransactionManager(gemfireCache);
}

However, I keep running into exception saying :

Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [com.gemstone.gemfire.cache.client.ClientCache] is defined
at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:372)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:369)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:332)
at org.springframework.data.gemfire.client.PoolFactoryBean.eagerlyInitializeClientCacheIfNotPresent(PoolFactoryBean.java:218)
at org.springframework.data.gemfire.client.PoolFactoryBean.getObject(PoolFactoryBean.java:171)
at org.springframework.data.gemfire.client.PoolFactoryBean.getObject(PoolFactoryBean.java:65)
at org.springframework.beans.factory.support.FactoryBeanRegistrySupport.doGetObjectFromFactoryBean(FactoryBeanRegistrySupport.java:168)
at org.springframework.beans.factory.support.FactoryBeanRegistrySupport.getObjectFromFactoryBean(FactoryBeanRegistrySupport.java:103)
at org.springframework.beans.factory.support.AbstractBeanFactory.getObjectForBeanInstance(AbstractBeanFactory.java:1590)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:317)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:197)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:296)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202)

Is it not possible to transform the above XML into Java based configuration?

Any help would be appreciated. Thanks.

===============Edited below per John's comments==============

@John,

I tried the example mentioned @ sample test case for gemfire client and the sample as it is, worked fine. I'm using locator and the example worked fine with the locators configuration as gemfirePool.setLocators(Collections.singletonList(new InetSocketAddress(host, port))). However, I'm using spring-data-gemfire version 1.8.2.RELEASE as per recommendations of Pivotal support. So, I

  • upgraded the spring-data-gemfire version to 1.8.2.RELEASE @ pom.xml,
  • fixed the compilation errors
    (commented gemfireCache.setLazyInitialize(true)),
  • and changed gemfirePool.setLocators(Collections.singletonList(new InetSocketAddress(locatorHost, locatorPort))) to gemfirePool.addLocators(Collections.singletonList(new ConnectionEndpoint(locatorHost, locatorPort))),

the test case went for a toss.

The test case started giving me below exception:

java.lang.IllegalStateException: Failed to load ApplicationContext
    at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:124)
    at org.springframework.test.context.support.DefaultTestContext.getApplicationContext(DefaultTestContext.java:83)
    at org.springframework.test.context.support.DependencyInjectionTestExecutionListener.injectDependencies(DependencyInjectionTestExecutionListener.java:117)
    at org.springframework.test.context.support.DependencyInjectionTestExecutionListener.prepareTestInstance(DependencyInjectionTestExecutionListener.java:83)
    at org.springframework.test.context.TestContextManager.prepareTestInstance(TestContextManager.java:228)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.createTest(SpringJUnit4ClassRunner.java:230)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner$1.runReflectiveCall(SpringJUnit4ClassRunner.java:289)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.methodBlock(SpringJUnit4ClassRunner.java:291)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:249)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:89)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
    at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
    at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:193)
    at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:86)
    at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:459)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:675)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:382)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:192)
Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'gemfireCache' defined in io.pivotal.gemfire.cache.client.SpringGemFireClientCacheTest$SpringGemFireClientConfiguration: Unsatisfied dependency expressed through constructor argument with index 1 of type [com.gemstone.gemfire.cache.client.Pool]: : Error creating bean with name 'gemfirePool': FactoryBean threw exception on object creation; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [com.gemstone.gemfire.cache.client.ClientCache] is defined; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'gemfirePool': FactoryBean threw exception on object creation; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [com.gemstone.gemfire.cache.client.ClientCache] is defined
    at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:749)
    at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:464)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1123)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1018)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:510)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:482)
    at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:306)
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230)
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:302)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:197)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:753)
    at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:839)
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:538)
    at org.springframework.test.context.support.AbstractGenericContextLoader.loadContext(AbstractGenericContextLoader.java:125)
    at org.springframework.test.context.support.AbstractGenericContextLoader.loadContext(AbstractGenericContextLoader.java:60)
    at org.springframework.test.context.support.AbstractDelegatingSmartContextLoader.delegateLoading(AbstractDelegatingSmartContextLoader.java:109)
    at org.springframework.test.context.support.AbstractDelegatingSmartContextLoader.loadContext(AbstractDelegatingSmartContextLoader.java:261)
    at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContextInternal(DefaultCacheAwareContextLoaderDelegate.java:98)
    at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:116)
    ... 25 more
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'gemfirePool': FactoryBean threw exception on object creation; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [com.gemstone.gemfire.cache.client.ClientCache] is defined
    at org.springframework.beans.factory.support.FactoryBeanRegistrySupport.doGetObjectFromFactoryBean(FactoryBeanRegistrySupport.java:175)
    at org.springframework.beans.factory.support.FactoryBeanRegistrySupport.getObjectFromFactoryBean(FactoryBeanRegistrySupport.java:103)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getObjectForBeanInstance(AbstractBeanFactory.java:1585)
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:254)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:197)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.findAutowireCandidates(DefaultListableBeanFactory.java:1192)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1116)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1014)
    at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:813)
    at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:741)
    ... 43 more
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [com.gemstone.gemfire.cache.client.ClientCache] is defined
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:372)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:332)
    at org.springframework.data.gemfire.client.PoolFactoryBean.eagerlyInitializeClientCacheIfNotPresent(PoolFactoryBean.java:218)
    at org.springframework.data.gemfire.client.PoolFactoryBean.getObject(PoolFactoryBean.java:171)
    at org.springframework.data.gemfire.client.PoolFactoryBean.getObject(PoolFactoryBean.java:65)
    at org.springframework.beans.factory.support.FactoryBeanRegistrySupport.doGetObjectFromFactoryBean(FactoryBeanRegistrySupport.java:168)
    ... 52 more

So, it looks like, it's an issue with the spring-data-gemfire 1.8.2.RELEASE. I'm not really sure if I should simply revert back to 1.6.2.RELEASE and, what would I lose on reverting back to a previous revision.

I created another sample spring boot application:

  • pom.xml

    http://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 com.cdk.test TestGemfireLocators 0.0.1-SNAPSHOT

    <parent>
        <groupId>io.spring.platform</groupId>
        <artifactId>platform-bom</artifactId>
        <version>2.0.6.RELEASE</version>
    </parent>
    
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
    
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-log4j</artifactId>
        </dependency>
    
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-api</artifactId>
        </dependency>
    
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-core</artifactId>
        </dependency>
    
        <dependency>
            <groupId>org.springframework.data</groupId>
            <artifactId>spring-data-gemfire</artifactId>
            <version>1.8.2.RELEASE</version>
        </dependency>
    
    </dependencies>
    

  • Application.java

    @SpringBootApplication public class Application {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
    
    @Bean(name = "region")
    public ClientRegionFactoryBean region(ClientCache gemfireCache, Pool gemfirePool) {
        ClientRegionFactoryBean versionedRegion = new ClientRegionFactoryBean();
        versionedRegion.setName("region");
        versionedRegion.setCache(gemfireCache);
        versionedRegion.setPool(gemfirePool);
        versionedRegion.setShortcut(ClientRegionShortcut.PROXY);
        return versionedRegion;
    }
    
    @Bean
    public ClientCacheFactoryBean gemfireCache(@Qualifier("gemfireProperties") Properties gemfireProperties,
            Pool gemfirePool) {
        ClientCacheFactoryBean gemfireCache = new ClientCacheFactoryBean();
        gemfireCache.setPool(gemfirePool);
        gemfireCache.setProperties(gemfireProperties);
        return gemfireCache;
    }
    
    @Bean(name = GemfireConstants.DEFAULT_GEMFIRE_POOL_NAME)
    public PoolFactoryBean gemfirePool(@Value("${locator.host}") String host, @Value("${locator.port}") int port) {
        PoolFactoryBean gemfirePool = new PoolFactoryBean();
        gemfirePool.setName(GemfireConstants.DEFAULT_GEMFIRE_POOL_NAME);
        gemfirePool.setSubscriptionEnabled(true);
        gemfirePool.addLocators(new ConnectionEndpoint(host, port));
        return gemfirePool;
    }
    
    @Bean
    public Properties gemfireProperties(@Value("${gemfire.log.level:config}") String logLevel) {
        Properties gemfireProperties = new Properties();
        gemfireProperties.setProperty("log-level", logLevel);
        return gemfireProperties;
    }
    

    }

The above code too, doesn't work and returns same exception. I tried above code with the default version of "spring-data-gemfire" contained with-in io.spring.platform (1.7.4.RELEASE) and still the same result.

-------------POST John's comments and reference program-------------------

Thanks a lot John. The sample provided by you helped me, however, I had to make a few changes to the code. Below is how my final project looks like (if you notice, I'm injecting GemfireCache instead of ClientCache):

  • pom.xml

    http://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 com.cdk.test TestGemfireLocators 0.0.1-SNAPSHOT

    <parent>
        <groupId>io.spring.platform</groupId>
        <artifactId>platform-bom</artifactId>
        <version>2.0.5.RELEASE</version>
    </parent>
    
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
    
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-log4j</artifactId>
        </dependency>
    
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-api</artifactId>
        </dependency>
    
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-core</artifactId>
        </dependency>
    
        <dependency>
            <groupId>org.springframework.data</groupId>
            <artifactId>spring-data-gemfire</artifactId>
            <version>1.8.2.RELEASE</version>
        </dependency>
    
    </dependencies>
    

  • Application.java

    @SpringBootApplication public class Application {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
    
    @Resource(name = "myRegion")
    private Region<String, Object> myRegion;
    
    int intValue(Long value) {
        return value.intValue();
    }
    
    String logLevel() {
        return System.getProperty("gemfire.log-level", "config");
    }
    
    Properties gemfireProperties() {
        Properties gemfireProperties = new Properties();
        gemfireProperties.setProperty("log-level", logLevel());
        return gemfireProperties;
    }
    
    @Bean
    ClientCacheFactoryBean gemfireCache() {
        ClientCacheFactoryBean gemfireCache = new ClientCacheFactoryBean();
    
        gemfireCache.setClose(true);
        gemfireCache.setProperties(gemfireProperties());
    
        return gemfireCache;
    }
    
    @Bean(name = GemfireConstants.DEFAULT_GEMFIRE_POOL_NAME)
    PoolFactoryBean gemfirePool(@Value("${locator.host}") String host, @Value("${locator.port}") int port) {
    
        PoolFactoryBean gemfirePool = new PoolFactoryBean();
    
        gemfirePool.setKeepAlive(false);
        gemfirePool.setSubscriptionEnabled(true);
        gemfirePool.setThreadLocalConnections(false);
    
        gemfirePool.addLocators(new ConnectionEndpoint(host, port));
    
        return gemfirePool;
    }
    
    @Bean(name = "myRegion")
    ClientRegionFactoryBean<String, Object> myRegion(@Value("${region.name}") String regionName, GemFireCache gemfireCache,
            Pool gemfirePool) {
        ClientRegionFactoryBean<String, Object> myRegion = new ClientRegionFactoryBean<>();
    
        myRegion.setCache(gemfireCache);
        myRegion.setName(regionName);
        myRegion.setPool(gemfirePool);
        myRegion.setShortcut(ClientRegionShortcut.PROXY);
    
        return myRegion;
    }
    
    @Bean
    PlatformTransactionManager gemfireTransactionManager(GemFireCache gemfireCache) {
        return new GemfireTransactionManager((Cache) gemfireCache);
    }
    

    }


Solution

  • The NoSuchBeanDefinitionException pertains to the injected Cache reference in your application Java configuration class...

    @Resource
    private Cache gemfireCache
    

    NOTE: you might prefer to use @Inject or Spring's @Autowired annotation instead, since this is technically more accurate.

    The problem here is there is no Spring bean defined of type "Cache" in your Spring Java configuration.

    If you look more closely at the ClientCacheFactoryBean, getObjectType() method, which Spring uses to inspect bean (definitions) during autowiring of application components "by type", you see that it returns ClientCache.class. Technically, ClientCacheFactoryBean.getObjectType() also returns the actual cache object's type, but I do not recall when Spring inspects the available bean definitions to resolve type dependencies. Plus, I think the Spring container also maintains a type-to-bean mapping (a.k.a. cache) during parsing. The only reason the later is relevant is because GemFire only has 1 implementation of Cache and ClientCache, and that implementation, namely GemFireCacheImpl implements both interfaces. So, it seemingly should be resolvable, but...

    Anyway, all of this is to explain the exception with a bit more clarity.

    Unfortunately, you cannot use...

    return new GemfireTransactionManager(gemfireCache());
    

    Since the gemfireCache() bean definition "method" returns an instance of the ClientCacheFactoryBean and the GemfireTransactionManager constructor expects an instance of Cache.

    However, you can define arguments of type Cache, ClientCache and GemFireCache as needed by the SDG FactoryBeans. For instance, your gemfireTransactionManager bean can be defined as...

    @Bean
    public PlatformTransactionManager gemfireTransactionManager(GemFireCache gemfireCache) {
      return new GemfireTransactionManager((Cache) gemfireCache));
    }
    

    NOTE: ClientCache extends GemFireCache.

    Likewise, your Region bean definition, "dataRegion" also expects and can take an instance of the cache like so...

    @Bean
    public ClientRegionFactoryBean<String, AbstractContent> dataRegion(ClientCache gemfireCache, Pool gemfirePool) {
      ...
      dataRegion.setCache(gemfireCache);
      dataRegion.setPool(gemfirePool);
      ...
    }
    

    Also notice, I can pass a reference to the GemFire Pool that the Region should use for data access operations to the cluster servers.

    Spring treats Java configuration @Bean definition method parameters as "type dependencies", so you can just defined the expected, resulting bean type required by the bean component in question (e.g. "gemfireTransactionManager" which expects a "Cache" bean dependency).

    In general, there is no problem switching from XML to Java configuration. In fact, I encourage it and have even been using Java config more and more in my examples (for instance). In time I plan to convert most of the spring-gemfire-examples over to using Spring Boot and Java configuration.

    Speaking of the spring-gemfire-examples, there is also an example with Java config. But, I think this example demonstrates a peer cache. So, you may find the examples in 3 more helpful since I compare and contrast both GemFire native configuration and Spring configuration using both XML and Java.

    For instance, my ClientCache Java configuration example has both a GemFire Cache server Java config and client Java config.

    Anyway, hope this helps and if you more questions, feel free to reach out.

    Cheers, John

    Update - 2016-07-19

    Example (& test) for this problem using your GemFire cache client, Spring Java configuration provided here.