├── application.properties ├── grails-app └── conf │ └── BuildConfig.groovy ├── .gitignore ├── .project ├── .classpath ├── src └── groovy │ └── com │ └── netflix │ └── grails │ └── contextParam │ └── ContextParam.groovy ├── README.md └── ContextParamGrailsPlugin.groovy /application.properties: -------------------------------------------------------------------------------- 1 | app.grails.version=2.0.4 2 | -------------------------------------------------------------------------------- /grails-app/conf/BuildConfig.groovy: -------------------------------------------------------------------------------- 1 | grails.project.work.dir = 'target' 2 | grails.project.source.level = 1.6 3 | 4 | grails.project.dependency.resolution = { 5 | 6 | inherits 'global' 7 | log 'warn' 8 | 9 | repositories { 10 | grailsCentral() 11 | mavenLocal() 12 | mavenCentral() 13 | } 14 | 15 | dependencies {} 16 | 17 | plugins { 18 | build(':release:2.0.3', ':rest-client-builder:1.0.2') { 19 | export = false 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # .gitignore for Grails 1.2 and 1.3 2 | 3 | # web application files 4 | /web-app/WEB-INF 5 | 6 | # IDE support files 7 | /.launch 8 | /.settings 9 | /*.launch 10 | /*.tmproj 11 | /ivy* 12 | /eclipse 13 | *.iws 14 | 15 | # default HSQL database files for production mode 16 | /prodDb.* 17 | 18 | # general HSQL database files 19 | *Db.properties 20 | *Db.script 21 | 22 | # logs 23 | *.log 24 | /test/reports 25 | /logs 26 | 27 | # plugin release file and XML metadata 28 | /*.zip 29 | /plugin.xml 30 | 31 | # older plugin install locations 32 | /web-app/plugins 33 | /web-app/WEB-INF/classes 34 | 35 | # "temporary" build files 36 | /target 37 | -------------------------------------------------------------------------------- /.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | grails-context-param 4 | 5 | 6 | 7 | 8 | 9 | org.eclipse.wst.common.project.facet.core.builder 10 | 11 | 12 | 13 | 14 | org.eclipse.jdt.core.javabuilder 15 | 16 | 17 | 18 | 19 | 20 | com.springsource.sts.grails.core.nature 21 | org.eclipse.jdt.groovy.core.groovyNature 22 | org.eclipse.jdt.core.javanature 23 | org.eclipse.wst.common.project.facet.core.nature 24 | 25 | 26 | 27 | .link_to_grails_plugins 28 | 2 29 | GRAILS_ROOT/grails-context-param/target/plugins 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /.classpath: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /src/groovy/com/netflix/grails/contextParam/ContextParam.groovy: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012 Netflix, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.netflix.grails.contextParam 18 | 19 | import java.lang.annotation.ElementType 20 | import java.lang.annotation.Retention 21 | import java.lang.annotation.RetentionPolicy 22 | import java.lang.annotation.Target 23 | 24 | /** 25 | * The value of this annotation indicates a parameter name that will be automatically sent along to any redirect or 26 | * chain calls to this controller. 27 | */ 28 | @Retention(RetentionPolicy.RUNTIME) 29 | @Target(ElementType.TYPE) 30 | @interface ContextParam { 31 | String value() 32 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Grails context param plugin 2 | 3 | In some cases, you may want to ensure a specific context is always passed along to a Grails controllers. For example, you may want to make sure a RESTful url always includes a region for certain controllers. Your URLMappings.groovy will have an entry like: 4 | 5 | `/$region/$controller/$action?/$id?` 6 | 7 | This requires passing the region parameter through every request if you want avoid keeping that state in the session. In practice, this means that all of the redirect and chain blocks will have to repeat appending the same parameters to every call. Your code will have lines like these scattered all over: 8 | 9 | `redirect(action: show, params: [region: region])` 10 | 11 | or 12 | 13 | `chain(action: create, model: [cmd: cmd], params: [region: region])` 14 | 15 | This plugin allows you to annotate controllers with `@ContextParam` annotations to specify these parameters and automatically include them on any redirect or chain calls. 16 | 17 | For example: 18 | ``` 19 | @ContextParam('region') 20 | class InstanceController { 21 | ... 22 | ``` 23 | will append the `region` param from the current request if `redirect(controller: 'instance')` is called, eliminating the need to append the params to the call. 24 | 25 | In addition, the configuration of context param declarations is available on the `grailsApplication` object in the field `Map> controllerNamesToContextParams`. To check if region is a `@ContextParam` for a particular controller you can call: 26 | 27 | `grailsApplication.controllerNamesToContextParams[(controllerName)].contains('region')` 28 | 29 | This plugin will be used by [Netflix's Asgard project](https://github.com/Netflix/asgard) for it's upgrade to Grails 2.1. -------------------------------------------------------------------------------- /ContextParamGrailsPlugin.groovy: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012 Netflix, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | import com.netflix.grails.contextParam.ContextParam 17 | import org.codehaus.groovy.grails.commons.ControllerArtefactHandler 18 | import org.codehaus.groovy.grails.commons.GrailsControllerClass 19 | import org.springframework.web.context.request.RequestContextHolder 20 | 21 | class ContextParamGrailsPlugin { 22 | 23 | def observe = ["controllers"] 24 | 25 | def version = "1.0" 26 | def grailsVersion = "1.3.7 > *" 27 | def loadAfter = ['controllers'] 28 | 29 | def author = "Jason Gritman" 30 | def authorEmail = "jgritman@netflix.com" 31 | def title = "Context param plugin" 32 | def description = 'Automatically adds parameters specified as @ContextParam on a controller to redirect calls.' 33 | def documentation = "https://github.com/Netflix-Skunkworks/grails-context-param#grails-context-param-plugin" 34 | 35 | // License: one of 'APACHE', 'GPL2', 'GPL3' 36 | def license = 'APACHE' 37 | 38 | // Details of company behind the plugin (if there is one) 39 | // def organization = [ name: "My Company", url: "http://www.my-company.com/" ] 40 | 41 | // Any additional developers beyond the author specified above. 42 | // def developers = [ [ name: "Joe Bloggs", email: "joe@bloggs.net" ]] 43 | 44 | // Location of the plugin's issue tracker. 45 | // def issueManagement = [ system: "JIRA", url: "http://jira.grails.org/browse/GPMYPLUGIN" ] 46 | 47 | def scm = [url: 'https://github.com/Netflix-Skunkworks/grails-context-param'] 48 | 49 | def doWithDynamicMethods = { ctx -> 50 | Map> controllerNamesToContextParams = [:] 51 | for (GrailsControllerClass controllerClass in application.controllerClasses) { 52 | Collection contextParams = findContextParams(controllerClass) 53 | replaceRedirectMethod(controllerClass, contextParams) 54 | controllerNamesToContextParams[(controllerClass.logicalPropertyName)] = contextParams 55 | } 56 | application.metaClass.getControllerNamesToContextParams = { -> 57 | controllerNamesToContextParams 58 | } 59 | } 60 | 61 | def onChange = { event -> 62 | // Replace the redirect/chain methods again if the controller has changed 63 | if(event.source instanceof Class && 64 | application.isArtefactOfType(ControllerArtefactHandler.TYPE, event.source)) { 65 | GrailsControllerClass controllerClass = application.getArtefact(ControllerArtefactHandler.TYPE, 66 | event.source.name) 67 | Collection contextParams = findContextParams(controllerClass) 68 | replaceRedirectMethod(controllerClass, contextParams) 69 | application.controllerNamesToContextParams[(controllerClass.logicalPropertyName)] = contextParams 70 | } 71 | } 72 | 73 | /** 74 | * Wraps the chain and redirect methods. 75 | */ 76 | private void replaceRedirectMethod(GrailsControllerClass controllerClass, Collection contextParams) { 77 | wrapMethod(controllerClass, contextParams, 'redirect') 78 | wrapMethod(controllerClass, contextParams, 'chain') 79 | } 80 | 81 | /** 82 | * Take a method by name, and add a call to appendContextParams before that method invokation. 83 | */ 84 | private void wrapMethod(GrailsControllerClass controllerClass, Collection contextParams, String name) { 85 | def oldMethod = controllerClass.metaClass.pickMethod(name, [Map] as Class[]) 86 | controllerClass.metaClass."${name}" = { Map args -> 87 | appendContextParams(contextParams, args) 88 | oldMethod.invoke(delegate, args) 89 | } 90 | } 91 | 92 | /** 93 | * Add any specified context params to the params on the arguments. If there is no params object it will be created. 94 | */ 95 | private void appendContextParams(Collection contextParams, Map arguments) { 96 | if (arguments && contextParams ) { 97 | // If redirect was called without a params map object then make an empty params map. 98 | Map params = arguments.params ?: [:] 99 | def request = RequestContextHolder.currentRequestAttributes() 100 | contextParams.each { String contextParam -> 101 | if (!params[(contextParam)]) { 102 | params[(contextParam)] = request.params[(contextParam)] 103 | } 104 | } 105 | arguments.params = params 106 | } 107 | } 108 | 109 | /** 110 | * Find all of the @ContextParam annotions on the controller. 111 | */ 112 | private Collection findContextParams(GrailsControllerClass controllerClass) { 113 | controllerClass.clazz.declaredAnnotations.findAll { it instanceof ContextParam }.collect { it.value() } 114 | } 115 | } 116 | --------------------------------------------------------------------------------