From a361f42b790753b8ac815fa93f7e2b86beadc161 Mon Sep 17 00:00:00 2001 From: Sandeep Poonia Date: Tue, 14 Jun 2016 12:44:07 +0530 Subject: [PATCH] upgrade to Grails 2.4.5 with Hibernate 4 --- .gitignore | 188 +- MultiTenantSingleDbGrailsPlugin.groovy | 179 +- application.properties | 5 +- grails-app/conf/BuildConfig.groovy | 87 +- grails-app/conf/Config.groovy | 35 +- grails-app/conf/DataSource.groovy | 63 +- grails-app/conf/UrlMappings.groovy | 13 + .../controllers/demo/ProductController.groovy | 4 +- grails-app/domain/demo/DemoAnimal.groovy | 6 +- grails-app/domain/demo/DemoPetOwner.groovy | 2 +- .../services/demo/AnotherDemoService.groovy | 3 - .../core/MultiTenantService.groovy | 12 +- grails-app/views/error.gsp | 18 + scripts/MtQuickstart.groovy | 3 - scripts/MtSpringSecurity.groovy | 3 - scripts/_Uninstall.groovy | 5 + scripts/_Upgrade.groovy | 10 + ...1.1 This plugin vs. multi-tenant-core.gdoc | 2 +- src/docs/guide/2.1. Tenant class.gdoc | 2 +- .../singledb/MtSingleDbPluginSupport.groovy | 16 +- .../core/annotation/MultiTenant.java | 4 +- .../multitenant/core/ast/MultiTenantAST.java | 5 +- .../exception/NoCurrentTenantException.java | 1 + .../core/exception/TenantException.java | 1 + .../exception/TenantNotFoundException.java | 1 + .../exception/TenantResolveException.java | 3 +- .../core/impl/CurrentTenantThreadLocal.java | 1 + .../core/resolve/TenantResolver.java | 5 +- .../servlet/CurrentTenantServletFilter.java | 21 +- .../ConfiguredTenantScopedBeanProcessor.java | 10 +- .../CurrentTenantAwarePostProcessor.java | 2 +- .../multitenant/core/spring/TenantScope.java | 13 +- .../TenantHibernateEventListener.java | 16 +- .../hibernate/TenantHibernateEventProxy.java | 7 +- .../TenantHibernateFilterConfigurator.java | 17 +- .../TenantHibernateFilterEnabler.java | 12 +- .../spring/FilterDefinitionFactoryBean.java | 143 + test/integration/demo/DemoCustomerSpec.groovy | 2 +- test/integration/demo/DemoDogSpec.groovy | 2 +- test/integration/demo/DemoProductSpec.groovy | 3 +- test/integration/demo/DemoTenantSpec.groovy | 8 +- .../demo/ScopedServicesSpec.groovy | 66 +- .../core/MultiTenantServiceSpec.groovy | 13 +- ...bernateEventListenerIntegrationSpec.groovy | 6 +- .../TenantHibernateFilterEnablerSpec.groovy | 14 +- .../core/ast/MultiTenantASTSpec.groovy | 4 +- .../CurrentTenantServletFilterSpec.groovy | 9 +- .../MtSingleDbPluginSupportSpec.groovy | 5 +- .../TenantHibernateEventListenerSpec.groovy | 40 +- .../TenantHibernateEventProxySpec.groovy | 7 +- ...enantHibernateFilterEnablerUnitSpec.groovy | 12 +- web-app/WEB-INF/applicationContext.xml | 31 + web-app/WEB-INF/sitemesh.xml | 14 + web-app/WEB-INF/tld/c.tld | 572 ++++ web-app/WEB-INF/tld/fmt.tld | 671 +++++ web-app/WEB-INF/tld/grails.tld | 550 ++++ web-app/WEB-INF/tld/spring-form.tld | 2411 +++++++++++++++++ web-app/WEB-INF/tld/spring.tld | 457 ++++ 58 files changed, 5478 insertions(+), 337 deletions(-) create mode 100644 grails-app/conf/UrlMappings.groovy create mode 100644 grails-app/views/error.gsp create mode 100644 scripts/_Uninstall.groovy create mode 100644 scripts/_Upgrade.groovy create mode 100644 src/java/grails/plugin/multitenant/spring/FilterDefinitionFactoryBean.java create mode 100644 web-app/WEB-INF/applicationContext.xml create mode 100644 web-app/WEB-INF/sitemesh.xml create mode 100644 web-app/WEB-INF/tld/c.tld create mode 100644 web-app/WEB-INF/tld/fmt.tld create mode 100644 web-app/WEB-INF/tld/grails.tld create mode 100644 web-app/WEB-INF/tld/spring-form.tld create mode 100644 web-app/WEB-INF/tld/spring.tld diff --git a/.gitignore b/.gitignore index bece19f..89cc355 100644 --- a/.gitignore +++ b/.gitignore @@ -1,20 +1,176 @@ -web-app -target -build -bin -build.sh -plugin.xml -stacktrace.log -*.tmproj -*.iws -*.zip -*.sha1 +##Grails.gitignore------------------------------------------------------------- +# web application files +/web-app/WEB-INF/classes + +# default HSQL database files for production mode +/prodDb.* + +# general HSQL database files +*Db.properties +*Db.script + +# logs +/stacktrace.log +/test/reports +/logs + +# project release file +/*.war + +# plugin release files +/*.zip +/plugin.xml + +# older plugin install locations +/plugins +/web-app/plugins + +# "temporary" build files +/target + + +##Java.gitignore--------------------------------------------------------------- +*.class + +# Mobile Tools for Java (J2ME) +.mtj.tmp/ + +# Package Files # +*.jar +*.war +*.ear + +# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml +hs_err_pid* + + +##Maven.gitignore-------------------------------------------------------------- +target/ +**/target/ +pom.xml.tag +pom.xml.releaseBackup +pom.xml.versionsBackup +pom.xml.next +release.properties +dependency-reduced-pom.xml +buildNumber.properties +.mvn/timing.properties + + +##Eclipse.gitignore------------------------------------------------------------ +*.pydevproject +.metadata .gradle -.DS_Store -.classpath +bin/ +tmp/ +*.tmp +*.bak +*.swp +*~.nib +local.properties +.settings/ +.loadpath + +# Eclipse Core .project -.settings -.idea + +# External tool builders +.externalToolBuilders/ + +# Locally stored "Eclipse launch configurations" +*.launch + +# CDT-specific +.cproject + +# JDT-specific (Eclipse Java Development Tools) +.classpath + +# Java annotation processor (APT) +.factorypath + +# PDT-specific +.buildpath + +# sbteclipse plugin +.target + +# TeXlipse plugin +.texlipse + + +##SublimeText.gitignore-------------------------------------------------------- +# cache files for sublime text +*.tmlanguage.cache +*.tmPreferences.cache +*.stTheme.cache + +# workspace files are user-specific +*.sublime-workspace + +# project files should be checked into the repository, unless a significant +# proportion of contributors will probably not be using SublimeText +# *.sublime-project + +# sftp configuration file +sftp-config.json + + +##NetBeans.gitignore----------------------------------------------------------- +nbproject/private/ +build/ +nbbuild/ +dist/ +nbdist/ +nbactions.xml +nb-configuration.xml +.nb-gradle/ + + +##JetBrains.gitignore---------------------------------------------------------- +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion + *.iml + +## Directory-based project format: +.idea/ +# if you remove the above rule, at least ignore the following: + +# User-specific stuff: +# .idea/workspace.xml +# .idea/tasks.xml +# .idea/dictionaries + +# Sensitive or high-churn files: +# .idea/dataSources.ids +# .idea/dataSources.xml +# .idea/sqlDataSources.xml +# .idea/dynamic.xml +# .idea/uiDesigner.xml + +# Gradle: +# .idea/gradle.xml +# .idea/libraries + +# Mongo Explorer plugin: +# .idea/mongoSettings.xml + +## File-based project format: *.ipr -.grails +*.iws + +## Plugin-specific files: + +# IntelliJ +/out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties \ No newline at end of file diff --git a/MultiTenantSingleDbGrailsPlugin.groovy b/MultiTenantSingleDbGrailsPlugin.groovy index a4b9628..8e1c67c 100644 --- a/MultiTenantSingleDbGrailsPlugin.groovy +++ b/MultiTenantSingleDbGrailsPlugin.groovy @@ -1,71 +1,108 @@ -import grails.plugin.multitenant.singledb.MtSingleDbPluginSupport -import grails.util.Environment - -import org.codehaus.groovy.grails.commons.GrailsApplication -import org.slf4j.Logger -import org.slf4j.LoggerFactory - -class MultiTenantSingleDbGrailsPlugin { - private Logger log = LoggerFactory.getLogger('grails.plugin.multiTenant.MultiTenantSingleDbPlugin') - - def version = "0.8.3" - def grailsVersion = "1.3.5 > *" - - def loadAfter = [ - 'hawk-eventing', - 'hibernate-hijacker', - 'controllers' - ] - - def pluginExcludes = ["**/demo/**" ] - - def author = "Kim A. Betti" - def authorEmail = "kim@developer-b.com" - def title = "MultiTenant - SingleDB" - def description = "Multi tenant setup focused on single database mode" - - def documentation = "https://github.com/multi-tenant/grails-multi-tenant-single-db" - - def license = "APACHE" - def developers = [ - [ name: "Steve Ronderos", email: "steve.ronderos@gmail.com" ] - ] - def issueManagement = [ system: "github", url: "https://github.com/multi-tenant/grails-multi-tenant-single-db/issues" ] - def scm = [ url: "https://github.com/multi-tenant/grails-multi-tenant-single-db" ] - - // make sure the filter chain filter is after the Grails filter - def getWebXmlFilterOrder() { - log.debug("Start getWebXmlFilterOrder") - def filterMap = [:] - try { - def classLoader = new GroovyClassLoader(getClass().getClassLoader()) - def slurper = new ConfigSlurper(Environment.getCurrent().getName()) - def config = slurper.parse(classLoader.loadClass(GrailsApplication.CONFIG_CLASS)) - - if(config.multiTenant.resolveTenantBeforeLogin) { - def SecurityFilterPosition = classLoader.loadClass('org.codehaus.groovy.grails.plugins.springsecurity.SecurityFilterPosition') - log.debug("set WebXmlFilterOrder before login") - filterMap = [tenantFilter: SecurityFilterPosition.FORM_LOGIN_FILTER.order - 100] - } else { - def FilterManager = classLoader.loadClass('grails.plugin.webxml.FilterManager') - log.debug("set WebXmlFilterOrder after login") - filterMap = [tenantFilter: FilterManager.SITEMESH_POSITION - 100] - } - } catch (ClassNotFoundException e) { - log.warn "Could not determine desired tenantFilter position." - } - return filterMap - } - - def doWithSpring = { - MtSingleDbPluginSupport.doWithSpring.delegate = delegate - MtSingleDbPluginSupport.doWithSpring application - } - - def doWithDynamicMethods = { ctx -> - MtSingleDbPluginSupport.doWithDynamicMethods.delegate = delegate - MtSingleDbPluginSupport.doWithDynamicMethods ctx, application - } - - def doWithWebDescriptor = MtSingleDbPluginSupport.doWithWebDescriptor -} +import grails.plugin.multitenant.singledb.MtSingleDbPluginSupport +import grails.util.Environment +import groovy.util.logging.Log4j +import org.codehaus.groovy.grails.commons.GrailsApplication + +@Log4j +class MultiTenantSingleDbGrailsPlugin { + // the plugin version + def version = "1.0" + // the version or versions of Grails the plugin is designed for + def grailsVersion = "2.4 > *" + // resources that are excluded from plugin packaging + def pluginExcludes = [ + '**/demo/**', + "grails-app/views/error.gsp", + ] + + def loadAfter = [ + 'hawk-eventing', + 'hibernate-hijacker', + 'controllers' + ] + + // TODO Fill in these fields + def title = "MultiTenant - SingleDB" // Headline display name of the plugin + def author = "Sandeep Poonia" + def authorEmail = "sandeep.poonia.90@gmail.com" + def description = '''\ +Multi tenant setup focused on single database mode +''' + + // URL to the plugin's documentation + def documentation = "https://github.com/spoonia/grails-multi-tenant-single-db" + + // 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: "github", url: "https://github.com/spoonia/grails-multi-tenant-single-db/issues"] + + // Online location of the plugin's browseable source code. + def scm = [url: "https://github.com/spoonia/grails-multi-tenant-single-db"] + + // make sure the filter chain filter is after the Grails filter + def getWebXmlFilterOrder() { + log.debug("Start getWebXmlFilterOrder") + def filterMap = [:] + try { + def classLoader = new GroovyClassLoader(getClass().getClassLoader()) + def slurper = new ConfigSlurper(Environment.getCurrent().getName()) + def config = slurper.parse(classLoader.loadClass(GrailsApplication.CONFIG_CLASS)) + + if (config.multiTenant.resolveTenantBeforeLogin) { + def SecurityFilterPosition = classLoader.loadClass('org.codehaus.groovy.grails.plugins.springsecurity.SecurityFilterPosition') + log.debug("set WebXmlFilterOrder before login") + filterMap = [tenantFilter: SecurityFilterPosition.FORM_LOGIN_FILTER.order - 100] + } else { + def FilterManager = classLoader.loadClass('grails.plugin.webxml.FilterManager') + log.debug("set WebXmlFilterOrder after login") + filterMap = [tenantFilter: FilterManager.SITEMESH_POSITION - 100] + } + } catch (ClassNotFoundException e) { + log.warn "Could not determine desired tenantFilter position." + } + return filterMap + } + + def doWithWebDescriptor = { xml -> + MtSingleDbPluginSupport.doWithWebDescriptor xml + } + + def doWithSpring = { + MtSingleDbPluginSupport.doWithSpring.delegate = delegate + MtSingleDbPluginSupport.doWithSpring application + } + + def doWithDynamicMethods = { ctx -> + MtSingleDbPluginSupport.doWithDynamicMethods.delegate = delegate + MtSingleDbPluginSupport.doWithDynamicMethods ctx, application + } + + def doWithApplicationContext = { ctx -> + // 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) + } +} diff --git a/application.properties b/application.properties index e151936..12628d8 100644 --- a/application.properties +++ b/application.properties @@ -1 +1,4 @@ -app.grails.version=2.0.4 +#Grails Metadata file +#Wed Mar 23 08:34:23 IST 2016 +app.grails.version=2.4.5 +app.name=multi-tenant-single-db diff --git a/grails-app/conf/BuildConfig.groovy b/grails-app/conf/BuildConfig.groovy index 201bb7b..3d13e7c 100644 --- a/grails-app/conf/BuildConfig.groovy +++ b/grails-app/conf/BuildConfig.groovy @@ -1,34 +1,57 @@ -grails.project.work.dir = 'target' - +grails.project.class.dir = "target/classes" +grails.project.test.class.dir = "target/test-classes" +grails.project.test.reports.dir = "target/test-reports" + +grails.project.fork = [ + // configure settings for compilation JVM, note that if you alter the Groovy version forked compilation is required + // compile: [maxMemory: 256, minMemory: 64, debug: false, maxPerm: 256, daemon:true], + + // configure settings for the test-app JVM, uses the daemon by default + test : [maxMemory: 768, minMemory: 64, debug: false, maxPerm: 256, daemon: true], + // configure settings for the run-app JVM + run : [maxMemory: 768, minMemory: 64, debug: false, maxPerm: 256, forkReserve: false], + // configure settings for the run-war JVM + war : [maxMemory: 768, minMemory: 64, debug: false, maxPerm: 256, forkReserve: false], + // configure settings for the Console UI JVM + console: [maxMemory: 768, minMemory: 64, debug: false, maxPerm: 256] +] + +grails.project.dependency.resolver = "maven" // or ivy grails.project.dependency.resolution = { - - inherits 'global' - log 'warn' - - repositories { - grailsCentral() - } - - plugins { - build ':release:2.2.1', ':rest-client-builder:1.0.3', { - export = false - } - - provided ':webxml:1.4.1' - - compile(":hibernate:$grailsVersion") { export = false } - - compile(':hawk-eventing:0.5.1') { - excludes 'svn' - } - - compile(':hibernate-hijacker:0.8.1') { - excludes 'svn' - } - - test(':spock:0.7') { - excludes 'svn' - export = false - } - } + // 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() + mavenLocal() + mavenCentral() + // uncomment the below to enable remote dependency resolution + // from public Maven repositories + //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.27' + } + + plugins { + build(":release:3.1.1", ":rest-client-builder:2.1.1") { + export = false + } + + provided ':webxml:1.4.1' + + compile(":hibernate4:4.3.10") { + export = false + } + + compile(':hibernate-hijacker:1.0') { + excludes 'svn' + } + } } diff --git a/grails-app/conf/Config.groovy b/grails-app/conf/Config.groovy index d787e3a..1a25c8f 100644 --- a/grails-app/conf/Config.groovy +++ b/grails-app/conf/Config.groovy @@ -1,19 +1,34 @@ +// configuration for plugin testing - will not be included in the plugin zip + log4j = { - error 'org.codehaus.groovy.grails', - 'org.springframework', - 'org.hibernate', - 'net.sf.ehcache.hibernate' - info 'grails.app' + // 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' - debug 'grails.plugin.multitenant' + debug 'grails.plugin.multitenant' } multiTenant { - perTenantBeans = [ "demoService" ] + perTenantBeans = ["demoService"] tenantClass = demo.DemoTenant } grails.doc.authors="Kim A. Betti" -grails.doc.license="Apache 2.0" -grails.doc.copyright="" -grails.doc.footer="Have a nice day!" +grails.doc.license = "Apache 2.0" +grails.doc.copyright = "" +grails.doc.footer = "Have a nice day!" \ No newline at end of file diff --git a/grails-app/conf/DataSource.groovy b/grails-app/conf/DataSource.groovy index e259d62..1eaa87c 100644 --- a/grails-app/conf/DataSource.groovy +++ b/grails-app/conf/DataSource.groovy @@ -1,14 +1,57 @@ dataSource { - pooled = true - driverClassName = 'org.h2.Driver' - username = 'sa' - password = '' - dbCreate = 'update' - url = 'jdbc:h2:mem:testDb' + pooled = true + jmxExport = true + driverClassName = "org.h2.Driver" + username = "sa" + password = "" } - hibernate { - cache.use_second_level_cache = false - cache.use_query_cache = false - cache.provider_class = 'org.hibernate.cache.EhCacheProvider' + cache.use_second_level_cache = false + cache.use_query_cache = false +// cache.region.factory_class = 'org.hibernate.cache.SingletonEhCacheRegionFactory' // Hibernate 3 + cache.region.factory_class = 'org.hibernate.cache.ehcache.SingletonEhCacheRegionFactory' // Hibernate 4 + singleSession = true // configure OSIV singleSession mode + flush.mode = 'manual' // OSIV session flush mode outside of transactional context +} + +// 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;DB_CLOSE_ON_EXIT=FALSE" + } + } + test { + dataSource { + dbCreate = "update" + url = "jdbc:h2:mem:testDb;MVCC=TRUE;LOCK_TIMEOUT=10000;DB_CLOSE_ON_EXIT=FALSE" + } + } + production { + dataSource { + dbCreate = "update" + url = "jdbc:h2:prodDb;MVCC=TRUE;LOCK_TIMEOUT=10000;DB_CLOSE_ON_EXIT=FALSE" + properties { + // See http://grails.org/doc/latest/guide/conf.html#dataSource for documentation + jmxEnabled = true + initialSize = 5 + maxActive = 50 + minIdle = 5 + maxIdle = 25 + maxWait = 10000 + maxAge = 10 * 60000 + timeBetweenEvictionRunsMillis = 5000 + minEvictableIdleTimeMillis = 60000 + validationQuery = "SELECT 1" + validationQueryTimeout = 3 + validationInterval = 15000 + testOnBorrow = true + testWhileIdle = true + testOnReturn = false + jdbcInterceptors = "ConnectionState" + defaultTransactionIsolation = java.sql.Connection.TRANSACTION_READ_COMMITTED + } + } + } } diff --git a/grails-app/conf/UrlMappings.groovy b/grails-app/conf/UrlMappings.groovy new file mode 100644 index 0000000..69634b8 --- /dev/null +++ b/grails-app/conf/UrlMappings.groovy @@ -0,0 +1,13 @@ +class UrlMappings { + + static mappings = { + "/$controller/$action?/$id?(.$format)?"{ + constraints { + // apply constraints here + } + } + + "/"(view:"/index") + "500"(view:'/error') + } +} diff --git a/grails-app/controllers/demo/ProductController.groovy b/grails-app/controllers/demo/ProductController.groovy index 2ecd713..12f07f4 100644 --- a/grails-app/controllers/demo/ProductController.groovy +++ b/grails-app/controllers/demo/ProductController.groovy @@ -1,5 +1,5 @@ package demo class ProductController { - static scaffold = DemoProduct -} + static scaffold = DemoProduct +} \ No newline at end of file diff --git a/grails-app/domain/demo/DemoAnimal.groovy b/grails-app/domain/demo/DemoAnimal.groovy index 510fc8f..7daef9c 100644 --- a/grails-app/domain/demo/DemoAnimal.groovy +++ b/grails-app/domain/demo/DemoAnimal.groovy @@ -5,7 +5,7 @@ import grails.plugin.multitenant.core.annotation.MultiTenant @MultiTenant class DemoAnimal { - static belongsTo = [ owner: DemoPetOwner ] + static belongsTo = [owner: DemoPetOwner] String name @@ -13,7 +13,9 @@ class DemoAnimal { owner nullable: true } - String toString() { + + @Override + public String toString() { "Animal[name: $name, tenantId: $tenantId]" } } diff --git a/grails-app/domain/demo/DemoPetOwner.groovy b/grails-app/domain/demo/DemoPetOwner.groovy index 1daa55c..f64b75a 100644 --- a/grails-app/domain/demo/DemoPetOwner.groovy +++ b/grails-app/domain/demo/DemoPetOwner.groovy @@ -5,7 +5,7 @@ import grails.plugin.multitenant.core.annotation.MultiTenant @MultiTenant class DemoPetOwner { - static hasMany = [ pets: DemoAnimal ] + static hasMany = [pets: DemoAnimal] String name } diff --git a/grails-app/services/demo/AnotherDemoService.groovy b/grails-app/services/demo/AnotherDemoService.groovy index 5bc407e..1701d6f 100644 --- a/grails-app/services/demo/AnotherDemoService.groovy +++ b/grails-app/services/demo/AnotherDemoService.groovy @@ -1,7 +1,4 @@ package demo - -import org.springframework.context.annotation.Scope - /** * Scope configured using Grails convention. * @author Kim A. Betti diff --git a/grails-app/services/grails/plugin/multitenant/core/MultiTenantService.groovy b/grails-app/services/grails/plugin/multitenant/core/MultiTenantService.groovy index a026207..4992a5a 100644 --- a/grails-app/services/grails/plugin/multitenant/core/MultiTenantService.groovy +++ b/grails-app/services/grails/plugin/multitenant/core/MultiTenantService.groovy @@ -1,8 +1,9 @@ package grails.plugin.multitenant.core -import static org.springframework.transaction.TransactionDefinition.PROPAGATION_REQUIRES_NEW import grails.plugin.hibernatehijacker.template.HibernateTemplates +import static org.springframework.transaction.TransactionDefinition.PROPAGATION_REQUIRES_NEW + /** * Allows for temporary manipulation of the current tenant. * Note: If you use this too often you're probably doing something wrong. @@ -23,17 +24,18 @@ class MultiTenantService { def doWithTenantId(Integer tenantId, Closure callback) { Integer oldTenantId = currentTenant.get() try { - if(log.debugEnabled) log.debug "doWithTenantId oldTenantId - $oldTenantId" + if (log.debugEnabled) log.debug "doWithTenantId oldTenantId - $oldTenantId" + currentTenant.set(tenantId) - if(log.debugEnabled) log.debug "doWithTenantId runin with tenant - $tenantId" + + if (log.debugEnabled) log.debug "doWithTenantId runin with tenant - $tenantId" hibernateTemplates.withNewSession { hibernateTemplates.withTransaction(PROPAGATION_REQUIRES_NEW) { callback.call() } } - } - finally{ + } finally { currentTenant.set(oldTenantId) } } diff --git a/grails-app/views/error.gsp b/grails-app/views/error.gsp new file mode 100644 index 0000000..95e11ed --- /dev/null +++ b/grails-app/views/error.gsp @@ -0,0 +1,18 @@ + + + + <g:if env="development">Grails Runtime Exception</g:if><g:else>Error</g:else> + + + + + + + + +
    +
  • An error has occurred
  • +
