Define optional (multiple) datasource in Grails - grails

I am currently developing a Grails plugin using Grails 2.3.11 and familiar using multiple datasources. But now I want that my domain objects provided by that plugin CAN use a different dataSource (because of performance reasons all data can be stored in a different database).
The problem is, that this second datasource is optional, which means, the user can define a second datasource in tomcat (accessible via JNDI), but doesn't have to. This is an important point: the user can define a second dataSource in the servlet container and the Grails application has to check if there is a second dataSource available!
Be to more specific: I've a domain class:
class MyDomain {
static mapping = {
datasource('optionalDS')
}
}
My DataSource.groovy:
dataSource {
jndiName = "..."
}
dataSource_optionalDS {
jndiName = "..."
}
The problem is, that this will fail if the user hasn't configured the JNDI name for that optional datasource (because it is optional, he doesn't have to).
I tried to create a delegating datasource instead:
class OptionalDataSource extends DelegatingDataSource {
...
// the main purpose is to check, if the optional DS
// can be created using JNDI. If this fails, the default
// DS is used
setTagetDataSource(dataSource)
...
}
And in my Plugin descriptor:
def doWithSpring = {
dataSource_optionalDS(OptionalDataSource) {
// set default DS in case optional can not be created
dataSource = ref('dataSource')
}
}
The problem with this solution is, that the dataSource optionalDS is not available. If I try to read data, i.e. MyDomain.findAll() I get the following error:
Methond on class MyDomain was used outside of a Grails application.
If running in the context of a test using the mocking API or
bootstrap Grails correctly.
I don't understand why, because I can define the default dataSource that way.
So, my question is: How can I define an optional dataSource in Grails?

How about looking up the JNDI dataSource in DataSource.groovy and then if it exists declaring your optional dataSource. Something like following
//default dataSource
dataSource {
jndiName = "..."
}
//optional dataSource
//let's first lookup the JNDI dataSource
def jndiDataSource
try {
jndiDataSource = InitialContext.doLookup("...")
}
catch(NamingException ne) {}
//now if jndiDataSource exists we can declare the optional dataSource
if(jndiDataSource) {
dataSource_optionalDS {
jndiName = "..."
}
}
I was looking for an alternate lightwight method for just checking if the JNDI dataSource exists instead of looking it up. But no luck.

Related

Grails 3.x multiple datasources

