Getting java.io.IOException: Stream Closed from DownloadStream

I’m currently developing a Vaadin application. The function I’m working on is creating two PDF’s, merging them and then downloading it to the client as a temporary file. Just a few times i get this strange IOException with no reference to my code. Any idea on what may cause it?

The stack trace:

java.io.IOException: Stream Closed
    at java.io.FileInputStream.readBytes(Native Method)
    at java.io.FileInputStream.read(Unknown Source)
    at com.vaadin.server.DownloadStream.writeResponse(DownloadStream.java:304)
    at com.vaadin.server.AbstractClientConnector.handleConnectorRequest(AbstractClientConnector.java:646)
    at com.vaadin.server.ConnectorResourceHandler.handleRequest(ConnectorResourceHandler.java:83)
    at com.vaadin.server.VaadinService.handleRequest(VaadinService.java:1408)
    at com.vaadin.server.VaadinServlet.service(VaadinServlet.java:350)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:790)
    at org.eclipse.jetty.servlet.ServletHolder.handle(ServletHolder.java:808)
    at org.eclipse.jetty.servlet.ServletHandler.doHandle(ServletHandler.java:587)
    at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:143)
    at org.eclipse.jetty.security.SecurityHandler.handle(SecurityHandler.java:577)
    at org.eclipse.jetty.server.session.SessionHandler.doHandle(SessionHandler.java:223)
    at org.eclipse.jetty.server.handler.ContextHandler.doHandle(ContextHandler.java:1127)
    at org.eclipse.jetty.servlet.ServletHandler.doScope(ServletHandler.java:515)
    at org.eclipse.jetty.server.session.SessionHandler.doScope(SessionHandler.java:185)
    at org.eclipse.jetty.server.handler.ContextHandler.doScope(ContextHandler.java:1061)
    at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:141)
    at org.eclipse.jetty.server.handler.HandlerList.handle(HandlerList.java:52)
    at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:97)
    at org.eclipse.jetty.server.Server.handle(Server.java:497)
    at org.eclipse.jetty.server.HttpChannel.handle(HttpChannel.java:310)
    at org.eclipse.jetty.server.HttpConnection.onFillable(HttpConnection.java:257)
    at org.eclipse.jetty.io.AbstractConnection$2.run(AbstractConnection.java:540)
    at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:635)
    at org.eclipse.jetty.util.thread.QueuedThreadPool$3.run(QueuedThreadPool.java:555)
    at java.lang.Thread.run(Unknown Source)

Yeah, your code just creates the FileInputStream in the StreamSource, after which the framework just reads from the stream.

It’s hard to say what could cause the problem. I hope there’s nothing special in your server setup that could damage the stream, like load-balancing, redeployment, or something.

I don’t know what tools you are using for merging the PDFs, but at least some tools (like pdftk) allow writing to stdout, which you could serve as a PipedInputStream or something.

Thanks for the response!

It could be a problem with the setup for the server, because I don’t seem to be able to reproduse the error on my local setup.

I have been looking a bit deeper in this problem and have found out that the error also appear in another function that uses JasperReports and fetching of pdf-files. I think that the problem may come from a class I call
TemporaryFileDownloadResource. Here are the code for generating a temporary file and downloading it to the client.

Generating the temporary file:

