users@jersey.java.net

Re: Adding listeners to Input Streams with Jersey

From: Paul Sandoz <Paul.Sandoz_at_Sun.COM>
Date: Thu, 27 Mar 2008 12:20:14 +0100

Hi James,

James Weir wrote:
> Hi all,
>
> I need attach listeners for when I upload or download files (either
> client or server)?. I filed an RFE against 0.5ea, was this RFE added
> in ea0.6 ?
>

No, no time unfortunately :-( i will try and get it into 0.7.


> My problem is that I request the service to download an ISO image that
> has been created by my Web Service. The download itself works well,
> however I wanted to be able to update another field in the database
> regarding the status of the download. It is important that the server
> updates a field in the database when the download is completed. That
> way if the user asks for the status (when re-logging in for example)
> then the server can reply saying that this file has been downloaded.
> The client today holds nothing on disk (only a cache in memory).
>
> If this modif was not done, no worries, I can always get the client to
> do a PUT to say when the download is finished on his side. Here is my
> server side code today:
>
> @Path("{itid}/image")
> @GET
> @ProduceMime("*/*")
> public Response downloadImage(@PathParam("name") String userName,
> @PathParam("itid") String itid) {
>
> .....omitted code
>
> //----------------------------------------------------------------------
> // Create the stream ready to send the contents
>
> //----------------------------------------------------------------------
> InputStream in = null;
> try {
> in = new BufferedInputStream(new FileInputStream(f));
> } catch (FileNotFoundException e) {
> logger.error("Appliance image file not found:\n" + e);
> dbManager.close();
> throw new NotFoundException("Appliance image not found");
> }
>
>
> /***********************************************************************
> * Returning the appliance information
>
> **********************************************************************/
> r = Response.ok(in).type(mt).build();
> dbManager.close();
> return(r);
> }
>
> It would be good if I could attach a listener to the buffered input
> stream to get the bytes transferred and know when the download is
> complete in order to do some extra work on the server (namely update the
> database with some information)
>

You could write your own InputStream adapter You would need to check
when the close is performed if all the bytes have been read (since it
could terminate prematurely).

   public class ListeningInputStream extends InputStream {
      private final InputStream s;
      private int read;

      public ListeningInputStream(int total, InputStream s) {
        this.total = total;
        this.s = s;
      }

      public int read() {
        read++
        return s.read();
      }

      public int read(byte[] b) {
        read += s.read(b);
      }

      public int read(byte[] b, int off, int len) {
        read += s.read(b);
      }

      public void close() {
         if (read == total) {
            // Update the DB
            ...
         }
      }
      // adapt other methods
   }


Another way to do this is to create a download ticket, from which the
client obtains and uses that to get the ISO image, and when it completed
calls DELETE on the ticket (tickets can timeout if no operation is
performed on them after a certain point). That can be a nice way of
performing explicit confirmation (e.g. the client might want to verify
the image for example), where as the server side only solution is
implicit, assuming that the client (or a proxy) confirms because it read
all the bytes.


I take the point about the client side, it is important to show feedback
when uploading/downloading such things so have an event reporting
mechanism while uploading/downloading is important.

Paul.

-- 
| ? + ? = To question
----------------\
    Paul Sandoz
         x38109
+33-4-76188109