Distributed James Server — Extending server behavior

Available extension mechanisms

Extension mechanisms for the Distributed Server

The Distributed Server exposes several interfaces allowing the user to write custom extensions in order to extend the Distributed Server behavior.

Writing Mailets and Matchers allows one to supply custom components for the Mail Processing and enables to take decisions, and implement your business logic at the transport level.

Writing Mailbox listeners enables to react to your user interaction with their mailbox. This powerful tool allows build advanced features for mail delivery servers.

Writing SMTP hookd enables to add features to your SMTP server.

Writing WebAdmin routes enables to add features to the WebAdmin REST API.

Writing IMAP extensions.

The examples are also a good reference.

Handling injections for your extensions

Injecting core components

You can very easily inject core components into your custom extensions.

All you need is to pass them via a constructor annotated via @Inject.

For instance:

public class MyMailet extends GenericMailet {
    private final UsersRepository usersRepository;

    @Inject
    public MyMailet(UsersRepository usersRepository) {
        this.usersRepository = usersRepository;
    }

    @Override
    public void service(Mail mail) throws MessagingException {
        // Do something
    }
}

Injecting simple extension components

Furthermore, concrete implementation, that are part of your extension, can be injected as well.

Consider the following example:

public class MyService {

}

public class MyMailet extends GenericMailet {
    private final MyService myService;

    @Inject
    public MyMailet(MyService myService) {
        this.usersRepository = myService;
    }

    @Override
    public void service(Mail mail) throws MessagingException {
        // Do something
    }
}

Defining custom injections for your extensions

However, to inject an interface into your extension, you will need additional injection definitions.

To so:

  • 1. Given an interface defined in an additional JAR:

public interface MyService {}
  • 2. And an implementation of that interface, in another additional JAR:

public class MyServiceImpl extends MyService {}
  • 3. We need to define a binding for MyService to be bound to MyServiceImpl

public class MyServiceModule extends AbstractModule {
    @Override
    protected void configure() {
        bind(MyServiceImpl.class).in(Scopes.SINGLETON);
        bind(MyService.class).to(MyServiceImpl.class);
    }
}

Both MyService, MyServiceImpl and MyServiceModule needs to be in the extensions-jars folder (potentially different jars).

  • 4. MyServiceModule needs to be registered in extensions.properties

  • 5. MyService can then be used as part of your extensions

public class MyMailet extends GenericMailet {
    private final MyService myService;

    @Inject
    public MyMailet(MyService myService) {
        this.usersRepository = myService;
    }

    @Override
    public void service(Mail mail) throws MessagingException {
        // Do something
    }
}

Note that overriding injection definitions of the Distributed Server for your injections is not supported.

Starting your components

Sometimes you wish to 'start' your extensions. This can be achieved through defining your own UserDefinedStartable:

public class MyStartable implements UserDefinedStartable {
    @Override
    public void start() {
        // Will be called
    }
}

Your startable then needs to be registered within extensions.properties:

guice.extension.startable=com.company.MyStartable

Pre-packaged extensions

Rate Limiting for mailet processing

Vendor: Apache Foundation (James project), Apache License V2

Project link contains detailed set up instructions and configuration examples as well as a pre-configured docker-compose.

This extension ships mailets for applying advanced rate limit criteria to the email transiting through your James server. It is shipped with two backends implemented:

  • in memory: For single server mode.

  • Redis: Uses Redis as a shared, fast and scalable in-memory datastore, allowing to apply rate limiting in a distributed fashion. Here is the link to the Redis extension for rate limiting.

  • Alternative extensions can be written and loaded into James using the Guice extension mechanism and providing custom injections for the RateLimiterFactoryProvider class.

This extension ships the following mailets:

  • PerSenderRateLimit allows defining limits applied to the senders of emails (count of email, count of recipients, size, size * recipients)

  • PerRecipientRateLimit allows defining limits applied to the recipients of emails (count of email, size)

  • GlobalRateLimit allows defining limits applied to all the emails (count of email, count of recipients, size, size * recipients)

Depending on their positions and the matcher they are being combined with, those rate limiting rules could be applied to submitted emails, received emails or emitted email being relayed to third parties.

Throttling

Can use combine with Requeue mailet for a throttler by re-enqueue mail. link