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
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.
I am doing Grails tutorials on IBM(here) but I am a quite disappointed by an integration test.
To sum up : I call a method who render a JSON object according to the ID (iata).
My domain is :
class Airport {
String name
String iata
}
My controller is :
class AirportController {
// In order to enable scaffolding
def scaffold = Airport
def iata = {
def iata = params.id?.toUpperCase() ?: "NO IATA"
def airport = Airport.findByIata(iata)
if (!airport) {
airport = new Airport(iata: iata, name: "Not found")
}
render airport as JSON
}
}
When I do :
http://localhost:8080/trip-planner/airport/iata/foo (in order to retreive null value) or
http://localhost:8080/trip-planner/airport/iata/DEN (for DENVER), the method works fine !
The issue is my Integration tests :
class AirportControllerTests extends GroovyTestCase {
void testWithGoodIata(){
def controller = new AirportController()
controller.metaClass.getParams = { ->
return ["id":"den"]
}
controller.iata()
def response = controller.response.contentAsString
assertTrue response.contains("Denver")
}
void testWithWrongIata() {
def controller = new AirportController()
controller.metaClass.getParams = { ->
return ["id":"foo"]
}
controller.iata()
def response = controller.response.contentAsString
assertTrue response.contains("\"name\":\"Not found\"")
}
}
The problem is:
Whenever I run the tests (by running : grails test-app -integration trip.planner.AirportControllerTests), I will always obtain a good behavior in the first test and a groovy.lang.MissingMethodException in the second test. (even if I switch the two : the second test always fail)
If I run them separately , it works.
The exception occurred at this line (in the controller) : def airport = Airport.findByIata(iata)
Is that someting to do with "transactional" ? Any help would be great :)
P.S : I am using Grails 2.2.1
The exception stacktrace :
groovy.lang.MissingMethodException: No signature of method: trip.planner.Airport.methodMissing() is applicable for argument types: () values: []
at trip.planner.AirportController$_closure4.doCall(AirportController.groovy:39)
at trip.planner.AirportControllerTests.testWithWrongIata(AirportControllerTests.groovy:25)
I suspect the metaclass changes you're making in one test are somehow leaking through to the other. But you don't need to (and shouldn't) manipulate the metaclass in an integration test, just say
def controller = new AirportController()
controller.params.id = "den"
You only need to do mocking for unit tests.
Bear in mind that the tutorial you're looking at was written way back in 2008 (in the Grails 1.0.x days), and Grails has moved on a very long way since then, with some components (including testing) having been through one or more complete rewrites.
I've got a grails domain class I have to persist in Redis, something like this:
class A {
String one
Integer two
B three
E four
mapWith = "redis"
}
class B {
String name
}
enum E {
VALUE1, VALUE2
}
When I persist an instance of class A with the GORM .save() method, Redis saves it correctly except for the enum field "four".
As you can see the fact is known and reported here: http://jira.grails.org/browse/GPREDIS-3
Is there a good workaround to save Enum or something similar?
We're thinking about an array of String objects, what do you think?
I've got this mostly implemented but it doesn't work for Gemfire and I'm waiting until it's fixed for all the supported nosql providers before pushing the fix. As a workaround you can use the inList constraint with a combination of a persistent String property and a non-persistent get/set pair with the name of your current property, e.g.
class A {
String one
Integer two
B three
String fourString
void setFour(E e) {
fourString = e?.name()
}
E getFour() {
fourString ? E.valueOf(fourString) : null
}
static constraints = {
fourString inList: E.values()*.name()
}
static transients = ['fourString']
static mapWith = "redis"
}
Is there any way I can override the value of dateCreated field in my domain class without turning off auto timestamping?
I need to test controller and I have to provide specific domain objects with specific creation date but GORM seems to override values I provide.
Edit
My classes look like this:
class Message {
String content
String title
User author
Date dateCreated
Date lastUpdated
static hasMany = [comments : Comment]
static constraints = {
content blank: false
author nullable: false
title nullable: false, blank: false
}
static mapping = {
tablePerHierarchy false
tablePerSubclass true
content type: "text"
sort dateCreated: 'desc'
}
}
class BlogMessage extends Message{
static belongsTo = [blog : Blog]
static constraints = {
blog nullable: false
}
}
I'm using console to shorten things up. The problem which I encountered with Victor's approach is, when I write:
Date someValidDate = new Date() - (20*365)
BlogMessage.metaClass.setDateCreated = {
Date d ->
delegate.#dateCreated = someValidDate
}
I get following exception:
groovy.lang.MissingFieldException: No such field: dateCreated for class: pl.net.yuri.league.blog.BlogMessage
When I tried
Message.metaClass.setDateCreated = {
Date d ->
delegate.#dateCreated = someValidDate
}
Script goes well, but unfortunately dateCreated is not being altered.
I was having a similar issue, and was able to overwrite dateCreated for my domain (in a Quartz Job test, so no #TestFor annotation on the Spec, Grails 2.1.0) by
Using the BuildTestData plugin (which we use regularly anyway, it is fantastic)
Double-tapping the domain instance with save(flush:true)
For reference, my test:
import grails.buildtestdata.mixin.Build
import spock.lang.Specification
import groovy.time.TimeCategory
#Build([MyDomain])
class MyJobSpec extends Specification {
MyJob job
def setup() {
job = new MyJob()
}
void "test execute fires my service"() {
given: 'mock service'
MyService myService = Mock()
job.myService = myService
and: 'the domains required to fire the job'
Date fortyMinutesAgo
use(TimeCategory) {
fortyMinutesAgo = 40.minutes.ago
}
MyDomain myDomain = MyDomain.build(stringProperty: 'value')
myDomain.save(flush: true) // save once, let it write dateCreated as it pleases
myDomain.dateCreated = fortyMinutesAgo
myDomain.save(flush: true) // on the double tap we can now persist dateCreated changes
when: 'job is executed'
job.execute()
then: 'my service should be called'
1 * myService.someMethod()
}
}
Getting a hold of the ClosureEventListener allows you to temporarily disable grails timestamping.
import org.codehaus.groovy.grails.web.servlet.GrailsApplicationAttributes
import org.codehaus.groovy.grails.commons.spring.GrailsWebApplicationContext
import org.codehaus.groovy.grails.orm.hibernate.cfg.GrailsAnnotationConfiguration
import org.codehaus.groovy.grails.orm.hibernate.support.ClosureEventTriggeringInterceptor
import org.codehaus.groovy.grails.orm.hibernate.support.ClosureEventListener
class FluxCapacitorController {
def backToFuture = {
changeTimestamping(new Message(), false)
Message m = new Message()
m.dateCreated = new Date("11/5/1955")
m.save(failOnError: true)
changeTimestamping(new Message(), true)
}
private void changeTimestamping(Object domainObjectInstance, boolean shouldTimestamp) {
GrailsWebApplicationContext applicationContext = servletContext.getAttribute(GrailsApplicationAttributes.APPLICATION_CONTEXT)
GrailsAnnotationConfiguration configuration = applicationContext.getBean("&sessionFactory").configuration
ClosureEventTriggeringInterceptor interceptor = configuration.getEventListeners().saveOrUpdateEventListeners[0]
ClosureEventListener listener = interceptor.findEventListener(domainObjectInstance)
listener.shouldTimestamp = shouldTimestamp
}
}
There may be an easier way to get the applicationContext or Hibernate configuration but that worked for me when running the app. It does not work in an integration test, if anyone figures out how to do that let me know.
Update
For Grails 2 use eventTriggeringInterceptor
private void changeTimestamping(Object domainObjectInstance, boolean shouldTimestamp) {
GrailsWebApplicationContext applicationContext = servletContext.getAttribute(GrailsApplicationAttributes.APPLICATION_CONTEXT)
ClosureEventTriggeringInterceptor closureInterceptor = applicationContext.getBean("eventTriggeringInterceptor")
HibernateDatastore datastore = closureInterceptor.datastores.values().iterator().next()
EventTriggeringInterceptor interceptor = datastore.getEventTriggeringInterceptor()
ClosureEventListener listener = interceptor.findEventListener(domainObjectInstance)
listener.shouldTimestamp = shouldTimestamp
}
I got this working by simply setting the field. The trick was to do that after the domain object has been saved first. I assume that the dateCreated timestamp is set on save and not on object creation.
Something along these lines
class Message {
String content
Date dateCreated
}
// ... and in test class
def yesterday = new Date() - 1
def m = new Message( content: 'hello world' )
m.save( flush: true )
m.dateCreated = yesterday
m.save( flush: true )
Using Grails 2.3.6
As of Grails 3 and GORM 6 you can tap into AutoTimestampEventListener to execute a Runnable that temporarily ignores all or select timestamps.
The following is a small snippet I use in my integration tests where this is necessary:
void executeWithoutTimestamps(Class domainClass, Closure closure){
ApplicationContext applicationContext = Holders.findApplicationContext()
HibernateDatastore mainBean = applicationContext.getBean(HibernateDatastore)
AutoTimestampEventListener listener = mainBean.getAutoTimestampEventListener()
listener.withoutTimestamps(domainClass, closure)
}
Then in your case you could do the following:
executeWithoutTimestamps(BlogMessage, {
Date someValidDate = new Date() - (20*365)
BlogMessage message = new BlogMessage()
message.dateCreated = someValidDate
message.save(flush: true)
})
I'm using something like this for an initial import/migration.
Taking gabe's post as a starter (which didn't work for me Grails 2.0), and looking at the old source code for ClosureEventTriggeringInterceptor in Grails 1.3.7, I came up with this:
class BootStrap {
private void changeTimestamping(Object domainObjectInstance, boolean shouldTimestamp) {
Mapping m = GrailsDomainBinder.getMapping(domainObjectInstance.getClass())
m.autoTimestamp = shouldTimestamp
}
def init = { servletContext ->
changeTimestamping(new Message(), false)
def fooMessage = new Message()
fooMessage.dateCreated = new Date("11/5/1955")
fooMessage.lastUpdated = new Date()
fooMessage.save(failOnError, true)
changeTimestamping(new Message(), true)
}
}
You can try to disable it by setting autoTimestamp = false in the domain class mapping. I doubt about global overriding because the value is taken directly from System.currentTimeMillis() (I'm looking at org.codehaus.groovy.grails.orm.hibernate.support.ClosureEventListener.java).
So I can only suggest that you override a setter for dateCreated field in your class, and assign your own value. Maybe even metaclass access will work, like
Date stubDateCreated
...
myDomainClass.metaClass.setDateCreated =
{ Date d -> delegate.#dateCreated = stubDateCreated }
I couldn't get the above techniques to work, the call to GrailsDomainBinder.getMapping always returned null???
However...
You can use the fixtures plugin to set the dateCreated property on a domain instance
The initial loading will not do it...
fixture {
// saves to db, but date is set as current date :(
tryDate( SomeDomain, dateCreated: Date.parse( 'yyyy-MM-dd', '2011-12-25') )
}
but if you follow up with a post handler
post {
// updates the date in the database :D
tryDate.dateCreated = Date.parse( 'yyyy-MM-dd', '2011-12-01')
}
Relevant part of the fixtures docs here
AFAIK fixtures don't work for unit testing, although the plugin authors may add unit testing support in the future.
A simpler solution is to use a SQL query in your integration test to set it as you please after you initialize your object with the other values you want.
YourDomainClass.executeUpdate(
"""UPDATE YourDomainClass SET dateCreated = :date
WHERE yourColumn = :something""",
[date:yourDate, something: yourThing])
As of grails 2.5.1, getMapping() method of GrailsDomainBinder class is not static,non of the above method works as is. However, #Volt0's method works with minor tweaking. Since all of us are trying to do so to make our tests working, instead of placing it in BootStrap, I placed it in actual integration test. Here is my tweak to Volt0's method:
def disableAutoTimestamp(Class domainClass) {
Mapping mapping = new GrailsDomainBinder().getMapping(domainClass)
mapping.autoTimestamp = false
}
def enableAutoTimestamp(Class domainClass) {
Mapping mapping = new GrailsDomainBinder().getMapping(domainClass)
mapping.autoTimestamp = true
}
And simply call these methods in tests like
disableAutoTimestamp(Domain.class)
//Your DB calls
enableAutoTimestamp(Domain.class)
The above code can also be placed in src directory and can be called in tests however I placed this in actual test as there was only one class in my app where I needed this.
The easy solution is to add a mapping:
static mapping = {
cache true
autoTimestamp false
}