Coverage Report - wjhk.jupload2.upload.FilePreparationThread
 
Classes in this File Line Coverage Branch Coverage Complexity
FilePreparationThread
77 %
67/87
75 %
12/16
3,143
 
 1  
 package wjhk.jupload2.upload;
 2  
 
 3  
 import java.io.File;
 4  
 import java.util.ArrayList;
 5  
 import java.util.List;
 6  
 import java.util.concurrent.BlockingQueue;
 7  
 
 8  
 import javax.swing.JProgressBar;
 9  
 
 10  
 import wjhk.jupload2.exception.JUploadException;
 11  
 import wjhk.jupload2.filedata.DefaultFileData;
 12  
 import wjhk.jupload2.filedata.FileData;
 13  
 import wjhk.jupload2.gui.JUploadPanel;
 14  
 import wjhk.jupload2.gui.filepanel.FilePanel;
 15  
 import wjhk.jupload2.policies.UploadPolicy;
 16  
 
 17  
 /**
 18  
  * This thread is responsible for preparing all files for upload. It stores each prepared file in the preparedFileQueue,
 19  
  * for further processing.
 20  
  * 
 21  
  * @author etienne_sf
 22  
  */
 23  
 public class FilePreparationThread extends Thread {
 24  
 
 25  
     /**
 26  
      * The array of files to send, from the {@link FilePanel}
 27  
      */
 28  7
     List<FileData> fileDataArray = null;
 29  
 
 30  
     /**
 31  
      * Number of files that are prepared for upload. A file is prepared for upload, if the
 32  
      * {@link FileData#beforeUpload()} has been called.
 33  
      */
 34  7
     int nbPreparedFiles = 0;
 35  
 
 36  
     /**
 37  
      * The total of files that must be sent. It is initialized by the total number of files in the list, and is
 38  
      * decremented each time an error occurs during a file preparation, and the user wants to go on.
 39  
      */
 40  7
     int nbFilesToPrepare = -1;
 41  
 
 42  
     /**
 43  
      * We need to know from the starting point, the actual number of files to upload. This is necessary, so the end of
 44  
      * the upload is always correctly detected.
 45  
      */
 46  7
     int nbFilesToSend = 0;
 47  
 
 48  
     /**
 49  
      * Sum of the length for all prepared files. This allow the calculation of the estimatedTotalLength.
 50  
      * 
 51  
      * @see #anotherFileIsPrepared(UploadFileData)
 52  
      */
 53  7
     long nbTotalNumberOfPreparedBytes = 0;
 54  
 
 55  
     /**
 56  
      * The {@link JUploadPanel} progress bar, to follow the file preparation progress.
 57  
      */
 58  7
     JProgressBar preparationProgressBar = null;
 59  
 
 60  
     /** A shortcut to the upload panel */
 61  7
     JUploadPanel uploadPanel = null;
 62  
 
 63  
     /** The current upload policy. */
 64  7
     UploadPolicy uploadPolicy = null;
 65  
 
 66  
     /** The thread which globally manages the upload */
 67  7
     FileUploadManagerThread fileUploadManagerThread = null;
 68  
 
 69  
     /** The current file list. */
 70  7
     FilePanel filePanel = null;
 71  
 
 72  
     /**
 73  
      * The queue where each prepared file will be stored, for further processing
 74  
      */
 75  7
     BlockingQueue<UploadFileData> preparedFileQueue = null;
 76  
 
 77  
     /**
 78  
      * @param preparedFileQueue
 79  
      * @param fileUploadManagerThread
 80  
      * @param uploadPolicy
 81  
      */
 82  
     public FilePreparationThread(BlockingQueue<UploadFileData> preparedFileQueue,
 83  
             FileUploadManagerThread fileUploadManagerThread, UploadPolicy uploadPolicy) {
 84  
         // A thread name is very useful, when debugging...
 85  7
         super("FilePreparationThread");
 86  
 
 87  7
         this.preparedFileQueue = preparedFileQueue;
 88  7
         this.fileUploadManagerThread = fileUploadManagerThread;
 89  7
         this.uploadPolicy = uploadPolicy;
 90  7
         this.uploadPanel = uploadPolicy.getContext().getUploadPanel();
 91  7
         this.filePanel = this.uploadPanel.getFilePanel();
 92  7
         this.preparationProgressBar = this.uploadPanel.getPreparationProgressBar();
 93  
 
 94  
         // Clean the file list, from all files which are not to upload
 95  7
         this.filePanel.removeFileNotToUpload();
 96  
 
 97  
         // Prepare the list of files to upload, cleaned from the file marked as 'no to upload', see just above.
 98  
         // To avoid concurrency issues, we clone the list, before starting the upload.
 99  7
         this.fileDataArray = new ArrayList<FileData>(this.filePanel.getFiles().size());
 100  7
         for (FileData fd : this.filePanel.getFiles()) {
 101  11
             if (fd.getUploadFlag()) {
 102  11
                 this.fileDataArray.add(fd);
 103  
             }
 104  11
         }
 105  7
         nbFilesToSend = this.fileDataArray.size();
 106  
 
 107  7
         this.uploadPolicy.displayDebug("Nb files to send: " + nbFilesToSend, 20);
 108  
 
 109  7
         this.preparationProgressBar.setMaximum(100 * nbFilesToSend);
 110  7
     }
 111  
 
 112  
     /**
 113  
      * The actual command to prepare files.
 114  
      * 
 115  
      * @see java.lang.Thread#run()
 116  
      * @see FileData#beforeUpload()
 117  
      */
 118  
     @Override
 119  
     final public void run() {
 120  
         // We loop through all files, and check before each if we should
 121  
         // stop (for instance if an error occurs)
 122  
 
 123  
         // Let's calculate the common root for all these files
 124  4
         File fileRoot = DefaultFileData.getRoot(fileDataArray);
 125  4
         String uploadFileRoot = (fileRoot == null) ? "" : fileRoot.getAbsolutePath();
 126  
 
 127  4
         if (fileDataArray.size() != nbFilesToSend) {
 128  0
             this.uploadPolicy.displayWarn(fileDataArray.size() + " files to prepare, but there are " + nbFilesToSend
 129  
                     + " files to send!");
 130  
         }
 131  
 
 132  
         // numFileInCurrentUpload is the index of the file in the current index.
 133  
         // It should be the array index. But, if a file preparation fails in error, numFileInCurrentUpload will be the
 134  
         // array index minus 1, if 2 files, it will be the array index minus 2...
 135  4
         int numFileInCurrentUpload = 0;
 136  
 
 137  11
         for (int i = 0; i < fileDataArray.size() && !this.fileUploadManagerThread.isUploadFinished(); i += 1) {
 138  
             // We upload only the files which have the uploadFlag set to true.
 139  
             // All other files have been removed in the constructor for this instance.
 140  
             try {
 141  14
                 this.uploadPolicy.displayDebug("============== Start of file preparation ("
 142  7
                         + fileDataArray.get(i).getFileName() + ")", 30);
 143  7
                 UploadFileData uploadFileData = new UploadFileData(fileDataArray.get(i), numFileInCurrentUpload,
 144  
                         this.fileUploadManagerThread, this.uploadPolicy);
 145  
 
 146  
                 // Let's indicate to the user what's running on.
 147  14
                 this.preparationProgressBar.setString(this.uploadPolicy.getLocalizedString("preparingFile",
 148  7
                         Integer.valueOf(i + 1), Integer.valueOf(fileDataArray.size())));
 149  
                 // We want an immediate repaint, to be sure that the new text is displayed to the user.
 150  7
                 this.preparationProgressBar.repaint(0);
 151  
 
 152  
                 // Then, we work
 153  
 
 154  
                 // Let's check that everything is Ok
 155  
                 // More debug output, to understand where the applet freezes.
 156  7
                 this.uploadPolicy.displayDebug(this.getClass().getName()
 157  
                         + ".prepareFiles(): before call to beforeUpload()", 100);
 158  
 
 159  
                 try {
 160  
                     // Let's try to prepare the upload.
 161  7
                     uploadFileData.beforeUpload(uploadFileRoot);
 162  
 
 163  
                     // If we arrive, here, it means that beforeUpload() did not throw an exception, that is: the
 164  
                     // file is now prepared.
 165  
                     // Next file will be ... next in the current upload.
 166  7
                     numFileInCurrentUpload += 1;
 167  
 
 168  
                     // TODO Whe error during file preparation ask the user.
 169  
 
 170  14
                     this.uploadPolicy.displayDebug(
 171  7
                             "============== End of file preparation (" + uploadFileData.getFileName() + ")", 30);
 172  
                     try {
 173  7
                         anotherFileIsPrepared(uploadFileData);
 174  0
                     } catch (InterruptedException e) {
 175  
                         // There was a problem putting the item. Let's try again in the next loop.
 176  0
                         i -= 1;
 177  7
                     }
 178  0
                 } catch (JUploadException e) {
 179  
                     // An error occurs during file preparation. We'll send one file less than expected.
 180  0
                     this.nbFilesToPrepare -= 1;
 181  0
                     this.uploadPolicy.displayErr(e.getMessage());
 182  0
                     throw e;
 183  0
                 } catch (Exception e) {
 184  
                     // Ooups !
 185  0
                     this.nbFilesToPrepare -= 1;
 186  0
                     String msg = "An exception " + e.getClass().getName()
 187  0
                             + " occured during file preparation for file " + fileDataArray.get(i).getAbsolutePath()
 188  
                             + ". This file will not be uploaded.";
 189  0
                     this.uploadPolicy.displayErr(msg);
 190  0
                     throw new JUploadException(msg, e);
 191  7
                 }
 192  
 
 193  
                 // The file preparation is finished. Let's update the
 194  
                 // progress
 195  
                 // bar.
 196  7
                 this.preparationProgressBar.setValue(this.nbPreparedFiles * 100);
 197  7
                 this.preparationProgressBar.repaint();
 198  0
             } catch (JUploadException e) {
 199  0
                 this.fileUploadManagerThread.setUploadException(e);
 200  7
             }
 201  
         }// for
 202  
 
 203  
         // All prepared files are posted on the preparedQueue. Let's send the
 204  
         // 'End of Queue' marker.
 205  
         try {
 206  4
             this.preparedFileQueue.put(new UploadFileDataPoisonned());
 207  0
         } catch (InterruptedException e) {
 208  
             // We should not be interrupted here. If it happens, it should be
 209  
             // because the upload was stopped. But, then, we may have the
 210  
             // PacketConstructionThread blocked, waiting for this packet. So,
 211  
             // let's log a warning.
 212  0
             this.uploadPolicy
 213  0
                     .displayWarn("Got interrupted, while posting the poisoned UploadFileData on the preparedQueue!");
 214  4
         }
 215  
 
 216  
         // Let's clear the bar, which is no more accurate. We let the value to 100%
 217  4
         this.preparationProgressBar.setString("");
 218  4
         this.uploadPolicy.displayDebug(this.nbPreparedFiles + " files has been prepared (for " + nbFilesToSend
 219  
                 + " files to send)", 10);
 220  4
     }
 221  
 
 222  
     /**
 223  
      * This method is called each time a new file is ready to upload. It calculates if a new packet of files is ready to
 224  
      * upload. It is private, as it may be called only from this class.
 225  
      * 
 226  
      * @throws JUploadException
 227  
      */
 228  
     private void anotherFileIsPrepared(UploadFileData newlyPreparedFileData) throws JUploadException,
 229  
             InterruptedException {
 230  7
         this.nbPreparedFiles += 1;
 231  7
         this.nbTotalNumberOfPreparedBytes += newlyPreparedFileData.getUploadLength();
 232  7
         this.preparedFileQueue.put(newlyPreparedFileData);
 233  7
     }
 234  
 
 235  
     /**
 236  
      * Returns the total number of bytes to upload. This takes into account only the prepared file content. It ignores
 237  
      * the possible head and tails of the request (for instance: http headers...). This gives a good idea of the total
 238  
      * amount to send, and allows is suffisiant to properly manage the upload progress bar.<BR>
 239  
      * The total number of bytes can only be calculated when all files are prepared. When this method is called before
 240  
      * this, an estimation is done for non prepared files, based on the average size of the already prepared files.
 241  
      * 
 242  
      * @return The real or estimated total number of bytes to send
 243  
      */
 244  
     public double getTotalFileBytesToSend() {
 245  
         double totalFileBytesToSend;
 246  
 
 247  
         // Let's estimate the total, or calculate it, of all files are prepared
 248  2
         if (this.nbPreparedFiles == this.nbFilesToSend) {
 249  
             // All files are prepared: it's no more an estimation !
 250  1
             totalFileBytesToSend = this.nbTotalNumberOfPreparedBytes;
 251  1
         } else if (this.nbPreparedFiles == 0) {
 252  1
             totalFileBytesToSend = 0;
 253  
         } else {
 254  
             // We sum the total number of prepared bytes, and we estimate
 255  
             // the size of the files that are not prepared yet
 256  0
             totalFileBytesToSend = this.nbTotalNumberOfPreparedBytes
 257  
                     +
 258  
                     // And we sum it with the average amount per file
 259  
                     // prepared for the others
 260  0
                     (this.fileDataArray.size() - this.nbPreparedFiles) * this.nbTotalNumberOfPreparedBytes
 261  
                     / this.nbPreparedFiles;
 262  
         }
 263  
 
 264  2
         return totalFileBytesToSend;
 265  
     }
 266  
 
 267  
     /**
 268  
      * @return the nbPreparedFiles
 269  
      */
 270  
     public int getNbPreparedFiles() {
 271  2
         return nbPreparedFiles;
 272  
     }
 273  
 
 274  
     /**
 275  
      * @return the nbFilesToSent
 276  
      */
 277  
     public int getNbFilesToSend() {
 278  9
         return nbFilesToSend;
 279  
     }
 280  
 
 281  
     /**
 282  
      * @return the nbTotalNumberOfPreparedBytes
 283  
      */
 284  
     public long getNbTotalNumberOfPreparedBytes() {
 285  2
         return nbTotalNumberOfPreparedBytes;
 286  
     }
 287  
 }