+
+ + diff --git a/scripts/MtQuickstart.groovy b/scripts/MtQuickstart.groovy index ab41b97..2a8691d 100644 --- a/scripts/MtQuickstart.groovy +++ b/scripts/MtQuickstart.groovy @@ -17,9 +17,6 @@ * excellent Spring Security Plugin by Burt Beckwith: * http://grails.org/plugin/spring-security-core */ - -import grails.util.GrailsNameUtils - includeTargets << new File(multiTenantSingleDbPluginDir, "scripts/_MTCommon.groovy") USAGE = """ diff --git a/scripts/MtSpringSecurity.groovy b/scripts/MtSpringSecurity.groovy index 832b5d3..ce4ee73 100644 --- a/scripts/MtSpringSecurity.groovy +++ b/scripts/MtSpringSecurity.groovy @@ -17,9 +17,6 @@ * excellent Spring Security Plugin by Burt Beckwith: * http://grails.org/plugin/spring-security-core */ - -import grails.util.GrailsNameUtils - includeTargets << new File(multiTenantSingleDbPluginDir, "scripts/_MTCommon.groovy") USAGE = """ diff --git a/scripts/_Uninstall.groovy b/scripts/_Uninstall.groovy new file mode 100644 index 0000000..7c53169 --- /dev/null +++ b/scripts/_Uninstall.groovy @@ -0,0 +1,5 @@ +// +// 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! +// diff --git a/scripts/_Upgrade.groovy b/scripts/_Upgrade.groovy new file mode 100644 index 0000000..6a1a4c9 --- /dev/null +++ b/scripts/_Upgrade.groovy @@ -0,0 +1,10 @@ +// +// 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") +// diff --git a/src/docs/guide/1.1 This plugin vs. multi-tenant-core.gdoc b/src/docs/guide/1.1 This plugin vs. multi-tenant-core.gdoc index e7af232..d9c7398 100644 --- a/src/docs/guide/1.1 This plugin vs. multi-tenant-core.gdoc +++ b/src/docs/guide/1.1 This plugin vs. multi-tenant-core.gdoc @@ -10,7 +10,7 @@ A few months later the multi-tenant organization was formed on GitHub by a group The organization was formed to drive the plugin development forward. The most serious problems mentioned above has been fixed in the current version of multi-tenant-core. -Kim A. Betti started working on webflow support without compile time dependency and a less intrusive way of intercepting Hibernate sessions. +Kim A. Betti started working on webflow support without compile time dependency and a less intrusive way of intercepting Hibernate sessions. The result of this work was this plugin (multi-tenant-single-db). The original plan was to merge at least some of the solutions in this plugin back into [multi-tenant-core|http://github.com/multi-tenant/grails-multi-tenant-core], but the plugins has drifted further apart while refactoring and improving single-database mode. diff --git a/src/docs/guide/2.1. Tenant class.gdoc b/src/docs/guide/2.1. Tenant class.gdoc index 2cd7cde..1112051 100644 --- a/src/docs/guide/2.1. Tenant class.gdoc +++ b/src/docs/guide/2.1. Tenant class.gdoc @@ -1,5 +1,5 @@ Most applications will have a domain class where you store tenant data. This will probably be named something like @User@, @Customer@ or @Tenant@. -By implementing the interface @grails.plugin.multitenant.core.Tenant@ and overriding the @Integer tenantId()@ method, +By implementing the interface @grails.plugin.multitenant.core.Tenant@ and overriding the @Integer tenantId()@ method, the plugin will give you some benefits out of the box. The examples below assumes that you have a tenant class similar to this. diff --git a/src/groovy/grails/plugin/multitenant/singledb/MtSingleDbPluginSupport.groovy b/src/groovy/grails/plugin/multitenant/singledb/MtSingleDbPluginSupport.groovy index 8597b9d..60763c4 100644 --- a/src/groovy/grails/plugin/multitenant/singledb/MtSingleDbPluginSupport.groovy +++ b/src/groovy/grails/plugin/multitenant/singledb/MtSingleDbPluginSupport.groovy @@ -1,6 +1,6 @@ package grails.plugin.multitenant.singledb -import grails.plugin.hibernatehijacker.hibernate.HibernateEventSubscriptionFactory +import grails.plugin.hibernatehijacker.spring.HibernateEventSubscriptionFactory import grails.plugin.multitenant.core.MultiTenantService import grails.plugin.multitenant.core.Tenant import grails.plugin.multitenant.core.exception.TenantException @@ -13,13 +13,12 @@ import grails.plugin.multitenant.singledb.hibernate.TenantHibernateEventListener import grails.plugin.multitenant.singledb.hibernate.TenantHibernateEventProxy import grails.plugin.multitenant.singledb.hibernate.TenantHibernateFilterConfigurator import grails.plugin.multitenant.singledb.hibernate.TenantHibernateFilterEnabler - import org.codehaus.groovy.grails.commons.GrailsApplication import org.slf4j.Logger import org.slf4j.LoggerFactory import org.springframework.beans.factory.config.CustomScopeConfigurer import org.springframework.context.ApplicationContext -import org.springframework.orm.hibernate3.FilterDefinitionFactoryBean +import grails.plugin.multitenant.spring.FilterDefinitionFactoryBean /** * Used by the plugin descriptor. @@ -61,14 +60,15 @@ class MtSingleDbPluginSupport { // Responsible for registering the custom 'tenant' scope with Spring. tenantScopeConfigurer(CustomScopeConfigurer) { - scopes = [ tenant: ref("tenantScope") ] + scopes = [tenant: ref("tenantScope")] } // Definition of the Hibernate filter making sure that // each tenant only sees and touches its own data. multiTenantHibernateFilter(FilterDefinitionFactoryBean) { + filterName = "multiTenantHibernateFilter" defaultFilterCondition = ":tenantId = tenant_id" - parameterTypes = [ tenantId: "java.lang.Integer" ] + parameterTypes = [tenantId: "java.lang.Integer"] } // Listens for new Hibernate sessions and enables the @@ -126,8 +126,8 @@ class MtSingleDbPluginSupport { Integer tenantId = tenantId() if (tenantId == null) { String exMessage = ("Can't execute closure in tenent namespace without a tenant id. " - + "Make sure that the domain instance has been saved to database " - + "(if you're using Hibernate and primary key as tenant id)") + + "Make sure that the domain instance has been saved to database " + + "(if you're using Hibernate and primary key as tenant id)") throw new TenantException(exMessage) } else { @@ -159,7 +159,7 @@ class MtSingleDbPluginSupport { def filterMappings = xml.'filter-mapping' // webxml plugin is responsible for filter mapping order. Put the filter mapping anywhere. - filterMappings[filterMappings.size() - 1] + { + filterMappings[filterMappings.size() - 1] + { 'filter-mapping' { 'filter-name'('tenantFilter') 'url-pattern'('/*') diff --git a/src/java/grails/plugin/multitenant/core/annotation/MultiTenant.java b/src/java/grails/plugin/multitenant/core/annotation/MultiTenant.java index 760bd74..c15ea75 100644 --- a/src/java/grails/plugin/multitenant/core/annotation/MultiTenant.java +++ b/src/java/grails/plugin/multitenant/core/annotation/MultiTenant.java @@ -1,12 +1,12 @@ package grails.plugin.multitenant.core.annotation; +import org.codehaus.groovy.transform.GroovyASTTransformationClass; + import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; -import org.codehaus.groovy.transform.GroovyASTTransformationClass; - /** * Domain classes annotated with this plugin * will get a tenantId property added during compilation. diff --git a/src/java/grails/plugin/multitenant/core/ast/MultiTenantAST.java b/src/java/grails/plugin/multitenant/core/ast/MultiTenantAST.java index e8a4380..25204d0 100644 --- a/src/java/grails/plugin/multitenant/core/ast/MultiTenantAST.java +++ b/src/java/grails/plugin/multitenant/core/ast/MultiTenantAST.java @@ -1,9 +1,6 @@ package grails.plugin.multitenant.core.ast; import grails.plugin.multitenant.core.MultiTenantDomainClass; - -import java.lang.reflect.Modifier; - import org.codehaus.groovy.ast.ASTNode; import org.codehaus.groovy.ast.ClassNode; import org.codehaus.groovy.ast.expr.ConstantExpression; @@ -14,6 +11,8 @@ import org.codehaus.groovy.transform.ASTTransformation; import org.codehaus.groovy.transform.GroovyASTTransformation; +import java.lang.reflect.Modifier; + /** * Adds a tenantId property to domain classes annotated MultiTenant. */ diff --git a/src/java/grails/plugin/multitenant/core/exception/NoCurrentTenantException.java b/src/java/grails/plugin/multitenant/core/exception/NoCurrentTenantException.java index b07b762..acf47c6 100644 --- a/src/java/grails/plugin/multitenant/core/exception/NoCurrentTenantException.java +++ b/src/java/grails/plugin/multitenant/core/exception/NoCurrentTenantException.java @@ -2,6 +2,7 @@ /** * Thrown when a current tenant isn't set. + * * @author Kim A. Betti */ @SuppressWarnings("serial") diff --git a/src/java/grails/plugin/multitenant/core/exception/TenantException.java b/src/java/grails/plugin/multitenant/core/exception/TenantException.java index 6bab4c6..08604c5 100644 --- a/src/java/grails/plugin/multitenant/core/exception/TenantException.java +++ b/src/java/grails/plugin/multitenant/core/exception/TenantException.java @@ -2,6 +2,7 @@ /** * Superclass for all multi tenant related exceptions. + * * @author Kim A. Betti */ @SuppressWarnings("serial") diff --git a/src/java/grails/plugin/multitenant/core/exception/TenantNotFoundException.java b/src/java/grails/plugin/multitenant/core/exception/TenantNotFoundException.java index 3433b10..265f1c2 100644 --- a/src/java/grails/plugin/multitenant/core/exception/TenantNotFoundException.java +++ b/src/java/grails/plugin/multitenant/core/exception/TenantNotFoundException.java @@ -2,6 +2,7 @@ /** * Thrown when a tenant can't be found. + * * @author Kim A. Betti */ @SuppressWarnings("serial") diff --git a/src/java/grails/plugin/multitenant/core/exception/TenantResolveException.java b/src/java/grails/plugin/multitenant/core/exception/TenantResolveException.java index 074c179..b1a5fd7 100644 --- a/src/java/grails/plugin/multitenant/core/exception/TenantResolveException.java +++ b/src/java/grails/plugin/multitenant/core/exception/TenantResolveException.java @@ -1,7 +1,8 @@ package grails.plugin.multitenant.core.exception; /** - * Thrown when a tenant can't be resolved. + * Thrown when a tenant can't be resolved. + * * @author Kim A. Betti */ @SuppressWarnings("serial") diff --git a/src/java/grails/plugin/multitenant/core/impl/CurrentTenantThreadLocal.java b/src/java/grails/plugin/multitenant/core/impl/CurrentTenantThreadLocal.java index b2f6b99..f408645 100644 --- a/src/java/grails/plugin/multitenant/core/impl/CurrentTenantThreadLocal.java +++ b/src/java/grails/plugin/multitenant/core/impl/CurrentTenantThreadLocal.java @@ -6,6 +6,7 @@ /** * Stores the current tenant id on the current thread. * Have a look at the link below if you want more information on ThreadLocal. + * * @see http://stackoverflow.com/questions/1490919/purpose-of-threadlocal */ public class CurrentTenantThreadLocal implements CurrentTenant { diff --git a/src/java/grails/plugin/multitenant/core/resolve/TenantResolver.java b/src/java/grails/plugin/multitenant/core/resolve/TenantResolver.java index bd0fa4f..e13a7df 100644 --- a/src/java/grails/plugin/multitenant/core/resolve/TenantResolver.java +++ b/src/java/grails/plugin/multitenant/core/resolve/TenantResolver.java @@ -10,15 +10,14 @@ * The implementation then have to be registered as a Spring bean * named 'tenantResolver'. Have a look at the documentation on the * bean dsl for more information on how to do this. - * + *

