JavaMail – Random NoSuchProviderException errors sending SMTP mail

The appearance of random NoSuchProviderException errors when testing a new application release turns out to be caused by a new Maven dependency’s dependency.

One of the big advantages of using Maven to manage your Java builds is that it frees you from having to keep track of all your application’s dependencies and their dependencies and so on and so on. Of course, one of the big drawbacks of using Maven to manage your Java builds is that you are no longer keeping track of your application’s dependencies and their dependencies and so on and so on.

Hoots mon, there’s a moose loose aboot this hoose!

While testing a new application release recently I found that some JavaMail 1.4.3 client code, unchanged for many-a-year, had started randomly spewing NoSuchProviderException errors:-

javax.mail.NoSuchProviderException: No provider for address type rfc822
        at javax.mail.Session.getTransport(Session.java:374)
        at javax.mail.Transport.send(Transport.java:67)
        at javax.mail.Transport.send(Transport.java:48)

Sometimes it would start fine and sometimes most of the email threads were throwing these errors every time an outbound message hit them.

Random problems like these are often down to thread-safety issues and sure enough my JavaMail client code does use multiple threads to send messages. But it hadn’t been changed for ages.

Something else must be going on.

O ye’ll take the high road

Thread-safety issues are by their very nature unpredictable and therefore don’t lend themselves very well to deterministic unit testing. Even in these cases though a simple unit test case still has value because it strips the problem down to the bare minimum. We might not be able to reproduce the problem every time, but when we can reproduce it we have the smallest possible number of causing factors to consider.

For a case like this we can strip down our SMTP message sending code to the bare minimum:-

public abstract class AbstractEmailer {
    protected String hostName = null;
    protected int portNumber = -1;

    public AbstractEmailer(String hostName, int portNumber) {
        this.hostName = hostName; this.portNumber = portNumber;
    }

    public abstract void sendMessage(String fromAddress, 
            String toAddress, String title, String content)
                    throws MessagingException;
}

public class SimpleEmailer extends AbstractEmailer {
    public SimpleEmailer(String hostName, int portNumber) {
        super(hostName, portNumber);
    }

    @Override
    public void sendMessage(String fromAddress, String toAddress,
            String title, String content) throws MessagingException {
        Properties mailProps = new Properties();
        mailProps.put("mail.smtp.host", hostName);
        mailProps.put("mail.smtp.port", Integer.toString(portNumber));
        mailProps.put("mail.transport.protocol", "smtp");

        Session mailSession = Session.getDefaultInstance(mailProps);
        InternetAddress senderAddress =
                new InternetAddress(fromAddress);
        InternetAddress recipientAddress =
                new InternetAddress(toAddress);

        MimeMessage theMessage = new MimeMessage(mailSession);
        theMessage.setFrom(senderAddress);
        theMessage.setSubject(title);
        theMessage.addRecipient(Message.RecipientType.TO,
                recipientAddress);

        MimeBodyPart bodyText = new MimeBodyPart();
        bodyText.setContent(content, "text/plain");
        MimeMultipart messageContent = new MimeMultipart();
        messageContent.addBodyPart(bodyText);
        theMessage.setContent(messageContent);
        Transport.send(theMessage);
    }
}

We can now exercise this a few times with a multi-threaded JUnit test. As this sort of test scenario isn’t an ideal fit for unit testing it isn’t surprising that JUnit doesn’t offer a lot of out-of-the-box support for multi-threaded testing, but we can use an ActiveTestSuite to exercise our class via multiple concurrent threads. There are plenty of open-source SMTP test frameworks available too and for our test we’ll use Dumbster, remembering to override its own JavaMail dependencies to ensure our application is running the version we want to test:-

<dependency>
    <groupId>dumbster</groupId>
    <artifactId>dumbster</artifactId>
    <version>1.6</version>
    <scope>test</scope>
    <exclusions>
        <exclusion>
            <groupId>javax.activation</groupId>
            <artifactId>activation</artifactId>
        </exclusion>
        <exclusion>
            <groupId>javax.mail</groupId>
            <artifactId>mail</artifactId>
        </exclusion>
    </exclusions>
