started on annotation-driven functions

This commit is contained in:
David Turanski
2012-10-09 08:21:38 -04:00
parent 467755fe00
commit 4da8c04af0
38 changed files with 5715 additions and 28 deletions

View File

@@ -50,7 +50,7 @@ dependencies {
// GemFire
compile("com.gemstone.gemfire:gemfire:$gemfireVersion")
runtime("antlr:antlr:2.7.7")
//runtime("commons-modeler;commons-modeler:2.0.1")
runtime("commons-modelercommons-modeler:2.0.1")
// Testing
testCompile "junit:junit-dep:$junitVersion"

View File

@@ -8,7 +8,6 @@ slf4jVersion = 1.6.4
springVersion = 3.1.2.RELEASE
springDataCommonsVersion = 1.4.0.RC1
gemfireVersion = 7.0.Beta-SNAPSHOT
gemfireVersion6x = 6.6.3
# Testing
junitVersion = 4.8.2

View File

@@ -27,8 +27,6 @@ def customizePom(pom, gradleProject) {
dep.scope == 'test'
}
generatedPom.dependencies.find { dep -> dep.groupId == 'com.gemstone.gemfire' && dep.artifactId == 'gemfire' }.version = "$gemfireVersion6x"
// add all items necessary for maven central publication
generatedPom.project {
name = gradleProject.description
@@ -61,6 +59,11 @@ def customizePom(pom, gradleProject) {
name = 'David Turanski'
email = 'dturanski@vmware.com'
}
developer {
id = 'ogierke'
name = 'Oliver Gierke'
email = 'ogierke@vmware.com'
}
}
}
}

View File

@@ -0,0 +1,49 @@
/*
* Copyright 2002-2012 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.config;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionReaderUtils;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.beans.factory.xml.BeanDefinitionParser;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.data.gemfire.function.config.GemfireFunctionBeanPostProcessor;
import org.w3c.dom.Element;
/**
* @author David Turanski
*
*/
public class AnnotationDrivenBeanDefinitionParser implements BeanDefinitionParser {
/* (non-Javadoc)
* @see org.springframework.beans.factory.xml.BeanDefinitionParser#parse(org.w3c.dom.Element, org.springframework.beans.factory.xml.ParserContext)
*/
@Override
public BeanDefinition parse(Element element, ParserContext parserContext) {
registerGemfireFunctionBeanPostProcessor(element, parserContext);
return null;
}
/**
*
*/
private void registerGemfireFunctionBeanPostProcessor(Element element, ParserContext parserContext) {
Object source = parserContext.extractSource(element);
RootBeanDefinition beanDefinition = new RootBeanDefinition(GemfireFunctionBeanPostProcessor.class);
beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
beanDefinition.setSource(source);
BeanDefinitionReaderUtils.registerWithGeneratedName(beanDefinition, parserContext.getRegistry());
}
}

View File

@@ -67,5 +67,6 @@ class GemfireNamespaceHandler extends NamespaceHandlerSupport {
registerBeanDefinitionParser("function-service", new FunctionServiceParser());
// V6 WAN parsers
registerBeanDefinitionParser("gateway-hub", new GatewayHubParser());
registerBeanDefinitionParser("annotation-driven", new AnnotationDrivenBeanDefinitionParser());
}
}

View File

@@ -0,0 +1,27 @@
/*
* Copyright 2002-2012 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 java.io.Serializable;
import java.util.Set;
/**
*
* @author David Turanski
*
*/
public interface FilterAware {
/*
* Return calling instance to support method chaining
*/
public Object setFilter(Set<? extends Serializable> filter);
}

View File

@@ -0,0 +1,139 @@
/*
* Copyright 2002-2011 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 java.io.Serializable;
import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
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;
/**
* @author David Turanski
*/
public abstract class FunctionExecution<T> {
protected final Log logger = LogFactory.getLog(this.getClass());
private volatile ResultCollector<?, ?> collector;
private final Serializable[] args;
private Function function;
private final String functionId;
private long timeout;
public FunctionExecution(Function function, Serializable... args) {
Assert.notNull(function,"function cannot be null");
this.function = function;
this.functionId = function.getId();
this.args = args;
}
public FunctionExecution(String functionId, Serializable... args) {
Assert.isTrue(StringUtils.hasLength(functionId),"functionId cannot be null or empty");
this.functionId = functionId;
this.args = args;
}
public ResultCollector<?, ?> getCollector() {
return collector;
}
public Serializable[] getArgs() {
return args;
}
public String getFunctionId() {
return functionId;
}
public Function getFunction() {
return function;
}
public void setCollector(ResultCollector<?,?> collector) {
this.collector = collector;
}
@SuppressWarnings("unchecked")
public List<T> execute() {
Execution execution = this.getExecution();
if (getKeys() != null) {
execution = execution.withFilter(getKeys());
}
if (getCollector() != null) {
execution = execution.withCollector(getCollector());
}
ResultCollector<?,?> resultsCollector = null;
execution = execution.withArgs(getArgs());
if (isRegisteredFunction()){
resultsCollector = (ResultCollector<?,?>) execution.execute(functionId);
} else {
resultsCollector = (ResultCollector<?,?>) execution.execute(function);
}
if (this.timeout > 0 ){
try {
return (List<T>)resultsCollector.getResult(this.timeout, TimeUnit.MILLISECONDS);
}
catch (FunctionException e) {
throw new RuntimeException(e);
}
catch (InterruptedException e) {
throw new RuntimeException(e);
}
} else {
return (List<T>)resultsCollector.getResult();
}
}
public T executeAndExtract() {
return this.execute().get(0);
}
protected abstract Execution getExecution();
protected Set<?> getKeys() {
return null;
}
public void setTimeout(long timeout) {
this.timeout = timeout;
}
public long getTimeout() {
return timeout;
}
/**
* @return
*/
private boolean isRegisteredFunction() {
return function == null;
}
}

View File

@@ -0,0 +1,23 @@
/*
* Copyright 2002-2011 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 com.gemstone.gemfire.cache.execute.Execution;
/**
* @author David Turanski
*
*/
public interface GemfireFunctionCallback<T> {
public T doInGemfire( Execution execution );
}

View File

@@ -0,0 +1,46 @@
/*
* Copyright 2002-2011 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 java.io.Serializable;
import java.util.List;
import java.util.Set;
import com.gemstone.gemfire.cache.execute.Function;
/**
* @author David Turanski
*
* @param <T>
*/
public interface GemfireFunctionOperations<T> {
public abstract List<T> executeOnRegion(Function function, String regionId, Serializable... args);
public abstract T executeOnRegionAndExtract(Function function, String regionId, Serializable... args);
public abstract List<T> executeOnRegion(Function function, String regionId, Set<?> keys, Serializable... args);
public abstract List<T> executeOnRegion(String functionId, String regionId, Serializable... args);
public abstract List<T> executeOnRegion(String functionId, String regionId, Set<?> keys, Serializable... args);
public abstract T executeOnRegion(String regionId, GemfireFunctionCallback<T> callback);
public abstract List<T> executeOnServers(Function function, Serializable... args);
public abstract List<T> executeOnServers(String functionId, Serializable... args);
public abstract T executeOnServers(GemfireFunctionCallback<T> callback);
}

View File

