Web Application Deployment

Most of the time you want to be able to customize the deployment of your web applications, for example by changing the contextPath, or by adding JNDI entries, or by configuring virtual hosts, etc.

Jetty supports the deployment of each web application to a specific environment. The available environments are:

  • Java EE 8 — Supports Servlet 4.0 (and associated specifications) in the javax.* packages.

  • Jakarta EE 9 — Supports Servlet 5.0 (and associated specifications) in the jakarta.* packages.

  • Jakarta EE 10 — Supports Servlet 6.0 (and associated specifications) in the jakarta.* packages.

  • Jetty Core — Supports web applications written against the Jetty Handler APIs, without any Servlet dependencies.

This means that you can simultaneously deploy an old Java EE 8 web application, say old-ee8.war, alongside a new Jakarta EE 10 web application, say new-ee10.war, alongside a web application that only uses the Jetty Handler APIs, say app-jetty.xml.

The customization of the deployment (for example, web application context path, etc.) is performed by processing Jetty context XML files.

The deploy module contains the DeploymentManager component that scans the $JETTY_BASE/webapps directory for changes, following the deployment rules described in this section.

For each specific environment there is a specific deploy module that you must enable:

Each of these modules provide the environment specific features, and depend on the deploy module that provides the scanning features.

Hot vs Static Deployment

The DeploymentManager scans the $JETTY_BASE/webapps directory for changes every N seconds, where N is configured via the jetty.deploy.scanInterval property.

By default, the scan interval is 0 seconds, which means static deployment, and the DeploymentManager will not scan the $JETTY_BASE/webapps directory for changes. This means that to deploy/redeploy/undeploy a web application you will need to stop and restart Jetty.

Setting the scan interval to a value of 1 second (or greater) means that hot deployment is enabled: if a file is added/changed/removed from the $JETTY_BASE/webapps directory, the DeploymentManager will notice the change and respectively deploy/redeploy/undeploy the web application.

The following command line enables hot deployment by specifying the jetty.deploy.scanInterval property on the command line, and therefore only for this particular run:

$ java -jar $JETTY_HOME/start.jar jetty.deploy.scanInterval=1

To make hot deployment persistent, you need to edit the appropriate <env>-deploy module configuration file, $JETTY_BASE/start.d/<env>-deploy.ini (eg: ee10-deploy.ini), uncomment the module property jetty.deploy.scanInterval and change the value to 1 second (or greater):

<env>-deploy.ini
--module=deploy
jetty.deploy.scanInterval=1
...

Deployment Rules

Adding a *.war file, a *.war directory, a Jetty context XML file or a normal directory to $JETTY_BASE/webapps causes the DeploymentManager to deploy the new web application.

Updating a *.war file or a Jetty context XML file causes the DeploymentManager to redeploy the web application, which means that the Jetty context component representing the web application is stopped, then reconfigured, and then restarted.

Removing a *.war file, a *.war directory, a Jetty context XML file or a normal directory from $JETTY_BASE/webapps causes the DeploymentManager to undeploy the web application, which means that the Jetty context component representing the web application is stopped and removed from the Jetty server.

Context Path Resolution

