CLOVERšŸ€

That was when it all began.

Apache Geodeć§ć‚¤ćƒ™ćƒ³ćƒˆå‡¦ē†

Apache Geode恫ćÆ态Regionćø恮ć‚Øćƒ³ćƒˆćƒŖ恮čæ½åŠ ć€å‰Šé™¤ćŖć©ć®ć‚¤ćƒ™ćƒ³ćƒˆć«åÆ¾ć—ć¦ē“ä»˜ć‘ć‚‰ć‚Œć‚‹ć€ć‚¤ćƒ™ćƒ³ćƒˆćƒćƒ³ćƒ‰ćƒŖćƒ³ć‚°ć®ä»•ēµ„ćæćŒć‚ć‚Šć¾ć™ć€‚

http://geode.docs.pivotal.io/docs/developing/events/chapter_overview.html

ꦂ要ćØ恗恦ćÆć€ć“ć”ć‚‰ć‚’č¦‹ć‚‹ć®ćŒć‚ˆć„ć§ć—ć‚‡ć†ć€‚

http://geode.docs.pivotal.io/docs/developing/events/how_events_work.html

Apache Geodeå†…ć®åˆ†ę•£ć‚·ć‚¹ćƒ†ćƒ å†…ć§ć€Cacheć«é–¢ć™ć‚‹ć‚¤ćƒ™ćƒ³ćƒˆć‚’å—ć‘å–ć‚‹ć“ćØćŒć§ćć¾ć™ć€‚Peer-to-Peerć§ć‚ć£ć¦ć‚‚ć€Clientļ¼Serverć§ć‚ć£ć¦ć‚‚åˆ©ē”Øć§ćć€ć¾ćŸć‚Æćƒ©ć‚¹ć‚æå†…ć®ä»–ć®ćƒ”ćƒ³ćƒćƒ¼ć§ē™ŗē”Ÿć—ćŸć‚¤ćƒ™ćƒ³ćƒˆć‚‚å—ć‘å–ć‚‹ć“ćØ恌恧恍悋悈恆恧恙怂

ē‰¹å¾“ćÆ态äøŠčØ˜ćƒšćƒ¼ć‚øć«ć‚ˆć‚‹ćØ恓悓ćŖꄟ恘怂

  • ć‚¤ćƒ™ćƒ³ćƒˆćƒ™ćƒ¼ć‚¹
  • åˆęˆåÆčƒ½ćŖéžåŒęœŸć‚¤ćƒ™ćƒ³ćƒˆé€šēŸ„
  • ä½Žćƒ¬ćƒ³ćƒ†ćƒ³ć‚·ćŖåŒęœŸć‚¤ćƒ™ćƒ³ćƒˆé€šēŸ„
  • å†—é•·åŒ–ć•ć‚ŒćŸåÆē”Øę€§ć®é«˜ć„ćƒ”ćƒƒć‚»ćƒ¼ć‚øćƒ³ć‚°ć‚­ćƒ„ćƒ¼
  • ć‚¤ćƒ™ćƒ³ćƒˆē™ŗē”Ÿé †ć‹ć¤ć€1åŗ¦ć ć‘ć®é…äæ”
  • åˆ†ę•£ć‚¤ćƒ™ćƒ³ćƒˆé€šēŸ„
  • ć‚µćƒ–ć‚¹ć‚ÆćƒŖćƒ—ć‚·ćƒ§ćƒ³ć®ę°øē¶šåŒ–

ć„ć‚ć„ć‚ć‚ć‚Šć¾ć™ć­ā€¦ć€‚

ć¾ćŸć€ć‚¤ćƒ™ćƒ³ćƒˆć«ćÆ仄äø‹ć®2ēØ®é”žćŒć‚ć‚‹ć‚ˆć†ć§ć™ć€‚

  • ć‚¢ćƒ—ćƒŖć‚±ćƒ¼ć‚·ćƒ§ćƒ³ćŒCacheć‚’ę“ä½œć™ć‚‹ć“ćØć«ć‚ˆć‚Šē™ŗē”Ÿć™ć‚‹ć‚¤ćƒ™ćƒ³ćƒˆć§ć€ćƒ‡ćƒ¼ć‚æć®å¤‰ę›“ć«ć¤ć„ć¦č©³ē“°ć«å—ć‘å–ć‚‹ć“ćØ恌åÆčƒ½
  • ē®”ē†ē³»ć®APIć®åˆ©ē”Øć«ć‚ˆć‚Šē™ŗē”Ÿć™ć‚‹ć‚¤ćƒ™ćƒ³ćƒˆ

ć„ćšć‚Œć®ć‚¤ćƒ™ćƒ³ćƒˆć‚‚ć€å˜äø€ć®ćƒ”ćƒ³ćƒćƒ¼ć®ę“ä½œć«ć‚ˆć‚Šē™ŗē”Ÿć—ć¾ć™ć€‚ć“ć‚Œć‚‰ć®ć‚¤ćƒ™ćƒ³ćƒˆćÆ恩恔悉恋ē‰‡ę–¹ć‚’ꉱ恆恓ćØ恌恧恍态ļ¼ˆć²ćØć¤ć®ćƒ”ćƒ³ćƒćƒ¼ć§ćÆļ¼‰äø”ę–¹ć‚’äø€ē·’恫ꉱ恆恓ćØćÆ恧恍ćŖ恄悈恆恧恙怂Cacheć«é–¢ć™ć‚‹ć‚¤ćƒ™ćƒ³ćƒˆćØē®”ē†ē³»ć®ć‚¤ćƒ™ćƒ³ćƒˆć‚’刄怅恫順åŗē®”ē†ć—ć¦ć„ć‚‹ćŸć‚ć€äŗˆęœŸć—ćŖ恄ēµęžœć‚’å¼•ćčµ·ć“ć™åÆčƒ½ę€§ćŒć‚ć‚‹ć‹ć‚‰ć€ć‚‰ć—ć„ć§ć™ć€‚