private void fillReport(List<?> datasource, String templateName) {
        byte buffer =  new FileReader(templateName).read();
        if (buffer == null) {
            // running from intellij? Then try relative path for file
            buffer = new FileReader(templateName.replace("classpath://", "classpath:///")).read();
        }
        if (buffer == null) {
            return; //just give up...
        }

        // Now all should be ok... byte has content
        File tempFile;
        try {
            tempFile = File.createTempFile("tmp", ".pdf");
            tempFile.deleteOnExit();

            // Now all should be ok... byte has content
            JasperReport jasperReport = (JasperReport) JRLoader.loadObject(new ByteArrayInputStream(buffer));
            JasperPrint jasperPrint = JasperFillManager.fillReport(jasperReport, new HashMap<>(),
                    new JRBeanCollectionDataSource(datasource));
            JasperExportManager.exportReportToPdfFile(jasperPrint, tempFile.getPath());
        } catch (IOException | JRException e) {
            logger.error(e.getMessage(), e);
            return;
        }

        // Show the produced label in a window. Let the user view and print the label
        String downloadFileName = System.currentTimeMillis() + "_filename.pdf";
        String contentType = "application/pdf";
        TemporaryFileDownloadResource resource;
        try {
            resource = new TemporaryFileDownloadResource(downloadFileName, contentType, tempFile);
            resource.setMIMEType("application/pdf");
            //Create window
            Window window = new Window();
            window.setResizable(true);
            window.setCaption(WorkstationTexts.get("labelPrintViewer.label.caption"));
            window.setWidth("800");
            window.setHeight("600");
            window.center();

            StreamResource r = new StreamResource(resource.getStreamSource(), "label.pdf");
            r.setMIMEType("application/pdf");
            r.setCacheTime(0); // no caching of this. Get fresh copy
            BrowserFrame frame = new BrowserFrame(WorkstationTexts.get("labelPrintViewer.label.caption"), r);
            frame.setSizeFull();
            window.setContent(frame);
            UI.getCurrent().addWindow(window);
        } catch (FileNotFoundException e) {
            logger.error(e.getMessage(), e);
        }
    }

Here are the class that creates the downloaded file on the client:

public class TemporaryFileDownloadResource extends StreamResource {
    /**
     * The filename.
     */
    private final String filename;

    /**
     * The content type.
     */
    private String contentType;

    /**
     * Instantiates a new temporary file download resource.
     *
     * @param fileName    the file name
     * @param contentType the content type
     * @param tempFile    the temp file
     * @throws FileNotFoundException the file not found exception
     */
    public TemporaryFileDownloadResource(final String fileName,
                                         final String contentType, final File tempFile) throws FileNotFoundException {
        super(new FileStreamResource(tempFile), fileName);
        this.filename = fileName;
        this.contentType = contentType;
    }

    /*
     * (non-Javadoc)
     *
     * @see com.vaadin.terminal.StreamResource#getStream()
     */
    @Override
    public DownloadStream getStream() {
        final DownloadStream stream =
                new DownloadStream(getStreamSource().getStream(), contentType, filename);
        stream.setParameter("Content-Disposition", "attachment;filename=" + filename);
        // This magic incantation should prevent anyone from caching the data
        stream.setParameter("Cache-Control", "private,no-cache,no-store");
        // In theory <=0 disables caching. In practice Chrome, Safari (and, apparently, IE) all
        // ignore <=0. Set to 1s
        stream.setCacheTime(1000);
        return stream;
    }

    /**
     * The Class FileStreamResource.
     */
    private static class FileStreamResource implements StreamResource.StreamSource {

        /**
         * The Constant serialVersionUID.
         */
        private static final long serialVersionUID = 3801605481686085335L;

        /**
         * The input stream.
         */
        private final InputStream inputStream;

        /**
         * Instantiates a new file stream resource.
         *
         * @param fileToDownload the file to download
         * @throws FileNotFoundException the file not found exception
         */
        public FileStreamResource(final File fileToDownload) throws FileNotFoundException {
            inputStream = new DeletingFileInputStream(fileToDownload);
        }

        /*
         * (non-Javadoc)
         *
         * @see com.vaadin.terminal.StreamResource.StreamSource#getStream()
         */
        @Override
        public InputStream getStream() {
            return inputStream;
        }
    }
}

class DeletingFileInputStream extends FileInputStream implements Serializable {

    /**
     * The file.
     */
    protected File file = null;

    /**
     * Instantiates a new deleting file input stream.
     *
     * @param file the file
     * @throws FileNotFoundException the file not found exception
     */
    public DeletingFileInputStream(final File file) throws FileNotFoundException {
        super(file);
        this.file = file;
    }

    /*
     * (non-Javadoc)
     *
     * @see java.io.FileInputStream#close()
     */
    @Override
    public void close() throws IOException {
        super.close();
        file.delete();
    }
}

In advance, thanks!