From 11dce8858b805b9d694fc5b84091bfaafa60279e Mon Sep 17 00:00:00 2001 From: David Turanski Date: Tue, 5 Feb 2013 09:29:05 -0500 Subject: [PATCH] SGF-151 Add support for ResultSender parameter in Function implementations --- ...ctionContextInjectingArgumentResolver.java | 15 +- .../function/config/GemfireFunctionUtils.java | 47 ++- .../FunctionArgumentResolverTest.java | 304 ++++++++++-------- 3 files changed, 209 insertions(+), 157 deletions(-) diff --git a/src/main/java/org/springframework/data/gemfire/function/FunctionContextInjectingArgumentResolver.java b/src/main/java/org/springframework/data/gemfire/function/FunctionContextInjectingArgumentResolver.java index d359b98b..d47735c2 100644 --- a/src/main/java/org/springframework/data/gemfire/function/FunctionContextInjectingArgumentResolver.java +++ b/src/main/java/org/springframework/data/gemfire/function/FunctionContextInjectingArgumentResolver.java @@ -27,6 +27,7 @@ import org.springframework.util.Assert; import com.gemstone.gemfire.cache.Region; import com.gemstone.gemfire.cache.execute.FunctionContext; import com.gemstone.gemfire.cache.execute.RegionFunctionContext; +import com.gemstone.gemfire.cache.execute.ResultSender; import com.gemstone.gemfire.cache.partition.PartitionRegionHelper; @@ -40,7 +41,8 @@ public class FunctionContextInjectingArgumentResolver extends DefaultFunctionArg private final int regionParameterPosition; private final int filterParameterPosition; - private final int functionContextParameterPosition; + private final int functionContextParameterPosition; + private final int resultSenderParameterPosition; private final Method method; public FunctionContextInjectingArgumentResolver(Method method) { @@ -62,13 +64,13 @@ public class FunctionContextInjectingArgumentResolver extends DefaultFunctionArg tempRegionParameterPosition = regionTypeParameterPosition; } - regionParameterPosition = tempRegionParameterPosition; + regionParameterPosition = tempRegionParameterPosition; filterParameterPosition = GemfireFunctionUtils.getAnnotationParameterPosition(method, Filter.class, new Class[]{Set.class}); functionContextParameterPosition = getArgumentTypePosition(method,FunctionContext.class); - + resultSenderParameterPosition = getArgumentTypePosition(method,ResultSender.class); if (regionParameterPosition >=0 && filterParameterPosition >=0) { - Assert.state(regionParameterPosition != filterParameterPosition, "region parameter and filter parameter must be different"); + Assert.state(regionParameterPosition != filterParameterPosition, "region parameter and filter parameter must be different"); } } @@ -91,12 +93,15 @@ public class FunctionContextInjectingArgumentResolver extends DefaultFunctionArg args = ArrayUtils.insert(args, functionContextParameterPosition, functionContext); } + if (this.resultSenderParameterPosition >=0 ) { + args = ArrayUtils.insert(args, resultSenderParameterPosition, functionContext.getResultSender()); + } + } Assert.isTrue(args.length == method.getParameterTypes().length, String.format("wrong number of arguments for method %s. Expected :%d, actual: %d", method.getName(), method.getParameterTypes().length, args.length)); - return args; diff --git a/src/main/java/org/springframework/data/gemfire/function/config/GemfireFunctionUtils.java b/src/main/java/org/springframework/data/gemfire/function/config/GemfireFunctionUtils.java index 3f4a8a4c..18fb22cf 100644 --- a/src/main/java/org/springframework/data/gemfire/function/config/GemfireFunctionUtils.java +++ b/src/main/java/org/springframework/data/gemfire/function/config/GemfireFunctionUtils.java @@ -51,20 +51,22 @@ public abstract class GemfireFunctionUtils { if (attributes.containsKey("HA")) { function.setHA((Boolean) attributes.get("HA")); } - + if (attributes.containsKey("optimizeForWrite")) { function.setOptimizeForWrite((Boolean) attributes.get("optimizeForWrite")); } - + if (attributes.containsKey("batchSize")) { int batchSize = (Integer) attributes.get("batchSize"); - Assert.isTrue(batchSize >= 0, String.format("batchSize must be a non-negative value %s.%s", - target.getClass().getName(), method.getName())); + Assert.isTrue( + batchSize >= 0, + String.format("batchSize must be a non-negative value %s.%s", target.getClass().getName(), + method.getName())); function.setBatchSize(batchSize); } - + if (attributes.containsKey("hasResult")) { - boolean hasResult = (Boolean)attributes.get("hasResult"); + boolean hasResult = (Boolean) attributes.get("hasResult"); //Only set if true if (hasResult) { function.setHasResult(hasResult); @@ -79,7 +81,7 @@ public abstract class GemfireFunctionUtils { FunctionService.unregisterFunction(function.getId()); } } - + if (!FunctionService.isRegistered(function.getId())) { FunctionService.registerFunction(function); if (log.isDebugEnabled()) { @@ -87,11 +89,11 @@ public abstract class GemfireFunctionUtils { } } else { if (log.isDebugEnabled()) { - log.debug("function " + function.getId()+ "is already registered"); + log.debug("function " + function.getId() + "is already registered"); } } } - + /** * Determine the order position of a an annotated method parameter * @@ -100,26 +102,37 @@ public abstract class GemfireFunctionUtils { * @param requiredTypes an array of valid parameter types for the annotation * @return the parameter position or -1 if the annotated parameter is not found */ - public static int getAnnotationParameterPosition(Method method, Class targetAnnotationType, Class[] requiredTypes) { + public static int getAnnotationParameterPosition(Method method, Class targetAnnotationType, + Class[] requiredTypes) { Annotation[][] parameterAnnotations = method.getParameterAnnotations(); if (parameterAnnotations.length > 0) { int position = -1; Class[] paramTypes = method.getParameterTypes(); - + List> requiredTypesList = Arrays.asList(requiredTypes); - for (int i = 0; i < parameterAnnotations.length; i++) { Annotation[] annotations = parameterAnnotations[i]; if (annotations.length > 0) { for (Annotation annotation : annotations) { if (annotation.annotationType().equals(targetAnnotationType)) { - Assert.state(position < 0, String.format("Method %s signature cannot contain more than one parameter annotated with type %s" - ,method.getName(),targetAnnotationType.getName())); - Assert.isTrue(requiredTypesList.contains(paramTypes[i]), String.format( - "Parameter annotated with %s must be one of type %s in method %s", targetAnnotationType.getName(), + Assert.state( + position < 0, + String.format( + "Method %s signature cannot contain more than one parameter annotated with type %s", + method.getName(), targetAnnotationType.getName())); + boolean isRequiredType = false; + for (Class requiredType: requiredTypesList) { + if (requiredType.isAssignableFrom(paramTypes[i])) { + isRequiredType = true; + break; + } + } + Assert.isTrue(isRequiredType, String.format( + "Parameter of type %s annotated with %s must be assignable from one of type %s in method %s", + paramTypes[i], targetAnnotationType.getName(), StringUtils.arrayToCommaDelimitedString(requiredTypes), method.getName())); - position = i; + position = i; } } diff --git a/src/test/java/org/springframework/data/gemfire/function/FunctionArgumentResolverTest.java b/src/test/java/org/springframework/data/gemfire/function/FunctionArgumentResolverTest.java index 46d1a10a..3f981e3b 100644 --- a/src/test/java/org/springframework/data/gemfire/function/FunctionArgumentResolverTest.java +++ b/src/test/java/org/springframework/data/gemfire/function/FunctionArgumentResolverTest.java @@ -30,6 +30,7 @@ import org.springframework.data.gemfire.function.config.RegionData; import com.gemstone.gemfire.cache.Region; import com.gemstone.gemfire.cache.execute.FunctionContext; import com.gemstone.gemfire.cache.execute.RegionFunctionContext; +import com.gemstone.gemfire.cache.execute.ResultSender; /** * @author David Turanski @@ -38,250 +39,283 @@ import com.gemstone.gemfire.cache.execute.RegionFunctionContext; public class FunctionArgumentResolverTest { @Test - public void testDefaultFunctionArgumentResolverSingleArg() { + public void testDefaultFunctionArgumentResolverSingleArg() { FunctionArgumentResolver far = new DefaultFunctionArgumentResolver(); - + FunctionContext functionContext = mock(FunctionContext.class); - - + when(functionContext.getArguments()).thenReturn("hello"); - + Object[] args = far.resolveFunctionArguments(functionContext); - - assertEquals(1,args.length); + + assertEquals(1, args.length); assertEquals("hello", args[0]); } - + @Test public void testDefaultFunctionArgumentResolverSingleArgAsArray() { FunctionArgumentResolver far = new DefaultFunctionArgumentResolver(); - + FunctionContext functionContext = mock(FunctionContext.class); - - - when(functionContext.getArguments()).thenReturn(new String[]{"hello"}); - + + when(functionContext.getArguments()).thenReturn(new String[] { "hello" }); + Object[] args = far.resolveFunctionArguments(functionContext); - - assertEquals(1,args.length); + + assertEquals(1, args.length); assertEquals("hello", args[0]); } - + @Test public void testMethodWithNoSpecialArgs() throws SecurityException, NoSuchMethodException { - RegionFunctionContext functionContext = mock(RegionFunctionContext.class); - - Method method = TestFunction.class.getDeclaredMethod("methodWithNoSpecialArgs", String.class,int.class,boolean.class); + RegionFunctionContext functionContext = mock(RegionFunctionContext.class); + + Method method = TestFunction.class.getDeclaredMethod("methodWithNoSpecialArgs", String.class, int.class, + boolean.class); FunctionArgumentResolver far = new FunctionContextInjectingArgumentResolver(method); - - - Object[] originalArgs = new Object[]{"hello",0,false}; + + Object[] originalArgs = new Object[] { "hello", 0, false }; when(functionContext.getArguments()).thenReturn(originalArgs); - + Object[] args = far.resolveFunctionArguments(functionContext); - + assertEquals(originalArgs.length, args.length); - + int i = 0; - for (Object arg: args) { - assertSame(originalArgs[i++], arg); - } - + for (Object arg : args) { + assertSame(originalArgs[i++], arg); + } + } - + @Test public void testMethodWithRegionType() throws SecurityException, NoSuchMethodException { RegionFunctionContext functionContext = mock(RegionFunctionContext.class); @SuppressWarnings("unchecked") - Region region = mock(Region.class); - - - Method method = TestFunction.class.getDeclaredMethod("methodWithRegionType", String.class,Region.class); + Region region = mock(Region.class); + + Method method = TestFunction.class.getDeclaredMethod("methodWithRegionType", String.class, Region.class); FunctionArgumentResolver far = new FunctionContextInjectingArgumentResolver(method); - - - Object[] originalArgs = new Object[]{"hello"}; + + Object[] originalArgs = new Object[] { "hello" }; when(functionContext.getArguments()).thenReturn(originalArgs); when(functionContext.getDataSet()).thenReturn(region); - + Object[] args = far.resolveFunctionArguments(functionContext); - + assertEquals(originalArgs.length + 1, args.length); - + int i = 0; - for (Object arg: args) { - if (i != 1) { - assertSame(originalArgs[i++], arg); - } else { - assertSame(region,arg); - } - } - + for (Object arg : args) { + if (i != 1) { + assertSame(originalArgs[i++], arg); + } else { + assertSame(region, arg); + } + } + } - + @Test public void testMethodWithOneArgRegionType() throws SecurityException, NoSuchMethodException { RegionFunctionContext functionContext = mock(RegionFunctionContext.class); @SuppressWarnings("unchecked") - Region region = mock(Region.class); - - + Region region = mock(Region.class); + Method method = TestFunction.class.getDeclaredMethod("methodWithOneArgRegionType", Region.class); FunctionArgumentResolver far = new FunctionContextInjectingArgumentResolver(method); - - - Object[] originalArgs = new Object[]{}; + + Object[] originalArgs = new Object[] {}; when(functionContext.getArguments()).thenReturn(originalArgs); when(functionContext.getDataSet()).thenReturn(region); - + Object[] args = far.resolveFunctionArguments(functionContext); - + assertEquals(1, args.length); - assertSame(region,args[0]); - + assertSame(region, args[0]); + } - + @Test public void testMethodWithAnnotatedRegion() throws SecurityException, NoSuchMethodException { RegionFunctionContext functionContext = mock(RegionFunctionContext.class); @SuppressWarnings("unchecked") - Region region = mock(Region.class); - - - Method method = TestFunction.class.getDeclaredMethod("methodWithAnnotatedRegion", Map.class, String.class); + Region region = mock(Region.class); + + Method method = TestFunction.class.getDeclaredMethod("methodWithAnnotatedRegion", Region.class, String.class); FunctionArgumentResolver far = new FunctionContextInjectingArgumentResolver(method); - - - Object[] originalArgs = new Object[]{"hello"}; + + Object[] originalArgs = new Object[] { "hello" }; when(functionContext.getArguments()).thenReturn(originalArgs); when(functionContext.getDataSet()).thenReturn(region); - + Object[] args = far.resolveFunctionArguments(functionContext); - + assertEquals(2, args.length); - assertSame(region,args[0]); - assertSame(originalArgs[0],args[1]); - + assertSame(region, args[0]); + assertSame(originalArgs[0], args[1]); + } - + @Test public void testMethodWithFunctionContext() throws SecurityException, NoSuchMethodException { RegionFunctionContext functionContext = mock(RegionFunctionContext.class); @SuppressWarnings("unchecked") - Region region = mock(Region.class); - - - Method method = TestFunction.class.getDeclaredMethod("methodWithFunctionContext", Map.class, String.class, FunctionContext.class); + Region region = mock(Region.class); + + Method method = TestFunction.class.getDeclaredMethod("methodWithFunctionContext", Map.class, String.class, + FunctionContext.class); FunctionArgumentResolver far = new FunctionContextInjectingArgumentResolver(method); - - - Object[] originalArgs = new Object[]{"hello"}; + + Object[] originalArgs = new Object[] { "hello" }; when(functionContext.getArguments()).thenReturn(originalArgs); when(functionContext.getDataSet()).thenReturn(region); - + Object[] args = far.resolveFunctionArguments(functionContext); - + assertEquals(3, args.length); - assertSame(region,args[0]); - assertSame(originalArgs[0],args[1]); - assertSame(functionContext,args[2]); + assertSame(region, args[0]); + assertSame(originalArgs[0], args[1]); + assertSame(functionContext, args[2]); } + @SuppressWarnings({ "unchecked", "rawtypes" }) + @Test + public void testMethodWithResultSender () throws SecurityException, NoSuchMethodException { + RegionFunctionContext functionContext = mock(RegionFunctionContext.class); + ResultSender resultSender = mock(ResultSender.class); + Region region = mock(Region.class); + + Method method = TestFunction.class.getDeclaredMethod("methodWithResultSender", Map.class, ResultSender.class); + FunctionArgumentResolver far = new FunctionContextInjectingArgumentResolver(method); + + Object[] originalArgs = new Object[] { }; + when(functionContext.getArguments()).thenReturn(originalArgs); + when(functionContext.getDataSet()).thenReturn(region); + when(functionContext.getResultSender()).thenReturn(resultSender); + + Object[] args = far.resolveFunctionArguments(functionContext); + + assertEquals(2, args.length); + assertSame(region, args[0]); + assertSame(resultSender, args[1]); + } + + @SuppressWarnings("unchecked") @Test public void testMethodWithFilterAndRegion() throws SecurityException, NoSuchMethodException { RegionFunctionContext functionContext = mock(RegionFunctionContext.class); - Region region = mock(Region.class); - - - Method method = TestFunction.class.getDeclaredMethod("methodWithFilterAndRegion", Map.class, Set.class, Object.class); + Region region = mock(Region.class); + + Method method = TestFunction.class.getDeclaredMethod("methodWithFilterAndRegion", Map.class, Set.class, + Object.class); FunctionArgumentResolver far = new FunctionContextInjectingArgumentResolver(method); - - - Object[] originalArgs = new Object[]{new Object()}; + + Object[] originalArgs = new Object[] { new Object() }; when(functionContext.getArguments()).thenReturn(originalArgs); when(functionContext.getDataSet()).thenReturn(region); @SuppressWarnings("rawtypes") Set keys = new HashSet(); when(functionContext.getFilter()).thenReturn(keys); - + Object[] args = far.resolveFunctionArguments(functionContext); - + assertEquals(3, args.length); - assertSame(region,args[0]); - assertSame(originalArgs[0],args[2]); - assertSame(keys,args[1]); + assertSame(region, args[0]); + assertSame(originalArgs[0], args[2]); + assertSame(keys, args[1]); } - - + @Test public void testMethodWithMultipleRegionData() throws SecurityException, NoSuchMethodException { - + Method method = TestFunction.class.getDeclaredMethod("methodWithMultipleRegionData", Map.class, Map.class); - + try { new FunctionContextInjectingArgumentResolver(method); fail("Should throw exception"); } catch (Exception e) { - + } - + } - - + @Test public void testMethodWithMultipleRegions() throws SecurityException, NoSuchMethodException { - + Method method = TestFunction.class.getDeclaredMethod("methodWithMultipleRegions", Region.class, Map.class); - + try { new FunctionContextInjectingArgumentResolver(method); fail("Should throw exception"); } catch (Exception e) { - + } } - @Test public void testMethodWithInvalidTypeForAnnotation() throws SecurityException, NoSuchMethodException { - + Method method = TestFunction.class.getDeclaredMethod("methodWithInvalidTypeForAnnotation", Region.class); - + try { new FunctionContextInjectingArgumentResolver(method); fail("Should throw exception"); } catch (Exception e) { - - } + + } } - - + @Test public void testMethodWithMultipleFunctionContext() throws SecurityException, NoSuchMethodException { - - Method method = TestFunction.class.getDeclaredMethod("methodWithMultipleFunctionContext", FunctionContext.class, FunctionContext.class); - + + Method method = TestFunction.class.getDeclaredMethod("methodWithMultipleFunctionContext", + FunctionContext.class, FunctionContext.class); + try { new FunctionContextInjectingArgumentResolver(method); fail("Should throw exception"); } catch (Exception e) { - - } + + } + } + + static class TestFunction { + public void methodWithNoSpecialArgs(String s1, int i1, boolean b1) { + } + + public void methodWithRegionType(String s1, Region region) { + } + + public void methodWithOneArgRegionType(Region region) { + } + + public void methodWithAnnotatedRegion(@RegionData Region data, String s1) { + } + + public void methodWithFunctionContext(@RegionData Map data, String s1, FunctionContext fc) { + } + + public void methodWithResultSender(@RegionData Map data, ResultSender resultSender) { + } + + public void methodWithFilterAndRegion(@RegionData Map region, @Filter Set keys, + Object arg) { + } + + //Invalid Method Signatures + public void methodWithMultipleRegionData(@RegionData Map r1, @RegionData Map r2) { + } + + public void methodWithMultipleRegions(Region r1, @RegionData Map r2) { + } + + public void methodWithInvalidTypeForAnnotation(@Filter Region r1) { + } + + public void methodWithMultipleFunctionContext(FunctionContext fc1, FunctionContext fc2) { + } + } - - static class TestFunction { - public void methodWithNoSpecialArgs(String s1, int i1, boolean b1) {} - public void methodWithRegionType(String s1, Region region){} - public void methodWithOneArgRegionType(Region region){} - public void methodWithAnnotatedRegion(@RegionData Map data, String s1){} - public void methodWithFunctionContext(@RegionData Map data, String s1, FunctionContext fc){} - public void methodWithFilterAndRegion(@RegionData Map region, @Filter Set keys, Object arg){} - //Invalid Method Signatures - public void methodWithMultipleRegionData(@RegionData Map r1, @RegionData Map r2){} - public void methodWithMultipleRegions(Region r1, @RegionData Map r2){} - public void methodWithInvalidTypeForAnnotation(@Filter Region r1){} - public void methodWithMultipleFunctionContext(FunctionContext fc1, FunctionContext fc2){} - - } }