Source Code Cross Referenced for RemoteDelivery.java in  » Net » james-2.3.1 » org » apache » james » transport » mailets » Java Source Code / Java DocumentationJava Source Code and Java Documentation

Java Source Code / Java Documentation
1. 6.0 JDK Core
2. 6.0 JDK Modules
3. 6.0 JDK Modules com.sun
4. 6.0 JDK Modules com.sun.java
5. 6.0 JDK Modules sun
6. 6.0 JDK Platform
7. Ajax
8. Apache Harmony Java SE
9. Aspect oriented
10. Authentication Authorization
11. Blogger System
12. Build
13. Byte Code
14. Cache
15. Chart
16. Chat
17. Code Analyzer
18. Collaboration
19. Content Management System
20. Database Client
21. Database DBMS
22. Database JDBC Connection Pool
23. Database ORM
24. Development
25. EJB Server geronimo
26. EJB Server GlassFish
27. EJB Server JBoss 4.2.1
28. EJB Server resin 3.1.5
29. ERP CRM Financial
30. ESB
31. Forum
32. GIS
33. Graphic Library
34. Groupware
35. HTML Parser
36. IDE
37. IDE Eclipse
38. IDE Netbeans
39. Installer
40. Internationalization Localization
41. Inversion of Control
42. Issue Tracking
43. J2EE
44. JBoss
45. JMS
46. JMX
47. Library
48. Mail Clients
49. Net
50. Parser
51. PDF
52. Portal
53. Profiler
54. Project Management
55. Report
56. RSS RDF
57. Rule Engine
58. Science
59. Scripting
60. Search Engine
61. Security
62. Sevlet Container
63. Source Control
64. Swing Library
65. Template Engine
66. Test Coverage
67. Testing
68. UML
69. Web Crawler
70. Web Framework
71. Web Mail
72. Web Server
73. Web Services
74. Web Services apache cxf 2.0.1
75. Web Services AXIS2
76. Wiki Engine
77. Workflow Engines
78. XML
79. XML UI
Java
Java Tutorial
Java Open Source
Jar File Download
Java Articles
Java Products
Java by API
Photoshop Tutorials
Maya Tutorials
Flash Tutorials
3ds-Max Tutorials
Illustrator Tutorials
GIMP Tutorials
C# / C Sharp
C# / CSharp Tutorial
C# / CSharp Open Source
ASP.Net
ASP.NET Tutorial
JavaScript DHTML
JavaScript Tutorial
JavaScript Reference
HTML / CSS
HTML CSS Reference
C / ANSI-C
C Tutorial
C++
C++ Tutorial
Ruby
PHP
Python
Python Tutorial
Python Open Source
SQL Server / T-SQL
SQL Server / T-SQL Tutorial
Oracle PL / SQL
Oracle PL/SQL Tutorial
PostgreSQL
SQL / MySQL
MySQL Tutorial
VB.Net
VB.Net Tutorial
Flash / Flex / ActionScript
VBA / Excel / Access / Word
XML
XML Tutorial
Microsoft Office PowerPoint 2007 Tutorial
Microsoft Office Excel 2007 Tutorial
Microsoft Office Word 2007 Tutorial
Java Source Code / Java Documentation » Net » james 2.3.1 » org.apache.james.transport.mailets 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


