Launch4j and embedded web container

Embedded web container is nice. Of course you’ll lose some benefits of a big container, but you’ll get something else. Sometimes it’s faster startup. And sometimes it’s ability to wrap your Java web application into executable.

In our system we have multiple modules and most of them are executables on Windows machine (not my preference anyway ;-)). There is some control console that can start them, kill them, etc. And our Tomcat kinda stuck out of this exe family. Before I could start Launch4j-ize our WAR I have to turn the thing around and embed the container into JAR. Something I did many times before with Jetty – although mostly only for development purposes with our exploded WAR somewhere in target directory.

Embedding web container

First step was to run some main class and get to a state when my application is accessible on some port+context – just like it would be after normal deployment. Because we were friends with Tomcat at the moment, I went for this option.

Code was easy, there are blogs you can find – I used this one with minor adjustments and with mere two tomcat artifacts (tomcat-embed-core and tomcat-embed-logging-juli) I was up and running (Tomcat 8.0.15).

I ran into couple of problems however:

  • Tomcat logging – I know it’s not Tomcat’s fault that Sun made such a horrible decision with JUL and I understand when someone uses it (portability is probably the only reason). I tried to use org.slf4j:jul-to-slf4j to little/no avail, but I didn’t give it so much time for more serious problems.
  • I was generally unhappy with the speed. It took maybe 10s of silence until my application showed its first log. No need for concrete numbers – maybe it can be adjusted, but when I compared it with expectations (and other containers – full-blown or embedded) the pause before actually starting the application was just too long.
  • The main blow however came from different direction. This is my main trouble with all these embedded containers – they kinda expect the WAR to be somewhere on disk (exploded or packed, doesn’t matter). What if I want to run it from classpath? I have web.xml as a resource already there! It seems that this is common to all the containers I met – or this kind of webapp loading is not documented. Maybe it’s not even possible because of Java EE specification – I really don’t know. So you have to have WAR. Or do you?

So my embedded application is running, why am I complaining about the last point then? I want to run the application as Windows executable and I want to avoid messing with filesystem if possible.

When JAR and WAR unite

But this seems not possible. Let’s say I run EXE wrapper for JAR file. This can be either embedded in EXE file directly, or lying somewhere around it. How am I supposed to provide WAR? Tried options:

  • Launch4j created app.exe that used the content of separate app.jar. JAR contained WEB-INF/web.xml too, but classes were placed like in normal JAR. This works fine when exploded, because web.xml is used to configure the application and the rest is on the classpath already. But not when packaged as JAR. For one – embedded Tomcat will not accept JAR file because its extension is .jar and not .war! Whatever the reason for this is, it was showstopper for this option.
  • Let’s change packaging of my “app” artifact to war then. I had to override couple of configuration options for launch4j maven plugin because now I wasn’t so unified with other non-web-app modules (based on jar packaging). This brings problems that classes in WEB-INF/classes are not on classpath before the webapp goes up. And because my main class is in that war (don’t let me create another Maven module for that class :-)) it is not found. I can meddle with my POM now to get the main class (with package structure) to root of the WAR file (working as JAR at that moment). This would probably double the size of the POM file (easy things made Maven way, you know), but I just gave up here.
  • Back to JAR packaging and let’s use something that can read WARs with “.jar” extension. 🙂 And so we get to Jetty.

To be fair, these are far from all the options. Just like jenkins.war (although that one uses Jetty instead of Tomcat) it is possible to do it with Tomcat after all – just leave JAR packaging and rename the resulting file (again too many lines for such a simple thing). This leads to further changes in launch4j configuration too however + we’re still stuck with slow start. It’s simply time to try something else. 🙂

From Tomcat to Jetty

I was friendly with Jetty for the last four years. Not a master of this cute pet- after all it’s actually more complex than people are generally aware. It’s typically one of the tools nobody cares to study, “because I just use it during development”. Well here I am and I don’t want to use it merely for development.

