Gumdrop

Gumdrop Configuration

Gumdrop uses a lightweight dependency injection (DI) framework to wire together its components. A gumdroprc XML configuration file defines components with properties, references, and listeners, and the framework instantiates, configures, and starts them.

Contents

Overview

The configuration file is an XML document with <gumdrop> as the root element. Inside it, top-level elements declare components: objects that are instantiated, configured via property injection, and registered in a component registry. Components can reference each other by ID, forming the object graph that constitutes a running server.

A minimal configuration declares at least one <service> with a <listener>:

<gumdrop>
    <service id="http" class="org.bluezoo.gumdrop.servlet.ServletService">
        <listener class="org.bluezoo.gumdrop.http.HTTPListener">
            <property name="port" value="8080"/>
        </listener>
    </service>
</gumdrop>

Components

Components are declared as XML elements with id and class attributes. The id is a unique name used to reference the component elsewhere. The class is the fully-qualified Java class name to instantiate (the class must have a public no-argument constructor).

Several element names are recognised as shortcuts for common component types:

ElementDefault Class
realmorg.bluezoo.gumdrop.auth.BasicRealm
containerorg.bluezoo.gumdrop.servlet.Container
mailbox-factoryorg.bluezoo.gumdrop.mailbox.mbox.MboxMailboxFactory
component(must specify class)

When using a shortcut element name, the class attribute can be omitted to use the default, or overridden to use a different implementation:

<!-- Uses default BasicRealm -->
<realm id="myRealm">
    <property name="href" path="users.xml"/>
</realm>

<!-- Overrides default to use MaildirMailboxFactory -->
<mailbox-factory id="maildir"
        class="org.bluezoo.gumdrop.mailbox.maildir.MaildirMailboxFactory">
    <property name="base-directory" path="mail"/>
</mailbox-factory>

Properties

<property> elements set bean properties on the enclosing component. The name attribute identifies the property (using hyphenated naming, e.g. keystore-file maps to the setter setKeystoreFile). Values can be specified in three ways: as a plain string, as a file path resolved relative to the configuration file, or as a reference to another component.

String Values — value

The value attribute provides a plain string value. The framework performs automatic type conversion to match the setter's parameter type (see Type Conversion).

<property name="port" value="8080"/>
<property name="secure" value="true"/>
<property name="hostname" value="mail.example.com"/>
<property name="idle-timeout" value="30m"/>

Use value for all non-path scalar properties: ports, flags, hostnames, timeouts, passwords, display strings, and any other configuration that is not a filesystem path.

File Path Values — path

The path attribute provides a filesystem path that is resolved relative to the configuration file's directory. The parser converts the attribute value into an absolute java.nio.file.Path object before injecting it.

<property name="href" path="realm.xml"/>
<property name="root-directory" path="web"/>
<property name="keystore-file" path="certs/keystore.p12"/>
<property name="base-directory" path="/var/mail"/>

Resolution works as follows:

Use path for any property that refers to a file or directory: realm configuration files, TLS keystores, certificates, private keys, root directories for file-based services, and mailbox base directories.

Because path values are resolved relative to the configuration file, configurations are portable — moving the entire configuration directory preserves all relative references without modification.

Component References — ref

