//
// ========================================================================
// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//

= Custom Jetty Modules

In addition to the modules that come packaged with Jetty, you can create your own custom modules.

NOTE: Make sure you have read the xref:modules/index.adoc[Jetty modules section] if you are not familiar with the concepts used in this section.

Custom modules can be used for a number of reasons -- they can extend Jetty features, or add new features, or make additional libraries available to the server, etc.

[[modify]]
== Modifying an Existing Module

The standard Jetty modules typically come with a number of configurable properties that can be easily customized without the need of writing a custom module.

However, there may be cases where the customization is more complex than a simple property, and a custom module is necessary.

For example, let's assume that you want to modify the order of the TLS cipher suites offered by the server when a client connects, using the https://www.openssl.org/docs/man1.1.0/man1/ciphers.html[OpenSSL cipher list format].

The Jetty class that handles the TLS configuration is `SslContextFactory`, and it already has a method `setCipherComparator(Comparator<String>)`; however, you need to pass your custom implementation, which cannot be represented with a simple module property.

The `SslContextFactory` component is already allocated by the standard Jetty module `ssl`, so what you need to do is the following:

* Write the custom cipher `Comparator` and package it into a `+*.jar+` file (exercise left to reader).
* Write a custom Jetty XML file that calls the `SslContextFactory.setCipherComparator(Comparator<String>)` method.
* Write a custom Jetty module file that depends on the standard `ssl` module.

Start with the custom Jetty XML file, `$JETTY_BASE/etc/custom-ssl.xml`:

.custom-ssl.xml
[,xml]
----
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "https://jetty.org/configure_10_0.dtd">
<Configure>
  <Ref refid="sslContextFactory"> <!--1-->
    <Set name="CipherComparator"> <!--2-->
      <New class="com.acme.ssl.CustomCipherComparator"> <!--3-->
        <Arg>
          <Property name="com.acme.ssl.cipherList"> <!--4-->
            <Default>ECDH+AESGCM:ECDH+AES256:!aNULL:!MD5:!DSS:!ADH</Default>
          </Property>
        </Arg>
      </New>
    </Set>
  </Ref>
</Configure>
----
<1> Reference the existing `SslContextFactory` object created by the standard `ssl` module using its `id`.
<2> Call the `setCipherComparator()` method.
<3> Instantiate your custom cipher comparator.
<4> Pass to the constructor the ordering string in OpenSSL format, reading it from the module property `com.acme.ssl.cipherList`.

CAUTION: The cipher list used above may not be secure -- it's just an example.

Then write your custom module in the `$JETTY_BASE/modules/custom-ssl.mod` file:

.custom-ssl.mod
[source]
----
[description]
Customizes the standard ssl module.

[tags] <1>
acme

[depends] <2>
ssl

[lib] <3>
lib/custom-cipher-comparator.jar

[xml] <4>
etc/custom-ssl.xml

[ini-template] <5>
## The cipher list in OpenSSL format.
# com.acme.ssl.cipherList=ECDH+AESGCM:ECDH+AES256:!aNULL:!MD5:!DSS:!ADH

