Grails 应用程序 response.outputStream << 自升级到 Grails 3.3 后失败
Grails application response.outputStream << fails since upgrade to Grails 3.3
我有一个 Grails 应用程序,最近从 2.5 升级到 3.3。大体上一切正常,但今天我们 运行 遇到了一个似乎与其他人共享但我找不到解决方案的问题。
在控制器中,我有一个方法可以将字符串附加到 response.outputStream。
代码现在显示为
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()
基于此处的建议
http://sergiodelamo.es/grails-tips-how-to-output-csv-from-a-grails-3-controller/
此代码在我的测试环境中执行得很好
$ grails -version
| Grails Version: 3.3.5
| Groovy Version: 2.4.15
| JVM Version: 1.8.0_162
但在生产服务器上严重失败
$ 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]
报告的失败以:
开头
org.springframework.web.util.NestedServletException: Handler dispatch failed; nested exception is java.lang.NoClassDefFoundError: javax/servlet/WriteListener
后跟堆栈跟踪,然后是这个
Caused by: java.lang.NoClassDefFoundError: javax/servlet/WriteListener
然后是关于 WriteListener 的更多堆栈跟踪和类似消息
我看到了替换此行的建议
provided "org.springframework.boot:spring-boot-starter-tomcat"
和
compile "org.springframework.boot:spring-boot-starter-tomcat"
但正如这里指出的那样
https://docs.grails.org/latest/guide/deployment.html
这不是一个好主意,事实上,当我尝试时,tomcat 没有启动。
我相信我在某处读到过,我可以通过将 Tomcat7 替换为 Tomcat8 来解决这个问题;但是现在我在服务器上 运行 Ubuntu 14.04 并且存储库中不提供 Tomcat8,因此测试它并不是很简单。
有人对我有什么建议吗?提前致谢。
您可以通过在您的方法中添加@CompileStatic 来解决这个问题,但这并不总是可行的。我们通过添加静态实用方法在我们的应用程序中解决了这个问题:
@CompileStatic
public static sendResponseData(ServletOutputStream outputStream, String s) { // but this could be byte[] s or InputStream s or whatever you need
outputStream << s
}
然后调用它而不是左移操作。
您可能需要添加额外的方法签名,以便它们可以静态编译,但概念是相同的。如果我没记错的话,这里的左移运算符使用了一些默认情况下(在 Tomcat 7 上)不包含但也不需要的注释或其他内容(显然我不记得细节了!)。
请注意,我们还添加了
@CompileStatic
public static flushOutputStream(ServletOutputStream outputStream) {
outputStream.flush()
}
为了方便起见,因为那个也必须静态编译。
我有一个 Grails 应用程序,最近从 2.5 升级到 3.3。大体上一切正常,但今天我们 运行 遇到了一个似乎与其他人共享但我找不到解决方案的问题。
在控制器中,我有一个方法可以将字符串附加到 response.outputStream。
代码现在显示为
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()
基于此处的建议
http://sergiodelamo.es/grails-tips-how-to-output-csv-from-a-grails-3-controller/
此代码在我的测试环境中执行得很好
$ grails -version
| Grails Version: 3.3.5
| Groovy Version: 2.4.15
| JVM Version: 1.8.0_162
但在生产服务器上严重失败
$ 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]
报告的失败以:
开头org.springframework.web.util.NestedServletException: Handler dispatch failed; nested exception is java.lang.NoClassDefFoundError: javax/servlet/WriteListener
后跟堆栈跟踪,然后是这个
Caused by: java.lang.NoClassDefFoundError: javax/servlet/WriteListener
然后是关于 WriteListener 的更多堆栈跟踪和类似消息
我看到了替换此行的建议
provided "org.springframework.boot:spring-boot-starter-tomcat"
和
compile "org.springframework.boot:spring-boot-starter-tomcat"
但正如这里指出的那样
https://docs.grails.org/latest/guide/deployment.html
这不是一个好主意,事实上,当我尝试时,tomcat 没有启动。
我相信我在某处读到过,我可以通过将 Tomcat7 替换为 Tomcat8 来解决这个问题;但是现在我在服务器上 运行 Ubuntu 14.04 并且存储库中不提供 Tomcat8,因此测试它并不是很简单。
有人对我有什么建议吗?提前致谢。
您可以通过在您的方法中添加@CompileStatic 来解决这个问题,但这并不总是可行的。我们通过添加静态实用方法在我们的应用程序中解决了这个问题:
@CompileStatic
public static sendResponseData(ServletOutputStream outputStream, String s) { // but this could be byte[] s or InputStream s or whatever you need
outputStream << s
}
然后调用它而不是左移操作。
您可能需要添加额外的方法签名,以便它们可以静态编译,但概念是相同的。如果我没记错的话,这里的左移运算符使用了一些默认情况下(在 Tomcat 7 上)不包含但也不需要的注释或其他内容(显然我不记得细节了!)。
请注意,我们还添加了
@CompileStatic
public static flushOutputStream(ServletOutputStream outputStream) {
outputStream.flush()
}
为了方便起见,因为那个也必须静态编译。