Groovy Mixins? - grails

I'm trying to mix-in a class in my Groovy/Grails app, and I'm using the syntax defined in the docs, but I keep getting an error.
I have a domain class that looks like this:
class Person {
mixin(ImagesMixin)
// ...
}
It compiles fine, but for some reason it won't work. The file containing ImagesMixin is located in my /src/groovy/ directory.
I've tried it using Groovy versions 1.5.7 and 1.6-RC1 without any luck. Does anyone know what I'm doing wrong?
stacktrace:
2008-12-30 17:58:25.258::WARN: Failed startup of context org.mortbay.jetty.webapp.WebAppContext#562791{/FinalTransmission,/home/kuccello/Development/workspaces/lifeforce/FinalTransmission/web-app}
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'pluginManager' defined in ServletContext resource [/WEB-INF/applicationContext.xml]: Invocation of init method failed; nested exception is java.lang.ExceptionInInitializerError
at java.security.AccessController.doPrivileged(Native Method)
at RunApp_groovy$_run_closure2_closure7.doCall(RunApp_groovy:67)
at RunApp_groovy$_run_closure2_closure7.doCall(RunApp_groovy)
at Init_groovy$_run_closure6.doCall(Init_groovy:131)
at RunApp_groovy$_run_closure2.doCall(RunApp_groovy:66)
at RunApp_groovy$_run_closure2.doCall(RunApp_groovy)
at RunApp_groovy$_run_closure1.doCall(RunApp_groovy:57)
at RunApp_groovy$_run_closure1.doCall(RunApp_groovy)
at gant.Gant.dispatch(Gant.groovy:271)
at gant.Gant.this$2$dispatch(Gant.groovy)
at gant.Gant.invokeMethod(Gant.groovy)
at gant.Gant.processTargets(Gant.groovy:436)
at gant.Gant.processArgs(Gant.groovy:372)
Caused by: java.lang.ExceptionInInitializerError
at java.lang.Class.forName0(Native Method)
at java.lang.Class.forName(Class.java:169)
at Episode.class$(Episode.groovy)
at Episode.<clinit>(Episode.groovy)
... 13 more
Caused by: groovy.lang.MissingMethodException: No signature of method: static Person.mixin() is applicable for argument types: (java.lang.Class) values: {class ImagesMixin}
at Broadcast.<clinit>(MyClass.groovy:17)
... 17 more
2008-12-30 17:58:25.259::WARN: Nested in org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'pluginManager' defined in ServletContext resource [/WEB-INF/applicationContext.xml]: Invocation of init method failed; nested exception is java.lang.ExceptionInInitializerError:
groovy.lang.MissingMethodException: No signature of method: Person.mixin() is applicable for argument types: (java.lang.Class) values: {class ImagesMixin}
at Broadcast.<clinit>(Person.groovy:17)
at java.lang.Class.forName0(Native Method)
at java.lang.Class.forName(Class.java:169)
at Episode.class$(BelongsToMyClass.groovy)
at Episode.<clinit>(BelongsToMyClass.groovy)
at java.security.AccessController.doPrivileged(Native Method)
at RunApp_groovy$_run_closure2_closure7.doCall(RunApp_groovy:67)
at RunApp_groovy$_run_closure2_closure7.doCall(RunApp_groovy)
at Init_groovy$_run_closure6.doCall(Init_groovy:131)
at RunApp_groovy$_run_closure2.doCall(RunApp_groovy:66)
at RunApp_groovy$_run_closure2.doCall(RunApp_groovy)
at RunApp_groovy$_run_closure1.doCall(RunApp_groovy:57)
at RunApp_groovy$_run_closure1.doCall(RunApp_groovy)
at gant.Gant.dispatch(Gant.groovy:271)
at gant.Gant.this$2$dispatch(Gant.groovy)
at gant.Gant.invokeMethod(Gant.groovy)
at gant.Gant.processTargets(Gant.groovy:436)
at gant.Gant.processArgs(Gant.groovy:372)
2008-12-30 17:58:25.271::INFO: Started SelectChannelConnector#0.0.0.0:8080

Since Groovy 1.6 you can either apply a mixin at compile-time to a class using an annotation
#Mixin(ImagesMixin)
class Person {
}
Or you can apply the mixin at runtime like this:
def myMixin = ImagesMixin
Person.mixin myMixin
The latter approach is more dynamic as the class to mixin can be determined at runtime. Further information about Groovy mixins is available here.
In my experience, a lot of meta-programming of domain classes simply doesn't work. I don't exactly know why, but suspect it's due to the fact these classes are already very heavily meta-programmed by the Grails runtime. In general my approach is
Try the meta-programming on a POGO in the Groovy console
If that works, try it on a non-domain class in the Grails console
If that works, try it on a domain class in the Grails console. If it doesn't work, then it must be because it's a domain class (rather than a problem with the syntax). At this point it's advisable to try and find another way of accomplishing your goal. If that's not possible, then use a combination of the Grails mailing list and/or Stackoverflow and/or the Grails source code to try and get the meta-programming working.

