KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > james > transport > mailets > RemoteDelivery


1 /***********************************************************************
2  * Copyright (c) 2000-2004 The Apache Software Foundation. *
3  * All rights reserved. *
4  * ------------------------------------------------------------------- *
5  * Licensed under the Apache License, Version 2.0 (the "License"); you *
6  * may not use this file except in compliance with the License. You *
7  * may obtain a copy of the License at: *
8  * *
9  * http://www.apache.org/licenses/LICENSE-2.0 *
10  * *
11  * Unless required by applicable law or agreed to in writing, software *
12  * distributed under the License is distributed on an "AS IS" BASIS, *
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or *
14  * implied. See the License for the specific language governing *
15  * permissions and limitations under the License. *
16  ***********************************************************************/

17
18 package org.apache.james.transport.mailets;
19
20 import java.io.PrintWriter JavaDoc;
21 import java.io.StringWriter JavaDoc;
22 import java.net.ConnectException JavaDoc;
23 import java.net.InetAddress JavaDoc;
24 import java.net.SocketException JavaDoc;
25 import java.net.UnknownHostException JavaDoc;
26 import java.util.Arrays JavaDoc;
27 import java.util.Collection JavaDoc;
28 import java.util.Date JavaDoc;
29 import java.util.Hashtable JavaDoc;
30 import java.util.HashMap JavaDoc;
31 import java.util.Iterator JavaDoc;
32 import java.util.Locale JavaDoc;
33 import java.util.Properties JavaDoc;
34 import java.util.StringTokenizer JavaDoc;
35 import java.util.Vector JavaDoc;
36 import java.util.ArrayList JavaDoc;
37
38 import javax.mail.Address JavaDoc;
39 import javax.mail.MessagingException JavaDoc;
40 import javax.mail.SendFailedException JavaDoc;
41 import javax.mail.Session JavaDoc;
42 import javax.mail.Transport JavaDoc;
43 import javax.mail.URLName JavaDoc;
44 import javax.mail.internet.AddressException JavaDoc;
45 import javax.mail.internet.InternetAddress JavaDoc;
46 import javax.mail.internet.MimeMessage JavaDoc;
47 import javax.mail.internet.ParseException JavaDoc;
48
49 import org.apache.avalon.framework.component.ComponentException;
50 import org.apache.avalon.framework.component.ComponentManager;
51 import org.apache.avalon.framework.configuration.DefaultConfiguration;
52 import org.apache.james.Constants;
53 import org.apache.james.core.MailImpl;
54 import org.apache.james.services.MailServer;
55 import org.apache.james.services.MailStore;
56 import org.apache.james.services.SpoolRepository;
57 import org.apache.mailet.MailetContext;
58 import org.apache.mailet.GenericMailet;
59 import org.apache.mailet.HostAddress;
60 import org.apache.mailet.Mail;
61 import org.apache.mailet.MailAddress;
62
63 import org.apache.oro.text.regex.MalformedPatternException;
64 import org.apache.oro.text.regex.Pattern;
65 import org.apache.oro.text.regex.Perl5Compiler;
66 import org.apache.oro.text.regex.Perl5Matcher;
67 import org.apache.oro.text.regex.MatchResult;
68
69
70 /**
71  * Receives a MessageContainer from JamesSpoolManager and takes care of delivery
72  * the message to remote hosts. If for some reason mail can't be delivered
73  * store it in the "outgoing" Repository and set an Alarm. After the next "delayTime" the
74  * Alarm will wake the servlet that will try to send it again. After "maxRetries"
75  * the mail will be considered undeliverable and will be returned to sender.
76  *
77  * TO DO (in priority):
78  * 1. Support a gateway (a single server where all mail will be delivered) (DONE)
79  * 2. Provide better failure messages (DONE)
80  * 3. More efficiently handle numerous recipients
81  * 4. Migrate to use Phoenix for the delivery threads
82  *
83  * You really want to read the JavaMail documentation if you are
84  * working in here, and you will want to view the list of JavaMail
85  * attributes, which are documented here:
86  *
87  * http://java.sun.com/products/javamail/1.3/docs/javadocs/com/sun/mail/smtp/package-summary.html
88  *
89  * as well as other places.
90  *
91  * @version CVS $Revision: 1.33.4.21 $ $Date: 2004/05/02 06:08:37 $
92  */

