2
votes

I am trying to use Play Framework to send out email. In applications.conf, when I set smtp.mock=true , it works perfectly well:

[info] application - HTML: Welcome to Play20StartApp. <br> Click on this link : http://localhost:9000/confirm/d77ea256-0d2e-4df5-b66e-e034159f8042 to confirm your email.<br><br>--<br>null
[debug] application - Mail sent - SMTP:smtp.google.com:465 SSL:yes user:[email protected] password:password

However, when I comment smtp.mock=true and attempt to send real emails, I get this error:

[debug] application - Mail.sendMail: Mail will be sent to [email protected]
[ERROR] [09/26/2013 03:46:05.014] [application-akka.actor.default-dispatcher-2] [TaskInvocation] From address required
org.apache.commons.mail.EmailException: From address required

I have set smtp.user to a proper value, i.e. [email protected] . Any idea what causes this error?

Troubleshooting steps

  1. The emails used are real emails, for the purpose of posting, they are anonymized. Likewise, a real domain name is used
  2. Tested with a working local mail server (exim), as well as directly sending through Gmail (smtp.google.com) and Google Apps (aspmx.l.google.com) servers. These settings have been verified using mail clients.
  3. The Java code snippet below works perfectly,

    import java.util.; import javax.mail.; import javax.mail.internet.; import javax.activation.;

    public class SendEmail
    {
        public static void main(String [] args)
        {
            String to = "[email protected]";
            String from = "[email protected]";
            String host = "localhost";
    
            Properties properties = System.getProperties();
    
            // Setup mail server
            properties.setProperty("mail.smtp.host", host);
    
            // Get the default Session object.
            Session session = Session.getDefaultInstance(properties);
    
            try{
                MimeMessage message = new MimeMessage(session);
    
                // Set From: header field of the header.
                message.setFrom(new InternetAddress(from));
    
                message.addRecipient(Message.RecipientType.TO,
                        new InternetAddress(to));
                message.setSubject("Subject of email");
                message.setText("This is actual message");
    
                Transport.send(message);
                System.out.println("Sent message successfully....");
            }catch (MessagingException mex) {
                mex.printStackTrace();
            }
        }
    }
    
1
I know nothing about the Play Framework; is it not using the (working) Java code you posted above? Are you actually setting the From address in the email message, or just the SMTP user name to authenticate with? - Bill Shannon
No, it is not using the Java code. I used that Java code with the exact email configuration (from, to, smtp server) just to make sure the problem is not with the SMTP/transport bit. i.e. Plain javax.mail works. When I try to send email from Play Framework it fails and complain about invalid form address. - Hanxue
Then my guess is that you need to tell the Play Framework to set the From address, but just the SMTP user. - Bill Shannon
@BillShannon That is correct. Which is what I did in the application.conf file smtp.host=smtp.google.com smtp.port=465 smtp.from="[email protected]" That did not work. - Hanxue
Looks like you need a Play Framework expert. Try posting on the Play Framework users list. - Bill Shannon

1 Answers

0
votes

In the event that you are wrapping your mailer action in a Future (or Play Java equivalent), then it may be a variable scoping issue that you're hitting:

This works:

val mail = use[MailerPlugin].email
val async: Future[Unit] = Future{
  mail.setSubject(subject)
  mail.setRecipient(recipient)
  mail.setFrom(from)
  mail.send(body)
}
async.onFailure{case(e)=>
  Logger.error(s"mailer failed for $recipient due to: ${e.getMessage}")
}

This does not work:

val mail = use[MailerPlugin].email
mail.setSubject(subject)
mail.setRecipient(recipient)
mail.setFrom(from)
val async: Future[Unit] = Future{mail.send(body)}
async.onFailure{case(e)=>
  Logger.error(s"mailer failed for $recipient due to: ${e.getMessage}")
}

I assumed that the enclosing scope would be available within the Future closure; in this case definitely not since underlying Apache Commons mailer bails with "From address required". Sure enough, setting smtp.mock=true reveals that fromAddress is null when mail.setFrom and friends are populated outside of Future{...} scope.

Interestingly, even if I break out async val into a local method and pass in constructed mailer and body text:

private def async(mail: MailerAPI, body: String): Future[Unit] = Future{
  mail.send(body)
}

the mail properties set outside of the Future still wind up null despite passing a constructed mailer directly -- surprising, no?