/** * Aptana Studio * Copyright (c) 2005-2012 by Appcelerator, Inc. All Rights Reserved. * Licensed under the terms of the GNU Public License (GPL) v3 (with exceptions). * Please see the license.html included with this distribution for details. * Any modifications to this file must keep this entire header intact. */ package com.aptana.filesystem.http; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.net.HttpURLConnection; import java.net.MalformedURLException; import java.net.URI; import java.net.URISyntaxException; import java.text.MessageFormat; import org.eclipse.core.filesystem.EFS; import org.eclipse.core.filesystem.IFileInfo; import org.eclipse.core.filesystem.IFileStore; import org.eclipse.core.filesystem.provider.FileInfo; import org.eclipse.core.filesystem.provider.FileStore; 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.Path; import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.SubMonitor; import com.aptana.core.logging.IdeLog; import com.aptana.core.util.FileUtil; public class HttpFileStore extends FileStore { private URI uri; HttpFileStore(URI uri) { this.uri = uri; } /* * (non-Javadoc) * @see org.eclipse.core.filesystem.IFileStore#childNames(int, org.eclipse.core.runtime.IProgressMonitor) */ public String[] childNames(int options, IProgressMonitor monitor) throws CoreException { // TODO Can we really tell what the child names are over http? I don't think we can return new String[0]; } /* * (non-Javadoc) * @see org.eclipse.core.filesystem.IFileStore#fetchInfo(int, org.eclipse.core.runtime.IProgressMonitor) */ public IFileInfo fetchInfo(int options, IProgressMonitor monitor) throws CoreException { FileInfo result = new FileInfo(getName()); try { HttpURLConnection connection = (HttpURLConnection) uri.toURL().openConnection(); connection.setUseCaches(false); connection.setAllowUserInteraction(false); connection.setDoOutput(false); connection.setRequestMethod("HEAD"); //$NON-NLS-1$ int length = connection.getContentLength(); long lastModified = connection.getLastModified(); result.setExists(true); result.setLastModified(lastModified); if (length == -1) { result.setLength(EFS.NONE); } else { result.setLength(length); } } catch (IOException e) { // throw new CoreException(new Status(IStatus.ERROR, HttpFilesystemPlugin.PLUGIN_ID, EFS.ERROR_READ, // e.getMessage(), e)); HttpFilesystemPlugin.log(e); result.setExists(false); } return result; } /* * (non-Javadoc) * @see org.eclipse.core.filesystem.IFileStore#getChild(java.lang.String) */ public IFileStore getChild(String name) { return new HttpFileStore(uri.resolve(name)); } /* * (non-Javadoc) * @see org.eclipse.core.filesystem.IFileStore#getName() */ public String getName() { return uri.toString(); } /* * (non-Javadoc) * @see org.eclipse.core.filesystem.IFileStore#getParent() */ public IFileStore getParent() { // We need to take the uri.getPath and lop off the last segment! try { String path = uri.getPath(); IPath aPath = new Path(path); aPath = aPath.removeLastSegments(1); // FIXME What about fragments/query params? URI parentURI = new URI(uri.getScheme(), uri.getUserInfo(), uri.getHost(), uri.getPort(), aPath .toPortableString(), uri.getQuery(), uri.getFragment()); return new HttpFileStore(parentURI); } catch (URISyntaxException e) { HttpFilesystemPlugin.log(e); } return null; } /* * (non-Javadoc) * @see org.eclipse.core.filesystem.IFileStore#toURI() */ public URI toURI() { return uri; } @Override public synchronized File toLocalFile(int options, IProgressMonitor monitor) throws CoreException { if ((options & EFS.CACHE) == 0) return null; SubMonitor sub = SubMonitor.convert(monitor); // Try to grab copied cache file based on URL! File tmpDir = FileUtil.getTempDirectory().toFile(); File cached = new File(tmpDir, getPath(uri)); if (cached.exists()) { IFileStore localFile = EFS.getLocalFileSystem().fromLocalFile(cached); long lastModified = localFile.fetchInfo(EFS.NONE, sub.newChild(5)).getLastModified(); long remoteLastModified = fetchInfo(EFS.NONE, sub.newChild(20)).getLastModified(); // TODO What if one or both return EFS.NONE? if (lastModified >= remoteLastModified) { if (IdeLog.isTraceEnabled(HttpFilesystemPlugin.getDefault(), IDebugScopes.FILESYSTEM)) { IdeLog.logTrace(HttpFilesystemPlugin.getDefault(), MessageFormat.format( "Returning locally cached URI {0} version for remote URI {0}", localFile.toURI(), uri)); //$NON-NLS-1$ } return cached; } } sub.worked(25); // make directory structure for our copy if (!cached.getParentFile().exists() && !cached.getParentFile().mkdirs()) { throw new CoreException(new Status(IStatus.ERROR, HttpFilesystemPlugin.PLUGIN_ID, EFS.ERROR_INTERNAL, MessageFormat .format("Unable to create directory structure {0} for locally cached copy", cached.getParentFile()), null)); //$NON-NLS-1$ } // Download to some filename we can associate and pull back up based on URL! (used above) IFileStore resultStore = EFS.getLocalFileSystem().fromLocalFile(cached); copy(resultStore, EFS.OVERWRITE, sub.newChild(75)); cached.deleteOnExit(); if (IdeLog.isTraceEnabled(HttpFilesystemPlugin.getDefault(), IDebugScopes.FILESYSTEM)) { IdeLog.logTrace(HttpFilesystemPlugin.getDefault(), MessageFormat.format("Caching remote URI {0} to local URI {1}", uri, resultStore.toURI())); //$NON-NLS-1$ } return cached; } /** * Given a URL, returns a filesystem-sanctioned-path * * @param uri * @return */ public static String getPath(URI uri) { char separator = '_'; // FIXME What about fragments/query params? StringBuilder builder = new StringBuilder(); builder.append(uri.getScheme()); builder.append(separator); builder.append(uri.getHost()); builder.append(separator); int port = uri.getPort(); int defaultPort = 80; if ("https".equalsIgnoreCase(uri.getScheme())) //$NON-NLS-1$ { defaultPort = 443; } if (port != -1 && port != defaultPort) { builder.append(uri.getPort()); builder.append(separator); } builder.append(separator); builder.append(builder.hashCode()); builder.append(new Path(uri.getPath().replace('/', separator)).toOSString()); return builder.toString(); } @Override public InputStream openInputStream(int options, IProgressMonitor monitor) throws CoreException { monitor = SubMonitor.convert(monitor, 2); try { monitor.beginTask("", 1); //$NON-NLS-1$ return uri.toURL().openStream(); } catch (MalformedURLException e) { HttpFilesystemPlugin.log(e); throw new CoreException(new Status(IStatus.ERROR, HttpFilesystemPlugin.PLUGIN_ID, EFS.ERROR_NO_LOCATION, e .getMessage(), e)); } catch (IOException e) { HttpFilesystemPlugin.log(e); throw new CoreException(new Status(IStatus.ERROR, HttpFilesystemPlugin.PLUGIN_ID, EFS.ERROR_READ, e .getMessage(), e)); } finally { monitor.done(); } } }