send serialized objects from an applet to the server w/ progress bars

modsiw
modsiw used Ask the Experts™
on
I'm looking for an example of sending objects to the doPost method of a servlet and receiving another object back to an applet.

I also need some way to get percent complete to update a progress bar?

An example that already has the threading to update swing stuff would be good.
Comment
Watch Question

Do more with

Expert Office
EXPERT OFFICE® is a registered trademark of EXPERTS EXCHANGE®
Top Expert 2016
Commented:
Should be able to use something like the below.  Make sure you setContentLength at the servlet
http://www.java2s.com/Code/Java/Swing-JFC/ProgressBarDemolongtask.htm

Open in new window

Mick BarryJava Developer
Top Expert 2010
Commented:
following shows how to send a POST request to your servlet
http://helpdesk.objects.com.au/java/how-do-i-send-a-post-request-using-java
you would just change it to write your object (using an ObjectOutputStream) in place of the query string.

Learn SQL Server Core 2016

This course will introduce you to SQL Server Core 2016, as well as teach you about SSMS, data tools, installation, server configuration, using Management Studio, and writing and executing queries.

Author

Commented:
Any shot of an example combining the two ideas?

Author

Commented:
Or, more specifically, the applet will call code similar to:

out.write(myByteArray);
out.flush();

How does it know how much of the data has been read by the servlet?
Top Expert 2016

Commented:
>>How does it know how much of the data has been read by the servlet?

It won't if you're using writeObject directly. You'd need to set the progress bar to indeterminate.

Author

Commented:
Ok,

But suppose I don't write an object. Instead, I write bytes from a byte[] ?
You can *send* and *receive* streams on portions. In this way :
InputStream in = fcontent.getBinaryStream();
                    OutputStream out = response.getOutputStream();
                    int count = -1;
                    byte buff[] = new byte[1024];
                    while ((count = in.read(buff)) != -1) {
                        out.write(buff, 0, count);
                    }
                    out.flush();
and in this *while* you can update your progress bar.

Author

Commented:
Perhaps I'm misunderstanding your code valeri, but you code appears to be on the servlet. The progress bar is in the applet.

Am I missing something? Does the applet have to poll the servlet to get progress?

Author

Commented:
"set the progress bar to indeterminate"

We do this now. This is what I'm trying to fix. It upsets the users with slow connections; they think the app has frozen.
You are right! My example is how to send the stream on portions. On the applet side you have to read the stream in the same way /on portions/ and there you have to update your progress bar.
The question is "How will you know how many KB you are about to send in order to calculate the percentage in the right way?!"
Probably first of all you have to send how many KB you are about to send.
Top Expert 2016

Commented:
You could writer first to a ByteArrayOutputStream, so you know how much is going to be sent and can monitor it, if memory permits

Author

Commented:
Hrm.

Am I correct in assuming that flush waits until they are read to continue?

What keeps the servlet from receiving a EOF? Or does it receive EOFand ignore it, using contentLength instead?
*flush* is on the servlet side. In your case you don't need to flush on portions, you have to read on portions in order to update your progress bar.
Receiving EOF is indicated by -1 returned by read(byte[] b, int off, int len). EOF is never ignored.
contentLenght is just to know how many KB you are about to send /respectively receive/ in order to calculate the percentage in the right way.

Author

Commented:
Does OutputStream.write(...) wait until the bytes are written to return?

In other words, suppose that there are 20mb of bytes being sent across a 1mb/s connection. Will OutputStream.write(...) take 20 seconds to complete?
Top Expert 2016

Commented:
>>Will OutputStream.write(...) take 20 seconds to complete?

Yes
Top Expert 2016

Commented:
... which is why you need to ensure that another thread handles it, hence the SwingWorker

Author

Commented:
Here's what I have so far for the applet. I haven't built a matching servlet yet.

Any thoughts?

Any obvious errors?
package com.floorsoft.utility;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.Serializable;
import java.net.URL;
import java.net.URLConnection;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import javax.swing.SwingUtilities;