When a file or directory is added to $JETTY_BASE/webapps, the DeploymentManager derives the web application contextPath from the file or directory name, with the following rules:

  • If the directory name is, for example, mywebapp/, it is deployed as a standard web application if it contains a WEB-INF/ subdirectory, otherwise it is deployed as a web application of static content. The contextPath would be /mywebapp (that is, the web application is reachable at http://localhost:8080/mywebapp/).

  • If the directory name is ROOT, case-insensitive, the contextPath is / (that is, the web application is reachable at http://localhost:8080/).

  • If the directory name ends with .d, for example config.d/, it is ignored, although it may be referenced to configure other web applications (for example to store common files).

  • If the *.war file name is, for example, mywebapp.war, it is deployed as a standard web application with the context path /mywebapp (that is, the web application is reachable at http://localhost:8080/mywebapp/).

  • If the file name is ROOT.war, case-insensitive, the contextPath is / (that is, the web application is reachable at http://localhost:8080/).

  • If both the mywebapp.war file and the mywebapp/ directory exist, only the file is deployed. This allows the directory with the same name to be the *.war file unpack location and avoid that the web application is deployed twice.

  • A Jetty context XML file named mywebapp.xml is deployed as a web application by processing the directives contained in the XML file itself, which must set the contextPath, which could be different from the name of the XML file.

  • If both mywebapp.xml and mywebapp.war exist, only the XML file is deployed. This allows the XML file to reference the *.war file and avoid that the web application is deployed twice.

Environment Resolution

A web application is always deployed to a specific environment, which is either configured for the deployed application or set to the default environment.

If only a single specific deployer module is enabled, for example ee10-deploy, then it is the default environment and applications will be deployed to it without any additional configuration.

If multiple deployer modules are enabled, then the default environment is:

  • The most recent Jakarta EE environment of the ee{8,9,10}-deploy modules that are enabled.

  • Otherwise, the core environment, if the core-deploy module is enabled.

  • Otherwise, no deployer environment has been enabled, and therefore no application can be deployed.

For example, if core-deploy, ee9-deploy and the ee10-deploy modules are enabled, then ee10 is the default environment, to which applications will be deployed unless otherwise configured (see below).

To configure a specific environment for an application, you add a *.properties file with the same name of the web application. For example, an application deployed to $JETTY_BASE/webapps/my-ee9-app.war is configured with the file $JETTY_BASE/webapps/my-ee9-app.properties, with the following content:

my-ee9-app.properties
environment=ee9

In case of simultaneous multiple deployer environments, it is good practice to always specify the *.properties file for your web applications.

If you do not specify the *.properties file for your web applications, then the deployer for the default environment will be used.

For example, if you have enabled the deployer Jetty module for all Jakarta EE versions, and you deploy an EE 9 web application without the *.properties file, then it will be deployed by the EE 10 deployer, with unspecified results.

This unspecified deployment may not work as the EE 9 web application may use APIs that have been removed in EE 10, causing an error at runtime.

Deploying Jetty Context XML Files

A Jetty context XML file is a Jetty XML file that allows you to customize the deployment of web applications.

Recall that the DeploymentManager component of the Jetty deploy module gives priority to Jetty context XML files over *.war files or directories.

To deploy a web application using a Jetty context XML file, simply place the file in the $JETTY_BASE/webapps directory.

A simple Jetty context XML file, for example named wiki.xml is the following:

wiki.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "https://jetty.org/configure_10_0.dtd">

<Configure class="org.eclipse.jetty.ee10.webapp.WebAppContext"> (1)
  <Set name="contextPath">/wiki</Set> (2)
  <Set name="war">/opt/myapps/myapp.war</Set> (3)
</Configure>
1 Configures a WebAppContext, which is the Jetty component that represents a standard Servlet web application.
2 Specifies the web application contextPath, which may be different from the *.war file name.
3 Specifies the file system path of the *.war file.

The Jetty content XML file may be accompanied by a *.properties file that specifies the environment to use for the deployment:

wiki.properties
environment=ee10

Refer to this section for more information about specifying the environment.

The $JETTY_BASE directory would look like this:

$JETTY_BASE
├── resources
│   └── jetty-logging.properties
├── start.d
│   ├── deploy.ini
│   └── http.ini
└── webapps
    ├── wiki.properties
    └── wiki.xml
The *.war file may be placed anywhere in the file system and does not need to be placed in the $JETTY_BASE/webapps directory.
If you place both the Jetty context XML file and the *.war file in the $JETTY_BASE/webapps directory, remember that they must have the same file name, for example wiki.xml and wiki.war, so that the DeploymentManager deploys the web application only once using the Jetty context XML file (and not the *.war file).

You can use the features of Jetty XML files to avoid to hard-code file system paths or other configurations in your Jetty context XML files, for example by using system properties:

wiki.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "https://jetty.org/configure_10_0.dtd">

<Configure class="org.eclipse.jetty.ee10.webapp.WebAppContext">
  <Set name="contextPath">/wiki</Set>
  <Set name="war"><SystemProperty name="myapps.dir"/>/myapp.war</Set>
</Configure>

Note how the *.war file path is now obtained by resolving the system property myapps.dir that you can specify on the command line when you start Jetty:

$ java -jar $JETTY_HOME/start.jar -Dmyapps.dir=/opt/myapps

Deploying Environment Specific Context XML Files

Jetty context XML file files can be applied to all webapps deployed to a particular environment.

Add a properties file to the deployment directory that contains a property naming the location of the xml file to apply. The properties file name must be prefixed by the name of the environment. For example ee8.properties, ee8-more.properties ee8-other.properties would all apply to all contexts deployed in the ee8 environment.

If the property file(s) contain one or more properties whose names are prefixed with org.eclipse.jetty.deploy.environmentXml, then their values are used as additional context XML files to apply to all contexts deployed in the corresponding environment. So for example org.eclipse.jetty.deploy.environmentXml, org.eclipse.jetty.deploy.environmentXml.more, org.eclipse.jetty.deploy.environmentXml.other are all acceptable as names. Each property configures the location of a context XML file to apply to a context when it is being created and deployed. The location may be either absolute or relative to the parent of the deployment directory. So if your webapp deployment directory is $JETTY_BASE/webapps, then $JETTY_BASE will be used to resolve any relative filenames.

All environment-specific Jetty context XML files will be applied to the webapp before any context XML file associated with the webapp. The order in which they are applied is determined by the name of the properties that define them.

The contents of the environment specific context XML file may only contain references to classes appropriate for that environment.

For example, given the previous example of a $JETTY_BASE/webapps/wiki.xml and its accompanying $JETTY_BASE/webapps/wiki.properties file that declares the wiki webapp should be deployed to environment ee10, files called $JETTY_BASE/webapps/ee10.properties and $JETTY_BASE/webapps/ee10-feature.properties can be defined to further configure the webapp.

The ee10.properties file contains:

ee10.properties
jetty.deploy.environmentXml=etc/ee10-context.xml

The ee10-feature.properties file contains:

ee10-feature.properties
jetty.deploy.environmentXml.feature=etc/ee10-feature.xml

The ee10-context.xml file contains:

ee10-context.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "https://jetty.org/configure_10_0.dtd">

<Configure id="wac" class="org.eclipse.jetty.ee10.webapp.WebAppContext">
  <Call name="setAttribute">
    <Arg>common</Arg>
    <Arg>value</Arg>
  </Call>
</Configure>

The ee10-feature.xml file contains:

ee10-feature.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "https://jetty.org/configure_10_0.dtd">

<Configure id="wac" class="org.eclipse.jetty.ee10.webapp.WebAppContext">
  <Call name="addEventListener">
    <Arg>
      <New class="com.example.MyListenerFeature"/>
    </Arg>
  </Call>
</Configure>

The directory structure would look like this:

directory structure
$JETTY_BASE
|- etc
│  |-ee10-context.xml
|  |-ee10-feature.xml
|- webapps
    |-ee10.properties
    |-ee10-feature.properties
    |-wiki.properties
    |- wiki.xml

The contents of the $JETTY_BASE/etc/ee10-context.xml then $JETTY_BASE/etc/ee10-feature.xml files will be applied to the wiki webapp instance before wiki.xml, allowing the contents of the latter to override the contents of the former.

WEB-INF/jetty-ee{8,9,10}-web.xml

As discussed above, a Jetty context XML file can be used to configure a webapp during deployment. The webapp can also be configured during its startup phase by a WEB-INF/jetty-ee{8,9,10}-web.xml file. The contents of this file is the same as a Jetty context XML file. This file can be useful:

  • to place all configuration inside your webapp archive

  • to perform configuration that can only occur after the webapp’s classpath has been created

it is good practice to name the file according to the environment into which the webapp will be deployed. If your webapp can be deployed to multiple environments then you should include a WEB-INF file for each one. If you only deploy to a single environment then you can omit it from the filename, however be aware that you cannot change environments without updating the contents of the file.

Configuring JNDI Entries

A web application may reference a JNDI entry, such as a JDBC DataSource from the web application web.xml file. The JNDI entry must be defined in a Jetty XML file, for example a context XML like so:

mywebapp.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "https://jetty.org/configure_10_0.dtd">

<Configure id="wac" class="org.eclipse.jetty.ee10.webapp.WebAppContext">
  <Set name="contextPath">/mywebapp</Set>
  <Set name="war">/opt/webapps/mywebapp.war</Set>
    <New class="org.eclipse.jetty.plus.jndi.Resource">
    <Arg><Ref refid="wac"/></Arg>
    <Arg>jdbc/myds</Arg>
     <Arg>
        <New class="com.mysql.cj.jdbc.MysqlConnectionPoolDataSource">
           <Set name="url">jdbc:mysql://localhost:3306/databasename</Set>
           <Set name="user">user</Set>
           <Set name="password">password</Set>
        </New>
     </Arg>
  </New>
</Configure>

For more information and examples on how to use JNDI in Jetty, refer to the JNDI feature section.

Class com.mysql.cj.jdbc.MysqlConnectionPoolDataSource is present in the MySQL JDBC driver file, mysql-connector-java-<version>.jar, which must be available on the server’s classpath .

If the class is instead present within the web application, then the JNDI entry must be declared in a WEB-INF/jetty-ee{8,9,10}-env.xml file - which is applied after the webapp’s classpath has been set up - see the JNDI feature section for more information and examples.

Configuring Virtual Hosts

A virtual host is an internet domain name, registered in the Domain Name Server (DNS), for an IP address such that multiple virtual hosts will resolve to the same IP address of a single server instance.

If you have multiple web applications deployed on the same Jetty server, by using virtual hosts you will be able to target a specific web application.

For example, you may have a web application for your business and a web application for your hobbies , both deployed in the same Jetty server. By using virtual hosts, you will be able to have the first web application available at http://domain.biz/, and the second web application available at http://hobby.net/.

Another typical case is when you want to use different subdomains for different web application, for example a project website is at http://project.org/ and the project documentation is at http://docs.project.org.

Virtual hosts can be used with any context that is a subclass of ContextHandler.

Virtual Host Names

Jetty supports the following variants to be specified as virtual host names:

www.hostname.com

A fully qualified domain name. It is important to list all variants as a site may receive traffic for both www.hostname.com and hostname.com.

*.hostname.com

A wildcard domain name which will match only one level of arbitrary subdomains. *.foo.com will match www.foo.com and m.foo.com, but not www.other.foo.com.

10.0.0.2

An IP address may be set as a virtual host to indicate that a web application should handle requests received on the network interface with that IP address for protocols that do not indicate a host name such as HTTP/0.9 or HTTP/1.0.

@ConnectorName

A Jetty server Connector name to indicate that a web application should handle requests received on the server Connector with that name, and therefore received on a specific socket address (either an IP port for ServerConnector, or a Unix-Domain path for UnixDomainServerConnector). A server Connector name can be set via https://javadoc.jetty.org/jetty-12/org/eclipse/jetty/server/AbstractConnector.html#setName(java.lang.String).

www.√integral.com

Non-ASCII and IDN domain names can be set as virtual hosts using Puny Code equivalents that may be obtained from a Punycode/IDN converters. For example if the non-ASCII domain name www.√integral.com is given to a browser, then the browser will make a request that uses the domain name www.xn—​integral-7g7d.com, which is the name that should be added as the virtual host name.

Virtual Hosts Configuration

If you have a web application mywebapp.war you can configure its virtual hosts in this way:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "https://jetty.org/configure_10_0.dtd">

<Configure class="org.eclipse.jetty.ee10.webapp.WebAppContext">
  <Set name="contextPath">/mywebapp</Set>
  <Set name="war">/opt/webapps/mywebapp.war</Set>
  <Set name="virtualHosts">
    <Array type="java.lang.String">
      <Item>mywebapp.com</Item>
      <Item>www.mywebapp.com</Item>
      <Item>mywebapp.net</Item>
      <Item>www.mywebapp.net</Item>
    </Array>
  </Set>
</Configure>

Your web application will be available at:

  • http://mywebapp.com/mywebapp

  • http://www.mywebapp.com/mywebapp

  • http://mywebapp.net/mywebapp

  • http://www.mywebapp.net/mywebapp

You configured the contextPath of your web application to /mywebapp.

As such, a request to http://mywebapp.com/other will not match your web application because the contextPath does not match.

Likewise, a request to http://other.com/mywebapp will not match your web application because the virtual host does not match.

Same Context Path, Different Virtual Hosts

If you want to deploy different web applications to the same context path, typically the root context path /, you must use virtual hosts to differentiate among web applications.

You have domain.war that you want to deploy at http://domain.biz/ and hobby.war that you want to deploy at http://hobby.net.

To achieve this, you simply use the same context path of / for each of your webapps, while specifying different virtual hosts for each of your webapps:

domain.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "https://jetty.org/configure_10_0.dtd">

<Configure class="org.eclipse.jetty.ee10.webapp.WebAppContext">
  <Set name="contextPath">/</Set>
  <Set name="war">/opt/webapps/domain.war</Set>
  <Set name="virtualHosts">
    <Array type="java.lang.String">
      <Item>domain.biz</Item>
    </Array>
  </Set>
</Configure>
hobby.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "https://jetty.org/configure_10_0.dtd">

<Configure class="org.eclipse.jetty.ee10.webapp.WebAppContext">
  <Set name="contextPath">/</Set>
  <Set name="war">/opt/webapps/hobby.war</Set>
  <Set name="virtualHosts">
    <Array type="java.lang.String">
      <Item>hobby.net</Item>
    </Array>
  </Set>
</Configure>

Different Port, Different Web Application

Sometimes it is required to serve different web applications from different socket addresses (either different IP ports, or different Unix-Domain paths), and therefore from different server Connectors.

For example, you want requests to http://localhost:8080/ to be served by one web application, but requests to http://localhost:9090/ to be served by another web application.

This configuration may be useful when Jetty sits behind a load balancer.

In this case, you want to configure multiple connectors, each with a different name, and then reference the connector name in the web application virtual host configuration:

domain.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "https://jetty.org/configure_10_0.dtd">

<Configure class="org.eclipse.jetty.ee10.webapp.WebAppContext">
  <Set name="contextPath">/</Set>
  <Set name="war">/opt/webapps/domain.war</Set>
  <Set name="virtualHosts">
    <Array type="java.lang.String">
      <Item>@port8080</Item>
    </Array>
  </Set>
</Configure>
hobby.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "https://jetty.org/configure_10_0.dtd">

<Configure class="org.eclipse.jetty.ee10.webapp.WebAppContext">
  <Set name="contextPath">/</Set>
  <Set name="war">/opt/webapps/hobby.war</Set>
  <Set name="virtualHosts">
    <Array type="java.lang.String">
      <Item>@port9090</Item>
    </Array>
  </Set>
</Configure>

Web application domain.war has a virtual host of @port8080, where port8080 is the name of a Jetty connector.

Likewise, web application hobby.war has a virtual host of @port9090, where port9090 is the name of another Jetty connector.

See this section for further information about how to configure connectors.

Configuring *.war File Extraction

By default, *.war files are uncompressed and its content extracted in a temporary directory. The web application resources are served by Jetty from the files extracted in the temporary directory, not from the files within the *.war file, for performance reasons.

If you do not want Jetty to extract the *.war files, you can disable this feature, for example:

mywebapp.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "https://jetty.org/configure_10_0.dtd">

<Configure class="org.eclipse.jetty.ee10.webapp.WebAppContext">
  <Set name="contextPath">/mywebapp</Set>
  <Set name="war">/opt/webapps/mywebapp.war</Set>
  <Set name="extractWAR">false</Set>
</Configure>

Overriding web.xml

You can configure an additional web.xml that complements the web.xml file that is present in the web application *.war file. This additional web.xml is processed after the *.war file web.xml. This allows you to add host specific configuration or server specific configuration without having to extract the web application web.xml, modify it, and repackage it in the *.war file.

mywebapp.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "https://jetty.org/configure_10_0.dtd">

<Configure class="org.eclipse.jetty.ee10.webapp.WebAppContext">
  <Set name="contextPath">/mywebapp</Set>
  <Set name="war">/opt/webapps/mywebapp.war</Set>
  <Set name="overrideDescriptor">/opt/webapps/mywebapp-web.xml</Set>
</Configure>

The format of the additional web.xml is exactly the same as a standard web.xml file, for example:

mywebapp-web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee https://jakarta.ee/xml/ns/jakartaee/web-app_6_0.xsd"
         version="6.0">
  <servlet>
    <servlet-name>my-servlet</servlet-name>
    <init-param>
      <param-name>host</param-name>
      <param-value>192.168.0.13</param-value>
    </init-param>
  </servlet>
</web-app>

In the example above, you configured the my-servlet Servlet (defined in the web application web.xml), adding a host specific init-param with the IP address of the host.

Configuring init-params

TODO