0001:        /****************************************************************
0002:         * Licensed to the Apache Software Foundation (ASF) under one   *
0003:         * or more contributor license agreements.  See the NOTICE file *
0004:         * distributed with this work for additional information        *
0005:         * regarding copyright ownership.  The ASF licenses this file   *
0006:         * to you under the Apache License, Version 2.0 (the            *
0007:         * "License"); you may not use this file except in compliance   *
0008:         * with the License.  You may obtain a copy of the License at   *
0009:         *                                                              *
0010:         *   http://www.apache.org/licenses/LICENSE-2.0                 *
0011:         *                                                              *
0012:         * Unless required by applicable law or agreed to in writing,   *
0013:         * software distributed under the License is distributed on an  *
0014:         * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
0015:         * KIND, either express or implied.  See the License for the    *
0016:         * specific language governing permissions and limitations      *
0017:         * under the License.                                           *
0018:         ****************************************************************/package org.apache.james.transport.mailets;
0019:
0020:        import org.apache.avalon.cornerstone.services.store.Store;
0021:        import org.apache.avalon.framework.configuration.DefaultConfiguration;
0022:        import org.apache.avalon.framework.container.ContainerUtil;
0023:        import org.apache.avalon.framework.service.ServiceException;
0024:        import org.apache.avalon.framework.service.ServiceManager;
0025:        import org.apache.james.Constants;
0026:        import org.apache.james.services.SpoolRepository;
0027:        import org.apache.mailet.GenericMailet;
0028:        import org.apache.mailet.HostAddress;
0029:        import org.apache.mailet.Mail;
0030:        import org.apache.mailet.MailAddress;
0031:        import org.apache.mailet.MailetContext;
0032:        import org.apache.oro.text.regex.MalformedPatternException;
0033:        import org.apache.oro.text.regex.MatchResult;
0034:        import org.apache.oro.text.regex.Pattern;
0035:        import org.apache.oro.text.regex.Perl5Compiler;
0036:        import org.apache.oro.text.regex.Perl5Matcher;
0037:
0038:        import com.sun.mail.smtp.SMTPAddressFailedException;
0039:        import com.sun.mail.smtp.SMTPAddressSucceededException;
0040:        import com.sun.mail.smtp.SMTPSendFailedException;
0041:        import com.sun.mail.smtp.SMTPTransport;
0042:
0043:        import javax.mail.Address;
0044:        import javax.mail.MessagingException;
0045:        import javax.mail.SendFailedException;
0046:        import javax.mail.Session;
0047:        import javax.mail.Transport;
0048:        import javax.mail.internet.InternetAddress;
0049:        import javax.mail.internet.MimeMessage;
0050:        import javax.mail.internet.MimeMultipart;
0051:        import javax.mail.internet.MimePart;
0052:        import javax.mail.internet.ParseException;
0053:
0054:        import java.io.IOException;
0055:        import java.io.PrintWriter;
0056:        import java.io.StringWriter;
0057:        import java.net.ConnectException;
0058:        import java.net.InetAddress;
0059:        import java.net.SocketException;
0060:        import java.net.UnknownHostException;
0061:        import java.util.ArrayList;
0062:        import java.util.Arrays;
0063:        import java.util.Collection;
0064:        import java.util.Date;
0065:        import java.util.HashMap;
0066:        import java.util.Hashtable;
0067:        import java.util.Iterator;
0068:        import java.util.Locale;
0069:        import java.util.Properties;
0070:        import java.util.StringTokenizer;
0071:        import java.util.Vector;
0072:
0073:        /**
0074:         * Receives a MessageContainer from JamesSpoolManager and takes care of delivery
0075:         * the message to remote hosts. If for some reason mail can't be delivered
0076:         * store it in the "outgoing" Repository and set an Alarm. After the next "delayTime" the
0077:         * Alarm will wake the servlet that will try to send it again. After "maxRetries"
0078:         * the mail will be considered undeliverable and will be returned to sender.
0079:         *
0080:         * TO DO (in priority):
0081:         * 1. Support a gateway (a single server where all mail will be delivered) (DONE)
0082:         * 2. Provide better failure messages (DONE)
0083:         * 3. More efficiently handle numerous recipients
0084:         * 4. Migrate to use Phoenix for the delivery threads
0085:         *
0086:         * You really want to read the JavaMail documentation if you are
0087:         * working in here, and you will want to view the list of JavaMail
0088:         * attributes, which are documented here:
0089:         *
0090:         * http://java.sun.com/products/javamail/1.3/docs/javadocs/com/sun/mail/smtp/package-summary.html
0091:         *
0092:         * as well as other places.
0093:         *
0094:         * @version CVS $Revision: 494056 $ $Date: 2007-01-08 14:15:03 +0100 (Mo, 08 Jan 2007) $
0095:         */
0096:        public class RemoteDelivery extends GenericMailet implements  Runnable {
0097:
0098:            private static final long DEFAULT_DELAY_TIME = 21600000; // default is 6*60*60*1000 millis (6 hours)
0099:            private static final String PATTERN_STRING = "\\s*([0-9]*\\s*[\\*])?\\s*([0-9]+)\\s*([a-z,A-Z]*)\\s*";//pattern to match
0100:            //[attempts*]delay[units]
0101:
0102:            private static Pattern PATTERN = null; //the compiled pattern of the above String
0103:            private static final HashMap MULTIPLIERS = new HashMap(10); //holds allowed units for delaytime together with
0104:            //the factor to turn it into the equivalent time in msec
0105:
0106:            /*
0107:             * Static initializer.<p>
0108:             * Compiles pattern for processing delaytime entries.<p>
0109:             * Initializes MULTIPLIERS with the supported unit quantifiers
0110:             */
0111:            static {
0112:                try {
0113:                    Perl5Compiler compiler = new Perl5Compiler();
0114:                    PATTERN = compiler.compile(PATTERN_STRING,
0115:                            Perl5Compiler.READ_ONLY_MASK);
0116:                } catch (MalformedPatternException mpe) {
0117:                    //this should not happen as the pattern string is hardcoded.
0118:                    System.err.println("Malformed pattern: " + PATTERN_STRING);
0119:                    mpe.printStackTrace(System.err);
0120:                }
0121:                //add allowed units and their respective multiplier
0122:                MULTIPLIERS.put("msec", new Integer(1));
0123:                MULTIPLIERS.put("msecs", new Integer(1));
0124:                MULTIPLIERS.put("sec", new Integer(1000));
0125:                MULTIPLIERS.put("secs", new Integer(1000));
0126:                MULTIPLIERS.put("minute", new Integer(1000 * 60));
0127:                MULTIPLIERS.put("minutes", new Integer(1000 * 60));
0128:                MULTIPLIERS.put("hour", new Integer(1000 * 60 * 60));
0129:                MULTIPLIERS.put("hours", new Integer(1000 * 60 * 60));
0130:                MULTIPLIERS.put("day", new Integer(1000 * 60 * 60 * 24));
0131:                MULTIPLIERS.put("days", new Integer(1000 * 60 * 60 * 24));
0132:            }
0133:
0134:            /**
0135:             * This filter is used in the accept call to the spool.
0136:             * It will select the next mail ready for processing according to the mails
0137:             * retrycount and lastUpdated time
0138:             **/
0139:            private class MultipleDelayFilter implements 
0140:                    SpoolRepository.AcceptFilter {
0141:                /**
0142:                 * holds the time to wait for the youngest mail to get ready for processing
0143:                 **/
0144:                long youngest = 0;
0145:
0146:                /**
0147:                 * Uses the getNextDelay to determine if a mail is ready for processing based on the delivered parameters
0148:                 * errorMessage (which holds the retrycount), lastUpdated and state
0149:                 * @param key the name/key of the message
0150:                 * @param state the mails state
0151:                 * @param lastUpdated the mail was last written to the spool at this time.
0152:                 * @param errorMessage actually holds the retrycount as a string (see failMessage below)
0153:                 **/
0154:                public boolean accept(String key, String state,
0155:                        long lastUpdated, String errorMessage) {
0156:                    if (state.equals(Mail.ERROR)) {
0157:                        //Test the time...
0158:                        int retries = Integer.parseInt(errorMessage);
0159:
0160:                        // If the retries count is 0 we should try to send the mail now!
0161:                        if (retries == 0)
0162:                            return true;
0163:
0164:                        long delay = getNextDelay(retries);
0165:                        long timeToProcess = delay + lastUpdated;
0166:
0167:                        if (System.currentTimeMillis() > timeToProcess) {
0168:                            //We're ready to process this again
0169:                            return true;
0170:                        } else {
0171:                            //We're not ready to process this.
0172:                            if (youngest == 0 || youngest > timeToProcess) {
0173:                                //Mark this as the next most likely possible mail to process
0174:                                youngest = timeToProcess;
0175:                            }
0176:                            return false;
0177:                        }
0178:                    } else {
0179:                        //This mail is good to go... return the key
0180:                        return true;
0181:                    }
0182:                }
0183:
0184:                /**
0185:                 * @return the optimal time the SpoolRepository.accept(AcceptFilter) method should wait before
0186:                 * trying to find a mail ready for processing again.
0187:                 **/
0188:                public long getWaitTime() {
0189:                    if (youngest == 0) {
0190:                        return 0;
0191:                    } else {
0192:                        long duration = youngest - System.currentTimeMillis();
0193:                        youngest = 0; //get ready for next run
0194:                        return duration <= 0 ? 1 : duration;
0195:                    }
0196:                }
0197:            }
0198:
0199:            /**
0200:             * Controls certain log messages
0201:             */
0202:            private boolean isDebug = false;
0203:
0204:            private SpoolRepository outgoing; // The spool of outgoing mail
0205:            private long[] delayTimes; //holds expanded delayTimes
0206:            private int maxRetries = 5; // default number of retries
0207:            private long smtpTimeout = 180000; //default number of ms to timeout on smtp delivery
0208:            private boolean sendPartial = false; // If false then ANY address errors will cause the transmission to fail
0209:            private int connectionTimeout = 60000; // The amount of time JavaMail will wait before giving up on a socket connect()
0210:            private int deliveryThreadCount = 1; // default number of delivery threads
0211:            private Collection gatewayServer = null; // the server(s) to send all email to
0212:            private String authUser = null; // auth for gateway server
0213:            private String authPass = null; // password for gateway server
0214:            private String bindAddress = null; // JavaMail delivery socket binds to this local address. If null the JavaMail default will be used.
0215:            private boolean isBindUsed = false; // true, if the bind configuration
0216:            // parameter is supplied, RemoteDeliverySocketFactory
0217:            // will be used in this case
0218:            private Collection deliveryThreads = new Vector();
0219:            private volatile boolean destroyed = false; //Flag that the run method will check and end itself if set to true
0220:            private String bounceProcessor = null; // the processor for creating Bounces
0221:
0222:            private Perl5Matcher delayTimeMatcher; //matcher use at init time to parse delaytime parameters
0223:            private MultipleDelayFilter delayFilter = new MultipleDelayFilter();//used by accept to selcet the next mail ready for processing
0224:            private Properties defprops = new Properties(); // default properties for the javamail Session
0225:
0226:            /**
0227:             * Initialize the mailet
0228:             */
0229:            public void init() throws MessagingException {
0230:                isDebug = (getInitParameter("debug") == null) ? false
0231:                        : new Boolean(getInitParameter("debug")).booleanValue();
0232:                ArrayList delay_times_list = new ArrayList();
0233:                try {
0234:                    if (getInitParameter("delayTime") != null) {
0235:                        delayTimeMatcher = new Perl5Matcher();
0236:                        String delay_times = getInitParameter("delayTime");
0237:                        //split on comma's
0238:                        StringTokenizer st = new StringTokenizer(delay_times,
0239:                                ",");
0240:                        while (st.hasMoreTokens()) {
0241:                            String delay_time = st.nextToken();
0242:                            delay_times_list.add(new Delay(delay_time));
0243:                        }
0244:                    } else {
0245:                        //use default delayTime.
0246:                        delay_times_list.add(new Delay());
0247:                    }
0248:                } catch (Exception e) {
0249:                    log("Invalid delayTime setting: "
0250:                            + getInitParameter("delayTime"));
0251:                }
0252:                try {
0253:                    if (getInitParameter("maxRetries") != null) {
0254:                        maxRetries = Integer
0255:                                .parseInt(getInitParameter("maxRetries"));
0256:                    }
0257:                    //check consistency with delay_times_list attempts
0258:                    int total_attempts = calcTotalAttempts(delay_times_list);
0259:                    if (total_attempts > maxRetries) {
0260:                        log("Total number of delayTime attempts exceeds maxRetries specified. Increasing maxRetries from "
0261:                                + maxRetries + " to " + total_attempts);
0262:                        maxRetries = total_attempts;
0263:                    } else {
0264:                        int extra = maxRetries - total_attempts;
0265:                        if (extra != 0) {
0266:                            log("maxRetries is larger than total number of attempts specified. Increasing last delayTime with "
0267:                                    + extra + " attempts ");
0268:
0269:                            if (delay_times_list.size() != 0) {
0270:                                Delay delay = (Delay) delay_times_list
0271:                                        .get(delay_times_list.size() - 1); //last Delay
0272:                                delay.setAttempts(delay.getAttempts() + extra);
0273:                                log("Delay of " + delay.getDelayTime()
0274:                                        + " msecs is now attempted: "
0275:                                        + delay.getAttempts() + " times");
0276:                            } else {
0277:                                log("NO, delaytimes cannot continue");
0278:                            }
0279:                        }
0280:                    }
0281:                    delayTimes = expandDelays(delay_times_list);
0282:
0283:                } catch (Exception e) {
0284:                    log("Invalid maxRetries setting: "
0285:                            + getInitParameter("maxRetries"));
0286:                }
0287:                try {
0288:                    if (getInitParameter("timeout") != null) {
0289:                        smtpTimeout = Integer
0290:                                .parseInt(getInitParameter("timeout"));
0291:                    }
0292:                } catch (Exception e) {
0293:                    log("Invalid timeout setting: "
0294:                            + getInitParameter("timeout"));
0295:                }
0296:
0297:                try {
0298:                    if (getInitParameter("connectiontimeout") != null) {
0299:                        connectionTimeout = Integer
0300:                                .parseInt(getInitParameter("connectiontimeout"));
0301:                    }
0302:                } catch (Exception e) {
0303:                    log("Invalid timeout setting: "
0304:                            + getInitParameter("timeout"));
0305:                }
0306:                sendPartial = (getInitParameter("sendpartial") == null) ? false
0307:                        : new Boolean(getInitParameter("sendpartial"))
0308:                                .booleanValue();
0309:
0310:                bounceProcessor = getInitParameter("bounceProcessor");
0311:
0312:                String gateway = getInitParameter("gateway");
0313:                String gatewayPort = getInitParameter("gatewayPort");
0314:
0315:                if (gateway != null) {
0316:                    gatewayServer = new ArrayList();
0317:                    StringTokenizer st = new StringTokenizer(gateway, ",");
0318:                    while (st.hasMoreTokens()) {
0319:                        String server = st.nextToken().trim();
0320:                        if (server.indexOf(':') < 0 && gatewayPort != null) {
0321:                            server += ":";
0322:                            server += gatewayPort;
0323:                        }
0324:
0325:                        if (isDebug)
0326:                            log("Adding SMTP gateway: " + server);
0327:                        gatewayServer.add(server);
0328:                    }
0329:                    authUser = getInitParameter("gatewayusername");
0330:                    authPass = getInitParameter("gatewayPassword");
0331:                }
0332:
0333:                ServiceManager compMgr = (ServiceManager) getMailetContext()
0334:                        .getAttribute(Constants.AVALON_COMPONENT_MANAGER);
0335:                String outgoingPath = getInitParameter("outgoing");
0336:                if (outgoingPath == null) {
0337:                    outgoingPath = "file:///../var/mail/outgoing";
0338:                }
0339:
0340:                try {
0341:                    // Instantiate the a MailRepository for outgoing mails
0342:                    Store mailstore = (Store) compMgr.lookup(Store.ROLE);
0343:
0344:                    DefaultConfiguration spoolConf = new DefaultConfiguration(
0345:                            "repository", "generated:RemoteDelivery.java");
0346:                    spoolConf.setAttribute("destinationURL", outgoingPath);
0347:                    spoolConf.setAttribute("type", "SPOOL");
0348:                    outgoing = (SpoolRepository) mailstore.select(spoolConf);
0349:                } catch (ServiceException cnfe) {
0350:                    log("Failed to retrieve Store component:"
0351:                            + cnfe.getMessage());
0352:                } catch (Exception e) {
0353:                    log("Failed to retrieve Store component:" + e.getMessage());
0354:                }
0355:
0356:                //Start up a number of threads
0357:                try {
0358:                    deliveryThreadCount = Integer
0359:                            .parseInt(getInitParameter("deliveryThreads"));
0360:                } catch (Exception e) {
0361:                }
0362:                for (int i = 0; i < deliveryThreadCount; i++) {
0363:                    StringBuffer nameBuffer = new StringBuffer(32).append(
0364:                            "Remote delivery thread (").append(i).append(")");
0365:                    Thread t = new Thread(this , nameBuffer.toString());
0366:                    t.start();
0367:                    deliveryThreads.add(t);
0368:                }
0369:
0370:                bindAddress = getInitParameter("bind");
0371:                isBindUsed = bindAddress != null;
0372:                try {
0373:                    if (isBindUsed)
0374:                        RemoteDeliverySocketFactory.setBindAdress(bindAddress);
0375:                } catch (UnknownHostException e) {
0376:                    log("Invalid bind setting (" + bindAddress + "): "
0377:                            + e.toString());
0378:                }
0379:
0380:                Iterator i = getInitParameterNames();
0381:                while (i.hasNext()) {
0382:                    String name = (String) i.next();
0383:                    if (name.startsWith("mail.")) {
0384:                        defprops.put(name, getInitParameter(name));
0385:                    }
0386:                }
0387:            }
0388:
0389:            /*
0390:             * private method to log the extended SendFailedException introduced in JavaMail 1.3.2.
0391:             */
0392:            private void logSendFailedException(SendFailedException sfe) {
0393:                if (isDebug) {
0394:                    MessagingException me = sfe;
0395:                    if (me instanceof  SMTPSendFailedException) {
0396:                        SMTPSendFailedException ssfe = (SMTPSendFailedException) me;
0397:                        log("SMTP SEND FAILED:");
0398:                        log(ssfe.toString());
0399:                        log("  Command: " + ssfe.getCommand());
0400:                        log("  RetCode: " + ssfe.getReturnCode());
0401:                        log("  Response: " + ssfe.getMessage());
0402:                    } else {
0403:                        log("Send failed: " + me.toString());
0404:                    }
0405:                    Exception ne;
0406:                    while ((ne = me.getNextException()) != null
0407:                            && ne instanceof  MessagingException) {
0408:                        me = (MessagingException) ne;
0409:                        if (me instanceof  SMTPAddressFailedException) {
0410:                            SMTPAddressFailedException e = (SMTPAddressFailedException) me;
0411:                            log("ADDRESS FAILED:");
0412:                            log(e.toString());
0413:                            log("  Address: " + e.getAddress());
0414:                            log("  Command: " + e.getCommand());
0415:                            log("  RetCode: " + e.getReturnCode());
0416:                            log("  Response: " + e.getMessage());
0417:                        } else if (me instanceof  SMTPAddressSucceededException) {
0418:                            log("ADDRESS SUCCEEDED:");
0419:                            SMTPAddressSucceededException e = (SMTPAddressSucceededException) me;
0420:                            log(e.toString());
0421:                            log("  Address: " + e.getAddress());
0422:                            log("  Command: " + e.getCommand());
0423:                            log("  RetCode: " + e.getReturnCode());
0424:                            log("  Response: " + e.getMessage());
0425:                        }
0426:                    }
0427:                }
0428:            }
0429:
0430:            /**
0431:             * We can assume that the recipients of this message are all going to the same
0432:             * mail server.  We will now rely on the DNS server to do DNS MX record lookup
0433:             * and try to deliver to the multiple mail servers.  If it fails, it should
0434:             * throw an exception.
0435:             *
0436:             * Creation date: (2/24/00 11:25:00 PM)
0437:             * @param mail org.apache.james.core.MailImpl
0438:             * @param session javax.mail.Session
0439:             * @return boolean Whether the delivery was successful and the message can be deleted
0440:             */
0441:            private boolean deliver(Mail mail, Session session) {
0442:                try {
0443:                    if (isDebug) {
0444:                        log("Attempting to deliver " + mail.getName());
0445:                    }
0446:                    MimeMessage message = mail.getMessage();
0447:
0448:                    //Create an array of the recipients as InternetAddress objects
0449:                    Collection recipients = mail.getRecipients();
0450:                    InternetAddress addr[] = new InternetAddress[recipients
0451:                            .size()];
0452:                    int j = 0;
0453:                    for (Iterator i = recipients.iterator(); i.hasNext(); j++) {
0454:                        MailAddress rcpt = (MailAddress) i.next();
0455:                        addr[j] = rcpt.toInternetAddress();
0456:                    }
0457:
0458:                    if (addr.length <= 0) {
0459:                        log("No recipients specified... not sure how this could have happened.");
0460:                        return true;
0461:                    }
0462:
0463:                    //Figure out which servers to try to send to.  This collection
0464:                    //  will hold all the possible target servers
0465:                    Iterator targetServers = null;
0466:                    if (gatewayServer == null) {
0467:                        MailAddress rcpt = (MailAddress) recipients.iterator()
0468:                                .next();
0469:                        String host = rcpt.getHost();
0470:
0471:                        //Lookup the possible targets
0472:                        targetServers = getMailetContext()
0473:                                .getSMTPHostAddresses(host);
0474:                        if (!targetServers.hasNext()) {
0475:                            log("No mail server found for: " + host);
0476:                            StringBuffer exceptionBuffer = new StringBuffer(128)
0477:                                    .append(
0478:                                            "There are no DNS entries for the hostname ")
0479:                                    .append(host)
0480:                                    .append(
0481:                                            ".  I cannot determine where to send this message.");
0482:                            return failMessage(mail, new MessagingException(
0483:                                    exceptionBuffer.toString()), false);
0484:                        }
0485:                    } else {
0486:                        targetServers = getGatewaySMTPHostAddresses(gatewayServer);
0487:                    }
0488:
0489:                    MessagingException lastError = null;
0490:
0491:                    while (targetServers.hasNext()) {
0492:                        try {
0493:                            HostAddress outgoingMailServer = (HostAddress) targetServers
0494:                                    .next();
0495:                            StringBuffer logMessageBuffer = new StringBuffer(
0496:                                    256).append("Attempting delivery of ")
0497:                                    .append(mail.getName()).append(" to host ")
0498:                                    .append(outgoingMailServer.getHostName())
0499:                                    .append(" at ").append(
0500:                                            outgoingMailServer.getHost())
0501:                                    .append(" for addresses ").append(
0502:                                            Arrays.asList(addr));
0503:                            log(logMessageBuffer.toString());
0504:
0505:                            Properties props = session.getProperties();
0506:                            if (mail.getSender() == null) {
0507:                                props.put("mail.smtp.from", "<>");
0508:                            } else {
0509:                                String sender = mail.getSender().toString();
0510:                                props.put("mail.smtp.from", sender);
0511:                            }
0512:
0513:                            //Many of these properties are only in later JavaMail versions
0514:                            //"mail.smtp.ehlo"  //default true
0515:                            //"mail.smtp.auth"  //default false
0516:                            //"mail.smtp.dsn.ret"  //default to nothing... appended as RET= after MAIL FROM line.
0517:                            //"mail.smtp.dsn.notify" //default to nothing...appended as NOTIFY= after RCPT TO line.
0518:
0519:                            Transport transport = null;
0520:                            try {
0521:                                transport = session
0522:                                        .getTransport(outgoingMailServer);
0523:                                try {
0524:                                    if (authUser != null) {
0525:                                        transport.connect(outgoingMailServer
0526:                                                .getHostName(), authUser,
0527:                                                authPass);
0528:                                    } else {
0529:                                        transport.connect();
0530:                                    }
0531:                                } catch (MessagingException me) {
0532:                                    // Any error on connect should cause the mailet to attempt
0533:                                    // to connect to the next SMTP server associated with this
0534:                                    // MX record.  Just log the exception.  We'll worry about
0535:                                    // failing the message at the end of the loop.
0536:                                    log(me.getMessage());
0537:                                    continue;
0538:                                }
0539:                                // if the transport is a SMTPTransport (from sun) some
0540:                                // performance enhancement can be done.
0541:                                if (transport instanceof  SMTPTransport) {
0542:                                    SMTPTransport smtpTransport = (SMTPTransport) transport;
0543:
0544:                                    // if the message is alredy 8bit or binary and the
0545:                                    // server doesn't support the 8bit extension it has
0546:                                    // to be converted to 7bit. Javamail api doesn't perform
0547:                                    // that conversion, but it is required to be a
0548:                                    // rfc-compliant smtp server.
0549:
0550:                                    // Temporarily disabled. See JAMES-638
0551:                                    /*
0552:                                    if (!smtpTransport.supportsExtension("8BITMIME")) { 
0553:                                        try {
0554:                                            convertTo7Bit(message);
0555:                                        } catch (IOException e) {
0556:                                            // An error has occured during the 7bit conversion.
0557:                                            // The error is logged and the message is sent anyway.
0558:                                            
0559:                                            log("Error during the conversion to 7 bit.", e);
0560:                                        }
0561:                                    }
0562:                                     */
0563:
0564:                                    /*
0565:                                     * Workaround for a javamail 1.3.2 bug: if
0566:                                     * a message is sent without encoding information
0567:                                     * and the 8bit allow property is set an exception
0568:                                     * is trown during the mail delivery.
0569:                                     */
0570:
0571:                                    try {
0572:                                        setEncodingIfMissing(message);
0573:                                    } catch (IOException e) {
0574:                                        log(
0575:                                                "Error while adding encoding information to the message",
0576:                                                e);
0577:                                    }
0578:                                } else {
0579:                                    // If the transport is not the one
0580:                                    // developed by Sun we are not sure of how it
0581:                                    // handles the 8 bit mime stuff,
0582:                                    // so I convert the message to 7bit.
0583:                                    try {
0584:                                        convertTo7Bit(message);
0585:                                    } catch (IOException e) {
0586:                                        log(
0587:                                                "Error during the conversion to 7 bit.",
0588:                                                e);
0589:                                    }
0590:                                }
0591:                                transport.sendMessage(message, addr);
0592:                            } finally {
0593:                                if (transport != null) {
0594:                                    transport.close();
0595:                                    transport = null;
0596:                                }
0597:                            }
0598:                            logMessageBuffer = new StringBuffer(256).append(
0599:                                    "Mail (").append(mail.getName()).append(
0600:                                    ") sent successfully to ").append(
0601:                                    outgoingMailServer.getHostName()).append(
0602:                                    " at ")
0603:                                    .append(outgoingMailServer.getHost())
0604:                                    .append(" for ").append(
0605:                                            mail.getRecipients());
0606:                            log(logMessageBuffer.toString());
0607:                            return true;
0608:                        } catch (SendFailedException sfe) {
0609:                            logSendFailedException(sfe);
0610:
0611:                            if (sfe.getValidSentAddresses() != null) {
0612:                                Address[] validSent = sfe
0613:                                        .getValidSentAddresses();
0614:                                if (validSent.length > 0) {
0615:                                    StringBuffer logMessageBuffer = new StringBuffer(
0616:                                            256).append("Mail (").append(
0617:                                            mail.getName()).append(
0618:                                            ") sent successfully for ").append(
0619:                                            Arrays.asList(validSent));
0620:                                    log(logMessageBuffer.toString());
0621:                                }
0622:                            }
0623:
0624:                            /* SMTPSendFailedException introduced in JavaMail 1.3.2, and provides detailed protocol reply code for the operation */
0625:                            if (sfe instanceof  SMTPSendFailedException) {
0626:                                SMTPSendFailedException ssfe = (SMTPSendFailedException) sfe;
0627:                                // if 5xx, terminate this delivery attempt by re-throwing the exception.
0628:                                if (ssfe.getReturnCode() >= 500
0629:                                        && ssfe.getReturnCode() <= 599)
0630:                                    throw sfe;
0631:                            }
0632:
0633:                            if (sfe.getValidUnsentAddresses() != null
0634:                                    && sfe.getValidUnsentAddresses().length > 0) {
0635:                                if (isDebug)
0636:                                    log("Send failed, "
0637:                                            + sfe.getValidUnsentAddresses().length
0638:                                            + " valid addresses remain, continuing with any other servers");
0639:                                lastError = sfe;
0640:                                continue;
0641:                            } else {
0642:                                // There are no valid addresses left to send, so rethrow
0643:                                throw sfe;
0644:                            }
0645:                        } catch (MessagingException me) {
0646:                            //MessagingException are horribly difficult to figure out what actually happened.
0647:                            StringBuffer exceptionBuffer = new StringBuffer(256)
0648:                                    .append("Exception delivering message (")
0649:                                    .append(mail.getName()).append(") - ")
0650:                                    .append(me.getMessage());
0651:                            log(exceptionBuffer.toString());
0652:                            if ((me.getNextException() != null)
0653:                                    && (me.getNextException() instanceof  java.io.IOException)) {
0654:                                //This is more than likely a temporary failure
0655:
0656:                                // If it's an IO exception with no nested exception, it's probably
0657:                                // some socket or weird I/O related problem.
0658:                                lastError = me;
0659:                                continue;
0660:                            }
0661:                            // This was not a connection or I/O error particular to one
0662:                            // SMTP server of an MX set.  Instead, it is almost certainly
0663:                            // a protocol level error.  In this case we assume that this
0664:                            // is an error we'd encounter with any of the SMTP servers
0665:                            // associated with this MX record, and we pass the exception
0666:                            // to the code in the outer block that determines its severity.
0667:                            throw me;
0668:                        }
0669:                    } // end while
0670:                    //If we encountered an exception while looping through,
0671:                    //throw the last MessagingException we caught.  We only
0672:                    //do this if we were unable to send the message to any
0673:                    //server.  If sending eventually succeeded, we exit
0674:                    //deliver() though the return at the end of the try
0675:                    //block.
0676:                    if (lastError != null) {
0677:                        throw lastError;
0678:                    }
0679:                } catch (SendFailedException sfe) {
0680:                    logSendFailedException(sfe);
0681:
0682:                    Collection recipients = mail.getRecipients();
0683:
0684:                    boolean deleteMessage = false;
0685:
0686:                    /*
0687:                     * If you send a message that has multiple invalid
0688:                     * addresses, you'll get a top-level SendFailedException
0689:                     * that that has the valid, valid-unsent, and invalid
0690:                     * address lists, with all of the server response messages
0691:                     * will be contained within the nested exceptions.  [Note:
0692:                     * the content of the nested exceptions is implementation
0693:                     * dependent.]
0694:                     *
0695:                     * sfe.getInvalidAddresses() should be considered permanent.
0696:                     * sfe.getValidUnsentAddresses() should be considered temporary.
0697:                     *
0698:                     * JavaMail v1.3 properly populates those collections based
0699:                     * upon the 4xx and 5xx response codes to RCPT TO.  Some
0700:                     * servers, such as Yahoo! don't respond to the RCPT TO,
0701:                     * and provide a 5xx reply after DATA.  In that case, we
0702:                     * will pick up the failure from SMTPSendFailedException.
0703:                     *
0704:                     */
0705:
0706:                    /* SMTPSendFailedException introduced in JavaMail 1.3.2, and provides detailed protocol reply code for the operation */
0707:                    if (sfe instanceof  SMTPSendFailedException) {
0708:                        // If we got an SMTPSendFailedException, use its RetCode to determine default permanent/temporary failure
0709:                        SMTPSendFailedException ssfe = (SMTPSendFailedException) sfe;
0710:                        deleteMessage = (ssfe.getReturnCode() >= 500 && ssfe
0711:                                .getReturnCode() <= 599);
0712:                    } else {
0713:                        // Sometimes we'll get a normal SendFailedException with nested SMTPAddressFailedException, so use the latter RetCode
0714:                        MessagingException me = sfe;
0715:                        Exception ne;
0716:                        while ((ne = me.getNextException()) != null
0717:                                && ne instanceof  MessagingException) {
0718:                            me = (MessagingException) ne;
0719:                            if (me instanceof  SMTPAddressFailedException) {
0720:                                SMTPAddressFailedException ssfe = (SMTPAddressFailedException) me;
0721:                                deleteMessage = (ssfe.getReturnCode() >= 500 && ssfe
0722:                                        .getReturnCode() <= 599);
0723:                            }
0724:                        }
0725:                    }
0726:
0727:                    // log the original set of intended recipients
0728:                    if (isDebug)
0729:                        log("Recipients: " + recipients);
0730:
0731:                    if (sfe.getInvalidAddresses() != null) {
0732:                        Address[] address = sfe.getInvalidAddresses();
0733:                        if (address.length > 0) {
0734:                            recipients.clear();
0735:                            for (int i = 0; i < address.length; i++) {
0736:                                try {
0737:                                    recipients.add(new MailAddress(address[i]
0738:                                            .toString()));
0739:                                } catch (ParseException pe) {
0740:                                    // this should never happen ... we should have
0741:                                    // caught malformed addresses long before we
0742:                                    // got to this code.
0743:                                    log("Can't parse invalid address: "
0744:                                            + pe.getMessage());
0745:                                }
0746:                            }
0747:                            if (isDebug)
0748:                                log("Invalid recipients: " + recipients);
0749:                            deleteMessage = failMessage(mail, sfe, true);
0750:                        }
0751:                    }
0752:
0753:                    if (sfe.getValidUnsentAddresses() != null) {
0754:                        Address[] address = sfe.getValidUnsentAddresses();
0755:                        if (address.length > 0) {
0756:                            recipients.clear();
0757:                            for (int i = 0; i < address.length; i++) {
0758:                                try {
0759:                                    recipients.add(new MailAddress(address[i]
0760:                                            .toString()));
0761:                                } catch (ParseException pe) {
0762:                                    // this should never happen ... we should have
0763:                                    // caught malformed addresses long before we
0764:                                    // got to this code.
0765:                                    log("Can't parse unsent address: "
0766:                                            + pe.getMessage());
0767:                                }
0768:                            }
0769:                            if (isDebug)
0770:                                log("Unsent recipients: " + recipients);
0771:                            if (sfe instanceof  SMTPSendFailedException) {
0772:                                SMTPSendFailedException ssfe = (SMTPSendFailedException) sfe;
0773:                                deleteMessage = failMessage(mail, sfe, ssfe
0774:                                        .getReturnCode() >= 500
0775:                                        && ssfe.getReturnCode() <= 599);
0776:                            } else {
0777:                                deleteMessage = failMessage(mail, sfe, false);
0778:                            }
0779:                        }
0780:                    }
0781:
0782:                    return deleteMessage;
0783:                } catch (MessagingException ex) {
0784:                    // We should do a better job checking this... if the failure is a general
0785:                    // connect exception, this is less descriptive than more specific SMTP command
0786:                    // failure... have to lookup and see what are the various Exception
0787:                    // possibilities
0788:
0789:                    // Unable to deliver message after numerous tries... fail accordingly
0790:
0791:                    // We check whether this is a 5xx error message, which
0792:                    // indicates a permanent failure (like account doesn't exist
0793:                    // or mailbox is full or domain is setup wrong).
0794:                    // We fail permanently if this was a 5xx error
0795:                    return failMessage(mail, ex, ('5' == ex.getMessage()
0796:                            .charAt(0)));
0797:                }
0798:
0799:                /* If we get here, we've exhausted the loop of servers without
0800:                 * sending the message or throwing an exception.  One case
0801:                 * where this might happen is if we get a MessagingException on
0802:                 * each transport.connect(), e.g., if there is only one server
0803:                 * and we get a connect exception.
0804:                 */
0805:                return failMessage(mail, new MessagingException(
0806:                        "No mail server(s) available at this time."), false);
0807:            }
0808:
0809:            /**
0810:             * Converts a message to 7 bit.
0811:             * 
0812:             * @param message
0813:             * @return
0814:             */
0815:            private void convertTo7Bit(MimePart part)
0816:                    throws MessagingException, IOException {
0817:                if (part.isMimeType("multipart/*")) {
0818:                    MimeMultipart parts = (MimeMultipart) part.getContent();
0819:                    int count = parts.getCount();
0820:                    for (int i = 0; i < count; i++) {
0821:                        convertTo7Bit((MimePart) parts.getBodyPart(i));
0822:                    }
0823:                } else {
0824:                    if (part.isMimeType("text/*")) {
0825:                        part.setHeader("Content-Transfer-Encoding",
0826:                                "quoted-printable");
0827:                        part.addHeader("X-MIME-Autoconverted",
0828:                                "from 8bit to quoted-printable by "
0829:                                        + getMailetContext().getServerInfo());
0830:                    } else {
0831:                        // if the part doesn't contain text it will be base64 encoded.
0832:                        part.setHeader("Content-Transfer-Encoding", "base64");
0833:                        part.addHeader("X-MIME-Autoconverted",
0834:                                "from 8bit to base64 by "
0835:                                        + getMailetContext().getServerInfo());
0836:                    }
0837:                }
0838:            }
0839:
0840:            /**
0841:             * Adds an encoding information to each text mime part. This is a workaround
0842:             * for a javamail 1.3.2 bug: if a message is sent without encoding
0843:             * information a null pointer exception is thrown during the message
0844:             * delivery.
0845:             * 
0846:             * @param part
0847:             * @throws MessagingException
0848:             * @throws IOException
0849:             */
0850:            private void setEncodingIfMissing(MimePart part)
0851:                    throws MessagingException, IOException {
0852:                if (part.isMimeType("text/*")) {
0853:                    String enc = part.getEncoding();
0854:                    if (enc == null)
0855:                        part.setHeader("Content-Transfer-Encoding", "7bit");
0856:                } else if (part.isMimeType("multipart/*")) {
0857:                    Object content = part.getContent();
0858:                    if (content instanceof  MimeMultipart) {
0859:                        MimeMultipart parts = (MimeMultipart) content;
0860:                        int count = parts.getCount();
0861:                        for (int i = 0; i < count; i++) {
0862:                            setEncodingIfMissing((MimePart) parts
0863:                                    .getBodyPart(i));
0864:                        }
0865:                    }
0866:                }
0867:            }
0868:
0869:            /**
0870:             * Insert the method's description here.
0871:             * Creation date: (2/25/00 1:14:18 AM)
0872:             * @param mail org.apache.james.core.MailImpl
0873:             * @param exception javax.mail.MessagingException
0874:             * @param boolean permanent
0875:             * @return boolean Whether the message failed fully and can be deleted
0876:             */
0877:            private boolean failMessage(Mail mail, MessagingException ex,
0878:                    boolean permanent) {
0879:                StringWriter sout = new StringWriter();
0880:                PrintWriter out = new PrintWriter(sout, true);
0881:                if (permanent) {
0882:                    out.print("Permanent");
0883:                } else {
0884:                    out.print("Temporary");
0885:                }
0886:                StringBuffer logBuffer = new StringBuffer(64).append(
0887:                        " exception delivering mail (").append(mail.getName())
0888:                        .append(": ");
0889:                out.print(logBuffer.toString());
0890:                if (isDebug)
0891:                    ex.printStackTrace(out);
0892:                log(sout.toString());
0893:                if (!permanent) {
0894:                    if (!mail.getState().equals(Mail.ERROR)) {
0895:                        mail.setState(Mail.ERROR);
0896:                        mail.setErrorMessage("0");
0897:                        mail.setLastUpdated(new Date());
0898:                    }
0899:                    int retries = Integer.parseInt(mail.getErrorMessage());
0900:                    if (retries < maxRetries) {
0901:                        logBuffer = new StringBuffer(128).append(
0902:                                "Storing message ").append(mail.getName())
0903:                                .append(" into outgoing after ")
0904:                                .append(retries).append(" retries");
0905:                        log(logBuffer.toString());
0906:                        ++retries;
0907:                        mail.setErrorMessage(retries + "");
0908:                        mail.setLastUpdated(new Date());
0909:                        return false;
0910:                    } else {
0911:                        logBuffer = new StringBuffer(128).append(
0912:                                "Bouncing message ").append(mail.getName())
0913:                                .append(" after ").append(retries).append(
0914:                                        " retries");
0915:                        log(logBuffer.toString());
0916:                    }
0917:                }
0918:
0919:                if (mail.getSender() == null) {
0920:                    log("Null Sender: no bounce will be generated for "
0921:                            + mail.getName());
0922:                    return true;
0923:                }
0924:
0925:                if (bounceProcessor != null) {
0926:                    // do the new DSN bounce
0927:                    // setting attributes for DSN mailet
0928:                    mail.setAttribute("delivery-error", ex);
0929:                    mail.setState(bounceProcessor);
0930:                    // re-insert the mail into the spool for getting it passed to the dsn-processor
0931:                    MailetContext mc = getMailetContext();
0932:                    try {
0933:                        mc.sendMail(mail);
0934:                    } catch (MessagingException e) {
0935:                        // we shouldn't get an exception, because the mail was already processed
0936:                        log("Exception re-inserting failed mail: ", e);
0937:                    }
0938:                } else {
0939:                    // do an old style bounce
0940:                    bounce(mail, ex);
0941:                }
0942:                return true;
0943:            }
0944:
0945:            private void bounce(Mail mail, MessagingException ex) {
0946:                StringWriter sout = new StringWriter();
0947:                PrintWriter out = new PrintWriter(sout, true);
0948:                String machine = "[unknown]";
0949:                try {
0950:                    InetAddress me = InetAddress.getLocalHost();
0951:                    machine = me.getHostName();
0952:                } catch (Exception e) {
0953:                    machine = "[address unknown]";
0954:                }
0955:                StringBuffer bounceBuffer = new StringBuffer(128).append(
0956:                        "Hi. This is the James mail server at ")
0957:                        .append(machine).append(".");
0958:                out.println(bounceBuffer.toString());
0959:                out
0960:                        .println("I'm afraid I wasn't able to deliver your message to the following addresses.");
0961:                out
0962:                        .println("This is a permanent error; I've given up. Sorry it didn't work out.  Below");
0963:                out
0964:                        .println("I include the list of recipients and the reason why I was unable to deliver");
0965:                out.println("your message.");
0966:                out.println();
0967:                for (Iterator i = mail.getRecipients().iterator(); i.hasNext();) {
0968:                    out.println(i.next());
0969:                }
0970:                if (ex.getNextException() == null) {
0971:                    out.println(ex.getMessage().trim());
0972:                } else {
0973:                    Exception ex1 = ex.getNextException();
0974:                    if (ex1 instanceof  SendFailedException) {
0975:                        out.println("Remote mail server told me: "
0976:                                + ex1.getMessage().trim());
0977:                    } else if (ex1 instanceof  UnknownHostException) {
0978:                        out.println("Unknown host: " + ex1.getMessage().trim());
0979:                        out
0980:                                .println("This could be a DNS server error, a typo, or a problem with the recipient's mail server.");
0981:                    } else if (ex1 instanceof  ConnectException) {
0982:                        //Already formatted as "Connection timed out: connect"
0983:                        out.println(ex1.getMessage().trim());
0984:                    } else if (ex1 instanceof  SocketException) {
0985:                        out.println("Socket exception: "
0986:                                + ex1.getMessage().trim());
0987:                    } else {
0988:                        out.println(ex1.getMessage().trim());
0989:                    }
0990:                }
0991:                out.println();
0992:
0993:                log("Sending failure message " + mail.getName());
0994:                try {
0995:                    getMailetContext().bounce(mail, sout.toString());
0996:                } catch (MessagingException me) {
0997:                    log("Encountered unexpected messaging exception while bouncing message: "
0998:                            + me.getMessage());
0999:                } catch (Exception e) {
1000:                    log("Encountered unexpected exception while bouncing message: "
1001:                            + e.getMessage());
1002:                }
1003:            }
1004:
1005:            public String getMailetInfo() {
1006:                return "RemoteDelivery Mailet";
1007:            }
1008:
1009:            /**
1010:             * For this message, we take the list of recipients, organize these into distinct
1011:             * servers, and duplicate the message for each of these servers, and then call
1012:             * the deliver (messagecontainer) method for each server-specific
1013:             * messagecontainer ... that will handle storing it in the outgoing queue if needed.
1014:             *
1015:             * @param mail org.apache.mailet.Mail
1016:             */
1017:            public void service(Mail mail) throws MessagingException {
1018:                // Do I want to give the internal key, or the message's Message ID
1019:                if (isDebug) {
1020:                    log("Remotely delivering mail " + mail.getName());
1021:                }
1022:                Collection recipients = mail.getRecipients();
1023:
1024:                if (gatewayServer == null) {
1025:                    // Must first organize the recipients into distinct servers (name made case insensitive)
1026:                    Hashtable targets = new Hashtable();
1027:                    for (Iterator i = recipients.iterator(); i.hasNext();) {
1028:                        MailAddress target = (MailAddress) i.next();
1029:                        String targetServer = target.getHost().toLowerCase(
1030:                                Locale.US);
1031:                        Collection temp = (Collection) targets
1032:                                .get(targetServer);
1033:                        if (temp == null) {
1034:                            temp = new ArrayList();
1035:                            targets.put(targetServer, temp);
1036:                        }
1037:                        temp.add(target);
1038:                    }
1039:
1040:                    //We have the recipients organized into distinct servers... put them into the
1041:                    //delivery store organized like this... this is ultra inefficient I think...
1042:
1043:                    // Store the new message containers, organized by server, in the outgoing mail repository
1044:                    String name = mail.getName();
1045:                    for (Iterator i = targets.keySet().iterator(); i.hasNext();) {
1046:                        String host = (String) i.next();
1047:                        Collection rec = (Collection) targets.get(host);
1048:                        if (isDebug) {
1049:                            StringBuffer logMessageBuffer = new StringBuffer(
1050:                                    128).append("Sending mail to ").append(rec)
1051:                                    .append(" on host ").append(host);
1052:                            log(logMessageBuffer.toString());
1053:                        }
1054:                        mail.setRecipients(rec);
1055:                        StringBuffer nameBuffer = new StringBuffer(128).append(
1056:                                name).append("-to-").append(host);
1057:                        mail.setName(nameBuffer.toString());
1058:                        outgoing.store(mail);
1059:                        //Set it to try to deliver (in a separate thread) immediately (triggered by storage)
1060:                    }
1061:                } else {
1062:                    // Store the mail unaltered for processing by the gateway server(s)
1063:                    if (isDebug) {
1064:                        StringBuffer logMessageBuffer = new StringBuffer(128)
1065:                                .append("Sending mail to ").append(
1066:                                        mail.getRecipients()).append(" via ")
1067:                                .append(gatewayServer);
1068:                        log(logMessageBuffer.toString());
1069:                    }
1070:
1071:                    //Set it to try to deliver (in a separate thread) immediately (triggered by storage)
1072:                    outgoing.store(mail);
1073:                }
1074:                mail.setState(Mail.GHOST);
1075:            }
1076:
1077:            // Need to synchronize to get object monitor for notifyAll()
1078:            public synchronized void destroy() {
1079:                //Mark flag so threads from this mailet stop themselves
1080:                destroyed = true;
1081:                //Wake up all threads from waiting for an accept
1082:                for (Iterator i = deliveryThreads.iterator(); i.hasNext();) {
1083:                    Thread t = (Thread) i.next();
1084:                    t.interrupt();
1085:                }
1086:                notifyAll();
1087:            }
1088:
1089:            /**
1090:             * Handles checking the outgoing spool for new mail and delivering them if
1091:             * there are any
1092:             */
1093:            public void run() {
1094:                /* TODO: CHANGE ME!!! The problem is that we need to wait for James to
1095:                 * finish initializing.  We expect the HELLO_NAME to be put into
1096:                 * the MailetContext, but in the current configuration we get
1097:                 * started before the SMTP Server, which establishes the value.
1098:                 * Since there is no contractual guarantee that there will be a
1099:                 * HELLO_NAME value, we can't just wait for it.  As a temporary
1100:                 * measure, I'm inserting this philosophically unsatisfactory
1101:                 * fix.
1102:                 */
1103:                long stop = System.currentTimeMillis() + 60000;
1104:                while ((getMailetContext().getAttribute(Constants.HELLO_NAME) == null)
1105:                        && stop > System.currentTimeMillis()) {
1106:
1107:                    try {
1108:                        Thread.sleep(1000);
1109:                    } catch (Exception ignored) {
1110:                    } // wait for James to finish initializing
1111:                }
1112:
1113:                //Checks the pool and delivers a mail message
1114:                Properties props = new Properties();
1115:                //Not needed for production environment
1116:                props.put("mail.debug", "false");
1117:                // Reactivated: javamail 1.3.2 should no more have problems with "250 OK"
1118:                // messages (WAS "false": Prevents problems encountered with 250 OK Messages)
1119:                props.put("mail.smtp.ehlo", "true");
1120:                // By setting this property to true the transport is allowed to
1121:                // send 8 bit data to the server (if it supports the 8bitmime extension).
1122:                // 2006/03/01 reverted to false because of a javamail bug converting to 8bit
1123:                // messages created by an inputstream.
1124:                props.setProperty("mail.smtp.allow8bitmime", "false");
1125:                //Sets timeout on going connections
1126:                props.put("mail.smtp.timeout", smtpTimeout + "");
1127:
1128:                props
1129:                        .put("mail.smtp.connectiontimeout", connectionTimeout
1130:                                + "");
1131:                props.put("mail.smtp.sendpartial", String.valueOf(sendPartial));
1132:
1133:                //Set the hostname we'll use as this server
1134:                if (getMailetContext().getAttribute(Constants.HELLO_NAME) != null) {
1135:                    props.put("mail.smtp.localhost", getMailetContext()
1136:                            .getAttribute(Constants.HELLO_NAME));
1137:                } else {
1138:                    String defaultDomain = (String) getMailetContext()
1139:                            .getAttribute(Constants.DEFAULT_DOMAIN);
1140:                    if (defaultDomain != null) {
1141:                        props.put("mail.smtp.localhost", defaultDomain);
1142:                    }
1143:                }
1144:
1145:                if (isBindUsed) {
1146:                    // undocumented JavaMail 1.2 feature, smtp transport will use
1147:                    // our socket factory, which will also set the local address
1148:                    props
1149:                            .put("mail.smtp.socketFactory.class",
1150:                                    "org.apache.james.transport.mailets.RemoteDeliverySocketFactory");
1151:                    // Don't fallback to the standard socket factory on error, do throw an exception
1152:                    props.put("mail.smtp.socketFactory.fallback", "false");
1153:                }
1154:
1155:                if (authUser != null) {
1156:                    props.put("mail.smtp.auth", "true");
1157:                }
1158:
1159:                props.putAll(defprops);
1160:
1161:                Session session = Session.getInstance(props, null);
1162:                try {
1163:                    while (!Thread.interrupted() && !destroyed) {
1164:                        try {
1165:                            Mail mail = (Mail) outgoing.accept(delayFilter);
1166:                            String key = mail.getName();
1167:                            try {
1168:                                if (isDebug) {
1169:                                    StringBuffer logMessageBuffer = new StringBuffer(
1170:                                            128).append(
1171:                                            Thread.currentThread().getName())
1172:                                            .append(" will process mail ")
1173:                                            .append(key);
1174:                                    log(logMessageBuffer.toString());
1175:                                }
1176:                                if (deliver(mail, session)) {
1177:                                    //Message was successfully delivered/fully failed... delete it
1178:                                    ContainerUtil.dispose(mail);
1179:                                    outgoing.remove(key);
1180:                                } else {
1181:                                    //Something happened that will delay delivery.  Store any updates
1182:                                    outgoing.store(mail);
1183:                                    ContainerUtil.dispose(mail);
1184:                                    // This is an update, we have to unlock and notify or this mail
1185:                                    // is kept locked by this thread
1186:                                    outgoing.unlock(key);
1187:                                    // We do not notify because we updated an already existing mail
1188:                                    // and we are now free to handle more mails.
1189:                                    // Furthermore this mail should not be processed now because we
1190:                                    // have a retry time scheduling.
1191:                                }
1192:                                //Clear the object handle to make sure it recycles this object.
1193:                                mail = null;
1194:                            } catch (Exception e) {
1195:                                // Prevent unexpected exceptions from causing looping by removing
1196:                                // message from outgoing.
1197:                                // DO NOT CHNANGE THIS to catch Error!  For example, if there were an OutOfMemory condition
1198:                                // caused because something else in the server was abusing memory, we would not want to
1199:                                // start purging the outgoing spool!
1200:                                ContainerUtil.dispose(mail);
1201:                                outgoing.remove(key);
1202:                                throw e;
1203:                            }
1204:                        } catch (Throwable e) {
1205:                            if (!destroyed)
1206:                                log("Exception caught in RemoteDelivery.run()",
1207:                                        e);
1208:                        }
1209:                    }
1210:                } finally {
1211:                    // Restore the thread state to non-interrupted.
1212:                    Thread.interrupted();
1213:                }
1214:            }
1215:
1216:            /**
1217:             * @param list holding Delay objects
1218:             * @return the total attempts for all delays
1219:             **/
1220:            private int calcTotalAttempts(ArrayList list) {
1221:                int sum = 0;
1222:                Iterator i = list.iterator();
1223:                while (i.hasNext()) {
1224:                    Delay delay = (Delay) i.next();
1225:                    sum += delay.getAttempts();
1226:                }
1227:                return sum;
1228:            }
1229:
1230:            /**
1231:             * This method expands an ArrayList containing Delay objects into an array holding the
1232:             * only delaytime in the order.<p>
1233:             * So if the list has 2 Delay objects the first having attempts=2 and delaytime 4000
1234:             * the second having attempts=1 and delaytime=300000 will be expanded into this array:<p>
1235:             * long[0] = 4000<p>
1236:             * long[1] = 4000<p>
1237:             * long[2] = 300000<p>
1238:             * @param list the list to expand
1239:             * @return the expanded list
1240:             **/
1241:            private long[] expandDelays(ArrayList list) {
1242:                long[] delays = new long[calcTotalAttempts(list)];
1243:                Iterator i = list.iterator();
1244:                int idx = 0;
1245:                while (i.hasNext()) {
1246:                    Delay delay = (Delay) i.next();
1247:                    for (int j = 0; j < delay.getAttempts(); j++) {
1248:                        delays[idx++] = delay.getDelayTime();
1249:                    }
1250:                }
1251:                return delays;
1252:            }
1253:
1254:            /**
1255:             * This method returns, given a retry-count, the next delay time to use.
1256:             * @param retry_count the current retry_count.
1257:             * @return the next delay time to use, given the retry count
1258:             **/
1259:            private long getNextDelay(int retry_count) {
1260:                if (retry_count > delayTimes.length) {
1261:                    return DEFAULT_DELAY_TIME;
1262:                }
1263:                return delayTimes[retry_count - 1];
1264:            }
1265:
1266:            /**
1267:             * This class is used to hold a delay time and its corresponding number
1268:             * of retries.
1269:             **/
1270:            private class Delay {
1271:                private int attempts = 1;
1272:                private long delayTime = DEFAULT_DELAY_TIME;
1273:
1274:                /**
1275:                 * This constructor expects Strings of the form "[attempt\*]delaytime[unit]". <p>
1276:                 * The optional attempt is the number of tries this delay should be used (default = 1)
1277:                 * The unit if present must be one of (msec,sec,minute,hour,day) (default = msec)
1278:                 * The constructor multiplies the delaytime by the relevant multiplier for the unit,
1279:                 * so the delayTime instance variable is always in msec.
1280:                 * @param init_string the string to initialize this Delay object from
1281:                 **/
1282:                public Delay(String init_string) throws MessagingException {
1283:                    String unit = "msec"; //default unit
1284:                    if (delayTimeMatcher.matches(init_string, PATTERN)) {
1285:                        MatchResult res = delayTimeMatcher.getMatch();
1286:                        //the capturing groups will now hold
1287:                        //at 1:  attempts * (if present)
1288:                        //at 2:  delaytime
1289:                        //at 3:  unit (if present)
1290:
1291:                        if (res.group(1) != null && !res.group(1).equals("")) {
1292:                            //we have an attempt *
1293:                            String attempt_match = res.group(1);
1294:                            //strip the * and whitespace
1295:                            attempt_match = attempt_match.substring(0,
1296:                                    attempt_match.length() - 1).trim();
1297:                            attempts = Integer.parseInt(attempt_match);
1298:                        }
1299:
1300:                        delayTime = Long.parseLong(res.group(2));
1301:
1302:                        if (!res.group(3).equals("")) {
1303:                            //we have a unit
1304:                            unit = res.group(3).toLowerCase(Locale.US);
1305:                        }
1306:                    } else {
1307:                        throw new MessagingException(init_string
1308:                                + " does not match " + PATTERN_STRING);
1309:                    }
1310:                    if (MULTIPLIERS.get(unit) != null) {
1311:                        int multiplier = ((Integer) MULTIPLIERS.get(unit))
1312:                                .intValue();
1313:                        delayTime *= multiplier;
1314:                    } else {
1315:                        throw new MessagingException("Unknown unit: " + unit);
1316:                    }
1317:                }
1318:
1319:                /**
1320:                 * This constructor makes a default Delay object, ie. attempts=1 and delayTime=DEFAULT_DELAY_TIME
1321:                 **/
1322:                public Delay() {
1323:                }
1324:
1325:                /**
1326:                 * @return the delayTime for this Delay
1327:                 **/
1328:                public long getDelayTime() {
1329:                    return delayTime;
1330:                }
1331:
1332:                /**
1333:                 * @return the number attempts this Delay should be used.
1334:                 **/
1335:                public int getAttempts() {
1336:                    return attempts;
1337:                }
1338:
1339:                /**
1340:                 * Set the number attempts this Delay should be used.
1341:                 **/
1342:                public void setAttempts(int value) {
1343:                    attempts = value;
1344:                }
1345:
1346:                /**
1347:                 * Pretty prints this Delay 
1348:                 **/
1349:                public String toString() {
1350:                    StringBuffer buf = new StringBuffer(15);
1351:                    buf.append(getAttempts());
1352:                    buf.append('*');
1353:                    buf.append(getDelayTime());
1354:                    buf.append("msec");
1355:                    return buf.toString();
1356:                }
1357:            }
1358:
1359:            /*
1360:             * Returns an Iterator over org.apache.mailet.HostAddress, a
1361:             * specialized subclass of javax.mail.URLName, which provides
1362:             * location information for servers that are specified as mail
1363:             * handlers for the given hostname.  If no host is found, the
1364:             * Iterator returned will be empty and the first call to hasNext()
1365:             * will return false.  The Iterator is a nested iterator: the outer
1366:             * iteration is over each gateway, and the inner iteration is over
1367:             * potentially multiple A records for each gateway.
1368:             *
1369:             * @see org.apache.james.DNSServer#getSMTPHostAddresses(String)
1370:             * @since v2.2.0a16-unstable
1371:             * @param gatewayServers - Collection of host[:port] Strings
1372:             * @return an Iterator over HostAddress instances, sorted by priority
1373:             */
1374:            private Iterator getGatewaySMTPHostAddresses(
1375:                    final Collection gatewayServers) {
1376:                return new Iterator() {
1377:                    private Iterator gateways = gatewayServers.iterator();
1378:                    private Iterator addresses = null;
1379:
1380:                    public boolean hasNext() {
1381:                        /* Make sure that when next() is called, that we can
1382:                         * provide a HostAddress.  This means that we need to
1383:                         * have an inner iterator, and verify that it has
1384:                         * addresses.  We could, for example, run into a
1385:                         * situation where the next gateway didn't have any
1386:                         * valid addresses.
1387:                         */
1388:                        if (!hasNextAddress() && gateways.hasNext()) {
1389:                            do {
1390:                                String server = (String) gateways.next();
1391:                                String port = "25";
1392:
1393:                                int idx = server.indexOf(':');
1394:                                if (idx > 0) {
1395:                                    port = server.substring(idx + 1);
1396:                                    server = server.substring(0, idx);
1397:                                }
1398:
1399:                                final String nextGateway = server;
1400:                                final String nextGatewayPort = port;
1401:                                try {
1402:                                    final InetAddress[] ips = org.apache.james.dnsserver.DNSServer
1403:                                            .getAllByName(nextGateway);
1404:                                    addresses = new Iterator() {
1405:                                        private InetAddress[] ipAddresses = ips;
1406:                                        int i = 0;
1407:
1408:                                        public boolean hasNext() {
1409:                                            return i < ipAddresses.length;
1410:                                        }
1411:
1412:                                        public Object next() {
1413:                                            return new org.apache.mailet.HostAddress(
1414:                                                    nextGateway,
1415:                                                    "smtp://"
1416:                                                            + (ipAddresses[i++])
1417:                                                                    .getHostAddress()
1418:                                                            + ":"
1419:                                                            + nextGatewayPort);
1420:                                        }
1421:
1422:                                        public void remove() {
1423:                                            throw new UnsupportedOperationException(
1424:                                                    "remove not supported by this iterator");
1425:                                        }
1426:                                    };
1427:                                } catch (java.net.UnknownHostException uhe) {
1428:                                    log("Unknown gateway host: "
1429:                                            + uhe.getMessage().trim());
1430:                                    log("This could be a DNS server error or configuration error.");
1431:                                }
1432:                            } while (!hasNextAddress() && gateways.hasNext());
1433:                        }
1434:
1435:                        return hasNextAddress();
1436:                    }
1437:
1438:                    private boolean hasNextAddress() {
1439:                        return addresses != null && addresses.hasNext();
1440:                    }
1441:
1442:                    public Object next() {
1443:                        return (addresses != null) ? addresses.next() : null;
1444:                    }
1445:
1446:                    public void remove() {
1447:                        throw new UnsupportedOperationException(
1448:                                "remove not supported by this iterator");
1449:                    }
1450:                };
1451:            }
1452:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.