Back in Grails 2.x world I could create multiple dataSources in an environment:
development {
dataSource {
...
}
dataSource_new {
...
}
}
and reference them in the controller:
def db = new SQL(dataSource_new)
and everything worked awesome. In Grails 3.x, everything is not awesome:
development:
dataSource:
...
dataSource_new:
...
and calling
def db = new SQL(dataSource_new)
throws:
Ambiguous method overloading for method groovy.sql.Sql#
Anyone have success with this (or can point out what's changed that I've missed)?
Tried mapping in domain with no luck:
class abc {
String ...
static mapping = {
datasource: ['DEFAULT', 'dataSource_new']
}
throws:
Ambiguous method overloading for method groovy.sql.Sql#<init>. Cannot resolve which method to invoke for [null] due to overlapping prototypes between: [interface java.sql.Connection] [interface javax.sql.DataSource]
I work with multiply datasources in Grails 3.x like following:
assuming you have configuration:
development {
dataSources {
dataSource {
url = 'your_url'
password = 'psw'
...
}
second {
url = 'you_url_2'
password = 'psw2'
...
}
}
So in a service you will have mapped data source like this:
dataSource_second
And to create Sql instance you will need to do the following:
def sql = new Sql(dataSource_second)
It works in Grails 3.0.11.
Hope it will help.
In official docs you can find an example for injecting datasource into service:
https://grails.github.io/grails-doc/latest/guide/single.html#multipleDatasources
class DataService {
static datasource = 'lookup'
void someMethod(...) {
//…
}
}
or into command:
http://grails.github.io/grails-doc/latest/guide/upgrading.html
import grails.dev.commands.*
import javax.sql.*
import groovy.sql.*
import org.springframework.beans.factory.annotation.*
class RunQueryCommand implements ApplicationCommand {
#Autowired
DataSource dataSource
boolean handle(ExecutionContext ctx) {
def sql = new Sql(dataSource)
println sql.executeQuery("select * from foo")
return true
}
}
Moreover look at this thread and quoted examples: https://github.com/grails/grails-core/issues/701

Grails Multitenancy with Multiple Databases (one per tenant) with 2.4.0

I can't seem to find any plugin or example about Grails Multitenancy with Multiple Databases (one per tenant). I am using grails 2.4.0 . Can anyone help me?
In my application I needed to be able to access multiple databases and I didn't have the connection strings to the databases at startup so I couldn't preconfigure them as datasources.
The strategy I used was as follows:
1) implement an abstractroutingdatasource which stores a collection of datasources. It also registers a global closure withDataSource{}, a accepted some parameters and code block. The parameters are a name and connection information of the data source. In the withDataSource method, it either creates a new DataSource and saves it to a map using its key, or uses the saved on from the map.
2) in my client code I'd use the withDataSource closure to access whichever database I needed.
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource
import org.springframework.context.ApplicationContextAware
import org.springframework.beans.factory.DisposableBean
import org.springframework.beans.factory.InitializingBean
import org.springframework.context.ApplicationContext
import javax.sql.DataSource
import org.springframework.jdbc.datasource.DriverManagerDataSource
import org.springframework.jdbc.datasource.lookup.DataSourceLookup
import org.springframework.jdbc.datasource.SimpleDriverDataSource
class SwitchableDataSource
extends AbstractRoutingDataSource
implements ApplicationContextAware,
InitializingBean,
DisposableBean
{
ApplicationContext applicationContext
Map _targetDataSources
private ThreadLocal<String> currentDataSource = new ThreadLocal<String>()
void afterPropertiesSet(){
super.afterPropertiesSet()
// register global closure for setting the current datasource
// ... should probably be restricted to services and controllers.
Object.metaClass.withDataSource = this.&executeWithDataSource
}
public synchronized def executeWithDataSource(def params, def method){
// if datasource doesn't exist, register new one in application context
// set a threadlocal key for the current datasource key
def dataSourceName = ""
def key = params.clientId.toString()
if( !_targetDataSources.containsKey(key) ){
dataSourceName = "dataSource_${params.clientId}".toString()
applicationContext.registerSingleton(dataSourceName,
//org.apache.commons.dbcp.BasicDataSource,
org.springframework.jdbc.datasource.DriverManagerDataSource,
new org.springframework.beans.MutablePropertyValues(
[
driverClassName : 'com.mysql.jdbc.Driver',
username : params.username.toString(),
password : params.password.toString(),
url : params.connectionString.toString()
]
)
)
// add datasource to map
_targetDataSources[key] = applicationContext.getBean(dataSourceName)
targetDataSources = _targetDataSources
super.afterPropertiesSet() // AbstractRoutingDataSource is lame... it converts 'targetDataSource' into 'resolvedDataSource' in afterPropertiesSet()
}
// set the thread local variable for the current datasource
currentDataSource.set(params.clientId.toString())
// call the closure that was passed in containing some data access code
// and return whatever it returns
return method()
}
void destroy(){
// remove global closure
Object.metaClass.withDataSource = null // may need to throw a missing method exception
}
protected Object determineCurrentLookupKey() {
(currentDataSource.get()?:"DEFAULT_DATASOURCE").toString()
}
}
2) in client code, make calls as follows:
withDataSource( [ clientId: client.id, // client.id was my key
connectionString: jdbcConnection.url,
username: jdbcConnection.username,
password: jdbcConnection.password ] ) {
SomeDomainObject.list()
SomeDomainObject.delete();
}
3) remember to register your routing datasource with spring in your resources.groovy. Notice that it assigns a default datasource so the initial datasource list isn't empty.
// Place your Spring DSL code here
beans = {
dataSource_defaultClientDb(org.apache.commons.dbcp.BasicDataSource) { bean ->
bean.singleton = true
driverClassName = 'com.mysql.jdbc.Driver'
username = '${defaultClientDB_username}'
password = '${defaultClientDB_password}'
url = '${defaultClientDB_url}'
}
dataSource_clientdb(SwitchableDataSource){ bean ->
bean.singleton = true
_targetDataSources = ["DEFAULT_DATASOURCE":ref("dataSource_defaultClientDb")]
targetDataSources = _targetDataSources
}
lobHandlerDetector_clientdb(org.springframework.jdbc.support.lob.DefaultLobHandler)
}
For your application, you probably don't need to use the withDataSource method as I used it - you just need to register your connections with the switchabledatasource and tell it how to lookup which datasource it should be using, for example you could assign some ID to a thread local data on each request and the switchabledatasource could use the same thread local value to determine which datasource to use.
I hope this at least gets you going in the right direction.

Configuring and injecting Grails services

