From cd4a2497b704fed6c2804bc45e1fbb133dd90649 Mon Sep 17 00:00:00 2001 From: John Blum Date: Fri, 7 Aug 2015 01:36:59 -0700 Subject: [PATCH] SGF-423 - Handle improper ClassCastException thrown from SDG's Function Execution interface and annotation-based support when a GemFire Function throws an Exception. Additional refactoring and added serveral unit tests. (cherry picked from commit a806f1540e6fc995410671f34382d14389ace90a) Signed-off-by: John Blum --- ...tFunctionExecutionConfigurationSource.java | 12 +- ...ctionExecutionBeanDefinitionRegistrar.java | 7 +- .../FunctionExecutionConfiguration.java | 6 + ...erBasedExecutionBeanDefinitionBuilder.java | 2 +- .../execution/AbstractFunctionExecution.java | 144 +++++------ .../execution/AbstractFunctionTemplate.java | 87 +++---- .../GemfireFunctionProxyFactoryBean.java | 3 +- .../GemfireOnServerFunctionTemplate.java | 22 +- .../execution/ServerFunctionExecution.java | 11 +- ...owingFunctionExecutionIntegrationTest.java | 143 +++++++++++ ...sedExecutionBeanDefinitionBuilderTest.java | 196 +++++++++++++++ .../AbstractFunctionExecutionTest.java | 199 ++++++++++++++++ .../AbstractFunctionTemplateTest.java | 224 ++++++++++++++++++ .../ExceptionThrowingFunctionExecution.java | 34 +++ ...nctionExecutionIntegrationTest-context.xml | 28 +++ ...xecutionIntegrationTest-server-context.xml | 39 +++ 16 files changed, 1005 insertions(+), 152 deletions(-) create mode 100644 src/test/java/org/springframework/data/gemfire/function/ExceptionThrowingFunctionExecutionIntegrationTest.java create mode 100644 src/test/java/org/springframework/data/gemfire/function/config/ServerBasedExecutionBeanDefinitionBuilderTest.java create mode 100644 src/test/java/org/springframework/data/gemfire/function/execution/AbstractFunctionExecutionTest.java create mode 100644 src/test/java/org/springframework/data/gemfire/function/execution/AbstractFunctionTemplateTest.java create mode 100644 src/test/java/org/springframework/data/gemfire/function/sample/ExceptionThrowingFunctionExecution.java create mode 100644 src/test/resources/org/springframework/data/gemfire/function/ExceptionThrowingFunctionExecutionIntegrationTest-context.xml create mode 100644 src/test/resources/org/springframework/data/gemfire/function/ExceptionThrowingFunctionExecutionIntegrationTest-server-context.xml diff --git a/src/main/java/org/springframework/data/gemfire/function/config/AbstractFunctionExecutionConfigurationSource.java b/src/main/java/org/springframework/data/gemfire/function/config/AbstractFunctionExecutionConfigurationSource.java index 38800c84..1e75d318 100644 --- a/src/main/java/org/springframework/data/gemfire/function/config/AbstractFunctionExecutionConfigurationSource.java +++ b/src/main/java/org/springframework/data/gemfire/function/config/AbstractFunctionExecutionConfigurationSource.java @@ -71,7 +71,9 @@ abstract class AbstractFunctionExecutionConfigurationSource implements FunctionE } public Collection getCandidates(ResourceLoader loader) { - ClassPathScanningCandidateComponentProvider scanner = new FunctionExecutionComponentProvider(getIncludeFilters(),functionExecutionAnnotationTypes); + ClassPathScanningCandidateComponentProvider scanner = new FunctionExecutionComponentProvider( + getIncludeFilters(), getFunctionExecutionAnnotationTypes()); + scanner.setResourceLoader(loader); for (TypeFilter filter : getExcludeFilters()) { @@ -84,9 +86,11 @@ abstract class AbstractFunctionExecutionConfigurationSource implements FunctionE if (logger.isDebugEnabled()) { logger.debug("scanning package " + basePackage); } - Collection components = scanner.findCandidateComponents(basePackage); - for (BeanDefinition definition : components) { - result.add((ScannedGenericBeanDefinition)definition); + + Collection candidateComponents = scanner.findCandidateComponents(basePackage); + + for (BeanDefinition beanDefinition : candidateComponents) { + result.add((ScannedGenericBeanDefinition) beanDefinition); } } diff --git a/src/main/java/org/springframework/data/gemfire/function/config/FunctionExecutionBeanDefinitionRegistrar.java b/src/main/java/org/springframework/data/gemfire/function/config/FunctionExecutionBeanDefinitionRegistrar.java index 9ccbae4a..a4b3d6f0 100644 --- a/src/main/java/org/springframework/data/gemfire/function/config/FunctionExecutionBeanDefinitionRegistrar.java +++ b/src/main/java/org/springframework/data/gemfire/function/config/FunctionExecutionBeanDefinitionRegistrar.java @@ -12,8 +12,6 @@ */ package org.springframework.data.gemfire.function.config; -import java.lang.annotation.Annotation; -import java.util.HashSet; import java.util.Set; import org.springframework.beans.factory.support.BeanDefinitionReaderUtils; @@ -21,7 +19,6 @@ import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.context.annotation.ImportBeanDefinitionRegistrar; import org.springframework.context.annotation.ScannedGenericBeanDefinition; import org.springframework.core.io.DefaultResourceLoader; -import org.springframework.core.io.ResourceLoader; import org.springframework.core.type.AnnotationMetadata; import org.springframework.util.Assert; import org.springframework.util.StringUtils; @@ -52,7 +49,9 @@ class FunctionExecutionBeanDefinitionRegistrar implements ImportBeanDefinitionRe void registerBeanDefinitions(AbstractFunctionExecutionConfigurationSource functionExecutionConfigurationSource, BeanDefinitionRegistry registry) { - for (ScannedGenericBeanDefinition beanDefinition : functionExecutionConfigurationSource.getCandidates(new DefaultResourceLoader())) { + for (ScannedGenericBeanDefinition beanDefinition : functionExecutionConfigurationSource.getCandidates( + new DefaultResourceLoader())) { + String functionExecutionAnnotation = getFunctionExecutionAnnotation(beanDefinition, AnnotationFunctionExecutionConfigurationSource.getFunctionExecutionAnnotationTypeNames()); diff --git a/src/main/java/org/springframework/data/gemfire/function/config/FunctionExecutionConfiguration.java b/src/main/java/org/springframework/data/gemfire/function/config/FunctionExecutionConfiguration.java index b6dff2b8..541d7250 100644 --- a/src/main/java/org/springframework/data/gemfire/function/config/FunctionExecutionConfiguration.java +++ b/src/main/java/org/springframework/data/gemfire/function/config/FunctionExecutionConfiguration.java @@ -31,6 +31,12 @@ class FunctionExecutionConfiguration { private final String annotationType; + /* constructor for testing purposes only! */ + FunctionExecutionConfiguration() { + this.annotationType = null; + this.attributes = null; + } + FunctionExecutionConfiguration(ScannedGenericBeanDefinition beanDefinition, String annotationType) { try { this.annotationType = annotationType; diff --git a/src/main/java/org/springframework/data/gemfire/function/config/ServerBasedExecutionBeanDefinitionBuilder.java b/src/main/java/org/springframework/data/gemfire/function/config/ServerBasedExecutionBeanDefinitionBuilder.java index 87a8338a..82033bab 100644 --- a/src/main/java/org/springframework/data/gemfire/function/config/ServerBasedExecutionBeanDefinitionBuilder.java +++ b/src/main/java/org/springframework/data/gemfire/function/config/ServerBasedExecutionBeanDefinitionBuilder.java @@ -44,7 +44,7 @@ abstract class ServerBasedExecutionBeanDefinitionBuilder extends AbstractFunctio String cache = (String) configuration.getAttribute("cache"); String pool = (String) configuration.getAttribute("pool"); - Assert.state(StringUtils.hasText(cache) && !(StringUtils.hasText(pool)), String.format( + Assert.state(!(StringUtils.hasText(cache) && StringUtils.hasText(pool)), String.format( "invalid configuration for interface %s; cannot specify both 'pool' and 'cache'", configuration.getFunctionExecutionInterface().getName())); diff --git a/src/main/java/org/springframework/data/gemfire/function/execution/AbstractFunctionExecution.java b/src/main/java/org/springframework/data/gemfire/function/execution/AbstractFunctionExecution.java index 84b572b5..33c3f561 100644 --- a/src/main/java/org/springframework/data/gemfire/function/execution/AbstractFunctionExecution.java +++ b/src/main/java/org/springframework/data/gemfire/function/execution/AbstractFunctionExecution.java @@ -29,22 +29,27 @@ import com.gemstone.gemfire.cache.execute.FunctionService; import com.gemstone.gemfire.cache.execute.ResultCollector; /** - * Base class for * Creating a GemFire {@link Execution} using {@link FunctionService} - * Protected setters support method chaining - * @author David Turanski + * Base class for * Creating a GemFire {@link Execution} using {@link FunctionService}. Protected setters support + * method chaining. + * + * @author David Turanski + * @author John Blum */ - abstract class AbstractFunctionExecution { private final static String NO_RESULT_MESSAGE = "Cannot return any result as the Function#hasResult() is false"; + private long timeout; + + private Function function; + protected final Log logger = LogFactory.getLog(this.getClass()); - private volatile ResultCollector resultCollector; private Object[] args; - private Function function; + + private volatile ResultCollector resultCollector; + private String functionId; - private long timeout; public AbstractFunctionExecution(Function function, Object... args) { Assert.notNull(function, "function cannot be null"); @@ -62,22 +67,22 @@ abstract class AbstractFunctionExecution { AbstractFunctionExecution() { } - ResultCollector getCollector() { - return resultCollector; - } - Object[] getArgs() { return args; } - String getFunctionId() { - return functionId; + ResultCollector getCollector() { + return resultCollector; } Function getFunction() { return function; } + String getFunctionId() { + return functionId; + } + long getTimeout() { return timeout; } @@ -88,33 +93,31 @@ abstract class AbstractFunctionExecution { @SuppressWarnings("unchecked") Iterable execute(Boolean returnResult) { - Execution execution = this.getExecution(); - if (getKeys() != null) { - execution = execution.withFilter(getKeys()); - } - if (getCollector() != null) { - execution = execution.withCollector(getCollector()); - } - - ResultCollector resultCollector = null; + Execution execution = getExecution(); execution = execution.withArgs(getArgs()); + execution = (getCollector() == null ? execution : execution.withCollector(getCollector())); + execution = (getKeys() == null ? execution : execution.withFilter(getKeys())); + + ResultCollector resultCollector; if (isRegisteredFunction()) { - resultCollector = (ResultCollector) execution.execute(functionId); - } else { - resultCollector = (ResultCollector) execution.execute(function); + resultCollector = execution.execute(functionId); + } + else { + resultCollector = execution.execute(function); + if (!function.hasResult()) { - return (Iterable) null; + return null; } } - + if (!returnResult) { - return (Iterable) null; + return null; } if (logger.isDebugEnabled()) { - logger.debug("using ResultsCollector:" + resultCollector.getClass().getName()); + logger.debug("using ResultsCollector " + resultCollector.getClass().getName()); } Iterable results = null; @@ -123,41 +126,53 @@ abstract class AbstractFunctionExecution { if (this.timeout > 0) { try { results = (Iterable) resultCollector.getResult(this.timeout, TimeUnit.MILLISECONDS); - } catch (FunctionException e) { - throw new RuntimeException(e); - } catch (InterruptedException e) { + } + catch (FunctionException e) { throw new RuntimeException(e); } - } else { - + catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + else { results = (Iterable) resultCollector.getResult(); } return replaceSingletonNullCollectionWithEmptyList(results); - - } catch (FunctionException e) { - //TODO: Come up with a better way to determine that the function should not return a result; + } + catch (FunctionException e) { + //TODO Come up with a better way to determine that the function should not return a result; if (!e.getMessage().equals(NO_RESULT_MESSAGE)) { throw e; } } - + return results; } + @SuppressWarnings("unchecked") T executeAndExtract() { - Iterable results = this.execute(); + Iterable results = execute(); + if (results == null || !results.iterator().hasNext()) { return null; } - return results.iterator().next(); + Object result = results.iterator().next(); + + if (result instanceof Throwable) { + throw new FunctionException(String.format("Execution of Function %1$s failed", + (function != null ? function.getClass().getName() : String.format("with ID '%1$s'", functionId))), + (Throwable) result); + } + + return (T) result; } protected abstract Execution getExecution(); - protected AbstractFunctionExecution setFunctionId(String functionId) { - this.functionId = functionId; + protected AbstractFunctionExecution setArgs(Object... args) { + this.args = args; return this; } @@ -166,17 +181,8 @@ abstract class AbstractFunctionExecution { return this; } - protected AbstractFunctionExecution setArgs(Object... args) { - this.args = args; - return this; - } - - protected Set getKeys() { - return null; - } - - protected AbstractFunctionExecution setTimeout(long timeout) { - this.timeout = timeout; + protected AbstractFunctionExecution setFunctionId(String functionId) { + this.functionId = functionId; return this; } @@ -185,29 +191,33 @@ abstract class AbstractFunctionExecution { return this; } - /** - * @return - */ + protected AbstractFunctionExecution setTimeout(long timeout) { + this.timeout = timeout; + return this; + } + + protected Set getKeys() { + return null; + } + private boolean isRegisteredFunction() { return function == null; } private Iterable replaceSingletonNullCollectionWithEmptyList(Iterable results) { - if (results == null) { - return results; - } - Iterator it = results.iterator(); + if (results != null) { + Iterator it = results.iterator(); - if (!it.hasNext()) { - return results; - } + if (!it.hasNext()) { + return results; + } - if (it.next() == null && !it.hasNext()) { - return new ArrayList(); + if (it.next() == null && !it.hasNext()) { + return new ArrayList(); + } } return results; - } -} \ No newline at end of file +} diff --git a/src/main/java/org/springframework/data/gemfire/function/execution/AbstractFunctionTemplate.java b/src/main/java/org/springframework/data/gemfire/function/execution/AbstractFunctionTemplate.java index 213e15e2..74b44f1f 100644 --- a/src/main/java/org/springframework/data/gemfire/function/execution/AbstractFunctionTemplate.java +++ b/src/main/java/org/springframework/data/gemfire/function/execution/AbstractFunctionTemplate.java @@ -15,100 +15,79 @@ package org.springframework.data.gemfire.function.execution; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; -import com.gemstone.gemfire.cache.execute.Execution; import com.gemstone.gemfire.cache.execute.Function; import com.gemstone.gemfire.cache.execute.ResultCollector; /** - * - * The base class for Gemfire function templates used to invoke Gemfire functions - * @author David Turanski + * The base class for GemFire FunctionTemplates used to invoke GemFire Functions. * + * @author David Turanski + * @author John Blum + * @see com.gemstone.gemfire.cache.execute.Function + * @see com.gemstone.gemfire.cache.execute.ResultCollector */ -abstract class AbstractFunctionTemplate implements GemfireFunctionOperations { +abstract class AbstractFunctionTemplate implements GemfireFunctionOperations { protected Log log = LogFactory.getLog(this.getClass()); - + protected long timeout; + protected volatile ResultCollector resultCollector; - + @Override public Iterable execute(Function function, Object... args) { - AbstractFunctionExecution functionExecution = getFunctionExecution() - .setArgs(args) - .setFunction(function); - return execute(functionExecution); + return execute(getFunctionExecution().setArgs(args).setFunction(function)); } @Override public T executeAndExtract(Function function, Object... args) { - AbstractFunctionExecution functionExecution = getFunctionExecution() - .setArgs(args) - .setFunction(function); - - return this. executeAndExtract(functionExecution); + return executeAndExtract(getFunctionExecution().setArgs(args).setFunction(function)); } @Override public Iterable execute(String functionId, Object... args) { - AbstractFunctionExecution functionExecution = getFunctionExecution() - .setArgs(args) - .setFunctionId(functionId); - return execute(functionExecution); + return execute(getFunctionExecution().setArgs(args).setFunctionId(functionId)); } - - @Override - public void executeWithNoResult(String functionId, Object... args) { - AbstractFunctionExecution functionExecution = getFunctionExecution() - .setArgs(args) - .setFunctionId(functionId); - execute(functionExecution,false); - } - + @Override public T executeAndExtract(String functionId, Object... args) { - AbstractFunctionExecution functionExecution = getFunctionExecution() - .setArgs(args) - .setFunctionId(functionId); - return this.executeAndExtract(functionExecution); + return executeAndExtract(getFunctionExecution().setArgs(args).setFunctionId(functionId)); + } + + @Override + public void executeWithNoResult(String functionId, Object... args) { + execute(getFunctionExecution().setArgs(args).setFunctionId(functionId), false); } @Override public T execute(GemfireFunctionCallback callback) { - Execution execution = getFunctionExecution().getExecution(); - return callback.doInGemfire(execution); + return callback.doInGemfire(getFunctionExecution().getExecution()); } - - + protected Iterable execute(AbstractFunctionExecution execution) { - execution.setTimeout(timeout) - .setResultCollector(resultCollector); - return execution.execute(); + return execution.setTimeout(timeout).setResultCollector(resultCollector).execute(); } protected Iterable execute(AbstractFunctionExecution execution, boolean returnResult) { - execution.setTimeout(timeout) - .setResultCollector(resultCollector); - return execution.execute(returnResult); + return execution.setTimeout(timeout).setResultCollector(resultCollector).execute(returnResult); } - + protected T executeAndExtract(AbstractFunctionExecution execution) { - execution.setTimeout(timeout) - .setResultCollector(resultCollector); - return execution.executeAndExtract(); + return execution.setTimeout(timeout).setResultCollector(resultCollector).executeAndExtract(); } - - public void setTimeout(long timeout) { - this.timeout = timeout; - } - + public void setResultCollector(ResultCollector resultCollector) { this.resultCollector = resultCollector; } - + public ResultCollector getResultCollector() { return this.resultCollector; } - + + public void setTimeout(long timeout) { + this.timeout = timeout; + } + protected abstract AbstractFunctionExecution getFunctionExecution(); + } diff --git a/src/main/java/org/springframework/data/gemfire/function/execution/GemfireFunctionProxyFactoryBean.java b/src/main/java/org/springframework/data/gemfire/function/execution/GemfireFunctionProxyFactoryBean.java index 01d6aa4b..d8d2fed0 100644 --- a/src/main/java/org/springframework/data/gemfire/function/execution/GemfireFunctionProxyFactoryBean.java +++ b/src/main/java/org/springframework/data/gemfire/function/execution/GemfireFunctionProxyFactoryBean.java @@ -75,9 +75,8 @@ public class GemfireFunctionProxyFactoryBean implements FactoryBean, Met @Override public Object invoke(MethodInvocation invocation) throws Throwable { - if (AopUtils.isToStringMethod(invocation.getMethod())) { - return "Gemfire function proxy for service interface [" + this.functionExecutionInterface + "]"; + return "GemFire Function Proxy for service interface [" + this.functionExecutionInterface + "]"; } if (logger.isDebugEnabled()) { diff --git a/src/main/java/org/springframework/data/gemfire/function/execution/GemfireOnServerFunctionTemplate.java b/src/main/java/org/springframework/data/gemfire/function/execution/GemfireOnServerFunctionTemplate.java index 16e459d2..79f28b0b 100644 --- a/src/main/java/org/springframework/data/gemfire/function/execution/GemfireOnServerFunctionTemplate.java +++ b/src/main/java/org/springframework/data/gemfire/function/execution/GemfireOnServerFunctionTemplate.java @@ -18,30 +18,26 @@ import com.gemstone.gemfire.cache.client.Pool; /** * @author David Turanski - * + * @author John Blum */ public class GemfireOnServerFunctionTemplate extends AbstractFunctionTemplate { - - private final RegionService cache; - private final Pool pool; - - public GemfireOnServerFunctionTemplate (RegionService cache) { + private final Pool pool; + private final RegionService cache; + + public GemfireOnServerFunctionTemplate(RegionService cache) { this.cache = cache; this.pool = null; } - public GemfireOnServerFunctionTemplate (Pool pool) { - this.pool = pool; + public GemfireOnServerFunctionTemplate(Pool pool) { this.cache = null; + this.pool = pool; } - + @Override protected AbstractFunctionExecution getFunctionExecution() { - if (this.pool == null) { - return new ServerFunctionExecution(this.cache); - } - return new PoolServerFunctionExecution(this.pool); + return (pool != null ? new PoolServerFunctionExecution(this.pool) : new ServerFunctionExecution(this.cache)); } } diff --git a/src/main/java/org/springframework/data/gemfire/function/execution/ServerFunctionExecution.java b/src/main/java/org/springframework/data/gemfire/function/execution/ServerFunctionExecution.java index 9a311269..4a8314d1 100644 --- a/src/main/java/org/springframework/data/gemfire/function/execution/ServerFunctionExecution.java +++ b/src/main/java/org/springframework/data/gemfire/function/execution/ServerFunctionExecution.java @@ -24,20 +24,17 @@ import com.gemstone.gemfire.cache.execute.FunctionService; * */ class ServerFunctionExecution extends AbstractFunctionExecution { - - private RegionService regionService; + private final RegionService regionService; - public ServerFunctionExecution(RegionService regionService) { - super(); - Assert.notNull(regionService,"regionService cannot be null"); + Assert.notNull(regionService, "RegionService must not be null"); this.regionService = regionService; } - - + @Override protected Execution getExecution() { return FunctionService.onServer(this.regionService); } + } diff --git a/src/test/java/org/springframework/data/gemfire/function/ExceptionThrowingFunctionExecutionIntegrationTest.java b/src/test/java/org/springframework/data/gemfire/function/ExceptionThrowingFunctionExecutionIntegrationTest.java new file mode 100644 index 00000000..5803aa1c --- /dev/null +++ b/src/test/java/org/springframework/data/gemfire/function/ExceptionThrowingFunctionExecutionIntegrationTest.java @@ -0,0 +1,143 @@ +/* + * Copyright 2010-2013 the original author or authors. + * + * Licensed 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.springframework.data.gemfire.function; + +import static org.hamcrest.CoreMatchers.containsString; +import static org.hamcrest.CoreMatchers.isA; + +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.TimeUnit; + +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.gemfire.fork.ServerProcess; +import org.springframework.data.gemfire.function.annotation.GemfireFunction; +import org.springframework.data.gemfire.function.sample.ExceptionThrowingFunctionExecution; +import org.springframework.data.gemfire.process.ProcessExecutor; +import org.springframework.data.gemfire.process.ProcessWrapper; +import org.springframework.data.gemfire.test.support.FileSystemUtils; +import org.springframework.data.gemfire.test.support.ThreadUtils; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.util.Assert; + +import com.gemstone.gemfire.cache.execute.FunctionAdapter; +import com.gemstone.gemfire.cache.execute.FunctionContext; +import com.gemstone.gemfire.cache.execute.FunctionException; + +/** + * The ExceptionThrowingFunctionExecutionIntegrationTest class is a test suite of test cases testing the invocation + * of a GemFire Function using Spring Data GemFire Function Execution Annotation support when that Function throws + * an Exception. + * + * @author John Blum + * @see org.junit.Rule + * @see org.junit.Test + * @see org.junit.runner.RunWith + * @see org.springframework.data.gemfire.fork.ServerProcess + * @see org.springframework.data.gemfire.function.annotation.GemfireFunction + * @see org.springframework.data.gemfire.function.sample.ExceptionThrowingFunctionExecution + * @see org.springframework.data.gemfire.process.ProcessExecutor + * @see org.springframework.data.gemfire.process.ProcessWrapper + * @see org.springframework.test.context.ContextConfiguration + * @see org.springframework.test.context.junit4.SpringJUnit4ClassRunner + * @since 1.7.0 + */ +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration +@SuppressWarnings("unused") +public class ExceptionThrowingFunctionExecutionIntegrationTest { + + private static ProcessWrapper serverProcess; + + @Rule + public ExpectedException expectedException = ExpectedException.none(); + + @Autowired + private ExceptionThrowingFunctionExecution exceptionThrowingFunctionExecution; + + @BeforeClass + public static void setup() throws IOException { + String serverName = ExceptionThrowingFunctionExecutionIntegrationTest.class.getSimpleName().concat("Server"); + + File serverWorkingDirectory = new File(FileSystemUtils.WORKING_DIRECTORY, serverName.toLowerCase()); + + Assert.isTrue(serverWorkingDirectory.isDirectory() || serverWorkingDirectory.mkdirs()); + + List arguments = new ArrayList(); + + arguments.add("-Dgemfire.name=" + serverName); + arguments.add(ExceptionThrowingFunctionExecutionIntegrationTest.class.getName().replaceAll("\\.", + File.separator) + .concat("-server-context.xml")); + + serverProcess = ProcessExecutor.launch(serverWorkingDirectory, ServerProcess.class, + arguments.toArray(new String[arguments.size()])); + + waitForServerStart(TimeUnit.SECONDS.toMillis(20)); + + System.out.println("GemFire Cache Server Process for ClientCache Indexing should be running..."); + } + + private static void waitForServerStart(final long milliseconds) { + ThreadUtils.timedWait(milliseconds, TimeUnit.MILLISECONDS.toMillis(500), new ThreadUtils.WaitCondition() { + private File serverPidControlFile = new File(serverProcess.getWorkingDirectory(), + ServerProcess.getServerProcessControlFilename()); + + @Override public boolean waiting() { + return !serverPidControlFile.isFile(); + } + }); + } + + @AfterClass + public static void tearDown() { + serverProcess.shutdown(); + + if (Boolean.valueOf(System.getProperty("spring.gemfire.fork.clean", Boolean.TRUE.toString()))) { + org.springframework.util.FileSystemUtils.deleteRecursively(serverProcess.getWorkingDirectory()); + } + } + + @Test + public void exceptionThrowingFunctionExecutionRethrowsException() { + expectedException.expect(FunctionException.class); + expectedException.expectCause(isA(IllegalArgumentException.class)); + expectedException.expectMessage(containsString("Execution of Function with ID 'exceptionThrowingFunction' failed")); + exceptionThrowingFunctionExecution.exceptionThrowingFunction(); + } + + public static class ExceptionThrowingFunction extends FunctionAdapter { + @Override + public String getId() { + return "exceptionThrowingFunction"; + } + @Override + public void execute(final FunctionContext context) { + context.getResultSender().sendException(new IllegalArgumentException("TEST")); + } + } + +} diff --git a/src/test/java/org/springframework/data/gemfire/function/config/ServerBasedExecutionBeanDefinitionBuilderTest.java b/src/test/java/org/springframework/data/gemfire/function/config/ServerBasedExecutionBeanDefinitionBuilderTest.java new file mode 100644 index 00000000..cb7d1fea --- /dev/null +++ b/src/test/java/org/springframework/data/gemfire/function/config/ServerBasedExecutionBeanDefinitionBuilderTest.java @@ -0,0 +1,196 @@ +/* + * Copyright 2010-2013 the original author or authors. + * + * Licensed 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.springframework.data.gemfire.function.config; + +import static org.hamcrest.CoreMatchers.containsString; +import static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.notNullValue; +import static org.hamcrest.CoreMatchers.nullValue; +import static org.junit.Assert.assertThat; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.stubbing.Answer; +import org.springframework.beans.factory.config.RuntimeBeanReference; +import org.springframework.beans.factory.support.AbstractBeanDefinition; +import org.springframework.beans.factory.support.BeanDefinitionBuilder; +import org.springframework.data.gemfire.config.GemfireConstants; + +/** + * The ServerBasedExecutionBeanDefinitionBuilderTest class is test suite of test cases testing the contract + * and functionality of the ServerBasedExecutionBeanDefinitionBuilder class. + * + * @author John Blum + * @see org.junit.Test + * @see org.mockito.Mockito + * @see org.springframework.data.gemfire.function.config.ServerBasedExecutionBeanDefinitionBuilder + * @since 1.7.0 + */ +public class ServerBasedExecutionBeanDefinitionBuilderTest { + + @Rule + public ExpectedException expectedException = ExpectedException.none(); + + @Test + @SuppressWarnings("unchecked") + public void getGemfireFunctionOperationsBeanDefinitionBuilder() { + FunctionExecutionConfiguration mockFunctionExecutionConfiguration = + mock(FunctionExecutionConfiguration.class, "MockFunctionExecutionConfiguration"); + + when(mockFunctionExecutionConfiguration.getAttribute(eq("cache"))).thenReturn(null); + when(mockFunctionExecutionConfiguration.getAttribute(eq("pool"))).thenReturn(" "); + when(mockFunctionExecutionConfiguration.getFunctionExecutionInterface()).thenAnswer(new Answer>() { + @Override public Class answer(final InvocationOnMock invocation) throws Throwable { + return Object.class; + } + }); + + ServerBasedExecutionBeanDefinitionBuilder builder = new ServerBasedExecutionBeanDefinitionBuilder(mockFunctionExecutionConfiguration) { + @Override protected Class getGemfireFunctionOperationsClass() { + return Object.class; + } + }; + + BeanDefinitionBuilder beanDefinitionBuilder = builder.getGemfireFunctionOperationsBeanDefinitionBuilder(null); + + assertThat(beanDefinitionBuilder, is(notNullValue())); + + AbstractBeanDefinition beanDefinition = beanDefinitionBuilder.getRawBeanDefinition(); + + assertThat(beanDefinition, is(notNullValue())); + assertThat((Class) beanDefinition.getBeanClass(), is(equalTo(Object.class))); + assertThat(String.valueOf(beanDefinition.getConstructorArgumentValues() + .getArgumentValue(0, RuntimeBeanReference.class).getValue()), + containsString(GemfireConstants.DEFAULT_GEMFIRE_CACHE_NAME)); + + verify(mockFunctionExecutionConfiguration, times(1)).getAttribute(eq("cache")); + verify(mockFunctionExecutionConfiguration, times(1)).getAttribute(eq("pool")); + verify(mockFunctionExecutionConfiguration, times(1)).getFunctionExecutionInterface(); + } + + @Test + @SuppressWarnings("unchecked") + public void getGemfireFunctionOperationsBeanDefinitionBuilderWithCache() { + FunctionExecutionConfiguration mockFunctionExecutionConfiguration = + mock(FunctionExecutionConfiguration.class, "MockFunctionExecutionConfiguration"); + + when(mockFunctionExecutionConfiguration.getAttribute(eq("cache"))).thenReturn("TestCache"); + when(mockFunctionExecutionConfiguration.getAttribute(eq("pool"))).thenReturn(" "); + when(mockFunctionExecutionConfiguration.getFunctionExecutionInterface()).thenAnswer(new Answer>() { + @Override public Class answer(final InvocationOnMock invocation) throws Throwable { + return Object.class; + } + }); + + ServerBasedExecutionBeanDefinitionBuilder builder = new ServerBasedExecutionBeanDefinitionBuilder(mockFunctionExecutionConfiguration) { + @Override protected Class getGemfireFunctionOperationsClass() { + return Object.class; + } + }; + + BeanDefinitionBuilder beanDefinitionBuilder = builder.getGemfireFunctionOperationsBeanDefinitionBuilder(null); + + assertThat(beanDefinitionBuilder, is(notNullValue())); + + AbstractBeanDefinition beanDefinition = beanDefinitionBuilder.getRawBeanDefinition(); + + assertThat(beanDefinition, is(notNullValue())); + assertThat((Class) beanDefinition.getBeanClass(), is(equalTo(Object.class))); + assertThat(String.valueOf(beanDefinition.getConstructorArgumentValues() + .getArgumentValue(0, RuntimeBeanReference.class).getValue()), containsString("TestCache")); + + verify(mockFunctionExecutionConfiguration, times(1)).getAttribute(eq("cache")); + verify(mockFunctionExecutionConfiguration, times(1)).getAttribute(eq("pool")); + verify(mockFunctionExecutionConfiguration, times(1)).getFunctionExecutionInterface(); + } + + @Test + @SuppressWarnings("unchecked") + public void getGemfireFunctionOperationsBeanDefinitionBuilderWithPool() { + FunctionExecutionConfiguration mockFunctionExecutionConfiguration = + mock(FunctionExecutionConfiguration.class, "MockFunctionExecutionConfiguration"); + + when(mockFunctionExecutionConfiguration.getAttribute(eq("cache"))).thenReturn(null); + when(mockFunctionExecutionConfiguration.getAttribute(eq("pool"))).thenReturn("TestPool"); + when(mockFunctionExecutionConfiguration.getFunctionExecutionInterface()).thenAnswer(new Answer>() { + @Override public Class answer(final InvocationOnMock invocation) throws Throwable { + return Object.class; + } + }); + + ServerBasedExecutionBeanDefinitionBuilder builder = new ServerBasedExecutionBeanDefinitionBuilder(mockFunctionExecutionConfiguration) { + @Override protected Class getGemfireFunctionOperationsClass() { + return Object.class; + } + }; + + BeanDefinitionBuilder beanDefinitionBuilder = builder.getGemfireFunctionOperationsBeanDefinitionBuilder(null); + + assertThat(beanDefinitionBuilder, is(notNullValue())); + + AbstractBeanDefinition beanDefinition = beanDefinitionBuilder.getRawBeanDefinition(); + + assertThat(beanDefinition, is(notNullValue())); + assertThat((Class) beanDefinition.getBeanClass(), is(equalTo(Object.class))); + assertThat(String.valueOf(beanDefinition.getConstructorArgumentValues() + .getArgumentValue(0, RuntimeBeanReference.class).getValue()), containsString("TestPool")); + + verify(mockFunctionExecutionConfiguration, times(1)).getAttribute(eq("cache")); + verify(mockFunctionExecutionConfiguration, times(1)).getAttribute(eq("pool")); + verify(mockFunctionExecutionConfiguration, times(1)).getFunctionExecutionInterface(); + } + + @Test + @SuppressWarnings("unchecked") + public void getGemfireFunctionOperationsBeanDefinitionBuilderWithCacheAndPool() { + FunctionExecutionConfiguration mockFunctionExecutionConfiguration = + mock(FunctionExecutionConfiguration.class, "MockFunctionExecutionConfiguration"); + + when(mockFunctionExecutionConfiguration.getAttribute(eq("cache"))).thenReturn("TestCache"); + when(mockFunctionExecutionConfiguration.getAttribute(eq("pool"))).thenReturn("TestPool"); + when(mockFunctionExecutionConfiguration.getFunctionExecutionInterface()).thenAnswer(new Answer>() { + @Override public Class answer(final InvocationOnMock invocation) throws Throwable { + return Object.class; + } + }); + + ServerBasedExecutionBeanDefinitionBuilder builder = new ServerBasedExecutionBeanDefinitionBuilder(mockFunctionExecutionConfiguration) { + @Override protected Class getGemfireFunctionOperationsClass() { + return Object.class; + } + }; + + expectedException.expect(IllegalStateException.class); + expectedException.expectCause(is(nullValue(Throwable.class))); + expectedException.expectMessage(is(equalTo("invalid configuration for interface java.lang.Object;" + + " cannot specify both 'pool' and 'cache'"))); + builder.getGemfireFunctionOperationsBeanDefinitionBuilder(null); + + verify(mockFunctionExecutionConfiguration, times(1)).getAttribute(eq("cache")); + verify(mockFunctionExecutionConfiguration, times(1)).getAttribute(eq("pool")); + verify(mockFunctionExecutionConfiguration, times(1)).getFunctionExecutionInterface(); + } + +} diff --git a/src/test/java/org/springframework/data/gemfire/function/execution/AbstractFunctionExecutionTest.java b/src/test/java/org/springframework/data/gemfire/function/execution/AbstractFunctionExecutionTest.java new file mode 100644 index 00000000..b7e95b35 --- /dev/null +++ b/src/test/java/org/springframework/data/gemfire/function/execution/AbstractFunctionExecutionTest.java @@ -0,0 +1,199 @@ +/* + * Copyright 2010-2013 the original author or authors. + * + * Licensed 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.springframework.data.gemfire.function.execution; + +import static org.hamcrest.CoreMatchers.containsString; +import static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.isA; +import static org.hamcrest.CoreMatchers.notNullValue; +import static org.hamcrest.CoreMatchers.nullValue; +import static org.junit.Assert.assertThat; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Set; +import java.util.concurrent.TimeUnit; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.runners.MockitoJUnitRunner; + +import com.gemstone.gemfire.cache.execute.Execution; +import com.gemstone.gemfire.cache.execute.Function; +import com.gemstone.gemfire.cache.execute.FunctionException; +import com.gemstone.gemfire.cache.execute.ResultCollector; + +/** + * The AbstractFunctionExecutionTest class is a test suite of test cases testing the contract and functionality + * of the AbstractFunctionExecution class. + * + * @author John Blum + * @see org.junit.Test + * @see org.junit.runner.RunWith + * @see org.mockito.Mockito + * @see org.mockito.runners.MockitoJUnitRunner + * @see org.springframework.data.gemfire.function.execution.AbstractFunctionExecution + * @see com.gemstone.gemfire.cache.execute.Execution + * @since 1.7.0 + */ +@RunWith(MockitoJUnitRunner.class) +public class AbstractFunctionExecutionTest { + + @Rule + public ExpectedException expectedException = ExpectedException.none(); + + @Mock + private Execution mockExecution; + + // TODO add more tests!!! + + @Test + @SuppressWarnings("unchecked") + public void executeWithResults() throws Exception { + Object[] args = { "one", "two", "three" }; + List results = Arrays.asList(args); + + Function mockFunction = mock(Function.class, "MockFunction"); + ResultCollector mockResultCollector = mock(ResultCollector.class, "MockResultCollector"); + + when(mockExecution.withArgs(eq(args))).thenReturn(mockExecution); + when(mockExecution.execute(eq(mockFunction))).thenReturn(mockResultCollector); + when(mockFunction.hasResult()).thenReturn(true); + when(mockResultCollector.getResult(500, TimeUnit.MILLISECONDS)).thenReturn(results); + + AbstractFunctionExecution functionExecution = new AbstractFunctionExecution() { + @Override protected Execution getExecution() { + return mockExecution; + } + }; + + Iterable actualResults = functionExecution.setFunction(mockFunction) + .setArgs(args).setTimeout(500).execute(); + + assertThat(actualResults, is(notNullValue())); + assertThat(actualResults, is(equalTo((Iterable) results))); + + verify(mockExecution, times(1)).withArgs(eq(args)); + verify(mockExecution, never()).withCollector(any(ResultCollector.class)); + verify(mockExecution, never()).withFilter(any(Set.class)); + verify(mockExecution, times(1)).execute(eq(mockFunction)); + verify(mockExecution, never()).execute(any(String.class)); + verify(mockResultCollector, times(1)).getResult(500, TimeUnit.MILLISECONDS); + verify(mockResultCollector, never()).getResult(); + } + + @Test + public void executeAndExtractWithSingleResult() { + final List results = Collections.singletonList("test"); + + AbstractFunctionExecution functionExecution = new AbstractFunctionExecution() { + @Override protected Execution getExecution() { + return mockExecution; + } + + @SuppressWarnings("unchecked") + @Override Iterable execute() { + return (Iterable) results; + } + }; + + assertThat(String.valueOf(functionExecution.executeAndExtract()), is(equalTo("test"))); + } + + @Test + public void executeAndExtractWithMultipleResults() { + final List results = Arrays.asList("one", "two", "three"); + + AbstractFunctionExecution functionExecution = new AbstractFunctionExecution() { + @Override protected Execution getExecution() { + return mockExecution; + } + + @SuppressWarnings("unchecked") + @Override Iterable execute() { + return (Iterable) results; + } + }; + + assertThat(String.valueOf(functionExecution.executeAndExtract()), is(equalTo("one"))); + } + + @Test + public void executeAndExtractWithNullResults() { + AbstractFunctionExecution functionExecution = new AbstractFunctionExecution() { + @Override protected Execution getExecution() { + return mockExecution; + } + + @SuppressWarnings("unchecked") + @Override Iterable execute() { + return null; + } + }; + + assertThat(functionExecution.executeAndExtract(), is(nullValue())); + } + + @Test + public void executeAndExtractWithNoResults() { + AbstractFunctionExecution functionExecution = new AbstractFunctionExecution() { + @Override protected Execution getExecution() { + return mockExecution; + } + + @SuppressWarnings("unchecked") + @Override Iterable execute() { + return Collections.emptyList(); + } + }; + + assertThat(functionExecution.executeAndExtract(), is(nullValue())); + } + + @Test + public void executeAndExtractWithThrowsException() { + AbstractFunctionExecution functionExecution = new AbstractFunctionExecution() { + @Override protected Execution getExecution() { + return mockExecution; + } + + @SuppressWarnings("unchecked") + @Override Iterable execute() { + return Collections.singletonList((T) new IllegalArgumentException("test")); + } + }; + + expectedException.expect(FunctionException.class); + expectedException.expectCause(isA(IllegalArgumentException.class)); + expectedException.expectMessage(containsString("Execution of Function with ID 'TestFunction' failed")); + + functionExecution.setFunctionId("TestFunction").executeAndExtract(); + } + +} diff --git a/src/test/java/org/springframework/data/gemfire/function/execution/AbstractFunctionTemplateTest.java b/src/test/java/org/springframework/data/gemfire/function/execution/AbstractFunctionTemplateTest.java new file mode 100644 index 00000000..644db3d7 --- /dev/null +++ b/src/test/java/org/springframework/data/gemfire/function/execution/AbstractFunctionTemplateTest.java @@ -0,0 +1,224 @@ +/* + * Copyright 2010-2013 the original author or authors. + * + * Licensed 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.springframework.data.gemfire.function.execution; + +import static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.notNullValue; +import static org.junit.Assert.assertThat; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.util.Arrays; +import java.util.List; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.runners.MockitoJUnitRunner; + +import com.gemstone.gemfire.cache.execute.Function; +import com.gemstone.gemfire.cache.execute.ResultCollector; + +/** + * The AbstractFunctionTemplateTest class is a test suite of test cases testing the contract and functionality + * of the AbstractFunctionTemplate class. + * + * @author John Blum + * @see org.junit.Test + * @see org.mockito.Mockito + * @see org.springframework.data.gemfire.function.execution.AbstractFunctionExecution + * @see org.springframework.data.gemfire.function.execution.AbstractFunctionTemplate + * @see com.gemstone.gemfire.cache.execute.Function + * @see com.gemstone.gemfire.cache.execute.ResultCollector + * @since 1.7.0 + */ +@RunWith(MockitoJUnitRunner.class) +public class AbstractFunctionTemplateTest { + + @Mock + private AbstractFunctionExecution mockFunctionExecution; + + @Mock + Function mockFunction; + + @Mock + private ResultCollector mockResultCollector; + + @Test + public void executeWithFunctionAndArgs() { + Object[] args = { "test", "testing", "tested" }; + List results = Arrays.asList(args); + + when(mockFunctionExecution.setArgs(args)).thenReturn(mockFunctionExecution); + when(mockFunctionExecution.setFunction(mockFunction)).thenReturn(mockFunctionExecution); + when(mockFunctionExecution.setResultCollector(mockResultCollector)).thenReturn(mockFunctionExecution); + when(mockFunctionExecution.setTimeout(500)).thenReturn(mockFunctionExecution); + when(mockFunctionExecution.execute()).thenReturn(results); + + AbstractFunctionTemplate functionTemplate = new AbstractFunctionTemplate() { + @Override protected AbstractFunctionExecution getFunctionExecution() { + return mockFunctionExecution; + } + }; + + functionTemplate.setResultCollector(mockResultCollector); + functionTemplate.setTimeout(500); + + Iterable actualResults = functionTemplate.execute(mockFunction, args); + + assertThat(functionTemplate.getResultCollector(), is(equalTo(mockResultCollector))); + assertThat(actualResults, is(notNullValue())); + assertThat(actualResults, is(equalTo((Iterable) results))); + + verify(mockFunctionExecution, times(1)).setArgs(args); + verify(mockFunctionExecution, times(1)).setFunction(mockFunction); + verify(mockFunctionExecution, times(1)).setResultCollector(eq(mockResultCollector)); + verify(mockFunctionExecution, times(1)).setTimeout(500); + verify(mockFunctionExecution, times(1)).execute(); + } + + @Test + public void executeAndExtractWithFunctionAndArgs() { + Object[] args = { "test", "testing", "tested" }; + + when(mockFunctionExecution.setArgs(args)).thenReturn(mockFunctionExecution); + when(mockFunctionExecution.setFunction(mockFunction)).thenReturn(mockFunctionExecution); + when(mockFunctionExecution.setResultCollector(mockResultCollector)).thenReturn(mockFunctionExecution); + when(mockFunctionExecution.setTimeout(500)).thenReturn(mockFunctionExecution); + when(mockFunctionExecution.executeAndExtract()).thenReturn(args[0]); + + AbstractFunctionTemplate functionTemplate = new AbstractFunctionTemplate() { + @Override protected AbstractFunctionExecution getFunctionExecution() { + return mockFunctionExecution; + } + }; + + functionTemplate.setResultCollector(mockResultCollector); + functionTemplate.setTimeout(500); + + String result = functionTemplate.executeAndExtract(mockFunction, args); + + assertThat(functionTemplate.getResultCollector(), is(equalTo(mockResultCollector))); + assertThat(result, is(notNullValue())); + assertThat(result, is(equalTo("test"))); + + verify(mockFunctionExecution, times(1)).setArgs(args); + verify(mockFunctionExecution, times(1)).setFunction(mockFunction); + verify(mockFunctionExecution, times(1)).setResultCollector(eq(mockResultCollector)); + verify(mockFunctionExecution, times(1)).setTimeout(500); + verify(mockFunctionExecution, times(1)).executeAndExtract(); + } + + @Test + public void executeWithFunctionIdAndArgs() { + Object[] args = { "test", "testing", "tested" }; + List results = Arrays.asList(args); + + when(mockFunctionExecution.setArgs(args)).thenReturn(mockFunctionExecution); + when(mockFunctionExecution.setFunctionId("TestFunction")).thenReturn(mockFunctionExecution); + when(mockFunctionExecution.setResultCollector(mockResultCollector)).thenReturn(mockFunctionExecution); + when(mockFunctionExecution.setTimeout(500)).thenReturn(mockFunctionExecution); + when(mockFunctionExecution.execute()).thenReturn(results); + + AbstractFunctionTemplate functionTemplate = new AbstractFunctionTemplate() { + @Override protected AbstractFunctionExecution getFunctionExecution() { + return mockFunctionExecution; + } + }; + + functionTemplate.setResultCollector(mockResultCollector); + functionTemplate.setTimeout(500); + + Iterable actualResults = functionTemplate.execute("TestFunction", args); + + assertThat(functionTemplate.getResultCollector(), is(equalTo(mockResultCollector))); + assertThat(actualResults, is(notNullValue())); + assertThat(actualResults, is(equalTo((Iterable) results))); + + verify(mockFunctionExecution, times(1)).setArgs(args); + verify(mockFunctionExecution, times(1)).setFunctionId("TestFunction"); + verify(mockFunctionExecution, times(1)).setResultCollector(eq(mockResultCollector)); + verify(mockFunctionExecution, times(1)).setTimeout(500); + verify(mockFunctionExecution, times(1)).execute(); + } + + @Test + public void executeAndExtractWithFunctionIdAndArgs() { + Object[] args = { "test", "testing", "tested" }; + + when(mockFunctionExecution.setArgs(args)).thenReturn(mockFunctionExecution); + when(mockFunctionExecution.setFunctionId("TestFunction")).thenReturn(mockFunctionExecution); + when(mockFunctionExecution.setResultCollector(mockResultCollector)).thenReturn(mockFunctionExecution); + when(mockFunctionExecution.setTimeout(500)).thenReturn(mockFunctionExecution); + when(mockFunctionExecution.executeAndExtract()).thenReturn(args[0]); + + AbstractFunctionTemplate functionTemplate = new AbstractFunctionTemplate() { + @Override protected AbstractFunctionExecution getFunctionExecution() { + return mockFunctionExecution; + } + }; + + functionTemplate.setResultCollector(mockResultCollector); + functionTemplate.setTimeout(500); + + String result = functionTemplate.executeAndExtract("TestFunction", args); + + assertThat(functionTemplate.getResultCollector(), is(equalTo(mockResultCollector))); + assertThat(result, is(notNullValue())); + assertThat(result, is(equalTo("test"))); + + verify(mockFunctionExecution, times(1)).setArgs(args); + verify(mockFunctionExecution, times(1)).setFunctionId("TestFunction"); + verify(mockFunctionExecution, times(1)).setResultCollector(eq(mockResultCollector)); + verify(mockFunctionExecution, times(1)).setTimeout(500); + verify(mockFunctionExecution, times(1)).executeAndExtract(); + } + + @Test + public void executeWithNoResultWithFunctionIdAndArgs() { + Object[] args = { "test", "testing", "tested" }; + + when(mockFunctionExecution.setArgs(args)).thenReturn(mockFunctionExecution); + when(mockFunctionExecution.setFunctionId("TestFunction")).thenReturn(mockFunctionExecution); + when(mockFunctionExecution.setResultCollector(mockResultCollector)).thenReturn(mockFunctionExecution); + when(mockFunctionExecution.setTimeout(500)).thenReturn(mockFunctionExecution); + when(mockFunctionExecution.execute(eq(false))).thenReturn(null); + + AbstractFunctionTemplate functionTemplate = new AbstractFunctionTemplate() { + @Override protected AbstractFunctionExecution getFunctionExecution() { + return mockFunctionExecution; + } + }; + + functionTemplate.setResultCollector(mockResultCollector); + functionTemplate.setTimeout(500); + + functionTemplate.executeWithNoResult("TestFunction", args); + + assertThat(functionTemplate.getResultCollector(), is(equalTo(mockResultCollector))); + + verify(mockFunctionExecution, times(1)).setArgs(args); + verify(mockFunctionExecution, times(1)).setFunctionId("TestFunction"); + verify(mockFunctionExecution, times(1)).setResultCollector(eq(mockResultCollector)); + verify(mockFunctionExecution, times(1)).setTimeout(500); + verify(mockFunctionExecution, times(1)).execute(eq(false)); + } + +} diff --git a/src/test/java/org/springframework/data/gemfire/function/sample/ExceptionThrowingFunctionExecution.java b/src/test/java/org/springframework/data/gemfire/function/sample/ExceptionThrowingFunctionExecution.java new file mode 100644 index 00000000..0c937747 --- /dev/null +++ b/src/test/java/org/springframework/data/gemfire/function/sample/ExceptionThrowingFunctionExecution.java @@ -0,0 +1,34 @@ +/* + * Copyright 2010-2013 the original author or authors. + * + * Licensed 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.springframework.data.gemfire.function.sample; + +import org.springframework.data.gemfire.function.annotation.OnServer; + +/** + * The ExceptionThrowingFunctionExecution interface defines a GemFire Function that throws a RuntimeException. + * + * @author John Blum + * @see org.springframework.data.gemfire.function.annotation.OnServer + * @since 1.7.0 + */ +@OnServer +@SuppressWarnings("unused") +public interface ExceptionThrowingFunctionExecution { + + Integer exceptionThrowingFunction(); + +} diff --git a/src/test/resources/org/springframework/data/gemfire/function/ExceptionThrowingFunctionExecutionIntegrationTest-context.xml b/src/test/resources/org/springframework/data/gemfire/function/ExceptionThrowingFunctionExecutionIntegrationTest-context.xml new file mode 100644 index 00000000..820d3ec7 --- /dev/null +++ b/src/test/resources/org/springframework/data/gemfire/function/ExceptionThrowingFunctionExecutionIntegrationTest-context.xml @@ -0,0 +1,28 @@ + + + + + config + + + + + + + + + + + + + diff --git a/src/test/resources/org/springframework/data/gemfire/function/ExceptionThrowingFunctionExecutionIntegrationTest-server-context.xml b/src/test/resources/org/springframework/data/gemfire/function/ExceptionThrowingFunctionExecutionIntegrationTest-server-context.xml new file mode 100644 index 00000000..3877bb36 --- /dev/null +++ b/src/test/resources/org/springframework/data/gemfire/function/ExceptionThrowingFunctionExecutionIntegrationTest-server-context.xml @@ -0,0 +1,39 @@ + + + + + localhost + localhost + 1 + 12480 + + + + + + ExceptionThrowingFunctionExecutionIntegrationTestServer + 0 + warning + + + + + + + + + + + + +