ć‚¤ćƒ™ćƒ³ćƒˆć®ćƒ©ć‚¤ćƒ•ć‚µć‚¤ć‚Æ惫ćØ恗恦ćÆ态ꬔ恮順ē•Ŗ恫ćŖć‚Šć¾ć™ć€‚

  1. ćƒ‡ćƒ¼ć‚æ恮put悄Cache恮closećØć„ć£ćŸć‚Ŗćƒšćƒ¬ćƒ¼ć‚·ćƒ§ćƒ³ć‚’é–‹å§‹ć—ć¾ć™
  2. ć‚Ŗćƒšćƒ¬ćƒ¼ć‚·ćƒ§ćƒ³ć‚’č”Œć†ć“ćØć§ć€ć‚¤ćƒ™ćƒ³ćƒˆć®ćƒˆćƒŖć‚¬ćƒ¼ćØćŖć£ćŸćƒ”ć‚½ćƒƒćƒ‰ć®č©³ē“°ć«ć¤ć„恦恮ć‚Ŗ惖ć‚ø悧ć‚Æ惈态ć‚Ŗćƒšćƒ¬ćƒ¼ć‚·ćƒ§ćƒ³ć‚’č”Œć£ćŸćƒ”ćƒ³ćƒćƒ¼ć‚„Regionć«é–¢ć™ć‚‹ęƒ…å ±ćØć„ć£ćŸć‚Ŗ惖ć‚ø悧ć‚Æ惈恌ē”Ÿęˆć•ć‚Œć¾ć™
  3. ć‚¤ćƒ™ćƒ³ćƒˆćƒćƒ³ćƒ‰ćƒ©ćŒå‘¼ć³å‡ŗć•ć‚Œć€ć‚¤ćƒ™ćƒ³ćƒˆć‚Ŗ惖ć‚ø悧ć‚Æ惈恌ęø”ć•ć‚Œć¾ć™ć€‚ć‚¤ćƒ™ćƒ³ćƒˆć”ćØ恫态ē•°ćŖć‚‹ć‚¤ćƒ™ćƒ³ćƒˆćƒćƒ³ćƒ‰ćƒ©ćŒåæ…要恧恂悊态åƾåæœć™ć‚‹ć‚¤ćƒ™ćƒ³ćƒˆćƒćƒ³ćƒ‰ćƒ©ćŒå­˜åœØ恗ćŖć„å “åˆćÆē‰¹ć«ä½•ć‚‚čµ·ć“ć‚Šć¾ć›ć‚“
  4. ć‚¤ćƒ™ćƒ³ćƒˆćƒćƒ³ćƒ‰ćƒ©ćŒć‚¤ćƒ™ćƒ³ćƒˆć‚’å—äæ”恙悋ćØ态åƾåæœć™ć‚‹ć‚¤ćƒ™ćƒ³ćƒˆć®ć‚³ćƒ¼ćƒ«ćƒćƒƒć‚Æćƒ”ć‚½ćƒƒćƒ‰ć‚’å‘¼ć³å‡ŗć—ć¾ć™ć€‚ć‚¤ćƒ™ćƒ³ćƒˆćƒćƒ³ćƒ‰ćƒ©ć®å‘¼ć³å‡ŗ恗ć‚æć‚¤ćƒŸćƒ³ć‚°ćŒć‚Ŗćƒšćƒ¬ćƒ¼ć‚·ćƒ§ćƒ³ć®å‰ćŖć®ć‹å¾ŒćŖ恮恋ćÆć€ć‚¤ćƒ™ćƒ³ćƒˆć®ēØ®é”žć«ä¾å­˜ć—ć€ć¾ćŸć‚æć‚¤ćƒŸćƒ³ć‚°ć‚‚ć‚¤ćƒ™ćƒ³ćƒˆćƒćƒ³ćƒ‰ćƒ©ć«ä¾å­˜ć—ć¾ć™
  5. ć‚Ŗćƒšćƒ¬ćƒ¼ć‚·ćƒ§ćƒ³ćŒåˆ†ę•£ē³»ć®ć‚‚ć®ć ć£ćŸå “åˆć€ä»–ć®ćƒ”ćƒ³ćƒćƒ¼ć§ć‚‚åŒć˜ć‚ˆć†ć«å‡¦ē†ć™ć‚‹ć“ćØćŒć§ćć‚‹ć‚¤ćƒ™ćƒ³ćƒˆć‚’ē”Ÿęˆć—ć¾ć™

č¦ć™ć‚‹ć«ć€é–¢åæƒć®ć‚ć‚‹ę“ä½œć«åÆ¾ć—ć¦ć‚¤ćƒ™ćƒ³ćƒˆćƒćƒ³ćƒ‰ćƒ©ć‚’ē™»éŒ²ć™ć‚‹ćØ态Apache GeodećŒé©åˆ‡ć«ć‚¤ćƒ™ćƒ³ćƒˆć‚’åˆ¤å®šć—ć¦ć‚¤ćƒ™ćƒ³ćƒˆćƒćƒ³ćƒ‰ćƒ©ć‚’å‘¼ć³å‡ŗć—ć¦ćć‚Œć¾ć™ć£ć¦ć“ćØ恧恙恭怂