@@ -0,0 +1,250 @@
/*
* Copyright 2002-2011 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 java.io.Serializable;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.List;
import java.util.Set;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.aop.framework.ProxyFactory;
import org.springframework.aop.support.AopUtils;
import org.springframework.beans.factory.BeanClassLoaderAware;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import com.gemstone.gemfire.cache.execute.FunctionException;
/**
* Creates a Proxy to a delegate that is executed as a Gemfire remote function
* using {@link MethodInvokingFunction}. Also, adds the {@link FilterAware}
*
* interface to the proxy which is used to set a data filter when execution is
* performed on a partitioned region.
*
* @author David Turanski
*
*/
public class GemfireFunctionProxyFactoryBean implements FactoryBean<Object>, MethodInterceptor, BeanClassLoaderAware {
private volatile ClassLoader beanClassLoader = ClassUtils.getDefaultClassLoader();
private final Class<?> serviceInterface;
private volatile Object serviceProxy;
private volatile boolean initialized;
private final String delegateClassName;
private final ThreadLocal<Set<? extends Serializable>> filter;
private boolean methodInvokingFunctionRegistered;
private volatile String regionName;
private final GemfireFunctionOperations<?> gemfireFunctionOperations;
private static Log logger = LogFactory.getLog(GemfireFunctionProxyFactoryBean.class);
/**
*
* @param serviceInterface the proxy interface
* @param delegateClassName the name of the implementation class
* @param gemfireFunctionOperations a strategy interface, normally a
* {@link GemfireFunctionTemplate}
*/
public GemfireFunctionProxyFactoryBean(Class<?> serviceInterface, String delegateClassName,
GemfireFunctionOperations<?> gemfireFunctionOperations) {
this.delegateClassName = delegateClassName;
Assert.notNull(serviceInterface, "'serviceInterface' must not be null");
Assert.isTrue(serviceInterface.isInterface(), "'serviceInterface' must be an interface");
this.serviceInterface = serviceInterface;
Assert.notNull(gemfireFunctionOperations);
this.gemfireFunctionOperations = gemfireFunctionOperations;
this.filter = new ThreadLocal<Set<? extends Serializable>>();
}
// @Override
public void setBeanClassLoader(ClassLoader classLoader) {
beanClassLoader = classLoader;
}
// @Override
@SuppressWarnings("unchecked")
public Object invoke(MethodInvocation invocation) throws Throwable {
if (AopUtils.isToStringMethod(invocation.getMethod())) {
return "Gemfire function proxy for service interface [" + this.serviceInterface + "]";
}
if (logger.isDebugEnabled()) {
logger.debug("invoking method " + invocation.getMethod().getName());
}
if (isSetFilterMethod(invocation.getMethod())) {
setFilter((Set<? extends Serializable>) invocation.getArguments()[0]);
return getObject();
}
RemoteMethodInvocation remoteInvocation = new RemoteMethodInvocation(this.delegateClassName, invocation
.getMethod().getName(), convertArgsToSerializable(invocation.getArguments()));
List<?> results = null;
if (this.methodInvokingFunctionRegistered) {
if (this.regionName != null) {
results = this.gemfireFunctionOperations.executeOnRegion(MethodInvokingFunction.FUNCTION_ID,
this.regionName, this.getFilter(), remoteInvocation);
}
else {
if (this.getFilter() != null) {
logger.warn("No region is specified. Filter has no effect on a data independent function execution");
}
results = this.gemfireFunctionOperations.executeOnServers(MethodInvokingFunction.FUNCTION_ID,
remoteInvocation);
}
}
else {
if (this.regionName != null) {
results = this.gemfireFunctionOperations.executeOnRegion(new MethodInvokingFunction(), this.regionName,
this.getFilter(), remoteInvocation);
}
else {
if (this.getFilter() != null) {
logger.warn("No region is specified. Filter has no effect on a data independent function execution");
}
results = this.gemfireFunctionOperations.executeOnServers(new MethodInvokingFunction(),
remoteInvocation);
}
}
return extractResult(results, invocation.getMethod().getReturnType());
}
// @Override
public Object getObject() throws Exception {
if (this.serviceProxy == null) {
this.onInit();
Assert.notNull(this.serviceProxy, "failed to initialize proxy");
}
return this.serviceProxy;
}
// @Override
public Class<?> getObjectType() {
return (this.serviceInterface != null ? this.serviceInterface : null);
}
// @Override
public boolean isSingleton() {
return true;
}
protected Set<?> getFilter() {
return filter.get();
}
protected void setFilter(Set<? extends Serializable> filter) {
this.filter.set(filter);
}
/**
* Set to true id {@link MethodInvokingFunction} is a registered function.
* If registered, invocations will not create and transport a new instance
* of the function to the cache server(s).
* @param methodInvokingFunctionRegistered
*/
public void setMethodInvokingFunctionRegistered(boolean methodInvokingFunctionRegistered) {
this.methodInvokingFunctionRegistered = methodInvokingFunctionRegistered;
}
/**
*
* Optional region to use. If set, the function will execute onRegion, if
* null the function will execute onServers
*
* @param regionName
*/
public void setRegionName(String regionName) {
this.regionName = regionName;
}
// TODO: Use something like cglib to implement setFilter() directly
// to eliminate the need to cast the proxy to FilterAware
protected void onInit() {
if (this.initialized) {
return;
}
ProxyFactory proxyFactory = new ProxyFactory(serviceInterface, this);
proxyFactory.addInterface(FilterAware.class);
this.serviceProxy = proxyFactory.getProxy(this.beanClassLoader);
this.initialized = true;
}
/*
* This tweek is needed to prevent an argument mismatch on the function
* invocation.
*/
private Serializable[] convertArgsToSerializable(Object[] array) {
return Arrays.copyOf(array, array.length, Serializable[].class);
}
/*
* Match the result to the declared return type
*/
private Object extractResult(List<?> results, Class<?> returnType) {
Object result = null;
if (List.class.isAssignableFrom(returnType)) {
result = results;
}
else {
int nonNullItems = 0;
for (Object obj : results) {
if (obj != null) {
if (++nonNullItems > 1) {
throw new FunctionException("multiple results found for single valued return type");
}
else {
result = obj;
}
}
}
}
if (logger.isDebugEnabled()) {
logger.debug("returning result as " + result.getClass().getName());
}
return result;
}
private static boolean isSetFilterMethod(Method method) {
try {
Method setFilterMethod = FilterAware.class.getMethod("setFilter", Set.class);
return method.equals(setFilterMethod);
}
catch (SecurityException e) {
}
catch (NoSuchMethodException e) {
}
return false;
}
}

View File

@@ -0,0 +1,168 @@
/*
* Copyright 2002-2011 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 java.io.Serializable;
import java.util.List;
import java.util.Set;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.util.Assert;
import com.gemstone.gemfire.cache.Region;
import com.gemstone.gemfire.cache.RegionService;
import com.gemstone.gemfire.cache.execute.Execution;
import com.gemstone.gemfire.cache.execute.Function;
import com.gemstone.gemfire.cache.execute.FunctionService;
/**
* @author David Turanski
*
*/
public class GemfireFunctionTemplate<T> implements InitializingBean, GemfireFunctionOperations<T> {
/** Logger available to subclasses */
protected final Log log = LogFactory.getLog(getClass());
private RegionService cache;
private long timeout;
/**
*
* @param cache
*/
public GemfireFunctionTemplate (RegionService cache) {
this.cache = cache;
afterPropertiesSet();
}
public void afterPropertiesSet() {
}
/* (non-Javadoc)
* @see org.springframework.data.gemfire.function.GemfireFunctionOperations#executeOnRegion(com.gemstone.gemfire.cache.execute.Function, java.lang.String, java.io.Serializable)
*/
public List<T> executeOnRegion(Function function, String regionId, Serializable... args) {
Region<?,?> region = getRegion(regionId);
Assert.notNull(region,"Region '" + regionId + "' not found");
RegionFunctionExecution<T> execution = new RegionFunctionExecution<T>(region, function, args);
execution.setTimeout(this.timeout);
return execution.execute();
}
/* (non-Javadoc)
* @see org.springframework.data.gemfire.function.GemfireFunctionOperations#executeOnRegionAndExtract(com.gemstone.gemfire.cache.execute.Function, java.lang.String, java.io.Serializable)
*/
public T executeOnRegionAndExtract(Function function, String regionId, Serializable... args) {
Region<?,?> region = getRegion(regionId);
Assert.notNull(region,"Region '" + regionId + "' not found");
RegionFunctionExecution<T> execution = new RegionFunctionExecution<T>(region, function, args);
execution.setTimeout(this.timeout);
return execution.executeAndExtract();
}
/* (non-Javadoc)
* @see org.springframework.data.gemfire.function.GemfireFunctionOperations#executeOnRegion(com.gemstone.gemfire.cache.execute.Function, java.lang.String, java.util.Set, java.io.Serializable)
*/
public List<T> executeOnRegion(Function function, String regionId, Set<?> keys, Serializable... args) {
Region<?,?> region = getRegion(regionId);
Assert.notNull(region,"Region '" + regionId + "' not found");
RegionFunctionExecution<T> execution = new RegionFunctionExecution<T>(region, function, args);
execution.setKeys(keys);
execution.setTimeout(this.timeout);
return execution.execute();
}
/* (non-Javadoc)
* @see org.springframework.data.gemfire.function.GemfireFunctionOperations#executeOnRegion(java.lang.String, java.lang.String, java.io.Serializable)
*/
public List<T> executeOnRegion(String functionId, String regionId, Serializable... args) {
Region<?,?> region = getRegion(regionId);
Assert.notNull(region,"Region '" + regionId + "' not found");
RegionFunctionExecution<T> execution = new RegionFunctionExecution<T>(region, functionId, args);
execution.setTimeout(this.timeout);
return execution.execute();
}
/* (non-Javadoc)
* @see org.springframework.data.gemfire.function.GemfireFunctionOperations#executeOnRegion(java.lang.String, java.lang.String, java.util.Set, java.io.Serializable)
*/
public List<T> executeOnRegion(String functionId, String regionId, Set<?> keys, Serializable... args) {
Region<?,?> region = getRegion(regionId);
Assert.notNull(region,"Region '" + regionId + "' not found");
RegionFunctionExecution<T> execution = new RegionFunctionExecution<T>(region, functionId, args);
execution.setKeys(keys);
execution.setTimeout(this.timeout);
return execution.execute();
}
/* (non-Javadoc)
* @see org.springframework.data.gemfire.function.GemfireFunctionOperations#executeOnRegion(java.lang.String, org.springframework.data.gemfire.function.GemfireFunctionCallback)
*/
public T executeOnRegion(String regionId, GemfireFunctionCallback<T> callback ) {
Region<?,?> region = getRegion(regionId);
Assert.notNull(region,"Region '" + regionId + "' not found");
Execution execution = FunctionService.onRegion(region);
return callback.doInGemfire(execution);
}
/* (non-Javadoc)
* @see org.springframework.data.gemfire.function.GemfireFunctionOperations#executeOnServers(com.gemstone.gemfire.cache.execute.Function, java.io.Serializable)
*/
public List<T> executeOnServers(Function function, Serializable... args) {
ServersFunctionExecution<T> execution = new ServersFunctionExecution<T>(this.cache, function, args);
execution.setTimeout(this.timeout);
return execution.execute();
}
/* (non-Javadoc)
* @see org.springframework.data.gemfire.function.GemfireFunctionOperations#executeOnServers(java.lang.String, java.io.Serializable)
*/
public List<T> executeOnServers(String functionId, Serializable... args) {
ServersFunctionExecution<T> execution = new ServersFunctionExecution<T>(this.cache, functionId, args);
execution.setTimeout(this.timeout);
return execution.execute();
}
/* (non-Javadoc)
* @see org.springframework.data.gemfire.function.GemfireFunctionOperations#executeOnServers(org.springframework.data.gemfire.function.GemfireFunctionCallback)
*/
public T executeOnServers(GemfireFunctionCallback<T> callback ) {
Execution execution = FunctionService.onServers(this.cache);
return callback.doInGemfire(execution);
}
public Region<?,?> getRegion(String regionId) {
return this.cache.getRegion(regionId);
}
public void setTimeout(long timeout) {
this.timeout = timeout;
}
public long getTimeout() {
return timeout;
}
}

