embedded-jettyjetty-9

How to enable embedded jetty 9 in SSL mode through xml configuration?


I have used single jetty.xml and added my contexts and http configuration here to handle my requests. I have followed this approach as I was using old jetty 6 earlier and was upgrading to 9 where they have divided the files and I was confused with the configurations mentioned in newer jetty.

<?xml version="1.0"?>
<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_3.dtd">

<!-- =============================================================== -->
<!-- Documentation of this file format can be found at:              -->
<!-- https://www.eclipse.org/jetty/documentation/current/            -->
<!--                                                                 -->
<!-- Additional configuration files are available in $JETTY_HOME/etc -->
<!-- and can be mixed in. See start.ini file for the default         -->
<!-- configuration files.                                            -->
<!--                                                                 -->
<!-- For a description of the configuration mechanism, see the       -->
<!-- output of:                                                      -->
<!--   java -jar start.jar -?                                        -->
<!-- =============================================================== -->

<!-- =============================================================== -->
<!-- Configure a Jetty Server instance with an ID "Server"           -->
<!-- Other configuration files may also configure the "Server"       -->
<!-- ID, in which case they are adding configuration to the same     -->
<!-- instance.  If other configuration have a different ID, they     -->
<!-- will create and configure another instance of Jetty.            -->
<!-- Consult the javadoc of o.e.j.server.Server for all              -->
<!-- configuration that may be set here.                             -->
<!-- =============================================================== -->
<Configure id="Server" class="org.eclipse.jetty.server.Server">

    <New id="threadPool" class="org.eclipse.jetty.util.thread.QueuedThreadPool">
        <Set name="minThreads" type="int"><Property name="jetty.threadPool.minThreads" deprecated="threads.min" default="10"/></Set>
        <Set name="maxThreads" type="int"><Property name="jetty.threadPool.maxThreads" deprecated="threads.max" default="200"/></Set>
        <Set name="reservedThreads" type="int"><Property name="jetty.threadPool.reservedThreads" default="-1"/></Set>
        <Set name="idleTimeout" type="int"><Property name="jetty.threadPool.idleTimeout" deprecated="threads.timeout" default="60000"/></Set>
        <Set name="detailedDump" type="boolean"><Property name="jetty.threadPool.detailedDump" default="false"/></Set>
    </New>


    <!-- =========================================================== -->
    <!-- Add shared Scheduler instance                               -->
    <!-- =========================================================== -->
    <Call name="addBean">
        <Arg>
            <New class="org.eclipse.jetty.util.thread.ScheduledExecutorScheduler">
                <Arg name="name"><Property name="jetty.scheduler.name"/></Arg>
                <Arg name="daemon" type="boolean"><Property name="jetty.scheduler.daemon" default="false" /></Arg>
                <Arg name="threads" type="int"><Property name="jetty.scheduler.threads" default="-1" /></Arg>
            </New>
        </Arg>
    </Call>

    <Set name="handler">
        <New id="Handlers" class="org.eclipse.jetty.server.handler.HandlerCollection">
            <Set name="handlers">
                <Array type="org.eclipse.jetty.server.Handler">
                    <Item>
                        <New id="Contexts" class="org.eclipse.jetty.server.handler.ContextHandlerCollection"/>
                    </Item>
