Expected Behavior
rendering should work within and outside the context of web requests (as it used to).
Actual Behaviour
rendering now only works within the context of web requests.
outside of web requests (e.g. async tasks, quartz jobs, etc.), a NullPointerException is thrown:
java.lang.NullPointerException: Cannot invoke "org.grails.web.servlet.mvc.GrailsWebRequest.getOut()" because the return value of "org.grails.web.taglib.encoder.WebOutputContextLookup$WebOutputContext.lookupWebRequest()" is null
i guess this change is relevant: 364bd4e
RenderEnvironment.init() is now only applied for Environment.TEST, which was not the case before.
trying to reproduce this, i saw that the code in question is using GrailsWebMockUtil which in turn uses spring-test classes (e.g. MockHttpServletRequest).
Those now seem not to be present on the runtime classpath anymore but on the test classpath only (which by itself is an improvement).
However this results in missing RequestAttributes/GrailsWebRequest with the error above outside of web requests.
perhaps worth a thought would be to use PageRenderer which takes care of (re-)setting the RequestAttributes/GrailsWebRequest instead of using GroovyPagesTemplateEngine directly?
Steps To Reproduce
java.lang.NullPointerException: Cannot invoke "org.grails.web.servlet.mvc.GrailsWebRequest.getOut()" because the return value of "org.grails.web.taglib.encoder.WebOutputContextLookup$WebOutputContext.lookupWebRequest()" is null
at java.base/java.util.concurrent.FutureTask.report(FutureTask.java:122)
at java.base/java.util.concurrent.FutureTask.get(FutureTask.java:191)
at java_util_concurrent_Future$get.call(Unknown Source)
at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:45)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:125)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:130)
at com.example.BootStrap$_closure1.doCall(BootStrap.groovy:12)
at com.example.BootStrap$_closure1.doCall(BootStrap.groovy)
at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103)
at java.base/java.lang.reflect.Method.invoke(Method.java:580)
at org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:343)
at groovy.lang.MetaMethod.doMethodInvoke(MetaMethod.java:328)
at org.codehaus.groovy.runtime.metaclass.ClosureMetaClass.invokeMethod(ClosureMetaClass.java:280)
at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:1009)
at groovy.lang.Closure.call(Closure.java:433)
at groovy.lang.Closure.call(Closure.java:412)
at grails.util.Environment.evaluateEnvironmentSpecificBlock(Environment.groovy:572)
at grails.util.Environment.executeForEnvironment(Environment.groovy:565)
at grails.util.Environment.executeForCurrentEnvironment(Environment.groovy:541)
at org.grails.web.servlet.boostrap.DefaultGrailsBootstrapClass.callInit(DefaultGrailsBootstrapClass.java:71)
at org.grails.web.servlet.context.GrailsConfigUtils.executeGrailsBootstraps(GrailsConfigUtils.java:88)
at org.grails.plugins.web.servlet.context.BootStrapClassRunner.onStartup(BootStrapClassRunner.groovy:59)
at grails.boot.config.GrailsApplicationPostProcessor.onApplicationEvent(GrailsApplicationPostProcessor.groovy:317)
at grails.boot.config.GrailsApplicationPostProcessor.onApplicationEvent(GrailsApplicationPostProcessor.groovy)
at org.springframework.context.event.SimpleApplicationEventMulticaster.doInvokeListener(SimpleApplicationEventMulticaster.java:185)
at org.springframework.context.event.SimpleApplicationEventMulticaster.invokeListener(SimpleApplicationEventMulticaster.java:178)
at org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:156)
at org.springframework.context.support.AbstractApplicationContext.publishEvent(AbstractApplicationContext.java:455)
at org.springframework.context.support.AbstractApplicationContext.publishEvent(AbstractApplicationContext.java:388)
at org.springframework.context.support.AbstractApplicationContext.finishRefresh(AbstractApplicationContext.java:1013)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:631)
at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:146)
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:752)
at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:439)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:318)
at grails.boot.GrailsApp.run(GrailsApp.groovy:111)
at grails.boot.GrailsApp.run(GrailsApp.groovy:428)
at grails.boot.GrailsApp.run(GrailsApp.groovy:417)
at com.example.Application.main(Application.groovy:11)
at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103)
at java.base/java.lang.reflect.Method.invoke(Method.java:580)
at org.springframework.boot.devtools.restart.RestartLauncher.run(RestartLauncher.java:50)
Caused by: java.lang.NullPointerException: Cannot invoke "org.grails.web.servlet.mvc.GrailsWebRequest.getOut()" because the return value of "org.grails.web.taglib.encoder.WebOutputContextLookup$WebOutputContext.lookupWebRequest()" is null
at org.grails.web.taglib.encoder.WebOutputContextLookup$WebOutputContext.getCurrentWriter(WebOutputContextLookup.java:87)
at org.grails.taglib.encoder.OutputEncodingStack.lookupCurrentWriter(OutputEncodingStack.java:122)
at org.grails.taglib.encoder.OutputEncodingStack.createNew(OutputEncodingStack.java:94)
at org.grails.taglib.encoder.OutputEncodingStack.currentStack(OutputEncodingStack.java:86)
at org.grails.taglib.encoder.OutputEncodingStack.currentStack(OutputEncodingStack.java:73)
at org.grails.taglib.encoder.OutputEncodingStack.currentStack(OutputEncodingStack.java:63)
at org.grails.taglib.encoder.OutputEncodingStack.currentStack(OutputEncodingStack.java:57)
at org.grails.taglib.encoder.OutputEncodingStack.currentStack(OutputEncodingStack.java:49)
at grails.artefact.TagLibrary$Trait$Helper.getOut(TagLibrary.groovy:114)
at org.grails.plugins.web.taglib.GrailsLayoutTagLib.getOut(GrailsLayoutTagLib.groovy)
at org.grails.plugins.web.taglib.GrailsLayoutTagLib$_closure1.doCall(GrailsLayoutTagLib.groovy:130)
at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103)
at java.base/java.lang.reflect.Method.invoke(Method.java:580)
at org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:343)
at groovy.lang.MetaMethod.doMethodInvoke(MetaMethod.java:328)
at org.codehaus.groovy.runtime.metaclass.ClosureMetaClass.invokeMethod(ClosureMetaClass.java:280)
at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:1009)
at groovy.lang.Closure.call(Closure.java:433)
at org.grails.gsp.GroovyPage.invokeTagLibClosure(GroovyPage.java:469)
at org.grails.gsp.GroovyPage.invokeTag(GroovyPage.java:387)
at org.codehaus.groovy.vmplugin.v8.IndyInterface.fromCache(IndyInterface.java:321)
at sample_demo_grails_app_views__example_gsp.run(sample_demo_grails_app_views__example_gsp:16)
at org.grails.gsp.GroovyPageWritable.doWriteTo(GroovyPageWritable.java:151)
at org.grails.gsp.GroovyPageWritable.writeTo(GroovyPageWritable.java:89)
at grails.plugins.rendering.document.XhtmlDocumentService$_generateXhtml_closure1.doCall(XhtmlDocumentService.groovy:70)
at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103)
at java.base/java.lang.reflect.Method.invoke(Method.java:580)
at org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:343)
at groovy.lang.MetaMethod.doMethodInvoke(MetaMethod.java:328)
at org.codehaus.groovy.runtime.metaclass.ClosureMetaClass.invokeMethod(ClosureMetaClass.java:280)
at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:1009)
at org.codehaus.groovy.vmplugin.v8.IndyInterface.fromCache(IndyInterface.java:321)
at grails.plugins.rendering.document.RenderEnvironment.with(RenderEnvironment.groovy:76)
at org.codehaus.groovy.vmplugin.v8.IndyInterface.fromCache(IndyInterface.java:321)
at grails.plugins.rendering.document.RenderEnvironment.with(RenderEnvironment.groovy:66)
at grails.plugins.rendering.document.XhtmlDocumentService.generateXhtml(XhtmlDocumentService.groovy:69)
at grails.plugins.rendering.document.XhtmlDocumentService.createDocument(XhtmlDocumentService.groovy:39)
at grails.plugins.rendering.RenderingService.render(RenderingService.groovy:39)
at grails.plugins.rendering.RenderingService.render(RenderingService.groovy:38)
at grails.plugins.rendering.RenderingService$render.call(Unknown Source)
at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:45)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:125)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:139)
at com.example.BootStrap$_closure1$_closure3.doCall(BootStrap.groovy:13)
at com.example.BootStrap$_closure1$_closure3.doCall(BootStrap.groovy)
at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103)
at java.base/java.lang.reflect.Method.invoke(Method.java:580)
at org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:343)
at groovy.lang.MetaMethod.doMethodInvoke(MetaMethod.java:328)
at org.codehaus.groovy.runtime.metaclass.ClosureMetaClass.invokeMethod(ClosureMetaClass.java:280)
at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:1009)
at groovy.lang.Closure.call(Closure.java:433)
at groovy.lang.Closure.call(Closure.java:412)
at groovy.lang.Closure.run(Closure.java:505)
at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:572)
at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:317)
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1144)
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:642)
at java.base/java.lang.Thread.run(Thread.java:1583)
Environment Information
- grails-7.2.3
- ubuntu-24.04
- jdk-25
Example Application
https://github.com/zyro23/grails-rendering-60
Version
7.0.1
Expected Behavior
rendering should work within and outside the context of web requests (as it used to).
Actual Behaviour
rendering now only works within the context of web requests.
outside of web requests (e.g. async tasks, quartz jobs, etc.), a
NullPointerExceptionis thrown:i guess this change is relevant: 364bd4e
RenderEnvironment.init()is now only applied forEnvironment.TEST, which was not the case before.trying to reproduce this, i saw that the code in question is using
GrailsWebMockUtilwhich in turn uses spring-test classes (e.g.MockHttpServletRequest).Those now seem not to be present on the runtime classpath anymore but on the test classpath only (which by itself is an improvement).
However this results in missing
RequestAttributes/GrailsWebRequestwith the error above outside of web requests.perhaps worth a thought would be to use
PageRendererwhich takes care of (re-)setting theRequestAttributes/GrailsWebRequestinstead of usingGroovyPagesTemplateEnginedirectly?Steps To Reproduce
Environment Information
Example Application
https://github.com/zyro23/grails-rendering-60
Version
7.0.1