Commits

Jason Stonebraker  committed c10a801 Merge
  • Participants
  • Parent commits e44a885, f9fb69b

Comments (0)

Files changed (18)

 stacktrace.log
 target
 /plugins
-/web-app/plugins
-/web-app/WEB-INF/classes
-web-app/WEB-INF/tld/c.tld
-web-app/WEB-INF/tld/fmt.tld
+/web-app
+plugin.xml
+*.zip

File JSONExclusionMarshallerGrailsPlugin.groovy

-class JSONExclusionMarshallerGrailsPlugin {
-    // the plugin version
-    def version = "0.2"
-    // the version or versions of Grails the plugin is designed for
-    def grailsVersion = "2.2 > *"
-    // resources that are excluded from plugin packaging
-    def pluginExcludes = [
-        "grails-app/views/error.gsp"
-    ]
-
-    // TODO Fill in these fields
-    def title = "JSON Exclusion Marshaller Plugin" // Headline display name of the plugin
-    def author = "Jason Stonebraker"
-    def authorEmail = "jasonstonebraker@gmail.com"
-    def description = '''\
-A Grails plugin that adds a convenience method to the Grails JSON converter for excluding properties from JSON output. Example use: excludeForPublicAPI(MyDomainClass, ['id', 'class']).
-'''
-
-    // URL to the plugin's documentation
-    def documentation = "http://grails.org/plugin/json-exclusion-marshaller"
-
-    // Extra (optional) plugin metadata
-
-    // License: one of 'APACHE', 'GPL2', 'GPL3'
-//    def license = "APACHE"
-
-    // Details of company behind the plugin (if there is one)
-//    def organization = [ name: "My Company", url: "http://www.my-company.com/" ]
-
-    // Any additional developers beyond the author specified above.
-//    def developers = [ [ name: "Joe Bloggs", email: "joe@bloggs.net" ]]
-
-    // Location of the plugin's issue tracker.
-   def issueManagement = [ system: "Bitbucket", url: "https://bitbucket.org/stonebraker/jsonexclusionmarshaller/issues" ]
-
-    // Online location of the plugin's browseable source code.
-   def scm = [ url: "https://bitbucket.org/stonebraker/jsonexclusionmarshaller/src" ]
-
-    def doWithWebDescriptor = { xml ->
-        // TODO Implement additions to web.xml (optional), this event occurs before
-    }
-
-    def doWithSpring = {
-        // TODO Implement runtime spring config (optional)
-    }
-
-    def doWithDynamicMethods = { ctx ->
-        // TODO Implement registering dynamic methods to classes (optional)
-        // println "Do with dynamic methods called..."
-
-        grails.converters.JSON.metaClass.'static'.methodMissing = { String name, args ->
-            def jsonConverter = delegate
-            def isAnExclusionaryMethod = name.startsWith('excludeFor')
-
-            if (isAnExclusionaryMethod) {
-                def impl = { Object[] vargs ->
-                    def configName = name // The name of the missing method called
-                    def type = vargs[0]
-                    def propertiesToExclude = vargs[1]
-
-                    jsonConverter.createNamedConfig(configName) {
-                        it.registerObjectMarshaller(type) {
-                            def obj = it, // Object being marshalled
-                                gdc = new org.codehaus.groovy.grails.commons.DefaultGrailsDomainClass(type),
-                                map = [:]
-
-                            map["${gdc.identifier.name}"] = obj."${gdc.identifier.name}"
-                            map['class'] = obj.getClass().name
-
-                            gdc.persistentProperties.each { property ->
-                                map["${property.name}"] = obj."${property.name}"
-                            }
-
-                            propertiesToExclude.each { property -> map.remove(property) }
-
-                            return map
-                        }
-                    }
-                }
-
-                grails.converters.JSON.metaClass."$name" = impl //future calls will use this
-
-                return impl(args)
-            } else {
-                throw new MissingMethodException(name, grails.converters.JSON.class, args)
-            }
-        }
-
-    }
-
-    def doWithApplicationContext = { applicationContext ->
-        // TODO Implement post initialization spring config (optional)
-    }
-
-    def onChange = { event ->
-        // TODO Implement code that is executed when any artefact that this plugin is
-        // watching is modified and reloaded. The event contains: event.source,
-        // event.application, event.manager, event.ctx, and event.plugin.
-    }
-
-    def onConfigChange = { event ->
-        // TODO Implement code that is executed when the project configuration changes.
-        // The event is the same as for 'onChange'.
-    }
-
-    def onShutdown = { event ->
-        // TODO Implement code that is executed when the application shuts down (optional)
-    }
-}

File JsonExclusionMarshallerGrailsPlugin.groovy

+import grails.converters.JSON
+import org.codehaus.groovy.grails.commons.DefaultGrailsDomainClass
+
+class JsonExclusionMarshallerGrailsPlugin {
+    def version = "0.2"
+    def grailsVersion = "2.0 > *"
+    def pluginExcludes = [
+        "grails-app/domain/**"
+    ]
+
+    def title = "JSON Exclusion Marshaller Plugin"
+    def author = "Jason Stonebraker"
+    def authorEmail = "jasonstonebraker@gmail.com"
+    def description = '''Adds a convenience method to the Grails JSON converter for excluding properties from JSON output. Example use: excludeForPublicAPI(MyDomainClass, ['id', 'class']).'''
+
+    def documentation = "http://grails.org/plugin/json-exclusion-marshaller"
+
+    def license = "APACHE"
+    def issueManagement = [ system: "Bitbucket", url: "https://bitbucket.org/stonebraker/jsonexclusionmarshaller/issues" ]
+    def scm = [ url: "https://bitbucket.org/stonebraker/jsonexclusionmarshaller/src" ]
+
+    def doWithDynamicMethods = { ctx ->
+        // TODO Implement registering dynamic methods to classes (optional)
+        // println "Do with dynamic methods called..."
+
+        JSON.metaClass.static.methodMissing = { String name, args ->
+            def jsonConverter = delegate
+            boolean isAnExclusionaryMethod = name.startsWith('excludeFor')
+
+            if (!isAnExclusionaryMethod) {
+                throw new MissingMethodException(name, JSON, args)
+            }
+
+            def impl = { Object[] vargs ->
+                String configName = name // The name of the missing method called
+                def type = vargs[0]
+                def propertiesToExclude = vargs[1]
+
+                jsonConverter.createNamedConfig(configName) {
+                    it.registerObjectMarshaller(type) {
+                        def obj = it // Object being marshalled
+                        def gdc = new DefaultGrailsDomainClass(type)
+                        def map = [:]
+
+                        map["${gdc.identifier.name}"] = obj."${gdc.identifier.name}"
+                        map['class'] = obj.getClass().name
+
+                        gdc.persistentProperties.each { property ->
+                            map["${property.name}"] = obj."${property.name}"
+                        }
+
+                        propertiesToExclude.each { property -> map.remove(property) }
+
+                        return map
+                    }
+                }
+            }
+
+            JSON.metaClass."$name" = impl //future calls will use this
+
+            return impl(args)
+        }
+    }
+}

File application.properties

-#Grails Metadata file
-#Fri Jun 28 17:59:51 EDT 2013
 app.grails.version=2.2.2
-app.name=JSONExclusionMarshaller

File grails-app/conf/BuildConfig.groovy

-grails.project.class.dir = "target/classes"
-grails.project.test.class.dir = "target/test-classes"
-grails.project.test.reports.dir = "target/test-reports"
+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'
-    legacyResolve false // whether to do a secondary resolve on plugin installation, not advised and here for backwards compatibility
-    repositories {
-        grailsCentral()
-        mavenCentral()
-        // uncomment the below to enable remote dependency resolution
-        // from public Maven repositories
-        //mavenLocal()
-        //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 {
-        // specify dependencies here under either 'build', 'compile', 'runtime', 'test' or 'provided' scopes eg.
 
-        // runtime 'mysql:mysql-connector-java:5.1.21'
-    }
+	inherits 'global'
+	log 'warn'
 
-    plugins {
-        build(":tomcat:$grailsVersion",
-              ":release:2.2.1",
-              ":rest-client-builder:1.0.3") {
-            export = false
-        }
-    }
+	repositories {
+		grailsCentral()
+	}
+
+	plugins {
+		build ':release:2.2.1', ':rest-client-builder:1.0.3', {
+			export = false
+		}
+	}
 }

File grails-app/conf/Config.groovy

-// configuration for plugin testing - will not be included in the plugin zip
-
 log4j = {
-    // Example of changing the log pattern for the default console
-    // appender:
-    //
-    //appenders {
-    //    console name:'stdout', layout:pattern(conversionPattern: '%c{2} %m%n')
-    //}
-
-    error  'org.codehaus.groovy.grails.web.servlet',  //  controllers
-           'org.codehaus.groovy.grails.web.pages', //  GSP
-           'org.codehaus.groovy.grails.web.sitemesh', //  layouts
-           'org.codehaus.groovy.grails.web.mapping.filter', // URL mapping
-           'org.codehaus.groovy.grails.web.mapping', // URL mapping
-           'org.codehaus.groovy.grails.commons', // core / classloading
-           'org.codehaus.groovy.grails.plugins', // plugins
-           'org.codehaus.groovy.grails.orm.hibernate', // hibernate integration
-           'org.springframework',
-           'org.hibernate',
-           'net.sf.ehcache.hibernate'
-
-    warn   'org.mortbay.log'
+	error 'org.codehaus.groovy.grails',
+	      'org.springframework',
+	      'org.hibernate',
+	      'net.sf.ehcache.hibernate'
 }

File grails-app/conf/DataSource.groovy

 dataSource {
-    pooled = true
-    driverClassName = "org.h2.Driver"
-    username = "sa"
-    password = ""
+	pooled = true
+	driverClassName = 'org.h2.Driver'
+	username = 'sa'
+	password = ''
+	dbCreate = 'update'
+	url = 'jdbc:h2:mem:testDb'
 }
+
 hibernate {
-    cache.use_second_level_cache = true
-    cache.use_query_cache = false
-    cache.region.factory_class = 'net.sf.ehcache.hibernate.EhCacheRegionFactory'
-}
-// environment specific settings
-environments {
-    development {
-        dataSource {
-            dbCreate = "create-drop" // one of 'create', 'create-drop', 'update', 'validate', ''
-            url = "jdbc:h2:mem:devDb;MVCC=TRUE;LOCK_TIMEOUT=10000"
-        }
-    }
-    test {
-        dataSource {
-            dbCreate = "update"
-            url = "jdbc:h2:mem:testDb;MVCC=TRUE;LOCK_TIMEOUT=10000"
-        }
-    }
-    production {
-        dataSource {
-            dbCreate = "update"
-            url = "jdbc:h2:prodDb;MVCC=TRUE;LOCK_TIMEOUT=10000"
-            pooled = true
-            properties {
-               maxActive = -1
-               minEvictableIdleTimeMillis=1800000
-               timeBetweenEvictionRunsMillis=1800000
-               numTestsPerEvictionRun=3
-               testOnBorrow=true
-               testWhileIdle=true
-               testOnReturn=true
-               validationQuery="SELECT 1"
-            }
-        }
-    }
+	cache.use_second_level_cache = false
+	cache.use_query_cache = false
+	cache.provider_class = 'org.hibernate.cache.EhCacheProvider'
 }

File grails-app/conf/UrlMappings.groovy

