/******************************************************************************* * Copyright (c) 2013 Red Hat, Inc. * Distributed under license by Red Hat, Inc. All rights reserved. * This program is made available under the terms of the * Eclipse Public License v1.0 which accompanies this distribution, * and is available at http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Red Hat, Inc. - initial API and implementation ******************************************************************************/ package org.jboss.tools.foundation.core.ecf; import java.io.File; import java.io.OutputStream; import java.net.URI; import java.net.URL; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.NullProgressMonitor; import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.SubProgressMonitor; import org.jboss.tools.foundation.core.ecf.internal.InternalURLTransport; import org.jboss.tools.foundation.core.ecf.internal.URLTransportCache; import org.jboss.tools.foundation.core.internal.FoundationCorePlugin; import org.jboss.tools.foundation.core.jobs.BarrierProgressWaitJob; import org.jboss.tools.foundation.core.jobs.BarrierProgressWaitJob.IRunnableWithProgress; /** * This class is intended to be used to perform work * associated with remote files for a variety of tasks using ECF, * including: * - fetch URL's (local files, eclipse-specific urls, or web urls) * - check remote timestamp of such urls * - store fetched urls in a local cache for repeated use * * @noextend This class is not intended to be subclassed by clients. */ public class URLTransportUtility { /** * When using the caching signature, getCachedFileForURL(etc), * this indicates the file should remain around cached indefinitely. */ public static final int CACHE_FOREVER = 1; /** * When using the caching signature, getCachedFileForURL(etc), * this indicates the file should be cached and available until * the eclipse session ends. */ public static final int CACHE_UNTIL_EXIT = 2; /** * Get the cached file, or download a new copy if the local cache is outdated or does not exist. * The default cache folder will be used. * * @param url The URL * @param displayName A user-visible string for this task * @param lifespan How long the download file should be kept: one of CACHE_FOREVER or CACHE_UNTIL_EXIT * @param monitor Progress monitor * @return A file representing the cached or newly cached file, or null if none exists * @throws CoreException If a download failed, was canceled, or unexpected error occurred */ public File getCachedFileForURL(String url, String displayName, int lifespan, IProgressMonitor monitor) throws CoreException { return getCachedFileForURL(url, displayName, lifespan, URLTransportCache.getDefault(), monitor); } /** * Get the cached file, or download a new copy if the local cache is outdated or does not exist. * The default connection timeouts for your system will be used. * * @param url The URL * @param displayName A user-visible string for this task * @param lifespan How long the download file should be kept: one of CACHE_FOREVER or CACHE_UNTIL_EXIT * @param timeout The timeout in ms to be passed to the connection. * A value of less than 0 will NOT pass in or override the timeouts. * @param monitor Progress monitor * @return A file representing the cached or newly cached file, or null if none exists * @throws CoreException If a download failed, was canceled, or unexpected error occurred */ public File getCachedFileForURL(String url, String displayName, int lifespan, int timeout, IProgressMonitor monitor) throws CoreException { return getCachedFileForURL(url, displayName, lifespan, URLTransportCache.getDefault(), timeout, monitor); } /** * Get the cached file, or download a new copy if the local cache is outdated or does not exist. * The default connection timeouts for your system will be used. * * @param url The URL * @param displayName A user-visible string for this task * @param lifespan How long the download file should be kept: one of CACHE_FOREVER or CACHE_UNTIL_EXIT * @param cacheRoot A path to a folder to use as a cache * @param monitor Progress monitor * @return A file representing the cached or newly cached file, or null if none exists * @throws CoreException If a download failed, was canceled, or unexpected error occurred */ public File getCachedFileForURL(String url, String displayName, int lifespan, IPath cacheRoot, IProgressMonitor monitor) throws CoreException { URLTransportCache cache = URLTransportCache.getCache(cacheRoot); return getCachedFileForURL(url, displayName, lifespan, cache, monitor); } /** * Get the cached file, or download a new copy if the local cache is outdated or does not exist. * * @param url The URL * @param displayName A user-visible string for this task * @param lifespan How long the download file should be kept: one of CACHE_FOREVER or CACHE_UNTIL_EXIT * @param cacheRoot A path to a folder to use as a cache * @param timeout The timeout in ms to be passed to the connection. * A value of less than 0 will NOT pass in or override the timeouts. * @param monitor Progress monitor * @return A file representing the cached or newly cached file, or null if none exists * @throws CoreException If a download failed, was canceled, or unexpected error occurred */ public File getCachedFileForURL(String url, String displayName, int lifespan, IPath cacheRoot, int timeout, IProgressMonitor monitor) throws CoreException { URLTransportCache cache = URLTransportCache.getCache(cacheRoot); return getCachedFileForURL(url, displayName, lifespan, cache, timeout, monitor); } public boolean isCacheOutdated(String url, IProgressMonitor mon) throws CoreException { return isCacheOutdated(url, URLTransportCache.getDefault(), mon); } public boolean isCacheOutdated(String url, IPath cacheRoot, IProgressMonitor mon) throws CoreException { return isCacheOutdated(url, URLTransportCache.getCache(cacheRoot), mon); } public boolean isCacheOutdated(String url, URLTransportCache cache, IProgressMonitor mon) throws CoreException { return cache.isCacheOutdated(url, mon); } /** * Get the cached file, or download a new copy if the local cache is outdated. * Timeouts will use the default values for your system * * @param url The URL * @param displayName A user-visible string for this task * @param lifespan How long the download file should be kept: one of CACHE_FOREVER or CACHE_UNTIL_EXIT * @param cache A transport cache instance * @param monitor Progress monitor * @return A file representing the cached or newly cached file, or null if none exists * @throws CoreException If a download failed, was canceled, or unexpected error occurred */ private File getCachedFileForURL(String url, String displayName, int lifespan, URLTransportCache cache, IProgressMonitor monitor) throws CoreException { return getCachedFileForURL(url, displayName, lifespan, cache, -1, monitor); } /** * Get the cached file, or download a new copy if the local cache is outdated. * Timeouts will use the default values for your system * * @param url The URL * @param displayName A user-visible string for this task * @param lifespan How long the download file should be kept: one of CACHE_FOREVER or CACHE_UNTIL_EXIT * @param cache A transport cache instance * @param timeout The timeout in ms to be passed to the connection. * A value of less than 0 will NOT pass in or override the timeouts. * @param monitor Progress monitor * @return A file representing the cached or newly cached file, or null if none exists * @throws CoreException If a download failed, was canceled, or unexpected error occurred */ private File getCachedFileForURL(String url, String displayName, int lifespan, URLTransportCache cache, int timeout, IProgressMonitor monitor) throws CoreException { monitor.beginTask(displayName, 200); IProgressMonitor sub1 = new SubProgressMonitor(monitor, 100); try { if( cache.isCacheOutdated(url, sub1)) { sub1.done(); // If the remote cache is outdated, fetch the new copy IProgressMonitor sub2 = new SubProgressMonitor(monitor, 100); return cache.downloadAndCache(url, displayName, lifespan, this, timeout, sub2); } else { // Else use the local cache return cache.getCachedFile(url); } } catch(CoreException ce) { // We cannot reach the remote url. If there's a local cache copy, use that File f = cache.getCachedFile(url); if( f != null && f.exists()) return f; // If not, re-throw the CoreException IStatus old = ce.getStatus(); IStatus rethrow = new Status(old.getSeverity(), old.getPlugin(), old.getMessage(), ce); throw new CoreException(rethrow); } } /** * Get the cached file, or download a new copy if the local cache is outdated. * * This signature is intended for use when supplying a time-to-live duration * to ensure a speedy return after a time limit has been exceeded. * * @param uri The URI * @param displayName A user-visible string for this task * @param lifespan How long the download file should be kept: one of CACHE_FOREVER or CACHE_UNTIL_EXIT * @param transport The transport utility * @param timeout The timeout in ms to be passed to the connection. * A value of less than 0 will NOT pass in or override the timeouts. * @param timeToLive A positive long duration in ms for this execution to live. * Negative or 0 will be treated as infinite * @param monitor Progress monitor * @return A file representing the cached or newly cached file, or null if none exists * @throws CoreException If a download failed, was canceled, or unexpected error occurred */ public File getCachedFileForURL(String uri, String displayName, int lifespan, int timeout, final long timeToLive, final IProgressMonitor monitor) throws CoreException { //We time-bomb the monitor in case resolving/downloading is stuck at the OS level, //i.e. http connection timeout would be ignored final Boolean[] returned = new Boolean[1]; returned[0] = false; if( timeToLive > 0 ) { new Thread() { public void run() { try { Thread.sleep(timeToLive); } catch(InterruptedException ie) {} // If the method still hasn't returned, cancel the monitor and abort if (!returned[0]) { monitor.setCanceled(true); } } }.start(); } // Updated to pass a timeout File propFile = getCachedFileForURL( uri, displayName, lifespan, timeout, monitor); returned[0] = true; return propFile; } /** * Get the last modified timestamp of a given URL * @param location * @return * @throws CoreException */ public long getLastModified(URL location) throws CoreException { return getLastModified(location, new NullProgressMonitor()); } /** * Get the last modified timestamp of a given URL * @param location * @param timeout * @return * @throws CoreException */ public long getLastModified(final URL location, IProgressMonitor monitor) throws CoreException { return getLastModified(location, null, null, monitor); } /** * Get the last modified timestamp of a given URL * @param location * @param timeout * @return * @throws CoreException */ public long getLastModified(final URL location, final String user, final String pass, IProgressMonitor monitor) throws CoreException { BarrierProgressWaitJob j = new BarrierProgressWaitJob("Check Remote URL Last Modified", new IRunnableWithProgress() { public Object run(IProgressMonitor monitor) throws Exception { return getTransport().getLastModified(location, user, pass, monitor); } }); j.schedule(); // This join will also poll the provided monitor for cancelations j.monitorSafeJoin(monitor); if( monitor.isCanceled()) return -1; if( j.getThrowable() != null ) { if( j.getThrowable() instanceof CoreException) throw (CoreException)j.getThrowable(); throw new RuntimeException(j.getThrowable()); } return ((Long)j.getReturnValue()).longValue(); } /** * * @param displayName A string representation of the URL suitable for display / progress purposes * @param url The URL to fetch * @param destination The output stream to feed the contents to * @param monitor A progress monitor * @return A status object indicating the success or failure */ public IStatus download(String displayName, String url, OutputStream destination, IProgressMonitor monitor) { return download(displayName, url, destination, -1, monitor); } /** * Download the given file to the given output stream * * @param displayName A string representation of the URL suitable for display / progress purposes * @param url The URL to fetch * @param destination The output stream to feed the contents to * @param timeout a timeout duration for how long connections should attempt to connect * @param monitor A progress monitor * @return A status object indicating the success or failure */ public IStatus download(final String displayName,final String url,final OutputStream destination, final int timeout, final IProgressMonitor monitor2) { return download(displayName, url, null, null, destination, timeout, monitor2); } public IStatus download(final String displayName,final String url, final String user, final String pass, final OutputStream destination, final int timeout, final IProgressMonitor monitor2) { BarrierProgressWaitJob j = new BarrierProgressWaitJob("Download Remote URL", new IRunnableWithProgress() { public Object run(IProgressMonitor monitor) throws Exception { return getTransport().download(displayName, url, user, pass, destination, timeout, monitor2); } }); j.schedule(); // This join will also poll the provided monitor for cancelations j.monitorSafeJoin(monitor2); if( j.getReturnValue() != null) return (IStatus)j.getReturnValue(); if( j.getThrowable() != null ) { throw new RuntimeException(j.getThrowable()); } return FoundationCorePlugin.statusFactory().cancelStatus(Messages.ECFTransport_Operation_canceled); } /* * Get the internal transport object */ private static InternalURLTransport getTransport() { return InternalURLTransport.getInstance(); } }