Skip to content

Commit 190bed3

Browse files
committed
reverting code formatting
1 parent 2448017 commit 190bed3

19 files changed

+851
-789
lines changed

MultiTenantCoreGrailsPlugin.groovy

Lines changed: 198 additions & 198 deletions
Large diffs are not rendered by default.

README

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +0,0 @@
1-
Grails Multi Tenant Core Plugin
2-
3-
Docs available at: http://multi-tenant.github.com/grails-multi-tenant-core/

application.properties

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
app.grails.version=1.3.6
44
app.name=multiTenantCore
55
app.servlet.version=2.4
6+
app.version=1.0.4
67
plugins.falcone-util=1.0
78
plugins.hibernate=1.3.6
89
plugins.tomcat=1.3.6

index.html

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
---
2+
layout: main
3+
title: My Grails Plugin
4+
---

main.html.tmpl

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
2+
"http://www.w3.org/TR/html4/loose.dtd">
3+
4+
<html lang="en">
5+
<head>
6+
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
7+
<title>{{ page.title }} @ GitHub</title>
8+
<style type="text/css">
9+
body {
10+
margin-top: 1.0em;
11+
font-family: "Helvetica,Arial,FreeSans";
12+
}
13+
14+
#container {
15+
margin: 0 auto;
16+
margin-left: 50px;
17+
margin-right: 50px;
18+
}
19+
20+
.propName { font-weight: bold; }
21+
22+
h1 { color: #00CC00; margin-bottom: 3px; }
23+
td { padding: 2px 5px; }
24+
</style>
25+
</head>
26+
<body>
27+
<a href="${remoteUrl}"><img style="position: absolute; top: 0; right: 0; border: 0;" src="http://s3.amazonaws.com/github/ribbons/forkme_right_darkblue_121621.png" alt="Fork me on GitHub" /></a>
28+
29+
<div id="container">
30+
<h1>{{ page.title }}</h1>
31+
<table id="properties">
32+
<tbody>
33+
<tr>
34+
<td class="propName">Version</td>
35+
<td>${version}<td>
36+
</tr>
37+
<tr>
38+
<td class="propName">Grails Version</td>
39+
<td>${grailsVersion}</td>
40+
</tr>
41+
<tr>
42+
<td class="propName">Author</td>
43+
<td>${author}</td>
44+
</tr>
45+
</tbody>
46+
</table>
47+
<hr />
48+
<h2>Documentation</h2>
49+
<ul>
50+
<li><a href="/grails-gwt/docs/manual/index.html">User guide</a></a>
51+
<li><a href="/grails-gwt/docs/api/index.html">Java API</a></li>
52+
<li><a href="/grails-gwt/docs/gapi/index.html">Groovy API</a></li>
53+
</ul>
54+
<hr />
55+
<div>{{ content }}</div>
56+
</body>
57+
</html>

src/docs/guide/2. Multi-Tenant Modes.gdoc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,4 @@ tenant {
88
}
99
{code}
1010

11-
{warning}multiTenant is the default{warning}
11+
{warn}multiTenant is the default{warn}
Lines changed: 8 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,19 @@
1-
The 'multiTenant' mode relies your application to use just one database for all tenants running. This is useful when you have a SaaS that allows user to sign up online and have their tenants running just after signing up. This is the simpler approach on multi-tenancy.
1+
h2. Annotate Domain Classes
22

3-
First thing to do, is to tell the plugin witch classes will be treated as multi-tenant ones. You can do it, but annotating your domain classes with the *@MultiTenant* annotation, as show below:
3+
As of 0.10, you only have to annotate domain classes that you want to be multi-tenant. Any domain classes that you want to be shared, such as acegi Role/Requestmap can be left without the annotation.
44

55
{code}
6-
import grails.plugin.multitenant.core.groovy.compiler.MultiTenant
6+
import grails.plugin.multitenant.core.groovy.compiler.MultiTenant;
77

8-
@MultiTenant
9-
class MyDomainClass {
8+
@MultiTenant class MyDomainClass {
109

1110
}
1211
{code}
1312

14-
This way, each instance of MyDomainClass will belong to some tenant. To achieve this, the plugin hooks into all hibernate events that query your database, adding another condition in your queries that will filter just instances of the current tenant.
13+
Because the plugin hooks into the compiler, make sure to do a "grails clean" after you've annotated your classes. Eventually, we'd like to get rid of this step.
1514

16-
For example, imagine you have 2 tenants, each one with 2 instances of MyDomainClass. When the app is running on tenant 1, calling
15+
h2. A note on indexing in multi-tenant environment
1716

18-
{code}
19-
def lst = MyDomainClass.list()
20-
{code}
21-
22-
will return just its 2 instances, even if you have 4 in your database.
23-
24-
The plugin hooks either on save/update events, setting the value of tenantId property automagically.
17+
Indexing a multi-tenant database is a tricky beast. If you have large datasets (100MM-1BB), prepare yourself for some fun. Google multi-tenant databases and start reading. :)
2518

26-
{warning}
27-
You don't need to declare the 'tenantId' property, the plugin even auto-inject it for you, so, keep your domain class code clean
28-
{warning}
19+
The short of it is that it's probably most efficient to create multi-column indexes that always include tenantId, that way the db can limit the working set to a single tenant before it starts filtering on another field.

src/docs/guide/2.1.1 Multi-Tenant mode gotchas.gdoc

Lines changed: 0 additions & 5 deletions
This file was deleted.

src/docs/guide/2.2 Single-Tenant Database Set Up.gdoc renamed to src/docs/guide/2.2 Single Tenant Database Set Up.gdoc

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
The single-tenant option is a little more immature than the multi-tenant option. It inherits all datasource configuration from the default datasource in Datasource.groovy, and allows you to provide a custom datasource url per tenant. This means that you can't mix and match drivers or user/pass combos for different tenants in the same instance.
22

3-
{warning}
3+
{warn}
44
TenantUtils.doWithTenant and currentTenant.set() do not work in single tenant mode, unless you manually create a new hibernate session and bind it to the current thread.
55

66
Also, normal second-level caches will not work in single-tenant mode, as you will get primary key collisions across databases. You'll need to use a multi-tenant wrapper for your cache implementation (see below)
7-
{warning}
7+
{warn}
88

99
The plugin supports the following types of datasources:
1010

src/docs/guide/3.1 DNS Resolver Set Up.gdoc

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,13 +39,13 @@ grails create-dns-map
3939

4040
h2. Security and TenantId
4141

42-
{warning}
42+
{warn}
4343
One advantage of identifying tenants by domain name is that any session cookies will be unique per domain, helping with security. This means that if a user authenticates (and gets an authentication cookie), if he changes the domain he's using to access the application, he won't stay logged on because the cookie for the old domain won't apply.
4444

4545
This does have a loophole, though. Because the cookie lives on the client browser, it may be possible for the user to inspect the cookie, and copy any values such as jsessionid to a cookie for a different domain, thus gaining access to another tenant's data. It really depends on how secure your cookie is (does an authentication token only work for one domain?)
4646

4747
The multi-tenant plugin uses a very simple messaging system from the falcone-util plugin. Whenever the tenantId changes, a message is published with the new and old tenantIds. You can use this event to perform any necessary checks, and clean up any resources you don't want around when the tenantId changes. Example as follows:
48-
{warning}
48+
{warn}
4949

5050
{code}
5151
//A sample class that might contain an authenticated user
Lines changed: 76 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -1,89 +1,94 @@
11
package grails.plugin.multitenant.core
22

3-
import org.apache.log4j.Logger
4-
53
import com.infusion.util.event.groovy.GroovyEventBroker
64
import com.infusion.util.domain.event.HibernateEvent
7-
8-
import net.sf.ehcache.Cache
9-
import net.sf.ehcache.Element
10-
import net.sf.ehcache.CacheManager
11-
125
import org.springframework.context.ApplicationContext
136
import org.springframework.context.ApplicationContextAware
14-
7+
import org.apache.log4j.Logger
8+
import net.sf.ehcache.CacheManager
9+
import net.sf.ehcache.Cache
10+
import net.sf.ehcache.Element
1511
import org.codehaus.groovy.grails.commons.ConfigurationHolder
1612

1713
/**
1814
* Implementation that looks up tenantIds from a local table DomainTenantMap that stores domain name to tenantId mappings
19-
* in support of the single or multi-tenant mode. The data will either be looked up from a configuration database on the
15+
* in support of the single or multi-tenant mode. The data will either be looked up from a configuration database on the
2016
* default datastore.
2117
*/
22-
public class DomainNameDatabaseTenantResolver extends BaseDomainNameTenantResolver implements ApplicationContextAware {
23-
24-
/** This is a logger for logging status and error messages. */
25-
private static Logger log = Logger.getLogger(DomainNameDatabaseTenantResolver.class)
18+
public class DomainNameDatabaseTenantResolver extends BaseDomainNameTenantResolver implements ApplicationContextAware
19+
{
20+
/** This is a logger for logging status and error messages. */
21+
private static Logger log = Logger.getLogger(DomainNameDatabaseTenantResolver.class)
2622

27-
/** Used for listening to save events for DomainTenantMap domain class */
28-
GroovyEventBroker eventBroker
23+
/**
24+
* Used for listening to save events for DomainTenantMap domain class
25+
*/
26+
GroovyEventBroker eventBroker
27+
/**
28+
* This is the injected context that is used to look up the DomainTenantMap Bean.
29+
*/
30+
ApplicationContext applicationContext
31+
/**
32+
* This will initialize the data for the application by loading the domain name tenant map data. This data is loaded
33+
* from tenant 0 which could either be a separate configuration database or the default data source as defined
34+
* in the data sources config file. This insures we don't end up with cyclic dependencies and that we don't have to store
35+
* the configuration data with each client.
36+
*/
37+
void initialize()
38+
{
39+
log.info "Initializing Domain Name Map for Multi Tenant support"
40+
loadDomainTenantMap()
41+
}
2942

30-
/** This is the injected context that is used to look up the DomainTenantMap Bean. */
31-
ApplicationContext applicationContext
32-
33-
/**
34-
* This will initialize the data for the application by loading the domain name tenant map data. This data is loaded
35-
* from tenant 0 which could either be a separate configuration database or the default data source as defined
36-
* in the data sources config file. This insures we don't end up with cyclic dependencies and that we don't have to store
37-
* the configuration data with each client.
38-
*/
39-
void initialize() {
40-
log.info "Initializing Domain Name Map for Multi Tenant support"
41-
loadDomainTenantMap()
43+
/**
44+
* This will load the hosts data which contains the map between domain and tenant. It will also load the database
45+
* objects in to another cache that can be accessed to retrieve additional data such as name etc.
46+
*/
47+
void loadDomainTenantMap()
48+
{
49+
hosts.clear()
50+
Cache tenantDataCache = CacheManager.getInstance()?.getCache(TENANT_DATA_CACHE_NAME)
51+
if (tenantDataCache == null)
52+
{
53+
// This insures the cache has no limit and items are never removed from the cache.
54+
CacheManager.getInstance().addCache(new Cache(TENANT_DATA_CACHE_NAME, 1000, true, true, 120, 120))
55+
tenantDataCache = CacheManager.getInstance()?.getCache(TENANT_DATA_CACHE_NAME)
4256
}
43-
44-
/**
45-
* This will load the hosts data which contains the map between domain and tenant. It will also load the database
46-
* objects in to another cache that can be accessed to retrieve additional data such as name etc.
47-
*/
48-
void loadDomainTenantMap() {
49-
50-
hosts.clear()
51-
Cache tenantDataCache = CacheManager.getInstance()?.getCache(TENANT_DATA_CACHE_NAME)
52-
if (tenantDataCache == null) {
53-
// This insures the cache has no limit and items are never removed from the cache.
54-
CacheManager.getInstance().addCache(new Cache(TENANT_DATA_CACHE_NAME, 1000, true, true, 120, 120))
55-
tenantDataCache = CacheManager.getInstance()?.getCache(TENANT_DATA_CACHE_NAME)
56-
} else {
57-
tenantDataCache.removeAll()
58-
}
59-
60-
// See if there is a custom data source bean name
61-
def domainTenantBeanName = ConfigurationHolder.config.tenant.domainTenantBeanName
62-
if (domainTenantBeanName == null || domainTenantBeanName?.size() == 0) {
63-
domainTenantBeanName = "tenant.DomainTenantMap"
64-
}
65-
66-
// Load the tenant information for the domain to tenant id from the database.
67-
def list = applicationContext.getBean(domainTenantBeanName).list()
68-
log.info "Loading " + list?.size() + " Domain Name to Mapped Tenant Id entries from the database object " + domainTenantBeanName
69-
list.each { map ->
70-
if (log.isDebugEnabled()) {
71-
log.debug "Domain->Tenant: ${map.domainName}->${map.mappedTenantId}"
72-
}
73-
hosts.put(map.domainName, map.mappedTenantId)
74-
tenantDataCache.put(new Element(map.mappedTenantId, map))
75-
}
57+
else
58+
{
59+
tenantDataCache.removeAll()
60+
}
61+
// See if there is a custom data source bean name
62+
def domainTenantBeanName = ConfigurationHolder.config.tenant.domainTenantBeanName
63+
if (domainTenantBeanName == null || domainTenantBeanName?.size() == 0)
64+
{
65+
domainTenantBeanName = "tenant.DomainTenantMap"
66+
}
67+
// Load the tenant information for the domain to tenant id from the database.
68+
def list = applicationContext.getBean(domainTenantBeanName).list()
69+
log.info "Loading " + list?.size() + " Domain Name to Mapped Tenant Id entries from the database object " + domainTenantBeanName
70+
list.each {map ->
71+
if (log.isDebugEnabled())
72+
{
73+
log.debug "Domain->Tenant: ${map.domainName}->${map.mappedTenantId}"
74+
}
75+
hosts.put(map.domainName, map.mappedTenantId)
76+
tenantDataCache.put(new Element(map.mappedTenantId, map))
7677
}
77-
/**
78-
* The event broker listens for every time a record is saved, then calls a refresh on the
79-
* list of hosts whenever the DomainTenantMap object is changed via a save or update.
80-
*/
81-
void setEventBroker(GroovyEventBroker inEventBroker) {
82-
if (inEventBroker != null) {
83-
inEventBroker.subscribe("hibernate.${HibernateEvent.saveOrUpdate}.DomainTenantMap") { event, broker ->
84-
log.info "DomainTenantMap was changed via a save or update. Reloading the Domain to Tenant information from the database."
85-
this.initialize()
86-
}
87-
}
78+
}
79+
/**
80+
* The event broker listens for every time a record is saved, then calls a refresh on the
81+
* list of hosts whenever the DomainTenantMap object is changed via a save or update.
82+
*/
83+
void setEventBroker(GroovyEventBroker inEventBroker)
84+
{
85+
if (inEventBroker != null)
86+
{
87+
inEventBroker.subscribe("hibernate.${HibernateEvent.saveOrUpdate}.DomainTenantMap") {
88+
event, broker ->
89+
log.info "DomainTenantMap was changed via a save or update. Reloading the Domain to Tenant information from the database."
90+
this.initialize()
91+
}
8892
}
93+
}
8994
}

src/groovy/grails/plugin/multitenant/core/DomainNamePropertyTenantResolver.groovy

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -8,16 +8,16 @@ import org.codehaus.groovy.grails.commons.ConfigurationHolder
88
* least the following keys:
99
* fullservername = mapped_tenant_id
1010
*/
11-
public class DomainNamePropertyTenantResolver extends BaseDomainNameTenantResolver {
12-
13-
/**
14-
* This will clear out the current list of tenants and load the most current copy form the data in the configuration
15-
* file.
16-
*/
17-
18-
public void initialize() {
19-
hosts.clear();
20-
Map domainTenants = ConfigurationHolder.config.tenant.domainTenantMap.flatten()
21-
hosts.putAll(domainTenants)
22-
}
11+
public class DomainNamePropertyTenantResolver extends BaseDomainNameTenantResolver
12+
{
13+
/**
14+
* This will clear out the current list of tenants and load the most current copy form the data in the configuration
15+
* file.
16+
*/
17+
public void initialize()
18+
{
19+
hosts.clear();
20+
Map domainTenants = ConfigurationHolder.config.tenant.domainTenantMap.flatten()
21+
hosts.putAll(domainTenants);
22+
}
2323
}

0 commit comments

Comments
 (0)