The Apache Software Foundation

Configure Custom Listeners

This document will help you understand what is Mailbox Listener. Then you will have a chance to implement your own Mailbox Listener and configure it to use it in James server.

  • What is Mailbox Listener
  • How to use a custom Mailbox Listener in James

What is Mailbox Listener

Mailbox Listener is a component in James Mailbox System. Each time an action is applied on a mailbox(adding, deleting), or on an email(adding, deleting, updating flags...), then an event representing that action is generated and delivered to all the Listeners that had been registered before. After receiving events, listeners retrieve information from the events then execute their business (Indexing emails in ElasticSearch, updating quota of users, detecting spam emails...)

There are two kinds of Listener registration:

  • Group Registration: a Group Registration will listen for all events fired and will deliver each event only once per group within a distributed topology. That means if there are three listeners registered to the same group, each time an event is fired, only one of these three listeners can receive that event. You can write your own custom Listener and configure James to register it as a Group Registration.
  • Key Registration: a Key Registration will listen only for events it is interested in, and with each event fired to a Key Registration, it will be delivered to all registered listeners. That means if there are three listeners registered to the same key, each time an event is fired, all of three listeners will receive that event. At the moment, James doesn't support to configure custom Listener for Key Registration. This feature is used internally to implement things like IMAP IDLE aka notifications. Therefore, it's not exposed through configurations as it makes little sense

How to use a custom Mailbox Listener in James

prerequisite: custom Mailbox Listeners can only work with James Guice products.

Use the custom BigMessageListener in James

Once you have a custom Listener, it's very simple to setup James with that Listener. In this example, we will use the BigMessageListener which will listen events fired after emails are appended to users mailboxes, then for each email added event, determine the size of added email by getting size information from the event. If the size of the email is greater than or equals 1 MB, then BigMessageListener will add a "BIG_MESSAGE" flag to that email

Starting James with BigMessageListener

We will take the simplest James product(JPA Guice) for the example

First, get template JPA product configuration:


$ git clone https://github.com/apache/james-project
$ cp -rf james-project/dockerfiles/run/guice/jpa/destination/conf conf
                

Then create the keystore file in the conf/ directory with the default password james72laBalle


$ keytool -genkey -alias james -keyalg RSA -keystore conf/keystore
                

Second, modify listener.xml configuration file in conf/ directory to use only BigMessageListener by specifying its full class name. We only need to use this Listener in our example.


<listeners>
    <listener>
        <class>org.apache.james.examples.custom.listeners.SetCustomFlagOnBigMessages</class>
    </listener>
</listeners>
                

Finally, starting a James Server by docker compose

Getting James docker-compose.yml


$ wget https://raw.githubusercontent.com/apache/james-project/master/dockerfiles/run/docker-compose.yml
                

Using James Guice JPA instead of the default product


services:
  james:
    ...
    image: linagora/james-jpa-guice:latest
                

Add the following volumes for james service:


volumes:
  - $PWD/conf:/root/conf/
                

When you are using your listeners, place the jar containing your listeners into "extensions-jars" directory and add a volume for it.


volumes:
  - $PWD/extensions-jars:/root/extensions-jars/
                

Putting compiled jars into extensions-jars directory

When you write a custom Listener, you have to compile it and place the compiled jar package inside "extensions-jars" directory to make James load your Listener when it starts up. In this case, you should compile the James's module examples/custom-listeners and put it to "extensions-jars". The jar name can be sightly different by the time because its name carries James version, but you can recognize it easily.


$ mkdir extensions-jars
$ cd james-project
$ mvn clean install -DskipTests -f examples/custom-listeners/pom.xml
$ cd ../
$ cp james-project/examples/custom-listeners/target/custom-listeners-****.jar extensions-jars/custom-listeners.jar
                

Check out the docker-compose.yml to get the host name of james service, currently it's james.local. So you, have to modify James domain list to use this james.local as James default domain:


<domainlist>
    <autodetect>true</autodetect>
    <autodetectIP>true</autodetectIP>
    <defaultDomain>james.local</defaultDomain>
</domainlist>
                

Now, you are able to start james by docker compose, then wait for all docker containers to be up.


$ docker-compose up -d
                

Verifying the result of the setup

Now that we have a proper James server on our local machine with custom listeners loaded, we are able to use them. To verify our work, here is this simple scenario:

  • Use James CLI to create user1@james.local/password1
  • Use James CLI to create user2@james.local/password2
  • Use a mail client to connect to user1, send a big email to use2
  • Use IMAP command to see the flags of the email user2 had received

Use James CLI to provision users


$ docker exec -it james /bin/bash
$ java -jar james-cli.jar AddUser user1@james.local password1
$ java -jar james-cli.jar AddUser user2@james.local password2
$ exit
                

Use thunderbird or any mail client on your local machine to connect with user1 and user2. The configuration should point to smpt server at localhost:25, and imap server at localhost:143. Use these credentials: user1@james.local/password1 and user2@james.local/password2

After that, use user1@james.local account to sent a big email to user2@james.local, the size of the emails should be greater than 1 MB. One of the simplest ways is to attach a picture bigger than 1MB to the mail.

If the mail client had sent a mail to user2@james.local, then you can see that this email appeared in user2 INBOX. We can see its flags by using the following IMAP command on your local host.


$ telnet localhost 143

# Login by user2
A1 LOGIN user2@james.local password2

# Select user2's INBOX
A1 SELECT INBOX

# Search for all emails in user2's INBOX. The result should be: * SEARCH 1. Where 1 is the UID of the email which had been sent before
A1 UID SEARCH ALL

# Display all flags of that email. The result should be: * 1 FETCH (FLAGS (\Recent \Seen BIG_MESSAGE)). You can see that BIG_MESSAGE has appeared
A1 FETCH 1 (FLAGS)
                

That's it, we are now sure that our BigMessageListener worked ! Now that you have learned how to set up a custom Listener, you can follow this setup to add your own !