-
Notifications
You must be signed in to change notification settings - Fork 5
/
MongodbMultitenantGrailsPlugin.groovy
217 lines (136 loc) · 6.57 KB
/
MongodbMultitenantGrailsPlugin.groovy
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
import se.webinventions.mongomultitenant.MongoTenantDatastoreFactoryBean
import org.springframework.aop.scope.ScopedProxyFactoryBean
import se.webinventions.mongomultitenant.DomainTenantResolverService
import se.webinventions.mongomultitenant.TenantService
class MongodbMultitenantGrailsPlugin {
// the plugin version
def version = "0.1.1-ALPHA"
// the version or versions of Grails the plugin is designed for
def grailsVersion = "1.3.7 > *"
// the other plugins this plugin depends on
def dependsOn = [mongodb: " * > 0.9"]
def loadAfter = ['mongodb']
// resources that are excluded from plugin packaging
def pluginExcludes = [
"grails-app/views/error.gsp"
]
// TODO Fill in these fields
def author = "Per Sundberg"
def authorEmail = "[email protected]"
def title = "Mongodb Multitenant plugin"
def description = '''\\
Plugin that enables multitenancy for mongodb. The plugin works by overrideing the mongoDatastore bean. All tenants have their own
MongoTemplate for choosen tenant domain classes and thereby enabling them to create their own collections and database settings.
Collections are postfixed with _tenantname_tenantid, and also the database is post fixed with an integer 'e.g. _0' for the first
e.g. 500 tenants (depending on setting in config file)
The plugin comes with 2 extra beans which is used by the overriden mongoDatastore bean
* tenantResolver which is responsible for resolving tenants. The default is a domain resover service which resolves against a tenantdomainmap
The interface it implements is as follows:
/**
* Get the tenant database name, you can use the originalDatabaseName as a 'starting' point if you want
* and just .e.g. append a tenant id to it or whatever you like.
*
* @param originalDatabaseName the database name it should have had if it wasn't a multi tenant
* @return
*/
public String getTenantDatabaseName(String originalDatabaseName)
/**
* Get the collection name used for this tenant.
* @param originalCollectionName name that it should have had if it wasnt a multi tenant
* @return
*/
public String getTenantCollectionName(String originalCollectionName)
/**
* Gets the current tenant id, (eg. based on url resolving for example or currently logged in user or whatever)
* @return
*/
public Object getTenantId()
/**
* override the current set tenant with this one
* @param tenantid
*/
public void setTenantId(Object tenantid)
/**
* resets the tenant to the default tenant (based on url for example or logged in user or whatever)
*/
public void resetToDefaultTenant()
//get the actual tenant object itself instead of it's id (could be same for simple tenants)
public Object getTenant()
public Object setTenant(Object tenant)
* tenantService which is a helper service for tenants, if you overide this you have to provide at least these methods:
public void doWithTenant(Object tenantId, Closure closure) throws Throwable //invokes a closure with a temporary tenantid, in this way superadmins and alike can reach all tenants in an easy way.
public TenantProvider createOrGetDefaultTenant(String name)
public TenantProvider createNewTenant(String name) // creates a new tenant (TenantProvider)
Start of by generating your tenant domain classes which will create
se/webinventions/TenantDomainMap.groovy
and
se/webinventions/Tenant.groovy
In your domains folder.
These can be moved to any package but then you have to specify where in config.groovy (se example below)
The Tenant implements the TenantProvider interface and can thus be replaced with any other class as long as it implements that interface.
so you can choose to store your tenants in any way you like.
You mark domainclasses either through inclusion or exclusion by adding the config field in Config.groovy
Config options include:
grails.mongo.tenant.tenantclassname = "your.package.TenantDomainclass"
grails.mongo.tenant.tenantsPerDb = 500
grails.mongo.tenant.excludingdomainclasses =[Tenant,TenantDomainMap,SecRole]
//alternatively grails.mongo.tenant.includingdomainclasses =[Author,Book,ContentItem,Article] ... etc
grails.mongo.tenant.defaultTenantName = "default"
grails.mongo.tenant.defaultTenantId = new ObjectId()
You cannot specify both exclude and include at this stage. If specifying exclude then 'ALL' Domain classes except those in list will be tenant dependent
Specifying include -> only those domain classes are tenant dependent.
* Sources: https://github.com/webinventions/mongodb-multitenant
* Docs: Here..
#Release history#
* 0.1-ALPHA
Initial release.
#Road map#
* Secure plugin with more tests and some cleanups.
#Limitations#
* Right now it is not possible to have different 'ports / ips' etc for tenants.
'''
// URL to the plugin's documentation
def documentation = "http://grails.org/plugin/mongodb-multitenant"
def doWithWebDescriptor = { xml ->
// TODO Implement additions to web.xml (optional), this event occurs before
}
def doWithSpring = {
def mongoConfig = application.config?.grails?.mongo
tenantResolver(DomainTenantResolverService) {
grailsApplication = ref("grailsApplication")
}
tenantResolverProxy(ScopedProxyFactoryBean) {
targetBeanName = 'tenantResolver'
proxyTargetClass = true
}
//this will override the mongoDatastore in the grails mongodbplugin so that we can handle multi tenants of
//some domain classes which are configured as exclude or include (not both) list of domain classes i Config.groovy
mongoDatastore(MongoTenantDatastoreFactoryBean) {
mongo = ref("mongoBean")
mappingContext = ref("mongoMappingContext")
config = mongoConfig.toProperties()
tenantResolverProxy = ref("tenantResolverProxy")
}
tenantService(TenantService) {
grailsApplication = ref("grailsApplication")
}
tenantServiceProxy(ScopedProxyFactoryBean) {
targetBeanName = 'tenantService'
proxyTargetClass = true
}
}
def doWithDynamicMethods = { ctx ->
}
def doWithApplicationContext = { applicationContext ->
// TODO Implement post initialization spring config (optional)
}
def onChange = { event ->
// TODO Implement code that is executed when any artefact that this plugin is
// watching is modified and reloaded. The event contains: event.source,
// event.application, event.manager, event.ctx, and event.plugin.
}
def onConfigChange = { event ->
// TODO Implement code that is executed when the project configuration changes.
// The event is the same as for 'onChange'.
}
}