在 Weblogic 12c 中以编程方式包含 JSP 的 RequestDispatcher 问题
Issue with RequestDispatcher including JSP programmatically in Weblogic 12c
我遇到以下情况:
在 Tomcat 7.0.64 上的当前 Web 应用程序 运行 中,我们在自己的 class CharArrayWriterResponse implementing HttpServletResponseWrapper
.
这样做的原因是我们将结果 HTML 包装到 AJAX 响应所需的 JSON 中。
依赖关系:
<dependency>
<groupId>javax</groupId>
<artifactId>javaee-web-api</artifactId>
<version>7.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
代码示例:
// somewhere in servlet doPost()/doGet()
try (PrintWriter out = response.getWriter()) {
out.println(getJspAsJson(request, response));
}
private static String getJspAsJson(HttpServletRequest request, HttpServletResponse response) {
String html = getHtmlByJSP(request, response, "WEB-INF/path/to/existing.jsp");
Gson gson = new GsonBuilder().disableHtmlEscaping().create();
return "{\"results\":" + gson.toJson(html) + "}";
}
public static String getHtmlByJSP(HttpServletRequest request, HttpServletResponse response, String jsp) {
CharArrayWriterResponse customResponse = new CharArrayWriterResponse(response);
request.getRequestDispatcher(jsp).include(request, customResponse);
return customResponse.getOutput();
}
public class CharArrayWriterResponse extends HttpServletResponseWrapper {
private final CharArrayWriter charArray = new CharArrayWriter();
public CharArrayWriterResponse(HttpServletResponse response) {
super(response);
}
@Override
public PrintWriter getWriter() throws IOException {
// this is called ONLY in tomcat
return new PrintWriter(charArray);
}
public String getOutput() {
return charArray.toString();
}
@Override
public ServletOutputStream getOutputStream() throws IOException {
// this is called ONLY in WebLogic
return null; // don't know how to handle it
}
}
提示:我在上面的代码示例中没有考虑异常处理。
我必须将此应用程序迁移到 WebLogic (12.2.1),但此解决方案不再有效。
到目前为止我发现了什么:
在Tomcat调用上面例子的request.getRequestDispatcher(jsp).include()
之后getWriter()
我的CharArrayWriterResponse
class被调用。
在 WebLogic 中,getWriter()
不再被调用,这就是它不再工作的原因。
经过一些调试,我发现在 WebLogic 而不是 getWriter()
中,如果我覆盖它,只会调用 getOutputStream()
。
getWriter()
不会在 Weblogic 上调用一次,因此 Tomcat 和 WebLogic 的底层实现必须有所不同。
问题是 getOutputStream()
我认为不可能在单独的流或其他东西中获得 include()
调用的响应并将其转换为字符串以用于构建最终的 JSON 包含 HTML.
是否有人已经解决了这个问题,并且可以提供一个有效的解决方案以结合 WebLogic 以编程方式包含 JSP?
有人知道实现我的目标的另一种解决方案吗?
感谢您的建议。
解决方案
查看工作示例
提示
我发现 Tomcat 和新的 Weblogic 解决方案之间的差异:
对于后一个,不可能再直接包含 JSPF,因为 Tomcat getWriter()
是。
解决方案是将 JSPF 包装在 JSP 文件中。
我这样做了:
@Override
public ServletOutputStream getOutputStream() throws IOException {
// this is called ONLY in WebLogic
// created a custom outputstream that wraps your charArray
return new CustomOutputStream(this.charArray);
}
// custom outputstream to wrap charArray writer
class CustomOutputStream extends ServletOutputStream {
private WriterOutputStream out;
public CustomOutputStream(CharArrayWriter writer) {
// WriterOutputStream has a constructor without charset but it's deprecated, so change the UTF-8 charset to the one you use, if needed
this.out = new WriterOutputStream(writer, "UTF-8");
}
@Override
public boolean isReady() {
return true;
}
@Override
public void setWriteListener(WriteListener writeListener) {
}
@Override
public void write(int b) throws IOException {
this.out.write(b);
this.out.flush(); // it doesn't work without flushing
}
}
我使用了来自 apache commons-io 的 WriterOutputStream
,所以我必须在我的 pom.xml 中包含:
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.5</version>
</dependency>
我不知道你的 jsp 文件中有什么,但我已经用一个简单的文件进行了测试,我相信它有效。
我的 jsp 文件:
<b>Hello world</b>
<p>testing</p>
<ul>test
<li>item</li>
<li>item2</li>
</ul>
输出(在浏览器中访问 servlet 时):
{"results":"<b>Hello world</b>\n\n<p>testing</p>\n\n<ul>test\n<li>item</li>\n<li>item2</li>\n</ul>"}
这是我更新后的工作示例,如果在实施 HttpServletResponseWrapper
时调用 getOutputStream()
而不是 getWriter()
,如何以编程方式包含 JSP 文件:
public class MyServletOutputStream extends ServletOutputStream {
private final BufferedOutputStream bufferedOut;
public MyServletOutputStream(CharArrayWriter charArray) {
this.bufferedOut = new BufferedOutputStream(new WriterOutputStream(charArray, "UTF-8"), 16384);
}
@Override
public void write(int b) throws IOException {
this.bufferedOut.write(b);
}
/**
* This is needed to get correct full content without anything missing
*/
@Override
public void flush() throws IOException {
if (this.bufferedOut != null) {
this.bufferedOut.flush();
}
super.flush();
}
@Override
public void close() throws IOException {
this.bufferedOut.close();
super.close();
}
@Override
public boolean isReady() {
return true;
}
@Override
public void setWriteListener(WriteListener writeListener) {
}
}
public class CharArrayWriterResponse extends HttpServletResponseWrapper {
private final CharArrayWriter charArray = new CharArrayWriter();
private ServletOutputStream servletOutputStream;
public CharArrayWriterResponse(HttpServletResponse response) {
super(response);
}
@Override
public ServletOutputStream getOutputStream() throws IOException {
if (servletOutputStream == null) {
servletOutputStream = new MyServletOutputStream(this.charArray);
}
return servletOutputStream;
}
public String getOutputAndClose() {
if (this.servletOutputStream != null) {
try {
// flush() is important to get complete content and not last "buffered" part missing
this.servletOutputStream.flush()
return this.charArray.toString();
} finally {
this.servletOutputStream.close()
}
}
throw new IllegalStateException("Empty (null) servletOutputStream not allowed");
}
// not necessary to override getWriter() if getOutputStream() is used by the "application server".
}
// ...somewhere in servlet process chain e.g. doGet()/doPost()
// request/response The original servlet request/response object e.g. from doGet/doPost(HttpServletRequest request, HttpServletResponse response)
CharArrayWriterResponse customResponse = new CharArrayWriterResponse(response);
request.getRequestDispatcher("/WEB-INF/path/to/existing.jsp").include(request, customResponse);
String jspOutput = customResponse.getOutputAndClose();
// do some processing with jspOut e.g. wrap inside JSON
// customResponse.getOutputStream() is already closed by calling getOutputAndClose()
由于我不知道如何在注释中放置多行代码,所以我就把它放在这里。
我可以通过覆盖 MyServletOutputStream
class:
中的 flush()
方法来修复
// inside MyServletOutputStream class
@Override
public void flush() throws IOException {
if (this.bufferedOut != null) {
this.bufferedOut.flush();
}
super.flush();
}
我遇到以下情况:
在 Tomcat 7.0.64 上的当前 Web 应用程序 运行 中,我们在自己的 class CharArrayWriterResponse implementing HttpServletResponseWrapper
.
这样做的原因是我们将结果 HTML 包装到 AJAX 响应所需的 JSON 中。
依赖关系:
<dependency>
<groupId>javax</groupId>
<artifactId>javaee-web-api</artifactId>
<version>7.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
代码示例:
// somewhere in servlet doPost()/doGet()
try (PrintWriter out = response.getWriter()) {
out.println(getJspAsJson(request, response));
}
private static String getJspAsJson(HttpServletRequest request, HttpServletResponse response) {
String html = getHtmlByJSP(request, response, "WEB-INF/path/to/existing.jsp");
Gson gson = new GsonBuilder().disableHtmlEscaping().create();
return "{\"results\":" + gson.toJson(html) + "}";
}
public static String getHtmlByJSP(HttpServletRequest request, HttpServletResponse response, String jsp) {
CharArrayWriterResponse customResponse = new CharArrayWriterResponse(response);
request.getRequestDispatcher(jsp).include(request, customResponse);
return customResponse.getOutput();
}
public class CharArrayWriterResponse extends HttpServletResponseWrapper {
private final CharArrayWriter charArray = new CharArrayWriter();
public CharArrayWriterResponse(HttpServletResponse response) {
super(response);
}
@Override
public PrintWriter getWriter() throws IOException {
// this is called ONLY in tomcat
return new PrintWriter(charArray);
}
public String getOutput() {
return charArray.toString();
}
@Override
public ServletOutputStream getOutputStream() throws IOException {
// this is called ONLY in WebLogic
return null; // don't know how to handle it
}
}
提示:我在上面的代码示例中没有考虑异常处理。
我必须将此应用程序迁移到 WebLogic (12.2.1),但此解决方案不再有效。
到目前为止我发现了什么:
在Tomcat调用上面例子的request.getRequestDispatcher(jsp).include()
之后getWriter()
我的CharArrayWriterResponse
class被调用。
在 WebLogic 中,getWriter()
不再被调用,这就是它不再工作的原因。
经过一些调试,我发现在 WebLogic 而不是 getWriter()
中,如果我覆盖它,只会调用 getOutputStream()
。
getWriter()
不会在 Weblogic 上调用一次,因此 Tomcat 和 WebLogic 的底层实现必须有所不同。
问题是 getOutputStream()
我认为不可能在单独的流或其他东西中获得 include()
调用的响应并将其转换为字符串以用于构建最终的 JSON 包含 HTML.
是否有人已经解决了这个问题,并且可以提供一个有效的解决方案以结合 WebLogic 以编程方式包含 JSP?
有人知道实现我的目标的另一种解决方案吗?
感谢您的建议。
解决方案
查看工作示例
提示
我发现 Tomcat 和新的 Weblogic 解决方案之间的差异:
对于后一个,不可能再直接包含 JSPF,因为 Tomcat getWriter()
是。
解决方案是将 JSPF 包装在 JSP 文件中。
我这样做了:
@Override
public ServletOutputStream getOutputStream() throws IOException {
// this is called ONLY in WebLogic
// created a custom outputstream that wraps your charArray
return new CustomOutputStream(this.charArray);
}
// custom outputstream to wrap charArray writer
class CustomOutputStream extends ServletOutputStream {
private WriterOutputStream out;
public CustomOutputStream(CharArrayWriter writer) {
// WriterOutputStream has a constructor without charset but it's deprecated, so change the UTF-8 charset to the one you use, if needed
this.out = new WriterOutputStream(writer, "UTF-8");
}
@Override
public boolean isReady() {
return true;
}
@Override
public void setWriteListener(WriteListener writeListener) {
}
@Override
public void write(int b) throws IOException {
this.out.write(b);
this.out.flush(); // it doesn't work without flushing
}
}
我使用了来自 apache commons-io 的 WriterOutputStream
,所以我必须在我的 pom.xml 中包含:
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.5</version>
</dependency>
我不知道你的 jsp 文件中有什么,但我已经用一个简单的文件进行了测试,我相信它有效。 我的 jsp 文件:
<b>Hello world</b>
<p>testing</p>
<ul>test
<li>item</li>
<li>item2</li>
</ul>
输出(在浏览器中访问 servlet 时):
{"results":"<b>Hello world</b>\n\n<p>testing</p>\n\n<ul>test\n<li>item</li>\n<li>item2</li>\n</ul>"}
这是我更新后的工作示例,如果在实施 HttpServletResponseWrapper
时调用 getOutputStream()
而不是 getWriter()
,如何以编程方式包含 JSP 文件:
public class MyServletOutputStream extends ServletOutputStream {
private final BufferedOutputStream bufferedOut;
public MyServletOutputStream(CharArrayWriter charArray) {
this.bufferedOut = new BufferedOutputStream(new WriterOutputStream(charArray, "UTF-8"), 16384);
}
@Override
public void write(int b) throws IOException {
this.bufferedOut.write(b);
}
/**
* This is needed to get correct full content without anything missing
*/
@Override
public void flush() throws IOException {
if (this.bufferedOut != null) {
this.bufferedOut.flush();
}
super.flush();
}
@Override
public void close() throws IOException {
this.bufferedOut.close();
super.close();
}
@Override
public boolean isReady() {
return true;
}
@Override
public void setWriteListener(WriteListener writeListener) {
}
}
public class CharArrayWriterResponse extends HttpServletResponseWrapper {
private final CharArrayWriter charArray = new CharArrayWriter();
private ServletOutputStream servletOutputStream;
public CharArrayWriterResponse(HttpServletResponse response) {
super(response);
}
@Override
public ServletOutputStream getOutputStream() throws IOException {
if (servletOutputStream == null) {
servletOutputStream = new MyServletOutputStream(this.charArray);
}
return servletOutputStream;
}
public String getOutputAndClose() {
if (this.servletOutputStream != null) {
try {
// flush() is important to get complete content and not last "buffered" part missing
this.servletOutputStream.flush()
return this.charArray.toString();
} finally {
this.servletOutputStream.close()
}
}
throw new IllegalStateException("Empty (null) servletOutputStream not allowed");
}
// not necessary to override getWriter() if getOutputStream() is used by the "application server".
}
// ...somewhere in servlet process chain e.g. doGet()/doPost()
// request/response The original servlet request/response object e.g. from doGet/doPost(HttpServletRequest request, HttpServletResponse response)
CharArrayWriterResponse customResponse = new CharArrayWriterResponse(response);
request.getRequestDispatcher("/WEB-INF/path/to/existing.jsp").include(request, customResponse);
String jspOutput = customResponse.getOutputAndClose();
// do some processing with jspOut e.g. wrap inside JSON
// customResponse.getOutputStream() is already closed by calling getOutputAndClose()
由于我不知道如何在注释中放置多行代码,所以我就把它放在这里。
我可以通过覆盖 MyServletOutputStream
class:
flush()
方法来修复 // inside MyServletOutputStream class
@Override
public void flush() throws IOException {
if (this.bufferedOut != null) {
this.bufferedOut.flush();
}
super.flush();
}