----
<1> A tag that characterizes this custom module (see xref:modules/index.adoc#directive-tags[here]).
<2> This custom module depends on the standard `ssl` module.
<3> The custom cipher comparator class is compiled and packaged into this `+*.jar+` file.
<4> The custom Jetty XML file from above.
<5> The text that will be copied in the `custom-ssl.ini` file when this custom module will be enabled.

Now you can xref:start/index.adoc#configure-enable[enable] the custom module with the following command issued from the `$JETTY_BASE` directory:

----
$ java -jar $JETTY_HOME/start.jar --add-modules=https,custom-ssl
----

The command above will produce the following `$JETTY_BASE` directory structure:

[source,subs=+quotes]
----
$JETTY_BASE
├── etc
│   └── custom-ssl.xml
├── modules
│   └── custom-ssl.mod
├── resources
│   └── jetty-logging.properties
└── start.d
    ├── https.ini
    └── ##custom-ssl.ini##
----

In the custom XML file you have used a custom module property to parametrize your custom cipher comparator.
This custom module property was then referenced in the `[ini-template]` section of the custom module file, so that when the custom module is enabled, a correspondent `custom-ssl.ini` file is created.

In this way, updating the cipher list won't require you to update the XML file, but just the `custom-ssl.ini` file.

[[create]]
== Creating a New Module

In the cases where you need to enhance Jetty with a custom functionality, you can write a new Jetty module that provides it.

For example, let's assume that you need to add a custom auditing component that integrates with the auditing tools used by your company.
This custom auditing component should measure the HTTP request processing times and record them (how they are recorded is irrelevant here -- could be in a local log file or sent via network to an external service).

The Jetty libraries already provide a way to measure HTTP request processing times via xref:programming-guide:server/http.adoc#handler-use-events[`EventsHandler`]: you write a custom `EventsHandler` subclass that overrides the methods corresponding to the events you are interested in.

The steps to create a Jetty module are similar to those necessary to <<modify,modify an existing module>>:

* Write the auditing component and package it into a `+*.jar+` file.
* Write a custom Jetty XML file that wires the auditing component to the `Handler` tree.
* Write a custom Jetty module file that puts everything together.

Let's start with the auditing component, sketched below:

[,java]
----
package com.acme.audit;

public class AuditingEventsHandler extends EventsHandler {
    // Auditing is implemented here.
}
----

Let's assume that this class is compiled and packaged into `acme-audit.jar`, and that it has a dependency on `acme-util.jar`.
Both `+*.jar+` files will be put in the `$JETTY_BASE/lib/` directory.

Next, let's write the Jetty XML file that wires the auditing component to the `ServerConnector`, `$JETTY_BASE/etc/acme-audit.xml`:

.acme-audit.xml
[,xml,options=nowrap]
----
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "https://jetty.org/configure_10_0.dtd">
<Configure>
  <Ref refid="Server"> <!--1-->
    <Call name="insertHandler"> <!--2-->
      <Arg>
        <New class="com.acme.audit.AuditingEventsHandler"> <!--3-->
          <Set name="someProperty">
            <Property name="com.acme.audit.some.property" default="42" /> <!--4-->
          </Set>
        </New>
      </Arg>
    </Call>
  </Ref>
</Configure>
----
<1> Reference `Server` instance.
<2> Call `insertHandler()` on the `Server` so that the auditing component will be inserted just after the `Server` and just before its child `Handler`.
<3> Instantiate the auditing component.
<4> Configure the auditing component with a property.

The last step is to create the custom Jetty module file for the auditing component, `$JETTY_BASE/modules/acme-audit.mod`:

.acme-audit.mod
----
[description]
Adds ACME auditing to the Jetty Server.

[tags] <1>
acme
audit

[depends] <2>
server

[libs] <3>
lib/acme-audit.jar
lib/acme-util.jar

[xml] <4>
etc/acme-audit.xml

[ini-template] <5>
## An auditing property.
# com.acme.audit.some.property=42
----
<1> The tags that characterize this custom module (see xref:modules/index.adoc#directive-tags[here]).
<2> This custom module depends on the standard `server` module.
<3> The `+*.jar+` files that contains the custom auditing component, and its dependencies.
<4> The custom Jetty XML file from above.
<5> The text that will be copied in the `acme-audit.ini` file when this custom module will be enabled.

Now you can xref:start/index.adoc#configure-enable[enable] the custom auditing module with the following command issued from the `$JETTY_BASE` directory:

[source]
----
$ java -jar $JETTY_HOME/start.jar --add-modules=http,acme-audit
----

The command above will produce the following `$JETTY_BASE` directory structure:

[source,subs=+quotes]
----
$JETTY_BASE
├── etc
│   └── acme-audit.xml
├── modules
│   └── acme-audit.mod
├── resources
│   └── jetty-logging.properties
└── start.d
    ├── http.ini
    └── ##acme-audit.ini##
----

Enabling the custom auditing component will create the `$JETTY_BASE/start.d/acme-audit.ini` module configuration file that you can edit to configure auditing properties.