View File

@@ -0,0 +1,51 @@
/*
* Copyright 2002-2011 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 java.io.Serializable;
import com.gemstone.gemfire.cache.execute.Execution;
import com.gemstone.gemfire.cache.execute.Function;
import com.gemstone.gemfire.cache.execute.FunctionService;
import com.gemstone.gemfire.distributed.DistributedSystem;
/**
* @author David Turanski
*
*/
public class MemberFunctionExecution extends FunctionExecution<Object> {
private final DistributedSystem distributedSystem;
/**
* @param functionId
* @param args
*/
public MemberFunctionExecution(DistributedSystem distributedSystem, Function function, Serializable... args) {
super(function, args);
this.distributedSystem = distributedSystem;
}
/* (non-Javadoc)
* @see org.springframework.data.gemfire.function.FunctionExecution#getExecution()
*/
@Override
protected Execution getExecution() {
return FunctionService.onMember(this.distributedSystem, this.distributedSystem.getDistributedMember());
}
}

View File

@@ -0,0 +1,50 @@
/*
* Copyright 2002-2011 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 java.io.Serializable;
import com.gemstone.gemfire.cache.execute.Execution;
import com.gemstone.gemfire.cache.execute.Function;
import com.gemstone.gemfire.cache.execute.FunctionService;
import com.gemstone.gemfire.distributed.DistributedSystem;
/**
* @author David Turanski
*
*/
public class MembersFunctionExecution extends FunctionExecution<Object> {
private final DistributedSystem distributedSystem;
/**
* @param functionId
* @param args
*/
public MembersFunctionExecution(DistributedSystem distributedSystem, Function function, Serializable... args) {
super(function, args);
this.distributedSystem = distributedSystem;
}
/* (non-Javadoc)
* @see org.springframework.data.gemfire.function.FunctionExecution#getExecution()
*/
@Override
protected Execution getExecution() {
return FunctionService.onMembers(this.distributedSystem);
}
}

View File

@@ -0,0 +1,242 @@
/*
* Copyright 2002-2011 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 java.io.Serializable;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.util.ClassUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.util.ReflectionUtils;
import com.gemstone.gemfire.cache.Region;
import com.gemstone.gemfire.cache.execute.Function;
import com.gemstone.gemfire.cache.execute.FunctionContext;
import com.gemstone.gemfire.cache.execute.FunctionException;
import com.gemstone.gemfire.cache.execute.RegionFunctionContext;
import com.gemstone.gemfire.cache.execute.ResultSender;
import com.gemstone.gemfire.cache.partition.PartitionRegionHelper;
/**
* Invokes a POJO's given method as a Gemfire remote function.
* If the POJO has a constructor that takes a Map, and the function context is Region, the
* region will be injected. The delegate class name, the method name, and the method arguments
* are part of a remote function invocation, therefore all arguments must be serializable.
* The delegate class must be the class path of the remote cache(s)
* @author David Turanski
*
*/
@SuppressWarnings("serial")
public class MethodInvokingFunction implements Function {
public static final String FUNCTION_ID = MethodInvokingFunction.class.getName();
private static transient Log logger = LogFactory.getLog(MethodInvokingFunction.class);
private volatile boolean HA;
private volatile boolean optimizeForWrite;
private volatile boolean hasResult;
private String id;
public MethodInvokingFunction( ) {
this.HA = false;
this.hasResult = true;
this.optimizeForWrite = false;
}
//@Override
public String getId() {
return this.id;
}
//@Override
public boolean hasResult() {
return hasResult;
}
public void setHasResult(boolean hasResult) {
this.hasResult = hasResult;
}
//@Override
public boolean isHA() {
return HA;
}
public void setHA(boolean HA) {
this.HA = HA;
}
public void setId(String id) {
this.id = id;
}
//@Override
public boolean optimizeForWrite() {
return optimizeForWrite;
}
public void setOptimizeForWrite(boolean optimizeForWrite) {
this.optimizeForWrite = optimizeForWrite;
}
//@Override
public void execute(FunctionContext functionContext) {
Region<?,?> region = null;
RegionFunctionContext regionFunctionContext = (RegionFunctionContext)functionContext;
region = getRegionForContext(regionFunctionContext);
RemoteMethodInvocation invocation = null;
if (regionFunctionContext.getArguments().getClass().isArray()) {
invocation = (RemoteMethodInvocation)((Serializable[])regionFunctionContext.getArguments())[0];
} else {
invocation = (RemoteMethodInvocation)regionFunctionContext.getArguments();
}
regionFunctionContext.getFilter();
Object instance = createDelegateInstance(invocation.getClassName(), region);
Serializable result = invokeDelegateMethod(instance,invocation, region);
if (hasResult()){
sendResults(regionFunctionContext.getResultSender(),result);
}
}
protected final Serializable invokeDelegateMethod(Object instance, RemoteMethodInvocation invocation, Region<?,?> region) {
// Null parameters returns any method signature
Method method = ReflectionUtils.findMethod(instance.getClass(),invocation.getMethodName(),(Class<?>[])null);
if (method == null ) {
throw new FunctionException("cannot find method ["+ invocation.getMethodName() +
"] on type [" + instance.getClass().getName() + "]");
}
if (logger.isDebugEnabled()) {
logger.debug(String.format("about to invoke method %s on class %s", invocation.getMethodName(),invocation.getClassName()));
for (Object arg: invocation.getArguments()) {
logger.debug("arg:"+ arg.getClass().getName() + " " + arg.toString());
}
}
return (Serializable)ReflectionUtils.invokeMethod(method, instance, (Object[]) invocation.getArguments());
}
protected final Object createDelegateInstance(String delegateClassName, Map<?,?> region) {
Object instance = null;
try {
Class<?> clazz = Class.forName(delegateClassName);
Constructor<?> defaultConstructor = ClassUtils.getConstructorIfAvailable(clazz, new Class<?>[] {});
if (region != null){
Constructor<?> mapConstructor = ClassUtils.getConstructorIfAvailable(clazz, Map.class);
if (mapConstructor != null){
instance = mapConstructor.newInstance(region);
}
}
if (instance == null) {
if (defaultConstructor == null) {
throw new FunctionException("Delegate type [" + delegateClassName + "] has no default constructor");
}
instance = defaultConstructor.newInstance();
}
} catch (ClassNotFoundException e) {
logger.error(e.getMessage(),e);
throw new FunctionException("Delegate type [" + delegateClassName + "] not found", e);
} catch (SecurityException e) {
logger.error(e.getMessage(),e);
throw new FunctionException(e);
} catch (IllegalArgumentException e) {
logger.error(e.getMessage(),e);
throw new FunctionException("Delegate constructor failed",e);
} catch (InstantiationException e) {
logger.error(e.getMessage(),e);
throw new FunctionException("Delegate constructor failed",e);
} catch (IllegalAccessException e) {
logger.error(e.getMessage(),e);
throw new FunctionException("Delegate constructor failed",e);
} catch (InvocationTargetException e) {
logger.error(e.getMessage(),e);
throw new FunctionException("Delegate constructor failed",e);
} catch (Throwable t){
logger.error(t.getMessage(),t);
throw new FunctionException("Delegate constructor failed",t);
}
return instance;
}
/*
* @param regionFunctionContext
* @return
*/
private Region<?, ?> getRegionForContext(RegionFunctionContext regionFunctionContext) {
Region<?,?> region = regionFunctionContext.getDataSet();
if (PartitionRegionHelper.isPartitionedRegion(region)) {
if (logger.isDebugEnabled()){
logger.debug("this is a partitioned region - filtering local data for context");
}
region = PartitionRegionHelper.getLocalDataForContext(regionFunctionContext);
}
if (logger.isDebugEnabled()){
logger.debug("region contains " + region.size() + " items");
}
return region;
}
@SuppressWarnings("unchecked")
private void sendResults(ResultSender<Object> resultSender, Serializable result) {
if (result == null){
resultSender.lastResult(null);
return;
}
Serializable lastItem = result;
List<Serializable> results = null;
if (ObjectUtils.isArray(result)){
results = Arrays.asList((Serializable[])result);
} else if (List.class.isAssignableFrom(result.getClass())) {
results = (List<Serializable>)result;
}
if (results != null){
int i = 0;
for (Serializable item: results){
if (i++ < results.size() - 1) {
resultSender.sendResult(item);
} else {
lastItem = item;
}
}
}
resultSender.lastResult(lastItem);
}
}

View File

@@ -0,0 +1,194 @@
/*
* Copyright 2002-2011 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 java.io.Serializable;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.List;
import java.util.Set;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.util.ClassUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.StringUtils;
import com.gemstone.gemfire.cache.Region;
import com.gemstone.gemfire.cache.execute.Function;
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;
import com.gemstone.gemfire.internal.util.ArrayUtils;
/**
* Invokes a POJO's given method as a Gemfire remote function.
* If the POJO has a constructor that takes a Map, and the function context is Region, the
* region will be injected. The delegate class name, the method name, and the method arguments
* are part of a remote function invocation, therefore all arguments must be serializable.
* The delegate class must be the class path of the remote cache(s)
* @author David Turanski
*
*/
@SuppressWarnings("serial")
public class PojoFunctionWrapper implements Function {
private static transient Log logger = LogFactory.getLog(PojoFunctionWrapper.class);
private volatile boolean HA;
private volatile boolean optimizeForWrite;
private final boolean hasResult;
private final Object target;
private final Method method;
private final String id;
private volatile Integer regionParameterPosition;
public PojoFunctionWrapper(Object target, Method method, String id) {
this.id = StringUtils.hasText(id) ? id : ClassUtils.getQualifiedMethodName(method);
this.target = target;
this.method = method;
this.HA = false;
this.hasResult = !(method.getReturnType().equals(void.class));
this.optimizeForWrite = false;
}
//@Override
public String getId() {
return this.id;
}
//@Override
public boolean hasResult() {
return this.hasResult;
}
//@Override
public boolean isHA() {
return this.HA;
}
public void setHA(boolean HA) {
this.HA = HA;
}
//@Override
public boolean optimizeForWrite() {
return this.optimizeForWrite;
}
public void setOptimizeForWrite(boolean optimizeForWrite) {
this.optimizeForWrite = optimizeForWrite;
}
public void setRegionParameterPosition(int regionParameterPosition) {
this.regionParameterPosition = regionParameterPosition;
}
//@Override
public void execute(FunctionContext functionContext) {
Object[] args = (functionContext.getArguments().getClass().isArray()) ? (Object[]) functionContext
.getArguments() : new Object[] { functionContext.getArguments() };
Serializable result = null;
if (functionContext instanceof RegionFunctionContext) {
RegionFunctionContext regionFunctionContext = (RegionFunctionContext) functionContext;
Region<?, ?> region = getRegionForContext(regionFunctionContext);
//TODO: Not sure if filter is needed at this point
Set<?> filter = regionFunctionContext.getFilter();
//Insert the region into the associated position
if (this.regionParameterPosition != null) {
ArrayUtils.insert(args, regionParameterPosition, region);
}
} else {
}
result = invokeTargetMethod(args);
if (hasResult()) {
sendResults(functionContext.getResultSender(), result);
}
}
protected final Serializable invokeTargetMethod(Object[] args) {
if (logger.isDebugEnabled()) {
logger.debug(String.format("about to invoke method %s on class %s as function %s", method.getName(), target
.getClass().getName(), this.id));
for (Object arg : args) {
logger.debug("arg:" + arg.getClass().getName() + " " + arg.toString());
}
}
return (Serializable) ReflectionUtils.invokeMethod(method, target, (Object[]) args);
}
/*
* @param regionFunctionContext
* @return
*/
private Region<?, ?> getRegionForContext(RegionFunctionContext regionFunctionContext) {
Region<?, ?> region = regionFunctionContext.getDataSet();
if (PartitionRegionHelper.isPartitionedRegion(region)) {
if (logger.isDebugEnabled()) {
logger.debug("this is a partitioned region - filtering local data for context");
}
region = PartitionRegionHelper.getLocalDataForContext(regionFunctionContext);
}
if (logger.isDebugEnabled()) {
logger.debug("region contains " + region.size() + " items");
}
return region;
}
@SuppressWarnings("unchecked")
private void sendResults(ResultSender<Object> resultSender, Serializable result) {
if (result == null) {
resultSender.lastResult(null);
return;
}
Serializable lastItem = result;
List<Serializable> results = null;
if (ObjectUtils.isArray(result)) {
results = Arrays.asList((Serializable[]) result);
} else if (List.class.isAssignableFrom(result.getClass())) {
results = (List<Serializable>) result;
}
if (results != null) {
int i = 0;
for (Serializable item : results) {
if (i++ < results.size() - 1) {
resultSender.sendResult(item);
} else {
lastItem = item;
}
}
}
resultSender.lastResult(lastItem);
}
}

View File

@@ -0,0 +1,58 @@
/*
* Copyright 2002-2011 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 java.io.Serializable;
import java.util.Set;
import com.gemstone.gemfire.cache.Region;
import com.gemstone.gemfire.cache.execute.Execution;
import com.gemstone.gemfire.cache.execute.Function;
import com.gemstone.gemfire.cache.execute.FunctionService;
/**
* @author David Turanski
*
*/
public class RegionFunctionExecution<T> extends FunctionExecution<T> {
private final Region<?, ?> region;
private volatile Set<?> keys;
public RegionFunctionExecution(Region<?, ?> region, Function function, Serializable... args) {
super(function, args);
this.region = region;
}
public RegionFunctionExecution(Region<?, ?> region, String functionId, Serializable... args) {
super(functionId, args);
this.region = region;
}
public void setKeys(Set<?> keys) {
this.keys = keys;
}
protected Set<?> getKeys() {
return this.keys;
}
/* (non-Javadoc)
* @see org.springframework.data.gemfire.function.FunctionExecution#getExecution()
*/
@Override
protected Execution getExecution() {
return FunctionService.onRegion(region);
}
}

View File

@@ -0,0 +1,60 @@
/*
* Copyright 2002-2011 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 java.io.Serializable;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
/**
* @author David Turanski
*
*/
@SuppressWarnings("serial")
public class RemoteMethodInvocation implements Serializable {
private final String methodName;
private String className;
private final Serializable[] arguments;
public RemoteMethodInvocation(Class<?> clazz, String methodName, Serializable... args) {
this(methodName,args);
Assert.notNull(clazz , "class cannot be null");
this.className = clazz.getName();
}
public RemoteMethodInvocation(String className, String methodName, Serializable... args) {
this(methodName,args);
Assert.isTrue(StringUtils.hasLength(className.trim()) , "class cannot be null or empty");
this.className = className;
}
private RemoteMethodInvocation(String methodName, Serializable... args){
Assert.isTrue(StringUtils.hasLength(methodName.trim()), "methodName cannot be null or empty");
Assert.isTrue(StringUtils.hasLength(methodName.trim()), "methodName cannot be null or empty");
this.methodName = methodName;
this.arguments = args == null ? new Serializable[]{} : args;
}
public String getMethodName() {
return methodName;
}
public String getClassName() {
return className;
}
public Serializable[] getArguments() {
return arguments;
}
}

View File

@@ -0,0 +1,60 @@
/*
* Copyright 2002-2011 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 java.io.Serializable;
import com.gemstone.gemfire.cache.RegionService;
import com.gemstone.gemfire.cache.execute.Execution;
import com.gemstone.gemfire.cache.execute.Function;
import com.gemstone.gemfire.cache.execute.FunctionService;
/**
* @author David Turanski
*
*/
public class ServerFunctionExecution<T> extends FunctionExecution<T> {
private final RegionService regionService;
/**
*
* @param regionService e.g., Cache,Client, or GemFireCache
* @param function
* @param args
*/
public ServerFunctionExecution(RegionService regionService, Function function, Serializable... args) {
super(function, args);
this.regionService = regionService;
}
/**
*
* @param regionService e.g., Cache,Client, or GemFireCache
* @param functionId
* @param args
*/
public ServerFunctionExecution(RegionService regionService, String functionId, Serializable... args) {
super(functionId, args);
this.regionService = regionService;
}
/* (non-Javadoc)
* @see org.springframework.data.gemfire.function.FunctionExecution#getExecution()
*/
@Override
protected Execution getExecution() {
return FunctionService.onServer(this.regionService);
}
}

View File

