package org.eclipse.dltk.ssh.internal.core; import java.io.InputStream; import java.io.OutputStream; import java.util.HashMap; import java.util.Map; import java.util.Vector; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.Path; import org.eclipse.dltk.ssh.core.ISshFileHandle; import com.jcraft.jsch.SftpATTRS; import com.jcraft.jsch.ChannelSftp.LsEntry; public class SshFileHandle implements ISshFileHandle, IOutputStreamCloseListener { private static final int CACHE_LIMIT = 1000; private static class CacheEntry { final SftpATTRS attrs; final long lastAccess; public CacheEntry(SftpATTRS attrs, long lastAccess) { this.attrs = attrs; this.lastAccess = lastAccess; } } private static final Map<SshFileHandle, CacheEntry> attrCache = new HashMap<SshFileHandle, CacheEntry>(); private SshConnection connection = null; private IPath path; // private IPath linkTarget; private SftpATTRS attrs; private Map<String, SshFileHandle> children = new HashMap<String, SshFileHandle>(); private boolean childrenFetched = false; public SshFileHandle(SshConnection connection, IPath path, SftpATTRS attrs) { this.connection = connection; this.path = path; this.attrs = attrs; } /* * (non-Javadoc) * * @see * org.eclipse.dltk.ssh.internal.core.ISshFileHandle#createFolder(java.lang * .String, org.eclipse.core.runtime.IProgressMonitor) */ public ISshFileHandle createFolder(String newEntryName, IProgressMonitor monitor) throws CoreException { ISshFileHandle child = getChild(newEntryName); if (child != null) { child.mkdir(); fetchAttrs(); } return child; } /* * (non-Javadoc) * * @see org.eclipse.dltk.ssh.internal.core.ISshFileHandle#mkdir() */ public void mkdir() { connection.mkdir(path); cleanAttrs(); } /* * (non-Javadoc) * * @see org.eclipse.dltk.ssh.internal.core.ISshFileHandle#delete() */ public void delete() throws CoreException { fetchAttrs(); if (attrs != null) { connection.delete(path, attrs.isDir()); cleanAttrs(); } } /* * (non-Javadoc) * * @see org.eclipse.dltk.ssh.internal.core.ISshFileHandle#exists() */ public boolean exists() { fetchAttrs(); return attrs != null; } private void fetchAttrs() { fetchAttrs(false); } private void cleanAttrs() { attrs = null; synchronized (attrCache) { attrCache.remove(this); } } private void fetchAttrs(boolean clean) { if (attrs == null || clean) { attrs = fetchCacheAttrs(clean); } if (attrs != null && attrs.isLink()) { attrs = fetchCacheAttrs(true); // this.linkTarget = connection.getResolvedPath(path); } } private SftpATTRS fetchCacheAttrs(boolean clean) { long c = 0; synchronized (attrCache) { if (attrCache.size() > CACHE_LIMIT) { attrCache.clear(); } else if (!clean) { final CacheEntry entry = attrCache.get(this); if (entry != null) { c = System.currentTimeMillis(); if ((c - entry.lastAccess) < 1000 * 10) { return entry.attrs; } } } } SftpATTRS attrs = connection.getAttrs(path); if (c == 0) { c = System.currentTimeMillis(); } synchronized (attrCache) { attrCache.put(this, new CacheEntry(attrs, c)); } return attrs; } public synchronized ISshFileHandle getChild(String newEntryName) { if (children.containsKey(newEntryName)) { return children.get(newEntryName); } ISshFileHandle child = new SshFileHandle(connection, path .append(newEntryName), null); return child; } /* * (non-Javadoc) * * @see * org.eclipse.dltk.ssh.internal.core.ISshFileHandle#getChildren(org.eclipse * .core.runtime.IProgressMonitor) */ public synchronized ISshFileHandle[] getChildren(IProgressMonitor monitor) throws CoreException { if (!childrenFetched) { // Fetch all child handles fetchChildren(); } return children.values().toArray(new SshFileHandle[children.size()]); } private void fetchChildren() { Vector<LsEntry> list = connection.list(path); if (list != null) { children.clear(); long c = System.currentTimeMillis(); for (LsEntry entry : list) { String filename = entry.getFilename(); if (filename.equals(".") || filename.equals("..")) { //$NON-NLS-1$ //$NON-NLS-2$ continue; } final SftpATTRS childAttrs = entry.getAttrs(); final IPath childPath; if (filename.indexOf(IPath.DEVICE_SEPARATOR) == -1) { childPath = path.append(filename); } else { // this way DEVICE_SEPARATOR is kept in path segment childPath = path.append(new Path(null, filename)); } SshFileHandle childHandle = new SshFileHandle(connection, childPath, childAttrs); synchronized (attrCache) { attrCache.put(childHandle, new CacheEntry(childAttrs, c)); } children.put(filename, childHandle); } childrenFetched = true; } } /* * (non-Javadoc) * * @see * org.eclipse.dltk.ssh.internal.core.ISshFileHandle#getInputStream(org. * eclipse.core.runtime.IProgressMonitor) */ public InputStream getInputStream(IProgressMonitor monitor) throws CoreException { // fetchAttrs(); // if (attrs != null) { // IPath current = this.path; // if (attrs.isLink() && linkTarget != null) { // current = linkTarget; // } final InputStream stream = connection.get(this.path); // TODO throw/wrap original exception? return stream; // } // return null; } /* * (non-Javadoc) * * @see org.eclipse.dltk.ssh.internal.core.ISshFileHandle#getName() */ public String getName() { return path.lastSegment(); } /* * (non-Javadoc) * * @see org.eclipse.dltk.ssh.core.ISshFileHandle#getPath() */ public IPath getPath() { return path; } /* * (non-Javadoc) * * @see java.lang.Object#toString() */ @Override public String toString() { return path.toString(); } /* * (non-Javadoc) * * @see * org.eclipse.dltk.ssh.internal.core.ISshFileHandle#getOutputStream(org * .eclipse.core.runtime.IProgressMonitor) */ public OutputStream getOutputStream(IProgressMonitor monitor) throws CoreException { final OutputStream stream = connection.put(this.path, this); return stream; } public void streamClosed() { cleanAttrs(); } /* * (non-Javadoc) * * @see org.eclipse.dltk.ssh.internal.core.ISshFileHandle#isDirectory() */ public boolean isDirectory() { fetchAttrs(); if (attrs != null) { return attrs.isDir(); } else { // IStatus status = new Status(IStatus.ERROR, "blah", // "Failed to retrive file information:" + path); // throw new CoreException(status); return false; } } /* * (non-Javadoc) * * @see * org.eclipse.dltk.ssh.internal.core.ISshFileHandle#lastModificationTime() */ public long lastModificationTime() { fetchAttrs(); if (attrs != null) { return attrs.getMTime() * 1000L; } else { // IStatus status = new Status(IStatus.ERROR, "blah", // "Failed to retrive file information:" + path); // throw new CoreException(status); return 0; } } /* * (non-Javadoc) * * @see * org.eclipse.dltk.ssh.internal.core.ISshFileHandle#setLastModified(long, * org.eclipse.core.runtime.IProgressMonitor) */ public void setLastModified(long timestamp, IProgressMonitor monitor) throws CoreException { connection.setLastModified(path, timestamp); cleanAttrs(); } /* * (non-Javadoc) * * @see org.eclipse.dltk.ssh.internal.core.ISshFileHandle#getSize() */ public long getSize() { fetchAttrs(); if (attrs != null) { return attrs.getSize(); } return 0; } public boolean isSymlink() { final SftpATTRS attrs = connection.getLAttrs(path); return attrs != null && attrs.isLink(); } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((connection == null) ? 0 : connection.hashCode()); result = prime * result + ((path == null) ? 0 : path.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; SshFileHandle other = (SshFileHandle) obj; if (connection == null) { if (other.connection != null) return false; } else if (!connection.equals(other.connection)) return false; if (path == null) { if (other.path != null) return false; } else if (!path.equals(other.path)) return false; return true; } public String readLink() { return connection.readLink(path); } public void move(IPath destination) throws CoreException { connection.move(path, destination); } }