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 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 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 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 static boolean sendResumableError = false; 85 86 /** 87 * Current retry number, for the resume upload feature. The first try occurs with nbRetry=0. 88 */ 89 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 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 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 super(threadName); 116 117 // Specific stuff. 118 this.packetQueue = packetQueue; 119 this.uploadPolicy = uploadPolicy; 120 this.fileUploadManagerThread = fileUploadManagerThread; 121 // Let's read up to date upload parameters. 122 this.maxChunkSize = this.uploadPolicy.getMaxChunkSize(); 123 124 this.uploadPolicy.displayDebug("DefaultFileUploadThread created", 30); 125 } 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 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 return this.responseMsg; 232 } 233 234 /** 235 * @return the packetQueue 236 */ 237 public BlockingQueue<UploadFilePacket> getPacketQueue() { 238 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 this.responseBody = normalizeCRLF(body); 248 } 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 this.responseMsg = normalizeCRLF(msg); 257 } 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 this.uploadPolicy.displayDebug("Start of the FileUploadThread", 5); 270 int nbSentFiles = 0; 271 272 // We'll stop the upload if an error occurs. So the try/catch is 273 // outside the while. 274 while (!this.fileUploadManagerThread.isUploadFinished()) { 275 UploadFilePacket packet = null; 276 277 try { 278 // We take the next packet. This method will block until a packet is ready. 279 packet = packetQueue.take(); 280 281 // If the packet is 'poisonned', then it's the standard end of work. 282 if (packet.isPoisonned()) { 283 break; 284 } 285 286 // ///////////////////////////////////////////////////////////////////////////////// 287 // Let's go to work : THIS IS THE UPLOAD, surrounded by the 288 // RESUME LOOP 289 // ///////////////////////////////////////////////////////////////////////////////// 290 nbRetry = 0; 291 while (true) { 292 try { 293 // Let's try to upload the current packet. 294 doUpload(packet); 295 296 // If we are here, the last upload is a success. Let's 297 // exit the loop. 298 break; 299 } catch (JUploadException jue) { 300 // manageRetry throw the exception, if no retry should 301 // be done. 302 manageRetry(jue); 303 304 // If we are here, the applet should retry the upload. 305 // We let it loop again. 306 nbRetry += 1; 307 308 // We must clean the resources of the previous attempt. 309 beforeRetry(packet); 310 } 311 }// while(resume) 312 // ///////////////////////////////////////////////////////////////////////////////// 313 // //////////////// ENF OF RESUME LOOP 314 // ///////////////////////////////////////////////////////////////////////////////// 315 316 this.uploadPolicy.displayDebug("After do upload", 50); 317 } catch (InterruptedException e) { 318 this.uploadPolicy.displayWarn(this.getClass().getName() + ".run(): received in " 319 + e.getClass().getName() + ", exiting..."); 320 break; 321 } catch (JUploadException e) { 322 if (this.fileUploadManagerThread.isUploadFinished()) { 323 // We ignore the error 324 this.uploadPolicy.displayWarn("Ignoring " + e.getClass().getName() + " because upload is finished"); 325 } else { 326 this.fileUploadManagerThread.setUploadException(e); 327 } 328 } catch (JUploadInterrupted e) { 329 // The upload has been interrupted, probably by the user 330 // (stop 331 // button). The fileManagerUploadThread aleady knows this. 332 this.uploadPolicy.displayInfo("Upload stopped by the user"); 333 this.uploadPolicy.displayDebug(e.getMessage(), 30); 334 } 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 for (UploadFileData uploadFileData : packet) { 340 if (uploadFileData.isPreparedForUpload()) { 341 uploadFileData.afterUpload(); 342 } 343 } 344 // We free any resources associated with this thread. 345 try { 346 cleanAll(); 347 } catch (Exception e) { 348 // We ignore the error 349 this.uploadPolicy.displayWarn("[run(), After cleanAll] Ignoring " + e.getClass().getName() 350 + " because upload is finished"); 351 } 352 } 353 354 nbSentFiles += packet.size(); 355 }// while (!isUploadFinished) 356 357 this.uploadPolicy.displayDebug("End of the FileUploadThread (" + nbSentFiles + " have been sent)", 5); 358 }// run 359 360 /** 361 * @param jue 362 * @throws JUploadException 363 */ 364 private void manageRetry(JUploadException jue) throws JUploadException { 365 String exceptionCauseClass = (jue.getCause() == null) ? "no exception cause" : jue.getCause().getClass() 366 .getName(); 367 String errMsg = jue.getClass().getName() + " (" + jue.getMessage() + "), caused by: " + exceptionCauseClass; 368 369 if (this.fileUploadManagerThread.isUploadFinished()) { 370 // The upload is stopped. This error may be caused 371 this.uploadPolicy.displayWarn("The following error occurs, but the upload is stopped, ignoring it ]" 372 + errMsg + "]"); 373 throw jue; 374 } else if (jue.getCause() instanceof SocketException) { 375 this.uploadPolicy.displayWarn("A 'resumable' error occurred: " + errMsg); 376 // If it was the last retry, we stop here. 377 if (nbRetry >= this.uploadPolicy.getRetryMaxNumberOf()) { 378 this.uploadPolicy.displayWarn("Too much retries (" + nbRetry + "), exiting..."); 379 throw jue; 380 } 381 382 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 if (dialogUploadRetry.isRetryValidated()) { 387 this.uploadPolicy.displayDebug("The user (or the timer) choosed to retry the upload", 30); 388 } else { 389 this.uploadPolicy.displayDebug("The user refuses to retry the upload, exiting...", 30); 390 // No retry, let's note the exception and go out 391 throw jue; 392 }// End of resumable exceptions management. 393 } else { 394 // This exception can't be resumed. We transmit it. 395 this.uploadPolicy.displayWarn("Non resumable error occured, exiting..."); 396 throw jue; 397 } 398 } 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 boolean bChunkEnabled = false; 412 long totalContentLength = 0; 413 long totalFileLength = 0; 414 415 // We are about to start a new upload. 416 this.fileUploadManagerThread.setUploadStatus(packet, packet.get(0), 417 FileUploadManagerThread.UPLOAD_STATUS_UPLOADING); 418 419 // Prepare upload, for all files to be uploaded. 420 beforeRequest(packet); 421 422 for (UploadFileData uploadFileData : packet) { 423 // The upload may be finished, while we're working on the files... 424 if (this.fileUploadManagerThread.isUploadFinished()) { 425 // Let's stop our work here. 426 return; 427 } 428 // Total length, for HTTP upload. 429 totalContentLength += uploadFileData.getUploadLength(); 430 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 totalFileLength += uploadFileData.getUploadLength(); 434 435 this.uploadPolicy.displayDebug( 436 "file " + uploadFileData.getFileName() + ": content=" + uploadFileData.getUploadLength() 437 + " bytes, getAdditionnalBytesForUpload=" + getAdditionnalBytesForUpload(uploadFileData) 438 + " bytes", 50); 439 }// for 440 441 // Ok, now we check that the totalContentLength is less than the chunk 442 // size. 443 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 if (packet.size() > 1) { 447 this.fileUploadManagerThread.setUploadException(new JUploadException( 448 "totalContentLength >= chunkSize: this.filesToUpload.length should be 1 (doUpload)")); 449 } 450 bChunkEnabled = true; 451 } 452 453 // Now, we can actually do the job. This is delegate into smaller 454 // method, for easier understanding. 455 if (bChunkEnabled) { 456 // No more than one file, when in chunk mode. 457 if (packet.size() > 1) { 458 throw new JUploadException( 459 "totalContentLength >= chunkSize: this.filesToUpload.length should not be more than 1 (doUpload)"); 460 } 461 doChunkedUpload(packet); 462 } else { 463 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 if (this.fileUploadManagerThread.getUploadException() == null 469 && !this.fileUploadManagerThread.isUploadStopped()) { 470 this.fileUploadManagerThread.currentRequestIsFinished(packet); 471 } 472 } 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 boolean bLastChunk = false; 483 int chunkPart = 0; 484 485 long contentLength = 0; 486 long thisChunkSize = 0; 487 488 if (packet.size() > 1) { 489 throw new JUploadException("doChunkedUpload called with a packet of more than 1 file (" + packet.size() 490 + " files)"); 491 } 492 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 while (!bLastChunk && !this.fileUploadManagerThread.isUploadFinished()) { 501 // Let's manage chunk: 502 // Files are uploaded one by one. This is checked just above. 503 chunkPart += 1; 504 bLastChunk = (uploadFileData.getRemainingLength() <= this.maxChunkSize); 505 506 // Is this the last chunk ? 507 if (bLastChunk) { 508 thisChunkSize = uploadFileData.getRemainingLength(); 509 } else { 510 thisChunkSize = this.maxChunkSize; 511 } 512 contentLength = thisChunkSize + getAdditionnalBytesForUpload(uploadFileData); 513 514 // We are about to start a new upload. 515 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 startRequest(contentLength, true, chunkPart, bLastChunk); 520 521 try { 522 523 // Let's add any file-specific header. 524 beforeFile(packet, uploadFileData); 525 526 // Actual upload of the file: 527 uploadFileData.uploadFile(getOutputStream(), thisChunkSize); 528 529 // Caution : this is for debug only. In production mode, 530 // sendResumableError should always be 'false' 531 if (chunkPart == 2 && sendResumableError) { 532 sendResumableError = false; 533 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 if (bLastChunk && uploadFileData.getRemainingLength() > 0) { 540 throw new JUploadExceptionUploadFailed("Files has not be entirely uploaded. The remaining size is " 541 + uploadFileData.getRemainingLength() + " bytes. File size was: " 542 + uploadFileData.getUploadLength() + " bytes."); 543 544 } 545 // Let's add any file-specific header. 546 afterFile(uploadFileData); 547 548 // Let's finish the request, and wait for the server Output, if 549 // any (not applicable in FTP) 550 int status = finishRequest(); 551 552 if (bLastChunk) { 553 // We are finished with this one. Let's display it. 554 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 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 this.uploadPolicy.checkUploadSuccess(status, getResponseMsg(), getResponseBody()); 566 }// try 567 finally { 568 cleanRequest(); 569 } 570 }// while 571 // Let's tell our manager that we've done the job! 572 this.fileUploadManagerThread.anotherFileHasBeenSent(packet, uploadFileData); 573 574 }// 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 startRequest(totalContentLength, false, 0, true); 590 591 try { 592 593 // Then, upload each file. 594 for (UploadFileData uploadFileData : packet) { 595 if (this.fileUploadManagerThread.isUploadFinished()) { 596 // Upload is finished (by the user or because of an error, 597 // or instance) 598 break; 599 } 600 // We are about to start a new upload. 601 this.fileUploadManagerThread.setUploadStatus(packet, uploadFileData, 602 FileUploadManagerThread.UPLOAD_STATUS_UPLOADING); 603 604 // Let's add any file-specific header. 605 beforeFile(packet, uploadFileData); 606 607 // Actual upload of the file: 608 if (!this.fileUploadManagerThread.isUploadFinished()) { 609 uploadFileData.uploadFile(getOutputStream(), uploadFileData.getUploadLength()); 610 } 611 612 // Let's add any file-specific header. 613 if (!this.fileUploadManagerThread.isUploadFinished()) { 614 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 this.fileUploadManagerThread.anotherFileHasBeenSent(packet, uploadFileData); 621 } 622 } 623 624 // We are finished with this one. Let's display it. 625 if (!this.fileUploadManagerThread.isUploadFinished()) { 626 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 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 this.uploadPolicy.checkUploadSuccess(status, getResponseMsg(), getResponseBody()); 636 } 637 }// try 638 finally { 639 cleanRequest(); 640 } 641 642 }// 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 if (packet != null) { 650 for (UploadFileData uploadFileData : packet) { 651 if (uploadFileData.isPreparedForUpload()) { 652 uploadFileData.beforeRetry(); 653 } 654 } 655 } 656 } 657 658 /** @see FileUploadThread#close() */ 659 public void close() { 660 try { 661 cleanAll(); 662 } catch (JUploadException e) { 663 this.uploadPolicy.displayErr(e); 664 } 665 } 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 Pattern p = Pattern.compile("\\r\\n|\\r|\\n", Pattern.MULTILINE); 676 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 StringBuffer sb = new StringBuffer(s.length() * 2); 680 for (int i = 0; i < lines.length; i += 1) { 681 sb.append(lines[i]).append("\r\n"); 682 } 683 684 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 return s.replaceAll("\r\n", "\\\\r\\\\n\n"); 696 } 697 698 /** 699 * {@inheritDoc} 700 */ 701 public void setFileUploadThreadManager(FileUploadManagerThread fileUploadManagerThread) throws JUploadException { 702 if (this.fileUploadManagerThread != null) { 703 throw new JUploadException( 704 "Can not override fileUploadManagerThread (in DefaultFileUpload.setFileUploadThreadManager()"); 705 } 706 this.fileUploadManagerThread = fileUploadManagerThread; 707 } 708 }