@@ -0,0 +1,60 @@
/*
* Copyright 2002-2011 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 java.io.Serializable;
import com.gemstone.gemfire.cache.RegionService;
import com.gemstone.gemfire.cache.execute.Execution;
import com.gemstone.gemfire.cache.execute.Function;
import com.gemstone.gemfire.cache.execute.FunctionService;
/**
* @author David Turanski
*
*/
public class ServersFunctionExecution<T> extends FunctionExecution<T> {
private final RegionService regionService;
/**
*
* @param regionService e.g., Cache,Client, or GemFireCache
* @param function
* @param args
*/
public ServersFunctionExecution(RegionService regionService, Function function, Serializable... args) {
super(function, args);
this.regionService = regionService;
}
/**
*
* @param regionService e.g., Cache,Client, or GemFireCache
* @param functionId
* @param args
*/
public ServersFunctionExecution(RegionService regionService, String functionId, Serializable... args) {
super(functionId, args);
this.regionService = regionService;
}
/* (non-Javadoc)
* @see org.springframework.data.gemfire.function.FunctionExecution#getExecution()
*/
@Override
protected Execution getExecution() {
return FunctionService.onServers(this.regionService);
}
}

View File

@@ -0,0 +1,64 @@
/*
* Copyright 2002-2012 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 java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.context.annotation.ComponentScan.Filter;
/**
* @author David Turanski
*
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface EnableGemfireFunctions {
/**
* Alias for the {@link #basePackages()} attribute. Allows for more concise annotation declarations e.g.:
* {@code @EnableGemfireRepositories("org.my.pkg")} instead of
* {@code @EnableGemfireRepositories(basePackages="org.my.pkg")}.
*/
String[] value() default {};
/**
* Base packages to scan for annotated components. {@link #value()} is an alias for (and mutually exclusive with) this
* attribute. Use {@link #basePackageClasses()} for a type-safe alternative to String-based package names.
*/
String[] basePackages() default {};
/**
* Type-safe alternative to {@link #basePackages()} for specifying the packages to scan for annotated components. The
* package of each class specified will be scanned. Consider creating a special no-op marker class or interface in
* each package that serves no purpose other than being referenced by this attribute.
*/
Class<?>[] basePackageClasses() default {};
/**
* Specifies which types are eligible for component scanning. Further narrows the set of candidate components from
* everything in {@link #basePackages()} to everything in the base packages that matches the given filter or filters.
*/
Filter[] includeFilters() default {};
/**
* Specifies which types are not eligible for component scanning.
*/
Filter[] excludeFilters() default {};
}

View File

@@ -0,0 +1,41 @@
/*
* Copyright 2002-2012 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 java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @author David Turanski
*
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface GemfireFunction {
/**
* The name of the registered function. If not provided the fully qualified method name will be used,
* e.g., 'com.example.SomeClass.someMethod'
* @return the function id
*/
String id() default "";
/**
* is the function HA - highly available
*/
boolean HA() default false;
/**
* is the function optimized for write operations
*/
boolean optimizeForWrite() default false;
}

View File

@@ -0,0 +1,86 @@
/*
* Copyright 2002-2012 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 java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Map;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.util.Assert;
import org.springframework.util.ReflectionUtils;
/**
* @author David Turanski
*
*/
public class GemfireFunctionBeanPostProcessor implements BeanPostProcessor {
private static final String GEMFIRE_FUNCTION_ANNOTATION_NAME = GemfireFunction.class.getName();
private static final String REGION_DATA_ANNOTATION_NAME = RegionData.class.getName();
/* (non-Javadoc)
* @see org.springframework.beans.factory.config.BeanPostProcessor#postProcessBeforeInitialization(java.lang.Object, java.lang.String)
*/
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
/* (non-Javadoc)
* @see org.springframework.beans.factory.config.BeanPostProcessor#postProcessAfterInitialization(java.lang.Object, java.lang.String)
*/
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
Method[] methods = ReflectionUtils.getAllDeclaredMethods(bean.getClass());
for (Method method: methods) {
GemfireFunction annotation = AnnotationUtils.getAnnotation(method, GemfireFunction.class);
if (annotation != null) {
Assert.isTrue(Modifier.isPublic(method.getModifiers()),"The method " + method.getName()+ " annotated with" + GEMFIRE_FUNCTION_ANNOTATION_NAME+ " must be public");
Map<String,Object> attributes = AnnotationUtils.getAnnotationAttributes(annotation,false,true);
processParameterAnnotations(method);
GemfireFunctionUtils.registerFunctionForPojoMethod(bean, method, attributes, false);
}
}
return bean;
}
private void processParameterAnnotations(Method method) {
Annotation[][] parameterAnnotations = method.getParameterAnnotations();
if (parameterAnnotations.length == 0) {
return;
}
Class<?>[] paramTypes = method.getParameterTypes();
for (int i=0; i< parameterAnnotations.length; i++) {
Annotation[] annotations = parameterAnnotations[i];
if (annotations.length > 0) {
System.out.println("found annotations for parameter in position " + i + " " + paramTypes[i] );
for (Annotation annotation:annotations) {
System.out.println("Annotation type:" + annotation.annotationType().getName());
}
}
}
}
}

View File

@@ -0,0 +1,63 @@
/*
* Copyright 2002-2012 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 java.lang.reflect.Method;
import java.util.Map;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.data.gemfire.function.PojoFunctionWrapper;
import com.gemstone.gemfire.cache.Region;
import com.gemstone.gemfire.cache.execute.FunctionService;
/**
* @author David Turanski
*
*/
public abstract class GemfireFunctionUtils {
private static Log log = LogFactory.getLog(GemfireFunctionUtils.class);
public static void registerFunctionForPojoMethod(Object target, Method method, Map<String,Object> attributes, boolean overwrite) {
String id = attributes.containsKey("id") ? (String)attributes.get("id") : "";
PojoFunctionWrapper function = new PojoFunctionWrapper(target,method, id);
if (attributes.containsKey("HA")) {
function.setHA((Boolean)attributes.get("HA"));
}
if (attributes.containsKey("optimizeForWrite")) {
function.setOptimizeForWrite((Boolean)attributes.get("optimizeForWrite"));
}
if (FunctionService.isRegistered(function.getId())) {
if (overwrite) {
if (log.isDebugEnabled()) {
log.debug("unregistering function definition " + function.getId());
}
FunctionService.unregisterFunction(function.getId());
}
}
if (!FunctionService.isRegistered(function.getId())){
FunctionService.registerFunction(function);
if (log.isDebugEnabled()) {
log.debug("registered function " + function.getId());
}
} else {
if (log.isDebugEnabled()) {
log.debug("function already registered " + function.getId());
}
}
}
}

View File

