 | Level: Advanced Kulvir Bhogal, WebSphere Services Consultant, IBM Corporation
01 Sep 2002 This article is the third in a series, and delves deeper into the JavaMail API. It describes how to create a Java thread-powered daemon, and how to create the environment for subscribing and unsubscribing to and from e-mail lists with e-mail messages.
Part 1 of this series introduced you to the JavaMail API and showed how to create a small setup for sending messages to a e-mail list housed in DB2. Part 2 showed how to tie together the JavaMail API with a Web application server, allowing users to interact with a mailing list with their Web browser.
Introduction
If you've been on the Internet for a while, you have probably heard of a program called Majordomo. Majordomo powers many of the mailing lists in the vast network we affectionately know as cyberspace. The program has gained a lot of popularity due to its renowned simplicity. With Majordomo you can manage a mailing list using simple commands that are sent to the mailing list program by electronic mail. Majordomo is free, and written in Perl. But wait a second. This article is supposed to be the third part in a series about JavaMail, isn't it? Don't worry, you are reading the right article. One of the things Majordomo offers is the ability to subscribe and unsubscribe from a mailing list by simply sending an e-mail. In this article, I show you how to use JavaMail to do the same thing. Prerequisites
This article builds on some of the information in parts 1 and 2 of this series. I assume you have the DB2 database and tables that were set up in the first article.
Overview of the setup
If you recall, we had a database named EMAILDB and a table named EMAIL_TBL with two columns:
Name of type CHAR(30), and Email also of type CHAR(30). To facilitate a subscribe operation, you need an insert on EMAIL_TBL. Similarly, to facilitate an unsubscribe operation, you need a remove from EMAIL_TBL. Figure 1 below shows how the setup will work. Figure 1. The project setup

