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.
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 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:
| Element | Default Class |
|---|---|
realm | org.bluezoo.gumdrop.auth.BasicRealm |
container | org.bluezoo.gumdrop.servlet.Container |
mailbox-factory | org.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>
<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.
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.
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:
gumdroprc file. If the configuration file is
/opt/gumdrop/etc/gumdroprc, then path="realm.xml"
resolves to /opt/gumdrop/etc/realm.xml./) are used as-is. This
allows referencing files outside the configuration directory when needed.path="../data/web"
resolves correctly.
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.
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.
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.
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>
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 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>
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>
The framework automatically converts property values to match setter parameter types. The following conversions are supported:
| Source | Target Type | Conversion |
|---|---|---|
value (String) | int / Integer | Integer.parseInt |
value (String) | long / Long | Long.parseLong |
value (String) | boolean / Boolean | Boolean.parseBoolean |
value (String) | double / Double | Double.parseDouble |
value (String) | float / Float | Float.parseFloat |
value (String) | Path | Paths.get (relative to CWD) |
value (String) | File | new File |
path (Path) | Path | injected directly |
path (Path) | String | path.toString() |
path (Path) | File | path.toFile() |
ref (Object) | any assignable type | injected 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.
gumdrop.workers — number of worker SelectorLoop threads
(default: 2× CPU cores)java.util.logging.config.file — logging configuration
file
Complete example configuration files are provided in the etc/
directory:
| File | Description |
|---|---|
gumdroprc.servlet | Servlet container with HTTP, HTTPS, and HTTP/3 |
gumdroprc.webdav | WebDAV file server with HTTP, HTTPS, and HTTP/3 |
gumdroprc.ftp.file.simple | Simple FTP file server |
gumdroprc.ftp.file.anonymous | Anonymous FTP (read-only) |
gumdroprc.ftp.file.rolebased | Role-based FTP with STARTTLS and implicit FTPS |
gumdroprc.imap | IMAP mailbox access with STARTTLS and IMAPS |
gumdroprc.pop3 | POP3 mailbox access with STARTTLS and POP3S |
gumdroprc.smtp.localdelivery | SMTP local delivery to Maildir mailboxes |
gumdroprc.smtp.simplerelay | Authenticated SMTP relay with STARTTLS |
gumdroprc.dns | DNS caching proxy with UDP, DoT, and DoQ |
Run any example with:
java -jar gumdrop.jar etc/gumdroprc.servlet
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