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
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
prerequisite: custom Mailbox Listeners can only work with James Guice products.
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
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
$ 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: apache/james:jpa-3.6.0
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
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 email@example.com/password1
- Use James CLI to create firstname.lastname@example.org/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 email@example.com password1 $ java -jar james-cli.jar AddUser firstname.lastname@example.org 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
Use these credentials: email@example.com/password1 and firstname.lastname@example.org/password2
After that, use email@example.com account to sent a big email to firstname.lastname@example.org, 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 email@example.com, 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.
$ openssl s_client -connect localhost:993 # Login by user2 A1 LOGIN firstname.lastname@example.org 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 !