I don't think you are using the proper mixin syntax. Try this:
class MyMixin {
static doStuff(Person) {
'stuff was done'
}
}
class Person {}
Person.mixin MyMixin
new Person().doStuff()

I guess what you've seen there is rather a proposal than a feature ;) Groovy does not support mixins out of the box in this way yet (if ever). But there is a 3rd party lib that can be used to emulate such a behavour: Injecto. And mixins can be defined using AST-Macros in the 1.6 version of Groovy (which is not final yet).
You should always check if your're reading the docs from the real groovy project or from the GroovyJSR project (which is rather a place where proposals are collected).
Another way is to use plain-old MOP to inject behaviour into groovy classes by modifying metaClasses.
Cheers

In case this helps anyone, Following on from #virtualeyes comment above, I've found that
Person.doStuff()
fails unless you call the following first:
new Person().doStuff()
Person.doStuff()
after which, the static method on the class does seem to work (for me, using Grails 2.2.4) I guess it's to do with initialising the class, or something, but I tried:
Person.metaClass.initialize()
Person.doStuff()
and that didn't work!

FYI: There is such a thing as "embedded" domains in Grails now, but it has problems. This is where you can logically include one domain as part of another and have its fields all occur physically in the one DB table. For example, if you find that you have the same subset of fields appearing in multiple tables, like street address/city/state/zip, you could define a StreetAddress domain and embed it. One of the current problems is that Grails will still create a street_address table in addition to embedding its fields in the other tables (unless you play tricks). There are submitted patches pending for this, it seems.

Grails domain objects are already heavily meta-programmed.
Instead of the groovy mixin try:
#grails.util.Mixin(ImagesMixin)
class Person {
}

Use Traits!
Traits are the reason they removed support for mixins, because that are just better. They're basically abstract classes that are implemented. Allowing you to use multiple and operate them as partial classes.
trait A {
void printSomething() {
println "foobar"
}
}
class B implements A {
void printAnything() {
printSomething()
}
}
new B().printAnything()
Try it out!

Related

Get a service bean from Grails Holders class

I'm trying to update a plugin to Grails 2.4. Thus I have to replace the deprecated ApplicationContext class by the newer Holders class.
But I'm having some problems getting a service bean from the Holders class.
Running the code below:
import grails.util.Holders;
def myService = Holders.grailsApplication.mainContext.myService
println myService
println myService.getClass()
println myService.serviceMethod()
It prints something like:
grails.plugin.my.MyService#1e5cd54d
Exception thrown
java.lang.NullPointerException
at ConsoleScript2.run(ConsoleScript2:6)
at org.springsource.loaded.ri.ReflectiveInterceptor.jlrMethodInvoke(ReflectiveInterceptor.java:1270)
It throws a NullPointerException when I call both: the "getClass" method and the "serviceMethod" method.
I tryed to see what's happening by debugging from eclipse too and I can observe "myService" variable being set to an object. When I click on it I can see it's String representation just like printed above. But I can't see anything inside the object. This service' class has member variables, but I can't see them on debug. It's like uf they were'nt there.
Can someone explain me what is going wrong?
Thanks
Don't use Holders, and avoid pulling in dependencies. Use dependency injection, it's a Good Thing.
Your src/groovy class is probably called from a controller, service, Quartz job, or some other Grails artifact that supports DI, right? If so, inject this service there, and when you call your src/groovy class, pass it as a method argument, or inject it once in a constructor or a setter.
Even if it's not called from a Grails artifact it's likely easy to get access to the service. E.g. a servlet can access the ServletContext and it's simple to access the ApplicationContext from there.

Call grails plugin closure from Groovy class

I am using the AWS plugin for grails. I am trying to use the sesMail closure from a Groovy class, not a service or controller e.g. in a method:
String msgId = sesMail {
from props.from
replyTo props.replyTo
to prop.to
html props.body
}
I am getting an error saying sesMail is not a method of the Groovy class. My questions are:
How can I call a plugin closure that is ordinarily available in grails service and controllers from a regular Groovy class
Following that I'm curious how these closures are defined and made available in the service and controller. I can't find a definition of sesMail in the plugin.
thanks
Good Question. And I appreciate that you are curious to find the root cause of your problem. Here are your answers:-
You can directly call the SendSesMail bean to send mail instead of calling the sesMail closure. Your groovy class would look like:
class MyGroovyClass{
def sendSesMail
def sendSomeMails(){
String msgId = sendSesMail.send{
from props.from
replyTo props.replyTo
to prop.to
html props.body
}
}
}
As you can see SendSesMail is not a service class it is just a POGO, so it will not be autowired in this groovy class unless you define that in resources.groovy. So:
Something like:
//resources.groovy
beans = {
sendSesMail(grails.plugin.aws.ses.SendSesMail)
}
Also keep a note by using the above method directly you would be bypassing grails.plugin.aws.ses.enabled configuration. So you have to handle it explicitly.
You can very well find the definition of sesMail method which is metaClasses over target classes to take a Closure as an argument here in MetaClassInjector. This class is basically used to inject/add dynamicMethods from the plugin definition. You can find it as
AwsGrailsPlugin (Line: 37) --> AwsPluginSupport (Line: 86) --> MetaClassInjector (Line: 50)
You can also see in MetaClassInjector (around line 45 and 46), the target classes are controller and service classes. Hence you find the closure sesMail available in those 2 artefacts.
I hope it is clear enough to address your curiousness. :)

