geode-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From kmil...@apache.org
Subject geode-examples git commit: GEODE-2231 A partitioned region example [Forced Update!]
Date Thu, 23 Feb 2017 19:07:12 GMT
Repository: geode-examples
Updated Branches:
  refs/heads/feature/GEODE-2231 c4b79dd9a -> d551b7c7e (forced update)


GEODE-2231 A partitioned region example

- partitioned/README.md has instructions for running this
example


Project: http://git-wip-us.apache.org/repos/asf/geode-examples/repo
Commit: http://git-wip-us.apache.org/repos/asf/geode-examples/commit/d551b7c7
Tree: http://git-wip-us.apache.org/repos/asf/geode-examples/tree/d551b7c7
Diff: http://git-wip-us.apache.org/repos/asf/geode-examples/diff/d551b7c7

Branch: refs/heads/feature/GEODE-2231
Commit: d551b7c7e999ee7f9774f9d84b72c3a06a770fd7
Parents: 338caa4
Author: Karen Miller <kmiller@pivotal.io>
Authored: Tue Jan 3 13:47:52 2017 -0800
Committer: Karen Miller <kmiller@pivotal.io>
Committed: Thu Feb 23 10:57:35 2017 -0800

----------------------------------------------------------------------
 README.md                                       |   2 +-
 partitioned/README.md                           | 276 +++++++++++++++++++
 partitioned/build.gradle                        |  20 ++
 partitioned/scripts/.gitignore                  |   2 +
 partitioned/scripts/pidkiller.sh                |  35 +++
 partitioned/scripts/setEnv.sh                   |  33 +++
 partitioned/scripts/startAll.sh                 |  48 ++++
 partitioned/scripts/stopAll.sh                  |  28 ++
 .../examples/partitioned/BadEmployeeKey.java    |  40 +++
 .../geode/examples/partitioned/BaseClient.java  |  79 ++++++
 .../geode/examples/partitioned/Consumer.java    |  81 ++++++
 .../examples/partitioned/EmployeeData.java      |  60 ++++
 .../geode/examples/partitioned/EmployeeKey.java |  69 +++++
 .../geode/examples/partitioned/Producer.java    | 151 ++++++++++
 partitioned/src/main/main2.iml                  |   6 +
 .../partitioned/BadEmployeeKeyTest.java         |  41 +++
 .../examples/partitioned/ConsumerTest.java      | 164 +++++++++++
 .../examples/partitioned/EmployeeDataTest.java  |  70 +++++
 .../examples/partitioned/EmployeeKeyTest.java   |  61 ++++
 .../examples/partitioned/PartitionedTest.java   | 165 +++++++++++
 .../examples/partitioned/ProducerTest.java      | 132 +++++++++
 partitioned/src/test/test5.iml                  |   6 +
 .../examples/replicated/ReplicatedTest.java     |   2 +-
 settings.gradle                                 |   1 +
 .../apache/geode/example/utils/ShellUtil.java   | 106 -------
 .../apache/geode/examples/utils/ShellUtil.java  | 106 +++++++
 26 files changed, 1676 insertions(+), 108 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/geode-examples/blob/d551b7c7/README.md
----------------------------------------------------------------------
diff --git a/README.md b/README.md
index 7b0079d..1e3eb55 100644
--- a/README.md
+++ b/README.md
@@ -44,7 +44,7 @@ All examples:
 ### Basics
 
 *  [Replicated Region](replicated/README.md)
-*  Partitioned Region
+*  [Partitioned Region](partitioned/README.md)
 *  Persistence
 *  OQL (Querying)
 

http://git-wip-us.apache.org/repos/asf/geode-examples/blob/d551b7c7/partitioned/README.md
----------------------------------------------------------------------
diff --git a/partitioned/README.md b/partitioned/README.md
new file mode 100644
index 0000000..c960fe0
--- /dev/null
+++ b/partitioned/README.md
@@ -0,0 +1,276 @@
+<!--
+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 partitioned region example
+
+This example demonstrates the basic property of partitioning, as well
+as what can go wrong with partitioned regions. 
+The example is presented in two parts.
+The first part shows partitioning, and the second part demonstrates
+what can go wrong with a flawed implementation.
+
+The basic property of partitioning is that data entries are distributed 
+across all servers that host a region.
+The distribution is like database sharding, except that the distribution
+occurs automatically. It is also similar to data striping on disks,
+except that the distribution is not based on hardware.
+
+In this example,
+two servers host two partitioned regions. 
+There is no redundancy, so that the basic property of partitioning
+may be observed.
+The Producer code puts the 10 entries into one of the two
+regions.
+The Consumer gets and prints the entries from one of the two regions.
+The regions are partitioned,
+so the entries are distributed among the two servers hosting the region.
+Since there is no redundancy of the data within the region,
+when one of the servers goes away,
+the entries hosted within that server are also gone.
+
+The two regions are the same, except for the hash code implementation.
+Part 1 of this example shows partitioning, and uses only the region
+called `EmployeeRegion`.
+Part 2 of this example shows what can go wrong with a partitioned
+region if a bad hashing function is implemented for the region keys.
+It uses only the region called `BadEmployeeRegion`.
+
+## Part 1: Partitioning
+1. Set directory ```geode-examples/partitioned``` to be the
+current working directory.
+Each step in this example specifies paths relative to that directory.
+
+1. Build the jar (with the ```EmployeeKey```, ```BadEmployeeKey```, 
+and ```EmployeeData``` classes):
+
+    ```
+    $   ../gradlew build
+    ```
+1. Run a script that starts a locator and two servers.
+The built JAR will be placed onto the classpath when the script 
+starts the servers:
+
+    ```
+    $ scripts/startAll.sh
+    ```
+    Each of the servers hosts both partitioned regions.
+    
+1. Run the producer to put 10 entries into the ```EmployeeRegion```.
+The argument specification identifies the region.
+
+    ```
+    $ ../gradlew run -Pmain=Producer -Pargs=EmployeeRegion
+    ...
+    ... 
+    INFO: Inserted 10 entries in EmployeeRegion.
+    ```
+
+1. There are several ways to see the contents of the region.
+Run the consumer to get and print all 10 entries in the `EmployeeRegion`.
+The argument specification identifies the region.
+
+    ```
+    $ ../gradlew run -Pmain=Consumer -Pargs=EmployeeRegion
+    ...
+    INFO: 10 entries in EmployeeRegion on the server(s).
+    ...
+    ```
+
+    To see contents of the region keys, use a ```gfsh``` query:
+
+    ```
+    $ $GEODE_HOME/bin/gfsh
+    ...
+    gfsh>connect
+    gfsh>query --query="select e.key from /EmployeeRegion.entries e"
+    ...
+    ```
+
+
+    Or, to see contents of the region values, use a ```gfsh``` query:
+
+    ```
+    gfsh>query --query="select * from /EmployeeRegion"
+    ...
+    ```
+
+    Note that the quantity of entries may also be observed with `gfsh`:
+ 
+    ```
+    gfsh>describe region --name=EmployeeRegion
+    ..........................................................
+    Name            : EmployeeRegion
+    Data Policy     : partition
+    Hosting Members : server2
+                      server1
+
+    Non-Default Attributes Shared By Hosting Members  
+
+     Type  |    Name     | Value
+    ------ | ----------- | ---------
+    Region | size        | 10
+           | data-policy | PARTITION
+    ```
+
+    As an alternative, `gfsh` maybe used to identify how many entries
+    there are for each region on each server by looking at statistics.
+
+    ```
+    gfsh>show metrics --categories=partition --region=/EmployeeRegion --member=server1
+    ```
+
+    Within the output, the result for `totalBucketSize` identifies
+    the number of entries hosted on the specified server.
+    Vary the command to see statistics for both `server1` and `server2`.
+
+1. The region entries are distributed across both servers.
+Kill one of the servers:
+
+    ```
+    $ $GEODE_HOME/bin/gfsh
+    ...
+    gfsh>connect
+    gfsh>stop server --name=server1
+    gfsh>quit
+    ```
+
+1. Run the consumer a second time, and notice that approximately half of
+the entries of the ```EmployeeRegion``` are still available on the 
+remaining server.
+Those hosted by the server that was stopped were lost.
+
+    ```
+    $ ../gradlew run -Pmain=Consumer -Pargs=EmployeeRegion
+    ...
+    ...
+    INFO: 5 entries in EmployeeRegion on the server(s).
+    ...
+    ```
+
+6. Shut down the system:
+
+    ```
+    $ scripts/stopAll.sh
+    ```
+
+## Part 2: What Can Go Wrong
+
+Hashing distributes entries among buckets that reside on servers.
+A good hash code is important in order to spread the entries among buckets.
+
+The ```EmployeeRegion``` used in Part 1 of this example has a good hashing
+function.
+Region entries are well distributed among buckets
+(and therefore, among servers).
+The ```BadEmployeeRegion``` used in this part of the example
+has a pointedly poor hash code implementation,
+to illustrate what can go wrong.
+The hash code is so bad that all entries in the
+```BadEmployeeRegion``` end up in the same bucket.
+Because of this,
+one of the servers will host all the entries,
+while the other server will not have any of the entries.
+
+Here are the steps to run through this example again, 
+using ```BadEmployeeRegion``` instead of ```EmployeeRegion```.
+This part assumes that you have already built the JAR (step 2 in Part 1 
+of this example).
+
+1. Set directory ```geode-examples/partitioned``` to be the
+current working directory.
+Each step in this example specifies paths relative to that directory.
+
+1. The logs for the locator and two servers were not removed as part
+of the system shut down in Part 1.  Part 2 of the example works fine
+without removing the logs, but the `startAll.sh` script output will
+note that the regions are already hosted as the servers are started.
+To start Part 2 from scratch, remove the logs and their directories:
+
+    ```
+    $ rm -r locator1 server1 server2
+    ```
+
+1. Run a script that starts a locator and two servers.
+The built JAR will be placed onto the classpath when the script 
+starts the servers:
+
+    ```
+    $ scripts/startAll.sh
+    ```
+
+1. Run the producer to put 10 entries into the ```BadEmployeeRegion```.
+The argument specification identifies the region.
+
+    ```
+    $ ../gradlew run -Pmain=Producer -Pargs=BadEmployeeRegion
+    ... 
+    INFO: Inserted 10 entries in BadEmployeeRegion.
+    ```
+
+1.  Run the consumer to get and print all 10 entries in the
+`BadEmployeeRegion`.  The argument specification identifies the region.
+Alternatively, use one of the `gfsh` commands 
+(given in Part 1 of this example)
+to verify that the servers are hosting the 10 entries in the region.
+
+    ```
+    $ ../gradlew run -Pmain=Consumer -Pargs=BadEmployeeRegion
+    ...
+    INFO: 10 entries in EmployeeRegion on the server(s).
+    ...
+    ```
+
+1.  Kill one of the servers:
+
+    ```
+    $ $GEODE_HOME/bin/gfsh
+    ...
+    gfsh>connect
+    gfsh>stop server --name=server1
+    gfsh>quit
+    ```
+
+1. Run the consumer or a `gfsh` query, and notice that either all or
+none of the entries of the ```BadEmployeeRegion``` are still available on the 
+remaining server.
+Those hosted by the server that was stopped were lost.
+
+    ```
+    $ ../gradlew run -Pmain=Consumer -Pargs=BadEmployeeRegion
+    ...
+    ...
+    INFO: 5 entries in BadEmployeeRegion on the server(s).
+    ...
+    ```
+
+6. Shut down the system:
+
+    ```
+    $ scripts/stopAll.sh
+    ```
+
+## Partitioning Example Takeaways
+
+1. Partitioned regions distribute region entries across buckets within servers.
+A robust system will use redundancy in conjunction with partitioning
+in production systems,
+so that data is not lost if a server goes down.
+
+2. A proper hashcode is important for distributing entries across buckets.
+Not demonstrated in this example, but a proper equals method is also
+required.
+

