Monday 2 February 2015

progress bar for file upload with Apache HttpClient 4 and StringEntity

There is a similar solution on stackoverflow, and it uses MultipartEntity to upload a file. However, if you are required to use StringEntity and put file data into JSON POST request like what I needed to do, please see my source code below.



code extract to use HttpPost with StringEntity with JSON in it.
        String jsonRequest = "the file data and related stuff";  
        StringEntity stringEntityForProgressBar = new StringEntity(jsonRequest, progressBean);  
        httpPost.setEntity(stringEntityForProgressBar);  
        HttpResponse response = httpClient.execute(httpPost);  



My own StringEntity.java

 import java.io.IOException;  
 import java.io.OutputStream;  
 import java.io.UnsupportedEncodingException;  
 import com.edentiti.registrations.verification.ProgressBean;  

 public class StringEntity extends org.apache.http.entity.StringEntity  
 {  
      private ProgressBean _progressBean;  
      public StringEntity(String string, ProgressBean progressBean) throws UnsupportedEncodingException  
      {  
           super(string);  
           _progressBean = progressBean;  
      }  
      private OutputStreamProgress outstream;  
      @Override  
      public void writeTo(OutputStream outstream) throws IOException  
      {  
           this.outstream = new OutputStreamProgress(outstream, _progressBean, getContentLength());  
           super.writeTo(this.outstream);  
      }  
 }  



My own StringEntity.java, for my own purpose, the progress goes up to 70% here and you can adjust it(e.g. up to 100%) to suit your own purpose.

 import java.io.IOException;  
 import java.io.OutputStream;  
 import com.edentiti.registrations.verification.ProgressBean;  

 public class OutputStreamProgress extends OutputStream {  
      private final static int CHUNK_SIZE = 40960; // Ming feels it is a lucky number, no solid reason for it.  
      private ProgressBean _progressBean;  
      private long _contentLength;  
   private final OutputStream outstream;  
   private volatile long bytesWritten=0;  
      public OutputStreamProgress(OutputStream outstream, ProgressBean progressBean, long contentLength)  
      {  
           this._progressBean = progressBean;  
           this._contentLength = contentLength;  
     this.outstream = outstream;  
   }  
   @Override  
   public void write(int b) throws IOException {  
     outstream.write(b);  
     bytesWritten++;  
   }  
   @Override  
   public void write(byte[] b) throws IOException {  
           int numChunks;  
           if (b.length % CHUNK_SIZE == 0)  
           {  
                numChunks = b.length / CHUNK_SIZE;  
           }  
           else  
           {  
                numChunks = (b.length / CHUNK_SIZE) + 1;  
           }  
           for (int i = 0; i <= numChunks; i++)  
           {  
                // if last block  
                if (i == numChunks)  
                {  
                     write(b, i * CHUNK_SIZE, b.length);  
                }  
                else  
                {  
                     write(b, i * CHUNK_SIZE, CHUNK_SIZE);  
                }  
           }  
   }  
   @Override  
   public void write(byte[] b, int off, int len) throws IOException {  
     outstream.write(b, off, len);  
     bytesWritten += len;  
           long progressValue = (long) ((70 * getWrittenLength()) / _contentLength);  
           if (progressValue > 70)  
           {  
                progressValue = 70;  
           }  
           if (progressValue < 1)  
           {  
                progressValue = 1;  
           }  
           // here we only allow something between 1 to 70  
           _progressBean.setValue(Long.valueOf(progressValue));  
   }  
   @Override  
   public void flush() throws IOException {  
     outstream.flush();  
   }  
   @Override  
   public void close() throws IOException {  
     outstream.close();  
   }  
   public long getWrittenLength() {  
     return bytesWritten;  
   }  
 }  


References:

  1. http://stackoverflow.com/questions/7057342/how-to-get-a-progress-bar-for-a-file-upload-with-apache-httpclient-4

Sunday 1 February 2015

seam richfaces progress bar

What I want to achieve here is:


After the end user clicks on 'confirm and submit' button, the server processes the input which could take minutes. While the end user is waiting, we show a richfaces modal with a progress bar to indicate the progress. When the processing is done, server redirects end user to the processing result URL.

 <h:form id="form-passportupload">  
           <a4j:commandButton value="confirm and submit" action="#{progressBarVerifier.updateDocumentAsync()}" oncomplete="#{progressBarVerifier.isPageValidated}?Richfaces.showModalPanel('progressBarModal'):Richfaces.hideModalPanel('progressBarModal');"  
                          ignoreDupResponses="true" ajaxSingle="true" reRender="progressBarPanel,fileupload-error,fileupload-terms-confirm">  
           </a4j:commandButton>  
 </h:form>  


      <rich:modalPanel id="progressBarModal">  
           <h:form>  
                <a4j:outputPanel id="progressBarPanel">  
                Please wait while we check your uploaded document. This may take up to 5 minutes.  
                <rich:progressBar value="#{progressBarVerifier.progressBean.value}" label="#{progressBarVerifier.progressBean.value} %" minValue="0" maxValue="100" timeout="300000" interval="3000" enabled="#{progressBarVerifier.progressBean.enabled}" >  
                  <f:facet name="initial">  
                    <h:outputText value="Processing" />  
                  </f:facet>  
                  <f:facet name="complete">  
                    <h:outputText value="Process completed">  
                         #{redirectHelper.redirectToVerifySinglePage()}  
                    </h:outputText>  
                  </f:facet>  
                </rich:progressBar>  
                </a4j:outputPanel>  
           </h:form>  
      </rich:modalPanel>  



ProgressBean looks like this. Please note the value is updated in updateDocumentAsync() method. At the back end, we use Httpclient to do the POST and update ProgressBean.value in the process.


 public class ProgressBean  
 {  
      private boolean _enabled = false;  
      public boolean isEnabled()  
      {  
           return _enabled;  
      }  
      public void setEnabled(boolean enabled)  
      {  
           _enabled = enabled;  
      }  
      private Long value = new Long(0);  
      public Long getValue()  
      {  
           return value;  
      }  
      public void setValue(final Long value)  
      {  
           this.value = value;  
      }  
 }  


asyncUpload() method has to be Seam @Asynchronous and you can do server end validation before it. Otherwise, progress bar will not start to update until updateDocumentAsync() has finished.



 public void updateDocumentAsync() throws Exception  
 {  
   _validator.doValidation();  
   _asyncUploader.asyncUpload();  
 }  




Please see attached screen shot below for the results.





References:
  1. https://achorniy.wordpress.com/2010/10/22/show-dynamic-process-progress-in-seam-richfaces/ 
  2. http://forkbomb-blog.de/2011/time-consuming-processes-with-seam-richfaces-a4jpoll-and-quartz 
  3. http://docs.jboss.org/richfaces/latest_3_3_X/en/devguide/html/rich_progressBar.html