Coverage Report - wjhk.jupload2.upload.FileUploadManagerThreadImpl
 
Classes in this File Line Coverage Branch Coverage Complexity
FileUploadManagerThreadImpl
0 %
0/130
0 %
0/60
3,357
 
 1  
 package wjhk.jupload2.upload;
 2  
 
 3  
 import java.util.concurrent.ArrayBlockingQueue;
 4  
 import java.util.concurrent.BlockingQueue;
 5  
 
 6  
 import wjhk.jupload2.exception.JUploadEOFException;
 7  
 import wjhk.jupload2.exception.JUploadException;
 8  
 import wjhk.jupload2.filedata.FileData;
 9  
 import wjhk.jupload2.gui.JUploadPanel;
 10  
 import wjhk.jupload2.gui.filepanel.FilePanel;
 11  
 import wjhk.jupload2.policies.UploadPolicy;
 12  
 import wjhk.jupload2.upload.helper.ProgressBarManager;
 13  
 
 14  
 /**
 15  
  * This class is responsible for managing the upload. At the end of the upload, the
 16  
  * {@link JUploadPanel#updateButtonState()} is called, to refresh the button state. Its job is to: <DIR> <LI>Prepare
 17  
  * upload for the file (calls to {@link FileData#beforeUpload()} for each file in the file list. <LI>Create the thread
 18  
  * to send a packet of files. <LI>Prepare the packets, that will be red by the upload thread. <LI>Manage the end of
 19  
  * upload: trigger the call to {@link JUploadPanel#updateButtonState()} and the call to
 20  
  * {@link UploadPolicy#afterUpload(Exception, String)}. <LI>Manage the 'stop' button reaction. </DIR> This class is
 21  
  * created by {@link JUploadPanel}, when the user clicks on the upload button.
 22  
  * 
 23  
  * @author etienne_sf
 24  
  */
 25  
 public class FileUploadManagerThreadImpl extends Thread implements FileUploadManagerThread {
 26  
 
 27  
     /**
 28  
      * Maximum number of files that can be stored in the filePreparationQueue. It's useless to have a too big value
 29  
      * here, as, if too many files are there, it means that the file preparation process is much quicker than the file
 30  
      * upload one.
 31  
      */
 32  
     final static int FILE_PREPARATION_QUEUE_SIZE = 50;
 33  
 
 34  
     // /////////////////////////////////////////////////////////////////////////////////////////
 35  
     // //////////////////// Possible Status for file upload
 36  
     // /////////////////////////////////////////////////////////////////////////////////////////
 37  
 
 38  
     /** The current file list. */
 39  0
     FilePanel filePanel = null;
 40  
 
 41  
     /**
 42  
      * The file preparatoin thread prepares each file for upload, and manage possible errors that can occurs at
 43  
      * preparation time.
 44  
      */
 45  0
     FilePreparationThread filePreparationThread = null;
 46  
 
 47  
     /**
 48  
      * This thread receives each prepared files in a queue, constructs the packets of files to be sent together, and
 49  
      * posts them in another queue.
 50  
      */
 51  0
     PacketConstructionThread packetConstructionThread = null;
 52  
 
 53  
     /**
 54  
      * The upload thread, that will wait for the next file packet to be ready, then send it.
 55  
      */
 56  0
     FileUploadThread fileUploadThread = null;
 57  
 
 58  
     /**
 59  
      * This help controls the display of the progress bar and the status bar. It updates these bar, based on a timer.
 60  
      */
 61  
     ProgressBarManager progressBarManager;
 62  
 
 63  
     /**
 64  
      * Number of files that have been successfully uploaded. already been sent. The control on the upload success may be
 65  
      * done or not. It's used to properly display the progress bar.
 66  
      */
 67  0
     int nbSuccessfullyUploadedFiles = 0;
 68  
 
 69  
     /**
 70  
      * Indicates whether the upload is finished or not. Passed to true as soon as one of these conditions becomes true:
 71  
      * <DIR> <LI>All files are uploaded (in the {@link #currentRequestIsFinished(UploadFilePacket)} method) <LI>An
 72  
      * exception occurs (in the {@link #setUploadException(JUploadException)} method) <LI>The user stops the upload (in
 73  
      * the {@link #stopUpload()} method) </DIR>
 74  
      */
 75  0
     boolean uploadFinished = false;
 76  
 
 77  
     /**
 78  
      * If set to 'true', the thread will stop the current upload.
 79  
      * 
 80  
      * @see UploadFileData#uploadFile(java.io.OutputStream, long)
 81  
      */
 82  0
     boolean stop = false;
 83  
 
 84  
     /** Thread Exception, if any occurred during upload. */
 85  0
     JUploadException uploadException = null;
 86  
 
 87  
     /** A shortcut to the upload panel */
 88  0
     JUploadPanel uploadPanel = null;
 89  
 
 90  
     /** The current upload policy. */
 91  0
     UploadPolicy uploadPolicy = null;
 92  
 
 93  
     // ////////////////////////////////////////////////////////////////////////////
 94  
     // To follow the upload speed.
 95  
     // ////////////////////////////////////////////////////////////////////////////
 96  
 
 97  
     /**
 98  
      * Standard constructor of the class.
 99  
      * 
 100  
      * @param uploadPolicy
 101  
      * @throws JUploadException
 102  
      */
 103  
     public FileUploadManagerThreadImpl(UploadPolicy uploadPolicy) throws JUploadException {
 104  0
         super("FileUploadManagerThreadImpl thread");
 105  0
         constructor(uploadPolicy, null);
 106  0
     }
 107  
 
 108  
     /**
 109  
      * Internal constructor. It is used by the JUnit test, to create a FileUploadManagerThreadImpl instance, based on a
 110  
      * non-active {@link FileUploadThread}.
 111  
      * 
 112  
      * @param uploadPolicy The current uploadPolicy
 113  
      * @param fileUploadThreadParam The instance of {@link FileUploadThread} that should be used. Allows execution of
 114  
      *            unit tests, based on a specific FileUploadThread, that does ... nothing.
 115  
      * @throws JUploadException
 116  
      */
 117  
     FileUploadManagerThreadImpl(UploadPolicy uploadPolicy, FileUploadThread fileUploadThreadParam)
 118  
             throws JUploadException {
 119  0
         super("FileUploadManagerThreadImpl test thread");
 120  0
         constructor(uploadPolicy, fileUploadThreadParam);
 121  0
     }
 122  
 
 123  
     /**
 124  
      * Called by the class constructors, to initialize the current instance.
 125  
      * 
 126  
      * @param uploadPolicy
 127  
      * @param fileUploadThreadParam
 128  
      * @throws JUploadException
 129  
      */
 130  
     private synchronized void constructor(UploadPolicy uploadPolicy, FileUploadThread fileUploadThreadParam)
 131  
             throws JUploadException {
 132  
 
 133  
         // General shortcuts on the current applet.
 134  0
         this.uploadPolicy = uploadPolicy;
 135  0
         this.uploadPanel = uploadPolicy.getContext().getUploadPanel();
 136  0
         this.filePanel = this.uploadPanel.getFilePanel();
 137  
 
 138  0
         BlockingQueue<UploadFileData> preparedFileQueue = new ArrayBlockingQueue<UploadFileData>(
 139  0
                 this.filePanel.getFilesLength());
 140  
 
 141  
         // If the FileUploadThread was already created, we must take the same
 142  
         // packetQueue.
 143  
         BlockingQueue<UploadFilePacket> packetQueue;
 144  0
         if (fileUploadThreadParam == null) {
 145  0
             packetQueue = new ArrayBlockingQueue<UploadFilePacket>(this.filePanel.getFilesLength());
 146  
         } else {
 147  0
             packetQueue = fileUploadThreadParam.getPacketQueue();
 148  
         }
 149  
         // Let's create (but not start) start the file preparation thread.
 150  0
         this.filePreparationThread = new FilePreparationThread(preparedFileQueue, this, this.uploadPolicy);
 151  
         // The packet tread groups files together, depending on the current
 152  
         // upload policy.
 153  0
         this.packetConstructionThread = new PacketConstructionThread(preparedFileQueue, packetQueue, this,
 154  
                 this.uploadPolicy);
 155  
         // Let's start the upload thread. It will wait until the first
 156  
         // packet is ready.
 157  0
         createUploadThread(packetQueue, fileUploadThreadParam);
 158  
         // We're now ready to start the bar update job.
 159  0
         this.progressBarManager = new ProgressBarManager(this.uploadPolicy, this.filePreparationThread);
 160  0
     }
 161  
 
 162  
     /**
 163  
      * @see wjhk.jupload2.upload.FileUploadManagerThread#run()
 164  
      */
 165  
     @Override
 166  
     final public void run() {
 167  
         try {
 168  0
             this.uploadPolicy.displayDebug("Start of the FileUploadManagerThreadImpl", 5);
 169  
 
 170  
             // Let's prepare the progress bar, to display the current upload
 171  
             // stage.
 172  0
             progressBarManager.uploadIsStarted();
 173  
 
 174  
             // Let's start the working threads.
 175  0
             this.filePreparationThread.start();
 176  0
             this.packetConstructionThread.start();
 177  0
             this.fileUploadThread.start();
 178  
 
 179  
             // Let's let the current upload policy have any preparation work
 180  0
             this.uploadPolicy.beforeUpload();
 181  
 
 182  
             // The upload is started. Let's change the button state.
 183  0
             this.uploadPanel.updateButtonState();
 184  
 
 185  
             // The thread upload may need some information about the current
 186  
             // one, like ... knowing that upload is actually finished (no more
 187  
             // file to send).
 188  
             // So we wait for it to finish.
 189  0
             while (this.fileUploadThread.isAlive() && !isUploadFinished()) {
 190  
                 try {
 191  0
                     this.uploadPolicy.displayDebug("Waiting for fileUploadThread to die", 10);
 192  0
                     this.fileUploadThread.join();
 193  0
                 } catch (InterruptedException e) {
 194  
                     // This should not occur, and should not be a problem. Let's
 195  
                     // trace a warning info.
 196  0
                     this.uploadPolicy
 197  0
                             .displayWarn("An InterruptedException occured in FileUploadManagerThreadImpl.run()");
 198  0
                 }
 199  
             }// while
 200  
 
 201  
             // If any error occurs, the prepared state of the file data may be true. We must free resources. So, to be
 202  
             // sure, we do it in all cases.
 203  0
             for (FileData fd : this.uploadPanel.getFilePanel().getFiles()) {
 204  0
                 if (fd.getUploadFlag() && fd.isPreparedForUpload()) {
 205  0
                     fd.afterUpload();
 206  
                 }
 207  0
             }// for
 208  
 
 209  
             // Let's restore the display.
 210  0
             this.uploadPanel.updateButtonState();
 211  0
             this.uploadPanel.getFilePanel().reload();
 212  0
             this.uploadPolicy.getContext().showStatus("");
 213  0
             this.uploadPolicy.getContext().getUploadPanel().getStatusLabel().setText("");
 214  
 
 215  
             // If no error occurs, we tell to the upload policy that a successful upload has been done.
 216  
 
 217  0
             if (getUploadException() != null) {
 218  0
                 this.uploadPolicy.sendDebugInformation("Error in Upload", getUploadException());
 219  0
             } else if (isUploadStopped()) {
 220  0
                 this.uploadPolicy.displayInfo("Upload stopped by the user. "
 221  
                         + this.nbSuccessfullyUploadedFiles
 222  
                         + " file(s) uploaded in "
 223  0
                         + (int) ((System.currentTimeMillis() - this.progressBarManager.getGlobalStartTime()) / 1000)
 224  
                         + " seconds. Average upload speed: "
 225  0
                         + ((this.progressBarManager.getUploadDuration() > 0) ? ((int) (this.progressBarManager
 226  0
                                 .getNbUploadedBytes() / this.progressBarManager.getUploadDuration())) : 0)
 227  
                         + " (kbytes/s)");
 228  
             } else {
 229  0
                 this.uploadPolicy.displayInfo("Upload finished normally. "
 230  
                         + this.nbSuccessfullyUploadedFiles
 231  
                         + " file(s) uploaded in "
 232  0
                         + (int) ((System.currentTimeMillis() - this.progressBarManager.getGlobalStartTime()) / 1000)
 233  
                         + " seconds. Average upload speed: "
 234  0
                         + ((this.progressBarManager.getUploadDuration() > 0) ? ((int) (this.progressBarManager
 235  0
                                 .getNbUploadedBytes() / this.progressBarManager.getUploadDuration())) : 0)
 236  
                         + " (kbytes/s)");
 237  
                 // FIXME uploadDuration displayed is 0!
 238  
                 try {
 239  0
                     this.uploadPolicy.afterUpload(this.getUploadException(), this.fileUploadThread.getResponseMsg());
 240  0
                 } catch (JUploadException e1) {
 241  0
                     this.uploadPolicy.displayErr("error in uploadPolicy.afterUpload (JUploadPanel)", e1);
 242  0
                 }
 243  
             }
 244  
 
 245  
             // The job is finished. Let's stop the timer, and have a last
 246  
             // refresh of the bars.
 247  0
             this.progressBarManager.uploadIsFinished();
 248  
 
 249  
             // We wait for 5 seconds, before clearing the progress bar.
 250  
             try {
 251  0
                 sleep(5000);
 252  0
             } catch (InterruptedException e) {
 253  
                 // Nothing to do
 254  0
             }
 255  
             // The job is finished for long enough, let's clear the progression
 256  
             // bars (and any associated ressource, like the time).
 257  
             // We'll clear the progress bar, only if this thread is in control
 258  
             // of the upload, that is: if this instance is the currently
 259  
             // FileUploadManagerThread referenced in the JUpload panel.
 260  0
             if (this == this.uploadPanel.getFileUploadManagerThread()) {
 261  0
                 this.progressBarManager.clearBarContent();
 262  0
                 this.uploadPolicy.getContext().getUploadPanel().getStatusLabel().setText("");
 263  
             }
 264  
 
 265  0
             this.uploadPolicy.displayDebug("End of the FileUploadManagerThreadImpl", 5);
 266  0
         } catch (Exception e) {
 267  
             // We need a JUploadException.
 268  0
             JUploadException jue = (e instanceof JUploadException) ? (JUploadException) e : new JUploadException(e);
 269  0
             setUploadException(jue);
 270  
 
 271  
             // And go back into a 'normal' way.
 272  0
             stopUpload();
 273  0
         } finally {
 274  
             // We restore the button state, just to be sure.
 275  0
             this.uploadPanel.updateButtonState();
 276  0
         }
 277  
 
 278  
         // And we die of our beautiful death ... until next upload.
 279  0
     }// run
 280  
 
 281  
     /**
 282  
      * @see wjhk.jupload2.upload.FileUploadManagerThread#setUploadException(wjhk.jupload2.exception.JUploadException)
 283  
      */
 284  
     public synchronized void setUploadException(JUploadException uploadExceptionParam) {
 285  
         // If the user stops the upload, the socket on which the applet reads
 286  
         // the server response got closed. So we ignore this error.
 287  0
         if (isUploadStopped() && uploadExceptionParam instanceof JUploadEOFException) {
 288  
             // Just ignore this error: the input stream from the server was
 289  
             // closed, but it probably occurs because the applet itself closed
 290  
             // the communicaiton.
 291  
         } else {
 292  
             // We don't override an existing exception
 293  0
             if (this.uploadException != null) {
 294  0
                 this.uploadPolicy
 295  0
                         .displayWarn("An exception has already been set in FileUploadManagerThreadImpl. The next one is just logged.");
 296  
             } else {
 297  0
                 this.uploadException = uploadExceptionParam;
 298  
             }
 299  
 
 300  0
             String exceptionMsg = (uploadExceptionParam.getCause() == null) ? uploadExceptionParam.getMessage()
 301  0
                     : uploadExceptionParam.getCause().getMessage();
 302  0
             String errMsg = this.uploadPolicy.getLocalizedString("errDuringUpload") + "\n\n" + exceptionMsg;
 303  0
             this.uploadPolicy.displayErr(errMsg, uploadException);
 304  
         }
 305  0
     }
 306  
 
 307  
     /**
 308  
      * @see wjhk.jupload2.upload.FileUploadManagerThread#getUploadException()
 309  
      */
 310  
     public JUploadException getUploadException() {
 311  0
         return this.uploadException;
 312  
     }
 313  
 
 314  
     /**
 315  
      * @see wjhk.jupload2.upload.FileUploadManagerThread#isUploadFinished()
 316  
      */
 317  
     public boolean isUploadFinished() {
 318  
         // Indicate whether or not the upload is finished. Several conditions:
 319  
         // all files are uploaded, there was an error and the user stops the
 320  
         // upload here...
 321  0
         return this.uploadFinished || this.stop || this.uploadException != null;
 322  
     }
 323  
 
 324  
     /**
 325  
      * @see FileUploadManagerThread#isUploadStopped()
 326  
      */
 327  
     public boolean isUploadStopped() {
 328  0
         return this.stop;
 329  
     }
 330  
 
 331  
     /**
 332  
      * @see wjhk.jupload2.upload.FileUploadManagerThread#nbBytesUploaded(long, UploadFileData)
 333  
      */
 334  
     public synchronized void nbBytesUploaded(long nbBytes, UploadFileData uploadFileData) throws JUploadException {
 335  0
         this.progressBarManager.nbBytesUploaded(nbBytes, uploadFileData);
 336  0
     }
 337  
 
 338  
     /**
 339  
      * @see wjhk.jupload2.upload.FileUploadManagerThread#setUploadStatus(wjhk.jupload2.upload.UploadFilePacket,
 340  
      *      wjhk.jupload2.upload.UploadFileData, int)
 341  
      */
 342  
     public synchronized void setUploadStatus(UploadFilePacket uploadFilePacket, UploadFileData uploadFileData,
 343  
             int uploadStatus) throws JUploadException {
 344  0
         this.progressBarManager.setUploadStatus(uploadFilePacket, uploadFileData, uploadStatus);
 345  0
     }
 346  
 
 347  
     /**
 348  
      * @see wjhk.jupload2.upload.FileUploadManagerThread#stopUpload()
 349  
      */
 350  
     public synchronized void stopUpload() {
 351  0
         this.stop = true;
 352  
 
 353  
         // The upload is now finished ...
 354  0
         this.uploadFinished = true;
 355  
 
 356  
         // We notify the various threads.
 357  0
         if (this.filePreparationThread != null && this.filePreparationThread.isAlive()) {
 358  0
             this.filePreparationThread.interrupt();
 359  
         }
 360  0
         if (this.packetConstructionThread != null && this.packetConstructionThread.isAlive()) {
 361  0
             this.packetConstructionThread.interrupt();
 362  
         }
 363  0
         if (this.fileUploadThread != null && this.fileUploadThread.isAlive()) {
 364  0
             this.fileUploadThread.interrupt();
 365  
         }
 366  
 
 367  
         // All 'sub-thread' is now interrupted. The upload thread can be stuck
 368  
         // while waiting for the server response. We also interrupt the current
 369  
         // thread.
 370  0
         this.interrupt();
 371  0
     }
 372  
 
 373  
     // //////////////////////////////////////////////////////////////////////////////////////////////
 374  
     // /////////////////// SYNCHRONIZATION METHODS
 375  
     // //////////////////////////////////////////////////////////////////////////////////////////////
 376  
 
 377  
     /**
 378  
      * @see wjhk.jupload2.upload.FileUploadManagerThread#anotherFileHasBeenSent(wjhk.jupload2.upload.UploadFilePacket,
 379  
      *      wjhk.jupload2.upload.UploadFileData)
 380  
      */
 381  
     public synchronized void anotherFileHasBeenSent(UploadFilePacket uploadFilePacket,
 382  
             UploadFileData newlyUploadedFileData) throws JUploadException {
 383  0
         this.progressBarManager.anotherFileHasBeenSent(uploadFilePacket, newlyUploadedFileData);
 384  0
     }
 385  
 
 386  
     /**
 387  
      * @see wjhk.jupload2.upload.FileUploadManagerThread#currentRequestIsFinished(wjhk.jupload2.upload.UploadFilePacket)
 388  
      */
 389  
     public synchronized void currentRequestIsFinished(UploadFilePacket uploadFilePacket) throws JUploadException {
 390  
         // We are finished with this packet. Let's display it.
 391  0
         this.progressBarManager.setUploadStatus(uploadFilePacket, uploadFilePacket.get(uploadFilePacket.size() - 1),
 392  
                 FileUploadManagerThread.UPLOAD_STATUS_UPLOADED);
 393  
 
 394  
         // We now remove this file from the list of files to upload, to show the user that there is less and less work
 395  
         // to do.
 396  0
         for (FileData fileData : uploadFilePacket) {
 397  0
             this.filePanel.remove(fileData);
 398  0
             this.nbSuccessfullyUploadedFiles += 1;
 399  0
         }
 400  
         
 401  0
         this.filePanel.cleanHierarchy();
 402  
 
 403  
         // If all files have been sent, the upload is finished.
 404  0
         if (!this.uploadFinished) {
 405  0
             this.uploadFinished = (this.nbSuccessfullyUploadedFiles == this.filePreparationThread.getNbFilesToSend());
 406  
         }
 407  0
     }
 408  
 
 409  
     // //////////////////////////////////////////////////////////////////////////////////////////////
 410  
     // /////////////////// PRIVATE METHODS
 411  
     // //////////////////////////////////////////////////////////////////////////////////////////////
 412  
 
 413  
     /**
 414  
      * Creates the upload thread, but does not start it. IThis thread will wait until the first packet is ready.
 415  
      * 
 416  
      * @throws JUploadException
 417  
      */
 418  
     private synchronized void createUploadThread(BlockingQueue<UploadFilePacket> packetQueue,
 419  
             FileUploadThread fileUploadThreadParam) throws JUploadException {
 420  0
         if (fileUploadThreadParam != null) {
 421  
             // The FileUploadThread has already been created.
 422  
             // We set the FileUploadThreadManager.
 423  0
             this.fileUploadThread = fileUploadThreadParam;
 424  0
             fileUploadThreadParam.setFileUploadThreadManager(this);
 425  
         } else {
 426  
             try {
 427  0
                 if (this.uploadPolicy.getPostURL().substring(0, 4).equals("ftp:")) {
 428  0
                     this.fileUploadThread = new FileUploadThreadFTP(this.uploadPolicy, packetQueue, this);
 429  
                 } else {
 430  0
                     this.fileUploadThread = new FileUploadThreadHTTP(this.uploadPolicy, packetQueue, this);
 431  
                 }
 432  0
             } catch (JUploadException e1) {
 433  
                 // Too bad !
 434  0
                 this.uploadPolicy.displayErr(e1);
 435  0
             }
 436  
         }
 437  0
     }
 438  
 }