I have a Grails application that I have recently upgraded to 3.3 from 2.5. Generally things are working but today we ran across a problem that seems to be shared by others but I cannot find a solution.
In a controller I have a method that appends a string to the response.outputStream.
The code now appears as
response.status = OK.value()
response.contentType = 'text/csv;charset=UTF-8'
response.setHeader "Content-disposition", "attachment; filename=rcCandidate.csv"
response.outputStream << converted
response.outputStream.flush()
response.outputStream.close()
based on a suggestion found here
http://sergiodelamo.es/grails-tips-how-to-output-csv-from-a-grails-3-controller/
This code executes just fine on my test environment
$ grails -version
| Grails Version: 3.3.5
| Groovy Version: 2.4.15
| JVM Version: 1.8.0_162
but fails badly on the production server
$ java -version
java version "1.8.0_161"
Java(TM) SE Runtime Environment (build 1.8.0_161-b12)
Java HotSpot(TM) 64-Bit Server VM (build 25.161-b12, mixed mode)
$ apt list | grep tomcat
tomcat7/trusty-security,trusty-updates,now 7.0.52-1ubuntu0.13 all [installed]
The failures are reported starting with:
org.springframework.web.util.NestedServletException: Handler dispatch failed; nested exception is java.lang.NoClassDefFoundError: javax/servlet/WriteListener
followed by a stack trace, then this
Caused by: java.lang.NoClassDefFoundError: javax/servlet/WriteListener
then more stack trace and similar messages about WriteListener
I have seen suggestions to replace this line
provided "org.springframework.boot:spring-boot-starter-tomcat"
with
compile "org.springframework.boot:spring-boot-starter-tomcat"
But as pointed out here
https://docs.grails.org/latest/guide/deployment.html
that is not a good idea, and indeed when I tried it, tomcat did not start up.
I believe I've read somewhere that I might be able to cure this problem by replacing Tomcat7 with Tomcat8; however right now I'm running Ubuntu 14.04 on the server and Tomcat8 is not on offer in the repositories, so it's not quite straightforward to test that.
Does anyone have any suggestions for me? Thanks in advance.
You can fix this by adding @CompileStatic to your method, but that is not always feasible. We have fixed this problem in our applications by adding a static utility method:
@CompileStatic
public static sendResponseData(ServletOutputStream outputStream, String s) { // but this could be byte[] s or InputStream s or whatever you need
outputStream << s
}
and then calling that instead of the left shift operation.
You may need to add additional method signatures so that they can be statically compiled but the concept is the same. If I recall correctly, the left shift operator here uses some annotation or something (clearly I don't remember details!) that is not included by default (on Tomcat 7) but is also not needed.
Note that we also added
@CompileStatic
public static flushOutputStream(ServletOutputStream outputStream) {
outputStream.flush()
}
for convenience since that one has to be statically compiled as well.