Issue with RequestDispatcher including JSP programmatically in Weblogic 12c

。_饼干妹妹 提交于 2020-02-01 22:50:29

问题


I'm struggling with the following situation:

In our current web application running on Tomcat 7.0.64, we manage to include a JSP page via Java with the help of an own class CharArrayWriterResponse implementing HttpServletResponseWrapper.

The reason for doing so is that we wrap the resulting HTML into JSON needed for an AJAX Response.

Dependencies:

<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>

Code example:

// 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
    }
}

Hint: I didn't consider exception handling in above code samples.

I have to migrate this application to WebLogic (12.2.1) but this solution is not working anymore.

What I found out so far:

In Tomcat after the call to request.getRequestDispatcher(jsp).include() of the example above getWriter() of my CharArrayWriterResponse class is called.

In WebLogic getWriter() is not called anymore and that's the reason why it doesn't work anymore.

After some debugging, I found out that in WebLogic instead of getWriter() only getOutputStream() is called if I override it. getWriter() is not called once on Weblogic so there have to be differences in the underlying implementation of Tomcat and WebLogic.

Problem is that with getOutputStream() I see no possibility to get the response of the include() call in a separate stream or something else and to convert it to String for usage to build the final JSON containing the HTML.

Has someone solved this problem already and can provide a working solution for including a JSP in a programmatic way in combination with WebLogic?

Does anyone know another solution to achieve my goal?

Thanks for suggestions.


Solution

See working example here

Hint

A difference I found out between Tomcat and new Weblogic solution: With latter one it's not possible to include JSPF's directly anymore wheras with Tomcat getWriter() it is.

Solution is wrapping the JSPF inside a JSP file.


回答1:


I did this:

@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
    }
}

I used WriterOutputStream from apache commons-io, so I had to include in my pom.xml:

<dependency>
  <groupId>commons-io</groupId>
  <artifactId>commons-io</artifactId>
  <version>2.5</version>
</dependency>

I don't know what's in your jsp file, but I've tested with a simple one and I believe it worked. My jsp file:

<b>Hello world</b>

<p>testing</p>

<ul>test
<li>item</li>
<li>item2</li>
</ul>

Output (when accessing the servlet in a browser):

{"results":"<b>Hello world</b>\n\n<p>testing</p>\n\n<ul>test\n<li>item</li>\n<li>item2</li>\n</ul>"}



回答2:


As I can't figure out how to put multi-line code inside comments, I'm just putting it here. I could fix the issue by overriding flush() method in MyServletOutputStream class:

// inside MyServletOutputStream class
@Override
public void flush() throws IOException {
    if (this.bufferedOut != null) {
        this.bufferedOut.flush();
    }
    super.flush();
}



回答3:


Here is my updated and working example how to include a JSP file programmatically if getOutputStream() instead of getWriter() is called when implementing HttpServletResponseWrapper:

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()


来源:https://stackoverflow.com/questions/42440437/issue-with-requestdispatcher-including-jsp-programmatically-in-weblogic-12c

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!