Grails 2.4.5 here. I did a grails create-service com.example.service.Simple which created a SimpleService for me, which I then modified to look like so:
class SimlpeService {
SimpleClient simpleClient
Buzz doSomething(String derp) {
// ...
Fizz fizz = simpleClient.doSomething(derp)
// ...
fizz.asBuzz()
}
}
I can now "inject" controllers with SimpleService like so:
class MyController {
SimpleService simpleService
def index() {
// etc...
}
}
But how do I configure/wire SimpleService with the correct SimpleClient instance. Let's pretend SimpleClient is typically built like so:
SimpleClient simpleClient = SimpleClientBuilder
.withURL('http://simpleclientdev.example.com/simple')
.withAuth('luggageCombo', '12345')
.isOptimized(true)
.build()
Depending on what environment I'm in, I may want my SimpleClient instance to connect to simpleclientdev.example.com, simpleclientqa.example.com, or even simpleclient.example.com. Also, I may use different auth credentials, and I might/might not want it to be "optimized", etc. The point is: How do I inject the SimpleService with a SimpleClient instance?
You can use the Java's PostConstruct annotation on one of your method in your service to do the stuff you want. From the docs:
The PostConstruct annotation is used on a method that needs to be
executed after dependency injection is done to perform any
initialization.
SimpleService.groovy
import javax.annotation.PostConstruct
class SimlpeService {
private SimpleClient simpleClient
def grailsApplication
#PostConstruct
void postConstruct() {
def config = grailsApplication.config.client.data
SimpleClient simpleClient = SimpleClientBuilder
.withURL(config.url)
.withAuth('luggageCombo', config.username)
.isOptimized(config.optimized)
.build()
}
Buzz doSomething(String derp) {
// ...
Fizz fizz = simpleClient.doSomething(derp)
// ...
fizz.asBuzz()
}
}
So, Grails or Spring will call this method postConstruct() automatically when all the dependencies (in this case grailsApplication) for this service are resolved and any of the service method is invoked.
This has been taken care that that method must invoke before you access any field member or method of the SimpleService.
Now, everything is already configured like you mentioned that you may need to call different URL with different credential, just you have to define them in your Config.groovy as:
environments {
development {
client {
data {
url = "simpleclientdev.example.com"
username = "test"
optimized = false
}
}
}
production {
client {
data {
url = "simpleclient.example.com"
username = "johndoe"
optimized = true
}
}
}
}
Now when you do run-app with development mode and calling simpleService.doSomething() in your example controller will automatically hit the simpleclientdev.example.com URL with test credential and when you deploy it using production environment, the same simpleService.doSomething() method will hit simpleclient.example.com with optimized set to true.
Update
The key point here based on your question is that we will not be injecting different instances of SimpleService since services are singleton by default. Instead we are changing the value's associated with the service based on the environment.
Sounds like you need to understand a bit more about how to leverage Spring maybe?
Grails Spring Docs
You can also do things like so in your Service class
#PostConstruct
void init() {
//configure variables, etc here ...
log.debug("Initialised some Service...")
}

Grails : Configuring data source at runtime

I am working on a small grails application. I would like to create a install wizard for the application giving the user the ability to configure data connection settings via a web interface. Is there a way to stall the data connection and configure the datasource while the application is running? I would like to do this while maintaining the ability to leverage GORM/Hibernate for all domain objects. Restarting/reloading the application after configuring the datasource would be ok.
In Config.groovy file add 1 more entry for value of grails.config.locations
in result you will have something like this
grails.config.locations = ["file:${userHome}/.grails/global-config.groovy",
"classpath:${appName}-config.properties",
"classpath:${appName}-config.groovy",
"file:${userHome}/.grails/${appName}-config.properties",
"file:${userHome}/.grails/${appName}-config.groovy",
"file:${userHome}/.grails/${appName}-MyDbconfig.groovy"]
In you application after user enters DB connection parameters write it to file
"file:${userHome}/.grails/${appName}-MyDbconfig.groovy"
in corresponding format. Like follwoing
dataSource{
url = '...'
username = '...'
password = '...'
properties {
...
}
}
save file and restart application.
Since you say the user should be able to configure the datasource via web interface then you should ask for configuration params since maxActive, maxIdle, etc... Once you have all the params you store anywhere you want.
Inside your Grails application:
// grails-app/conf/BootStrap.groovy
import utils.DataSourceConfigurator
class BootStrap {
def init = { servletContext ->
DataSourceConfigurator.configure(servletContext)
}
}
Like this you say to your application that once its ready to run it needs to configure the DataSource first.
package utils
import org.codehaus.groovy.grails.commons.ApplicationAttributes
import org.codehaus.groovy.grails.web.json.JSONObject
class DataSourceConfigurator {
private static JSONObject getDSConfig() {
JSONObject conf = new JSONObject()
// Go find the configuration wherever you stored it from the web interface, parse it and return it.
return conf
}
public static final configure = { servletContext ->
if(!servletContext) {
// Invalid servlet context
return
}
def context = servletContext.getAttribute(ApplicationAttributes.APPLICATION_CONTEXT)
def ds = context?.dataSourceUnproxied
if(!ds) {
// There is no datasource available to configure.
return
}
JSONObject config = getDSConfig()
if(!config) {
// No previous configuration saved from web interface.
return
}
ds.setInitialSize(config.initialSize)
ds.setMaxWait(config.maxWait)
ds.setMaxActive(config.maxActive)
ds.setMinIdle(config.minIdle)
ds.setMaxIdle(config.maxIdle)
....
// There are many other variables to configure. Check them by yourself.
}
}
For more information you could check the documentation regarding DataSource configuration: http://docs.grails.org/latest/guide/conf.html#transactionAwareDataSourceProxy

