The Apache Software Foundation

Write Custom James server assembly

Find this example on GitHub.

The current project demonstrates how to write a custom assembly in order to write your own tailor-made server.

    This enables:
  • Arbitrary composition of technologies (example JPA mailbox with Cassandra user management)
  • Write any additional components
  • Drop any unneeded component
  • You have control on the dependencies and can reduce the classpath size

Example: Write an IMAP+SMTP only memory server

In order to do this select the modules you wished to assemble in the Guice building blocks. We encourage you to have a fine grain control of your dependencies but for the sake of simplicity this example will reuse the dependencies of an existing James application:

<dependency>
    <groupId>${james.groupId}</groupId>
    <artifactId>james-server-memory-app</artifactId>
    <version>${project.version}</version>
</dependency>

Once done assemble the guice modules together in a class implementing JamesServerMain:

public class CustomJamesServerMain implements JamesServerMain {
       public static final Module PROTOCOLS = Modules.combine(
           new IMAPServerModule(),
           new ProtocolHandlerModule(),
           new MailRepositoryTaskSerializationModule(),
           new SMTPServerModule());

       public static final Module CUSTOM_SERVER_MODULE = Modules.combine(
           new MailetProcessingModule(),
           new MailboxModule(),
           new MemoryDataModule(),
           new MemoryEventStoreModule(),
           new MemoryMailboxModule(),
           new MemoryMailQueueModule(),
           new TaskManagerModule(),
           new RawPostDequeueDecoratorModule(),
           binder -> binder.bind(MailetContainerModule.DefaultProcessorsConfigurationSupplier.class)
               .toInstance(BaseHierarchicalConfiguration::new));

       public static final Module CUSTOM_SERVER_AGGREGATE_MODULE = Modules.combine(
           CUSTOM_SERVER_MODULE,
           PROTOCOLS);

       public static void main(String[] args) throws Exception {
           Configuration configuration = Configuration.builder()
               .useWorkingDirectoryEnvProperty()
               .build();

           JamesServerMain.main(GuiceJamesServer.forConfiguration(configuration)
               .combineWith(CUSTOM_SERVER_AGGREGATE_MODULE));
       }
   }

You need to write a minimal main method launching your guice module composition.

We do provide in this example JIB to package this custom James assembly into docker:

<plugin>
    <groupId>com.google.cloud.tools</groupId>
    <artifactId>jib-maven-plugin</artifactId>
    <version>2.7.1</version>
    <configuration>
        <from>
            <image>adoptopenjdk:11-jdk-hotspot</image>
        </from>
        <to>
            <image>apache/james</image>
            <tags>
                <tag>custom-latest</tag>
            </tags>
        </to>
        <container>
            <mainClass>org.apache.james.examples.CustomJamesServerMain</mainClass>
            <ports>
                <port>25</port> <!-- JMAP -->
                <port>143</port> <!-- IMAP -->
            </ports>
            <appRoot>/root</appRoot>
            <jvmFlags>
                <jvmFlag>-Dlogback.configurationFile=/root/conf/logback.xml</jvmFlag>
                <jvmFlag>-Dworking.directory=/root/</jvmFlag>
            </jvmFlags>
            <creationTime>USE_CURRENT_TIMESTAMP</creationTime>
        </container>
        <extraDirectories>
            <paths>
                <path>
                    <from>sample-configuration</from>
                    <into>/root/conf</into>
                </path>
            </paths>
        </extraDirectories>
    </configuration>
    <executions>
        <execution>
            <goals>
                <goal>buildTar</goal>
            </goals>
            <phase>package</phase>
        </execution>
    </executions>
</plugin>}

We provide a minimal sample configuration.

You can compile this example project:

mvn clean install

Create a keystore (default password being james72laBalle):

keytool -genkey -alias james -keyalg RSA -keystore keystore

Import the build result:

$ docker load -i target/jib-image.tar

Then launch your custom server with docker:

docker run \
    -v $PWD/keystore:/root/conf/keystore \
    -p 25:25 \
    -p 143:143 \
    -ti  \
    apache/james:custom-latest
                

You will see that your custom James server starts smoothly:

...
09:40:25.884 [INFO ] o.a.j.GuiceJamesServer - JAMES server started