Skip to content

Commit

Permalink
Fix and test for GRAILS-9044 FilterConfig.methodMissing gets called f…
Browse files Browse the repository at this point in the history
…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
lhotari committed Apr 23, 2012
1 parent 0f2c77b commit 73b248a
Show file tree
Hide file tree
Showing 4 changed files with 254 additions and 27 deletions.
Original file line number Diff line number Diff line change
@@ -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();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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.
Expand All @@ -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.
Expand All @@ -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"
}

Expand All @@ -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
Expand All @@ -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() {
Expand Down
Original file line number Diff line number Diff line change
@@ -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();
}
}
Loading

0 comments on commit 73b248a

Please sign in to comment.