Coverage Report - wjhk.jupload2.upload.DefaultFileUploadThread
 
Classes in this File Line Coverage Branch Coverage Complexity
DefaultFileUploadThread
0 %
0/188
0 %
0/76
3,077
 
 1  
 //
 2  
 // $Id: DefaultFileUploadThread.java 287 2007-06-17 09:07:04 +0000 (dim., 17
 3  
 // juin 2007) felfert $
 4  
 //
 5  
 // jupload - A file upload applet.
 6  
 // Copyright 2007 The JUpload Team
 7  
 //
 8  
 // Created: ?
 9  
 // Creator: William JinHua Kwong
 10  
 // Last modified: $Date: 2015-03-10 20:59:42 +0100 (mar., 10 mars 2015) $
 11  
 //
 12  
 // This program is free software; you can redistribute it and/or modify it under
 13  
 // the terms of the GNU General Public License as published by the Free Software
 14  
 // Foundation; either version 2 of the License, or (at your option) any later
 15  
 // version. This program is distributed in the hope that it will be useful, but
 16  
 // WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 17  
 // FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
 18  
 // details. You should have received a copy of the GNU General Public License
 19  
 // along with this program; if not, write to the Free Software Foundation, Inc.,
 20  
 // 675 Mass Ave, Cambridge, MA 02139, USA.
 21  
 
 22  
 package wjhk.jupload2.upload;
 23  
 
 24  
 import java.io.OutputStream;
 25  
 import java.net.SocketException;
 26  
 import java.util.concurrent.BlockingQueue;
 27  
 import java.util.regex.Pattern;
 28  
 
 29  
 import wjhk.jupload2.exception.JUploadException;
 30  
 import wjhk.jupload2.exception.JUploadExceptionUploadFailed;
 31  
 import wjhk.jupload2.exception.JUploadIOException;
 32  
 import wjhk.jupload2.exception.JUploadInterrupted;
 33  
 import wjhk.jupload2.gui.DialogUploadRetry;
 34  
 import wjhk.jupload2.policies.UploadPolicy;
 35  
 
 36  
 /**
 37  
  * This class is based on the {@link FileUploadThread} class. It's an abstract class that contains the default
 38  
  * implementation for the {@link FileUploadThread} interface. <BR>
 39  
  * It contains the following abstract methods, which must be implemented in the children classes. These methods are
 40  
  * called in this order: <DIR> <LI>For each upload request (for instance, upload of 3 files with nbFilesPerRequest to 2,
 41  
  * makes 2 request: 2 files, then the last one): <DIR> <LI><I>try</I> <LI>
 42  
  * {@link #startRequest}: start of the
 43  
  * UploadRequest. <LI>Then, for each file to upload (according to the nbFilesPerRequest and maxChunkSize applet
 44  
  * parameters) <DIR> <LI>beforeFile(int) is called before writting the bytes for this file (or this chunk) <LI>
 45  
  * afterFile(int) is called after writting the bytes for this file (or this chunk) </DIR> <LI>finishRequest() </DIR></LI>
 46  
  * <I>finally</I>cleanRequest() <LI>Call of cleanAll(), to clean up any used resources, common to the whole upload.
 47  
  * </DIR>
 48  
  */
 49  
 public abstract class DefaultFileUploadThread extends Thread implements FileUploadThread {
 50  
 
 51  
     // ////////////////////////////////////////////////////////////////////////
 52  
     // /////////////////////// VARIABLES //////////////////////////////////////
 53  
     // ////////////////////////////////////////////////////////////////////////
 54  
 
 55  
     /**
 56  
      * The queue that'll transmit each packet to upload to the server.
 57  
      */
 58  0
     BlockingQueue<UploadFilePacket> packetQueue = null;
 59  
 
 60  
     /**
 61  
      * The upload manager. The thread that prepares files, and is responsible to manage the upload process.
 62  
      * 
 63  
      * @see FileUploadManagerThread
 64  
      */
 65  0
     FileUploadManagerThread fileUploadManagerThread = null;
 66  
 
 67  
     /**
 68  
      * The upload policy contains all parameters needed to define the way files should be uploaded, including the URL.
 69  
      */
 70  0
     protected UploadPolicy uploadPolicy = null;
 71  
 
 72  
     /**
 73  
      * The value of the applet parameter maxChunkSize, or its default value.
 74  
      */
 75  
     long maxChunkSize;
 76  
 
 77  
     // ////////////////////////////////////////////////////////////////////////////////////
 78  
     // /////////////////////// PRIVATE ATTRIBUTES
 79  
     // ////////////////////////////////////////////////////////////////////////////////////
 80  
 
 81  
     // This variable should only be set to false. Setting to true should be used
 82  
     // only to
 83  
     // debug the JUPload retry feature.
 84  0
     static boolean sendResumableError = false;
 85  
 
 86  
     /**
 87  
      * Current retry number, for the resume upload feature. The first try occurs with nbRetry=0.
 88  
      */
 89  0
     int nbRetry = 0;
 90  
 
 91  
     /**
 92  
      * The full response message from the server, if any. For instance, in HTTP mode, this contains both the headers and
 93  
      * the body.
 94  
      */
 95  0
     protected String responseMsg = "";
 96  
 
 97  
     /**
 98  
      * The response message from the application. For instance, in HTTP mode, this contains the body response.<BR>
 99  
      * Note: for easier management on the various server configurations, all end or line characters (CR, LF or CRLF) are
 100  
      * changed to uniform CRLF.
 101  
      */
 102  0
     protected String responseBody = "";
 103  
 
 104  
     /**
 105  
      * Creates a new instance.
 106  
      * 
 107  
      * @param threadName The name of the thread, that will be displayed in the debugger and in the logs.
 108  
      * @param packetQueue The queue from wich packets to upload are available.
 109  
      * @param uploadPolicy The upload policy to be applied.
 110  
      * @param fileUploadManagerThread The thread that is managing the upload.
 111  
      */
 112  
     public DefaultFileUploadThread(String threadName, BlockingQueue<UploadFilePacket> packetQueue,
 113  
             UploadPolicy uploadPolicy, FileUploadManagerThread fileUploadManagerThread) {
 114  
         // Thread parameters.
 115  0
         super(threadName);
 116  
 
 117  
         // Specific stuff.
 118  0
         this.packetQueue = packetQueue;
 119  0
         this.uploadPolicy = uploadPolicy;
 120  0
         this.fileUploadManagerThread = fileUploadManagerThread;
 121  
         // Let's read up to date upload parameters.
 122  0
         this.maxChunkSize = this.uploadPolicy.getMaxChunkSize();
 123  
 
 124  0
         this.uploadPolicy.displayDebug("DefaultFileUploadThread created", 30);
 125  0
     }
 126  
 
 127  
     /**
 128  
      * This method calculates the upload overhead for the file number indexFile in the filesDataParam given to the
 129  
      * constructor. For instance, in HTTP, the upload contains a head and a tail for each files.
 130  
      * 
 131  
      * @param uploadFileData The file whose additional length is asked.
 132  
      * @return The additional number of bytes for this file.
 133  
      */
 134  
     abstract long getAdditionnalBytesForUpload(UploadFileData uploadFileData) throws JUploadIOException;
 135  
 
 136  
     /**
 137  
      * This method is called before starting of each request. It can be used to prepare any work, before starting the
 138  
      * request. For instance, in HTTP, the tail must be properly calculated, as the last one must be different from the
 139  
      * others.<BR>
 140  
      * The packets to send are available through the {@link #packetQueue} queue.
 141  
      */
 142  
     abstract void beforeRequest(UploadFilePacket packet) throws JUploadException;
 143  
 
 144  
     /**
 145  
      * This method is called for each upload request to the server. The number of request to the server depends on:
 146  
      * <DIR> <LI>The total number of files to upload. <LI>The value of the nbFilesPerRequest applet parameter. <LI>The
 147  
      * value of the maxChunkSize applet parameter. </DIR> The main objective of this method is to open the connection to
 148  
      * the server, where the files to upload will be written. It should also send any header necessary for this upload
 149  
      * request. The {@link #getOutputStream()} methods is then called to know where the uploaded files should be
 150  
      * written. <BR>
 151  
      * Note: it's up to the class containing this method to internally manage the connection.
 152  
      * 
 153  
      * @param contentLength The total number of bytes for the files (or the chunk) to upload in this query.
 154  
      * @param bChunkEnabled True if this upload is part of a file (can occurs only if the maxChunkSize applet parameter
 155  
      *            is set). False otherwise.
 156  
      * @param chunkPart The chunk number. Should be ignored if bChunkEnabled is false.
 157  
      * @param bLastChunk True if in chunk mode, and this upload is the last one. Should be ignored if bChunkEnabled is
 158  
      *            false.
 159  
      */
 160  
     abstract void startRequest(long contentLength, boolean bChunkEnabled, int chunkPart, boolean bLastChunk)
 161  
             throws JUploadException;
 162  
 
 163  
     /**
 164  
      * This method is called at the end of each request.
 165  
      * 
 166  
      * @return The response status code from the server (200 == OK)
 167  
      * @see #startRequest(long, boolean, int, boolean)
 168  
      */
 169  
     abstract int finishRequest() throws JUploadException;
 170  
 
 171  
     /**
 172  
      * Reaction of the upload thread, when an interruption has been received. This method should close all resource to
 173  
      * the server, to allow the server to free any resource (temporary file, network connection...).
 174  
      */
 175  
     abstract void interruptionReceived();
 176  
 
 177  
     /**
 178  
      * This method is called before sending the bytes corresponding to the file whose index is given in argument. If the
 179  
      * file is splitted in chunks (see the maxChunkSize applet parameter), this method is called before each chunk for
 180  
      * this file.
 181  
      * 
 182  
      * @param uploadFilePacket The bunch of files in the current request
 183  
      * @param uploadFileData The next file that will be sent
 184  
      */
 185  
     abstract void beforeFile(UploadFilePacket uploadFilePacket, UploadFileData uploadFileData) throws JUploadException;
 186  
 
 187  
     /**
 188  
      * Idem as {@link #beforeFile(UploadFilePacket, UploadFileData)}, but is called after each file (and each chunks for
 189  
      * each file).
 190  
      * 
 191  
      * @param uploadFileData The file that was just sent.
 192  
      */
 193  
     abstract void afterFile(UploadFileData uploadFileData) throws JUploadException;
 194  
 
 195  
     /**
 196  
      * Clean any used resource of the last executed request. In HTTP mode, the output stream, input stream and the
 197  
      * socket should be cleaned here.
 198  
      */
 199  
     abstract void cleanRequest() throws JUploadException;
 200  
 
 201  
     /**
 202  
      * Clean any used resource, like a 'permanent' connection. This method is called after the end of the last request
 203  
      * (see on the top of this page for details).
 204  
      */
 205  
     abstract void cleanAll() throws JUploadException;
 206  
 
 207  
     /**
 208  
      * Get the output stream where the files should be written for upload.
 209  
      * 
 210  
      * @return The target output stream for upload.
 211  
      */
 212  
     abstract OutputStream getOutputStream() throws JUploadException;
 213  
 
 214  
     /**
 215  
      * Return the the body for the server response. That is: the server response without the http header. This is the
 216  
      * functional response from the server application, that has been as the HTTP reply body, for instance: all 'echo'
 217  
      * PHP commands. <BR>
 218  
      * 
 219  
      * @return The last application response (HTTP body, in HTTP upload)
 220  
      */
 221  
     public String getResponseBody() {
 222  0
         return this.responseBody;
 223  
     }
 224  
 
 225  
     /**
 226  
      * Get the server Output.
 227  
      * 
 228  
      * @return The status message from the first line of the response (e.g. "200 OK").
 229  
      */
 230  
     public String getResponseMsg() {
 231  0
         return this.responseMsg;
 232  
     }
 233  
 
 234  
     /**
 235  
      * @return the packetQueue
 236  
      */
 237  
     public BlockingQueue<UploadFilePacket> getPacketQueue() {
 238  0
         return packetQueue;
 239  
     }
 240  
 
 241  
     /**
 242  
      * Unused Store the String that contains the server response body.
 243  
      * 
 244  
      * @param body The response body that has been read.
 245  
      */
 246  
     void setResponseBody(String body) {
 247  0
         this.responseBody = normalizeCRLF(body);
 248  0
     }
 249  
 
 250  
     /**
 251  
      * Add a String that has been read from the server response.
 252  
      * 
 253  
      * @param msg The status message from the first line of the response (e.g. "200 OK").
 254  
      */
 255  
     void setResponseMsg(String msg) {
 256  0
         this.responseMsg = normalizeCRLF(msg);
 257  0
     }
 258  
 
 259  
     // ////////////////////////////////////////////////////////////////////////////////////
 260  
     // /////////////////////// PRIVATE FUNCTIONS
 261  
     // ////////////////////////////////////////////////////////////////////////////////////
 262  
 
 263  
     /**
 264  
      * This method waits for a packet on the packetQueue. Then, it calls the doUpload() method, to send these files to
 265  
      * the server.
 266  
      */
 267  
     @Override
 268  
     final public void run() {
 269  0
         this.uploadPolicy.displayDebug("Start of the FileUploadThread", 5);
 270  0
         int nbSentFiles = 0;
 271  
 
 272  
         // We'll stop the upload if an error occurs. So the try/catch is
 273  
         // outside the while.
 274  0
         while (!this.fileUploadManagerThread.isUploadFinished()) {
 275  0
             UploadFilePacket packet = null;
 276  
 
 277  
             try {
 278  
                 // We take the next packet. This method will block until a packet is ready.
 279  0
                 packet = packetQueue.take();
 280  
 
 281  
                 // If the packet is 'poisonned', then it's the standard end of work.
 282  0
                 if (packet.isPoisonned()) {
 283  0
                     break;
 284  
                 }
 285  
 
 286  
                 // /////////////////////////////////////////////////////////////////////////////////
 287  
                 // Let's go to work : THIS IS THE UPLOAD, surrounded by the
 288  
                 // RESUME LOOP
 289  
                 // /////////////////////////////////////////////////////////////////////////////////
 290  0
                 nbRetry = 0;
 291  
                 while (true) {
 292  
                     try {
 293  
                         // Let's try to upload the current packet.
 294  0
                         doUpload(packet);
 295  
 
 296  
                         // If we are here, the last upload is a success. Let's
 297  
                         // exit the loop.
 298  0
                         break;
 299  0
                     } catch (JUploadException jue) {
 300  
                         // manageRetry throw the exception, if no retry should
 301  
                         // be done.
 302  0
                         manageRetry(jue);
 303  
 
 304  
                         // If we are here, the applet should retry the upload.
 305  
                         // We let it loop again.
 306  0
                         nbRetry += 1;
 307  
 
 308  
                         // We must clean the resources of the previous attempt.
 309  0
                         beforeRetry(packet);
 310  0
                     }
 311  
                 }// while(resume)
 312  
                  // /////////////////////////////////////////////////////////////////////////////////
 313  
                  // //////////////// ENF OF RESUME LOOP
 314  
                  // /////////////////////////////////////////////////////////////////////////////////
 315  
 
 316  0
                 this.uploadPolicy.displayDebug("After do upload", 50);
 317  0
             } catch (InterruptedException e) {
 318  0
                 this.uploadPolicy.displayWarn(this.getClass().getName() + ".run(): received in "
 319  0
                         + e.getClass().getName() + ", exiting...");
 320  0
                 break;
 321  0
             } catch (JUploadException e) {
 322  0
                 if (this.fileUploadManagerThread.isUploadFinished()) {
 323  
                     // We ignore the error
 324  0
                     this.uploadPolicy.displayWarn("Ignoring " + e.getClass().getName() + " because upload is finished");
 325  
                 } else {
 326  0
                     this.fileUploadManagerThread.setUploadException(e);
 327  
                 }
 328  0
             } catch (JUploadInterrupted e) {
 329  
                 // The upload has been interrupted, probably by the user
 330  
                 // (stop
 331  
                 // button). The fileManagerUploadThread aleady knows this.
 332  0
                 this.uploadPolicy.displayInfo("Upload stopped by the user");
 333  0
                 this.uploadPolicy.displayDebug(e.getMessage(), 30);
 334  0
             } finally {
 335  
                 // Let's free any locked resource for the current packet.
 336  
                 // This is done here, to allow the resume feature (and, even in
 337  
                 // case an error occurs, we free resources only after the last
 338  
                 // retry)
 339  0
                 for (UploadFileData uploadFileData : packet) {
 340  0
                     if (uploadFileData.isPreparedForUpload()) {
 341  0
                         uploadFileData.afterUpload();
 342  
                     }
 343  0
                 }
 344  
                 // We free any resources associated with this thread.
 345  
                 try {
 346  0
                     cleanAll();
 347  0
                 } catch (Exception e) {
 348  
                     // We ignore the error
 349  0
                     this.uploadPolicy.displayWarn("[run(), After cleanAll] Ignoring " + e.getClass().getName()
 350  
                             + " because upload is finished");
 351  0
                 }
 352  0
             }
 353  
 
 354  0
             nbSentFiles += packet.size();
 355  0
         }// while (!isUploadFinished)
 356  
 
 357  0
         this.uploadPolicy.displayDebug("End of the FileUploadThread (" + nbSentFiles + " have been sent)", 5);
 358  0
     }// run
 359  
 
 360  
     /**
 361  
      * @param jue
 362  
      * @throws JUploadException
 363  
      */
 364  
     private void manageRetry(JUploadException jue) throws JUploadException {
 365  0
         String exceptionCauseClass = (jue.getCause() == null) ? "no exception cause" : jue.getCause().getClass()
 366  0
                 .getName();
 367  0
         String errMsg = jue.getClass().getName() + " (" + jue.getMessage() + "), caused by: " + exceptionCauseClass;
 368  
 
 369  0
         if (this.fileUploadManagerThread.isUploadFinished()) {
 370  
             // The upload is stopped. This error may be caused
 371  0
             this.uploadPolicy.displayWarn("The following error occurs, but the upload is stopped, ignoring it ]"
 372  
                     + errMsg + "]");
 373  0
             throw jue;
 374  0
         } else if (jue.getCause() instanceof SocketException) {
 375  0
             this.uploadPolicy.displayWarn("A 'resumable' error occurred: " + errMsg);
 376  
             // If it was the last retry, we stop here.
 377  0
             if (nbRetry >= this.uploadPolicy.getRetryMaxNumberOf()) {
 378  0
                 this.uploadPolicy.displayWarn("Too much retries (" + nbRetry + "), exiting...");
 379  0
                 throw jue;
 380  
             }
 381  
 
 382  0
             DialogUploadRetry dialogUploadRetry = new DialogUploadRetry(this.uploadPolicy.getContext().getFrame(), jue,
 383  
                     nbRetry, this.uploadPolicy);
 384  
             // The constructor returns, when the dialog is closed. Let's check
 385  
             // the user answer:
 386  0
             if (dialogUploadRetry.isRetryValidated()) {
 387  0
                 this.uploadPolicy.displayDebug("The user (or the timer) choosed to retry the upload", 30);
 388  
             } else {
 389  0
                 this.uploadPolicy.displayDebug("The user refuses to retry the upload, exiting...", 30);
 390  
                 // No retry, let's note the exception and go out
 391  0
                 throw jue;
 392  
             }// End of resumable exceptions management.
 393  0
         } else {
 394  
             // This exception can't be resumed. We transmit it.
 395  0
             this.uploadPolicy.displayWarn("Non resumable error occured, exiting...");
 396  0
             throw jue;
 397  
         }
 398  0
     }
 399  
 
 400  
     /**
 401  
      * Actual execution file(s) upload. It's called by the run methods, once for all files, or file by file, depending
 402  
      * on the UploadPolicy. The list of files to upload is stored in the packet parameter.<BR>
 403  
      * This method is called by the run() method. The prerequisite about the filesToUpload array are: <DIR> <LI>If the
 404  
      * sum of contentLength for the files in the array is more than the maxChunkSize, then nbFilesToUploadParam is one.
 405  
      * <LI>The number of elements in filesToUpload is less (or equal) than the nbMaxFilesPerUpload. </DIR>
 406  
      * 
 407  
      * @throws JUploadException
 408  
      * @throws JUploadInterrupted Thrown when an interruption of the thread is detected.
 409  
      */
 410  
     void doUpload(UploadFilePacket packet) throws JUploadException, JUploadInterrupted {
 411  0
         boolean bChunkEnabled = false;
 412  0
         long totalContentLength = 0;
 413  0
         long totalFileLength = 0;
 414  
 
 415  
         // We are about to start a new upload.
 416  0
         this.fileUploadManagerThread.setUploadStatus(packet, packet.get(0),
 417  
                 FileUploadManagerThread.UPLOAD_STATUS_UPLOADING);
 418  
 
 419  
         // Prepare upload, for all files to be uploaded.
 420  0
         beforeRequest(packet);
 421  
 
 422  0
         for (UploadFileData uploadFileData : packet) {
 423  
             // The upload may be finished, while we're working on the files...
 424  0
             if (this.fileUploadManagerThread.isUploadFinished()) {
 425  
                 // Let's stop our work here.
 426  0
                 return;
 427  
             }
 428  
             // Total length, for HTTP upload.
 429  0
             totalContentLength += uploadFileData.getUploadLength();
 430  0
             totalContentLength += getAdditionnalBytesForUpload(uploadFileData);
 431  
             // Total file length: used to manage the progress bar (we don't
 432  
             // follow the bytes uploaded within headers and forms).
 433  0
             totalFileLength += uploadFileData.getUploadLength();
 434  
 
 435  0
             this.uploadPolicy.displayDebug(
 436  0
                     "file " + uploadFileData.getFileName() + ": content=" + uploadFileData.getUploadLength()
 437  0
                             + " bytes, getAdditionnalBytesForUpload=" + getAdditionnalBytesForUpload(uploadFileData)
 438  
                             + " bytes", 50);
 439  0
         }// for
 440  
 
 441  
         // Ok, now we check that the totalContentLength is less than the chunk
 442  
         // size.
 443  0
         if (totalFileLength >= this.maxChunkSize) {
 444  
             // hum, hum, we have to download file by file, with chunk enabled.
 445  
             // This a prerequisite of this method.
 446  0
             if (packet.size() > 1) {
 447  0
                 this.fileUploadManagerThread.setUploadException(new JUploadException(
 448  
                         "totalContentLength >= chunkSize: this.filesToUpload.length should be 1 (doUpload)"));
 449  
             }
 450  0
             bChunkEnabled = true;
 451  
         }
 452  
 
 453  
         // Now, we can actually do the job. This is delegate into smaller
 454  
         // method, for easier understanding.
 455  0
         if (bChunkEnabled) {
 456  
             // No more than one file, when in chunk mode.
 457  0
             if (packet.size() > 1) {
 458  0
                 throw new JUploadException(
 459  
                         "totalContentLength >= chunkSize: this.filesToUpload.length should not be more than 1 (doUpload)");
 460  
             }
 461  0
             doChunkedUpload(packet);
 462  
         } else {
 463  0
             doNonChunkedUpload(packet, totalContentLength, totalFileLength);
 464  
         }
 465  
 
 466  
         // If the request properly finished, we remove the files from the list
 467  
         // of files to upload.
 468  0
         if (this.fileUploadManagerThread.getUploadException() == null
 469  0
                 && !this.fileUploadManagerThread.isUploadStopped()) {
 470  0
             this.fileUploadManagerThread.currentRequestIsFinished(packet);
 471  
         }
 472  0
     }
 473  
 
 474  
     /**
 475  
      * Execution of an upload, in chunk mode. This method expects that the given packet contains only one file.
 476  
      * 
 477  
      * @param packet The packet that contains the file to upload in chunk mode
 478  
      * @throws JUploadException When any error occurs, or when there is more than one file in packet.
 479  
      * @throws JUploadInterrupted Thrown when an interruption of the thread is detected.
 480  
      */
 481  
     void doChunkedUpload(UploadFilePacket packet) throws JUploadException, JUploadInterrupted {
 482  0
         boolean bLastChunk = false;
 483  0
         int chunkPart = 0;
 484  
 
 485  0
         long contentLength = 0;
 486  0
         long thisChunkSize = 0;
 487  
 
 488  0
         if (packet.size() > 1) {
 489  0
             throw new JUploadException("doChunkedUpload called with a packet of more than 1 file (" + packet.size()
 490  
                     + " files)");
 491  
         }
 492  0
         UploadFileData uploadFileData = packet.get(0);
 493  
 
 494  
         // This while enables the chunk management:
 495  
         // In chunk mode, it loops until the last chunk is uploaded. This works
 496  
         // only because, in chunk mode, files are uploaded one y one (the for
 497  
         // loop within the while loops through ... 1 unique file).
 498  
         // In normal mode, it does nothing, as the bLastChunk is set to true in
 499  
         // the first test, within the while.
 500  0
         while (!bLastChunk && !this.fileUploadManagerThread.isUploadFinished()) {
 501  
             // Let's manage chunk:
 502  
             // Files are uploaded one by one. This is checked just above.
 503  0
             chunkPart += 1;
 504  0
             bLastChunk = (uploadFileData.getRemainingLength() <= this.maxChunkSize);
 505  
 
 506  
             // Is this the last chunk ?
 507  0
             if (bLastChunk) {
 508  0
                 thisChunkSize = uploadFileData.getRemainingLength();
 509  
             } else {
 510  0
                 thisChunkSize = this.maxChunkSize;
 511  
             }
 512  0
             contentLength = thisChunkSize + getAdditionnalBytesForUpload(uploadFileData);
 513  
 
 514  
             // We are about to start a new upload.
 515  0
             this.fileUploadManagerThread.setUploadStatus(packet, uploadFileData,
 516  
                     FileUploadManagerThread.UPLOAD_STATUS_UPLOADING);
 517  
 
 518  
             // Ok, we've prepare the job for chunk upload. Let's do it!
 519  0
             startRequest(contentLength, true, chunkPart, bLastChunk);
 520  
 
 521  
             try {
 522  
 
 523  
                 // Let's add any file-specific header.
 524  0
                 beforeFile(packet, uploadFileData);
 525  
 
 526  
                 // Actual upload of the file:
 527  0
                 uploadFileData.uploadFile(getOutputStream(), thisChunkSize);
 528  
 
 529  
                 // Caution : this is for debug only. In production mode,
 530  
                 // sendResumableError should always be 'false'
 531  0
                 if (chunkPart == 2 && sendResumableError) {
 532  0
                     sendResumableError = false;
 533  0
                     throw new JUploadException(new SocketException(
 534  
                             "This is a debug error. Should not happen in production."));
 535  
                 }
 536  
 
 537  
                 // If we are not in chunk mode, or if it was the last chunk,
 538  
                 // upload should be finished.
 539  0
                 if (bLastChunk && uploadFileData.getRemainingLength() > 0) {
 540  0
                     throw new JUploadExceptionUploadFailed("Files has not be entirely uploaded. The remaining size is "
 541  0
                             + uploadFileData.getRemainingLength() + " bytes. File size was: "
 542  0
                             + uploadFileData.getUploadLength() + " bytes.");
 543  
 
 544  
                 }
 545  
                 // Let's add any file-specific header.
 546  0
                 afterFile(uploadFileData);
 547  
 
 548  
                 // Let's finish the request, and wait for the server Output, if
 549  
                 // any (not applicable in FTP)
 550  0
                 int status = finishRequest();
 551  
 
 552  0
                 if (bLastChunk) {
 553  
                     // We are finished with this one. Let's display it.
 554  0
                     this.fileUploadManagerThread.setUploadStatus(packet, uploadFileData,
 555  
                             FileUploadManagerThread.UPLOAD_STATUS_FILE_UPLOADED_WAITING_FOR_RESPONSE);
 556  
                 } else {
 557  
                     // We are finished with the current chunk, but not with the
 558  
                     // file. Let's display it.
 559  0
                     this.fileUploadManagerThread.setUploadStatus(packet, uploadFileData,
 560  
                             FileUploadManagerThread.UPLOAD_STATUS_CHUNK_UPLOADED_WAITING_FOR_RESPONSE);
 561  
                 }
 562  
 
 563  
                 // We now ask to the uploadPolicy, if it was a success.
 564  
                 // If not, the isUploadSuccessful should raise an exception.
 565  0
                 this.uploadPolicy.checkUploadSuccess(status, getResponseMsg(), getResponseBody());
 566  0
             }// try
 567  
             finally {
 568  0
                 cleanRequest();
 569  0
             }
 570  
         }// while
 571  
          // Let's tell our manager that we've done the job!
 572  0
         this.fileUploadManagerThread.anotherFileHasBeenSent(packet, uploadFileData);
 573  
 
 574  0
     }// doChunkedUpload
 575  
 
 576  
     /**
 577  
      * Execution of an upload, in standard mode. This method uploads all files in the given packet.
 578  
      * 
 579  
      * @param packet The files to upload in the current request to the server
 580  
      * @param totalContentLength The total size of the upload, including any protocol-specific header or footer.
 581  
      * @param totalFileLength The sum of each file length.
 582  
      * @throws JUploadException When any error occurs
 583  
      * @throws JUploadInterrupted Thrown when an interruption of the thread is detected.
 584  
      */
 585  
     void doNonChunkedUpload(UploadFilePacket packet, final long totalContentLength, final long totalFileLength)
 586  
             throws JUploadException, JUploadInterrupted {
 587  
 
 588  
         // First step is to prepare all files.
 589  0
         startRequest(totalContentLength, false, 0, true);
 590  
 
 591  
         try {
 592  
 
 593  
             // Then, upload each file.
 594  0
             for (UploadFileData uploadFileData : packet) {
 595  0
                 if (this.fileUploadManagerThread.isUploadFinished()) {
 596  
                     // Upload is finished (by the user or because of an error,
 597  
                     // or instance)
 598  0
                     break;
 599  
                 }
 600  
                 // We are about to start a new upload.
 601  0
                 this.fileUploadManagerThread.setUploadStatus(packet, uploadFileData,
 602  
                         FileUploadManagerThread.UPLOAD_STATUS_UPLOADING);
 603  
 
 604  
                 // Let's add any file-specific header.
 605  0
                 beforeFile(packet, uploadFileData);
 606  
 
 607  
                 // Actual upload of the file:
 608  0
                 if (!this.fileUploadManagerThread.isUploadFinished()) {
 609  0
                     uploadFileData.uploadFile(getOutputStream(), uploadFileData.getUploadLength());
 610  
                 }
 611  
 
 612  
                 // Let's add any file-specific header.
 613  0
                 if (!this.fileUploadManagerThread.isUploadFinished()) {
 614  0
                     afterFile(uploadFileData);
 615  
 
 616  
                     // Let's tell our manager that we've done the job!
 617  
                     // Ok, maybe the server will refuse it, but we won't say
 618  
                     // that
 619  
                     // now!
 620  0
                     this.fileUploadManagerThread.anotherFileHasBeenSent(packet, uploadFileData);
 621  
                 }
 622  0
             }
 623  
 
 624  
             // We are finished with this one. Let's display it.
 625  0
             if (!this.fileUploadManagerThread.isUploadFinished()) {
 626  0
                 this.fileUploadManagerThread.setUploadStatus(packet, packet.get(packet.size() - 1),
 627  
                         FileUploadManagerThread.UPLOAD_STATUS_FILE_UPLOADED_WAITING_FOR_RESPONSE);
 628  
 
 629  
                 // Let's finish the request, and wait for the server Output, if
 630  
                 // any (not applicable in FTP)
 631  0
                 int status = finishRequest();
 632  
 
 633  
                 // We now ask to the uploadPolicy, if it was a success.
 634  
                 // If not, the isUploadSuccessful should raise an exception.
 635  0
                 this.uploadPolicy.checkUploadSuccess(status, getResponseMsg(), getResponseBody());
 636  
             }
 637  0
         }// try
 638  
         finally {
 639  0
             cleanRequest();
 640  0
         }
 641  
 
 642  0
     }// doNonChunkedUpload
 643  
 
 644  
     /**
 645  
      * Clean any resource of the last attempt for this packet, which would be in inconsistent step, in order to retry
 646  
      * the upload of the current packet.
 647  
      */
 648  
     private void beforeRetry(UploadFilePacket packet) throws JUploadException {
 649  0
         if (packet != null) {
 650  0
             for (UploadFileData uploadFileData : packet) {
 651  0
                 if (uploadFileData.isPreparedForUpload()) {
 652  0
                     uploadFileData.beforeRetry();
 653  
                 }
 654  0
             }
 655  
         }
 656  0
     }
 657  
 
 658  
     /** @see FileUploadThread#close() */
 659  
     public void close() {
 660  
         try {
 661  0
             cleanAll();
 662  0
         } catch (JUploadException e) {
 663  0
             this.uploadPolicy.displayErr(e);
 664  0
         }
 665  0
     }
 666  
 
 667  
     /**
 668  
      * Replace single \r and \n by uniform end of line characters (CRLF). This makes it easier, to search for string
 669  
      * within the body.
 670  
      * 
 671  
      * @param s The original string
 672  
      * @return The string with single \r and \n modified changed to CRLF (\r\n).
 673  
      */
 674  
     public final String normalizeCRLF(String s) {
 675  0
         Pattern p = Pattern.compile("\\r\\n|\\r|\\n", Pattern.MULTILINE);
 676  0
         String[] lines = p.split(s);
 677  
         // Worst case: the s string contains only \n or \r characters: we then
 678  
         // need to triple the string length. Let's say double is enough.
 679  0
         StringBuffer sb = new StringBuffer(s.length() * 2);
 680  0
         for (int i = 0; i < lines.length; i += 1) {
 681  0
             sb.append(lines[i]).append("\r\n");
 682  
         }
 683  
 
 684  0
         return sb.toString();
 685  
     }
 686  
 
 687  
     /**
 688  
      * Replace \r and \n by correctly displayed end of line characters. Used to display debug output. It also replace
 689  
      * any single \r or \n by \r\n, to make it easier, to search for string within the body.
 690  
      * 
 691  
      * @param s The original string
 692  
      * @return The string with \r and \n modified, to be correctly displayed.
 693  
      */
 694  
     public final String quoteCRLF(String s) {
 695  0
         return s.replaceAll("\r\n", "\\\\r\\\\n\n");
 696  
     }
 697  
 
 698  
     /**
 699  
      * {@inheritDoc}
 700  
      */
 701  
     public void setFileUploadThreadManager(FileUploadManagerThread fileUploadManagerThread) throws JUploadException {
 702  0
         if (this.fileUploadManagerThread != null) {
 703  0
             throw new JUploadException(
 704  
                     "Can not override fileUploadManagerThread (in DefaultFileUpload.setFileUploadThreadManager()");
 705  
         }
 706  0
         this.fileUploadManagerThread = fileUploadManagerThread;
 707  0
     }
 708  
 }