2

I'm experiencing a discrepancy between the first compilation of a Grails app and the compilation that happens when a file changes while the app is running.

Background:

  • My app creates some spring beans from Spring LDAP (docs) using conf/spring/resources.groovy.
  • I have an LdapUser.groovy class in src/groovy (I'm using it similarly to a domain class, except it isn't in grails-app/domain as it doesn't map to a database table).
  • In BootStrap.groovy I register a JSON marshaller for LdapUser (using JSON.registerObjectMarshaller).
  • I have a controller with an index method that responds a list of LdapUser objects. This renders correctly in JSON (according to the marshaller).

With that background, here are the pieces of the problem:

  • When the show method, which responds a single LdapUser, gets called, I get an exception that LdapUser cannot be converted to grails.converters.JSON. (fair enough)
  • But, if I save the LdapUser.groovy file, thus invoking a recompile on the file while the app is running, the JSON marshaller suddenly works fine.
  • Before saving the LdapUser.groovy, my controller has a to an LdapUserRepo (a class instantiated via an @EnableLdapRepositories annotation on the controller), but this reference becomes null after I save LdapUser.groovy. I'm not sure how this relates to the problem, as I was also able to reproduce the problem in a controller lacking an injected LdapUserRepo (but with the annotated controllers still in the app).
  • I also at one point was setting an asType method on the LdapUser class, which was called as expected before the save-invoked recompile. After the recompile, however, my asType method was no longer called and the JSON marshaller was taking over. ( I was doing exception-worthy things in the asType that were throwing before recompile and not throwing after... )

My understanding of the problem is therefore:

  • Somehow the asType method of the LdapUser.groovy class is not being automatically generated on first compile when running the app, but is being generated on subsequent compiles.
  • The LdapUser class is tied to the LdapUserRepo in more ways than merely being a type the Repo uses, and the recompile is not reflecting that connection correctly.
  • Methods rendering lists of objects are somehow unaffected by the asType method. This leads me to believe that the JSON marshaller gets called directly on list elements (instead of via asType) when the list asType has been called (whether or not the "as" operation is implicit...).

My question then is:

  • what is the Grails compiler doing differently on run-app vs on compile while app is running that could be causing this behavior?
  • how can I restructure things to ensure it works properly out of the box?
  • If I need to RTFM, what would be the FM section? (My google-fu is sadly quite weak).

Note: this question is vaguely similar, but doesn't have any meaningfulness to the answer: Grails: Defining a JSON custom marshaller as static method in domain

Community
  • 1
  • 1
Ryan Heathcote
  • 836
  • 8
  • 19
  • Here's something interesting that I didn't know (maybe I should have...): "However, Grails over-writes the implementation of asType() for all objects at runtime so that it can support idioms like render as JSON." -- from [this thread](http://stackoverflow.com/questions/14243525/what-are-the-alternatives-to-overriding-astype-when-writing-conversion-code) – Ryan Heathcote Jul 29 '15 at 02:45
  • Perhaps my question should be rewritten as "Why, when, and how does Grails overwrite the asType? And how can I stop that happening / work around it?" Which is a question for which I can probably find some love on the google. – Ryan Heathcote Jul 29 '15 at 02:52
  • It has been a long time since I hit this issue, but I was reading this [thread](http://grails.1312388.n4.nabble.com/Marshallers-are-blowing-up-in-2-3-5-anyone-else-td4653954.html), and it basically says that the restBuilder constructor blows away any of the non-default JSON marshallers. This could explain the issues -- on recompile, it recreates the restBuilder, which overwrites the custom marshallers. I can't investigate at the moment though. – Ryan Heathcote Jan 05 '16 at 17:27
  • Relevant quote: The problem is in the RestBuilder constructor, in particular, the line: `if(ConvertersConfigurationHolder.getConverterConfiguration(JSON) instanceof DefaultConverterConfiguration) { // init manually new ConvertersConfigurationInitializer().initialize(new DefaultGrailsApplication()) }` – Ryan Heathcote Jan 05 '16 at 17:28

0 Answers0