Rendering lists from Google AppEngine as JSON
I ran into another problem with Google AppEngine. I finally got it to save my entities, only to find it crashing as I wanted to send them back again rendered as JSON.
I had a piece of code looking like this:
def services = { params.max = Math.min(params.max ? params.max.toInteger() : 10, 100) render Offering.list(params) as JSON; }
The code is simple enough, it loads all the Service offerings using GORM and then renders them as JSON using the built in converter. This resulted in the following error:
Uncaught exception from servlet org.codehaus.groovy.runtime.InvokerInvocationException: org.codehaus.groovy.grails.web.converters.exceptions.ConverterException: Error converting Bean with class org.codehaus.groovy.reflection.ClassLoaderForClassArtifacts ... Caused by: org.codehaus.groovy.grails.web.converters.exceptions.ConverterException: Error converting Bean with class org.codehaus.groovy.reflection.ClassLoaderForClassArtifacts at grails.converters.JSON.value(JSON.java:199) at grails.converters.JSON.convertAnother(JSON.java:156) ... at com.google.apphosting.runtime.security.shared.intercept.java.lang.reflect.Method_$3.run(Method_.java:149) at java.security.AccessController.doPrivileged(Native Method) at com.google.apphosting.runtime.security.shared.intercept.java.lang.reflect.Method_.privilegedInvoke(Method_.java:147) at com.google.apphosting.runtime.security.shared.intercept.java.lang.reflect.Method_.invoke(Method_.java:120) ... 26 more Caused by: java.lang.reflect.InvocationTargetException at com.google.apphosting.runtime.security.shared.intercept.java.lang.reflect.Method_$3.run(Method_.java:149) at java.security.AccessController.doPrivileged(Native Method) at com.google.apphosting.runtime.security.shared.intercept.java.lang.reflect.Method_.privilegedInvoke(Method_.java:147) at com.google.apphosting.runtime.security.shared.intercept.java.lang.reflect.Method_.invoke(Method_.java:120) ... Caused by: java.security.AccessControlException: access denied (java.lang.RuntimePermission getClassLoader) at java.security.AccessControlContext.checkPermission(Unknown Source) at java.security.AccessController.checkPermission(Unknown Source) at java.lang.SecurityManager.checkPermission(Unknown Source) at java.lang.ClassLoader.getParent(Unknown Source) ... 59 more
The problem is that the GORM loads the class with some meta data attached to it. I’m not allowed to reflect over this meta data, so the normal converter fails with the above error. I took the privilege to shorten the stack trace somewhat.
The work around is simple and elegant. I use the groovy collection method called select. With this I can construct a list with maps with the fields I want in my JSON and render them using the normal converter.
def services = { params.max = Math.min(params.max ? params.max.toInteger() : 10, 100) def list = Offering.list(params); render list.collect { offering->[id:offering.id, name:offering.name]} as JSON }
The above code works fine on Google AppEngine.
I hope this helps someone else.