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]

Reply via email to