You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@geode.apache.org by up...@apache.org on 2017/10/19 22:45:58 UTC

[geode-examples] branch develop updated: GEODE-3711: Add an example of a cache listener. (#20)

This is an automated email from the ASF dual-hosted git repository.

upthewaterspout pushed a commit to branch develop
in repository https://gitbox.apache.org/repos/asf/geode-examples.git


The following commit(s) were added to refs/heads/develop by this push:
     new d1a0928  GEODE-3711: Add an example of a cache listener. (#20)
d1a0928 is described below

commit d1a0928eec253b86d5e4e80f937c7d14b2bffa12
Author: Michael "Sarge" Dodge <md...@pivotal.io>
AuthorDate: Thu Oct 19 15:45:56 2017 -0700

    GEODE-3711: Add an example of a cache listener. (#20)
    
    Adding an example cache listener that captures events on a region.
---
 README.md                                          |   4 +-
 listener/README.md                                 |  45 +++++++++
 listener/scripts/start.gfsh                        |  24 +++++
 listener/scripts/stop.gfsh                         |  19 ++++
 .../apache/geode/examples/listener/Example.java    |  95 ++++++++++++++++++
 .../examples/listener/ExampleCacheListener.java    |  39 ++++++++
 .../listener/ExampleCacheListenerTest.java         |  44 +++++++++
 .../geode/examples/listener/ExampleTest.java       |  32 ++++++
 settings.gradle                                    |   1 +
 src/test/java/org/geode/examples/util/Mocks.java   |   6 +-
 .../org/geode/examples/util/TestEntryEvent.java    | 108 +++++++++++++++++++++
 11 files changed, 413 insertions(+), 4 deletions(-)

diff --git a/README.md b/README.md
index 06e398f..bf0cdbd 100644
--- a/README.md
+++ b/README.md
@@ -70,8 +70,8 @@ tutorial.
 *  Functions
 *  [Cache Loader](loader/README.md)
 *  [Cache Writer](writer/README.md)
-*  Listeners
-*  Async Event Queues
+*  [Cache Listeners](listener/README.md)
+*  Async Event Queues & Async Event Listeners
 *  Continuous Querying
 *  Transactions
 *  Eviction
diff --git a/listener/README.md b/listener/README.md
new file mode 100644
index 0000000..6010925
--- /dev/null
+++ b/listener/README.md
@@ -0,0 +1,45 @@
+<!--
+Licensed to the Apache Software Foundation (ASF) under one or more
+contributor license agreements.  See the NOTICE file distributed with
+this work for additional information regarding copyright ownership.
+The ASF licenses this file to You under the Apache License, Version 2.0
+(the "License"); you may not use this file except in compliance with
+the License.  You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+-->
+
+# Geode Cache Listener Example
+
+This is a simple example that demonstrates the use of a cache listener to capture modifications to a region.
+
+A cache listener is added to a region before the region is created. _After_ an event (e.g., create a new entry, update an existing entry) occurs on that region, the cache listener has the appropriate handler method invoked, e.g., `afterCreate()` for creating a new entry. This method invocation can _not_ affect the operation on the region.
+
+In this example, a cache listener is installed that captures all of the creation events for the region. A number of entries are created in the region. The cache listener is notified of each creation and adds it to its queue of events. In other applications, the event could either be persisted to some other data store (i.e., write-behind) or a notification about the activity could be sent via some other mechanism.
+
+This example assumes you have installed Java and Geode.
+
+## Steps
+
+1. From the `geode-examples/listener` directory, build the example and
+   run unit tests.
+
+        $ ../gradlew build
+
+2. Next start a locator, start a server, and create a region.
+
+        $ gfsh run --file=scripts/start.gfsh
+
+3. Run the example to add a cache listener, put entries into the region, and capture the events.
+
+        $ ../gradlew run
+
+4. Shut down the system.
+
+        $ gfsh run --file=scripts/stop.gfsh
diff --git a/listener/scripts/start.gfsh b/listener/scripts/start.gfsh
new file mode 100755
index 0000000..0173ff9
--- /dev/null
+++ b/listener/scripts/start.gfsh
@@ -0,0 +1,24 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+start locator --name=locator --bind-address=127.0.0.1
+start server --name=server1 --locators=127.0.0.1[10334] --server-port=0
+start server --name=server2 --locators=127.0.0.1[10334] --server-port=0
+list members
+
+create region --name=example-region --type=REPLICATE
+describe region --name=example-region
diff --git a/listener/scripts/stop.gfsh b/listener/scripts/stop.gfsh
new file mode 100755
index 0000000..15cd93c
--- /dev/null
+++ b/listener/scripts/stop.gfsh
@@ -0,0 +1,19 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+connect --locator=127.0.0.1[10334]
+shutdown --include-locators=true
diff --git a/listener/src/main/java/org/apache/geode/examples/listener/Example.java b/listener/src/main/java/org/apache/geode/examples/listener/Example.java
new file mode 100644
index 0000000..fbbe000
--- /dev/null
+++ b/listener/src/main/java/org/apache/geode/examples/listener/Example.java
@@ -0,0 +1,95 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more contributor license
+ * agreements. See the NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License. You may obtain a
+ * copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package org.apache.geode.examples.listener;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.Queue;
+import java.util.Random;
+import java.util.concurrent.ArrayBlockingQueue;
+import java.util.function.Consumer;
+import java.util.stream.IntStream;
+
+import org.apache.geode.cache.CacheListener;
+import org.apache.geode.cache.EntryEvent;
+import org.apache.geode.cache.Region;
+import org.apache.geode.cache.client.ClientCache;
+import org.apache.geode.cache.client.ClientCacheFactory;
+import org.apache.geode.cache.client.ClientRegionFactory;
+import org.apache.geode.cache.client.ClientRegionShortcut;
+
+public class Example implements Consumer<Region<Integer, String>> {
+  public static final int ITERATIONS = 100;
+
+  private final CacheListener cacheListener;
+
+  private Queue<EntryEvent<Integer, String>> events =
+      new ArrayBlockingQueue<EntryEvent<Integer, String>>(100, true);
+
+  public static void main(String[] args) {
+    // connect to the locator using default port 10334
+    ClientCache cache = new ClientCacheFactory().addPoolLocator("127.0.0.1", 10334)
+        .set("log-level", "WARN").create();
+
+    Example example = new Example();
+
+    // create a local region that matches the server region
+    ClientRegionFactory<Integer, String> clientRegionFactory =
+        cache.<Integer, String>createClientRegionFactory(ClientRegionShortcut.CACHING_PROXY);
+    clientRegionFactory.addCacheListener(example.getCacheListener());
+    Region<Integer, String> region = clientRegionFactory.create("example-region");
+
+    example.accept(region);
+    cache.close();
+  }
+
+  Example() {
+    cacheListener = new ExampleCacheListener(events);
+  }
+
+  CacheListener getCacheListener() {
+    return cacheListener;
+  }
+
+  Queue<EntryEvent<Integer, String>> getEvents() {
+    return events;
+  }
+
+  private Collection<Integer> generateIntegers() {
+    IntStream stream = new Random().ints(0, ITERATIONS);
+    Iterator<Integer> iterator = stream.iterator();
+    Collection<Integer> integers = new ArrayList<>();
+    while (iterator.hasNext() && integers.size() < ITERATIONS) {
+      Integer integer = iterator.next();
+      if (!integers.contains(integer)) {
+        integers.add(integer);
+      }
+    }
+    return integers;
+  }
+
+  @Override
+  public void accept(Region<Integer, String> region) {
+    Collection<Integer> integers = generateIntegers();
+    Iterator<Integer> iterator = integers.iterator();
+
+    while (iterator.hasNext()) {
+      Integer integer = iterator.next();
+      region.put(integer, integer.toString());
+    }
+    System.out.println("Created " + region.size() + " entries.");
+  }
+}
diff --git a/listener/src/main/java/org/apache/geode/examples/listener/ExampleCacheListener.java b/listener/src/main/java/org/apache/geode/examples/listener/ExampleCacheListener.java
new file mode 100644
index 0000000..b91506a
--- /dev/null
+++ b/listener/src/main/java/org/apache/geode/examples/listener/ExampleCacheListener.java
@@ -0,0 +1,39 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more contributor license
+ * agreements. See the NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License. You may obtain a
+ * copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package org.apache.geode.examples.listener;
+
+import java.util.Queue;
+
+import org.apache.geode.cache.CacheListener;
+import org.apache.geode.cache.EntryEvent;
+import org.apache.geode.cache.RegionEvent;
+import org.apache.geode.cache.util.CacheListenerAdapter;
+
+public class ExampleCacheListener extends CacheListenerAdapter<Integer, String> {
+  private final Queue<EntryEvent<Integer, String>> events;
+
+  public ExampleCacheListener(Queue<EntryEvent<Integer, String>> events) {
+    this.events = events;
+  }
+
+  public Queue<EntryEvent<Integer, String>> getEvents() {
+    return events;
+  }
+
+  @Override
+  public void afterCreate(EntryEvent<Integer, String> event) {
+    events.add(event);
+  }
+}
diff --git a/listener/src/test/java/org/apache/geode/examples/listener/ExampleCacheListenerTest.java b/listener/src/test/java/org/apache/geode/examples/listener/ExampleCacheListenerTest.java
new file mode 100644
index 0000000..a260614
--- /dev/null
+++ b/listener/src/test/java/org/apache/geode/examples/listener/ExampleCacheListenerTest.java
@@ -0,0 +1,44 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more contributor license
+ * agreements. See the NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License. You may obtain a
+ * copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package org.apache.geode.examples.listener;
+
+import static org.junit.Assert.assertEquals;
+
+import java.util.LinkedList;
+import java.util.Queue;
+
+import org.geode.examples.util.TestEntryEvent;
+import org.junit.Test;
+
+import org.apache.geode.cache.EntryEvent;
+import org.apache.geode.cache.Operation;
+
+public class ExampleCacheListenerTest {
+  @Test
+  public void testAfterCreate() {
+    Queue<EntryEvent<Integer, String>> events = new LinkedList<>();
+    events.add(new TestEntryEvent<>(null, Operation.CREATE, 1, "one", "uno"));
+    events.add(new TestEntryEvent<>(null, Operation.CREATE, 2, "two", "dos"));
+    events.add(new TestEntryEvent<>(null, Operation.CREATE, 3, "three", "tres"));
+
+    ExampleCacheListener cacheListener =
+        new ExampleCacheListener(new LinkedList<EntryEvent<Integer, String>>());
+    for (EntryEvent<Integer, String> event : events) {
+      cacheListener.afterCreate(event);
+    }
+
+    assertEquals(events, cacheListener.getEvents());
+  }
+}
diff --git a/listener/src/test/java/org/apache/geode/examples/listener/ExampleTest.java b/listener/src/test/java/org/apache/geode/examples/listener/ExampleTest.java
new file mode 100644
index 0000000..e17aacf
--- /dev/null
+++ b/listener/src/test/java/org/apache/geode/examples/listener/ExampleTest.java
@@ -0,0 +1,32 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more contributor license
+ * agreements. See the NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License. You may obtain a
+ * copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package org.apache.geode.examples.listener;
+
+import static org.junit.Assert.assertEquals;
+
+import org.apache.geode.cache.Region;
+import org.geode.examples.util.Mocks;
+import org.junit.Test;
+
+public class ExampleTest {
+  @Test
+  public void testExample() throws Exception {
+    Example example = new Example();
+    Region<Integer, String> region = Mocks.region("example-region");
+    example.accept(region);
+
+    assertEquals(Example.ITERATIONS, region.size());
+  }
+}
diff --git a/settings.gradle b/settings.gradle
index 5402e65..6d0d478 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -25,3 +25,4 @@ include 'clientSecurity'
 include 'functions'
 include 'persistence'
 include 'writer'
+include 'listener'
diff --git a/src/test/java/org/geode/examples/util/Mocks.java b/src/test/java/org/geode/examples/util/Mocks.java
index dcb5520..b1e0d8f 100644
--- a/src/test/java/org/geode/examples/util/Mocks.java
+++ b/src/test/java/org/geode/examples/util/Mocks.java
@@ -15,9 +15,9 @@
 package org.geode.examples.util;
 
 import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
-import static org.mockito.Mockito.doAnswer;
 
 import java.util.HashMap;
 import java.util.Map;
@@ -28,6 +28,8 @@ import org.apache.geode.cache.execute.ResultCollector;
 
 import org.mockito.invocation.InvocationOnMock;
 
+import org.apache.geode.cache.Region;
+
 public class Mocks {
   private Mocks() {
   }
@@ -42,7 +44,7 @@ public class Mocks {
     when(region.get(any())).then(inv -> data.get(getKey(inv)));
     when(region.keySet()).thenReturn(data.keySet());
     when(region.values()).thenReturn(data.values());
-    when(region.size()).thenReturn(data.size());
+    when(region.size()).then(inv -> { return data.size(); });
     when(region.keySetOnServer()).thenReturn(data.keySet());
     when(region.containsKey(any())).then(inv -> data.containsKey(getKey(inv)));
     when(region.containsKeyOnServer(any())).then(inv -> data.containsKey(getKey(inv)));
diff --git a/src/test/java/org/geode/examples/util/TestEntryEvent.java b/src/test/java/org/geode/examples/util/TestEntryEvent.java
new file mode 100644
index 0000000..7fe0d58
--- /dev/null
+++ b/src/test/java/org/geode/examples/util/TestEntryEvent.java
@@ -0,0 +1,108 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more contributor license
+ * agreements. See the NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License. You may obtain a
+ * copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package org.geode.examples.util;
+
+import org.apache.geode.cache.EntryEvent;
+import org.apache.geode.cache.Operation;
+import org.apache.geode.cache.Region;
+import org.apache.geode.cache.SerializedCacheValue;
+import org.apache.geode.cache.TransactionId;
+import org.apache.geode.distributed.DistributedMember;
+
+public class TestEntryEvent<K, V> implements EntryEvent<K, V> {
+  final Region region;
+  final Operation operation;
+  final K key;
+  final V oldValue;
+  final V newValue;
+
+  public TestEntryEvent(Region region, Operation operation, K key, V oldValue, V newValue) {
+    this.region = region;
+    this.operation = operation;
+    this.key = key;
+    this.oldValue = oldValue;
+    this.newValue = newValue;
+  }
+
+  @Override
+  public K getKey() {
+    return key;
+  }
+
+  @Override
+  public V getOldValue() {
+    return oldValue;
+  }
+
+  @Override
+  public SerializedCacheValue getSerializedOldValue() {
+    return null;
+  }
+
+  @Override
+  public V getNewValue() {
+    return newValue;
+  }
+
+  @Override
+  public SerializedCacheValue getSerializedNewValue() {
+    return null;
+  }
+
+  @Override
+  public TransactionId getTransactionId() {
+    return null;
+  }
+
+  @Override
+  public boolean hasClientOrigin() {
+    return false;
+  }
+
+  @Override
+  public boolean isOldValueAvailable() {
+    return true;
+  }
+
+  @Override
+  public Region getRegion() {
+    return region;
+  }
+
+  @Override
+  public Operation getOperation() {
+    return operation;
+  }
+
+  @Override
+  public Object getCallbackArgument() {
+    return null;
+  }
+
+  @Override
+  public boolean isCallbackArgumentAvailable() {
+    return false;
+  }
+
+  @Override
+  public boolean isOriginRemote() {
+    return false;
+  }
+
+  @Override
+  public DistributedMember getDistributedMember() {
+    return null;
+  }
+}

-- 
To stop receiving notification emails like this one, please contact
['"commits@geode.apache.org" <co...@geode.apache.org>'].