Mail Servlet API Extentions

by Stefano Mazzocchi, Pierpaolo Fumagalli

Introduction

Servlet are a very powerful yet simple API inteface to allow users and developers to enhance the behavior of their servers. Currently, only HTTPServlets were designed to extend the abstract request/response framework designed by the generic javax.servlet package. With this proposal, our intention is to show that others types of client/server paradigms may be enhanced with the use of appropriate servlet extentions.

Since most of the internet traffic is generated by web and mail, we thought appropriate to concentrate our effort on the most used technology ever on the internet: the electronic mail. Even if complete and powerful solutions already exist, the HTTP servlet experience showed how powerful and easy to use Java servlets are compared to other solutions. Mail servlets go in this direction, willing to change the mail processing on the server side, allowing easy customization and creation of complex mail systems on top of mail servlet engines without requiring specific knowledge of underlying protocols or other complex technologies.

The goals

Mail servlets are designed to be the mail equivalent of HTTP servlets for the web: this means that it should be easy for a user/developer to write his own servlets to enhance his/her server, like it's done today for web servers. It's true that the medium and the purpose of the two systems are different, but our goal was to create a seemless integration with existing servlet technology without requiring a single modification in the existing (Servlet API 2.1) framework.

The design

A generic servlet is a simple container for a request/response handler. It's a plugin that processes data coming to a particular server location (abstracted from the protocol and server type) thru the ServletRequest object and creates some output and sends it to its destination using the ServletResponse object.

Note: this framework doesn't specify the location of the servlet, the way to map a servlet to a particular resource, the way to create the request and response and, more important, where to send the response to!

This is the key to the ability to enhance existing classes and solutions without requiring changes in the generic servlet  package: while HTTPServlets send the reponse back to the requester, MailServlet send the response to the specified mail recipients. While HTTPServlets work as bouncing applications, MailServlets work as data filters. While HTTPServlets do not copy data coming from the input to the output, MailServlets copy untouched data from the input (the mail sent) to the output (the processed mail).

The only thing that is changed by this vision, it the behavior of a generic servlet that is left unspecified. We suggest, as a precaution, to remove any generic servlet implementation from the API (nobody uses it directly, anyway) and to state it's unspecified behavior in the Servlet interface.

In current servlet technology framework, both users and developers are biased by the fact that there is no alternative from the HTTPServlet. Even GenericServlet is seen like an example, rather than a usable class and package separation is not understood nor appreciated. MailServlets (and other servlets types we are working on such as VoiceServlets for the Apache Java Answering Machine (J.A.M.) Project) may enhance the platform and give servlet users the ability to understand the general idea behind servlets and allow them to easily move from one servlet-enable server platform (web) to another (mail) with very little effort.

A possible implementation

Mail servlets do not specify how they are mapped and accessed by the clients. A possible implementation (that will be used in Apache James) is the mapping of virtual mailboxes to a specified mail servlet.

An example may clarify the picture.

Let us suppose we want to create a mailing list in a mailservlet-enable mail system. Our mail server is listening on the SMTP port on the host java.apache.org. We want the address james-dev@java.apache.org to be the Apache James development mailing list.

Let's write the MailingListServlet as follows (note that since we are not allowed to use the javax package, we will use org.apache until JavaSoft incorporates them officially into the servlet API framework, if this ever happens)

import javax.servlet.*;
import org.apache.servlet.mail.*;
import java.io.*;