The ref attribute injects a reference to another component by its ID (prefixed with #):

<property name="realm" ref="#myRealm"/>
<property name="mailbox-factory" ref="#maildir"/>
<property name="telemetry-config" ref="#telemetry"/>

The referenced component must be declared before it is referenced (earlier in the configuration file). The framework resolves the reference and injects the component instance directly.

Text Content (Legacy)

For backward compatibility, property values can also be specified as text content of the <property> element:

<property name="port">8080</property>

This form is supported but not recommended for new configurations. Attribute syntax (value or path) is preferred because it reserves element content for structured children such as <list> and <map>, and provides a clear distinction between plain strings and filesystem paths.

Collections

Properties that accept java.util.List or java.util.Map types use structured child elements:

<property name="realms">
    <map>
        <entry key="myRealm" ref="#myRealm"/>
        <entry key="Manager" ref="#managerRealm"/>
    </map>
</property>

<property name="contexts">
    <list>
        <context path="" root="web"/>
        <context path="/api" root="api.war"/>
    </list>
</property>

Services and Listeners

A <service> element declares a service component that manages one or more listeners. Listeners are declared as <listener> child elements of the service, each specifying the protocol listener class.

Non-path scalar attributes (port, secure, name) can appear as inline XML attributes on the <listener> element. Path-typed properties and the keystore password should be specified as <property> child elements:

<service id="ftp" class="org.bluezoo.gumdrop.ftp.file.RoleBasedFTPService">
    <property name="realm" ref="#ftpRealm"/>
    <property name="root-directory" path="data"/>

    <!-- Plaintext listener with STARTTLS -->
    <listener class="org.bluezoo.gumdrop.ftp.FTPListener" port="21">
        <property name="keystore-file" path="certs/keystore.p12"/>
        <property name="keystore-pass" value="secret"/>
    </listener>

    <!-- Implicit TLS listener -->
    <listener class="org.bluezoo.gumdrop.ftp.FTPListener" port="990" secure="true">
        <property name="keystore-file" path="certs/keystore.p12"/>
        <property name="keystore-pass" value="secret"/>
    </listener>
</service>

This convention keeps the <listener> element readable: simple scalars are visible at a glance as attributes, while paths benefit from the path attribute's relative resolution.

HTTP/3 Listeners

HTTP/3 uses QUIC over UDP and requires PEM certificate and key files rather than a Java keystore:

<listener class="org.bluezoo.gumdrop.http.h3.HTTP3Listener">
    <property name="port" value="443"/>
    <property name="cert-file" path="certs/cert.pem"/>
    <property name="key-file" path="certs/key.pem"/>
</listener>

Multiple Listeners

A service can have any number of listeners, enabling a single service to accept connections on multiple ports and protocols simultaneously:

<service id="web" class="org.bluezoo.gumdrop.webdav.WebDAVService">
    <property name="root-path" path="web"/>

    <!-- HTTP -->
    <listener class="org.bluezoo.gumdrop.http.HTTPListener">
        <property name="port" value="8080"/>
    </listener>

    <!-- HTTPS -->
    <listener class="org.bluezoo.gumdrop.http.HTTPListener">
        <property name="port" value="8443"/>
        <property name="secure" value="true"/>
        <property name="keystore-file" path="localhost+2.p12"/>
        <property name="keystore-pass" value="changeit"/>
    </listener>

    <!-- HTTP/3 -->
    <listener class="org.bluezoo.gumdrop.http.h3.HTTP3Listener">
        <property name="port" value="8443"/>
        <property name="cert-file" path="localhost+2.pem"/>
        <property name="key-file" path="localhost+2-key.pem"/>
    </listener>
</service>

Type Conversion

The framework automatically converts property values to match setter parameter types. The following conversions are supported:

SourceTarget TypeConversion
value (String)int / IntegerInteger.parseInt
value (String)long / LongLong.parseLong
value (String)boolean / BooleanBoolean.parseBoolean
value (String)double / DoubleDouble.parseDouble
value (String)float / FloatFloat.parseFloat
value (String)PathPaths.get (relative to CWD)
value (String)Filenew File
path (Path)Pathinjected directly
path (Path)Stringpath.toString()
path (Path)Filepath.toFile()
ref (Object)any assignable typeinjected directly

When a setter has overloads accepting different types (e.g. both Path and String), the path attribute prefers the Path overload, and the value attribute prefers the String overload. This distinction is important: path values are resolved against the configuration file location, while value strings that happen to be converted to Path or File are resolved against the current working directory.

System Properties

Example Configurations

Complete example configuration files are provided in the etc/ directory:

FileDescription
gumdroprc.servletServlet container with HTTP, HTTPS, and HTTP/3
gumdroprc.webdavWebDAV file server with HTTP, HTTPS, and HTTP/3
gumdroprc.ftp.file.simpleSimple FTP file server
gumdroprc.ftp.file.anonymousAnonymous FTP (read-only)
gumdroprc.ftp.file.rolebasedRole-based FTP with STARTTLS and implicit FTPS
gumdroprc.imapIMAP mailbox access with STARTTLS and IMAPS
gumdroprc.pop3POP3 mailbox access with STARTTLS and POP3S
gumdroprc.smtp.localdeliverySMTP local delivery to Maildir mailboxes
gumdroprc.smtp.simplerelayAuthenticated SMTP relay with STARTTLS
gumdroprc.dnsDNS caching proxy with UDP, DoT, and DoQ

Run any example with:

java -jar gumdrop.jar etc/gumdroprc.servlet

Full Example

The following example shows a mail server configuration using most of the features described above:

<gumdrop>
    <!-- Authentication realm -->
    <realm id="myRealm">
        <property name="href" path="realm.xml"/>
    </realm>

    <!-- Mailbox storage -->
    <mailbox-factory id="maildir"
            class="org.bluezoo.gumdrop.mailbox.maildir.MaildirMailboxFactory">
        <property name="base-directory" path="/var/mail"/>
    </mailbox-factory>

    <!-- Telemetry -->
    <component id="telemetry" class="org.bluezoo.gumdrop.telemetry.TelemetryConfig">
        <property name="enabled" value="true"/>
        <property name="service-name" value="mail-server"/>
        <property name="endpoint" value="http://collector:4318"/>
    </component>

    <!-- IMAP service with implicit TLS -->
    <service id="imaps" class="org.bluezoo.gumdrop.imap.IMAPService">
        <property name="realm" ref="#myRealm"/>
        <property name="mailbox-factory" ref="#maildir"/>
        <property name="telemetry-config" ref="#telemetry"/>
        <listener class="org.bluezoo.gumdrop.imap.IMAPListener" port="993" secure="true">
            <property name="keystore-file" path="keystore.p12"/>
            <property name="keystore-pass" value="secret"/>
        </listener>
    </service>
</gumdrop>

Gumdrop