* TODO: Make a more pluggable / configurable approach to how the current tenant is looked up - * + * * @see http://grails.org/doc/latest/guide/single.html#14.%20Grails%20and%20Spring */ public interface TenantResolver { /** - * * @param request * @return tenant id, don't return 'null' unless you know what you're doing * @throws TenantResolveException diff --git a/src/java/grails/plugin/multitenant/core/servlet/CurrentTenantServletFilter.java b/src/java/grails/plugin/multitenant/core/servlet/CurrentTenantServletFilter.java index 648634f..af9be63 100644 --- a/src/java/grails/plugin/multitenant/core/servlet/CurrentTenantServletFilter.java +++ b/src/java/grails/plugin/multitenant/core/servlet/CurrentTenantServletFilter.java @@ -2,28 +2,21 @@ import grails.plugin.multitenant.core.CurrentTenant; import grails.plugin.multitenant.core.resolve.TenantResolver; - -import java.io.IOException; - -import javax.servlet.Filter; -import javax.servlet.FilterChain; -import javax.servlet.FilterConfig; -import javax.servlet.ServletContext; -import javax.servlet.ServletException; -import javax.servlet.ServletRequest; -import javax.servlet.ServletResponse; -import javax.servlet.http.HttpServletRequest; - import org.springframework.context.ApplicationContext; import org.springframework.web.context.support.WebApplicationContextUtils; +import javax.servlet.*; +import javax.servlet.http.HttpServletRequest; +import java.io.IOException; + /** * This filter will be applied to incoming http requests. * The current tenant id is looked up using the application * provided tenantResolver and saved to a thread local storage. - * + *

