4

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.

Steve Kuo
  • 61,876
  • 75
  • 195
  • 257

5 Answers5

7

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.

billjamesdev
  • 14,554
  • 6
  • 53
  • 76
  • 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 Jan 03 '09 at 03:31
  • Not through annotations, but you can do this through the "transients" static property – Michael Borgwardt Apr 07 '09 at 10:44
  • Doesn't sound like Grails is funny Java compatible since getSomething(int) is perfectly valid in Java. – Steve Kuo May 24 '09 at 22:26
  • 1
    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 :) – billjamesdev May 25 '09 at 01:50
5

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
  • 3,911
  • 4
  • 29
  • 33
4

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
  • 118,520
  • 32
  • 167
  • 192
1

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
  • 61,876
  • 75
  • 195
  • 257
1

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
  • 3,149
  • 2
  • 21
  • 20