Thinking about how Jenkins is run, first I try to solve this part. Answer was found in the post Executable WARs with Jetty (or this SO question that references it too). I’ll copy my version of final code:

public static void main(String[] args) throws Exception {
    int port = Integer.parseInt(System.getProperty("port", "8080"));
    String contextPath = System.getProperty("context", "app");

    Server server = new Server(port);
    WebAppContext context = new WebAppContext();
    context.setServer(server);
    context.setContextPath('/' + contextPath);

    ProtectionDomain protectionDomain = RestMain.class.getProtectionDomain();
    String warPath = protectionDomain.getCodeSource().getLocation().toExternalForm();
    context.setWar(warPath);
    server.setHandler(context);

    server.start();
    if (!context.isAvailable() || context.getWebInf() == null) {
     server.stop();
    }
    server.join();
}

I skipped all my logging and imports (those from jetty are obvious after all). Most interesting part here is the one setting the warPath. This actually works equally great for exploded wars (during development) and for JARs lying next to executable wrapper.

Obviously, not even Jetty is reading the WAR part from classpath. And it uses some temporary directory too, just less obvious than Tomcat’s tomcat.8080 placed in working directory. But Jetty can tread JAR as WAR when told to.

How about JAR wrapped in executable?

This is in fact how Launch4j creates our exe files by default (option dontWrapJar is false). Maybe it’s time to show our plugin setup for Launch4j for your convenience. Note that you need both launch4j-maven-plugin and maven-dependency-plugin. This is the default setup from our parent POM (it’s in build/plugin-management/plugins section, in our case further nested in profiles/profile, because we don’t create EXE files by default on our Linux based CI machine):