* The thread local storage is nulled out when other filters * and servlet has processed the request. + * * @author Kim A. Betti */ public class CurrentTenantServletFilter implements Filter { @@ -41,7 +34,7 @@ public void init(FilterConfig fc) throws ServletException { @Override public void doFilter(ServletRequest request, ServletResponse response, - FilterChain filterChain) throws IOException, ServletException { + FilterChain filterChain) throws IOException, ServletException { try { if (request instanceof HttpServletRequest) { diff --git a/src/java/grails/plugin/multitenant/core/spring/ConfiguredTenantScopedBeanProcessor.java b/src/java/grails/plugin/multitenant/core/spring/ConfiguredTenantScopedBeanProcessor.java index de59356..4ba45bf 100644 --- a/src/java/grails/plugin/multitenant/core/spring/ConfiguredTenantScopedBeanProcessor.java +++ b/src/java/grails/plugin/multitenant/core/spring/ConfiguredTenantScopedBeanProcessor.java @@ -1,8 +1,5 @@ package grails.plugin.multitenant.core.spring; -import java.util.ArrayList; -import java.util.List; - import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.codehaus.groovy.grails.commons.spring.BeanConfiguration; @@ -16,10 +13,14 @@ import org.springframework.beans.factory.support.DefaultListableBeanFactory; import org.springframework.beans.factory.support.GenericBeanDefinition; +import java.util.ArrayList; +import java.util.List; + /** * Creates a tenant scoped proxy around beans configured to be unique per tenant. * In short terms, this works by lazily creating a new prototyped definition of * each per-tenant bean for each tenant. + * * @author Kim A. Betti */ public class ConfiguredTenantScopedBeanProcessor implements BeanFactoryPostProcessor { @@ -53,7 +54,7 @@ private List getPerTenantBeanNames(BeanDefinitionRegistry beanFactory) { if (perTenantBeans.contains(beanName)) { perTenantBeanNames.add(beanName); - } else if (beanDefinition.getScope() == TenantScope.NAME) { + } else if (beanDefinition.getScope().equals(TenantScope.NAME)) { perTenantBeanNames.add(beanName); } } @@ -89,6 +90,7 @@ private void replaceWithScopedProxy(BeanDefinitionRegistry appCtx, String beanNa /** * Usually configured in the Grails bean DSL + * * @param perTenantBeans */ public void setPerTenantBeans(List perTenantBeans) { diff --git a/src/java/grails/plugin/multitenant/core/spring/CurrentTenantAwarePostProcessor.java b/src/java/grails/plugin/multitenant/core/spring/CurrentTenantAwarePostProcessor.java index f38143f..5f8dbe9 100644 --- a/src/java/grails/plugin/multitenant/core/spring/CurrentTenantAwarePostProcessor.java +++ b/src/java/grails/plugin/multitenant/core/spring/CurrentTenantAwarePostProcessor.java @@ -2,13 +2,13 @@ import grails.plugin.multitenant.core.CurrentTenant; import grails.plugin.multitenant.core.CurrentTenantAware; - import org.springframework.beans.BeansException; import org.springframework.beans.factory.config.BeanPostProcessor; /** * Inject the currentTenant bean into beans * implementing the CurrentTenantAware interface. + * * @author Kim A. Betti */ public class CurrentTenantAwarePostProcessor implements BeanPostProcessor { diff --git a/src/java/grails/plugin/multitenant/core/spring/TenantScope.java b/src/java/grails/plugin/multitenant/core/spring/TenantScope.java index ebe6c46..f4f07de 100644 --- a/src/java/grails/plugin/multitenant/core/spring/TenantScope.java +++ b/src/java/grails/plugin/multitenant/core/spring/TenantScope.java @@ -1,31 +1,30 @@ package grails.plugin.multitenant.core.spring; import grails.plugin.multitenant.core.CurrentTenant; - -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; - import org.springframework.beans.factory.ObjectFactory; import org.springframework.beans.factory.config.Scope; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + /** * Custom Spring scope for per-tenant Spring beans. - * + *

* Important: There is currently no listener making sure * to remove any per-tenant beans when you delete a tenant. * So if you delete a tenant this class will potentially * hold a strong reference to tenant scoped beans belonging * to the deleted tenant. - * + *

* Important: This class is expected to be thread-safe! * * @author Kim A. Betti */ public class TenantScope implements Scope, ApplicationContextAware { - public static final String NAME = "tenant"; + static final String NAME = "tenant"; private CurrentTenant currentTenant; private ApplicationContext applicationContext; diff --git a/src/java/grails/plugin/multitenant/singledb/hibernate/TenantHibernateEventListener.java b/src/java/grails/plugin/multitenant/singledb/hibernate/TenantHibernateEventListener.java index 823dc90..6408c94 100644 --- a/src/java/grails/plugin/multitenant/singledb/hibernate/TenantHibernateEventListener.java +++ b/src/java/grails/plugin/multitenant/singledb/hibernate/TenantHibernateEventListener.java @@ -6,18 +6,8 @@ import grails.plugin.multitenant.core.ast.MultiTenantAST; import grails.plugin.multitenant.core.exception.NoCurrentTenantException; import grails.plugin.multitenant.core.exception.TenantSecurityException; - import org.hibernate.HibernateException; -import org.hibernate.event.LoadEvent; -import org.hibernate.event.LoadEventListener; -import org.hibernate.event.PostLoadEvent; -import org.hibernate.event.PostLoadEventListener; -import org.hibernate.event.PreDeleteEvent; -import org.hibernate.event.PreDeleteEventListener; -import org.hibernate.event.PreInsertEvent; -import org.hibernate.event.PreInsertEventListener; -import org.hibernate.event.PreUpdateEvent; -import org.hibernate.event.PreUpdateEventListener; +import org.hibernate.event.spi.*; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -25,7 +15,7 @@ * Listens for pre-insert, pre-update and load / fetch Hibernate events. If the * domain class related to the event is a multi-tenant class we apply the * relevant constraints. - * + * * @author Kim A. Betti */ @SuppressWarnings("serial") @@ -40,7 +30,7 @@ public class TenantHibernateEventListener implements PreInsertEventListener, Pre * One important thing to know. It's not enough to update the entity * instance with the new tenant-id. Hibernate will _not_ pick up on this and * will therefore not save the updated tenant id to database. - * + *

* We have to get hold of the JPA meta model, find the index of the tenantId * field and update the entity state in the event. */ diff --git a/src/java/grails/plugin/multitenant/singledb/hibernate/TenantHibernateEventProxy.java b/src/java/grails/plugin/multitenant/singledb/hibernate/TenantHibernateEventProxy.java index 1bc7879..351628b 100644 --- a/src/java/grails/plugin/multitenant/singledb/hibernate/TenantHibernateEventProxy.java +++ b/src/java/grails/plugin/multitenant/singledb/hibernate/TenantHibernateEventProxy.java @@ -5,7 +5,6 @@ import grails.plugins.hawkeventing.EventBroker; import grails.plugins.hawkeventing.EventConsumer; import grails.util.GrailsNameUtils; - import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.InitializingBean; @@ -15,7 +14,7 @@ * Works as proxy translating Hibernate events to non-Hibernate * specific events. This class will have to be moved if we want * to make a -core plugin completely independent of Hibernate. - * + * * @author Kim A. Betti */ public class TenantHibernateEventProxy implements InitializingBean { @@ -73,8 +72,8 @@ public void setTenantClass(Class tenantClass) { */ class HawkEventProxy implements EventConsumer { - EventBroker eventBroker; - String eventName; + private EventBroker eventBroker; + private String eventName; public HawkEventProxy(EventBroker eventBroker, String eventName) { this.eventBroker = eventBroker; diff --git a/src/java/grails/plugin/multitenant/singledb/hibernate/TenantHibernateFilterConfigurator.java b/src/java/grails/plugin/multitenant/singledb/hibernate/TenantHibernateFilterConfigurator.java index bee4079..4506a0d 100644 --- a/src/java/grails/plugin/multitenant/singledb/hibernate/TenantHibernateFilterConfigurator.java +++ b/src/java/grails/plugin/multitenant/singledb/hibernate/TenantHibernateFilterConfigurator.java @@ -2,19 +2,18 @@ import grails.plugin.hibernatehijacker.hibernate.HibernateConfigPostProcessor; import grails.plugin.multitenant.core.MultiTenantDomainClass; - -import java.util.Iterator; - import org.hibernate.HibernateException; import org.hibernate.cfg.Configuration; -import org.hibernate.engine.FilterDefinition; +import org.hibernate.engine.spi.FilterDefinition; import org.hibernate.mapping.PersistentClass; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.util.Iterator; + /** * Defines the Hibernate filter. - * + *

* The Hibernate Hijacker detects beans implementing * HibernateConfigPostProcessor and will make sure that the Configuration * instances is passed through this instance when the application is starting. @@ -29,13 +28,13 @@ public class TenantHibernateFilterConfigurator implements HibernateConfigPostPro @Override public void doPostProcessing(Configuration configuration) throws HibernateException { - log.debug("Configuring multi-tenant Hibernate filter"); + log.debug("Defining MultiTenant Hibernate filter"); addMultiTenantFilterDefinition(configuration); + log.debug("Configuring multi-tenant Hibernate filter"); enrichMultiTenantDomainClasses(configuration); } private void addMultiTenantFilterDefinition(Configuration configuration) { - log.debug("Defining Multi Tenant Hibernate filer"); configuration.addFilterDefinition(multiTenantHibernateFilter); } @@ -50,7 +49,7 @@ private void enrichMultiTenantDomainClasses(Configuration configuration) { } private void enrichMultiTenantDomainClass(PersistentClass persistentClass) { - log.trace("Enabling multi-tenant mode for domain class {}", persistentClass.getClassName()); + log.debug("Enabling multi-tenant mode for domain class {}", persistentClass.getClassName()); addDomainFilter(persistentClass); } @@ -61,7 +60,7 @@ private boolean isMultiTenantClass(Class mappedClass) { private void addDomainFilter(PersistentClass persistentClass) { String filterName = multiTenantHibernateFilter.getFilterName(); String condition = multiTenantHibernateFilter.getDefaultFilterCondition(); - persistentClass.addFilter(filterName, condition); + persistentClass.addFilter(filterName, condition, false, null, null); } public void setMultiTenantHibernateFilter(FilterDefinition multiTenantHibernateFilter) { diff --git a/src/java/grails/plugin/multitenant/singledb/hibernate/TenantHibernateFilterEnabler.java b/src/java/grails/plugin/multitenant/singledb/hibernate/TenantHibernateFilterEnabler.java index e546305..05a0ce7 100644 --- a/src/java/grails/plugin/multitenant/singledb/hibernate/TenantHibernateFilterEnabler.java +++ b/src/java/grails/plugin/multitenant/singledb/hibernate/TenantHibernateFilterEnabler.java @@ -4,19 +4,19 @@ import grails.plugins.hawkeventing.Event; import grails.plugins.hawkeventing.annotation.Consuming; import grails.plugins.hawkeventing.annotation.HawkEventConsumer; - -import java.util.Set; - +import org.hibernate.Session; import org.hibernate.SessionFactory; -import org.hibernate.classic.Session; -import org.hibernate.engine.FilterDefinition; +import org.hibernate.engine.spi.FilterDefinition; import org.springframework.transaction.support.TransactionSynchronizationManager; +import java.util.Set; + /** * Subscribes itself to hibernate.sessionCreated events. Enables the tenant * Hibernate filter with the current tenant id (if any). - * + *

* Important: tenantId = null will disable the Hibernate filter! + * * @author Kim A. Betti */ @HawkEventConsumer diff --git a/src/java/grails/plugin/multitenant/spring/FilterDefinitionFactoryBean.java b/src/java/grails/plugin/multitenant/spring/FilterDefinitionFactoryBean.java new file mode 100644 index 0000000..633b865 --- /dev/null +++ b/src/java/grails/plugin/multitenant/spring/FilterDefinitionFactoryBean.java @@ -0,0 +1,143 @@ +/* + * Copyright 2002-2013 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package grails.plugin.multitenant.spring; + +import org.hibernate.engine.spi.FilterDefinition; +import org.hibernate.type.Type; +import org.hibernate.type.TypeResolver; +import org.springframework.beans.factory.BeanNameAware; +import org.springframework.beans.factory.FactoryBean; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.orm.hibernate3.LocalSessionFactoryBean; + +import java.util.HashMap; +import java.util.Map; + +/** + * Convenient FactoryBean for defining Hibernate FilterDefinitions. + * Exposes a corresponding Hibernate FilterDefinition object. + *

+ *

Typically defined as an inner bean within a LocalSessionFactoryBean + * definition, as the list element for the "filterDefinitions" bean property. + * For example: + *

+ *

+ * <bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
+ * ...
+ * <property name="filterDefinitions">
+ * <list>
+ * <bean class="grails.plugin.multitenant.spring.FilterDefinitionFactoryBean">
+ * <property name="filterName" value="myFilter"/>
+ * <property name="parameterTypes">
+ * <map>
+ * <entry key="myParam" value="string"/>
+ * <entry key="myOtherParam" value="long"/>
+ * </map>
+ * </property>
+ * </bean>
+ * </list>
+ * </property>
+ * ...
+ * </bean>
+ *

+ * Alternatively, specify a bean id (or name) attribute for the inner bean, + * instead of the "filterName" property. + * + * @author Juergen Hoeller + * @see org.hibernate.engine.spi.FilterDefinition + * @see LocalSessionFactoryBean#setFilterDefinitions + * @since 1.2 + */ +public class FilterDefinitionFactoryBean implements FactoryBean, BeanNameAware, InitializingBean { + + private final TypeResolver typeResolver = new TypeResolver(); + + private String filterName; + + private Map parameterTypeMap = new HashMap(); + + private String defaultFilterCondition; + + private FilterDefinition filterDefinition; + + + /** + * Set the name of the filter. + */ + public void setFilterName(String filterName) { + this.filterName = filterName; + } + + /** + * Set the parameter types for the filter, + * with parameter names as keys and type names as values. + * See {@code org.hibernate.type.TypeResolver#heuristicType(String)}. + */ + public void setParameterTypes(Map parameterTypes) { + if (parameterTypes != null) { + this.parameterTypeMap = new HashMap(parameterTypes.size()); + for (Map.Entry entry : parameterTypes.entrySet()) { + this.parameterTypeMap.put(entry.getKey(), this.typeResolver.heuristicType(entry.getValue())); + } + } else { + this.parameterTypeMap = new HashMap(); + } + } + + /** + * Specify a default filter condition for the filter, if any. + */ + public void setDefaultFilterCondition(String defaultFilterCondition) { + this.defaultFilterCondition = defaultFilterCondition; + } + + /** + * If no explicit filter name has been specified, the bean name of + * the FilterDefinitionFactoryBean will be used. + * + * @see #setFilterName + */ + @Override + public void setBeanName(String name) { + if (this.filterName == null) { + this.filterName = name; + } + } + + @Override + public void afterPropertiesSet() { + this.filterDefinition = + new FilterDefinition(this.filterName, this.defaultFilterCondition, this.parameterTypeMap); + } + + + @Override + public FilterDefinition getObject() { + return this.filterDefinition; + } + + @Override + public Class getObjectType() { + return FilterDefinition.class; + } + + @Override + public boolean isSingleton() { + return true; + } + +} diff --git a/test/integration/demo/DemoCustomerSpec.groovy b/test/integration/demo/DemoCustomerSpec.groovy index 7ab567a..b8e5992 100644 --- a/test/integration/demo/DemoCustomerSpec.groovy +++ b/test/integration/demo/DemoCustomerSpec.groovy @@ -1,6 +1,6 @@ package demo -import grails.plugin.spock.IntegrationSpec +import grails.test.spock.IntegrationSpec /** * Mostly as a smoke test to detect if any multi-tenant diff --git a/test/integration/demo/DemoDogSpec.groovy b/test/integration/demo/DemoDogSpec.groovy index 649ec5a..716de6e 100644 --- a/test/integration/demo/DemoDogSpec.groovy +++ b/test/integration/demo/DemoDogSpec.groovy @@ -2,7 +2,7 @@ package demo import grails.plugin.multitenant.core.MultiTenantDomainClass import grails.plugin.multitenant.core.Tenant -import grails.plugin.spock.IntegrationSpec +import grails.test.spock.IntegrationSpec /** * Dog extends Animal, but it's not stamped with the @MultiTenant diff --git a/test/integration/demo/DemoProductSpec.groovy b/test/integration/demo/DemoProductSpec.groovy index be337f4..7233f40 100644 --- a/test/integration/demo/DemoProductSpec.groovy +++ b/test/integration/demo/DemoProductSpec.groovy @@ -1,8 +1,7 @@ package demo import grails.plugin.multitenant.core.Tenant -import grails.plugin.spock.IntegrationSpec - +import grails.test.spock.IntegrationSpec import org.springframework.dao.DataIntegrityViolationException /** diff --git a/test/integration/demo/DemoTenantSpec.groovy b/test/integration/demo/DemoTenantSpec.groovy index ea633a1..3186577 100644 --- a/test/integration/demo/DemoTenantSpec.groovy +++ b/test/integration/demo/DemoTenantSpec.groovy @@ -1,9 +1,9 @@ package demo -import grails.plugin.spock.IntegrationSpec import grails.plugins.hawkeventing.Event import grails.plugins.hawkeventing.EventBroker import grails.plugins.hawkeventing.EventConsumer +import grails.test.spock.IntegrationSpec /** * @author Kim A. Betti @@ -12,7 +12,7 @@ class DemoTenantSpec extends IntegrationSpec { EventBroker eventBroker - def "New tenants triggers events" () { + def "New tenants triggers events"() { given: "Subscription to the expected event" def newTenantConsumer = Mock(EventConsumer) eventBroker.subscribe("tenant.created", newTenantConsumer) @@ -25,7 +25,7 @@ class DemoTenantSpec extends IntegrationSpec { 1 * newTenantConsumer.consume(_ as Event) } - def "Changes to a tenant triggers event" () { + def "Changes to a tenant triggers event"() { given: "Subscription to the expected event" def updatedTenantConsumer = Mock(EventConsumer) eventBroker.subscribe("tenant.updated", updatedTenantConsumer) @@ -42,7 +42,7 @@ class DemoTenantSpec extends IntegrationSpec { 1 * updatedTenantConsumer.consume(_ as Event) } - def "Deleting a tenant triggers event" () { + def "Deleting a tenant triggers event"() { given: "Subscription to the expected event" def deletedTenantConsumer = Mock(EventConsumer) eventBroker.subscribe("tenant.deleted", deletedTenantConsumer) diff --git a/test/integration/demo/ScopedServicesSpec.groovy b/test/integration/demo/ScopedServicesSpec.groovy index 65a810f..d3eb851 100644 --- a/test/integration/demo/ScopedServicesSpec.groovy +++ b/test/integration/demo/ScopedServicesSpec.groovy @@ -1,7 +1,7 @@ package demo import grails.plugin.multitenant.core.Tenant -import grails.plugin.spock.IntegrationSpec +import grails.test.spock.IntegrationSpec /** * Mostly as a smoke test to detect if any multi-tenant @@ -15,47 +15,47 @@ class ScopedServicesSpec extends IntegrationSpec { def "tenant scope is working from config"() { given: "each tenant set a val on service" - Tenant.withTenantId 1, { - assert "none" == demoService.touchedByTenant - demoService.touchedByTenant = "Tenant-1" - } + Tenant.withTenantId 1, { + assert "none" == demoService.touchedByTenant + demoService.touchedByTenant = "Tenant-1" + } - Tenant.withTenantId 2, { - assert "none" == demoService.touchedByTenant - demoService.touchedByTenant = "Tenant-2" - } + Tenant.withTenantId 2, { + assert "none" == demoService.touchedByTenant + demoService.touchedByTenant = "Tenant-2" + } expect: "we should get values that were set" - Tenant.withTenantId 1, { - println demoService.touchedByTenant - "Tenant-1" == demoService.touchedByTenant - } - Tenant.withTenantId 2, { - println demoService.touchedByTenant - "Tenant-2" == demoService.touchedByTenant - } + Tenant.withTenantId 1, { + println demoService.touchedByTenant + "Tenant-1" == demoService.touchedByTenant + } + Tenant.withTenantId 2, { + println demoService.touchedByTenant + "Tenant-2" == demoService.touchedByTenant + } } def "tenant scope is working in service"() { given: "each tenant set a val on service" - Tenant.withTenantId 1, { - assert "none" == anotherDemoService.touchedByTenant - anotherDemoService.touchedByTenant = "Tenant-1" - } + Tenant.withTenantId 1, { + assert "none" == anotherDemoService.touchedByTenant + anotherDemoService.touchedByTenant = "Tenant-1" + } - Tenant.withTenantId 2, { - assert "none" == anotherDemoService.touchedByTenant - anotherDemoService.touchedByTenant = "Tenant-2" - } + Tenant.withTenantId 2, { + assert "none" == anotherDemoService.touchedByTenant + anotherDemoService.touchedByTenant = "Tenant-2" + } expect: "we should get values that were set" - Tenant.withTenantId 1, { - println anotherDemoService.touchedByTenant - "Tenant-1" == anotherDemoService.touchedByTenant - } - Tenant.withTenantId 2, { - println anotherDemoService.touchedByTenant - "Tenant-2" == anotherDemoService.touchedByTenant - } + Tenant.withTenantId 1, { + println anotherDemoService.touchedByTenant + "Tenant-1" == anotherDemoService.touchedByTenant + } + Tenant.withTenantId 2, { + println anotherDemoService.touchedByTenant + "Tenant-2" == anotherDemoService.touchedByTenant + } } } diff --git a/test/integration/grails/plugin/multitenant/core/MultiTenantServiceSpec.groovy b/test/integration/grails/plugin/multitenant/core/MultiTenantServiceSpec.groovy index 72cb5a5..d1df976 100644 --- a/test/integration/grails/plugin/multitenant/core/MultiTenantServiceSpec.groovy +++ b/test/integration/grails/plugin/multitenant/core/MultiTenantServiceSpec.groovy @@ -1,18 +1,17 @@ package grails.plugin.multitenant.core -import grails.plugin.spock.IntegrationSpec - import demo.DemoAnimal import demo.DemoProduct import demo.DemoTenant +import grails.test.spock.IntegrationSpec /** * @author Kim A. Betti */ class MultiTenantServiceSpec extends IntegrationSpec { - def testTenant - def multiTenantService + DemoTenant testTenant + MultiTenantService multiTenantService def setup() { testTenant = new DemoTenant(name: "test tenant", domain: "test.com") @@ -21,7 +20,7 @@ class MultiTenantServiceSpec extends IntegrationSpec { def "checked exceptions should roll back transaction"() { given: - def product = null + DemoProduct product = null multiTenantService.doWithTenantId(123) { product = new DemoProduct(name: "Some product") product.save flush: true, failOnError: true @@ -44,14 +43,14 @@ class MultiTenantServiceSpec extends IntegrationSpec { def "unchecked exception should also roll back exception"() { given: - def product = null + DemoProduct product = null multiTenantService.doWithTenantId(123) { product = new DemoProduct(name: "Another product") product.save flush: true, failOnError: true } when: - tmultiTenantService.doWithTenantId(123) { + multiTenantService.doWithTenantId(123) { product.name = "Another name" product.save failOnError: true, flush: true throw new RuntimeException("Should cause exception rollback") diff --git a/test/integration/grails/plugin/multitenant/singledb/hibernate/TenantHibernateEventListenerIntegrationSpec.groovy b/test/integration/grails/plugin/multitenant/singledb/hibernate/TenantHibernateEventListenerIntegrationSpec.groovy index e662048..5b8ead2 100644 --- a/test/integration/grails/plugin/multitenant/singledb/hibernate/TenantHibernateEventListenerIntegrationSpec.groovy +++ b/test/integration/grails/plugin/multitenant/singledb/hibernate/TenantHibernateEventListenerIntegrationSpec.groovy @@ -1,14 +1,12 @@ package grails.plugin.multitenant.singledb.hibernate +import demo.DemoProduct import grails.plugin.multitenant.core.Tenant import grails.plugin.multitenant.core.exception.TenantSecurityException -import grails.plugin.spock.IntegrationSpec - +import grails.test.spock.IntegrationSpec import org.hibernate.Session import org.hibernate.SessionFactory - import spock.lang.FailsWith -import demo.DemoProduct /** * These tests are also good for highlighting various aspects diff --git a/test/integration/grails/plugin/multitenant/singledb/hibernate/TenantHibernateFilterEnablerSpec.groovy b/test/integration/grails/plugin/multitenant/singledb/hibernate/TenantHibernateFilterEnablerSpec.groovy index 541bea5..3b5b7e9 100644 --- a/test/integration/grails/plugin/multitenant/singledb/hibernate/TenantHibernateFilterEnablerSpec.groovy +++ b/test/integration/grails/plugin/multitenant/singledb/hibernate/TenantHibernateFilterEnablerSpec.groovy @@ -1,15 +1,13 @@ package grails.plugin.multitenant.singledb.hibernate -import grails.plugin.spock.IntegrationSpec import grails.plugins.hawkeventing.BaseEvent import grails.plugins.hawkeventing.Event - +import grails.test.spock.IntegrationSpec import org.hibernate.Filter +import org.hibernate.Session import org.hibernate.SessionFactory -import org.hibernate.classic.Session -import org.hibernate.engine.FilterDefinition -import org.hibernate.impl.FilterImpl - +import org.hibernate.engine.spi.FilterDefinition +import org.hibernate.internal.FilterImpl import spock.lang.Unroll class TenantHibernateFilterEnablerSpec extends IntegrationSpec { @@ -41,12 +39,12 @@ class TenantHibernateFilterEnablerSpec extends IntegrationSpec { then: Session session = sessionFactory.getCurrentSession() String multiTenantFilterName = multiTenantHibernateFilter.filterName - FilterImpl enabledFilter = session.getEnabledFilter(multiTenantFilterName) + FilterImpl enabledFilter = session.getEnabledFilter(multiTenantFilterName) as FilterImpl and: enabledFilter.getParameter("tenantId") == tenantId where: - tenantId << [ -1, 0, 1 ] + tenantId << [-1, 0, 1] } } diff --git a/test/unit/grails/plugin/multitenant/core/ast/MultiTenantASTSpec.groovy b/test/unit/grails/plugin/multitenant/core/ast/MultiTenantASTSpec.groovy index ef0a415..543a254 100644 --- a/test/unit/grails/plugin/multitenant/core/ast/MultiTenantASTSpec.groovy +++ b/test/unit/grails/plugin/multitenant/core/ast/MultiTenantASTSpec.groovy @@ -2,14 +2,14 @@ package grails.plugin.multitenant.core.ast import grails.plugin.multitenant.core.MultiTenantDomainClass import grails.plugin.multitenant.core.annotation.MultiTenant -import grails.plugin.spock.UnitSpec +import spock.lang.Specification import java.lang.reflect.Field /** * @author Kim A. Betti */ -class MultiTenantASTSpec extends UnitSpec { +class MultiTenantASTSpec extends Specification { def "should have a tenantId field"() { expect: diff --git a/test/unit/grails/plugin/multitenant/core/servlet/CurrentTenantServletFilterSpec.groovy b/test/unit/grails/plugin/multitenant/core/servlet/CurrentTenantServletFilterSpec.groovy index e91d8e7..218e95a 100644 --- a/test/unit/grails/plugin/multitenant/core/servlet/CurrentTenantServletFilterSpec.groovy +++ b/test/unit/grails/plugin/multitenant/core/servlet/CurrentTenantServletFilterSpec.groovy @@ -2,20 +2,19 @@ package grails.plugin.multitenant.core.servlet import grails.plugin.multitenant.core.CurrentTenant import grails.plugin.multitenant.core.resolve.TenantResolver -import grails.plugin.spock.UnitSpec +import org.springframework.mock.web.MockHttpServletRequest +import org.springframework.mock.web.MockHttpServletResponse +import spock.lang.Specification import javax.servlet.FilterChain import javax.servlet.ServletRequest import javax.servlet.ServletResponse import javax.servlet.http.HttpServletRequest -import org.springframework.mock.web.MockHttpServletRequest -import org.springframework.mock.web.MockHttpServletResponse - /** * @author Kim A. Betti */ -class CurrentTenantServletFilterSpec extends UnitSpec { +class CurrentTenantServletFilterSpec extends Specification { def "current tenant should be sat to null after an exception in the filter chain"() { given: "a filter instance and mocked tenant classes" diff --git a/test/unit/grails/plugin/multitenant/singledb/MtSingleDbPluginSupportSpec.groovy b/test/unit/grails/plugin/multitenant/singledb/MtSingleDbPluginSupportSpec.groovy index 2323c6a..9c20796 100644 --- a/test/unit/grails/plugin/multitenant/singledb/MtSingleDbPluginSupportSpec.groovy +++ b/test/unit/grails/plugin/multitenant/singledb/MtSingleDbPluginSupportSpec.groovy @@ -3,12 +3,12 @@ package grails.plugin.multitenant.singledb import grails.plugin.multitenant.core.MultiTenantService import grails.plugin.multitenant.core.Tenant import grails.plugin.multitenant.core.exception.TenantException -import grails.plugin.spock.UnitSpec +import spock.lang.Specification /** * @author Kim A. Betti */ -class MtSingleDbPluginSupportSpec extends UnitSpec { +class MtSingleDbPluginSupportSpec extends Specification { def "add withTenantId method"() { given: @@ -73,6 +73,7 @@ class MtSingleDbPluginSupportSpec extends UnitSpec { private class MetaTenant implements Tenant { Integer id Integer tenantId = 123 + Integer tenantId() { tenantId } } } diff --git a/test/unit/grails/plugin/multitenant/singledb/hibernate/TenantHibernateEventListenerSpec.groovy b/test/unit/grails/plugin/multitenant/singledb/hibernate/TenantHibernateEventListenerSpec.groovy index b8c3d9a..0e1d9a2 100644 --- a/test/unit/grails/plugin/multitenant/singledb/hibernate/TenantHibernateEventListenerSpec.groovy +++ b/test/unit/grails/plugin/multitenant/singledb/hibernate/TenantHibernateEventListenerSpec.groovy @@ -5,18 +5,16 @@ import grails.plugin.multitenant.core.CurrentTenant import grails.plugin.multitenant.core.MultiTenantDomainClass import grails.plugin.multitenant.core.exception.NoCurrentTenantException import grails.plugin.multitenant.core.exception.TenantSecurityException -import grails.plugin.spock.UnitSpec - -import org.hibernate.event.PreDeleteEvent -import org.hibernate.event.PreInsertEvent -import org.hibernate.event.PreUpdateEvent - +import org.hibernate.event.spi.PreDeleteEvent +import org.hibernate.event.spi.PreInsertEvent +import org.hibernate.event.spi.PreUpdateEvent +import spock.lang.Specification import spock.lang.Unroll /** * @author Kim A. Betti */ -class TenantHibernateEventListenerSpec extends UnitSpec { +class TenantHibernateEventListenerSpec extends Specification { TenantHibernateEventListener eventListener @@ -53,7 +51,7 @@ class TenantHibernateEventListenerSpec extends UnitSpec { 1 * eventListener.currentTenant.get() >> 123 and: "mocked event state updater" - HibernateEventPropertyUpdater propertyUpdater = Mock() + HibernateEventPropertyUpdater propertyUpdater = Mock(HibernateEventPropertyUpdater) eventListener.hibernateEventPropertyUpdater = propertyUpdater when: "we invoke the event method" @@ -66,7 +64,7 @@ class TenantHibernateEventListenerSpec extends UnitSpec { entity.tenantId == 123 and: "we dont veto the event" - vetoInsert == false + !vetoInsert } @Unroll("Allow #currentTenantId to allow an entity owned by #entityTenantId, #message") @@ -76,11 +74,11 @@ class TenantHibernateEventListenerSpec extends UnitSpec { eventListener.allowEntityLoad(currentTenantId, entity) == shouldAllow where: - currentTenantId | entityTenantId | shouldAllow | message - null | 123 | true | "No tenant, no restriction" - 123 | 123 | true | "Must be able to load its own entities" - 123 | 321 | false | "Should not be allowed to load others" - 123 | null | false | "We dont allow loading of entities without tenant id" // Can be discussed.. + currentTenantId | entityTenantId | shouldAllow | message + null | 123 | true | "No tenant, no restriction" + 123 | 123 | true | "Must be able to load its own entities" + 123 | 321 | false | "Should not be allowed to load others" + 123 | null | false | "We dont allow loading of entities without tenant id" // Can be discussed.. } @Unroll("Tenant id #tenantId, current tenant #currentTenantId, belongs to current tenant #belongsToCurrent") @@ -90,11 +88,11 @@ class TenantHibernateEventListenerSpec extends UnitSpec { eventListener.belongsToCurrentTenant(currentTenantId, entity) == belongsToCurrent where: - currentTenantId | entityTenantId | belongsToCurrent - 123 | 123 | true - 123 | null | false - null | 123 | false - null | null | false + currentTenantId | entityTenantId | belongsToCurrent + 123 | 123 | true + 123 | null | false + null | 123 | false + null | null | false } def "update without tenant id is allowed"() { @@ -105,7 +103,7 @@ class TenantHibernateEventListenerSpec extends UnitSpec { expect: "the listener should not veto the event" def entity = new DummyEntity(tenantId: 123) def preUpdateEvent = new PreUpdateEvent(entity, null, null, null, null, null) - eventListener.onPreUpdate(preUpdateEvent) == false + !eventListener.onPreUpdate(preUpdateEvent) } def "delete without tenant id is allowed"() { @@ -116,7 +114,7 @@ class TenantHibernateEventListenerSpec extends UnitSpec { expect: "the listener should not veto the event" def entity = new DummyEntity(tenantId: 123) def preUpdateEvent = new PreDeleteEvent(entity, null, null, null, null) - eventListener.onPreDelete(preUpdateEvent) == false + !eventListener.onPreDelete(preUpdateEvent) } def "attempts to update another tenants entity should throw an exception"() { diff --git a/test/unit/grails/plugin/multitenant/singledb/hibernate/TenantHibernateEventProxySpec.groovy b/test/unit/grails/plugin/multitenant/singledb/hibernate/TenantHibernateEventProxySpec.groovy index 3eb9a64..88f2f03 100644 --- a/test/unit/grails/plugin/multitenant/singledb/hibernate/TenantHibernateEventProxySpec.groovy +++ b/test/unit/grails/plugin/multitenant/singledb/hibernate/TenantHibernateEventProxySpec.groovy @@ -1,17 +1,16 @@ package grails.plugin.multitenant.singledb.hibernate -import grails.plugin.spock.UnitSpec +import demo.DemoTenant import grails.plugins.hawkeventing.BaseEvent import grails.plugins.hawkeventing.Event import grails.plugins.hawkeventing.EventBroker import grails.plugins.hawkeventing.EventConsumer - -import demo.DemoTenant +import spock.lang.Specification /** * @author Kim A. Betti */ -class TenantHibernateEventProxySpec extends UnitSpec { +class TenantHibernateEventProxySpec extends Specification { def "subscriptions are added for relevant tenant events"() { given: diff --git a/test/unit/grails/plugin/multitenant/singledb/hibernate/TenantHibernateFilterEnablerUnitSpec.groovy b/test/unit/grails/plugin/multitenant/singledb/hibernate/TenantHibernateFilterEnablerUnitSpec.groovy index 063f59f..338a843 100644 --- a/test/unit/grails/plugin/multitenant/singledb/hibernate/TenantHibernateFilterEnablerUnitSpec.groovy +++ b/test/unit/grails/plugin/multitenant/singledb/hibernate/TenantHibernateFilterEnablerUnitSpec.groovy @@ -1,22 +1,20 @@ package grails.plugin.multitenant.singledb.hibernate import grails.plugin.multitenant.core.CurrentTenant -import grails.plugin.spock.UnitSpec import grails.plugins.hawkeventing.BaseEvent import grails.plugins.hawkeventing.Event - import org.hibernate.Filter -import org.hibernate.classic.Session -import org.hibernate.engine.FilterDefinition +import org.hibernate.Session +import org.hibernate.engine.spi.FilterDefinition +import spock.lang.Specification /** * @author Kim A. Betti */ -class TenantHibernateFilterEnablerUnitSpec extends UnitSpec { +class TenantHibernateFilterEnablerUnitSpec extends Specification { TenantHibernateFilterEnabler filterEnabler - FilterDefinition filterDefinition = new FilterDefinition ( - "multiTenantHibernateFilter", ":tenantId = tennatId", [ "tenantId": "java.lang.Integer" ]) + FilterDefinition filterDefinition = new FilterDefinition("multiTenantHibernateFilter", ":tenantId = tennatId", ["tenantId": "java.lang.Integer"]) def setup() { filterEnabler = new TenantHibernateFilterEnabler() diff --git a/web-app/WEB-INF/applicationContext.xml b/web-app/WEB-INF/applicationContext.xml new file mode 100644 index 0000000..130e70d --- /dev/null +++ b/web-app/WEB-INF/applicationContext.xml @@ -0,0 +1,31 @@ + + + + + Grails application factory bean + + + + + A bean that manages Grails plugins + + + + + + + + + + + + + + utf-8 + + + + + \ No newline at end of file diff --git a/web-app/WEB-INF/sitemesh.xml b/web-app/WEB-INF/sitemesh.xml new file mode 100644 index 0000000..72399ce --- /dev/null +++ b/web-app/WEB-INF/sitemesh.xml @@ -0,0 +1,14 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/web-app/WEB-INF/tld/c.tld b/web-app/WEB-INF/tld/c.tld new file mode 100644 index 0000000..5e18236 --- /dev/null +++ b/web-app/WEB-INF/tld/c.tld @@ -0,0 +1,572 @@ + + + + + JSTL 1.2 core library + JSTL core + 1.2 + c + http://java.sun.com/jsp/jstl/core + + + + Provides core validation features for JSTL tags. + + + org.apache.taglibs.standard.tlv.JstlCoreTLV + + + + + + Catches any Throwable that occurs in its body and optionally + exposes it. + + catch + org.apache.taglibs.standard.tag.common.core.CatchTag + JSP + + +Name of the exported scoped variable for the +exception thrown from a nested action. The type of the +scoped variable is the type of the exception thrown. + + var + false + false + + + + + + Simple conditional tag that establishes a context for + mutually exclusive conditional operations, marked by + <when> and <otherwise> + + choose + org.apache.taglibs.standard.tag.common.core.ChooseTag + JSP + + + + + Simple conditional tag, which evalutes its body if the + supplied condition is true and optionally exposes a Boolean + scripting variable representing the evaluation of this condition + + if + org.apache.taglibs.standard.tag.rt.core.IfTag + JSP + + +The test condition that determines whether or +not the body content should be processed. + + test + true + true + boolean + + + +Name of the exported scoped variable for the +resulting value of the test condition. The type +of the scoped variable is Boolean. + + var + false + false + + + +Scope for var. + + scope + false + false + + + + + + Retrieves an absolute or relative URL and exposes its contents + to either the page, a String in 'var', or a Reader in 'varReader'. + + import + org.apache.taglibs.standard.tag.rt.core.ImportTag + org.apache.taglibs.standard.tei.ImportTEI + JSP + + +The URL of the resource to import. + + url + true + true + + + +Name of the exported scoped variable for the +resource's content. The type of the scoped +variable is String. + + var + false + false + + + +Scope for var. + + scope + false + false + + + +Name of the exported scoped variable for the +resource's content. The type of the scoped +variable is Reader. + + varReader + false + false + + + +Name of the context when accessing a relative +URL resource that belongs to a foreign +context. + + context + false + true + + + +Character encoding of the content at the input +resource. + + charEncoding + false + true + + + + + + The basic iteration tag, accepting many different + collection types and supporting subsetting and other + functionality + + forEach + org.apache.taglibs.standard.tag.rt.core.ForEachTag + org.apache.taglibs.standard.tei.ForEachTEI + JSP + + +Collection of items to iterate over. + + items + false + true + java.lang.Object + + java.lang.Object + + + + +If items specified: +Iteration begins at the item located at the +specified index. First item of the collection has +index 0. +If items not specified: +Iteration begins with index set at the value +specified. + + begin + false + true + int + + + +If items specified: +Iteration ends at the item located at the +specified index (inclusive). +If items not specified: +Iteration ends when index reaches the value +specified. + + end + false + true + int + + + +Iteration will only process every step items of +the collection, starting with the first one. + + step + false + true + int + + + +Name of the exported scoped variable for the +current item of the iteration. This scoped +variable has nested visibility. Its type depends +on the object of the underlying collection. + + var + false + false + + + +Name of the exported scoped variable for the +status of the iteration. Object exported is of type +javax.servlet.jsp.jstl.core.LoopTagStatus. This scoped variable has nested +visibility. + + varStatus + false + false + + + + + + Iterates over tokens, separated by the supplied delimeters + + forTokens + org.apache.taglibs.standard.tag.rt.core.ForTokensTag + JSP + + +String of tokens to iterate over. + + items + true + true + java.lang.String + + java.lang.String + + + + +The set of delimiters (the characters that +separate the tokens in the string). + + delims + true + true + java.lang.String + + + +Iteration begins at the token located at the +specified index. First token has index 0. + + begin + false + true + int + + + +Iteration ends at the token located at the +specified index (inclusive). + + end + false + true + int + + + +Iteration will only process every step tokens +of the string, starting with the first one. + + step + false + true + int + + + +Name of the exported scoped variable for the +current item of the iteration. This scoped +variable has nested visibility. + + var + false + false + + + +Name of the exported scoped variable for the +status of the iteration. Object exported is of +type +javax.servlet.jsp.jstl.core.LoopTag +Status. This scoped variable has nested +visibility. + + varStatus + false + false + + + + + + Like <%= ... >, but for expressions. + + out + org.apache.taglibs.standard.tag.rt.core.OutTag + JSP + + +Expression to be evaluated. + + value + true + true + + + +Default value if the resulting value is null. + + default + false + true + + + +Determines whether characters <,>,&,'," in the +resulting string should be converted to their +corresponding character entity codes. Default value is +true. + + escapeXml + false + true + + + + + + + Subtag of <choose> that follows <when> tags + and runs only if all of the prior conditions evaluated to + 'false' + + otherwise + org.apache.taglibs.standard.tag.common.core.OtherwiseTag + JSP + + + + + Adds a parameter to a containing 'import' tag's URL. + + param + org.apache.taglibs.standard.tag.rt.core.ParamTag + JSP + + +Name of the query string parameter. + + name + true + true + + + +Value of the parameter. + + value + false + true + + + + + + Redirects to a new URL. + + redirect + org.apache.taglibs.standard.tag.rt.core.RedirectTag + JSP + + +The URL of the resource to redirect to. + + url + false + true + + + +Name of the context when redirecting to a relative URL +resource that belongs to a foreign context. + + context + false + true + + + + + + Removes a scoped variable (from a particular scope, if specified). + + remove + org.apache.taglibs.standard.tag.common.core.RemoveTag + empty + + +Name of the scoped variable to be removed. + + var + true + false + + + +Scope for var. + + scope + false + false + + + + + + Sets the result of an expression evaluation in a 'scope' + + set + org.apache.taglibs.standard.tag.rt.core.SetTag + JSP + + +Name of the exported scoped variable to hold the value +specified in the action. The type of the scoped variable is +whatever type the value expression evaluates to. + + var + false + false + + + +Expression to be evaluated. + + value + false + true + + java.lang.Object + + + + +Target object whose property will be set. Must evaluate to +a JavaBeans object with setter property property, or to a +java.util.Map object. + + target + false + true + + + +Name of the property to be set in the target object. + + property + false + true + + + +Scope for var. + + scope + false + false + + + + + + Creates a URL with optional query parameters. + + url + org.apache.taglibs.standard.tag.rt.core.UrlTag + JSP + + +Name of the exported scoped variable for the +processed url. The type of the scoped variable is +String. + + var + false + false + + + +Scope for var. + + scope + false + false + + + +URL to be processed. + + value + false + true + + + +Name of the context when specifying a relative URL +resource that belongs to a foreign context. + + context + false + true + + + + + + Subtag of <choose> that includes its body if its + condition evalutes to 'true' + + when + org.apache.taglibs.standard.tag.rt.core.WhenTag + JSP + + +The test condition that determines whether or not the +body content should be processed. + + test + true + true + boolean + + + + diff --git a/web-app/WEB-INF/tld/fmt.tld b/web-app/WEB-INF/tld/fmt.tld new file mode 100644 index 0000000..2ae4776 --- /dev/null +++ b/web-app/WEB-INF/tld/fmt.tld @@ -0,0 +1,671 @@ + + + + + JSTL 1.2 i18n-capable formatting library + JSTL fmt + 1.2 + fmt + http://java.sun.com/jsp/jstl/fmt + + + + Provides core validation features for JSTL tags. + + + org.apache.taglibs.standard.tlv.JstlFmtTLV + + + + + + Sets the request character encoding + + requestEncoding + org.apache.taglibs.standard.tag.rt.fmt.RequestEncodingTag + empty + + +Name of character encoding to be applied when +decoding request parameters. + + value + false + true + + + + + + Stores the given locale in the locale configuration variable + + setLocale + org.apache.taglibs.standard.tag.rt.fmt.SetLocaleTag + empty + + +A String value is interpreted as the +printable representation of a locale, which +must contain a two-letter (lower-case) +language code (as defined by ISO-639), +and may contain a two-letter (upper-case) +country code (as defined by ISO-3166). +Language and country codes must be +separated by hyphen (-) or underscore +(_). + + value + true + true + + + +Vendor- or browser-specific variant. +See the java.util.Locale javadocs for +more information on variants. + + variant + false + true + + + +Scope of the locale configuration variable. + + scope + false + false + + + + + + Specifies the time zone for any time formatting or parsing actions + nested in its body + + timeZone + org.apache.taglibs.standard.tag.rt.fmt.TimeZoneTag + JSP + + +The time zone. A String value is interpreted as +a time zone ID. This may be one of the time zone +IDs supported by the Java platform (such as +"America/Los_Angeles") or a custom time zone +ID (such as "GMT-8"). See +java.util.TimeZone for more information on +supported time zone formats. + + value + true + true + + + + + + Stores the given time zone in the time zone configuration variable + + setTimeZone + org.apache.taglibs.standard.tag.rt.fmt.SetTimeZoneTag + empty + + +The time zone. A String value is interpreted as +a time zone ID. This may be one of the time zone +IDs supported by the Java platform (such as +"America/Los_Angeles") or a custom time zone +ID (such as "GMT-8"). See java.util.TimeZone for +more information on supported time zone +formats. + + value + true + true + + + +Name of the exported scoped variable which +stores the time zone of type +java.util.TimeZone. + + var + false + false + + + +Scope of var or the time zone configuration +variable. + + scope + false + false + + + + + + Loads a resource bundle to be used by its tag body + + bundle + org.apache.taglibs.standard.tag.rt.fmt.BundleTag + JSP + + +Resource bundle base name. This is the bundle's +fully-qualified resource name, which has the same +form as a fully-qualified class name, that is, it uses +"." as the package component separator and does not +have any file type (such as ".class" or ".properties") +suffix. + + basename + true + true + + + +Prefix to be prepended to the value of the message +key of any nested <fmt:message> action. + + prefix + false + true + + + + + + Loads a resource bundle and stores it in the named scoped variable or + the bundle configuration variable + + setBundle + org.apache.taglibs.standard.tag.rt.fmt.SetBundleTag + empty + + +Resource bundle base name. This is the bundle's +fully-qualified resource name, which has the same +form as a fully-qualified class name, that is, it uses +"." as the package component separator and does not +have any file type (such as ".class" or ".properties") +suffix. + + basename + true + true + + + +Name of the exported scoped variable which stores +the i18n localization context of type +javax.servlet.jsp.jstl.fmt.LocalizationC +ontext. + + var + false + false + + + +Scope of var or the localization context +configuration variable. + + scope + false + false + + + + + + Maps key to localized message and performs parametric replacement + + message + org.apache.taglibs.standard.tag.rt.fmt.MessageTag + JSP + + +Message key to be looked up. + + key + false + true + + + +Localization context in whose resource +bundle the message key is looked up. + + bundle + false + true + + + +Name of the exported scoped variable +which stores the localized message. + + var + false + false + + + +Scope of var. + + scope + false + false + + + + + + Supplies an argument for parametric replacement to a containing + <message> tag + + param + org.apache.taglibs.standard.tag.rt.fmt.ParamTag + JSP + + +Argument used for parametric replacement. + + value + false + true + + + + + + Formats a numeric value as a number, currency, or percentage + + formatNumber + org.apache.taglibs.standard.tag.rt.fmt.FormatNumberTag + JSP + + +Numeric value to be formatted. + + value + false + true + + + +Specifies whether the value is to be +formatted as number, currency, or +percentage. + + type + false + true + + + +Custom formatting pattern. + + pattern + false + true + + + +ISO 4217 currency code. Applied only +when formatting currencies (i.e. if type is +equal to "currency"); ignored otherwise. + + currencyCode + false + true + + + +Currency symbol. Applied only when +formatting currencies (i.e. if type is equal +to "currency"); ignored otherwise. + + currencySymbol + false + true + + + +Specifies whether the formatted output +will contain any grouping separators. + + groupingUsed + false + true + + + +Maximum number of digits in the integer +portion of the formatted output. + + maxIntegerDigits + false + true + + + +Minimum number of digits in the integer +portion of the formatted output. + + minIntegerDigits + false + true + + + +Maximum number of digits in the +fractional portion of the formatted output. + + maxFractionDigits + false + true + + + +Minimum number of digits in the +fractional portion of the formatted output. + + minFractionDigits + false + true + + + +Name of the exported scoped variable +which stores the formatted result as a +String. + + var + false + false + + + +Scope of var. + + scope + false + false + + + + + + Parses the string representation of a number, currency, or percentage + + parseNumber + org.apache.taglibs.standard.tag.rt.fmt.ParseNumberTag + JSP + + +String to be parsed. + + value + false + true + + + +Specifies whether the string in the value +attribute should be parsed as a number, +currency, or percentage. + + type + false + true + + + +Custom formatting pattern that determines +how the string in the value attribute is to be +parsed. + + pattern + false + true + + + +Locale whose default formatting pattern (for +numbers, currencies, or percentages, +respectively) is to be used during the parse +operation, or to which the pattern specified +via the pattern attribute (if present) is +applied. + + parseLocale + false + true + + + +Specifies whether just the integer portion of +the given value should be parsed. + + integerOnly + false + true + + + +Name of the exported scoped variable which +stores the parsed result (of type +java.lang.Number). + + var + false + false + + + +Scope of var. + + scope + false + false + + + + + + Formats a date and/or time using the supplied styles and pattern + + formatDate + org.apache.taglibs.standard.tag.rt.fmt.FormatDateTag + empty + + +Date and/or time to be formatted. + + value + true + true + + + +Specifies whether the time, the date, or both +the time and date components of the given +date are to be formatted. + + type + false + true + + + +Predefined formatting style for dates. Follows +the semantics defined in class +java.text.DateFormat. Applied only +when formatting a date or both a date and +time (i.e. if type is missing or is equal to +"date" or "both"); ignored otherwise. + + dateStyle + false + true + + + +Predefined formatting style for times. Follows +the semantics defined in class +java.text.DateFormat. Applied only +when formatting a time or both a date and +time (i.e. if type is equal to "time" or "both"); +ignored otherwise. + + timeStyle + false + true + + + +Custom formatting style for dates and times. + + pattern + false + true + + + +Time zone in which to represent the formatted +time. + + timeZone + false + true + + + +Name of the exported scoped variable which +stores the formatted result as a String. + + var + false + false + + + +Scope of var. + + scope + false + false + + + + + + Parses the string representation of a date and/or time + + parseDate + org.apache.taglibs.standard.tag.rt.fmt.ParseDateTag + JSP + + +Date string to be parsed. + + value + false + true + + + +Specifies whether the date string in the +value attribute is supposed to contain a +time, a date, or both. + + type + false + true + + + +Predefined formatting style for days +which determines how the date +component of the date string is to be +parsed. Applied only when formatting a +date or both a date and time (i.e. if type +is missing or is equal to "date" or "both"); +ignored otherwise. + + dateStyle + false + true + + + +Predefined formatting styles for times +which determines how the time +component in the date string is to be +parsed. Applied only when formatting a +time or both a date and time (i.e. if type +is equal to "time" or "both"); ignored +otherwise. + + timeStyle + false + true + + + +Custom formatting pattern which +determines how the date string is to be +parsed. + + pattern + false + true + + + +Time zone in which to interpret any time +information in the date string. + + timeZone + false + true + + + +Locale whose predefined formatting styles +for dates and times are to be used during +the parse operation, or to which the +pattern specified via the pattern +attribute (if present) is applied. + + parseLocale + false + true + + + +Name of the exported scoped variable in +which the parsing result (of type +java.util.Date) is stored. + + var + false + false + + + +Scope of var. + + scope + false + false + + + + diff --git a/web-app/WEB-INF/tld/grails.tld b/web-app/WEB-INF/tld/grails.tld new file mode 100644 index 0000000..9bd036b --- /dev/null +++ b/web-app/WEB-INF/tld/grails.tld @@ -0,0 +1,550 @@ + + + The Grails custom tag library + 0.2 + grails + http://grails.codehaus.org/tags + + + link + org.codehaus.groovy.grails.web.taglib.jsp.JspLinkTag + JSP + + action + false + true + + + controller + false + true + + + id + false + true + + + url + false + true + + + params + false + true + + true + + + form + org.codehaus.groovy.grails.web.taglib.jsp.JspFormTag + JSP + + action + false + true + + + controller + false + true + + + id + false + true + + + url + false + true + + + method + true + true + + true + + + select + org.codehaus.groovy.grails.web.taglib.jsp.JspSelectTag + JSP + + name + true + true + + + value + false + true + + + optionKey + false + true + + + optionValue + false + true + + true + + + datePicker + org.codehaus.groovy.grails.web.taglib.jsp.JspDatePickerTag + empty + + name + true + true + + + value + false + true + + + precision + false + true + + false + + + currencySelect + org.codehaus.groovy.grails.web.taglib.jsp.JspCurrencySelectTag + empty + + name + true + true + + + value + false + true + + true + + + localeSelect + org.codehaus.groovy.grails.web.taglib.jsp.JspLocaleSelectTag + empty + + name + true + true + + + value + false + true + + true + + + timeZoneSelect + org.codehaus.groovy.grails.web.taglib.jsp.JspTimeZoneSelectTag + empty + + name + true + true + + + value + false + true + + true + + + checkBox + org.codehaus.groovy.grails.web.taglib.jsp.JspCheckboxTag + empty + + name + true + true + + + value + true + true + + true + + + hasErrors + org.codehaus.groovy.grails.web.taglib.jsp.JspHasErrorsTag + JSP + + model + false + true + + + bean + false + true + + + field + false + true + + false + + + eachError + org.codehaus.groovy.grails.web.taglib.jsp.JspEachErrorTag + JSP + + model + false + true + + + bean + false + true + + + field + false + true + + false + + + renderErrors + org.codehaus.groovy.grails.web.taglib.jsp.JspEachErrorTag + JSP + + model + false + true + + + bean + false + true + + + field + false + true + + + as + true + true + + false + + + message + org.codehaus.groovy.grails.web.taglib.jsp.JspMessageTag + JSP + + code + false + true + + + error + false + true + + + default + false + true + + false + + + remoteFunction + org.codehaus.groovy.grails.web.taglib.jsp.JspRemoteFunctionTag + empty + + before + false + true + + + after + false + true + + + action + false + true + + + controller + false + true + + + id + false + true + + + url + false + true + + + params + false + true + + + asynchronous + false + true + + + method + false + true + + + update + false + true + + + onSuccess + false + true + + + onFailure + false + true + + + onComplete + false + true + + + onLoading + false + true + + + onLoaded + false + true + + + onInteractive + false + true + + true + + + remoteLink + org.codehaus.groovy.grails.web.taglib.jsp.JspRemoteLinkTag + JSP + + before + false + true + + + after + false + true + + + action + false + true + + + controller + false + true + + + id + false + true + + + url + false + true + + + params + false + true + + + asynchronous + false + true + + + method + false + true + + + update + false + true + + + onSuccess + false + true + + + onFailure + false + true + + + onComplete + false + true + + + onLoading + false + true + + + onLoaded + false + true + + + onInteractive + false + true + + true + + + formRemote + org.codehaus.groovy.grails.web.taglib.jsp.JspFormRemoteTag + JSP + + before + false + true + + + after + false + true + + + action + false + true + + + controller + false + true + + + id + false + true + + + url + false + true + + + params + false + true + + + asynchronous + false + true + + + method + false + true + + + update + false + true + + + onSuccess + false + true + + + onFailure + false + true + + + onComplete + false + true + + + onLoading + false + true + + + onLoaded + false + true + + + onInteractive + false + true + + true + + + invokeTag + org.codehaus.groovy.grails.web.taglib.jsp.JspInvokeGrailsTagLibTag + JSP + + it + java.lang.Object + true + NESTED + + + tagName + true + true + + true + + + diff --git a/web-app/WEB-INF/tld/spring-form.tld b/web-app/WEB-INF/tld/spring-form.tld new file mode 100644 index 0000000..1520a68 --- /dev/null +++ b/web-app/WEB-INF/tld/spring-form.tld @@ -0,0 +1,2411 @@ + + + + Spring Framework JSP Form Tag Library + 3.0 + form + http://www.springframework.org/tags/form + + + Renders an HTML 'form' tag and exposes a binding path to inner tags for binding. + form + org.springframework.web.servlet.tags.form.FormTag + JSP + + HTML Standard Attribute + id + false + true + + + HTML Standard Attribute - added for backwards compatibility cases + name + false + true + + + Enable/disable HTML escaping of rendered values. + htmlEscape + false + true + + + Equivalent to "class" - HTML Optional Attribute + cssClass + false + true + + + Equivalent to "style" - HTML Optional Attribute + cssStyle + false + true + + + HTML Standard Attribute + lang + false + true + + + HTML Standard Attribute + title + false + true + + + HTML Standard Attribute + dir + false + true + + + HTML Event Attribute + onclick + false + true + + + HTML Event Attribute + ondblclick + false + true + + + HTML Event Attribute + onmousedown + false + true + + + HTML Event Attribute + onmouseup + false + true + + + HTML Event Attribute + onmouseover + false + true + + + HTML Event Attribute + onmousemove + false + true + + + HTML Event Attribute + onmouseout + false + true + + + HTML Event Attribute + onkeypress + false + true + + + HTML Event Attribute + onkeyup + false + true + + + HTML Event Attribute + onkeydown + false + true + + + Name of the model attribute under which the form object is exposed. + Defaults to 'command'. + modelAttribute + false + true + + + Name of the model attribute under which the form object is exposed. + Defaults to 'command'. + commandName + false + true + + + HTML Required Attribute + action + false + true + + + HTML Optional Attribute + method + false + true + + + HTML Optional Attribute + target + false + true + + + HTML Optional Attribute + enctype + false + true + + + Specifies the list of character encodings for input data that is accepted by the server processing this form. The value is a space- and/or comma-delimited list of charset values. The client must interpret this list as an exclusive-or list, i.e., the server is able to accept any single character encoding per entity received. + acceptCharset + false + true + + + HTML Event Attribute + onsubmit + false + true + + + HTML Event Attribute + onreset + false + true + + + Common Optional Attribute + autocomplete + false + true + + + The parameter name used for HTTP methods other then GET and POST. Default is '_method' + methodParam + false + true + + true + + + + Renders an HTML 'input' tag with type 'text' using the bound value. + input + org.springframework.web.servlet.tags.form.InputTag + empty + + Path to property for data binding + path + true + true + + + HTML Standard Attribute + id + false + true + + + Enable/disable HTML escaping of rendered values. + htmlEscape + false + true + + + Equivalent to "class" - HTML Optional Attribute + cssClass + false + true + + + Equivalent to "class" - HTML Optional Attribute. Used when the bound field has errors. + cssErrorClass + false + true + + + Equivalent to "style" - HTML Optional Attribute + cssStyle + false + true + + + HTML Standard Attribute + lang + false + true + + + HTML Standard Attribute + title + false + true + + + HTML Standard Attribute + dir + false + true + + + HTML Standard Attribute + tabindex + false + true + + + HTML Optional Attribute. Setting the value of this attribute to 'true' (without the quotes) will disable the HTML element. + disabled + false + true + + + HTML Event Attribute + onclick + false + true + + + HTML Event Attribute + ondblclick + false + true + + + HTML Event Attribute + onmousedown + false + true + + + HTML Event Attribute + onmouseup + false + true + + + HTML Event Attribute + onmouseover + false + true + + + HTML Event Attribute + onmousemove + false + true + + + HTML Event Attribute + onmouseout + false + true + + + HTML Event Attribute + onkeypress + false + true + + + HTML Event Attribute + onkeyup + false + true + + + HTML Event Attribute + onkeydown + false + true + + + HTML Event Attribute + onfocus + false + true + + + HTML Event Attribute + onblur + false + true + + + HTML Event Attribute + onchange + false + true + + + HTML Standard Attribute + accesskey + false + true + + + HTML Optional Attribute + size + false + true + + + HTML Optional Attribute + maxlength + false + true + + + HTML Optional Attribute + alt + false + true + + + HTML Event Attribute + onselect + false + true + + + HTML Optional Attribute. Setting the value of this attribute to 'true' (without the quotes) will make the HTML element readonly. + readonly + false + true + + + Common Optional Attribute + autocomplete + false + true + + true + + + + Renders an HTML 'input' tag with type 'password' using the bound value. + password + org.springframework.web.servlet.tags.form.PasswordInputTag + empty + + Path to property for data binding + path + true + true + + + HTML Standard Attribute + id + false + true + + + Enable/disable HTML escaping of rendered values. + htmlEscape + false + true + + + Equivalent to "class" - HTML Optional Attribute + cssClass + false + true + + + Equivalent to "class" - HTML Optional Attribute. Used when the bound field has errors. + cssErrorClass + false + true + + + Equivalent to "style" - HTML Optional Attribute + cssStyle + false + true + + + HTML Standard Attribute + lang + false + true + + + HTML Standard Attribute + title + false + true + + + HTML Standard Attribute + dir + false + true + + + HTML Standard Attribute + tabindex + false + true + + + HTML Optional Attribute. Setting the value of this attribute to 'true' (without the quotes) will disable the HTML element. + disabled + false + true + + + HTML Event Attribute + onclick + false + true + + + HTML Event Attribute + ondblclick + false + true + + + HTML Event Attribute + onmousedown + false + true + + + HTML Event Attribute + onmouseup + false + true + + + HTML Event Attribute + onmouseover + false + true + + + HTML Event Attribute + onmousemove + false + true + + + HTML Event Attribute + onmouseout + false + true + + + HTML Event Attribute + onkeypress + false + true + + + HTML Event Attribute + onkeyup + false + true + + + HTML Event Attribute + onkeydown + false + true + + + HTML Event Attribute + onfocus + false + true + + + HTML Event Attribute + onblur + false + true + + + HTML Event Attribute + onchange + false + true + + + HTML Standard Attribute + accesskey + false + true + + + HTML Optional Attribute + size + false + true + + + HTML Optional Attribute + maxlength + false + true + + + HTML Optional Attribute + alt + false + true + + + HTML Event Attribute + onselect + false + true + + + HTML Optional Attribute. Setting the value of this attribute to 'true' (without the quotes) will make the HTML element readonly. + readonly + false + true + + + Common Optional Attribute + autocomplete + false + true + + + Is the password value to be shown? Defaults to false. + showPassword + false + true + + true + + + + Renders an HTML 'input' tag with type 'hidden' using the bound value. + hidden + org.springframework.web.servlet.tags.form.HiddenInputTag + empty + + Path to property for data binding + path + true + true + + + HTML Standard Attribute + id + false + true + + + Enable/disable HTML escaping of rendered values. + htmlEscape + false + true + + + Equivalent to "class" - HTML Optional Attribute + cssClass + false + true + + + Equivalent to "class" - HTML Optional Attribute. Used when the bound field has errors. + cssErrorClass + false + true + + + Equivalent to "style" - HTML Optional Attribute + cssStyle + false + true + + + HTML Standard Attribute + lang + false + true + + + HTML Standard Attribute + title + false + true + + + HTML Standard Attribute + dir + false + true + + + HTML Standard Attribute + tabindex + false + true + + + HTML Optional Attribute. Setting the value of this attribute to 'true' (without the quotes) will disable the HTML element. + disabled + false + true + + + HTML Event Attribute + onclick + false + true + + + HTML Event Attribute + ondblclick + false + true + + + HTML Event Attribute + onmousedown + false + true + + + HTML Event Attribute + onmouseup + false + true + + + HTML Event Attribute + onmouseover + false + true + + + HTML Event Attribute + onmousemove + false + true + + + HTML Event Attribute + onmouseout + false + true + + + HTML Event Attribute + onkeypress + false + true + + + HTML Event Attribute + onkeyup + false + true + + + HTML Event Attribute + onkeydown + false + true + + true + + + + Renders an HTML 'select' element. Supports databinding to the selected option. + select + org.springframework.web.servlet.tags.form.SelectTag + JSP + + Path to property for data binding + path + true + true + + + HTML Standard Attribute + id + false + true + + + Enable/disable HTML escaping of rendered values. + htmlEscape + false + true + + + Equivalent to "class" - HTML Optional Attribute + cssClass + false + true + + + Equivalent to "class" - HTML Optional Attribute. Used when the bound field has errors. + cssErrorClass + false + true + + + Equivalent to "style" - HTML Optional Attribute + cssStyle + false + true + + + HTML Standard Attribute + lang + false + true + + + HTML Standard Attribute + title + false + true + + + HTML Standard Attribute + dir + false + true + + + HTML Standard Attribute + tabindex + false + true + + + HTML Optional Attribute. Setting the value of this attribute to 'true' (without the quotes) will disable the HTML element. + disabled + false + true + + + HTML Event Attribute + onclick + false + true + + + HTML Event Attribute + ondblclick + false + true + + + HTML Event Attribute + onmousedown + false + true + + + HTML Event Attribute + onmouseup + false + true + + + HTML Event Attribute + onmouseover + false + true + + + HTML Event Attribute + onmousemove + false + true + + + HTML Event Attribute + onmouseout + false + true + + + HTML Event Attribute + onkeypress + false + true + + + HTML Event Attribute + onkeyup + false + true + + + HTML Event Attribute + onkeydown + false + true + + + HTML Event Attribute + onfocus + false + true + + + HTML Event Attribute + onblur + false + true + + + HTML Event Attribute + onchange + false + true + + + HTML Standard Attribute + accesskey + false + true + + + The Collection, Map or array of objects used to generate the inner 'option' tags + items + false + true + + + Name of the property mapped to 'value' attribute of the 'option' tag + itemValue + false + true + + + Name of the property mapped to the inner text of the 'option' tag + itemLabel + false + true + + + HTML Optional Attribute + size + false + true + + + HTML Optional Attribute + multiple + false + true + + true + + + + Renders a single HTML 'option'. Sets 'selected' as appropriate based on bound value. + option + org.springframework.web.servlet.tags.form.OptionTag + JSP + + The actual value bound to the 'value' attribute + value + java.lang.Object + + + The String representation of thr value bound to the 'value' attribute, taking into consideration + any PropertyEditor associated with the enclosing 'select' tag. + displayValue + java.lang.String + + + HTML Standard Attribute + id + false + true + + + HTML Optional Attribute + value + true + true + + + HTML Optional Attribute + label + false + true + + + Enable/disable HTML escaping of rendered values. + htmlEscape + false + true + + + Equivalent to "class" - HTML Optional Attribute + cssClass + false + true + + + Equivalent to "class" - HTML Optional Attribute. Used when the bound field has errors. + cssErrorClass + false + true + + + Equivalent to "style" - HTML Optional Attribute + cssStyle + false + true + + + HTML Standard Attribute + lang + false + true + + + HTML Standard Attribute + title + false + true + + + HTML Standard Attribute + dir + false + true + + + HTML Standard Attribute + tabindex + false + true + + + HTML Optional Attribute. Setting the value of this attribute to 'true' (without the quotes) will disable the HTML element. + disabled + false + true + + + HTML Event Attribute + onclick + false + true + + + HTML Event Attribute + ondblclick + false + true + + + HTML Event Attribute + onmousedown + false + true + + + HTML Event Attribute + onmouseup + false + true + + + HTML Event Attribute + onmouseover + false + true + + + HTML Event Attribute + onmousemove + false + true + + + HTML Event Attribute + onmouseout + false + true + + + HTML Event Attribute + onkeypress + false + true + + + HTML Event Attribute + onkeyup + false + true + + + HTML Event Attribute + onkeydown + false + true + + true + + + + Renders a list of HTML 'option' tags. Sets 'selected' as appropriate based on bound value. + options + org.springframework.web.servlet.tags.form.OptionsTag + empty + + HTML Standard Attribute + id + false + true + + + The Collection, Map or array of objects used to generate the inner 'option' tags. This attribute is required unless the containing select's property for data binding is an Enum, in which case the enum's values are used. + items + false + true + + + Name of the property mapped to 'value' attribute of the 'option' tag + itemValue + false + true + + + Name of the property mapped to the inner text of the 'option' tag + itemLabel + false + true + + + Enable/disable HTML escaping of rendered values. + htmlEscape + false + true + + + Equivalent to "class" - HTML Optional Attribute + cssClass + false + true + + + Equivalent to "class" - HTML Optional Attribute. Used when the bound field has errors. + cssErrorClass + false + true + + + Equivalent to "style" - HTML Optional Attribute + cssStyle + false + true + + + HTML Standard Attribute + lang + false + true + + + HTML Standard Attribute + title + false + true + + + HTML Standard Attribute + dir + false + true + + + HTML Standard Attribute + tabindex + false + true + + + HTML Optional Attribute. Setting the value of this attribute to 'true' (without the quotes) will disable the HTML element. + disabled + false + true + + + HTML Event Attribute + onclick + false + true + + + HTML Event Attribute + ondblclick + false + true + + + HTML Event Attribute + onmousedown + false + true + + + HTML Event Attribute + onmouseup + false + true + + + HTML Event Attribute + onmouseover + false + true + + + HTML Event Attribute + onmousemove + false + true + + + HTML Event Attribute + onmouseout + false + true + + + HTML Event Attribute + onkeypress + false + true + + + HTML Event Attribute + onkeyup + false + true + + + HTML Event Attribute + onkeydown + false + true + + true + + + + Renders an HTML 'input' tag with type 'radio'. + radiobutton + org.springframework.web.servlet.tags.form.RadioButtonTag + empty + + Path to property for data binding + path + true + true + + + HTML Standard Attribute + id + false + true + + + Enable/disable HTML escaping of rendered values. + htmlEscape + false + true + + + Equivalent to "class" - HTML Optional Attribute + cssClass + false + true + + + Equivalent to "class" - HTML Optional Attribute. Used when the bound field has errors. + cssErrorClass + false + true + + + Equivalent to "style" - HTML Optional Attribute + cssStyle + false + true + + + HTML Standard Attribute + lang + false + true + + + HTML Standard Attribute + title + false + true + + + HTML Standard Attribute + dir + false + true + + + HTML Standard Attribute + tabindex + false + true + + + HTML Optional Attribute. Setting the value of this attribute to 'true' (without the quotes) will disable the HTML element. + disabled + false + true + + + HTML Event Attribute + onclick + false + true + + + HTML Event Attribute + ondblclick + false + true + + + HTML Event Attribute + onmousedown + false + true + + + HTML Event Attribute + onmouseup + false + true + + + HTML Event Attribute + onmouseover + false + true + + + HTML Event Attribute + onmousemove + false + true + + + HTML Event Attribute + onmouseout + false + true + + + HTML Event Attribute + onkeypress + false + true + + + HTML Event Attribute + onkeyup + false + true + + + HTML Event Attribute + onkeydown + false + true + + + HTML Event Attribute + onfocus + false + true + + + HTML Event Attribute + onblur + false + true + + + HTML Event Attribute + onchange + false + true + + + HTML Standard Attribute + accesskey + false + true + + + HTML Optional Attribute + value + false + true + + + Value to be displayed as part of the tag + label + false + true + + true + + + + Renders multiple HTML 'input' tags with type 'radio'. + radiobuttons + org.springframework.web.servlet.tags.form.RadioButtonsTag + empty + + Path to property for data binding + path + true + true + + + HTML Standard Attribute + id + false + true + + + Enable/disable HTML escaping of rendered values. + htmlEscape + false + true + + + Equivalent to "class" - HTML Optional Attribute + cssClass + false + true + + + Equivalent to "class" - HTML Optional Attribute. Used when the bound field has errors. + cssErrorClass + false + true + + + Equivalent to "style" - HTML Optional Attribute + cssStyle + false + true + + + HTML Standard Attribute + lang + false + true + + + HTML Standard Attribute + title + false + true + + + HTML Standard Attribute + dir + false + true + + + HTML Standard Attribute + tabindex + false + true + + + HTML Optional Attribute. Setting the value of this attribute to 'true' (without the quotes) will disable the HTML element. + disabled + false + true + + + HTML Event Attribute + onclick + false + true + + + HTML Event Attribute + ondblclick + false + true + + + HTML Event Attribute + onmousedown + false + true + + + HTML Event Attribute + onmouseup + false + true + + + HTML Event Attribute + onmouseover + false + true + + + HTML Event Attribute + onmousemove + false + true + + + HTML Event Attribute + onmouseout + false + true + + + HTML Event Attribute + onkeypress + false + true + + + HTML Event Attribute + onkeyup + false + true + + + HTML Event Attribute + onkeydown + false + true + + + HTML Event Attribute + onfocus + false + true + + + HTML Event Attribute + onblur + false + true + + + HTML Event Attribute + onchange + false + true + + + HTML Standard Attribute + accesskey + false + true + + + The Collection, Map or array of objects used to generate the 'input' tags with type 'radio'. This attribute is required unless the property for data binding is an Enum, in which case the enum's values are used. + items + false + true + + + Name of the property mapped to 'value' attribute of the 'input' tags with type 'radio' + itemValue + false + true + + + Value to be displayed as part of the 'input' tags with type 'radio' + itemLabel + false + true + + + Delimiter to use between each 'input' tag with type 'radio'. There is no delimiter by default. + delimiter + false + true + + + Specifies the HTML element that is used to enclose each 'input' tag with type 'radio'. Defaults to 'span'. + element + false + true + + true + + + + Renders an HTML 'input' tag with type 'checkbox'. + checkbox + org.springframework.web.servlet.tags.form.CheckboxTag + empty + + Path to property for data binding + path + true + true + + + HTML Standard Attribute + id + false + true + + + Enable/disable HTML escaping of rendered values. + htmlEscape + false + true + + + Equivalent to "class" - HTML Optional Attribute + cssClass + false + true + + + Equivalent to "class" - HTML Optional Attribute. Used when the bound field has errors. + cssErrorClass + false + true + + + Equivalent to "style" - HTML Optional Attribute + cssStyle + false + true + + + HTML Standard Attribute + lang + false + true + + + HTML Standard Attribute + title + false + true + + + HTML Standard Attribute + dir + false + true + + + HTML Standard Attribute + tabindex + false + true + + + HTML Optional Attribute. Setting the value of this attribute to 'true' (without the quotes) will disable the HTML element. + disabled + false + true + + + HTML Event Attribute + onclick + false + true + + + HTML Event Attribute + ondblclick + false + true + + + HTML Event Attribute + onmousedown + false + true + + + HTML Event Attribute + onmouseup + false + true + + + HTML Event Attribute + onmouseover + false + true + + + HTML Event Attribute + onmousemove + false + true + + + HTML Event Attribute + onmouseout + false + true + + + HTML Event Attribute + onkeypress + false + true + + + HTML Event Attribute + onkeyup + false + true + + + HTML Event Attribute + onkeydown + false + true + + + HTML Event Attribute + onfocus + false + true + + + HTML Event Attribute + onblur + false + true + + + HTML Event Attribute + onchange + false + true + + + HTML Standard Attribute + accesskey + false + true + + + HTML Optional Attribute + value + false + true + + + Value to be displayed as part of the tag + label + false + true + + true + + + + Renders multiple HTML 'input' tags with type 'checkbox'. + checkboxes + org.springframework.web.servlet.tags.form.CheckboxesTag + empty + + Path to property for data binding + path + true + true + + + HTML Standard Attribute + id + false + true + + + Enable/disable HTML escaping of rendered values. + htmlEscape + false + true + + + Equivalent to "class" - HTML Optional Attribute + cssClass + false + true + + + Equivalent to "class" - HTML Optional Attribute. Used when the bound field has errors. + cssErrorClass + false + true + + + Equivalent to "style" - HTML Optional Attribute + cssStyle + false + true + + + HTML Standard Attribute + lang + false + true + + + HTML Standard Attribute + title + false + true + + + HTML Standard Attribute + dir + false + true + + + HTML Standard Attribute + tabindex + false + true + + + HTML Optional Attribute. Setting the value of this attribute to 'true' (without the quotes) will disable the HTML element. + disabled + false + true + + + HTML Event Attribute + onclick + false + true + + + HTML Event Attribute + ondblclick + false + true + + + HTML Event Attribute + onmousedown + false + true + + + HTML Event Attribute + onmouseup + false + true + + + HTML Event Attribute + onmouseover + false + true + + + HTML Event Attribute + onmousemove + false + true + + + HTML Event Attribute + onmouseout + false + true + + + HTML Event Attribute + onkeypress + false + true + + + HTML Event Attribute + onkeyup + false + true + + + HTML Event Attribute + onkeydown + false + true + + + HTML Event Attribute + onfocus + false + true + + + HTML Event Attribute + onblur + false + true + + + HTML Event Attribute + onchange + false + true + + + HTML Standard Attribute + accesskey + false + true + + + The Collection, Map or array of objects used to generate the 'input' tags with type 'checkbox' + items + true + true + + + Name of the property mapped to 'value' attribute of the 'input' tags with type 'checkbox' + itemValue + false + true + + + Value to be displayed as part of the 'input' tags with type 'checkbox' + itemLabel + false + true + + + Delimiter to use between each 'input' tag with type 'checkbox'. There is no delimiter by default. + delimiter + false + true + + + Specifies the HTML element that is used to enclose each 'input' tag with type 'checkbox'. Defaults to 'span'. + element + false + true + + true + + + + Renders an HTML 'textarea'. + textarea + org.springframework.web.servlet.tags.form.TextareaTag + empty + + Path to property for data binding + path + true + true + + + HTML Standard Attribute + id + false + true + + + Enable/disable HTML escaping of rendered values. + htmlEscape + false + true + + + Equivalent to "class" - HTML Optional Attribute + cssClass + false + true + + + Equivalent to "class" - HTML Optional Attribute. Used when the bound field has errors. + cssErrorClass + false + true + + + Equivalent to "style" - HTML Optional Attribute + cssStyle + false + true + + + HTML Standard Attribute + lang + false + true + + + HTML Standard Attribute + title + false + true + + + HTML Standard Attribute + dir + false + true + + + HTML Standard Attribute + tabindex + false + true + + + HTML Optional Attribute. Setting the value of this attribute to 'true' (without the quotes) will disable the HTML element. + disabled + false + true + + + HTML Event Attribute + onclick + false + true + + + HTML Event Attribute + ondblclick + false + true + + + HTML Event Attribute + onmousedown + false + true + + + HTML Event Attribute + onmouseup + false + true + + + HTML Event Attribute + onmouseover + false + true + + + HTML Event Attribute + onmousemove + false + true + + + HTML Event Attribute + onmouseout + false + true + + + HTML Event Attribute + onkeypress + false + true + + + HTML Event Attribute + onkeyup + false + true + + + HTML Event Attribute + onkeydown + false + true + + + HTML Event Attribute + onfocus + false + true + + + HTML Event Attribute + onblur + false + true + + + HTML Event Attribute + onchange + false + true + + + HTML Standard Attribute + accesskey + false + true + + + HTML Required Attribute + rows + false + true + + + HTML Required Attribute + cols + false + true + + + HTML Event Attribute + onselect + false + true + + + HTML Optional Attribute. Setting the value of this attribute to 'true' (without the quotes) will make the HTML element readonly. + readonly + false + true + + true + + + + Renders field errors in an HTML 'span' tag. + errors + org.springframework.web.servlet.tags.form.ErrorsTag + JSP + + messages + java.util.List + + + Path to errors object for data binding + path + false + true + + + HTML Standard Attribute + id + false + true + + + Enable/disable HTML escaping of rendered values. + htmlEscape + false + true + + + Delimiter for displaying multiple error messages. Defaults to the br tag. + delimiter + false + true + + + Equivalent to "class" - HTML Optional Attribute + cssClass + false + true + + + Equivalent to "style" - HTML Optional Attribute + cssStyle + false + true + + + HTML Standard Attribute + lang + false + true + + + HTML Standard Attribute + title + false + true + + + HTML Standard Attribute + dir + false + true + + + HTML Standard Attribute + tabindex + false + true + + + HTML Event Attribute + onclick + false + true + + + HTML Event Attribute + ondblclick + false + true + + + HTML Event Attribute + onmousedown + false + true + + + HTML Event Attribute + onmouseup + false + true + + + HTML Event Attribute + onmouseover + false + true + + + HTML Event Attribute + onmousemove + false + true + + + HTML Event Attribute + onmouseout + false + true + + + HTML Event Attribute + onkeypress + false + true + + + HTML Event Attribute + onkeyup + false + true + + + HTML Event Attribute + onkeydown + false + true + + + Specifies the HTML element that is used to render the enclosing errors. + element + false + true + + true + + + + Renders a form field label in an HTML 'label' tag. + label + org.springframework.web.servlet.tags.form.LabelTag + JSP + + Path to property for data binding + path + true + true + + + HTML Standard Attribute + id + false + true + + + Enable/disable HTML escaping of rendered values. + htmlEscape + false + true + + + HTML Standard Attribute + for + false + true + + + Equivalent to "class" - HTML Optional Attribute. + cssClass + false + true + + + Equivalent to "class" - HTML Optional Attribute. Used only when errors are present. + cssErrorClass + false + true + + + Equivalent to "style" - HTML Optional Attribute + cssStyle + false + true + + + HTML Standard Attribute + lang + false + true + + + HTML Standard Attribute + title + false + true + + + HTML Standard Attribute + dir + false + true + + + HTML Standard Attribute + tabindex + false + true + + + HTML Event Attribute + onclick + false + true + + + HTML Event Attribute + ondblclick + false + true + + + HTML Event Attribute + onmousedown + false + true + + + HTML Event Attribute + onmouseup + false + true + + + HTML Event Attribute + onmouseover + false + true + + + HTML Event Attribute + onmousemove + false + true + + + HTML Event Attribute + onmouseout + false + true + + + HTML Event Attribute + onkeypress + false + true + + + HTML Event Attribute + onkeyup + false + true + + + HTML Event Attribute + onkeydown + false + true + + true + + + + Renders an HTML 'button' tag. + button + org.springframework.web.servlet.tags.form.ButtonTag + JSP + + HTML Standard Attribute + id + false + true + + + The name attribute for the HTML button tag + name + false + true + + + The value attribute for the HTML button tag + value + false + true + + + HTML Optional Attribute. Setting the value of this attribute to 'true' (without the quotes) will disable the HTML element. + disabled + false + true + + true + + + diff --git a/web-app/WEB-INF/tld/spring.tld b/web-app/WEB-INF/tld/spring.tld new file mode 100644 index 0000000..a0a8c6f --- /dev/null +++ b/web-app/WEB-INF/tld/spring.tld @@ -0,0 +1,457 @@ + + + + Spring Framework JSP Tag Library + 3.0 + spring + http://www.springframework.org/tags + + + + Sets default HTML escape value for the current page. + Overrides a "defaultHtmlEscape" context-param in web.xml, if any. + + htmlEscape + org.springframework.web.servlet.tags.HtmlEscapeTag + JSP + + Set the default value for HTML escaping, to be put + into the current PageContext. + defaultHtmlEscape + true + true + + + + + + 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). + + escapeBody + org.springframework.web.servlet.tags.EscapeBodyTag + JSP + + Set HTML escaping for this tag, as boolean value. Overrides the + default HTML escaping setting for the current page. + htmlEscape + false + true + + + Set JavaScript escaping for this tag, as boolean value. + Default is false. + javaScriptEscape + false + true + + + + + + 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). + + message + org.springframework.web.servlet.tags.MessageTag + JSP + + A MessageSourceResolvable argument (direct or through JSP EL). + Fits nicely when used in conjunction with Spring's own validation error + classes which all implement the MessageSourceResolvable interface. For + example, this allows you to iterate over all of the errors in a form, + passing each error (using a runtime expression) as the value of this + 'message' attribute, thus effecting the easy display of such error + messages. + message + false + true + + + The code (key) to use when looking up the message. + If code is not provided, the text attribute will be used. + code + false + true + + + Set optional message arguments for this tag, as a + (comma-)delimited String (each String argument can contain JSP EL), + an Object array (used as argument array), or a single Object (used + as single argument). + arguments + false + true + + + The separator character to be used for splitting the + arguments string value; defaults to a 'comma' (','). + argumentSeparator + false + true + + + Default text to output when a message for the given code + could not be found. If both text and code are not set, the tag will + output null. + text + false + true + + + The string to use when binding the result to the page, + request, session or application scope. If not specified, the result + gets outputted to the writer (i.e. typically directly to the JSP). + var + false + true + + + The scope to use when exporting the result to a variable. + This attribute is only used when var is also set. Possible values are + page, request, session and application. + scope + false + true + + + Set HTML escaping for this tag, as boolean value. + Overrides the default HTML escaping setting for the current page. + htmlEscape + false + true + + + Set JavaScript escaping for this tag, as boolean value. Default is false. + javaScriptEscape + false + true + + + + + + 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). + + theme + org.springframework.web.servlet.tags.ThemeTag + JSP + + A MessageSourceResolvable argument (direct or through JSP EL). + message + false + true + + + The code (key) to use when looking up the message. + If code is not provided, the text attribute will be used. + code + false + true + + + Set optional message arguments for this tag, as a + (comma-)delimited String (each String argument can contain JSP EL), + an Object array (used as argument array), or a single Object (used + as single argument). + arguments + false + true + + + The separator character to be used for splitting the + arguments string value; defaults to a 'comma' (','). + argumentSeparator + false + true + + + Default text to output when a message for the given code + could not be found. If both text and code are not set, the tag will + output null. + text + false + true + + + The string to use when binding the result to the page, + request, session or application scope. If not specified, the result + gets outputted to the writer (i.e. typically directly to the JSP). + var + false + true + + + The scope to use when exporting the result to a variable. + This attribute is only used when var is also set. Possible values are + page, request, session and application. + scope + false + true + + + Set HTML escaping for this tag, as boolean value. + Overrides the default HTML escaping setting for the current page. + htmlEscape + false + true + + + Set JavaScript escaping for this tag, as boolean value. Default is false. + javaScriptEscape + false + true + + + + + + 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). + + hasBindErrors + org.springframework.web.servlet.tags.BindErrorsTag + JSP + + errors + org.springframework.validation.Errors + + + The name of the bean in the request, that needs to be + inspected for errors. If errors are available for this bean, they + will be bound under the 'errors' key. + name + true + true + + + Set HTML escaping for this tag, as boolean value. + Overrides the default HTML escaping setting for the current page. + htmlEscape + false + true + + + + + + Sets a nested path to be used by the bind tag's path. + + nestedPath + org.springframework.web.servlet.tags.NestedPathTag + JSP + + nestedPath + java.lang.String + + + Set the path that this tag should apply. E.g. 'customer' + to allow bind paths like 'address.street' rather than + 'customer.address.street'. + path + true + true + + + + + + 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). + + bind + org.springframework.web.servlet.tags.BindTag + JSP + + status + org.springframework.web.servlet.support.BindStatus + + + The path to the bean or bean property to bind status + information for. For instance account.name, company.address.zipCode + or just employee. The status object will exported to the page scope, + specifically for this bean or bean property + path + true + true + + + Set whether to ignore a nested path, if any. Default is to not ignore. + ignoreNestedPath + false + true + + + Set HTML escaping for this tag, as boolean value. Overrides + the default HTML escaping setting for the current page. + htmlEscape + false + true + + + + + + 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). + + transform + org.springframework.web.servlet.tags.TransformTag + JSP + + The value to transform. This is the actual object you want + to have transformed (for instance a Date). Using the PropertyEditor that + is currently in use by the 'spring:bind' tag. + value + true + true + + + The string to use when binding the result to the page, + request, session or application scope. If not specified, the result gets + outputted to the writer (i.e. typically directly to the JSP). + var + false + true + + + The scope to use when exported the result to a variable. + This attribute is only used when var is also set. Possible values are + page, request, session and application. + scope + false + true + + + Set HTML escaping for this tag, as boolean value. Overrides + the default HTML escaping setting for the current page. + htmlEscape + false + true + + + + + URL tag based on the JSTL c:url tag. This variant is fully + backwards compatible with the standard tag. Enhancements include support + for URL template parameters. + url + org.springframework.web.servlet.tags.UrlTag + JSP + + The URL to build. This value can include template place holders + that are replaced with the URL encoded value of the named parameter. Parameters + must be defined using the param tag inside the body of this tag. + value + true + true + + + Specifies a remote application context path. The default is the + current application context path. + context + false + true + + + The name of the variable to export the URL value to. + var + false + true + + + The scope for the var. 'application', 'session', 'request' and + 'page' scopes are supported. Defaults to page scope. This attribute has no + effect unless the var attribute is also defined. + scope + false + true + + + Set HTML escaping for this tag, as a boolean value. Overrides the + default HTML escaping setting for the current page. + htmlEscape + false + true + + + Set JavaScript escaping for this tag, as a boolean value. + Default is false. + javaScriptEscape + false + true + + + + + Parameter tag based on the JSTL c:param tag. The sole purpose is to + support params inside the spring:url tag. + param + org.springframework.web.servlet.tags.ParamTag + JSP + + The name of the parameter. + name + true + true + + + The value of the parameter. + value + false + true + + + + + Evaluates a Spring expression (SpEL) and either prints the result or assigns it to a variable. + eval + org.springframework.web.servlet.tags.EvalTag + JSP + + The expression to evaluate. + expression + true + true + + + The name of the variable to export the evaluation result to. + var + false + true + + + The scope for the var. 'application', 'session', 'request' and + 'page' scopes are supported. Defaults to page scope. This attribute has no + effect unless the var attribute is also defined. + scope + false + true + + + Set HTML escaping for this tag, as a boolean value. Overrides the + default HTML escaping setting for the current page. + htmlEscape + false + true + + + Set JavaScript escaping for this tag, as a boolean value. Default is false. + javaScriptEscape + false + true + + + +