public class HttpServletConnection <T extends Serializable>
{
  private final          URL                  url                                                                      ;
  private final          Serializable         serSend                                                                  ;
  private final          boolean              receive                                                                  ;
  private                boolean              isStarted                 = false                                        ;
  public  final          List<ActionListener> lstUpdateActionListener   = new CopyOnWriteArrayList<ActionListener> ( ) ;
  public  final          List<ActionListener> lstFinishedActionListener = new CopyOnWriteArrayList<ActionListener> ( ) ;
  private final          double               weightSend                                                               ;
  private final          double               weightProcess                                                            ;
  private final          double               weightReceive                                                            ;
  private final          int                  timeProcess                                                              ;
  private final          int                  writeBufferSize                                                          ;
  private final          int                  readBufferSize                                                           ;
  private       volatile double               percent                   = 0                                            ;
  private       volatile boolean              isFinished                = false                                        ;
  private       volatile boolean              isProcessFinished         = false                                        ;
  private       volatile boolean              isError                   = false                                        ;
  private       volatile T                    t                         = null                                         ;
  private       volatile double               minUpdateStep                                                            ;
  private       volatile double               lastUpdate                = -100                                         ;


  public HttpServletConnection(URL url, Serializable serSend, boolean receive)
  {
    this(url,serSend,receive,2,2,1,500);
  }

  public HttpServletConnection(URL url, Serializable serSend, boolean receive, double weightSend, double weightProcess, double weightReceive, int timeProcess)
  {
    this (url,serSend,receive,weightSend,weightReceive,weightProcess,timeProcess,0.5,1024,1024);
  }

  public HttpServletConnection(URL url, Serializable serSend, boolean receive, double weightSend, double weightProcess, double weightReceive, int timeProcess, double minUpdateStep, int writeBufferSize, int readBufferSize)
  {
    this.url             = url             ;
    this.serSend         = serSend         ;
    this.receive         = receive         ;
    this.weightSend      = weightSend      ;
    this.weightProcess   = weightProcess   ;
    this.weightReceive   = weightReceive   ;
    this.timeProcess     = timeProcess     ;
    this.minUpdateStep   = minUpdateStep   ;
    this.writeBufferSize = writeBufferSize ;
    this.readBufferSize  = readBufferSize  ;
  }

  public synchronized void start()
  {
    if (isStarted) throw new IllegalStateException("already started");
    isStarted = true;
    new Thread()
    {
      public void run()
      {
        OutputStream os = null;
        InputStream is = null;
        try {
          URLConnection con = url.openConnection();
          con.setUseCaches(false);
          con.setDoOutput(serSend != null);
          con.setDoInput(receive);
          if (con.getDoOutput()) {
            os = con.getOutputStream();
            byte[] arySend = SerialFunctions.compress(SerialFunctions.toByteArray(serSend));
            ByteArrayInputStream bais = new ByteArrayInputStream(arySend);
            byte[] aryWrite = new byte[writeBufferSize];
            int count = 0;
            int sent = 0;
            while (count >= 0) {
              count = bais.read(aryWrite);
              if (count <= 0) continue;
              os.write(aryWrite,0,count);
              sent += count;
              setInputPercent(sent,arySend.length);
            }
            os.flush();
          }
          if (con.getDoInput()) {
            startProcessWait();
            is = con.getInputStream();
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            byte[] aryRead = new byte[readBufferSize];
            int count = 0;
            while (count >= 0) {
              count = is.read(aryRead);
              if (count <= 0) continue;
              stopProcessWait();
              baos.write(aryRead,0,count);
              setOutputPercent(baos.size(),con.getContentLength());
            }
            Serializable serReturn = SerialFunctions.toSerializable(SerialFunctions.decompress(baos.toByteArray()));
            if (serReturn instanceof Throwable) {
              t = null;
              isError = true;
              finish();
              throw new IllegalArgumentException((Throwable)serReturn);
            }
            try {
              t = (T)serReturn;
            }
            catch (ClassCastException cce) {
              t = null;
              isError = true;
              finish();
              throw cce;
            }
            finish();
          }
        }
        catch(IOException ioe) {
          throw new IllegalStateException(ioe);
        }
        finally {
          try {
            if (os != null) os.close();
          }
          catch (Exception e) {
            e.printStackTrace();
          }
          try {
            if (is != null) is.close();
          }
          catch (Exception e) {
            e.printStackTrace();
          }
        }
      }
    }.start();
  }