ćƒ‰ć‚­ćƒ„ćƒ”ćƒ³ćƒˆäøŠć®čŖ¬ę˜ŽćÆć¾ć ć¾ć ē¶šćć¾ć™ćŒć€ćØć‚Šć‚ćˆćšć“ć‚Œćć‚‰ć„ć«ć—ć¦å®Ÿéš›ć«ä½æć£ć¦ćæć¾ć—ć‚‡ć†ć€‚

今回ćÆ态Peer-to-Peerć§ć®åˆ©ē”ØćØć—ć¾ć™ć€‚ć¾ćŸć€åˆ†ę•£ē’°å¢ƒć§ćÆä½æē”Øć›ćšć€å˜äø€ć®ćƒ”ćƒ³ćƒćƒ¼ć§å®Ÿč”Œć—ć¦ćæć¾ć™ć€‚

ęŗ–å‚™

ć¾ćšćÆ态Maven依存関äæ‚恋悉怂

        <dependency>
            <groupId>org.apache.geode</groupId>
            <artifactId>gemfire-core</artifactId>
            <version>1.0.0-incubating.M1</version>
        </dependency>

        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.assertj</groupId>
            <artifactId>assertj-core</artifactId>
            <version>3.3.0</version>
            <scope>test</scope>
        </dependency>

利ē”Ø恙悋Apache Geodeć®ćƒćƒ¼ć‚øćƒ§ćƒ³ćÆ态1.0.0-incubating.M1ćØć—ć¾ć™ć€‚JUnitćØAssertJćÆć€ćƒ†ć‚¹ćƒˆć‚³ćƒ¼ćƒ‰ē”Ø恧恙怂

ć‚¤ćƒ™ćƒ³ćƒˆćƒćƒ³ćƒ‰ćƒ©ć®å®Ÿč£…

恝悌恧ćÆć€ć‚¤ćƒ™ćƒ³ćƒˆćƒćƒ³ćƒ‰ćƒ©ć‚’å®Ÿč£…ć—ć¾ć™ć€‚

http://geode.docs.pivotal.io/docs/developing/events/implementing_cache_event_handlers.html

ć©ć®ć‚ˆć†ćŖć‚¤ćƒ™ćƒ³ćƒˆć«é–¢é€£ć™ć‚‹ć‚¤ćƒ™ćƒ³ćƒˆćƒćƒ³ćƒ‰ćƒ©ć‚’ä½œęˆć—ćŸć„ć‹ć§ć€å®Ÿč£…ę–¹ę³•ćŒå¤‰ć‚ć‚Šć¾ć™ć€‚

åŒęœŸćŖć®ć‹éžåŒęœŸćŖ恮恋态Regionę“ä½œć«é–¢ć™ć‚‹ć‚‚ć®ćŖć®ć‹ć€ćƒ”ćƒ³ćƒćƒ¼ć«é–¢ć™ć‚‹ć‚‚ć®ćŖć®ć‹ć€ćƒˆćƒ©ćƒ³ć‚¶ć‚Æć‚·ćƒ§ćƒ³ć«é–¢ć™ć‚‹ć‚‚ć®ćŖ恮恋怂

ć‚¤ćƒ™ćƒ³ćƒˆćƒćƒ³ćƒ‰ćƒ©ćÆē‰¹å®šć®ć‚¤ćƒ³ć‚æćƒ¼ćƒ•ć‚§ćƒ¼ć‚¹ć‚’å®Ÿč£…ć—ć¦å®Ÿē¾ć—ć¾ć™ćŒć€Cache悄Regionć«é–¢ć™ć‚‹ć‚¤ćƒ³ć‚æćƒ¼ćƒ•ć‚§ćƒ¼ć‚¹ć®é ‚ē‚¹ć«ć„悋恮ćÆCacheCallbackć‚¤ćƒ³ć‚æćƒ¼ćƒ•ć‚§ćƒ¼ć‚¹ć§ć™ć€‚åˆ©ē”Øć—ćŸć„ć‚¤ćƒ™ćƒ³ćƒˆćƒćƒ³ćƒ‰ćƒ©ć«åæœć˜ć¦ć€CacheCallbackć‚¤ćƒ³ć‚æćƒ¼ćƒ•ć‚§ćƒ¼ć‚¹ć®ę“¾ē”Ÿć‚¤ćƒ³ć‚æćƒ¼ćƒ•ć‚§ćƒ¼ć‚¹ć‚’å®Ÿč£…ć—ć¾ć™ć€‚äø»ćŖć‚¤ćƒ™ćƒ³ćƒˆćƒćƒ³ćƒ‰ćƒ©ćØé–¢é€£ć™ć‚‹ć‚¤ćƒ™ćƒ³ćƒˆć«é–¢ć™ć‚‹äø€č¦§ćÆ态恓恔悉怂

http://geode.docs.pivotal.io/docs/developing/events/list_of_event_handlers_and_events.html

CachećŠć‚ˆć³Regionć«é–¢ć™ć‚‹ę“ä½œć§ć‚ć‚Œć°äø»ć«CacheListenerć€éžåŒęœŸę“ä½œć§ć‚ć‚Œć°AsyncEventListenerć‚’å®Ÿč£…ć—ć¾ć™ć€‚

