Wednesday, June 23, 2010

Basic Embedded Jetty Setup: JSF 1.2 Webapp

Here are some basic code snippets I've used (to-date ... subject to change) to get an IceFaces-based (JSF 1.2) webapp deployed as an embedded Jetty webapp - using Maven for building. The artifacts include a configuration file, a minimal bootstrap class, the pom and the file used as the maven assembly-plugin descriptor.

The configuration file is jetty.xml, and doesn't require much. It lives under the ./etc directory of my project:

<!DOCTYPE Configure PUBLIC "-//Mort Bay Consulting//DTD Configure//EN" "http://jetty.mortbay.org/configure.dtd">

<Configure id="Server" class="org.mortbay.jetty.Server">

    <Set name="ThreadPool">
        <New class="org.mortbay.thread.QueuedThreadPool">
            <!-- initial threads set to 10 -->
            <Set name="minThreads">10</Set>
            <!-- the thread pool will grow only up to 200 -->
            <Set name="maxThreads">200</Set>
            <!-- indicates that having 20 and below, the pool will be considered low on threads -->
            <Set name="lowThreads">20</Set>
            <!-- The number of queued jobs (or idle threads) needed before the thread pool is grown (or shrunk) -->
            <Set name="SpawnOrShrinkAt">2</Set>
        </New>
    </Set>

    <Call name="addConnector">
        <Arg>
            <New class="org.mortbay.jetty.nio.SelectChannelConnector">
                <!-- the ip address or domain to bind -->
                <Set name="host">
                    <SystemProperty name="jetty.host"/>
                </Set>
                <!-- the port to use/bind, defaults to 8080 if property not set -->
                <Set name="port">
                    <SystemProperty name="jetty.port" default="8080"/>
                </Set>
                <!-- the time in milliseconds when a connection is considered idle -->
                <Set name="maxIdleTime">300000</Set>
                <!-- the number of acceptors (their job is to accept the connection and dispatch to thread pool) -->
                <Set name="Acceptors">2</Set>
                <!-- should the connection statistics be turned on? (Not advisable in production) -->
                <Set name="statsOn">false</Set>
                <!-- the confidential port -->
                <Set name="confidentialPort">8443</Set>
                <!-- indicates the minimum number of connections when the server is considered low on resources -->
                <Set name="lowResourcesConnections">5000</Set>
                <!-- when low on resources, this indicates the maximum time a connection must be idle to not be closed -->
                <Set name="lowResourcesMaxIdleTime">5000</Set>
            </New>
        </Arg>
    </Call>

    <!-- Stops the server when ctrl+c is pressed (registers to Runtime.addShutdownHook) -->
    <Set name="stopAtShutdown">true</Set>
    <!-- send the server version in the response header? -->
    <Set name="sendServerVersion">true</Set>
    <!-- send the date header in the response header? -->
    <Set name="sendDateHeader">true</Set>
    <!-- allows requests(prior to shutdown) to finish gracefully -->
    <Set name="gracefulShutdown">1000</Set>

</Configure>

This configuration file is referenced by the bootstrap class, using it to configure Jetty. This class also sets the context for the webapp, points to the top-level directory of the exploded WAR content, sets a webapp context and a default context as handlers for Jetty, and starts up the webserver:

public class MyJettyWebServer {

    public static void main(String[] args) throws Exception {

        Server jetty = new Server();

        // configure Jetty by pointing to config file(s)
        String[] configFiles = { "etc/jetty.xml" };
        for (String configFile : configFiles) {
            XmlConfiguration configuration = new XmlConfiguration(new File(configFile).toURI().toURL());
            configuration.configure(jetty);
        }

        // set the context for the webapp
        WebAppContext appContext = new WebAppContext();
        appContext.setContextPath("/mycontext");

        // point to the top-level directory of the exploded WAR content
        File warPath = new File(System.getProperty("basedir"));
        appContext.setWar(warPath.getAbsolutePath());

        // set a webapp context and a default context as handlers for Jetty
        HandlerList handlers = new HandlerList();
        handlers.setHandlers(new Handler[]{ appContext, new DefaultHandler() });
        jetty.setHandler(handlers);

        // start up the webserver
        jetty.start();
    }
}


