Search code examples
spring-datagemfirespring-data-gemfire

Spring data gemfire support for subregion cache listener


I am trying to port my existing application code to startup Gemfire using the Spring-data-gemfire. So I am basically moving my region configurations from the cache.xml to spring context.

Versions used: Gemfire 6.6.3.2 Spring-data-gemfire-1.3.4 Jdk 7

It all works fine upto the point where I need to configure Cache Listeners. A simple Cache listener on a region works but I cant get a cache listener to work on subregions.

As an example I have the below regions. I want the CacheUpdateListener (implements the CacheListener interface) notified when the /User/Details/Address region is updated. I know Gemfire supports it because I already have it working with cache.xml way. But does anyone know if I can get this to work with Spring-data-gemfire. This is what I tried and didnt work.

<gfe:replicated-region id="VCCache" name="User" scope="distributed-no-ack">
    <gfe:replicated-region name="Details" scope="distributed-no-ack">
        <gfe:replicated-region name="Address" scope="distributed-ack">
            <gfe:cache-listener>
                <bean class="com.vc.cache.CacheUpdateListener" />
            </gfe:cache-listener>
        </gfe:replicated-region>
    </gfe:replicated-region>
</gfe:replicated-region>    

** EDIT: Added the listener code Here is the listener. I haven't put all the over ridden functions here just to be concise.

public class CacheUpdateListener implements CacheListener<Object, Object>
{
private static final Logger LOGGER = LoggerFactory.getLogger(CacheUpdateListener.class);

private String name = "defaultName";

@Override
public void afterCreate(EntryEvent<Object, Object> event)
{
    LOGGER.info("[afterCreate] region [{}] key [{}] created remote [{}] with value [{}]",
            new Object[] { event.getRegion().getFullPath(), event.getKey(), event.isOriginRemote(), event.getNewValue() });
}

@Override
public void afterUpdate(EntryEvent<Object, Object> event)
{
    LOGGER.info("[afterUpdate] region [{}] key [{}] updated remote [{}] with value [{}] old value [{}]",
            new Object[] { event.getRegion().getFullPath(), event.getKey(), event.isOriginRemote(), event.getNewValue(), event.getOldValue() });
}
}

Solution

  • I created a simple test with a Peer Cache Subregion having a registered CacheListener configured with SDG and the listener was called back as expected. It is possible there are other factors or reasons why this does not work for you...

    1. Not knowing your entire SDG XML configuration, but perhaps if there were competing GemFire configuration in play, such as using native GemFire cache.xml with Spring config with conflicting Region definitions (but you do state afterRegionCreate is being called), e.g. ...

    2.I was using SDG 1.4.0.RELEASE and both GemFire 7.0.2.12 and 8 in my testing. However, you should be able to use both my test and example SDG XML config to test with your setup...

    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration("/subRegionCacheListener.xml")
    @SuppressWarnings("unused")
    public class SubRegionCacheListenerTest {
    
      private static final Stack<EntryEvent<Integer, String>> entryEvents = new Stack<EntryEvent<Integer, String>>();
    
      @Resource(name = "/Parent/Child")
      private Region<Integer, String> child;
    
      @Test
      public void testCacheListenerCallback() {
        assertNotNull("The '/Parent/Child' Cache Sub-Region was not properly configured and initialized!", child);
        assertEquals("Child", child.getName());
        assertEquals("/Parent/Child", child.getFullPath());
        assertTrue(child.isEmpty());
        assertTrue(entryEvents.isEmpty());
    
        child.put(1, "TEST");
    
        assertFalse(child.isEmpty());
        assertEquals(1, child.size());
        assertEquals("TEST", child.get(1));
        assertFalse(entryEvents.isEmpty());
    
        EntryEvent event = entryEvents.pop();
    
        assertNotNull(event);
        assertEquals(1, event.getKey());
        assertNull(event.getOldValue());
        assertEquals("TEST", event.getNewValue());
        assertTrue(entryEvents.isEmpty());
    
        child.put(1, "TESTING");
    
        assertFalse(child.isEmpty());
        assertEquals(1, child.size());
        assertEquals("TESTING", child.get(1));
        assertFalse(entryEvents.isEmpty());
    
        event = entryEvents.pop();
    
        assertNotNull(event);
        assertEquals(1, event.getKey());
        assertEquals("TEST", event.getOldValue());
        assertEquals("TESTING", event.getNewValue());
        assertTrue(entryEvents.isEmpty());
    
        child.remove(1);
    
        assertTrue(child.isEmpty());
    
        event = entryEvents.pop();
    
        assertNotNull(event);
        assertEquals(1, event.getKey());
        assertEquals("TESTING", event.getOldValue());
        assertNull(event.getNewValue());
      }
    
      public static final class SubRegionCacheListener extends CacheListenerAdapter<Integer, String> {
    
        @Override
        public void afterCreate(final EntryEvent<Integer, String> event) {
          entryEvents.push(event);
        }
    
        @Override
        public void afterDestroy(final EntryEvent<Integer, String> event) {
          entryEvents.push(event);
        }
    
        @Override
        public void afterUpdate(final EntryEvent<Integer, String> event) {
          entryEvents.push(event);
        }
      }
    }
    

    And the corresponding SDG XML config...

    <?xml version="1.0" encoding="utf-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:gfe="http://www.springframework.org/schema/gemfire"
       xmlns:util="http://www.springframework.org/schema/util"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/gemfire http://www.springframework.org/schema/gemfire/spring-gemfire.xsd
        http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd
    ">
    
      <util:properties id="gemfireProperties">
        <prop key="name">SpringGemFirePeerCacheSubRegionCacheListenerTest</prop>
        <prop key="mcast-port">0</prop>
        <prop key="log-level">config</prop>
      </util:properties>
    
      <gfe:cache properties-ref="gemfireProperties"/>
    
      <gfe:replicated-region id="Parent">
        <gfe:replicated-region name="Child">
          <gfe:cache-listener>
            <bean class="org.spring.data.gemfire.cache.SubRegionCacheListenerTest$SubRegionCacheListener"/>
          </gfe:cache-listener>
        </gfe:replicated-region>
      </gfe:replicated-region>
    
    </beans>
    

    Hope this helps.

    Most likely, there maybe a configuration issue here, or possibly a problem with GemFire 6.x (though you state it works with GemFire's cache.xml?).