http://git-wip-us.apache.org/repos/asf/geode-examples/blob/d551b7c7/partitioned/build.gradle
----------------------------------------------------------------------
diff --git a/partitioned/build.gradle b/partitioned/build.gradle
new file mode 100644
index 0000000..52283ec
--- /dev/null
+++ b/partitioned/build.gradle
@@ -0,0 +1,20 @@
+/*
+ * 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.
+ */
+
+sourceSets.test {
+  resources.srcDirs = ["${projectDir}/scripts"]
+}

http://git-wip-us.apache.org/repos/asf/geode-examples/blob/d551b7c7/partitioned/scripts/.gitignore
----------------------------------------------------------------------
diff --git a/partitioned/scripts/.gitignore b/partitioned/scripts/.gitignore
new file mode 100644
index 0000000..32f8870
--- /dev/null
+++ b/partitioned/scripts/.gitignore
@@ -0,0 +1,2 @@
+locator1/
+server*/

http://git-wip-us.apache.org/repos/asf/geode-examples/blob/d551b7c7/partitioned/scripts/pidkiller.sh
----------------------------------------------------------------------
diff --git a/partitioned/scripts/pidkiller.sh b/partitioned/scripts/pidkiller.sh
new file mode 100755
index 0000000..ecf8f2d
--- /dev/null
+++ b/partitioned/scripts/pidkiller.sh
@@ -0,0 +1,35 @@
+#!/bin/bash
+#
+# 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.
+#
+# @brief Script that look for .pid files on a directory and kill those processes.
+#
+
+export DIR=$1
+
+if [ $# -eq 0 ]
+  then
+    echo "No arguments supplied. Script needs directory to look for pid files."
+    exit 1
+fi
+
+for pid in `find $DIR -name "*.pid"`
+do
+ echo "Found: $pid"
+ kill -9 `cat $pid`
+ echo "Killed."
+done
+

http://git-wip-us.apache.org/repos/asf/geode-examples/blob/d551b7c7/partitioned/scripts/setEnv.sh
----------------------------------------------------------------------
diff --git a/partitioned/scripts/setEnv.sh b/partitioned/scripts/setEnv.sh
new file mode 100755
index 0000000..e9e860e
--- /dev/null
+++ b/partitioned/scripts/setEnv.sh
@@ -0,0 +1,33 @@
+#!/bin/bash
+#
+# 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.
+#
+
+## check if locator port has been set otherwise set to default
+export GEODE_LOCATOR_PORT="${GEODE_LOCATOR_PORT:-10334}"
+
+## check if GEODE_HOME has been set
+: ${GEODE_HOME?"GEODE_HOME enviroment variable needs to be set"}
+
+## check if gfsh script is accessible and print version
+: ${GEODE_HOME/bin/gfsh?"gfsh doesn't seem to be available. Please check $GEODE_HOME"}
+echo "Geode version: `$GEODE_HOME/bin/gfsh version`"
+
+## prefer GEODE_HOME for finding gfsh
+export PATH=$GEODE_HOME/bin:$PATH
+
+
+: ${GEODE_LOCATOR_PORT?}

http://git-wip-us.apache.org/repos/asf/geode-examples/blob/d551b7c7/partitioned/scripts/startAll.sh
----------------------------------------------------------------------
diff --git a/partitioned/scripts/startAll.sh b/partitioned/scripts/startAll.sh
new file mode 100755
index 0000000..165fd44
--- /dev/null
+++ b/partitioned/scripts/startAll.sh
@@ -0,0 +1,48 @@
+#!/bin/bash
+#
+# 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.
+#
+
+set -e
+
+current=`pwd`
+
+cd `dirname $0`
+
+. ./setEnv.sh
+
+cd $current
+
+#export GEODE_LOCATOR_PORT="${GEODE_LOCATOR_PORT:-10334}"
+# start a locator
+gfsh start locator --name=locator1 --mcast-port=0 --port=${GEODE_LOCATOR_PORT}
+
+# start 2 servers on a random available port
+for N in {1..2}
+do
+ gfsh start server --locators=localhost[${GEODE_LOCATOR_PORT}] --name=server$N  --server-port=0 --mcast-port=0 --classpath=${PWD}/build/libs/partitioned-0.1.0-SNAPSHOT.jar
+done
+
+# create 2 regions with the same data using GFSH, one that works well,
+#  good keys, and the other that implements a bad hashCode on the key
+gfsh -e "connect --locator=localhost[${GEODE_LOCATOR_PORT}]" -e "create region --name=EmployeeRegion --type=PARTITION"
+
+gfsh -e "connect --locator=localhost[${GEODE_LOCATOR_PORT}]" -e "create region --name=BadEmployeeRegion --type=PARTITION"
+
+gfsh -e "connect --locator=localhost[${GEODE_LOCATOR_PORT}]" -e "list members"
+
+exit 0
+

http://git-wip-us.apache.org/repos/asf/geode-examples/blob/d551b7c7/partitioned/scripts/stopAll.sh
----------------------------------------------------------------------
diff --git a/partitioned/scripts/stopAll.sh b/partitioned/scripts/stopAll.sh
new file mode 100755
index 0000000..a6364a8
--- /dev/null
+++ b/partitioned/scripts/stopAll.sh
@@ -0,0 +1,28 @@
+#!/bin/bash
+#
+# 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.
+#
+set -e
+
+current=`pwd`
+
+cd `dirname $0`
+
+. ./setEnv.sh
+
+cd $current
+
+gfsh -e "connect --locator=localhost[${GEODE_LOCATOR_PORT}]" -e "shutdown --include-locators=true"

http://git-wip-us.apache.org/repos/asf/geode-examples/blob/d551b7c7/partitioned/src/main/java/org/apache/geode/examples/partitioned/BadEmployeeKey.java
----------------------------------------------------------------------
diff --git a/partitioned/src/main/java/org/apache/geode/examples/partitioned/BadEmployeeKey.java b/partitioned/src/main/java/org/apache/geode/examples/partitioned/BadEmployeeKey.java
new file mode 100644
index 0000000..31c2610
--- /dev/null
+++ b/partitioned/src/main/java/org/apache/geode/examples/partitioned/BadEmployeeKey.java
@@ -0,0 +1,40 @@
+/*
+ * 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.partitioned;
+
+
+public class BadEmployeeKey extends EmployeeKey {
+
+  private static final long serialVersionUID = 1L;
+
+  public BadEmployeeKey() {}
+
+  public BadEmployeeKey(String n, int en) {
+    super(n, en);
+  }
+
+  /*
+   * This hashCode is what makes this class a very poor implementation. It always returns the value
+   * 1, so that every entry gets placed in the same bucket, and partitioning is useless.
+   *
+   * Forgetting to define, or implementing an erroneous hashCode can result in rotten distribution
+   * of region entries across buckets (and therefore, across partitions).
+   */
+  @Override
+  public int hashCode() {
+    return 1;
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/geode-examples/blob/d551b7c7/partitioned/src/main/java/org/apache/geode/examples/partitioned/BaseClient.java
----------------------------------------------------------------------
diff --git a/partitioned/src/main/java/org/apache/geode/examples/partitioned/BaseClient.java b/partitioned/src/main/java/org/apache/geode/examples/partitioned/BaseClient.java
new file mode 100644
index 0000000..5fdc236
--- /dev/null
+++ b/partitioned/src/main/java/org/apache/geode/examples/partitioned/BaseClient.java
@@ -0,0 +1,79 @@
+/*
+ * 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.partitioned;
+
+import java.util.logging.Logger;
+
+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.ClientRegionShortcut;
+
+
+public abstract class BaseClient {
+
+  static final Logger logger = Logger.getAnonymousLogger();
+  protected ClientCache clientCache;
+
+  protected void setRegion1(Region region) {
+    this.region1 = region;
+  }
+
+  protected void setRegion2(Region region) {
+    this.region2 = region;
+  }
+
+  private Region region1;
+  private Region region2;
+  private final String locatorHost = System.getProperty("GEODE_LOCATOR_HOST", "localhost");
+  private final int locatorPort = Integer.getInteger("GEODE_LOCATOR_PORT", 10334);
+  protected static final String REGION1_NAME = "EmployeeRegion";
+  protected static final String REGION2_NAME = "BadEmployeeRegion";
+  static final int NUM_ENTRIES = 10;
+
+  public BaseClient() {
+    this.clientCache = getClientCache();
+  }
+
+  public BaseClient(ClientCache clientCache) {
+    this.clientCache = clientCache;
+  }
+
+  protected Region getRegion1() {
+    if (region1 == null) {
+      region1 = getClientCache()
+          .<EmployeeKey, EmployeeData>createClientRegionFactory(ClientRegionShortcut.PROXY)
+          .create(REGION1_NAME);
+    }
+    return (region1);
+  }
+
+  protected Region getRegion2() {
+    if (region2 == null) {
+      region2 = getClientCache()
+          .<BadEmployeeKey, EmployeeData>createClientRegionFactory(ClientRegionShortcut.PROXY)
+          .create(REGION2_NAME);
+    }
+    return (region2);
+  }
+
+  protected ClientCache getClientCache() {
+    if (clientCache == null) {
+      clientCache = new ClientCacheFactory().addPoolLocator(locatorHost, locatorPort)
+          .set("log-level", "WARN").create();
+    }
+    return clientCache;
+  }
+}

http://git-wip-us.apache.org/repos/asf/geode-examples/blob/d551b7c7/partitioned/src/main/java/org/apache/geode/examples/partitioned/Consumer.java
----------------------------------------------------------------------
diff --git a/partitioned/src/main/java/org/apache/geode/examples/partitioned/Consumer.java b/partitioned/src/main/java/org/apache/geode/examples/partitioned/Consumer.java
new file mode 100644
index 0000000..2e61866
--- /dev/null
+++ b/partitioned/src/main/java/org/apache/geode/examples/partitioned/Consumer.java
@@ -0,0 +1,81 @@
+/*
+ * 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.partitioned;
+
+import java.util.*;
+import org.apache.geode.cache.client.ClientCache;
+import org.apache.geode.cache.Region;
+
+public class Consumer extends BaseClient {
+
+  public static void main(String[] args) {
+    Consumer c = new Consumer();
+    c.checkAndPrint(args);
+  }
+
+  public void checkAndPrint(String[] args) {
+    if (0 == args.length) {
+      throw new RuntimeException("Expected argument specifying region name.");
+    } else {
+      if (args.length > 1) {
+        throw new RuntimeException("Expected only 1 argument, and received more than 1.");
+      } else {
+        if (args[0].equals("EmployeeRegion")) {
+          this.printRegionContents();
+        } else {
+          if (args[0].equals("BadEmployeeRegion")) {
+            this.printBadRegionContents();
+          } else {
+            throw new RuntimeException("Unrecognized region name in argument specification.");
+          }
+        }
+      }
+    }
+  }
+
+  public Consumer() {}
+
+  public Consumer(ClientCache clientCache) {
+    this.clientCache = clientCache;
+  }
+
+
+  public void printRegionContents() {
+    /* Print EmployeeRegion size and contents */
+    Region r1 = this.getRegion1();
+    Set<EmployeeKey> setOfKeys1 = r1.keySetOnServer();
+    logger.info(setOfKeys1.size() + " entries in EmployeeRegion on the server(s).");
+    if (setOfKeys1.size() > 0) {
+      logger.info("Contents of EmployeeRegion:");
+      for (EmployeeKey key : setOfKeys1) {
+        logger.info(r1.get(key).toString());
+      }
+    }
+  }
+
+  public void printBadRegionContents() {
+    /* Print BadEmployeeRegion size and contents */
+    Region r2 = this.getRegion2();
+    Set<EmployeeKey> setOfKeys2 = r2.keySetOnServer();
+    logger.info(setOfKeys2.size() + " entries in BadEmployeeRegion on the server(s).");
+    if (setOfKeys2.size() > 0) {
+      logger.info("Contents of BadEmployeeRegion:");
+      for (EmployeeKey key : setOfKeys2) {
+        logger.info(r2.get(key).toString());
+      }
+    }
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/geode-examples/blob/d551b7c7/partitioned/src/main/java/org/apache/geode/examples/partitioned/EmployeeData.java
----------------------------------------------------------------------
diff --git a/partitioned/src/main/java/org/apache/geode/examples/partitioned/EmployeeData.java b/partitioned/src/main/java/org/apache/geode/examples/partitioned/EmployeeData.java
new file mode 100644
index 0000000..c79962d
--- /dev/null
+++ b/partitioned/src/main/java/org/apache/geode/examples/partitioned/EmployeeData.java
@@ -0,0 +1,60 @@
+/*
+ * 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.partitioned;
+
+import java.io.Serializable;
+
+public class EmployeeData implements Serializable {
+
+  private static final long serialVersionUID = 1L;
+
+  private EmployeeKey nameAndNumber;
+  private int salary;
+  private int hoursPerWeek;
+
+  public EmployeeData() {}
+
+  public EmployeeData(EmployeeKey k, int s, int hrs) {
+    this.nameAndNumber = k;
+    this.salary = s;
+    this.hoursPerWeek = hrs;
+  }
+
+  public EmployeeKey getNameAndNumber() {
+    return (nameAndNumber);
+  }
+
+  public int getSalary() {
+    return (salary);
+  }
+
+  public int getHoursPerWeek() {
+    return (hoursPerWeek);
+  }
+
+  public boolean equals(EmployeeData e) {
+    if (this.nameAndNumber.equals(e.getNameAndNumber()) && (this.salary == e.getSalary())
+        && (this.hoursPerWeek == e.getHoursPerWeek())) {
+      return true;
+    }
+    return false;
+  }
+
+  public String toString() {
+    return (this.nameAndNumber.toString() + " salary=" + this.salary + " hoursPerWeek="
+        + this.hoursPerWeek);
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/geode-examples/blob/d551b7c7/partitioned/src/main/java/org/apache/geode/examples/partitioned/EmployeeKey.java
----------------------------------------------------------------------
diff --git a/partitioned/src/main/java/org/apache/geode/examples/partitioned/EmployeeKey.java b/partitioned/src/main/java/org/apache/geode/examples/partitioned/EmployeeKey.java
new file mode 100644
index 0000000..7581676
--- /dev/null
+++ b/partitioned/src/main/java/org/apache/geode/examples/partitioned/EmployeeKey.java
@@ -0,0 +1,69 @@
+/*
+ * 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.partitioned;
+
+import java.io.Serializable;
+
+public class EmployeeKey implements Serializable {
+
+  private static final long serialVersionUID = 1L;
+
+  private String name;
+  private int emplNumber;
+
+  public EmployeeKey() {}
+
+  public EmployeeKey(String n, int en) {
+    this.name = n;
+    this.emplNumber = en;
+  }
+
+  public String getName() {
+    return (name);
+  }
+
+  public int getEmplNumber() {
+    return (emplNumber);
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (this == o) {
+      return true;
+    }
+    if (o == null || getClass() != o.getClass()) {
+      return false;
+    }
+
+    EmployeeKey that = (EmployeeKey) o;
+
+    if (emplNumber != that.emplNumber) {
+      return false;
+    }
+    return name.equals(that.name);
+  }
+
+  @Override
+  public int hashCode() {
+    int result = name.hashCode();
+    result = 31 * result + emplNumber;
+    return result;
+  }
+
+  public String toString() {
+    return ("Name: " + this.name + " Employee Number: " + this.emplNumber);
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/geode-examples/blob/d551b7c7/partitioned/src/main/java/org/apache/geode/examples/partitioned/Producer.java
----------------------------------------------------------------------
diff --git a/partitioned/src/main/java/org/apache/geode/examples/partitioned/Producer.java b/partitioned/src/main/java/org/apache/geode/examples/partitioned/Producer.java
new file mode 100644
index 0000000..04fcfc1
--- /dev/null
+++ b/partitioned/src/main/java/org/apache/geode/examples/partitioned/Producer.java
@@ -0,0 +1,151 @@
+/*
+ * 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.partitioned;
+
+import org.apache.geode.cache.client.ClientCache;
+import org.apache.geode.cache.Region;
+
+public class Producer extends BaseClient {
+
+  public static void main(String[] args) {
+    Producer p = new Producer();
+    p.checkAndPopulate(args);
+  }
+
+  public void checkAndPopulate(String[] args) {
+    if (0 == args.length) {
+      throw new RuntimeException("Expected argument specifying region name.");
+    } else {
+      if (args.length > 1) {
+        throw new RuntimeException("Expected only 1 argument, and received more than 1.");
+      } else {
+        if (args[0].equals("EmployeeRegion")) {
+          this.populateRegion();
+        } else {
+          if (args[0].equals("BadEmployeeRegion")) {
+            this.populateBadRegion();
+          } else {
+            throw new RuntimeException("Unrecognized region name in argument specification.");
+          }
+        }
+      }
+    }
+  }
+
+  public Producer() {
+    super();
+  }
+
+  public Producer(ClientCache clientCache) {
+    super(clientCache);
+  }
+
+  /* Put 10 entries into the region with the quality hashing function. */
+  public void populateRegion() {
+
+    Region r = getRegion1();
+    EmployeeKey k1 = new EmployeeKey("Alex Able", 160);
+    EmployeeData d1 = new EmployeeData(k1, 70000, 40);
+    r.put(k1, d1);
+
+    EmployeeKey k2 = new EmployeeKey("Bertie Bell", 170);
+    EmployeeData d2 = new EmployeeData(k2, 72000, 40);
+    r.put(k2, d2);
+
+    EmployeeKey k3 = new EmployeeKey("Kris Call", 180);
+    EmployeeData d3 = new EmployeeData(k3, 68000, 40);
+    r.put(k3, d3);
+
+    EmployeeKey k4 = new EmployeeKey("Dale Driver", 190);
+    EmployeeData d4 = new EmployeeData(k4, 81500, 36);
+    r.put(k4, d4);
+
+    EmployeeKey k5 = new EmployeeKey("Frankie Forth", 201);
+    EmployeeData d5 = new EmployeeData(k5, 45000, 40);
+    r.put(k5, d5);
+
+    EmployeeKey k6 = new EmployeeKey("Jamie Jive", 220);
+    EmployeeData d6 = new EmployeeData(k6, 56500, 40);
+    r.put(k6, d6);
+
+    EmployeeKey k7 = new EmployeeKey("Morgan Minnow", 230);
+    EmployeeData d7 = new EmployeeData(k7, 65000, 36);
+    r.put(k7, d7);
+
+    EmployeeKey k8 = new EmployeeKey("Pat Puts", 240);
+    EmployeeData d8 = new EmployeeData(k8, 67000, 40);
+    r.put(k8, d8);
+
+    EmployeeKey k9 = new EmployeeKey("Ricky Reliable", 2500);
+    EmployeeData d9 = new EmployeeData(k9, 71000, 40);
+    r.put(k9, d9);
+
+    EmployeeKey k10 = new EmployeeKey("Taylor Tack", 260);
+    EmployeeData d10 = new EmployeeData(k10, 70000, 40);
+    r.put(k10, d10);
+
+    logger.info("Inserted 10 entries in EmployeeRegion.");
+  }
+
+  /*
+   * Put 10 entries into the region with the bad hashing function. The entries are the same as those
+   * put into the region with the quality hashing function.
+   */
+  public void populateBadRegion() {
+
+    Region r = getRegion2();
+    BadEmployeeKey k1 = new BadEmployeeKey("Alex Able", 160);
+    EmployeeData d1 = new EmployeeData(k1, 70000, 40);
+    r.put(k1, d1);
+
+    BadEmployeeKey k2 = new BadEmployeeKey("Bertie Bell", 170);
+    EmployeeData d2 = new EmployeeData(k2, 72000, 40);
+    r.put(k2, d2);
+
+    BadEmployeeKey k3 = new BadEmployeeKey("Kris Call", 180);
+    EmployeeData d3 = new EmployeeData(k3, 68000, 40);
+    r.put(k3, d3);
+
+    BadEmployeeKey k4 = new BadEmployeeKey("Dale Driver", 190);
+    EmployeeData d4 = new EmployeeData(k4, 81500, 36);
+    r.put(k4, d4);
+
+    BadEmployeeKey k5 = new BadEmployeeKey("Frankie Forth", 201);
+    EmployeeData d5 = new EmployeeData(k5, 45000, 40);
+    r.put(k5, d5);
+
+    BadEmployeeKey k6 = new BadEmployeeKey("Jamie Jive", 220);
+    EmployeeData d6 = new EmployeeData(k6, 56500, 40);
+    r.put(k6, d6);
+
+    BadEmployeeKey k7 = new BadEmployeeKey("Morgan Minnow", 230);
+    EmployeeData d7 = new EmployeeData(k7, 65000, 36);
+    r.put(k7, d7);
+
+    BadEmployeeKey k8 = new BadEmployeeKey("Pat Puts", 240);
+    EmployeeData d8 = new EmployeeData(k8, 67000, 40);
+    r.put(k8, d8);
+
+    BadEmployeeKey k9 = new BadEmployeeKey("Ricky Reliable", 2500);
+    EmployeeData d9 = new EmployeeData(k9, 71000, 40);
+    r.put(k9, d9);
+
+    BadEmployeeKey k10 = new BadEmployeeKey("Taylor Tack", 260);
+    EmployeeData d10 = new EmployeeData(k10, 70000, 40);
+    r.put(k10, d10);
+
+    logger.info("Inserted 10 entries in BadEmployeeRegion.");
+  }
+}

http://git-wip-us.apache.org/repos/asf/geode-examples/blob/d551b7c7/partitioned/src/main/main2.iml
----------------------------------------------------------------------
diff --git a/partitioned/src/main/main2.iml b/partitioned/src/main/main2.iml
new file mode 100644
index 0000000..19dbd15
--- /dev/null
+++ b/partitioned/src/main/main2.iml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module version="4">
+  <component name="NewModuleRootManager" inherit-compiler-output="false">
+    <orderEntry type="sourceFolder" forTests="false" />
+  </component>
+</module>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/geode-examples/blob/d551b7c7/partitioned/src/test/java/org/apache/geode/examples/partitioned/BadEmployeeKeyTest.java
----------------------------------------------------------------------
diff --git a/partitioned/src/test/java/org/apache/geode/examples/partitioned/BadEmployeeKeyTest.java b/partitioned/src/test/java/org/apache/geode/examples/partitioned/BadEmployeeKeyTest.java
new file mode 100644
index 0000000..dacd669
--- /dev/null
+++ b/partitioned/src/test/java/org/apache/geode/examples/partitioned/BadEmployeeKeyTest.java
@@ -0,0 +1,41 @@
+/*
+ * 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.partitioned;
+
+import static org.junit.Assert.*;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+
+public class BadEmployeeKeyTest {
+
+  private BadEmployeeKey k;
+
+  @Rule
+  public ExpectedException expectedException = ExpectedException.none();
+
+  @Before
+  public void setup() {
+    k = new BadEmployeeKey("First Last", 3001);
+  }
+
+  @Test
+  public void testHashCode() {
+    assertEquals(1, k.hashCode());
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/geode-examples/blob/d551b7c7/partitioned/src/test/java/org/apache/geode/examples/partitioned/ConsumerTest.java
----------------------------------------------------------------------
diff --git a/partitioned/src/test/java/org/apache/geode/examples/partitioned/ConsumerTest.java b/partitioned/src/test/java/org/apache/geode/examples/partitioned/ConsumerTest.java
new file mode 100644
index 0000000..e6a550e
--- /dev/null
+++ b/partitioned/src/test/java/org/apache/geode/examples/partitioned/ConsumerTest.java
@@ -0,0 +1,164 @@
+/*
+ * 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.partitioned;
+
+import static org.junit.Assert.*;
+import static org.mockito.Mockito.*;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.geode.cache.client.ClientRegionFactory;
+import org.apache.geode.cache.client.ClientRegionShortcut;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+
+import org.apache.geode.cache.Region;
+import org.apache.geode.cache.client.ClientCache;
+
+public class ConsumerTest {
+
+  @Rule
+  public ExpectedException expectedException = ExpectedException.none();
+
+  private Consumer consumer;
+  private ClientCache clientCache = mock(ClientCache.class);
+  private Region region1 = mock(Region.class);
+  private Region region2 = mock(Region.class);
+  private ClientRegionFactory clientRegionFactory = mock(ClientRegionFactory.class);
+  private Set keys = mock(Set.class);
+  private static final String[] EMPTYARGS = new String[0];
+  private static final String[] GOODARGS1 = {"EmployeeRegion"};
+  private static final String[] GOODARGS2 = {"BadEmployeeRegion"};
+  private static final String[] BADARGS1 = {""};
+  private static final String[] BADARGS2 = {"BadRegionName"};
+  private static final String[] BADARGS3 = {"BadEmployeeRegion", "2"};
+
+  @Before
+  public void setup() {
+    when(region1.getName()).thenReturn(Consumer.REGION1_NAME);
+    when(region2.getName()).thenReturn(Consumer.REGION2_NAME);
+    when(keys.size()).thenReturn(Consumer.NUM_ENTRIES);
+    when(clientCache.createClientRegionFactory(ClientRegionShortcut.PROXY))
+        .thenReturn(clientRegionFactory);
+    when(clientRegionFactory.create(Consumer.REGION1_NAME)).thenReturn(region1);
+    when(clientRegionFactory.create(Consumer.REGION2_NAME)).thenReturn(region2);
+
+    /* Make a small map that will provide values as a region would */
+    Map<EmployeeKey, EmployeeData> emplMap = new HashMap<>();
+    EmployeeKey k1 = new EmployeeKey("Bertie Bell", 170);
+    EmployeeData d1 = new EmployeeData(k1, 72000, 40);
+    emplMap.put(k1, d1);
+    EmployeeKey k2 = new EmployeeKey("Toni Tiptoe", 180);
+    EmployeeData d2 = new EmployeeData(k2, 70000, 40);
+    emplMap.put(k2, d2);
+    /* Use HashMap as fake region for keySetOnServer, size, and get methods */
+    when(region1.keySetOnServer()).thenReturn(emplMap.keySet());
+    when(region1.size()).thenReturn(emplMap.size());
+    when(region1.get(eq(k1))).thenReturn(emplMap.get(k1));
+    when(region1.get(eq(k2))).thenReturn(emplMap.get(k2));
+
+    Map<BadEmployeeKey, EmployeeData> badEmplMap = new HashMap<>();
+    BadEmployeeKey bk1 = new BadEmployeeKey("Bertie Bell", 170);
+    EmployeeData bd1 = new EmployeeData(bk1, 72000, 40);
+    badEmplMap.put(bk1, bd1);
+    BadEmployeeKey bk2 = new BadEmployeeKey("Toni Tiptoe", 180);
+    EmployeeData bd2 = new EmployeeData(bk2, 70000, 40);
+    badEmplMap.put(bk2, bd2);
+    /* Use HashMap as fake region for keySetOnServer, size, and get methods */
+    when(region2.keySetOnServer()).thenReturn(badEmplMap.keySet());
+    when(region2.size()).thenReturn(badEmplMap.size());
+    when(region2.get(eq(bk1))).thenReturn(badEmplMap.get(bk1));
+    when(region2.get(eq(bk2))).thenReturn(badEmplMap.get(bk2));
+
+    consumer = new Consumer(clientCache);
+  }
+
+  @Test
+  public void numberOfEntriesShouldBeGreaterThanZero() throws Exception {
+    assertTrue(consumer.NUM_ENTRIES > 0);
+  }
+
+  @Test
+  public void testConsumerGetRegion1() {
+    assertEquals("Region names do not match", Consumer.REGION1_NAME,
+        consumer.getRegion1().getName());
+  }
+
+  @Test
+  public void testConsumerGetRegion2() {
+    assertEquals("Region names do not match", Consumer.REGION2_NAME,
+        consumer.getRegion2().getName());
+  }
+
+  @Test
+  public void testPrintRegionContents() {
+    consumer.printRegionContents();
+  }
+
+  @Test
+  public void testPrintBadRegionContents() {
+    consumer.printBadRegionContents();
+  }
+
+  @Test
+  public void testCheckAndPrint1() {
+    /* no exception expected */
+    consumer.checkAndPrint(GOODARGS1);
+  }
+
+  @Test
+  public void testCheckAndPrint2() {
+    /* no exception expected */
+    consumer.checkAndPrint(GOODARGS2);
+  }
+
+  @Test
+  public void testCheckAndPrint3() {
+    /* no arguments specified, array length should be 0 */
+    expectedException.expect(Exception.class);
+    expectedException.expectMessage("Expected argument specifying region name.");
+    spy(Consumer.class).checkAndPrint(EMPTYARGS);
+  }
+
+  @Test
+  public void testCheckAndPrint4() {
+    /* First argument is the empty string */
+    expectedException.expect(Exception.class);
+    expectedException.expectMessage("Unrecognized region name in argument specification.");
+    spy(Consumer.class).checkAndPrint(BADARGS1);
+  }
+
+  @Test
+  public void testCheckAndPrint5() {
+    /* Arguments are an invalid region name */
+    expectedException.expect(Exception.class);
+    expectedException.expectMessage("Unrecognized region name in argument specification.");
+    spy(Consumer.class).checkAndPrint(BADARGS2);
+  }
+
+  @Test
+  public void testCheckAndPrint6() {
+    /* Arguments are an invalid region name and an extra but unused argument */
+    expectedException.expect(Exception.class);
+    expectedException.expectMessage("Expected only 1 argument, and received more than 1.");
+    spy(Consumer.class).checkAndPrint(BADARGS3);
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/geode-examples/blob/d551b7c7/partitioned/src/test/java/org/apache/geode/examples/partitioned/EmployeeDataTest.java
----------------------------------------------------------------------
diff --git a/partitioned/src/test/java/org/apache/geode/examples/partitioned/EmployeeDataTest.java b/partitioned/src/test/java/org/apache/geode/examples/partitioned/EmployeeDataTest.java
new file mode 100644
index 0000000..2ec546c
--- /dev/null
+++ b/partitioned/src/test/java/org/apache/geode/examples/partitioned/EmployeeDataTest.java
@@ -0,0 +1,70 @@
+/*
+ * 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.partitioned;
+
+import static org.junit.Assert.*;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+
+/**
+ * Created by kmiller on 1/31/17.
+ */
+public class EmployeeDataTest {
+  private EmployeeData d;
+
+  @Rule
+  public ExpectedException expectedException = ExpectedException.none();
+
+  @Before
+  public void setup() {
+    EmployeeKey k = new EmployeeKey("First Last", 3001);
+    d = new EmployeeData(k, 40000, 38);
+  }
+
+  @Test
+  public void testGetNameAndNumber() {
+    assertEquals("First Last", d.getNameAndNumber().getName());
+    assertEquals(3001, d.getNameAndNumber().getEmplNumber());
+  }
+
+  @Test
+  public void testGetSalary() {
+    assertEquals(40000, d.getSalary());
+  }
+
+  @Test
+  public void testGetHoursPerWeek() {
+    assertEquals(38, d.getHoursPerWeek());
+  }
+
+  @Test
+  public void testEquals() {
+    EmployeeKey otherKey = new EmployeeKey("First Last", 3001);
+    EmployeeData otherData = new EmployeeData(otherKey, 40000, 38);
+    assertTrue(d.equals(otherData));
+    EmployeeKey nonMatchingKey = new EmployeeKey("Othername", 1);
+    EmployeeData nonMatchingData = new EmployeeData(nonMatchingKey, 39999, 40);
+    assertFalse(d.equals(nonMatchingData));
+  }
+
+  @Test
+  public void testToString() {
+    assertEquals(d.getNameAndNumber().toString() + " salary=40000 hoursPerWeek=38", d.toString());
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/geode-examples/blob/d551b7c7/partitioned/src/test/java/org/apache/geode/examples/partitioned/EmployeeKeyTest.java
----------------------------------------------------------------------
diff --git a/partitioned/src/test/java/org/apache/geode/examples/partitioned/EmployeeKeyTest.java b/partitioned/src/test/java/org/apache/geode/examples/partitioned/EmployeeKeyTest.java
new file mode 100644
index 0000000..2ce499f
--- /dev/null
+++ b/partitioned/src/test/java/org/apache/geode/examples/partitioned/EmployeeKeyTest.java
@@ -0,0 +1,61 @@
+/*
+ * 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.partitioned;
+
+import static org.junit.Assert.*;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+
+/**
+ * Created by kmiller on 1/30/17.
+ */
+public class EmployeeKeyTest {
+
+  private EmployeeKey k;
+
+  @Rule
+  public ExpectedException expectedException = ExpectedException.none();
+
+  @Before
+  public void setup() {
+    k = new EmployeeKey("First Last", 3001);
+  }
+
+  @Test
+  public void testGetName() {
+    assertEquals("First Last", k.getName());
+  }
+
+  @Test
+  public void testGetEmplNumber() {
+    assertEquals(3001, k.getEmplNumber());
+  }
+
+  @Test
+  public void testEquals() {
+    EmployeeKey otherKey = new EmployeeKey("First Last", 3001);
+    assertTrue(k.equals(otherKey));
+    EmployeeKey nonMatchingKey = new EmployeeKey("Othername", 1);
+    assertFalse(k.equals(nonMatchingKey));
+  }
+
+  @Test
+  public void testToString() {
+    assertEquals("Name: First Last Employee Number: 3001", k.toString());
+  }
+}

http://git-wip-us.apache.org/repos/asf/geode-examples/blob/d551b7c7/partitioned/src/test/java/org/apache/geode/examples/partitioned/PartitionedTest.java
----------------------------------------------------------------------
diff --git a/partitioned/src/test/java/org/apache/geode/examples/partitioned/PartitionedTest.java b/partitioned/src/test/java/org/apache/geode/examples/partitioned/PartitionedTest.java
new file mode 100644
index 0000000..4d41a5b
--- /dev/null
+++ b/partitioned/src/test/java/org/apache/geode/examples/partitioned/PartitionedTest.java
@@ -0,0 +1,165 @@
+/*
+ * 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.partitioned;
+
+import static org.hamcrest.core.Is.*;
+import static org.junit.Assert.*;
+import static org.junit.Assume.*;
+
+import java.io.IOException;
+import java.net.ServerSocket;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import org.apache.commons.exec.CommandLine;
+import org.apache.commons.exec.DefaultExecuteResultHandler;
+import org.apache.commons.exec.ExecuteException;
+import org.apache.commons.exec.environment.EnvironmentUtils;
+import org.apache.geode.examples.utils.ShellUtil;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+
+/**
+ * Tests for the shell scripts of the partitioned example
+ */
+public class PartitionedTest {
+
+  // TODO: parameterize
+  public static final String GEODE_LOCATOR_PORT = "GEODE_LOCATOR_PORT=";
+  private static final String startScriptFileName = "startAll.sh";
+  private static final String stopScriptFileName = "stopAll.sh";
+  private static final String pidkillerScriptFileName = "pidkiller.sh";
+  private boolean processRunning = false;
+  private ShellUtil shell = new ShellUtil();
+  private final long scriptTimeout = TimeUnit.SECONDS.toMillis(120);
+  private static final Logger logger = Logger.getAnonymousLogger();
+
+  @Rule
+  public TemporaryFolder testFolder = new TemporaryFolder();
+
+  private int locatorPort;
+  private Map environment;
+
+  @Before
+  public void setup() throws IOException {
+    // ignores test if running on windows
+    assumeThat(System.getProperty("os.name").startsWith("Windows"), is(false));
+
+    locatorPort = getAvailablePort();
+    environment = EnvironmentUtils.getProcEnvironment();
+    EnvironmentUtils.addVariableToEnvironment(environment, GEODE_LOCATOR_PORT + locatorPort);
+    logger.fine("Locator port: " + locatorPort);
+  }
+
+  @Test
+  public void checkIfScriptsExistsAndAreExecutable() throws IOException {
+    assertTrue(
+        shell.getFileFromClassLoader(startScriptFileName).map(x -> x.isFile()).orElse(false));
+    assertTrue(shell.getFileFromClassLoader(stopScriptFileName).map(x -> x.isFile()).orElse(false));
+  }
+
+  @Test
+  public void executeStartThenStopScript() throws InterruptedException, IOException {
+    final int exitCodeStart = executeScript(startScriptFileName);
+    assertEquals(0, exitCodeStart);
+
+    final int exitCodeStop = executeScript(stopScriptFileName);
+    assertEquals(0, exitCodeStop);
+  }
+
+  @Test
+  public void failToStopWhenNoServersAreRunning() throws InterruptedException, IOException {
+    final int exitCode;
+
+    exitCode = executeScript(stopScriptFileName);
+    assertEquals(1, exitCode);
+  }
+
+  /**
+   * Execute the kill script that looks for pid files
+   * 
+   * @throws IOException
+   * @throws InterruptedException
+   */
+  private void runKillScript() throws IOException, InterruptedException {
+    CommandLine cmdLine = CommandLine.parse(shell.getFileFromClassLoader(pidkillerScriptFileName)
+        .map(x -> x.getAbsolutePath()).orElseThrow(IllegalArgumentException::new));
+    cmdLine.addArgument(testFolder.getRoot().getAbsolutePath());
+
+    DefaultExecuteResultHandler resultHandler =
+        shell.execute(cmdLine, scriptTimeout, environment, testFolder.getRoot());
+    resultHandler.waitFor(scriptTimeout);
+  }
+
+  /**
+   * Given a script file name, runs the script and return the exit code. If exitCode != 0 extract
+   * and prints exception.
+   * 
+   * @param scriptName
+   * @return <code>int</code> with exitCode
+   * @throws IOException
+   * @throws InterruptedException
+   */
+  private int executeScript(String scriptName) throws IOException, InterruptedException {
+    final int exitCode;
+    DefaultExecuteResultHandler resultHandler =
+        shell.execute(scriptName, scriptTimeout, environment, testFolder.getRoot());
+    processRunning = true;
+    resultHandler.waitFor();
+
+    logger.finest(String.format("Executing %s...", scriptName));
+    exitCode = resultHandler.getExitValue();
+
+    // extract and log exception if any happened
+    if (exitCode != 0) {
+      ExecuteException executeException = resultHandler.getException();
+      logger.log(Level.SEVERE, executeException.getMessage(), executeException);
+    }
+    return exitCode;
+  }
+
+  @After
+  public void tearDown() {
+    if (processRunning) {
+      try {
+        runKillScript();
+      } catch (IOException | InterruptedException e) {
+        e.printStackTrace();
+      }
+    }
+  }
+
+  /**
+   * Get a random available port
+   * 
+   * @return <code>int</code> port number
+   */
+  private static int getAvailablePort() {
+    try (ServerSocket socket = new ServerSocket(0)) {
+      int port = socket.getLocalPort();
+      socket.close();
+      return port;
+    } catch (IOException ioex) {
+      logger.log(Level.SEVERE, ioex.getMessage(), ioex);
+    }
+    throw new IllegalStateException("No TCP/IP ports available.");
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/geode-examples/blob/d551b7c7/partitioned/src/test/java/org/apache/geode/examples/partitioned/ProducerTest.java
----------------------------------------------------------------------
diff --git a/partitioned/src/test/java/org/apache/geode/examples/partitioned/ProducerTest.java b/partitioned/src/test/java/org/apache/geode/examples/partitioned/ProducerTest.java
new file mode 100644
index 0000000..350d96d
--- /dev/null
+++ b/partitioned/src/test/java/org/apache/geode/examples/partitioned/ProducerTest.java
@@ -0,0 +1,132 @@
+/*
+ * 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.partitioned;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.*;
+
+import java.util.Set;
+
+import org.apache.geode.cache.client.ClientRegionFactory;
+import org.apache.geode.cache.client.ClientRegionShortcut;
+import static org.junit.Assert.*;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+
+import org.apache.geode.cache.Region;
+import org.apache.geode.cache.client.ClientCache;
+
+public class ProducerTest {
+
+  @Rule
+  public ExpectedException expectedException = ExpectedException.none();
+
+  private Producer producer;
+  private Producer pMock = mock(Producer.class);
+  private ClientCache clientCache = mock(ClientCache.class);
+  private Region<EmployeeKey, EmployeeData> region1 = mock(Region.class);
+  private Region<BadEmployeeKey, EmployeeData> region2 = mock(Region.class);
+  private ClientRegionFactory clientRegionFactory = mock(ClientRegionFactory.class);
+  private Set keys = mock(Set.class);
+  private static final String[] EMPTYARGS = new String[0];
+  private static final String[] GOODARGS1 = {"EmployeeRegion"};
+  private static final String[] GOODARGS2 = {"BadEmployeeRegion"};
+  private static final String[] BADARGS1 = {""};
+  private static final String[] BADARGS2 = {"BadRegionName"};
+  private static final String[] BADARGS3 = {"BadEmployeeRegion", "2"};
+
+
+  @Before
+  public void setup() {
+    when(region1.getName()).thenReturn(Producer.REGION1_NAME);
+    when(region2.getName()).thenReturn(Producer.REGION2_NAME);
+    when(keys.size()).thenReturn(Producer.NUM_ENTRIES);
+    when(clientCache.createClientRegionFactory(ClientRegionShortcut.PROXY))
+        .thenReturn(clientRegionFactory);
+    when(clientRegionFactory.create(Consumer.REGION1_NAME)).thenReturn(region1);
+    when(clientRegionFactory.create(Consumer.REGION2_NAME)).thenReturn(region2);
+    doNothing().when(pMock).populateRegion();
+
+  }
+
+
+  @Test
+  public void testPopulateRegion() {
+    producer = new Producer(clientCache);
+    producer.populateRegion();
+    verify(region1, times(10)).put(any(), any());
+
+  }
+
+  @Test
+  public void testPopulateBadRegion() {
+    producer = new Producer(clientCache);
+    producer.populateBadRegion();
+    verify(region2, times(10)).put(any(), any());
+
+  }
+
+  @Test
+  public void testCheckAndPopulate1() {
+    /* no exception expected */
+    pMock.checkAndPopulate(GOODARGS1);
+  }
+
+  @Test
+  public void testCheckAndPopulate2() {
+    /* no exception expected */
+    pMock.checkAndPopulate(GOODARGS2);
+  }
+
+  @Test
+  public void testCheckAndPopulate3() {
+    /* no arguments specified, array length should be 0 */
+    expectedException.expect(Exception.class);
+    expectedException.expectMessage("Expected argument specifying region name.");
+    spy(Producer.class).checkAndPopulate(EMPTYARGS);
+  }
+
+  @Test
+  public void testCheckAndPopulate4() {
+    /* First argument is the empty string */
+    expectedException.expect(Exception.class);
+    expectedException.expectMessage("Unrecognized region name in argument specification.");
+    spy(Producer.class).checkAndPopulate(BADARGS1);
+  }
+
+  @Test
+  public void testCheckAndPopulate5() {
+    /* Arguments are an invalid region name */
+    expectedException.expect(Exception.class);
+    expectedException.expectMessage("Unrecognized region name in argument specification.");
+    spy(Producer.class).checkAndPopulate(BADARGS2);
+  }
+
+  @Test
+  public void testCheckAndPopulate6() {
+    /* Arguments are an invalid region name and an extra but unused argument */
+    expectedException.expect(Exception.class);
+    expectedException.expectMessage("Expected only 1 argument, and received more than 1.");
+    spy(Producer.class).checkAndPopulate(BADARGS3);
+  }
+
+  @After
+  public void tearDown() {
+
+  }
+}

http://git-wip-us.apache.org/repos/asf/geode-examples/blob/d551b7c7/partitioned/src/test/test5.iml
----------------------------------------------------------------------
diff --git a/partitioned/src/test/test5.iml b/partitioned/src/test/test5.iml
new file mode 100644
index 0000000..19dbd15
--- /dev/null
+++ b/partitioned/src/test/test5.iml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module version="4">
+  <component name="NewModuleRootManager" inherit-compiler-output="false">
+    <orderEntry type="sourceFolder" forTests="false" />
+  </component>
+</module>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/geode-examples/blob/d551b7c7/replicated/src/test/java/org/apache/geode/examples/replicated/ReplicatedTest.java
----------------------------------------------------------------------
diff --git a/replicated/src/test/java/org/apache/geode/examples/replicated/ReplicatedTest.java b/replicated/src/test/java/org/apache/geode/examples/replicated/ReplicatedTest.java
index 91b4d33..927af49 100644
--- a/replicated/src/test/java/org/apache/geode/examples/replicated/ReplicatedTest.java
+++ b/replicated/src/test/java/org/apache/geode/examples/replicated/ReplicatedTest.java
@@ -29,7 +29,7 @@ import org.apache.commons.exec.CommandLine;
 import org.apache.commons.exec.DefaultExecuteResultHandler;
 import org.apache.commons.exec.ExecuteException;
 import org.apache.commons.exec.environment.EnvironmentUtils;
-import org.apache.geode.example.utils.ShellUtil;
+import org.apache.geode.examples.utils.ShellUtil;
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Rule;

http://git-wip-us.apache.org/repos/asf/geode-examples/blob/d551b7c7/settings.gradle
----------------------------------------------------------------------
diff --git a/settings.gradle b/settings.gradle
index 432a8eb..9af877b 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -17,5 +17,6 @@
 rootProject.name = 'geode-examples'
 
 include 'replicated'
+include 'partitioned'
 include 'utils'
 

http://git-wip-us.apache.org/repos/asf/geode-examples/blob/d551b7c7/utils/src/main/java/org/apache/geode/example/utils/ShellUtil.java
----------------------------------------------------------------------
diff --git a/utils/src/main/java/org/apache/geode/example/utils/ShellUtil.java b/utils/src/main/java/org/apache/geode/example/utils/ShellUtil.java
deleted file mode 100644
index c123854..0000000
--- a/utils/src/main/java/org/apache/geode/example/utils/ShellUtil.java
+++ /dev/null
@@ -1,106 +0,0 @@
-/*
- * 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.example.utils;
-
-import java.io.File;
-import java.io.IOException;
-import java.net.URL;
-import java.util.Map;
-import java.util.Optional;
-
-import org.apache.commons.exec.CommandLine;
-import org.apache.commons.exec.DefaultExecuteResultHandler;
-import org.apache.commons.exec.DefaultExecutor;
-import org.apache.commons.exec.ExecuteWatchdog;
-import org.apache.commons.exec.PumpStreamHandler;
-import org.apache.commons.exec.ShutdownHookProcessDestroyer;
-
-/**
- * Utility class for executing shell commands using Apache commons-exec
- */
-public class ShellUtil {
-
-  private final ClassLoader classLoader = getClass().getClassLoader();
-
-  public Optional<File> getFileFromClassLoader(String fileName) {
-    URL resourceURL = classLoader.getResource(fileName);
-    return (resourceURL == null) ? Optional.empty() : Optional.of(new File(resourceURL.getFile()));
-  }
-
-  public CommandLine parseCommandLine(String fileName) {
-    return getFileFromClassLoader(fileName).map(file -> CommandLine.parse(file.getAbsolutePath()))
-        .orElseThrow(IllegalArgumentException::new);
-  }
-
-  public DefaultExecuteResultHandler execute(CommandLine cmdLine, long timeout, Map environment,
-      File dir) throws IOException {
-    ExecutorTemplate exampleTestExecutor = new ExecutorTemplate(timeout, dir).invoke();
-    DefaultExecutor executor = exampleTestExecutor.getExecutor();
-    DefaultExecuteResultHandler resultHandler = exampleTestExecutor.getResultHandler();
-    executor.execute(cmdLine, environment, resultHandler);
-
-    return resultHandler;
-
-  }
-
-  public DefaultExecuteResultHandler execute(String fileName, long timeout, Map environment,
-      File dir) throws IOException {
-    ExecutorTemplate exampleTestExecutor = new ExecutorTemplate(timeout, dir).invoke();
-    DefaultExecutor executor = exampleTestExecutor.getExecutor();
-    DefaultExecuteResultHandler resultHandler = exampleTestExecutor.getResultHandler();
-    executor.execute(parseCommandLine(fileName), environment, resultHandler);
-
-    return resultHandler;
-  }
-
-  /**
-   * Executor template for common scenarios
-   */
-  private static class ExecutorTemplate {
-
-    private final long timeout;
-    private final File dir;
-    private DefaultExecutor executor;
-    private DefaultExecuteResultHandler resultHandler;
-
-    public ExecutorTemplate(final long timeout, final File dir) {
-      this.timeout = timeout;
-      this.dir = dir;
-    }
-
-    public DefaultExecutor getExecutor() {
-      return executor;
-    }
-
-    public DefaultExecuteResultHandler getResultHandler() {
-      return resultHandler;
-    }
-
-    public ExecutorTemplate invoke() {
-      executor = new DefaultExecutor();
-      ExecuteWatchdog watchdog = new ExecuteWatchdog(timeout);
-      executor.setWatchdog(watchdog);
-
-      PumpStreamHandler psh = new PumpStreamHandler(System.out, System.err);
-      executor.setProcessDestroyer(new ShutdownHookProcessDestroyer());
-      executor.setStreamHandler(psh);
-      executor.setWorkingDirectory(dir);
-
-      resultHandler = new DefaultExecuteResultHandler();
-      return this;
-    }
-  }
-}

http://git-wip-us.apache.org/repos/asf/geode-examples/blob/d551b7c7/utils/src/main/java/org/apache/geode/examples/utils/ShellUtil.java
----------------------------------------------------------------------
diff --git a/utils/src/main/java/org/apache/geode/examples/utils/ShellUtil.java b/utils/src/main/java/org/apache/geode/examples/utils/ShellUtil.java
new file mode 100644
index 0000000..f02b961
--- /dev/null
+++ b/utils/src/main/java/org/apache/geode/examples/utils/ShellUtil.java
@@ -0,0 +1,106 @@
+/*
+ * 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.utils;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.URL;
+import java.util.Map;
+import java.util.Optional;
+
+import org.apache.commons.exec.CommandLine;
+import org.apache.commons.exec.DefaultExecuteResultHandler;
+import org.apache.commons.exec.DefaultExecutor;
+import org.apache.commons.exec.ExecuteWatchdog;
+import org.apache.commons.exec.PumpStreamHandler;
+import org.apache.commons.exec.ShutdownHookProcessDestroyer;
+
+/**
+ * Utility class for executing shell commands using Apache commons-exec
+ */
+public class ShellUtil {
+
+  private final ClassLoader classLoader = getClass().getClassLoader();
+
+  public Optional<File> getFileFromClassLoader(String fileName) {
+    URL resourceURL = classLoader.getResource(fileName);
+    return (resourceURL == null) ? Optional.empty() : Optional.of(new File(resourceURL.getFile()));
+  }
+
+  public CommandLine parseCommandLine(String fileName) {
+    return getFileFromClassLoader(fileName).map(file -> CommandLine.parse(file.getAbsolutePath()))
+        .orElseThrow(IllegalArgumentException::new);
+  }
+
+  public DefaultExecuteResultHandler execute(CommandLine cmdLine, long timeout, Map environment,
+      File dir) throws IOException {
+    ExecutorTemplate exampleTestExecutor = new ExecutorTemplate(timeout, dir).invoke();
+    DefaultExecutor executor = exampleTestExecutor.getExecutor();
+    DefaultExecuteResultHandler resultHandler = exampleTestExecutor.getResultHandler();
+    executor.execute(cmdLine, environment, resultHandler);
+
+    return resultHandler;
+
+  }
+
+  public DefaultExecuteResultHandler execute(String fileName, long timeout, Map environment,
+      File dir) throws IOException {
+    ExecutorTemplate exampleTestExecutor = new ExecutorTemplate(timeout, dir).invoke();
+    DefaultExecutor executor = exampleTestExecutor.getExecutor();
+    DefaultExecuteResultHandler resultHandler = exampleTestExecutor.getResultHandler();
+    executor.execute(parseCommandLine(fileName), environment, resultHandler);
+
+    return resultHandler;
+  }
+
+  /**
+   * Executor template for common scenarios
+   */
+  private static class ExecutorTemplate {
+
+    private final long timeout;
+    private final File dir;
+    private DefaultExecutor executor;
+    private DefaultExecuteResultHandler resultHandler;
+
+    public ExecutorTemplate(final long timeout, final File dir) {
+      this.timeout = timeout;
+      this.dir = dir;
+    }
+
+    public DefaultExecutor getExecutor() {
+      return executor;
+    }
+
+    public DefaultExecuteResultHandler getResultHandler() {
+      return resultHandler;
+    }
+
+    public ExecutorTemplate invoke() {
+      executor = new DefaultExecutor();
+      ExecuteWatchdog watchdog = new ExecuteWatchdog(timeout);
+      executor.setWatchdog(watchdog);
+
+      PumpStreamHandler psh = new PumpStreamHandler(System.out, System.err);
+      executor.setProcessDestroyer(new ShutdownHookProcessDestroyer());
+      executor.setStreamHandler(psh);
+      executor.setWorkingDirectory(dir);
+
+      resultHandler = new DefaultExecuteResultHandler();
+      return this;
+    }
+  }
+}


Mime
View raw message