Coverage Report - wjhk.jupload2.upload.helper.ProgressBarManager
 
Classes in this File Line Coverage Branch Coverage Complexity
ProgressBarManager
47 %
68/142
17 %
7/39
2,812
 
 1  
 //
 2  
 // $Id$
 3  
 //
 4  
 // jupload - A file upload applet.
 5  
 //
 6  
 // Copyright 2010 The JUpload Team
 7  
 //
 8  
 // Created: 10 fevr. 2010
 9  
 // Creator: etienne_sf
 10  
 // Last modified: $Date$
 11  
 //
 12  
 // This program is free software; you can redistribute it and/or modify
 13  
 // it under the terms of the GNU General Public License as published by
 14  
 // the Free Software Foundation; either version 2 of the License, or
 15  
 // (at your option) any later version.
 16  
 //
 17  
 // This program is distributed in the hope that it will be useful,
 18  
 // but WITHOUT ANY WARRANTY; without even the implied warranty of
 19  
 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 20  
 // GNU General Public License for more details.
 21  
 //
 22  
 // You should have received a copy of the GNU General Public License
 23  
 // along with this program; if not, write to the Free Software
 24  
 // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 25  
 
 26  
 package wjhk.jupload2.upload.helper;
 27  
 
 28  
 import java.awt.event.ActionEvent;
 29  
 import java.awt.event.ActionListener;
 30  
 
 31  
 import javax.swing.JProgressBar;
 32  
 import javax.swing.Timer;
 33  
 
 34  
 import wjhk.jupload2.exception.JUploadException;
 35  
 import wjhk.jupload2.gui.JUploadPanel;
 36  
 import wjhk.jupload2.gui.filepanel.SizeRenderer;
 37  
 import wjhk.jupload2.policies.UploadPolicy;
 38  
 import wjhk.jupload2.upload.FilePreparationThread;
 39  
 import wjhk.jupload2.upload.FileUploadManagerThread;
 40  
 import wjhk.jupload2.upload.UploadFileData;
 41  
 import wjhk.jupload2.upload.UploadFilePacket;
 42  
 
 43  
 /**
 44  
  * @author etienne_sf
 45  
  */
 46  
 public class ProgressBarManager implements ActionListener {
 47  
     /**
 48  
      * The delay between to updates of the progress bar, in ms.
 49  
      */
 50  
     public final static int DELAY_FOR_UPDATE_OF_PROGRESS_BAR = 100;
 51  
 
 52  
     /**
 53  
      * Contains the date/time (as a long) of the start of the current upload. This allows to sum the time of the actual
 54  
      * upload, and ignore the time the applet is waiting for the server's response. Once the request is finished, and
 55  
      * the applet waits for the server's response, the duration of the sending to the server is added to
 56  
      * currentRequestStartTime, and currentRequestStartTime is reseted to 0. It's then ready for the next upload
 57  
      * request.
 58  
      */
 59  6
     long currentRequestStartTime = 0;
 60  
 
 61  
     /**
 62  
      * The file that is currently being uploaded. Allow to refresh the progress bar, with up to date information, based
 63  
      * on a timer event.
 64  
      */
 65  6
     UploadFileData currentUploadFileData = null;
 66  
 
 67  
     /**
 68  
      * The files which is currently being sent to the server.
 69  
      */
 70  6
     UploadFilePacket currentUploadFilePacket = null;
 71  
 
 72  
     /**
 73  
      * The file preparatoin thread prepares each file for upload, and manage possible errors that can occurs at
 74  
      * preparation time.
 75  
      */
 76  6
     FilePreparationThread filePreparationThread = null;
 77  
 
 78  
     /**
 79  
      * Contains the system time of the start of the global upload. This is used to calculate the ETA, and display it to
 80  
      * the user, on the status bar.
 81  
      */
 82  6
     long globalStartTime = 0;
 83  
 
 84  
     /**
 85  
      * Indicated the number of bytes that have currently been sent for the current file. This allows the management of
 86  
      * the progress bar.
 87  
      */
 88  6
     long nbBytesUploadedForCurrentFile = 0;
 89  
 
 90  
     /**
 91  
      * Number of files that have already been sent. The control on the upload success may be done or not. It's used to
 92  
      * properly display the progress bar.
 93  
      */
 94  6
     int nbSentFiles = 0;
 95  
 
 96  
     /** Current number of bytes that have been uploaded. */
 97  6
     long nbUploadedBytes = 0;
 98  
 
 99  
     /**
 100  
      * The {@link JUploadPanel} progress bar, to follow the file preparation progress.
 101  
      */
 102  6
     JProgressBar preparationProgressBar = null;
 103  
 
 104  
     /**
 105  
      * The timer which schedules the update for the progress and status bar.
 106  
      */
 107  
     Timer timer;
 108  
 
 109  
     /**
 110  
      * Contains the sum of the upload duration for all requests, in milliseconds. For instance, if sending in 10 chunks
 111  
      * one big file, the uploadDuration contains the sum of the sending of these 10 request to the server. This allows
 112  
      * to calculate the true upload speed, and ignore the time we'll wait for the server's response.
 113  
      */
 114  6
     long totalUploadDuration = 0;
 115  
 
 116  
     /** The current upload policy */
 117  
     UploadPolicy uploadPolicy;
 118  
 
 119  
     /**
 120  
      * The {@link JUploadPanel} progress bar, to follow the upload of the prepared files to the server.
 121  
      */
 122  6
     JProgressBar uploadProgressBar = null;
 123  
 
 124  
     /**
 125  
      * Indicates what is the current file being uploaded, and its upload status.
 126  
      */
 127  6
     int uploadStatus = FileUploadManagerThread.UPLOAD_STATUS_NOT_STARTED;
 128  
 
 129  
     /**
 130  
      * @param uploadPolicy
 131  
      * @param filePreparationThread
 132  
      */
 133  6
     public ProgressBarManager(UploadPolicy uploadPolicy, FilePreparationThread filePreparationThread) {
 134  6
         this.uploadPolicy = uploadPolicy;
 135  6
         this.filePreparationThread = filePreparationThread;
 136  
         // our timer is a daemon.
 137  6
         this.timer = new Timer(DELAY_FOR_UPDATE_OF_PROGRESS_BAR, this);
 138  
 
 139  6
         JUploadPanel uploadPanel = uploadPolicy.getContext().getUploadPanel();
 140  
 
 141  6
         this.uploadProgressBar = uploadPanel.getUploadProgressBar();
 142  6
         this.preparationProgressBar = uploadPanel.getPreparationProgressBar();
 143  
 
 144  6
         updateUploadProgressBarText(null);
 145  6
     }
 146  
 
 147  
     /**
 148  
      * The only event managed here is the timer event. We update the progress and status bar.
 149  
      * 
 150  
      * @see java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent)
 151  
      */
 152  
     public void actionPerformed(ActionEvent arg0) {
 153  0
         updateUploadProgressBarValue();
 154  0
         updateUploadStatusBar();
 155  
 
 156  0
     }
 157  
 
 158  
     /**
 159  
      * Called when a new file is uploaded. This method update the bars accordingly to this new status, by calling the
 160  
      * updateUploadProgressBarText() method.
 161  
      * 
 162  
      * @param uploadFilePacket
 163  
      * @param uploadFileData
 164  
      * @throws JUploadException
 165  
      * @see wjhk.jupload2.upload.FileUploadManagerThread#anotherFileHasBeenSent(wjhk.jupload2.upload.UploadFilePacket,
 166  
      *      wjhk.jupload2.upload.UploadFileData)
 167  
      */
 168  
     public synchronized void anotherFileHasBeenSent(UploadFilePacket uploadFilePacket, UploadFileData uploadFileData)
 169  
             throws JUploadException {
 170  0
         if (uploadFilePacket != this.currentUploadFilePacket) {
 171  0
             throw new java.lang.AssertionError("Wrong file packet in " + this.getClass().getName()
 172  
                     + ".anotherFileHasBeenSent()");
 173  
         }
 174  0
         if (uploadFileData != this.currentUploadFileData) {
 175  0
             throw new java.lang.AssertionError("Wrong file packet in " + this.getClass().getName()
 176  
                     + ".anotherFileHasBeenSent()");
 177  
         }
 178  0
         this.nbSentFiles += 1;
 179  0
         this.nbBytesUploadedForCurrentFile = 0;
 180  0
         this.uploadPolicy.displayDebug(this.getClass().getName()
 181  
                 + ".anotherFileHasBeenSent(): before call to newlyUploadedFileData.getUploadLength()", 100);
 182  
 
 183  
         // We are finished with this one. Let's display it.
 184  0
         this.uploadStatus = FileUploadManagerThread.UPLOAD_STATUS_UPLOADED;
 185  0
         updateUploadProgressBarText(uploadFilePacket);
 186  0
     }
 187  
 
 188  
     /**
 189  
      * Clean all bar content.
 190  
      */
 191  
     public void clearBarContent() {
 192  
         // Let's stop the update process.
 193  3
         this.timer.stop();
 194  
 
 195  3
         this.preparationProgressBar.setValue(0);
 196  3
         this.preparationProgressBar.setString("");
 197  3
         this.uploadProgressBar.setValue(0);
 198  3
         this.uploadProgressBar.setString("");
 199  
 
 200  3
     }
 201  
 
 202  
     /**
 203  
      * @return the globalStartTime
 204  
      */
 205  
     public long getGlobalStartTime() {
 206  3
         return this.globalStartTime;
 207  
     }
 208  
 
 209  
     /**
 210  
      * @return the nbUploadedBytes
 211  
      */
 212  
     public long getNbUploadedBytes() {
 213  0
         return this.nbUploadedBytes;
 214  
     }
 215  
 
 216  
     /**
 217  
      * @return the uploadDuration
 218  
      */
 219  
     public long getUploadDuration() {
 220  3
         long currentRequestDuration = 0;
 221  3
         if (this.currentRequestStartTime != 0) {
 222  
             // A request is running on
 223  0
             currentRequestDuration = System.currentTimeMillis() - this.currentRequestStartTime;
 224  
         }
 225  
 
 226  3
         return this.totalUploadDuration + currentRequestDuration;
 227  
     }
 228  
 
 229  
     /**
 230  
      * Initialize the maximum value for the two progress bar: 100*the number of files to upload.
 231  
      * 
 232  
      * @throws JUploadException
 233  
      * @see #updateUploadProgressBar(UploadFilePacket, UploadFileData)
 234  
      */
 235  
     private void initProgressBar() throws JUploadException {
 236  
         // To follow the state of file preparation
 237  3
         this.preparationProgressBar.setMaximum(100 * this.filePreparationThread.getNbFilesToSend());
 238  3
         this.preparationProgressBar.setString("");
 239  
 
 240  
         // To follow the state of the actual upload.
 241  3
         this.uploadProgressBar.setMaximum(100 * filePreparationThread.getNbFilesToSend());
 242  3
         this.uploadProgressBar.setString("");
 243  3
     }
 244  
 
 245  
     /**
 246  
      * The progressBar is updated each 50ms and each 10% of the target file.
 247  
      * 
 248  
      * @param nbBytes
 249  
      * @param uploadFileData
 250  
      * @throws JUploadException
 251  
      * @see wjhk.jupload2.upload.FileUploadManagerThread#nbBytesUploaded(long, UploadFileData)
 252  
      */
 253  
     public synchronized void nbBytesUploaded(long nbBytes, UploadFileData uploadFileData) throws JUploadException {
 254  0
         this.nbUploadedBytes += nbBytes;
 255  0
         this.nbBytesUploadedForCurrentFile += nbBytes;
 256  0
     }
 257  
 
 258  
     /**
 259  
      * Set an error text, that will be displayed on the progress bar
 260  
      * 
 261  
      * @param errorTexte
 262  
      */
 263  
     public void setErrorMessage(String errorTexte) {
 264  0
         this.preparationProgressBar.setString(errorTexte);
 265  0
     }
 266  
 
 267  
     /**
 268  
      * @param uploadFilePacket
 269  
      * @param uploadFileData
 270  
      * @param uploadStatus
 271  
      * @throws JUploadException
 272  
      */
 273  
     public synchronized void setUploadStatus(UploadFilePacket uploadFilePacket, UploadFileData uploadFileData,
 274  
             int uploadStatus) throws JUploadException {
 275  
         // Let's store the file we're working on.
 276  1
         this.currentUploadFileData = uploadFileData;
 277  1
         this.currentUploadFilePacket = uploadFilePacket;
 278  
 
 279  1
         switch (uploadStatus) {
 280  
             case FileUploadManagerThread.UPLOAD_STATUS_CHUNK_UPLOADED_WAITING_FOR_RESPONSE:
 281  
             case FileUploadManagerThread.UPLOAD_STATUS_FILE_UPLOADED_WAITING_FOR_RESPONSE:
 282  
                 // We're waiting for the server: let's add it to the sending
 283  
                 // duration.
 284  0
                 this.totalUploadDuration += System.currentTimeMillis() - this.currentRequestStartTime;
 285  0
                 this.currentRequestStartTime = 0;
 286  0
                 break;
 287  
             case FileUploadManagerThread.UPLOAD_STATUS_UPLOADING:
 288  
                 // We mark the start of the request, if it was not already done.
 289  0
                 if (this.currentRequestStartTime == 0) {
 290  0
                     this.currentRequestStartTime = System.currentTimeMillis();
 291  
                 }
 292  
                 break;
 293  
             case FileUploadManagerThread.UPLOAD_STATUS_UPLOADED:
 294  
                 // Indicated that the current request is finished. Nothing to do
 295  1
                 break;
 296  
             default:
 297  0
                 this.uploadPolicy.displayWarn("Unknown value for uploadStatus: " + uploadStatus);
 298  
         }
 299  1
         this.uploadStatus = uploadStatus;
 300  
 
 301  1
         this.updateUploadProgressBarText(uploadFilePacket);
 302  1
     }
 303  
 
 304  
     /**
 305  
      * Update the progress bar, based on the following data: <DIR> <LI>nbSentFiles: number of files that have already
 306  
      * been updated. <LI>nbBytesUploadedForCurrentFile: allows calculation of the upload progress for the current file,
 307  
      * based on it total upload length. </DIR> <BR>
 308  
      * Note 1: The progress bar update is ignored, if last update was less than 100ms before.<BR>
 309  
      * Note 2: This method calls the {@link #updateUploadProgressBarValue(UploadFileData)} method, to also update its
 310  
      * value.
 311  
      * 
 312  
      * @throws JUploadException
 313  
      */
 314  
     private void updateUploadProgressBarText(UploadFilePacket uploadFilePacket) {
 315  
         /*
 316  
          * final String msgInfoUploaded = this.uploadPolicy .getLocalizedString("infoUploaded"); final String
 317  
          * msgInfoUploading = this.uploadPolicy .getLocalizedString("infoUploading"); final String msgNbUploadedFiles =
 318  
          * this.uploadPolicy .getLocalizedString("nbUploadedFiles");
 319  
          */
 320  10
         updateUploadProgressBarValue();
 321  
 
 322  10
         String msg = null;
 323  10
         switch (this.uploadStatus) {
 324  
             case FileUploadManagerThread.UPLOAD_STATUS_NOT_STARTED:
 325  9
                 msg = "";
 326  9
                 break;
 327  
             case FileUploadManagerThread.UPLOAD_STATUS_UPLOADING:
 328  
             case FileUploadManagerThread.UPLOAD_STATUS_CHUNK_UPLOADED_WAITING_FOR_RESPONSE:
 329  
                 // Uploading files %1$s
 330  0
                 msg = this.uploadPolicy.getLocalizedString("infoUploading", (this.nbSentFiles + 1));
 331  0
                 break;
 332  
             case FileUploadManagerThread.UPLOAD_STATUS_FILE_UPLOADED_WAITING_FOR_RESPONSE:
 333  
                 // %1$s file(s) uploaded. Waiting for server response ...
 334  
 
 335  
                 // nbSentFiles it number of files whose data is already sent to the server. This include the
 336  
                 // currentUploadFileData (which should be the last file in the packet)
 337  0
                 int firstFileInPacket = this.nbSentFiles - uploadFilePacket.size() + 1;
 338  0
                 int currentFile = this.nbSentFiles;
 339  
 
 340  0
                 if (this.currentUploadFilePacket.size() == 1) {
 341  0
                     msg = currentFile + "/" + this.filePreparationThread.getNbFilesToSend();
 342  
                 } else {
 343  0
                     msg = firstFileInPacket + "-" + currentFile + "/" + this.filePreparationThread.getNbFilesToSend();
 344  
                 }
 345  0
                 msg = this.uploadPolicy.getLocalizedString("infoUploaded", msg);
 346  
 
 347  0
                 break;
 348  
             case FileUploadManagerThread.UPLOAD_STATUS_UPLOADED:
 349  
                 // %1$d file(s) uploaded
 350  1
                 msg = this.uploadPolicy.getLocalizedString("nbUploadedFiles", (this.nbSentFiles));
 351  1
                 break;
 352  
             default:
 353  
                 // Hum, that's strange !
 354  0
                 this.uploadPolicy
 355  0
                         .displayWarn("Unknown upload status in FileUploadManagerThreadImpl.updateProgressBar(): "
 356  
                                 + this.uploadStatus);
 357  
         }
 358  
 
 359  
         // Let's show the modifications to the user
 360  10
         this.uploadProgressBar.setString(msg);
 361  
         // To be sure that the new text is displayed, we force instantaneous
 362  
         // refresh. This won't slow down the upload, as it's done in separate
 363  
         // thread.
 364  10
         this.uploadProgressBar.repaint(0);
 365  10
     }
 366  
 
 367  
     /**
 368  
      * Update the progress bar value, that is: the percent of upload of the current file. This is based on
 369  
      * nbBytesUploadedForCurrentFile and the total upload length of the current file.<BR>
 370  
      * Note: The progress bar update is ignored, if last update was less than 100ms before.
 371  
      * 
 372  
      * @throws JUploadException
 373  
      */
 374  
     private void updateUploadProgressBarValue() {
 375  
         /*
 376  
          * final String msgInfoUploaded = this.uploadPolicy .getLocalizedString("infoUploaded"); final String
 377  
          * msgInfoUploading = this.uploadPolicy .getLocalizedString("infoUploading"); final String msgNbUploadedFiles =
 378  
          * this.uploadPolicy .getLocalizedString("nbUploadedFiles");
 379  
          */
 380  10
         int percent = 0;
 381  
 
 382  
         // First, we update the bar itself.
 383  10
         if (this.nbBytesUploadedForCurrentFile == 0
 384  0
                 || this.nbSentFiles == this.filePreparationThread.getNbFilesToSend()) {
 385  10
             percent = 0;
 386  0
         } else if (this.currentUploadFileData == null) {
 387  0
             percent = 0;
 388  
         } else {
 389  0
             if (this.currentUploadFileData.isPreparedForUpload()) {
 390  0
                 percent = (int) (this.nbBytesUploadedForCurrentFile * 100 / this.currentUploadFileData
 391  0
                         .getUploadLength());
 392  
             } else {
 393  
                 // Hum, hum. The file is not prepared for upload yet. So we
 394  
                 // actually didn't send any thing for it.
 395  0
                 percent = 0;
 396  
             }
 397  
             // Usually, a percentage if advancement for one file is no more than
 398  
             // 100. Let's check that.
 399  0
             if (percent > 100) {
 400  0
                 this.uploadPolicy.displayWarn("percent is more than 100 (" + percent
 401  
                         + ") in FileUploadManagerThreadImpl.update.UploadProgressBar");
 402  0
                 percent = 100;
 403  
             }
 404  
         }
 405  
 
 406  10
         this.uploadProgressBar.setValue(100 * this.nbSentFiles + percent);
 407  10
     }
 408  
 
 409  
     /**
 410  
      * Displays the current upload speed on the status bar.
 411  
      */
 412  
     private void updateUploadStatusBar() {
 413  
         // We'll update the status bar, only if it exists and if the upload
 414  
         // actually started.
 415  3
         if (null != this.uploadPolicy.getContext().getUploadPanel().getStatusLabel() && this.nbUploadedBytes > 0) {
 416  
             double percent;
 417  
             // uploadCPS: contains the upload speed.
 418  
             double uploadSpeed;
 419  
             // globalCPS: contains the average speed, including the time the
 420  
             // applet is waiting for the server response.
 421  
             double globalCPS;
 422  
             long remaining;
 423  
             String eta;
 424  
 
 425  
             try {
 426  0
                 percent = 100.0 * this.nbUploadedBytes / this.filePreparationThread.getTotalFileBytesToSend();
 427  
 
 428  0
             } catch (ArithmeticException e1) {
 429  0
                 percent = 100;
 430  0
             }
 431  
 
 432  
             // Calculation of the 'pure' upload speed.
 433  0
             uploadSpeed = ((double) this.nbUploadedBytes) / ((double) getUploadDuration() / 1000);
 434  0
             if (uploadSpeed == Double.POSITIVE_INFINITY) {
 435  0
                 this.uploadPolicy.displayDebug("uploadSpeed is Infinity, for nbUploadedBytes=" + nbUploadedBytes
 436  0
                         + " and actualUploadDuration(ms)=" + getUploadDuration(), 80);
 437  
             }
 438  
 
 439  
             // Calculation of the 'global' upload speed.
 440  
             try {
 441  0
                 globalCPS = ((double) this.nbUploadedBytes) / (System.currentTimeMillis() - this.globalStartTime)
 442  
                         * 1000;
 443  0
             } catch (ArithmeticException e1) {
 444  0
                 globalCPS = this.nbUploadedBytes;
 445  0
             }
 446  
 
 447  
             // Calculation of the ETA. It's based on the global upload speed.
 448  
             try {
 449  0
                 remaining = (long) ((this.filePreparationThread.getTotalFileBytesToSend() - this.nbUploadedBytes) / globalCPS);
 450  0
                 if (remaining > 3600) {
 451  0
                     eta = this.uploadPolicy.getLocalizedString("timefmt_hms", Long.valueOf(remaining / 3600),
 452  0
                             Long.valueOf((remaining / 60) % 60), Long.valueOf(remaining % 60));
 453  0
                 } else if (remaining > 60) {
 454  0
                     eta = this.uploadPolicy.getLocalizedString("timefmt_ms", Long.valueOf(remaining / 60),
 455  0
                             Long.valueOf(remaining % 60));
 456  
                 } else
 457  0
                     eta = this.uploadPolicy.getLocalizedString("timefmt_s", Long.valueOf(remaining));
 458  0
             } catch (ArithmeticException e1) {
 459  0
                 eta = this.uploadPolicy.getLocalizedString("timefmt_unknown");
 460  0
             }
 461  0
             String status = this.uploadPolicy.getLocalizedString("status_msg", Integer.valueOf((int) percent),
 462  0
                     SizeRenderer.formatFileUploadSpeed(uploadSpeed, this.uploadPolicy), eta);
 463  0
             this.uploadPolicy.getContext().getUploadPanel().getStatusLabel().setText(status);
 464  
             // this.uploadPanel.getStatusLabel().repaint();
 465  0
             this.uploadPolicy.getContext().showStatus(status);
 466  
             // this.uploadPolicy.displayDebug("[updateUploadStatusBar] " +
 467  
             // status, 101);
 468  
         }
 469  3
     }
 470  
 
 471  
     /**
 472  
      * This just stops the timer. A 'last' update is done.
 473  
      */
 474  
     public void uploadIsFinished() {
 475  
         // Let's stop the update process.
 476  3
         this.timer.stop();
 477  
 
 478  3
         updateUploadProgressBarText(null);
 479  3
         updateUploadStatusBar();
 480  3
     }
 481  
 
 482  
     /**
 483  
      * @throws JUploadException
 484  
      */
 485  
     public void uploadIsStarted() throws JUploadException {
 486  
         // Ok, the upload just starts. We keep the date, to later calculate the
 487  
         // ETA.
 488  3
         this.globalStartTime = System.currentTimeMillis();
 489  3
         initProgressBar();
 490  
 
 491  
         // Let's start the update process.
 492  3
         this.timer.start();
 493  3
     }
 494  
 
 495  
 }