Permalink
Browse files

Fix and test for GRAILS-9044 FilterConfig.methodMissing gets called f…

…or every invocation when calling a method with several arguments

Fix and test for GRAILS-9050 Grails Filters's helper methods are shared between all Filters classes
  • Loading branch information...
1 parent 0f2c77b commit 73b248a0ecb7ee0e9b00a9273ad4952bbb55fd8f @lhotari lhotari committed Apr 23, 2012
@@ -0,0 +1,133 @@
+package org.codehaus.groovy.grails.plugins.web.filters;
+
+import groovy.lang.MetaMethod;
+
+import org.codehaus.groovy.reflection.CachedClass;
+
+/**
+ * MetaMethod implementation that delegates to real MetaMethod implementation
+ *
+ * This can be used to efficiently proxy a metamethod from another metaClass in methodMissing.
+ * An example can be found in FilterConfig's methodMissing .
+ *
+ * Without this class it's hard to implement efficient methodMissing "caching" supporting methods with multiple signatures (same method name, different set of arguments).
+ *
+ * This class could be moved to org.codehaus.groovy.grails.commons.metaclass for reuse.
+ *
+ * @author Lari Hotari
+ *
+ */
+@SuppressWarnings("rawtypes")
+class DelegateMetaMethod extends MetaMethod {
+ static interface DelegateMetaMethodTargetStrategy {
+ public Object getTargetInstance(Object instance);
+ }
+
+ private MetaMethod delegateMethod;
+ private DelegateMetaMethodTargetStrategy targetStrategy;
+
+ public DelegateMetaMethod(MetaMethod delegateMethod, DelegateMetaMethodTargetStrategy targetStrategy) {
+ this.delegateMethod=delegateMethod;
+ this.targetStrategy=targetStrategy;
+ }
+
+ public int getModifiers() {
+ return delegateMethod.getModifiers();
+ }
+
+ public String getName() {
+ return delegateMethod.getName();
+ }
+
+ public Class getReturnType() {
+ return delegateMethod.getReturnType();
+ }
+
+ public Object invoke(Object object, Object[] arguments) {
+ return delegateMethod.invoke(targetStrategy.getTargetInstance(object), arguments);
+ }
+
+ public void checkParameters(Class[] arguments) {
+ delegateMethod.checkParameters(arguments);
+ }
+
+ public CachedClass[] getParameterTypes() {
+ return delegateMethod.getParameterTypes();
+ }
+
+ public boolean isMethod(MetaMethod method) {
+ return delegateMethod.isMethod(method);
+ }
+
+ public Class[] getNativeParameterTypes() {
+ return delegateMethod.getNativeParameterTypes();
+ }
+
+ public boolean isVargsMethod(Object[] arguments) {
+ return delegateMethod.isVargsMethod(arguments);
+ }
+
+ public String toString() {
+ return delegateMethod.toString();
+ }
+
+ public boolean equals(Object obj) {
+ return delegateMethod.equals(obj);
+ }
+
+ public Object clone() {
+ return delegateMethod.clone();
+ }
+
+ public boolean isStatic() {
+ return delegateMethod.isStatic();
+ }
+
+ public boolean isAbstract() {
+ return delegateMethod.isAbstract();
+ }
+
+ public Object[] correctArguments(Object[] argumentArray) {
+ return delegateMethod.correctArguments(argumentArray);
+ }
+
+ public boolean isCacheable() {
+ return delegateMethod.isCacheable();
+ }
+
+ public String getDescriptor() {
+ return delegateMethod.getDescriptor();
+ }
+
+ public String getSignature() {
+ return delegateMethod.getSignature();
+ }
+
+ public String getMopName() {
+ return delegateMethod.getMopName();
+ }
+
+ public Object doMethodInvoke(Object object, Object[] argumentArray) {
+ return delegateMethod.doMethodInvoke(targetStrategy.getTargetInstance(object), argumentArray);
+ }
+
+ public boolean isValidMethod(Class[] arguments) {
+ return delegateMethod.isValidMethod(arguments);
+ }
+
+ public boolean isValidExactMethod(Object[] args) {
+ return delegateMethod.isValidExactMethod(args);
+ }
+
+ public boolean isValidExactMethod(Class[] args) {
+ return delegateMethod.isValidExactMethod(args);
+ }
+
+ public boolean isValidMethod(Object[] arguments) {
+ return delegateMethod.isValidMethod(arguments);
+ }
+
+ public CachedClass getDeclaringClass() {
+ return delegateMethod.getDeclaringClass();
+ }
+}
@@ -34,7 +34,7 @@ import org.codehaus.groovy.grails.web.servlet.mvc.GrailsWebRequest
* @author mike
* @author Graeme Rocher
*/
-class FilterConfig extends ControllersApi{
+class FilterConfig extends ControllersApi {
String name
Map scope
Closure before
@@ -43,7 +43,18 @@ class FilterConfig extends ControllersApi{
// this modelAndView overrides ControllersApi's modelAndView
ModelAndView modelAndView
boolean initialised = false
-
+
+ public FilterConfig() {
+ initializeMetaClass()
+ }
+
+ void initializeMetaClass() {
+ // use per-instance metaclass
+ ExpandoMetaClass emc = new ExpandoMetaClass(getClass(), false, true)
+ emc.initialize()
+ setMetaClass(emc)
+ }
+
/**
* Redirects attempt to access an 'errors' property, so we provide
* one here with a null value.
@@ -56,7 +67,7 @@ class FilterConfig extends ControllersApi{
* delegate any missing properties or methods to it.
*/
def filtersDefinition
-
+
/**
* When the filter does not have a particular property, it passes
* the request on to the filter definition class.
@@ -65,7 +76,7 @@ class FilterConfig extends ControllersApi{
// Delegate to the parent definition if it has this property.
if (filtersDefinition.metaClass.hasProperty(filtersDefinition, propertyName)) {
def getterName = GrailsClassUtils.getGetterName(propertyName)
- metaClass."$getterName" = {-> delegate.filtersDefinition."$propertyName" }
+ metaClass."$getterName" = {-> delegate.filtersDefinition.getProperty(propertyName) }
return filtersDefinition."$propertyName"
}
@@ -78,18 +89,15 @@ class FilterConfig extends ControllersApi{
*/
def methodMissing(String methodName, args) {
// Delegate to the parent definition if it has this method.
- if (filtersDefinition.metaClass.respondsTo(filtersDefinition, methodName)) {
- if (!args) {
- // No argument method.
- metaClass."$methodName" = {-> filtersDefinition."$methodName"() }
- }
- else {
- metaClass."$methodName" = { varArgs -> filtersDefinition."$methodName"(varArgs) }
- }
-
- // We've created the forwarding method now, but we still
- // need to invoke the target method this time around.
- return filtersDefinition."$methodName"(*args)
+ List<MetaMethod> respondsTo = filtersDefinition.metaClass.respondsTo(filtersDefinition, methodName, args)
+ if (respondsTo) {
+ // Use DelegateMetaMethod to proxy calls to actual MetaMethod for subsequent calls to this method
+ DelegateMetaMethod dmm=new DelegateMetaMethod(respondsTo[0], FilterConfigDelegateMetaMethodTargetStrategy.instance)
+ // register the metamethod to EMC
+ metaClass.registerInstanceMethod(dmm)
+
+ // for this invocation we still have to make the call
+ return respondsTo[0].invoke(filtersDefinition, args)
}
// Ideally, we would throw a MissingMethodException here
@@ -104,7 +112,7 @@ class FilterConfig extends ControllersApi{
// The required method was not found on the parent filter definition either.
throw new MissingMethodException(methodName, filtersDefinition.getClass(), args)
}
-
+
String toString() {"FilterConfig[$name, scope=$scope]"}
String getActionUri() {
@@ -0,0 +1,11 @@
+package org.codehaus.groovy.grails.plugins.web.filters;
+
+import org.codehaus.groovy.grails.plugins.web.filters.DelegateMetaMethod.DelegateMetaMethodTargetStrategy;
+
+class FilterConfigDelegateMetaMethodTargetStrategy implements DelegateMetaMethodTargetStrategy{
+ public static final FilterConfigDelegateMetaMethodTargetStrategy instance=new FilterConfigDelegateMetaMethodTargetStrategy();
+
+ public Object getTargetInstance(Object instance) {
+ return ((FilterConfig)instance).getFiltersDefinition();
+ }
+}
Oops, something went wrong.

0 comments on commit 73b248a

Please sign in to comment.