Return-Path: X-Original-To: archive-asf-public-internal@cust-asf2.ponee.io Delivered-To: archive-asf-public-internal@cust-asf2.ponee.io Received: from cust-asf.ponee.io (cust-asf.ponee.io [163.172.22.183]) by cust-asf2.ponee.io (Postfix) with ESMTP id CA420200C24 for ; Thu, 23 Feb 2017 20:07:15 +0100 (CET) Received: by cust-asf.ponee.io (Postfix) id C8B26160B64; Thu, 23 Feb 2017 19:07:15 +0000 (UTC) Delivered-To: archive-asf-public@cust-asf.ponee.io Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by cust-asf.ponee.io (Postfix) with SMTP id 8FC3B160B3E for ; Thu, 23 Feb 2017 20:07:13 +0100 (CET) Received: (qmail 91748 invoked by uid 500); 23 Feb 2017 19:07:12 -0000 Mailing-List: contact commits-help@geode.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@geode.apache.org Delivered-To: mailing list commits@geode.apache.org Received: (qmail 91738 invoked by uid 99); 23 Feb 2017 19:07:12 -0000 Received: from git1-us-west.apache.org (HELO git1-us-west.apache.org) (140.211.11.23) by apache.org (qpsmtpd/0.29) with ESMTP; Thu, 23 Feb 2017 19:07:12 +0000 Received: by git1-us-west.apache.org (ASF Mail Server at git1-us-west.apache.org, from userid 33) id 52730DFC1C; Thu, 23 Feb 2017 19:07:12 +0000 (UTC) Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit From: kmiller@apache.org To: commits@geode.apache.org Message-Id: <3af3d72bb22e4a319bbe8e49bf153cde@git.apache.org> X-Mailer: ASF-Git Admin Mailer Subject: geode-examples git commit: GEODE-2231 A partitioned region example [Forced Update!] Date: Thu, 23 Feb 2017 19:07:12 +0000 (UTC) archived-at: Thu, 23 Feb 2017 19:07:16 -0000 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 Authored: Tue Jan 3 13:47:52 2017 -0800 Committer: Karen Miller 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 @@ + + +# 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() + .createClientRegionFactory(ClientRegionShortcut.PROXY) + .create(REGION1_NAME); + } + return (region1); + } + + protected Region getRegion2() { + if (region2 == null) { + region2 = getClientCache() + .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 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 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 @@ + + + + + + \ 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 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 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 int 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 int 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 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(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 @@ + + + + + + \ 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 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 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; + } + } +}