<plugin>
    <groupId>com.akathist.maven.plugins.launch4j</groupId>
    <artifactId>launch4j-maven-plugin</artifactId>
    <version>1.7.1</version>
    <executions>
        <execution>
            <id>l4j-clui</id>
            <phase>package</phase>
            <goals>
                <goal>launch4j</goal>
            </goals>
            <configuration>
                <headerType>console</headerType>
                <jar>${project.build.directory}/${project.artifactId}-${project.version}.jar</jar>
                <outfile>${project.build.directory}/${executable.name}.exe</outfile>
                <dontWrapJar>${launch4j.dontWrapJar}</dontWrapJar>
                <classPath>
                    <mainClass>${mainClass.name}</mainClass>
                    <preCp>dependency_libs/*.jar</preCp>
                </classPath>
                <icon>src/main/resources/${executable.name}.ico</icon>
                <versionInfo>
                    <fileVersion>1.0.0.0</fileVersion>
                    <txtFileVersion>${project.version}</txtFileVersion>
                    <fileDescription>${project.name}</fileDescription>
                    <copyright>...</copyright>
                    <productVersion>1.0.0.0</productVersion>
                    <txtProductVersion>1.0.0.0</txtProductVersion>
                    <productName>${project.name}</productName>
                    <companyName>...</companyName>
                    <internalName>...</internalName>
                    <originalFilename>${executable.name}.exe</originalFilename>
                </versionInfo>
            </configuration>
        </execution>
    </executions>
</plugin>
<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-dependency-plugin</artifactId>
    <version>2.9</version>
    <executions>
        <execution>
            <id>copy-dependencies</id>
            <phase>package</phase>
            <goals>
                <goal>copy-dependencies</goal>
            </goals>
            <configuration>
                <outputDirectory>${project.build.directory}/dependency_libs</outputDirectory>
                <overWriteReleases>false</overWriteReleases>
                <overWriteSnapshots>false</overWriteSnapshots>
                <overWriteIfNewer>true</overWriteIfNewer>
            </configuration>
        </execution>
    </executions>
</plugin>

I override our global config in my module’s POM like this (I’m mentioning maven-dependency-plugin too to trigger it):

<build>
    <plugins>
        <plugin>
            <groupId>com.akathist.maven.plugins.launch4j</groupId>
            <artifactId>launch4j-maven-plugin</artifactId>
            <executions>
                <execution>
                    <id>l4j-clui</id>
                    <configuration>
                        <jar>${project.artifactId}-${project.version}.jar</jar>
                        <dontWrapJar>true</dontWrapJar>
                    </configuration>
                </execution>
            </executions>
        </plugin>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-dependency-plugin</artifactId>
        </plugin>
    </plugins>
</build>

There are many other ways how to do this – you can go for some uberjar solution, etc. Important thing here is that <jar> element here does not reference JAR to be included into EXE, but it must be relative path to JAR when the EXE is started. Meaning of this element is changed by dontWrapJar flag.

Do I need to do this? Isn’t it possible to use EXE as WAR? Sounds silly at first, but I was surprised to discover that you can “enter” the EXE in Total Commander using Ctrl+PgDn – just like any other archive. Obviously – it’s self-extracting archive! When you include your JAR into it, it is correctly identified as a location of the main class in the bootstrap code – but Jetty will silently ignore it. In fact, this is the reason for context.getWebInf() == null check in the bootstrap code.

Anyway we’re still much better here than with Tomcat. Startup is faster, file can be named with JAR extension, what more do we want?

Other options

Before we try to enhance our current solution, let’s talk about other options. But not for long, really. Originally I wanted to try Undertow because it just seems to be cool. After reading its documentation I realized that I’d have to do more than just show it some web.xml file to start the application for me.

How about JBoss’s WildFly then? It is full-blown AS and can be embedded! However I found no documentation how to embed it that was not related to Arquillian, that is testing. Too bad. I love WildFly otherwise.

Glassfish? Probably possible but at this stage I just decided to go for simpler web containers. If you have anything useful to this topic, or other embeddable containers, please, mention it in discussion (no registration required, BTW).

Get out of my classpath!

I’ll not go further with examples, just with one idea found during extensive browsing while I was hunting the solutions. So Jetty/Tomcat/any container wants WAR in the file – or exploded WAR somewhere on the disk. They all create some kind of temporary files… so why can’t I? My bootstrap code can read web.xml (which can in turn be placed wherever on the classpath, not just WEB-INF) and put it in some temporary directory (Files#createTempDirectory) into WEB-INF subdirectory. Then we can point our WAR path there and let any worthy embedded container do the rest! Of course – if you have more than just a web.xml (JSPs, static stuff) you have to do more work, but that doesn’t make it less possible.

You may want to avoid this process for development – but again, this is easy to detect (“if the location of the main class is within EXE then do this else just use the location directly”).

Conclusion

Creating application with embedded web containers is a bit more complicated when Java application is wrapped in EXE – but that is expected. Any way you package your application, however, the biggest problem is that you somehow have to get your WAR on the disk (or something your embedded container likes like a WAR). Using app.war and running it with java -jar app.war is probably most straightforward. However if you want to share libraries with other modules and avoid uberjars, and/or use exe wrappers, things get more and more complicated. Externalizing WAR content (without classes, those are on the classpath already) in the bootstrap code may be a solution.

There is one more class of problems if you use Spring as well. Imagine all your modules bootstrap Spring application context. But your web module is doing it differently – it bootstraps webapp somehow and Spring is configured in web.xml. This is far less flexible during development too, as you can’t easily swap @Configuration class defined in the web.xml (or XML config). You can “shadow” them from test classpath but that mostly sucks. But if you invert the process and bootstrap Spring first, then it doesn’t know about web application yet and god knows what happens (or not and should) to your Jersey resources, etc.

However, this is different story for a different day… and looking at the calendar, probably for a different year too! Merry Christmas and Happy New Year!

Advertisements

About virgo47
Java Developer by profession in the first place. Gamer and amateur musician. And father too. Naive believer in brighter future. Step by step.

3 Responses to Launch4j and embedded web container

  1. Pingback: Jetty with self-extracting WAR from Launch4J EXE | Virgo's Naive Stories

  2. Pingback: Jetty hardening | Virgo's Naive Stories

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s