DATAGEODE-119 - Check result type against function return type.

This commit is contained in:
Patrick Johnson
2020-01-08 16:20:15 -08:00
committed by John Blum
parent 248c0b90a4
commit 22b5d13df0
2 changed files with 176 additions and 5 deletions

View File

@@ -1,9 +1,5 @@
/*
<<<<<<< Updated upstream
* Copyright 2002-2020 the original author or authors.
=======
* Copyright 2002-2020 the original author or authors.
>>>>>>> Stashed changes
*
* 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
@@ -17,6 +13,7 @@
package org.springframework.data.gemfire.function.execution;
import java.lang.reflect.Method;
import java.util.stream.StreamSupport;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
@@ -35,6 +32,7 @@ import org.springframework.util.ClassUtils;
*
* @author David Turanski
* @author John Blum
* @author Patrick Johnson
* @see java.lang.reflect.Method
* @see org.aopalliance.intercept.MethodInterceptor
* @see org.springframework.beans.factory.BeanClassLoaderAware
@@ -93,7 +91,16 @@ public class GemfireFunctionProxyFactoryBean implements BeanClassLoaderAware, Fa
logger.debug("Invoking method {}", invocation.getMethod().getName());
}
return invokeFunction(invocation.getMethod(), invocation.getArguments());
Object result = invokeFunction(invocation.getMethod(), invocation.getArguments());
if(!invocation.getMethod().getReturnType().isAssignableFrom(result.getClass()) && result instanceof Iterable) {
Iterable iter = (Iterable)result;
if(StreamSupport.stream(iter.spliterator(), false).count() == 1) {
result = iter.iterator().next();
}
}
return result;
}
protected Object invokeFunction(Method method, Object[] args) {

View File

@@ -0,0 +1,164 @@
/*
* Copyright 2002-2020 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
*
* https://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.result;
import org.apache.geode.cache.DataPolicy;
import org.apache.geode.cache.GemFireCache;
import org.apache.geode.cache.client.ClientRegionShortcut;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.data.gemfire.ReplicatedRegionFactoryBean;
import org.springframework.data.gemfire.client.ClientRegionFactoryBean;
import org.springframework.data.gemfire.config.annotation.CacheServerApplication;
import org.springframework.data.gemfire.config.annotation.ClientCacheApplication;
import org.springframework.data.gemfire.function.annotation.FunctionId;
import org.springframework.data.gemfire.function.annotation.GemfireFunction;
import org.springframework.data.gemfire.function.annotation.OnRegion;
import org.springframework.data.gemfire.function.config.EnableGemfireFunctionExecutions;
import org.springframework.data.gemfire.function.config.EnableGemfireFunctions;
import org.springframework.data.gemfire.process.ProcessWrapper;
import org.springframework.data.gemfire.test.support.ClientServerIntegrationTestsSupport;
import org.springframework.data.gemfire.transaction.config.EnableGemfireCacheTransactions;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringRunner;
import java.io.IOException;
import java.math.BigDecimal;
import java.util.Collections;
import java.util.List;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author Patrick Johnson
*/
@RunWith(SpringRunner.class)
@ContextConfiguration(classes = FunctionResultTypeIntegrationTest.FunctionInvocationClientApplicationConfig.class)
public class FunctionResultTypeIntegrationTest extends ClientServerIntegrationTestsSupport {
private static ProcessWrapper server;
@Autowired
private FunctionExecutions functionExecutions;
@BeforeClass
public static void startServer() throws IOException {
final String GEMFIRE_LOCALHOST_PORT = "localhost[%d]";
int availablePort = findAvailablePort();
System.setProperty(GEMFIRE_CACHE_SERVER_PORT_PROPERTY, String.valueOf(availablePort));
System.setProperty(GEMFIRE_POOL_SERVERS_PROPERTY, String.format(GEMFIRE_LOCALHOST_PORT, availablePort));
server = run(FunctionServerApplicationConfig.class,
String.format("-D%s=%d", GEMFIRE_CACHE_SERVER_PORT_PROPERTY, availablePort));
waitForServerToStart(DEFAULT_HOSTNAME, availablePort);
}
@AfterClass
public static void stopServer() {
server.stop();
server = null;
}
@Test
public void singleResultFunctionsExecuteCorrectly() {
BigDecimal num = functionExecutions.returnFive();
assertThat(num.doubleValue()).isEqualTo(5);
}
@Test
public void listResultFunctionsExecuteCorrectly() {
List<BigDecimal> list = functionExecutions.returnList();
assertThat(list.size()).isEqualTo(1);
}
@Test
public void primitiveResultFunctionsExecuteCorrectly() {
int num = functionExecutions.returnPrimitive();
assertThat(num).isEqualTo(7);
}
@EnableGemfireFunctionExecutions(basePackageClasses = FunctionExecutions.class)
@ClientCacheApplication(name = "SingleResultFunctionExecutionTest")
@EnableGemfireCacheTransactions
public static class FunctionInvocationClientApplicationConfig {
@Bean("Numbers")
protected ClientRegionFactoryBean<Long, BigDecimal> configureProxyClientNumberRegion(GemFireCache gemFireCache) {
ClientRegionFactoryBean<Long, BigDecimal> clientRegionFactoryBean = new ClientRegionFactoryBean<>();
clientRegionFactoryBean.setCache(gemFireCache);
clientRegionFactoryBean.setName("Numbers");
clientRegionFactoryBean.setShortcut(ClientRegionShortcut.PROXY);
return clientRegionFactoryBean;
}
}
@EnableGemfireFunctions
@CacheServerApplication(name = "SingleResultFunctionExecutionTestServer")
public static class FunctionServerApplicationConfig {
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext =
new AnnotationConfigApplicationContext(FunctionServerApplicationConfig.class);
applicationContext.registerShutdownHook();
}
@Bean
ReplicatedRegionFactoryBean<Long, BigDecimal> createNumberRegion(GemFireCache gemfireCache) {
ReplicatedRegionFactoryBean replicatedRegionFactoryBean = new ReplicatedRegionFactoryBean();
replicatedRegionFactoryBean.setCache(gemfireCache);
replicatedRegionFactoryBean.setRegionName("Numbers");
replicatedRegionFactoryBean.setDataPolicy(DataPolicy.REPLICATE);
return replicatedRegionFactoryBean;
}
@GemfireFunction(id = "returnSingleObject", hasResult = true)
public BigDecimal returnSingleObject() {
return new BigDecimal(5);
}
@GemfireFunction(id = "returnList", hasResult = true)
public List<BigDecimal> returnList() {
return Collections.singletonList(new BigDecimal(10));
}
@GemfireFunction(id = "returnPrimitive", hasResult = true)
public int returnPrimitive() {
return 7;
}
}
}
@OnRegion(region = "Numbers")
interface FunctionExecutions {
@FunctionId("returnSingleObject")
BigDecimal returnFive();
@FunctionId("returnList")
List<BigDecimal> returnList();
@FunctionId("returnPrimitive")
int returnPrimitive();
}