<!--                    <Item>-->
<!--                        <New id="DefaultHandler" class="org.eclipse.jetty.server.handler.DefaultHandler"/>-->
<!--                    </Item>-->
                    <Item>
                        <New id="RequestLog" class="org.eclipse.jetty.server.handler.RequestLogHandler"/>
                    </Item>
                </Array>
            </Set>
        </New>
    </Set>
    
    <!-- ======================================================= -->
    <!-- Configure a Context for CTS                              -->
    <!-- ======================================================= -->
    <New class="org.eclipse.jetty.servlet.ServletContextHandler">
        <Arg>
            <Ref id="Contexts"/>
        </Arg>
        <Arg>/cts</Arg>
        <Set name="resourceBase">
            <SystemProperty name="jetty.home" default="."/>/</Set>
        <Set name="sessionHandler">
            <New class="org.eclipse.jetty.server.session.SessionHandler"/>
        </Set>
        <Call name="addServlet">
            <Arg>com.documentum.cts.webservices.remote.TransformationServlet</Arg>
            <Arg>/</Arg>
        </Call>
    </New>
    <New class="org.eclipse.jetty.servlet.ServletContextHandler">
        <Arg>
            <Ref id="Contexts"/>
        </Arg>
        <Arg>/cts/resource</Arg>
        <Set name="resourceBase">
            <SystemProperty name="jetty.home" default="."/>/../resource/</Set>
        <Call name="addServlet">
            <Arg>com.documentum.cts.webservices.remote.FileProxyServlet</Arg>
            <Arg>/</Arg>
        </Call>
    </New>

    <!-- =========================================================== -->
    <!-- Http Configuration.                                         -->
    <!-- This is a common configuration instance used by all         -->
    <!-- connectors that can carry HTTP semantics (HTTP, HTTPS, etc.)-->
    <!-- It configures the non wire protocol aspects of the HTTP     -->
    <!-- semantic.                                                   -->
    <!--                                                             -->
    <!-- This configuration is only defined here and is used by      -->
    <!-- reference from other XML files such as jetty-http.xml,      -->
    <!-- jetty-https.xml and other configuration files which         -->
    <!-- instantiate the connectors.                                 -->
    <!--                                                             -->
    <!-- Consult the javadoc of o.e.j.server.HttpConfiguration       -->
    <!-- for all configuration that may be set here.                 -->
    <!-- =========================================================== -->

    <Call name="addConnector">
        <Arg>
            <New id="httpConnector" class="org.eclipse.jetty.server.ServerConnector">
                <Arg name="server">
                    <Ref refid="Server" />
                </Arg>
                <Set name="host">
                    <SystemProperty name="jetty.http.host"/>
                </Set>
                <Set name="port">
                    <Property name="jetty.http.port" deprecated="jetty.port" default="9096" />
                </Set>
                <Set name="idleTimeout">
                    <Property name="jetty.http.idleTimeout" deprecated="http.timeout" default="30000"/>
                </Set>
                <Get name="SelectorManager">
                    <Set name="connectTimeout"><Property name="jetty.http.connectTimeout" default="15000"/></Set>
                </Get>
            </New>
        </Arg>
    </Call>


    <!-- =========================================================== -->
    <!-- Set the default handler structure for the Server            -->
    <!-- A handler collection is used to pass received requests to   -->
    <!-- both the ContextHandlerCollection, which selects the next   -->
    <!-- handler by context path and virtual host, and the           -->
    <!-- DefaultHandler, which handles any requests not handled by   -->
    <!-- the context handlers.                                       -->
    <!-- Other handlers may be added to the "Handlers" collection,   -->
    <!-- for example the jetty-requestlog.xml file adds the          -->
    <!-- RequestLogHandler after the default handler                 -->
    <!-- =========================================================== -->
    <Ref id="RequestLog">
        <Set name="requestLog">
            <New id="RequestLogImpl" class="org.eclipse.jetty.server.NCSARequestLog">
                <Set name="filename">
                    <SystemProperty name="jetty.logs" default="../logs"/>/ws.request_yyyy_mm_dd.log</Set>
                <Set name="filenameDateFormat">yyyy_MM_dd</Set>
                <Set name="retainDays">90</Set>
                <Set name="append">true</Set>
                <Set name="extended">true</Set>
                <Set name="logCookies">false</Set>
                <Set name="LogTimeZone">GMT</Set>
            </New>
        </Set>
    </Ref>

    <!-- =========================================================== -->
    <!-- extra server options                                        -->
    <!-- =========================================================== -->
    <Set name="stopAtShutdown"><Property name="jetty.server.stopAtShutdown" default="true"/></Set>
    <Set name="stopTimeout"><Property name="jetty.server.stopTimeout" default="5000"/></Set>
    <Set name="dumpAfterStart"><Property name="jetty.server.dumpAfterStart" deprecated="jetty.dump.start" default="false"/></Set>
    <Set name="dumpBeforeStop"><Property name="jetty.server.dumpBeforeStop" deprecated="jetty.dump.stop" default="false"/></Set>

</Configure>

Following code has been written to call this configuration:

Server myServer;
File theConfigFile = new File( "C://jetty//etc//jetty.xml" );
XmlConfiguration theXmlConfiguration = new XmlConfiguration( theConfigFile.toURL() );
theXmlConfiguration.configure( myServer );
myServer.start();

I want to stick to the approach of configuring jetty with xml configuration and enable http in SSL mode. Is it possible to achieve this? Any help provided will be really helpful.