-class UrlMappings {
-
-	static mappings = {
-		"/$controller/$action?/$id?"{
-			constraints {
-				// apply constraints here
-			}
-		}
-
-		"/"(view:"/index")
-		"500"(view:'/error')
-	}
-}

File grails-app/domain/stonebraker/jxm/TestStudent.groovy

 	Number gradePointAverage
 	String studentID
 	String socialSecurityNumber
-
-    static constraints = {
-    }
 }

File grails-app/views/error.gsp

-<!DOCTYPE html>
-<html>
-	<head>
-		<title><g:if env="development">Grails Runtime Exception</g:if><g:else>Error</g:else></title>
-		<meta name="layout" content="main">
-		<g:if env="development"><link rel="stylesheet" href="${resource(dir: 'css', file: 'errors.css')}" type="text/css"></g:if>
-	</head>
-	<body>
-		<g:if env="development">
-			<g:renderException exception="${exception}" />
-		</g:if>
-		<g:else>
-			<ul class="errors">
-				<li>An error has occurred</li>
-			</ul>
-		</g:else>
-	</body>
-</html>

File scripts/_Install.groovy

-//
-// This script is executed by Grails after plugin was installed to project.
-// This script is a Gant script so you can use all special variables provided
-// by Gant (such as 'baseDir' which points on project base dir). You can
-// use 'ant' to access a global instance of AntBuilder
-//
-// For example you can create directory under project tree:
-//
-//    ant.mkdir(dir:"${basedir}/grails-app/jobs")
-//

File scripts/_Uninstall.groovy

-//
-// This script is executed by Grails when the plugin is uninstalled from project.
-// Use this script if you intend to do any additional clean-up on uninstall, but
-// beware of messing up SVN directories!
-//

File scripts/_Upgrade.groovy

-//
-// This script is executed by Grails during application upgrade ('grails upgrade'
-// command). This script is a Gant script so you can use all special variables
-// provided by Gant (such as 'baseDir' which points on project base dir). You can
-// use 'ant' to access a global instance of AntBuilder
-//
-// For example you can create directory under project tree:
-//
-//    ant.mkdir(dir:"${basedir}/grails-app/jobs")
-//

