Skip to content

Instantly share code, notes, and snippets.

@jesperfj
Created June 29, 2011 17:43
Show Gist options
  • Star 9 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save jesperfj/1054400 to your computer and use it in GitHub Desktop.
Save jesperfj/1054400 to your computer and use it in GitHub Desktop.
Embedded Jetty Archetype

A better way to set up Java web apps

If you're building a Java web app that you yourself or your organization will be deploying then you can save yourself a lot of trouble by avoiding the whole build-to-war + deploy-to-server approach. Instead, you should build your web app as a normal Java application with an embedded web app server. Don't build a WAR, just compile the code and serve the files out of their source location. This has the following advantages:

  • You can code and test iteratively because you don't have to copy files and create war packages every time you make a change. This is similar to what the mvn jetty:run command is being used for by many developers today.
  • You run the same exact code in production as you do in development. Hopefully I don't have to elaborate on the advantages of that. Most developers today use mvn jetty:run or similar to achieve a quick, iterative dev cycle. But come time for deployment, they build a WAR and throw it over the wall to be deployed in some app server that is managed separately from the application code. This almost always introduces problems. And it doesn't have to be that way.

You can use this maven archetype to get started with the approach:

$ mvn archetype:generate \
    -DarchetypeCatalog=http://maven.publicstaticvoidmain.net/archetype-catalog.xml \
    -DarchetypeGroupId=net.publicstaticvoidmain \
    -DarchetypeArtifactId=embedded-jetty-archetype

The command will output something like this and ask for a groupId and artifactId for your new application:

[INFO] Scanning for projects...
[INFO]                                                                         
[INFO] ------------------------------------------------------------------------
[INFO] Building Maven Stub Project (No POM) 1
[INFO] ------------------------------------------------------------------------
[INFO] 
[INFO] >>> maven-archetype-plugin:2.0:generate (default-cli) @ standalone-pom >>>
[INFO] 
[INFO] <<< maven-archetype-plugin:2.0:generate (default-cli) @ standalone-pom <<<
[INFO] 
[INFO] --- maven-archetype-plugin:2.0:generate (default-cli) @ standalone-pom ---
[INFO] Generating project in Interactive mode
[INFO] Archetype [net.publicstaticvoidmain:embedded-jetty-archetype:1.2] found in catalog http://maven.publicstaticvoidmain.net/archetype-catalog.xml
Downloading: http://maven.publicstaticvoidmain.net/net/publicstaticvoidmain/embedded-jetty-archetype/1.2/embedded-jetty-archetype-1.2.jar
Downloaded: http://maven.publicstaticvoidmain.net/net/publicstaticvoidmain/embedded-jetty-archetype/1.2/embedded-jetty-archetype-1.2.jar (13 KB at 58.2 KB/sec)
Downloading: http://maven.publicstaticvoidmain.net/net/publicstaticvoidmain/embedded-jetty-archetype/1.2/embedded-jetty-archetype-1.2.pom
Downloaded: http://maven.publicstaticvoidmain.net/net/publicstaticvoidmain/embedded-jetty-archetype/1.2/embedded-jetty-archetype-1.2.pom (2 KB at 6.0 KB/sec)
Define value for property 'groupId': : com.example
Define value for property 'artifactId': : helloworld

(groupId is like your Java package name and artifactId is your application name). You can safely hit return on all other prompts and go with the default choices.

Now build the project

$ cd helloworld
$ mvn install

This builds the code and generates an execution wrapper using the Maven appassembler plugin. The wrapper will execute the main method in src/main/java/Main.java which looks like this (comments stripped):

