10

Good evening, i want to know how to clear the data written to a PrintWriter, i.e. is it possible to remove the data from a PrintWriter after printing?

here in this servlet i print some text to the response and at the line denoted by # i want to remove all the previously printed data and print new stuff:

protected void doGet(HttpServletRequest request,
        HttpServletResponse response) throws ServletException, IOException {
    PrintWriter out = response.getWriter();
    String uName = request.getParameter("uName");
    String uPassword = request.getParameter("uPassword");

    if (uName .equals("Islam")) {
        out.println("Valid-Name");
        if (uPassword !=null) {
            if (uPassword .equals("Islam")) {
                // # clear the writer from any printed data here
                out.println("Valid-password");
            } else {
                out.println("");
                out.println("InValid-password");
            }
        }
    } else {
        out.println("InValid-Name");

    }

}

Note: i tried out.flush() but the old printed text remains

Eslam Hamdy
  • 7,126
  • 27
  • 105
  • 165

4 Answers4

10

Create an in-memory PrintWriter using a StringWriter. You can get the underlying buffer from the StringWriter and clear it if you need to.

StringWriter sr = new StringWriter();
PrintWriter w = new PrintWriter(sr);

w.print("Some stuff");
// Flush writer to ensure that it's not buffering anything
w.flush();
// clear stringwriter
sr.getBuffer().setLength(0);

w.print("New stuff");

// write to Servlet out
w.flush();
response.getWriter().print(sr.toString());
Charles Forsythe
  • 1,831
  • 11
  • 12
  • Servlet's spec provides a way to do this. Please see the answer below. – Ramesh PVK Mar 24 '13 at 07:09
  • That method doesn't always work. There is no standard way to guarantee the size of the buffer underlying the Servlet PrintWriter; in fact, it has no obligation to provide one. This method always works. – Charles Forsythe Mar 24 '13 at 18:00
  • It will work always provided one knows what Servlet specs says. – Ramesh PVK Mar 24 '13 at 18:19
  • The Servlet spec says the the response can be committed when you overflow the buffer in the PrintWriter. You don't know what the buffer size is, and it couldn't, in fact, be **0** bytes. How does that help you make this "always work"? – Charles Forsythe Mar 25 '13 at 15:47
  • You can always set your required buffer size. Even if you use your own PrintWriter, you can buffer only to some extent. What cant you set that to Response buffer size. – Ramesh PVK Mar 25 '13 at 15:49
  • Oops. You're right. You can both set and query the buffer size. However, if you buffer using a StringWriter, your buffer is limited only by the heap size. If you set the buffer size, you must check to make sure that you are not overwriting it, which requires keeping an accurate count of bytes written. This is complicated because the bytes you write may not match the number of characters you write. The only way for this to be simple is if you make the buffer so large that you never overflow it, which puts unnecessary strain on memory resources. – Charles Forsythe Mar 25 '13 at 16:34
3

HttpServlteResponse.resetBuffer() will clear the buffered content. But yes, if the response is already flushed to the client it will throw IllegalStateException. Because it is illegal to clear after partial response is sent to the client.

resetBuffer........

void resetBuffer()
Clears the content of the underlying buffer in the response without clearing headers or status code. If the response has been committed, this method throws an IllegalStateException.

References:

Cause of Servlet's 'Response Already Committed'

Community
  • 1
  • 1
Ramesh PVK
  • 15,200
  • 2
  • 46
  • 50
0

You can't do that with the original PrintWriter you get from the response, as that's backed by the actual OutputStream corresponding to the client connection. What you write there goes right to the browser via the wire (after some buffering), so you can't "take it back".

What you can do is write your message in some StringBuilder and once you know it's good to go, write it to the PrintWriter.

If you want this logic to be applied in multiple places (transparently), you can consider writing a filter that wraps the original response in an HttpServletResponseWrapper which returns a "fake" OutputStream or PrintWriter and performs this check prior to actually sending it over the wire.

public class CensorshipFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        HttpServletResponse httpServletResponse = (HttpServletResponse) response;
        CensorshipResponseWrapper wrapper = new CensorshipResponseWrapper(httpServletResponse);
        chain.doFilter(request, wrapper);
        String output = wrapper.sw.toString();
        if ( output.contains("Some forbidden pattern") ) { // your check goes here
            // throw exception or whatever
        } else { // write the whole thing
            httpServletResponse.getWriter().write(output);
        }
    }

    @Override
    public void destroy() {
    }

    static class CensorshipResponseWrapper extends HttpServletResponseWrapper {
        private final StringWriter sw = new StringWriter();

        public CensorshipResponseWrapper(HttpServletResponse response) {
            super(response);
        }

        @Override
        public ServletOutputStream getOutputStream() throws IOException {
            // you may also fake the output stream, if some of your servlets use this method
            return super.getOutputStream();
        }

        @Override
        public PrintWriter getWriter() throws IOException {
            return new PrintWriter(sw);
        }
    }
}
Costi Ciudatu
  • 37,042
  • 7
  • 56
  • 92
0

What ended up working for me was to change the logic of how I was outputting my data.

enter image description here

This is the data structure I was outputting that stored the results of a search using the text from a html form as input.

private final TreeMap<String, ArrayList<SearchResult>> searchResults;

So I was iterating over the contents of this data structure and printing it out to html.

public void writeSearchResultsToHtml(PrintWriter writer)
{
    try
    {
        JSONTreeWriter. writeSearchResultsToHtml(searchResults, writer);
    } catch (ArithmeticException | IllegalArgumentException | IOException | NoSuchElementException e)
    {
        System.err.println("Unable to write the search results builder to JSON to the file html.");
    }

    // clear results for next search otherwise
    // the next search will contain the previous
    // results, store them in history.
    searchResults.clear();
}

Clearing the data structure worked great given my servlet setup.

Here was my main serverlet loop logic:

public void startServer()
{
    // seed the database for testing
    crawler.startCrawl("http://cs.usfca.edu/~cs212/birds/birds.html");
    index.toJSON("index.json");

    // type of handler that supports sessions
    ServletContextHandler servletContext = null;

    // turn on sessions and set context
    servletContext = new ServletContextHandler(ServletContextHandler.SESSIONS);
    servletContext.setContextPath("/");
    servletContext.addServlet(ViewServlet.class, "/");

    // default handler for favicon.ico requests
    DefaultHandler defaultHandler = new DefaultHandler();
    defaultHandler.setServeIcon(true);

    ContextHandler defaultContext = new ContextHandler("/favicon.ico");
    defaultContext.setHandler(defaultHandler);

    // setup handler order
    HandlerList handlers = new HandlerList();
    handlers.setHandlers(new Handler[]{defaultContext, servletContext});

    openWebBrowser();

    // setup jetty server
    Server server = new Server(portNumber);
    server.setHandler(handlers);
    try
    {
        server.start();
        server.join();
    } catch (Exception e)
    {
        e.printStackTrace();
    }
}
Tim Siwula
  • 966
  • 11
  • 15