File test/integration/stonebraker/jxm/TestStudentTests.groovy

 class TestStudentTests {
     // Logger log = LoggerFactory.getLogger(TestStudentTests)
 
-    @Before
-    void setUp() {
-        // Setup logic here
-    }
-
-    @After
-    void tearDown() {
-        // Tear down logic here
-    }
-
     @Test
     void testCanPrintATestStudentAsJSON() {
         // Given
         // it may warrant some explanation. Though hopefully not a lot.
 
         // In order to exclude class properties from JSON output, we create 'excluders.' Don't go looking for
-        // an 'Excluder' class, you won't find it. That's just what I call ObjectMarshallers that exclude 
+        // an 'Excluder' class, you won't find it. That's just what I call ObjectMarshallers that exclude
         // properties from rendering.
 
         // Excluders are created using: excludeFor*(Class class, List<String> propertiesToExclude)
         // Each excluder needs an excluder name, the target class name, and a list of properties to exclude from that class.
 
         // We'll look at the 'excludeForTeachers' excluder.
-        // Name: "excludeForTeachers" <--- You can use anything you want after "excludeFor", in this case I 
-        //       chose "Teachers" since in my domain, teachers aren't allowed to see a student's social 
+        // Name: "excludeForTeachers" <--- You can use anything you want after "excludeFor", in this case I
+        //       chose "Teachers" since in my domain, teachers aren't allowed to see a student's social
         //       security number. In this way, I'm able to use domain specific language in code.
         // Class: TestStudent <--- The domain class being marshalled
         // List<String>: ['socialSecurityNumber', 'id', 'class'] <--- The properties that will be excluded
         // Usage: Once you've created an excluder, you simply call JSON.use('excludeForWhatever') { ... }
         // ... and of course do stuff within the closure per the example below.
         // As seen here, we can create multiple excluders and use them as needed.
-        
+
         JSON.excludeForTeachers(TestStudent, ['socialSecurityNumber', 'id', 'class'])
         JSON.excludeForOtherStudents(TestStudent, ['gradePointAverage', 'studentID', 'socialSecurityNumber', 'id', 'class'])
         JSON.excludeForTestAdministrators(TestStudent, ['gradePointAverage', 'socialSecurityNumber', 'id', 'class'])
 
 /*
 
-What's actually happening? Basically the excludeFor*() method creates a Named Configuration with a name of 
+What's actually happening? Basically the excludeFor*() method creates a Named Configuration with a name of
 'excludeForWhateverYouDecideToNameIt'. The Named Configuration then registers a custom ObjectMarshaller for the
 TestStudent domain class. The custom object marshaller is built into the excludesFor* sythesized method. It
 removes the properties we provided to it in the list. The JSON converter then outputs the desired JSON formatted string.

File web-app/WEB-INF/applicationContext.xml

-<?xml version="1.0" encoding="UTF-8"?>
-<beans xmlns="http://www.springframework.org/schema/beans"
-       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-       xsi:schemaLocation="
-http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
-
-	<bean id="grailsApplication" class="org.codehaus.groovy.grails.commons.GrailsApplicationFactoryBean">
-		<description>Grails application factory bean</description>
-		<property name="grailsDescriptor" value="/WEB-INF/grails.xml" />
-		<property name="grailsResourceLoader" ref="grailsResourceLoader" />
-	</bean>
-
-	<bean id="pluginManager" class="org.codehaus.groovy.grails.plugins.GrailsPluginManagerFactoryBean">
-		<description>A bean that manages Grails plugins</description>
-		<property name="grailsDescriptor" value="/WEB-INF/grails.xml" />
-		<property name="application" ref="grailsApplication" />
-	</bean>
-
-	<bean id="grailsConfigurator" class="org.codehaus.groovy.grails.commons.spring.GrailsRuntimeConfigurator">
-		<constructor-arg>
-			<ref bean="grailsApplication" />
-		</constructor-arg>
-		<property name="pluginManager" ref="pluginManager" />
-	</bean>
-
-	<bean id="grailsResourceLoader" class="org.codehaus.groovy.grails.commons.GrailsResourceLoaderFactoryBean" />
-
-	<bean id="characterEncodingFilter" class="org.springframework.web.filter.CharacterEncodingFilter">
-		<property name="encoding">
-			<value>utf-8</value>
-		</property>
-	</bean>
-</beans>

File web-app/WEB-INF/sitemesh.xml

-<sitemesh>
-    <page-parsers>
-        <parser content-type="text/html"
-            class="org.codehaus.groovy.grails.web.sitemesh.GrailsHTMLPageParser" />
-        <parser content-type="text/html;charset=ISO-8859-1"
-            class="org.codehaus.groovy.grails.web.sitemesh.GrailsHTMLPageParser" />
-        <parser content-type="text/html;charset=UTF-8"
-            class="org.codehaus.groovy.grails.web.sitemesh.GrailsHTMLPageParser" />
-    </page-parsers>
-
-    <decorator-mappers>
-        <mapper class="org.codehaus.groovy.grails.web.sitemesh.GrailsLayoutDecoratorMapper" />
-    </decorator-mappers>
-</sitemesh>

File web-app/WEB-INF/tld/grails.tld

-<?xml version="1.0" encoding="UTF-8"?>
-<taglib xmlns="http://java.sun.com/xml/ns/j2ee"
-        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-        xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
-            http://java.sun.com/xml/ns/j2ee/web-jsptaglibrary_2_0.xsd"
-        version="2.0">
-    <description>The Grails custom tag library</description>
-    <tlib-version>0.2</tlib-version>
-    <short-name>grails</short-name>
-    <uri>http://grails.codehaus.org/tags</uri>
-
-    <tag>
-        <name>link</name>
-        <tag-class>org.codehaus.groovy.grails.web.taglib.jsp.JspLinkTag</tag-class>
-        <body-content>JSP</body-content>
-        <attribute>
-            <name>action</name>
-            <required>false</required>
-            <rtexprvalue>true</rtexprvalue>
-        </attribute>
-        <attribute>
-            <name>controller</name>
-            <required>false</required>
-            <rtexprvalue>true</rtexprvalue>
-        </attribute>
-        <attribute>
-            <name>id</name>
-            <required>false</required>
-            <rtexprvalue>true</rtexprvalue>
-        </attribute>
-        <attribute>
-            <name>url</name>
-            <required>false</required>
-            <rtexprvalue>true</rtexprvalue>
-        </attribute>
-        <attribute>
-            <name>params</name>
-            <required>false</required>
-            <rtexprvalue>true</rtexprvalue>
-        </attribute>
-        <dynamic-attributes>true</dynamic-attributes>
-    </tag>
-    <tag>
-        <name>form</name>
-        <tag-class>org.codehaus.groovy.grails.web.taglib.jsp.JspFormTag</tag-class>
-        <body-content>JSP</body-content>
-        <attribute>
-            <name>action</name>
-            <required>false</required>
-            <rtexprvalue>true</rtexprvalue>
-        </attribute>
-        <attribute>
-            <name>controller</name>
-            <required>false</required>
-            <rtexprvalue>true</rtexprvalue>
-        </attribute>
-        <attribute>
-            <name>id</name>
-            <required>false</required>
-            <rtexprvalue>true</rtexprvalue>
-        </attribute>
-        <attribute>
-            <name>url</name>
-            <required>false</required>
-            <rtexprvalue>true</rtexprvalue>
-        </attribute>
-        <attribute>
-            <name>method</name>
-            <required>true</required>
-            <rtexprvalue>true</rtexprvalue>
-        </attribute>
-        <dynamic-attributes>true</dynamic-attributes>
-    </tag>
-    <tag>
-        <name>select</name>
-        <tag-class>org.codehaus.groovy.grails.web.taglib.jsp.JspSelectTag</tag-class>
-        <body-content>JSP</body-content>
-        <attribute>
-            <name>name</name>
-            <required>true</required>
-            <rtexprvalue>true</rtexprvalue>
-        </attribute>
-        <attribute>
-            <name>value</name>
-            <required>false</required>
-            <rtexprvalue>true</rtexprvalue>
-        </attribute>
-        <attribute>
-            <name>optionKey</name>
-            <required>false</required>
-            <rtexprvalue>true</rtexprvalue>
-        </attribute>
-        <attribute>
-            <name>optionValue</name>
-            <required>false</required>
-            <rtexprvalue>true</rtexprvalue>
-        </attribute>
-        <dynamic-attributes>true</dynamic-attributes>
-    </tag>
-    <tag>
-        <name>datePicker</name>
-        <tag-class>org.codehaus.groovy.grails.web.taglib.jsp.JspDatePickerTag</tag-class>
-        <body-content>empty</body-content>
-        <attribute>
-            <name>name</name>
-            <required>true</required>
-            <rtexprvalue>true</rtexprvalue>
-        </attribute>
-        <attribute>
-            <name>value</name>
-            <required>false</required>
-            <rtexprvalue>true</rtexprvalue>
-        </attribute>
-        <attribute>
-            <name>precision</name>
-            <required>false</required>
-            <rtexprvalue>true</rtexprvalue>
-        </attribute>
-        <dynamic-attributes>false</dynamic-attributes>
-    </tag>
-    <tag>
-        <name>currencySelect</name>
-        <tag-class>org.codehaus.groovy.grails.web.taglib.jsp.JspCurrencySelectTag</tag-class>
-        <body-content>empty</body-content>
-        <attribute>
-            <name>name</name>
-            <required>true</required>
-            <rtexprvalue>true</rtexprvalue>
-        </attribute>
-        <attribute>
-            <name>value</name>
-            <required>false</required>
-            <rtexprvalue>true</rtexprvalue>
-        </attribute>
-        <dynamic-attributes>true</dynamic-attributes>
-    </tag>
-    <tag>
-        <name>localeSelect</name>
-        <tag-class>org.codehaus.groovy.grails.web.taglib.jsp.JspLocaleSelectTag</tag-class>
-        <body-content>empty</body-content>
-        <attribute>
-            <name>name</name>
-            <required>true</required>
-            <rtexprvalue>true</rtexprvalue>
-        </attribute>
-        <attribute>
-            <name>value</name>
-            <required>false</required>
-            <rtexprvalue>true</rtexprvalue>
-        </attribute>
-        <dynamic-attributes>true</dynamic-attributes>
-    </tag>
-    <tag>
-        <name>timeZoneSelect</name>
-        <tag-class>org.codehaus.groovy.grails.web.taglib.jsp.JspTimeZoneSelectTag</tag-class>
-        <body-content>empty</body-content>
-        <attribute>
-            <name>name</name>
-            <required>true</required>
-            <rtexprvalue>true</rtexprvalue>
-        </attribute>
-        <attribute>
-            <name>value</name>
-            <required>false</required>
-            <rtexprvalue>true</rtexprvalue>
-        </attribute>
-        <dynamic-attributes>true</dynamic-attributes>
-    </tag>
-    <tag>
-        <name>checkBox</name>
-        <tag-class>org.codehaus.groovy.grails.web.taglib.jsp.JspCheckboxTag</tag-class>
-        <body-content>empty</body-content>
-        <attribute>
-            <name>name</name>
-            <required>true</required>
-            <rtexprvalue>true</rtexprvalue>
-        </attribute>
-        <attribute>
-            <name>value</name>
-            <required>true</required>
-            <rtexprvalue>true</rtexprvalue>
-        </attribute>
-        <dynamic-attributes>true</dynamic-attributes>
-    </tag>
-    <tag>
-        <name>hasErrors</name>
-        <tag-class>org.codehaus.groovy.grails.web.taglib.jsp.JspHasErrorsTag</tag-class>
-        <body-content>JSP</body-content>
-        <attribute>
-            <name>model</name>
-            <required>false</required>
-            <rtexprvalue>true</rtexprvalue>
-        </attribute>
-        <attribute>
-            <name>bean</name>
-            <required>false</required>
-            <rtexprvalue>true</rtexprvalue>
-        </attribute>
-        <attribute>
-            <name>field</name>
-            <required>false</required>
-            <rtexprvalue>true</rtexprvalue>
-        </attribute>
-        <dynamic-attributes>false</dynamic-attributes>
-    </tag>
-    <tag>
-        <name>eachError</name>
-        <tag-class>org.codehaus.groovy.grails.web.taglib.jsp.JspEachErrorTag</tag-class>
-        <body-content>JSP</body-content>
-        <attribute>
-            <name>model</name>
-            <required>false</required>
-            <rtexprvalue>true</rtexprvalue>
-        </attribute>
-        <attribute>
-            <name>bean</name>
-            <required>false</required>
-            <rtexprvalue>true</rtexprvalue>
-        </attribute>
-        <attribute>
-            <name>field</name>
-            <required>false</required>
-            <rtexprvalue>true</rtexprvalue>
-        </attribute>
-        <dynamic-attributes>false</dynamic-attributes>
-    </tag>
-    <tag>
-        <name>renderErrors</name>
-        <tag-class>org.codehaus.groovy.grails.web.taglib.jsp.JspEachErrorTag</tag-class>
-        <body-content>JSP</body-content>
-        <attribute>
-            <name>model</name>
-            <required>false</required>
-            <rtexprvalue>true</rtexprvalue>
-        </attribute>
-        <attribute>
-            <name>bean</name>
-            <required>false</required>
-            <rtexprvalue>true</rtexprvalue>
-        </attribute>
-        <attribute>
-            <name>field</name>
-            <required>false</required>
-            <rtexprvalue>true</rtexprvalue>
-        </attribute>
-        <attribute>
-            <name>as</name>
-            <required>true</required>
-            <rtexprvalue>true</rtexprvalue>
-        </attribute>
-        <dynamic-attributes>false</dynamic-attributes>
-    </tag>
-    <tag>
-        <name>message</name>
-        <tag-class>org.codehaus.groovy.grails.web.taglib.jsp.JspMessageTag</tag-class>
-        <body-content>JSP</body-content>
-        <attribute>
-            <name>code</name>
-            <required>false</required>
-            <rtexprvalue>true</rtexprvalue>
-        </attribute>
-        <attribute>
-            <name>error</name>
-            <required>false</required>
-            <rtexprvalue>true</rtexprvalue>
-        </attribute>
-        <attribute>
-            <name>default</name>
-            <required>false</required>
-            <rtexprvalue>true</rtexprvalue>
-        </attribute>
-        <dynamic-attributes>false</dynamic-attributes>
-    </tag>
-    <tag>
-        <name>remoteFunction</name>
-        <tag-class>org.codehaus.groovy.grails.web.taglib.jsp.JspRemoteFunctionTag</tag-class>
-        <body-content>empty</body-content>
-        <attribute>
-            <name>before</name>
-            <required>false</required>
-            <rtexprvalue>true</rtexprvalue>
-        </attribute>
-        <attribute>
-            <name>after</name>
-            <required>false</required>
-            <rtexprvalue>true</rtexprvalue>
-        </attribute>
-        <attribute>
-            <name>action</name>
-            <required>false</required>
-            <rtexprvalue>true</rtexprvalue>
-        </attribute>
-        <attribute>
-            <name>controller</name>
-            <required>false</required>
-            <rtexprvalue>true</rtexprvalue>
-        </attribute>
-        <attribute>
-            <name>id</name>
-            <required>false</required>
-            <rtexprvalue>true</rtexprvalue>
-        </attribute>
-        <attribute>
-            <name>url</name>
-            <required>false</required>
-            <rtexprvalue>true</rtexprvalue>
-        </attribute>
-        <attribute>
-            <name>params</name>
-            <required>false</required>
-            <rtexprvalue>true</rtexprvalue>
-        </attribute>
-        <attribute>
-            <name>asynchronous</name>
-            <required>false</required>
-            <rtexprvalue>true</rtexprvalue>
-        </attribute>
-        <attribute>
-            <name>method</name>
-            <required>false</required>
-            <rtexprvalue>true</rtexprvalue>
-        </attribute>
-        <attribute>
-            <name>update</name>
-            <required>false</required>
-            <rtexprvalue>true</rtexprvalue>
-        </attribute>
-        <attribute>
-            <name>onSuccess</name>
-            <required>false</required>
-            <rtexprvalue>true</rtexprvalue>
-        </attribute>
-        <attribute>
-            <name>onFailure</name>
-            <required>false</required>
-            <rtexprvalue>true</rtexprvalue>
-        </attribute>
-        <attribute>
-            <name>onComplete</name>
-            <required>false</required>
-            <rtexprvalue>true</rtexprvalue>
-        </attribute>
-        <attribute>
-            <name>onLoading</name>
-            <required>false</required>
-            <rtexprvalue>true</rtexprvalue>
-        </attribute>
-        <attribute>
-            <name>onLoaded</name>
-            <required>false</required>
-            <rtexprvalue>true</rtexprvalue>
-        </attribute>
-        <attribute>
-            <name>onInteractive</name>
-            <required>false</required>
-            <rtexprvalue>true</rtexprvalue>
-        </attribute>
-        <dynamic-attributes>true</dynamic-attributes>
-    </tag>
-    <tag>
-        <name>remoteLink</name>
-        <tag-class>org.codehaus.groovy.grails.web.taglib.jsp.JspRemoteLinkTag</tag-class>
-        <body-content>JSP</body-content>
-        <attribute>
-            <name>before</name>
-            <required>false</required>
-            <rtexprvalue>true</rtexprvalue>
-        </attribute>
-        <attribute>
-            <name>after</name>
-            <required>false</required>
-            <rtexprvalue>true</rtexprvalue>
-        </attribute>
-        <attribute>
-            <name>action</name>
-            <required>false</required>
-            <rtexprvalue>true</rtexprvalue>
-        </attribute>
-        <attribute>
-            <name>controller</name>
-            <required>false</required>
-            <rtexprvalue>true</rtexprvalue>
-        </attribute>
-        <attribute>
-            <name>id</name>
-            <required>false</required>
-            <rtexprvalue>true</rtexprvalue>
-        </attribute>
-        <attribute>
-            <name>url</name>
-            <required>false</required>
-            <rtexprvalue>true</rtexprvalue>
-        </attribute>
-        <attribute>
-            <name>params</name>
-            <required>false</required>
-            <rtexprvalue>true</rtexprvalue>
-        </attribute>
-        <attribute>
-            <name>asynchronous</name>
-            <required>false</required>
-            <rtexprvalue>true</rtexprvalue>
-        </attribute>
-        <attribute>
-            <name>method</name>
-            <required>false</required>
-            <rtexprvalue>true</rtexprvalue>
-        </attribute>
-        <attribute>
-            <name>update</name>
-            <required>false</required>
-            <rtexprvalue>true</rtexprvalue>
-        </attribute>
-        <attribute>
-            <name>onSuccess</name>
-            <required>false</required>
-            <rtexprvalue>true</rtexprvalue>
-        </attribute>
-        <attribute>
-            <name>onFailure</name>
-            <required>false</required>
-            <rtexprvalue>true</rtexprvalue>
-        </attribute>
-        <attribute>
-            <name>onComplete</name>
-            <required>false</required>
-            <rtexprvalue>true</rtexprvalue>
-        </attribute>
-        <attribute>
-            <name>onLoading</name>
-            <required>false</required>
-            <rtexprvalue>true</rtexprvalue>
-        </attribute>
-        <attribute>
-            <name>onLoaded</name>
-            <required>false</required>
-            <rtexprvalue>true</rtexprvalue>
-        </attribute>
-        <attribute>
-            <name>onInteractive</name>
-            <required>false</required>
-            <rtexprvalue>true</rtexprvalue>
-        </attribute>
-        <dynamic-attributes>true</dynamic-attributes>
-    </tag>
-    <tag>
-        <name>formRemote</name>
-        <tag-class>org.codehaus.groovy.grails.web.taglib.jsp.JspFormRemoteTag</tag-class>
-        <body-content>JSP</body-content>
-        <attribute>
-            <name>before</name>
-            <required>false</required>
-            <rtexprvalue>true</rtexprvalue>
-        </attribute>
-        <attribute>
-            <name>after</name>
-            <required>false</required>
-            <rtexprvalue>true</rtexprvalue>
-        </attribute>
-        <attribute>
-            <name>action</name>
-            <required>false</required>
-            <rtexprvalue>true</rtexprvalue>
-        </attribute>
-        <attribute>
-            <name>controller</name>
-            <required>false</required>
-            <rtexprvalue>true</rtexprvalue>
-        </attribute>
-        <attribute>
-            <name>id</name>
-            <required>false</required>
-            <rtexprvalue>true</rtexprvalue>
-        </attribute>
-        <attribute>
-            <name>url</name>
-            <required>false</required>
-            <rtexprvalue>true</rtexprvalue>
-        </attribute>
-        <attribute>
-            <name>params</name>
-            <required>false</required>
-            <rtexprvalue>true</rtexprvalue>
-        </attribute>
-        <attribute>
-            <name>asynchronous</name>
-            <required>false</required>
-            <rtexprvalue>true</rtexprvalue>
-        </attribute>
-        <attribute>
-            <name>method</name>
-            <required>false</required>
-            <rtexprvalue>true</rtexprvalue>
-        </attribute>
-        <attribute>
-            <name>update</name>
-            <required>false</required>
-            <rtexprvalue>true</rtexprvalue>
-        </attribute>
-        <attribute>
-            <name>onSuccess</name>
-            <required>false</required>
-            <rtexprvalue>true</rtexprvalue>
-        </attribute>
-        <attribute>
-            <name>onFailure</name>
-            <required>false</required>
-            <rtexprvalue>true</rtexprvalue>
-        </attribute>
-        <attribute>
-            <name>onComplete</name>
-            <required>false</required>
-            <rtexprvalue>true</rtexprvalue>
-        </attribute>
-        <attribute>
-            <name>onLoading</name>
-            <required>false</required>
-            <rtexprvalue>true</rtexprvalue>
-        </attribute>
-        <attribute>
-            <name>onLoaded</name>
-            <required>false</required>
-            <rtexprvalue>true</rtexprvalue>
-        </attribute>
-        <attribute>
-            <name>onInteractive</name>
-            <required>false</required>
-            <rtexprvalue>true</rtexprvalue>
-        </attribute>
-        <dynamic-attributes>true</dynamic-attributes>
-    </tag>
-    <tag>
-        <name>invokeTag</name>
-        <tag-class>org.codehaus.groovy.grails.web.taglib.jsp.JspInvokeGrailsTagLibTag</tag-class>
-        <body-content>JSP</body-content>
-        <variable>
-            <name-given>it</name-given>
-            <variable-class>java.lang.Object</variable-class>
-            <declare>true</declare>
-            <scope>NESTED</scope>
-        </variable>
-        <attribute>
-            <name>tagName</name>
-            <required>true</required>
-            <rtexprvalue>true</rtexprvalue>
-        </attribute>
-        <dynamic-attributes>true</dynamic-attributes>
-    </tag>
-</taglib>
-

File web-app/WEB-INF/tld/spring.tld

-<?xml version="1.0" encoding="ISO-8859-1" ?>
-<!DOCTYPE taglib PUBLIC "-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.2//EN" "http://java.sun.com/dtd/web-jsptaglibrary_1_2.dtd">
-
-<taglib>
-
-	<tlib-version>1.1.1</tlib-version>
-
-	<jsp-version>1.2</jsp-version>
-
-	<short-name>Spring</short-name>
-
-	<uri>http://www.springframework.org/tags</uri>
-
-	<description>Spring Framework JSP Tag Library. Authors: Rod Johnson, Juergen Hoeller</description>
-
-
-	<tag>
-
-		<name>htmlEscape</name>
-		<tag-class>org.springframework.web.servlet.tags.HtmlEscapeTag</tag-class>
-		<body-content>JSP</body-content>
-
-		<description>
-			Sets default HTML escape value for the current page.
-			Overrides a "defaultHtmlEscape" context-param in web.xml, if any.
-		</description>
-
-		<attribute>
-			<name>defaultHtmlEscape</name>
-			<required>true</required>
-			<rtexprvalue>true</rtexprvalue>
-		</attribute>
-
-	</tag>
-
-
-	<tag>
-
-		<name>escapeBody</name>
-		<tag-class>org.springframework.web.servlet.tags.EscapeBodyTag</tag-class>
-		<body-content>JSP</body-content>
-
-		<description>
-			Escapes its enclosed body content, applying HTML escaping and/or JavaScript escaping.
-			The HTML escaping flag participates in a page-wide or application-wide setting
-			(i.e. by HtmlEscapeTag or a "defaultHtmlEscape" context-param in web.xml).
-		</description>
-
-		<attribute>
-			<name>htmlEscape</name>
-			<required>false</required>
-			<rtexprvalue>true</rtexprvalue>
-		</attribute>
-
-		<attribute>
-			<name>javaScriptEscape</name>
-			<required>false</required>
-			<rtexprvalue>true</rtexprvalue>
-		</attribute>
-
-	</tag>
-
-
-	<tag>
-
-		<name>message</name>
-		<tag-class>org.springframework.web.servlet.tags.MessageTag</tag-class>
-		<body-content>JSP</body-content>
-
-		<description>
-			Retrieves the message with the given code, or text if code isn't resolvable.
-			The HTML escaping flag participates in a page-wide or application-wide setting
-			(i.e. by HtmlEscapeTag or a "defaultHtmlEscape" context-param in web.xml).
-		</description>
-
-		<attribute>
-			<name>code</name>
-			<required>false</required>
-			<rtexprvalue>true</rtexprvalue>
-		</attribute>
-
-		<attribute>
-			<name>arguments</name>
-			<required>false</required>
-			<rtexprvalue>true</rtexprvalue>
-		</attribute>
-
-		<attribute>
-			<name>text</name>
-			<required>false</required>
-			<rtexprvalue>true</rtexprvalue>
-		</attribute>
-
-		<attribute>
-			<name>var</name>
-			<required>false</required>
-			<rtexprvalue>true</rtexprvalue>
-		</attribute>
-
-		<attribute>
-			<name>scope</name>
-			<required>false</required>
-			<rtexprvalue>true</rtexprvalue>
-		</attribute>
-
-		<attribute>
-			<name>htmlEscape</name>
-			<required>false</required>
-			<rtexprvalue>true</rtexprvalue>
-		</attribute>
-
-		<attribute>
-			<name>javaScriptEscape</name>
-			<required>false</required>
-			<rtexprvalue>true</rtexprvalue>
-		</attribute>
-
-	</tag>
-
-
-	<tag>
-
-		<name>theme</name>
-		<tag-class>org.springframework.web.servlet.tags.ThemeTag</tag-class>
-		<body-content>JSP</body-content>
-
-		<description>
-			Retrieves the theme message with the given code, or text if code isn't resolvable.
-			The HTML escaping flag participates in a page-wide or application-wide setting
-			(i.e. by HtmlEscapeTag or a "defaultHtmlEscape" context-param in web.xml).
-		</description>
-
-		<attribute>
-			<name>code</name>
-			<required>false</required>
-			<rtexprvalue>true</rtexprvalue>
-		</attribute>
-
-		<attribute>
-			<name>arguments</name>
-			<required>false</required>
-			<rtexprvalue>true</rtexprvalue>
-		</attribute>
-
-		<attribute>
-			<name>text</name>
-			<required>false</required>
-			<rtexprvalue>true</rtexprvalue>
-		</attribute>
-
-		<attribute>
-			<name>var</name>
-			<required>false</required>
-			<rtexprvalue>true</rtexprvalue>
-		</attribute>
-
-		<attribute>
-			<name>scope</name>
-			<required>false</required>
-			<rtexprvalue>true</rtexprvalue>
-		</attribute>
-
-		<attribute>
-			<name>htmlEscape</name>
-			<required>false</required>
-			<rtexprvalue>true</rtexprvalue>
-		</attribute>
-
-		<attribute>
-			<name>javaScriptEscape</name>
-			<required>false</required>
-			<rtexprvalue>true</rtexprvalue>
-		</attribute>
-
-	</tag>
-
-
-	<tag>
-
-		<name>hasBindErrors</name>
-		<tag-class>org.springframework.web.servlet.tags.BindErrorsTag</tag-class>
-		<body-content>JSP</body-content>
-
-		<description>
-			Provides Errors instance in case of bind errors.
-			The HTML escaping flag participates in a page-wide or application-wide setting
-			(i.e. by HtmlEscapeTag or a "defaultHtmlEscape" context-param in web.xml).
-		</description>
-
-		<variable>
-			<name-given>errors</name-given>
-			<variable-class>org.springframework.validation.Errors</variable-class>
-		</variable>
-
-		<attribute>
-			<name>name</name>
-			<required>true</required>
-			<rtexprvalue>true</rtexprvalue>
-		</attribute>
-
-		<attribute>
-			<name>htmlEscape</name>
-			<required>false</required>
-			<rtexprvalue>true</rtexprvalue>
-		</attribute>
-
-	</tag>
-
-
-	<tag>
-
-		<name>nestedPath</name>
-		<tag-class>org.springframework.web.servlet.tags.NestedPathTag</tag-class>
-		<body-content>JSP</body-content>
-
-		<description>
-			Sets a nested path to be used by the bind tag's path.
-		</description>
-
-		<variable>
-			<name-given>nestedPath</name-given>
-			<variable-class>java.lang.String</variable-class>
-		</variable>
-
-		<attribute>
-			<name>path</name>
-			<required>true</required>
-			<rtexprvalue>true</rtexprvalue>
-		</attribute>
-
-	</tag>
-
-
-	<tag>
-
-		<name>bind</name>
-		<tag-class>org.springframework.web.servlet.tags.BindTag</tag-class>
-		<body-content>JSP</body-content>
-
-		<description>
-			Provides BindStatus object for the given bind path.
-			The HTML escaping flag participates in a page-wide or application-wide setting
-			(i.e. by HtmlEscapeTag or a "defaultHtmlEscape" context-param in web.xml).
-		</description>
-
-		<variable>
-			<name-given>status</name-given>
-			<variable-class>org.springframework.web.servlet.support.BindStatus</variable-class>
-		</variable>
-
-		<attribute>
-			<name>path</name>
-			<required>true</required>
-			<rtexprvalue>true</rtexprvalue>
-		</attribute>
-
-		<attribute>
-			<name>ignoreNestedPath</name>
-			<required>false</required>
-			<rtexprvalue>true</rtexprvalue>
-		</attribute>
-
-		<attribute>
-			<name>htmlEscape</name>
-			<required>false</required>
-			<rtexprvalue>true</rtexprvalue>
-		</attribute>
-
-	</tag>
-
-
-	<tag>
-
-		<name>transform</name>
-		<tag-class>org.springframework.web.servlet.tags.TransformTag</tag-class>
-		<body-content>JSP</body-content>
-
-		<description>
-			Provides transformation of variables to Strings, using an appropriate
-			custom PropertyEditor from BindTag (can only be used inside BindTag).
-			The HTML escaping flag participates in a page-wide or application-wide setting
-			(i.e. by HtmlEscapeTag or a "defaultHtmlEscape" context-param in web.xml).
-		</description>
-
-		<attribute>
-			<name>value</name>
-			<required>true</required>
-			<rtexprvalue>true</rtexprvalue>
-		</attribute>
-
-		<attribute>
-			<name>var</name>
-			<required>false</required>
-			<rtexprvalue>true</rtexprvalue>
-		</attribute>
-
-		<attribute>
-			<name>scope</name>
-			<required>false</required>
-			<rtexprvalue>true</rtexprvalue>
-		</attribute>
-
-		<attribute>
-			<name>htmlEscape</name>
-			<required>false</required>
-			<rtexprvalue>true</rtexprvalue>
-		</attribute>
-
-	</tag>
-
-</taglib>