Browse Source

Make <scp> support the sftp protocol. PR 39373

git-svn-id: https://svn.apache.org/repos/asf/ant/core/trunk@405300 13f79535-47bb-0310-9956-ffa450edef68
master
Stefan Bodewig 19 years ago
parent
commit
59e489be6d
7 changed files with 545 additions and 12 deletions
  1. +2
    -0
      WHATSNEW
  2. +51
    -1
      src/main/org/apache/tools/ant/taskdefs/optional/ssh/AbstractSshMessage.java
  3. +42
    -10
      src/main/org/apache/tools/ant/taskdefs/optional/ssh/Scp.java
  4. +19
    -1
      src/main/org/apache/tools/ant/taskdefs/optional/ssh/ScpFromMessage.java
  5. +173
    -0
      src/main/org/apache/tools/ant/taskdefs/optional/ssh/ScpFromMessageBySftp.java
  6. +18
    -0
      src/main/org/apache/tools/ant/taskdefs/optional/ssh/ScpToMessage.java
  7. +240
    -0
      src/main/org/apache/tools/ant/taskdefs/optional/ssh/ScpToMessageBySftp.java

+ 2
- 0
WHATSNEW View File

@@ -410,6 +410,8 @@ Other changes:
* Handling of ' ', '#' in CLASSPATH and '#' in -lib (cannot use ' ' * Handling of ' ', '#' in CLASSPATH and '#' in -lib (cannot use ' '
in -lib on UNIX at the moment). Bugzilla Report 39295. in -lib on UNIX at the moment). Bugzilla Report 39295.


* <scp> now optionally supports the sftp protocol. Bugzilla Report 39373.

Changes from Ant 1.6.4 to Ant 1.6.5 Changes from Ant 1.6.4 to Ant 1.6.5
=================================== ===================================




+ 51
- 1
src/main/org/apache/tools/ant/taskdefs/optional/ssh/AbstractSshMessage.java View File

@@ -1,5 +1,5 @@
/* /*
* Copyright 2003-2005 The Apache Software Foundation
* Copyright 2003-2006 The Apache Software Foundation
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@@ -21,6 +21,10 @@ import com.jcraft.jsch.Channel;
import com.jcraft.jsch.ChannelExec; import com.jcraft.jsch.ChannelExec;
import com.jcraft.jsch.JSchException; import com.jcraft.jsch.JSchException;
import com.jcraft.jsch.Session; import com.jcraft.jsch.Session;
import com.jcraft.jsch.ChannelSftp;
import com.jcraft.jsch.SftpATTRS;
import com.jcraft.jsch.SftpException;
import com.jcraft.jsch.SftpProgressMonitor;


import java.io.IOException; import java.io.IOException;
import java.io.OutputStream; import java.io.OutputStream;
@@ -74,6 +78,17 @@ public abstract class AbstractSshMessage {
return channel; return channel;
} }


/**
* Open an ssh sftp channel.
* @return the channel
* @throws JSchException on error
*/
protected ChannelSftp openSftpChannel() throws JSchException {
ChannelSftp channel = (ChannelSftp) session.openChannel("sftp");

return channel;
}

/** /**
* Send an ack. * Send an ack.
* @param out the output stream to use * @param out the output stream to use
@@ -213,4 +228,39 @@ public abstract class AbstractSshMessage {
return percent; return percent;
} }


private ProgressMonitor monitor = null;

protected SftpProgressMonitor getProgressMonitor(){
if (monitor == null) {
monitor = new ProgressMonitor();
}
return monitor;
}

private class ProgressMonitor implements SftpProgressMonitor {
private long initFileSize = 0;
private long totalLength = 0;
private int percentTransmitted = 0;

public void init(int op, String src, String dest, long max) {
initFileSize = max;
totalLength = 0;
percentTransmitted = 0;
}

public boolean count(long len) {
totalLength += len;
percentTransmitted = trackProgress(initFileSize,
totalLength,
percentTransmitted);
return true;
}

public void end() {
}

public long getTotalLength() {
return totalLength;
}
}
} }

+ 42
- 10
src/main/org/apache/tools/ant/taskdefs/optional/ssh/Scp.java View File

@@ -1,5 +1,5 @@
/* /*
* Copyright 2003-2005 The Apache Software Foundation
* Copyright 2003-2006 The Apache Software Foundation
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@@ -44,6 +44,7 @@ public class Scp extends SSHBase {
private String toUri; private String toUri;
private List fileSets = null; private List fileSets = null;
private boolean isFromRemote, isToRemote; private boolean isFromRemote, isToRemote;
private boolean isSftp = false;


/** /**
* Sets the file to be transferred. This can either be a remote * Sets the file to be transferred. This can either be a remote
@@ -141,6 +142,15 @@ public class Scp extends SSHBase {
this.isToRemote = true; this.isToRemote = true;
} }


/**
* Setting this to true to use sftp protocol.
*
* @param yesOrNo if true sftp protocol will be used.
*/
public void setSftp(boolean yesOrNo) {
isSftp = yesOrNo;
}

/** /**
* Adds a FileSet tranfer to remote host. NOTE: Either * Adds a FileSet tranfer to remote host. NOTE: Either
* addFileSet() or setFile() are required. But, not both. * addFileSet() or setFile() are required. But, not both.
@@ -213,10 +223,18 @@ public class Scp extends SSHBase {
Session session = null; Session session = null;
try { try {
session = openSession(); session = openSession();
ScpFromMessage message =
new ScpFromMessage(getVerbose(), session, file,
getProject().resolveFile(toPath),
fromSshUri.endsWith("*"));
ScpFromMessage message = null;
if (!isSftp){
message =
new ScpFromMessage(getVerbose(), session, file,
getProject().resolveFile(toPath),
fromSshUri.endsWith("*"));
} else{
message =
new ScpFromMessageBySftp(getVerbose(), session, file,
getProject().resolveFile(toPath),
fromSshUri.endsWith("*"));
}
log("Receiving file: " + file); log("Receiving file: " + file);
message.setLogListener(this); message.setLogListener(this);
message.execute(); message.execute();
@@ -243,8 +261,14 @@ public class Scp extends SSHBase {
} }
if (!list.isEmpty()) { if (!list.isEmpty()) {
session = openSession(); session = openSession();
ScpToMessage message = new ScpToMessage(getVerbose(), session,
list, file);
ScpToMessage message = null;
if (!isSftp){
message = new ScpToMessage(getVerbose(), session,
list, file);
} else{
message = new ScpToMessageBySftp(getVerbose(), session,
list, file);
}
message.setLogListener(this); message.setLogListener(this);
message.execute(); message.execute();
} }
@@ -262,9 +286,17 @@ public class Scp extends SSHBase {
Session session = null; Session session = null;
try { try {
session = openSession(); session = openSession();
ScpToMessage message =
new ScpToMessage(getVerbose(), session,
getProject().resolveFile(fromPath), file);
ScpToMessage message = null;
if (!isSftp){
message =
new ScpToMessage(getVerbose(), session,
getProject().resolveFile(fromPath), file);
} else{
message =
new ScpToMessageBySftp(getVerbose(), session,
getProject().resolveFile(fromPath),
file);
}
message.setLogListener(this); message.setLogListener(this);
message.execute(); message.execute();
} finally { } finally {


+ 19
- 1
src/main/org/apache/tools/ant/taskdefs/optional/ssh/ScpFromMessage.java View File

@@ -1,5 +1,5 @@
/* /*
* Copyright 2003-2005 The Apache Software Foundation
* Copyright 2003-2006 The Apache Software Foundation
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@@ -40,6 +40,24 @@ public class ScpFromMessage extends AbstractSshMessage {
private File localFile; private File localFile;
private boolean isRecursive = false; private boolean isRecursive = false;


/**
* Constructor for ScpFromMessage
* @param session the ssh session to use
*/
public ScpFromMessage(Session session) {
super(session);
}

/**
* Constructor for ScpFromMessage
* @param verbose if true do verbose logging
* @param session the ssh session to use
* @since Ant 1.6.2
*/
public ScpFromMessage(boolean verbose, Session session) {
super(verbose, session);
}

/** /**
* Constructor for ScpFromMessage. * Constructor for ScpFromMessage.
* @param verbose if true log extra information * @param verbose if true log extra information


+ 173
- 0
src/main/org/apache/tools/ant/taskdefs/optional/ssh/ScpFromMessageBySftp.java View File

@@ -0,0 +1,173 @@
/*
* Copyright 2006 The Apache Software Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/

package org.apache.tools.ant.taskdefs.optional.ssh;

import java.io.File;
import java.io.IOException;
import java.io.EOFException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.FileOutputStream;
import java.io.ByteArrayOutputStream;

import com.jcraft.jsch.JSchException;
import com.jcraft.jsch.Session;
import com.jcraft.jsch.Channel;
import com.jcraft.jsch.ChannelSftp;
import com.jcraft.jsch.SftpException;
import com.jcraft.jsch.SftpATTRS;
import com.jcraft.jsch.SftpProgressMonitor;

/**
* A helper object representing an scp download.
*/
public class ScpFromMessageBySftp extends ScpFromMessage {

private String remoteFile;
private File localFile;
private boolean isRecursive = false;
private boolean verbose = false;

/**
* Constructor for ScpFromMessageBySftp.
* @param verbose if true log extra information
* @param session the Scp session to use
* @param aRemoteFile the remote file name
* @param aLocalFile the local file
* @param recursive if true use recursion
* @since Ant 1.7
*/
public ScpFromMessageBySftp(boolean verbose,
Session session,
String aRemoteFile,
File aLocalFile,
boolean recursive) {
super(verbose, session);
this.verbose = verbose;
this.remoteFile = aRemoteFile;
this.localFile = aLocalFile;
this.isRecursive = recursive;
}

/**
* Constructor for ScpFromMessageBySftp.
* @param session the Scp session to use
* @param aRemoteFile the remote file name
* @param aLocalFile the local file
* @param recursive if true use recursion
*/
public ScpFromMessageBySftp(Session session,
String aRemoteFile,
File aLocalFile,
boolean recursive) {
this(false, session, aRemoteFile, aLocalFile, recursive);
}

/**
* Carry out the transfer.
* @throws IOException on i/o errors
* @throws JSchException on errors detected by scp
*/
public void execute() throws IOException, JSchException {
ChannelSftp channel = openSftpChannel();
try {
channel.connect();
try {
SftpATTRS attrs = channel.stat(remoteFile);
if (attrs.isDir() && !remoteFile.endsWith("/")) {
remoteFile=remoteFile+"/";
}
} catch(SftpException ee) {
}
getDir(channel, remoteFile, localFile);
} catch(SftpException e) {
throw new JSchException(e.toString());
} finally {
if (channel != null) {
channel.disconnect();
}
}
log("done\n");
}

private void getDir(ChannelSftp channel,
String remoteFile,
File localFile) throws IOException, SftpException {
String pwd=remoteFile;
if (remoteFile.lastIndexOf('/')!=-1) {
if (remoteFile.length()>1) {
pwd=remoteFile.substring(0, remoteFile.lastIndexOf('/'));
}
}
channel.cd(pwd);
if (!localFile.exists()) {
localFile.mkdirs();
}
java.util.Vector files = channel.ls(remoteFile);
for(int i = 0; i < files.size(); i++){
ChannelSftp.LsEntry le = (ChannelSftp.LsEntry) files.elementAt(i);
String name = le.getFilename();
if (le.getAttrs().isDir()) {
if (name.equals(".") || name.equals("..")) {
continue;
}
getDir(channel,
channel.pwd() + "/" + name + "/",
new File(localFile, le.getFilename()));
} else{
getFile(channel, le, localFile);
}
}
channel.cd("..");
}

private void getFile(ChannelSftp channel,
ChannelSftp.LsEntry le,
File localFile) throws IOException, SftpException {
String remoteFile = le.getFilename();
if (!localFile.exists()) {
String path = localFile.getAbsolutePath();
int i = 0;
if ((i = path.lastIndexOf(File.pathSeparator)) != -1) {
if (path.length()>File.pathSeparator.length()) {
new File(path.substring(0, i)).mkdirs();
}
}
}

if (localFile.isDirectory()) {
localFile=new File(localFile, remoteFile);
}

long startTime = System.currentTimeMillis();
long totalLength = le.getAttrs().getSize();

SftpProgressMonitor monitor = null;
boolean trackProgress = getVerbose() && totalLength > 102400;
if (trackProgress){
monitor = getProgressMonitor();
}
try{
log("Receiving: " + remoteFile + " : " + le.getAttrs().getSize());
channel.get(remoteFile, localFile.getAbsolutePath(), monitor);
} finally{
long endTime = System.currentTimeMillis();
logStats(startTime, endTime, (int)totalLength);
}
}
}

+ 18
- 0
src/main/org/apache/tools/ant/taskdefs/optional/ssh/ScpToMessage.java View File

@@ -39,6 +39,24 @@ public class ScpToMessage extends AbstractSshMessage {
private String remotePath; private String remotePath;
private List directoryList; private List directoryList;


/**
* Constructor for ScpToMessage
* @param session the ssh session to use
*/
public ScpToMessage(Session session) {
super(session);
}

/**
* Constructor for ScpToMessage
* @param verbose if true do verbose logging
* @param session the ssh session to use
* @since Ant 1.7
*/
public ScpToMessage(boolean verbose, Session session) {
super(verbose, session);
}

/** /**
* Constructor for a local file to remote. * Constructor for a local file to remote.
* @param verbose if true do verbose logging * @param verbose if true do verbose logging


+ 240
- 0
src/main/org/apache/tools/ant/taskdefs/optional/ssh/ScpToMessageBySftp.java View File

@@ -0,0 +1,240 @@
/*
* Copyright 2006 The Apache Software Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/

package org.apache.tools.ant.taskdefs.optional.ssh;

import com.jcraft.jsch.Channel;
import com.jcraft.jsch.Session;
import com.jcraft.jsch.ChannelSftp;
import com.jcraft.jsch.JSchException;
import com.jcraft.jsch.SftpException;
import com.jcraft.jsch.SftpProgressMonitor;
import com.jcraft.jsch.SftpATTRS;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.FileInputStream;
import java.io.OutputStream;
import java.util.List;
import java.util.Iterator;

public class ScpToMessageBySftp extends ScpToMessage/*AbstractSshMessage*/ {

private File localFile;
private String remotePath;
private List directoryList;

/**
* Constructor for a local file to remote.
* @param verbose if true do verbose logging
* @param session the scp session to use
* @param aLocalFile the local file
* @param aRemotePath the remote path
* @since Ant 1.7
*/
public ScpToMessageBySftp(boolean verbose,
Session session,
File aLocalFile,
String aRemotePath) {
this(verbose, session, aRemotePath);

this.localFile = aLocalFile;
}

/**
* Constructor for a local directories to remote.
* @param verbose if true do verbose logging
* @param session the scp session to use
* @param aDirectoryList a list of directories
* @param aRemotePath the remote path
* @since Ant 1.7
*/
public ScpToMessageBySftp(boolean verbose,
Session session,
List aDirectoryList,
String aRemotePath) {
this(verbose, session, aRemotePath);

this.directoryList = aDirectoryList;
}

/**
* Constructor for ScpToMessage.
* @param verbose if true do verbose logging
* @param session the scp session to use
* @param aRemotePath the remote path
* @since Ant 1.6.2
*/
private ScpToMessageBySftp(boolean verbose,
Session session,
String aRemotePath) {
super(verbose, session);
this.remotePath = aRemotePath;
}

/**
* Constructor for ScpToMessage.
* @param session the scp session to use
* @param aLocalFile the local file
* @param aRemotePath the remote path
*/
public ScpToMessageBySftp(Session session,
File aLocalFile,
String aRemotePath) {
this(false, session, aLocalFile, aRemotePath);
}

/**
* Constructor for ScpToMessage.
* @param session the scp session to use
* @param aDirectoryList a list of directories
* @param aRemotePath the remote path
*/
public ScpToMessageBySftp(Session session,
List aDirectoryList,
String aRemotePath) {
this(false, session, aDirectoryList, aRemotePath);
}

/**
* Carry out the transfer.
* @throws IOException on i/o errors
* @throws JSchException on errors detected by scp
*/
public void execute() throws IOException, JSchException {
if (directoryList != null) {
doMultipleTransfer();
}
if (localFile != null) {
doSingleTransfer();
}
log("done.\n");
}

private void doSingleTransfer() throws IOException, JSchException {
ChannelSftp channel = openSftpChannel();
try {
channel.connect();
try{
sendFileToRemote(channel, localFile, remotePath);
}
catch(SftpException e){
throw new JSchException(e.toString());
}
} finally {
if (channel != null) {
channel.disconnect();
}
}
}

private void doMultipleTransfer() throws IOException, JSchException {
ChannelSftp channel = openSftpChannel();
try {
channel.connect();

try{
channel.cd(remotePath);
for (Iterator i = directoryList.iterator(); i.hasNext();) {
Directory current = (Directory) i.next();
sendDirectory(channel, current);
}
}
catch(SftpException e){
throw new JSchException(e.toString());
}
} finally {
if (channel != null) {
channel.disconnect();
}
}
}

private void sendDirectory(ChannelSftp channel,
Directory current)
throws IOException, SftpException {
for (Iterator fileIt = current.filesIterator(); fileIt.hasNext();) {
sendFileToRemote(channel, (File) fileIt.next(), null);
}
for (Iterator dirIt = current.directoryIterator(); dirIt.hasNext();) {
Directory dir = (Directory) dirIt.next();
sendDirectoryToRemote(channel, dir);
}
}

private void sendDirectoryToRemote(ChannelSftp channel,
Directory directory)
throws IOException, SftpException {
String dir=directory.getDirectory().getName();
try{
channel.stat(dir);
}
catch(SftpException e){
// dir does not exist.
if (e.id==ChannelSftp.SSH_FX_NO_SUCH_FILE) {
channel.mkdir(dir);
}
}
channel.cd(dir);
sendDirectory(channel, directory);
channel.cd("..");
}

private void sendFileToRemote(ChannelSftp channel,
File localFile,
String remotePath)
throws IOException, SftpException {
long filesize = localFile.length();

if (remotePath==null) {
remotePath=localFile.getName();
}

long startTime = System.currentTimeMillis();
long totalLength = filesize;

// only track progress for files larger than 100kb in verbose mode
boolean trackProgress = getVerbose() && filesize > 102400;

SftpProgressMonitor monitor = null;
if (trackProgress){
monitor = getProgressMonitor();
}

try{
if (this.getVerbose()) {
log("Sending: " + localFile.getName() + " : " + filesize);
}
channel.put(localFile.getAbsolutePath(), remotePath, monitor);
}
finally {
if (this.getVerbose()) {
long endTime = System.currentTimeMillis();
logStats(startTime, endTime, (int) totalLength);
}
}
}

public File getLocalFile() {
return localFile;
}

public String getRemotePath() {
return remotePath;
}
}

Loading…
Cancel
Save