Running on Java 24-ea+21-2447 (Preview)
Home of The JavaSpecialists' Newsletter

131Sending Emails from Java

Author: Dr. Heinz M. KabutzDate: 2006-08-28Java Version: 5Category: Tips and Tricks
 

Abstract: In this newsletter, we show how simple it is to send emails from Java. This should obviously not be used for sending unsolicited emails, but will nevertheless illustrate why we are flooded with SPAM.

 

Welcome to the 131st edition of The Java(tm) Specialists' Newsletter. This will be one of my last newsletters sent from South Africa, as we are moving to Greece in October. I sold my trusty Alfa Romeo 156 Twin Spark last week. At least she (sniff sniff) was sold to a good friend.

Since the last newsletter, our children count has increased by 50%. We are grateful for the safe arrival of Evangeline Kineta Kabutz.

Here is another copy of the quiz that I sent last month, which less than 25% got right. Even if you cannot make it to Oslo in September for my tutorial, have a look if you know the answer:

import java.util.*;
public class Conference {
  private Collection delegates = new ArrayList();
  public void add(String... names) {
    Collections.addAll(delegates, names);
  }
  public void removeFirst() {
    delegates.remove(0);
  }
  public String toString() {
    return "Conference " + delegates;
  }
  public static void main(String[] args) {
    Conference sun_tech_days = new Conference();
    sun_tech_days.add("Herman", "Bobby", "Robert");
    sun_tech_days.removeFirst();
    System.out.println(sun_tech_days);
  }
}

javaspecialists.teachable.com: Please visit our new self-study course catalog to see how you can upskill your Java knowledge.

Sending Emails from Java

Sometimes we need to send an email to a group of friends to announce some event (birth of child, move to Greece, farewell party). Due to the scourge of SPAM, we have to be careful how we do this, otherwise our email will be caught in the net and the other party will not see it. Over the years of publishing this email newsletter, I have discovered several things:

  • Do not SPAM.
  • Don't start an email with "Dear ..."
  • If possible, avoid HTML tags. Text is best.
  • Definitely avoid JavaScript.
  • Don't send an email to 100 people by putting their addresses in the "TO", "CC" or "BCC" fields.
  • Use a SMTP server on a static IP address.
  • Do not SPAM.

Let us imagine that I want to invite 30 friends for a "braai", which is a South African version of the barbeque. It works a bit differently here. First off, when we say: come at 18:00, we mean 20:00. And if you do come at 20:00, don't expect the fire to have started yet. Another curious feature is that it is fairly common to ask your guests to bring their own meat and drinks. This way, the braai scales better. So here is my invitation, "braai.txt", where the first line is the subject:

    Invitation to Braai 5th August
    We are having a braai at our house on the 5th of August at 18:00
    to celebrate the birth of our daughter, Evangeline Kineta Kabutz.
    Be there or be square.  Bring own meat and drinks.  We will
    provide the salads, the fire and the music.

    Heinz + Helene

Of course, we also need a list of email addresses that we can send the invitation to. These can be in various formats, but the one that I prefer is "FirstName Surname <email>". Here is the start of my file "addresses.txt":

    Heinz Kabutz <heinz@javaspecialists.eu>
    Peter East <peter.east@javaspecialists.eu>
    Bad Name <bad.name@funkyunregistereddomain.com>

We need to create a utility class called FileCollection, before we delve into the emailing. The FileCollection is a Collection of Strings that pulls in the contents of a text file at start up and contains all the lines as elements.

package com.cretesoft.mailer;

import java.util.*;
import java.io.*;

public class FileCollection extends ArrayList<String> {
  public FileCollection(String filename) throws IOException {
    BufferedReader in = new BufferedReader(new FileReader(filename));
    String s;
    while ((s = in.readLine()) != null) {
      add(s);
    }
    in.close();
  }
}

We need another utility class called MessageProvider, which extracts the subject and the message body from a file:

package com.cretesoft.mailer;

import java.io.*;
import java.util.*;

public class MessageProvider {
  private final String subject;
  private final String content;

  public MessageProvider(String filename) throws IOException {
    Iterator<String> lines = new FileCollection(filename).iterator();
    subject = lines.next();
    StringBuilder cb = new StringBuilder();
    while(lines.hasNext()) {
      cb.append(lines.next());
      cb.append('\n');
    }
    content = cb.toString();
  }

  public final String getSubject() {
    return subject;
  }

  public final String getContent() {
    return content;
  }
}

Now comes the tricky part of deciding which SMTP server to use. If you travel alot, or move between ISPs and networks, you should use a server that allows you to authenticate yourself. Sending emails from your own machine, especially if you have a dynamic IP address, is almost guaranteed to land you in the SPAM bin. You will have to make your own arrangements with your ISP to find out what the SMTP server settings are.