@@ -0,0 +1,33 @@
/*
* Copyright 2002-2012 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 java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
*
* Used to inject Region data into a function execution. The annotated parameter must be of type
* {@link Map}. The contents depends on the region configuration (for a partitioned region, this will
* contain only entries for the local partition)
* and any filters configured for the function context.
* @author David Turanski
*
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.PARAMETER})
public @interface RegionData {
String value() default "";
}

View File

@@ -1,6 +1,8 @@
http\://www.springframework.org/schema/gemfire/spring-gemfire-1.0.xsd=org/springframework/data/gemfire/config/spring-gemfire-1.0.xsd
http\://www.springframework.org/schema/gemfire/spring-gemfire-1.1.xsd=org/springframework/data/gemfire/config/spring-gemfire-1.1.xsd
http\://www.springframework.org/schema/gemfire/spring-gemfire-1.2.xsd=org/springframework/data/gemfire/config/spring-gemfire-1.2.xsd
http\://www.springframework.org/schema/gemfire/spring-gemfire.xsd=org/springframework/data/gemfire/config/spring-gemfire-1.2.xsd
http\://www.springframework.org/schema/gemfire/spring-gemfire-1.3.xsd=org/springframework/data/gemfire/config/spring-gemfire-1.3.xsd
http\://www.springframework.org/schema/gemfire/spring-gemfire.xsd=org/springframework/data/gemfire/config/spring-gemfire-1.3.xsd
http\://www.springframework.org/schema/data/gemfire/spring-data-gemfire-1.2.xsd=org/springframework/data/gemfire/config/spring-data-gemfire-1.2.xsd
http\://www.springframework.org/schema/data/gemfire/spring-data-gemfire.xsd=org/springframework/data/gemfire/config/spring-data-gemfire-1.2.xsd
http\://www.springframework.org/schema/data/gemfire/spring-data-gemfire-1.3.xsd=org/springframework/data/gemfire/config/spring-data-gemfire-1.3.xsd
http\://www.springframework.org/schema/data/gemfire/spring-data-gemfire.xsd=org/springframework/data/gemfire/config/spring-data-gemfire-1.3.xsd

View File

@@ -0,0 +1,50 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<xsd:schema xmlns="http://www.springframework.org/schema/data/gemfire" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:tool="http://www.springframework.org/schema/tool"
xmlns:repository="http://www.springframework.org/schema/data/repository"
targetNamespace="http://www.springframework.org/schema/data/gemfire" elementFormDefault="qualified" attributeFormDefault="unqualified" version="1.3">
<xsd:import namespace="http://www.springframework.org/schema/beans"/>
<xsd:import namespace="http://www.springframework.org/schema/tool"/>
<xsd:import namespace="http://www.springframework.org/schema/data/repository" schemaLocation="http://www.springframework.org/schema/data/repository/spring-repository.xsd"/>
<xsd:import namespace="http://www.springframework.org/schema/gemfire" schemaLocation="http://www.springframework.org/schema/gemfire/spring-gemfire.xsd"/>
<!-- -->
<xsd:annotation>
<xsd:documentation><![CDATA[
Namespace support for the Spring Data GemFire Repositories.
]]></xsd:documentation>
</xsd:annotation>
<!-- -->
<!-- Repositories -->
<xsd:element name="repositories">
<xsd:complexType>
<xsd:complexContent>
<xsd:extension base="repository:repositories">
<xsd:attributeGroup ref="gemfire-repository-attributes"/>
<xsd:attributeGroup ref="repository:repository-attributes"/>
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
</xsd:element>
<!-- -->
<xsd:attributeGroup name="gemfire-repository-attributes">
<xsd:attribute name="mapping-context-ref" type="mappingContextRef">
<xsd:annotation>
<xsd:documentation>
The reference to a MappingContext. If not set a default one will be created.
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
</xsd:attributeGroup>
<!-- -->
<xsd:simpleType name="mappingContextRef">
<xsd:annotation>
<xsd:appinfo>
<tool:annotation kind="ref">
<tool:assignable-to type="org.springframework.data.gemfire.GemfireMappingContext"/>
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>
<xsd:union memberTypes="xsd:string"/>
</xsd:simpleType>
<!-- -->
</xsd:schema>

File diff suppressed because it is too large Load Diff

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2011-2012 the original author or authors.
* Copyright 2011 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.
@@ -35,24 +35,27 @@ public class ForkUtil {
public static OutputStream cloneJVM(String argument) {
String cp = System.getProperty("java.class.path");
String home = System.getProperty("java.home");
String home = System.getProperty("java.home");
Process proc = null;
String sp = System.getProperty("file.separator");
String java = home + sp + "bin" + sp + "java";
String argCp = " -cp " + cp;
String argClass = argument;
String cmd = java + "-cp " + cp + " " + argClass;
final Process proc;
String cmd = java + argCp + " " + argClass;
try {
ProcessBuilder builder = new ProcessBuilder(java , "-cp", cp, argClass);
builder.redirectErrorStream(true);
proc = builder.start();
//ProcessBuilder builder = new ProcessBuilder(cmd, argCp, argClass);
//builder.redirectErrorStream(true);
proc = Runtime.getRuntime().exec(cmd);
} catch (IOException ioe) {
throw new IllegalStateException("Cannot start command " + cmd, ioe);
}
System.out.println("Started fork from command\n" + cmd);
final BufferedReader br = new BufferedReader(new InputStreamReader(proc.getInputStream()));
final Process p = proc;
final BufferedReader br = new BufferedReader(new InputStreamReader(p.getInputStream()));
final AtomicBoolean run = new AtomicBoolean(true);
Thread reader = new Thread(new Runnable() {
@@ -80,11 +83,11 @@ public class ForkUtil {
System.out.println("Stopping fork...");
run.set(false);
os = null;
if (proc != null)
proc.destroy();
if (p != null)
p.destroy();
try {
proc.waitFor();
p.waitFor();
} catch (InterruptedException e) {
// ignore
}
@@ -95,9 +98,17 @@ public class ForkUtil {
os = proc.getOutputStream();
return os;
}
public static OutputStream cacheServer(Class<?> clazz) {
return startCacheServer(clazz.getName());
}
public static OutputStream cacheServer() {
String className = "org.springframework.data.gemfire.fork.CacheServerProcess";
return startCacheServer("org.springframework.data.gemfire.fork.CacheServerProcess");
}
private static OutputStream startCacheServer(String className) {
if (controlFileExists(className)) {
deleteControlFile(className);
}
@@ -131,22 +142,17 @@ public class ForkUtil {
}
public static boolean deleteControlFile(String name) {
String path = getControlFilePath(name);
String path = TEMP_DIR + File.separator + name;
return new File(path).delete();
}
public static boolean createControlFile(String name) throws IOException {
String path = getControlFilePath(name);
System.out.println("creating " + path);
String path = TEMP_DIR + File.separator + name;
return new File(path).createNewFile();
}
public static boolean controlFileExists(String name) {
String path = getControlFilePath(name);
String path = TEMP_DIR + File.separator + name;
return new File(path).exists();
}
private static String getControlFilePath(String name) {
return TEMP_DIR.endsWith(File.separator)? TEMP_DIR + name : TEMP_DIR + File.separator + name;
}
}

View File

@@ -0,0 +1,107 @@
/*
* Copyright 2011 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.fork;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.Properties;
import org.springframework.data.gemfire.ForkUtil;
import org.springframework.data.gemfire.function.MethodInvokingFunction;
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.Scope;
import com.gemstone.gemfire.cache.execute.FunctionAdapter;
import com.gemstone.gemfire.cache.execute.FunctionContext;
import com.gemstone.gemfire.cache.execute.FunctionService;
import com.gemstone.gemfire.cache.server.CacheServer;
import com.gemstone.gemfire.distributed.DistributedSystem;
/**
* @author Costin Leau
*/
public class FunctionCacheServerProcess {
static Region testRegion;
public static void main(String[] args) throws Exception {
Properties props = new Properties();
props.setProperty("name", "CqServer");
props.setProperty("log-level", "config");
System.out.println("\nConnecting to the distributed system and creating the cache.");
DistributedSystem ds = DistributedSystem.connect(props);
Cache cache = CacheFactory.create(ds);
// Create region.
AttributesFactory factory = new AttributesFactory();
factory.setDataPolicy(DataPolicy.REPLICATE);
factory.setScope(Scope.DISTRIBUTED_ACK);
testRegion = cache.createRegion("test-function", factory.create());
System.out.println("Test region, " + testRegion.getFullPath() + ", created in cache.");
// Start Cache Server.
CacheServer server = cache.addCacheServer();
server.setPort(40404);
server.setNotifyBySubscription(true);
server.start();
System.out.println("Server started");
testRegion.put("one", 1);
testRegion.put("two", 2);
testRegion.put("three", 3);
FunctionService.registerFunction(new ServerFunction());
FunctionService.registerFunction(new MethodInvokingFunction());
ForkUtil.createControlFile(FunctionCacheServerProcess.class.getName());
System.out.println("Waiting for shutdown");
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));
bufferedReader.readLine();
}
static class ServerFunction extends FunctionAdapter {
/* (non-Javadoc)
* @see com.gemstone.gemfire.cache.execute.FunctionAdapter#execute(com.gemstone.gemfire.cache.execute.FunctionContext)
*/
@Override
public void execute(FunctionContext functionContext) {
Object[] args = (Object[])functionContext.getArguments();
testRegion.put(args[0], args[1]);
functionContext.getResultSender().lastResult(null);
}
/* (non-Javadoc)
* @see com.gemstone.gemfire.cache.execute.FunctionAdapter#getId()
*/
@Override
public String getId() {
return "serverFunction";
}
}
}

View File

@@ -0,0 +1,157 @@
/*
* Copyright 2002-2011 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.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import java.util.HashSet;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
import org.springframework.data.gemfire.ForkUtil;
import org.springframework.data.gemfire.fork.FunctionCacheServerProcess;
import org.springframework.data.gemfire.function.foo.Foo;
import com.gemstone.gemfire.cache.Region;
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.Pool;
import com.gemstone.gemfire.cache.client.PoolFactory;
import com.gemstone.gemfire.cache.client.PoolManager;
/**
* @author David Turanski
*
*/
public class FunctionExecutionTests {
private static ClientCache cache = null;
private static Pool pool = null;
private static Region<String, Integer> clientRegion = null;
@BeforeClass
public static void startUp() throws Exception {
ForkUtil.cacheServer(FunctionCacheServerProcess.class);
Properties props = new Properties();
props.put("mcast-port", "0");
props.put("name", "function-client");
props.put("log-level", "warning");
ClientCacheFactory ccf = new ClientCacheFactory(props);
ccf.setPoolSubscriptionEnabled(true);
cache = ccf.create();
PoolFactory pf = PoolManager.createFactory();
pf.addServer("localhost", 40404);
pf.setSubscriptionEnabled(true);
pool = pf.create("client");
ClientRegionFactory<String, Integer> crf = cache.createClientRegionFactory(ClientRegionShortcut.PROXY);
crf.setPoolName("client");
clientRegion = crf.create("test-function");
}
@AfterClass
public static void cleanUp() {
ForkUtil.sendSignal();
if (clientRegion != null) {
clientRegion.destroyRegion();
}
if (pool != null) {
pool.destroy();
pool = null;
}
if (cache != null) {
cache.close();
}
cache = null;
}
@Test
public void testRegionExecution() {
RemoteMethodInvocation invocation = new RemoteMethodInvocation(Foo.class, "oneArg", "one");
RegionFunctionExecution<Integer> execution = new RegionFunctionExecution<Integer>(clientRegion,
new MethodInvokingFunction(), invocation);
int result = execution.executeAndExtract();
assertEquals(1, result);
}
@Test
public void testRegionExecutionWithRegisteredFunction() {
RemoteMethodInvocation invocation = new RemoteMethodInvocation(Foo.class, "oneArg", "one");
RegionFunctionExecution<Integer> execution = new RegionFunctionExecution<Integer>(clientRegion,
new MethodInvokingFunction().getId(), invocation);
int result = execution.executeAndExtract();
assertEquals(1, result);
}
// TODO: Filter only works on partitioned region. No effect here, but server
// won't start with a partitioned region. Probably because no locator
@Test
public void testRegionExecutionWithFilter() {
RemoteMethodInvocation invocation = new RemoteMethodInvocation(Foo.class, "oneArg", "one");
Set<String> keys = new HashSet<String>();
keys.add("two");
RegionFunctionExecution<Integer> execution = new RegionFunctionExecution<Integer>(clientRegion,
new MethodInvokingFunction().getId(), invocation);
execution.setKeys(keys);
Integer result = execution.executeAndExtract();
// assertEquals(null,result.get(0));
assertEquals(1, result.intValue());
}
@Test
public void testRegionExecutionForMap() {
RemoteMethodInvocation invocation = new RemoteMethodInvocation(Foo.class, "getMapWithNoArgs");
RegionFunctionExecution<Map<String, Integer>> execution = new RegionFunctionExecution<Map<String, Integer>>(
clientRegion, new MethodInvokingFunction().getId(), invocation);
execution.execute();
Map<String, Integer> result = execution.executeAndExtract();
assertTrue(result.containsKey("one"));
assertEquals(1, result.get("one").intValue());
}
@Test
public void testServersExecutionWithRegisteredFunction() {
assertNull(clientRegion.get("four"));
ServersFunctionExecution<?> execution = new ServersFunctionExecution<Object>(cache, "serverFunction", "four", new Integer(
4));
execution.execute();
assertNotNull(clientRegion.get("four"));
}
@Test
public void testServerExecutionWithRegisteredFunction() {
assertNull(clientRegion.get("five"));
ServerFunctionExecution<?> execution = new ServerFunctionExecution<Object>(cache, "serverFunction", "five", new Integer(5));
Object result = execution.executeAndExtract();
assertNull(result);
assertNotNull(clientRegion.get("five"));
}
}