You will need a dedicated POP3 e-mail address that will act as the "moderator" address. This e-mail address will receive subscribe and unsubscribe messages. (You will need the password of this account to make sure things go smoothly.) There are various settings, such as the POP3 e-mail box username and password, that must be provided to the program. These settings are established in the file daemonsettings.ini. You'll also see the pollingFrequency setting in the daemonsettings.ini file; I'll cover that one in just a bit. The POP moderator mailbox will be checked to find out if there have been any requests to be subscribed or unsubscribed from the list. In this example, to subscribe to the mailing list a user will send an e-mail from their own e-mail account to the moderator e-mail address, with a subject line "SUBSCRIBE myFirstName myLastName". For this article, the moderator e-mail address will be test@bogusserver.com. To unsubscribe from the e-mail list, a user must send an e-mail from his or her own e-mail account to the moderator e-mail address with a subject line "UNSUBSCRIBE". Now that we've established that e-mails to unsubscribe and subscribe will be sent to test@boguserver.com, we have to devise a way to check the account and act on unsubscribe and subscribe messages.
Automated checking
This task of checking the moderator e-mail box will have to occur periodically. Accordingly, the program that will manage the mailing list will act as a daemon, continuously checking the moderator mailbox. To build that daemon, I use threads in Java. A thread will be put to sleep and be awakened after a specified interval. Remember that setting in the daemonsettings.ini file named pollingFrequency? This setting defines how long the thread will sleep before it checks the moderator e-mail box.
The code that powers the project
The code for the project is shown below. Be sure to download the zip file and refer to the code while it is discussed in this article.
package com.ibm.MailDaemon;
import javax.mail.*;
import javax.mail.internet.*;
import java.util.*;
import javax.activation.*;
import java.io.*;
import java.sql.*;
import java.util.Properties;
public class MailDaemon extends Thread
{
/**
* MailDaemon constructor.
*/
public MailDaemon()
{
super();
}
/**
* Method handles adding an e-mail address to
* e-mail database.
*/
public int handleAdd(String emailAddress,
String firstName, String lastName)
{
try
{
Class.forName("COM.ibm.db2.jdbc.app.DB2Driver");
/*
change values in the next line to respect your
database username and password
*/
Connection db2Conn =
DriverManager.getConnection("jdbc:db2:emailDB",
"db2admin","db2admin");
Statement st = db2Conn.createStatement();
String checkIfExists
= "select count(*) from EMAIL_TBL where email = '"
+ emailAddress + "'" ;
ResultSet rs = st.executeQuery(checkIfExists);
rs.next();
int numberOfEntries
= Integer.parseInt(rs.getString(1));
if (numberOfEntries == 0)
{
String addStatement
= "insert into EMAIL_TBL values('"
+ firstName + " " + lastName +"','"
+ emailAddress + "')" ;
st.executeUpdate(addStatement);
}
db2Conn.close();
}
catch (Exception e)
{
System.err.println("A problem has" +
" occurred - " + e.toString());
return 0;
}
return 1;
}
/**
* Method handles the removal of an e-mail address
* from e-mail database.
*/
public int handleRemove(String emailAddress)
{
try
{
Class.forName("COM.ibm.db2.jdbc.app.DB2Driver");
/*
change values in the next line to respect your
database username and password
*/
Connection db2Conn =
DriverManager.getConnection("jdbc:db2:emailDB",
"db2admin","db2admin");
Statement st = db2Conn.createStatement();
String checkIfExists
= "select count(*) from EMAIL_TBL where email = '"
+ emailAddress + "'" ;
ResultSet rs = st.executeQuery(checkIfExists);
rs.next();
int numberOfEntries
= Integer.parseInt(rs.getString(1));
if (numberOfEntries > 0)
{
String delStatement =
"delete from EMAIL_TBL where email = '"
+ emailAddress + "'" ;
st.executeUpdate(delStatement);
}
db2Conn.close();
}
catch (Exception e)
{
System.err.println("A problem has" +
" occurred - " + e.toString());
return 0;
}
return 1;
}
/**
* Main method - acts as a daemon.
*/
public static void main(String[] args)
{
int pollingInterval = 0;
String mailServerName = "";
String userName = "";
String password = "";
Properties p =
new Properties(System.getProperties());
try
{
FileInputStream propFile
= new FileInputStream
("daemonsettings.ini");
p.load(propFile);
pollingInterval
= Integer.
parseInt(p.getProperty("pollingInterval"));
mailServerName = p.getProperty("mailServerName");
userName = p.getProperty("userName");
password = p.getProperty("password");
}
catch (Exception e)
{
System.out.println("Error opening properties file."
+ e);
}
int times = 0;
MailDaemon myMailDaemon = new MailDaemon();
while (true)
{
try
{
System.out.println("Sleeping");
sleep(pollingInterval);
System.out.println("Awakened");
// Setup the default parameter
Properties props = new Properties();
props.put("mail.transport.protocol","pop");
props.put("mail.pop.port","110");
// Open up session and connect to the mail store
Session session = Session.getInstance(props);
Store store = session.getStore("pop3");
store.connect(mailServerName,userName,password);
// Open Folder
Folder folder = store.getDefaultFolder();
if (folder==null)
{
System.out.println("Problem Occurred");
}
Folder popFolder = folder.getFolder("INBOX");
// open up the folder in read_write mode
// to allow for deletion
popFolder.open(Folder.READ_WRITE);
Message myMessage[] = popFolder.getMessages();
for (int i=0; i>myMessage.length; i++)
{
String subjectOfMessage
= myMessage[i].getSubject();
subjectOfMessage
= subjectOfMessage.toUpperCase();
System.out.println(subjectOfMessage);
// check if subject contains REMOVE
if (subjectOfMessage.indexOf("UNSUBSCRIBE")!=-1)
{
StringTokenizer st
= new StringTokenizer(subjectOfMessage);
String from
= ((InternetAddress)myMessage[i].
getFrom()[0]).getPersonal();
if (from==null) from
=((InternetAddress)myMessage[i].
getFrom()[0]).getAddress();
int successOfRemove
= myMailDaemon.handleRemove(from);
// queue the unsubscribe message from
// the messages folder for deletion
myMessage[i].setFlag(Flags.Flag.DELETED, true);
}
// check if subject contains ADD
else if
(subjectOfMessage.indexOf("SUBSCRIBE")!=-1)
{
StringTokenizer st
= new StringTokenizer(subjectOfMessage);
// skip over the "SUBSCRIBE" word
st.nextToken();
String firstName = st.nextToken();
String lastName = st.nextToken();
String emailAddressToBeAdded
= ((InternetAddress)myMessage[i].
getFrom()[0]).getPersonal();
if (emailAddressToBeAdded==null)
emailAddressToBeAdded
= ((InternetAddress)myMessage[i].
getFrom()[0]).getAddress();
myMailDaemon.handleAdd(emailAddressToBeAdded,
firstName, lastName);
// queue the subscribe message from the
// messages folder for deletion
myMessage[i].setFlag(Flags.Flag.DELETED, true);
}
}
popFolder.close(true);
store.close();
}
catch (Exception e)
{
System.out.println("An error has occurred: " + e);
}
times++;
} // end while loop
}
}
|
pollingFrequencyProperties
props.put("mail.transport.protocol","pop");
props.put("mail.pop.port","110");
|
In the code below, I instantiate a session object and connect to the mail store. Using the JavaMail API, I use the Store object's connect method to connect to the mail store using the mail server's name, user name, and password settings (obtained from the daemonsettings.ini file).
Session session = Session.getInstance(props);
Store store = session.getStore("pop3");
store.connect(mailServerName,userName,password);
|
The next part of the code shows the grabbing of the INBOX folder. As you look through the code, notice how systematic but simple it is to retrieve messages from the server. Note that I open the folder with a READ_WRITE flag. This permission specification is important, because after retrieving a message to subscribe/unsubscribe, I want to be able to delete the message from our message box. The READ_WRITE flag lets me do a delete. Specifying a delete does not remove the e-mail from the e-mail server immediately; only after the Folder object is closed are the messages marked for deletion expunged from the server. Moving on with the code, you will note that the retrieval of messages is housed in an array of Message objects. Next is a "for loop" where I loop through the messages in the array and grab the subject of each message. In this setup, I do not care about what is in the body of the message. I entertain the possibility of a message being a subscribe, unsubscribe, or some other type of message. I simply scrutinize the subject using the indexOf method of the String class to find out what type of message we're dealing with. If I see an "UNSUBSCRIBE" I use the offerings of the StringTokenizer class to parse the subject for a corresponding e-mail address to be removed from the e-mailing list. Similarly, if I see a "SUBSCRIBE" I parse the subject for an e-mail address and associated name of the person to be added to the e-mailing list. The real work
The real work of removing or adding an address to the mailing list is covered in the handleRemove and handleAdd methods. There is nothing very daunting about these two methods. I simply use the JDBC API, much as I did in the second article of this series, to either insert a new row to the database table or to remove a row from the database table, depending on the task.
A tangent about spam
Sadly, a lot of unscrupulous people are out there to abuse the world of e-mail. I personally am a victim of a constant barrage of e-mails, known in the industry as spam. I have some unkind words about these types of e-mail (but by the time you read this article, my editor will probably have taken those words out!). The Federal Trade Commission suggests that users forward spam to uce@ftc.gov instead of sending "remove me from your list" messages to spammers, which signals that an e-mail address is in active use.
Conclusion
On the flip side, a lot of e-mail lists are legitimate. This series of articles was written with the intent to manage a legitimate e-mail list. People who participate in these e-mail lists subscribe by their consent. When users no longer want to be part of the e-mail list, they simply relay their consent to unsubscribe. In this article, the JavaMail API was used to build a system that allows one to subscribe and unsubscribe from a list via the simple sending of an e-mail. You can further expand the project in this article for a more robust mailing system. There are a number of open-source projects on the Internet that use this API to do some pretty amazing things. As you've probably noticed by now, the offerings of the JavaMail API are quite extensive. This series of articles exposed you to many key concepts; I hope you feel comfortable about venturing out on your own JavaMail escapades.
Download | Name | Size | Download method |
|---|
| i-email3.zip | | HTTP |
Resources - Download a zip file with the source code discussed in this article.
- Learn more about the JavaMail API from the explanatory overview provided by Sun.
- Read some of Kulvir's other articles: "Special delivery! Bulk e-mailing with DB2, Java, JDBC, and the JavaMail API" (developerWorks, February 2002),
"User-power and e-mailing: Using the Web, DB2, Java JDBC, and the JavaMail API to manage bulk e-mail lists" (developerWorks, May 2002), "Enforcing Business Logic using DB2 Triggers, Java UDFs, and the JavaMail API" (DB2 Developer Domain, May 2002), and "How WebSphere Voice SDK can get your enterprise data to speak up for itself (developerWorks, September 2002).
About the author  | 
|  |
Kulvir Singh Bhogal works as an IBM consultant, devising and implementing Java-centric solutions at customer sites across the nation. You can contact Kulvir at kbhogal@us.ibm.com.
|
Rate this page
|  |