93 public class RemoteDelivery extends GenericMailet implements Runnable JavaDoc {
94
95     private static final long DEFAULT_DELAY_TIME = 21600000; // default is 6*60*60*1000 millis (6 hours)
96
private static final String JavaDoc PATTERN_STRING =
97         "\\s*([0-9]*\\s*[\\*])?\\s*([0-9]+)\\s*([a-z,A-Z]*)\\s*";//pattern to match
98
//[attempts*]delay[units]
99

100     private static Pattern PATTERN = null; //the compiled pattern of the above String
101
private static final HashMap JavaDoc MULTIPLIERS = new HashMap JavaDoc (10); //holds allowed units for delaytime together with
102
//the factor to turn it into the equivalent time in msec
103

104     /*
105      * Static initializer.<p>
106      * Compiles pattern for processing delaytime entries.<p>
107      * Initializes MULTIPLIERS with the supported unit quantifiers
108      */

109     static {
110         try {
111             Perl5Compiler compiler = new Perl5Compiler();
112             PATTERN = compiler.compile(PATTERN_STRING, Perl5Compiler.READ_ONLY_MASK);
113         } catch(MalformedPatternException mpe) {
114             //this should not happen as the pattern string is hardcoded.
115
System.err.println ("Malformed pattern: " + PATTERN_STRING);
116             mpe.printStackTrace (System.err);
117         }
118         //add allowed units and their respective multiplier
119
MULTIPLIERS.put ("msec", new Integer JavaDoc (1));
120         MULTIPLIERS.put ("msecs", new Integer JavaDoc (1));
121         MULTIPLIERS.put ("sec", new Integer JavaDoc (1000));
122         MULTIPLIERS.put ("secs", new Integer JavaDoc (1000));
123         MULTIPLIERS.put ("minute", new Integer JavaDoc (1000*60));
124         MULTIPLIERS.put ("minutes", new Integer JavaDoc (1000*60));
125         MULTIPLIERS.put ("hour", new Integer JavaDoc (1000*60*60));
126         MULTIPLIERS.put ("hours", new Integer JavaDoc (1000*60*60));
127         MULTIPLIERS.put ("day", new Integer JavaDoc (1000*60*60*24));
128         MULTIPLIERS.put ("days", new Integer JavaDoc (1000*60*60*24));
129     }
130     
131     /**
132      * This filter is used in the accept call to the spool.
133      * It will select the next mail ready for processing according to the mails
134      * retrycount and lastUpdated time
135      **/

136     private class MultipleDelayFilter implements SpoolRepository.AcceptFilter
137     {
138         /**
139          * holds the time to wait for the youngest mail to get ready for processing
140          **/

141         long youngest = 0;
142
143         /**
144          * Uses the getNextDelay to determine if a mail is ready for processing based on the delivered parameters
145          * errorMessage (which holds the retrycount), lastUpdated and state
146          * @param key the name/key of the message
147          * @param state the mails state
148          * @param lastUpdated the mail was last written to the spool at this time.
149          * @param errorMessage actually holds the retrycount as a string (see failMessage below)
150          **/

151         public boolean accept (String JavaDoc key, String JavaDoc state, long lastUpdated, String JavaDoc errorMessage) {
152             if (state.equals(Mail.ERROR)) {
153                 //Test the time...
154
int retries = Integer.parseInt(errorMessage);
155                 long delay = getNextDelay (retries);
156                 long timeToProcess = delay + lastUpdated;
157
158                 
159                 if (System.currentTimeMillis() > timeToProcess) {
160                     //We're ready to process this again
161
return true;
162                 } else {
163                     //We're not ready to process this.
164
if (youngest == 0 || youngest > timeToProcess) {
165                         //Mark this as the next most likely possible mail to process
166
youngest = timeToProcess;
167                     }
168                     return false;
169                 }
170             } else {
171                 //This mail is good to go... return the key
172
return true;
173             }
174         }
175
176         /**
177          * @return the optimal time the SpoolRepository.accept(AcceptFilter) method should wait before
178          * trying to find a mail ready for processing again.
179          **/

180         public long getWaitTime () {
181             if (youngest == 0) {
182                 return 0;
183             } else {
184                 long duration = youngest - System.currentTimeMillis();
185                 youngest = 0; //get ready for next run
186
return duration <= 0 ? 1 : duration;
187             }
188         }
189     }
190
191     /**
192      * Controls certain log messages
193      */

194     private boolean isDebug = false;
195
196     private SpoolRepository outgoing; // The spool of outgoing mail
197
private long[] delayTimes; //holds expanded delayTimes
198
private int maxRetries = 5; // default number of retries
199
private long smtpTimeout = 600000; //default number of ms to timeout on smtp delivery
200
private boolean sendPartial = false; // If false then ANY address errors will cause the transmission to fail
201
private int connectionTimeout = 60000; // The amount of time JavaMail will wait before giving up on a socket connect()
202
private int deliveryThreadCount = 1; // default number of delivery threads
203
private Collection JavaDoc gatewayServer = null; // the server(s) to send all email to
204
private String JavaDoc bindAddress = null; // JavaMail delivery socket binds to this local address. If null the JavaMail default will be used.
205
private boolean isBindUsed = false; // true, if the bind configuration
206
// parameter is supplied, RemoteDeliverySocketFactory
207
// will be used in this case
208
private Collection JavaDoc deliveryThreads = new Vector JavaDoc();
209     private MailServer mailServer;
210     private volatile boolean destroyed = false; //Flag that the run method will check and end itself if set to true
211
private String JavaDoc bounceProcessor = null; // the processor for creating Bounces
212

213     private Perl5Matcher delayTimeMatcher; //matcher use at init time to parse delaytime parameters
214
private MultipleDelayFilter delayFilter = new MultipleDelayFilter ();//used by accept to selcet the next mail ready for processing
215

216     /**
217      * Initialize the mailet
218      */

219     public void init() throws MessagingException JavaDoc {
220         isDebug = (getInitParameter("debug") == null) ? false : new Boolean JavaDoc(getInitParameter("debug")).booleanValue();
221         ArrayList JavaDoc delay_times_list = new ArrayList JavaDoc();
222         try {
223             if (getInitParameter("delayTime") != null) {
224                 delayTimeMatcher = new Perl5Matcher();
225                 String JavaDoc delay_times = getInitParameter("delayTime");
226                 //split on comma's
227
StringTokenizer JavaDoc st = new StringTokenizer JavaDoc (delay_times,",");
228                 while (st.hasMoreTokens()) {
229                     String JavaDoc delay_time = st.nextToken();
230                     delay_times_list.add (new Delay(delay_time));
231                 }
232             } else {
233                 //use default delayTime.
234
delay_times_list.add (new Delay());
235             }
236         } catch (Exception JavaDoc e) {
237             log("Invalid delayTime setting: " + getInitParameter("delayTime"));
238         }
239         try {
240             if (getInitParameter("maxRetries") != null) {
241                 maxRetries = Integer.parseInt(getInitParameter("maxRetries"));
242             }
243             //check consistency with delay_times_list attempts
244
int total_attempts = calcTotalAttempts (delay_times_list);
245             if (total_attempts > maxRetries) {
246                 log("Total number of delayTime attempts exceeds maxRetries specified. Increasing maxRetries from "+maxRetries+" to "+total_attempts);
247                 maxRetries = total_attempts;
248             } else {
249                 int extra = maxRetries - total_attempts;
250                 if (extra != 0) {
251                     log("maxRetries is larger than total number of attempts specified. Increasing last delayTime with "+extra+" attempts ");
252
253                     if (delay_times_list.size() != 0) {
254                         Delay delay = (Delay)delay_times_list.get (delay_times_list.size()-1); //last Delay
255
delay.setAttempts (delay.getAttempts()+extra);
256                         log("Delay of "+delay.getDelayTime()+" msecs is now attempted: "+delay.getAttempts()+" times");
257                     } else {
258                         log ("NO, delaytimes cannot continue");
259                     }
260                 }
261             }
262             delayTimes = expandDelays (delay_times_list);
263             
264         } catch (Exception JavaDoc e) {
265             log("Invalid maxRetries setting: " + getInitParameter("maxRetries"));
266         }
267         try {
268             if (getInitParameter("timeout") != null) {
269                 smtpTimeout = Integer.parseInt(getInitParameter("timeout"));
270             }
271         } catch (Exception JavaDoc e) {
272             log("Invalid timeout setting: " + getInitParameter("timeout"));
273         }
274
275         try {
276             if (getInitParameter("connectiontimeout") != null) {
277                 connectionTimeout = Integer.parseInt(getInitParameter("connectiontimeout"));
278             }
279         } catch (Exception JavaDoc e) {
280             log("Invalid timeout setting: " + getInitParameter("timeout"));
281         }
282         sendPartial = (getInitParameter("sendpartial") == null) ? false : new Boolean JavaDoc(getInitParameter("sendpartial")).booleanValue();
283
284         bounceProcessor = getInitParameter("bounceProcessor");
285
286         String JavaDoc gateway = getInitParameter("gateway");
287         String JavaDoc gatewayPort = getInitParameter("gatewayPort");
288
289         if (gateway != null) {
290             gatewayServer = new ArrayList JavaDoc();
291             StringTokenizer JavaDoc st = new StringTokenizer JavaDoc(gateway, ",") ;
292             while (st.hasMoreTokens()) {
293                 String JavaDoc server = st.nextToken().trim() ;
294                 if (server.indexOf(':') < 0 && gatewayPort != null) {
295                     server += ":";
296                     server += gatewayPort;
297                 }
298
299                 if (isDebug) log("Adding SMTP gateway: " + server) ;
300                 gatewayServer.add(server);
301             }
302         }
303
304         ComponentManager compMgr = (ComponentManager)getMailetContext().getAttribute(Constants.AVALON_COMPONENT_MANAGER);
305         String JavaDoc outgoingPath = getInitParameter("outgoing");
306         if (outgoingPath == null) {
307             outgoingPath = "file:///../var/mail/outgoing";
308         }
309
310         try {
311             // Instantiate the a MailRepository for outgoing mails
312
MailStore mailstore = (MailStore) compMgr.lookup("org.apache.james.services.MailStore");
313
314             DefaultConfiguration spoolConf
315                 = new DefaultConfiguration("repository", "generated:RemoteDelivery.java");
316             spoolConf.setAttribute("destinationURL", outgoingPath);
317             spoolConf.setAttribute("type", "SPOOL");
318             outgoing = (SpoolRepository) mailstore.select(spoolConf);
319         } catch (ComponentException cnfe) {
320             log("Failed to retrieve Store component:" + cnfe.getMessage());
321         } catch (Exception JavaDoc e) {
322             log("Failed to retrieve Store component:" + e.getMessage());
323         }
324
325         //Start up a number of threads
326
try {
327             deliveryThreadCount = Integer.parseInt(getInitParameter("deliveryThreads"));
328         } catch (Exception JavaDoc e) {
329         }
330         for (int i = 0; i < deliveryThreadCount; i++) {
331             StringBuffer JavaDoc nameBuffer =
332                 new StringBuffer JavaDoc(32)
333                         .append("Remote delivery thread (")
334                         .append(i)
335                         .append(")");
336             Thread JavaDoc t = new Thread JavaDoc(this, nameBuffer.toString());
337             t.start();
338             deliveryThreads.add(t);
339         }
340
341         bindAddress = getInitParameter("bind");
342         isBindUsed = bindAddress != null;
343         try {
344             if (isBindUsed) RemoteDeliverySocketFactory.setBindAdress(bindAddress);
345         } catch (UnknownHostException JavaDoc e) {
346             log("Invalid bind setting (" + bindAddress + "): " + e.toString());
347         }
348     }
349
350     /**
351      * We can assume that the recipients of this message are all going to the same
352      * mail server. We will now rely on the DNS server to do DNS MX record lookup
353      * and try to deliver to the multiple mail servers. If it fails, it should
354      * throw an exception.
355      *
356      * Creation date: (2/24/00 11:25:00 PM)
357      * @param mail org.apache.james.core.MailImpl
358      * @param session javax.mail.Session
359      * @return boolean Whether the delivery was successful and the message can be deleted
360      */

361     private boolean deliver(MailImpl mail, Session JavaDoc session) {
362         try {
363             if (isDebug) {
364                 log("Attempting to deliver " + mail.getName());
365             }
366             MimeMessage JavaDoc message = mail.getMessage();
367
368             //Create an array of the recipients as InternetAddress objects
369
Collection JavaDoc recipients = mail.getRecipients();
370             InternetAddress JavaDoc addr[] = new InternetAddress JavaDoc[recipients.size()];
371             int j = 0;
372             for (Iterator JavaDoc i = recipients.iterator(); i.hasNext(); j++) {
373                 MailAddress rcpt = (MailAddress)i.next();
374                 addr[j] = rcpt.toInternetAddress();
375             }
376
377             if (addr.length <= 0) {
378                 log("No recipients specified... not sure how this could have happened.");
379                 return true;
380             }
381
382             //Figure out which servers to try to send to. This collection
383
// will hold all the possible target servers
384
Iterator JavaDoc targetServers = null;
385             if (gatewayServer == null) {
386                 MailAddress rcpt = (MailAddress) recipients.iterator().next();
387                 String JavaDoc host = rcpt.getHost();
388
389                 //Lookup the possible targets
390
targetServers = getMailetContext().getSMTPHostAddresses(host);
391                 if (!targetServers.hasNext()) {
392                     log("No mail server found for: " + host);
393                     StringBuffer JavaDoc exceptionBuffer =
394                         new StringBuffer JavaDoc(128)
395                         .append("There are no DNS entries for the hostname ")
396                         .append(host)
397                         .append(". I cannot determine where to send this message.");
398                     return failMessage(mail, new MessagingException JavaDoc(exceptionBuffer.toString()), false);
399                 }
400             } else {
401                 targetServers = getGatewaySMTPHostAddresses(gatewayServer);
402             }
403
404             MessagingException JavaDoc lastError = null;
405
406             while ( targetServers.hasNext()) {
407                 try {
408                     HostAddress outgoingMailServer = (HostAddress) targetServers.next();
409                     StringBuffer JavaDoc logMessageBuffer =
410                         new StringBuffer JavaDoc(256)
411                         .append("Attempting delivery of ")
412                         .append(mail.getName())
413                         .append(" to host ")
414                         .append(outgoingMailServer.getHostName())
415                         .append(" at ")
416                         .append(outgoingMailServer.getHost())
417                         .append(" to addresses ")
418                         .append(Arrays.asList(addr));
419                     log(logMessageBuffer.toString());
420
421                     Properties JavaDoc props = session.getProperties();
422                     if (mail.getSender() == null) {
423                         props.put("mail.smtp.from", "<>");
424                     } else {
425                         String JavaDoc sender = mail.getSender().toString();
426                         props.put("mail.smtp.from", sender);
427                     }
428
429                     //Many of these properties are only in later JavaMail versions
430
//"mail.smtp.ehlo" //default true
431
//"mail.smtp.auth" //default false
432
//"mail.smtp.dsn.ret" //default to nothing... appended as RET= after MAIL FROM line.
433
//"mail.smtp.dsn.notify" //default to nothing...appended as NOTIFY= after RCPT TO line.
434

435                     Transport JavaDoc transport = null;
436                     try {
437                         transport = session.getTransport(outgoingMailServer);
438                         try {
439                             transport.connect();
440                         } catch (MessagingException JavaDoc me) {
441                             // Any error on connect should cause the mailet to attempt
442
// to connect to the next SMTP server associated with this
443
// MX record. Just log the exception. We'll worry about
444
// failing the message at the end of the loop.
445
log(me.getMessage());
446                             continue;
447                         }
448                         transport.sendMessage(message, addr);
449                     } finally {
450                         if (transport != null) {
451                             transport.close();
452                             transport = null;
453                         }
454                     }
455                     logMessageBuffer =
456                                       new StringBuffer JavaDoc(256)
457                                       .append("Mail (")
458                                       .append(mail.getName())
459                                       .append(") sent successfully to ")
460                                       .append(outgoingMailServer.getHostName())
461                                       .append(" at ")
462                                       .append(outgoingMailServer.getHost());
463                     log(logMessageBuffer.toString());
464                     return true;
465                 } catch (SendFailedException JavaDoc sfe) {
466                     if (sfe.getValidSentAddresses() == null
467                           || sfe.getValidSentAddresses().length < 1) {
468                         if (isDebug) log("Send failed, continuing with any other servers");
469                         lastError = sfe;
470                         continue;
471                     } else {
472                         // If any mail was sent then the outgoing
473
// server config must be ok, therefore rethrow
474
throw sfe;
475                     }
476                 } catch (MessagingException JavaDoc me) {
477                     //MessagingException are horribly difficult to figure out what actually happened.
478
StringBuffer JavaDoc exceptionBuffer =
479                         new StringBuffer JavaDoc(256)
480                         .append("Exception delivering message (")
481                         .append(mail.getName())
482                         .append(") - ")
483                         .append(me.getMessage());
484                     log(exceptionBuffer.toString());
485                     if ((me.getNextException() != null) &&
486                           (me.getNextException() instanceof java.io.IOException JavaDoc)) {
487                         //This is more than likely a temporary failure
488

489                         // If it's an IO exception with no nested exception, it's probably
490
// some socket or weird I/O related problem.
491
lastError = me;
492                         continue;
493                     }
494                     // This was not a connection or I/O error particular to one
495
// SMTP server of an MX set. Instead, it is almost certainly
496
// a protocol level error. In this case we assume that this
497
// is an error we'd encounter with any of the SMTP servers
498
// associated with this MX record, and we pass the exception
499
// to the code in the outer block that determines its severity.
500
throw me;
501                 }
502             } // end while
503
//If we encountered an exception while looping through,
504
//throw the last MessagingException we caught. We only
505
//do this if we were unable to send the message to any
506
//server. If sending eventually succeeded, we exit
507
//deliver() though the return at the end of the try
508
//block.
509
if (lastError != null) {
510                 throw lastError;
511             }
512         } catch (SendFailedException JavaDoc sfe) {
513             boolean deleteMessage = false;
514             Collection JavaDoc recipients = mail.getRecipients();
515
516             //Would like to log all the types of email addresses
517
if (isDebug) log("Recipients: " + recipients);
518
519             /*
520             if (sfe.getValidSentAddresses() != null) {
521                 Address[] validSent = sfe.getValidSentAddresses();
522                 Collection recipients = mail.getRecipients();
523                 //Remove these addresses for the recipients
524                 for (int i = 0; i < validSent.length; i++) {
525                     try {
526                         MailAddress addr = new MailAddress(validSent[i].toString());
527                         recipients.remove(addr);
528                     } catch (ParseException pe) {
529                         //ignore once debugging done
530                         pe.printStackTrace();
531                     }
532                 }
533             }
534             */

535
536             /*
537              * The rest of the recipients failed for one reason or
538              * another.
539              *
540              * SendFailedException actually handles this for us. For
541              * example, if you send a message that has multiple invalid
542              * addresses, you'll get a top-level SendFailedException
543              * that that has the valid, valid-unsent, and invalid
544              * address lists, with all of the server response messages
545              * will be contained within the nested exceptions. [Note:
546              * the content of the nested exceptions is implementation
547              * dependent.]
548              *
549              * sfe.getInvalidAddresses() should be considered permanent.
550              * sfe.getValidUnsentAddresses() should be considered temporary.
551              *
552              * JavaMail v1.3 properly populates those collections based
553              * upon the 4xx and 5xx response codes.
554              *
555              */

556
557             if (sfe.getInvalidAddresses() != null) {
558                 Address JavaDoc[] address = sfe.getInvalidAddresses();
559                 if (address.length > 0) {
560                     recipients.clear();
561                     for (int i = 0; i < address.length; i++) {
562                         try {
563                             recipients.add(new MailAddress(address[i].toString()));
564                         } catch (ParseException JavaDoc pe) {
565                             // this should never happen ... we should have
566
// caught malformed addresses long before we
567
// got to this code.
568
log("Can't parse invalid address: " + pe.getMessage());
569                         }
570                     }
571                     if (isDebug) log("Invalid recipients: " + recipients);
572                     deleteMessage = failMessage(mail, sfe, true);
573                 }
574             }
575
576             if (sfe.getValidUnsentAddresses() != null) {
577                 Address JavaDoc[] address = sfe.getValidUnsentAddresses();
578                 if (address.length > 0) {
579                     recipients.clear();
580                     for (int i = 0; i < address.length; i++) {
581                         try {
582                             recipients.add(new MailAddress(address[i].toString()));
583                         } catch (ParseException JavaDoc pe) {
584                             // this should never happen ... we should have
585
// caught malformed addresses long before we
586
// got to this code.
587
log("Can't parse unsent address: " + pe.getMessage());
588                         }
589                     }
590                     if (isDebug) log("Unsent recipients: " + recipients);
591                     deleteMessage = failMessage(mail, sfe, false);
592                 }
593             }
594
595             return deleteMessage;
596         } catch (MessagingException JavaDoc ex) {
597             // We should do a better job checking this... if the failure is a general
598
// connect exception, this is less descriptive than more specific SMTP command
599
// failure... have to lookup and see what are the various Exception
600
// possibilities
601

602             // Unable to deliver message after numerous tries... fail accordingly
603

604             // We check whether this is a 5xx error message, which
605
// indicates a permanent failure (like account doesn't exist
606
// or mailbox is full or domain is setup wrong).
607
// We fail permanently if this was a 5xx error
608
return failMessage(mail, ex, ('5' == ex.getMessage().charAt(0)));
609         }
610
611         /* If we get here, we've exhausted the loop of servers without
612          * sending the message or throwing an exception. One case
613          * where this might happen is if we get a MessagingException on
614          * each transport.connect(), e.g., if there is only one server
615          * and we get a connect exception.
616          */

617         return failMessage(mail, new MessagingException JavaDoc("No mail server(s) available at this time."), false);
618     }
619
620     /**
621      * Insert the method's description here.
622      * Creation date: (2/25/00 1:14:18 AM)
623      * @param mail org.apache.james.core.MailImpl
624      * @param exception javax.mail.MessagingException
625      * @param boolean permanent
626      * @return boolean Whether the message failed fully and can be deleted
627      */

628     private boolean failMessage(MailImpl mail, MessagingException JavaDoc ex, boolean permanent) {
629         StringWriter JavaDoc sout = new StringWriter JavaDoc();
630         PrintWriter JavaDoc out = new PrintWriter JavaDoc(sout, true);
631         if (permanent) {
632             out.print("Permanent");
633         } else {
634             out.print("Temporary");
635         }
636         StringBuffer JavaDoc logBuffer =
637             new StringBuffer