/**
* This servlet implements a general mailing list engine that could
* be used to handle many simultaneous mailing list backended by
* a database of names.
*
* @author Stefano Mazzocchi
* @author Pierpaolo Fumagalli
*/
public class MailingListServlet extends MailServlet {

public void service(MailServletRequest req, MailServletResponse res) 
throws ServletException, IOException {
    
    // Get headers from the incoming mail
    MailHeaders headers = req.getHeaders();

    // Get the address this servlet is currently serving
    // This is used by the servlet to know which mailing list
    // have been requested.
    MailAddress list = req.getServletAddress();

    // Get the mail sender
    MailAddress sender = req.getSender();

    String subject = headers.getSubject().toLowerCase();

    // The sender of this mail is always the list.
    res.setSender(list);

    if (subject.startsWith("subscribe")) {
       res.setRecipient(sender);
       if (addSubscriber(list, sender)) {
          headers.setSubject("Welcome");
          res.getOutputStream().println("Welcome to the list!");
       } else {
          headers.setSubject("You're already listed");
          res.getOutputStream().println("You are already subscribed to this list");
       }
    } else if (subject.startsWith("unsubscribe")) {
       res.setRecipient(sender);
       if (removeSubscriber(list, sender)) {
          headers.setSubject("Good bye");
          res.getOutputStream().println("We'll miss you!");
       } else {
          headers.setSubject("You are not subscribed");
          res.getOutputStream().println("You are not subscribed to this list");
       }
    } else if (this.isPresent(sender, list)) {
       // Get the names of the subscribers
       MailAddress[] subs = this.getSubscribers(list);

       // Every part of the request that is not touched by the mail servlet
       // is copied onto the response stream. In this case, the subject.

       // Set the recipients of this mail, hiding the list of the recipients
       res.setRecipients(subs, false);

       BufferedInputStream input = new BufferedInputStream(req.getInputStream());
       ServletOutputStream output = res.getOutputStream();

       // Copy the mail message
       int ch;
       while ((ch = input.read()) != -1) {
          output.write(ch);
       }

       // Append a signature to the message.
       output.println("\n--\nManaged by " + this.getServletInfo()); 
    } else {
       res.setRecipient(sender);
       headers.setSubject("Message Rejected");
       res.getOutputStream().println("Sorry, but you have to subscribe to the list " + 
          "to be allowed to send messages");
    }

    res.setHeaders(headers);
}

private MailAddress[] getSubscribers(MailAddress list) {
    // return the array of subscribers to this list
    return null; // for now
}

private boolean isPresent(MailAddress sender, MailAddress list) {
    return true; // if sender is present on the list
}

private boolean addSubscriber(MailAddress list, MailAddress sender) {
    // add sender from the list associated to the list mail address
    // this could use JDBC or whatever to keep track of these changes.

    return true; // if name wasn't present and got included
}

private boolean removeSubscriber(MailAddress list, MailAddress sender) {
    // add sender from the list associated to the list mail address
    // this could use JDBC or whatever to keep track of these changes.

    return true; // if name was present and got removed
}

public String getServletInfo() {
    return "Mailing List Servlet ver. 0.1";
}
}

Once this mail servlet is written and compiled, it can be mapped to the james-dev mailbox of the mail server using the appropriate configurations and creating the appropriate database tables in the JDBC connected database used by this servlet.

As you see, a powerful and highly flexible mailing list system can be created with just a few lines of work and can be ported on any mail-servlet enabled mail server.

A detailed look at the proposal

The javadoc generated documents for the proposal may be found here.

A package containing the source code may be found here. You may not redistribute, modify or use these classes in any form whatsoever without explicit permissions from the authors. The source code are intended to be just a review and a working draft and will be subjected to changes in the future.

Future directions

One of the Java Apache Project goals is to create a family of enterprise servers using pure Java technologies. Apache JServ, Apache James, Apache Jam will try to share common code and resources as well as to propose common solutions such as servlet extentions.

This mail servlet specification is currently just a working draft and will be subjected to changes directly by JavaSoft, by other servlet group interests or by the Apache James development team before it's definately standardized by inclusion in the JavaSoft Servlet API specifications. In case this cannot happen, the "org.apache.servlet.mail" package will be ruled by the Apache James development team (even if we don't wish this to happen).

The authors would like to thanks James Duncan Davidson (JavaSoft Servlet API lead) for reviewing this proposal and for the work he is doing to make the servlet platform a truly open one.