且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

使用Tomcat / Websphere将JSP编译为字符串或内存bytearray

更新时间:2023-09-15 18:34:58

这是一个彻头彻尾的恼人问题,我必须处理几次,一次,我从来没有找到一个令人满意的解决方案。



基本问题是servlet API在这里没有帮助,所以你必须欺骗它。我的解决方案是编写HttpServletResponseWrapper的子类,它覆盖getWriter()和getOutput()方法并将数据捕获到缓冲区中。然后,将您的请求转发()到您要捕获的JSP的URI,用您的包装器响应替换原始响应。然后,您从缓冲区中提取数据,对其进行操作,并将最终结果写回原始响应。



这是我执行此操作的代码:

 公共类CapturingResponseWrapper扩展了HttpServletResponseWrapper {

private final OutputStream buffer;

私人PrintWriter作家;
private ServletOutputStream outputStream;

public CapturingResponseWrapper(HttpServletResponse response,OutputStream buffer){
super(response);
this.buffer = buffer;
}

@Override
public ServletOutputStream getOutputStream(){
if(outputStream == null){
outputStream = new DelegatingServletOutputStream(buffer);
}
返回outputStream;
}

@Override
public PrintWriter getWriter(){
if(writer == null){
writer = new PrintWriter(buffer);
}
返回作家;
}

@Override
public void flushBuffer()抛出IOException {
if(writer!= null){
writer.flush();
}
if(outputStream!= null){
outputStream.flush();
}
}

}

代码使用它可以是这样的:

  HttpServletRequest originalRequest = ... 
HttpServletResponse originalResponse = ...

ByteArrayOutputStream bufferStream = new ByteArrayOutputStream();
CapturingResponseWrapper responseWrapper = new CapturingResponseWrapper(originalResponse,bufferStream);

originalRequest.getRequestDispatcher(/ my.jsp)。forward(originalRequest,responseWrapper);

responseWrapper.flushBuffer();
byte [] buffer = bufferStream.toByteArray();
//现在使用数据

这非常难看,但这是我***的解决方案我找到了。如果您想知道,包装器响应必须包含原始响应,因为servlet规范说您在转发时无法替换完全不同的请求或响应对象,您必须使用它们的原件或包装版本。 / p>

I am doing conversion to image and PDF output. I need an input HTML document that is generated by our application JSPs. Essentially, I need to render the final output product of a JSP based application to a String or memory and then use that string for other processing.

What are some ways that I can just invoke the JSP renderer to get the final HTML content that is normally output to the user? Ideally, I am looking for something that will work for multiple application servers like websphere. But something that is Tomcat specific will also work.

There are a couple of other different approaches, but I think rendering the JSP (which may include sub JSPs) is the best approach.

Optional Paths that I would rather stay away from.

  1. I could perform a network request to the page using the Socket APIs and then read the final output that is rendered from that particular page. This is probably the next best option, but we work on multiple servers and JVMs, targeting the page I need would be complicated.

  2. Use a filter to get that final page output. This Ok but I have always had problems with filters and illegalstateexceptions. It never seems to work 100% the way I need to.

It seems like this should be simple. The JSP compiler is essentially just a library for parsing an input JSP document and subdocuments and then output some HTML content. I would like to invoke that process through Java code. On the server and possibly as a standalone console application.

This is a downright irritating problem, one I've had to handle a few times and one I've never found a satisfactory solution to.

The basic problem is that the servlet API is of no help here, so you have to trick it. My solution is to write a subclass of HttpServletResponseWrapper which override the getWriter() and getOutput() methods and captures the data into a buffer. You then forward() your request to the URI of the JSP you want to capture, substituting your wrapper response for the original response. You then extract the data from the buffer, manipulate it, and write the end result back to the original response.

Here's my code that does this:

public class CapturingResponseWrapper extends HttpServletResponseWrapper {

    private final OutputStream buffer;

    private PrintWriter writer;
    private ServletOutputStream outputStream;

    public CapturingResponseWrapper(HttpServletResponse response, OutputStream buffer) {
        super(response);
        this.buffer = buffer;
    }

    @Override
    public ServletOutputStream getOutputStream() {
        if (outputStream == null) {
            outputStream = new DelegatingServletOutputStream(buffer);
        }
        return outputStream;
    }

    @Override
    public PrintWriter getWriter() {
        if (writer == null) {
            writer = new PrintWriter(buffer);
        }
        return writer;
    }

    @Override
    public void flushBuffer() throws IOException {
        if (writer != null) {
            writer.flush();
        }
        if (outputStream != null) {
            outputStream.flush();
        }
    }

}

The code to use it can be something like this:

HttpServletRequest originalRequest = ...
HttpServletResponse originalResponse = ...

ByteArrayOutputStream bufferStream = new ByteArrayOutputStream();
CapturingResponseWrapper responseWrapper = new CapturingResponseWrapper(originalResponse, bufferStream);

originalRequest.getRequestDispatcher("/my.jsp").forward(originalRequest, responseWrapper);

responseWrapper.flushBuffer();
byte[] buffer = bufferStream.toByteArray();
// now use the data

It's very ugly, but it's the best solution I've found. In case you're wondering, the wrapper response has to contain the original response because the servlet spec says that you cannot substitute a completely different request or response object when you forward, you have to use the originals, or wrapped versions of them.