in geronimo-mail_2.1_impl/geronimo-mail_2.1_provider/src/main/java/org/apache/geronimo/mail/transport/smtp/SMTPTransport.java [176:416]
public void sendMessage(Message message, Address[] addresses) throws MessagingException {
if (!isConnected()) {
throw new IllegalStateException("Not connected");
}
// don't bother me w/ null messages or no addreses
if (message == null) {
throw new MessagingException("Null message");
}
// SMTP only handles instances of MimeMessage, not the more general
// message case.
if (!(message instanceof MimeMessage)) {
throw new MessagingException("SMTP can only send MimeMessages");
}
// we must have a message list.
if (addresses == null || addresses.length == 0) {
throw new MessagingException("Null or empty address array");
}
boolean reportSuccess = getReportSuccess();
// now see how we're configured for this send operation.
boolean partialSends = false;
// this can be attached directly to the message.
if (message instanceof SMTPMessage) {
partialSends = ((SMTPMessage) message).getSendPartial();
}
// if still false on the message object, check for a property
// version also
if (!partialSends) {
partialSends = props.getBooleanProperty(MAIL_SMTP_SENDPARTIAL, false);
}
boolean haveGroup = false;
// enforce the requirement that all of the targets are InternetAddress
// instances.
for (int i = 0; i < addresses.length; i++) {
if (addresses[i] instanceof InternetAddress) {
// and while we're here, see if we have a groups in the address
// list. If we do, then
// we're going to need to expand these before sending.
if (((InternetAddress) addresses[i]).isGroup()) {
haveGroup = true;
}
} else {
throw new MessagingException("Illegal InternetAddress " + addresses[i]);
}
}
// did we find a group? Time to expand this into our full target list.
if (haveGroup) {
addresses = expandGroups(addresses);
}
SendStatus[] stats = new SendStatus[addresses.length];
// create our lists for notification and exception reporting.
Address[] sent = null;
Address[] unsent = null;
Address[] invalid = null;
try {
// send sender first. If this failed, send a failure notice of the
// event, using the full list of
// addresses as the unsent, and nothing for the rest.
if (!connection.sendMailFrom(message)) {
unsent = addresses;
sent = new Address[0];
invalid = new Address[0];
// notify of the error.
notifyTransportListeners(TransportEvent.MESSAGE_NOT_DELIVERED, sent, unsent, invalid, message);
// include the reponse information here.
SMTPReply last = connection.getLastServerResponse();
// now send an "uber-exception" to indicate the failure.
throw new SMTPSendFailedException("MAIL FROM", last.getCode(), last.getMessage(), null, sent, unsent,
invalid);
}
// get the additional notification status, if available
String dsn = getDeliveryStatusNotification(message);
// we need to know about any failures once we've gone through the
// complete list, so keep a
// failure flag.
boolean sendFailure = false;
// event notifcation requires we send lists of successes and
// failures broken down by category.
// The categories are:
//
// 1) addresses successfully processed.
// 2) addresses deemed valid, but had a processing failure that
// prevented sending.
// 3) addressed deemed invalid (basically all other processing
// failures).
ArrayList sentAddresses = new ArrayList();
ArrayList unsentAddresses = new ArrayList();
ArrayList invalidAddresses = new ArrayList();
// Now we add a MAIL TO record for each recipient. At this point, we
// just collect
for (int i = 0; i < addresses.length; i++) {
InternetAddress target = (InternetAddress) addresses[i];
// write out the record now.
SendStatus status = connection.sendRcptTo(target, dsn);
stats[i] = status;
switch (status.getStatus()) {
// successfully sent
case SendStatus.SUCCESS:
sentAddresses.add(target);
break;
// we have an invalid address of some sort, or a general sending
// error (which we'll
// interpret as due to an invalid address.
case SendStatus.INVALID_ADDRESS:
case SendStatus.GENERAL_ERROR:
sendFailure = true;
invalidAddresses.add(target);
break;
// good address, but this was a send failure.
case SendStatus.SEND_FAILURE:
sendFailure = true;
unsentAddresses.add(target);
break;
}
}
// if we had a send failure, then we need to check if we allow
// partial sends. If not allowed,
// we abort the send operation now.
if (sendFailure) {
// if we're not allowing partial successes or we've failed on
// all of the addresses, it's
// time to abort.
if (!partialSends || sentAddresses.isEmpty()) {
// we send along the valid and invalid address lists on the
// notifications and
// exceptions.
// however, since we're aborting the entire send, the
// successes need to become
// members of the failure list.
unsentAddresses.addAll(sentAddresses);
// this one is empty.
sent = new Address[0];
unsent = (Address[]) unsentAddresses.toArray(new Address[0]);
invalid = (Address[]) invalidAddresses.toArray(new Address[0]);
// go reset our connection so we can process additional
// sends.
connection.resetConnection();
// get a list of chained exceptions for all of the failures.
MessagingException failures = generateExceptionChain(stats, false);
// now send an "uber-exception" to indicate the failure.
throw new SMTPSendFailedException("MAIL TO", 0, "Invalid Address", failures, sent, unsent, invalid);
}
}
try {
// try to send the data
connection.sendData((MimeMessage)message);
} catch (MessagingException e) {
// If there's an error at this point, this is a complete
// delivery failure.
// we send along the valid and invalid address lists on the
// notifications and
// exceptions.
// however, since we're aborting the entire send, the successes
// need to become
// members of the failure list.
unsentAddresses.addAll(sentAddresses);
// this one is empty.
sent = new Address[0];
unsent = (Address[]) unsentAddresses.toArray(new Address[0]);
invalid = (Address[]) invalidAddresses.toArray(new Address[0]);
// notify of the error.
notifyTransportListeners(TransportEvent.MESSAGE_NOT_DELIVERED, sent, unsent, invalid, message);
// send a send failure exception.
throw new SMTPSendFailedException("DATA", 0, "Send failure", e, sent, unsent, invalid);
}
// create our lists for notification and exception reporting from
// this point on.
sent = (Address[]) sentAddresses.toArray(new Address[0]);
unsent = (Address[]) unsentAddresses.toArray(new Address[0]);
invalid = (Address[]) invalidAddresses.toArray(new Address[0]);
// if sendFailure is true, we had an error during the address phase,
// but we had permission to
// process this as a partial send operation. Now that the data has
// been sent ok, it's time to
// report the partial failure.
if (sendFailure) {
// notify our listeners of the partial delivery.
notifyTransportListeners(TransportEvent.MESSAGE_PARTIALLY_DELIVERED, sent, unsent, invalid, message);
// get a list of chained exceptions for all of the failures (and
// the successes, if reportSuccess has been
// turned on).
MessagingException failures = generateExceptionChain(stats, reportSuccess);
// now send an "uber-exception" to indicate the failure.
throw new SMTPSendFailedException("MAIL TO", 0, "Invalid Address", failures, sent, unsent, invalid);
}
// notify our listeners of successful delivery.
notifyTransportListeners(TransportEvent.MESSAGE_DELIVERED, sent, unsent, invalid, message);
// we've not had any failures, but we've been asked to report
// success as an exception. Do
// this now.
if (reportSuccess) {
// generate the chain of success exceptions (we already know
// there are no failure ones to report).
MessagingException successes = generateExceptionChain(stats, reportSuccess);
if (successes != null) {
throw successes;
}
}
} catch (SMTPSendFailedException e) {
// if this is a send failure, we've already handled
// notifications....just rethrow it.
throw e;
} catch (MessagingException e) {
// notify of the error.
notifyTransportListeners(TransportEvent.MESSAGE_NOT_DELIVERED, sent, unsent, invalid, message);
throw e;
}
}