public static void main(String[] args) throws Exception{
    String webappDirLocation = "src/main/webapp/";

    String webPort = System.getenv("PORT");
    if(webPort == null || webPort.isEmpty()) {
        webPort = "8080";
    }

    Server server = new Server(Integer.valueOf(webPort));
    WebAppContext root = new WebAppContext();

    root.setContextPath("/");
    root.setDescriptor(webappDirLocation+"/WEB-INF/web.xml");
    root.setResourceBase(webappDirLocation);
    root.setParentLoaderPriority(true);

    server.setHandler(root);
    server.start();
    server.join();   
}

Before you run the app, you must set the REPO environment variable to point to your local maven repository so that the execution wrapper script can find the dependencies:

$ export REPO=$HOME/.m2/repository

The application can now be executed with:

$ sh target/bin/webapp

How is this different from using mvn jetty:run? I like jetty:run but this approach gives me two additional benefits:

  1. I can control my own main method. This is great for configuring the web app server just the way I want it and write any other logic needed to get my app started the way I want.
  2. I can run the very same command on my production environment and my webapp will start up in exactly the same way as when I test it in development. mvn jetty:run isn't designed for production use. At the very least it comes with the overhead of the maven wrapper, but knowing that it wasn't meant for production would make me uncomfortable using it.

Deploying my application to production now consists of a few very simple steps:

  1. Check out the project on the production server
  2. Run mvn install
  3. Set the production REPO location
  4. Run sh target/bin/webapp and pipe output to a log stream handler

Can you already feel the inner peace of using this kind of setup?

@xupyprmv
Copy link

Looks like publicstaticvoidmain.net is dead.

@timxor
Copy link

timxor commented Nov 21, 2016

no go

