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.io.RandomAccessFile;
042: import java.net.URI;
043: import java.util.Date;
044: import java.util.concurrent.locks.ReentrantLock;
045: import org.netbeans.installer.sandbox.download.connection.Connection;
046: import org.netbeans.installer.utils.helper.ErrorLevel;
047: import org.netbeans.installer.utils.exceptions.InitializationException;
048: import org.netbeans.installer.utils.ErrorManager;
049:
050: /**
051: *
052: * @author Kirill Sorokin
053: */
054: class DownloadThread extends Thread {
055: private URI uri;
056: private File file;
057: private long offset;
058: private long length;
059:
060: private Download download;
061: private DownloadOptions options;
062:
063: private int maximumSpeed = -1;
064:
065: private byte[] buffer;
066:
067: private long contentLength;
068: private long readLength;
069: private boolean supportsRanges;
070:
071: private Date modificationTime;
072:
073: private boolean paused;
074: private boolean canceled;
075:
076: private boolean resumeFailed;
077:
078: private boolean canWrite;
079:
080: private Connection connection;
081: private RandomAccessFile output;
082:
083: private ReentrantLock lock = new ReentrantLock();
084:
085: DownloadThread(Download aDownload, URI anURI, File aFile,
086: DownloadOptions someOptions) {
087: this (aDownload, anURI, aFile, Download.ZERO_OFFSET,
088: Download.UNDEFINED_LENGTH, someOptions);
089: }
090:
091: DownloadThread(Download aDownload, URI anURI, File aFile,
092: long anOffset, long aLength, DownloadOptions someOptions) {
093: // save the incoming parameters
094: download = aDownload;
095:
096: uri = anURI;
097: file = aFile;
098: offset = anOffset;
099: length = aLength;
100:
101: // save the download options
102: options = someOptions;
103:
104: // initialize the buffer
105: buffer = new byte[BUFFER_LENGTH];
106: }
107:
108: public void start(boolean isCanWrite) {
109: canWrite = isCanWrite;
110: start();
111: }
112:
113: public void run() {
114: try {
115: // notify the download that we have started this thread
116: download.threadStarted(this );
117:
118: // open the streams (connection and file output)
119: openStreams();
120:
121: // get the connection properties
122: contentLength = connection.getContentLength();
123: supportsRanges = connection.supportsRanges();
124: modificationTime = connection.getModificationDate();
125:
126: // loop while we haven't read all that is available or all that we
127: // need to read
128: while (!downloadComplete()) {
129: if (canceled || resumeFailed) {
130: return;
131: }
132:
133: if (!paused) {
134: // notify the download that we are still running
135: download.threadRunning(this );
136:
137: // if the thread is not yet allowed to write, skip the i/o
138: // operations
139: if (canWrite) {
140: lock.lock();
141: try {
142: // read what's available from the connection and if we
143: // have read more than we needed to - truncate
144: int read = correctReadAmount(connection
145: .read(buffer));
146:
147: // write the read bytes to the output
148: output.write(buffer, 0, read);
149:
150: // add the read bytes to the total amount of read data
151: readLength += read;
152:
153: // if the maximum speed is specified - wait
154: if (maximumSpeed != -1) {
155: try {
156: Thread.sleep(250);
157: } catch (InterruptedException e) {
158: ErrorManager
159: .notify(
160: ErrorLevel.DEBUG,
161: "Interrupted while sleeping",
162: e);
163: }
164: }
165: } finally {
166: lock.unlock();
167: }
168: }
169: } else {
170: try {
171: Thread.sleep(50);
172: } catch (InterruptedException e) {
173: ErrorManager.notify(ErrorLevel.DEBUG,
174: "Interrupted while sleeping", e);
175: }
176: }
177: }
178: } catch (InitializationException e) {
179: download.threadFailed(this , e);
180: return;
181: } catch (IOException e) {
182: download.threadFailed(this , e);
183: return;
184: } finally {
185: // close the streams
186: closeStreams();
187: }
188:
189: // we have successfully completed - notify the download
190: download.threadCompleted(this );
191: }
192:
193: private int correctReadAmount(int read) {
194: if ((length != Download.UNDEFINED_LENGTH)
195: && (read > (length - readLength))) {
196: return (int) (length - readLength);
197: } else if ((contentLength != Download.UNDEFINED_LENGTH)
198: && (read > (contentLength - readLength))) {
199: return (int) (contentLength - readLength);
200: } else {
201: return read;
202: }
203: }
204:
205: private void openStreams() throws InitializationException,
206: IOException {
207: lock.lock();
208: try {
209: long correctLength = length != Download.UNDEFINED_LENGTH ? length
210: - readLength
211: : length;
212:
213: // initialize the connection and open it
214: connection = Connection.getConnection(uri, offset
215: + readLength, correctLength, options);
216: connection.open();
217:
218: // initialize the output file and move to the specified offset
219: output = new RandomAccessFile(file, "rw");
220: output.seek(offset + readLength);
221: } finally {
222: lock.unlock();
223: }
224: }
225:
226: private void closeStreams() {
227: lock.lock();
228: try {
229: // if the output is not null - try to close it
230: try {
231: if (output != null) {
232: output.close();
233: }
234: } catch (IOException e) {
235: ErrorManager.notify(ErrorLevel.WARNING, e);
236: }
237:
238: // if the connection is not null - try to close it
239: try {
240: if (connection != null) {
241: connection.close();
242: }
243: } catch (IOException e) {
244: ErrorManager.notify(ErrorLevel.WARNING, e);
245: }
246: } finally {
247: lock.unlock();
248: }
249: }
250:
251: public void cancelThread() {
252: download.threadCanceled(this );
253:
254: canceled = true;
255: }
256:
257: public void startWriting() {
258: canWrite = true;
259: }
260:
261: public void pauseThread() {
262: // notify the download
263: download.threadPaused(this );
264:
265: // set the marker
266: paused = true;
267:
268: closeStreams();
269: }
270:
271: public void resumeThread() {
272: try {
273: // notify the download
274: download.threadResumed(this );
275:
276: openStreams();
277:
278: // set the marker
279: paused = false;
280: } catch (InitializationException e) {
281: resumeFailed = true;
282: download.threadFailed(this , e);
283: } catch (IOException e) {
284: resumeFailed = true;
285: download.threadFailed(this , e);
286: }
287: }
288:
289: // if the length is finite, we should read until we read as much as we
290: // are told to. if the length in infinite (undefined) we should read as
291: // much as it is available. if we don't know how much is available (the
292: // content length is infinite - we should read while there is something to
293: // read.
294: private boolean downloadComplete() throws IOException {
295: lock.lock();
296: try {
297: if (length != Download.UNDEFINED_LENGTH) {
298: if (readLength == length) {
299: return true;
300: } else {
301: return false;
302: }
303: } else {
304: if (contentLength != Download.UNDEFINED_LENGTH) {
305: if (readLength == contentLength) {
306: return true;
307: } else {
308: return false;
309: }
310: } else {
311: if (paused || connection.available() > 0) {
312: return false;
313: } else {
314: return true;
315: }
316: }
317: }
318: } finally {
319: lock.unlock();
320: }
321: }
322:
323: public long getOffset() {
324: return offset;
325: }
326:
327: public long getLength() {
328: return length;
329: }
330:
331: public void setLength(long aLength) {
332: length = aLength;
333: }
334:
335: public boolean supportsRanges() {
336: return supportsRanges;
337: }
338:
339: public long getContentLength() {
340: return contentLength;
341: }
342:
343: public Date getModificationTime() {
344: return modificationTime;
345: }
346:
347: public long getReadLength() {
348: return readLength;
349: }
350:
351: public boolean isCanceled() {
352: return canceled;
353: }
354:
355: public boolean isPaused() {
356: return paused;
357: }
358:
359: public void setMaximumSpeed(int aMaximumSpeed) {
360: maximumSpeed = aMaximumSpeed;
361:
362: if (maximumSpeed == -1) {
363: buffer = new byte[BUFFER_LENGTH];
364: } else {
365: int bufferSize = maximumSpeed / 4;
366: buffer = new byte[bufferSize];
367: }
368: }
369:
370: public int getMaximumSpeed() {
371: return maximumSpeed;
372: }
373:
374: ////////////////////////////////////////////////////////////////////////////
375: // Constants
376: private static final int BUFFER_LENGTH = 1024 * 100;
377: }
|