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

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

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

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