This patch implements multiple delayTimes in RemoteDelivery, for that to work 
SpoolRepository, and implementing classes has also been modified, as have the 
sqlResources.xml.

The format for each delayTime entry is <attempts*delaytime unit> attempts and 
unit are optional. Attempt is the number of times the delay is used. Unit is 
one of msec(s),sec(s),minute(s),hour(s),day(s).

maxRetries are made consistent with the total number of delaytime attempts by:
   Increasing the attempts of the last delay if maxRetries are larger.
   Increasing maxRetries if the totalnumber of attempts are larger
If either of these is done a log of the action are made.

It uses a new method in SpoolRepository: accept (AcceptFilter) to select the 
mail that should be processed next.

When/if this gets accepted I will of course produce a similar patch for the 
2.1fcs branch.

--S�ren

-- 
S�ren Hilmer, M.Sc.
R&D manager             Phone:  +45 70 27 64 00
TietoEnator IT+         Fax:    +45 70 27 64 40
Ved Lunden 12           Direct: +45 87 46 64 57
DK-8230 �byh�j          Email:  [EMAIL PROTECTED]

? patch
Index: src/conf/sqlResources.xml
===================================================================
RCS file: /home/cvspublic/james-server/src/conf/sqlResources.xml,v
retrieving revision 1.22
diff -u -w -b -r1.22 sqlResources.xml
--- src/conf/sqlResources.xml	18 Aug 2003 15:43:54 -0000	1.22
+++ src/conf/sqlResources.xml	23 Oct 2003 20:57:30 -0000
@@ -346,7 +346,8 @@
     <sql name="removeMessageSQL">DELETE FROM ${table} WHERE message_name = ? AND repository_name = ?</sql>
 
     <!-- Statements used to list all messages stored in this repository. -->
-    <sql name="listMessagesSQL">SELECT message_name, message_state, last_updated FROM ${table} WHERE repository_name = ? ORDER BY last_updated ASC</sql>
+    <sql name="listMessagesSQL">SELECT message_name, message_state,
+    last_updated, error_message FROM ${table} WHERE repository_name = ? ORDER BY last_updated ASC</sql>
 
     <!-- Statements used to create the table associated with this class. -->
     <sql name="createTable" db="hypersonic">
Index: src/java/org/apache/james/mailrepository/AvalonSpoolRepository.java
===================================================================
RCS file: /home/cvspublic/james-server/src/java/org/apache/james/mailrepository/AvalonSpoolRepository.java,v
retrieving revision 1.17
diff -u -w -b -r1.17 AvalonSpoolRepository.java
--- src/java/org/apache/james/mailrepository/AvalonSpoolRepository.java	24 Jun 2003 21:09:56 -0000	1.17
+++ src/java/org/apache/james/mailrepository/AvalonSpoolRepository.java	23 Oct 2003 20:57:31 -0000
@@ -135,12 +135,65 @@
      *
      * @return the key for the mail
      */
