Skip to content

Commit

Permalink
JAMES-1877 Introduce a helper for working with MessagingException
Browse files Browse the repository at this point in the history
  • Loading branch information
chibenwa committed Jan 10, 2017
1 parent 2a4936d commit a0ca1bf
Show file tree
Hide file tree
Showing 2 changed files with 200 additions and 96 deletions.
Expand Up @@ -51,6 +51,7 @@
import org.apache.mailet.MailetContext; import org.apache.mailet.MailetContext;
import org.slf4j.Logger; import org.slf4j.Logger;


import com.google.common.base.Optional;
import com.sun.mail.smtp.SMTPTransport; import com.sun.mail.smtp.SMTPTransport;


@SuppressWarnings("deprecation") @SuppressWarnings("deprecation")
Expand Down Expand Up @@ -334,6 +335,7 @@ private ExecutionResult handleSenderFailedException(Mail mail, SendFailedExcepti
Collection<MailAddress> recipients = new ArrayList<MailAddress>(mail.getRecipients()); Collection<MailAddress> recipients = new ArrayList<MailAddress>(mail.getRecipients());


ExecutionResult deleteMessage = ExecutionResult.temporaryFailure(); ExecutionResult deleteMessage = ExecutionResult.temporaryFailure();
EnhancedMessagingException enhancedMessagingException = new EnhancedMessagingException(sfe);


/* /*
* If you send a message that has multiple invalid addresses, you'll * If you send a message that has multiple invalid addresses, you'll
Expand All @@ -357,32 +359,12 @@ private ExecutionResult handleSenderFailedException(Mail mail, SendFailedExcepti
* SMTPSendFailedException introduced in JavaMail 1.3.2, and * SMTPSendFailedException introduced in JavaMail 1.3.2, and
* provides detailed protocol reply code for the operation * provides detailed protocol reply code for the operation
*/ */
try { if (enhancedMessagingException.hasReturnCode()) {
if (sfe.getClass().getName().endsWith(".SMTPSendFailedException")) { if (enhancedMessagingException.isServerError()) {
int returnCode = (Integer) invokeGetter(sfe, "getReturnCode"); deleteMessage = ExecutionResult.permanentFailure(sfe);
// If we got an SMTPSendFailedException, use its RetCode to
// determine default permanent/temporary failure
deleteMessage = ExecutionResult.onFailure(returnCode >= 500 && returnCode <= 599, sfe);
} else { } else {
// Sometimes we'll get a normal SendFailedException with deleteMessage = ExecutionResult.temporaryFailure(sfe);
// nested SMTPAddressFailedException, so use the latter
// RetCode
MessagingException me = sfe;
Exception ne;
while ((ne = me.getNextException()) != null && ne instanceof MessagingException) {
me = (MessagingException) ne;
if (me.getClass().getName().endsWith(".SMTPAddressFailedException")) {
int returnCode = (Integer) invokeGetter(me, "getReturnCode");
deleteMessage = ExecutionResult.onFailure(returnCode >= 500 && returnCode <= 599, sfe);
}
}
} }
} catch (IllegalStateException ise) {
// unexpected exception (not a compatible javamail
// implementation)
} catch (ClassCastException cce) {
// unexpected exception (not a compatible javamail
// implementation)
} }


// log the original set of intended recipients // log the original set of intended recipients
Expand Down Expand Up @@ -432,9 +414,8 @@ private ExecutionResult handleSenderFailedException(Mail mail, SendFailedExcepti
if (configuration.isDebug()) if (configuration.isDebug())
logger.debug("Unsent recipients: " + recipients); logger.debug("Unsent recipients: " + recipients);


if (sfe.getClass().getName().endsWith(".SMTPSendFailedException")) { if (enhancedMessagingException.hasReturnCode()) {
int returnCode = (Integer) invokeGetter(sfe, "getReturnCode"); boolean isPermanent = enhancedMessagingException.isServerError();
boolean isPermanent = returnCode >= 500 && returnCode <= 599;
deleteMessage = ExecutionResult.onFailure(isPermanent, sfe); deleteMessage = ExecutionResult.onFailure(isPermanent, sfe);
logger.debug(messageComposer.composeFailLogMessage(mail, deleteMessage)); logger.debug(messageComposer.composeFailLogMessage(mail, deleteMessage));
} else { } else {
Expand All @@ -458,20 +439,13 @@ private MessagingException handleSendFailException(Mail mail, SendFailedExceptio
} }
} }


/* /*
* SMTPSendFailedException introduced in JavaMail 1.3.2, and * SMTPSendFailedException introduced in JavaMail 1.3.2, and
* provides detailed protocol reply code for the operation * provides detailed protocol reply code for the operation
*/ */
if (sfe.getClass().getName().endsWith(".SMTPSendFailedException")) { EnhancedMessagingException enhancedMessagingException = new EnhancedMessagingException(sfe);
try { if (enhancedMessagingException.isServerError()) {
int returnCode = (Integer) invokeGetter(sfe, "getReturnCode"); throw sfe;
// if 5xx, terminate this delivery attempt by
// re-throwing the exception.
if (returnCode >= 500 && returnCode <= 599)
throw sfe;
} catch (ClassCastException cce) {
} catch (IllegalArgumentException iae) {
}
} }


if (sfe.getValidUnsentAddresses() != null && sfe.getValidUnsentAddresses().length > 0) { if (sfe.getValidUnsentAddresses() != null && sfe.getValidUnsentAddresses().length > 0) {
Expand Down Expand Up @@ -585,65 +559,34 @@ private long getNextDelay(int retry_count) {
return configuration.getDelayTimes().get(retry_count - 1); return configuration.getDelayTimes().get(retry_count - 1);
} }



private Object invokeGetter(Object target, String getter) {
try {
Method getAddress = target.getClass().getMethod(getter);
return getAddress.invoke(target);
} catch (NoSuchMethodException nsme) {
// An SMTPAddressFailedException with no getAddress method.
} catch (IllegalAccessException iae) {
} catch (IllegalArgumentException iae) {
} catch (InvocationTargetException ite) {
// Other issues with getAddress invokation.
}
return new IllegalStateException("Exception invoking " + getter + " on a " + target.getClass() + " object");
}

private void logSendFailedException(SendFailedException sfe) { private void logSendFailedException(SendFailedException sfe) {
if (configuration.isDebug()) { if (configuration.isDebug()) {
MessagingException me = sfe; EnhancedMessagingException enhancedMessagingException = new EnhancedMessagingException(sfe);
if (me.getClass().getName().endsWith(".SMTPSendFailedException")) { if (enhancedMessagingException.hasReturnCode()) {
try { logger.debug("SMTP SEND FAILED:");
String command = (String) invokeGetter(sfe, "getCommand"); logger.debug(sfe.toString());
Integer returnCode = (Integer) invokeGetter(sfe, "getReturnCode"); logger.debug(" Command: " + enhancedMessagingException.computeCommand());
logger.debug("SMTP SEND FAILED:"); logger.debug(" RetCode: " + enhancedMessagingException.getReturnCode());
logger.debug(sfe.toString()); logger.debug(" Response: " + sfe.getMessage());
logger.debug(" Command: " + command);
logger.debug(" RetCode: " + returnCode);
logger.debug(" Response: " + sfe.getMessage());
} catch (IllegalStateException ise) {
// Error invoking the getAddress method
logger.debug("Send failed: " + me.toString());
} catch (ClassCastException cce) {
// The getAddress method returned something different than
// InternetAddress
logger.debug("Send failed: " + me.toString());
}
} else { } else {
logger.debug("Send failed: " + me.toString()); logger.debug("Send failed: " + sfe.toString());
} }
Exception ne; logLevels(sfe);
while ((ne = me.getNextException()) != null && ne instanceof MessagingException) { }
me = (MessagingException) ne; }
if (me.getClass().getName().endsWith(".SMTPAddressFailedException") || me.getClass().getName().endsWith(".SMTPAddressSucceededException")) {
try { private void logLevels(MessagingException me) {
String action = me.getClass().getName().endsWith(".SMTPAddressFailedException") ? "FAILED" : "SUCCEEDED"; Exception ne;
InternetAddress address = (InternetAddress) invokeGetter(me, "getAddress"); while ((ne = me.getNextException()) != null && ne instanceof MessagingException) {
String command = (String) invokeGetter(me, "getCommand"); me = (MessagingException) ne;
Integer returnCode = (Integer) invokeGetter(me, "getReturnCode"); EnhancedMessagingException enhancedMessagingException = new EnhancedMessagingException(me);
logger.debug("ADDRESS " + action + ":"); if (me.getClass().getName().endsWith(".SMTPAddressFailedException") || me.getClass().getName().endsWith(".SMTPAddressSucceededException")) {
logger.debug(me.toString()); logger.debug("ADDRESS " + enhancedMessagingException.computeAction() + ":");
logger.debug(" Address: " + address); logger.debug(me.toString());
logger.debug(" Command: " + command); logger.debug(" Address: " + enhancedMessagingException.computeAddress());
logger.debug(" RetCode: " + returnCode); logger.debug(" Command: " + enhancedMessagingException.computeCommand());
logger.debug(" Response: " + me.getMessage()); logger.debug(" RetCode: " + enhancedMessagingException.getReturnCode());
} catch (IllegalStateException ise) { logger.debug(" Response: " + me.getMessage());
// Error invoking the getAddress method
} catch (ClassCastException cce) {
// A method returned something different than expected
}
}
} }
} }
} }
Expand Down
@@ -0,0 +1,161 @@
/****************************************************************
* Licensed to the Apache Software Foundation (ASF) under one *
* or more contributor license agreements. See the NOTICE file *
* distributed with this work for additional information *
* regarding copyright ownership. The ASF licenses this file *
* to you under the Apache License, Version 2.0 (the *
* "License"); you may not use this file except in compliance *
* with the License. You may obtain a copy of the License at *
* *
* http://www.apache.org/licenses/LICENSE-2.0 *
* *
* Unless required by applicable law or agreed to in writing, *
* software distributed under the License is distributed on an *
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY *
* KIND, either express or implied. See the License for the *
* specific language governing permissions and limitations *
* under the License. *
****************************************************************/

package org.apache.james.transport.mailets.remoteDelivery;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

import javax.mail.MessagingException;
import javax.mail.internet.InternetAddress;

import com.google.common.base.Function;
import com.google.common.base.Optional;
import com.google.common.base.Preconditions;

public class EnhancedMessagingException {

private final MessagingException messagingException;
private final Optional<Integer> returnCode;
private final Optional<Integer> nestedReturnCode;

public EnhancedMessagingException(MessagingException messagingException) {
this.messagingException = messagingException;
this.returnCode = computeReturnCode();
this.nestedReturnCode = computeNestedReturnCode();
}

public boolean hasReturnCode() {
return returnCode.isPresent();
}

public boolean hasNestedReturnCode() {
return nestedReturnCode.isPresent();
}

public boolean isServerError() {
return isServerError(returnCode) || isServerError(nestedReturnCode);
}

private boolean isServerError(Optional<Integer> returnCode) {
return (returnCode.isPresent()
&& returnCode.get() >= 500
&& returnCode.get() <= 599)
|| messageIndicatesServerException();
}

private boolean messageIndicatesServerException() {
return Optional.fromNullable(messagingException.getMessage())
.transform(startWith5())
.or(false);
}

private Function<String, Boolean> startWith5() {
return new Function<String, Boolean>() {
@Override
public Boolean apply(String input) {
return input.startsWith("5");
}
};
}

private Optional<Integer> computeReturnCode() {
if (messagingException.getClass().getName().endsWith(".SMTPSendFailedException")
|| messagingException.getClass().getName().endsWith(".SMTPAddressSucceededException")) {
try {
return Optional.of ((Integer) invokeGetter(messagingException, "getReturnCode"));
} catch (ClassCastException cce) {
} catch (IllegalArgumentException iae) {
} catch (IllegalStateException ise) {
}
}
return Optional.absent();
}

public Optional<String> computeCommand() {
if (hasReturnCode()) {
try {
return Optional.of((String) invokeGetter(messagingException, "getCommand"));
} catch (ClassCastException cce) {
} catch (IllegalArgumentException iae) {
} catch (IllegalStateException ise) {
}
}
return Optional.absent();
}

public Optional<InternetAddress> computeAddress() {
if (hasReturnCode()) {
try {
return Optional.of((InternetAddress) invokeGetter(messagingException, "getAddress"));
} catch (ClassCastException cce) {
} catch (IllegalArgumentException iae) {
} catch (IllegalStateException ise) {
}
}
return Optional.absent();
}

public String computeAction() {
return messagingException.getClass().getName().endsWith(".SMTPAddressFailedException") ? "FAILED" : "SUCCEEDED";
}

public Optional<Integer> getReturnCode() {
return returnCode;
}

private Optional<Integer> computeNestedReturnCode() {
EnhancedMessagingException currentMessagingException = this;
while (true) {
Optional<Integer> returnCode = currentMessagingException.computeReturnCode();
if (returnCode.isPresent()) {
return returnCode;
}
if (currentMessagingException.hasNestedMessagingException()) {
currentMessagingException = currentMessagingException.getNestedMessagingException();
} else {
return Optional.absent();
}
}
}

private boolean hasNestedMessagingException() {
return messagingException.getNextException() != null
&& messagingException.getNextException() instanceof MessagingException;
}

private EnhancedMessagingException getNestedMessagingException() {
Preconditions.checkState(hasNestedMessagingException());
return new EnhancedMessagingException((MessagingException) messagingException.getNextException());
}

private Object invokeGetter(Object target, String getter) {
try {
Method getAddress = target.getClass().getMethod(getter);
return getAddress.invoke(target);
} catch (NoSuchMethodException nsme) {
// An SMTPAddressFailedException with no getAddress method.
} catch (IllegalAccessException iae) {
} catch (IllegalArgumentException iae) {
} catch (InvocationTargetException ite) {
// Other issues with getAddress invokation.
}
return new IllegalStateException("Exception invoking " + getter + " on a " + target.getClass() + " object");
}
}

0 comments on commit a0ca1bf

Please sign in to comment.