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.
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:
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
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.
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
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)
We deliver relevant courses, by top Java developers to produce more resourceful and efficient programmers within their organisations.