CacheWriter悄CacheLoaderć‚‚ć“ć®ć‚¤ćƒ™ćƒ³ćƒˆćƒćƒ³ćƒ‰ćƒŖćƒ³ć‚°ć«ć¤ć„ć¦ć®ć‚«ćƒ†ć‚“ćƒ©ć‚¤ć‚ŗćØć•ć‚Œć¦ć„ć¦ć€ć‚„ć£ć±ć‚ŠCacheCallbackć®ć‚µćƒ–ć‚¤ćƒ³ć‚æćƒ¼ćƒ•ć‚§ćƒ¼ć‚¹ćØćŖć£ć¦ć„ć¾ć™ć€‚

Read Through态Write Through态Write BehindćÆ恓恮仕ēµ„ćæäøŠć§å®Ÿč£…恙悋恓ćØ恫ćŖ悋悏恑恧恙恭怂Write BehindćÆ态AsyncEventListenerćØć—ć¦å®Ÿč£…ć™ć‚‹ć“ćØ恫ćŖ悋悈恆恧恙恌怂

http://geode.docs.pivotal.io/docs/developing/events/implementing_write_behind_event_handler.html

CacheListenerć‚’å®Ÿč£…ć™ć‚‹

ć¾ćšćÆCacheListenerć‚’å®Ÿč£…ć—ć¦ć€Region恫åÆ¾ć™ć‚‹čæ½åŠ ć€ę›“ę–°ć€å‰Šé™¤ć«åÆ¾ć™ć‚‹ć‚¤ćƒ™ćƒ³ćƒˆć‚’ę‰±ć£ć¦ćæ恟恄ćØę€ć„ć¾ć™ć€‚

å®Ÿéš›ć«ćÆCacheListenerć‚¤ćƒ³ć‚æćƒ¼ćƒ•ć‚§ćƒ¼ć‚¹ć‚’ē›“ęŽ„å®Ÿč£…ć™ć‚‹ć®ć§ćÆćŖ恏态CacheListenerAdapterć‚Æćƒ©ć‚¹ć‚’ē¶™ę‰æ恗恦态åæ…要ćŖć‚¤ćƒ™ćƒ³ćƒˆę“ä½œć®ćæå®Ÿč£…ć—ć¾ć™ć€‚
src/main/java/org/littlewings/geode/event/MyCacheListener.java

package org.littlewings.geode.event;

import com.gemstone.gemfire.cache.EntryEvent;
import com.gemstone.gemfire.cache.util.CacheListenerAdapter;

public class MyCacheListener extends CacheListenerAdapter<String, String> {
    @Override
    public void afterCreate(EntryEvent<String, String> event) {
        KeyValueStore<String, String> store = KeyValueStore.getInstance("create-event-store");

        String key = event.getKey();
        String oldValue = event.getOldValue();
        String newValue = event.getNewValue();

        store.put(key, oldValue + ":" + newValue);
    }

    @Override
    public void afterUpdate(EntryEvent<String, String> event) {
        KeyValueStore<String, String> store = KeyValueStore.getInstance("update-event-store");

        String key = event.getKey();
        String oldValue = event.getOldValue();
        String newValue = event.getNewValue();

        store.put(key, oldValue + ":" + newValue);
    }

    @Override
    public void afterDestroy(EntryEvent<String, String> event) {
        KeyValueStore<String, String> store = KeyValueStore.getInstance("destroy-event-store");

        String key = event.getKey();
        String oldValue = event.getOldValue();
        String newValue = event.getNewValue();

        store.put(key, oldValue + ":" + newValue);
    }
}

å„ćƒ”ć‚½ćƒƒćƒ‰ć®å¼•ę•°ćØ恗恦态EntryEvent恌ęø”ć£ć¦ćć‚‹ć®ć§ć€ćć“ć§ć‚­ćƒ¼ć‚„å¤‰ę›“å‰å¾Œć®å€¤ćŒå–å¾—ć§ćć¾ć™ć€‚å€¤ć«ć¤ć„ć¦ćÆäø”ę–¹ć®ć‚‚ć®ćŒć„ć¤ć‚‚å–ć‚Œć‚‹ćØćÆé™ć‚‰ćšć€ä¾‹ćˆć°ę–°č¦ć‚Øćƒ³ćƒˆćƒŖä½œęˆę™‚ć§ć‚ć‚Œć°NewValuećŒå–ć‚Œć‚‹ć ć‘ć«ćŖć‚Šć¾ć™ć€‚

ćŖć‚“ć‹å¦™ćŖć‚Æćƒ©ć‚¹ćŒé–“ć«ęŒŸć¾ć£ć¦ć„ć¾ć™ćŒć€ć“ć‚ŒćÆćƒ†ć‚¹ćƒˆę™‚ć«å‘¼ć³å‡ŗć—å†…å®¹ć‚’ē¢ŗčŖć™ć‚‹ćŸć‚ć®ć‚Æćƒ©ć‚¹ć§ć€ćƒ‘ćƒ©ćƒ”ćƒ¼ć‚æćƒ¼ćŖ恩悒äæå­˜ć—ć¦ć„ć¾ć™ć€‚å®Ÿč£…ćÆ恓悓ćŖꄟ恘恧恙怂
src/main/java/org/littlewings/geode/event/KeyValueStore.java

package org.littlewings.geode.event;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.BiFunction;

public class KeyValueStore<K, V> {
    private static final Map<String, KeyValueStore<?, ?>> INSTANCES = new ConcurrentHashMap<>();
    private Map<K, V> store = new ConcurrentHashMap<>();


    protected KeyValueStore() {
    }

    public static <K, V> KeyValueStore getInstance(String name) {
        return (KeyValueStore<K, V>) INSTANCES.computeIfAbsent(name, key -> new KeyValueStore<K, V>());
    }

