Difference between revisions of "Grails Plugin - Multi Tenant"
From Blue-IT.org Wiki
(Created page with "An introduction on the concept of multitenant can be found at: * [http://en.wikipedia.org/wiki/Multitenancy http://en.wikipedia.org/wiki/Multitenancy] * [http://www.itwissen.info...") |
|||
(13 intermediate revisions by the same user not shown) | |||
Line 17: | Line 17: | ||
Later on you can use ''<nowiki>tenant[dot]</nowiki>'' to refer to the TenantMap domain class (see later on). | Later on you can use ''<nowiki>tenant[dot]</nowiki>'' to refer to the TenantMap domain class (see later on). | ||
+ | |||
+ | You also have to set a per-environment serverUrl stem for creating absolute links depending on your hostname: | ||
+ | |||
+ | environments { | ||
+ | [...] | ||
+ | development { | ||
+ | grails.serverURL = "http://localhost:8080/${appName}" | ||
+ | '''grails.tenant.domain = "localhost"''' | ||
== 2. Declare Tenant Class == | == 2. Declare Tenant Class == | ||
− | This class should be a top level class. E.g. "Organisation" hasMany "Users". | + | This class - I use ''Organisation.groovy'' for example - should be a top level class. |
+ | |||
+ | E.g. "Organisation" hasMany "Users". | ||
import grails.plugin.multitenant.core.groovy.compiler.MultiTenant; | import grails.plugin.multitenant.core.groovy.compiler.MultiTenant; | ||
Line 62: | Line 72: | ||
def init = { servletContext -> | def init = { servletContext -> | ||
+ | // This refers to Config.groovy: ''grails.tenantDomain = 'myweb1''' | ||
def domainName = ConfigurationHolder.config.grails.'''tenantDomain''' | def domainName = ConfigurationHolder.config.grails.'''tenantDomain''' | ||
− | + | ||
− | + | def myweb1Tenant = DomainTenantMap.findByName('myweb1_dev') | |
− | + | ||
− | + | if ( myweb1Tenant ) {} | |
− | + | else { | |
− | + | myweb1Tenant = new DomainTenantMap( | |
− | + | name:'myweb1_dev', | |
− | + | domainName:domainName, | |
− | + | mappedTenantId:1) | |
+ | |||
+ | if (myweb1Tenant.save(flush:true) ) { | ||
+ | println "TenantMap entry for myweb1_dev created." | ||
+ | } else { | ||
+ | println "Error creating TenantMap entry for myweb1_dev." | ||
+ | website1Tenant.errors.allErrors.each { println it.defaultMessage } | ||
+ | } | ||
+ | } | ||
+ | |||
The property ''domainName'' refers to the correspondent field in the database ''domain_name''. | The property ''domainName'' refers to the correspondent field in the database ''domain_name''. | ||
The property ''mappedtenantId '' to the field name. | The property ''mappedtenantId '' to the field name. | ||
Line 79: | Line 99: | ||
| id | version | domain_name | mapped_tenant_id | name | | | id | version | domain_name | mapped_tenant_id | name | | ||
+----+---------+-----------------+------------------+----------------+ | +----+---------+-----------------+------------------+----------------+ | ||
− | | 1 | 0 | | + | | 1 | 0 | myweb1 | 1 | myweb1_dev | |
[...] | [...] | ||
− | You can use '''''sub.'''domain.tld'' by concatenating the strings when setting domainName | + | You can use '''''sub.'''domain.tld'' by concatenating the strings when setting domainName. |
+ | |||
+ | Now you can create data for your tenant: | ||
+ | |||
+ | TenantUtils.doWithTenant (website1Tenant.mappedTenantId) { | ||
+ | |||
+ | def organisation1 = new Organisation (name: 'Organisation 1') | ||
+ | |||
+ | [...] // create Users and other stuff | ||
+ | |||
+ | == 5. Include a login from spring security plugin == | ||
+ | ==== OrganisationController ==== | ||
+ | |||
+ | The ''save()''-method of the OrganisationController does the magic of adding a tenant. | ||
+ | |||
+ | [...] | ||
+ | def save() = { | ||
+ | |||
+ | def myOrganisation = new Organisation(params) | ||
+ | |||
+ | // Sorry Confidental ... ;) | ||
+ | |||
+ | ==== LoginController ==== | ||
+ | The LoginController class is responsible for authorisation. This authorisation has to be multitenant aware! | ||
+ | |||
+ | import grails.plugin.multitenant.core.util.TenantUtils | ||
+ | |||
+ | class LoginController { | ||
+ | |||
+ | [...] | ||
+ | |||
+ | /** | ||
+ | * Show the login page. | ||
+ | */ | ||
+ | def auth = { | ||
+ | |||
+ | // Sorry Confidental ... ;) | ||
+ | |||
+ | } | ||
+ | |||
+ | [[Category:Java, Groovy and Grails]] |
Latest revision as of 19:43, 8 January 2012
An introduction on the concept of multitenant can be found at:
Documentation
Contents
1. Declare mode in Config.groovy
// Multi Tenant tenant { // multiTenant is the default // do "grails create-dns-map" when changing tenantDomains // this stroes the dns/tenant mappings into the database resolver.request.dns.type = "db" }
Later on you can use tenant[dot] to refer to the TenantMap domain class (see later on).
You also have to set a per-environment serverUrl stem for creating absolute links depending on your hostname:
environments { [...] development { grails.serverURL = "http://localhost:8080/${appName}" grails.tenant.domain = "localhost"
2. Declare Tenant Class
This class - I use Organisation.groovy for example - should be a top level class.
E.g. "Organisation" hasMany "Users".
import grails.plugin.multitenant.core.groovy.compiler.MultiTenant; @MultiTenant class Organisation { [...]
3. Create a TenantMap domain class
This is achieved by using the command
grails create-dns-map
This automatically creates a domain class with the name DomainTenantMap.groovy.
package tenant /** * Maps domain name to tenantId */ class DomainTenantMap { String domainName Integer mappedTenantId String name static constraints = {} }
This class maps to a database table domain_tenant_map.
4. Build a MultiTenant environment in BootStrap.groovy
Tenants (e.g. organisations) are created by instantiating a new tenantDomain ConfigurationHolder.
import grails.plugin.multitenant.core.util.TenantUtils import org.codehaus.groovy.grails.commons.ConfigurationHolder import tenant.DomainTenantMap // was created with grails create-domain-map class BootStrap { def init = { servletContext -> // This refers to Config.groovy: grails.tenantDomain = 'myweb1' def domainName = ConfigurationHolder.config.grails.tenantDomain def myweb1Tenant = DomainTenantMap.findByName('myweb1_dev') if ( myweb1Tenant ) {} else { myweb1Tenant = new DomainTenantMap( name:'myweb1_dev', domainName:domainName, mappedTenantId:1) if (myweb1Tenant.save(flush:true) ) { println "TenantMap entry for myweb1_dev created." } else { println "Error creating TenantMap entry for myweb1_dev." website1Tenant.errors.allErrors.each { println it.defaultMessage } } }
The property domainName refers to the correspondent field in the database domain_name. The property mappedtenantId to the field name.
mysql> select * from domain_tenant_map; +----+---------+-----------------+------------------+----------------+ | id | version | domain_name | mapped_tenant_id | name | +----+---------+-----------------+------------------+----------------+ | 1 | 0 | myweb1 | 1 | myweb1_dev | [...]
You can use sub.domain.tld by concatenating the strings when setting domainName.
Now you can create data for your tenant:
TenantUtils.doWithTenant (website1Tenant.mappedTenantId) { def organisation1 = new Organisation (name: 'Organisation 1') [...] // create Users and other stuff
5. Include a login from spring security plugin
OrganisationController
The save()-method of the OrganisationController does the magic of adding a tenant.
[...] def save() = { def myOrganisation = new Organisation(params) // Sorry Confidental ... ;)
LoginController
The LoginController class is responsible for authorisation. This authorisation has to be multitenant aware!
import grails.plugin.multitenant.core.util.TenantUtils class LoginController { [...] /** * Show the login page. */ def auth = { // Sorry Confidental ... ;) }