Multiple streams download

Hi!

My application allows user to download files from the server.
The problem is - sometimes, when the user clicks “download” button there are two (or more) files to download.
My code is:

		
for (FileNameContainer fileContainer : pdfReader.getFilesPathFromPublicatorFileName(value)) {
			final FileInputStream fis = pdfReader.getFile(fileContainer.getPath());
			
		    AttachmentStreamResource sr = new AttachmentStreamResource(new StreamSource() {
			      private static final long serialVersionUID = 1L;

			      @Override
			      public InputStream getStream() {

			        return fis;
			      }
			    },  fileContainer.getFileName() + ".pdf", getApplication());

		    sr.setMIMEType("application/octet-stream");
			sr.setCacheTime(1); // bug IE, nie obsluguje wartosci <=0

			getApplication().getMainWindow().open(sr, "");
}

AttachmentStreamResource is a class that extends StreamResource and adds:

ds.setParameter("Content-Disposition", "attachment; filename="+getFilename()); to getStream method.

And it works fine if there is only one file to download. Unfortunately if there are more files I get only the last file to download, and my application throws an exception:

java.net.SocketException: Software caused connection abort: socket write error

So my question is: what is the proper way to do it?
Maybe there is some way to check if the output stream is available?
Or maybe I have to extend the AbstractApplicationServlet and override the handleDownload method?

I use Vaadin 6.5.1 and Tomcat 7.0.4

Best regards,
Karol Stojek

Ok, I’ve found the solution.
First of all - we have to extend the FileInputStream (or other InputStream) and override the close() method.
In that method we have to set some flag that tells us whether the stram is closed or not (default - not).

Second task - run a Timer, that tells the application to open the first stream and checks if the current inputStream is closed. If it is - tell the application to open another stream.

Third task - the application doesn’t react if we do something on the server side - te request isn’t transfered to the client side. So we have to use some component that lets us notify the client side. The dontpush addon should do the job, but i wasn’t able to make it work with my current application.
So I wrote my own component, that has a timer in it and calls client.updateVariable.
Remember to add that component to your application :).

That should be it.

Code samples:

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;

public class FileInputStreamWrapper extends FileInputStream{

	private boolean isClosed = false;
	
	public FileInputStreamWrapper(String path) throws FileNotFoundException {
		super(path);
	}
    
	public boolean isClosed() {
		return isClosed;
	}
	
	public void close() throws IOException {
		isClosed = true;
		super.close();
	}
}
        timer = new ServerCallTimer();
        addComponent(timer);
        timer.setTime(500);
    private void downloadPublicator(String value) {
    	
		for (FileNameContainer fileContainer : pdfReader.getFilesPathFromPublicatorFileName(value)) {
			final FileInputStreamWrapper fis = pdfReader.getFile(fileContainer.getPath());
			
		    final AttachmentStreamResource sr = new AttachmentStreamResource(new StreamSource() {
			      private static final long serialVersionUID = 1L;

			      @Override
			      public InputStream getStream() {

			        return fis;
			      }
			    },  "fileName.pdf", getApplication());

		    sr.setMIMEType("application/octet-stream");
			sr.setCacheTime(1);
			
			streamsList.add(sr);			
		}
		
		
    	timer.setRunning(true);
    	
		Timer tim = new Timer();
		tim.schedule(new TimerTask() {
			
			boolean isRunning;
			
			@Override
			public void run() {
				if (streamsList.isEmpty()) {
					cancel();
					timer.setRunning(false);
				} else {
					if (!((FileInputStreamWrapper)streamsList.get(0).getStream().getStream()).isClosed()) {
						if (!isRunning) {
							Runnable t = new Runnable() {
								
								@Override
								public void run() {
									isRunning = true;
									getApplication().getMainWindow().open(streamsList.get(0), "");
								}
							};					
							
							new Thread(t).start();
						}
					} else {
						streamsList.remove(0);
						isRunning = false;
					}
				}
				
			}
		}, 500, 500);		
    } 
import com.google.gwt.user.client.Timer;
import com.google.gwt.user.client.ui.FlowPanel;
import com.vaadin.terminal.gwt.client.ApplicationConnection;
import com.vaadin.terminal.gwt.client.Paintable;
import com.vaadin.terminal.gwt.client.UIDL;

public class VServerCallTimer extends FlowPanel implements Paintable {
	
	/** The client side widget identifier */
	protected String paintableId;

	/** Reference to the server connection object. */
	protected ApplicationConnection client;	
	
	private Timer timer;
	private int time = 1000;

	public VServerCallTimer() {		
		timer = new Timer() {
			
			@Override
			public void run() {
				if (client!=null) {
					client.updateVariable(paintableId, "update", true, true);
				}
			}
		};
		
	}
	
	@Override
	public void updateFromUIDL(UIDL uidl, ApplicationConnection client) {
		this.client = client;
		this.paintableId = uidl.getId();
		
		if (client.updateComponent(this, uidl, true)) {
			return;
		}
		
		if (uidl.hasAttribute("time")) {
			this.time = uidl.getIntAttribute("time");
		}
		
		if (uidl.hasAttribute("run")) {
			if (uidl.getBooleanAttribute("run")) {
				timer.scheduleRepeating(this.time);	
			} else {
				timer.cancel();
			}
				
		}
	}
	
    @Override
    protected void onDetach() {
    	timer.cancel();
        super.onDetach();
    }
	
}