Set datasource values during run time

So essentially I'm trying to get my project up and running on AppFog. The datasource information is stored in an enviornment variable, which is essentially JSON. My goal is to take this data and set my datasource config from it.
Here is what I have tried:
Code to set the datasource config which is a method in a POGO. The POGO is instantiated and the method called at the beginning of DataSource.groovy:
import appfog.ParseDataSource
new ParseDataSource().setConfig()
dataSource {
...
}
class ParseDataSource {
void setConfig() {
String env = java.lang.System.getenv("VCAP_SERVICES")
if (env) {
def config = JSON.parse(env)
config = config["mysql-5.1"][0].credentials
grailsApplication.config.environments.production.dataSource.username = config.username
grailsApplication.config.environments.production.dataSource.password = config.password
grailsApplication.config.environments.production.dataSource.url = "jdbc:mysql://" + config.host + ":" + config.port + "/" + config.name
}
}
}
The problem is that grailsApplication is always null. I've tried registering a spring bean in resources.groovy:
beans = {
parseDataSource(appfog.ParseDataSource) {
grailsApplication = ref('grailsApplication')
}
}
class ParseDataSource {
def grailsAPplication
...
}
I've also tried getting it via Holders:
GrailsApplication grailsApplication = Holders.grailsApplication
Either way it is null so I'm not doing something right. Any ideas?
I think you are making this overly complex. Overwriting the grails config object while still in the process of building it would cause an order of operations issue that would make the code very fragile.
Simply setting the values directly seems more straightforward:
Datasource.groovy:
def configJson = JSON.parse(java.lang.System.getenv("VCAP_SERVICES"))
def mysqlConfig = configJson["mysql-5.1"][0].credentials
dataSource = {
production = {
username = mysqlConfig.username
// etc.
}
}
If you wanted to keep parsing in its own class for clarity's sake, make the values properties and read them in the dataSource block rather than trying to put them in the grails config object:
config parsing:
class EnvironmentConfigParser {
String username
String password
String url
EnvironmentConfigParser() {
def configJson = JSON.parse(java.lang.System.getenv("VCAP_SERVICES"))
def mysqlConfig = configJson["mysql-5.1"][0].credentials
username = mysqlConfig.username
password = mysqlConfig.password
url = "jdbc:mysql://${mysqlConfig.host}:${mysqlConfig.port}/${mysqlConfig.name}"
}
}
in Datasource.groovy:
def parser = new EnvironmentConfigParser()
dataSource = {
production = {
username = parser.username
// etc
}
}
You should be able to access grailsApplication the way you have injected in resources.groovy provided you are injecting the bean parseDataSource somewhere in your application in any artefact.
In your special case you need the bean to be available in datasource.groovy. You were instantiating the POGO which will not help you injecting grailsApplication to the POGO. On the other hand, you cannot actually inject the POGO to datasource.groovy like
def parseDataSource
because it(datasource) is a config object during bootstrap.
The best way remains will be to metaClass the pogo at BootStrap and make grailsApplication available to it. Burt has shown it here exactly that way.
I was also thinking whether BeanPostProcessor can be useful in this case but I am not sure whether config per environment will be achieved. But you can give it a try if it helps in achieving your business need. It generally goes like:
//src/groovy
import org.springframework.beans.factory.config.BeanPostProcessor
class DatasourcePostProcessor implements BeanPostProcessor{
def parseDataSource
#Override
Object postProcessBeforeInitialization(Object bean, String beanName) {
return bean
}
#Override
Object postProcessAfterInitialization(Object bean, String beanName) {
if(beanName == 'dataSource') {
//Set values to dataSource bean as required
parseDataSource.setConfig(bean)
}
return bean
}
}
//resources.groovy
parseDataSource(ParseDataSource){
grailsApplication = ref('grailsApplication')
}
datasourcePostProcessor(DatasourcePostProcessor){
parseDataSource = ref('parseDataSource')
}

Resources