From ce1198b01d7b62f4b066d1eff1c9bcf0cc6c4961 Mon Sep 17 00:00:00 2001 From: Burt Beckwith Date: Thu, 4 Apr 2013 02:33:23 -0400 Subject: [PATCH 1/3] whitespace and general cleanup --- ResourcesGrailsPlugin.groovy | 145 ++--- grails-app/conf/BuildConfig.groovy | 52 +- grails-app/conf/ResourcesBootStrap.groovy | 12 +- .../resource/BaseUrlResourceMapper.groovy | 48 +- .../resource/BundleResourceMapper.groovy | 12 +- .../CSSPreprocessorResourceMapper.groovy | 21 +- .../resource/CSSRewriterResourceMapper.groovy | 16 +- .../resource/test/TestResourceMapper.groovy | 6 +- .../plugin/resource/ResourceTagLib.groovy | 278 ++++---- grails-app/views/index.gsp | 4 +- .../resource/AggregatedResourceMeta.groovy | 52 +- .../resource/CSSBundleResourceMeta.groovy | 12 +- .../plugin/resource/CSSLinkProcessor.groovy | 59 +- .../resource/DevModeSanityFilter.groovy | 61 +- .../JavaScriptBundleResourceMeta.groovy | 9 +- .../plugin/resource/ProcessingFilter.groovy | 22 +- .../plugin/resource/ResourceMeta.groovy | 348 +++++----- .../plugin/resource/ResourceModule.groovy | 86 ++- .../plugin/resource/ResourceProcessor.groovy | 613 +++++++++--------- .../resource/ResourceProcessorBatch.groovy | 7 +- .../grails/plugin/resource/URLUtils.groovy | 18 +- .../plugin/resource/mapper/MapperPhase.groovy | 23 +- .../resource/mapper/ResourceMapper.groovy | 131 ++-- .../mapper/ResourceMappersFactory.groovy | 58 +- .../resource/module/ModuleBuilder.groovy | 40 +- .../module/ModuleDeclarationsFactory.groovy | 75 ++- .../resource/module/ModulesBuilder.groovy | 110 ++-- .../util/HalfBakedLegacyLinkGenerator.groovy | 22 +- .../resource/util/ResourceMetaStore.groovy | 70 +- .../AbstractResourcesArtefactHandler.java | 8 +- .../artefacts/DefaultResourceMapperClass.java | 5 +- .../artefacts/DefaultResourcesClass.java | 6 +- .../ResourceMapperArtefactHandler.java | 11 +- .../artefacts/ResourcesArtefactHandler.java | 11 +- .../ResourceProcessorIntegTests.groovy | 12 +- .../resource/ResourceTagLibIntegTests.groovy | 72 +- .../resource/module/ModulesIntegTests.groovy | 53 +- .../AbstractResourcePluginTests.groovy | 17 + .../AggregatedResourceMetaTests.groovy | 39 +- .../resource/BaseUrlResourceMapperSpec.groovy | 7 +- .../resource/BundleResourceMapperTests.groovy | 18 +- .../resource/CSSLinkProcessorTests.groovy | 33 +- .../CSSPreprocessorResourceMapperTests.groovy | 65 +- .../CSSRewriterResourceMapperTests.groovy | 148 ++--- .../plugin/resource/PathMatcherTests.groovy | 3 +- .../resource/ProcessingFilterTests.groovy | 9 +- .../resource/ResourceMapperTests.groovy | 131 ++-- .../resource/ResourceMetaStoreTests.groovy | 19 +- .../plugin/resource/ResourceMetaTests.groovy | 22 +- .../resource/ResourceModuleTests.groovy | 52 +- .../ResourceModulesBuilderTests.groovy | 45 +- .../resource/ResourceProcessorTests.groovy | 144 ++-- .../resource/ResourceTagLibTests.groovy | 132 ++-- .../plugin/resource/URLUtilsTests.groovy | 4 +- 54 files changed, 1613 insertions(+), 1863 deletions(-) create mode 100644 test/unit/org/grails/plugin/resource/AbstractResourcePluginTests.groovy diff --git a/ResourcesGrailsPlugin.groovy b/ResourcesGrailsPlugin.groovy index c7e7d6a..f3ac5b2 100644 --- a/ResourcesGrailsPlugin.groovy +++ b/ResourcesGrailsPlugin.groovy @@ -1,16 +1,12 @@ import grails.util.Environment -import org.codehaus.groovy.grails.commons.ConfigurationHolder - -import org.springframework.beans.factory.config.MethodInvokingFactoryBean -import org.springframework.core.io.FileSystemResource - -import org.grails.plugin.resource.util.HalfBakedLegacyLinkGenerator - +import java.util.concurrent.ScheduledFuture import java.util.concurrent.ScheduledThreadPoolExecutor import java.util.concurrent.TimeUnit -import java.util.concurrent.ScheduledFuture +import org.grails.plugin.resource.ResourceProcessor +import org.grails.plugin.resource.util.HalfBakedLegacyLinkGenerator +import org.springframework.core.io.FileSystemResource import org.springframework.util.AntPathMatcher /** @@ -19,8 +15,20 @@ import org.springframework.util.AntPathMatcher */ class ResourcesGrailsPlugin { - static DEFAULT_URI_PREFIX = 'static' - static DEFAULT_ADHOC_PATTERNS = ["/images/*", "*.css", "*.js"].asImmutable() + static final String DEFAULT_URI_PREFIX = 'static' + static final List DEFAULT_ADHOC_PATTERNS = ["/images/*", "*.css", "*.js"].asImmutable() + static final int RELOAD_THROTTLE_DELAY = 500 + + private ScheduledThreadPoolExecutor delayedChangeThrottle = new ScheduledThreadPoolExecutor(1) + private ScheduledFuture reloadTask + + private static final AntPathMatcher PATH_MATCHER = new AntPathMatcher() + private static final List RELOADABLE_RESOURCE_EXCLUDES = [ + '**/.svn/**/*.*', + '**/.git/**/*.*', + 'WEB-INF/**/*.*', + 'META-INF/**/*.*' + ] def version = "1.2" def grailsVersion = "1.3 > *" @@ -47,71 +55,70 @@ class ResourcesGrailsPlugin { "file:./web-app/**/*.*" // Watch for resource changes, we need excludes here for WEB-INF+META-INF when grails impls this ] - def author = "Marc Palmer, Luke Daley" - def authorEmail = "marc@grailsrocks.com, ld@ldaley.com" def title = "Resources" def description = 'HTML resource management enhancements to replace g.resource etc.' def documentation = "http://grails-plugins.github.com/grails-resources" def license = "APACHE" - def organization = [ name: "Grails Community", url: "http://grails.org/" ] + def organization = [name: "Grails Community", url: "http://grails.org/"] def developers = [ - [ name: "Marc Palmer", email: "marc@grailsrocks.com" ], - [ name: "Luke Daley", email: "ld@ldaley.com" ] + [name: "Marc Palmer", email: "marc@grailsrocks.com"], + [name: "Luke Daley", email: "ld@ldaley.com"] ] - def issueManagement = [ system: "JIRA", url: "http://jira.grails.org/browse/GPRESOURCES" ] - def scm = [ url: "https://github.com/grails-plugins/grails-resources" ] - + def issueManagement = [system: "JIRA", url: "http://jira.grails.org/browse/GPRESOURCES"] + def scm = [url: "https://github.com/grails-plugins/grails-resources"] + def getWebXmlFilterOrder() { def FilterManager = getClass().getClassLoader().loadClass('grails.plugin.webxml.FilterManager') - [ DeclaredResourcesPluginFilter: FilterManager.DEFAULT_POSITION - 300, - AdHocResourcesPluginFilter: FilterManager.DEFAULT_POSITION - 250, - ResourcesDevModeFilter: FilterManager.RELOAD_POSITION + 100] + [DeclaredResourcesPluginFilter: FilterManager.DEFAULT_POSITION - 300, + AdHocResourcesPluginFilter: FilterManager.DEFAULT_POSITION - 250, + ResourcesDevModeFilter: FilterManager.RELOAD_POSITION + 100] } - def getResourcesConfig(application) { + private getResourcesConfig(application) { application.config.grails.resources } - - def getUriPrefix(application) { + + private getUriPrefix(application) { def prf = getResourcesConfig(application).uri.prefix prf instanceof String ? prf : DEFAULT_URI_PREFIX } - - def getAdHocPatterns(application) { + + private getAdHocPatterns(application) { def patterns = getResourcesConfig(application).adhoc.patterns patterns instanceof List ? patterns : DEFAULT_ADHOC_PATTERNS } - + def doWithSpring = { -> if (!springConfig.containsBean('grailsLinkGenerator')) { grailsLinkGenerator(HalfBakedLegacyLinkGenerator) { pluginManager = ref('pluginManager') } } - - grailsResourceProcessor(org.grails.plugin.resource.ResourceProcessor) { + + grailsResourceProcessor(ResourceProcessor) { grailsLinkGenerator = ref('grailsLinkGenerator') if (springConfig.containsBean('grailsResourceLocator')) { grailsResourceLocator = ref('grailsResourceLocator') } grailsApplication = ref('grailsApplication') } - + // Legacy service name springConfig.addAlias "resourceService", "grailsResourceProcessor" } - + def doWithWebDescriptor = { webXml -> def adHocPatterns = getAdHocPatterns(application) - def declaredResFilter = [ - name:'DeclaredResourcesPluginFilter', - filterClass:"org.grails.plugin.resource.ProcessingFilter", - urlPatterns:["/${getUriPrefix(application)}/*"] + def declaredResFilter = [ + name:'DeclaredResourcesPluginFilter', + filterClass:"org.grails.plugin.resource.ProcessingFilter", + urlPatterns:["/${getUriPrefix(application)}/*"] ] - def adHocFilter = [ - name:'AdHocResourcesPluginFilter', + + def adHocFilter = [ + name:'AdHocResourcesPluginFilter', filterClass:"org.grails.plugin.resource.ProcessingFilter", params: [adhoc:true], urlPatterns: adHocPatterns @@ -123,19 +130,19 @@ class ResourcesGrailsPlugin { } if ( Environment.current == Environment.DEVELOPMENT) { - filtersToAdd << [ - name:'ResourcesDevModeFilter', + filtersToAdd << [ + name:'ResourcesDevModeFilter', filterClass:"org.grails.plugin.resource.DevModeSanityFilter", urlPatterns:['/*'], dispatchers:['REQUEST'] ] } - + log.info("Adding servlet filters") def filters = webXml.filter[0] filters + { filtersToAdd.each { f -> - log.info "Adding filter: ${f.name} with class ${f.filterClass} and init-params: ${f.params}" + log.info "Adding filter: $f.name with class $f.filterClass and init-params: $f.params" 'filter' { 'filter-name'(f.name) 'filter-class'(f.filterClass) @@ -148,11 +155,11 @@ class ResourcesGrailsPlugin { } } } - def mappings = webXml.'filter-mapping'[0] + def mappings = webXml.'filter-mapping'[0] mappings + { filtersToAdd.each { f -> f.urlPatterns?.each { p -> - log.info "Adding url pattern ${p} for filter ${f.name}" + log.info "Adding url pattern $p for filter $f.name" 'filter-mapping' { 'filter-name'(f.name) 'url-pattern'(p) @@ -172,28 +179,16 @@ class ResourcesGrailsPlugin { applicationContext.grailsResourceProcessor.reloadAll() } - static PATH_MATCHER = new AntPathMatcher() - static RELOADABLE_RESOURCE_EXCLUDES = [ - '**/.svn/**/*.*', - '**/.git/**/*.*', - 'WEB-INF/**/*.*', - 'META-INF/**/*.*' - ] - boolean isResourceWeShouldProcess(File file) { // Make windows filenams safe for matching def fileName = file.canonicalPath.replaceAll('\\\\', '/').replaceAll('^.*?/web-app/', '') boolean shouldProcess = !(RELOADABLE_RESOURCE_EXCLUDES.any { PATH_MATCHER.match(it, fileName ) }) return shouldProcess } - - ScheduledThreadPoolExecutor delayedChangeThrottle = new ScheduledThreadPoolExecutor(1) - ScheduledFuture reloadTask - static final RELOAD_THROTTLE_DELAY = 500 - + void triggerReload(Closure reloader) { reloadTask?.cancel(false) - reloadTask = delayedChangeThrottle.schedule( { + reloadTask = delayedChangeThrottle.schedule( { try { reloader() } catch (Throwable t) { @@ -202,7 +197,7 @@ class ResourcesGrailsPlugin { } }, RELOAD_THROTTLE_DELAY, TimeUnit.MILLISECONDS) } - + def onChange = { event -> if (event.source instanceof FileSystemResource) { if (isResourceWeShouldProcess(event.source.file)) { @@ -224,25 +219,25 @@ class ResourcesGrailsPlugin { } } - protected handleChange(application, event, type, log) { - if (application.isArtefactOfType(type, event.source)) { - log.debug("reloading $event.source.name ($type)") - def oldClass = application.getArtefact(type, event.source.name) - application.addArtefact(type, event.source) - // Reload subclasses - if (oldClass) { - application.getArtefacts(type).each { - if (it.clazz != event.source && oldClass.clazz.isAssignableFrom(it.clazz)) { - def newClass = application.classLoader.reloadClass(it.clazz.name) - application.addArtefact(type, newClass) - } + protected boolean handleChange(application, event, type, log) { + if (!application.isArtefactOfType(type, event.source)) { + return false + } + + log.debug("reloading $event.source.name ($type)") + def oldClass = application.getArtefact(type, event.source.name) + application.addArtefact(type, event.source) + // Reload subclasses + if (oldClass) { + application.getArtefacts(type).each { + if (it.clazz != event.source && oldClass.clazz.isAssignableFrom(it.clazz)) { + def newClass = application.classLoader.reloadClass(it.clazz.name) + application.addArtefact(type, newClass) } } - - true - } else { - false } + + true } def onConfigChange = { event -> @@ -250,7 +245,7 @@ class ResourcesGrailsPlugin { } /** - * We have to soft load this class so this file can be compiled on it's own. + * We have to soft load this class so this file can be compiled on its own. */ static getResourceMapperArtefactHandler() { softLoadClass('org.grails.plugin.resources.artefacts.ResourceMapperArtefactHandler') diff --git a/grails-app/conf/BuildConfig.groovy b/grails-app/conf/BuildConfig.groovy index 2d79a16..27a8102 100644 --- a/grails-app/conf/BuildConfig.groovy +++ b/grails-app/conf/BuildConfig.groovy @@ -1,40 +1,18 @@ -grails.project.class.dir = "target/classes" -grails.project.test.class.dir = "target/test-classes" -grails.project.test.reports.dir = "target/test-reports" -//grails.project.war.file = "target/${appName}-${appVersion}.war" +grails.project.work.dir = 'target' + grails.project.dependency.resolution = { - // inherit Grails' default dependencies - inherits( "global" ) { - // uncomment to disable ehcache - // excludes 'ehcache' - } - log "warn" // log level of Ivy resolver, either 'error', 'warn', 'info', 'debug' or 'verbose' - repositories { - grailsCentral() - grailsRepo "http://grails.org/plugins" - // uncomment the below to enable remote dependency resolution - // from public Maven repositories - //mavenLocal() - //mavenCentral() - //mavenRepo "http://snapshots.repository.codehaus.org" - //mavenRepo "http://repository.codehaus.org" - //mavenRepo "http://download.java.net/maven/2/" - //mavenRepo "http://repository.jboss.com/maven2/" - } - dependencies { -// build 'org.codehaus.gpars:gpars:0.12' - } - plugins { - provided(":webxml:1.4.1") - build(":tomcat:$grailsVersion") { - export = false - } - build(":release:2.1.0") { - export = false - } - test(":spock:0.6"){ - export = false - } - } + inherits 'global' + log 'warn' + + repositories { + grailsCentral() + } + + plugins { + provided(":webxml:1.4.1") + test(":spock:0.6"){ + export = false + } + } } diff --git a/grails-app/conf/ResourcesBootStrap.groovy b/grails-app/conf/ResourcesBootStrap.groovy index e2c3dcf..393feba 100644 --- a/grails-app/conf/ResourcesBootStrap.groovy +++ b/grails-app/conf/ResourcesBootStrap.groovy @@ -1,7 +1,5 @@ import grails.util.Environment -import org.grails.plugin.resource.* - /** * Bootstraps the plugin by loading the app resources from config * @@ -9,17 +7,13 @@ import org.grails.plugin.resource.* * @author Luke Daley (ld@ldaley.com) */ class ResourcesBootStrap { - + def grailsResourceProcessor - + def init = { servletContext -> /*grailsResourceProcessor.reload()*/ if (Environment.current == Environment.DEVELOPMENT) { grailsResourceProcessor.dumpResources() } } - - def destroy = { - - } -} \ No newline at end of file +} diff --git a/grails-app/resourceMappers/org/grails/plugin/resource/BaseUrlResourceMapper.groovy b/grails-app/resourceMappers/org/grails/plugin/resource/BaseUrlResourceMapper.groovy index af97f85..9ee0c3a 100644 --- a/grails-app/resourceMappers/org/grails/plugin/resource/BaseUrlResourceMapper.groovy +++ b/grails-app/resourceMappers/org/grails/plugin/resource/BaseUrlResourceMapper.groovy @@ -3,49 +3,51 @@ package org.grails.plugin.resource import org.grails.plugin.resource.mapper.MapperPhase /** - * Mapper that applies an optional base url to resources, e.g. for putting content out to + * Mapper that applies an optional base url to resources, e.g. for putting content out to * one or more pull CDNs * @author Tomas Lin * @since 1.2 */ class BaseUrlResourceMapper { - static priority = 0 + static int priority = 0 - static phase = MapperPhase.ABSOLUTISATION + static MapperPhase phase = MapperPhase.ABSOLUTISATION def map(resource, config) { - if (config.enabled) { - def url + if (!config.enabled) { + return + } - if (isResourceBundle(resource)) { - verifySameBaseUrlForAllModulesInBundle(resource, config) - } + String url - String moduleName = getModuleName(resource) - if (moduleName && config.modules[moduleName]) { - url = config.modules[moduleName] - } + if (isResourceBundle(resource)) { + verifySameBaseUrlForAllModulesInBundle(resource, config) + } - if (!url) { - url = config.default - } + String moduleName = getModuleName(resource) + if (moduleName && config.modules[moduleName]) { + url = config.modules[moduleName] + } + + if (!url) { + url = config.default + } - if (url) { - if (url.endsWith('/')) { - url = url[0..-2] - } - resource.linkOverride = url + resource.linkUrl + if (url) { + if (url.endsWith('/')) { + url = url[0..-2] } + resource.linkOverride = url + resource.linkUrl } } void verifySameBaseUrlForAllModulesInBundle(AggregatedResourceMeta bundle, Map config) { def moduleNames = bundle.resources.collect this.&getModuleName def baseUrlsForBundleModules = moduleNames.collectEntries { [it, config.modules[it] ?: config.default] } - def uniqueUrls = baseUrlsForBundleModules.values().unique(false) + List uniqueUrls = baseUrlsForBundleModules.values().unique(false) if (uniqueUrls.size() != 1) { - def bundleName = bundle.resources.first().bundle + String bundleName = bundle.resources.first().bundle throw new IllegalArgumentException("All modules bundled together must have the same baseUrl override. " + "Offending bundle is [$bundleName] and baseUrl overrides for its' modules are: $baseUrlsForBundleModules") } @@ -61,4 +63,4 @@ class BaseUrlResourceMapper { private boolean isResourceBundle(resource) { resource instanceof AggregatedResourceMeta && resource.resources } -} \ No newline at end of file +} diff --git a/grails-app/resourceMappers/org/grails/plugin/resource/BundleResourceMapper.groovy b/grails-app/resourceMappers/org/grails/plugin/resource/BundleResourceMapper.groovy index 6f9fd36..1ee21dc 100644 --- a/grails-app/resourceMappers/org/grails/plugin/resource/BundleResourceMapper.groovy +++ b/grails-app/resourceMappers/org/grails/plugin/resource/BundleResourceMapper.groovy @@ -6,25 +6,25 @@ import org.grails.plugin.resource.mapper.MapperPhase * This mapper creates synthetic AggregatedResourceMeta instances for any bundle * names found in the resource declarations, and gathers up info about those resources * so that when the bundle itself is requested, the aggregated file is created and returned. - * + * * This sets any ResourceMeta to which this mapper applies, to be "delegating" to the new aggregated resource * so when those resources are rendered/requested, the bundle URI is written out. * * @author Marc Palmer (marc@grailsrocks.com) */ class BundleResourceMapper { - + def phase = MapperPhase.AGGREGATION - + def grailsResourceProcessor - + static MIMETYPE_TO_RESOURCE_META_CLASS = [ 'text/css': CSSBundleResourceMeta, 'text/javascript': JavaScriptBundleResourceMeta, 'application/javascript': JavaScriptBundleResourceMeta, 'application/x-javascript': JavaScriptBundleResourceMeta ] - + /** * Find resources that belong in bundles, and create the bundles, and make the resource delegate to the bundle. * Creates a new aggregated resource for the bundle and shoves all the resourceMetas into it. @@ -37,7 +37,7 @@ class BundleResourceMapper { if (bundleId) { def resType = MIMETYPE_TO_RESOURCE_META_CLASS[resource.contentType] if (!resType) { - log.warn "Cannot create a bundle from resource [${resource.sourceUrl}], "+ + log.warn "Cannot create a bundle from resource [${resource.sourceUrl}], " + "the content type [${resource.contentType}] is not supported. Set the resource to exclude bundle mapper." return } diff --git a/grails-app/resourceMappers/org/grails/plugin/resource/CSSPreprocessorResourceMapper.groovy b/grails-app/resourceMappers/org/grails/plugin/resource/CSSPreprocessorResourceMapper.groovy index 41d1d0a..2bc9faf 100644 --- a/grails-app/resourceMappers/org/grails/plugin/resource/CSSPreprocessorResourceMapper.groovy +++ b/grails-app/resourceMappers/org/grails/plugin/resource/CSSPreprocessorResourceMapper.groovy @@ -5,7 +5,7 @@ import org.grails.plugin.resource.mapper.MapperPhase /** * This mapper is the first phase of CSS rewriting. * - * It will find any relative URIs in the CSS and convert them to a "resource:" + * It will find any relative URIs in the CSS and convert them to a "resource:" * so that later after mappers have been applied, the URIs can be fixed up and restored to URIs relative to the * new CSS output file's location. For example a bundle or "hashandcache" mapper may move the CSS file to a completely * different place, thus breaking all the relative links to images. @@ -22,7 +22,7 @@ class CSSPreprocessorResourceMapper { static defaultIncludes = ['**/*.css'] def grailsResourceProcessor - + /** * Find all url() and fix up the url if it is not absolute * NOTE: This needs to run after any plugins that move resources around, but before any that obliterate @@ -31,24 +31,24 @@ class CSSPreprocessorResourceMapper { def map(resource, config) { if (resource instanceof AggregatedResourceMeta) { if (log.debugEnabled) { - log.debug "CSS Preprocessor skipping ${resource} because it is aggregated (already processed each file in it)" + log.debug "CSS Preprocessor skipping $resource because it is aggregated (already processed each file in it)" } return null } - + def processor = new CSSLinkProcessor() - + if (log.debugEnabled) { log.debug "CSS Preprocessor munging ${resource}" } processor.process(resource, grailsResourceProcessor) { prefix, originalUrl, suffix -> - + if (log.debugEnabled) { log.debug "CSS Preprocessor munging url $originalUrl" } - - // We don't do absolutes or full URLs - perhaps we should do "/" at some point? If app + + // We don't do absolutes or full URLs - perhaps we should do "/" at some point? If app // is mapped to root context then some people might do this but its lame // Also skip already-processed resources (i.e. bundled CSS) if (!URLUtils.isRelativeURL(originalUrl)) { @@ -81,11 +81,10 @@ class CSSPreprocessorResourceMapper { log.debug "Calculated absoluted URI of CSS resource [$originalUrl] as [$uri]" } return "${prefix}${uri}${suffix}" - } else { - return "${prefix}${originalUrl}${suffix}" } + return "${prefix}${originalUrl}${suffix}" } return null } -} \ No newline at end of file +} diff --git a/grails-app/resourceMappers/org/grails/plugin/resource/CSSRewriterResourceMapper.groovy b/grails-app/resourceMappers/org/grails/plugin/resource/CSSRewriterResourceMapper.groovy index 13deaff..0684668 100644 --- a/grails-app/resourceMappers/org/grails/plugin/resource/CSSRewriterResourceMapper.groovy +++ b/grails-app/resourceMappers/org/grails/plugin/resource/CSSRewriterResourceMapper.groovy @@ -16,13 +16,13 @@ import org.grails.plugin.resource.mapper.MapperPhase class CSSRewriterResourceMapper { // def priority = 1000 - + def phase = MapperPhase.LINKREALISATION - + def grailsResourceProcessor - + static defaultIncludes = ['**/*.css'] - + /** * Find all url() and fix up the url if it is not absolute * NOTE: This needs to run after any plugins that move resources around, but before any that obliterate @@ -34,10 +34,10 @@ class CSSRewriterResourceMapper { def processor = new CSSLinkProcessor() processor.process(resource, grailsResourceProcessor) { prefix, originalUrl, suffix -> - + if (originalUrl.startsWith('resource:')) { def uri = originalUrl - 'resource:' - + if (log.debugEnabled) { log.debug "Calculated URI of CSS resource [$originalUrl] as [$uri]" } @@ -53,14 +53,12 @@ class CSSRewriterResourceMapper { if (log.debugEnabled) { log.debug "Calculating URL of ${linkedToResource?.dump()} relative to ${resource.dump()}" } - def fixedUrl = linkedToResource.relativeToWithQueryParams(resource) def replacement = "${prefix}${fixedUrl}${suffix}" if (log.debugEnabled) { log.debug "Rewriting CSS URL '${originalUrl}' to '$replacement'" } - return replacement } else { log.warn "Cannot resolve CSS resource, leaving link as is: ${originalUrl}" @@ -69,4 +67,4 @@ class CSSRewriterResourceMapper { return "${prefix}${originalUrl}${suffix}" } } -} \ No newline at end of file +} diff --git a/grails-app/resourceMappers/org/grails/plugin/resource/test/TestResourceMapper.groovy b/grails-app/resourceMappers/org/grails/plugin/resource/test/TestResourceMapper.groovy index 2087a6f..1129922 100644 --- a/grails-app/resourceMappers/org/grails/plugin/resource/test/TestResourceMapper.groovy +++ b/grails-app/resourceMappers/org/grails/plugin/resource/test/TestResourceMapper.groovy @@ -1,16 +1,16 @@ package org.grails.plugin.resource.test +import org.grails.plugin.resource.ResourceMeta import org.grails.plugin.resource.mapper.MapperPhase class TestResourceMapper { def phase = MapperPhase.MUTATION - def map(resource, config) { + def map(ResourceMeta resource, config) { def file = new File(resource.processedFile.parentFile, "_${resource.processedFile.name}") assert resource.processedFile.renameTo(file) resource.processedFile = file resource.updateActualUrlFromProcessedFile() } - -} \ No newline at end of file +} diff --git a/grails-app/taglib/org/grails/plugin/resource/ResourceTagLib.groovy b/grails-app/taglib/org/grails/plugin/resource/ResourceTagLib.groovy index ae83ec9..3f91968 100644 --- a/grails-app/taglib/org/grails/plugin/resource/ResourceTagLib.groovy +++ b/grails-app/taglib/org/grails/plugin/resource/ResourceTagLib.groovy @@ -1,14 +1,10 @@ package org.grails.plugin.resource -import grails.util.Environment import grails.util.GrailsUtil -import org.codehaus.groovy.grails.commons.ConfigurationHolder import org.apache.commons.io.FilenameUtils -import org.grails.plugin.resource.util.HalfBakedLegacyLinkGenerator import org.codehaus.groovy.grails.web.taglib.exceptions.GrailsTagException - /** * This taglib handles creation of all the links to resources, including the smart de-duping of them. * @@ -18,60 +14,60 @@ import org.codehaus.groovy.grails.web.taglib.exceptions.GrailsTagException * @author Luke Daley (ld@ldaley.com) */ class ResourceTagLib { - + static namespace = "r" - - static REQ_ATTR_PREFIX_PAGE_FRAGMENTS = 'resources.plugin.page.fragments' - static REQ_ATTR_PREFIX_AUTO_DISPOSITION = 'resources.plugin.auto.disposition' - - static stashWriters = [ - 'script': { out, stash -> - out << "" + static final String REQ_ATTR_PREFIX_PAGE_FRAGMENTS = 'resources.plugin.page.fragments' + static final String REQ_ATTR_PREFIX_AUTO_DISPOSITION = 'resources.plugin.auto.disposition' + private static final List fragmentTypes = ['script', 'style'] + + static stashWriters = [ + script: { out, stash -> + writeStash '', out, stash }, - 'style': { out, stash -> - out << "" + style: { out, stash -> + writeStash '', out, stash } ] - + + protected static void writeStash(String start, String end, out, stash) { + out << start + for (s in stash) { + out << s + } + out << end + } + static writeAttrs( attrs, output) { // Output any remaining user-specified attributes attrs.each { k, v -> if (v != null) { - output << k - output << '="' - output << v.encodeAsHTML() - output << '" ' - } + output << k + output << '="' + output << v.encodeAsHTML() + output << '" ' + } } } - // Closures to write links of different types + // Closures to write links of different types static LINK_WRITERS = [ js: { url, constants, attrs -> def o = new StringBuilder() - o << "' - return o + return o }, - + link: { url, constants, attrs -> def o = new StringBuilder() - o << " @@ -334,15 +330,15 @@ class ResourceTagLib { declareModuleRequiredByPage(name, mandatory) } } - - private stashPageFragment(String type, String disposition, def fragment) { + + private stashPageFragment(String type, String disposition, fragment) { if (log.debugEnabled) { - log.debug "stashing request script for disposition [${disposition}]: ${ fragment}" + log.debug "stashing request script for disposition [$disposition]: $fragment" } needsResourceLayout() - - def trkName = ResourceTagLib.makePageFragmentKey(type, disposition) + + def trkName = makePageFragmentKey(type, disposition) grailsResourceProcessor.addDispositionToRequest(request, disposition, '-page-fragments-') def trk = request[trkName] @@ -352,28 +348,27 @@ class ResourceTagLib { } trk << fragment } - + private static String makePageFragmentKey(String type, String disposition) { "${REQ_ATTR_PREFIX_PAGE_FRAGMENTS}:${type}:${disposition}" } - + private consumePageFragments(String type, String disposition) { - def fraggles = request[ResourceTagLib.makePageFragmentKey(type, disposition)] ?: Collections.EMPTY_LIST - return fraggles + request[makePageFragmentKey(type, disposition)] ?: Collections.EMPTY_LIST } - + private static String makeAutoDispositionKey( String disposition) { "${REQ_ATTR_PREFIX_AUTO_DISPOSITION}:${disposition}" } private isAutoLayoutDone(disposition) { - request[ResourceTagLib.makeAutoDispositionKey(disposition)] + request[makeAutoDispositionKey(disposition)] } - + private autoLayoutDone(disposition) { - request[ResourceTagLib.makeAutoDispositionKey(disposition)] = true + request[makeAutoDispositionKey(disposition)] = true } - + /** * Render the resources. First invocation renders head JS and CSS, second renders deferred JS only, and any more spews. */ @@ -398,17 +393,18 @@ class ResourceTagLib { } return } - } else if (!remainingDispositions.contains(dispositionToRender)) { + } + else if (!remainingDispositions.contains(dispositionToRender)) { if (log.warnEnabled) { log.warn "A request was made to render resources for disposition [${dispositionToRender}] but there are no resources scheduled for that disposition, or it has already been rendered" } return } - + if (log.debugEnabled) { log.debug "Rendering resources for disposition [${dispositionToRender}]" } - + def trk = request.resourceModuleTracker if (log.debugEnabled) { log.debug "Rendering resources, modules in tracker: ${trk}" @@ -417,32 +413,28 @@ class ResourceTagLib { if (log.debugEnabled) { log.debug "Rendering resources, modules needed: ${modulesNeeded}" } - def modulesInOrder = grailsResourceProcessor.getModulesInDependencyOrder(modulesNeeded) if (log.debugEnabled) { log.debug "Rendering non-deferred resources, modules: ${modulesInOrder}..." } - for (module in modulesInOrder) { // @todo where a module resource is bundled, need to satisfy deps of all resources in the bundle first! out << r.renderModule(name:module, disposition:dispositionToRender) } - + if (log.debugEnabled) { log.debug "Rendering page fragments for disposition: ${dispositionToRender}" } - layoutPageStash(dispositionToRender) if (log.debugEnabled) { log.debug "Removing outstanding request disposition: ${dispositionToRender}" } - + grailsResourceProcessor.doneDispositionResources(request, dispositionToRender) } private layoutPageStash(String disposition) { - def fragmentTypes = ['script', 'style'] for (t in fragmentTypes) { def stash = consumePageFragments(t, disposition) if (stash) { @@ -463,7 +455,7 @@ class ResourceTagLib { def stash = { attrs, body -> stashPageFragment(attrs.type, attrs.disposition, body()) } - + protected getModuleByName(name) { def module = grailsResourceProcessor.getModule(name) if (!module) { @@ -491,23 +483,23 @@ class ResourceTagLib { if (!module) { return } - + def s = new StringBuilder() - + def renderingDisposition = attrs.remove('disposition') if (log.debugEnabled) { log.debug "Rendering the resources of module [${name}]: ${module.dump()}" } - + def debugMode = grailsResourceProcessor.isDebugMode(request) - - for (r in module.resources) { + + for (r in module.resources) { if (!r.exists() && !r.actualUrl?.contains('://')) { - throw new IllegalArgumentException("Module [$name] depends on resource [${r.sourceUrl}] but the file cannot be found") + throw new IllegalArgumentException("Module [$name] depends on resource [$r.sourceUrl] but the file cannot be found") } if (log.debugEnabled) { - log.debug "Resource: ${r.sourceUrl} - disposition ${r.disposition} - rendering disposition ${renderingDisposition}" + log.debug "Resource: $r.sourceUrl - disposition $r.disposition - rendering disposition ${renderingDisposition}" } if (r.disposition == renderingDisposition) { def args = [:] @@ -515,11 +507,11 @@ class ResourceTagLib { args.uri = debugMode ? r.originalUrl : "${r.actualUrl}" args.wrapper = r.prePostWrapper args.disposition = r.disposition - + if (r.tagAttributes) { args.putAll(r.tagAttributes) // Copy the attrs originally provided e.g. type override } - + if (log.debugEnabled) { log.debug "Rendering one of the module's resource links: ${args}" } @@ -532,14 +524,14 @@ class ResourceTagLib { /** * Get the uri to use for linking, and - if relevant - the resource instance. - * - * NOTE: The URI handling mechanics in here are pretty evil and nuanced (i.e. + * + * NOTE: The URI handling mechanics in here are pretty evil and nuanced (i.e. * ad-hoc vs declared, ad-hoc and not found, ad-hoc and excluded etc). * There is reasonable test coverage, but only fools rush in. - * + * * @attr uri - to be resolved, i.e. id of the ResourceMeta * @attr disposition - of the resource - * + * * @return Map with uri/url property and *maybe* a resource property */ def resolveLinkUriToUriAndResource(attrs) { @@ -556,30 +548,30 @@ class ResourceTagLib { // via g.resource attrs.contextPath = ctxPath uri = grailsLinkGenerator.resource(attrs) - abs = uri.contains('://') + abs = uri.contains('://') } else { if (!abs) { uri = ctxPath + uri } } - + def debugMode = grailsResourceProcessor.isDebugMode(request) // Get out quick and add param to tell filter we don't want any fancy stuff if (debugMode) { - + // Some JS libraries can't handle different query params being sent to other dependencies // so we reuse the same timestamp for the lifecycle of the request - - // Here we allow a refresh arg that will generate a new timestamp, normally we used the last we - // generated. Otherwise, you can't debug anything in a JS debugger as the URI of the JS + + // Here we allow a refresh arg that will generate a new timestamp, normally we used the last we + // generated. Otherwise, you can't debug anything in a JS debugger as the URI of the JS // is different every time. if (params._refreshResources && !request.'grails-resources.debug-timestamp-refreshed') { // Force re-generation of a new timestamp in debug mode session.removeAttribute('grails-resources.debug-timestamp') request.'grails-resources.debug-timestamp-refreshed' = true } - + def timestamp = session['grails-resources.debug-timestamp'] if (!timestamp) { timestamp = System.currentTimeMillis() @@ -588,14 +580,14 @@ class ResourceTagLib { uri += (uri.indexOf('?') >= 0) ? "&_debugResources=y&n=$timestamp" : "?_debugResources=y&n=$timestamp" return [uri:uri, debug:true] - } - + } + def disposition = attrs.remove('disposition') if (!abs) { uri = forcePrefixedWithSlash(uri) } - + // If its a bad or empty URI, get out of here. It must at least contain the context path if it is relative if (!abs && (uri.size() <= ctxPath.size())) { return [uri:uri] @@ -603,7 +595,7 @@ class ResourceTagLib { def contextRelUri = abs ? uri : uri[ctxPath.size()..-1] def reluri = ResourceProcessor.removeQueryParams(contextRelUri) - + // Get ResourceMeta or create one if uri is not absolute def res = grailsResourceProcessor.getExistingResourceMeta(reluri) if (!res && !abs) { @@ -616,19 +608,19 @@ class ResourceTagLib { } // If the link has to support linkUrl for override, or fall back to the full requested url - // we resolve without query params, but must keep them for linking + // we resolve without query params, but must keep them for linking def linkUrl = res ? res.linkUrl : contextRelUri if (linkUrl.contains('://')) { // @todo do we need to toggle http/https here based on current request protocol? return [uri:linkUrl, resource:res] - } else { - // Only apply static prefix if the resource actually has ResourceMeta created for it - uri = res ? ctxPath+grailsResourceProcessor.staticUrlPrefix+linkUrl : ctxPath+linkUrl - return [uri:uri, resource:res] } + + // Only apply static prefix if the resource actually has ResourceMeta created for it + uri = res ? ctxPath+grailsResourceProcessor.staticUrlPrefix+linkUrl : ctxPath+linkUrl + [uri:uri, resource:res] } - + /** * Get the URL for a resource * @todo this currently won't work for absolute="true" invocations, it should just passthrough these @@ -646,7 +638,7 @@ class ResourceTagLib { out << info.uri } } - + /** * Write out an HTML tag using resource processing for the image */ @@ -656,15 +648,15 @@ class ResourceTagLib { } def args = attrs.clone() args.disposition = "image" - + def info = resolveLinkUriToUriAndResource(args) def res = info.resource attrs.remove('uri') def o = new StringBuilder() - o << "" out << o } - + protected forcePrefixedWithSlash(uri) { if (uri) { if (uri[0] != '/') { diff --git a/grails-app/views/index.gsp b/grails-app/views/index.gsp index 50fbb66..cf411c7 100644 --- a/grails-app/views/index.gsp +++ b/grails-app/views/index.gsp @@ -18,7 +18,7 @@ - + ${output} @@ -31,7 +31,7 @@ ${output.encodeAsHTML()}

Grails logo using processing:

- + document.write("

This is going to come out in the footer

"); diff --git a/src/groovy/org/grails/plugin/resource/AggregatedResourceMeta.groovy b/src/groovy/org/grails/plugin/resource/AggregatedResourceMeta.groovy index 205c750..1f64176 100644 --- a/src/groovy/org/grails/plugin/resource/AggregatedResourceMeta.groovy +++ b/src/groovy/org/grails/plugin/resource/AggregatedResourceMeta.groovy @@ -1,9 +1,5 @@ package org.grails.plugin.resource -import org.apache.commons.logging.LogFactory - -import org.apache.commons.io.FilenameUtils - /** * Holder for info about a resource that is made up of other resources * @@ -12,19 +8,13 @@ import org.apache.commons.io.FilenameUtils */ class AggregatedResourceMeta extends ResourceMeta { - def log = LogFactory.getLog(this.class) - - def resources = [] - def inheritedModuleDependencies = new HashSet() - - void reset() { - super.reset() - } + List resources = [] + Set inheritedModuleDependencies = [] boolean containsResource(ResourceMeta r) { resources.find { r.sourceUrl == it.sourceUrl } } - + @Override boolean isDirty() { resources.any { it.dirty } @@ -36,7 +26,7 @@ class AggregatedResourceMeta extends ResourceMeta { if (!containsResource(r)) { resources << r inheritedModuleDependencies << r.module - + // Update our aggregated sourceUrl sourceUrl = "${sourceUrl}, ${r.sourceUrl}" } @@ -50,8 +40,8 @@ class AggregatedResourceMeta extends ResourceMeta { processedFile.newWriter('UTF-8', true) } - protected initFile(grailsResourceProcessor) { - def commaPos = sourceUrl.indexOf(',') + protected void initFile(grailsResourceProcessor) { + int commaPos = sourceUrl.indexOf(',') if (commaPos == -1) { commaPos = sourceUrl.size() } @@ -60,48 +50,48 @@ class AggregatedResourceMeta extends ResourceMeta { processedFile = grailsResourceProcessor.makeFileForURI(actualUrl) processedFile.createNewFile() - this.contentType = grailsResourceProcessor.getMimeType(actualUrl) + contentType = grailsResourceProcessor.getMimeType(actualUrl) } @Override void beginPrepare(grailsResourceProcessor) { initFile(grailsResourceProcessor) - this.originalSize = resources.originalSize.sum() - + originalSize = resources.originalSize.sum() + buildAggregateResource(grailsResourceProcessor) } void buildAggregateResource(grailsResourceProcessor) { - def moduleOrder = grailsResourceProcessor.modulesInDependencyOrder + List moduleOrder = grailsResourceProcessor.modulesInDependencyOrder + + long newestLastMod = 0 - def newestLastMod = 0 - def bundledContent = new StringBuilder() - + // Add the resources to the file in the order determined by module dependencies! - moduleOrder.each { m -> - resources.each { r -> + for (String m in moduleOrder) { + for (ResourceMeta r in resources) { if (r.module.name == m) { // Append to the existing file if (log.debugEnabled) { - log.debug "Appending contents of ${r.processedFile} to ${processedFile}" + log.debug "Appending contents of $r.processedFile to $processedFile" } bundledContent << r.processedFile.getText("UTF-8") bundledContent << "\r\n" - + if (r.originalLastMod > newestLastMod) { newestLastMod = r.originalLastMod } } } } - + def out = getWriter() out << bundledContent out << "\r\n" out.close() - - this.originalLastMod = newestLastMod + + originalLastMod = newestLastMod } -} \ No newline at end of file +} diff --git a/src/groovy/org/grails/plugin/resource/CSSBundleResourceMeta.groovy b/src/groovy/org/grails/plugin/resource/CSSBundleResourceMeta.groovy index 9eb510c..3bc2b72 100644 --- a/src/groovy/org/grails/plugin/resource/CSSBundleResourceMeta.groovy +++ b/src/groovy/org/grails/plugin/resource/CSSBundleResourceMeta.groovy @@ -1,9 +1,5 @@ package org.grails.plugin.resource -import org.apache.commons.logging.LogFactory - -import org.apache.commons.io.FilenameUtils - /** * Holder for info about a resource that is made up of other resources * @@ -12,16 +8,14 @@ import org.apache.commons.io.FilenameUtils */ class CSSBundleResourceMeta extends AggregatedResourceMeta { - def log = LogFactory.getLog(this.class) - @Override - void beginPrepare(grailsResourceProcessor) { + void beginPrepare(ResourceProcessor grailsResourceProcessor) { initFile(grailsResourceProcessor) - + def out = getWriter() out << '@charset "UTF-8";\n' out.close() buildAggregateResource(grailsResourceProcessor) } -} \ No newline at end of file +} diff --git a/src/groovy/org/grails/plugin/resource/CSSLinkProcessor.groovy b/src/groovy/org/grails/plugin/resource/CSSLinkProcessor.groovy index c9e1b2f..d22025c 100644 --- a/src/groovy/org/grails/plugin/resource/CSSLinkProcessor.groovy +++ b/src/groovy/org/grails/plugin/resource/CSSLinkProcessor.groovy @@ -1,68 +1,69 @@ package org.grails.plugin.resource -import org.apache.commons.logging.LogFactory +import org.slf4j.Logger +import org.slf4j.LoggerFactory /** * This class is used to parse out and replace CSS links - * + * * @author Marc Palmer (marc@grailsrocks.com) * @author Luke Daley (ld@ldaley.com) */ class CSSLinkProcessor { - - def log = LogFactory.getLog(CSSLinkProcessor) - + + private Logger log = LoggerFactory.getLogger(getClass()) + // We need to successfully match any kind of @import and url(), mappers are responsible for checking type - static CSS_URL_PATTERN = ~/(?:(@import\s*['"])(.+?)(['"]))|(url\(\s*['"]?)(.+?)(['"]?\s*\))/ - - boolean isCSSRewriteCandidate(resource, grailsResourceProcessor) { - def enabled = grailsResourceProcessor.config.rewrite.css instanceof Boolean ? grailsResourceProcessor.config.rewrite.css : true - def yes = enabled && (resource.contentType == "text/css" || resource.tagAttributes?.type == "css") + static final CSS_URL_PATTERN = ~/(?:(@import\s*['"])(.+?)(['"]))|(url\(\s*['"]?)(.+?)(['"]?\s*\))/ + + boolean isCSSRewriteCandidate(ResourceMeta resource, ResourceProcessor grailsResourceProcessor) { + boolean enabled = grailsResourceProcessor.config.rewrite.css instanceof Boolean ? grailsResourceProcessor.config.rewrite.css : true + boolean yes = enabled && (resource.contentType == "text/css" || resource.tagAttributes?.type == "css") if (log.debugEnabled) { - log.debug "Resource ${resource.actualUrl} being CSS rewritten? $yes" + log.debug "Resource $resource.actualUrl being CSS rewritten? $yes" } return yes } - + /** * Find all url() and fix up the url if it is not absolute * NOTE: This needs to run after any plugins that move resources around, but before any that obliterate * the content i.e. before minify or gzip */ - void process(ResourceMeta resource, grailsResourceProcessor, Closure urlMapper) { - + void process(ResourceMeta resource, ResourceProcessor grailsResourceProcessor, Closure urlMapper) { + if (!isCSSRewriteCandidate(resource, grailsResourceProcessor)) { if (log.debugEnabled) { - log.debug "CSS link processor skipping ${resource} because its not a CSS rewrite candidate" + log.debug "CSS link processor skipping $resource because its not a CSS rewrite candidate" } return } - + // Move existing to tmp file, then write to the correct file - def origFileTempCopy = new File(resource.processedFile.toString()+'.tmp') - + File origFileTempCopy = new File(resource.processedFile.toString()+'.tmp') + // Make sure temp file doesn't exist already new File(origFileTempCopy.toString()).delete() // On MS Windows if we don't do this origFileTempCopy gets corrupt after delete - + // Move the existing file to temp resource.processedFile.renameTo(origFileTempCopy) if (log.debugEnabled) { - log.debug "Pre-processing CSS resource ${resource.sourceUrl} to rewrite links" + log.debug "Pre-processing CSS resource $resource.sourceUrl to rewrite links" } - def inputCss = origFileTempCopy.getText('UTF-8') + String inputCss = origFileTempCopy.getText('UTF-8') def processedCss = inputCss.replaceAll(CSS_URL_PATTERN) { Object[] args -> - int modifier = args[1] ? 0 : 3 // determine: @import or url() match - def prefix = args[1 + modifier] - def originalUrl = args[2 + modifier].trim() - def suffix = args[3 + modifier] + int modifier = args[1] ? 0 : 3 // determine: @import or url() match + def prefix = args[1 + modifier] + String originalUrl = args[2 + modifier].trim() + def suffix = args[3 + modifier] - return urlMapper(prefix, originalUrl, suffix) + return urlMapper(prefix, originalUrl, suffix) } resource.processedFile.setText(processedCss, 'UTF-8') - + // Delete the temp file - origFileTempCopy.delete() + origFileTempCopy.delete() } -} \ No newline at end of file +} diff --git a/src/groovy/org/grails/plugin/resource/DevModeSanityFilter.groovy b/src/groovy/org/grails/plugin/resource/DevModeSanityFilter.groovy index 933be85..f9dc8d3 100644 --- a/src/groovy/org/grails/plugin/resource/DevModeSanityFilter.groovy +++ b/src/groovy/org/grails/plugin/resource/DevModeSanityFilter.groovy @@ -1,16 +1,22 @@ package org.grails.plugin.resource -import javax.servlet.* +import javax.servlet.Filter +import javax.servlet.FilterChain +import javax.servlet.FilterConfig +import javax.servlet.ServletException +import javax.servlet.ServletRequest +import javax.servlet.ServletResponse + import org.springframework.web.context.support.WebApplicationContextUtils -import grails.util.Environment /** * This just traps any obvious mistakes the user has made and warns them in dev mode - * + * * @author Marc Palmer (marc@grailsrocks.com) */ class DevModeSanityFilter implements Filter { - static RELOADING_DOC = """ + + static final String RELOADING_DOC = """ @@ -23,37 +29,40 @@ class DevModeSanityFilter implements Filter {

Resources are being processed, please wait...

""" - - def grailsResourceProcessor - + + ResourceProcessor grailsResourceProcessor + void init(FilterConfig config) throws ServletException { def applicationContext = WebApplicationContextUtils.getWebApplicationContext(config.servletContext) grailsResourceProcessor = applicationContext.grailsResourceProcessor } - void destroy() { - } + void destroy() {} - void doFilter(ServletRequest request, ServletResponse response, - FilterChain chain) throws IOException, ServletException { + void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { if (grailsResourceProcessor.reloading) { response.contentType = "text/html" response.writer << RELOADING_DOC - } else { - chain.doFilter(request, response) - - if (request.getAttribute('resources.need.layout')) { - def dispositionsLeftOver = grailsResourceProcessor.getRequestDispositionsRemaining(request) - if (dispositionsLeftOver) { - def optionals = grailsResourceProcessor.optionalDispositions - dispositionsLeftOver -= optionals - if (dispositionsLeftOver) { - throw new RuntimeException("It looks like you are missing some calls to the r:layoutResources tag. "+ - "After rendering your page the following have not been rendered: ${dispositionsLeftOver}") - } - } - } + return + } + + chain.doFilter(request, response) + + if (!request.getAttribute('resources.need.layout')) { + return + } + + def dispositionsLeftOver = grailsResourceProcessor.getRequestDispositionsRemaining(request) + if (!dispositionsLeftOver) { + return + } + + List optionals = grailsResourceProcessor.optionalDispositions + dispositionsLeftOver -= optionals + if (dispositionsLeftOver) { + throw new RuntimeException("It looks like you are missing some calls to the r:layoutResources tag. "+ + "After rendering your page the following have not been rendered: ${dispositionsLeftOver}") } } -} \ No newline at end of file +} diff --git a/src/groovy/org/grails/plugin/resource/JavaScriptBundleResourceMeta.groovy b/src/groovy/org/grails/plugin/resource/JavaScriptBundleResourceMeta.groovy index 1e34230..607f229 100644 --- a/src/groovy/org/grails/plugin/resource/JavaScriptBundleResourceMeta.groovy +++ b/src/groovy/org/grails/plugin/resource/JavaScriptBundleResourceMeta.groovy @@ -1,15 +1,8 @@ package org.grails.plugin.resource -import org.apache.commons.logging.LogFactory - -import org.apache.commons.io.FilenameUtils - /** * Holder for info about a JS resource that is made up of other resources * * @author Marc Palmer (marc@grailsrocks.com) */ -class JavaScriptBundleResourceMeta extends AggregatedResourceMeta { - - def log = LogFactory.getLog(this.class) -} \ No newline at end of file +class JavaScriptBundleResourceMeta extends AggregatedResourceMeta {} \ No newline at end of file diff --git a/src/groovy/org/grails/plugin/resource/ProcessingFilter.groovy b/src/groovy/org/grails/plugin/resource/ProcessingFilter.groovy index 4f81e30..388f632 100644 --- a/src/groovy/org/grails/plugin/resource/ProcessingFilter.groovy +++ b/src/groovy/org/grails/plugin/resource/ProcessingFilter.groovy @@ -1,23 +1,27 @@ package org.grails.plugin.resource -import javax.servlet.* +import javax.servlet.Filter +import javax.servlet.FilterChain +import javax.servlet.FilterConfig +import javax.servlet.ServletException +import javax.servlet.ServletRequest +import javax.servlet.ServletResponse + import org.springframework.web.context.support.WebApplicationContextUtils -import grails.util.Environment /** - * This is the servlet filter that handles all static resource requests and delegates to the service - * to return them. - * + * Handles all static resource requests and delegates to the service to return them. + * * @author Marc Palmer (marc@grailsrocks.com) */ class ProcessingFilter implements Filter { def grailsResourceProcessor - + boolean adhoc - + void init(FilterConfig config) throws ServletException { adhoc = config.getInitParameter('adhoc') == 'true' - + def applicationContext = WebApplicationContextUtils.getWebApplicationContext(config.servletContext) grailsResourceProcessor = applicationContext.grailsResourceProcessor } @@ -44,4 +48,4 @@ class ProcessingFilter implements Filter { chain.doFilter(request, response) } } -} \ No newline at end of file +} diff --git a/src/groovy/org/grails/plugin/resource/ResourceMeta.groovy b/src/groovy/org/grails/plugin/resource/ResourceMeta.groovy index 4bf04cb..ad46773 100644 --- a/src/groovy/org/grails/plugin/resource/ResourceMeta.groovy +++ b/src/groovy/org/grails/plugin/resource/ResourceMeta.groovy @@ -1,16 +1,16 @@ package org.grails.plugin.resource +import org.apache.commons.io.FilenameUtils +import org.grails.plugin.resource.mapper.ResourceMapper +import org.slf4j.Logger import org.slf4j.LoggerFactory import org.springframework.core.io.Resource import org.springframework.core.io.UrlResource -import org.apache.commons.io.FilenameUtils - -import org.grails.plugin.resource.mapper.ResourceMapper /** * Holder for info about a resource declaration at runtime * - * This is actually non-trivial. A lot of data kept here. Be wary of what you think a "url" is. + * This is actually non-trivial. A lot of data kept here. Be wary of what you think a "url" is. * See the javadocs for each URL property. * * @author Marc Palmer (marc@grailsrocks.com) @@ -18,23 +18,23 @@ import org.grails.plugin.resource.mapper.ResourceMapper */ class ResourceMeta { - static final PROCESSED_BY_PREFIX = 'processed.by.' - - def log = LoggerFactory.getLogger(ResourceMeta) + static final String PROCESSED_BY_PREFIX = 'processed.by.' + + private Logger log = LoggerFactory.getLogger(getClass()) /** * The optional module-unique id */ String id - + /** * The owning module */ ResourceModule module - + /** * Set on instantiation to be the dir that content is served from - * + * * @see ResourceProcessor#workDir */ File workDir @@ -58,118 +58,117 @@ class ResourceMeta { * The original sourceUrlParamsAndFragment of the resource, if any */ String sourceUrlParamsAndFragment - + /** * The url of the local resource, after processing. (no query params) */ String actualUrl - + /** * The url to use when rendering links - e.g. for absolute CDN overrides */ String linkOverride - + String bundle - + /** * The original mime type */ String contentType - + /** * Where do you want this resource? "defer", "head" etc */ String disposition Set excludedMappers - + // For per-resource options like "nominify", 'nozip' Map attributes = [:] - + // For per-resource tag resource attributes like "media", 'width', 'height' etc Map tagAttributes = [:] Closure prePostWrapper // ***** Below here is state we determine at runtime during processing ******* - + /** * The delegate to actually use when linking, if any. Think bundling. */ private ResourceMeta delegate - + Resource originalResource - + Long originalSize - + Long processedSize File processedFile - + long originalLastMod - + // A list of Closures taking request & response. Delegates to resourceMeta List requestProcessors = [] - + private String _linkUrl - + private boolean processed - + private Boolean _resourceExists - + /** * The URI of the resource that resulted in the processing of this resource, or null * For resources ref'd in CSS or stuff loaded up by bundles for example */ String declaringResource - + Integer contentLength - + Integer originalContentLength = 0 void delegateTo(ResourceMeta target) { delegate = target - + // No more processing to be done on us processed = true } - + boolean isOriginalAbsolute() { sourceUrl.indexOf(':/') > 0 } - + boolean isActualAbsolute() { actualUrl.indexOf(':/') > 0 } - + boolean isDirty() { - !originalResource || - (originalResource.lastModified() != originalLastMod) + !originalResource || (originalResource.lastModified() != originalLastMod) } - + boolean needsProcessing() { processed } - + void updateContentLength() { if (processedFile) { - this.@contentLength = processedFile.size().toInteger() + contentLength = processedFile.size().toInteger() } else if (originalResource?.URL.protocol in ['jndi', 'file']) { - this.@contentLength = originalResource?.URL.openConnection().contentLength + contentLength = originalResource?.URL.openConnection().contentLength } else { - this.@contentLength = 0 + contentLength = 0 } } void setOriginalResource(Resource res) { - this.originalResource = res + originalResource = res updateExists() - this.originalContentLength = originalResource?.URL.openConnection().contentLength + originalContentLength = originalResource?.URL.openConnection().contentLength updateContentLength() } - + void setProcessedFile(File f) { - this.processedFile = f + processedFile = f updateExists() updateContentLength() } @@ -177,88 +176,86 @@ class ResourceMeta { void updateExists() { if (processedFile) { _resourceExists = processedFile.exists() - if (!this.originalLastMod && _resourceExists) { - this.originalLastMod = processedFile.lastModified() + if (!originalLastMod && _resourceExists) { + originalLastMod = processedFile.lastModified() } - if (this.originalSize == null) { - this.originalSize = _resourceExists ? processedFile.length() : 0 + if (originalSize == null) { + originalSize = _resourceExists ? processedFile.length() : 0 } } else if (originalResource) { - _resourceExists = originalResource.exists() - if (!this.originalLastMod && _resourceExists) { - this.originalLastMod = originalResource.lastModified() + _resourceExists = originalResource.exists() + if (!originalLastMod && _resourceExists) { + originalLastMod = originalResource.lastModified() } } } private void copyOriginalResourceToWorkArea() { - def inputStream = this.originalResource.inputStream + def inputStream = originalResource.inputStream try { // Now copy in the resource from this app deployment into the cache, ready for mutation - this.processedFile << inputStream - _resourceExists = this.processedFile.exists() - } finally { - inputStream?.close() + processedFile << inputStream + _resourceExists = processedFile.exists() + } + finally { + inputStream?.close() } } /** - * Return a new input stream for serving the resource - if processing is disabled + * Return a new input stream for serving the resource - if processing is disabled * the processedFile will be null and the original resource is used */ InputStream newInputStream() { return processedFile ? processedFile.newInputStream() : originalResource.inputStream } - + // Hook for when preparation is starting - void beginPrepare(grailsResourceProcessor) { - def uri = this.sourceUrl - if (!uri.contains('://')) { - - // Delete whatever file may already be there - processedFile?.delete() - - def uriWithoutFragment = uri - if (uri.contains('#')) { - uriWithoutFragment = uri.substring(0, uri.indexOf('#')) - } - - def origResourceURL = grailsResourceProcessor.getOriginalResourceURLForURI(uriWithoutFragment) - if (!origResourceURL) { - if (log.errorEnabled) { - if (this.declaringResource) { - log.error "While processing ${this.declaringResource}, a resource was required but not found: ${uriWithoutFragment}" - } else { - log.error "Resource not found: ${uriWithoutFragment}" - } - } - throw new FileNotFoundException("Cannot locate resource [$uri]") - } + void beginPrepare(ResourceProcessor grailsResourceProcessor) { + String uri = sourceUrl + if (uri.contains('://')) { + setOriginalResource(new UrlResource(sourceUrl)) + setActualUrl(sourceUrl) - this.contentType = grailsResourceProcessor.getMimeType(uriWithoutFragment) - if (log.debugEnabled) { - log.debug "Resource [$uriWithoutFragment] ($origResourceURL) has content type [${this.contentType}]" - } + log.warn "Skipping mappers for $actualUrl because its an absolute URL." + return + } - setOriginalResource(new UrlResource(origResourceURL)) + // Delete whatever file may already be there + processedFile?.delete() - if (grailsResourceProcessor.processingEnabled) { - setActualUrl(uriWithoutFragment) + String uriWithoutFragment = uri + if (uri.contains('#')) { + uriWithoutFragment = uri.substring(0, uri.indexOf('#')) + } - setProcessedFile(grailsResourceProcessor.makeFileForURI(uriWithoutFragment)) - // copy the file ready for mutation - this.copyOriginalResourceToWorkArea() + URL origResourceURL = grailsResourceProcessor.getOriginalResourceURLForURI(uriWithoutFragment) + if (!origResourceURL) { + if (declaringResource) { + log.error "While processing ${declaringResource}, a resource was required but not found: ${uriWithoutFragment}" } else { - setActualUrl(uriWithoutFragment) + log.error "Resource not found: ${uriWithoutFragment}" } + throw new FileNotFoundException("Cannot locate resource [$uri]") + } - } else { - setOriginalResource(new UrlResource(this.sourceUrl)) - setActualUrl(this.sourceUrl) + contentType = grailsResourceProcessor.getMimeType(uriWithoutFragment) + if (log.debugEnabled) { + log.debug "Resource [$uriWithoutFragment] ($origResourceURL) has content type [${this.contentType}]" + } + + setOriginalResource(new UrlResource(origResourceURL)) - log.warn "Skipping mappers for ${this.actualUrl} because its an absolute URL." + if (grailsResourceProcessor.processingEnabled) { + setActualUrl(uriWithoutFragment) + + setProcessedFile(grailsResourceProcessor.makeFileForURI(uriWithoutFragment)) + // copy the file ready for mutation + copyOriginalResourceToWorkArea() + } else { + setActualUrl(uriWithoutFragment) } } - + // Hook for when preparation is done void endPrepare(grailsResourceProcessor) { if (!delegating) { @@ -270,46 +267,37 @@ class ResourceMeta { updateExists() processed = true } - + boolean isDelegating() { delegate != null } - + boolean exists() { _resourceExists } - + String getLinkUrl() { - if (!delegate) { - return linkOverride ?: _linkUrl - } else { - return delegate.linkUrl - } + delegate ? delegate.linkUrl : linkOverride ?: _linkUrl } - + String getActualUrl() { - if (!delegate) { - return this.@actualUrl - } else { - return delegate.actualUrl - } + delegate ? delegate.actualUrl : actualUrl } void setActualUrl(String url) { - this.@actualUrl = url + actualUrl = url _linkUrl = sourceUrlParamsAndFragment ? actualUrl + sourceUrlParamsAndFragment : url } - - + void setSourceUrl(String url) { - if (this.@originalUrl == null) { - this.@originalUrl = url // the full monty + if (originalUrl == null) { + originalUrl = url // the full monty } - - def qidx = url.indexOf('?') - def hidx = url.indexOf('#') - def chopIdx = -1 + int qidx = url.indexOf('?') + int hidx = url.indexOf('#') + + int chopIdx = -1 // if there's hash we chop there, it comes before query if (hidx >= 0 && qidx < 0) { chopIdx = hidx @@ -323,7 +311,7 @@ class ResourceMeta { // Strictly speaking this is query params plus fragment ... sourceUrlParamsAndFragment = chopIdx >= 0 ? url[chopIdx..-1] : null - + sourceUrlExtension = FilenameUtils.getExtension(sourceUrl) ?: null } @@ -335,59 +323,51 @@ class ResourceMeta { FilenameUtils.getExtension(processedFile.name) ?: null } } - + String getWorkDirRelativeParentPath() { workDirRelativePath - "$processedFile.name" } - + String getWorkDirRelativePath() { - if (processedFile) { - return processedFile.path - workDir.path - } else { - return null - } + processedFile ? processedFile.path - workDir.path : null } - + String getActualUrlParent() { - def lastSlash = actualUrl.lastIndexOf('/') - if (lastSlash >= 0) { - return actualUrl[0..lastSlash-1] - } else { - return '' - } + int lastSlash = actualUrl.lastIndexOf('/') + lastSlash >= 0 ? actualUrl[0..lastSlash-1] : '' } - + String relativeToWithQueryParams(ResourceMeta base) { - def url = relativeTo(base) + String url = relativeTo(base) return sourceUrlParamsAndFragment ? url + sourceUrlParamsAndFragment : url } - + ResourceMeta getDelegate() { delegate } - + /** * Reset the resource state to how it was after loading from the module definition * i.e. keep only declared info, nothing generated later during processing * // @todo should we delete the file in here? */ void reset() { - this.@contentType = null - this.@actualUrl = null - this.@processedFile = null - this.@originalResource = null - this.@_resourceExists = false - this.@originalContentLength = 0 - this.@_linkUrl = null - this.@delegate = null - this.@originalLastMod = 0 - this.@contentLength = 0 - this.@declaringResource = null - this.@requestProcessors.clear() - this.@processed = false + contentType = null + actualUrl = null + processedFile = null + originalResource = null + _resourceExists = false + originalContentLength = 0 + _linkUrl = null + delegate = null + originalLastMod = 0 + contentLength = 0 + declaringResource = null + requestProcessors.clear() + processed = false attributes.entrySet().removeAll { it.key.startsWith(PROCESSED_BY_PREFIX) } } - + /** * Calculate the URI of this resource relative to the base resource. * All resource URLs must be app-relative with no ../ or ./ @@ -396,36 +376,36 @@ class ResourceMeta { if (actualAbsolute) { return actualUrl } - def baseDirStr = base.actualUrlParent - def thisDirStr = this.actualUrlParent + String baseDirStr = base.actualUrlParent + String thisDirStr = actualUrlParent boolean isChild = thisDirStr.startsWith(baseDirStr+'/') if (isChild) { // Truncate to the part that is after the base dir - return this.actualUrl[baseDirStr.size()+1..-1] - } else { - def result = new StringBuilder() - - def commonStem = new StringBuilder() - def baseUrl = base.actualUrl - // Eliminate the common portion - the base to which we need to ".." - def baseParts = baseUrl.tokenize('/') - def thisParts = actualUrl.tokenize('/') - int i = 0 - for (; i < baseParts.size(); i++) { - if (thisParts[i] == baseParts[i]) { - commonStem << baseParts[i]+'/' - } else { - break; - } - } - if (baseParts.size()-1 > i) { - result << '../' * (baseParts.size()-1 - i) + return actualUrl[baseDirStr.size()+1..-1] + } + + def result = new StringBuilder() + + def commonStem = new StringBuilder() + String baseUrl = base.actualUrl + // Eliminate the common portion - the base to which we need to ".." + def baseParts = baseUrl.tokenize('/') + def thisParts = actualUrl.tokenize('/') + int i = 0 + for (; i < baseParts.size(); i++) { + if (thisParts[i] == baseParts[i]) { + commonStem << baseParts[i]+'/' + } else { + break } - result << actualUrl[commonStem.size()+1..-1] - return result.toString() } + if (baseParts.size()-1 > i) { + result << '../' * (baseParts.size()-1 - i) + } + result << actualUrl[commonStem.size()+1..-1] + result.toString() } - + void updateActualUrlFromProcessedFile() { def p = workDirRelativePath?.replace('\\', '/') if (p != null) { @@ -435,13 +415,13 @@ class ResourceMeta { setActualUrl(sourceUrl) } } - + boolean excludesMapperOrOperation(String mapperName, String operationName) { if (!excludedMappers) { return false } - - def exclude = excludedMappers.contains("*") + + boolean exclude = excludedMappers.contains("*") if (!exclude) { exclude = excludedMappers.contains(mapperName) } @@ -450,12 +430,12 @@ class ResourceMeta { } return exclude } - + void wasProcessedByMapper(ResourceMapper mapper, boolean processed = true) { attributes[PROCESSED_BY_PREFIX+mapper.name] = processed } - + String toString() { - "ResourceMeta for URI ${sourceUrl} served by ${actualUrl} (delegate: ${delegating ? delegate : 'none'})" + "ResourceMeta for URI $sourceUrl served by $actualUrl (delegate: ${delegating ? delegate : 'none'})" } -} \ No newline at end of file +} diff --git a/src/groovy/org/grails/plugin/resource/ResourceModule.groovy b/src/groovy/org/grails/plugin/resource/ResourceModule.groovy index 18e9fbd..cee7a14 100644 --- a/src/groovy/org/grails/plugin/resource/ResourceModule.groovy +++ b/src/groovy/org/grails/plugin/resource/ResourceModule.groovy @@ -1,6 +1,5 @@ package org.grails.plugin.resource -import org.codehaus.groovy.grails.web.servlet.mvc.GrailsWebRequest import java.util.concurrent.CopyOnWriteArrayList /** @@ -12,32 +11,29 @@ import java.util.concurrent.CopyOnWriteArrayList class ResourceModule { String name String cachedMarkup // Saves calling the tags every time - + List resources = new CopyOnWriteArrayList() List dependsOn = [] def defaultBundle - - def pluginManager - - private HashSet dispositions + + private Set dispositions /** * Constructor for testing only */ - ResourceModule() { - } + ResourceModule() {} - ResourceModule(name, svc) { + ResourceModule(String name, ResourceProcessor svc) { this.name = name - this.pluginManager = svc.pluginManager - this.defaultBundle = false + pluginManager = svc.pluginManager + defaultBundle = false } - - ResourceModule(name, Map resourceInfo, defBundle, svc) { + + ResourceModule(String name, Map resourceInfo, defBundle, ResourceProcessor svc) { this(name, svc) - this.defaultBundle = defBundle + defaultBundle = defBundle def args = [:] args.putAll(resourceInfo) @@ -48,13 +44,13 @@ class ResourceModule { if (args.url instanceof Map) { args.url = svc.buildLinkToOriginalResource(args.url) } - this.resources << newResourceFromArgs(args, svc, true) + resources << newResourceFromArgs(args, svc, true) lockDown() } ResourceModule(name, List resourceInfoList, defBundle, svc) { this(name, svc) - this.defaultBundle = defBundle + defaultBundle = defBundle resourceInfoList.each { i -> if (i instanceof Map) { def args = i.clone() @@ -62,35 +58,35 @@ class ResourceModule { args.url = svc.buildLinkToOriginalResource(args.url) } def r = newResourceFromArgs(args, svc, resourceInfoList.size()==1) - this.resources << r + resources << r } else if (i instanceof String) { - this.resources << newResourceFromArgs(url:i, svc, resourceInfoList.size()==1) + resources << newResourceFromArgs(url:i, svc, resourceInfoList.size()==1) } else { throw new IllegalArgumentException("I don't understand this resource: ${i}") } } lockDown() } - + void addModuleDependency(String name) { dependsOn << name } - + def getBundleTypes() { ['css', 'js'] } - + ResourceMeta addNewSyntheticResource(Class type, String uri, resSvc) { def agg = type.newInstance(module:this) agg.sourceUrl = uri // Hack agg.actualUrl = uri agg.workDir = resSvc.workDir - + resources << agg - + agg } - + ResourceMeta newResourceFromArgs(Map args, svc, boolean singleResourceModule) { def url = args.remove('url') if (url) { @@ -117,35 +113,37 @@ class ResourceModule { if (excludedMappers) { if (excludedMappers instanceof List) { r.excludedMappers = excludedMappers as Set - } else if (!(excludedMappers instanceof Set)) { + } + else if (!(excludedMappers instanceof Set)) { r.excludedMappers = excludedMappers.toString().split(',')*.trim() as Set } - } - + } + // We cannot auto bundle this if attrs, wrapper are set, or its a single resource module, or its not // a bundle-able type - def canAutoBundle = + def canAutoBundle = (!singleResourceModule || (singleResourceModule && defaultBundle)) && // single resource with defaultbundle specified is OK - !r.bundle && - !args.wrapper && - !args.attrs && + !r.bundle && + !args.wrapper && + !args.attrs && (r.sourceUrlExtension in bundleTypes) - + if (canAutoBundle) { if (defaultBundle == null) { // use module name by default r.bundle = "bundle_$name" - } else if (defaultBundle) { + } + else if (defaultBundle) { // use supplied value as a default r.bundle = defaultBundle.toString() } } - + // Namespace bundle by disposition - if (r.bundle) { + if (r.bundle) { r.bundle += '_'+r.disposition } - + r.prePostWrapper = args.remove('wrapper') def resattrs = ti.attrs?.clone() ?: [:] def attrs = args.remove('attrs') @@ -156,19 +154,17 @@ class ResourceModule { } r.tagAttributes = resattrs r.attributes.putAll(args) - return r + return r } - + void lockDown() { - this.resources = this.resources.asImmutable() + resources = resources.asImmutable() } - + Set getRequiredDispositions() { if (!dispositions) { - dispositions = (resources.findAll { r -> - r.disposition - }).disposition as Set + dispositions = (resources.findAll { r -> r.disposition }).disposition as Set } - return dispositions + dispositions } -} \ No newline at end of file +} diff --git a/src/groovy/org/grails/plugin/resource/ResourceProcessor.groovy b/src/groovy/org/grails/plugin/resource/ResourceProcessor.groovy index 6acb09f..61cccfd 100644 --- a/src/groovy/org/grails/plugin/resource/ResourceProcessor.groovy +++ b/src/groovy/org/grails/plugin/resource/ResourceProcessor.groovy @@ -1,20 +1,25 @@ package org.grails.plugin.resource import grails.util.Environment + +import java.util.concurrent.ConcurrentHashMap + +import javax.servlet.ServletContext +import javax.servlet.ServletRequest + import org.apache.commons.io.FilenameUtils -import org.apache.commons.logging.LogFactory import org.codehaus.groovy.grails.plugins.PluginManagerHolder import org.grails.plugin.resource.mapper.ResourceMapper import org.grails.plugin.resource.mapper.ResourceMappersFactory import org.grails.plugin.resource.module.ModuleDeclarationsFactory import org.grails.plugin.resource.module.ModulesBuilder import org.grails.plugin.resource.util.ResourceMetaStore +import org.slf4j.Logger +import org.slf4j.LoggerFactory import org.springframework.beans.factory.InitializingBean import org.springframework.util.AntPathMatcher import org.springframework.web.util.WebUtils -import javax.servlet.ServletRequest -import java.util.concurrent.ConcurrentHashMap /** * This is where it all happens. * @@ -26,113 +31,113 @@ import java.util.concurrent.ConcurrentHashMap */ class ResourceProcessor implements InitializingBean { - static transactional = false - boolean reloading - - def log = LogFactory.getLog(ResourceProcessor) - - static final PATH_MATCHER = new AntPathMatcher() - static ADHOC_MODULE = "__@adhoc-files@__" // The placeholder for all undeclared resources that are linked to - static SYNTHETIC_MODULE = "__@synthetic-files@__" // The placeholder for all the generated (i.e. aggregate) resources - - static REQ_ATTR_DEBUGGING = 'resources.debug' - static REQ_ATTR_DISPOSITIONS_REMAINING = 'resources.dispositions.remaining' - static REQ_ATTR_DISPOSITIONS_DONE = "resources.dispositions.done" - - static DISPOSITION_HEAD = 'head' - static DISPOSITION_DEFER = 'defer' - static DEFAULT_DISPOSITION_LIST = [DISPOSITION_HEAD, DISPOSITION_DEFER] - static DEFAULT_ADHOC_INCLUDES = [ - '**/*.*' - ] - - static DEFAULT_ADHOC_EXCLUDES = [ - ] - - static DEFAULT_MODULE_SETTINGS = [ + + private Logger log = LoggerFactory.getLogger(getClass()) + + static final AntPathMatcher PATH_MATCHER = new AntPathMatcher() + static final String ADHOC_MODULE = "__@adhoc-files@__" // The placeholder for all undeclared resources that are linked to + static final String SYNTHETIC_MODULE = "__@synthetic-files@__" // The placeholder for all the generated (i.e. aggregate) resources + + static final String REQ_ATTR_DEBUGGING = 'resources.debug' + static final String REQ_ATTR_DISPOSITIONS_REMAINING = 'resources.dispositions.remaining' + static final String REQ_ATTR_DISPOSITIONS_DONE = "resources.dispositions.done" + + static final String DISPOSITION_HEAD = 'head' + static final String DISPOSITION_DEFER = 'defer' + static final List DEFAULT_DISPOSITION_LIST = [DISPOSITION_HEAD, DISPOSITION_DEFER] + static final List DEFAULT_ADHOC_INCLUDES = ['**/*.*'] + + static final List DEFAULT_ADHOC_EXCLUDES = [] + + static final Map DEFAULT_MODULE_SETTINGS = [ css:[disposition: 'head'], rss:[disposition: 'head'], gif:[disposition: 'head'], jpg:[disposition: 'head'], png:[disposition: 'head'], ico:[disposition: 'head'], - js:[disposition: 'defer'] + js: [disposition: 'defer'] ] Map statistics = [:] - + def grailsLinkGenerator def grailsResourceLocator // A Grails 2-only bean - - def staticUrlPrefix - + + String staticUrlPrefix + private File workDir - - def modulesByName = new ConcurrentHashMap() - def resourceInfo = new ResourceMetaStore() - def allResourcesByOriginalSourceURI = new ConcurrentHashMap() + Map modulesByName = new ConcurrentHashMap() + + ResourceMetaStore resourceInfo = new ResourceMetaStore() + Map allResourcesByOriginalSourceURI = new ConcurrentHashMap() + + List modulesInDependencyOrder = [] - def modulesInDependencyOrder = [] - def resourceMappers - + def grailsApplication - @Lazy servletContext = { grailsApplication.mainContext.servletContext }() - + @Lazy ServletContext servletContext = { grailsApplication.mainContext.servletContext }() + boolean processingEnabled - + List adHocIncludes - List adHocExcludes - List optionalDispositions - - boolean isInternalModule(def moduleOrName) { + List adHocExcludes + List optionalDispositions + + boolean isInternalModule(moduleOrName) { def n = moduleOrName instanceof ResourceModule ? moduleOrName.name : moduleOrName - return n in [ADHOC_MODULE, SYNTHETIC_MODULE] + return n in [ADHOC_MODULE, SYNTHETIC_MODULE] } - + /** * Topographic Sort ordering for a Directed Acyclic Graph of dependencies. * Less scary than it seemed, but still a bit hard to read. Think about vectors and edges. * Visit wikipedia. */ void updateDependencyOrder() { - def modules = (modulesByName.collect { it.value }).findAll { !isInternalModule(it) } - def noIncomingEdges = modules.findAll { l -> !modules.any { l.name in it.dependsOn } } - def ordered = [] + List modules = (modulesByName.collect { it.value }).findAll { !isInternalModule(it) } + List noIncomingEdges = modules.findAll { l -> !modules.any { l.name in it.dependsOn } } + List ordered = [] Set visited = new HashSet() - - def visit - visit = { n -> - if (!(n.name in visited)) { - visited << n.name - def incomingEdges = [] - n.dependsOn?.each {d -> - def m = modules.find { mod -> mod.name == d } - if (!m) { - log.warn "There is a dependency on module [${d}] by module [${n.name}] but no such module has been defined" - } else { - incomingEdges << m - } - } - for (m in incomingEdges) { - visit(m) - } - ordered << n.name - } - } - for (module in noIncomingEdges) { - visit(module) + for (ResourceModule module in noIncomingEdges) { + visit module, visited, modules, ordered } - + ordered << ADHOC_MODULE - ordered << SYNTHETIC_MODULE - + ordered << SYNTHETIC_MODULE + modulesInDependencyOrder = ordered } - + + protected visit(ResourceModule n, Set visited, List modules, List ordered) { + if (n.name in visited) { + return + } + + visited << n.name + + List incomingEdges = [] + for (d in n.dependsOn) { + ResourceModule m = modules.find { mod -> mod.name == d } + if (!m) { + log.warn "There is a dependency on module [${d}] by module [${n.name}] but no such module has been defined" + } + else { + incomingEdges << m + } + } + + for (ResourceModule m in incomingEdges) { + visit m, visited, modules, ordered + } + + ordered << n.name + } + void afterPropertiesSet() { processingEnabled = getConfigParamOrDefault('processing.enabled', true) adHocIncludes = getConfigParamOrDefault('adhoc.includes', DEFAULT_ADHOC_INCLUDES) @@ -143,7 +148,7 @@ class ResourceProcessor implements InitializingBean { optionalDispositions = getConfigParamOrDefault('optional.dispositions', ['inline', 'image']) } - + File getWorkDir() { // @todo this isn't threadsafe at startup if its lazy. We should change it. if (!this.@workDir) { @@ -153,35 +158,35 @@ class ResourceProcessor implements InitializingBean { assert this.@workDir return this.@workDir } - + def getPluginManager() { // The plugin manager bean configured in integration testing is not the real thing and causes errors. // Using the pluginManager from the holder means that we always get a legit instance. // http://jira.codehaus.org/browse/GRAILSPLUGINS-2712 PluginManagerHolder.pluginManager } - - def extractURI(request, adhoc) { - def uriStart = (adhoc ? request.contextPath : request.contextPath+staticUrlPrefix).size() - return uriStart < request.requestURI.size() ? request.requestURI[uriStart..-1] : '' + + String extractURI(request, boolean adhoc) { + int uriStart = (adhoc ? request.contextPath : request.contextPath + staticUrlPrefix).size() + uriStart < request.requestURI.size() ? request.requestURI[uriStart..-1] : '' } boolean canProcessLegacyResource(uri) { // Apply our own url filtering rules because servlet mapping uris are too lame boolean included = adHocIncludes.find { p -> - PATH_MATCHER.match(p, uri) + PATH_MATCHER.match(p, uri) } if (log.debugEnabled) { - log.debug "Legacy resource ${uri} matched includes? ${included}" + log.debug "Legacy resource $uri matched includes? $included" } if (included) { included = !(adHocExcludes.find { PATH_MATCHER.match(it, uri) }) if (log.debugEnabled) { - log.debug "Legacy resource ${uri} passed excludes? ${included}" + log.debug "Legacy resource $uri passed excludes? $included" } } - + return included } @@ -196,7 +201,7 @@ class ResourceProcessor implements InitializingBean { * This is not recommended, its just a quick out of the box fix for legacy (or pre-"resources plugin" plugin) code. * * So a request for /css/main.css comes in. This needs to redirect to e.g. /static/css/342342353345343534.css - * This involves looking it up by source uri. Therefore the same resource may have multiple mappings in the + * This involves looking it up by source uri. Therefore the same resource may have multiple mappings in the * resourceInfo map but they should not be conflicting. */ boolean processLegacyResource(request, response) { @@ -204,7 +209,7 @@ class ResourceProcessor implements InitializingBean { log.debug "Handling Legacy resource ${request.requestURI}" } def uri = ResourceProcessor.removeQueryParams(extractURI(request, true)) - + // Only handle it if it should be included in processing if (canProcessLegacyResource(uri)) { if (log.debugEnabled) { @@ -218,7 +223,7 @@ class ResourceProcessor implements InitializingBean { response.sendError(404, fnfe.message) return } - + if (Environment.current == Environment.DEVELOPMENT) { if (res) { response.setHeader('X-Grails-Resources-Original-Src', res?.sourceUrl) @@ -230,11 +235,11 @@ class ResourceProcessor implements InitializingBean { response.sendError(404) } return true - } else { - return false // we didn't handle this } + + return false // we didn't handle this } - + /** * Redirect the client to the actual processed Url, used for when an ad-hoc resource is accessed */ @@ -243,37 +248,35 @@ class ResourceProcessor implements InitializingBean { // NOTE: only works for local resources def u = request.contextPath+staticUrlPrefix+res.linkUrl if (log.debugEnabled) { - log.debug "Redirecting ad-hoc resource ${request.requestURI} to $u which makes it UNCACHEABLE - declare this resource "+ + log.debug "Redirecting ad-hoc resource $request.requestURI to $u which makes it UNCACHEABLE - declare this resource "+ "and use resourceLink/module tags to avoid redirects and enable client-side caching" } response.sendRedirect(u) } - + /** * Process a URI where the input URI matches a cached and declared resource URI, * without any redirects. This is the real deal - */ + */ void processModernResource(request, response) { if (log.debugEnabled) { log.debug "Handling resource ${request.requestURI}" } // Find the ResourceMeta for the request, or create it - def uri = ResourceProcessor.removeQueryParams(extractURI(request, false)) - def inf + String uri = removeQueryParams(extractURI(request, false)) + ResourceMeta inf try { - // Allow ad hoc creation of these resources too, incase they are requested directly + // Allow ad hoc creation of these resources too, incase they are requested directly // across multiple nodes, even if this node has not yet created a link to that resource inf = getResourceMetaForURI(uri, true) - } catch (FileNotFoundException fnfe) { - response.sendError(404, fnfe.message) + } catch (FileNotFoundException e) { + response.sendError(404, e.message) return } - + if (inf) { if (Environment.current == Environment.DEVELOPMENT) { - if (inf) { - response.setHeader('X-Grails-Resources-Original-Src', inf.sourceUrl) - } + response.setHeader('X-Grails-Resources-Original-Src', inf.sourceUrl) } // See if its an ad-hoc resource that has come here via a relative link @@ -289,7 +292,7 @@ class ResourceProcessor implements InitializingBean { if (log.debugEnabled) { log.debug "Returning processed resource ${request.requestURI}" } - def data = inf.newInputStream() + InputStream data = inf.newInputStream() try { // Now set up the response response.contentType = inf.contentType @@ -303,14 +306,14 @@ class ResourceProcessor implements InitializingBean { } for (processor in inf.requestProcessors) { if (log.debugEnabled) { - log.debug "Applying request processor on ${request.requestURI}: "+processor.class.name + log.debug "Applying request processor on ${request.requestURI}: ${processor.getClass().name}" } def p = processor.clone() p.delegate = inf p(request, response) } } - + // @todo Could we do something faster here? Feels wrong, buffer size is tiny in Groovy response.outputStream << data } finally { @@ -320,7 +323,7 @@ class ResourceProcessor implements InitializingBean { response.sendError(404) } } - + /** * See if we have a ResourceMeta for this URI. * @return null if not processed/created yet, the instance if it exists @@ -328,33 +331,28 @@ class ResourceProcessor implements InitializingBean { ResourceMeta findResourceForURI(String uri) { resourceInfo[uri] } - + ResourceMeta findSyntheticResourceById(bundleId) { - def mod = modulesByName[SYNTHETIC_MODULE] - if (mod) { - return mod.resources.find { r -> r.id == bundleId } - } else { - return null - } + modulesByName[SYNTHETIC_MODULE]?.resources?.find { ResourceMeta r -> r.id == bundleId } } - + ResourceMeta newSyntheticResource(String uri, Class type) { if (log.debugEnabled) { - log.debug "Creating synthetic resource of type ${type} for URI [${uri}]" + log.debug "Creating synthetic resource of type $type for URI [$uri]" } - def synthModule = getOrCreateSyntheticOrImplicitModule(true) - def agg = synthModule.addNewSyntheticResource(type, uri, this) - + ResourceModule synthModule = getOrCreateSyntheticOrImplicitModule(true) + ResourceModule agg = synthModule.addNewSyntheticResource(type, uri, this) + if (log.debugEnabled) { log.debug "synthetic module resources: ${synthModule.resources}" } - + return agg } - + ResourceModule getOrCreateSyntheticOrImplicitModule(boolean synthetic) { - def mod - def moduleName = synthetic ? SYNTHETIC_MODULE : ADHOC_MODULE + ResourceModule mod + String moduleName = synthetic ? SYNTHETIC_MODULE : ADHOC_MODULE // We often get multiple simultaneous requests at startup and this causes // multiple creates and loss of concurrently processed resources synchronized (moduleName) { @@ -369,20 +367,20 @@ class ResourceProcessor implements InitializingBean { } return mod } - + ResourceMeta getExistingResourceMeta(uri) { resourceInfo[uri] } - + /** * Get the existing or create a new ad-hoc ResourceMeta for the URI. * @returns The resource instance - which may have a null processedFile if the resource cannot be found */ - ResourceMeta getResourceMetaForURI(uri, Boolean createAdHocResourceIfNeeded = true, String declaringResource = null, + ResourceMeta getResourceMetaForURI(uri, Boolean createAdHocResourceIfNeeded = true, String declaringResource = null, Closure postProcessor = null) { // Declared resources will already exist, but ad-hoc or synthetic may need to be created - def res = resourceInfo.getOrCreateAdHocResource(uri) { -> + def res = resourceInfo.getOrCreateAdHocResource(uri) { -> if (!createAdHocResourceIfNeeded) { if (log.warnEnabled) { @@ -390,26 +388,26 @@ class ResourceProcessor implements InitializingBean { } return null } - + if (!canProcessLegacyResource(uri)) { if (log.debugEnabled) { log.debug("Skipping ad-hoc resource $uri as it is excluded") } return null } - + // If we don't already have it, its either not been declared in the DSL or its Synthetic and its // not already been retrieved - def mod = getOrCreateSyntheticOrImplicitModule(false) - + ResourceModule mod = getOrCreateSyntheticOrImplicitModule(false) + // Need to create ad-hoc resource, its not synthetic if (log.debugEnabled) { log.debug "Creating new implicit resource for ${uri}" } - def r = new ResourceMeta(sourceUrl: uri, workDir: getWorkDir(), module:mod) + ResourceMeta r = new ResourceMeta(sourceUrl: uri, workDir: getWorkDir(), module:mod) r.declaringResource = declaringResource - - r = prepareResource(r, true) + + r = prepareResource(r, true) // Only if the URI mapped to a real file, do we add the resource // Prevents DoS with zillions of 404s @@ -425,18 +423,18 @@ class ResourceProcessor implements InitializingBean { } } } - + allResourcesByOriginalSourceURI[r.sourceUrl] = r return r } // end of closure return res } - + /** * Workaround for replaceAll problems with \ in Java */ - String makeFileSystemPathFromURI(uri) { + String makeFileSystemPathFromURI(String uri) { def chars = uri.chars chars.eachWithIndex { c, i -> if (c == '/') { @@ -445,13 +443,13 @@ class ResourceProcessor implements InitializingBean { } new String(chars) } - + File makeFileForURI(String uri) { - def splitPoint = uri.lastIndexOf('/') - def fileSystemDir = splitPoint > 0 ? makeFileSystemPathFromURI(uri[0..splitPoint-1]) : '' - def fileSystemFile = makeFileSystemPathFromURI(uri[splitPoint+1..-1]) - def staticDir = new File(getWorkDir(), fileSystemDir) - + int splitPoint = uri.lastIndexOf('/') + String fileSystemDir = splitPoint > 0 ? makeFileSystemPathFromURI(uri[0..splitPoint-1]) : '' + String fileSystemFile = makeFileSystemPathFromURI(uri[splitPoint+1..-1]) + File staticDir = new File(getWorkDir(), fileSystemDir) + // force the structure if (!staticDir.exists()) { // Do not assert this, we are re-entrant and may get multiple simultaneous calls. @@ -461,18 +459,18 @@ class ResourceProcessor implements InitializingBean { log.error "Unable to create static resource cache directory: ${staticDir}" } } - + if (log.debugEnabled) { log.debug "Creating file object for URI [$uri] from [${staticDir}] and [${fileSystemFile}]" } - def f = new File(staticDir, fileSystemFile) + File f = new File(staticDir, fileSystemFile) // Delete the existing file - it may be from previous release, we cannot tell. if (f.exists()) { assert f.delete() } return f } - + /** * Take g.resource style args and create a link to that original resource in the app, relative to the app context path */ @@ -487,31 +485,27 @@ class ResourceProcessor implements InitializingBean { * */ URL getOriginalResourceURLForURI(uri) { - if(grailsResourceLocator != null) { - def res = grailsResourceLocator.findResourceForURI(uri) - if(res != null) { - return res.URL - } - } - else { - servletContext.getResource(uri) + if (grailsResourceLocator == null) { + return servletContext.getResource(uri) } + + grailsResourceLocator.findResourceForURI(uri)?.URL } - + /** * Resolve mime type for a URI by file extension */ String getMimeType(uri) { servletContext.getMimeType(uri) } - + /** * Execute the processing chain for the resource, returning list of URIs to add to uri -> resource mappings * for this resource */ ResourceMeta prepareResource(ResourceMeta r, boolean adHocResource) { if (log.debugEnabled) { - log.debug "Preparing resource ${r.sourceUrl} (${r.dump()})" + log.debug "Preparing resource $r.sourceUrl (${r.dump()})" } if (r.delegating) { if (log.debugEnabled) { @@ -519,10 +513,9 @@ class ResourceProcessor implements InitializingBean { } return } - + if (!adHocResource && findResourceForURI(r.sourceUrl)) { - def existing = allResourcesByOriginalSourceURI[r.sourceUrl] - def modName = existing.module.name + String modName = allResourcesByOriginalSourceURI[r.sourceUrl].module.name throw new IllegalArgumentException( "Skipping prepare resource for [${r.sourceUrl}] - This resource is declared in module [${r.module.name}] as well as module [${modName}]" ) @@ -530,17 +523,15 @@ class ResourceProcessor implements InitializingBean { r.beginPrepare(this) - if (processingEnabled) { - if (!r.originalAbsolute) { - applyMappers(r) - } + if (processingEnabled && !r.originalAbsolute) { + applyMappers(r) } - - r.endPrepare(this) + + r.endPrepare(this) return r } - + def getStatValue(category, subcategory, defaultValue = 0) { def cat = statistics[category] if (cat == null) { @@ -549,30 +540,30 @@ class ResourceProcessor implements InitializingBean { } return cat[subcategory] != null ? cat[subcategory] : defaultValue } - + void storeAggregateStat(category, subcategory, value) { def v = getStatValue(category, subcategory) statistics[category][subcategory] = v + value } - + void applyMappers(ResourceMeta r) { // Now iterate over the mappers... if (log.debugEnabled) { log.debug "Applying mappers to ${r.processedFile}" } - + // Apply all mappers / or only those until the resource becomes delegated // Once delegated, its the delegate that needs to be processed, not the original def phase for (m in resourceMappers) { if (isNotDisabledInGlobalConfig(m)) { if (r.delegating) { - break; + break } if (log.debugEnabled) { - log.debug "Running mapper ${m.name} (${m.artefact})" + log.debug "Running mapper $m.name ($m.artefact)" } if (m.phase != phase) { @@ -583,16 +574,16 @@ class ResourceProcessor implements InitializingBean { } if (log.debugEnabled) { - log.debug "Applying mapper ${m.name} to ${r.processedFile} - delegating? ${r.delegating}" + log.debug "Applying mapper $m.name to $r.processedFile - delegating? $r.delegating" } - def startTime = System.currentTimeMillis() + long startTime = System.currentTimeMillis() def appliedMapper = m.invokeIfNotExcluded(r) if (log.debugEnabled) { - log.debug "Applied mapper ${m.name} to ${r.processedFile}" + log.debug "Applied mapper $m.name to $r.processedFile" } - def endTime = System.currentTimeMillis() + long endTime = System.currentTimeMillis() storeAggregateStat('mappers-time', m.name, endTime-startTime) r.wasProcessedByMapper(m, appliedMapper) @@ -609,30 +600,31 @@ class ResourceProcessor implements InitializingBean { try { prepareResource(r, false) } catch (FileNotFoundException fnfe) { - log.warn fnfe.message + if (log.warnEnabled) { + log.warn fnfe.message + } } } if (postPrepare) { postPrepare() } } - + void prepareResourceBatch(ResourceProcessorBatch batch) { if (log.debugEnabled) { log.debug "Preparing resource batch:" - batch.each { r -> + batch.each { ResourceMeta r -> log.debug "Batch includes resource: ${r.sourceUrl}" } } - + def affectedSynthetics = [] - batch.each { r -> - r.reset() + batch.each { ResourceMeta r -> + r.reset() resourceInfo.evict(r.sourceUrl) prepareSingleDeclaredResource(r) { - def u = r.sourceUrl - allResourcesByOriginalSourceURI[u] = r + allResourcesByOriginalSourceURI[r.sourceUrl] = r } if (r.delegating) { @@ -646,17 +638,16 @@ class ResourceProcessor implements InitializingBean { if (log.debugEnabled) { log.debug "Preparing synthetic resources" } - for (r in affectedSynthetics) { + for (ResourceMeta r in affectedSynthetics) { if (log.debugEnabled) { log.debug "Preparing synthetic resource: ${r.sourceUrl}" } - + r.reset() resourceInfo.evict(r.sourceUrl) - + prepareSingleDeclaredResource(r) { - def u = r.sourceUrl - allResourcesByOriginalSourceURI[u] = r + allResourcesByOriginalSourceURI[r.sourceUrl] = r } } } @@ -665,52 +656,48 @@ class ResourceProcessor implements InitializingBean { if (log.debugEnabled) { log.debug "Storing resource module definition ${m.dump()}" } - + if (batch) { - for (r in m.resources) { + for (ResourceMeta r in m.resources) { batch.add(r) } } modulesByName[m.name] = m } - + def defineModule(String name) { storeModule(new ResourceModule(name, this), null) } def defineModuleFromBuilder(builderInfo, ResourceProcessorBatch resBatch) { - def m = new ResourceModule(builderInfo.name, builderInfo.resources, builderInfo.defaultBundle, this) + ResourceModule m = new ResourceModule(builderInfo.name, builderInfo.resources, builderInfo.defaultBundle, this) storeModule(m, resBatch) builderInfo.dependencies?.each { d -> m.addModuleDependency(d) } } - + /** * Resolve a resource to a URL by resource name */ - def getModule(name) { + ResourceModule getModule(String name) { modulesByName[name] } - + void forgetModules() { - if (log.infoEnabled) { - log.info "Forgetting all known modules..." - } + log.info "Forgetting all known modules..." modulesByName.clear() modulesInDependencyOrder.clear() - + // If we forget modules we have to forget resources too forgetResources() } void forgetResources() { - if (log.infoEnabled) { - log.info "Forgetting all known resources..." - } + log.info "Forgetting all known resources..." - // These are bi-products of resource processing so need to go + // These are byproducts of resource processing so need to go modulesByName.remove(SYNTHETIC_MODULE) modulesByName.remove(ADHOC_MODULE) @@ -720,29 +707,27 @@ class ResourceProcessor implements InitializingBean { } private loadModules(ResourceProcessorBatch batch) { - if (log.infoEnabled) { - log.info "Loading resource declarations..." - } - forgetModules() + log.info "Loading resource declarations..." + forgetModules() + + Map declarations = ModuleDeclarationsFactory.getModuleDeclarations(grailsApplication) - def declarations = ModuleDeclarationsFactory.getModuleDeclarations(grailsApplication) - def modules = [] def builder = new ModulesBuilder(modules) - declarations.each { sourceClassName, dsl -> + declarations.each { String sourceClassName, Closure dsl -> if (log.debugEnabled) { log.debug("evaluating resource modules from $sourceClassName") } - + dsl.delegate = builder dsl.resolveStrategy = Closure.DELEGATE_FIRST - dsl.owner.class.metaClass.static.main = { builder.main(it) } // GPRESOURCES-50: hack to allow module name 'main' + dsl.owner.getClass().metaClass.static.main = { builder.main(it) } // GPRESOURCES-50: hack to allow module name 'main' dsl() } // Always do app modules after - def appModules = ModuleDeclarationsFactory.getApplicationConfigDeclarations(grailsApplication) + Closure appModules = ModuleDeclarationsFactory.getApplicationConfigDeclarations(grailsApplication) if (appModules) { if (log.debugEnabled) { log.debug("evaluating resource modules from application Config") @@ -751,16 +736,17 @@ class ResourceProcessor implements InitializingBean { appModules.resolveStrategy = Closure.DELEGATE_FIRST appModules() } - + if (log.debugEnabled) { log.debug("resource modules after evaluation: $modules") } - + // Now merge in any overrides if (log.debugEnabled) { log.debug "Merging in module overrides ${builder._moduleOverrides}" } - builder._moduleOverrides.each { overriddenModule -> + + for (overriddenModule in builder._moduleOverrides) { if (log.debugEnabled) { log.debug "Merging in module overrides for ${overriddenModule}" } @@ -780,48 +766,45 @@ class ResourceProcessor implements InitializingBean { existingModule.dependencies = overriddenModule.dependencies } overriddenModule.resources.each { res -> - def existingResources = existingModule.resources.findAll { + def existingResources = existingModule.resources.findAll { it.id ? (it.id == res.id) : (it.url == res.id) } if (existingResources) { if (log.debugEnabled) { - log.debug "Overriding ${overriddenModule.name} resources with id ${res.id} with "+ - "new settings: ${res}" + log.debug "Overriding $overriddenModule.name resources with id $res.id with new settings: $res" } // Merge, not replace - for each matching resource existingResources.each { r -> r.putAll(res) } } - } + } } else { if (log.warnEnabled) { - log.warn "Attempt to override resource module ${overriddenModule.name} but "+ - "there is nothing to override, this module does not exist" + log.warn "Attempt to override resource module $overriddenModule.name but there is nothing to override, this module does not exist" } } } - + modules.each { m -> defineModuleFromBuilder(m, batch) } - + updateDependencyOrder() - + resourcesChanged(batch) } - + private resourcesChanged(ResourceProcessorBatch batch) { prepareResourceBatch(batch) resolveSyntheticResourceDependencies() } - private collectResourcesThatNeedProcessing(module, batch) { + private void collectResourcesThatNeedProcessing(ResourceModule module, ResourceProcessorBatch batch) { // Reset them all in case this is a reload - for (r in module?.resources) { + for (ResourceMeta r in module?.resources) { if (log.debugEnabled) { log.debug "Has resource [${r.sourceUrl}] changed? ${r.dirty}" } - if (r.dirty) { batch.add(r) } @@ -829,17 +812,15 @@ class ResourceProcessor implements InitializingBean { } private loadResources(ResourceProcessorBatch resBatch) { - if (log.infoEnabled) { - log.info "Loading declared resources..." - } - + log.info "Loading declared resources..." + // Check for any declared or ad hoc resources that need processing for (m in modulesInDependencyOrder) { if (m != SYNTHETIC_MODULE) { - def module = modulesByName[m] - collectResourcesThatNeedProcessing(module, resBatch) + collectResourcesThatNeedProcessing(modulesByName[m], resBatch) } } + /* @todo add this later when we understand what less-css-resources needs // Now do the derived synthetic resources as we know any changed components // have now been reset @@ -858,54 +839,50 @@ class ResourceProcessor implements InitializingBean { if (log.debugEnabled) { statistics.each { cat, subcats -> subcats.each { sc, v -> - log.debug " ${sc} = $v" + log.debug " $sc = $v" } } } } - - /** - * - */ + void resolveSyntheticResourceDependencies() { // @todo: - // 1. Go through all SYNTHETIC resources + // 1. Go through all SYNTHETIC resources // 2. Add all resources before it in the current module as deps to the resources // 3. Iterate over module deps of resource's owning module, in module dep order // 4. Add all their resources as deps, before existing deps // 5. Repeat 2-4 for all resources on all declared modules, in module dep order (bottom up) } - - static removeQueryParams(uri) { - def qidx = uri.indexOf('?') + + static String removeQueryParams(String uri) { + int qidx = uri.indexOf('?') qidx > 0 ? uri[0..qidx-1] : uri } - + def getDefaultSettingsForURI(uri, typeOverride = null) { - + if (!typeOverride) { // Strip off query args - def extUrl = ResourceProcessor.removeQueryParams(uri) - - def ext = FilenameUtils.getExtension(extUrl) + String extUrl = ResourceProcessor.removeQueryParams(uri) + + String ext = FilenameUtils.getExtension(extUrl) if (log.debugEnabled) { - log.debug "Extension extracted from ${uri} ([$extUrl]) is ${ext}" + log.debug "Extension extracted from $uri ([$extUrl]) is $ext" } typeOverride = ext } - + DEFAULT_MODULE_SETTINGS[typeOverride] } - - - def dumpResources(toLog = true) { + + def dumpResources(boolean toLog = true) { def s1 = new StringBuilder() - modulesByName.keySet().sort().each { moduleName -> - def mod = modulesByName[moduleName] + modulesByName.keySet().sort().each { String moduleName -> + ResourceModule mod = modulesByName[moduleName] s1 << "Module: ${moduleName}\n" s1 << " Depends on modules: ${mod.dependsOn}\n" - def res = []+mod.resources - res.sort({ a,b -> a.actualUrl <=> b.actualUrl}).each { resource -> + List res = [] + mod.resources + res.sort({ a,b -> a.actualUrl <=> b.actualUrl}).each { ResourceMeta resource -> if (resource instanceof AggregatedResourceMeta) { s1 << " Synthetic Resource: ${resource.sourceUrl}\n" } else { @@ -920,7 +897,7 @@ class ResourceProcessor implements InitializingBean { s1 << " -- source Extension: ${resource.sourceUrlExtension}\n" s1 << " -- query params/fragment: ${resource.sourceUrlParamsAndFragment}\n" s1 << " -- url for linking: ${resource.linkUrl}\n" - s1 << " -- content length: ${resource.contentLength} (original ${resource.originalContentLength})\n" + s1 << " -- content length: $resource.contentLength (original ${resource.originalContentLength})\n" s1 << " -- link override: ${resource.linkOverride}\n" s1 << " -- excluded mappers: ${resource.excludedMappers?.join(', ')}\n" s1 << " -- attributes: ${resource.attributes}\n" @@ -930,41 +907,41 @@ class ResourceProcessor implements InitializingBean { } } def s2 = new StringBuilder() - resourceInfo.keySet().sort().each { uri -> + resourceInfo.keySet().sort().each { String uri -> def res = resourceInfo[uri] - s2 << "Resource URI: ${uri} => ${res.processedFile}\n" + s2 << "Resource URI: $uri => $res.processedFile\n" } updateDependencyOrder() def s4 = "Dependency load order: ${modulesInDependencyOrder}\n" def s5 = "Mapper application order: ${resourceMappers*.name}\n" - + if (toLog) { log.debug '-'*50 log.debug "Resource definitions" - log.debug(s1) + log.debug(s1.toString()) log.debug '-'*50 log.debug "Resource URI cache" log.debug '-'*50 - log.debug(s2) + log.debug(s2.toString()) log.debug '-'*50 log.debug "Module load order" log.debug '-'*50 - log.debug(s4) + log.debug(s4.toString()) log.debug '-'*50 - log.debug(s5) + log.debug(s5.toString()) log.debug '-'*50 - } + } return s1.toString() + s2.toString() + s4.toString() + s5.toString() } - + /** * Returns the config object under 'grails.resources' */ ConfigObject getConfig() { grailsApplication.config.grails.resources } - + /** * Used to retrieve a resources config param, or return the supplied * default value if no explicit value was set in config @@ -979,7 +956,7 @@ class ResourceProcessor implements InitializingBean { param } } - + boolean isDebugMode(ServletRequest request) { if (getConfigParamOrDefault('debug', false)) { config.debug @@ -989,7 +966,7 @@ class ResourceProcessor implements InitializingBean { false } } - + private isExplicitDebugRequest(ServletRequest request) { if (Environment.current == Environment.DEVELOPMENT) { def requestContainsDebug = request.getParameter('_debugResources') != null @@ -1000,7 +977,7 @@ class ResourceProcessor implements InitializingBean { false } } - + private void loadMappers() { resourceMappers = ResourceMappersFactory.createResourceMappers(grailsApplication, config.mappers) } @@ -1012,23 +989,22 @@ class ResourceProcessor implements InitializingBean { reloading = true try { log.info("Performing a resource mapper reload") - + resetStats() - + loadMappers() - + ResourceProcessorBatch reloadBatch = new ResourceProcessorBatch() - + loadResources(reloadBatch) - + dumpStats() log.info("Finished resource mapper reload") } finally { reloading = false } } - - + synchronized reloadModules() { reloading = true try { @@ -1036,9 +1012,9 @@ class ResourceProcessor implements InitializingBean { resetStats() ResourceProcessorBatch reloadBatch = new ResourceProcessorBatch() - + loadModules(reloadBatch) - + dumpStats() log.info("Finished module definition reload") } finally { @@ -1054,16 +1030,16 @@ class ResourceProcessor implements InitializingBean { resetStats() ResourceProcessorBatch reloadBatch = new ResourceProcessorBatch() - + loadResources(reloadBatch) - + dumpStats() log.info("Finished changed file reload") } finally { reloading = false } } - + void reloadAll() { reloading = true try { @@ -1072,11 +1048,11 @@ class ResourceProcessor implements InitializingBean { resetStats() loadMappers() - + ResourceProcessorBatch reloadBatch = new ResourceProcessorBatch() - + loadModules(reloadBatch) - + dumpStats() log.info("Finished full reload") } catch (Throwable t) { @@ -1085,13 +1061,13 @@ class ResourceProcessor implements InitializingBean { reloading = false } } - + void resetStats() { statistics.clear() } - + /** - * Return a list of all the names of all modules required (included the input modules) to + * Return a list of all the names of all modules required (included the input modules) to * satisfy the dependencies of the input list of module names. */ def getAllModuleNamesRequired(moduleNameList) { @@ -1108,12 +1084,12 @@ class ResourceProcessor implements InitializingBean { throw new IllegalArgumentException("No module found with name [${m}]") } } - + return result } - + /** - * Return a list of all the names of all modules required (included the input modules) to + * Return a list of all the names of all modules required (included the input modules) to * satisfy the dependencies of the input list of module names. */ def getAllModuleNamesRequired(Map moduleNamesAndMandatory) { @@ -1135,7 +1111,7 @@ class ResourceProcessor implements InitializingBean { throw new IllegalArgumentException("No module found with name [${m.key}]") } } - + return result } @@ -1145,27 +1121,27 @@ class ResourceProcessor implements InitializingBean { def getModulesInDependencyOrder(moduleNameList) { def result = [] - def modules = moduleNameList as HashSet + Set modules = moduleNameList for (m in modulesInDependencyOrder) { if (modules.contains(m)) { result << m } } - - return result + + return result } - + /** * Get the set of dispositions required by resources in the current request, which have not yet been rendered */ Set getRequestDispositionsRemaining(request) { - def dispositions = request[REQ_ATTR_DISPOSITIONS_REMAINING] + Set dispositions = request[REQ_ATTR_DISPOSITIONS_REMAINING] // Return a new set of HEAD + DEFER if there is nothing in the request currently, this is our baseline if (dispositions == null) { - dispositions = new HashSet() + dispositions = [] request[REQ_ATTR_DISPOSITIONS_REMAINING] = dispositions } - return dispositions + return dispositions } /** @@ -1173,43 +1149,43 @@ class ResourceProcessor implements InitializingBean { */ void addDispositionToRequest(request, String disposition, String reason) { if (haveAlreadyDoneDispositionResources(request, disposition)) { - throw new IllegalArgumentException("""Cannot disposition [$disposition] to this request (required for [$reason]) - + throw new IllegalArgumentException("""Cannot disposition [$disposition] to this request (required for [$reason]) - that disposition has already been rendered. Check that your r:layoutResources tag comes after all Resource tags that add content to that disposition.""") } - def dispositions = request[REQ_ATTR_DISPOSITIONS_REMAINING] - if (dispositions != null) { - dispositions << disposition - } else { - request[REQ_ATTR_DISPOSITIONS_REMAINING] = [disposition] as Set + Set dispositions = request[REQ_ATTR_DISPOSITIONS_REMAINING] + if (dispositions == null) { + dispositions = [] + request[REQ_ATTR_DISPOSITIONS_REMAINING] = dispositions } + dispositions << disposition } - + void addModuleDispositionsToRequest(request, String moduleName) { if (log.debugEnabled) { log.debug "Adding module dispositions for module [${moduleName}]" - } + } def module = modulesByName[moduleName] - if (module) { + if (module) { if (log.debugEnabled) { log.debug "Adding module's dispositions to request: ${module.requiredDispositions}" - } + } for (d in module.requiredDispositions) { addDispositionToRequest(request, d, moduleName) } } } - + /** * Add a disposition to the current request's set of them */ void removeDispositionFromRequest(request, String disposition) { - def dispositions = request[REQ_ATTR_DISPOSITIONS_REMAINING] + def dispositions = request[REQ_ATTR_DISPOSITIONS_REMAINING] if (dispositions != null) { dispositions.remove(disposition) } } - + void doneDispositionResources(request, String disposition) { removeDispositionFromRequest(request, disposition) def s = request[REQ_ATTR_DISPOSITIONS_DONE] @@ -1219,10 +1195,9 @@ Resource tags that add content to that disposition.""") } s << disposition } - + boolean haveAlreadyDoneDispositionResources(request,String disposition) { def s = request[REQ_ATTR_DISPOSITIONS_DONE] s == null ? false : s.contains(disposition) } - } diff --git a/src/groovy/org/grails/plugin/resource/ResourceProcessorBatch.groovy b/src/groovy/org/grails/plugin/resource/ResourceProcessorBatch.groovy index 3098e4e..69f5669 100644 --- a/src/groovy/org/grails/plugin/resource/ResourceProcessorBatch.groovy +++ b/src/groovy/org/grails/plugin/resource/ResourceProcessorBatch.groovy @@ -1,14 +1,15 @@ package org.grails.plugin.resource class ResourceProcessorBatch { + private List dirtyResources = [] - + void each(Closure c) { for (r in dirtyResources) { c(r) } } - + void add(ResourceMeta r) { dirtyResources << r } @@ -16,4 +17,4 @@ class ResourceProcessorBatch { void add(List resources) { dirtyResources.addAll(resources) } -} \ No newline at end of file +} diff --git a/src/groovy/org/grails/plugin/resource/URLUtils.groovy b/src/groovy/org/grails/plugin/resource/URLUtils.groovy index 8739bba..69ec244 100644 --- a/src/groovy/org/grails/plugin/resource/URLUtils.groovy +++ b/src/groovy/org/grails/plugin/resource/URLUtils.groovy @@ -4,30 +4,30 @@ package org.grails.plugin.resource * @author Marc Palmer (marc@grailsrocks.com) */ class URLUtils { - + /** * Take a base URI and a target URI and resolve target against the base * using the normal rules e.g. "../x", "./x" "x" results in a link relative to the base's folder * and / is app-absolute, and anything with a protocol :// is absolute * - * Please note, I take full responsibility for the nastiness of this code. I could not + * Please note, I take full responsibility for the nastiness of this code. I could not * find a nice way to do this, and I wanted to find an existing lib to do it. Its * certainly not my finest moment. Sorry. Rely on the MenuTagTests. * * It's quite ugly in there. */ - static String relativeURI(base, target) { + static String relativeURI(String base, String target) { new URI(base).resolve(new URI(target)).normalize().toString() } - + /** * Works out if url is relative, such that it would need to be corrected if * the file containing the url is moved */ - static Boolean isRelativeURL(url) { - !url.startsWith('/') && - !url.startsWith('data:') && - !url.startsWith('#') && + static Boolean isRelativeURL(String url) { + !url.startsWith('/') && + !url.startsWith('data:') && + !url.startsWith('#') && !(url.indexOf('://') >= 0) } -} \ No newline at end of file +} diff --git a/src/groovy/org/grails/plugin/resource/mapper/MapperPhase.groovy b/src/groovy/org/grails/plugin/resource/mapper/MapperPhase.groovy index 390bb96..813ef62 100644 --- a/src/groovy/org/grails/plugin/resource/mapper/MapperPhase.groovy +++ b/src/groovy/org/grails/plugin/resource/mapper/MapperPhase.groovy @@ -2,19 +2,18 @@ package org.grails.plugin.resource.mapper /** * The ORDERED phases of mapper execution. - * Change the order, you break everything. + * Change the order, you break everything. */ enum MapperPhase { - GENERATION, // create new assets = compile less files - MUTATION, // alter/improve assets (may mean creating new/deleting aggregated resources) = spriting - COMPRESSION, // reducing the file size but maintaining semantics = minify - LINKNORMALISATION, // convert all inter asset references into a normal form = css links - AGGREGATION, // combining mutiple assets into one = bundling - RENAMING, // moving of physical assets = hashing - LINKREALISATION, // convert normalised inter asset references into real form = css links + GENERATION, // create new assets = compile less files + MUTATION, // alter/improve assets (may mean creating new/deleting aggregated resources) = spriting + COMPRESSION, // reducing the file size but maintaining semantics = minify + LINKNORMALISATION, // convert all inter asset references into a normal form = css links + AGGREGATION, // combining mutiple assets into one = bundling + RENAMING, // moving of physical assets = hashing + LINKREALISATION, // convert normalised inter asset references into real form = css links ALTERNATEREPRESENTATION, // attach different representations of the asset = gzipping - DISTRIBUTION, // moving assets to their hosting environment = s3, cdn - ABSOLUTISATION, // update inter asset references to their distributed equivalent - NOTIFICATION // let the world know about the new resources = cache invalidation + DISTRIBUTION, // moving assets to their hosting environment = s3, cdn + ABSOLUTISATION, // update inter asset references to their distributed equivalent + NOTIFICATION // let the world know about the new resources = cache invalidation } - diff --git a/src/groovy/org/grails/plugin/resource/mapper/ResourceMapper.groovy b/src/groovy/org/grails/plugin/resource/mapper/ResourceMapper.groovy index 8c40496..73be276 100644 --- a/src/groovy/org/grails/plugin/resource/mapper/ResourceMapper.groovy +++ b/src/groovy/org/grails/plugin/resource/mapper/ResourceMapper.groovy @@ -1,11 +1,12 @@ package org.grails.plugin.resource.mapper -import org.codehaus.groovy.grails.* -import org.grails.plugin.resources.artefacts.ResourceMapperArtefactHandler +import grails.util.GrailsNameUtils + import org.grails.plugin.resource.ResourceMeta -import org.springframework.util.AntPathMatcher +import org.grails.plugin.resources.artefacts.ResourceMapperArtefactHandler +import org.slf4j.Logger import org.slf4j.LoggerFactory -import grails.util.GrailsNameUtils +import org.springframework.util.AntPathMatcher /** * The artefact facade used by the service to communicate with resource mapper artefacts. @@ -15,77 +16,55 @@ import grails.util.GrailsNameUtils */ class ResourceMapper { - static final DEFAULT_PRIORITY = 0 - static final PATH_MATCHER = new AntPathMatcher() - + static final int DEFAULT_PRIORITY = 0 + static final AntPathMatcher PATH_MATCHER = new AntPathMatcher() + final artefact final config - final log - + private Logger log + @Lazy phase = { - try { - artefact.phase - } catch (MissingPropertyException e) { - throw new IllegalArgumentException("Resource mapper ${name} must have a phase property defined") + try { artefact.phase } + catch (MissingPropertyException e) { + throw new IllegalArgumentException("Resource mapper $name must have a phase property defined") } }() - + @Lazy operation = { - try { - artefact.operation - } catch (MissingPropertyException e) { - null - } + try { artefact.operation } + catch (MissingPropertyException e) { null } }() - + @Lazy priority = { - try { - artefact.priority - } catch (MissingPropertyException e) { - DEFAULT_PRIORITY - } + try { artefact.priority } + catch (MissingPropertyException e) { DEFAULT_PRIORITY } }() - + @Lazy name = { - try { - artefact.name - } catch (MissingPropertyException e) { - GrailsNameUtils.getLogicalName(artefact.class, ResourceMapperArtefactHandler.SUFFIX).toLowerCase() + try { artefact.name } + catch (MissingPropertyException e) { + GrailsNameUtils.getLogicalName(artefact.getClass(), ResourceMapperArtefactHandler.SUFFIX).toLowerCase() } }() - + @Lazy defaultExcludes = { - try { - toStringList(artefact.defaultExcludes) - } catch (MissingPropertyException e) { - [] - } + try { toStringList(artefact.defaultExcludes) } + catch (MissingPropertyException e) { [] } }() @Lazy defaultIncludes = { - try { - toStringList(artefact.defaultIncludes) - } catch (MissingPropertyException e) { - ['**/*'] - } + try { toStringList(artefact.defaultIncludes) } + catch (MissingPropertyException e) { ['**/*'] } }() @Lazy excludes = { - if (config?.excludes) { - toStringList(config.excludes) - } else { - defaultExcludes - } + config?.excludes ? toStringList(config.excludes) : defaultExcludes }() - + @Lazy includes = { - if (config?.includes) { - toStringList(config.includes) - } else { - defaultIncludes - } + config?.includes ? toStringList(config.includes) : defaultIncludes }() - + /** * @param artefact an instance of the resource mapper artefact * @param mappersConfig the config object that is the config for all mappers @@ -107,68 +86,64 @@ class ResourceMapper { def excludingPattern = getExcludingPattern(resource) if (!includingPattern) { if (log.debugEnabled) { - log.debug "Skipping ${resource.sourceUrl} due to includes pattern ${includes} not including it" + log.debug "Skipping $resource.sourceUrl due to includes pattern $includes not including it" } return false } - + if (excludingPattern) { if (log.debugEnabled) { - log.debug "Skipping ${resource.sourceUrl} due to excludes pattern ${excludes}" + log.debug "Skipping $resource.sourceUrl due to excludes pattern $excludes" } - return false - } else if (resource.excludesMapperOrOperation(name, operation)) { + } + + if (resource.excludesMapperOrOperation(name, operation)) { if (log.debugEnabled) { - log.debug "Skipping ${resource.sourceUrl} due to definition excluding mapper" + log.debug "Skipping $resource.sourceUrl due to definition excluding mapper" } - return false - } else { - invoke(resource) - return true } + + invoke(resource) + true } - + private invoke(ResourceMeta resource) { if (log.debugEnabled) { log.debug "Beginning mapping ${resource.dump()}" } - + try { artefact.map(resource, config) } catch (MissingMethodException e) { - if (artefact.class == e.type && e.method == "map") { + if (artefact.getClass() == e.type && e.method == "map") { throw new Exception("The resource mapper '$name' does not implement the appropriate map method") - } else { - throw e } - + throw e } - + if (log.debugEnabled) { log.debug "Done mapping ${resource.dump()}" } } - String stripLeadingSlash(s) { + String stripLeadingSlash(String s) { s.startsWith("/") ? s.substring(1) : s } String getExcludingPattern(ResourceMeta resource) { // The path matcher won't match **/* against a path starting with /, so it makes sense to remove it. - def sourceUrl = stripLeadingSlash(resource.sourceUrl) - + String sourceUrl = stripLeadingSlash(resource.sourceUrl) excludes.find { PATH_MATCHER.match(it, sourceUrl) } } - - String getIncludingPattern(ResourceMeta resource) { - def sourceUrl = stripLeadingSlash(resource.sourceUrl) + String getIncludingPattern(ResourceMeta resource) { + String sourceUrl = stripLeadingSlash(resource.sourceUrl) includes.find { PATH_MATCHER.match(it, sourceUrl) } } - + private toStringList(value) { value instanceof Collection ? value*.toString() : value.toString() } -} \ No newline at end of file +} diff --git a/src/groovy/org/grails/plugin/resource/mapper/ResourceMappersFactory.groovy b/src/groovy/org/grails/plugin/resource/mapper/ResourceMappersFactory.groovy index 252e6ee..0d63185 100644 --- a/src/groovy/org/grails/plugin/resource/mapper/ResourceMappersFactory.groovy +++ b/src/groovy/org/grails/plugin/resource/mapper/ResourceMappersFactory.groovy @@ -1,8 +1,11 @@ package org.grails.plugin.resource.mapper import grails.spring.BeanBuilder -import org.grails.plugin.resources.artefacts.ResourceMapperClass + import java.lang.reflect.Modifier + +import org.grails.plugin.resources.artefacts.ResourceMapperClass +import org.slf4j.Logger import org.slf4j.LoggerFactory /** @@ -13,61 +16,60 @@ import org.slf4j.LoggerFactory */ class ResourceMappersFactory { + static Logger log = LoggerFactory.getLogger(this) + static List createResourceMappers(grailsApplication, mappersConfig) { - - def log = LoggerFactory.getLogger('org.grails.plugin.resource.mapper.ResourceMappersFactory') - + // filter out abstracts - def resourceMapperClasses = grailsApplication.resourceMapperClasses.findAll { - !Modifier.isAbstract(it.clazz.modifiers) + List resourceMapperClasses = grailsApplication.resourceMapperClasses.findAll { ResourceMapperClass rm -> + !Modifier.isAbstract(rm.clazz.modifiers) } - + // Create an application context to allow the mapper instances to be autowired def beanNames = [] def bb = new BeanBuilder(grailsApplication.mainContext, grailsApplication.classLoader) bb.beans { - for (resourceMapperClass in resourceMapperClasses) { - def name = resourceMapperClass.fullName - def instanceName = "${name}Instance" - - "$instanceName"(resourceMapperClass.clazz) { - it.autowire = true + for (ResourceMapperClass resourceMapperClass in resourceMapperClasses) { + String name = resourceMapperClass.fullName + String instanceName = "${name}Instance" + + "$instanceName"(resourceMapperClass.clazz) { + it.autowire = true } - - "$name"(ResourceMapper, ref(instanceName), mappersConfig) - + + "$name"(ResourceMapper, ref(instanceName), mappersConfig) + beanNames << name } } - + def ctx = bb.createApplicationContext() - def mapperOrdering = beanNames.collect { ctx.getBean(it) }.sort { ResourceMapper lhs, ResourceMapper rhs -> + List mapperOrdering = beanNames.collect { ctx.getBean(it) }.sort { ResourceMapper lhs, ResourceMapper rhs -> if (lhs == null || rhs == null) { throw new NullPointerException("compareTo() called with a null parameter") } - - def phaseComp = lhs.phase <=> rhs.phase + + int phaseComp = lhs.phase <=> rhs.phase if (phaseComp != 0) { return phaseComp - } else { - // Same phase, compare priorities, fall back to name (arbitrary but makes order reliable with dupe priorities) - return lhs.priority <=> rhs.priority ?: lhs.name <=> rhs.name } + // Same phase, compare priorities, fall back to name (arbitrary but makes order reliable with dupe priorities) + return lhs.priority <=> rhs.priority ?: lhs.name <=> rhs.name } - + def operations = mapperOrdering.operation - mapperOrdering.each { m -> + for (ResourceMapper m in mapperOrdering) { if (m.name in operations) { throw new IllegalArgumentException( "The mapper ${m.name} is not valid because there is an operation with the same name. Please change the name of the mapper.") } } - + // Let's throw people a bone with some nice debug if (log.debugEnabled) { def s = new StringBuilder() def phase - mapperOrdering.each { m -> + for (ResourceMapper m in mapperOrdering) { if (m.phase != phase) { s << "Phase: ${m.phase}\n" phase = m.phase @@ -78,4 +80,4 @@ class ResourceMappersFactory { } return mapperOrdering } -} \ No newline at end of file +} diff --git a/src/groovy/org/grails/plugin/resource/module/ModuleBuilder.groovy b/src/groovy/org/grails/plugin/resource/module/ModuleBuilder.groovy index 6035313..ef5f9f2 100644 --- a/src/groovy/org/grails/plugin/resource/module/ModuleBuilder.groovy +++ b/src/groovy/org/grails/plugin/resource/module/ModuleBuilder.groovy @@ -1,10 +1,11 @@ package org.grails.plugin.resource.module +import org.slf4j.Logger import org.slf4j.LoggerFactory /** * Implements the DSL for a resource as part of a module. - * + * * Is designed to be reused for multiple invocations. The caller is * responsible for clearing the given resources and dependencies between invocations. * @@ -12,40 +13,41 @@ import org.slf4j.LoggerFactory * @author Luke Daley (ld@ldaley.com) */ class ModuleBuilder { - - private final _data - private final log = LoggerFactory.getLogger(this.class.name) - - ModuleBuilder(def data) { - _data = data + private final Map _data + + private final Logger log = LoggerFactory.getLogger(getClass()) + + ModuleBuilder(Map data) { + _data = data } - + void dependsOn(List dependencies) { _data.dependencies.addAll(dependencies) - } + } void dependsOn(String[] dependencies) { - _data.dependencies.addAll(dependencies.toList()) - } - + _data.dependencies.addAll(dependencies as List) + } + void dependsOn(String dependencies) { dependsOn(dependencies.split(',')*.trim()) - } - + } + void defaultBundle(value) { _data.defaultBundle = value - } - + } + Object getResource() { - throw new IllegalArgumentException("You must supply arguments to 'resource' - check that you do not have a line break before your argument list, or add parentheses") + throw new IllegalArgumentException("You must supply arguments to 'resource' - check that you do " + + "not have a line break before your argument list, or add parentheses") } void resource(args) { _data.resources << args } - + def missingMethod(String name, args) { throw new RuntimeException("Method calls such as ${name}($args) not yet supported by the builder!") } -} \ No newline at end of file +} diff --git a/src/groovy/org/grails/plugin/resource/module/ModuleDeclarationsFactory.groovy b/src/groovy/org/grails/plugin/resource/module/ModuleDeclarationsFactory.groovy index f09ebc4..43865f4 100644 --- a/src/groovy/org/grails/plugin/resource/module/ModuleDeclarationsFactory.groovy +++ b/src/groovy/org/grails/plugin/resource/module/ModuleDeclarationsFactory.groovy @@ -1,6 +1,9 @@ package org.grails.plugin.resource.module import grails.util.Environment + +import org.grails.plugin.resources.artefacts.ResourcesClass +import org.slf4j.Logger import org.slf4j.LoggerFactory /** @@ -10,69 +13,65 @@ import org.slf4j.LoggerFactory */ class ModuleDeclarationsFactory { - static private final log = LoggerFactory.getLogger(ModuleDeclarationsFactory.name) - + private static Logger log = LoggerFactory.getLogger(this) + /** - * Gathers all of the module declarations (i.e. DSL closures) present in the application. - * + * Gathers all of the module declarations (i.e. DSL closures) present in the application. + * * The closures are returned ordered by the name of the script class that defined them, except * for the last item being from the application config if a declaration was present there. They are * keyed by the name of the class that defined them. - * + * * Scripts are parsed with environment sensitivity. */ static Map getModuleDeclarations(grailsApplication, String environment = Environment.current.name) { - - def slurper = new ConfigSlurper(environment) + + ConfigSlurper slurper = new ConfigSlurper(environment) // give module DSL closures access to grailsApplication slurper.setBinding([grailsApplication: grailsApplication]) - + // order so we are guaranteed of consistency - def orderedResourceClasses = grailsApplication.resourcesClasses.sort { it.name } + List orderedResourceClasses = grailsApplication.resourcesClasses.sort { it.name } if (log.debugEnabled) { log.debug("resource config order: ${orderedResourceClasses*.clazz*.name}") } - + def moduleDeclarations = [:] - + // gather all the module declarations - orderedResourceClasses.collect { - if (log.debugEnabled) { - log.debug("consuming resources config from $it.clazz.name") + for (ResourcesClass rc in orderedResourceClasses) { + if (log.debugEnabled) { + log.debug("consuming resources config from $rc.clazz.name") } - - def modules = slurper.parse(it.clazz).modules + + def modules = slurper.parse(rc.clazz).modules if (modules instanceof Closure) { - moduleDeclarations[it.clazz.name] = modules + moduleDeclarations[rc.clazz.name] = modules } else { if (modules instanceof ConfigObject) { - log.warn("resources artefact $it.clazz.name does not define any modules") + log.warn("resources artefact $rc.clazz.name does not define any modules") } else { - log.warn("resources artefact $it.clazz.name mapper element is not a Closure") + log.warn("resources artefact $rc.clazz.name mapper element is not a Closure") } } } - - // compact - moduleDeclarations = moduleDeclarations.findAll { it != null} - - moduleDeclarations + + moduleDeclarations.findAll { it != null } } - + static Closure getApplicationConfigDeclarations(grailsApplication, String environment = Environment.current.name) { - // get the modules from app config (last so they take precedence) - def appModuleDeclarations = grailsApplication.config.grails.resources.modules - if (appModuleDeclarations instanceof Closure) { - return appModuleDeclarations - } else { - if (appModuleDeclarations instanceof ConfigObject) { - log.warn("'grails.resources.modules' in config does not define any modules") - } else { - log.warn("'grails.resources.modules' in config is not a Closure") - } - return null - } + // get the modules from app config (last so they take precedence) + def appModuleDeclarations = grailsApplication.config.grails.resources.modules + if (appModuleDeclarations instanceof Closure) { + return appModuleDeclarations + } + + if (appModuleDeclarations instanceof ConfigObject) { + log.warn("'grails.resources.modules' in config does not define any modules") + } else { + log.warn("'grails.resources.modules' in config is not a Closure") + } } -} \ No newline at end of file +} diff --git a/src/groovy/org/grails/plugin/resource/module/ModulesBuilder.groovy b/src/groovy/org/grails/plugin/resource/module/ModulesBuilder.groovy index bc837eb..c6e2571 100644 --- a/src/groovy/org/grails/plugin/resource/module/ModulesBuilder.groovy +++ b/src/groovy/org/grails/plugin/resource/module/ModulesBuilder.groovy @@ -1,10 +1,11 @@ package org.grails.plugin.resource.module +import org.slf4j.Logger import org.slf4j.LoggerFactory /** * Implements the resource modules DSL. - * + * * The caller provides a list at construction that will be populated during * DSL evaluation of maps defining the resource modules. * @@ -12,73 +13,68 @@ import org.slf4j.LoggerFactory * @author Luke Daley (ld@ldaley.com) */ class ModulesBuilder implements GroovyInterceptable { - - private _modules - private _moduleOverrides - private _collatedData - private _moduleBuilder + + private List _modules + private List _moduleOverrides + private Map _collatedData + private ModuleBuilder _moduleBuilder private boolean _strict - + static METHODNAME_OVERRIDES = 'overrides' - - private final log = LoggerFactory.getLogger(this.class.name) - - ModulesBuilder(List modules, strict = false) { + + private final Logger log = LoggerFactory.getLogger(this.class.name) + + ModulesBuilder(List modules, boolean strict = false) { _modules = modules _strict = strict _collatedData = [resources:[], dependencies:[]] _moduleBuilder = new ModuleBuilder(_collatedData) } - + def invokeMethod(String name, args) { - if (args.size() == 1 && args[0] instanceof Closure) { + if (args.size() != 1 || !(args[0] instanceof Closure)) { + throw new IllegalStateException("Only 1 closure argument is accepted (args were: $args)") + } - if (name != METHODNAME_OVERRIDES) { - - if (_strict && _modules.find { m -> m.name == name}) { - throw new IllegalArgumentException("A module called [$name] has already been defined") - } - - // build it - def moduleDefinition = args[0] - moduleDefinition.delegate = _moduleBuilder - moduleDefinition.resolveStrategy = Closure.DELEGATE_FIRST - moduleDefinition() + if (name == METHODNAME_OVERRIDES) { + if (log.debugEnabled) { + log.debug("Processing module overrides") + } + ModulesBuilder nestedBuilder = new ModulesBuilder(_moduleOverrides == null ? [] : _moduleOverrides, false) + Closure moduleDefinition = args[0] + moduleDefinition.delegate = nestedBuilder + moduleDefinition.resolveStrategy = Closure.DELEGATE_FIRST + moduleDefinition() + // Copy these nested decls into separate data for post-processing + _moduleOverrides = nestedBuilder._modules + return + } - def module = [name: name, - resources: _collatedData.resources.clone(), - defaultBundle: _collatedData.defaultBundle, - dependencies: _collatedData.dependencies.clone()] - - if (log.debugEnabled) { - log.debug("Defined module '$module'") - } - - // add it - _modules << module + if (_strict && _modules.find { m -> m.name == name}) { + throw new IllegalArgumentException("A module called [$name] has already been defined") + } - // clear for next - _collatedData.clear() - _collatedData.resources = [] - _collatedData.dependencies = [] - - } else { - - if (log.debugEnabled) { - log.debug("Processing module overrides") - } - def nestedBuilder = new ModulesBuilder(_moduleOverrides == null ? [] : _moduleOverrides, false) - def moduleDefinition = args[0] - moduleDefinition.delegate = nestedBuilder - moduleDefinition.resolveStrategy = Closure.DELEGATE_FIRST - moduleDefinition() - // Copy these nested decls into separate data for post-processing - _moduleOverrides = nestedBuilder._modules - } + // build it + Closure moduleDefinition = args[0] + moduleDefinition.delegate = _moduleBuilder + moduleDefinition.resolveStrategy = Closure.DELEGATE_FIRST + moduleDefinition() - } else { - throw new IllegalStateException("Only 1 closure argument is accepted (args were: $args)") + def module = [ + name: name, + resources: _collatedData.resources.clone(), + defaultBundle: _collatedData.defaultBundle, + dependencies: _collatedData.dependencies.clone()] + if (log.debugEnabled) { + log.debug("Defined module '$module'") } - } -} \ No newline at end of file + // add it + _modules << module + + // clear for next + _collatedData.clear() + _collatedData.resources = [] + _collatedData.dependencies = [] + } +} diff --git a/src/groovy/org/grails/plugin/resource/util/HalfBakedLegacyLinkGenerator.groovy b/src/groovy/org/grails/plugin/resource/util/HalfBakedLegacyLinkGenerator.groovy index 9b435bb..9403cfa 100644 --- a/src/groovy/org/grails/plugin/resource/util/HalfBakedLegacyLinkGenerator.groovy +++ b/src/groovy/org/grails/plugin/resource/util/HalfBakedLegacyLinkGenerator.groovy @@ -1,8 +1,9 @@ package org.grails.plugin.resource.util -import org.codehaus.groovy.grails.commons.ConfigurationHolder import grails.util.Environment +import org.codehaus.groovy.grails.commons.ConfigurationHolder + /** * This is a horrible hack to replicate what we need from g.resource() for pre-Grails 1.4 apps * where in Grails 1.4 we now have the wonderful grailsLinkGenerator bean @@ -10,13 +11,12 @@ import grails.util.Environment * NOTE this is a lame implementation that NEVER adds servletContextPath! */ class HalfBakedLegacyLinkGenerator { - + def pluginManager - + String resource(Map args) { getResourceUrl(args) } - // ********************* EVIL - I HATE INABILITY TO REUSE! *********************** /** @@ -54,8 +54,7 @@ class HalfBakedLegacyLinkGenerator { } else { throw new IllegalArgumentException("Attribute absolute='true' specified but no grails.serverURL set in Config") } - } - else { + } else { // @todo work out how to get servlet context path // For servlets SDK 2.5 you can servletContext.getContextPath() s << '' @@ -65,19 +64,18 @@ class HalfBakedLegacyLinkGenerator { if (args.contextPath) { s << args.contextPath } - + def dir = args['dir'] if (args.plugin) { s << pluginManager.getPluginPath(args.plugin) ?: '' } if (dir) { - s << (dir.startsWith("/") ? dir : "/${dir}") + s << (dir.startsWith("/") ? dir : "/${dir}") } def file = args['file'] if (file) { - s << (file.startsWith("/") || dir?.endsWith('/') ? file : "/${file}") - } + s << (file.startsWith("/") || dir?.endsWith('/') ? file : "/${file}") + } return s.toString() } - -} \ No newline at end of file +} diff --git a/src/groovy/org/grails/plugin/resource/util/ResourceMetaStore.groovy b/src/groovy/org/grails/plugin/resource/util/ResourceMetaStore.groovy index f9dde22..af546ef 100644 --- a/src/groovy/org/grails/plugin/resource/util/ResourceMetaStore.groovy +++ b/src/groovy/org/grails/plugin/resource/util/ResourceMetaStore.groovy @@ -1,11 +1,11 @@ package org.grails.plugin.resource.util -import org.apache.commons.logging.LogFactory - import java.util.concurrent.ConcurrentHashMap import java.util.concurrent.CountDownLatch import org.grails.plugin.resource.ResourceMeta +import org.slf4j.Logger +import org.slf4j.LoggerFactory /** * A special URI -> ResourceMeta store that is non-reentrant and will create @@ -15,13 +15,13 @@ import org.grails.plugin.resource.ResourceMeta * @author Marc Palmer (marc@grailsrocks.com) */ class ResourceMetaStore { - def log = LogFactory.getLog(this.class) + private Logger log = LoggerFactory.getLogger(getClass()) Map latches = new ConcurrentHashMap() Map resourcesByURI = new ConcurrentHashMap() - - static CLOSED_LATCH = new CountDownLatch(0) - + + static final CLOSED_LATCH = new CountDownLatch(0) + /** * Note that this is not re-entrant safe, and is only to be called at app startup, before requests come in */ @@ -30,7 +30,7 @@ class ResourceMetaStore { if (log.debugEnabled) { log.debug "Adding declared resource ${resource}" } - + // It may be null if it is not found / broken in some way if (resource) { addResource(resource, false) @@ -45,8 +45,8 @@ class ResourceMetaStore { resourcesByURI.remove(uri) latches.remove(uri) } - - private addResource(resource, boolean adHocResource = false) { + + private addResource(ResourceMeta resource, boolean adHocResource = false) { def uris = new HashSet() // Add the actual linking URL to the cache so resourceLink resolves @@ -62,8 +62,8 @@ class ResourceMetaStore { // As the original URL is used, we need this to resolve to the actualUrl for redirect uris << resource.sourceUrl resource = resource.delegating ? resource.delegate : resource - - uris.each { u -> + + for (u in uris) { if (log.debugEnabled) { log.debug "Storing mapping for resource URI $u to ${resource}" } @@ -71,8 +71,8 @@ class ResourceMetaStore { latches[u] = CLOSED_LATCH // so that future calls for alternative URLs succeed } } - - /** + + /** * A threadsafe synchronous method to get an existing resource or create an ad-hoc resource */ ResourceMeta getOrCreateAdHocResource(String uri, Closure resourceCreator) { @@ -80,17 +80,17 @@ class ResourceMetaStore { log.debug "getOrCreateAdHocResource for ${uri}" } - def latch = latches.get(uri) + CountDownLatch latch = latches.get(uri) if (latch == null) { if (log.debugEnabled) { log.debug "getOrCreateAdHocResource for ${uri}, latch is null" } - def thisLatch = new CountDownLatch(1) - def otherLatch = latches.putIfAbsent(uri, thisLatch) + CountDownLatch thisLatch = new CountDownLatch(1) + CountDownLatch otherLatch = latches.putIfAbsent(uri, thisLatch) if (otherLatch == null) { // process resource - def resource + ResourceMeta resource try { if (log.debugEnabled) { log.debug "getOrCreateAdHocResource for ${uri}, creating resource as not found" @@ -99,7 +99,8 @@ class ResourceMetaStore { if (log.debugEnabled) { log.debug "Creating resource for URI $uri returned ${resource}" } - } catch (Throwable t) { + } + catch (Throwable t) { thisLatch.countDown() // reset this in case anyone else has reference to it latches.remove(uri) // Ditch the latch, so that next attempt will try again in case we are mid-reload/init if (t instanceof FileNotFoundException) { @@ -113,34 +114,33 @@ class ResourceMetaStore { if (resource) { addResource(resource, true) } - + // indicate that we are done thisLatch.countDown() - return resource - } else { - if (log.debugEnabled) { - log.debug "getOrCreateAdHocResource for ${uri}, waiting for latch, another thread has crept in and is creating resource" - } - otherLatch.await() - return resourcesByURI[uri] - } - } else { - if (log.debugEnabled) { - log.debug "getOrCreateAdHocResource for ${uri}, waiting for latch, another thread is creating resource..." + return resource } - latch.await() if (log.debugEnabled) { - log.debug "getOrCreateAdHocResource for ${uri}, done waiting for latch, another thread created resource already" + log.debug "getOrCreateAdHocResource for ${uri}, waiting for latch, another thread has crept in and is creating resource" } + otherLatch.await() return resourcesByURI[uri] } + + if (log.debugEnabled) { + log.debug "getOrCreateAdHocResource for ${uri}, waiting for latch, another thread is creating resource..." + } + latch.await() + if (log.debugEnabled) { + log.debug "getOrCreateAdHocResource for ${uri}, done waiting for latch, another thread created resource already" + } + return resourcesByURI[uri] } - + def keySet() { resourcesByURI.keySet() } - + def getAt(String key) { resourcesByURI[key] } -} \ No newline at end of file +} diff --git a/src/java/org/grails/plugin/resources/artefacts/AbstractResourcesArtefactHandler.java b/src/java/org/grails/plugin/resources/artefacts/AbstractResourcesArtefactHandler.java index a56dc61..76fb434 100644 --- a/src/java/org/grails/plugin/resources/artefacts/AbstractResourcesArtefactHandler.java +++ b/src/java/org/grails/plugin/resources/artefacts/AbstractResourcesArtefactHandler.java @@ -5,14 +5,14 @@ /** * @author Luke Daley (ld@ldaley.com) */ -abstract public class AbstractResourcesArtefactHandler extends ArtefactHandlerAdapter { - +public abstract class AbstractResourcesArtefactHandler extends ArtefactHandlerAdapter { + public AbstractResourcesArtefactHandler(String type, Class grailsClassType, Class grailsClassImpl, String artefactSuffix) { super(type, grailsClassType, grailsClassImpl, artefactSuffix, true); } - + @Override public String getPluginName() { return "resources"; } -} \ No newline at end of file +} diff --git a/src/java/org/grails/plugin/resources/artefacts/DefaultResourceMapperClass.java b/src/java/org/grails/plugin/resources/artefacts/DefaultResourceMapperClass.java index 32b7132..7d86e7d 100644 --- a/src/java/org/grails/plugin/resources/artefacts/DefaultResourceMapperClass.java +++ b/src/java/org/grails/plugin/resources/artefacts/DefaultResourceMapperClass.java @@ -1,13 +1,12 @@ package org.grails.plugin.resources.artefacts; -import org.codehaus.groovy.grails.commons.*; -import groovy.lang.Closure; +import org.codehaus.groovy.grails.commons.AbstractGrailsClass; /** * @author Luke Daley (ld@ldaley.com) */ public class DefaultResourceMapperClass extends AbstractGrailsClass implements ResourceMapperClass { - public DefaultResourceMapperClass(Class clazz) { + public DefaultResourceMapperClass(Class clazz) { super(clazz, ResourceMapperArtefactHandler.SUFFIX); } } diff --git a/src/java/org/grails/plugin/resources/artefacts/DefaultResourcesClass.java b/src/java/org/grails/plugin/resources/artefacts/DefaultResourcesClass.java index 381a9e5..895a03a 100644 --- a/src/java/org/grails/plugin/resources/artefacts/DefaultResourcesClass.java +++ b/src/java/org/grails/plugin/resources/artefacts/DefaultResourcesClass.java @@ -1,12 +1,12 @@ package org.grails.plugin.resources.artefacts; -import org.codehaus.groovy.grails.commons.*; +import org.codehaus.groovy.grails.commons.AbstractGrailsClass; /** * @author Luke Daley (ld@ldaley.com) */ public class DefaultResourcesClass extends AbstractGrailsClass implements ResourcesClass { - public DefaultResourcesClass(Class clazz) { + public DefaultResourcesClass(Class clazz) { super(clazz, ResourcesArtefactHandler.SUFFIX); } -} \ No newline at end of file +} diff --git a/src/java/org/grails/plugin/resources/artefacts/ResourceMapperArtefactHandler.java b/src/java/org/grails/plugin/resources/artefacts/ResourceMapperArtefactHandler.java index 4f50e2c..6c7623c 100644 --- a/src/java/org/grails/plugin/resources/artefacts/ResourceMapperArtefactHandler.java +++ b/src/java/org/grails/plugin/resources/artefacts/ResourceMapperArtefactHandler.java @@ -1,17 +1,14 @@ package org.grails.plugin.resources.artefacts; -import org.codehaus.groovy.grails.commons.*; - /** * @author Luke Daley (ld@ldaley.com) */ public class ResourceMapperArtefactHandler extends AbstractResourcesArtefactHandler { - static public final String TYPE = "ResourceMapper"; - static public final String SUFFIX = "ResourceMapper"; - + public static final String TYPE = "ResourceMapper"; + public static final String SUFFIX = "ResourceMapper"; + public ResourceMapperArtefactHandler() { super(TYPE, ResourceMapperClass.class, DefaultResourceMapperClass.class, SUFFIX); } - -} \ No newline at end of file +} diff --git a/src/java/org/grails/plugin/resources/artefacts/ResourcesArtefactHandler.java b/src/java/org/grails/plugin/resources/artefacts/ResourcesArtefactHandler.java index 5ce5124..a8affa0 100644 --- a/src/java/org/grails/plugin/resources/artefacts/ResourcesArtefactHandler.java +++ b/src/java/org/grails/plugin/resources/artefacts/ResourcesArtefactHandler.java @@ -1,17 +1,14 @@ package org.grails.plugin.resources.artefacts; -import org.codehaus.groovy.grails.commons.*; - /** * @author Luke Daley (ld@ldaley.com) */ public class ResourcesArtefactHandler extends AbstractResourcesArtefactHandler { - static public final String TYPE = "Resources"; - static public final String SUFFIX = "Resources"; - + public static final String TYPE = "Resources"; + public static final String SUFFIX = "Resources"; + public ResourcesArtefactHandler() { super(TYPE, ResourcesClass.class, DefaultResourcesClass.class, SUFFIX); } - -} \ No newline at end of file +} diff --git a/test/integration/org/grails/plugin/resource/ResourceProcessorIntegTests.groovy b/test/integration/org/grails/plugin/resource/ResourceProcessorIntegTests.groovy index 9aa4a87..9e56515 100644 --- a/test/integration/org/grails/plugin/resource/ResourceProcessorIntegTests.groovy +++ b/test/integration/org/grails/plugin/resource/ResourceProcessorIntegTests.groovy @@ -1,15 +1,11 @@ package org.grails.plugin.resource class ResourceProcessorIntegTests extends GroovyTestCase { - + def grailsResourceProcessor - + protected makeMockResource(uri) { - [ - uri:uri, - disposition:'head', - exists: { -> true } - ] + [uri:uri, disposition:'head', exists: { -> true }] } def testGettingModulesInDependencyOrder() { @@ -28,7 +24,7 @@ class ResourceProcessorIntegTests extends GroovyTestCase { grailsResourceProcessor.modulesByName.putAll(testModules) grailsResourceProcessor.updateDependencyOrder() - + def moduleNames = grailsResourceProcessor.getAllModuleNamesRequired(modsNeeded) println "Module names: ${moduleNames}" def moduleNameResults = grailsResourceProcessor.getModulesInDependencyOrder(moduleNames) diff --git a/test/integration/org/grails/plugin/resource/ResourceTagLibIntegTests.groovy b/test/integration/org/grails/plugin/resource/ResourceTagLibIntegTests.groovy index aa3d89c..8102718 100644 --- a/test/integration/org/grails/plugin/resource/ResourceTagLibIntegTests.groovy +++ b/test/integration/org/grails/plugin/resource/ResourceTagLibIntegTests.groovy @@ -3,15 +3,11 @@ package org.grails.plugin.resource import grails.test.GroovyPagesTestCase class ResourceTagLibIntegTests extends GroovyPagesTestCase { - + def grailsResourceProcessor - + protected makeMockResource(uri) { - [ - uri:uri, - disposition:'head', - exists: { -> true } - ] + [uri:uri, disposition:'head', exists: { -> true }] } def testExternalWithAbsoluteURI() { @@ -43,35 +39,35 @@ class ResourceTagLibIntegTests extends GroovyPagesTestCase { def result = applyTemplate('', [:]) assertTrue result.indexOf('/static/js/_adhoc.js') != -1 } - - def testGoogleFontsWithQueriesInModule() { - def template = ''' - - - - - -

Hi

- - ''' - def result = applyTemplate(template, [:]) - def expectedLink = ' - - - - - -

Hi

- - - ''' - def result = applyTemplate(template, [:]) - def expectedScript = '