001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU General
007: * Public License Version 2 only ("GPL") or the Common Development and Distribution
008: * License("CDDL") (collectively, the "License"). You may not use this file except in
009: * compliance with the License. You can obtain a copy of the License at
010: * http://www.netbeans.org/cddl-gplv2.html or nbbuild/licenses/CDDL-GPL-2-CP. See the
011: * License for the specific language governing permissions and limitations under the
012: * License. When distributing the software, include this License Header Notice in
013: * each file and include the License file at nbbuild/licenses/CDDL-GPL-2-CP. Sun
014: * designates this particular file as subject to the "Classpath" exception as
015: * provided by Sun in the GPL Version 2 section of the License file that
016: * accompanied this code. If applicable, add the following below the License Header,
017: * with the fields enclosed by brackets [] replaced by your own identifying
018: * information: "Portions Copyrighted [year] [name of copyright owner]"
019: *
020: * Contributor(s):
021: *
022: * The Original Software is NetBeans. The Initial Developer of the Original Software
023: * is Sun Microsystems, Inc. Portions Copyright 1997-2007 Sun Microsystems, Inc. All
024: * Rights Reserved.
025: *
026: * If you wish your version of this file to be governed by only the CDDL or only the
027: * GPL Version 2, indicate your decision by adding "[Contributor] elects to include
028: * this software in this distribution under the [CDDL or GPL Version 2] license." If
029: * you do not indicate a single choice of license, a recipient has the option to
030: * distribute your version of this file under either the CDDL, the GPL Version 2 or
031: * to extend the choice of license to its licensees as provided above. However, if
032: * you add GPL Version 2 code and therefore, elected the GPL Version 2 license, then
033: * the option applies only if the new code is made subject to such option by the
034: * copyright holder.
035: */
036:
037: package org.netbeans.installer.sandbox.download;
038:
039: import java.io.File;
040: import java.io.IOException;
041: import java.net.URI;
042: import java.security.NoSuchAlgorithmException;
043: import java.util.Date;
044: import java.util.Timer;
045: import java.util.TimerTask;
046: import java.util.Vector;
047: import org.netbeans.installer.utils.FileUtils;
048: import org.netbeans.installer.utils.ErrorManager;
049: import org.netbeans.installer.utils.exceptions.DownloadException;
050: import org.netbeans.installer.utils.helper.ErrorLevel;
051: import org.netbeans.installer.utils.LogManager;
052:
053: /**
054: * This class represents an individual download.
055: *
056: * @author Kirill Sorokin
057: */
058: public class Download {
059: private URI uri;
060:
061: private File destination;
062:
063: private File tempFile;
064:
065: private DownloadOptions options;
066:
067: private Vector<DownloadThread> activeThreads = new Vector<DownloadThread>();
068: private Vector<DownloadThread> finishedThreads = new Vector<DownloadThread>();
069:
070: private long contentLength = UNDEFINED_LENGTH;
071:
072: private long readLength = ZERO_LENGTH;
073:
074: private Date lastModified;
075:
076: private long currentSpeed = ZERO_SPEED;
077:
078: private int errorsNumber = INITIAL_ERRORS_NUMBER;
079:
080: private Vector<DownloadListener> listeners = new Vector<DownloadListener>();
081:
082: private Timer statusTimer = new Timer();
083:
084: Download(URI anURI, File aDestination, DownloadOptions someOptions,
085: DownloadListener aListener) {
086: // save the download source and destination parameters
087: uri = anURI;
088: destination = aDestination;
089:
090: // save the download options
091: options = someOptions;
092:
093: if (aListener != null) {
094: addDownloadListener(aListener);
095: }
096: }
097:
098: // download control methods ////////////////////////////////////////////////
099: public void start() {
100: // notify the download state listeners of what's happening
101: notifyDownloadListeners(DownloadState.STARTED,
102: "Started download from " + uri, null);
103:
104: // log
105: LogManager.log("starting download from " + uri);
106: LogManager.log(" ... source uri: " + uri);
107: LogManager.log(" ... destination file: " + destination);
108:
109: // check the destination
110: if (!destination.exists()) {
111: File destinationParent = destination.getParentFile();
112: if (!destinationParent.exists()
113: && !destinationParent.mkdirs()) {
114: LogManager
115: .log(ErrorLevel.ERROR,
116: "... download failed -- cannot create the destination directory");
117: notifyDownloadListeners(
118: DownloadState.FAILED,
119: "Download failed - cannot create destination directory",
120: null);
121: return;
122: }
123: } else if (destination.isDirectory()) {
124: LogManager
125: .log(ErrorLevel.ERROR,
126: "... download failed -- the destination file is a directory");
127: notifyDownloadListeners(
128: DownloadState.FAILED,
129: "Download failed - destination file is a directory",
130: null);
131: return;
132: } else if (!destination.canRead()) {
133: LogManager
134: .log(ErrorLevel.ERROR,
135: "... download failed -- cannot read the destination file");
136: notifyDownloadListeners(
137: DownloadState.FAILED,
138: "Download failed - cannot read the destination file",
139: null);
140: return;
141: } else if (!destination.canWrite()) {
142: LogManager
143: .log(ErrorLevel.ERROR,
144: "... download failed -- cannot write to the destination file");
145: notifyDownloadListeners(
146: DownloadState.FAILED,
147: "Download failed - cannot write to the destination file",
148: null);
149: return;
150: }
151:
152: // create the temp file
153: try {
154: tempFile = FileUtils.createTempFile(destination
155: .getParentFile());
156: } catch (IOException e) {
157: LogManager
158: .log(ErrorLevel.ERROR,
159: "... download failed -- cannot create temporary file");
160: notifyDownloadListeners(DownloadState.FAILED,
161: "Download failed - cannot create temp file", e);
162: return;
163: }
164: LogManager.log(ErrorLevel.DEBUG, " ... temporary file: "
165: + tempFile);
166:
167: // create the initial thread
168: DownloadThread initialThread = new DownloadThread(this , uri,
169: tempFile, options);
170:
171: if (options.getInt(DownloadOptions.MAX_SPEED) != -1) {
172: initialThread.setMaximumSpeed(options
173: .getInt(DownloadOptions.MAX_SPEED));
174: }
175:
176: initialThread.start(false);
177: }
178:
179: public void cancel() {
180: LogManager.log("canceling download from " + uri);
181:
182: // cancel the threads
183: cancelAllThreads();
184:
185: LogManager.log(ErrorLevel.DEBUG, "... canceled successfully");
186:
187: // notify the download state listeners of what's happening
188: notifyDownloadListeners(DownloadState.CANCELED,
189: "Canceled download from " + uri, null);
190: }
191:
192: public void pause() {
193: LogManager.log("pausing download from " + uri);
194:
195: // call pauseThread() on each active thread
196: for (DownloadThread thread : activeThreads) {
197: thread.pauseThread();
198: }
199:
200: LogManager.log(ErrorLevel.DEBUG, "... paused successfully");
201: }
202:
203: public void resume() {
204: LogManager.log("resuming download from " + uri);
205:
206: // call resumeThread() on each paused thread
207: for (DownloadThread thread : activeThreads) {
208: if (thread.isPaused()) {
209: thread.resumeThread();
210: }
211: }
212:
213: LogManager.log(ErrorLevel.DEBUG, "... resumed successfully");
214: }
215:
216: // download <-> thread interaction /////////////////////////////////////////
217: synchronized void threadStarted(DownloadThread thread) {
218: LogManager.log(ErrorLevel.DEBUG,
219: " ... download thread started -- " + thread);
220:
221: notifyDownloadListeners(DownloadState.THREAD_STARTED,
222: "Thread Started");
223: }
224:
225: synchronized void threadRunning(DownloadThread thread) {
226: LogManager.log(ErrorLevel.DEBUG,
227: " ... download thread running -- " + thread);
228:
229: // handle the first thread specially - we need to get some
230: // information from it and perfrom some actions accordingly
231: if (activeThreads.size() == 0) {
232: // add the initial thread to the list
233: activeThreads.add(thread);
234:
235: // save the total content length
236: contentLength = thread.getContentLength();
237:
238: // save the last modified
239: lastModified = thread.getModificationTime();
240:
241: // check the download conditions and decide whether we should
242: // continue with downloading or the existing destination is OK
243: if (!checkConditions()) {
244: // check the available disk space
245: long availableSpace = FileUtils
246: .getFreeSpace(destination);
247: if ((availableSpace != -1)
248: && (availableSpace < contentLength)) {
249: cancelAllThreads();
250: LogManager
251: .log(ErrorLevel.ERROR,
252: "... download failed -- not enough disk space");
253: notifyDownloadListeners(DownloadState.FAILED,
254: "Not enough space");
255: return;
256: }
257:
258: // instruct the thread to start writing
259: thread.startWriting();
260:
261: // if the download source support partial downloads - start
262: // additional threads
263: if (thread.supportsRanges()) {
264: startAdditionalThreads(thread);
265: }
266:
267: TimerTask statusTask = new TimerTask() {
268: private long oldLength = 0;
269:
270: public void run() {
271: long length = readLength;
272:
273: currentSpeed = (long) (((double) (length - oldLength)) / 0.5);
274:
275: oldLength = length;
276: }
277: };
278:
279: statusTimer.schedule(statusTask, 0, 500);
280: } else {
281: readLength = contentLength; // we need to make the percentage to be 100%
282:
283: cancelAllThreads();
284: tempFile.delete();
285:
286: notifyDownloadListeners(DownloadState.COMPLETED,
287: "Download from " + uri + " completed");
288: return;
289: }
290: }
291:
292: // reset read length and get the read length for each thread and sum them
293: readLength = ZERO_LENGTH;
294: for (DownloadThread i : activeThreads) {
295: readLength += i.getReadLength();
296: }
297: for (DownloadThread i : finishedThreads) {
298: readLength += i.getReadLength();
299: }
300:
301: // notify the download progress listeners of what's happening
302: notifyDownloadListeners(DownloadState.RUNNING, "Thread Running");
303: }
304:
305: synchronized void threadPaused(DownloadThread thread) {
306: LogManager.log(ErrorLevel.DEBUG,
307: " ... download thread paused -- " + thread);
308:
309: notifyDownloadListeners(DownloadState.THREAD_PAUSED,
310: "Thread Paused");
311: }
312:
313: synchronized void threadResumed(DownloadThread thread) {
314: LogManager.log(ErrorLevel.DEBUG,
315: " ... download thread resumed -- " + thread);
316:
317: notifyDownloadListeners(DownloadState.THREAD_RESUMED,
318: "Thread Resumed");
319: }
320:
321: synchronized void threadCompleted(DownloadThread thread) {
322: LogManager.log(ErrorLevel.DEBUG,
323: " ... download thread completed -- " + thread);
324:
325: notifyDownloadListeners(DownloadState.THREAD_COMPLETED,
326: "Thread completed");
327:
328: activeThreads.remove(thread);
329: finishedThreads.add(thread);
330:
331: // reset read length and get the read length for each thread and sum them
332: readLength = ZERO_LENGTH;
333: for (DownloadThread i : activeThreads) {
334: readLength += i.getReadLength();
335: }
336: for (DownloadThread i : finishedThreads) {
337: readLength += i.getReadLength();
338: }
339:
340: // check whether there are any threads still downloading, if there
341: // are none - the download is complete
342: if (activeThreads.size() > 0) {
343: return;
344: }
345:
346: statusTimer.cancel();
347:
348: DownloadOptions verificationOptions = DownloadOptions
349: .getDefaults();
350: verificationOptions.put(DownloadOptions.MAX_THREADS, 1);
351: verificationOptions.put(DownloadOptions.MAX_ERRORS, 1);
352:
353: if (options.getBoolean(DownloadOptions.VERIFY_CRC)) {
354: LogManager.log(ErrorLevel.DEBUG,
355: " trying to verify the crc32 checksum");
356:
357: try {
358: String crc32 = FileUtils.readFile(
359: DownloadManager.getInstance().download(
360: uri.toString() + ".crc32",
361: verificationOptions)).trim();
362: if (!crc32.equals(FileUtils.getCrc32String(tempFile))) {
363: LogManager
364: .log(ErrorLevel.ERROR,
365: "... download failed -- crc32 verification failed");
366: notifyDownloadListeners(
367: DownloadState.FAILED,
368: "CRC32 Checksum verification failed for the downloaded file",
369: null);
370: return;
371: }
372: } catch (DownloadException e) {
373: LogManager.log(ErrorLevel.DEBUG,
374: " ... failed to verify the crc32 checksum");
375: } catch (IOException e) {
376: LogManager.log(ErrorLevel.DEBUG,
377: " ... failed to verify the crc32 checksum");
378: }
379: }
380:
381: if (options.getBoolean(DownloadOptions.VERIFY_MD5)) {
382: LogManager.log(ErrorLevel.DEBUG,
383: " trying to verify the md5 checksum");
384:
385: try {
386: String md5 = FileUtils.readFile(
387: DownloadManager.getInstance().download(
388: uri.toString() + ".md5",
389: verificationOptions)).trim();
390: if (!md5.equals(FileUtils.getMd5String(tempFile))) {
391: LogManager
392: .log(ErrorLevel.ERROR,
393: "... download failed -- md5 verification failed");
394: notifyDownloadListeners(
395: DownloadState.FAILED,
396: "MD5 Checksum verification failed for the downloaded file",
397: null);
398: return;
399: }
400: } catch (DownloadException e) {
401: LogManager.log(ErrorLevel.DEBUG,
402: " ... failed to verify the md5 checksum");
403: } catch (IOException e) {
404: LogManager.log(ErrorLevel.DEBUG,
405: " ... failed to verify the md5 checksum");
406: } catch (NoSuchAlgorithmException e) {
407: LogManager.log(ErrorLevel.DEBUG,
408: " ... failed to verify the md5 checksum");
409: }
410: }
411:
412: if (destination.exists()) {
413: if (!destination.delete()) {
414: LogManager
415: .log(ErrorLevel.ERROR,
416: "... download failed -- cannot delete the existing destination file");
417: notifyDownloadListeners(DownloadState.FAILED,
418: "Failed to delete the existing file....", null);
419: return;
420: }
421: }
422:
423: try {
424: FileUtils.moveFile(tempFile, destination);
425: } catch (IOException e) {
426: LogManager
427: .log(
428: ErrorLevel.ERROR,
429: "... download failed -- cannot rename the temporary file to the destination file");
430: notifyDownloadListeners(DownloadState.FAILED,
431: "Failed to rename the temporary file....", null);
432: return;
433: }
434:
435: if (lastModified != null) {
436: destination.setLastModified(lastModified.getTime());
437: }
438:
439: LogManager.log(ErrorLevel.DEBUG, "... download from " + uri
440: + " completed");
441: notifyDownloadListeners(DownloadState.COMPLETED,
442: "Download from " + uri + " completed");
443: return;
444: }
445:
446: synchronized void threadFailed(DownloadThread thread,
447: Throwable exception) {
448: final int maxErrors = options
449: .getInt(DownloadOptions.MAX_ERRORS);
450:
451: LogManager.log(ErrorLevel.DEBUG,
452: " ... download thread failed -- " + thread
453: + "; errors/max -- " + (errorsNumber + 1) + "/"
454: + maxErrors);
455:
456: // notify the download progress listeners of what's happening
457: notifyDownloadListeners(DownloadState.THREAD_FAILED,
458: "Thread Failed", exception);
459:
460: // increment the errors number counter
461: errorsNumber++;
462:
463: // if we have reached the maximum number of possible errors per
464: // download - cancelThread everything and fail the download, restart the
465: // thread otherwise
466: if (errorsNumber == maxErrors) {
467: // cancel all threads
468: cancelAllThreads();
469:
470: statusTimer.cancel();
471:
472: // delete the temp file
473: tempFile.delete();
474:
475: LogManager.log(ErrorLevel.ERROR,
476: "... download failed -- maximum numbers of errors ("
477: + maxErrors + ") exceeded");
478:
479: // notify the download progress listeners of what's happening
480: notifyDownloadListeners(DownloadState.FAILED,
481: "Error occurred in downloading from " + uri);
482: } else {
483: // restart the thread
484: restartThread(thread);
485: }
486: }
487:
488: synchronized void threadCanceled(DownloadThread thread) {
489: LogManager.log(ErrorLevel.DEBUG,
490: " ... download thread canceled -- " + thread);
491:
492: notifyDownloadListeners(DownloadState.THREAD_CANCELED,
493: "Thread Canceled");
494: }
495:
496: // getters & setters ///////////////////////////////////////////////////////
497: public URI getURI() {
498: return uri;
499: }
500:
501: public File getDestination() {
502: return destination;
503: }
504:
505: public File getTempFile() {
506: return tempFile;
507: }
508:
509: public DownloadOptions getOptions() {
510: return options;
511: }
512:
513: public long getContentLength() {
514: return contentLength;
515: }
516:
517: public long getReadLength() {
518: return readLength;
519: }
520:
521: public Date getLastModified() {
522: return lastModified;
523: }
524:
525: public long getCurrentSpeed() {
526: return currentSpeed;
527: }
528:
529: public int getErrorsNumber() {
530: return errorsNumber;
531: }
532:
533: public int getPercentage() {
534: if (contentLength != UNDEFINED_LENGTH) {
535: return (int) (readLength * 100 / contentLength);
536: } else {
537: return ZERO_LENGTH;
538: }
539: }
540:
541: // private stuff ///////////////////////////////////////////////////////////
542: private void cancelAllThreads() {
543: // call cancelThread() on each active thread
544: for (DownloadThread thread : activeThreads) {
545: thread.cancelThread();
546: }
547: }
548:
549: private void startAdditionalThreads(DownloadThread initialThread) {
550: // assertion
551: assert initialThread.supportsRanges();
552:
553: // get the maximum threads number
554: int threadsNumber = options.getInt(DownloadOptions.MAX_THREADS);
555:
556: // get the length to read per thread
557: long chunk = contentLength / threadsNumber;
558:
559: // if the chunk is not zero length
560: if (chunk != ZERO_LENGTH) {
561: // correct the langth of the first thread
562: initialThread.setLength(chunk);
563:
564: int speedPerThread;
565: if (options.getInt(DownloadOptions.MAX_SPEED) != -1) {
566: speedPerThread = options
567: .getInt(DownloadOptions.MAX_SPEED)
568: / options.getInt(DownloadOptions.MAX_THREADS);
569: } else {
570: speedPerThread = -1;
571: }
572:
573: initialThread.setMaximumSpeed(speedPerThread);
574:
575: // start additional threads
576: for (int i = 1; i < threadsNumber; i++) {
577: // set the offset and length for each started thread; the length for
578: // the last thread will be undefined as it will read as much as
579: // possible
580: long offset = i * chunk;
581: long length = (i != threadsNumber - 1) ? chunk
582: : UNDEFINED_LENGTH;
583:
584: // create the thread
585: DownloadThread thread = new DownloadThread(this , uri,
586: tempFile, offset, length, options);
587:
588: thread.setMaximumSpeed(speedPerThread);
589:
590: // start the thread, note that since it's not the first thread
591: // it can start writing from the very beginning
592: thread.start(true);
593:
594: // add it to the active threads list
595: activeThreads.add(thread);
596: }
597: }
598: }
599:
600: private void restartThread(DownloadThread failedThread) {
601: // get the thread's properties
602: long offset = failedThread.getOffset()
603: + failedThread.getReadLength();
604: long length = failedThread.getLength();
605:
606: // if length is finite - truncate it by the read length amount
607: if (length != UNDEFINED_LENGTH) {
608: length = length - failedThread.getReadLength();
609: }
610:
611: activeThreads.remove(failedThread);
612: finishedThreads.add(failedThread);
613:
614: // create a new thread with the settings of the failed one
615: DownloadThread thread = new DownloadThread(this , uri, tempFile,
616: offset, length, options);
617:
618: // add it to the active threads list
619: activeThreads.add(thread);
620:
621: int speedPerThread;
622: if (options.getInt(DownloadOptions.MAX_SPEED) != -1) {
623: thread.setMaximumSpeed(options
624: .getInt(DownloadOptions.MAX_SPEED)
625: / options.getInt(DownloadOptions.MAX_THREADS));
626: }
627:
628: // register this download as the listener for thread's events and start the
629: // thread
630: thread.start(true);
631: }
632:
633: private boolean checkConditions() {
634: boolean checkExistance = options
635: .getBoolean(DownloadOptions.CHECK_EXISTANCE);
636: boolean checkSize = options
637: .getBoolean(DownloadOptions.CHECK_SIZE);
638: boolean checkLastModified = options
639: .getBoolean(DownloadOptions.CHECK_LAST_MODIFIED);
640:
641: if (checkExistance || checkSize || checkLastModified) {
642: if (checkExistance && !destination.exists()) {
643: return false;
644: }
645: if (checkSize && (destination.length() != contentLength)) {
646: return false;
647: }
648: if (checkLastModified
649: && (destination.lastModified() < lastModified
650: .getTime())) {
651: return false;
652: }
653:
654: return true;
655: } else {
656: return false;
657: }
658: }
659:
660: // listeners ///////////////////////////////////////////////////////////////
661: public void addDownloadListener(DownloadListener listener) {
662: synchronized (listeners) {
663: listeners.add(listener);
664: }
665: }
666:
667: public void removeDownloadListener(DownloadListener listener) {
668: synchronized (listeners) {
669: listeners.remove(listener);
670: }
671: }
672:
673: private void notifyDownloadListeners(DownloadState state,
674: String message) {
675: notifyDownloadListeners(state, message, null);
676: }
677:
678: private void notifyDownloadListeners(DownloadState state,
679: String message, Throwable exception) {
680: DownloadEvent event = new DownloadEvent(this , state, message,
681: exception);
682:
683: for (DownloadListener listener : listeners
684: .toArray(new DownloadListener[0])) {
685: switch (state) {
686: case STARTED:
687: listener.downloadStarted(event);
688: break;
689: case RUNNING:
690: listener.downloadRunning(event);
691: break;
692: case PAUSED:
693: listener.downloadPaused(event);
694: break;
695: case RESUMED:
696: listener.downloadResumed(event);
697: break;
698: case FAILED:
699: listener.downloadFailed(event);
700: break;
701: case CANCELED:
702: listener.downloadCanceled(event);
703: break;
704: case COMPLETED:
705: listener.downloadCompleted(event);
706: break;
707: case THREAD_STARTED:
708: listener.downloadThreadStarted(event);
709: break;
710: case THREAD_PAUSED:
711: listener.downloadThreadPaused(event);
712: break;
713: case THREAD_RESUMED:
714: listener.downloadThreadResumed(event);
715: break;
716: case THREAD_COMPLETED:
717: listener.downloadThreadCompleted(event);
718: break;
719: case THREAD_FAILED:
720: listener.downloadThreadFailed(event);
721: break;
722: case THREAD_CANCELED:
723: listener.downloadThreadCanceled(event);
724: break;
725: default:
726: ErrorManager
727: .notify(ErrorLevel.CRITICAL,
728: "WTF WTF The download state is not recognized..");
729: }
730: }
731: }
732:
733: /////////////////////////////////////////////////////////////////////////////////
734: // Inner classes
735: public static interface DownloadListener {
736: public void downloadStarted(DownloadEvent event);
737:
738: public void downloadRunning(DownloadEvent event);
739:
740: public void downloadPaused(DownloadEvent event);
741:
742: public void downloadResumed(DownloadEvent event);
743:
744: public void downloadFailed(DownloadEvent event);
745:
746: public void downloadCanceled(DownloadEvent event);
747:
748: public void downloadCompleted(DownloadEvent event);
749:
750: public void downloadThreadStarted(DownloadEvent event);
751:
752: public void downloadThreadPaused(DownloadEvent event);
753:
754: public void downloadThreadResumed(DownloadEvent event);
755:
756: public void downloadThreadCompleted(DownloadEvent event);
757:
758: public void downloadThreadFailed(DownloadEvent event);
759:
760: public void downloadThreadCanceled(DownloadEvent event);
761: }
762:
763: public static class DownloadAdapter implements DownloadListener {
764: public void downloadStarted(DownloadEvent event) {
765: }
766:
767: public void downloadRunning(DownloadEvent event) {
768: }
769:
770: public void downloadPaused(DownloadEvent event) {
771: }
772:
773: public void downloadResumed(DownloadEvent event) {
774: }
775:
776: public void downloadFailed(DownloadEvent event) {
777: }
778:
779: public void downloadCanceled(DownloadEvent event) {
780: }
781:
782: public void downloadCompleted(DownloadEvent event) {
783: }
784:
785: public void downloadThreadStarted(DownloadEvent event) {
786: }
787:
788: public void downloadThreadPaused(DownloadEvent event) {
789: }
790:
791: public void downloadThreadResumed(DownloadEvent event) {
792: }
793:
794: public void downloadThreadCompleted(DownloadEvent event) {
795: }
796:
797: public void downloadThreadFailed(DownloadEvent event) {
798: }
799:
800: public void downloadThreadCanceled(DownloadEvent event) {
801: }
802: }
803:
804: public static class DownloadEvent {
805: private Download source;
806:
807: private DownloadState state;
808: private String message;
809: private Throwable exception;
810:
811: public DownloadEvent(Download aSource, DownloadState aState,
812: String aMessage, Throwable anException) {
813: source = aSource;
814:
815: state = aState;
816: message = aMessage;
817: exception = anException;
818: }
819:
820: public Download getSource() {
821: return source;
822: }
823:
824: public DownloadState getState() {
825: return state;
826: }
827:
828: public String getMessage() {
829: return message;
830: }
831:
832: public Throwable getException() {
833: return exception;
834: }
835: }
836:
837: public static enum DownloadState {
838: STARTED, RUNNING, PAUSED, RESUMED, FAILED, CANCELED, COMPLETED, THREAD_STARTED, THREAD_PAUSED, THREAD_RESUMED, THREAD_COMPLETED, THREAD_FAILED, THREAD_CANCELED;
839: }
840:
841: /////////////////////////////////////////////////////////////////////////////////
842: // Constants
843: public static final int INITIAL_ERRORS_NUMBER = 0;
844: public static final int ZERO_OFFSET = 0;
845: public static final int ZERO_LENGTH = 0;
846: public static final int ZERO_SPEED = 0;
847: public static final int UNDEFINED_LENGTH = -1;
848: }
|