diff --git a/src/main/java/org/springframework/data/gemfire/config/GemfireDataNamespaceHandler.java b/src/main/java/org/springframework/data/gemfire/config/GemfireDataNamespaceHandler.java index 23c04674..3e0b4240 100644 --- a/src/main/java/org/springframework/data/gemfire/config/GemfireDataNamespaceHandler.java +++ b/src/main/java/org/springframework/data/gemfire/config/GemfireDataNamespaceHandler.java @@ -17,6 +17,7 @@ package org.springframework.data.gemfire.config; import org.springframework.beans.factory.xml.NamespaceHandlerSupport; +import org.springframework.data.gemfire.function.config.FunctionExecutionBeanDefinitionParser; import org.springframework.data.gemfire.repository.config.GemfireRepositoryConfigurationExtension; import org.springframework.data.repository.config.RepositoryBeanDefinitionParser; import org.springframework.data.repository.config.RepositoryConfigurationExtension; @@ -35,5 +36,6 @@ class GemfireDataNamespaceHandler extends NamespaceHandlerSupport { // Repository namespace RepositoryConfigurationExtension extension = new GemfireRepositoryConfigurationExtension(); registerBeanDefinitionParser("repositories", new RepositoryBeanDefinitionParser(extension)); + registerBeanDefinitionParser("function-executions", new FunctionExecutionBeanDefinitionParser()); } } \ No newline at end of file diff --git a/src/main/java/org/springframework/data/gemfire/function/FilterAware.java b/src/main/java/org/springframework/data/gemfire/function/FilterAware.java deleted file mode 100644 index 50c40dca..00000000 --- a/src/main/java/org/springframework/data/gemfire/function/FilterAware.java +++ /dev/null @@ -1,27 +0,0 @@ -/* - * 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 filter); -} diff --git a/src/main/java/org/springframework/data/gemfire/function/MethodInvokingFunction.java b/src/main/java/org/springframework/data/gemfire/function/MethodInvokingFunction.java deleted file mode 100644 index f64b0e25..00000000 --- a/src/main/java/org/springframework/data/gemfire/function/MethodInvokingFunction.java +++ /dev/null @@ -1,243 +0,0 @@ -/* - * 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.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; - this.id = FUNCTION_ID; - } - - //@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 resultSender, Serializable result) { - if (result == null){ - resultSender.lastResult(null); - return; - } - - Serializable lastItem = result; - - List results = null; - if (ObjectUtils.isArray(result)){ - results = Arrays.asList((Serializable[])result); - } else if (List.class.isAssignableFrom(result.getClass())) { - results = (List)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); - } -} - diff --git a/src/main/java/org/springframework/data/gemfire/function/RemoteMethodInvocation.java b/src/main/java/org/springframework/data/gemfire/function/RemoteMethodInvocation.java deleted file mode 100644 index 585f9379..00000000 --- a/src/main/java/org/springframework/data/gemfire/function/RemoteMethodInvocation.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * 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 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; - } -} diff --git a/src/main/java/org/springframework/data/gemfire/function/config/AbstractFunctionExecutionConfigurationSource.java b/src/main/java/org/springframework/data/gemfire/function/config/AbstractFunctionExecutionConfigurationSource.java new file mode 100644 index 00000000..6ecf6c95 --- /dev/null +++ b/src/main/java/org/springframework/data/gemfire/function/config/AbstractFunctionExecutionConfigurationSource.java @@ -0,0 +1,78 @@ +/* + * 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.util.Collection; +import java.util.HashSet; +import java.util.Set; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider; +import org.springframework.context.annotation.ScannedGenericBeanDefinition; +import org.springframework.core.io.ResourceLoader; +import org.springframework.core.type.filter.TypeFilter; + +/** + * Annotation based configuration source for function executions + * + * @author David Turanski + * + */ +abstract class AbstractFunctionExecutionConfigurationSource implements FunctionExecutionConfigurationSource { + protected Log logger = LogFactory.getLog(this.getClass()); + + + private static Set> functionExecutionAnnotationTypes; + + static { + functionExecutionAnnotationTypes = new HashSet>(); + functionExecutionAnnotationTypes.add(OnRegion.class); + functionExecutionAnnotationTypes.add(OnServer.class); + functionExecutionAnnotationTypes.add(OnServers.class); + functionExecutionAnnotationTypes.add(OnMember.class); + functionExecutionAnnotationTypes.add(OnMembers.class); + } + + + static Set> getFunctionExecutionAnnotationTypes() { + return functionExecutionAnnotationTypes; + } + + + public Collection getCandidates(ResourceLoader loader) { + ClassPathScanningCandidateComponentProvider scanner = new FunctionExecutionComponentProvider(getIncludeFilters(),functionExecutionAnnotationTypes); + scanner.setResourceLoader(loader); + + for (TypeFilter filter : getExcludeFilters()) { + scanner.addExcludeFilter(filter); + } + + Set result = new HashSet(); + + for (String basePackage : getBasePackages()) { + if (logger.isDebugEnabled()) { + logger.debug("scanning package " + basePackage); + } + Collection components = scanner.findCandidateComponents(basePackage); + for (BeanDefinition definition : components) { + result.add((ScannedGenericBeanDefinition)definition); + } + } + + return result; + } + +} diff --git a/src/main/java/org/springframework/data/gemfire/function/config/AnnotationFunctionExecutionConfigurationSource.java b/src/main/java/org/springframework/data/gemfire/function/config/AnnotationFunctionExecutionConfigurationSource.java index 313cc6ba..b64285b7 100644 --- a/src/main/java/org/springframework/data/gemfire/function/config/AnnotationFunctionExecutionConfigurationSource.java +++ b/src/main/java/org/springframework/data/gemfire/function/config/AnnotationFunctionExecutionConfigurationSource.java @@ -15,21 +15,14 @@ package org.springframework.data.gemfire.function.config; import java.lang.annotation.Annotation; import java.util.ArrayList; import java.util.Arrays; -import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; import org.springframework.beans.BeanUtils; -import org.springframework.beans.factory.config.BeanDefinition; -import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider; import org.springframework.context.annotation.FilterType; -import org.springframework.context.annotation.ScannedGenericBeanDefinition; import org.springframework.core.annotation.AnnotationAttributes; -import org.springframework.core.io.ResourceLoader; import org.springframework.core.type.AnnotationMetadata; import org.springframework.core.type.filter.AnnotationTypeFilter; import org.springframework.core.type.filter.AssignableTypeFilter; @@ -43,25 +36,14 @@ import org.springframework.util.ClassUtils; * @author David Turanski * */ -class AnnotationFunctionExecutionConfigurationSource implements FunctionExecutionConfigurationSource { - private static Log logger = LogFactory.getLog(AnnotationFunctionExecutionConfigurationSource.class); - +class AnnotationFunctionExecutionConfigurationSource extends AbstractFunctionExecutionConfigurationSource { + private static final String BASE_PACKAGES = "basePackages"; private static final String BASE_PACKAGE_CLASSES = "basePackageClasses"; private final AnnotationMetadata metadata; private final AnnotationAttributes attributes; - private static Set> functionExecutionAnnotationTypes; - - static { - functionExecutionAnnotationTypes = new HashSet>(); - functionExecutionAnnotationTypes.add(OnRegion.class); - functionExecutionAnnotationTypes.add(OnServer.class); - functionExecutionAnnotationTypes.add(OnServers.class); - functionExecutionAnnotationTypes.add(OnMember.class); - functionExecutionAnnotationTypes.add(OnMembers.class); - } - + /** * Creates a new {@link AnnotationFunctionExecutionConfigurationSource} from the given {@link AnnotationMetadata} and @@ -87,13 +69,7 @@ class AnnotationFunctionExecutionConfigurationSource implements FunctionExecutio public Object getSource() { // TODO Auto-generated method stub return this.metadata; - } - - static Set> getFunctionExecutionAnnotationTypes() { - return functionExecutionAnnotationTypes; - } - - + } /* (non-Javadoc) * @see org.springframework.data.gemfire.function.config.FunctionExecutionConfigurationSource#getBasePackages() @@ -121,37 +97,14 @@ class AnnotationFunctionExecutionConfigurationSource implements FunctionExecutio return packages; } - @Override - public Collection getCandidates(ResourceLoader loader) { - ClassPathScanningCandidateComponentProvider scanner = new FunctionExecutionComponentProvider(getIncludeFilters(),functionExecutionAnnotationTypes); - scanner.setResourceLoader(loader); - - for (TypeFilter filter : getExcludeFilters()) { - scanner.addExcludeFilter(filter); - } - - Set result = new HashSet(); - - for (String basePackage : getBasePackages()) { - if (logger.isDebugEnabled()) { - logger.debug("scanning package " + basePackage); - } - Collection components = scanner.findCandidateComponents(basePackage); - for (BeanDefinition definition : components) { - result.add((ScannedGenericBeanDefinition)definition); - } - } - - return result; - } - - protected Iterable getIncludeFilters() { + @Override + public Iterable getIncludeFilters() { return parseFilters("includeFilters"); } - - - protected Iterable getExcludeFilters() { + + @Override + public Iterable getExcludeFilters() { return parseFilters("excludeFilters"); } diff --git a/src/main/java/org/springframework/data/gemfire/function/config/FunctionExecutionBeanDefinitionParser.java b/src/main/java/org/springframework/data/gemfire/function/config/FunctionExecutionBeanDefinitionParser.java new file mode 100644 index 00000000..5b84e9fb --- /dev/null +++ b/src/main/java/org/springframework/data/gemfire/function/config/FunctionExecutionBeanDefinitionParser.java @@ -0,0 +1,38 @@ +/* + * 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 org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.beans.factory.xml.BeanDefinitionParser; +import org.springframework.beans.factory.xml.ParserContext; +import org.w3c.dom.Element; + +/** + * @author David Turanski + * + */ +public class FunctionExecutionBeanDefinitionParser 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) { + + AbstractFunctionExecutionConfigurationSource configurationSource = + new XmlFunctionExecutionConfigurationSource(element, parserContext); + new FunctionExecutionBeanDefinitionRegistrar().registerBeanDefinitions(configurationSource, parserContext.getRegistry()); + return null; + } + +} diff --git a/src/main/java/org/springframework/data/gemfire/function/config/FunctionExecutionBeanDefinitionRegistrar.java b/src/main/java/org/springframework/data/gemfire/function/config/FunctionExecutionBeanDefinitionRegistrar.java index a2ddfa7d..89b5f581 100644 --- a/src/main/java/org/springframework/data/gemfire/function/config/FunctionExecutionBeanDefinitionRegistrar.java +++ b/src/main/java/org/springframework/data/gemfire/function/config/FunctionExecutionBeanDefinitionRegistrar.java @@ -15,8 +15,6 @@ package org.springframework.data.gemfire.function.config; import java.util.HashSet; import java.util.Set; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; import org.springframework.beans.factory.support.BeanDefinitionReaderUtils; import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.context.annotation.ImportBeanDefinitionRegistrar; @@ -34,21 +32,27 @@ import org.springframework.util.StringUtils; * */ public class FunctionExecutionBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar { - - private static Log logger = LogFactory.getLog(FunctionExecutionBeanDefinitionRegistrar.class); + /* (non-Javadoc) * @see org.springframework.context.annotation.ImportBeanDefinitionRegistrar#registerBeanDefinitions(org.springframework.core.type.AnnotationMetadata, org.springframework.beans.factory.support.BeanDefinitionRegistry) */ @Override public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry registry) { - - ResourceLoader resourceLoader = new DefaultResourceLoader(); - AnnotationFunctionExecutionConfigurationSource configurationSource = new AnnotationFunctionExecutionConfigurationSource( + AbstractFunctionExecutionConfigurationSource configurationSource = new AnnotationFunctionExecutionConfigurationSource( annotationMetadata); + registerBeanDefinitions(configurationSource, registry); + + } + + /* + * This registers bean definitions from any function execution configuration source + */ + void registerBeanDefinitions (AbstractFunctionExecutionConfigurationSource configurationSource, BeanDefinitionRegistry registry) { + ResourceLoader resourceLoader = new DefaultResourceLoader(); Set functionExecutionAnnotationTypes = new HashSet( AnnotationFunctionExecutionConfigurationSource.getFunctionExecutionAnnotationTypes().size()); - for (Class annotation : AnnotationFunctionExecutionConfigurationSource.getFunctionExecutionAnnotationTypes()) { + for (Class annotation : AbstractFunctionExecutionConfigurationSource.getFunctionExecutionAnnotationTypes()) { functionExecutionAnnotationTypes.add(annotation.getName()); } @@ -70,7 +74,6 @@ public class FunctionExecutionBeanDefinitionRegistrar implements ImportBeanDefin registry.registerBeanDefinition(beanName, builder.build(registry)); } - } private String getFunctionExecutionAnnotation(ScannedGenericBeanDefinition beanDefinition, @@ -91,4 +94,5 @@ public class FunctionExecutionBeanDefinitionRegistrar implements ImportBeanDefin return functionExecutionAnnotation; } + } diff --git a/src/main/java/org/springframework/data/gemfire/function/config/FunctionExecutionConfigurationSource.java b/src/main/java/org/springframework/data/gemfire/function/config/FunctionExecutionConfigurationSource.java index a7452a75..d8d5fcd0 100644 --- a/src/main/java/org/springframework/data/gemfire/function/config/FunctionExecutionConfigurationSource.java +++ b/src/main/java/org/springframework/data/gemfire/function/config/FunctionExecutionConfigurationSource.java @@ -12,10 +12,7 @@ */ package org.springframework.data.gemfire.function.config; -import java.util.Collection; - -import org.springframework.context.annotation.ScannedGenericBeanDefinition; -import org.springframework.core.io.ResourceLoader; +import org.springframework.core.type.filter.TypeFilter; /** * Interface for function execution configuration sources (e.g., annotation or XML configuration) to configure @@ -40,11 +37,17 @@ interface FunctionExecutionConfigurationSource { */ Iterable getBasePackages(); + /** - * Returns the scanned bean definitions - * - * @param loader - * @return + * Returns configured {@link TypeFilter}s + * @return include filters */ - Collection getCandidates(ResourceLoader loader); + Iterable getIncludeFilters(); + + /** + * Returns configured {@link TypeFilter}s + * @return exclude filters + */ + Iterable getExcludeFilters(); + } diff --git a/src/main/java/org/springframework/data/gemfire/function/config/TypeFilterParser.java b/src/main/java/org/springframework/data/gemfire/function/config/TypeFilterParser.java new file mode 100644 index 00000000..8cbb63b1 --- /dev/null +++ b/src/main/java/org/springframework/data/gemfire/function/config/TypeFilterParser.java @@ -0,0 +1,232 @@ +/* + * Copyright 2010-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.util.Collection; +import java.util.HashSet; +import java.util.regex.Pattern; + +import org.springframework.beans.BeanUtils; +import org.springframework.beans.FatalBeanException; +import org.springframework.beans.factory.parsing.ReaderContext; +import org.springframework.beans.factory.xml.XmlReaderContext; +import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider; +import org.springframework.core.type.filter.AnnotationTypeFilter; +import org.springframework.core.type.filter.AspectJTypeFilter; +import org.springframework.core.type.filter.AssignableTypeFilter; +import org.springframework.core.type.filter.RegexPatternTypeFilter; +import org.springframework.core.type.filter.TypeFilter; +import org.springframework.util.Assert; +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; + +/** + * Parser to populate the given {@link ClassPathScanningCandidateComponentProvider} with {@link TypeFilter}s parsed from + * the given {@link Element}'s children. + * + * @author Oliver Gierke + */ +class TypeFilterParser { + + private static final String FILTER_TYPE_ATTRIBUTE = "type"; + private static final String FILTER_EXPRESSION_ATTRIBUTE = "expression"; + + private final ReaderContext readerContext; + private final ClassLoader classLoader; + + /** + * Creates a new {@link TypeFilterParser} with the given {@link ReaderContext}. + * + * @param readerContext must not be {@literal null}. + */ + public TypeFilterParser(XmlReaderContext readerContext) { + this(readerContext, readerContext.getResourceLoader().getClassLoader()); + } + + /** + * Constructor to ease testing as {@link XmlReaderContext#getBeanClassLoader()} is final and thus cannot be mocked + * easily. + * + * @param readerContext must not be {@literal null}. + * @param classLoader must not be {@literal null}. + */ + TypeFilterParser(ReaderContext readerContext, ClassLoader classLoader) { + + Assert.notNull(readerContext, "ReaderContext must not be null!"); + Assert.notNull(classLoader, "ClassLoader must not be null!"); + + this.readerContext = readerContext; + this.classLoader = classLoader; + } + + public Iterable parseTypeFilters(Element element, Type type) { + + NodeList nodeList = element.getChildNodes(); + Collection filters = new HashSet(); + + for (int i = 0; i < nodeList.getLength(); i++) { + + Node node = nodeList.item(i); + Element childElement = type.getElement(node); + + if (childElement != null) { + + try { + filters.add(createTypeFilter(childElement, classLoader)); + } catch (RuntimeException e) { + readerContext.error(e.getMessage(), readerContext.extractSource(element), e.getCause()); + } + } + } + + return filters; + } + + protected TypeFilter createTypeFilter(Element element, ClassLoader classLoader) { + + String filterType = element.getAttribute(FILTER_TYPE_ATTRIBUTE); + String expression = element.getAttribute(FILTER_EXPRESSION_ATTRIBUTE); + + try { + + FilterType filter = FilterType.fromString(filterType); + return filter.getFilter(expression, classLoader); + + } catch (ClassNotFoundException ex) { + throw new FatalBeanException("Type filter class not found: " + expression, ex); + } + } + + /** + * Enum representing all the filter types available for {@code include} and {@code exclude} elements. This acts as + * factory for {@link TypeFilter} instances. + * + * @author Oliver Gierke + * @see #getFilter(String, ClassLoader) + */ + private static enum FilterType { + + ANNOTATION { + @Override + @SuppressWarnings("unchecked") + public TypeFilter getFilter(String expression, ClassLoader classLoader) throws ClassNotFoundException { + + return new AnnotationTypeFilter((Class) classLoader.loadClass(expression)); + } + }, + + ASSIGNABLE { + @Override + public TypeFilter getFilter(String expression, ClassLoader classLoader) throws ClassNotFoundException { + + return new AssignableTypeFilter(classLoader.loadClass(expression)); + } + + }, + + ASPECTJ { + @Override + public TypeFilter getFilter(String expression, ClassLoader classLoader) { + + return new AspectJTypeFilter(expression, classLoader); + } + + }, + + REGEX { + @Override + public TypeFilter getFilter(String expression, ClassLoader classLoader) { + + return new RegexPatternTypeFilter(Pattern.compile(expression)); + } + + }, + + CUSTOM { + @Override + public TypeFilter getFilter(String expression, ClassLoader classLoader) throws ClassNotFoundException { + + Class filterClass = classLoader.loadClass(expression); + if (!TypeFilter.class.isAssignableFrom(filterClass)) { + throw new IllegalArgumentException("Class is not assignable to [" + TypeFilter.class.getName() + "]: " + + expression); + } + return (TypeFilter) BeanUtils.instantiateClass(filterClass); + } + }; + + /** + * Returns the {@link TypeFilter} for the given expression and {@link ClassLoader}. + * + * @param expression + * @param classLoader + * @return + * @throws ClassNotFoundException + */ + abstract TypeFilter getFilter(String expression, ClassLoader classLoader) throws ClassNotFoundException; + + /** + * Returns the {@link FilterType} for the given type as {@link String}. + * + * @param typeString + * @return + * @throws IllegalArgumentException if no {@link FilterType} could be found for the given argument. + */ + static FilterType fromString(String typeString) { + + for (FilterType filter : FilterType.values()) { + if (filter.name().equalsIgnoreCase(typeString)) { + return filter; + } + } + + throw new IllegalArgumentException("Unsupported filter type: " + typeString); + } + } + + static enum Type { + + INCLUDE("include-filter"), EXCLUDE("exclude-filter"); + + private String elementName; + + private Type(String elementName) { + + this.elementName = elementName; + } + + /** + * Returns the {@link Element} if the given {@link Node} is an {@link Element} and it's name equals the one of the + * type. + * + * @param node + * @return + */ + Element getElement(Node node) { + + if (node.getNodeType() == Node.ELEMENT_NODE) { + String localName = node.getLocalName(); + if (elementName.equals(localName)) { + return (Element) node; + } + } + + return null; + } + } +} diff --git a/src/main/java/org/springframework/data/gemfire/function/config/XmlFunctionExecutionConfigurationSource.java b/src/main/java/org/springframework/data/gemfire/function/config/XmlFunctionExecutionConfigurationSource.java new file mode 100644 index 00000000..c9d794f8 --- /dev/null +++ b/src/main/java/org/springframework/data/gemfire/function/config/XmlFunctionExecutionConfigurationSource.java @@ -0,0 +1,81 @@ +/* + * 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.util.Arrays; + +import org.springframework.beans.factory.xml.ParserContext; +import org.springframework.core.type.filter.TypeFilter; +import org.springframework.data.gemfire.function.config.TypeFilterParser.Type; +import org.springframework.util.Assert; +import org.springframework.util.StringUtils; +import org.w3c.dom.Element; + +/** + * @author David Turanski + * + */ +class XmlFunctionExecutionConfigurationSource extends AbstractFunctionExecutionConfigurationSource { + private static final String BASE_PACKAGE = "base-package"; + private Element element; + private ParserContext context; + private Iterable includeFilters; + private Iterable excludeFilters; + + XmlFunctionExecutionConfigurationSource(Element element, ParserContext context) { + Assert.notNull(element); + Assert.notNull(context); + + this.element = element; + this.context = context; + + TypeFilterParser parser = new TypeFilterParser(context.getReaderContext()); + this.includeFilters = parser.parseTypeFilters(element, Type.INCLUDE); + this.excludeFilters = parser.parseTypeFilters(element, Type.EXCLUDE); + } + + /* (non-Javadoc) + * @see org.springframework.data.gemfire.function.config.FunctionExecutionConfigurationSource#getSource() + */ + @Override + public Object getSource() { + return context.extractSource(element); + } + + /* (non-Javadoc) + * @see org.springframework.data.gemfire.function.config.FunctionExecutionConfigurationSource#getBasePackages() + */ + @Override + public Iterable getBasePackages() { + String attribute = element.getAttribute(BASE_PACKAGE); + return Arrays.asList(StringUtils.delimitedListToStringArray(attribute, ",", " ")); + } + + + /* (non-Javadoc) + * @see org.springframework.data.gemfire.function.config.FunctionExecutionConfigurationSource#getIncludeFilters() + */ + @Override + public Iterable getIncludeFilters() { + return includeFilters; + } + + /* (non-Javadoc) + * @see org.springframework.data.gemfire.function.config.FunctionExecutionConfigurationSource#getExcludeFilters() + */ + @Override + public Iterable getExcludeFilters() { + return excludeFilters; + } + +} diff --git a/src/main/resources/org/springframework/data/gemfire/config/spring-data-gemfire-1.3.xsd b/src/main/resources/org/springframework/data/gemfire/config/spring-data-gemfire-1.3.xsd index 2de60eaa..51a2b7ae 100644 --- a/src/main/resources/org/springframework/data/gemfire/config/spring-data-gemfire-1.3.xsd +++ b/src/main/resources/org/springframework/data/gemfire/config/spring-data-gemfire-1.3.xsd @@ -31,7 +31,14 @@ targetNamespace="http://www.springframework.org/schema/data/gemfire" elementForm - + + + + + + @@ -55,7 +62,8 @@ targetNamespace="http://www.springframework.org/schema/data/gemfire" elementForm ]]> - + + diff --git a/src/test/java/org/springframework/data/gemfire/function/config/FunctionExecutionClientCacheTests.java b/src/test/java/org/springframework/data/gemfire/function/config/FunctionExecutionClientCacheTests.java index 6d808573..febe4898 100644 --- a/src/test/java/org/springframework/data/gemfire/function/config/FunctionExecutionClientCacheTests.java +++ b/src/test/java/org/springframework/data/gemfire/function/config/FunctionExecutionClientCacheTests.java @@ -18,13 +18,8 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext; -import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.FilterType; import org.springframework.context.annotation.ImportResource; -import org.springframework.data.gemfire.function.config.EnableGemfireFunctionExecutions; -import org.springframework.data.gemfire.function.execution.GemfireFunctionProxyFactoryBean; -import org.springframework.data.gemfire.function.execution.OnRegionFunctionProxyFactoryBean; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; diff --git a/src/test/java/org/springframework/data/gemfire/function/config/XmlConfiguredFunctionExecutionIntegrationTests.java b/src/test/java/org/springframework/data/gemfire/function/config/XmlConfiguredFunctionExecutionIntegrationTests.java new file mode 100644 index 00000000..d335c262 --- /dev/null +++ b/src/test/java/org/springframework/data/gemfire/function/config/XmlConfiguredFunctionExecutionIntegrationTests.java @@ -0,0 +1,59 @@ +/* + * 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.assertEquals; +import static org.junit.Assert.assertSame; + +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.TestUtils; +import org.springframework.data.gemfire.function.config.two.TestOnRegionFunction; +import org.springframework.data.gemfire.function.execution.GemfireOnRegionFunctionTemplate; +import org.springframework.data.gemfire.function.execution.OnRegionFunctionProxyFactoryBean; +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 XmlConfiguredFunctionExecutionIntegrationTests { + @Autowired + ApplicationContext context; + + @Test + public void testProxyFactoryBeanCreated() throws Exception { + OnRegionFunctionProxyFactoryBean factoryBean = (OnRegionFunctionProxyFactoryBean)context.getBean("&testFunction"); + Class serviceInterface = TestUtils.readField("serviceInterface",factoryBean); + assertEquals(serviceInterface, TestOnRegionFunction.class); + + Region r1 = context.getBean("r1",Region.class); + + GemfireOnRegionFunctionTemplate template = TestUtils.readField("gemfireFunctionOperations",factoryBean); + + assertSame(r1, TestUtils.readField("region",template)); + } + +} + + + + + diff --git a/src/test/java/org/springframework/data/gemfire/function/config/three/TestClientOnServerFunction.java b/src/test/java/org/springframework/data/gemfire/function/config/three/TestClientOnServerFunction.java index d9fa0130..50eb7a7e 100644 --- a/src/test/java/org/springframework/data/gemfire/function/config/three/TestClientOnServerFunction.java +++ b/src/test/java/org/springframework/data/gemfire/function/config/three/TestClientOnServerFunction.java @@ -14,7 +14,6 @@ package org.springframework.data.gemfire.function.config.three; import java.util.Set; -import org.springframework.data.gemfire.config.GemfireConstants; import org.springframework.data.gemfire.function.config.Filter; import org.springframework.data.gemfire.function.config.FunctionId; import org.springframework.data.gemfire.function.config.OnServer; diff --git a/src/test/java/org/springframework/data/gemfire/function/config/two/TestOnRegionFunction.java b/src/test/java/org/springframework/data/gemfire/function/config/two/TestOnRegionFunction.java index 14694276..20766a8b 100644 --- a/src/test/java/org/springframework/data/gemfire/function/config/two/TestOnRegionFunction.java +++ b/src/test/java/org/springframework/data/gemfire/function/config/two/TestOnRegionFunction.java @@ -17,7 +17,6 @@ import java.util.Set; import org.springframework.data.gemfire.function.config.Filter; import org.springframework.data.gemfire.function.config.FunctionId; import org.springframework.data.gemfire.function.config.OnRegion; -import org.springframework.data.gemfire.function.config.OnServer; /** * @author David Turanski diff --git a/src/test/resources/org/springframework/data/gemfire/function/config/XmlConfiguredFunctionExecutionIntegrationTests-context.xml b/src/test/resources/org/springframework/data/gemfire/function/config/XmlConfiguredFunctionExecutionIntegrationTests-context.xml new file mode 100644 index 00000000..c53c5c1c --- /dev/null +++ b/src/test/resources/org/springframework/data/gemfire/function/config/XmlConfiguredFunctionExecutionIntegrationTests-context.xml @@ -0,0 +1,13 @@ + + + + + + +