noel 2003/02/20 18:22:21
Modified: src/java/org/apache/james/transport/mailets Tag:
branch_2_1_fcs RemoteDelivery.java
Log:
(1) Fixed error where a connection error could cause discarded outgoing messages.
(2) Fixed handling of SendFailedException for temporary/permanent exceptions. This
should also be the code necessary to support mail.smtp.sendpartial. Added some log
statements related to the recipient sets to aid in debugging.
Revision Changes Path
No revision
No revision
1.33.4.5 +178 -125
jakarta-james/src/java/org/apache/james/transport/mailets/RemoteDelivery.java
Index: RemoteDelivery.java
===================================================================
RCS file:
/home/cvs/jakarta-james/src/java/org/apache/james/transport/mailets/RemoteDelivery.java,v
retrieving revision 1.33.4.4
retrieving revision 1.33.4.5
diff -u -r1.33.4.4 -r1.33.4.5
--- RemoteDelivery.java 20 Feb 2003 07:37:48 -0000 1.33.4.4
+++ RemoteDelivery.java 21 Feb 2003 02:22:21 -0000 1.33.4.5
@@ -50,9 +50,6 @@
*
* as well as other places.
*
- * @author Serge Knystautas <[EMAIL PROTECTED]>
- * @author Federico Barbieri <[EMAIL PROTECTED]>
- *
* This is $Revision$
*/
public class RemoteDelivery extends GenericMailet implements Runnable {
@@ -66,6 +63,8 @@
private long delayTime = 21600000; // default is 6*60*60*1000 millis (6 hours)
private int maxRetries = 5; // default number of retries
private long smtpTimeout = 600000; //default number of ms to timeout on smtp
delivery
+ private boolean sendPartial = false; // If false then ANY address errors will
cause the transmission to fail
+ private int connectionTimeout = 60000; // The amount of time JavaMail will
wait before giving up on a socket connect()
private int deliveryThreadCount = 1; // default number of delivery threads
private String gatewayServer = null; // the server to send all email to
private String gatewayPort = null; //the port of the gateway server to send
all email to
@@ -99,8 +98,19 @@
} catch (Exception e) {
log("Invalid timeout setting: " + getInitParameter("timeout"));
}
+
+ try {
+ if (getInitParameter("connectiontimeout") != null) {
+ connectionTimeout =
Integer.parseInt(getInitParameter("connectiontimeout"));
+ }
+ } catch (Exception e) {
+ log("Invalid timeout setting: " + getInitParameter("timeout"));
+ }
+ sendPartial = (getInitParameter("sendpartial") == null) ? false : new
Boolean(getInitParameter("sendpartial")).booleanValue();
+
gatewayServer = getInitParameter("gateway");
gatewayPort = getInitParameter("gatewayPort");
+
ComponentManager compMgr =
(ComponentManager)getMailetContext().getAttribute(Constants.AVALON_COMPONENT_MANAGER);
String outgoingPath = getInitParameter("outgoing");
if (outgoingPath == null) {
@@ -146,8 +156,8 @@
* throw an exception.
*
* Creation date: (2/24/00 11:25:00 PM)
- * @param Mail org.apache.mailet.Mail
- * @param Session javax.mail.Session
+ * @param mail org.apache.james.core.MailImpl
+ * @param session javax.mail.Session
* @return boolean Whether the delivery was successful and the message can be
deleted
*/
private boolean deliver(MailImpl mail, Session session) {
@@ -166,6 +176,11 @@
addr[j] = rcpt.toInternetAddress();
}
+ if (addr.length <= 0) {
+ log("No recipients specified... not sure how this could have
happened.");
+ return true;
+ }
+
//Figure out which servers to try to send to. This collection
// will hold all the possible target servers
Collection targetServers = null;
@@ -179,9 +194,9 @@
log("No mail server found for: " + host);
StringBuffer exceptionBuffer =
new StringBuffer(128)
- .append("There are no DNS entries for the hostname ")
- .append(host)
- .append(". I cannot determine where to send this
message.");
+ .append("There are no DNS entries for the hostname ")
+ .append(host)
+ .append(". I cannot determine where to send this
message.");
return failMessage(mail, new
MessagingException(exceptionBuffer.toString()), false);
}
} else {
@@ -191,102 +206,109 @@
MessagingException lastError = null;
- if (addr.length > 0) {
- Iterator i = targetServers.iterator();
- while ( i.hasNext()) {
- try {
- String outgoingMailServer = i.next().toString ();
- StringBuffer logMessageBuffer =
- new StringBuffer(256)
- .append("Attempting delivery of ")
- .append(mail.getName())
- .append(" to host ")
- .append(outgoingMailServer)
- .append(" to addresses ")
- .append(Arrays.asList(addr));
- log(logMessageBuffer.toString());
- URLName urlname = new URLName("smtp://" +
outgoingMailServer);
-
- Properties props = session.getProperties();
- if (mail.getSender() == null) {
- props.put("mail.smtp.from", "<>");
- } else {
- String sender = mail.getSender().toString();
- props.put("mail.smtp.from", sender);
- }
+ Iterator i = targetServers.iterator();
+ while ( i.hasNext()) {
+ try {
+ String outgoingMailServer = i.next().toString ();
+ StringBuffer logMessageBuffer =
+ new StringBuffer(256)
+ .append("Attempting delivery of ")
+ .append(mail.getName())
+ .append(" to host ")
+ .append(outgoingMailServer)
+ .append(" to addresses ")
+ .append(Arrays.asList(addr));
+ log(logMessageBuffer.toString());
+ URLName urlname = new URLName("smtp://" + outgoingMailServer);
+
+ Properties props = session.getProperties();
+ if (mail.getSender() == null) {
+ props.put("mail.smtp.from", "<>");
+ } else {
+ String sender = mail.getSender().toString();
+ props.put("mail.smtp.from", sender);
+ }
- //Many of these properties are only in later JavaMail
versions
- //"mail.smtp.ehlo" //default true
- //"mail.smtp.auth" //default false
- //"mail.smtp.dsn.ret" //default to nothing... appended as
RET= after MAIL FROM line.
- //"mail.smtp.dsn.notify" //default to nothing...appended as
NOTIFY= after RCPT TO line.
+ //Many of these properties are only in later JavaMail versions
+ //"mail.smtp.ehlo" //default true
+ //"mail.smtp.auth" //default false
+ //"mail.smtp.dsn.ret" //default to nothing... appended as RET=
after MAIL FROM line.
+ //"mail.smtp.dsn.notify" //default to nothing...appended as
NOTIFY= after RCPT TO line.
- Transport transport = null;
+ Transport transport = null;
+ try {
+ transport = session.getTransport(urlname);
try {
- transport = session.getTransport(urlname);
- try {
- transport.connect();
- } catch (MessagingException me) {
- // Any error on connect should cause the mailet to
attempt
- // to connect to the next SMTP server associated
with this MX record,
- // assuming the number of retries hasn't been
exceeded.
- if (failMessage(mail, me, false)) {
- return true;
- } else {
- continue;
- }
- }
- transport.sendMessage(message, addr);
- } finally {
- if (transport != null) {
- transport.close();
- transport = null;
+ transport.connect();
+ } catch (MessagingException me) {
+ // Any error on connect should cause the mailet to
attempt
+ // to connect to the next SMTP server associated with
this MX record,
+ // assuming the number of retries hasn't been exceeded.
+ if (failMessage(mail, me, false)) {
+ return true;
+ } else {
+ continue;
}
}
- logMessageBuffer =
- new StringBuffer(256)
- .append("Mail (")
- .append(mail.getName())
- .append(") sent successfully to ")
- .append(outgoingMailServer);
- log(logMessageBuffer.toString());
- return true;
- } catch (MessagingException me) {
- //MessagingException are horribly difficult to figure out
what actually happened.
- StringBuffer exceptionBuffer =
- new StringBuffer(256)
- .append("Exception delivering message (")
- .append(mail.getName())
- .append(") - ")
- .append(me.getMessage());
- log(exceptionBuffer.toString());
- if ((me.getNextException() != null) &&
- (me.getNextException() instanceof java.io.IOException))
{
- //This is more than likely a temporary failure
-
- // If it's an IO exception with no nested exception,
it's probably
- // some socket or weird I/O related problem.
- lastError = me;
- continue;
+ transport.sendMessage(message, addr);
+ } finally {
+ if (transport != null) {
+ transport.close();
+ transport = null;
}
- // This was not a connection or I/O error particular to one
- // SMTP server of an MX set. Instead, it is almost
certainly
- // a protocol level error. In this case we assume that this
- // is an error we'd encounter with any of the SMTP servers
- // associated with this MX record, and we pass the exception
- // to the code in the outer block that determines its
severity.
- throw me;
}
- } // end while
- //If we encountered an exception while looping through, send the
last exception we got
- if (lastError != null) {
- throw lastError;
+ logMessageBuffer =
+ new StringBuffer(256)
+ .append("Mail (")
+ .append(mail.getName())
+ .append(") sent successfully to ")
+ .append(outgoingMailServer);
+ log(logMessageBuffer.toString());
+ return true;
+ } catch (MessagingException me) {
+ //MessagingException are horribly difficult to figure out what
actually happened.
+ StringBuffer exceptionBuffer =
+ new StringBuffer(256)
+ .append("Exception delivering message (")
+ .append(mail.getName())
+ .append(") - ")
+ .append(me.getMessage());
+ log(exceptionBuffer.toString());
+ if ((me.getNextException() != null) &&
+ (me.getNextException() instanceof java.io.IOException)) {
+ //This is more than likely a temporary failure
+
+ // If it's an IO exception with no nested exception, it's
probably
+ // some socket or weird I/O related problem.
+ lastError = me;
+ continue;
+ }
+ // This was not a connection or I/O error particular to one
+ // SMTP server of an MX set. Instead, it is almost certainly
+ // a protocol level error. In this case we assume that this
+ // is an error we'd encounter with any of the SMTP servers
+ // associated with this MX record, and we pass the exception
+ // to the code in the outer block that determines its severity.
+ throw me;
}
- } else {
- log("No recipients specified... not sure how this could have
happened.");
+ } // end while
+ //If we encountered an exception while looping through,
+ //throw the last MessagingException we caught. We only
+ //do this if we were unable to send the message to any
+ //server. If sending eventually succeeded, we exit
+ //deliver() though the return at the end of the try
+ //block.
+ if (lastError != null) {
+ throw lastError;
}
} catch (SendFailedException sfe) {
+ boolean deleteMessage = false;
+ Collection recipients = mail.getRecipients();
+
//Would like to log all the types of email addresses
+ if (isDebug) log("Recipients: " + recipients);
+
+ /*
if (sfe.getValidSentAddresses() != null) {
Address[] validSent = sfe.getValidSentAddresses();
Collection recipients = mail.getRecipients();
@@ -301,10 +323,12 @@
}
}
}
- // The rest of the recipients failed for one reason or another.
- // let failMessage handle it -- 5xx codes are permanent, others
temporary.
+ */
/*
+ * The rest of the recipients failed for one reason or
+ * another.
+ *
* SendFailedException actually handles this for us. For
* example, if you send a message that has multiple invalid
* addresses, you'll get a top-level SendFailedException
@@ -314,56 +338,82 @@
* the content of the nested exceptions is implementation
* dependent.]
*
- * For an immediate fix, we will simply go one level down.
- * THIS IS ONLY TEMPORARY.
- *
- * Proposal:
- *
- * By the time we get here, we've removed the valid sent
- * addresses from the message (see the code right above).
- *
* sfe.getInvalidAddresses() should be considered permanent.
* sfe.getValidUnsentAddresses() should be considered temporary.
*
* JavaMail v1.3 properly populates those collections based
* upon the 4xx and 5xx response codes.
*
- * failMessage could re-spool for ValidUnsetAddresses, and
- * bounce InvalidAddresses.
- *
*/
- //Assume it is a permanent exception, or prove ourselves otherwise
- boolean permanent = true;
- switch (sfe.getMessage().charAt(0)) {
- case '4':
- permanent = false;
- break;
- case '5':
- permanent = true;
- break;
- default:
- Exception nested = sfe.getNextException();
- if (nested != null) {
- permanent = ('5' == nested.getMessage().charAt(0));
+
+ if (sfe.getInvalidAddresses() != null) {
+ Address[] address = sfe.getInvalidAddresses();
+ if (address.length > 0) {
+ recipients.clear();
+ for (int i = 0; i < address.length; i++) {
+ try {
+ recipients.add(new MailAddress(address[i].toString()));
+ } catch (ParseException pe) {
+ // this should never happen ... we should have
+ // caught malformed addresses long before we
+ // got to this code.
+ log("Can't parse invalid address: " + pe.getMessage());
+ }
}
+ if (isDebug) log("Invalid recipients: " + recipients);
+ deleteMessage = failMessage(mail, sfe, true);
+ }
}
- return failMessage(mail, sfe, permanent);
+
+ if (sfe.getValidUnsentAddresses() != null) {
+ Address[] address = sfe.getValidUnsentAddresses();
+ if (address.length > 0) {
+ recipients.clear();
+ for (int i = 0; i < address.length; i++) {
+ try {
+ recipients.add(new MailAddress(address[i].toString()));
+ } catch (ParseException pe) {
+ // this should never happen ... we should have
+ // caught malformed addresses long before we
+ // got to this code.
+ log("Can't parse unsent address: " + pe.getMessage());
+ }
+ }
+ if (isDebug) log("Unsent recipients: " + recipients);
+ deleteMessage = failMessage(mail, sfe, false);
+ }
+ }
+
+ return deleteMessage;
} catch (MessagingException ex) {
// We should do a better job checking this... if the failure is a
general
// connect exception, this is less descriptive than more specific SMTP
command
// failure... have to lookup and see what are the various Exception
// possibilities
- //Unable to deliver message after numerous tries... fail accordingly
+ // Unable to deliver message after numerous tries... fail accordingly
+
+ // We check whether this is a 5xx error message, which
+ // indicates a permanent failure (like account doesn't exist
+ // or mailbox is full or domain is setup wrong).
+ // We fail permanently if this was a 5xx error
return failMessage(mail, ex, ('5' == ex.getMessage().charAt(0)));
}
- return true;
+
+ /* If we get here, we've exhausted the loop of servers without
+ * sending the message or throwing an exception. One case
+ * where this might happen is if we get a MessagingException on
+ * each transport.connect(), e.g., if there is only one server
+ * and we get a connect exception. Return FALSE to keep run()
+ * from deleting the message.
+ */
+ return false;
}
/**
* Insert the method's description here.
* Creation date: (2/25/00 1:14:18 AM)
- * @param mail org.apache.mailet.MailImpl
+ * @param mail org.apache.james.core.MailImpl
* @param exception java.lang.Exception
* @param boolean permanent
* @return boolean Whether the message failed fully and can be deleted
@@ -485,7 +535,6 @@
* messagecontainer ... that will handle storing it in the outgoing queue if
needed.
*
* @param mail org.apache.mailet.Mail
- * @return org.apache.mailet.MessageContainer
*/
public void service(Mail genericmail) throws AddressException {
MailImpl mail = (MailImpl)genericmail;
@@ -594,6 +643,10 @@
props.put("mail.smtp.ehlo", "false");
//Sets timeout on going connections
props.put("mail.smtp.timeout", smtpTimeout + "");
+
+ //props.put("mail.smtp.connectiontimeout", connectionTimeout + "");
+ //props.put("mail.smtp.sendpartial",String.valueOf(sendPartial));
+
//Set the hostname we'll use as this server
if (getMailetContext().getAttribute(Constants.HELLO_NAME) != null) {
props.put("mail.smtp.localhost", (String)
getMailetContext().getAttribute(Constants.HELLO_NAME));
---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]