Return-Path: X-Original-To: apmail-geode-commits-archive@minotaur.apache.org Delivered-To: apmail-geode-commits-archive@minotaur.apache.org Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by minotaur.apache.org (Postfix) with SMTP id 80EA518685 for ; Thu, 25 Feb 2016 23:50:14 +0000 (UTC) Received: (qmail 7012 invoked by uid 500); 25 Feb 2016 23:50:14 -0000 Delivered-To: apmail-geode-commits-archive@geode.apache.org Received: (qmail 6980 invoked by uid 500); 25 Feb 2016 23:50:14 -0000 Mailing-List: contact commits-help@geode.incubator.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@geode.incubator.apache.org Delivered-To: mailing list commits@geode.incubator.apache.org Received: (qmail 6971 invoked by uid 99); 25 Feb 2016 23:50:14 -0000 Received: from pnap-us-west-generic-nat.apache.org (HELO spamd4-us-west.apache.org) (209.188.14.142) by apache.org (qpsmtpd/0.29) with ESMTP; Thu, 25 Feb 2016 23:50:14 +0000 Received: from localhost (localhost [127.0.0.1]) by spamd4-us-west.apache.org (ASF Mail Server at spamd4-us-west.apache.org) with ESMTP id D671BC0D4B for ; Thu, 25 Feb 2016 23:50:13 +0000 (UTC) X-Virus-Scanned: Debian amavisd-new at spamd4-us-west.apache.org X-Spam-Flag: NO X-Spam-Score: -3.549 X-Spam-Level: X-Spam-Status: No, score=-3.549 tagged_above=-999 required=6.31 tests=[KAM_ASCII_DIVIDERS=0.8, KAM_LAZY_DOMAIN_SECURITY=1, RCVD_IN_DNSWL_HI=-5, RCVD_IN_MSPIKE_H3=-0.01, RCVD_IN_MSPIKE_WL=-0.01, RP_MATCHES_RCVD=-0.329] autolearn=disabled Received: from mx2-lw-eu.apache.org ([10.40.0.8]) by localhost (spamd4-us-west.apache.org [10.40.0.11]) (amavisd-new, port 10024) with ESMTP id gYJpneaI5rVn for ; Thu, 25 Feb 2016 23:50:01 +0000 (UTC) Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by mx2-lw-eu.apache.org (ASF Mail Server at mx2-lw-eu.apache.org) with SMTP id 4C68A5FE53 for ; Thu, 25 Feb 2016 23:49:59 +0000 (UTC) Received: (qmail 5886 invoked by uid 99); 25 Feb 2016 23:49:58 -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, 25 Feb 2016 23:49:58 +0000 Received: by git1-us-west.apache.org (ASF Mail Server at git1-us-west.apache.org, from userid 33) id 1EF6FE8F37; Thu, 25 Feb 2016 23:49:58 +0000 (UTC) Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit From: bschuchardt@apache.org To: commits@geode.incubator.apache.org Date: Thu, 25 Feb 2016 23:50:01 -0000 Message-Id: <04c1b55e887140fe9e94d22f17f8d8c2@git.apache.org> In-Reply-To: <316df9edd09b4a6baba6cd102d389853@git.apache.org> References: <316df9edd09b4a6baba6cd102d389853@git.apache.org> X-Mailer: ASF-Git Admin Mailer Subject: [4/5] incubator-geode git commit: adding unit tests for REST API http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/9b55879e/geode-assembly/src/test/java/com/gemstone/gemfire/rest/internal/web/controllers/RestAPIOnRegionFunctionExecutionDUnitTest.java ---------------------------------------------------------------------- diff --git a/geode-assembly/src/test/java/com/gemstone/gemfire/rest/internal/web/controllers/RestAPIOnRegionFunctionExecutionDUnitTest.java b/geode-assembly/src/test/java/com/gemstone/gemfire/rest/internal/web/controllers/RestAPIOnRegionFunctionExecutionDUnitTest.java new file mode 100644 index 0000000..4a958ce --- /dev/null +++ b/geode-assembly/src/test/java/com/gemstone/gemfire/rest/internal/web/controllers/RestAPIOnRegionFunctionExecutionDUnitTest.java @@ -0,0 +1,617 @@ +/* + * 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 com.gemstone.gemfire.rest.internal.web.controllers; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Random; +import java.util.Set; + +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClients; + +import com.gemstone.gemfire.LogWriter; +import com.gemstone.gemfire.cache.AttributesFactory; +import com.gemstone.gemfire.cache.DataPolicy; +import com.gemstone.gemfire.cache.Region; +import com.gemstone.gemfire.cache.RegionAttributes; +import com.gemstone.gemfire.cache.Scope; +import com.gemstone.gemfire.cache.execute.Function; +import com.gemstone.gemfire.cache.execute.FunctionContext; +import com.gemstone.gemfire.cache.execute.FunctionService; +import com.gemstone.gemfire.cache.execute.RegionFunctionContext; +import com.gemstone.gemfire.cache.partition.PartitionRegionHelper; +import com.gemstone.gemfire.distributed.DistributedSystem; +import com.gemstone.gemfire.distributed.internal.InternalDistributedSystem; +import com.gemstone.gemfire.internal.cache.PartitionAttributesImpl; +import com.gemstone.gemfire.internal.cache.PartitionedRegion; +import com.gemstone.gemfire.internal.cache.PartitionedRegionTestHelper; +import com.gemstone.gemfire.internal.cache.functions.DistributedRegionFunction; +import com.gemstone.gemfire.test.dunit.Host; +import com.gemstone.gemfire.test.dunit.IgnoredException; +import com.gemstone.gemfire.test.dunit.SerializableCallable; +import com.gemstone.gemfire.test.dunit.VM; + +/** + * Dunit Test to validate OnRegion function execution with REST APIs + * + * @author Nilkanth Patel + * @since 8.0 + */ + +public class RestAPIOnRegionFunctionExecutionDUnitTest extends RestAPITestBase { + + private static final long serialVersionUID = 1L; + + public static final String REGION_NAME = "DistributedRegionFunctionExecutionDUnitTest"; + + public static final String PR_REGION_NAME = "samplePRRegion"; + + public static Region region = null; + + public static List restURLs = new ArrayList(); + + public static String restEndPoint = null; + + public static String getRestEndPoint() { + return restEndPoint; + } + + public static void setRestEndPoint(String restEndPoint) { + RestAPIOnRegionFunctionExecutionDUnitTest.restEndPoint = restEndPoint; + } + + public static final Function function = new DistributedRegionFunction(); + + public static final Function functionWithNoResultThrowsException = new MyFunctionException(); + + public RestAPIOnRegionFunctionExecutionDUnitTest(String name) { + super(name); + } + + public void setUp() throws Exception { + super.setUp(); + final Host host = Host.getHost(0); + } + + static class FunctionWithNoLastResult implements Function { + private static final long serialVersionUID = -1032915440862585532L; + public static final String Id = "FunctionWithNoLastResult"; + public static int invocationCount; + + @Override + public void execute(FunctionContext context) { + invocationCount++; + InternalDistributedSystem + .getConnectedInstance() + .getLogWriter() + .info( + "did not send last result" + + ""); + context.getResultSender().sendResult( + (Serializable) context.getArguments()); + } + + @Override + public String getId() { + return Id; + } + + @Override + public boolean hasResult() { + return true; + } + + @Override + public boolean optimizeForWrite() { + return false; + } + + @Override + public boolean isHA() { + return false; + } + } + + static class SampleFunction implements Function { + private static final long serialVersionUID = -1032915440862585534L; + public static final String Id = "SampleFunction"; + public static int invocationCount; + + @Override + public void execute(FunctionContext context) { + invocationCount++; + if (context instanceof RegionFunctionContext) { + RegionFunctionContext rfContext = (RegionFunctionContext) context; + rfContext.getDataSet().getCache().getLogger() + .info("Executing function : TestFunction2.execute " + rfContext); + if (rfContext.getArguments() instanceof Boolean) { + /* return rfContext.getArguments(); */ + if (hasResult()) { + rfContext.getResultSender().lastResult( + (Serializable) rfContext.getArguments()); + } else { + rfContext + .getDataSet() + .getCache() + .getLogger() + .info( + "Executing function : TestFunction2.execute " + rfContext); + while (true && !rfContext.getDataSet().isDestroyed()) { + rfContext.getDataSet().getCache().getLogger() + .info("For Bug43513 "); + try { + Thread.sleep(100); + } catch (InterruptedException ie) { + Thread.currentThread().interrupt(); + return; + } + } + } + } else if (rfContext.getArguments() instanceof String) { + String key = (String) rfContext.getArguments(); + if (key.equals("TestingTimeOut")) { // for test + // PRFunctionExecutionDUnitTest#testRemoteMultiKeyExecution_timeout + try { + Thread.sleep(2000); + } catch (InterruptedException e) { + rfContext.getDataSet().getCache().getLogger() + .warning("Got Exception : Thread Interrupted" + e); + } + } + if (PartitionRegionHelper.isPartitionedRegion(rfContext.getDataSet())) { + /* + * return + * (Serializable)PartitionRegionHelper.getLocalDataForContext( + * rfContext).get(key); + */ + rfContext.getResultSender().lastResult( + (Serializable) PartitionRegionHelper.getLocalDataForContext( + rfContext).get(key)); + } else { + rfContext.getResultSender().lastResult( + (Serializable) rfContext.getDataSet().get(key)); + } + /* return (Serializable)rfContext.getDataSet().get(key); */ + } else if (rfContext.getArguments() instanceof Set) { + Set origKeys = (Set) rfContext.getArguments(); + ArrayList vals = new ArrayList(); + for (Object key : origKeys) { + Object val = PartitionRegionHelper + .getLocalDataForContext(rfContext).get(key); + if (val != null) { + vals.add(val); + } + } + rfContext.getResultSender().lastResult(vals); + /* return vals; */ + } else if (rfContext.getArguments() instanceof HashMap) { + HashMap putData = (HashMap) rfContext.getArguments(); + for (Iterator i = putData.entrySet().iterator(); i.hasNext();) { + Map.Entry me = (Map.Entry) i.next(); + rfContext.getDataSet().put(me.getKey(), me.getValue()); + } + rfContext.getResultSender().lastResult(Boolean.TRUE); + } else { + rfContext.getResultSender().lastResult(Boolean.FALSE); + } + } else { + if (hasResult()) { + context.getResultSender().lastResult(Boolean.FALSE); + } else { + DistributedSystem ds = InternalDistributedSystem.getAnyInstance(); + LogWriter logger = ds.getLogWriter(); + logger.info("Executing in TestFunction on Server : " + + ds.getDistributedMember() + "with Context : " + context); + while (ds.isConnected()) { + logger + .fine("Just executing function in infinite loop for Bug43513"); + try { + Thread.sleep(250); + } catch (InterruptedException e) { + return; + } + } + } + } + } + + @Override + public String getId() { + return Id; + } + + @Override + public boolean hasResult() { + return true; + } + + @Override + public boolean optimizeForWrite() { + return false; + } + + @Override + public boolean isHA() { + return false; + } + } + + private int getInvocationCount(VM vm) { + return (Integer) vm.invoke(new SerializableCallable() { + /** + * + */ + private static final long serialVersionUID = 1L; + + @Override + public Object call() throws Exception { + SampleFunction f = (SampleFunction) FunctionService + .getFunction(SampleFunction.Id); + int count = f.invocationCount; + f.invocationCount = 0; + return count; + } + }); + } + + private void verifyAndResetInvocationCount(VM vm, final int count) { + vm.invoke(new SerializableCallable() { + /** + * + */ + private static final long serialVersionUID = 1L; + + @Override + public Object call() throws Exception { + SampleFunction f = (SampleFunction) FunctionService + .getFunction(SampleFunction.Id); + assertEquals(count, f.invocationCount); + // assert succeeded, reset count + f.invocationCount = 0; + return null; + } + }); + } + + public static void createPeer(DataPolicy policy) { + AttributesFactory factory = new AttributesFactory(); + factory.setScope(Scope.DISTRIBUTED_ACK); + factory.setDataPolicy(policy); + assertNotNull(cache); + region = cache.createRegion(REGION_NAME, factory.create()); + com.gemstone.gemfire.test.dunit.LogWriterUtils.getLogWriter().info("Region Created :" + region); + assertNotNull(region); + } + + public static boolean createPeerWithPR() { + RegionAttributes ra = PartitionedRegionTestHelper.createRegionAttrsForPR(0, + 10); + AttributesFactory raf = new AttributesFactory(ra); + PartitionAttributesImpl pa = new PartitionAttributesImpl(); + pa.setAll(ra.getPartitionAttributes()); + pa.setTotalNumBuckets(17); + raf.setPartitionAttributes(pa); + + if (cache == null || cache.isClosed()) { + // Cache not available + } + assertNotNull(cache); + + region = cache.createRegion(PR_REGION_NAME, raf.create()); + com.gemstone.gemfire.test.dunit.LogWriterUtils.getLogWriter().info("Region Created :" + region); + assertNotNull(region); + return Boolean.TRUE; + } + + public static void populateRegion() { + assertNotNull(cache); + region = cache.getRegion(REGION_NAME); + assertNotNull(region); + for (int i = 1; i <= 200; i++) { + region.put("execKey-" + i, new Integer(i)); + } + } + + public static void populatePRRegion() { + assertNotNull(cache); + region = cache.getRegion(REGION_NAME); + + PartitionedRegion pr = (PartitionedRegion) cache.getRegion(PR_REGION_NAME); + DistributedSystem.setThreadsSocketPolicy(false); + final HashSet testKeys = new HashSet(); + + for (int i = (pr.getTotalNumberOfBuckets() * 3); i > 0; i--) { + testKeys.add("execKey-" + i); + } + int j = 0; + for (Iterator i = testKeys.iterator(); i.hasNext();) { + Integer val = new Integer(j++); + pr.put(i.next(), val); + } + // Assert there is data in each bucket + for (int bid = 0; bid < pr.getTotalNumberOfBuckets(); bid++) { + assertTrue(pr.getBucketKeys(bid).size() > 0); + } + } + + public static void populateRRRegion() { + assertNotNull(cache); + region = cache.getRegion(REGION_NAME); + assertNotNull(region); + + final HashSet testKeys = new HashSet(); + for (int i = 17 * 3; i > 0; i--) { + testKeys.add("execKey-" + i); + } + int j = 0; + for (Iterator i = testKeys.iterator(); i.hasNext();) { + Integer val = new Integer(j++); + region.put(i.next(), val); + } + + } + + public static void executeFunction_NoLastResult(String regionName) { + + try { + CloseableHttpClient httpclient = HttpClients.createDefault(); + CloseableHttpResponse response = null; + Random randomGenerator = new Random(); + int index = randomGenerator.nextInt(restURLs.size()); + HttpPost post = new HttpPost(restURLs.get(index) + "/functions/" + + "FunctionWithNoLastResult" + "?onRegion=" + regionName); + post.addHeader("Content-Type", "application/json"); + post.addHeader("Accept", "application/json"); + response = httpclient.execute(post); + } catch (Exception e) { + throw new RuntimeException("unexpected exception", e); + } + + } + + public static void executeFunctionThroughRestCall(String regionName) { + com.gemstone.gemfire.test.dunit.LogWriterUtils.getLogWriter().info("Entering executeFunctionThroughRestCall"); + try { + CloseableHttpClient httpclient = HttpClients.createDefault(); + CloseableHttpResponse response = null; + Random randomGenerator = new Random(); + int index = randomGenerator.nextInt(restURLs.size()); + + HttpPost post = new HttpPost(restURLs.get(index) + "/functions/" + + "SampleFunction" + "?onRegion=" + regionName); + post.addHeader("Content-Type", "application/json"); + post.addHeader("Accept", "application/json"); + + com.gemstone.gemfire.test.dunit.LogWriterUtils.getLogWriter().info("Request: POST " + post.toString()); + response = httpclient.execute(post); + com.gemstone.gemfire.test.dunit.LogWriterUtils.getLogWriter().info("Response: POST " + response.toString()); + + assertEquals(response.getStatusLine().getStatusCode(), 200); + assertNotNull(response.getEntity()); + } catch (Exception e) { + throw new RuntimeException("unexpected exception", e); + } + com.gemstone.gemfire.test.dunit.LogWriterUtils.getLogWriter().info("Exiting executeFunctionThroughRestCall"); + + } + + private void registerFunction(VM vm) { + vm.invoke(new SerializableCallable() { + private static final long serialVersionUID = 1L; + @Override + public Object call() throws Exception { + FunctionService.registerFunction(new FunctionWithNoLastResult()); + return null; + } + }); + } + + private void registerSampleFunction(VM vm) { + vm.invoke(new SerializableCallable() { + private static final long serialVersionUID = 1L; + + @Override + public Object call() throws Exception { + FunctionService.registerFunction(new SampleFunction()); + return null; + } + }); + } + + public void __testOnRegionExecutionOnDataPolicyEmpty_NoLastResult() { + // Step-1 : create cache on each VM, this will start HTTP service in + // embedded mode and deploy REST APIs web app on it. + + fail("This test is trying to invoke non existent methods"); +// String url1 = (String) vm3.invoke(() -> createCacheInVm( vm3 )); +// restURLs.add(url1); +// +// String url2 = (String) vm0.invoke(() -> createCacheInVm( vm0 )); +// restURLs.add(url2); +// +// String url3 = (String) vm1.invoke(() -> createCacheInVm( vm1 )); +// restURLs.add(url3); +// +// String url4 = (String) vm2.invoke(() -> createCacheInVm( vm2 )); +// restURLs.add(url4); + + // Step-2: Register function in all VMs + registerFunction(vm3); + registerFunction(vm0); + registerFunction(vm1); + registerFunction(vm2); + + // Step-3: Create and configure Region on all VMs + vm3.invoke(() -> createPeer( DataPolicy.EMPTY )); + vm0.invoke(() -> createPeer( DataPolicy.REPLICATE )); + vm1.invoke(() -> createPeer( DataPolicy.REPLICATE )); + vm2.invoke(() -> createPeer( DataPolicy.REPLICATE )); + + // Step-4 : Do some puts on region created earlier + vm3.invoke(() -> populateRegion()); + + // add expected exception to avoid suspect strings + final IgnoredException ex = IgnoredException.addIgnoredException("did not send last result"); + + // Step-5 : Execute function randomly (in iteration) on all available (per + // VM) REST end-points and verify its result + for (int i = 0; i < 10; i++) { + executeFunction_NoLastResult(REGION_NAME); + } + ex.remove(); + + restURLs.clear(); + } + + public void testOnRegionExecutionWithRR() { + // Step-1 : create cache on each VM, this will start HTTP service in + // embedded mode and deploy REST APIs web app on it. + // + String url1 = (String) vm3.invoke(() -> RestAPITestBase.createCache( vm3 )); + restURLs.add(url1); + + String url2 = (String) vm0.invoke(() -> RestAPITestBase.createCache( vm0 )); + restURLs.add(url2); + + String url3 = (String) vm1.invoke(() -> RestAPITestBase.createCache( vm1 )); + restURLs.add(url3); + + String url4 = (String) vm2.invoke(() -> RestAPITestBase.createCache( vm2 )); + restURLs.add(url4); + + // Step-2: Register function in all VMs + registerSampleFunction(vm3); + registerSampleFunction(vm0); + registerSampleFunction(vm1); + registerSampleFunction(vm2); + + // Step-3: Create and configure PR on all VMs + vm3.invoke(() -> createPeer( DataPolicy.EMPTY )); + vm0.invoke(() -> createPeer( DataPolicy.REPLICATE )); + vm1.invoke(() -> createPeer( DataPolicy.REPLICATE )); + vm2.invoke(() -> createPeer( DataPolicy.REPLICATE )); + + // Step-4 : Do some puts in Replicated region on vm3 + vm3.invoke(() -> populateRRRegion()); + + // Step-5 : Execute function randomly (in iteration) on all available (per + // VM) REST end-points and verify its result + executeFunctionThroughRestCall(REGION_NAME); + int c0 = getInvocationCount(vm0); + int c1 = getInvocationCount(vm1); + int c2 = getInvocationCount(vm2); + int c3 = getInvocationCount(vm3); + + assertEquals(1, c0 + c1 + c2 + c3); + + // remove the expected exception + restURLs.clear(); + } + + public void testOnRegionExecutionWithPR() throws Exception { + final String rName = getUniqueName(); + + // Step-1 : create cache on each VM, this will start HTTP service in + // embedded mode and deploy REST APIs web app on it. + String url1 = (String) vm3.invoke(() -> RestAPITestBase.createCache( vm3 )); + restURLs.add(url1); + + String url2 = (String) vm0.invoke(() -> RestAPITestBase.createCache( vm0 )); + restURLs.add(url2); + + String url3 = (String) vm1.invoke(() -> RestAPITestBase.createCache( vm1 )); + restURLs.add(url3); + + String url4 = (String) vm2.invoke(() -> RestAPITestBase.createCache( vm2 )); + restURLs.add(url4); + + // Step-2: Register function in all VMs + registerSampleFunction(vm3); + registerSampleFunction(vm0); + registerSampleFunction(vm1); + registerSampleFunction(vm2); + + // Step-3: Create and configure PR on all VMs + vm3.invoke(() -> createPeerWithPR()); + vm0.invoke(() -> createPeerWithPR()); + vm1.invoke(() -> createPeerWithPR()); + vm2.invoke(() -> createPeerWithPR()); + + // Step-4: Do some puts such that data exist in each bucket + vm3.invoke(() -> populatePRRegion()); + + // Step-5 : Execute function randomly (in iteration) on all available (per + // VM) REST end-points and verify its result + executeFunctionThroughRestCall(PR_REGION_NAME); + + // Assert that each node has executed the function once. + verifyAndResetInvocationCount(vm0, 1); + verifyAndResetInvocationCount(vm1, 1); + verifyAndResetInvocationCount(vm2, 1); + verifyAndResetInvocationCount(vm3, 1); + + int c0 = getInvocationCount(vm0); + int c1 = getInvocationCount(vm1); + int c2 = getInvocationCount(vm2); + int c3 = getInvocationCount(vm3); + + restURLs.clear(); + } + +} + +class MyFunctionException implements Function { + + /** + * + */ + private static final long serialVersionUID = 1L; + + @Override + public void execute(FunctionContext context) { + throw new RuntimeException("failure"); + } + + @Override + public String getId() { + return this.getClass().getName(); + } + + @Override + public boolean hasResult() { + return true; + } + + @Override + public boolean isHA() { + return false; + } + + @Override + public boolean optimizeForWrite() { + return false; + } + +} http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/9b55879e/geode-assembly/src/test/java/com/gemstone/gemfire/rest/internal/web/controllers/RestAPITestBase.java ---------------------------------------------------------------------- diff --git a/geode-assembly/src/test/java/com/gemstone/gemfire/rest/internal/web/controllers/RestAPITestBase.java b/geode-assembly/src/test/java/com/gemstone/gemfire/rest/internal/web/controllers/RestAPITestBase.java new file mode 100644 index 0000000..3709475 --- /dev/null +++ b/geode-assembly/src/test/java/com/gemstone/gemfire/rest/internal/web/controllers/RestAPITestBase.java @@ -0,0 +1,133 @@ +/* + * 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 com.gemstone.gemfire.rest.internal.web.controllers; + +import java.util.Properties; + +import com.gemstone.gemfire.cache.Cache; +import com.gemstone.gemfire.cache.CacheClosedException; +import com.gemstone.gemfire.cache.CacheFactory; +import com.gemstone.gemfire.cache.RegionShortcut; +import com.gemstone.gemfire.distributed.internal.DistributionConfig; +import com.gemstone.gemfire.distributed.internal.InternalDistributedSystem; +import com.gemstone.gemfire.internal.AvailablePortHelper; +import com.gemstone.gemfire.internal.GemFireVersion; +import com.gemstone.gemfire.management.internal.AgentUtil; +import com.gemstone.gemfire.test.dunit.DistributedTestCase; +import com.gemstone.gemfire.test.dunit.Host; +import com.gemstone.gemfire.test.dunit.Invoke; +import com.gemstone.gemfire.test.dunit.VM; +import com.gemstone.gemfire.test.dunit.Wait; + +public class RestAPITestBase extends DistributedTestCase { + private static final long serialVersionUID = 1L; + public static Cache cache = null; + VM vm0 = null; + VM vm1 = null; + VM vm2 = null; + VM vm3 = null; + + public RestAPITestBase(String name) { + super(name); + } + + + @Override + public void setUp() throws Exception { + super.setUp(); + disconnectAllFromDS(); + AgentUtil agentUtil = new AgentUtil(GemFireVersion.getGemFireVersion()); + if (agentUtil.findWarLocation("geode-web-api") == null) { + fail("unable to locate geode-web-api WAR file"); + } + Wait.pause(5000); + final Host host = Host.getHost(0); + vm0 = host.getVM(0); + vm1 = host.getVM(1); + vm2 = host.getVM(2); + vm3 = host.getVM(3); + // gradle sets a property telling us where the build is located + final String buildDir = System.getProperty("geode.build.dir", System.getProperty("user.dir")); + Invoke.invokeInEveryVM(()-> System.setProperty("geode.build.dir", buildDir)); + } + + /** + * close the clients and teh servers + */ + @Override + protected final void preTearDown() throws Exception { + vm0.invoke(() -> closeCache()); + vm1.invoke(() -> closeCache()); + vm2.invoke(() -> closeCache()); + vm3.invoke(() -> closeCache()); + } + + /** + * close the cache + * + */ + public static void closeCache() { + if (cache != null && !cache.isClosed()) { + cache.close(); + cache.getDistributedSystem().disconnect(); + } + } + + protected static String createCache(VM currentVM) { + + RestAPITestBase test = new RestAPITestBase(getTestMethodName()); + + final String hostName = currentVM.getHost().getHostName(); + final int serverPort = AvailablePortHelper.getRandomAvailableTCPPort(); + + Properties props = new Properties(); + + props.setProperty(DistributionConfig.START_DEV_REST_API_NAME, "true"); + props.setProperty(DistributionConfig.HTTP_SERVICE_BIND_ADDRESS_NAME, hostName); + props.setProperty(DistributionConfig.HTTP_SERVICE_PORT_NAME,String.valueOf(serverPort)); + + + InternalDistributedSystem ds = test.getSystem(props); + cache = CacheFactory.create(ds); + return "http://" + hostName + ":" + serverPort + "/gemfire-api/v1"; + + } + + public static String createCacheWithGroups (VM vm, final String groups, final String regionName ) { + RestAPITestBase test = new RestAPITestBase(getTestMethodName()); + + final String hostName = vm.getHost().getHostName(); + final int serverPort = AvailablePortHelper.getRandomAvailableTCPPort(); + + Properties props = new Properties(); + + if(groups != null) { + props.put("groups", groups); + } + + props.setProperty(DistributionConfig.START_DEV_REST_API_NAME, "true"); + props.setProperty(DistributionConfig.HTTP_SERVICE_BIND_ADDRESS_NAME, hostName); + props.setProperty(DistributionConfig.HTTP_SERVICE_PORT_NAME, String.valueOf(serverPort)); + + InternalDistributedSystem ds = test.getSystem(props); + cache = CacheFactory.create(ds); + + String restEndPoint = "http://" + hostName + ":" + serverPort + "/gemfire-api/v1"; + return restEndPoint; + } + +} http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/9b55879e/geode-assembly/src/test/java/com/gemstone/gemfire/rest/internal/web/controllers/RestAPIsAndInterOpsDUnitTest.java ---------------------------------------------------------------------- diff --git a/geode-assembly/src/test/java/com/gemstone/gemfire/rest/internal/web/controllers/RestAPIsAndInterOpsDUnitTest.java b/geode-assembly/src/test/java/com/gemstone/gemfire/rest/internal/web/controllers/RestAPIsAndInterOpsDUnitTest.java new file mode 100644 index 0000000..0245fa0 --- /dev/null +++ b/geode-assembly/src/test/java/com/gemstone/gemfire/rest/internal/web/controllers/RestAPIsAndInterOpsDUnitTest.java @@ -0,0 +1,915 @@ +/* + * 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 com.gemstone.gemfire.rest.internal.web.controllers; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.util.Arrays; +import java.util.Calendar; +import java.util.HashMap; +import java.util.Map; +import java.util.Properties; + +import org.apache.http.HttpEntity; +import org.apache.http.client.ClientProtocolException; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpDelete; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.client.methods.HttpPut; +import org.apache.http.entity.StringEntity; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClients; +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import com.gemstone.gemfire.cache.AttributesFactory; +import com.gemstone.gemfire.cache.Cache; +import com.gemstone.gemfire.cache.CacheFactory; +import com.gemstone.gemfire.cache.DataPolicy; +import com.gemstone.gemfire.cache.Region; +import com.gemstone.gemfire.cache.RegionAttributes; +import com.gemstone.gemfire.cache.RegionFactory; +import com.gemstone.gemfire.cache.RegionShortcut; +import com.gemstone.gemfire.cache.client.ClientCache; +import com.gemstone.gemfire.cache.client.ClientCacheFactory; +import com.gemstone.gemfire.cache.client.ClientRegionFactory; +import com.gemstone.gemfire.cache.client.ClientRegionShortcut; +import com.gemstone.gemfire.cache.client.internal.LocatorTestBase; +import com.gemstone.gemfire.cache.server.CacheServer; +import com.gemstone.gemfire.cache.server.ServerLoadProbe; +import com.gemstone.gemfire.distributed.DistributedSystem; +import com.gemstone.gemfire.distributed.internal.DistributionConfig; +import com.gemstone.gemfire.internal.AvailablePort; +import com.gemstone.gemfire.internal.AvailablePortHelper; +import com.gemstone.gemfire.internal.cache.GemFireCacheImpl; +import com.gemstone.gemfire.management.ManagementTestBase; +import com.gemstone.gemfire.pdx.PdxInstance; +import com.gemstone.gemfire.test.dunit.Host; +import com.gemstone.gemfire.test.dunit.NetworkUtils; +import com.gemstone.gemfire.test.dunit.SerializableCallable; +import com.gemstone.gemfire.test.dunit.SerializableRunnable; +import com.gemstone.gemfire.test.dunit.VM; + +/** + * Dunit Test containing inter - operations between REST Client and Gemfire cache client + * @author Nilkanth Patel + * @since 8.0 + */ + +public class RestAPIsAndInterOpsDUnitTest extends LocatorTestBase { + + private static final long serialVersionUID = -254776154266339226L; + + private ManagementTestBase helper; + + public static final String PEOPLE_REGION_NAME = "People"; + + //private static RestTemplate restTemplate; + + private static final String findAllPeopleQuery = "/queries?id=findAllPeople&q=SELECT%20*%20FROM%20/People"; + private static final String findPeopleByGenderQuery = "/queries?id=filterByGender&q=SELECT%20*%20from%20/People%20where%20gender=$1"; + private static final String findPeopleByLastNameQuery = "/queries?id=filterByLastName&q=SELECT%20*%20from%20/People%20where%20lastName=$1"; + + private static final String[] PARAM_QUERY_IDS_ARRAY = { "findAllPeople", + "filterByGender", "filterByLastName" }; + + final static String QUERY_ARGS = "[" + + "{" + + "\"@type\": \"string\"," + + "\"@value\": \"Patel\"" + + "}" + + "]"; + + final static String PERSON_AS_JSON_CAS = "{" + + "\"@old\" :" + + "{" + + "\"@type\": \"com.gemstone.gemfire.rest.internal.web.controllers.Person\"," + + "\"id\": 101," + " \"firstName\": \"Mithali\"," + + " \"middleName\": \"Dorai\"," + " \"lastName\": \"Raj\"," + + " \"birthDate\": \"12/04/1982\"," + "\"gender\": \"FEMALE\"" + + "}," + + "\"@new\" :" + + "{" + + "\"@type\": \"com.gemstone.gemfire.rest.internal.web.controllers.Person\"," + + "\"id\": 1101," + " \"firstName\": \"Virat\"," + + " \"middleName\": \"Premkumar\"," + " \"lastName\": \"Kohli\"," + + " \"birthDate\": \"08/11/1988\"," + "\"gender\": \"MALE\"" + + "}" + + "}"; + + final static String PERSON_AS_JSON_REPLACE = "{" + + "\"@type\": \"com.gemstone.gemfire.rest.internal.web.controllers.Person\"," + + "\"id\": 501," + " \"firstName\": \"Barack\"," + + " \"middleName\": \"Hussein\"," + " \"lastName\": \"Obama\"," + + " \"birthDate\": \"04/08/1961\"," + "\"gender\": \"MALE\"" + + "}"; + + private static final String PERSON_LIST_AS_JSON = "[" + "{" + + "\"@type\": \"com.gemstone.gemfire.rest.internal.web.controllers.Person\"," + + "\"id\": 3," + " \"firstName\": \"Nishka3\"," + + " \"middleName\": \"Nilkanth3\"," + " \"lastName\": \"Patel3\"," + + " \"birthDate\": \"07/31/2009\"," + "\"gender\": \"FEMALE\"" + "}," + + "{" + "\"@type\": \"com.gemstone.gemfire.rest.internal.web.controllers.Person\"," + + "\"id\": 4," + " \"firstName\": \"Tanay4\"," + + " \"middleName\": \"kiran4\"," + " \"lastName\": \"Patel4\"," + + " \"birthDate\": \"23/08/2012\"," + "\"gender\": \"MALE\"" + "}," + "{" + + "\"@type\": \"com.gemstone.gemfire.rest.internal.web.controllers.Person\"," + + "\"id\": 5," + " \"firstName\": \"Nishka5\"," + + " \"middleName\": \"Nilkanth5\"," + " \"lastName\": \"Patel5\"," + + " \"birthDate\": \"31/09/2009\"," + "\"gender\": \"FEMALE\"" + "}," + + "{" + "\"@type\": \"com.gemstone.gemfire.rest.internal.web.controllers.Person\"," + + "\"id\": 6," + " \"firstName\": \"Tanay6\"," + + " \"middleName\": \"Kiran6\"," + " \"lastName\": \"Patel\"," + + " \"birthDate\": \"23/08/2012\"," + "\"gender\": \"MALE\"" + "}," + "{" + + "\"@type\": \"com.gemstone.gemfire.rest.internal.web.controllers.Person\"," + + "\"id\": 7," + " \"firstName\": \"Nishka7\"," + + " \"middleName\": \"Nilkanth7\"," + " \"lastName\": \"Patel\"," + + " \"birthDate\": \"31/09/2009\"," + "\"gender\": \"FEMALE\"" + "}," + + "{" + "\"@type\": \"com.gemstone.gemfire.rest.internal.web.controllers.Person\"," + + "\"id\": 8," + " \"firstName\": \"Tanay8\"," + + " \"middleName\": \"kiran8\"," + " \"lastName\": \"Patel\"," + + " \"birthDate\": \"23/08/2012\"," + "\"gender\": \"MALE\"" + "}," + "{" + + "\"@type\": \"com.gemstone.gemfire.rest.internal.web.controllers.Person\"," + + "\"id\": 9," + " \"firstName\": \"Nishka9\"," + + " \"middleName\": \"Nilkanth9\"," + " \"lastName\": \"Patel\"," + + " \"birthDate\": \"31/09/2009\"," + "\"gender\": \"FEMALE\"" + "}," + + "{" + "\"@type\": \"com.gemstone.gemfire.rest.internal.web.controllers.Person\"," + + "\"id\": 10," + " \"firstName\": \"Tanay10\"," + + " \"middleName\": \"kiran10\"," + " \"lastName\": \"Patel\"," + + " \"birthDate\": \"23/08/2012\"," + "\"gender\": \"MALE\"" + "}," + "{" + + "\"@type\": \"com.gemstone.gemfire.rest.internal.web.controllers.Person\"," + + "\"id\": 11," + " \"firstName\": \"Nishka11\"," + + " \"middleName\": \"Nilkanth11\"," + " \"lastName\": \"Patel\"," + + " \"birthDate\": \"31/09/2009\"," + "\"gender\": \"FEMALE\"" + "}," + + "{" + "\"@type\": \"com.gemstone.gemfire.rest.internal.web.controllers.Person\"," + + "\"id\": 12," + " \"firstName\": \"Tanay12\"," + + " \"middleName\": \"kiran12\"," + " \"lastName\": \"Patel\"," + + " \"birthDate\": \"23/08/2012\"," + "\"gender\": \"MALE\"" + "}" + "]"; + + public RestAPIsAndInterOpsDUnitTest(String name) { + super(name); + this.helper = new ManagementTestBase(name); + + } + + public void setUp() throws Exception { + disconnectAllFromDS(); + super.setUp(); + } + + @Override + protected final void postTearDownLocatorTestBase() throws Exception { + disconnectAllFromDS(); + } + + public static String startBridgeServerWithRestServiceOnInVM(VM vm, final String[] groups, final String locators, final String[] regions, final ServerLoadProbe probe) { + + final String hostName = vm.getHost().getHostName(); + final int serverPort = AvailablePortHelper.getRandomAvailableTCPPort(); + + //create Cache of given VM and start HTTP service with REST APIs service + new RestAPIsAndInterOpsDUnitTest("temp").startBridgeServer(hostName, serverPort, groups, locators, regions, probe); + + String restEndPoint = "http://" + hostName + ":" + serverPort + "/gemfire-api/v1"; + return restEndPoint; + } + + @SuppressWarnings("deprecation") + protected int startBridgeServer(String hostName, int restServicerPort, final String[] groups, final String locators, final String[] regions, final ServerLoadProbe probe) { + + Properties props = new Properties(); + props.setProperty(DistributionConfig.MCAST_PORT_NAME, String.valueOf(0)); + props.setProperty(DistributionConfig.LOCATORS_NAME, locators); + props.setProperty(DistributionConfig.START_DEV_REST_API_NAME, "true"); + props.setProperty(DistributionConfig.HTTP_SERVICE_BIND_ADDRESS_NAME, hostName); + props.setProperty(DistributionConfig.HTTP_SERVICE_PORT_NAME, String.valueOf(restServicerPort)); + + DistributedSystem ds = getSystem(props); + Cache cache = CacheFactory.create(ds); + ((GemFireCacheImpl)cache).setReadSerialized(true); + AttributesFactory factory = new AttributesFactory(); + + factory.setEnableBridgeConflation(true); + factory.setDataPolicy(DataPolicy.REPLICATE); + RegionAttributes attrs = factory.create(); + for(int i = 0; i < regions.length; i++) { + cache.createRegion(regions[i], attrs); + } + + CacheServer server = cache.addCacheServer(); + final int serverPort = AvailablePortHelper.getRandomAvailableTCPPort(); + server.setPort(serverPort); + server.setGroups(groups); + server.setLoadProbe(probe); + try { + server.start(); + } catch (IOException e) { + e.printStackTrace(); + } + remoteObjects.put(CACHE_KEY, cache); + return new Integer(serverPort); + } + + public static void doPutsInClientCache() { + ClientCache cache = GemFireCacheImpl.getInstance(); + assertNotNull(cache); + Region region = cache.getRegion(PEOPLE_REGION_NAME); + + //put person object + final Person person1 = new Person(101L, "Mithali", "Dorai", "Raj", DateTimeUtils.createDate(1982, Calendar.DECEMBER, 4), Gender.FEMALE); + final Person person2 = new Person(102L, "Sachin", "Ramesh", "Tendulkar", DateTimeUtils.createDate(1975, Calendar.DECEMBER, 14), Gender.MALE); + final Person person3 = new Person(103L, "Saurabh", "Baburav", "Ganguly", DateTimeUtils.createDate(1972, Calendar.AUGUST, 29), Gender.MALE); + final Person person4 = new Person(104L, "Rahul", "subrymanyam", "Dravid", DateTimeUtils.createDate(1979, Calendar.MARCH, 17), Gender.MALE); + final Person person5 = new Person(105L, "Jhulan", "Chidambaram", "Goswami", DateTimeUtils.createDate(1983, Calendar.NOVEMBER, 25), Gender.FEMALE); + + region.put("1", person1); + region.put("2", person2); + region.put("3", person3); + region.put("4", person4); + region.put("5", person5); + + final Person person6 = new Person(101L, "Rahul", "Rajiv", "Gndhi", DateTimeUtils.createDate(1970, Calendar.MAY, 14), Gender.MALE); + final Person person7 = new Person(102L, "Narendra", "Damodar", "Modi", DateTimeUtils.createDate(1945, Calendar.DECEMBER, 24), Gender.MALE); + final Person person8 = new Person(103L, "Atal", "Bihari", "Vajpayee", DateTimeUtils.createDate(1920, Calendar.AUGUST, 9), Gender.MALE); + final Person person9 = new Person(104L, "Soniya", "Rajiv", "Gandhi", DateTimeUtils.createDate(1929, Calendar.MARCH, 27), Gender.FEMALE); + final Person person10 = new Person(104L, "Priyanka", "Robert", "Gandhi", DateTimeUtils.createDate(1973, Calendar.APRIL, 15), Gender.FEMALE); + + final Person person11 = new Person(104L, "Murali", "Manohar", "Joshi", DateTimeUtils.createDate(1923, Calendar.APRIL, 25), Gender.MALE); + final Person person12 = new Person(104L, "Lalkrishna", "Parmhansh", "Advani", DateTimeUtils.createDate(1910, Calendar.JANUARY, 01), Gender.MALE); + final Person person13 = new Person(104L, "Shushma", "kumari", "Swaraj", DateTimeUtils.createDate(1943, Calendar.AUGUST, 10), Gender.FEMALE); + final Person person14 = new Person(104L, "Arun", "raman", "jetly", DateTimeUtils.createDate(1942, Calendar.OCTOBER, 27), Gender.MALE); + final Person person15 = new Person(104L, "Amit", "kumar", "shah", DateTimeUtils.createDate(1958, Calendar.DECEMBER, 21), Gender.MALE); + final Person person16 = new Person(104L, "Shila", "kumari", "Dixit", DateTimeUtils.createDate(1927, Calendar.FEBRUARY, 15), Gender.FEMALE); + + Map userMap = new HashMap(); + userMap.put("6", person6); + userMap.put("7", person7); + userMap.put("8", person8); + userMap.put("9", person9); + userMap.put("10", person10); + userMap.put("11", person11); + userMap.put("12", person12); + userMap.put("13", person13); + userMap.put("14", person14); + userMap.put("15", person15); + userMap.put("16", person16); + + region.putAll(userMap); + + if (cache != null) + cache.getLogger().info("Gemfire Cache Client: Puts successfully done"); + + } + + public static void doQueryOpsUsingRestApis(String restEndpoint) { + String currentQueryOp = null; + try { + // Query TestCase-1 :: Prepare parameterized Queries + { + currentQueryOp = "findAllPeopleQuery"; + CloseableHttpClient httpclient = HttpClients.createDefault(); + HttpPost post = new HttpPost(restEndpoint + findAllPeopleQuery); + post.addHeader("Content-Type", "application/json"); + post.addHeader("Accept", "application/json"); + CloseableHttpResponse createNamedQueryResponse = httpclient.execute(post); + assertEquals(createNamedQueryResponse.getStatusLine().getStatusCode(), 201); + assertNotNull(createNamedQueryResponse.getEntity()); + createNamedQueryResponse.close(); + + + post = new HttpPost(restEndpoint + findPeopleByGenderQuery); + post.addHeader("Content-Type", "application/json"); + post.addHeader("Accept", "application/json"); + createNamedQueryResponse = httpclient.execute(post); + assertEquals(createNamedQueryResponse.getStatusLine().getStatusCode(), 201); + assertNotNull(createNamedQueryResponse.getEntity()); + createNamedQueryResponse.close(); + + + post = new HttpPost(restEndpoint + findPeopleByLastNameQuery); + post.addHeader("Content-Type", "application/json"); + post.addHeader("Accept", "application/json"); + createNamedQueryResponse = httpclient.execute(post); + assertEquals(createNamedQueryResponse.getStatusLine().getStatusCode(), 201); + assertNotNull(createNamedQueryResponse.getEntity()); + createNamedQueryResponse.close(); + } + + // Query TestCase-2 :: List all parameterized queries + { + currentQueryOp = "listAllQueries"; + HttpGet get = new HttpGet(restEndpoint + "/queries"); + CloseableHttpClient httpclient = HttpClients.createDefault(); + CloseableHttpResponse listAllQueriesResponse = httpclient.execute(get); + assertEquals(listAllQueriesResponse.getStatusLine().getStatusCode(), 200); + assertNotNull(listAllQueriesResponse.getEntity()); + + HttpEntity entity = listAllQueriesResponse.getEntity(); + InputStream content = entity.getContent(); + BufferedReader reader = new BufferedReader(new InputStreamReader( + content)); + String line; + StringBuffer sb = new StringBuffer(); + while ((line = reader.readLine()) != null) { + sb.append(line); + } + listAllQueriesResponse.close(); + + // Check whether received response contains expected query IDs. + + JSONObject jsonObject = new JSONObject(sb.toString()); + JSONArray jsonArray = jsonObject.getJSONArray("queries"); + for (int i = 0; i < jsonArray.length(); i++) { + assertTrue( + "PREPARE_PARAMETERIZED_QUERY: function IDs are not matched", + Arrays.asList(PARAM_QUERY_IDS_ARRAY).contains( + jsonArray.getJSONObject(i).getString("id"))); + } + } + + // Query TestCase-3 :: Run the specified named query passing in scalar values for query parameters. + { + currentQueryOp = "filterByLastName"; + CloseableHttpClient httpclient = HttpClients.createDefault(); + HttpPost post = new HttpPost(restEndpoint + "/queries/filterByLastName"); + post.addHeader("Content-Type", "application/json"); + post.addHeader("Accept", "application/json"); + StringEntity entity = new StringEntity(QUERY_ARGS); + post.setEntity(entity); + CloseableHttpResponse runNamedQueryResponse = httpclient.execute(post); + + assertEquals(200, runNamedQueryResponse.getStatusLine().getStatusCode()); + assertNotNull(runNamedQueryResponse.getEntity()); + } + } catch ( Exception e ) { + throw new RuntimeException("unexpected exception", e); + } + } + + public static void verifyUpdatesInClientCache() { + ClientCache cache = GemFireCacheImpl.getInstance(); + assertNotNull(cache); + Region region = cache.getRegion(PEOPLE_REGION_NAME); + + { + Person expectedPerson = new Person(3L, "Nishka3", "Nilkanth3", "Patel3", DateTimeUtils.createDate(2009, Calendar.JULY, 31), Gender.FEMALE ); + Object value = region.get("3"); + if (value instanceof PdxInstance) { + PdxInstance pi3 = (PdxInstance) value; + Person actualPerson = (Person) pi3.getObject(); + assertEquals(actualPerson.getId(), expectedPerson.getId()); + assertEquals(actualPerson.getFirstName(), expectedPerson.getFirstName()); + assertEquals(actualPerson.getMiddleName(), expectedPerson.getMiddleName()); + assertEquals(actualPerson.getLastName(), expectedPerson.getLastName()); + assertEquals(actualPerson.getBirthDate(), expectedPerson.getBirthDate()); + assertEquals(actualPerson.getGender(), expectedPerson.getGender()); + } else if (value instanceof Person) { + fail("VerifyUpdatesInClientCache, Get on key 3, Expected to get value of type PdxInstance "); + } + } + + + //TODO: uncomment it once following issue encountered in put?op=CAS is fixed or document the issue + // CAS functionality is not working in following test case + // step-1: Java client, Region.put("K", A); + //Step-2: Rest CAS request for key "K" with data "@old" = A. CAS is failing as existing PdxInstance in cache and + // PdxInstance generated from JSON (CAS request) does not match as their value's type are getting changed + /* + //verify update on key "1" + { + Object obj = region.get("1"); + if (obj instanceof PdxInstance) { + PdxInstance pi = (PdxInstance)obj; + Person p1 = (Person)pi.getObject(); + System.out.println("Nilkanth1 : verifyUpdatesInClientCache() : GET ON KEY=1" + p1.toString()); + }else { + System.out.println("Nilkanth1 : verifyUpdatesInClientCache() GET ON KEY=1 returned OBJECT: " + obj.toString()); + } + } + */ + + //verify update on key "2" + { + Person expectedPerson = new Person(501L, "Barack", "Hussein", "Obama", DateTimeUtils.createDate(1961, Calendar.APRIL, 8), Gender.MALE ); + Object value = region.get("2"); + if (value instanceof PdxInstance) { + PdxInstance pi3 = (PdxInstance) value; + Person actualPerson = (Person) pi3.getObject(); + assertEquals(actualPerson.getId(), expectedPerson.getId()); + assertEquals(actualPerson.getFirstName(), expectedPerson.getFirstName()); + assertEquals(actualPerson.getMiddleName(), expectedPerson.getMiddleName()); + assertEquals(actualPerson.getLastName(), expectedPerson.getLastName()); + assertEquals(actualPerson.getBirthDate(), expectedPerson.getBirthDate()); + assertEquals(actualPerson.getGender(), expectedPerson.getGender()); + }else { + fail("VerifyUpdatesInClientCache, Get on key 2, Expected to get value of type PdxInstance "); + } + } + + //verify Deleted key "13" + { + Object obj = region.get("13"); + assertEquals(obj, null); + + obj = region.get("14"); + assertEquals(obj, null); + + obj = region.get("15"); + assertEquals(obj, null); + + obj = region.get("16"); + assertEquals(obj, null); + } + + } + + public static void doUpdatesUsingRestApis(String restEndpoint) { + //UPdate keys using REST calls + { + + try { + CloseableHttpClient httpclient = HttpClients.createDefault(); + HttpPut put = new HttpPut(restEndpoint + + "/People/3,4,5,6,7,8,9,10,11,12"); + put.addHeader("Content-Type", "application/json"); + put.addHeader("Accept", "application/json"); + StringEntity entity = new StringEntity(PERSON_LIST_AS_JSON); + put.setEntity(entity); + CloseableHttpResponse result = httpclient.execute(put); + } catch (Exception e) { + throw new RuntimeException("unexpected exception", e); + } + } + + //Delete Single keys + { + try { + CloseableHttpClient httpclient = HttpClients.createDefault(); + HttpDelete delete = new HttpDelete(restEndpoint + "/People/13"); + delete.addHeader("Content-Type", "application/json"); + delete.addHeader("Accept", "application/json"); + CloseableHttpResponse result = httpclient.execute(delete); + } catch (Exception e) { + throw new RuntimeException("unexpected exception", e); + } + } + + //Delete set of keys + { + try { + CloseableHttpClient httpclient = HttpClients.createDefault(); + HttpDelete delete = new HttpDelete(restEndpoint + "/People/14,15,16"); + delete.addHeader("Content-Type", "application/json"); + delete.addHeader("Accept", "application/json"); + CloseableHttpResponse result = httpclient.execute(delete); + } catch (Exception e) { + throw new RuntimeException("unexpected exception", e); + } + } + + //REST put?op=CAS for key 1 + /* + try { + { + HttpEntity entity = new HttpEntity(PERSON_AS_JSON_CAS, headers); + ResponseEntity result = RestTestUtils.getRestTemplate().exchange( + restEndpoint + "/People/1?op=cas", + HttpMethod.PUT, entity, String.class); + } + } catch (HttpClientErrorException e) { + + fail("Caught HttpClientErrorException while doing put with op=cas"); + }catch (HttpServerErrorException se) { + fail("Caught HttpServerErrorException while doing put with op=cas"); + } + */ + + //REST put?op=REPLACE for key 2 + { + /*HttpEntity entity = new HttpEntity(PERSON_AS_JSON_REPLACE, headers); + ResponseEntity result = RestTestUtils.getRestTemplate().exchange( + restEndpoint + "/People/2?op=replace", + HttpMethod.PUT, entity, String.class);*/ + + try { + CloseableHttpClient httpclient = HttpClients.createDefault(); + HttpPut put = new HttpPut(restEndpoint + + "/People/2?op=replace"); + put.addHeader("Content-Type", "application/json"); + put.addHeader("Accept", "application/json"); + StringEntity entity = new StringEntity(PERSON_AS_JSON_REPLACE); + put.setEntity(entity); + CloseableHttpResponse result = httpclient.execute(put); + } catch (Exception e) { + throw new RuntimeException("unexpected exception", e); + } + } + } + + public static void fetchRestServerEndpoints(String restEndpoint) { + HttpGet get = new HttpGet(restEndpoint + "/servers"); + get.addHeader("Content-Type", "application/json"); + get.addHeader("Accept", "application/json"); + CloseableHttpClient httpclient = HttpClients.createDefault(); + CloseableHttpResponse response; + + try { + response = httpclient.execute(get); + HttpEntity entity = response.getEntity(); + InputStream content = entity.getContent(); + BufferedReader reader = new BufferedReader(new InputStreamReader( + content)); + String line; + StringBuffer str = new StringBuffer(); + while ((line = reader.readLine()) != null) { + str.append(line); + } + + //validate the satus code + assertEquals(response.getStatusLine().getStatusCode(), 200); + + if(response.getStatusLine().getStatusCode() == 200) { + JSONArray jsonArray = new JSONArray(str.toString()); + + //verify total number of REST service endpoints in DS + assertEquals(jsonArray.length(), 2); + } + + } catch (ClientProtocolException e) { + e.printStackTrace(); + fail(" Rest Request should not have thrown ClientProtocolException!"); + } catch (IOException e) { + e.printStackTrace(); + fail(" Rest Request should not have thrown IOException!"); + } catch (JSONException e) { + e.printStackTrace(); + fail(" Rest Request should not have thrown JSONException!"); + } + + } + + public static void doGetsUsingRestApis(String restEndpoint) { + + //HttpHeaders headers = setAcceptAndContentTypeHeaders(); + String currentOperation = null; + JSONObject jObject; + JSONArray jArray; + try { + //1. Get on key="1" and validate result. + { + currentOperation = "GET on key 1"; + + HttpGet get = new HttpGet(restEndpoint + "/People/1"); + get.addHeader("Content-Type", "application/json"); + get.addHeader("Accept", "application/json"); + CloseableHttpClient httpclient = HttpClients.createDefault(); + CloseableHttpResponse response = httpclient.execute(get); + + HttpEntity entity = response.getEntity(); + InputStream content = entity.getContent(); + BufferedReader reader = new BufferedReader(new InputStreamReader( + content)); + String line; + StringBuffer str = new StringBuffer(); + while ((line = reader.readLine()) != null) { + str.append(line); + } + + jObject = new JSONObject(str.toString()); + + assertEquals(jObject.get("id"), 101); + assertEquals(jObject.get("firstName"), "Mithali"); + assertEquals(jObject.get("middleName"), "Dorai"); + assertEquals(jObject.get("lastName"), "Raj"); + assertEquals(jObject.get("gender"), Gender.FEMALE.name()); + } + + //2. Get on key="16" and validate result. + { + currentOperation = "GET on key 16"; + + + HttpGet get = new HttpGet(restEndpoint + "/People/16"); + get.addHeader("Content-Type", "application/json"); + get.addHeader("Accept", "application/json"); + CloseableHttpClient httpclient = HttpClients.createDefault(); + CloseableHttpResponse response = httpclient.execute(get); + + HttpEntity entity = response.getEntity(); + InputStream content = entity.getContent(); + BufferedReader reader = new BufferedReader(new InputStreamReader( + content)); + String line; + StringBuffer str = new StringBuffer(); + while ((line = reader.readLine()) != null) { + str.append(line); + } + + jObject = new JSONObject(str.toString()); + + + assertEquals(jObject.get("id"), 104); + assertEquals(jObject.get("firstName"), "Shila"); + assertEquals(jObject.get("middleName"), "kumari"); + assertEquals(jObject.get("lastName"), "Dixit"); + assertEquals(jObject.get("gender"), Gender.FEMALE.name()); + } + + //3. Get all (getAll) entries in Region + { + + HttpGet get = new HttpGet(restEndpoint + "/People"); + get.addHeader("Content-Type", "application/json"); + get.addHeader("Accept", "application/json"); + CloseableHttpClient httpclient = HttpClients.createDefault(); + CloseableHttpResponse result = httpclient.execute(get); + assertEquals(result.getStatusLine().getStatusCode(), 200); + assertNotNull(result.getEntity()); + + HttpEntity entity = result.getEntity(); + InputStream content = entity.getContent(); + BufferedReader reader = new BufferedReader(new InputStreamReader( + content)); + String line; + StringBuffer sb = new StringBuffer(); + while ((line = reader.readLine()) != null) { + sb.append(line); + } + result.close(); + + try { + jObject = new JSONObject(sb.toString()); + jArray = jObject.getJSONArray("People"); + assertEquals(jArray.length(), 16); + } catch (JSONException e) { + fail(" Rest Request ::" + currentOperation + " :: should not have thrown JSONException "); + } + } + + //4. GetAll?limit=10 (10 entries) and verify results + { + HttpGet get = new HttpGet(restEndpoint + "/People?limit=10"); + get.addHeader("Content-Type", "application/json"); + get.addHeader("Accept", "application/json"); + CloseableHttpClient httpclient = HttpClients.createDefault(); + CloseableHttpResponse response = httpclient.execute(get); + assertEquals(response.getStatusLine().getStatusCode(), 200); + assertNotNull(response.getEntity()); + + HttpEntity entity = response.getEntity(); + InputStream content = entity.getContent(); + BufferedReader reader = new BufferedReader(new InputStreamReader( + content)); + String line; + StringBuffer str = new StringBuffer(); + while ((line = reader.readLine()) != null) { + str.append(line); + } + + try { + jObject = new JSONObject(str.toString()); + jArray = jObject.getJSONArray("People"); + assertEquals(jArray.length(), 10); + } catch (JSONException e) { + fail(" Rest Request ::" + currentOperation + " :: should not have thrown JSONException "); + } + } + + //5. Get keys - List all keys in region + { + + HttpGet get = new HttpGet(restEndpoint + "/People/keys"); + get.addHeader("Content-Type", "application/json"); + get.addHeader("Accept", "application/json"); + CloseableHttpClient httpclient = HttpClients.createDefault(); + CloseableHttpResponse response = httpclient.execute(get); + assertEquals(response.getStatusLine().getStatusCode(), 200); + assertNotNull(response.getEntity()); + + HttpEntity entity = response.getEntity(); + InputStream content = entity.getContent(); + BufferedReader reader = new BufferedReader(new InputStreamReader( + content)); + String line; + StringBuffer str = new StringBuffer(); + while ((line = reader.readLine()) != null) { + str.append(line); + } + + try { + jObject = new JSONObject(str.toString()); + jArray = jObject.getJSONArray("keys"); + assertEquals(jArray.length(), 16); + } catch (JSONException e) { + fail(" Rest Request ::" + currentOperation + " :: should not have thrown JSONException "); + } + } + + //6. Get data for specific keys + { + + HttpGet get = new HttpGet(restEndpoint + "/People/1,3,5,7,9,11"); + get.addHeader("Content-Type", "application/json"); + get.addHeader("Accept", "application/json"); + CloseableHttpClient httpclient = HttpClients.createDefault(); + CloseableHttpResponse response = httpclient.execute(get); + assertEquals(response.getStatusLine().getStatusCode(), 200); + assertNotNull(response.getEntity()); + + HttpEntity entity = response.getEntity(); + InputStream content = entity.getContent(); + BufferedReader reader = new BufferedReader(new InputStreamReader( + content)); + String line; + StringBuffer str = new StringBuffer(); + while ((line = reader.readLine()) != null) { + str.append(line); + } + + try { + jObject = new JSONObject(str.toString()); + jArray = jObject.getJSONArray("People"); + assertEquals(jArray.length(), 6); + + } catch (JSONException e) { + fail(" Rest Request ::" + currentOperation + " :: should not have thrown JSONException "); + } + } + }catch ( Exception e ) { + throw new RuntimeException("unexpected exception", e); + } + } + + public static void createRegionInClientCache() { + ClientCache cache = GemFireCacheImpl.getInstance(); + assertNotNull(cache); + ClientRegionFactory crf = cache + .createClientRegionFactory(ClientRegionShortcut.PROXY); + Region region = crf.create(PEOPLE_REGION_NAME); + + } + + public static void createRegionInManager() { + Cache cache = GemFireCacheImpl.getInstance(); + assertNotNull(cache); + + RegionFactory rf = cache + .createRegionFactory(RegionShortcut.REPLICATE); + Region region = rf.create(PEOPLE_REGION_NAME); + } + + public static void createRegionInPeerServer() { + Cache cache = GemFireCacheImpl.getInstance(); + assertNotNull(cache); + + RegionFactory rf = cache + .createRegionFactory(RegionShortcut.REPLICATE); + Region region = rf.create(PEOPLE_REGION_NAME); + } + + /** + * InterOps Test between REST-client, Peer Cache Client and Client Cache + * @throws Exception + */ + + public void testInterOpsWithReplicatedRegion() throws Exception { + + final Host host = Host.getHost(0); + VM locator = host.getVM(0); + VM manager = host.getVM(1); + VM server = host.getVM(2); + VM client = host.getVM(3); + + // start locator + int locatorPort = AvailablePort.getRandomAvailablePort(AvailablePort.SOCKET); + + startLocatorInVM(locator, locatorPort, ""); + + // find locators + String locators = NetworkUtils.getServerHostName(locator.getHost()) + "[" + locatorPort + + "]"; + + // start manager (peer cache) + int managerPort = startManagerInVM(manager,/* groups */null, locators, + new String[] {REGION_NAME}, CacheServer.DEFAULT_LOAD_PROBE); + + //start startCacheServer With RestService enabled + String restEndpoint = (String)server.invoke(RestAPIsAndInterOpsDUnitTest.class, + "startBridgeServerWithRestServiceOnInVM", new Object[] { server , null, locators, new String[] {REGION_NAME}, CacheServer.DEFAULT_LOAD_PROBE }); + + // create a client cache + createClientCacheInVM(client, NetworkUtils.getServerHostName(locator.getHost()), + locatorPort); + + // create region in Manager, peer cache and Client cache nodes + manager.invoke(() -> RestAPIsAndInterOpsDUnitTest.createRegionInManager()); + server.invoke(() -> RestAPIsAndInterOpsDUnitTest.createRegionInPeerServer()); + client.invoke(() -> RestAPIsAndInterOpsDUnitTest.createRegionInClientCache()); + + // do some person puts from clientcache + client.invoke(() -> RestAPIsAndInterOpsDUnitTest.doPutsInClientCache()); + + //TEST: fetch all available REST endpoints + RestAPIsAndInterOpsDUnitTest.fetchRestServerEndpoints(restEndpoint); + + // Controller VM - config REST Client and make HTTP calls + RestAPIsAndInterOpsDUnitTest.doGetsUsingRestApis(restEndpoint); + + //update Data using REST APIs + RestAPIsAndInterOpsDUnitTest.doUpdatesUsingRestApis(restEndpoint); + + client.invoke(() -> RestAPIsAndInterOpsDUnitTest.verifyUpdatesInClientCache()); + + //Querying + RestAPIsAndInterOpsDUnitTest.doQueryOpsUsingRestApis(restEndpoint); + + // stop the client and make sure the bridge server notifies + // stopBridgeMemberVM(client); + helper.closeCache(locator); + helper.closeCache(manager); + helper.closeCache(server); + helper.closeCache(client); + + } + + private void createClientCacheInVM(VM vm, final String host, final int port) throws Exception { + SerializableRunnable connect = new SerializableRunnable( + "Start Cache client") { + public void run() { + // Connect using the GemFire locator and create a Caching_Proxy cache + ClientCache c = new ClientCacheFactory().setPdxReadSerialized(true).addPoolLocator(host, port) + .create(); + + Region r = c.createClientRegionFactory( + ClientRegionShortcut.PROXY).create(REGION_NAME); + } + }; + + if (vm == null) { + connect.run(); + } else { + vm.invoke(connect); + } + } + + private int startManagerInVM(VM vm, final String[] groups, + final String locators, final String[] regions, final ServerLoadProbe probe) { + SerializableCallable connect = new SerializableCallable("Start Manager ") { + public Object call() throws IOException { + Properties props = new Properties(); + props + .setProperty(DistributionConfig.MCAST_PORT_NAME, String.valueOf(0)); + props.setProperty(DistributionConfig.LOCATORS_NAME, locators); + + props.setProperty("jmx-manager", "true"); + props.setProperty("jmx-manager-start", "true"); + props.setProperty(DistributionConfig.JMX_MANAGER_PORT_NAME, "0"); + + final int httpPort = AvailablePortHelper.getRandomAvailableTCPPort(); + //Set REST service related configuration + props.setProperty(DistributionConfig.START_DEV_REST_API_NAME, "true"); + props.setProperty(DistributionConfig.HTTP_SERVICE_BIND_ADDRESS_NAME, "localhost"); + props.setProperty(DistributionConfig.HTTP_SERVICE_PORT_NAME, String.valueOf(httpPort)); + + DistributedSystem ds = getSystem(props); + Cache cache = CacheFactory.create(ds); + AttributesFactory factory = new AttributesFactory(); + + factory.setEnableBridgeConflation(true); + factory.setDataPolicy(DataPolicy.REPLICATE); + RegionAttributes attrs = factory.create(); + for (int i = 0; i < regions.length; i++) { + cache.createRegion(regions[i], attrs); + } + CacheServer server = cache.addCacheServer(); + final int serverPort = AvailablePortHelper.getRandomAvailableTCPPort(); + server.setPort(serverPort); + server.setGroups(groups); + server.setLoadProbe(probe); + server.start(); + + return new Integer(serverPort); + } + }; + Integer port = (Integer) vm.invoke(connect); + return port.intValue(); + } +} http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/9b55879e/geode-assembly/src/test/java/com/gemstone/gemfire/rest/internal/web/controllers/RestAPIsOnGroupsFunctionExecutionDUnitTest.java ---------------------------------------------------------------------- diff --git a/geode-assembly/src/test/java/com/gemstone/gemfire/rest/internal/web/controllers/RestAPIsOnGroupsFunctionExecutionDUnitTest.java b/geode-assembly/src/test/java/com/gemstone/gemfire/rest/internal/web/controllers/RestAPIsOnGroupsFunctionExecutionDUnitTest.java new file mode 100644 index 0000000..1ae3810 --- /dev/null +++ b/geode-assembly/src/test/java/com/gemstone/gemfire/rest/internal/web/controllers/RestAPIsOnGroupsFunctionExecutionDUnitTest.java @@ -0,0 +1,306 @@ +/* + * 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 com.gemstone.gemfire.rest.internal.web.controllers; + +import java.io.BufferedReader; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Random; + +import org.apache.http.HttpEntity; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClients; +import org.json.JSONArray; + +import com.gemstone.gemfire.cache.execute.Function; +import com.gemstone.gemfire.cache.execute.FunctionContext; +import com.gemstone.gemfire.cache.execute.FunctionService; +import com.gemstone.gemfire.distributed.internal.InternalDistributedSystem; +import com.gemstone.gemfire.test.dunit.Host; +import com.gemstone.gemfire.test.dunit.IgnoredException; +import com.gemstone.gemfire.test.dunit.LogWriterUtils; +import com.gemstone.gemfire.test.dunit.SerializableCallable; +import com.gemstone.gemfire.test.dunit.VM; + +public class RestAPIsOnGroupsFunctionExecutionDUnitTest extends RestAPITestBase { + + public RestAPIsOnGroupsFunctionExecutionDUnitTest(String name) { + super(name); + } + + public void setUp() throws Exception { + super.setUp(); + final Host host = Host.getHost(0); + } + + private void registerFunction(VM vm) { + vm.invoke(new SerializableCallable() { + private static final long serialVersionUID = 1L; + + @Override + public Object call() throws Exception { + FunctionService.registerFunction(new OnGroupsFunction()); + return null; + } + }); + } + + static class OnGroupsFunction implements Function { + private static final long serialVersionUID = -1032915440862585532L; + public static final String Id = "OnGroupsFunction"; + public static int invocationCount; + + @Override + public void execute(FunctionContext context) { + LogWriterUtils.getLogWriter().fine("SWAP:1:executing OnGroupsFunction:"+invocationCount); + InternalDistributedSystem ds = InternalDistributedSystem.getConnectedInstance(); + invocationCount++; + ArrayList l = (ArrayList) context.getArguments(); + if (l != null) { + assertFalse(Collections.disjoint(l, ds.getDistributedMember().getGroups())); + } + context.getResultSender().lastResult(Boolean.TRUE); + } + + @Override + public String getId() { + return Id; + } + + @Override + public boolean hasResult() { + return true; + } + + @Override + public boolean optimizeForWrite() { + return false; + } + + @Override + public boolean isHA() { + return false; + } + } + + + public static void executeFunctionThroughRestCall(List restURLs) { + Random randomGenerator = new Random(); + int index = randomGenerator.nextInt(restURLs.size()); + + try { + + CloseableHttpClient httpclient = HttpClients.createDefault(); + CloseableHttpResponse response = null; + HttpPost post = new HttpPost(restURLs.get(index) + "/functions/OnGroupsFunction?onGroups=g0,g1"); + post.addHeader("Content-Type", "application/json"); + post.addHeader("Accept", "application/json"); + LogWriterUtils.getLogWriter().info("Request POST : " + post.toString()); + response = httpclient.execute(post); + + HttpEntity entity = response.getEntity(); + InputStream content = entity.getContent(); + BufferedReader reader = new BufferedReader(new InputStreamReader( + content)); + String line; + StringBuffer sb = new StringBuffer(); + while ((line = reader.readLine()) != null) { + sb.append(line); + } + LogWriterUtils.getLogWriter().info("Response : " + sb.toString()); + + //verify response status code. expected status code is 200 OK. + assertEquals(response.getStatusLine().getStatusCode(), 200); + + + //verify response hasbody flag, expected is true. + assertNotNull(response.getEntity()); + + + response.close(); + + //verify function execution result + JSONArray resultArray = new JSONArray(sb.toString()); + assertEquals(resultArray.length(), 3); + + } catch (Exception e) { + throw new RuntimeException("unexpected exception", e); + } + + } + + public static void executeFunctionOnMemberThroughRestCall(List restURLs) { + Random randomGenerator = new Random(); + int index = randomGenerator.nextInt(restURLs.size()); + + //Testcase-1: Executing function on non-existing group. + final IgnoredException ex = IgnoredException.addIgnoredException("com.gemstone.gemfire.cache.execute.FunctionException"); + try { + + CloseableHttpClient httpclient = HttpClients.createDefault(); + CloseableHttpResponse response = null; + HttpPost post = new HttpPost(restURLs.get(index) + "/functions/OnGroupsFunction?onGroups=no%20such%20group"); + post.addHeader("Content-Type", "application/json"); + post.addHeader("Accept", "application/json"); + response = httpclient.execute(post); + + if ( response.getStatusLine().getStatusCode() == 200 ) { + fail("FunctionException expected : no member(s) are found belonging to the provided group(s)"); + } + } catch (Exception e) { + throw new RuntimeException("unexpected exception", e); + + } finally { + ex.remove(); + } + + //Testcase-2: Executing function on group with args. + + final String FUNCTION_ARGS1 = "{" + + "\"@type\": \"string\"," + + "\"@value\": \"gm\"" + + "}"; + + try { + + CloseableHttpClient httpclient = HttpClients.createDefault(); + CloseableHttpResponse response = null; + HttpPost post = new HttpPost(restURLs.get(index) + "/functions/OnGroupsFunction?onGroups=gm"); + post.addHeader("Content-Type", "application/json"); + post.addHeader("Accept", "application/json"); + response = httpclient.execute(post); + + //verify response status code + assertEquals(response.getStatusLine().getStatusCode(), 200); + + //verify response hasbody flag + assertNotNull(response.getEntity()); + + HttpEntity entity = response.getEntity(); + InputStream content = entity.getContent(); + BufferedReader reader = new BufferedReader(new InputStreamReader( + content)); + String line; + StringBuffer sb = new StringBuffer(); + while ((line = reader.readLine()) != null) { + sb.append(line); + } + response.close(); + + //verify function execution result + JSONArray resultArray = new JSONArray(sb.toString()); + assertEquals(resultArray.length(), 1); + + } catch (Exception e) { + throw new RuntimeException("unexpected exception", e); + } + } + + private void verifyAndResetInvocationCount(VM vm, final int count) { + vm.invoke(new SerializableCallable() { + @Override + public Object call() throws Exception { + OnGroupsFunction f = (OnGroupsFunction) FunctionService.getFunction(OnGroupsFunction.Id); + assertEquals(count, f.invocationCount); + // assert succeeded, reset count + f.invocationCount = 0; + return null; + } + }); + } + + private void resetInvocationCount(VM vm) { + vm.invoke(new SerializableCallable() { + @Override + public Object call() throws Exception { + OnGroupsFunction f = (OnGroupsFunction) FunctionService.getFunction(OnGroupsFunction.Id); + f.invocationCount = 0; + return null; + } + }); + } + + public void testonGroupsExecutionOnAllMembers() { + + List restURLs = new ArrayList(); + //Step-1 : create cache on each VM, this will start HTTP service in embedded mode and deploy REST APIs web app on it. + // Create and configure Region on all VMs. Add Rest end-point into the restURLs list. + + String url1 = (String)vm0.invoke(() -> RestAPITestBase.createCacheWithGroups(vm0, "g0,gm", "TEST_REGION")); + restURLs.add(url1); + + String url2 = (String)vm1.invoke(() -> RestAPITestBase.createCacheWithGroups(vm1, "g1", "TEST_REGION" )); + restURLs.add(url2); + + String url3 = (String)vm2.invoke(() -> RestAPITestBase.createCacheWithGroups(vm2, "g0,g1", "TEST_REGION")); + restURLs.add(url3); + + //Step-2: Register function in all VMs + registerFunction(vm0); + registerFunction(vm1); + registerFunction(vm2); + + //Step-3 : Execute function randomly (in iteration) on all available (per VM) REST end-points and verify its result + for (int i=0; i< 10; i++) + executeFunctionThroughRestCall(restURLs); + + //Verify that each node belonging to specified group has run the function + verifyAndResetInvocationCount(vm0, 10); + verifyAndResetInvocationCount(vm1, 10); + verifyAndResetInvocationCount(vm2, 10); + + restURLs.clear(); + } + + + public void testBasicP2PFunctionSelectedGroup() { + + List restURLs = new ArrayList(); + + //Step-1 : create cache on each VM, this will start HTTP service in embedded mode and deploy REST APIs web app on it. + // Create and configure Region on all VMs. Add Rest end-point into the restURLs list. + String url1 = (String)vm0.invoke(() -> RestAPITestBase.createCacheWithGroups(vm0, "g0,gm", "null" )); + restURLs.add(url1); + + String url2 = (String)vm1.invoke(() -> RestAPITestBase.createCacheWithGroups(vm1, "g1", "null" )); + restURLs.add(url2); + + String url3 = (String)vm2.invoke(() -> RestAPITestBase.createCacheWithGroups(vm2, "g0,g1", "null" )); + restURLs.add(url3); + + //Step-2: Register function in all VMs + registerFunction(vm0); + registerFunction(vm1); + registerFunction(vm2); + + //Step-3 : Execute function randomly (in iteration) on all available (per VM) REST end-points and verify its result + for (int i=0; i< 5; i++) + executeFunctionOnMemberThroughRestCall(restURLs); + + resetInvocationCount(vm0); + resetInvocationCount(vm1); + resetInvocationCount(vm2); + + restURLs.clear(); + } +} +