The MailSender class is currently hardcoded with my own settings which you need to replace with your own. All it does is create a transport for SMTP and then allow you to send the message to an email address.

package com.cretesoft.mailer;

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

public class MailSender {
  private static final String SMTP_SERVER =
      "smtp.javaspecialists.eu";
  private static final String USERNAME =
      "heinz@javaspecialists.eu";
  private static final String PASSWORD = "some_password";
  private static final String FROM =
      "Dr Heinz M. Kabutz <heinz@javaspecialists.eu>";
  private static final String mailer = "TJSNMailer";

  private final Transport transport;
  private final Session session;
  private final MessageProvider provider;

  public MailSender(MessageProvider provider)
      throws MessagingException {
    this.provider = provider;
    Properties props = System.getProperties();
    props.put("mail.smtp.host", SMTP_SERVER);
    props.put("mail.smtp.auth", "true");
    // Get a Session object
    session = Session.getInstance(props, null);
    transport = session.getTransport("smtp");
    transport.connect(SMTP_SERVER, USERNAME, PASSWORD);
  }

  public void sendMessageTo(String to) throws MessagingException {
    Message msg = new MimeMessage(session);
    // set headers
    msg.setFrom(InternetAddress.parse(FROM, false)[0]);
    msg.setHeader("X-Mailer", mailer);
    msg.setSentDate(new Date());
    msg.setRecipients(Message.RecipientType.TO,
        InternetAddress.parse(to, false));

    // set title and body
    msg.setSubject(provider.getSubject());
    msg.setText(provider.getContent());

    // off goes the message...
    transport.sendMessage(msg, msg.getAllRecipients());
  }
}

Depending on how reliable your SMTP server is, you might need to build in some retries into the sendMessageTo() method.

Lastly we have the Mailer class:

package com.cretesoft.mailer;

import javax.mail.MessagingException;
import java.io.IOException;

public class Mailer {
  private final FileCollection to;
  private final MessageProvider provider;
  public Mailer(String addressFile, String messageFile)
      throws IOException {
    to = new FileCollection(addressFile);
    provider = new MessageProvider(messageFile);
  }

  public void sendMessages() throws MessagingException {
    MailSender sender = new MailSender(provider);
    for (String email : to) {
      sender.sendMessageTo(email);
      System.out.println("Mail sent to " + email);
    }
  }

  public static void main(String[] args) throws Exception {
    if (args.length != 2) {
      System.err.println(
          "Usage: java Mailer address_file message_file");
      System.exit(1);
    }

    long time = -System.currentTimeMillis();
    Mailer sender = new Mailer(args[0], args[1]);
    sender.sendMessages();
    time += System.currentTimeMillis();
    System.out.println(time + "ms");
    System.out.println("Finished");
  }
}

When we run this (with the correct password), we get:

    Mail sent to Heinz Kabutz <heinz@javaspecialists.eu>
    Mail sent to John Smith <john.smith@javaspecialists.eu>
    Mail sent to Bad Name <bad.name@funkyunregistereddomain.com>
    17749ms
    Finished

Application of Mailer

I use this mailer in several applications. For example, when you fill in our enquiry form it sends me a lovely email listing what you have filled in. In that case, I am sending an HTML email to myself. Using HTML looks smarter, but might get caught up in a SPAM net.

Another application for sending emails is when an exception occurs on some critical applications. This way, we immediately know when a problem has occurred.

Enhancements

Besides retrying to establish transport connections, you can also improve the program by using multi-threading to create several concurrent connections to your ISP's SMTP server. Your ISP might not allow that. Infact, they might black-list you if you send too many emails sequentially. However, if it does allow you, you will get a great performance improvement.

Kind regards

Heinz

 

Comments

We are always happy to receive comments from our readers. Feel free to send me a comment via email or discuss the newsletter in our JavaSpecialists Slack Channel (Get an invite here)

When you load these comments, you'll be connected to Disqus. Privacy Statement.

Related Articles

Browse the Newsletter Archive

About the Author

Heinz Kabutz Java Conference Speaker

Java Champion, author of the Javaspecialists Newsletter, conference speaking regular... About Heinz

Superpack '23

Superpack '23 Our entire Java Specialists Training in one huge bundle more...

Free Java Book

Dynamic Proxies in Java Book
Java Training

We deliver relevant courses, by top Java developers to produce more resourceful and efficient programmers within their organisations.

Java Consulting

We can help make your Java application run faster and trouble-shoot concurrency and performance bugs...