Skip to content

Commit 08d543b

Browse files
rechtlhotari
authored andcommitted
Also cache negative uri cache lookups.
Serving views or resources which do not have a controller is pretty slow due to uri lookups on every request. This fixes it by also caching uri looks which do not have a controller. To avoid DOS attacks, the cache is also limited in size, and the size can be controlled via the grails.urlmapping.cache.maxsize config property.
1 parent ba77c1b commit 08d543b

File tree

3 files changed

+45
-12
lines changed

3 files changed

+45
-12
lines changed

grails-bootstrap/build.gradle

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,10 @@ dependencies {
2525
}
2626
}
2727

28+
compile( "com.googlecode.concurrentlinkedhashmap:concurrentlinkedhashmap-lru:${concurrentlinkedhashmapVersion}" ) {
29+
exclude group: 'commons-logging', module:'commons-logging'
30+
}
31+
2832
// These 3 are required by UAA but not Grails (non OSGI version of excludes above)
2933
compile 'com.google.protobuf:protobuf-java:2.3.0',
3034
'com.googlecode.json-simple:json-simple:1.1',

grails-core/src/main/groovy/org/codehaus/groovy/grails/commons/ControllerArtefactHandler.java

Lines changed: 41 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -17,26 +17,48 @@
1717

1818
import grails.util.Environment;
1919

20-
import java.util.concurrent.ConcurrentHashMap;
20+
import org.codehaus.groovy.grails.plugins.support.aware.GrailsApplicationAware;
21+
22+
import com.googlecode.concurrentlinkedhashmap.ConcurrentLinkedHashMap;
2123

2224
/**
25+
* Lookup controllers for uris.
26+
*
27+
* <p>This class is responsible for looking up controller classes for uris.</p>
28+
*
29+
* <p>Lookups are cached in non-development mode, and the cache size can be controlled using the grails.urlmapping.cache.maxsize config property.</p>
30+
*
2331
* @author Marc Palmer ([email protected])
2432
*/
25-
public class ControllerArtefactHandler extends ArtefactHandlerAdapter {
33+
public class ControllerArtefactHandler extends ArtefactHandlerAdapter implements GrailsApplicationAware {
2634

35+
private static final String URL_MAPPING_CACHE_MAX_SIZE = "grails.urlmapping.cache.maxsize";
36+
private static final GrailsClass NO_CONTROLLER = new AbstractGrailsClass(Object.class, "Controller") {};
37+
2738
public static final String TYPE = "Controller";
2839
public static final String PLUGIN_NAME = "controllers";
29-
private ConcurrentHashMap<String, GrailsClass> uriToControllerClassCache;
40+
private ConcurrentLinkedHashMap<String, GrailsClass> uriToControllerClassCache;
3041
private ArtefactInfo artefactInfo;
3142

43+
private GrailsApplication grailsApplication;
44+
3245
public ControllerArtefactHandler() {
3346
super(TYPE, GrailsControllerClass.class, DefaultGrailsControllerClass.class,
3447
DefaultGrailsControllerClass.CONTROLLER, false);
3548
}
3649

3750
@Override
3851
public void initialize(ArtefactInfo artefacts) {
39-
uriToControllerClassCache = new ConcurrentHashMap<String, GrailsClass>();
52+
Object cacheSize = grailsApplication.getFlatConfig().get(URL_MAPPING_CACHE_MAX_SIZE);
53+
if (cacheSize == null) {
54+
cacheSize = 10000;
55+
}
56+
57+
uriToControllerClassCache = new ConcurrentLinkedHashMap.Builder<String, GrailsClass>()
58+
.initialCapacity(500)
59+
.maximumWeightedCapacity(new Integer(cacheSize.toString()))
60+
.build();
61+
4062
artefactInfo = artefacts;
4163
}
4264

@@ -63,13 +85,23 @@ public GrailsClass getArtefactForFeature(Object feature) {
6385
break;
6486
}
6587
}
66-
if (controllerClass != null) {
67-
// don't cache for dev environment
68-
if (Environment.getCurrent() != Environment.DEVELOPMENT) {
69-
uriToControllerClassCache.putIfAbsent(uri, controllerClass);
70-
}
88+
if (controllerClass == null) {
89+
controllerClass = NO_CONTROLLER;
7190
}
91+
92+
// don't cache for dev environment
93+
if (Environment.getCurrent() != Environment.DEVELOPMENT) {
94+
uriToControllerClassCache.put(uri, controllerClass);
95+
}
96+
}
97+
98+
if (controllerClass == NO_CONTROLLER) {
99+
controllerClass = null;
72100
}
73101
return controllerClass;
74102
}
103+
104+
public void setGrailsApplication(GrailsApplication grailsApplication) {
105+
this.grailsApplication = grailsApplication;
106+
}
75107
}

grails-web/build.gradle

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,6 @@ dependencies {
1818
compile "commons-io:commons-io:${commonsIOVersion}"
1919
compile "commons-lang:commons-lang:$commonsLangVersion"
2020

21-
compile( "com.googlecode.concurrentlinkedhashmap:concurrentlinkedhashmap-lru:${concurrentlinkedhashmapVersion}" ) {
22-
exclude group: 'commons-logging', module:'commons-logging'
23-
}
2421
compile 'opensymphony:sitemesh:2.4'
2522

2623
compile "org.springframework:spring-webmvc:${springVersion}"

0 commit comments

Comments
 (0)