The pom specifies IceFaces 1.8.2, being careful to exclude the EL API jar wherever that's brought in transitively, as per http://www.icefaces.org/docs/v1_8_0/htmlguide/devguide/appendixA.html; and it specifies Jetty and Log4J artifacts. Note that JSP support is explicitly specified:

    ....
    <!-- use JAR packaging for embedded Jetty -->
    <packaging>jar</packaging>
    ....
    <dependencies>
        <dependency>
        <dependency>
            <groupId>org.icefaces</groupId>
            <artifactId>icefaces</artifactId>
            <version>1.8.2</version>
            <exclusions>
                <exclusion>
                    <groupId>javax.el</groupId>
                    <artifactId>el-api</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.icefaces</groupId>
            <artifactId>icefaces-comps</artifactId>
            <version>1.8.2</version>
        </dependency>
        <dependency>
            <groupId>org.icefaces</groupId>
            <artifactId>icefaces-facelets</artifactId>
            <version>1.8.2</version>
            <exclusions>
                <exclusion>
                    <groupId>javax.el</groupId>
                    <artifactId>el-api</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>javax.faces</groupId>
            <artifactId>jsf-api</artifactId>
            <version>1.2_12</version>
        </dependency>
        <dependency>
            <groupId>javax.faces</groupId>
            <artifactId>jsf-impl</artifactId>
            <version>1.2_12</version>
        </dependency>
        <dependency>
            <groupId>org.mortbay.jetty</groupId>
            <artifactId>jetty</artifactId>
            <version>6.1.21</version>
        </dependency>
        <dependency>
            <groupId>org.mortbay.jetty</groupId>
            <artifactId>jetty-util</artifactId>
            <version>6.1.21</version>
        </dependency>
        <dependency>
            <groupId>org.mortbay.jetty</groupId>
            <artifactId>jsp-2.1-jetty</artifactId>
            <version>6.1.21</version>
        </dependency>
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>${log4jVersion}</version>
        </dependency>
    </dependencies>
</project>

The assembly-plugin descriptor has a few things worth mentioning. In the dependency sets, I lay down my dependencies in the standard webapp location, ./WEB-INF/lib:

        <dependencySet>
            <unpack>false</unpack>
            <scope>runtime</scope>
            <outputDirectory>war/WEB-INF/lib</outputDirectory>
        </dependencySet>

I also specify several destinations for my dev-time files to deal with Jetty-related stuff:

        <!--
        Jetty deployment: configuration file location
        -->
        <fileSet>
            <directory>etc</directory>
            <outputDirectory>/usr/local/mywebapp/etc</outputDirectory>
            <fileMode>0644</fileMode>
        </fileSet>
        <!--
        Jetty deployment: webapp deployment location - hmmm, this one might not be needed...
        -->
        <fileSet>
            <directory>webapps</directory>
            <outputDirectory>/usr/local/mywebapp/webapps</outputDirectory>
            <fileMode>0644</fileMode>
        </fileSet>
        <!--
        Jetty deployment: production-time exploded warfile location
        -->
        <fileSet>
            <directory>src/main/webapp</directory>
            <outputDirectory>/usr/local/mywebapp/war</outputDirectory>
            <fileMode>0644</fileMode>
        </fileSet>

Finally, to start the program:

java -server -Dbasedir=/usr/local/mywebapp/war -cp /usr/local/mywebapp/war/WEB-INF/lib/* com.mybiz.MyJettyWebServer

Browse to localhost:8080 and your webapp should appear.

You'll notice I did not use the maven-jetty plugin (nor the jetty-maven plugin - yes, there are two different ones, each with different names, schemas and behaviors). The good news about the plugin is that it shields you from much of the configuration/deployment exercises you'll need; that's also the bad news. I needed to understand explicitly what dependencies, etc. I'd need for production, so I chose to do things manually. The good news around this is that I've done most of the heavy lifting to grease the skids for future embedded-Jetty exercises.

References

Jetty 6.x Wiki
Jetty 7x, 8.x Wiki

No comments:

Post a Comment