grails run-app throws NoClassDefFoundError for domain class

I'm trying to run a Grails app inherited from another developer. It has just one domain class, which we'll call foopackage.DomainObject. When I compile it, a .class file turns up in target/classes just as I'd expect. However, when I try grails run-app, I get
org.springframework.beans.factory.BeanCreationException: Error
creating bean with name 'pluginManager' defined in ServletContext
resource [/WEB-INF/applicationContext.xml]: Invocation of init method
failed; nested exception is java.lang.NoClassDefFoundError: Could not
initialize class foopackage.DomainObject
At first I thought this might be because DomainObject was trying to talk to a nonexistent database in a broken static initializer, so I reduced it down to just
package foopackage
class DomainObject {}
And I still get the same error. Maybe there's some underlying configuration/GORM problem, but it's not showing up with --stacktrace. Any hints?
All right, it turns out it was the static initializer after all, and when I thought I'd ruled that out I was editing the wrong file.

Bean definition using Grails Spring DSL needs index/type/name arguments

In resources.groovy, I'm trying to define a bean named 'sql' which is a groovy.sql.Sql constructed using the dataSource defined by the application.
sql(groovy.sql.Sql, 'firstArgument', ref('dataSource') ) {}
Unfortunately this fails as follows --
Error executing script Console: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'sql': Could not resolve matching constructor (hint: specify index/type/name arguments for simple parameters to avoid type ambiguities).
I looked at http://grails.org/doc/1.3.x/guide/14.%20Grails%20and%20Spring but there is no further information about legal syntax for specifying arguments to avoid type ambiguities beyond the example given there which uses "firstArgument".
I have tried passing "dataSource" and "javax.sql.DataSource" instead of "firstArgument", both unquoted and in double quotes but no success.
The example given in the grails documentation is misleading you. They are constructing an instance of a class with two arguments; the first being a string and the second an int. You will want something like:
sql(groovy.sql.Sql, ref('dataSource') ) {}

I cant get Domain.count() static method to work

The Grails documentation defines a "count" static method, defined in the documentation like this:
Description
Counts the number of instances in the database and returns the result
Parameters
None
Example
def noOfBooks = Book.count()
However, whenever I call it, I get this error! I simple added a call to the name of my Domain Class (Company) like this to a working Controller
def companies = Company.count()
and when it exectutes that line, it blows up with the following error
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'pluginManager' defined in ServletContext resource [/WEB-INF/applicationContext.xml]: Invocation of init method failed; nested exception is java.lang.RuntimeException: Unable to locate constructor with Class parameter for class org.codehaus.groovy.grails.commons.DefaultGrailsControllerClass
Other, working, code in the controller (pre-generated with the static scaffolding commands) access Company.get(...) etc, with no error
What am I doing wrong?
This
class HomeController {
def companies = Company.count()
def index = {
render(view:"index")
}
}
Fails because as lael pointed out the count() method is not available when the application is started. The count() method is a Dynamic method which GORM addeds to the domain classes. Spring (the underlying framework) creates an object for every artifact (Controller/Service/Domain Class etc) at start up. GORM would be after this.
Spring is trying to create a HomeController class, the construction of Home Controller will assign the value of company.count to Companies at startup, However GORM has not started yet so the dynamic methods have not been added.
This code is incorrect anyway, if it did work then the companies variable would only have the value of the number of companies at start up of the application. Once you get "used" to groovy and grails I think you will appreciate the speed of development.
Hope this helps.
The short answer is that the count() method and many others are added to the metaClass at runtime after the Hibernate Plugin is loaded. count() isn't a static method that is available at compile time as Java does it, but rather a static method that is added to the Domain's metaClass at runtime (apparently after parsing the controllers).
Why? Because it doesn't make sense to have count(), get() or read(), etc. methods until after Hibernate/GORM is initialized. It would not be hooked up to the datasource and the methods would be invalid.
I can't help but wonder why you would need to put a property like that on a controller. As soon as you save a new Company or delete one - your companies count would be off.
The long answer would be diving into Grails' source, From the GORMNamespaceHandler -> GORMSessionFactoryDefinitionParser -> GORMEnhancingBeanPostProcessor -> HibernatePluginSupport -> HibernatePluginSupport.addBasicPersistenceMethods()

Resources