  public double getMinUpdateStep()
  {
    return minUpdateStep;
  }

  public void setMinUpdateStep(double minUpdateStep)
  {
    this.minUpdateStep = minUpdateStep;
  }

  private void finish()
  {
    percent = 100;
    fireUpdate();
    isFinished = true;
    SwingUtilities.invokeLater(new FireFinishedActionListener());
  }

  private void fireUpdate()
  {
    if (percent < lastUpdate + minUpdateStep) return;
    lastUpdate = percent;
    SwingUtilities.invokeLater(new FireUpdateActionListener());
  }

  private void stopProcessWait()
  {
    isProcessFinished = true;
  }

  public void startProcessWait()
  {
    new Thread() {
      public void run()
      {
        int step = (int)(100 * weightProcess / (weightSend + weightProcess + weightReceive));
        final int delay = timeProcess / step;
        int i = 0;
        try {
          sleep(delay);
        }
        catch (Exception e) {
          e.printStackTrace();
        }
        while (!isProcessFinished) {
          if (i == step) {
            isProcessFinished = true;
            break;
          }
          percent = (weightSend + i / step * weightProcess) / (weightSend + weightProcess + weightReceive);
          i++;
          fireUpdate();
          try {
            sleep(delay);
          }
          catch (Exception e) {
            e.printStackTrace();
          }
        }
      }
    }.start();
  }

  private void setInputPercent(double currentSize, double expectedSize)
  {
    if (expectedSize < 0) return;
    if (currentSize > expectedSize) currentSize = expectedSize;
    percent = (weightSend + weightProcess) / (weightSend + weightProcess + weightReceive) +  currentSize / expectedSize * weightReceive;
    fireUpdate();
  }

  private void setOutputPercent(double currentSize, double expectedSize)
  {
    if (expectedSize < 0) return;
    if (currentSize > expectedSize) currentSize = expectedSize;
    percent = currentSize / expectedSize * weightSend;
    fireUpdate();
  }

  public double getPercent()
  {
    return percent;
  }

  public boolean isError()
  {
    return isError;
  }

  public T getReturn()
  {
    if (!isFinished) throw new IllegalStateException("not finished");
    if (isError) throw new IllegalStateException("error");
    return t;
  }

  private class FireUpdateActionListener implements Runnable
  {
    public void run()
    {
      for (ActionListener actionListener : lstUpdateActionListener)
        actionListener.actionPerformed(new ActionEvent(HttpServletConnection.this,0,""));
    }
  }

  private class FireFinishedActionListener implements Runnable
  {
    public void run()
    {
      for (ActionListener actionListener : lstUpdateActionListener)
        actionListener.actionPerformed(new ActionEvent(HttpServletConnection.this,0,""));
    }
  }

}

Open in new window

Author

Commented:
Suppose i do:

outputStream.write();
//wait...
outputStream.write();
//wait....
outputStream.write();
//wait...
outputStream.flush();

How many EOF will the matching inputstream incur?
Top Expert 2016

Commented:
>>How many EOF will the matching inputstream incur?

None. EOF happens when the output stream is closed

Author

Commented:
ah

Author

Commented:
The code below works if I am sending byte[] to the servlet and also receiving. It does not work if I am only sending (  receive = false  )  . The doPost method of the servlet isn't called.

