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.net.URISyntaxException;
043: import java.util.ArrayList;
044: import java.util.HashMap;
045: import java.util.List;
046: import java.util.Map;
047: import org.netbeans.installer.sandbox.download.Download.DownloadAdapter;
048: import org.netbeans.installer.sandbox.download.Download.DownloadState;
049: import org.netbeans.installer.sandbox.download.Download.DownloadEvent;
050: import org.netbeans.installer.sandbox.download.Download.DownloadListener;
051: import org.netbeans.installer.sandbox.download.proxy.Proxy.ProxyType;
052: import org.netbeans.installer.sandbox.download.proxy.Proxy;
053: import org.netbeans.installer.utils.helper.ErrorLevel;
054: import org.netbeans.installer.utils.FileUtils;
055: import org.netbeans.installer.utils.ResourceUtils;
056: import org.netbeans.installer.utils.exceptions.DownloadException;
057: import org.netbeans.installer.utils.LogManager;
058: import org.netbeans.installer.utils.progress.Progress;
059:
060: /**
061: * This class serves as a single entry point for the download module. Clients should
062: * use the instance of this class to initiate downloads instead of addressing the
063: * <code>Download</code> class directly. <code>DownloadManager</code> provides means
064: * to initiate downloads in both blocking and non blocking modes.
065: * This class is also responsible for managing proxies. Connections are expected to
066: * address the <code>DownloadManager</code> in order to get the list of proxies that
067: * they should use to connect to remote machines.
068: *
069: * @author Kirill Sorokin
070: */
071: public class DownloadManager {
072: /////////////////////////////////////////////////////////////////////////////////
073: // Constants
074: /**
075: * The name of the system property which holds the host of the system SOCKS
076: * proxy.
077: */
078: private static final String SOCKS_PROXY_HOST_PROPERTY = "socksProxyHost"; // NOI18N
079:
080: /**
081: * The name of the system property which holds the port of the system SOCKS
082: * proxy.
083: */
084: private static final String SOCKS_PROXY_PORT_PROPERTY = "socksProxyPort"; // NOI18N
085:
086: /**
087: * The name of the system property which holds the host of the system HTTP
088: * proxy.
089: */
090: private static final String HTTP_PROXY_HOST_PROPERTY = "http.proxyHost"; // NOI18N
091:
092: /**
093: * The name of the system property which holds the port of the system HTTP
094: * proxy.
095: */
096: private static final String HTTP_PROXY_PORT_PROPERTY = "http.proxyPort"; // NOI18N
097:
098: /**
099: * The name of the system property which holds the list of hosts for which the
100: * system HTTP proxy should be bypassed.
101: */
102: private static final String HTTP_NON_PROXY_HOSTS_PROPERTY = "http.nonProxyHosts"; // NOI18N
103:
104: /**
105: * The name of the system property which holds the host of the system HTTPS
106: * proxy.
107: */
108: private static final String HTTPS_PROXY_HOST_PROPERTY = "https.proxyHost"; // NOI18N
109:
110: /**
111: * The name of the system property which holds the port of the system HTTPS
112: * proxy.
113: */
114: private static final String HTTPS_PROXY_PORT_PROPERTY = "https.proxyPort"; // NOI18N
115:
116: /**
117: * The name of the system property which holds the list of hosts for which the
118: * system HTTPS proxy should be bypassed.
119: */
120: private static final String HTTPS_NON_PROXY_HOSTS_PROPERTY = "https.nonProxyHosts"; // NOI18N
121:
122: /**
123: * The name of the system property which holds the host of the system FTP
124: * proxy.
125: */
126: private static final String FTP_PROXY_HOST_PROPERTY = "ftp.proxyHost"; // NOI18N
127:
128: /**
129: * The name of the system property which holds the port of the system FTP
130: * proxy.
131: */
132: private static final String FTP_PROXY_PORT_PROPERTY = "ftp.proxyPort"; // NOI18N
133:
134: /**
135: * The name of the system property which holds the list of hosts for which the
136: * system FTP proxy should be bypassed.
137: */
138: private static final String FTP_NON_PROXY_HOSTS_PROPERTY = "ftp.nonProxyHosts"; // NOI18N
139:
140: /**
141: * Resource bundle key name - warning message yelded when the source URI for a
142: * requested download cannot be parsed.
143: */
144: private static final String KEY_CANNOT_PARSE_URI = "DownloadManager.exception.cannotParseURI"; // NOI18N
145:
146: /**
147: * Resource bundle key name - warning message yelded when the destination file
148: * for a requested download cannot be parsed.
149: */
150: private static final String KEY_CANNOT_CREATE_TEMP_FILE = "DownloadManager.exception.cannotCreateTempFile"; // NOI18N
151:
152: /////////////////////////////////////////////////////////////////////////////////
153: // Static
154: /**
155: * The only instance of the <code>DownloadManager</code>. It's lazily
156: * initialized.
157: */
158: private static DownloadManager instance;
159:
160: /**
161: * Returns the instance of <code>DownloadManager</code>. If the instance does
162: * not yet exist it is constructed.
163: *
164: * @return The instance of <code>DownloadManager</code>
165: */
166: public static synchronized DownloadManager getInstance() {
167: if (instance == null) {
168: instance = new DownloadManager();
169: }
170:
171: return instance;
172: }
173:
174: /////////////////////////////////////////////////////////////////////////////////
175: // Instance
176: /**
177: * The list of proxies registered with the <code>DownloadManager</code>.
178: * <code>Connection</code>s are expected to take this list into account when
179: * connecting to remote machines.
180: */
181: private List<Proxy> proxies = new ArrayList<Proxy>();
182:
183: /**
184: * Completed downloads cache. If a client requests a download from a specified
185: * URI and states that a cached version will suffice, the cache is checked for
186: * the URI being requested and if it present, the cached version is returned.
187: * Caching is only supported for downloads in blocking mode and the client must
188: * explicitly state that the download he is requesting can be placed into cache.
189: */
190: private Map<URI, File> downloadsCache = new HashMap<URI, File>();
191:
192: /**
193: * Constructs a new instance of <code>DownloadManager</code>. Upon construction,
194: * the system properties are checked for presence of system proxies and there
195: * are any, they are registered with the <code>DownloadManager</code>.
196: */
197: private DownloadManager() {
198: LogManager.log(ErrorLevel.MESSAGE,
199: "initializing download manager subsystem");
200:
201: LogManager.log(ErrorLevel.DEBUG,
202: " parsing proxy servers defined by the system");
203: Proxy proxy;
204:
205: LogManager.log(ErrorLevel.DEBUG,
206: " parsing system socks proxy");
207: proxy = Proxy.parseProxy(ProxyType.SOCKS,
208: SOCKS_PROXY_HOST_PROPERTY, SOCKS_PROXY_PORT_PROPERTY,
209: null);
210: if (proxy != null) {
211: proxies.add(proxy);
212: }
213:
214: LogManager.log(ErrorLevel.DEBUG,
215: " parsing system http proxy");
216: proxy = Proxy.parseProxy(ProxyType.HTTP,
217: HTTP_PROXY_HOST_PROPERTY, HTTP_PROXY_PORT_PROPERTY,
218: HTTP_NON_PROXY_HOSTS_PROPERTY);
219: if (proxy != null) {
220: proxies.add(proxy);
221: }
222:
223: LogManager.log(ErrorLevel.DEBUG,
224: " parsing system https proxy");
225: proxy = Proxy.parseProxy(ProxyType.HTTPS,
226: HTTPS_PROXY_HOST_PROPERTY, HTTPS_PROXY_PORT_PROPERTY,
227: HTTPS_NON_PROXY_HOSTS_PROPERTY);
228: if (proxy != null) {
229: proxies.add(proxy);
230: }
231:
232: LogManager.log(ErrorLevel.DEBUG,
233: " parsing system ftp proxy");
234: proxy = Proxy.parseProxy(ProxyType.FTP,
235: FTP_PROXY_HOST_PROPERTY, FTP_PROXY_PORT_PROPERTY,
236: FTP_NON_PROXY_HOSTS_PROPERTY);
237: if (proxy != null) {
238: proxies.add(proxy);
239: }
240:
241: LogManager.log(ErrorLevel.MESSAGE,
242: "... download manager subsystem initialized correctly");
243: }
244:
245: /**
246: * Constructs an instance of <code>Download</code>. This method should be used
247: * by the clients wishing to initiate a download in non-blocking mode.
248: *
249: * @param uri The source URI.
250: * @param file The destination file.
251: * @param options The options for the download.
252: * @param listener The initial download listener.
253: * @return An instance of <code>Download</code> constructed according to the
254: * supplied parameters.
255: */
256: public Download getDownload(URI uri, File file,
257: DownloadOptions options, DownloadListener listener) {
258: return new Download(uri, file, options, listener);
259: }
260:
261: /**
262: * Constructs an instance of <code>Download</code>. This method should be used
263: * by the clients wishing to initiate a download in non-blocking mode.
264: *
265: * @param uri The source URI.
266: * @param file The destination file.
267: * @param options The options for the download.
268: * @return An instance of <code>Download</code> constructed according to the
269: * supplied parameters.
270: */
271: public Download getDownload(URI uri, File file,
272: DownloadOptions options) {
273: return getDownload(uri, file, options, null);
274: }
275:
276: /**
277: * Constructs an instance of <code>Download</code>. This method should be used
278: * by the clients wishing to initiate a download in non-blocking mode.
279: *
280: * @param uri The source URI.
281: * @param filename The destination file name.
282: * @param options The options for the download.
283: * @param listener The initial download listener.
284: * @return An instance of <code>Download</code> constructed according to the
285: * supplied parameters.
286: * @throws java.net.URISyntaxException if the supplied string cannot be parsed
287: * into an <code>URI</code>.
288: */
289: public Download getDownload(String uri, String filename,
290: DownloadOptions options, DownloadListener listener)
291: throws URISyntaxException {
292: return getDownload(new URI(uri), new File(filename), options,
293: listener);
294: }
295:
296: /**
297: * Constructs an instance of <code>Download</code>. This method should be used
298: * by the clients wishing to initiate a download in non-blocking mode.
299: *
300: * @param uri The source URI.
301: * @param filename The destination file name.
302: * @param options The options for the download.
303: * @return An instance of <code>Download</code> constructed according to the
304: * supplied parameters.
305: * @throws java.net.URISyntaxException if the supplied string cannot be parsed
306: * into an <code>URI</code>.
307: */
308: public Download getDownload(String uri, String filename,
309: DownloadOptions options) throws URISyntaxException {
310: return getDownload(uri, filename, options, null);
311: }
312:
313: /**
314: * Performs a download in blocking mode. This method provides support for
315: * caching. To enable it the client must explicitly define it in the download
316: * options. The same download options property is used for both enabling cache
317: * hits and completed download registration in the cache.
318: *
319: * @param uri The source URI.
320: * @param file The destination file.
321: * @param options The options for the download.
322: * @throws org.netbeans.installer.utils.exceptions.DownloadException if the
323: * download fails for whatever reason.
324: * @return The destination file.
325: */
326: public File download(URI uri, File file, DownloadOptions options)
327: throws DownloadException {
328: boolean cachingEnabled = options
329: .getBoolean(DownloadOptions.CACHING_ENABLED);
330:
331: // check the cache for the given URI, if the URI is present in the cache -
332: // validate the destination file corresponding to this URI
333: File cacheHit = downloadsCache.get(uri);
334: boolean cacheHitValid = (cacheHit != null) && cacheHit.exists()
335: && cacheHit.isFile();
336:
337: // if we are allowed to return the cached file and the file is valid -
338: // return it
339: if (cachingEnabled && cacheHitValid) {
340: try {
341: FileUtils.copyFile(cacheHit, file);
342: return file;
343: } catch (IOException ex) {
344: LogManager.log(
345: "faild transfer file from cache to dist", ex);
346: return null;
347: }
348: }
349:
350: // otherwise perform the download
351: File result = new DownloadHandler(uri, file, options)
352: .download();
353:
354: if (cachingEnabled) {
355: downloadsCache.put(uri, result);
356: }
357:
358: return result;
359: }
360:
361: /**
362: * Performs a download in blocking mode.
363: *
364: * @param uri The source URI.
365: * @param file The destination file.
366: * @param options The options for the download.
367: * @throws org.netbeans.installer.utils.exceptions.DownloadException if the
368: * download fails for whatever reason.
369: * @return The destination file.
370: */
371: public File download(String uri, File file, DownloadOptions options)
372: throws DownloadException {
373: try {
374: return download(new URI(uri), file, options);
375: } catch (URISyntaxException e) {
376: String message = ResourceUtils.getString(
377: DownloadManager.class, KEY_CANNOT_PARSE_URI, uri);
378: throw new DownloadException(message, e);
379: }
380: }
381:
382: public File download(String uri, File file)
383: throws DownloadException {
384: return download(uri, file, DownloadOptions.getDefaults());
385: }
386:
387: /**
388: * Performs a download in blocking mode.
389: *
390: * @param uri The source URI.
391: * @param options The options for the download.
392: * @throws org.netbeans.installer.utils.exceptions.DownloadException if the
393: * download fails for whatever reason.
394: * @return The destination file.
395: */
396: public File download(URI uri, DownloadOptions options)
397: throws DownloadException {
398: try {
399: return download(uri, FileUtils.createTempFile(), options);
400: } catch (IOException e) {
401: String message = ResourceUtils.getString(
402: DownloadManager.class, KEY_CANNOT_CREATE_TEMP_FILE);
403: throw new DownloadException(message, e);
404: }
405: }
406:
407: /**
408: * Performs a download in blocking mode.
409: *
410: * @param uri The source URI.
411: * @param options The options for the download.
412: * @throws org.netbeans.installer.utils.exceptions.DownloadException if the
413: * download fails for whatever reason.
414: * @return The destination file.
415: */
416: public File download(String uri, DownloadOptions options)
417: throws DownloadException {
418: try {
419: return download(new URI(uri), options);
420: } catch (URISyntaxException e) {
421: String message = ResourceUtils.getString(
422: DownloadManager.class, KEY_CANNOT_PARSE_URI, uri);
423: throw new DownloadException(message, e);
424: }
425: }
426:
427: /**
428: * Performs a download in blocking mode.
429: *
430: * @param uri The source URI.
431: * @throws org.netbeans.installer.utils.exceptions.DownloadException if the
432: * download fails for whatever reason.
433: * @return The destination file.
434: */
435: public File download(URI uri) throws DownloadException {
436: return download(uri, DownloadOptions.getDefaults());
437: }
438:
439: /**
440: * Performs a download in blocking mode.
441: *
442: * @param uri The source URI.
443: * @throws org.netbeans.installer.utils.exceptions.DownloadException if the
444: * download fails for whatever reason.
445: * @return The destination file.
446: */
447: public File download(String uri) throws DownloadException {
448: return download(uri, DownloadOptions.getDefaults());
449: }
450:
451: /**
452: * Returns a list of registered proxies of the given type.
453: *
454: * @param type The type of proxies to return.
455: * @return The list of proxies of the given type.
456: */
457: public List<Proxy> getProxies(ProxyType type) {
458: List<Proxy> filteredProxies = new ArrayList<Proxy>();
459:
460: synchronized (proxies) {
461: for (Proxy proxy : proxies) {
462: if (proxy.getType() == type) {
463: filteredProxies.add(proxy);
464: }
465: }
466: }
467:
468: return filteredProxies;
469: }
470:
471: /**
472: * Registers a new proxy with the <code>DownloadManager</code>.
473: *
474: * @param proxy The proxy to register.
475: */
476: public void addProxy(Proxy proxy) {
477: synchronized (proxies) {
478: proxies.add(proxy);
479: }
480: }
481:
482: /**
483: * Removes a proxy from the list of registered proxies.
484: *
485: * @param proxy The proxy to remove.
486: */
487: public void removeProxy(Proxy proxy) {
488: synchronized (proxies) {
489: proxies.remove(proxy);
490: }
491: }
492:
493: /////////////////////////////////////////////////////////////////////////////////
494: // Inner classes
495: /**
496: * A helper class which is used by the <code>DownloadManager</code> in order to
497: * perform downloads in blocking mode.
498: *
499: * @author Kirill Sorokin
500: */
501: public static class DownloadHandler extends DownloadAdapter {
502: /**
503: * The <code>Download</code> object, which actually performs the download.
504: */
505: private Download download;
506:
507: /**
508: * Indicates whether the download has completed/failed or is still running.
509: * The initial value is true, will be reset to false as soon as the
510: * download is completed or failed.
511: */
512: private boolean downloading = true;
513:
514: /**
515: * The resulting state of the download, should be either COMPLETED or
516: * FAILED.
517: */
518: private DownloadState downloadState;
519:
520: /**
521: * The message with which the download failed, used as a message for the
522: * <code>DownloadException</code> thrown in this case.
523: */
524: private String downloadMessage;
525:
526: /**
527: * The exception with which the download failed, used as a cause for the
528: * <code>DownloadException</code> thrown in this case.
529: */
530: private Throwable downloadException;
531:
532: /**
533: * A Progress object to which to report download progress.
534: */
535: private Progress progress;
536:
537: /**
538: * Constructs a new instance of <code>DownloadHandler</code>. It in its turn
539: * creates a new instance of <code>Download</code>.
540: *
541: * @param uri The source URI.
542: * @param file The destination file.
543: * @param options The options for the download.
544: */
545: public DownloadHandler(URI uri, File file,
546: DownloadOptions options) {
547: download = DownloadManager.getInstance().getDownload(uri,
548: file, options, this );
549: }
550:
551: /**
552: * Starts the download and waits for it to complete or fail. If the download
553: * fails, a <code>DownloadException</code> is thrown.
554: *
555: * @return The destination file.
556: * @throws org.netbeans.installer.utils.exceptions.DownloadException if the
557: * download fails.
558: */
559: public File download() throws DownloadException {
560: download.start();
561:
562: while (downloading) {
563: try {
564: Thread.sleep(DELAY);
565: } catch (InterruptedException e) {
566: continue;
567: }
568: }
569:
570: // if the download completed successully return the file, throw an
571: // exception otherwise
572: if (downloadState == DownloadState.COMPLETED) {
573: return download.getDestination();
574: } else {
575: throw new DownloadException(downloadMessage,
576: downloadException);
577: }
578: }
579:
580: /**
581: * Starts the download and waits for it to complete or fail. If the download
582: * fails, a <code>DownloadException</code> is thrown. The progress is
583: * reported to the supplied Progress object.
584: *
585: * @return The destination file.
586: * @throws org.netbeans.installer.utils.exceptions.DownloadException if the
587: * download fails.
588: */
589: public File download(final Progress progress)
590: throws DownloadException {
591: this .progress = progress;
592: return download();
593: }
594:
595: /**
596: * {@inheritDoc}
597: */
598: public void downloadRunning(DownloadEvent event) {
599: if (progress != null) {
600: progress.setPercentage(event.getSource()
601: .getPercentage());
602: }
603: }
604:
605: /**
606: * {@inheritDoc}
607: */
608: public void downloadCompleted(DownloadEvent event) {
609: downloading = false;
610: downloadState = DownloadState.COMPLETED;
611:
612: if (progress != null) {
613: progress.setPercentage(event.getSource()
614: .getPercentage());
615: }
616: }
617:
618: /**
619: * {@inheritDoc}
620: */
621: public void downloadFailed(DownloadEvent event) {
622: downloading = false;
623: downloadState = DownloadState.FAILED;
624: downloadMessage = event.getMessage();
625: downloadException = event.getException();
626:
627: if (progress != null) {
628: progress.setPercentage(event.getSource()
629: .getPercentage());
630: }
631: }
632:
633: /**
634: * The amount of time in milliseconds, that we should sleep between checks
635: * for download state.
636: */
637: private static final int DELAY = 15;
638: }
639: }
|