Skip to content

Commit

Permalink
Performance Optimization - Relates to grails#640
Browse files Browse the repository at this point in the history
  • Loading branch information
graemerocher committed Jun 10, 2015
1 parent 9df41e0 commit 2cbbe81
Show file tree
Hide file tree
Showing 4 changed files with 56 additions and 13 deletions.
10 changes: 10 additions & 0 deletions grails-core/src/main/groovy/grails/core/GrailsControllerClass.java
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,16 @@ public interface GrailsControllerClass extends InjectableGrailsClass {
*/
String getNamespace();

/**
* @return The scope of the controller, defaults to singleton
*/
String getScope();

/**
* @return Whether the scope is singleton
*/
boolean isSingleton();


/**
* Returns the default action for this Controller.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@
import grails.core.GrailsControllerClass;
import grails.web.Action;
import groovy.lang.GroovyObject;
import org.springframework.beans.factory.config.Scope;
import org.springframework.cglib.reflect.FastClass;
import org.springframework.cglib.reflect.FastMethod;
import org.springframework.util.ReflectionUtils;

import java.lang.reflect.Method;
Expand All @@ -38,7 +41,11 @@ public class DefaultGrailsControllerClass extends AbstractInjectableGrailsClass

private static final String DEFAULT_CLOSURE_PROPERTY = "defaultAction";
public static final String ALLOWED_HTTP_METHODS_PROPERTY = "allowedMethods";
private Map<String, Method> actions = new HashMap<String, Method>();
public static final Object[] EMPTY_ARGS = new Object[0];
public static final String SCOPE = "scope";
public static final String SCOPE_SINGLETON = "singleton";
private final String scope;
private Map<String, FastMethod> actions = new HashMap<String, FastMethod>();
private String defaultActionName;
private String namespace;

Expand All @@ -49,6 +56,8 @@ public DefaultGrailsControllerClass(Class<?> clazz) {
if (defaultActionName == null) {
defaultActionName = INDEX_ACTION;
}
final String t = getStaticPropertyValue(SCOPE, String.class);
this.scope = t != null ? t : SCOPE_SINGLETON;
methodStrategy(actions);
}

Expand All @@ -63,23 +72,36 @@ public String getNamespace() {
return namespace;
}

@Override
public String getScope() {
return scope;
}

@Override
public boolean isSingleton() {
return SCOPE_SINGLETON.equalsIgnoreCase(getScope());
}

@Override
public String getDefaultAction() {
return this.defaultActionName;
}

private void methodStrategy(Map<String, Method> methodNames) {
private void methodStrategy(Map<String, FastMethod> methodNames) {

Class superClass = getClazz();
FastClass fastClass = FastClass.create(superClass);
while (superClass != null && superClass != Object.class && superClass != GroovyObject.class) {
for (Method method : superClass.getMethods()) {
if (Modifier.isPublic(method.getModifiers()) && method.getAnnotation(Action.class) != null) {
String methodName = method.getName();

ReflectionUtils.makeAccessible(method);
methodNames.put(methodName, method);
methodNames.put(methodName, fastClass.getMethod(method));
}
}
superClass = superClass.getSuperclass();
fastClass = FastClass.create(superClass);
}

if (!isActionMethod(defaultActionName) && methodNames.size() == 1 && !isReadableProperty("scaffold")) {
Expand Down Expand Up @@ -120,9 +142,9 @@ public boolean mapsToURI(String uri) {
@Override
public Object invoke(Object controller, String action) throws Throwable {
if(action == null) action = this.defaultActionName;
Method handle = actions.get(action);
FastMethod handle = actions.get(action);
if(handle == null) throw new IllegalArgumentException("Invalid action name: " + action);
return ReflectionUtils.invokeMethod(handle, controller);
return handle.invoke(this, EMPTY_ARGS);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -99,17 +99,14 @@ class TagLibraryMetaUtils {
@CompileStatic
protected static boolean doesMethodExist(final MetaClass mc, final String methodName, final Class[] parameterTypes, boolean staticScope=false, boolean onlyReal=false) {
boolean methodExists = false
try {
MetaMethod existingMethod = mc.pickMethod(methodName, parameterTypes)
def existinMethods = mc.respondsTo(methodName, parameterTypes)
for(MetaMethod existingMethod in existinMethods) {
if(existingMethod && existingMethod.isStatic()==staticScope && (!onlyReal || isRealMethod(existingMethod)) && parameterTypes.length==existingMethod.parameterTypes.length) {
methodExists = true
}
} catch (MethodSelectionException mse) {
// the metamethod already exists with multiple signatures, must check if the exact method exists
methodExists = mc.methods.contains { MetaMethod existingMethod ->
existingMethod.name == methodName && existingMethod.isStatic()==staticScope && (!onlyReal || isRealMethod(existingMethod)) && ((!parameterTypes && !existingMethod.parameterTypes) || Arrays.equals(parameterTypes, existingMethod.getNativeParameterTypes()))
break
}
}
return methodExists
}

@CompileStatic
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import org.springframework.web.servlet.ModelAndView

import javax.servlet.http.HttpServletRequest
import javax.servlet.http.HttpServletResponse
import java.util.concurrent.ConcurrentHashMap

/**
* A {@link HandlerAdapter} that takes a matched {@link UrlMappingInfo} and executes the underlying controller producing an appropriate model
Expand All @@ -27,6 +28,7 @@ class UrlMappingsInfoHandlerAdapter implements HandlerAdapter, ApplicationContex
ApplicationContext applicationContext

protected Collection<ActionResultTransformer> actionResultTransformers = Collections.emptyList();
protected Map<String, Object> controllerCache = new ConcurrentHashMap<>()

void setApplicationContext(ApplicationContext applicationContext) {
this.actionResultTransformers = applicationContext.getBeansOfType(ActionResultTransformer.class).values();
Expand Down Expand Up @@ -54,7 +56,19 @@ class UrlMappingsInfoHandlerAdapter implements HandlerAdapter, ApplicationContex
if(info instanceof GrailsControllerUrlMappingInfo) {
GrailsControllerUrlMappingInfo controllerUrlMappingInfo = (GrailsControllerUrlMappingInfo)info
GrailsControllerClass controllerClass = controllerUrlMappingInfo.controllerClass
Object controller = applicationContext ? applicationContext.getBean(controllerClass.fullName) : controllerClass.newInstance()
Object controller

def fullName = controllerClass.fullName
if( controllerClass.isSingleton() ) {
controller = controllerCache.get(fullName)
if(controller == null) {
controller = applicationContext ? applicationContext.getBean(fullName) : controllerClass.newInstance()
controllerCache.put(fullName, controller)
}
}
else {
controller = applicationContext ? applicationContext.getBean(fullName) : controllerClass.newInstance()
}

def action = controllerUrlMappingInfo.actionName ?: controllerClass.defaultAction
if (!webRequest.actionName) {
Expand Down

0 comments on commit 2cbbe81

Please sign in to comment.