scohen 2005/05/29 17:40:21
Modified: src/testcases/org/apache/tools/ant/taskdefs/optional/net
FTPTest.java
src/main/org/apache/tools/ant/taskdefs/optional/net FTP.java
src/etc/testcases/taskdefs/optional/net ftp.xml
docs/manual/OptionalTasks ftp.html
Added: src/main/org/apache/tools/ant/util Retryable.java
RetryHandler.java
Log:
Based on a patch submitted by Neeme Praks, allow support for a retry count on
FTP transfers. Some
servers are unreliable for unknown - this allows for a retry count to be
specified to accomodate work on such
flaky servers.
Revision Changes Path
1.1 ant/src/main/org/apache/tools/ant/util/Retryable.java
Index: Retryable.java
===================================================================
/*
* Copyright 2005 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.util;
import java.io.IOException;
/**
* Simple interface for executing a piece of code. Used for writing anonymous
inner
* classes in FTP task for retry-on-IOException behaviour.
*
* @see RetryHandler
*/
public interface Retryable {
public static final int RETRY_FOREVER = -1;
void execute() throws IOException;
}
1.1 ant/src/main/org/apache/tools/ant/util/RetryHandler.java
Index: RetryHandler.java
===================================================================
/*
* Copyright 2005 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.util;
import java.io.IOException;
import org.apache.tools.ant.Project;
import org.apache.tools.ant.Task;
/**
* A simple utility class to take a piece of code (that implements
* <code>Retryable</code> interface) and executes that with possibility to
* retry the execution in case of IOException.
*/
public class RetryHandler {
private int retriesAllowed = 0;
private Task task;
/**
* Create a new RetryingHandler.
*
* @param retriesAllowed how many times to retry
* @param task the Ant task that is is executed from, used for logging
only
*/
public RetryHandler(int retriesAllowed, Task task) {
this.retriesAllowed = retriesAllowed;
this.task = task;
}
/**
* Execute the <code>Retryable</code> code with specified number of
retries.
*
* @param exe the code to execute
* @param desc some descriptive text for this piece of code, used for
logging
* @throws IOException if the number of retries has exceeded the allowed
limit
*/
public void execute(Retryable exe, String desc) throws IOException {
int retries = 0;
while (true) {
try {
exe.execute();
break;
} catch (IOException e) {
retries++;
if (retries > this.retriesAllowed && this.retriesAllowed >
-1) {
task.log("try #" + retries + ": IO error ("
+ desc + "), number of maximum retries reached ("
+ this.retriesAllowed + "), giving up",
Project.MSG_WARN);
throw e;
} else {
task.log("try #" + retries + ": IO error (" + desc + "),
retrying", Project.MSG_WARN);
}
}
}
}
}
1.19 +86 -0
ant/src/testcases/org/apache/tools/ant/taskdefs/optional/net/FTPTest.java
Index: FTPTest.java
===================================================================
RCS file:
/home/cvs/ant/src/testcases/org/apache/tools/ant/taskdefs/optional/net/FTPTest.java,v
retrieving revision 1.18
retrieving revision 1.19
diff -u -r1.18 -r1.19
--- FTPTest.java 29 May 2005 03:01:10 -0000 1.18
+++ FTPTest.java 30 May 2005 00:40:20 -0000 1.19
@@ -21,17 +21,21 @@
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
+import java.util.Random;
import java.util.Vector;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.tools.ant.BuildEvent;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.BuildFileTest;
+import org.apache.tools.ant.ComponentHelper;
import org.apache.tools.ant.DefaultLogger;
import org.apache.tools.ant.DirectoryScanner;
import org.apache.tools.ant.Project;
import org.apache.tools.ant.taskdefs.condition.Os;
import org.apache.tools.ant.types.FileSet;
+import org.apache.tools.ant.util.RetryHandler;
+import org.apache.tools.ant.util.Retryable;
import org.apache.tools.ant.util.regexp.RegexpMatcher;
import org.apache.tools.ant.util.regexp.RegexpMatcherFactory;
@@ -779,6 +783,88 @@
public String resolveFile(String file) {
return super.resolveFile(file);
}
+ }
+
+
+ public abstract static class myRetryableFTP extends FTP {
+ private final int numberOfFailuresToSimulate;
+ private int simulatedFailuresLeft;
+
+ protected myRetryableFTP(int numberOfFailuresToSimulate) {
+ this.numberOfFailuresToSimulate = numberOfFailuresToSimulate;
+ this.simulatedFailuresLeft = numberOfFailuresToSimulate;
+ }
+ protected void getFile(FTPClient ftp, String dir, String filename)
+ throws IOException, BuildException
+ {
+ if (this.simulatedFailuresLeft > 0) {
+ this.simulatedFailuresLeft--;
+ throw new IOException("Simulated failure for testing");
+ }
+ super.getFile(ftp, dir, filename);
+ }
+ protected void executeRetryable(RetryHandler h, Retryable r,
+ String filename) throws IOException
+ {
+ this.simulatedFailuresLeft = this.numberOfFailuresToSimulate;
+ super.executeRetryable(h, r, filename);
+ }
}
+ public static class oneFailureFTP extends myRetryableFTP {
+ public oneFailureFTP() {
+ super(1);
+ }
+ }
+ public static class twoFailureFTP extends myRetryableFTP {
+ public twoFailureFTP() {
+ super(2);
+ }
+ }
+ public static class threeFailureFTP extends myRetryableFTP {
+ public threeFailureFTP() {
+ super(3);
+ }
+ }
+
+ public static class randomFailureFTP extends myRetryableFTP {
+ public randomFailureFTP() {
+ super(new Random(30000).nextInt());
+ }
+ }
+ public void testGetWithSelectorRetryable1() {
+ getProject().addTaskDefinition("ftp", oneFailureFTP.class);
+ try {
+ getProject().executeTarget("ftp-get-with-selector-retryable");
+ } catch (BuildException bx) {
+ fail("Two retries expected, failed after one.");
+ }
+ }
+ public void testGetWithSelectorRetryable2() {
+ getProject().addTaskDefinition("ftp", twoFailureFTP.class);
+ try {
+ getProject().executeTarget("ftp-get-with-selector-retryable");
+ } catch (BuildException bx) {
+ fail("Two retries expected, failed after two.");
+ }
+ }
+
+ public void testGetWithSelectorRetryable3() {
+ getProject().addTaskDefinition("ftp", threeFailureFTP.class);
+ try {
+ getProject().executeTarget("ftp-get-with-selector-retryable");
+ fail("Two retries expected, continued after two.");
+ } catch (BuildException bx) {
+ }
+ }
+ public void testGetWithSelectorRetryableRandom() {
+ getProject().addTaskDefinition("ftp", threeFailureFTP.class);
+ try {
+ getProject().setProperty("ftp.retries", "forever");
+ getProject().executeTarget("ftp-get-with-selector-retryable");
+ } catch (BuildException bx) {
+ fail("Retry forever specified, but failed.");
+ }
+ }
+
}
1.77 +82 -23
ant/src/main/org/apache/tools/ant/taskdefs/optional/net/FTP.java
Index: FTP.java
===================================================================
RCS file:
/home/cvs/ant/src/main/org/apache/tools/ant/taskdefs/optional/net/FTP.java,v
retrieving revision 1.76
retrieving revision 1.77
diff -u -r1.76 -r1.77
--- FTP.java 29 May 2005 03:01:10 -0000 1.76
+++ FTP.java 30 May 2005 00:40:20 -0000 1.77
@@ -51,6 +51,8 @@
import org.apache.tools.ant.types.FileSet;
import org.apache.tools.ant.types.selectors.SelectorUtils;
import org.apache.tools.ant.util.FileUtils;
+import org.apache.tools.ant.util.RetryHandler;
+import org.apache.tools.ant.util.Retryable;
/**
* Basic FTP client. Performs the following actions:
@@ -126,6 +128,7 @@
private String shortMonthNamesConfig = null;
private Granularity timestampGranularity = Granularity.getDefault();
private boolean isConfigurationSet = false;
+ private int retriesAllowed = 0;
protected static final String[] ACTION_STRS = {
"sending",
@@ -1360,6 +1363,37 @@
}
+
+ /**
+ * How many times to retry executing FTP command before giving up?
+ * Default is 0 - try once and if failure then give up.
+ *
+ * @param retriesAllowed number of retries to allow. -1 means
+ * keep trying forever. "forever" may also be specified as a
+ * synonym for -1.
+ */
+ public void setRetriesAllowed(String retriesAllowed) {
+ if ("FOREVER".equalsIgnoreCase(retriesAllowed)) {
+ this.retriesAllowed = Retryable.RETRY_FOREVER;
+ } else {
+ try {
+ int retries = Integer.parseInt(retriesAllowed);
+ if (retries < Retryable.RETRY_FOREVER) {
+ throw new BuildException(
+ "Invalid value for retriesAllowed attribute: "
+ + retriesAllowed);
+
+ }
+ this.retriesAllowed = retries;
+ } catch (NumberFormatException px) {
+ throw new BuildException(
+ "Invalid value for retriesAllowed attribute: "
+ + retriesAllowed);
+
+ }
+
+ }
+ }
/**
* @return Returns the systemTypeKey.
*/
@@ -1451,6 +1485,12 @@
}
}
}
+
+ protected void executeRetryable(RetryHandler h, Retryable r, String
filename)
+ throws IOException
+ {
+ h.execute(r, filename);
+ }
/**
@@ -1465,7 +1505,7 @@
* @throws IOException if there is a problem reading a file
* @throws BuildException if there is a problem in the configuration.
*/
- protected int transferFiles(FTPClient ftp, FileSet fs)
+ protected int transferFiles(final FTPClient ftp, FileSet fs)
throws IOException, BuildException {
DirectoryScanner ds;
if (action == SEND_FILES) {
@@ -1512,38 +1552,51 @@
}
bw = new BufferedWriter(new FileWriter(listing));
}
+ RetryHandler h = new RetryHandler(this.retriesAllowed, this);
if (action == RM_DIR) {
// to remove directories, start by the end of the list
// the trunk does not let itself be removed before the leaves
for (int i = dsfiles.length - 1; i >= 0; i--) {
- rmDir(ftp, dsfiles[i]);
+ final String dsfile = dsfiles[i];
+ executeRetryable(h, new Retryable() {
+ public void execute() throws IOException {
+ rmDir(ftp, dsfile);
+ }
+ }, dsfile);
}
} else {
+ final BufferedWriter fbw = bw;
+ final String fdir = dir;
if (this.newerOnly) {
this.granularityMillis =
this.timestampGranularity.getMilliseconds(action);
}
for (int i = 0; i < dsfiles.length; i++) {
- switch (action) {
- case SEND_FILES:
- sendFile(ftp, dir, dsfiles[i]);
- break;
- case GET_FILES:
- getFile(ftp, dir, dsfiles[i]);
- break;
- case DEL_FILES:
- delFile(ftp, dsfiles[i]);
- break;
- case LIST_FILES:
- listFile(ftp, bw, dsfiles[i]);
- break;
- case CHMOD:
- doSiteCommand(ftp, "chmod " + chmod + " " +
resolveFile(dsfiles[i]));
- transferred++;
- break;
- default:
- throw new BuildException("unknown ftp action " +
action);
- }
+ final String dsfile = dsfiles[i];
+ executeRetryable(h, new Retryable() {
+ public void execute() throws IOException {
+ switch (action) {
+ case SEND_FILES:
+ sendFile(ftp, fdir, dsfile);
+ break;
+ case GET_FILES:
+ getFile(ftp, fdir, dsfile);
+ break;
+ case DEL_FILES:
+ delFile(ftp, dsfile);
+ break;
+ case LIST_FILES:
+ listFile(ftp, fbw, dsfile);
+ break;
+ case CHMOD:
+ doSiteCommand(ftp, "chmod " + chmod + "
" + resolveFile(dsfile));
+ transferred++;
+ break;
+ default:
+ throw new BuildException("unknown ftp
action " + action);
+ }
+ }
+ }, dsfile);
}
}
} finally {
@@ -2198,7 +2251,13 @@
// directory is the directory to create.
if (action == MK_DIR) {
- makeRemoteDir(ftp, remotedir);
+ RetryHandler h = new RetryHandler(this.retriesAllowed, this);
+ final FTPClient lftp = ftp;
+ executeRetryable(h, new Retryable() {
+ public void execute() throws IOException {
+ makeRemoteDir(lftp, remotedir);
+ }
+ }, remotedir);
} else {
if (remotedir != null) {
log("changing the remote directory",
Project.MSG_VERBOSE);
1.13 +13 -0 ant/src/etc/testcases/taskdefs/optional/net/ftp.xml
Index: ftp.xml
===================================================================
RCS file: /home/cvs/ant/src/etc/testcases/taskdefs/optional/net/ftp.xml,v
retrieving revision 1.12
retrieving revision 1.13
diff -u -r1.12 -r1.13
--- ftp.xml 29 May 2005 03:01:10 -0000 1.12
+++ ftp.xml 30 May 2005 00:40:20 -0000 1.13
@@ -15,6 +15,7 @@
<property name="server.timestamp.granularity.millis" value="60000"/>
<property name="ftp.server.timezone" value="GMT"/>
<property name="ftp.listing.file" value="/dev/null"/>
+ <property name="ftp.retries" value="2"/>
<fileset dir="${tmp.get.dir}" id="fileset-destination-with-selector">
<include name="alpha/**"/>
@@ -272,5 +273,17 @@
<fileset dir="${tmp.local}"/>
</ftp>
</target>
+ <target name="ftp-get-with-selector-retryable">
+ <ftp action="get"
+ server="${ftp.host}"
+ userid="${ftp.user}"
+ password="${ftp.password}"
+ separator="${ftp.filesep}"
+ remotedir="${tmp.dir}"
+ retriesAllowed="${ftp.retries}"
+ >
+ <fileset refid="fileset-destination-with-selector"/>
+ </ftp>
+ </target>
</project>
\ No newline at end of file
1.37 +8 -0 ant/docs/manual/OptionalTasks/ftp.html
Index: ftp.html
===================================================================
RCS file: /home/cvs/ant/docs/manual/OptionalTasks/ftp.html,v
retrieving revision 1.36
retrieving revision 1.37
diff -u -r1.36 -r1.37
--- ftp.html 22 May 2005 18:49:46 -0000 1.36
+++ ftp.html 30 May 2005 00:40:21 -0000 1.37
@@ -192,6 +192,14 @@
(<em>Note</em>: Ignored on Java 1.1)</td>
<td valign="top" align="center">No; defaults to false.</td>
</tr>
+ <tr>
+ <td valign="top">retriesAllowed</td>
+ <td valign="top">Set the number of retries allowed on an file-transfer
operation.
+ If a number > 0 specified, each file transfer can fail up to that
+ many times before the operation is failed. If -1 or "forever"
specified, the
+ operation will keep trying until it succeeds.</td>
+ <td valign="top" align="center">No; defaults to 0</td>
+ </tr>
<tr>
<td colspan="3">
---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]