</dependency>

Our unit test has a simple method which sends a message and then checks that the message was received by the stubbed SMTP server:-

public void testSendAnEmail() throws Exception {
    String messageId = UUID.randomUUID().toString();
    SimpleEmailer cut = new SimpleEmailer("127.0.0.1", 1234);
    cut.sendMessage("sender@devsumo.com", "recipient@devsumo.com",
            messageId, "A test message");

    Iterator<SmtpMessage> emails = smtpServer.getReceivedEmail();
    while(emails.hasNext()) {
        if(messageId.equals(emails.next().getHeaderValue("Subject"))) {
            return;
        }
    }
    fail("Sent email not found on SMTP server.");
}

To run it we’ll create an ActiveTestSuite which fires off a set number of concurrent tests of this method. Using JUnit 4 we’ll need to annotate our test class with @RunWith(AllTests.class) to get this picked up:-

public static TestSuite suite() {
    ActiveTestSuite suite = new ActiveTestSuite();
    for(int i = 0; 5 > i; i++) {
        suite.addTest(TestSuite.createTest(
                SimpleEmailerTest.class, "testSendAnEmail"));
    }
    smtpServer = SimpleSmtpServer.start(1234);
    return suite;
}

With plain old JavaMail 1.4.3 and SMTP 1.4.5 this runs fine every time:-

<dependency>
    <groupId>javax.mail</groupId>
    <artifactId>mailapi</artifactId>
    <version>1.4.3</version>
</dependency>
<dependency>
    <groupId>com.sun.mail</groupId>
    <artifactId>smtp</artifactId>
    <version>1.4.5</version>
</dependency>

Adding in our main application’s other dependencies one-by-one though:-

<dependency>
    <groupId>org.apache.cxf</groupId>
    <artifactId>cxf-rt-core</artifactId>
    <version>2.7.5</version>
</dependency>

Soon re-creates our problem:-

javax.mail.NoSuchProviderException: No provider for address type rfc822
    at javax.mail.Session.getTransport(Session.java:374)
    at javax.mail.Transport.send(Transport.java:67)
    at javax.mail.Transport.send(Transport.java:48)

And a look at the changes in our aggregate dependencies reveals a very likely candidate:-

CXF core dependencies showin in Eclipse

 

And I’ll take the low

There are less formulaic (i.e. lazier) ways to find problems like these too. If your application is version controlled or if you keep copies of previous working builds, it’s easy to compare your POM files and eliminate your new dependencies one-by-one.

And in this case, as the problem exhibits itself in the JavaMail Transport class, an Eclipse search for this class shows me I’ve now got two implementations in my classpath:-

Two Transport class instances on my classpath

This gives us a pretty clear indication of a potential problem and a first triage-point to attack.

And I’ll be in Scotland afore ye

So, we’ve identified that the inclusion of Apache Geronimo’s JavaMail 1.4 JAR as a result of starting to use Apache CXF is the cause of our random NoSuchProviderException errors. Finding the cause of a problem is, of course, only half the job of finding a solution to it.

If it’s a dependency we can’t do without we may need to find a later version of our primary dependency, switch our other dependencies to match it, or rework our code to cope with the change.

In this case my new application release is using CXF but isn’t using any email functionality through it, and since we’ve got a JavaMail spec as part of our build anyway, excluding this dependency and re-testing seems an appropriate way to go:-

<dependency>
    <groupId>org.apache.cxf</groupId>
    <artifactId>cxf-rt-core</artifactId>
    <version>2.7.5</version>
    <exclusions>
        <exclusion>
            <groupId>org.apache.geronimo.specs</groupId>
            <artifactId>geronimo-javamail_1.4_spec</artifactId>
       </exclusion>
    </exclusions>
</dependency>

One response to “JavaMail – Random NoSuchProviderException errors sending SMTP mail

  1. I knew I was looking for classpath issue but I hit a brick wall once I thought I had removed all other references to javamail…… then I read your post about sneaky little geronimo.

    A great help, thank you!

Leave a Reply

Your email address will not be published. Required fields are marked *