/archetype-catalog.xml -DarchetypeGroupId=net.publicstaticvoidmain -DarchetypeArtifactId=embedded-jetty-archetype
[INFO] Scanning for projects...
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] Building Maven Stub Project (No POM) 1
[INFO] ------------------------------------------------------------------------
[INFO]
[INFO] >>> maven-archetype-plugin:2.4:generate (default-cli) > generate-sources @ standalone-pom >>>
[INFO]
[INFO] <<< maven-archetype-plugin:2.4:generate (default-cli) < generate-sources @ standalone-pom <<<
[INFO]
[INFO] --- maven-archetype-plugin:2.4:generate (default-cli) @ standalone-pom ---
[INFO] Generating project in Interactive mode
[WARNING] Error reading archetype catalog http://maven.publicstaticvoidmain.net/archetype-catalog.xml
org.apache.maven.wagon.TransferFailedException: maven.publicstaticvoidmain.net
at org.apache.maven.wagon.providers.http.AbstractHttpClientWagon.fillInputData(AbstractHttpClientWagon.java:1066)
at org.apache.maven.wagon.providers.http.AbstractHttpClientWagon.fillInputData(AbstractHttpClientWagon.java:960)
at org.apache.maven.wagon.StreamWagon.getInputStream(StreamWagon.java:116)
at org.apache.maven.wagon.StreamWagon.getIfNewer(StreamWagon.java:88)
at org.apache.maven.wagon.StreamWagon.get(StreamWagon.java:61)
at org.apache.maven.archetype.source.RemoteCatalogArchetypeDataSource.downloadCatalog(RemoteCatalogArchetypeDataSource.java:119)
at org.apache.maven.archetype.source.RemoteCatalogArchetypeDataSource.getArchetypeCatalog(RemoteCatalogArchetypeDataSource.java:87)
at org.apache.maven.archetype.DefaultArchetypeManager.getRemoteCatalog(DefaultArchetypeManager.java:216)
at org.apache.maven.archetype.ui.generation.DefaultArchetypeSelector.getArchetypesByCatalog(DefaultArchetypeSelector.java:218)
at org.apache.maven.archetype.ui.generation.DefaultArchetypeSelector.selectArchetype(DefaultArchetypeSelector.java:71)
at org.apache.maven.archetype.mojos.CreateProjectFromArchetypeMojo.execute(CreateProjectFromArchetypeMojo.java:181)
at org.apache.maven.plugin.DefaultBuildPluginManager.executeMojo(DefaultBuildPluginManager.java:134)
at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:207)
at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:153)
at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:145)
at org.apache.maven.lifecycle.internal.LifecycleModuleBuilder.buildProject(LifecycleModuleBuilder.java:116)
at org.apache.maven.lifecycle.internal.LifecycleModuleBuilder.buildProject(LifecycleModuleBuilder.java:80)
at org.apache.maven.lifecycle.internal.builder.singlethreaded.SingleThreadedBuilder.build(SingleThreadedBuilder.java:51)
at org.apache.maven.lifecycle.internal.LifecycleStarter.execute(LifecycleStarter.java:128)
at org.apache.maven.DefaultMaven.doExecute(DefaultMaven.java:307)
at org.apache.maven.DefaultMaven.doExecute(DefaultMaven.java:193)
at org.apache.maven.DefaultMaven.execute(DefaultMaven.java:106)
at org.apache.maven.cli.MavenCli.execute(MavenCli.java:863)
at org.apache.maven.cli.MavenCli.doMain(MavenCli.java:288)
at org.apache.maven.cli.MavenCli.main(MavenCli.java:199)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.codehaus.plexus.classworlds.launcher.Launcher.launchEnhanced(Launcher.java:289)
at org.codehaus.plexus.classworlds.launcher.Launcher.launch(Launcher.java:229)
at org.codehaus.plexus.classworlds.launcher.Launcher.mainWithExitCode(Launcher.java:415)
at org.codehaus.plexus.classworlds.launcher.Launcher.main(Launcher.java:356)
Caused by: java.net.UnknownHostException: maven.publicstaticvoidmain.net
at java.net.InetAddress.getAllByName0(InetAddress.java:1280)
at java.net.InetAddress.getAllByName(InetAddress.java:1192)
at java.net.InetAddress.getAllByName(InetAddress.java:1126)
at org.apache.maven.wagon.providers.http.httpclient.impl.conn.SystemDefaultDnsResolver.resolve(SystemDefaultDnsResolver.java:44)
at org.apache.maven.wagon.providers.http.httpclient.impl.conn.HttpClientConnectionOperator.connect(HttpClientConnectionOperator.java:101)
at org.apache.maven.wagon.providers.http.httpclient.impl.conn.PoolingHttpClientConnectionManager.connect(PoolingHttpClientConnectionManager.java:318)
at org.apache.maven.wagon.providers.http.httpclient.impl.execchain.MainClientExec.establishRoute(MainClientExec.java:363)
at org.apache.maven.wagon.providers.http.httpclient.impl.execchain.MainClientExec.execute(MainClientExec.java:219)
at org.apache.maven.wagon.providers.http.httpclient.impl.execchain.ProtocolExec.execute(ProtocolExec.java:195)
at org.apache.maven.wagon.providers.http.httpclient.impl.execchain.RetryExec.execute(RetryExec.java:86)
at org.apache.maven.wagon.providers.http.httpclient.impl.execchain.RedirectExec.execute(RedirectExec.java:108)
at org.apache.maven.wagon.providers.http.httpclient.impl.client.InternalHttpClient.doExecute(InternalHttpClient.java:184)
at org.apache.maven.wagon.providers.http.httpclient.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:82)
at org.apache.maven.wagon.providers.http.AbstractHttpClientWagon.execute(AbstractHttpClientWagon.java:832)
at org.apache.maven.wagon.providers.http.AbstractHttpClientWagon.fillInputData(AbstractHttpClientWagon.java:983)
... 32 more
[WARNING] Specified archetype not found.
[INFO] No archetype defined. Using maven-archetype-quickstart (org.apache.maven.archetypes:maven-archetype-quickstart:1.0)
Choose archetype:
Your filter doesn't match any archetype (hint: enter to return to initial list)
Choose a number or apply filter (format: [groupId:]artifactId, case sensitive contains):

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment