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); | |||
} | |||
} | |||
} |