-    public synchronized String accept(long delay) throws InterruptedException {
+    public synchronized String accept(final long delay) throws InterruptedException {
         if ((DEEP_DEBUG) && (getLogger().isDebugEnabled())) {
             getLogger().debug("Method accept(delay) called");
         }
-        while (!Thread.currentThread().isInterrupted()) {
+        return accept(new SpoolRepository.AcceptFilter () {
             long youngest = 0;
+                
+                public boolean accept (String key, String state, long lastUpdated, String errorMessage) {
+                    if (state.equals(Mail.ERROR)) {
+                        //Test the time...
+                        long timeToProcess = delay + lastUpdated;
+                
+                        if (System.currentTimeMillis() > timeToProcess) {
+                            //We're ready to process this again
+                            return true;
+                        } else {
+                            //We're not ready to process this.
+                            if (youngest == 0 || youngest > timeToProcess) {
+                                //Mark this as the next most likely possible mail to process
+                                youngest = timeToProcess;
+                            }
+                            return false;
+                        }
+                    } else {
+                        //This mail is good to go... return the key
+                        return true;
+                    }
+                }
+        
+                public long getWaitTime () {
+                    if (youngest == 0) {
+                        return 0;
+                    } else {
+                        long duration = youngest - System.currentTimeMillis();
+                        youngest = 0; //get ready for next round
+                        return duration <= 0 ? 1 : duration;
+                    }
+                }
+            });
+    }
+
+
+    /**
+     * Returns the key for an arbitrarily select mail deposited in this Repository for
+     * which the supplied filter's accept method returns true.
+     * Usage: RemoteDeliverySpool calls accept(filter) with some a filter which determines
+     * based on number of retries if the mail is ready for processing.
+     * If no message is ready the method will block until one is, the amount of time to block is
+     * determined by calling the filters getWaitTime method.
+     *
+     * <p>Synchronized to ensure thread safe access to the underlying spool.</p>
+     *
+     * @return the key for the mail
+     */
+    public synchronized String accept(SpoolRepository.AcceptFilter filter) throws InterruptedException {
+        if ((DEEP_DEBUG) && (getLogger().isDebugEnabled())) {
+            getLogger().debug("Method accept(Filter) called");
+        }
+        while (!Thread.currentThread().isInterrupted()) {
             for (Iterator it = list(); it.hasNext(); ) {
                 String s = it.next().toString();
                 if ((DEEP_DEBUG) && (getLogger().isDebugEnabled())) {
@@ -155,42 +208,22 @@
                     if ((DEEP_DEBUG) && (getLogger().isDebugEnabled())) {
                         getLogger().debug("accept(delay) has locked: " + s);
                     }
-                    //We have a lock on this object... let's grab the message
-                    //  and see if it's a valid time.
-
-                    // Retrieve can return null if the mail is no longer in the store.
-                    // In this case we simply continue to the next key
                     Mail mail = retrieve(s);
                     if (mail == null) {
                         continue;
                     }
-                    if (mail.getState().equals(Mail.ERROR)) {
-                        //Test the time...
-                        long timeToProcess = delay + mail.getLastUpdated().getTime();
-                        if (System.currentTimeMillis() > timeToProcess) {
-                            //We're ready to process this again
-                            return s;
-                        } else {
-                            //We're not ready to process this.
-                            if (youngest == 0 || youngest > timeToProcess) {
-                                //Mark this as the next most likely possible mail to process
-                                youngest = timeToProcess;
-                            }
-                        }
-                    } else {
-                        //This mail is good to go... return the key
+                    if (filter.accept (mail.getName(),
+                                       mail.getState(),
+                                       mail.getLastUpdated().getTime(),
+                                       mail.getErrorMessage())) {
                         return s;
                     }
+                    
                 }
             }
             //We did not find any... let's wait for a certain amount of time
             try {
-                if (youngest == 0) {
-                    wait();
-                } else {
-                    long duration = youngest - System.currentTimeMillis();
-                    wait(duration <= 0 ? 1 : duration);
-                }
+                wait (filter.getWaitTime());
             } catch (InterruptedException ex) {
                 throw ex;
             } catch (ConcurrentModificationException cme) {
@@ -200,4 +233,5 @@
         }
         throw new InterruptedException();
     }
+    
 }
Index: src/java/org/apache/james/mailrepository/JDBCSpoolRepository.java
===================================================================
RCS file: /home/cvspublic/james-server/src/java/org/apache/james/mailrepository/JDBCSpoolRepository.java,v
retrieving revision 1.28
diff -u -w -b -r1.28 JDBCSpoolRepository.java
--- src/java/org/apache/james/mailrepository/JDBCSpoolRepository.java	14 Aug 2003 16:28:03 -0000	1.28
+++ src/java/org/apache/james/mailrepository/JDBCSpoolRepository.java	23 Oct 2003 20:57:32 -0000
@@ -194,50 +194,72 @@
      * then check the last updated time, and don't try it until the long 'delay' parameter
      * milliseconds has passed.
      */
-    public synchronized String accept(long delay) throws InterruptedException {
-        while (!Thread.currentThread().isInterrupted()) {
-            //Loop through until we are either out of pending messages or have a message
-            // that we can lock
-            PendingMessage next = null;
+    public synchronized String accept(final long delay) throws InterruptedException {
+        return accept (new SpoolRepository.AcceptFilter () {
             long sleepUntil = 0;
-            while ((next = getNextPendingMessage()) != null && !Thread.currentThread().isInterrupted()) {
-                //Check whether this is time to expire
-                boolean shouldProcess = false;
-                if (Mail.ERROR.equals(next.state)) {
+                
+                public boolean accept (String key, String state, long lastUpdated, String errorMessage) {
+                    if (Mail.ERROR.equals(state)) {
                     //if it's an error message, test the time
-                    long processingTime = delay + next.lastUpdated;
+                        long processingTime = delay + lastUpdated;
                     if (processingTime < System.currentTimeMillis()) {
                         //It's time to process
-                        shouldProcess = true;
+                            return true;
                     } else {
                         //We don't process this, but we want to possibly reduce the amount of time
                         //  we sleep so we wake when this message is ready.
                         if (sleepUntil == 0 || processingTime < sleepUntil) {
                             sleepUntil = processingTime;
                         }
+                            return false;
                     }
                 } else {
-                    shouldProcess = true;
+                        return true;
+                    }
+                }
+                
+
+                public long getWaitTime () {
+                    if (sleepUntil == 0) {
+                        sleepUntil = System.currentTimeMillis() + WAIT_LIMIT;
+                    }
+                    long waitTime = sleepUntil - System.currentTimeMillis();
+                    sleepUntil = 0;
+                    return waitTime <= 0 ? 1 : waitTime;
+                }
+                
+            });
                 }
+
+    /**
+     * Returns the key for an arbitrarily select mail deposited in this Repository for
+     * which the supplied filter's accept method returns true.
+     * Usage: RemoteDeliverySpool calls accept(filter) with some a filter which determines
+     * based on number of retries if the mail is ready for processing.
+     * If no message is ready the method will block until one is, the amount of time to block is
+     * determined by calling the filters getWaitTime method.
+     *
+     * @return the key for the mail
+     */
+    public synchronized String accept(SpoolRepository.AcceptFilter filter) throws InterruptedException {
+        while (!Thread.currentThread().isInterrupted()) {
+            //Loop through until we are either out of pending messages or have a message
+            // that we can lock
+            PendingMessage next = null;
+            while ((next = getNextPendingMessage()) != null && !Thread.currentThread().isInterrupted()) {
+                //Check whether this is time to expire
+                
+                boolean shouldProcess = filter.accept (next.key, next.state, next.lastUpdated, next.errorMessage);
+                
                 if (shouldProcess && lock(next.key)) {
                     return next.key;
                 }
             }
             //Nothing to do... sleep!
-            if (sleepUntil == 0) {
-                sleepUntil = System.currentTimeMillis() + WAIT_LIMIT;
-            }
+            long wait_time = filter.getWaitTime();
             try {
                 synchronized (this) {
-                    long waitTime = sleepUntil - System.currentTimeMillis();
-                    //StringBuffer errorBuffer =
-                    //    new StringBuffer(128)
-                    //            .append("waiting ")
-                    //            .append((waitTime) / 1000L)
-                    //            .append(" in ")
-                    //            .append(repositoryName);
-                    //System.err.println(errorBuffer.toString());
-                    wait(waitTime <= 0 ? 1 : waitTime);
+                    wait (wait_time);
                 }
             } catch (InterruptedException ex) {
                 throw ex;
@@ -306,7 +328,8 @@
                     String key = rsListMessages.getString(1);
                     String state = rsListMessages.getString(2);
                     long lastUpdated = rsListMessages.getTimestamp(3).getTime();
-                    pendingMessages.add(new PendingMessage(key, state, lastUpdated));
+                    String errorMessage = rsListMessages.getString(4);
+                    pendingMessages.add(new PendingMessage(key, state, lastUpdated, errorMessage));
                 }
             } catch (SQLException sqle) {
                 //Log it and avoid reloading for a bit
@@ -327,11 +350,13 @@
         protected String key;
         protected String state;
         protected long lastUpdated;
+        protected String errorMessage;
 
-        public PendingMessage(String key, String state, long lastUpdated) {
+        public PendingMessage(String key, String state, long lastUpdated, String errorMessage) {
             this.key = key;
             this.state = state;
             this.lastUpdated = lastUpdated;
+            this.errorMessage = errorMessage;
         }
     }
 }
Index: src/java/org/apache/james/transport/mailets/RemoteDelivery.java
===================================================================
RCS file: /home/cvspublic/james-server/src/java/org/apache/james/transport/mailets/RemoteDelivery.java,v
retrieving revision 1.51
diff -u -w -b -r1.51 RemoteDelivery.java
--- src/java/org/apache/james/transport/mailets/RemoteDelivery.java	18 Jun 2003 15:59:44 -0000	1.51
+++ src/java/org/apache/james/transport/mailets/RemoteDelivery.java	23 Oct 2003 20:57:35 -0000
@@ -68,6 +68,7 @@
 import java.util.Collection;
 import java.util.Date;
 import java.util.Hashtable;
+import java.util.HashMap;
 import java.util.Iterator;
 import java.util.Locale;
 import java.util.Properties;
@@ -92,12 +93,19 @@
 import org.apache.mailet.MailetContextConstants;
 import org.apache.mailet.SpoolRepository;
 
+import org.apache.oro.text.regex.MalformedPatternException;
+import org.apache.oro.text.regex.Pattern;
+import org.apache.oro.text.regex.Perl5Compiler;
+import org.apache.oro.text.regex.Perl5Matcher;
+import org.apache.oro.text.regex.MatchResult;
+
+
 /**
  * Receives a MessageContainer from JamesSpoolManager and takes care of delivery
  * the message to remote hosts. If for some reason mail can't be delivered
- * store it in the "outgoing" Repository and set an Alarm. After "delayTime" the
+ * store it in the "outgoing" Repository and set an Alarm. After the next "delayTime" the
  * Alarm will wake the servlet that will try to send it again. After "maxRetries"
- * the mail will be considered underiverable and will be returned to sender.
+ * the mail will be considered undeliverable and will be returned to sender.
  *
  * TO DO (in priority):
  * 1. Support a gateway (a single server where all mail will be delivered) (DONE)
@@ -117,13 +125,110 @@
  */
 public class RemoteDelivery extends GenericMailet implements Runnable {
 
+    private static final long DEFAULT_DELAY_TIME = 21600000; // default is 6*60*60*1000 millis (6 hours)
+    private static final String PATTERN_STRING =
+        "\\s*([0-9]*\\s*[\\*])?\\s*([0-9]+)\\s*([a-z,A-Z]*)\\s*";//pattern to match
+                                                                 //attempts*delay[units]
+                                            
+    private static Pattern PATTERN = null; //the compiled pattern of the above String
+    private static final Perl5Matcher MATCHER  = new Perl5Matcher(); //matcher use at init time to parse delaytime parameters
+    private static final HashMap MULTIPLIERS = new HashMap (10); //holds allowed units for delaytime together with
+                                                                //the factor to turn it into the equivalent time in msec
+
+    /*
+     * Static initializer.<p>
+     * Compiles pattern for processing delaytime entries.<p>
+     * Initializes MULTIPLIERS with the supported unit quantifiers
+     */
+    static {
+        try {
+            Perl5Compiler compiler = new Perl5Compiler(); 
+            PATTERN = compiler.compile(PATTERN_STRING);
+        } catch(MalformedPatternException mpe) {
+            //this should not happen as the pattern string is hardcoded.
+            System.err.println ("Malformed pattern: " + PATTERN_STRING);
+            mpe.printStackTrace (System.err);
+        }
+        //add allowed units and their respective multiplier
+        MULTIPLIERS.put ("msec", new Integer (1));
+        MULTIPLIERS.put ("msecs", new Integer (1));
+        MULTIPLIERS.put ("sec",  new Integer (1000));
+        MULTIPLIERS.put ("secs",  new Integer (1000));
+        MULTIPLIERS.put ("minute", new Integer (1000*60));
+        MULTIPLIERS.put ("minutes", new Integer (1000*60));
+        MULTIPLIERS.put ("hour", new Integer (1000*60*60));
+        MULTIPLIERS.put ("hours", new Integer (1000*60*60));
+        MULTIPLIERS.put ("day", new Integer (1000*60*60*24));
+        MULTIPLIERS.put ("days", new Integer (1000*60*60*24));
+    }
+    
+    /**
+     * This filter is used in the accept call to the spool.
+     * It will select the next mail ready for processing according to the mails
+     * retrycount and lastUpdated time
+     **/
+    private class MultipleDelayFilter implements SpoolRepository.AcceptFilter
+    {
+        /**
+         * holds the time to wait for the youngest mail to get ready for processing
+         **/
+        long youngest = 0;
+
+        /**
+         * Uses the getNextDelay to determine if a mail is ready for processing based on the delivered parameters
+         * errorMessage (which holds the retrycount), lastUpdated and state
+         * @param key the name/key of the message
+         * @param state the mails state
+         * @param lastUpdated the mail was last written to the spool at this time.
+         * @param errorMessage actually holds the retrycount as a string (see failMessage below)
+         **/
+        public boolean accept (String key, String state, long lastUpdated, String errorMessage) {
+            if (state.equals(Mail.ERROR)) {
+                //Test the time...
+                int retries = Integer.parseInt(errorMessage);
+                long delay = getNextDelay (retries);
+                long timeToProcess = delay + lastUpdated;
+
+                
+                if (System.currentTimeMillis() > timeToProcess) {
+                    //We're ready to process this again
+                    return true;
+                } else {
+                    //We're not ready to process this.
+                    if (youngest == 0 || youngest > timeToProcess) {
+                        //Mark this as the next most likely possible mail to process
+                        youngest = timeToProcess;
+                    }
+                    return false;
+                }
+            } else {
+                //This mail is good to go... return the key
+                return true;
+            }
+        }
+
+        /**
+         * @return the optimal time the SpoolRepository.accept(AcceptFilter) method should wait before
+         * trying to find a mail ready for processing again.
+         **/
+        public long getWaitTime () {
+            if (youngest == 0) {
+                return 0;
+            } else {
+                long duration = youngest - System.currentTimeMillis();
+                youngest = 0; //get ready for next run
+                return duration <= 0 ? 1 : duration;
+            }
+        }
+    }
+    
     /**
      * Controls certain log messages
      */
     private boolean isDebug = false;
 
     private SpoolRepository outgoing; // The spool of outgoing mail
-    private long delayTime = 21600000; // default is 6*60*60*1000 millis (6 hours)
+    private ArrayList delayTimes = new ArrayList();
     private int maxRetries = 5; // default number of retries
     private long smtpTimeout = 600000;  //default number of ms to timeout on smtp delivery
     private boolean sendPartial = false; // If false then ANY address errors will cause the transmission to fail
@@ -139,6 +244,8 @@
 
     private volatile boolean destroyed = false; //Flag that the run method will check and end itself if set to true
 
+    private MultipleDelayFilter delayFilter = new MultipleDelayFilter ();
+
     /**
      * Initialize the mailet
      */
@@ -146,7 +253,16 @@
         isDebug = (getInitParameter("debug") == null) ? false : new Boolean(getInitParameter("debug")).booleanValue();
         try {
             if (getInitParameter("delayTime") != null) {
-                delayTime = Long.parseLong(getInitParameter("delayTime"));
+                String delay_times = getInitParameter("delayTime");
+                //split on comma's
+                StringTokenizer st = new StringTokenizer (delay_times,",");
+                while (st.hasMoreTokens()) {
+                    String delay_time = st.nextToken();
+                    delayTimes.add (new Delay(delay_time));
+                }
+            } else {
+                //use default delayTime.
+                delayTimes.add (new Delay());
             }
         } catch (Exception e) {
             log("Invalid delayTime setting: " + getInitParameter("delayTime"));
@@ -155,6 +271,25 @@
             if (getInitParameter("maxRetries") != null) {
                 maxRetries = Integer.parseInt(getInitParameter("maxRetries"));
             }
+            //check consistency with delayTimes attempts
+            int total_attempts = calcTotalAttempts ();
+            if (total_attempts > maxRetries) {
+                log("Total number of delayTime attempts exceeds maxRetries specified. Increasing maxRetries from "+maxRetries+" to "+total_attempts);
+                maxRetries = total_attempts;
+            } else {
+                int extra = maxRetries - total_attempts;
+                if (extra != 0) {
+                    log("maxRetries is larger than total number of attempts specified. Increasing last delayTime with "+extra+" attempts ");
+
+                    if (delayTimes.size() != 0) { 
+                        Delay delay = (Delay)delayTimes.get (delayTimes.size()-1); //last Delay
+                        delay.setAttempts (delay.getAttempts()+extra);
+                        log("Delay of "+delay.getDelayTime()+" msecs is now attempted: "+delay.getAttempts()+" times");
+                    } else {
+                        log ("NO, delaytimes cannot continue");
+                    }
+                }
+            }
         } catch (Exception e) {
             log("Invalid maxRetries setting: " + getInitParameter("maxRetries"));
         }
@@ -752,7 +887,8 @@
         try {
             while (!Thread.currentThread().interrupted() && !destroyed) {
                 try {
-                    String key = outgoing.accept(delayTime);
+                    //String key = outgoing.accept(delayTime);
+                    String key = outgoing.accept(delayFilter); 
                     try {
                         if (isDebug) {
                             StringBuffer logMessageBuffer =
@@ -790,6 +926,133 @@
         } finally {
             // Restore the thread state to non-interrupted.
             Thread.currentThread().interrupted();
+        }
+    }
+
+    /**
+     * @return the total attempts for all delays
+     **/
+    private int calcTotalAttempts () {
+        int sum = 0;
+        Iterator i = delayTimes.iterator();
+        while (i.hasNext()) {
+            Delay delay = (Delay)i.next();
+            sum += delay.getAttempts();
+        }
+        return sum;
+    }
+    
+    /**
+     * This method returns, given a retry-count, the next delay time to use.
+     * @param retry_count the current retry_count.
+     * @return the next delay time to use, given the retry count, 0 if retries are exhausted
+     **/
+    private long getNextDelay (int retry_count) {
+        int sum = 0;
+        Iterator i = delayTimes.iterator();
+        while (i.hasNext()) {
+            Delay delay = (Delay)i.next();
+            sum += delay.getAttempts();
+            if (retry_count <= sum) {
+                return delay.getDelayTime();
+            }
+        }
+        //as this method is called consistently with maxRetries we should never end up here.
+        return 0;
+    }
+
+
+    /**
+     * This class is used to hold a delay time and its corresponding number
+     * of retries.
+     **/
+    private class Delay {
+        private int attempts = 1;
+        private long delayTime = DEFAULT_DELAY_TIME;
+        
+            
+        /**
+         * This constructor expects Strings of the form "[attempt\*]delaytime[unit]". <p>
+         * The optional attempt is the number of tries this delay should be used (default = 1)
+         * The unit if present must be one of (msec,sec,minute,hour,day) (default = msec)
+         * The constructor multiplies the delaytime by the relevant multiplier for the unit,
+         * so the delayTime instance variable is always in msec.
+         * @param init_string the string to initialize this Delay object from
+         **/
+        public Delay (String init_string) throws MessagingException
+        {
+            String unit = "msec"; //default unit
+            synchronized (MATCHER) {
+                if (MATCHER.matches (init_string, PATTERN)) {
+                    MatchResult res = MATCHER.getMatch ();
+                    //the capturing groups will now hold
+                    //at 1:  attempts * (if present)
+                    //at 2:  delaytime
+                    //at 3:  unit (if present)
+                    
+                    if (res.group(1) != null && !res.group(1).equals ("")) {
+                        //we have an attempt *
+                        String attempt_match = res.group(1);
+                        //strip the * and whitespace
+                        attempt_match = attempt_match.substring (0,attempt_match.length()-1).trim();
+                        attempts = Integer.parseInt (attempt_match);
+                    }
+                    
+                    delayTime = Long.parseLong (res.group(2));
+                    
+                    if (!res.group(3).equals ("")) {
+                        //we have a unit
+                        unit = res.group(3).toLowerCase();
+                    }
+                } else {
+                    throw new MessagingException(init_string+" does not match "+PATTERN_STRING);
+                }
+            }
+            if (MULTIPLIERS.get (unit)!=null) {
+                int multiplier = ((Integer)MULTIPLIERS.get (unit)).intValue();
+                delayTime *= multiplier;
+            } else {
+                throw new MessagingException("Unknown unit: "+unit);
+            }
+        }
+
+        /**
+         * This constructor makes a default Delay object, ie. attempts=1 and delayTime=DEFAULT_DELAY_TIME
+         **/
+        public Delay () {
+        }
+
+        /**
+         * @return the delayTime for this Delay
+         **/
+        public long getDelayTime () {
+            return delayTime;
+        }
+
+        /**
+         * @return the number attempts this Delay should be used.
+         **/
+        public int getAttempts () {
+            return attempts;
+        }
+        
+        /**
+         * Set the number attempts this Delay should be used.
+         **/
+        public void setAttempts (int value) {
+            attempts = value;
+        }
+        
+        /**
+         * Pretty prints this Delay 
+         **/
+        public String toString () {
+            StringBuffer buf = new StringBuffer(15);
+            buf.append (getAttempts ());
+            buf.append ('*');
+            buf.append (getDelayTime());
+            buf.append ("msec");
+            return buf.toString();
         }
     }
 }
Index: src/java/org/apache/mailet/SpoolRepository.java
===================================================================
RCS file: /home/cvspublic/james-server/src/java/org/apache/mailet/SpoolRepository.java,v
retrieving revision 1.6
diff -u -w -b -r1.6 SpoolRepository.java
--- src/java/org/apache/mailet/SpoolRepository.java	8 Mar 2003 21:14:14 -0000	1.6
+++ src/java/org/apache/mailet/SpoolRepository.java	23 Oct 2003 20:57:36 -0000
@@ -69,6 +69,34 @@
     extends MailRepository {
 
     /**
+     * Implementations of AcceptFilter can be used to select which mails a SpoolRepository
+     * implementation returns from its accept (AcceptFilter) method
+     **/
+    public static interface AcceptFilter
+    {
+        /**
+         * This method is called by accept(Filter) to determine if the message is
+         * ready for delivery.
+         *
+         * @param key message key
+         * @param state the state of the message
+         * @param lastUpdated the last time the message was written to the spool
+         * @param errorMessage the current errorMessage
+         * @return true if the message is ready for delivery
+         **/
+        boolean accept (String key, String state, long lastUpdated, String errorMessage) ;
+
+
+        /**
+         * This method allows the filter to determine how long the thread should wait for a
+         * message to get ready for delivery, when currently there are none.
+         * @return the time to wait for a message to get ready for delivery
+         **/
+        long getWaitTime ();
+    }
+    
+    
+    /**
      * Define a STREAM repository. Streams are stored in the specified
      * destination.
      */
@@ -93,4 +121,16 @@
      * @return the key for the mail
      */
     String accept(long delay) throws InterruptedException;
+    
+    /**
+     * Returns the key for an arbitrarily select mail deposited in this Repository for
+     * which the supplied filter's accept method returns true.
+     * Usage: RemoteDeliverySpool calls accept(filter) with some a filter which determines
+     * based on number of retries if the mail is ready for processing.
+     * If no message is ready the method will block until one is, the amount of time to block is
+     * determined by calling the filters getWaitTime method.
+     *
+     * @return the key for the mail
+     */
+    String accept(AcceptFilter filter) throws InterruptedException;
 }
Index: src/xdocs/provided_mailets_2_1.xml
===================================================================
RCS file: /home/cvspublic/james-server/src/xdocs/provided_mailets_2_1.xml,v
retrieving revision 1.10
diff -u -w -b -r1.10 provided_mailets_2_1.xml
--- src/xdocs/provided_mailets_2_1.xml	29 May 2003 20:40:18 -0000	1.10
+++ src/xdocs/provided_mailets_2_1.xml	23 Oct 2003 20:57:37 -0000
@@ -162,10 +162,23 @@
 <ul>
 <li><strong>outgoing</strong> (required) - The URL for the repository that will hold messages being processed
 by the RemoteDelivery Mailet.</li>
-<li><strong>delayTime</strong> (optional) - a non-negative Long value that is the time in
-milliseconds between redelivery attempts for a particular mail.  Defaults to six hours.</li>
+<li><strong>delayTime</strong> (optional) - takes the form
+&lt;attempts*delay quantifier&gt; where attempts and quantifier are both
+optional an defaults to 1 and miliseconds respectively.
+The delay is a non-negative value that is the time between redelivery
+attempts for a particular mail. Defaults to six hours. Allowed
+quantifiers are msec(s),sec(s),minute(s),hour(s),day(s). The attempts
+is the number of times the specified delay is used. It is possible to
+have multiple entries of this parameter, and if so the the delayTimes
+are used in the order specified, this allows you to try a few times
+in rapid succession and then move on to longer delays</li>
 <li><strong>maxRetries</strong> (optional) - a non-negative Integer value that is number of times
-the Mailet will attempt to deliver a particular mail.  Defaults to five.</li>
+the Mailet will attempt to deliver a particular mail.  If inconsistent
+with number of attempts in <strong>delayTime</strong>
+specifications. <strong>maxRetries<strong> will be increased if it is
+less than the total number of attempts in <strong>delayTime</strong>,
+if it is more the last specified <strong>delayTime</strong> is used
+for the missing delays. Defaults to five. </li>
 <li><strong>timeout</strong> (optional) - The SMTP connection timeout for SMTP connections generated
 by this Mailet.  Defaults to 60 seconds.</li>
 <li><strong>deliveryThreads</strong> (optional) - The number of threads this Mailet will use to generate
---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]

Reply via email to