Don't reinvent the wheel." We've all heard, and perhaps uttered, this maxim many times during our programming careers. ColdFusion MX has introduced new features to help CF developers avoid reinventing the wheel, including tag-based user-defined functions (UDFs) and ColdFusion components (CFCs).
As more CF developers begin to use these features, more code will be available for other developers to download and use. However, code reuse in ColdFusion is not limited to ColdFusion code. Integrating with Java is easier than ever with MX. Since the developer base for Java is large and it has been around much longer than ColdFusion UDFs and CFCs, there is a lot of code already out there that CF developers can use. I discovered this firsthand on a recent project when I didn't think about using existing Java libraries before starting down another path.
This project required me to programmatically access e-mail messages in a subfolder of an e-mail inbox. I couldn't use the CFPOP tag to do this because the protocol implemented by CFPOP - the "Post Office Protocol" - is designed to allow users to download messages from the "inbox" folder only. Another protocol is required to access subfolders - the Internet Message Access Protocol (IMAP).
After discovering that I needed to use IMAP to accomplish my task, I looked for a way to use it with ColdFusion. Unfortunately, since there is no built-in ColdFusion tag that implements IMAP, and I had been looking for an excuse to play around with Java, my first reaction was "Cool! I'll write my own!"
I learned a few IMAP commands and started playing around with the low-level Java networking libraries. Two days and many lines of code later, I had written a marginal IMAP client. Although it mostly worked, it still needed additional functionality and I wasn't at all confident in its stability. I knew there had to be a better way.
JavaMail API
Fortunately, I discovered that there is a much better way - use the JavaMail API. According to Sun's JavaMail Web site (http://java.sun.com/products/javamail), this API "provides a set of abstract classes that model a mail system" including support for IMAP - the same functionality (plus a lot more) that I had been struggling with for two days. Best of all, since it's included in the J2EE platform, on which ColdFusion MX is built, I didn't have to download anything or make any configuration changes to use it. This was the option I should have considered from the start.
Design Goals
Now that I had found a library, I wanted to make it simple to use from ColdFusion. Since I was already comfortable using the CFPOP tag, I decided to encapsulate some of JavaMail's functionality into a custom tag that, as much as possible, provides the same interface as CFPOP. The tag's full code is shown in the code listing. Listing 1 shows the functions that interact with the API.
Listing 2 shows the tag's main logic. For the sake of brevity, the code shown in the listing includes only one of the actions provided by CFPOP's interface - the "GETHEADERONLY" action. This excludes the "GETALL" action, which provides the ability to view a message's contents and attachments, and the "DELETE" action.
The Code
The JavaMail API consists of a bunch of classes that model a mail system. The highest-level class is the "Session" class. The session class allows you to specify properties like "port" and "timeout." To specify properties, first create and set a "Properties" object. The following sets the protocol to IMAP and the port to 143:
obj_Properties =
createObject("Java",
"java.util.Properties");
obj_Properties.init();
obj_Properties.put(
"mail.store.protocol",
"imap");
obj_Properties.put(
"mail.imap.port",
"143");
Now create the session object and pass in the properties object:
cls_Session =
createObject("Java",
"javax.mail.Session");
obj_Session =
cls_Session.getInstance(
obj_Properties);
The session class also provides a method for getting the "store." The store class abstracts the underlying protocol (in this case IMAP) and provides methods for logging into the mailbox.
obj_Store = createObject("Java",
"javax.mail.Store");
obj_Store =
obj_Session.getStore();
obj_Store.connect(
"my_mailserver",
"my_username",
"my_password");
Now you are ready to start accessing folders. The only folder that you can access using POP is the "inbox" folder. With IMAP, you can access subfolders below the inbox. The following code accesses and opens the folder "test," which is a subfolder of "inbox":
obj_Folder =
obj_Store.getFolder("inbox/test");
obj_Folder.open(
obj_Folder.READ_ONLY);
Note that this code opens the folder with read permissions only. This is a good idea if you only want to browse the contents of the folder. If you want to delete, move, or update messages, open the folder with read and write permissions:
obj_Folder.open(
obj_Folder.READ_WRITE);
The next step is to get a list of messages in the folder. With the JavaMail API, this is done by retrieving an array of "Message" objects.
ar_Messages =
obj_Folder.getMessages();
This code returns all messages in the folder. If you only need a subset of the messages, you must pass an array of message numbers to the "getMessages" function. Because I modeled my IMAP tag after the CFPOP tag, I wanted to allow the user to pass a list of message numbers to the tag. This discrepancy was easily remedied by using the built-in "ListToArray" ColdFusion function.
ar_Messages =
obj_Folder.getMessages(
ListToArray("1,2"));
Once you have the messages, you are ready to read the message headers using methods of the "Message" object. The following shows how to access the "subject" and "to addresses" of the first message in the folder (which is located at position 1 of the messages array):
// Get the received date
str_Subject =
ar_Messages[1].getSubject();
// Get the to addresses
cls_RecipientType =
CreateObject("Java",
"javax.mail.Message$RecipientType");
ar_To =
ar_Messages[1].getRecipients(
cls_RecipientType.TO);
lst_ToAddresses =
arrayToList(ar_To);
Note that the "getRecipients" method of the "Message" object returns an array of addresses since an e-mail message can be addressed to more than one person. This is also true when getting the "from", "reply-to", "CC", and "BCC" addresses. Also note the dollar sign ($) syntax used to invoke the "RecipientType" class. RecipientType is an "inner class" of the "Message" class. Although you can access inner classes in Java code by using dot notation (e.g., "Message.RecipientType"), this doesn't work in ColdFusion code.
Now that you have read the messages, the next step is to delete the unwanted ones. To do this, you set the "deleted" flag of the message and then call the folder object's "expunge" method:
cls_Flag =
CreateObject("Java",
"javax.mail.Flags$Flag");
ar_Messages[1].setFlag(
cls_Flag.DELETED,
true);
arguments.obj_Folder.expunge();
Where to Go from Here
The code presented here only scratches the surface of the functionality provided by IMAP. Most obviously, it lacks the ability to read message contents and attachments. Another useful feature would allow users to retrieve a listing of subfolders. If you are interested in adding these or other features, check out the JavaMail tutorial at http://developer.java.sun.com/developer/
onlineTraining/JavaMail/contents.html and the API specification at http://java.sun.com/products/
javamail/1.3/docs/javadocs/index.html.
Summary
With the release of MX, ColdFusion developers have more options for code reuse, including native access to a wide array of predeveloped Java software. Sometimes, especially for developers who don't have extensive Java experience, using existing Java libraries is not always the most obvious option. However, in ColdFusion MX, it can be a very worthwhile option to consider. It certainly is one that I strongly recommend the next time you start a project and think "Cool, I'll just write it myself..."