    public static void clear() {
        INSTANCES.clear();
    }

    public void put(K key, V value) {
        store.put(key, value);
    }

    public V compute(K key, BiFunction<? super K,? super V,? extends V> remappingFunction) {
        return store.compute(key, remappingFunction);
    }

    public V get(K key) {
        return store.get(key);
    }

    public int size() {
        return store.size();
    }
}

恝悌恧ćÆ态恓悌悉悒ä½æć£ć¦å‹•ä½œē¢ŗčŖć—恦ćæć¾ć—ć‚‡ć†ć€‚

ćƒ†ć‚¹ćƒˆć‚³ćƒ¼ćƒ‰ć®é››å½¢

src/test/java/org/littlewings/geode/event/EventHandlingTest.java

package org.littlewings.geode.event;

import com.gemstone.gemfire.cache.Cache;
import com.gemstone.gemfire.cache.CacheFactory;
import com.gemstone.gemfire.cache.Region;
import com.gemstone.gemfire.cache.RegionShortcut;
import org.junit.Before;
import org.junit.Test;

import static org.assertj.core.api.Assertions.assertThat;

public class EventHandlingTest {
    @Before
    public void setUp() {
        KeyValueStore.clear();
    }

    // ć“ć“ć«ć€ćƒ†ć‚¹ćƒˆć‚’ę›ø恏ļ¼
}

Listenerć§å‘¼ć³å‡ŗć—å†…å®¹ć‚’äæå­˜ć™ć‚‹å†…å®¹ć‚’ć€ęƎ回ć‚ÆćƒŖć‚¢ć™ć‚‹ć‚ˆć†ć«ć—ć¦ć‚‹ć ć‘ć§ć™ć­ć€‚

動作ē¢ŗčŖ

恧ćÆć€ä½œęˆć—ćŸCacheListenerć®å‹•ä½œē¢ŗčŖć‚’恗恦ćæć¾ć—ć‚‡ć†ć€‚

ä½œęˆć—ćŸćƒ†ć‚¹ćƒˆć‚³ćƒ¼ćƒ‰ćÆ态恓恔悉怂

    @Test
    public void cacheListener() {
        try (Cache cache = new CacheFactory().create()) {
            Region<String, String> region =
                    cache
                            .<String, String>createRegionFactory(RegionShortcut.PARTITION)
                            .addCacheListener(new MyCacheListener())
                            .create("sampleRegion");

            // create
            region.put("key1", "value1");

            assertThat(KeyValueStore.getInstance("create-event-store").get("key1"))
                    .isEqualTo("null:value1");
            assertThat(KeyValueStore.getInstance("update-event-store").size())
                    .isEqualTo(0);
            assertThat(KeyValueStore.getInstance("destroy-event-store").size())
                    .isEqualTo(0);


            // update
            region.put("key1", "value1-1");

            assertThat(KeyValueStore.getInstance("create-event-store").get("key1"))
                    .isEqualTo("null:value1");
            assertThat(KeyValueStore.getInstance("update-event-store").get("key1"))
                    .isEqualTo("value1:value1-1");
            assertThat(KeyValueStore.getInstance("destroy-event-store").size())
                    .isEqualTo(0);


            // destroy
            region.remove("key1");

            assertThat(KeyValueStore.getInstance("create-event-store").get("key1"))
                    .isEqualTo("null:value1");
            assertThat(KeyValueStore.getInstance("update-event-store").get("key1"))
                    .isEqualTo("value1:value1-1");
            assertThat(KeyValueStore.getInstance("destroy-event-store").get("key1"))
                    .isEqualTo("value1-1:null");
        }
    }

Regionä½œęˆę™‚ć«CacheListener悒add恙悋恓ćØ恧态CacheListener悒ē™»éŒ²ć™ć‚‹ć“ćØćŒć§ćć¾ć™ć€‚

            Region<String, String> region =
                    cache
                            .<String, String>createRegionFactory(RegionShortcut.PARTITION)
                            .addCacheListener(new MyCacheListener())
                            .create("sampleRegion");

恂ćØć®ć‚³ćƒ¼ćƒ‰ćÆRegin恫åÆ¾ć—ć¦putļ¼removeć—ć¦ć„ć‚‹ć ć‘ć§ć™ćŒć€ć‚¤ćƒ™ćƒ³ćƒˆć”ćØ恫CacheListenerćŒå‘¼ć³å‡ŗć•ć‚Œć¦ć„ć‚‹ć“ćØćŒć‚ć‹ć‚Šć¾ć™ć€‚

Cache XML恧CacheListenerć‚’å®šē¾©ć™ć‚‹

CacheListener悒态Cache XMLć§å®šē¾©ć—恦ćæć¾ć™ć€‚

Cache XML恫CacheListener悒ē™»éŒ²ć™ć‚‹ć®ć§ć™ćŒć€ć“ć®å “合ćÆCacheListener恌Declarableć‚¤ćƒ³ć‚æćƒ¼ćƒ•ć‚§ćƒ¼ć‚¹ć‚’å®Ÿč£…ć—ć¦ćŠćåæ…č¦ćŒć‚ć‚Šć¾ć™ć€‚ä»Šå›žćÆ态ē°”å˜ć®ćŸć‚å…ˆć»ć©ä½œęˆć—ćŸCacheListener悒ē¶™ę‰æć—ć€ćć“ć«Declarableć‚¤ćƒ³ć‚æćƒ¼ćƒ•ć‚§ćƒ¼ć‚¹ć‚’å®Ÿč£…ć™ć‚‹å½¢ć«ć—ć¾ć—ćŸć€‚
src/main/java/org/littlewings/geode/event/MyDeclaredCacheListener.java

