source: 3thparty/jupload/src/main/java/wjhk/jupload2/upload/DefaultFileUploadThread.java @ 3951

Revision 3951, 27.0 KB checked in by alexandrecorreia, 13 years ago (diff)

Ticket #1709 - Adicao de codigo fonte java do componente jupload

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