Permalink
Browse files

changed to load caches from *CacheConfig.groovy classes (using an art…

…efact handler) in addition to Config.groovy
  • Loading branch information...
1 parent 0d6d250 commit d04b711249b04b3d1d38ce072de73cc8d18b1572 Burt Beckwith committed Apr 30, 2012
View
@@ -8,8 +8,9 @@
<classpathentry kind="src" path="grails-app/services"/>
<classpathentry kind="src" path="grails-app/taglib"/>
<classpathentry kind="src" path="grails-app/views"/>
- <classpathentry kind="src" path="test/unit"/>
<classpathentry kind="src" path="test/functional"/>
+ <classpathentry kind="src" path="test/integration"/>
+ <classpathentry kind="src" path="test/unit"/>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
<classpathentry exported="true" kind="con" path="GROOVY_DSL_SUPPORT"/>
<classpathentry kind="con" path="com.springsource.sts.grails.core.CLASSPATH_CONTAINER"/>
View
@@ -12,7 +12,8 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-import grails.plugin.cache.ConfigBuilder
+import grails.plugin.cache.CacheConfigArtefactHandler
+import grails.plugin.cache.ConfigLoader
import grails.plugin.cache.GrailsConcurrentMapCacheManager
import grails.plugin.cache.web.ProxyAwareMixedGrailsControllerHelper
import grails.plugin.cache.web.filter.DefaultWebKeyGenerator
@@ -24,6 +25,7 @@ import org.codehaus.groovy.grails.commons.GrailsApplication
import org.slf4j.Logger
import org.slf4j.LoggerFactory
import org.springframework.cache.concurrent.ConcurrentMapCacheFactoryBean
+import org.springframework.context.ApplicationContext
import org.springframework.core.Ordered
import org.springframework.web.filter.DelegatingFilterProxy
@@ -35,6 +37,11 @@ class CacheGrailsPlugin {
def grailsVersion = '2.0 > *'
def observe = ['controllers']
def loadAfter = ['controllers']
+ def artefacts = [CacheConfigArtefactHandler]
+ def watchedResources = [
+ 'file:./grails-app/conf/**/*CacheConfig.groovy',
+ 'file:./plugins/*/grails-app/conf/**/*CacheConfig.groovy'
+ ]
def title = 'Cache Plugin'
def author = 'Jeff Brown'
@@ -50,7 +57,7 @@ class CacheGrailsPlugin {
def pluginExcludes = [
'**/com/demo/**',
- 'grails-app/i18n/**',
+ 'grails-app/conf/TestCacheConfig.groovy',
'grails-app/views/**',
'web-app/**'
]
@@ -109,19 +116,9 @@ class CacheGrailsPlugin {
mode: 'proxy', order: order,
'proxy-target-class': proxyTargetClass)
- // TODO how do extension plugins configure these?
- def configuredCacheNames = ['grailsBlocksCache', 'grailsTemplatesCache']
- if (cacheConfig.config instanceof Closure) {
- ConfigBuilder builder = new ConfigBuilder()
- builder.parse cacheConfig.config
- configuredCacheNames.addAll builder.cacheNames
- }
+ grailsCacheManager(GrailsConcurrentMapCacheManager)
- grailsCacheManager(GrailsConcurrentMapCacheManager) {
- // TODO this locks the manager and doesn't allow new caches;
- // could call getCache() for each name in doWithApplicationContext instead
- cacheNames = configuredCacheNames
- }
+ grailsCacheConfigLoader(ConfigLoader)
webCacheKeyGenerator(DefaultWebKeyGenerator)
@@ -140,6 +137,10 @@ class CacheGrailsPlugin {
}
}
+ def doWithApplicationContext = { ctx ->
+ reloadCaches ctx
+ }
+
def onChange = { event ->
if (!isEnabled(event.application)) {
@@ -157,6 +158,18 @@ class CacheGrailsPlugin {
if (event.application.isServiceClass(event.source)) {
// TODO reload CacheOperation config based on updated annotations
}
+
+ if (event.application.isCacheConfigClass(event.source)) {
+ reloadCaches event.ctx
+ }
+ }
+
+ def onConfigChange = { event ->
+ reloadCaches event.ctx
+ }
+
+ private void reloadCaches(ctx) {
+ ctx.grailsCacheConfigLoader.reload ctx
}
private boolean isEnabled(GrailsApplication application) {
@@ -16,10 +16,6 @@ log4j = {
// for tests
grails.cache.config = {
cache {
- name 'basic'
- eternal false
- overflowToDisk true
- maxElementsInMemory 10000
- maxElementsOnDisk 10000000
+ name 'fromConfigGroovy'
}
}
@@ -0,0 +1,8 @@
+cacheConfig = {
+ cache {
+ name 'grailsBlocksCache'
+ }
+ cache {
+ name 'grailsTemplatesCache'
+ }
+}
@@ -0,0 +1,9 @@
+cacheConfig = {
+ cache {
+ name 'basic'
+ eternal false
+ overflowToDisk true
+ maxElementsInMemory 10000
+ maxElementsOnDisk 10000000
+ }
+}
@@ -47,6 +47,11 @@ class ConfigBuilder extends BuilderSupport {
resolveCaches()
}
+ void parse(o) {
+ // if there's no explicit method, the missing method logic kicks in and fails poorly
+ throw new IllegalArgumentException('parse must be called with a Closure argument')
+ }
+
@Override
protected createNode(name) {
if (_unrecognizedElementDepth) {
@@ -0,0 +1,117 @@
+/* Copyright 2012 SpringSource.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package grails.plugin.cache
+
+import org.codehaus.groovy.grails.commons.GrailsApplication
+import org.springframework.context.ApplicationContext
+
+import grails.plugin.cache.CacheConfigArtefactHandler.CacheConfigGrailsClass
+import grails.util.Environment
+
+/**
+ * @author Burt Beckwith
+ */
+class ConfigLoader {
+
+ static final int DEFAULT_ORDER = 1000
+
+ void reload(ApplicationContext ctx) {
+ def application = ctx.grailsApplication
+ List<ConfigObject> configs = loadOrderedConfigs(application)
+ reload configs, ctx
+ }
+
+ void reload(List<ConfigObject> configs, ApplicationContext ctx) {
+
+ // order doesn't matter in this impl, but in general process in reverse order so
+ // lower order values have higher priority and can override previous settings
+ def configuredCacheNames = [] as LinkedHashSet
+ for (ListIterator<ConfigObject> iter = configs.listIterator(configs.size()); iter.hasPrevious(); ) {
+ ConfigObject co = iter.previous()
+ ConfigBuilder builder = new ConfigBuilder()
+ def config = co.cacheConfig ?: co.config
+ if (config instanceof Closure) {
+ builder.parse config
+ }
+ configuredCacheNames.addAll builder.cacheNames
+ }
+
+ GrailsCacheManager cacheManager = ctx.grailsCacheManager
+
+ for (String name in cacheManager.cacheNames) {
+ if (!configuredCacheNames.contains(name)) {
+ cacheManager.destroyCache name
+ }
+ }
+
+ for (String cacheName in configuredCacheNames) {
+ cacheManager.getCache cacheName
+ }
+ }
+
+ List<ConfigObject> loadOrderedConfigs(GrailsApplication application) {
+ ConfigSlurper slurper = new ConfigSlurper(Environment.current.name)
+
+ List<ConfigObject> configs = []
+ def cacheConfig
+ for (configClass in application.cacheConfigClasses) {
+ def config = slurper.parse(configClass.clazz)
+ cacheConfig = config.cacheConfig
+ if ((cacheConfig instanceof Closure) && processConfig(config, configClass)) {
+ configs << config
+ }
+ }
+
+ cacheConfig = application.config.grails.cache
+ if ((cacheConfig.config instanceof Closure) && processConfig(cacheConfig, null)) {
+ configs << cacheConfig
+ }
+
+ sortConfigs configs
+
+ configs
+ }
+
+ protected boolean processConfig(ConfigObject config, CacheConfigGrailsClass configClass) {
+ def cacheConfig
+ String sourceClassName
+
+ if (configClass == null) {
+ cacheConfig = config.config
+ sourceClassName = 'Config'
+ }
+ else {
+ cacheConfig = config.cacheConfig
+ sourceClassName = configClass.clazz.name
+ }
+
+ if (cacheConfig instanceof Closure) {
+ def order = config.order
+ if (!(order instanceof Number)) {
+ config.order = DEFAULT_ORDER
+ }
+ config._sourceClassName = sourceClassName
+ return true
+ }
+
+ false
+ }
+
+ protected void sortConfigs(List<Closure> configs) {
+ configs.sort { c1, c2 ->
+ c1.order == c2.order ? c1._sourceClassName <=> c2._sourceClassName : c1.order <=> c2.order
+ }
+ }
+}
@@ -0,0 +1,83 @@
+/* Copyright 2012 SpringSource.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package grails.plugin.cache;
+
+import groovy.lang.GroovySystem;
+import groovy.lang.MetaClass;
+
+import org.codehaus.groovy.grails.commons.AbstractInjectableGrailsClass;
+import org.codehaus.groovy.grails.commons.ArtefactHandlerAdapter;
+import org.codehaus.groovy.grails.commons.InjectableGrailsClass;
+import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
+import org.springframework.context.ConfigurableApplicationContext;
+
+/**
+ * Artefact handler for CacheConfig classes.
+ *
+ * @author Burt Beckwith
+ */
+public class CacheConfigArtefactHandler extends ArtefactHandlerAdapter {
+
+ /** The artefact type. */
+ public static final String TYPE = "CacheConfig";
+
+ /**
+ * Default constructor.
+ */
+ public CacheConfigArtefactHandler() {
+ super(TYPE, CacheConfigGrailsClass.class, DefaultCacheConfigGrailsClass.class, TYPE);
+ }
+
+ /**
+ * GrailsClass interface for CacheConfig definitions.
+ */
+ public static interface CacheConfigGrailsClass extends InjectableGrailsClass {
+ // no methods
+ }
+
+ /**
+ * Default implementation of <code>CacheConfigGrailsClass</code>.
+ */
+ public static class DefaultCacheConfigGrailsClass extends AbstractInjectableGrailsClass
+ implements CacheConfigGrailsClass {
+
+ /**
+ * Default constructor.
+ * @param wrappedClass
+ */
+ public DefaultCacheConfigGrailsClass(Class<?> wrappedClass) {
+ super(wrappedClass, CacheConfigArtefactHandler.TYPE);
+ }
+
+ @Override
+ public MetaClass getMetaClass() {
+ // Workaround for http://jira.codehaus.org/browse/GRAILS-4542
+ return GroovySystem.getMetaClassRegistry().getMetaClass(DefaultCacheConfigGrailsClass.class);
+ }
+
+ @Override
+ public Object newInstance() {
+ Object instance = super.newInstance();
+ autowireBeanProperties(instance);
+ return instance;
+ }
+
+ protected void autowireBeanProperties(Object instance) {
+ ConfigurableApplicationContext ctx = (ConfigurableApplicationContext)grailsApplication.getMainContext();
+ ctx.getBeanFactory().autowireBeanProperties(instance,
+ AutowireCapableBeanFactory.AUTOWIRE_BY_NAME, false);
+ }
+ }
+}
@@ -0,0 +1,27 @@
+/* Copyright 2012 SpringSource.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package grails.plugin.cache;
+
+import org.springframework.cache.CacheManager;
+
+/**
+ * @author Burt Beckwith
+ */
+public interface GrailsCacheManager extends CacheManager {
+
+ boolean cacheExists(String name);
+
+ boolean destroyCache(String name);
+}
Oops, something went wrong.

0 comments on commit d04b711

Please sign in to comment.