package org.littlewings.geode.event;

import java.util.Properties;

import com.gemstone.gemfire.cache.Declarable;

public class MyDeclaredCacheListener extends MyCacheListener implements Declarable {
    @Override
    public void init(Properties props) {
        // no-op
    }
}

initćƒ”ć‚½ćƒƒćƒ‰ć§ćÆ态Cache XMLć§å®šē¾©ć—ćŸćƒ—ćƒ­ćƒ‘ćƒ†ć‚£ć‚’å—ć‘å–ć‚‹ć“ćØćŒć§ćć¾ć™ć€‚ćŒć€ä»Šå›žćÆē«ÆęŠ˜ć‚Šć¾ć™ā€¦ć€‚

Cache XMLćÆć€ć“ć®ć‚ˆć†ć«å®šē¾©ć€‚
src/test/resources/cache-predefined-listener.xml

<?xml version="1.0" encoding="UTF-8"?>
<cache
        xmlns="http://schema.pivotal.io/gemfire/cache"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://schema.pivotal.io/gemfire/cache http://schema.pivotal.io/gemfire/cache/cache-8.1.xsd"
        version="8.1">
    <region name="sampleRegion" refid="PARTITION">
        <region-attributes>
            <cache-listener>
                <class-name>org.littlewings.geode.event.MyDeclaredCacheListener</class-name>
            </cache-listener>
            <!-- multiple cache listener
            <cache-listener>
                <class-name>XYZ</class-name>
            </cache-listener>
            -->
        </region-attributes>
    </region>
</cache>

ć‚³ćƒ”ćƒ³ćƒˆć‚¢ć‚¦ćƒˆć—ć¦ć„ć¾ć™ćŒć€cache-listenerć‚æ悰悒äø¦ć¹ć‚‹ć“ćØ恧态複ꕰ恮CacheListener悒ē™»éŒ²ć™ć‚‹ć“ćØćŒć§ćć¾ć™ć€‚

動作ē¢ŗčŖē”Øć®ćƒ†ć‚¹ćƒˆć‚³ćƒ¼ćƒ‰ćÆ态恓恔悉怂

    @Test
    public void declaredCacheListener() {
        try (Cache cache = new CacheFactory().set("cache-xml-file", "cache-predefined-listener.xml").create()) {
            Region<String, String> region = cache.<String, String>getRegion("sampleRegion");

            // create
            region.put("key1", "value1");

            assertThat(KeyValueStore.getInstance("create-event-store").get("key1"))
                    .isEqualTo("null:value1");
            assertThat(KeyValueStore.getInstance("update-event-store").size())
                    .isEqualTo(0);
            assertThat(KeyValueStore.getInstance("destroy-event-store").size())
                    .isEqualTo(0);


            // update
            region.put("key1", "value1-1");

            assertThat(KeyValueStore.getInstance("create-event-store").get("key1"))
                    .isEqualTo("null:value1");
            assertThat(KeyValueStore.getInstance("update-event-store").get("key1"))
                    .isEqualTo("value1:value1-1");
            assertThat(KeyValueStore.getInstance("destroy-event-store").size())
                    .isEqualTo(0);


            // destroy
            region.remove("key1");

            assertThat(KeyValueStore.getInstance("create-event-store").get("key1"))
                    .isEqualTo("null:value1");
            assertThat(KeyValueStore.getInstance("update-event-store").get("key1"))
                    .isEqualTo("value1:value1-1");
            assertThat(KeyValueStore.getInstance("destroy-event-store").get("key1"))
                    .isEqualTo("value1-1:null");
        }
    }