View File

@@ -0,0 +1,135 @@
/*
* Copyright 2002-2011 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;
/**
* @author David Turanski
*
*/
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.mock;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Map;
import java.util.Set;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.data.gemfire.function.foo.Foo;
import org.springframework.data.gemfire.function.foo.IFoo;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import com.gemstone.gemfire.cache.Region;
/**
*
* @author David Turanski
*
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
public class GemfireFunctionProxyFactoryBeanTest {
private GemfireFunctionOperations<?> functionOperations;
static Log logger = LogFactory.getLog(GemfireFunctionProxyFactoryBeanTest.class);
@Autowired
private IFoo foo;
@Autowired
private ApplicationContext context;
private Region<String,Integer> region;
@SuppressWarnings("unchecked")
@Before
public void setUp() {
assertNotNull(foo);
functionOperations = mock(GemfireFunctionOperations.class);
region = context.getBean("someRegion",Region.class);
assertNotNull(region);
region.put("one",1);
region.put("two",2);
region.put("three",3);
}
@Test
public void testInstance() throws Exception {
GemfireFunctionOperations<?> functionOperations = mock(GemfireFunctionOperations.class);
GemfireFunctionProxyFactoryBean proxy = new GemfireFunctionProxyFactoryBean(IFoo.class,Foo.class.getName(),functionOperations);
IFoo foo = (IFoo)proxy.getObject();
assertTrue(foo instanceof FilterAware);
}
@Test
public void testSetFilter() throws Exception {
GemfireFunctionProxyFactoryBean proxy = new GemfireFunctionProxyFactoryBean(IFoo.class,Foo.class.getName(),functionOperations);
IFoo foo = (IFoo)proxy.getObject();
Set<String> filter = Collections.singleton("foo");
Object obj = ((FilterAware) foo).setFilter(filter);
assertSame(obj,foo);
assertSame(filter,proxy.getFilter());
}
@Test
public void testRemoteExecutionOneArg() {
assertEquals(1,foo.oneArg("one").intValue());
((FilterAware)foo).setFilter(Collections.singleton("one"));
assertEquals(1,foo.oneArg("one").intValue());
}
@Test
public void testRemoteExecutionTwoArg() {
assertEquals(3,foo.twoArg("one","two").intValue());
}
@Test
public void testRemoteExectionArrayList() {
ArrayList<Integer> ints = new ArrayList<Integer>(Arrays.asList(new Integer[]{1,2,3}));
assertEquals(1,foo.collections(ints).get(0).intValue());
}
@Test
public void testRemoteExectionMap() {
Map<String,Integer> result = foo.getMapWithNoArgs();
assertEquals(1,result.get("one").intValue());
}
@After
public void tearDown() {
}
}

View File

@@ -0,0 +1,112 @@
/*
* Copyright 2002-2011 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.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import java.util.List;
import java.util.Properties;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
import org.springframework.data.gemfire.ForkUtil;
import org.springframework.data.gemfire.fork.FunctionCacheServerProcess;
import org.springframework.data.gemfire.function.foo.Foo;
import com.gemstone.gemfire.cache.Region;
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.Pool;
import com.gemstone.gemfire.cache.client.PoolFactory;
import com.gemstone.gemfire.cache.client.PoolManager;
/**
* @author David Turanski
*
*/
public class GemfireFunctionTemplateTests {
private static ClientCache cache = null;
private static Pool pool = null;
private static Region<String, Integer> clientRegion = null;
@BeforeClass
public static void startUp() throws Exception {
ForkUtil.cacheServer(FunctionCacheServerProcess.class);
Properties props = new Properties();
props.put("mcast-port", "0");
props.put("name", "function-client");
props.put("log-level", "warning");
ClientCacheFactory ccf = new ClientCacheFactory(props);
ccf.setPoolSubscriptionEnabled(true);
cache = ccf.create();
PoolFactory pf = PoolManager.createFactory();
pf.addServer("localhost", 40404);
pf.setSubscriptionEnabled(true);
pool = pf.create("client");
ClientRegionFactory<String, Integer> crf = cache.createClientRegionFactory(ClientRegionShortcut.PROXY);
crf.setPoolName("client");
clientRegion = crf.create("test-function");
}
@AfterClass
public static void cleanUp() {
ForkUtil.sendSignal();
if (clientRegion != null) {
clientRegion.destroyRegion();
}
if (pool != null) {
pool.destroy();
pool = null;
}
if (cache != null) {
cache.close();
}
cache = null;
}
@Test
public void testExecuteOnRegion() {
GemfireFunctionOperations<Integer> functionTemplate = new GemfireFunctionTemplate<Integer>(cache);
RemoteMethodInvocation invocation = new RemoteMethodInvocation(Foo.class,"oneArg", "two");
List<Integer> result = functionTemplate.executeOnRegion(new MethodInvokingFunction().getId(),"test-function",invocation);
assertEquals(2,result.get(0).intValue());
}
@Test
public void testExecuteOnRegionAndExtract() {
GemfireFunctionOperations<Integer> functionTemplate = new GemfireFunctionTemplate<Integer>(cache);
RemoteMethodInvocation invocation = new RemoteMethodInvocation(Foo.class,"twoArg", "two","three");
int result = functionTemplate.executeOnRegionAndExtract(new MethodInvokingFunction(),"test-function",invocation);
assertEquals(5,result);
}
@Test
public void testExecuteOnServer() {
assertNull(clientRegion.get("four"));
GemfireFunctionOperations<?> functionTemplate = new GemfireFunctionTemplate<Object>(cache);
functionTemplate.executeOnServers("serverFunction","four",4);
assertEquals(4,clientRegion.get("four").intValue());
}
}

View File

