grails / grails-core

The Grails Web Application Framework
http://grails.org
Apache License 2.0
2.77k stars 952 forks source link

Jackson Errors in /env actuator if Closures defined in application.groovy #10279

Open robertoschwald opened 7 years ago

robertoschwald commented 7 years ago

When you define a closure in application.groovy, Jackson fails in the /env endpoint.

Steps to reproduce

Create a standard Grails 3.1.13 project, which only has the following things changed:

Caused by: com.fasterxml.jackson.databind.JsonMappingException: 
No serializer found for class groovy.lang.GroovyClassLoader$1 and no properties discovered to
 create BeanSerializer (to avoid exception, disable SerializationFeature.FAIL_ON_EMPTY_BEANS) ) 
(through reference chain: java.util.LinkedHashMap["applicationConfig: 
[classpath:/application.groovy]"]->java.util.LinkedHashMap["grails.gorm.default.mapping"]-
>_run_closure1["delegate"]->script14775830011902070609279["binding"]-
>groovy.util.ConfigBinding["callable"]->groovy.util._parse_closure6["delegate"]-
>groovy.util.ConfigSlurper["classLoader"]->groovy.lang.GroovyClassLoader["resourceLoader"])
    at com.fasterxml.jackson.databind.ser.impl.UnknownSerializer.failForEmpty(UnknownSerializer.java:69)
    at com.fasterxml.jackson.databind.ser.impl.UnknownSerializer.serialize(UnknownSerializer.java:32)
    at com.fasterxml.jackson.databind.ser.BeanPropertyWriter.serializeAsField(BeanPropertyWriter.java:693)
    at com.fasterxml.jackson.databind.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:675)
    at com.fasterxml.jackson.databind.ser.BeanSerializer.serialize(BeanSerializer.java:157)
    at com.fasterxml.jackson.databind.ser.BeanPropertyWriter.serializeAsField(BeanPropertyWriter.java:693)
    at com.fasterxml.jackson.databind.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:675)
    at com.fasterxml.jackson.databind.ser.BeanSerializer.serialize(BeanSerializer.java:157)
    at com.fasterxml.jackson.databind.ser.BeanPropertyWriter.serializeAsField(BeanPropertyWriter.java:693)
    at com.fasterxml.jackson.databind.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:675)
    at com.fasterxml.jackson.databind.ser.BeanSerializer.serialize(BeanSerializer.java:157)
    at com.fasterxml.jackson.databind.ser.BeanPropertyWriter.serializeAsField(BeanPropertyWriter.java:693)
    at com.fasterxml.jackson.databind.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:675)
    at com.fasterxml.jackson.databind.ser.BeanSerializer.serialize(BeanSerializer.java:157)
    at com.fasterxml.jackson.databind.ser.BeanPropertyWriter.serializeAsField(BeanPropertyWriter.java:693)
    at com.fasterxml.jackson.databind.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:675)
    at com.fasterxml.jackson.databind.ser.BeanSerializer.serialize(BeanSerializer.java:157)
    at com.fasterxml.jackson.databind.ser.BeanPropertyWriter.serializeAsField(BeanPropertyWriter.java:693)
    at com.fasterxml.jackson.databind.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:675)
    at com.fasterxml.jackson.databind.ser.BeanSerializer.serialize(BeanSerializer.java:157)
    at com.fasterxml.jackson.databind.ser.std.MapSerializer.serializeFields(MapSerializer.java:561)
    at com.fasterxml.jackson.databind.ser.std.MapSerializer.serialize(MapSerializer.java:469)
    at com.fasterxml.jackson.databind.ser.std.MapSerializer.serialize(MapSerializer.java:29)
    at com.fasterxml.jackson.databind.ser.std.MapSerializer.serializeFields(MapSerializer.java:561)
    at com.fasterxml.jackson.databind.ser.std.MapSerializer.serialize(MapSerializer.java:469)
    at com.fasterxml.jackson.databind.ser.std.MapSerializer.serialize(MapSerializer.java:29)
    at com.fasterxml.jackson.databind.ser.DefaultSerializerProvider.serializeValue(DefaultSerializerProvider.java:130)
    at com.fasterxml.jackson.databind.ObjectWriter$Prefetch.serialize(ObjectWriter.java:1387)
    at com.fasterxml.jackson.databind.ObjectWriter.writeValue(ObjectWriter.java:889)
    at org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.writeInternal(AbstractJackson2HttpMessageConverter.java:269)
    ... 19 common frames omitted

Expected Behaviour

https://github.com/robertoschwald/grails3-actuator-jackson-error

Just run it and access /env

graemerocher commented 7 years ago

Not sure if there is anything we can do about this really. The endpoint is part of Boot and the issue may need to be reported there.

robertoschwald commented 7 years ago

Is it possible to either workaround that in Grails somehow, or provide the required serializer (or a NOOP error serializer) for this issue?

graemerocher commented 7 years ago

Will see what we can do

wilkinsona commented 7 years ago

Not sure if there is anything we can do about this really. The endpoint is part of Boot and the issue may need to be reported there.

On the other hand, the logic that's "polluting" the Environment with a Groovy closure is part of Grails so it could be argued that Grails should ensure that things are sanitised such that Jackson can serialise them, or that it has customised the Actuator's ObjectMapper such that it can serialise the closure. If Boot is lacking the hook points to let you do that then we can see if we can add them.

graemerocher commented 7 years ago

@wilkinsona Yes that could be argued. However, Grails for years has allowed richer configuration with closures to for example configure the default constraints and mapping in GORM http://gorm.grails.org/latest/hibernate/manual/index.html#_the_default_mapping_constraints

This issue would also impact use of GORM standalone outside of Grails if you were to use it in Spring Boot as is now possible http://gorm.grails.org/latest/hibernate/manual/index.html#springBoot

However, yes we can of course fix it only for Grails if the hook is available to do this. Configuration of GORM when using it standalone with Spring Boot would continue to be an issue.

graemerocher commented 7 years ago

@wilkinsona currently we have dependency on Grails to Actuator itself. Is there a way to fix this problem without introducing a dependency on Actuator?

wilkinsona commented 7 years ago

Is there a way to fix this problem without introducing a dependency on Actuator?

I don't know, as I don't know what's required to fix the problem. I'm guessing a Jackson module might work but a bit of Googling hasn't turned up anything. If we can figure out how to fix the problem then hopefully it'll become obvious where that fix should go.