Ideas?
      OutputStream os = null;
      InputStream is = null;
      try {
        URLConnection con = url.openConnection();
        con.setUseCaches(false);
        con.setDoOutput(serSend != null);
        con.setDoInput(receive);
        if (con.getDoOutput()) {
          byte[] arySend = SerialFunctions.compress(SerialFunctions.toByteArray(serSend));
          con.setRequestProperty("size",""+arySend.length);
          os = con.getOutputStream();
          ByteArrayInputStream bais = new ByteArrayInputStream(arySend);
          byte[] aryWrite = new byte[writeBufferSize];
          int count = 0;
          int sent = 0;
          while (count >= 0) {
            count = bais.read(aryWrite);
            if (count <= 0) continue;
            os.write(aryWrite,0,count);
            sent += count;
            setInputPercent(sent,arySend.length);
          }
          os.flush();
          os.close();
          os = null;
        }
        if (con.getDoInput()) {
          if (weightProcess != 0) startProcessWait();
          is = con.getInputStream();
          ByteArrayOutputStream baos = new ByteArrayOutputStream();
          byte[] aryRead = new byte[readBufferSize];
          int count = 0;
          while (count >= 0) {
            count = is.read(aryRead);
            if (count <= 0) continue;
            stopProcessWait();
            baos.write(aryRead,0,count);
            setOutputPercent(baos.size(),con.getContentLength());
          }
          is.close();
          is = null;
          Serializable serReturn = SerialFunctions.toSerializable(SerialFunctions.decompress(baos.toByteArray()));
          if (serReturn instanceof Throwable) {
            t = null;
            isError = true;
            finish();
            throw new IllegalArgumentException((Throwable)serReturn);
          }
          try {
            t = (T)serReturn;
          }
          catch (ClassCastException cce) {
            t = null;
            isError = true;
            finish();
            throw cce;
          }
        }
        finish();
      }
      catch(IOException ioe) {
        throw new IllegalStateException(ioe);
      }
      finally {
        try {
          if (os != null) os.close();
        }
        catch (Exception e) {
          e.printStackTrace();
        }
        try {
          if (is != null) is.close();
        }
        catch (Exception e) {
          e.printStackTrace();
        }
      }

Open in new window

Mick BarryJava Developer
Top Expert 2010

Commented:
looks like you never actually read the response in that case so the request does not actually get sent.
Even if you're not reading input you should be still checking that the response was successful (via response code)
Yes, objects is right. You always have to read the response code. In this way :
int status = ((HttpURLConnection) connection).getResponseCode();

Author

Commented:
When should I do this?

Author

Commented:
os.write doesn't seem to wait for the write to complete before returning in the code below.

This occurred on a 9mbit connection. If this were true, it would have to be at an impossibly fast connection.

Ideas?
arySend.length = 2097328
time to send: 15ms


          byte[] arySend = /*SerialFunctions.compress(*/SerialFunctions.toByteArray(serSend)/*)*/;
          con.setRequestProperty("size",""+arySend.length);
          os = con.getOutputStream();
          ByteArrayInputStream bais = new ByteArrayInputStream(arySend);
          byte[] aryWrite = new byte[writeBufferSize];
          int count = 0;
          int sent = 0;
long ts = System.currentTimeMillis();
System.out.println("arySend.length = " + arySend.length);
          while (count >= 0) {
            count = bais.read(aryWrite);
            if (count <= 0) continue;
            os.write(aryWrite,0,count);
            sent += count;
            setInputPercent(sent,arySend.length);
          }
          os.flush();
          os.close();
System.out.println("time to send: " + (System.currentTimeMillis() - ts) + "ms");
          os = null;

Open in new window

Author

Commented:
This fixed the buffering issue causing os.write to instantly return:


        con.setChunkedStreamingMode(writeBufferSize);

con is a HttpURLConnection

Author

Commented:
Thanks much.

It works!

Do more with

Expert Office
Submit tech questions to Ask the Experts™ at any time to receive solutions, advice, and new ideas from leading industry professionals.

Start 7-Day Free Trial