今回ćÆRegionćÆ定ē¾©ęøˆćæćŖ恮恧态Cacheć‹ć‚‰å–å¾—ć™ć‚‹ć®ćæ恧恙怂

        try (Cache cache = new CacheFactory().set("cache-xml-file", "cache-predefined-listener.xml").create()) {
            Region<String, String> region = cache.<String, String>getRegion("sampleRegion");

ä»„é™ć®å‹•ä½œć«ć¤ć„ć¦ćÆ态API恧addCacheListener恗恟ꙂćØåŒć˜ēµęžœć«ćŖć‚Šć¾ć™ć€‚

CacheLoaderć‚’å®Ÿč£…ć™ć‚‹

ć‚‚ć†ć²ćØć¤ć‚µćƒ³ćƒ—ćƒ«ćØ恗恦态CacheLoaderć‚’å®Ÿč£…ć—ć¦ćæć¾ć—ć‚‡ć†ć€‚

CacheLoaderć‚’ä½œęˆć™ć‚‹ć«ćÆ态CacheLoaderć‚¤ćƒ³ć‚æćƒ¼ćƒ•ć‚§ćƒ¼ć‚¹ć‚’å®Ÿč£…ć—ćŸć‚Æćƒ©ć‚¹ć‚’ä½œęˆć—ć¾ć™ć€‚ä»„äø‹ć«ć‚ć‚‹ć‚ˆć†ć«ć€Region#get恌null悒čæ”ć™å “åˆć«CacheLoaderćŒå‘¼ć³å‡ŗć•ć‚Œć€ć‚­ćƒ¼ć«åƾåæœć™ć‚‹å€¤ć‚’ē”Ÿęˆć™ć‚‹å½¹å‰²ć‚’ę‹…ć„ć¾ć™ć€‚

Allows data from outside of the VM to be placed into a region. When Region.get(Object) is called for a region entry that has a null value, the load method of the region's cache loader is invoked. The load method creates the value for the desired key by performing an operation such as a database query. The load may also perform a net search that will look for the value in a cache instance hosted by another member of the distributed system.

http://data-docs-samples.cfapps.io/docs-gemfire/latest/javadocs/japi/com/gemstone/gemfire/cache/CacheLoader.html

ć§ć€ä½œęˆć—ćŸCacheLoaderćÆ恓恔悉怂
src/main/java/org/littlewings/geode/event/MyCacheLoader.java

package org.littlewings.geode.event;

import com.gemstone.gemfire.cache.CacheLoader;
import com.gemstone.gemfire.cache.CacheLoaderException;
import com.gemstone.gemfire.cache.LoaderHelper;

public class MyCacheLoader implements CacheLoader<String, String> {
    @Override
    public String load(LoaderHelper<String, String> helper) throws CacheLoaderException {
        String key = helper.getKey();

        KeyValueStore<String, Integer> store = KeyValueStore.getInstance("load-counter");
        store.compute(key, (k, v) -> (v == null) ? 1 : v + 1);

        Thread.dumpStack();

        return key + "-value-loaded";
    }

    @Override
    public void close() {
        // no-op
    }
}

今回ćÆē°”å˜ć«ć€ć‚­ćƒ¼ć«åÆ¾ć—ć¦ę©Ÿę¢°ēš„ć«å€¤ć‚’ä½œęˆć™ć‚‹ć®ćæć«ć—ć¾ć—ćŸć€‚ć‚ćØ态恓恮CacheLoaderćŒä½•å›žå‘¼ć³å‡ŗć•ć‚ŒćŸć‹ć®ć‚«ć‚¦ćƒ³ćƒˆć‚’å–ć‚‹ć‚ˆć†ć«ć—ć¦ć„ć¾ć™ć€‚1åŗ¦å€¤ć‚’čæ”ć—ćŸå¾ŒćÆ态ć‚Øćƒ³ćƒˆćƒŖćŒę®‹ć£ć¦ć„ć‚‹é™ć‚ŠćÆå‘¼ć³å‡ŗ恕悌ćŖ恄ćÆ恚ćŖ恮恧怂

ē¢ŗčŖć€‚

    @Test
    public void cacheLoader() {
        try (Cache cache = new CacheFactory().create()) {
            Region<String, String> region =
                    cache
                            .<String, String>createRegionFactory(RegionShortcut.PARTITION)
                            .setCacheLoader(new MyCacheLoader())
                            .create("sampleRegion");

            assertThat(region.get("key1"))
                    .isEqualTo("key1-value-loaded");
            assertThat(KeyValueStore.getInstance("load-counter").get("key1"))
                    .isEqualTo(1);
            region.get("key1");
            assertThat(KeyValueStore.getInstance("load-counter").get("key1"))
                    .isEqualTo(1);  // not-increment

            assertThat(region.get("key2"))
                    .isEqualTo("key2-value-loaded");
            assertThat(region.get("key3"))
                    .isEqualTo("key3-value-loaded");

            assertThat(KeyValueStore.getInstance("load-counter").get("key2"))
                    .isEqualTo(1);
            assertThat(KeyValueStore.getInstance("load-counter").get("key3"))
                    .isEqualTo(1);
        }
    }

CacheLoader恮適ē”ØćÆ态Regionä½œęˆę™‚ć«č”Œć„ć¾ć™ć€‚

            Region<String, String> region =
                    cache
                            .<String, String>createRegionFactory(RegionShortcut.PARTITION)
                            .setCacheLoader(new MyCacheLoader())
                            .create("sampleRegion");

ä»Šå›žć®CacheLoader恌恄悋ćØ态Region#put恗恦恄ćŖć„ć®ć«ć„ććŖć‚Šå€¤ćŒå–ć‚Œć‚‹ć‚ˆć†ć«ćŖć‚Šć¾ć™ć€‚

            assertThat(region.get("key1"))
                    .isEqualTo("key1-value-loaded");

1åŗ¦CacheLoaderćŒå€¤ć‚’čæ”ć—ćŸå¾ŒćÆ态2åŗ¦ē›®ä»„é™ć®Region#getć®å‘¼ć³å‡ŗ恗恧ćÆCacheLoaderć®å‘¼ć³å‡ŗ恗ćÆč”Œć‚ć‚Œć¾ć›ć‚“ć€‚

            assertThat(KeyValueStore.getInstance("load-counter").get("key1"))
                    .isEqualTo(1);
            region.get("key1");
            assertThat(KeyValueStore.getInstance("load-counter").get("key1"))
                    .isEqualTo(1);  // not-increment

Cache XML恧CacheLoaderć‚’å®šē¾©ć™ć‚‹

ęœ€å¾Œć«ć€Cache XML恧悂CacheLoaderć‚’å®šē¾©ć—恦ćæć¾ć™ć€‚

CacheListenerćØåŒę§˜ć€Declarableć‚¤ćƒ³ć‚æćƒ¼ćƒ•ć‚§ćƒ¼ć‚¹ć‚’å®Ÿč£…ć™ć‚‹åæ…č¦ćŒć‚ć‚Šć¾ć™ć€‚ć“ć”ć‚‰ć‚‚ć€å…ˆć»ć©ä½œęˆć—ćŸCacheLoader悒ē¶™ę‰æć™ć‚‹å½¢ć§ē°”å˜ć«ęøˆć¾ć›ć¾ć™ć€‚
src/main/java/org/littlewings/geode/event/MyDeclaredCacheLoader.java

package org.littlewings.geode.event;

import java.util.Properties;

import com.gemstone.gemfire.cache.Declarable;

public class MyDeclaredCacheLoader extends MyCacheLoader implements Declarable {
    @Override
    public void init(Properties props) {
        // no-op
    }
}

Cache XML恧ćÆ态仄äø‹ć®ć‚ˆć†ć«ć—恦CacheLoader悒čØ­å®šć—ć¾ć™ć€‚
src/test/resources/cache-predefined-loader.xml

<?xml version="1.0" encoding="UTF-8"?>
<cache
        xmlns="http://schema.pivotal.io/gemfire/cache"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://schema.pivotal.io/gemfire/cache http://schema.pivotal.io/gemfire/cache/cache-8.1.xsd"
        version="8.1">
    <region name="sampleRegion" refid="PARTITION">
        <region-attributes>
            <cache-loader>
                <class-name>org.littlewings.geode.event.MyDeclaredCacheLoader</class-name>
            </cache-loader>
        </region-attributes>
    </region>
</cache>

CacheLoaderćÆCacheListnerćØē•°ćŖ悊态複ꕰčØ­å®šć™ć‚‹ć“ćØćŒć§ćć¾ć›ć‚“ć€‚Regionä½œęˆę™‚ć‚‚ć€"setCacheLoader"恧恗恟恋悉恭ā€¦ć€‚

ćƒ†ć‚¹ćƒˆć‚³ćƒ¼ćƒ‰ć«ć¤ć„ć¦ćÆ态Cache XMLć‚’ęŒ‡å®šć—ć¦ä½œęˆęøˆćæ恮Regionć‚’å–å¾—ć™ć‚‹ä»„å¤–ćÆ态恻ćØć‚“ć©å¤‰ć‚ć‚Šć¾ć›ć‚“ć€‚

    @Test
    public void declaredCacheLoader() {
        try (Cache cache = new CacheFactory().set("cache-xml-file", "cache-predefined-loader.xml").create()) {
            Region<String, String> region = cache.<String, String>getRegion("sampleRegion");

            assertThat(region.get("key1"))
                    .isEqualTo("key1-value-loaded");
            assertThat(KeyValueStore.getInstance("load-counter").get("key1"))
                    .isEqualTo(1);
            region.get("key1");
            assertThat(KeyValueStore.getInstance("load-counter").get("key1"))
                    .isEqualTo(1);  // not-increment

            assertThat(region.get("key2"))
                    .isEqualTo("key2-value-loaded");
            assertThat(region.get("key3"))
                    .isEqualTo("key3-value-loaded");

            assertThat(KeyValueStore.getInstance("load-counter").get("key2"))
                    .isEqualTo(1);
            assertThat(KeyValueStore.getInstance("load-counter").get("key3"))
                    .isEqualTo(1);
        }
    }

ćƒ†ć‚¹ćƒˆēµęžœć§ć™ć€‚

ć¾ćØ悁

Apache Geodeć®ć€ć‚¤ćƒ™ćƒ³ćƒˆćƒćƒ³ćƒ‰ćƒŖćƒ³ć‚°ć®åˆę­©ēš„ćŖ悂恮悒試恗恦ćæć¾ć—ćŸć€‚ćØ悊恂恈恚态ē°”単ćŖä½æć„ę–¹ćÆć‚ć‹ć£ćŸę„Ÿć˜ć‹ćŖćØę€ć„ć¾ć™ć€‚

ćØćÆć„ćˆć€ć‚„ć£ć¦ć„ćŖ恄恓ćØć‚‚ćŸćć•ć‚“ć‚ć‚Šć¾ć™ć€‚

Clientļ¼ServerćÆć‚„ć£ć¦ć„ć¾ć›ć‚“ć—ć€ć‚Æćƒ©ć‚¹ć‚æćƒŖćƒ³ć‚°ć‚‚ę‰±ć£ć¦ć„ć¾ć›ć‚“ć€‚

http://geode.docs.pivotal.io/docs/developing/events/how_client_server_distribution_works.html

http://geode.docs.pivotal.io/docs/developing/events/how_cache_events_work.html

http://geode.docs.pivotal.io/docs/developing/events/configure_p2p_event_messaging.html

éžåŒęœŸć‚¤ćƒ™ćƒ³ćƒˆćƒćƒ³ćƒ‰ćƒ©ć‚‚ć€ę‰±ć„ć¾ć›ć‚“ć§ć—ćŸć€‚

http://geode.docs.pivotal.io/docs/developing/events/implementing_write_behind_event_handler.html

Clientļ¼Server恫恊恄恦ćÆ态HAć€ć‚­ćƒ„ćƒ¼ć®ć‚µć‚¤ć‚ŗęŒ‡å®šćŖć©ćƒ†ćƒ¼ćƒžćÆć¾ć ć¾ć ćŸćć•ć‚“ć‚ć‚Šć¾ć™ć€‚

http://geode.docs.pivotal.io/docs/developing/events/configuring_highly_available_servers.html

ćØć„ć£ć¦ć‚‚ę¦‚č¦ćÆćŖ悓ćØćŖćć‚ć‹ć£ćŸę°—ćŒć™ć‚‹ć®ć§ć€ć“ć®å¾ŒćÆåæ…要恫ćŖ悋恓ćØ恌恂悌恰态恧恗悇恆恋恭怂