Solution

  • Oh boy, there's many things to talk about here.

    It's not great idea to dump a large variety of issues into a single question on stackoverflow!

    1. Use <Ref> properly. it's <Ref refid="otherid">, not <Ref id="idtothisref">
    2. Don't use the constructor references in ServletContextHandler or WebAppContext to bind the location in the handler tree or to define the context Path. These should be defined in XML as being within the location of the handler tree you want them.
    3. Don't redefine the Scheduler, the way you have it now, you'll have 2 schedulers!
    4. Don't remove the DefaultHandler is a critical component of Jetty and many things will break if you remove it.
    5. Don't use RequestLogHandler on Jetty 9, it was deprecated in Jetty 8 and only exists in Jetty 9 to prevent breaking configs (you don't actually have a fully functional RequestLog using the handler approach, use the server approach.
    6. NCSARequestLog is deprecated as well, don't use that, use CustomRequestLog with a specific RequestLog.Writer specified.
    7. Your ServletContextHandler definitions declare a resourceBase but then add a servlet on the default servlet url-pattern of /. This is a conflicting combination of behavior.
      • You either have a resourceBase and a DefaultServlet on the default servlet url-pattern of / (which is also named "default" per spec),
      • or you have a custom servlet that implements all of the required features of DefaultServlet so that you have a sane ServletContext (this means a lot, including error handling, error dispatch, http cache responses, missing resources, non-path resource requests, full http spec path normalization rules, etc. And that's just the surface things that DefaultServlet gives you along with a long list of other ServletContext spec defined required features)
    8. the context path of /cts/resource is invalid and will not work. This kind of pattern is best done with url-pattern within an existing context.
      • The context path rules are:
        • Must start with '/'.
        • Cannot contain another slash '/' or ''
        • Must conform to URL path encoding rules.

    Your usage of the XmlConfiguration class doesn't use the properties features, or the ID Map that is built into the XmlConfiguration class, so that means all of your usages of <Property> and <SystemProperty> and id="foo" references in your XML are not expanded or valid.

    Either use XmlConfiguration properly, or remove all of the <Property> and <SystemProperty> elements in your XML.

    What this means ...

    <!-- in your XML -->
    <Set name="maxThreads" type="int"><Property name="jetty.threadPool.maxThreads" deprecated="threads.max" default="200"/></Set>
    
    <!-- should be -->
    <Set name="maxThreads" type="int">200</Set>
    

    But why go to that extreme just to avoid using XmlConfiguration properly?

    Using XmlConfiguration without properties is no different than just using embedded-jetty directly, so skip the XML and just go embedded-jetty (way easier).

    If you want to use XmlConfiguration properly, continue to read on.

    First, don't redo what exists in Jetty already, use it. This means taking the existing XML, the existing default properties, and using XmlConfiguration properly.

    In your example XML the only "custom" thing is your 2 ServletContextHandlers, which can be their own XML files easily.

    First lets show you how you can do those 2 custom XML using straight up jetty-home and jetty-base, which uses XML and properties with XmlConfiguration as well.

    I'll assume you have the classes for com.documentum.cts.webservices.remote.TransformationServlet and com.documentum.cts.webservices.remote.FileProxyServlet in a JAR somewhere (we'll call it app.jar for this demo).

    So follow along ...

    $ curl -O https://repo1.maven.org/maven2/org/eclipse/jetty/jetty-home/9.4.44.v20210927/jetty-home-9.4.44.v20210927.tar.gz
    $ tar -zxf jetty-home-9.4.44.v20210927.tar.gz
    $ mkdir my-jetty-base
    [my-jetty-base]$ java -jar ../jetty-home-9.4.44.v20210927/start.jar --add-to-start=http,https,deploy,ext
    [my-jetty-base]$ cp $HOME/projects/documentum/context.xml webapps/
    [my-jetty-base]$ cp $HOME/projects/documentum/target/app.jar lib/ext
    [my-jetty-base]$ mkdir webapps/context
    [my-jetty-base]$ mkdir ctx-file-resources
    [my-jetty-base]$ tree -F
    .
    ├── ctx-file-resources/
    ├── etc/
    │   └── keystore
    ├── lib/
    │   └── ext/
    │       └── app.jar
    ├── start.ini
    └── webapps/
        └── context.xml
    
    5 directories, 4 files
    [my-jetty-base]$ cat webapps/context.xml
    <?xml version="1.0"?>
    <!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_3.dtd">
    
    <Configure id="custom-context" class="org.eclipse.jetty.servlet.ServletContextHandler">
      <Set name="contextPath">/ctx</Set>
      <Set name="resourceBase"><Property name="jetty.webapps"/>/context</Set>
      <Call name="addServlet">
        <Arg>com.custom.TranServlet</Arg>
        <Arg>/*</Arg>
      </Call>
      <Call id="fileProxHolder" name="addServlet">
        <Arg>com.custom.FileProxServlet</Arg>
        <Arg>/resource/*</Arg>
        <Call name="setInitParameter">
          <Arg>resourcesPath</Arg>
          <Arg><Property name="jetty.base"/>/ctx-file-resources</Arg>
        </Call>
      </Call>
    </Configure>
    

    Now if we start this server ...

    []$ cd my-jetty-base
    [my-jetty-base]$ java -jar ../jetty-home-9.4.44.v20210927/start.jar
    2021-10-29 11:16:11.446:INFO::main: Logging initialized @407ms to org.eclipse.jetty.util.log.StdErrLog
    2021-10-29 11:16:11.696:INFO:oejs.Server:main: jetty-9.4.44.v20210927; built: 2021-09-27T23:02:44.612Z; git: 8da83308eeca865e495e53ef315a249d63ba9332; jvm 11.0.12+7
    2021-10-29 11:16:11.712:INFO:oejdp.ScanningAppProvider:main: Deployment monitor [file:///home/joakim/code/jetty/distros/bases/my-jetty-base/webapps/] at interval 1
    2021-10-29 11:16:11.748:INFO:oejsh.ContextHandler:main: Started o.e.j.s.ServletContextHandler@1500b2f3{/ctx,file:///home/joakim/code/jetty/distros/bases/my-jetty-base/webapps/context,AVAILABLE}
    2021-10-29 11:16:11.767:INFO:oejs.AbstractConnector:main: Started ServerConnector@12ec64c4{HTTP/1.1, (http/1.1)}{0.0.0.0:8080}
    2021-10-29 11:16:11.796:INFO:oejus.SslContextFactory:main: x509=X509@4f704591(jetty,h=[jetty.eclipse.org],a=[],w=[]) for Server@3ddc6915[provider=null,keyStore=file:///home/joakim/code/jetty/distros/bases/my-jetty-base/etc/keystore,trustStore=file:///home/joakim/code/jetty/distros/bases/my-jetty-base/etc/keystore]
    2021-10-29 11:16:11.796:INFO:oejus.SslContextFactory:main: x509=X509@704deff2(mykey,h=[jetty server],a=[],w=[]) for Server@3ddc6915[provider=null,keyStore=file:///home/joakim/code/jetty/distros/bases/my-jetty-base/etc/keystore,trustStore=file:///home/joakim/code/jetty/distros/bases/my-jetty-base/etc/keystore]
    2021-10-29 11:16:11.872:INFO:oejs.AbstractConnector:main: Started ServerConnector@435f001f{SSL, (ssl, http/1.1)}{0.0.0.0:8443}
    2021-10-29 11:16:11.873:INFO:oejs.Server:main: Started @834ms
    

    There you go, you've the minimal necessary to have your ServletContextHandler running. It can serve http://localhost:8080/ctx/ and http://localhost:8080/ctx/resource/ with only 1 context. Supports both http and https at the same time. No webapp, no war, etc.

    But if you still want to do this entirely in XML on your own, use XmlConfiguration properly.

    There's an example of this at the Eclipse Jetty Embedded Cookbook

    See: XmlServer, note that this links to the jetty-9.4.x branch version, there's also a jetty-10.0.x and jetty-11.0.x version on their own branches as well.

    Important Note: Jetty 9.4.x is in maintenance mode and is being wound down, only bug fixes and security patches are going into that major version.
    The mainline stable branches on Eclipse Jetty are current Jetty 10.0.x and 11.0.x, use those for new development.

    To use XmlConfiguration properly, you have to be aware of the separate XML, properties, and ID Maps.

    The normal execution loop of XmlConfiguration is as follows ...

    /**
     * Configure for the list of XML Resources and Properties.
     *
     * @param xmls the xml resources (in order of execution)
     * @param properties the properties to use with the XML
     * @return the ID Map of configured objects (key is the id name in the XML, and the value is configured object)
     * @throws Exception if unable to create objects or read XML
     */
    public static Map<String, Object> configure(List<Resource> xmls, Map<String, String> properties) throws Exception
    {
        Map<String, Object> idMap = new HashMap<>();
    
        // Configure everything
        for (Resource xmlResource : xmls)
        {
            XmlConfiguration configuration = new XmlConfiguration(xmlResource);
            configuration.getIdMap().putAll(idMap);
            configuration.getProperties().putAll(properties);
            configuration.configure();
            idMap.putAll(configuration.getIdMap());
        }
    
        return idMap;
    }
    

    This takes a list of XML (in the correct order of execution), and a properties map. The XML are loaded in sequence and you get a Map of <id-string> to <configured-object> returned.

    The example linked above has a complete example, with XML from jetty-home (unmodified), and some custom XML to include new functionality. It even includes the SSL / HTTPS layer and shows how to configure it (via properties). And it shows how to setup the list of XML and the Properties. Along with how to get the Server out of the ID Map.

    For the best success treat the XML from jetty-home as read-only, don't modify it, don't edit it, you don't need to. Honest, don't change the XML, you don't have to do it. In the 200+ million installs of Jetty 9.4 there hasn't been one verified case of needing to change/edit the XML from jetty-home (people think they need to, but that's just because they don't understand how to use the Jetty XML or the XmlConfiguration properly, and each example given to us as "proof" that they need to edit the XML has been shown with often multiple ways where you don't need to). We believe in this so much that the XML in jetty-home is now read-only!