git-svn-id: https://svn.apache.org/repos/asf/ant/core/trunk@677187 13f79535-47bb-0310-9956-ffa450edef68master
| @@ -236,6 +236,7 @@ Roman Ivashin | |||
| Ronen Mashal | |||
| Russell Gold | |||
| Sam Ruby | |||
| Sandra Metz | |||
| Scott Carlson | |||
| Scott Ellsworth | |||
| Scott M. Stirling | |||
| @@ -168,6 +168,10 @@ Other changes: | |||
| * <sshexec> now supports input in a way similar to <exec> | |||
| Bugzilla report 39197. | |||
| * <scp> can now preserve the file modification time when downloading | |||
| files. | |||
| Bugzilla Issue 33939. | |||
| Changes from Ant 1.7.0 TO Ant 1.7.1 | |||
| ============================================= | |||
| @@ -955,6 +955,10 @@ | |||
| <first>Sam</first> | |||
| <last>Ruby</last> | |||
| </name> | |||
| <name> | |||
| <first>Sandra</first> | |||
| <last>Metz</last> | |||
| </name> | |||
| <name> | |||
| <first>Scott</first> | |||
| <last>Carlson</last> | |||
| @@ -174,6 +174,15 @@ for more information. This task has been tested with jsch-0.1.2 and later.</p> | |||
| server that doesn't support scp1. <em>since Ant 1.7</em></td> | |||
| <td valign="top" align="center">No; defaults to false.</td> | |||
| </tr> | |||
| <tr> | |||
| <td valign="top">preserveLastModified</td> | |||
| <td valign="top">Determines whether the last modification | |||
| timestamp of downloaded files is preserved. It only works when | |||
| transferring from a remote to a local system and probably doesn't | |||
| work with a server that doesn't support SSH2. <em>since Ant | |||
| 1.8.0</em></td> | |||
| <td valign="top" align="center">No; defaults to false.</td> | |||
| </tr> | |||
| </table> | |||
| <h3>Parameters specified as nested elements</h3> | |||
| @@ -49,6 +49,7 @@ public class Scp extends SSHBase { | |||
| private String fromUri; | |||
| private String toUri; | |||
| private boolean preserveLastModified = false; | |||
| private List fileSets = null; | |||
| private boolean isFromRemote, isToRemote; | |||
| private boolean isSftp = false; | |||
| @@ -116,6 +117,15 @@ public class Scp extends SSHBase { | |||
| this.isToRemote = false; | |||
| } | |||
| /** | |||
| * Sets flag to determine if file timestamp from | |||
| * remote system is to be preserved during copy. | |||
| * @since Ant 1.8.0 | |||
| */ | |||
| public void setPreservelastmodified(boolean yesOrNo) { | |||
| this.preserveLastModified = yesOrNo; | |||
| } | |||
| /** | |||
| * Similiar to {@link #setTodir setTodir} but explicitly states | |||
| * that the directory is a remote. | |||
| @@ -231,12 +241,14 @@ public class Scp extends SSHBase { | |||
| message = | |||
| new ScpFromMessage(getVerbose(), session, file, | |||
| getProject().resolveFile(toPath), | |||
| fromSshUri.endsWith("*")); | |||
| fromSshUri.endsWith("*"), | |||
| preserveLastModified); | |||
| } else { | |||
| message = | |||
| new ScpFromMessageBySftp(getVerbose(), session, file, | |||
| getProject().resolveFile(toPath), | |||
| fromSshUri.endsWith("*")); | |||
| fromSshUri.endsWith("*"), | |||
| preserveLastModified); | |||
| } | |||
| log("Receiving file: " + file); | |||
| message.setLogListener(this); | |||
| @@ -27,7 +27,11 @@ import java.io.FileOutputStream; | |||
| import java.io.ByteArrayOutputStream; | |||
| import com.jcraft.jsch.JSchException; | |||
| import com.jcraft.jsch.Session; | |||
| import com.jcraft.jsch.SftpATTRS; | |||
| import com.jcraft.jsch.SftpException; | |||
| import com.jcraft.jsch.Channel; | |||
| import com.jcraft.jsch.ChannelSftp; | |||
| import org.apache.tools.ant.util.FileUtils; | |||
| /** | |||
| * A helper object representing an scp download. | |||
| @@ -41,6 +45,7 @@ public class ScpFromMessage extends AbstractSshMessage { | |||
| private String remoteFile; | |||
| private File localFile; | |||
| private boolean isRecursive = false; | |||
| private boolean preserveLastModified = false; | |||
| /** | |||
| * Constructor for ScpFromMessage | |||
| @@ -74,10 +79,7 @@ public class ScpFromMessage extends AbstractSshMessage { | |||
| String aRemoteFile, | |||
| File aLocalFile, | |||
| boolean recursive) { | |||
| super(verbose, session); | |||
| this.remoteFile = aRemoteFile; | |||
| this.localFile = aLocalFile; | |||
| this.isRecursive = recursive; | |||
| this(false, session, aRemoteFile, aLocalFile, recursive, false); | |||
| } | |||
| /** | |||
| @@ -94,6 +96,30 @@ public class ScpFromMessage extends AbstractSshMessage { | |||
| this(false, session, aRemoteFile, aLocalFile, recursive); | |||
| } | |||
| /** | |||
| * Constructor for ScpFromMessage. | |||
| * @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 (-r option to scp) | |||
| * @param preservceLastModified whether to preserve file | |||
| * modification times | |||
| * @since Ant 1.8.0 | |||
| */ | |||
| public ScpFromMessage(boolean verbose, | |||
| Session session, | |||
| String aRemoteFile, | |||
| File aLocalFile, | |||
| boolean recursive, | |||
| boolean preserveLastModified) { | |||
| super(verbose, session); | |||
| this.remoteFile = aRemoteFile; | |||
| this.localFile = aLocalFile; | |||
| this.isRecursive = recursive; | |||
| this.preserveLastModified = preserveLastModified; | |||
| } | |||
| /** | |||
| * Carry out the transfer. | |||
| * @throws IOException on i/o errors | |||
| @@ -123,9 +149,14 @@ public class ScpFromMessage extends AbstractSshMessage { | |||
| log("done\n"); | |||
| } | |||
| protected boolean getPreserveLastModified() { | |||
| return preserveLastModified; | |||
| } | |||
| private void startRemoteCpProtocol(InputStream in, | |||
| OutputStream out, | |||
| File localFile) throws IOException { | |||
| File localFile) | |||
| throws IOException, JSchException { | |||
| File startFile = localFile; | |||
| while (true) { | |||
| // C0644 filesize filename - header for a regular file | |||
| @@ -147,7 +178,7 @@ public class ScpFromMessage extends AbstractSshMessage { | |||
| parseAndFetchFile(serverResponse, startFile, out, in); | |||
| } else if (serverResponse.charAt(0) == 'D') { | |||
| startFile = parseAndCreateDirectory(serverResponse, | |||
| startFile); | |||
| startFile); | |||
| sendAck(out); | |||
| } else if (serverResponse.charAt(0) == 'E') { | |||
| startFile = startFile.getParentFile(); | |||
| @@ -178,7 +209,8 @@ public class ScpFromMessage extends AbstractSshMessage { | |||
| private void parseAndFetchFile(String serverResponse, | |||
| File localFile, | |||
| OutputStream out, | |||
| InputStream in) throws IOException { | |||
| InputStream in) | |||
| throws IOException, JSchException { | |||
| int start = 0; | |||
| int end = serverResponse.indexOf(" ", start + 1); | |||
| start = end + 1; | |||
| @@ -197,7 +229,8 @@ public class ScpFromMessage extends AbstractSshMessage { | |||
| private void fetchFile(File localFile, | |||
| long filesize, | |||
| OutputStream out, | |||
| InputStream in) throws IOException { | |||
| InputStream in) | |||
| throws IOException, JSchException { | |||
| byte[] buf = new byte[BUFFER_SIZE]; | |||
| sendAck(out); | |||
| @@ -241,6 +274,37 @@ public class ScpFromMessage extends AbstractSshMessage { | |||
| fos.flush(); | |||
| fos.close(); | |||
| } | |||
| if (getPreserveLastModified()) { | |||
| setLastModified(localFile); | |||
| } | |||
| } | |||
| private void setLastModified(File localFile) throws JSchException { | |||
| SftpATTRS fileAttributes = null; | |||
| String remotePath = null; | |||
| ChannelSftp channel = openSftpChannel(); | |||
| channel.connect(); | |||
| try { | |||
| fileAttributes = channel.lstat(remoteDir(remoteFile) | |||
| + localFile.getName()); | |||
| } catch (SftpException e) { | |||
| throw new JSchException("failed to stat remote file", e); | |||
| } | |||
| FileUtils.getFileUtils().setFileLastModified(localFile, | |||
| ((long) fileAttributes | |||
| .getMTime()) | |||
| * 1000); | |||
| } | |||
| /** | |||
| * returns the directory part of the remote file, if any. | |||
| */ | |||
| private static String remoteDir(String remoteFile) { | |||
| int index = remoteFile.lastIndexOf("/"); | |||
| if (index < 0) { | |||
| index = remoteFile.lastIndexOf("\\"); | |||
| } | |||
| return index > -1 ? remoteFile.substring(0, index + 1) : ""; | |||
| } | |||
| } | |||
| @@ -28,6 +28,8 @@ import com.jcraft.jsch.SftpException; | |||
| import com.jcraft.jsch.SftpATTRS; | |||
| import com.jcraft.jsch.SftpProgressMonitor; | |||
| import org.apache.tools.ant.util.FileUtils; | |||
| /** | |||
| * A helper object representing an scp download. | |||
| */ | |||
| @@ -54,11 +56,7 @@ public class ScpFromMessageBySftp extends ScpFromMessage { | |||
| String aRemoteFile, | |||
| File aLocalFile, | |||
| boolean recursive) { | |||
| super(verbose, session); | |||
| this.verbose = verbose; | |||
| this.remoteFile = aRemoteFile; | |||
| this.localFile = aLocalFile; | |||
| this.isRecursive = recursive; | |||
| this(verbose, session, aRemoteFile, aLocalFile, recursive, false); | |||
| } | |||
| /** | |||
| @@ -75,6 +73,31 @@ public class ScpFromMessageBySftp extends ScpFromMessage { | |||
| this(false, session, aRemoteFile, aLocalFile, recursive); | |||
| } | |||
| /** | |||
| * 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 | |||
| * @param preservceLastModified whether to preserve file | |||
| * modification times | |||
| * @since Ant 1.8.0 | |||
| */ | |||
| public ScpFromMessageBySftp(boolean verbose, | |||
| Session session, | |||
| String aRemoteFile, | |||
| File aLocalFile, | |||
| boolean recursive, | |||
| boolean preserveLastModified) { | |||
| super(verbose, session, aRemoteFile, aLocalFile, recursive, | |||
| preserveLastModified); | |||
| this.verbose = verbose; | |||
| this.remoteFile = aRemoteFile; | |||
| this.localFile = aLocalFile; | |||
| this.isRecursive = recursive; | |||
| } | |||
| /** | |||
| * Carry out the transfer. | |||
| * @throws IOException on i/o errors | |||
| @@ -171,5 +194,11 @@ public class ScpFromMessageBySftp extends ScpFromMessage { | |||
| long endTime = System.currentTimeMillis(); | |||
| logStats(startTime, endTime, (int) totalLength); | |||
| } | |||
| if (getPreserveLastModified()) { | |||
| FileUtils.getFileUtils().setFileLastModified(localFile, | |||
| ((long) le.getAttrs() | |||
| .getMTime()) | |||
| * 1000); | |||
| } | |||
| } | |||
| } | |||