Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Embedded Jetty : how to use a .war that is included in the .jar from which Jetty starts?

I'm trying to generate a .jar containing a main() that would start Jetty.

My problem is that I'd like the .war that Jetty loads to be included in the same .jar.

I've been able to create the .jar containing the .war with :

In the POM.xml :

<plugin>
    <artifactId>maven-assembly-plugin</artifactId>
    <version>2.3</version>
    <configuration>
        <finalName>myApp</finalName>
        <appendAssemblyId>false</appendAssemblyId>
        <archive>
            <manifest>
                <mainClass>com.myApp.Server</mainClass>
            </manifest>
        </archive>
        <descriptors>
            <descriptor>src/main/resources/executable-jar-assembly.xml</descriptor>
        </descriptors>
    </configuration>
    <executions>
        <execution>
            <id>make-my-jar-with-dependencies</id>
            <phase>package</phase>
            <goals>
                <goal>single</goal>
            </goals>
        </execution>
    </executions>
</plugin>

executable-jar-assembly.xml :

<assembly xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0" 
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0 http://maven.apache.org/xsd/assembly-1.1.0.xsd">

<id>jar-with-dependencies-and-war</id>
<formats>
<format>jar</format>
</formats>
<includeBaseDirectory>false</includeBaseDirectory>
<dependencySets>
    <dependencySet>
        <outputDirectory>/</outputDirectory>
        <useProjectArtifact>false</useProjectArtifact>
        <unpack>true</unpack>
        <scope>runtime</scope>
    </dependencySet>
</dependencySets>
<fileSets>
    <fileSet>
        <directory>target/classes/</directory>
        <outputDirectory>/</outputDirectory>
    </fileSet>
    <fileSet>
        <directory>target/</directory>
        <includes>
            <include>
                myApp.war
            </include>
        </includes>
        <outputDirectory>/</outputDirectory>
    </fileSet>
</fileSets>

The code to set the war in Jetty :

handler.setWar("myApp.war");

... I also tried :

URL res = Server.class.getResource("myApp.war");
handler.setWar(res.toExternalForm());

... and :

URL res = Thread.currentThread().getContextClassLoader().getSystemResource("myApp.war");
handler.setWar(res.toExternalForm());

But nothing works!

Using that last example code to start Jetty, the server seems to start correctly but no requests work. The configuration is obviously wrong.

I know there are some workarounds to make a .war itself executable, but I'd like to make the ".war inside the .jar" work.

Any idea how the .war should be configured?

like image 761
electrotype Avatar asked Oct 05 '22 23:10

electrotype


1 Answers

I played with this a lot in 2009-10 for a specific app.

The problem I found is that when you have a war embedded within a jar, the URIs can end up being ones that do not work with the default jar: uri handler trying to access the war file.

There are a number of solutions that I found:

  1. Tell jetty to extract/explode the war file into a temporary directory

  2. Implement your own URI handler (that was an interesting exercise, but if you need to support the code yourself, I would not recommend doing that)

  3. Make the war file an executable war file, e.g. the same type of trick used by Jenkins. To do this you overlay the jetty server classes and your main class with the war file. Then you just point jetty to the jar file itself as the war file. Jetty does not care that the filename does not end in .war

All three worked for me on Jetty 7. I suspect that the situation will remain the same.

In the end I opted for option 3. It works out as the simplest, does not leave temporary files on the users system, and is quickest to start up.

like image 92
Stephen Connolly Avatar answered Oct 10 '22 03:10

Stephen Connolly