0

Somewhere within my Wicket Link, that calls a Utility, which serves a report file, generated by BIRT, I am getting an IllegalStateException.

From the Wicket page:

Link<Void> downloadLink = new Link<Void>("download") {
    private static final long serialVersionUID = 1L;

    @Override
    public void onClick() {

        HttpServletResponse response =
                (HttpServletResponse)((WebResponse)getResponse()).getContainerResponse();
        ReportUtility.getInstance().serveFile("myFileName.rptdesign", "pdf", response, null);

    }

};
add (downloadLink);

From the ReportUtility.java:

public void serveFile(String reportDesignFile, String extType,
        HttpServletResponse response, Map<String, Object> paramMap) {

    InputStream is = null;
    ServletOutputStream os = null;
    try {
        is = ReportUtility.class.getResourceAsStream(reportDesignFile);
        os = response.getOutputStream();

        IReportRunnable irr = engine.openReportDesign(is);
        IRunAndRenderTask task = engine.createRunAndRenderTask(irr);

        HTMLRenderOption options = new HTMLRenderOption();
        options.setOutputFormat(extType);
        options.closeOutputStreamOnExit(true);
        options.setOutputStream(os);

        // to force open/save/cancel with our specified filename
        String outputFileName = createReportFileName(reportDesignFile, extType);
        response.addHeader("Content-Disposition", "attachment;filename=" + outputFileName);

        if (paramMap != null) {
            task.setParameterValues(paramMap);
        }
        task.setRenderOption(options);      
        task.run();

    }
    catch (EngineException ex) {
        logger.error("Engine Exception while serving file", ex);
        throw new RuntimeException(ex);
    }
    catch (IOException ioex) {
        logger.error("IO Exception while openning response output stream", ioex);
        throw new RuntimeException(ioex);
    }

    finally {
        try {
            if (os != null)
                os.close();
            if (is != null)
                is.close();
        } catch (IOException e) {
            // ignore
        }
    }
}

If it matters, the createReportFileName method appends today's date and the proper file extension to the basename of the report design file, i.e. "myFileName.rptdesign" becomes "myFileName_04_24_2012.pdf"

Here is the statck trace:

Apr 24, 2012 9:35:04 AM org.apache.catalina.core.StandardWrapperValve invoke
    SEVERE: Servlet.service() for servlet default threw exception
    java.lang.IllegalStateException
at org.apache.catalina.connector.ResponseFacade.sendRedirect(ResponseFacade.java:435)
at org.apache.wicket.protocol.http.servlet.ServletWebResponse.sendRedirect(ServletWebResponse.java:230)
at org.apache.wicket.protocol.http.BufferedWebResponse$SendRedirectAction.invoke(BufferedWebResponse.java:392)
at org.apache.wicket.protocol.http.BufferedWebResponse.writeTo(BufferedWebResponse.java:580)
at org.apache.wicket.protocol.http.HeaderBufferingWebResponse.flush(HeaderBufferingWebResponse.java:89)
at org.apache.wicket.protocol.http.WicketFilter.processRequest(WicketFilter.java:195)
at org.apache.wicket.protocol.http.WicketFilter.doFilter(WicketFilter.java:241)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
at org.springframework.orm.jpa.support.OpenEntityManagerInViewFilter.doFilterInternal(OpenEntityManagerInViewFilter.java:113)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:76)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:233)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:191)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:127)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:102)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:109)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:293)
at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:859)
at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.process(Http11Protocol.java:602)
at org.apache.tomcat.util.net.JIoEndpoint$Worker.run(JIoEndpoint.java:489)
at java.lang.Thread.run(Unknown Source)

It is important to state that this does not effect the operation: the user clicks, the open/save/cancel appears, the file comes, and looks very pretty. After ward, however, whatever the user tries to do next, sends to our error page with a StalePageException. After that, everything returns to normal again.

I suspect this involves the HttpServletResponse, or perhaps how I am getting it from Wicket. However, that portion of my code that adds the file to the response with the header is copied almost exactly from BIRT's tutorials. (Maybe BIRT and Wicket just don't like each other.)

I note that none of my own code shows in the stack trace. Also, I have tried to "catch" the IllegalStateException in a handful of places, including the onClick, the serveFile, and even my Wicket Application, all without success. Of course, even if I could catch it, I would prefer to have never caused it in the first place.

cobaltduck
  • 1,598
  • 5
  • 25
  • 51

1 Answers1

0

A better way to deliver the report will be to implement your own ResourceStream (...implements IResourceStream). Your implementation returns the generated report when the user click.

Here is some example how to add a wicket link that streams the report to the client:

add(new Link("reportAsPdfButton") {
    @Override
    public void onClick() {
        BirtReportResourceStream brrs = new BirtReportResourceStream();
        getRequestCycle().scheduleRequestHandlerAfterCurrent(new ResourceStreamRequestHandler(brrs, "your_report_name.pdf"));
    }
});

Here is a short sample hot to implement the IResourceStream:

public class BirtReportResourceStream implements IResourceStream {
    private byte[] content = null;

    ...

    public BirtReportResourceStream() {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        EngineConfig config = new EngineConfig();
        config.setEngineHome(basePath + "/WEB-INF/ext_lib/");

        try {
            Platform.startup(config);
            IReportEngineFactory factory = (IReportEngineFactory) Platform.createFactoryObject(IReportEngineFactory.EXTENSION_REPORT_ENGINE_FACTORY);
            IReportEngine engine = factory.createReportEngine(config);
            IReportRunnable report = engine.openReportDesign("path/to/your/report-definiton.rpt");
            IRunAndRenderTask task = engine.createRunAndRenderTask(report);

            if (null != getParameter()) {
                task.setParameterValues(getParameter());
            }  

            PDFRenderOption options = new PDFRenderOption();
            options.setOutputFormat(format.toString().toLowerCase());
            options.setOutputStream(baos);
            task.setRenderOption(options);
            task.run();
            task.close();
        } catch (BirtException e) {
            // handle the exception...
        }

        content = baos.toByteArray();
    }

    public InputStream getInputStream() throws ResourceStreamNotFoundException {
        return new ByteArrayInputStream(content);
    }

    ...
}

There are some more methods to implement but they are simple. The most complex part was to find out how to create the report and stream it into an byte array and how to stream it to the client. These two methods are shown above.

Hope it helps.

magomi
  • 6,599
  • 5
  • 31
  • 38