blob: b2bb487112a6a88ef4b8d039d05bb58a3cf9b212 [file] [log] [blame]
package org.eclipse.dltk.ssh.internal.core;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Date;
import java.util.Vector;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.Path;
import org.eclipse.dltk.ssh.core.ISshConnection;
import org.eclipse.dltk.ssh.core.ISshFileHandle;
import com.jcraft.jsch.ChannelSftp;
import com.jcraft.jsch.JSchException;
import com.jcraft.jsch.SftpATTRS;
import com.jcraft.jsch.SftpException;
import com.jcraft.jsch.ChannelSftp.LsEntry;
public class SshConnection extends ChannelPool implements ISshConnection {
private long disabledTime = 0;
private static abstract class Operation {
private boolean finished = false;
public boolean isLongRunning() {
return false;
}
public abstract void perform(ChannelSftp channel) throws SftpException;
public void setFinished() {
finished = true;
}
public boolean isFinished() {
return finished;
}
}
private static class GetStatOperation extends Operation {
protected IPath path;
protected SftpATTRS attrs;
public GetStatOperation(IPath path) {
this.path = path;
}
@Override
public String toString() {
return "Get information for file:" + path; //$NON-NLS-1$
}
@Override
public void perform(ChannelSftp channel) throws SftpException {
attrs = channel.stat(path.toString());
}
public SftpATTRS getAttrs() {
return attrs;
}
}
private static class ReadLinkOperation extends Operation {
protected IPath path;
protected String link;
public ReadLinkOperation(IPath path) {
this.path = path;
}
@Override
public String toString() {
return "Get information for file:" + path; //$NON-NLS-1$
}
@Override
public void perform(ChannelSftp channel) throws SftpException {
if (path.segmentCount() == 0) {
return;
}
String spath = path.toString();
link = channel.readlink(spath);
if (link != null && !link.equals(spath)) {
String curDir = channel.pwd();
String parentPath = path.removeLastSegments(1).toString();
if (!parentPath.equals(curDir)) {
channel.cd(parentPath);
}
SftpATTRS attrs = channel.stat(link);
if (attrs.isDir()) {
channel.cd(spath);
link = channel.pwd();
}
}
}
public String getLink() {
return link;
}
}
private static class GetLStatOperation extends GetStatOperation {
public GetLStatOperation(IPath path) {
super(path);
}
@Override
public void perform(ChannelSftp channel) throws SftpException {
attrs = channel.lstat(path.toString());
}
}
private static class ResolveLinkOperation extends Operation {
private IPath path;
private IPath resolvedPath;
public ResolveLinkOperation(IPath path) {
this.path = path;
}
@Override
public String toString() {
return "Resolve link information for file:" + path; //$NON-NLS-1$
}
@Override
public void perform(ChannelSftp channel) throws SftpException {
SftpATTRS attrs = channel.stat(path.toString());
boolean isRoot = (path.segmentCount() == 0);
String linkTarget = null;
String canonicalPath;
String parentPath = path.removeLastSegments(1).toString();
if (attrs.isLink() && !isRoot) {
try {
String fullPath = path.toString();
boolean readlinkDone = false;
try {
linkTarget = channel.readlink(fullPath);
readlinkDone = true;
} catch (Throwable t) {
channel.cd(fullPath);
linkTarget = channel.pwd();
canonicalPath = linkTarget;
}
if (linkTarget != null && !linkTarget.equals(fullPath)) {
if (readlinkDone) {
String curdir = channel.pwd();
if (!parentPath.equals(curdir)) {
channel.cd(parentPath);
}
}
SftpATTRS attrsTarget = channel.stat(linkTarget);
if (readlinkDone && attrsTarget.isDir()) {
channel.cd(fullPath);
canonicalPath = channel.pwd();
}
} else {
linkTarget = null;
}
} catch (Exception e) {
if (e instanceof SftpException
&& ((SftpException) e).id == ChannelSftp.SSH_FX_NO_SUCH_FILE) {
if (linkTarget == null) {
linkTarget = ":dangling link"; //$NON-NLS-1$
} else {
linkTarget = ":dangling link:" + linkTarget; //$NON-NLS-1$
}
}
}
resolvedPath = new Path(linkTarget);
}
}
public IPath getResolvedPath() {
return resolvedPath;
}
}
private static final int STREAM_BUFFER_SIZE = 32000;
private static interface StreamOperation {
boolean isActiveCall();
long getLastActivity();
}
private class GetOperation extends Operation implements StreamOperation {
private IPath path;
private GetOperationInputStream stream;
public GetOperation(IPath path) {
this.path = path;
}
@Override
public boolean isLongRunning() {
return true;
}
@Override
public void perform(ChannelSftp channel) throws SftpException {
stream = new GetOperationInputStream(channel.get(path.toString()),
channel);
}
@Override
public String toString() {
return "Get input stream for file:" + path; //$NON-NLS-1$
}
public InputStream getStream() {
return stream;
}
public boolean isActiveCall() {
return stream != null && stream.activeCalls != 0;
}
public long getLastActivity() {
if (stream != null) {
return stream.lastActivity;
} else {
return Long.MIN_VALUE;
}
}
}
private class GetOperationInputStream extends BufferedInputStream {
private final ChannelSftp channel;
private int activeCalls;
private long lastActivity;
public GetOperationInputStream(InputStream in, ChannelSftp channel) {
super(in, STREAM_BUFFER_SIZE);
this.channel = channel;
updateLastActivity();
}
@Override
public void close() throws IOException {
try {
super.close();
} finally {
releaseChannel(channel);
}
}
private void updateLastActivity() {
lastActivity = System.currentTimeMillis();
}
private void beginActivity() {
++activeCalls;
updateLastActivity();
}
private void endActivity() {
--activeCalls;
updateLastActivity();
}
@Override
public synchronized int read() throws IOException {
beginActivity();
try {
return super.read();
} finally {
endActivity();
}
}
@Override
public int read(byte[] b) throws IOException {
beginActivity();
try {
return super.read(b);
} finally {
endActivity();
}
}
@Override
public synchronized int read(byte[] b, int off, int len)
throws IOException {
beginActivity();
try {
return super.read(b, off, len);
} finally {
endActivity();
}
}
}
private class PutOperation extends Operation implements StreamOperation {
private final IPath path;
private final IOutputStreamCloseListener closeListener;
private PutOperationOutputStream stream;
public PutOperation(IPath path, IOutputStreamCloseListener closeListener) {
this.path = path;
this.closeListener = closeListener;
}
@Override
public String toString() {
return "Get output stream for file:" + path; //$NON-NLS-1$
}
@Override
public boolean isLongRunning() {
return true;
}
@Override
public void perform(ChannelSftp channel) throws SftpException {
final OutputStream rawStream = channel.put(path.toString(),
ChannelSftp.OVERWRITE);
stream = new PutOperationOutputStream(rawStream, channel,
closeListener);
}
public OutputStream getStream() {
return stream;
}
public boolean isActiveCall() {
return stream != null && stream.activeCalls != 0;
}
public long getLastActivity() {
if (stream != null) {
return stream.lastActivity;
} else {
return Long.MIN_VALUE;
}
}
}
private class PutOperationOutputStream extends BufferedOutputStream {
private final ChannelSftp channel;
private final IOutputStreamCloseListener closeListener;
private int activeCalls;
private long lastActivity;
public PutOperationOutputStream(OutputStream out, ChannelSftp channel,
IOutputStreamCloseListener closeListener) {
super(out, STREAM_BUFFER_SIZE);
this.channel = channel;
this.closeListener = closeListener;
updateActivity();
}
@Override
public void close() throws IOException {
try {
super.close();
if (closeListener != null) {
closeListener.streamClosed();
}
} finally {
releaseChannel(channel);
}
}
private void updateActivity() {
lastActivity = System.currentTimeMillis();
}
private void beginActivity() {
++activeCalls;
updateActivity();
}
private void endActivity() {
--activeCalls;
updateActivity();
}
@Override
public synchronized void write(int b) throws IOException {
beginActivity();
try {
super.write(b);
} finally {
endActivity();
}
}
@Override
public void write(byte[] b) throws IOException {
beginActivity();
try {
super.write(b);
} finally {
endActivity();
}
}
@Override
public synchronized void write(byte[] b, int off, int len)
throws IOException {
beginActivity();
try {
super.write(b, off, len);
} finally {
endActivity();
}
}
}
private static class ListFolderOperation extends Operation {
private IPath path;
private Vector<LsEntry> v;
public ListFolderOperation(IPath path) {
this.path = path;
}
@Override
public String toString() {
return "List folder:" + path + " for files"; //$NON-NLS-1$ //$NON-NLS-2$
}
@Override
@SuppressWarnings("unchecked")
public void perform(ChannelSftp channel) throws SftpException {
v = channel.ls(path.toString());
}
public Vector<LsEntry> getVector() {
return v;
}
}
private static final int DEFAULT_RETRY_COUNT = 2;
private static final long DEFAULT_ACQUIRE_TIMEOUT = 30 * 1000;
private static final long DEFAULT_INACTIVITY_TIMEOUT = 60 * 1000;
public SshConnection(String userName, String hostName, int port) {
super(userName, hostName, port, DEFAULT_INACTIVITY_TIMEOUT);
}
public boolean connect() {
try {
final ChannelSftp channel = acquireChannel("connect()"); //$NON-NLS-1$
try {
return true;
} finally {
releaseChannel(channel);
}
} catch (JSchException e) {
return false;
}
}
private static boolean DEBUG = false;
private void performOperation(final Operation op) {
performOperation(op, DEFAULT_RETRY_COUNT);
}
private void performOperation(final Operation op, int tryCount) {
final ChannelSftp channel = acquireChannel(op, DEFAULT_ACQUIRE_TIMEOUT);
if (channel != null) {
try {
if (DEBUG) {
log(" [do] " + op); //$NON-NLS-1$
}
op.perform(channel);
op.setFinished();
} catch (SftpException e) {
if (e.id == ChannelSftp.SSH_FX_FAILURE
&& e.getCause() instanceof JSchException) {
Activator.log(e);
destroyChannel(channel);
disconnect();
if (tryCount > 0) {
performOperation(op, tryCount - 1);
}
} else if (e.id != ChannelSftp.SSH_FX_NO_SUCH_FILE) {
if (e.id == ChannelSftp.SSH_FX_PERMISSION_DENIED) {
Activator.log("Permission denied to perform:" //$NON-NLS-1$
+ op.toString());
} else {
Activator.log(e);
}
}
} finally {
if (!(op.isLongRunning() && op.isFinished())) {
releaseChannel(channel);
}
}
}
}
@Override
protected boolean canClose(Object context) {
return context instanceof StreamOperation;
}
@Override
protected long getLastActivity(Object context) {
if (context instanceof StreamOperation) {
return ((StreamOperation) context).getLastActivity();
} else {
return super.getLastActivity(context);
}
}
/*
* (non-Javadoc)
*
* @see org.eclipse.dltk.ssh.core.ISshConnection#getHandle(org.eclipse.core
* .runtime .IPath)
*/
public ISshFileHandle getHandle(IPath path) throws Exception {
if (isDisabled()) {
return null;
}
// GetStatOperation op = new GetStatOperation(path);
// performOperation(op, DEFAULT_RETRY_COUNT);
// if (op.isFinished()) {
// return new SshFileHandle(this, path, op.getAttrs());
// }
return new SshFileHandle(this, path, null);
}
public boolean isDisabled() {
return disabledTime > System.currentTimeMillis();
}
public void setDisabled(int timeout) {
disabledTime = System.currentTimeMillis() + timeout;
}
SftpATTRS getAttrs(IPath path) {
GetStatOperation op = new GetStatOperation(path);
performOperation(op);
if (op.isFinished()) {
return op.getAttrs();
}
return null;
}
SftpATTRS getLAttrs(IPath path) {
GetLStatOperation op = new GetLStatOperation(path);
performOperation(op);
if (op.isFinished()) {
return op.getAttrs();
}
return null;
}
IPath getResolvedPath(IPath path) {
ResolveLinkOperation op = new ResolveLinkOperation(path);
performOperation(op);
if (op.isFinished()) {
return op.getResolvedPath();
}
return null;
}
Vector<LsEntry> list(IPath path) {
ListFolderOperation op = new ListFolderOperation(path);
performOperation(op);
if (op.isFinished()) {
return op.getVector();
}
return null;
}
void setLastModified(final IPath path, final long timestamp) {
Operation op = new Operation() {
@Override
public void perform(ChannelSftp channel) throws SftpException {
Date date = new Date(timestamp);
System.out.println(date.toString());
channel.setMtime(path.toString(), (int) (timestamp / 1000L));
}
@Override
public String toString() {
return "setLastModified " + path; //$NON-NLS-1$
}
};
performOperation(op);
}
void delete(final IPath path, final boolean dir) {
Operation op = new Operation() {
@Override
public void perform(ChannelSftp channel) throws SftpException {
if (!dir) {
channel.rm(path.toString());
} else {
channel.rmdir(path.toString());
}
}
@Override
public String toString() {
return "delete " + path; //$NON-NLS-1$
}
};
performOperation(op);
}
void mkdir(final IPath path) {
Operation op = new Operation() {
@Override
public void perform(ChannelSftp channel) throws SftpException {
channel.mkdir(path.toString());
}
@Override
public String toString() {
return "mkdir " + path; //$NON-NLS-1$
}
};
performOperation(op);
}
InputStream get(IPath path) {
GetOperation op = new GetOperation(path);
performOperation(op);
if (op.isFinished()) {
return op.getStream();
}
return null;
}
String readLink(IPath path) {
ReadLinkOperation op = new ReadLinkOperation(path);
performOperation(op);
if (op.isFinished()) {
return op.getLink();
}
return null;
}
OutputStream put(IPath path) {
return put(path, null);
}
OutputStream put(IPath path, IOutputStreamCloseListener closeListener) {
PutOperation op = new PutOperation(path, closeListener);
performOperation(op);
if (op.isFinished()) {
return op.getStream();
}
return null;
}
}