Created: 2011-07-10 21:08
Updated: 2018-02-21 20:31

Grey Matter

Apache James is a Java-based mail server and provides a simple Mailet API that you can use to write your own email-processing apps. Each Mailet subclass is instantiated once, while multiple mail spool threads can call it concurrently to process mail.

Grey Matter provides a simple Mailet trait that converts each received email into a message that is then sent to an Akka actor, making concurrent email processing much easier. Clients simply extend a trait, override a few methods, and then implement the rest of the mail processing app as Akka actors.

This simple example creates a mailet that converts each received Mail into a MyMessage and then sends it to a MyActor:

class ExampleMailet extends ActorSystemMailet {
  override def newActor(system: ActorSystem): ActorRef = system.actorOf(Props[MyActor])
  override def messageFor(mail: Mail): Any = MyMessage(mail) //assume apply() performs Mail => MyMessage

Currently uses Akka 2.0.5 & Scala 2.9.1. Future versions will use Akka 2.1.x & Scala 2.10.x. Grey Matter has only been tested with James 3.0, but may very well work with James 2.3.x.


//Grey Matter is available in central Maven repos
val greyMatter = "com.pongr" %% "greymatter" % "1.0.0"


The most basic component that Grey Matter provides is the ActorMailet trait. It is simply a Mailet that converts each received Mail into some other message object and sends that message to some ActorRef. The implementing class must provide the Mail-to-message conversion function and the ActorRef instance.

While ActorMailet could just send the Mail object directly to an actor, in our experience we've run into problems when the Mail's MimeMessage object is used outside of the James threads (or potentially after it's finished going through the James pipeline). NullPointerExceptions tend to get thrown non-deterministically when trying to use the MimeMessage in an actor. Thus, it's best to have the Mailet pull everything it needs out of Mail & MimeMessage, wrap it in some other container object (such as a Scala case class instance) and then send that as the message to the actor.

ActorMailet can also set the Mail's state to GHOST, so that it will not be further processed by James (which it does by default).

While ActorMailet is as high-level as possible, it will usually be more convenient to base your mailets on ActorSystemMailet instead.


Grey Matter also provides a more convenient ActorSystemMailet trait. It creates an ActorSystem on mailet initialization and then defers to a subclass to create an ActorRef to send all mail messages to. The implementing class may create a chain of actors as complex as needed, as long as it provides a single ActorRef for all mail messages to enter.

Integrating Mailets into James

TODO describe mailetcontainer.xml config...

Config and Settings

TODO describe how to include an Akka application.conf file in James conf dir, and use it in a Settings object.


Grey Matter ships with an example LogMailet and LogActor (guess what they do...). Here are some simple commands to set up a James 3.0 mail server on EC2 that sends all incoming email through LogMailet to LogActor.

For James 3.0-beta3:

#Run an Ubuntu 12.04 64-bit ec2 instance with port 25 open to (for SMTP) and port 22 open to your IP (for SSH), for example:
ec2run ami-0145d268 -k your-key -t m1.large -g your-smtp-group -g your-ssh-group

#Set up your DNS like this:
 - MX 10
 - A <public IP of EC2 instance>

#SSH in to your new EC2 instance and perform all of the below:

sudo apt-get update

#32-bit instance
sudo apt-get install -y libc6 libc6-dev default-jre-headless

#64-bit instance
sudo apt-get install -y libc6 libc6-dev libc6-i386 libc6-dev-i386 default-jre-headless

#Download James and set it up at /usr/local/james
cd /usr/local
sudo wget
sudo tar xvzf apache-james-3.0-beta3-app.tar.gz
sudo rm apache-james-3.0-beta3-app.tar.gz
sudo ln -s apache-james-3.0-beta3 james

#In /usr/local/james/conf, set enabled="false" in imapserver.xml & pop3server.xml

#Replace /usr/local/james/conf/domainlist.xml with:
<domainlist class="org.apache.james.domainlist.xml.XMLDomainList">
#In /usr/local/james/conf/smtpserver.xml:
 - Comment out: <!-- <handler class="org.apache.james.smtpserver.fastfail.ValidRcptHandler"/> -->
#In /usr/local/james/conf/
 - Add FILE to: log4j.rootLogger=DEBUG, FILE
#In /usr/local/james/conf/mailetcontainer.xml:
 - Comment-out all existing mailets in root processor
 - Add to root processor: <mailet match="All" class="com.pongr.greymatter.example.LogMailet" />
#Put greymatter-assembly-1.0.0.jar in /usr/local/james/conf/lib (use "sbt assembly" to build this, it will have all dependency classes in one fat jar)
sudo /usr/local/james/bin/james start

tail -f /usr/local/james/log/james-server.log

Send email to and watch logging appear in james-server.log

For James 3.0-beta4:

#Download James and set it up at /usr/local/james
cd /usr/local
sudo wget
sudo tar xvzf apache-james-3.0-beta4-app.tar.gz
sudo rm apache-james-3.0-beta4-app.tar.gz
sudo ln -s apache-james-3.0-beta4 james

#Create /usr/local/james/conf/domainlist.conf:
<domainlist class="org.apache.james.domainlist.xml.XMLDomainList">

#Create conf/imapserver.conf
  <imapserver enabled="false" />

#In /usr/local/james/conf/
 - Add FILE to: log4j.rootLogger=DEBUG, FILE

#Create conf/pop3server.conf
  <pop3server enabled="false" />

#Create conf/smtpserver.conf
  <smtpserver enabled="true">
    <tls socketTLS="false" startTLS="false"></tls>
    <!--  This sets the maximum allowed message size (in kilobytes) for this -->
    <!--  SMTP service. If unspecified, the value defaults to 0, which means no limit. -->
      <handler class="org.apache.james.smtpserver.CoreCmdHandlerLoader"/>

#Create conf/mailetcontainer.conf
<?xml version="1.0"?>
<mailetcontainer enableJmx="true">
    <processor state="root" enableJmx="true">
      <mailet match="All" class="com.pongr.greymatter.example.LogMailet" />
    <!-- For some reason we have to include this processor and mailet, otherwise James won't start up... #donotremove -->
    <processor state="error" enableJmx="true">
      <mailet match="All" class="ToRepository">

#Add to conf/wrapper.conf: (via*

#Put greymatter-assembly-1.0.0.jar in /usr/local/james/conf/lib (use "sbt assembly" to build this, it will have all dependency classes in one fat jar)
sudo /usr/local/james/bin/james start

tail -f /usr/local/james/log/james-server.log

Send email to and watch logging appear in james-server.log


Grey Matter is licensed under the Apache 2 License.

Cookies help us deliver our services. By using our services, you agree to our use of cookies Learn more