Skip to content

Rendering a template on a background thread causes NPE #10789

@donald-jackson

Description

@donald-jackson

In previous versions of Grails it was possible to generate templates on a background thread (things like emails, etc) for async generation. I'm not exactly sure which version this broke in.

A simple method like this:

    String renderTemplate(String key, String content, Map model) {
        Template template = groovyPagesTemplateEngine.createTemplate(content, key)
        Writable renderedTemplate = template.make(model);
        StringWriter stringWriter = new StringWriter();
        renderedTemplate.writeTo(stringWriter);
        return stringWriter.toString();
    }

Example call:

String template = renderTemplate("myTemplateId", '<h3>Hello, ${user} </h3>', [user: "Grails User"]);

Doing this on a background thread, causes the exception below. Looks like it relates to the WebOutputContext - however as you can see in my template generation this should have no dependency on a web context.

Steps to Reproduce

  1. Clone https://github.com/donald-jackson/grails-template-error3211.git
  2. Run-app
  3. Visit http://localhost:8080/example/background
  4. Visit http://localhost:8080/example/foreground - same code but on foreground thread, renders out the template.

Expected Behaviour

No exceptions, a template is rendered in the background

Actual Behaviour

java.lang.NullPointerException: null
        at org.grails.web.taglib.encoder.WebOutputContextLookup$WebOutputContext.getBinding(WebOutputContextLookup.java:89)
        at org.grails.gsp.GroovyPageWritable.doWriteTo(GroovyPageWritable.java:112)
        at org.grails.gsp.GroovyPageWritable.writeTo(GroovyPageWritable.java:82)
        at groovy.lang.Writable$writeTo$0.call(Unknown Source)
        at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:48)
        at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:113)
        at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:125)
        at grails.template.demo.TemplateService.$tt__renderTemplate(TemplateService.groovy:20)
        at grails.template.demo.TemplateService$_renderTemplate_closure1.doCall(TemplateService.groovy)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:498)
        at org.springsource.loaded.ri.ReflectiveInterceptor.jlrMethodInvoke(ReflectiveInterceptor.java:1426)
        at org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:93)
        at groovy.lang.MetaMethod.doMethodInvoke(MetaMethod.java:325)
        at org.codehaus.groovy.runtime.metaclass.ClosureMetaClass.invokeMethod(ClosureMetaClass.java:294)
        at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:1022)
        at groovy.lang.Closure.call(Closure.java:414)
        at groovy.lang.Closure.call(Closure.java:430)
        at grails.transaction.GrailsTransactionTemplate$2.doInTransaction(GrailsTransactionTemplate.groovy:96)
        at org.springframework.transaction.support.TransactionTemplate.execute(TransactionTemplate.java:133)
        at grails.transaction.GrailsTransactionTemplate.execute(GrailsTransactionTemplate.groovy:93)
        at grails.template.demo.TemplateService.renderTemplate(TemplateService.groovy)
        at grails.template.demo.TemplateService$renderTemplate$0.callCurrent(Unknown Source)
        at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCallCurrent(CallSiteArray.java:52)
        at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callCurrent(AbstractCallSite.java:154)
        at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callCurrent(AbstractCallSite.java:182)
        at grails.template.demo.TemplateService.$tt__backgroundTask(TemplateService.groovy:26)
        at grails.template.demo.TemplateService$_backgroundTask_closure2.doCall(TemplateService.groovy)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:498)
        at org.springsource.loaded.ri.ReflectiveInterceptor.jlrMethodInvoke(ReflectiveInterceptor.java:1426)
        at org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:93)
        at groovy.lang.MetaMethod.doMethodInvoke(MetaMethod.java:325)
        at org.codehaus.groovy.runtime.metaclass.ClosureMetaClass.invokeMethod(ClosureMetaClass.java:294)
        at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:1022)
        at groovy.lang.Closure.call(Closure.java:414)
        at groovy.lang.Closure.call(Closure.java:430)
        at grails.transaction.GrailsTransactionTemplate$2.doInTransaction(GrailsTransactionTemplate.groovy:96)
        at org.springframework.transaction.support.TransactionTemplate.execute(TransactionTemplate.java:133)
        at grails.transaction.GrailsTransactionTemplate.execute(GrailsTransactionTemplate.groovy:93)
        at grails.template.demo.TemplateService.backgroundTask(TemplateService.groovy)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:498)
        at org.springsource.loaded.ri.ReflectiveInterceptor.jlrMethodInvoke(ReflectiveInterceptor.java:1426)
        at org.springframework.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:216)
        at org.springframework.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:201)
        at reactor.spring.context.config.ConsumerBeanAutoConfiguration$Invoker.apply(ConsumerBeanAutoConfiguration.java:333)
        at reactor.spring.context.config.ConsumerBeanAutoConfiguration$ServiceConsumer.accept(ConsumerBeanAutoConfiguration.java:297)
        at reactor.spring.context.config.ConsumerBeanAutoConfiguration$ServiceConsumer.accept(ConsumerBeanAutoConfiguration.java:284)
        at reactor.bus.EventBus$3.accept(EventBus.java:317)
        at reactor.bus.EventBus$3.accept(EventBus.java:310)
        at reactor.bus.routing.ConsumerFilteringRouter.route(ConsumerFilteringRouter.java:72)
        at reactor.bus.EventBus.accept(EventBus.java:591)
        at reactor.bus.EventBus.accept(EventBus.java:63)
        at reactor.core.dispatch.AbstractLifecycleDispatcher.route(AbstractLifecycleDispatcher.java:160)
        at reactor.core.dispatch.MultiThreadDispatcher$MultiThreadTask.run(MultiThreadDispatcher.java:74)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
        at java.lang.Thread.run(Thread.java:745)
Caused by: reactor.core.support.Exceptions$ValueCause: Exception while signaling value: reactor.bus.Event.class : Event{id=null, headers=null, replyTo=null, key=background.task, data={}}
        at reactor.core.support.Exceptions.addValueAsLastCause(Exceptions.java:127)
        at reactor.bus.routing.ConsumerFilteringRouter.route(ConsumerFilteringRouter.java:77)
        ... 7 common frames omitted

Environment Information

  • Operating System: Mac OS X 10.12
  • Grails Version: 3.2.11
  • JDK Version: 1.8.0_111

Example Application

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions