views:

856

answers:

5

Can someone tell me why this Grails domain class will not compile (at runtime)?

class Person {
    String name

    String getSomething(int i) {
    }
}

I get this error when I run with grails run-app:

2008-12-27 15:26:33.955::WARN:  Failed startup of context org.mortbay.jetty.webapp.WebAppContext@187e184{/asrs2,C:\Steve\asrs2/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.NullPointerException
        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.NullPointerException
        at java.lang.Class.isAssignableFrom(Native Method)
        ... 13 more

If I change the method getSomething to doSomething then it works. Is getSomething(int i) somehow being treated as a bean method?

Follow up: This is a Grails bug which will be fixed in 1.2.

+1  A: 

Before I answer the question let me tell you my environment:
Grails 1.0.4
Java 1.6.0_10-beta
Groovy 1.6-RC-1
on a Windows Vista machine

In grails, dynamic get methods are added at runtime for all the fields in the domain class. For the Person class metioned in the question, a getName() method would be added at runtime which would allow one to use it wtithout defining it. Now the problem, with getSomething(int i) is that you do not have a field called String something in your class. If you try adding a method called getName(int i) it would work without any issues or if you add a field String something then the getSomething() method would work.

I hope this resolves the issue for the time being...I would keep looking and post an updates on the exact working soon.

andHapp
+6  A: 

Well, you've got two problems:

  1. Domain classes in Grails try to make sure that every property has a getter and setter during startup. It does this by looking for all the getters, and making sure an appropriate setter exists. So, if you have a getSomething(), you have to have a setSomething( def something ), even if there is no property "something" in the class. Really, by creating the getSomething() function, you've implied that there is such a property, and you must create a setSomething() as well.

  2. Getters do not take arguments. Yours does. Now I realize you weren't thinking this was a "getter" when you wrote it, but your naming of it makes it one.

Best bet? Don't use "get" or "set" or "is" prefixes unless you really are making a full property that can be gotten and set. I would also avoid "find" in Domain classes, as that has it's own set of generated methods.

Bill James
there ought to be an annotation that you could add to mark a method beginning with 'get' to not be a getter/setter pair.
Chii
Not through annotations, but you can do this through the "transients" static property
Michael Borgwardt
Doesn't sound like Grails is funny Java compatible since getSomething(int) is perfectly valid in Java.
Steve Kuo
First, Grails isn't the language, it's the framework (Groovy is the language). Any framework that uses conventions is going to have SOME things that aren't valid. Now, is Groovy 100% Java compatible, also no, but minimally in-compatible, and no one says Groovy is the same as Java... they say it's better :)
Bill James
+4  A: 

Instead of defining the method as having a return type, try just using def:

class Person {
    String name

    def getSomething(int i) {
        // foo
    }
}

Another solution might be to define something as a transient (providing that you don't actually have a property called 'something'):

class Person {
    String name

    static transients = ['something']

    String getSomething(int i) {
        // foo
    }
}
Rob Hruska
+4  A: 

A couple of notes...

  • If you provide a setter, you don't also have to provide a getter. And vice-versa. What you're really doing is overriding the default accessor methods Groovy is attaching for your. You can override one and not the other if you wish.
  • There is no problem if you make a method on your domain class starting with 'find' because the dynamic finder methods are actually static method additions (and they all start with 'findBy*').
  • The transients solution won't work. Making 'something' a transient value doesn't help the fact that you've got a getter with the wrong parameter signature. Same thing when you dynamically type the return value.
  • This is not problem for Groovy, but for Grails.

I think the solution is to change your method name from getSomething to findSomething or whatever you'd like that doesn't go against convention. The following works fine:

class Person {
    String name

    String findSomething(int i) {
    }
}
Matthew Taylor
+1  A: 

I've concluded that this is a bug with Grails. I've created GRAILS-3760 which has been fixed in Grails 1.1.2.

Steve Kuo