Skip to content

Commit fe7ea7f

Browse files
committed
Use reflection in development mode to invoke actions. Fixes #9686
1 parent 9320a4a commit fe7ea7f

File tree

1 file changed

+46
-11
lines changed

1 file changed

+46
-11
lines changed

grails-core/src/main/groovy/org/grails/core/DefaultGrailsControllerClass.java

Lines changed: 46 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
import grails.config.Settings;
1919
import grails.core.GrailsApplication;
2020
import grails.core.GrailsControllerClass;
21+
import grails.util.Environment;
2122
import grails.util.GrailsClassUtils;
2223
import grails.web.Action;
2324
import grails.web.UrlConverter;
@@ -52,7 +53,7 @@ public class DefaultGrailsControllerClass extends AbstractInjectableGrailsClass
5253
public static final String SCOPE = "scope";
5354
public static final String SCOPE_SINGLETON = "singleton";
5455
private String scope;
55-
private Map<String, MethodHandle> actions = new HashMap<String, MethodHandle>();
56+
private Map<String, ActionInvoker> actions = new HashMap<String, ActionInvoker>();
5657
private String defaultActionName;
5758
private String namespace;
5859
protected Map<String, String> actionUriToViewName = new HashMap<String, String>();
@@ -104,23 +105,27 @@ public String getDefaultAction() {
104105
return this.defaultActionName;
105106
}
106107

107-
private void methodStrategy(Map<String, MethodHandle> methodNames) {
108+
private void methodStrategy(Map<String, ActionInvoker> methodNames) {
108109

109110
Class superClass = getClazz();
110111
MethodHandles.Lookup lookup = MethodHandles.lookup();
111112
while (superClass != Object.class && superClass != GroovyObject.class) {
112113
for (Method method : superClass.getMethods()) {
113114
if (Modifier.isPublic(method.getModifiers()) && method.getAnnotation(Action.class) != null) {
114115
String methodName = method.getName();
115-
116-
ReflectionUtils.makeAccessible(method);
117-
MethodHandle mh;
118-
try {
119-
mh = lookup.findVirtual(superClass, methodName, MethodType.methodType(method.getReturnType()));
120-
methodNames.put(methodName, mh);
121-
} catch (NoSuchMethodException | IllegalAccessException e) {
122-
throw new GrailsConfigurationException("Cannot find invokable controller action: " + methodName, e);
116+
if(Environment.isDevelopmentMode()) {
117+
methodNames.put(methodName, new ReflectionInvoker(method));
123118
}
119+
else {
120+
MethodHandle mh;
121+
try {
122+
mh = lookup.findVirtual(superClass, methodName, MethodType.methodType(method.getReturnType()));
123+
methodNames.put(methodName, new MethodHandleInvoker(mh));
124+
} catch (NoSuchMethodException | IllegalAccessException e) {
125+
methodNames.put(methodName, new ReflectionInvoker(method));
126+
}
127+
}
128+
124129
}
125130
}
126131
superClass = superClass.getSuperclass();
@@ -177,7 +182,7 @@ public void registerUrlConverter(UrlConverter urlConverter) {
177182
@Override
178183
public Object invoke(Object controller, String action) throws Throwable {
179184
if(action == null) action = this.defaultActionName;
180-
MethodHandle handle = actions.get(action);
185+
ActionInvoker handle = actions.get(action);
181186
if(handle == null) throw new IllegalArgumentException("Invalid action name: " + action);
182187
return handle.invoke(controller);
183188
}
@@ -187,4 +192,34 @@ public String actionUriToViewName(String actionUri) {
187192

188193
return actionName != null ? actionName : actionUri;
189194
}
195+
196+
private interface ActionInvoker {
197+
Object invoke(Object controller) throws Throwable;
198+
}
199+
200+
private class ReflectionInvoker implements ActionInvoker {
201+
private final Method method;
202+
203+
public ReflectionInvoker(Method method) {
204+
this.method = method;
205+
ReflectionUtils.makeAccessible(method);
206+
}
207+
208+
@Override
209+
public Object invoke(Object controller) throws Throwable {
210+
return method.invoke(controller);
211+
}
212+
}
213+
private class MethodHandleInvoker implements ActionInvoker {
214+
private final MethodHandle handle;
215+
216+
public MethodHandleInvoker(MethodHandle handle) {
217+
this.handle = handle;
218+
}
219+
220+
@Override
221+
public Object invoke(Object controller) throws Throwable{
222+
return handle.invoke(controller);
223+
}
224+
}
190225
}

0 commit comments

Comments
 (0)