@@ -0,0 +1,194 @@
/*
* Copyright 2002-2011 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.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
import org.springframework.data.gemfire.ForkUtil;
import org.springframework.data.gemfire.GemfireCallback;
import org.springframework.data.gemfire.GemfireTemplate;
import org.springframework.data.gemfire.function.foo.Foo;
import com.gemstone.gemfire.GemFireCheckedException;
import com.gemstone.gemfire.GemFireException;
import com.gemstone.gemfire.cache.Region;
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.Pool;
import com.gemstone.gemfire.cache.client.PoolFactory;
import com.gemstone.gemfire.cache.client.PoolManager;
import com.gemstone.gemfire.cache.execute.Execution;
import com.gemstone.gemfire.cache.execute.FunctionService;
import com.gemstone.gemfire.cache.execute.ResultCollector;
/**
* @author David Turanski
*
*/
public class MethodInvokingFunctionTests {
private static ClientCache cache = null;
private static MethodInvokingFunction methodInvokingFunction = new MethodInvokingFunction();
private static Pool pool = null;
private static Region<String,Integer> clientRegion = null;
private static GemfireTemplate gemfireTemplate;
@BeforeClass
public static void startUp() throws Exception {
ForkUtil.cacheServer();
Properties props = new Properties();
props.put("mcast-port", "0");
props.put("name", "cq-client");
props.put("log-level", "warning");
ClientCacheFactory ccf = new ClientCacheFactory(props);
ccf.setPoolSubscriptionEnabled(true);
cache = ccf.create();
PoolFactory pf = PoolManager.createFactory();
pf.addServer("localhost", 40404);
pf.setSubscriptionEnabled(true);
pool = pf.create("client");
ClientRegionFactory<String, Integer> crf = cache.createClientRegionFactory(ClientRegionShortcut.LOCAL);
crf.setPoolName("client");
clientRegion = crf.create("test-cq");
gemfireTemplate = new GemfireTemplate(clientRegion);
ForkUtil.sendSignal();
Thread.sleep(500);
}
@AfterClass
public static void cleanUp() {
ForkUtil.sendSignal();
if (clientRegion != null) {
clientRegion.destroyRegion();
}
if (pool != null) {
pool.destroy();
pool = null;
}
if (cache != null) {
cache.close();
}
cache = null;
}
@Test
public void testInvokeRemoteFunctionOneArg() throws Exception {
gemfireTemplate.setExposeNativeRegion(true);
Integer val = gemfireTemplate.execute(new GemfireCallback<Integer>() {
public Integer doInGemfire(@SuppressWarnings("rawtypes") Region region) throws GemFireCheckedException, GemFireException {
RemoteMethodInvocation invocation = new RemoteMethodInvocation(Foo.class,"oneArg","one");
Execution execution = FunctionService.onRegion(region);
ResultCollector<?, ?> resultsCollector = execution.withArgs(invocation).execute(methodInvokingFunction);
ArrayList<?> result = (ArrayList<?>)resultsCollector.getResult();
return (Integer)result.get(0);
}
});
assertEquals(1,val.intValue());
}
@Test
public void testInvokeRemoteFunctionTwoArgs() throws Exception {
gemfireTemplate.setExposeNativeRegion(true);
Integer val = gemfireTemplate.execute(new GemfireCallback<Integer>() {
public Integer doInGemfire(@SuppressWarnings("rawtypes") Region region) throws GemFireCheckedException, GemFireException {
RemoteMethodInvocation invocation = new RemoteMethodInvocation(Foo.class,"twoArg","one","three");
Execution execution = FunctionService.onRegion(region);
ResultCollector<?, ?> resultsCollector = execution.withArgs(invocation).execute(methodInvokingFunction);
ArrayList<?> result = (ArrayList<?>)resultsCollector.getResult();
return (Integer)result.get(0);
}
});
assertEquals(4,val.intValue());
}
@Test
public void testInvokeRemoteFunctionCollections() throws Exception {
gemfireTemplate.setExposeNativeRegion(true);
List<Integer> val = gemfireTemplate.execute(new GemfireCallback<List<Integer>>() {
@SuppressWarnings("unchecked")
public List<Integer> doInGemfire(@SuppressWarnings("rawtypes") Region region) throws GemFireCheckedException, GemFireException {
ArrayList<Integer> list = new ArrayList<Integer>(Arrays.asList(new Integer[]{1,2,3,4,5}));
RemoteMethodInvocation invocation = new RemoteMethodInvocation(Foo.class,"collections",list);
Execution execution = FunctionService.onRegion(region);
ResultCollector<?, ?> resultsCollector = execution.withArgs(invocation).execute(methodInvokingFunction);
ArrayList<?> result = (ArrayList<?>)resultsCollector.getResult();
//If result type is a list, Gemfire merges it into the results.
return (List<Integer>)result;
}
});
assertEquals(5,val.size());
for (int i=0; i<5; i++) {
assertEquals(i+1,val.get(i).intValue());
}
}
@Test
public void testInvokeRemoteFunctionMap() throws Exception {
gemfireTemplate.setExposeNativeRegion(true);
Map<String, Integer> val = gemfireTemplate.execute(new GemfireCallback<Map<String, Integer>>() {
@SuppressWarnings("unchecked")
public Map<String, Integer> doInGemfire(@SuppressWarnings("rawtypes") Region region) throws GemFireCheckedException, GemFireException {
RemoteMethodInvocation invocation = new RemoteMethodInvocation(Foo.class,"getMapWithNoArgs");
Execution execution = FunctionService.onRegion(region);
ResultCollector<?, ?> resultsCollector = execution.withArgs(invocation).execute(methodInvokingFunction);
ArrayList<?> result = (ArrayList<?>)resultsCollector.getResult();
return (Map<String, Integer>)result.get(0);
}
});
assertEquals(3,val.size());
for (int i=0; i<3; i++) {
assertTrue(val.values().contains(i+1));
}
}
}

View File

@@ -0,0 +1,90 @@
/*
* Copyright 2002-2012 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.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import java.util.List;
import java.util.Map;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import com.gemstone.gemfire.cache.execute.Function;
import com.gemstone.gemfire.cache.execute.FunctionService;
/**
* @author David Turanski
*
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
public class AnnotationDrivenFunctionsTest {
@Autowired
ApplicationContext applicationContext;
@Test
public void testAnnotatedFunctions() {
assertTrue(FunctionService.isRegistered(FooFunction.class.getName()+".foo"));
Function function = FunctionService.getFunction(FooFunction.class.getName()+".foo");
assertFalse(function.isHA());
assertFalse(function.optimizeForWrite());
assertFalse(function.hasResult());
assertTrue(FunctionService.isRegistered(FooFunction.class.getName()+".bar"));
function = FunctionService.getFunction(FooFunction.class.getName()+".bar");
assertTrue(function.isHA());
assertFalse(function.optimizeForWrite());
assertTrue(function.hasResult());
assertTrue(FunctionService.isRegistered("foo"));
function = FunctionService.getFunction("foo");
assertTrue(function.isHA());
assertTrue(function.optimizeForWrite());
assertTrue(function.hasResult());
}
@Component
public static class FooFunction {
@GemfireFunction
public void foo () {
}
@GemfireFunction(HA=true,optimizeForWrite=false)
public String bar () {
return null;
}
}
@Component
public static class Foo2Function {
@GemfireFunction(id="foo", HA=true,optimizeForWrite=true)
public List<String> foo (Object someVal, @RegionData Map<?,?> region, Object someOtherValue) {
return null;
}
@GemfireFunction(id="injectMultipleRegions", HA=true,optimizeForWrite=true)
public List<String> injectMultipleRegions (@RegionData("someRegion") Map<?,?> someRegion, @RegionData("someOtherRegion") Map<?,?> someOtherRegion) {
return null;
}
}
}

View File

@@ -0,0 +1,73 @@
/*
* Copyright 2002-2011 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.foo;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* @author David Turanski
*
*/
public class Foo implements IFoo {
private Map<String, Integer> dataSet;
public Foo(Map<String, Integer> dataSet) {
this.dataSet = dataSet;
}
public Foo() {
}
/* (non-Javadoc)
* @see org.springframework.data.gemfire.function.foo.IFoo#oneArg(java.lang.String)
*/
public Integer oneArg(String key) {
return dataSet.get(key);
}
/* (non-Javadoc)
* @see org.springframework.data.gemfire.function.foo.IFoo#twoArg(java.lang.String, java.lang.String)
*/
public Integer twoArg(String akey, String bkey) {
if (dataSet.get(akey) != null && dataSet.get(bkey) != null) {
return dataSet.get(akey) + dataSet.get(bkey);
}
else {
return null;
}
}
/* (non-Javadoc)
* @see org.springframework.data.gemfire.function.foo.IFoo#collections(java.util.List)
*/
public List<Integer> collections(List<Integer> args) {
return args;
}
/* (non-Javadoc)
* @see org.springframework.data.gemfire.function.foo.IFoo#getMapWithNoArgs()
*/
public Map<String, Integer> getMapWithNoArgs() {
if (dataSet.size() == 0) {
return null;
}
return new HashMap<String, Integer>(dataSet);
}
}

View File

@@ -0,0 +1,32 @@
/*
* Copyright 2002-2011 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.foo;
import java.util.List;
import java.util.Map;
/**
* @author David Turanski
*
*/
public interface IFoo {
public abstract Integer oneArg(String key);
public abstract Integer twoArg(String akey, String bkey);
public abstract List<Integer> collections(List<Integer> args);
public abstract Map<String, Integer> getMapWithNoArgs();
}

View File

@@ -0,0 +1,29 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:gfe="http://www.springframework.org/schema/gemfire"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/gemfire http://www.springframework.org/schema/gemfire/spring-gemfire-1.0.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.0.xsd">
<gfe:cache/>
<gfe:replicated-region id="someRegion" />
<bean id="functionTemplate"
class="org.springframework.data.gemfire.function.GemfireFunctionTemplate">
<constructor-arg ref="gemfire-cache"/>
<property name="timeout" value="1000"/>
</bean>
<bean id="foo" class="org.springframework.data.gemfire.function.GemfireFunctionProxyFactoryBean">
<constructor-arg value="org.springframework.data.gemfire.function.foo.IFoo"/>
<constructor-arg value="org.springframework.data.gemfire.function.foo.Foo"/>
<constructor-arg ref="functionTemplate"/>
<property name="regionName" value="someRegion"/>
</bean>
</beans>

View File

@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:gfe="http://www.springframework.org/schema/gemfire"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/gemfire http://www.springframework.org/schema/gemfire/spring-gemfire-1.3.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd">
<context:component-scan base-package="org.springframework.data.gemfire.function.config"/>
<gfe:annotation-driven/>
</beans>