Grails : Configuring data source at runtime - grails

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

Related

How to get values from properties file in grails?

How to get values from properties file please? and where should I put the file ?
Thank you
EDIT : I'm using grails 3.1.5 And I'm trying to get properties from a job class (quartz)
Either keep your properties directly in Config.groovy file.
Or you can create a .properties file to keep properties and add this file in Config.groovy
grails.config.locations = [ "classpath:grails-app-config.properties"]
and access it anywhere in application using
grailsApplication.config."propertyName"
We have a trait like this:
/**
* Load config from config locations given by property grails.config.locations.
* Based on http://grails.1312388.n4.nabble.com/Grails-3-External-config-td4658823.html
*/
trait ExternalConfigurationLoader implements EnvironmentAware {
#Override
void setEnvironment(Environment environment) {
loadExternalConfigLocations(environment)
}
void loadExternalConfigLocations(Environment environment) {
if (environment) {
def configLocations = findConfigLocationsFromApplicationGroovy()
DefaultResourceLocator resourceLocator = new DefaultResourceLocator()
for (String configLocation in configLocations) {
loadConfigLocation(configLocation, grails.util.Environment.current.name, environment, resourceLocator)
}
}
}
List<String> findConfigLocationsFromApplicationGroovy() {
def applicationGroovy = this.getClass().classLoader.getResource('application.groovy')
if (applicationGroovy) {
def applicationConfiguration = new ConfigSlurper(grails.util.Environment.current.name).parse(applicationGroovy)
return applicationConfiguration.grails.config.locations
}
[]
}
void loadConfigLocation(String configLocation, String currentEnvironmentName, Environment environment, ResourceLocator resourceLocator) {
def configurationResource = resourceLocator.findResourceForURI(configLocation)
if (configurationResource) {
log.debug "External config '$configLocation' found. Loading."
def configSlurper = new ConfigSlurper(currentEnvironmentName)
def config = configSlurper.parse(configurationResource.getURL())
environment.propertySources.addFirst(new MapPropertySource(configLocation, config))
} else {
log.debug "External config '$configLocation' not found."
}
}
}
Then we can add this trait to Application.groovy:
class Application extends GrailsAutoConfiguration implements ExternalConfigurationLoader {
and configure external config files in application.groovy:
grails.config.locations = ["classpath:myapp-config.groovy", "file:dev-config.groovy"]
If using Tomcat, you can then put myapp-config.groovy in Tomcats lib folder.
Note: this variant only supports external config files of type .groovy but you can extend it to support .yml or .properties if you prefer that. Also note that this example has some issues with overriding values from environment block in application.yml, so if you plan to override dataSource you will need to move the default configuration of dataSource from application.yml to application.groovy first.
There is also a plugin in the making that is adding similar support for grails.config.locations. See https://github.com/sbglasius/external-config

grails jms start listening after apllication is completly launched

I'm using grails jms-1.3plugin and I have the problem, that my jms listener Service starts consuming messages from activeMQ before the application is fully up and running. This results in an error when I try to write some messages to the DB.
So my question is, how can I manage to start consuming from a queue manually. So that I can set autoStartup to false.
here is my example grails code:
ConsumerService.groovy
package jmsstartstop
import grails.plugin.jms.Queue
class ConsumerService {
static exposes = ["jms"]
#Queue(name="liesMich")
def receiveMessage(String msg) {
log.info("Received Message:" + msg)
}
}
resources.groovy
import org.apache.activemq.ActiveMQConnectionFactory
import org.springframework.jms.connection.SingleConnectionFactory
beans = {
jmsConnectionFactory(SingleConnectionFactory) {
targetConnectionFactory = { ActiveMQConnectionFactory cf ->
brokerURL = grailsApplication.config.jms.brokerURL
}
}
}
Config.groovy
jms{
brokerURL='tcp://localhost:61616'
containers {
standard {
autoStartup = false
}
}
}
What I'm looking for is something like jmsConnectionFactory.getTargetConnectionFactory().start() that can be called in Bootstrap.groovy or maybe in a controller manually. But unfortunately this start method does not exist in the TargetConnectionFactory.
Is there a way to do it, or any other suggestions?
Bootstrap.groovy (which is not working)
class BootStrap {
def jmsConnectionFactory
def init = { servletContext ->
jmsConnectionFactory.??WHATEVER??.start()
}
def destroy = {
}
}
The issue is that the plugin starts processing messages before the Datasource plugin (part of Grails) has finished it's own startup.
The good news is that this appears to be fixed in the latest SNAPSHOT version of the plugin.
To use the SNAPSHOT change your plugin as such: :jms:1.3-SNAPSHOT in your BuildConfig.groovy
What worked for me is to MANUALLY start the JMSListener services on Bootstrap file:
e.g.
In listener.groovy:
class ClientListenerService {
boolean transactional = true
static exposes = ["jms"]
static destination = "com.moviesxd.api.domain.Client_QUEUE"
static isTopic = false
static container = "manualStart"
In bootstrap.groovy:
def clientRequestListenerJmsListenerContainer
...
clientRequestListenerJmsListenerContainer.start()
This solves the problem.

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...")
}

Define optional (multiple) datasource in 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.

Resources