Is there a good way to use maven to run an executable jar?

Shaun picture Shaun · Mar 1, 2013 · Viewed 27.9k times · Source

I have a multimodule maven project, and one of the modules is for distribution.

Project \
| moduleA \
| moduleB \
| ...
| dist-module \

The distribution contains an executable jar that I'd like to easily execute. However, to execute it I have to type something like:

java -jar dist-module/target/dist-module-1.0-SNAPSHOT-full-dist/myJar.jar

It would be quite preferable to simply type:

mvn exec:java \ OR
mvn exec:exec

Unfortunately, I can't find a way to get the java goal to execute a .jar. The exec goal will actually do it, but there's a catch: the jar contains an embedded jetty server, and due to the way exec works (doesn't use the same JVM as maven) the server doesn't terminate unless I kill the process manually, which is similarly bothersome.

Any recommendations on how to do this using maven?

Edit:
For clarification, this command will primarily be used for simple manual smoke-testing; to read the log output and ensure the program started up properly in its distribution environment. As such, it should probably be a blocking call with ctrl-c terminating both the server and maven.

The embedded server is specialized to run a few specific applications, and does not automatically reload them. So, there's not a big need to keep the server running independently of maven for long periods of time.

Answer

user1050755 picture user1050755 · Mar 7, 2013

mvn exec:java runs the app directly from the compiler output below target/ - no need to run from the jar:

    <plugin>
        <groupId>org.codehaus.mojo</groupId>
        <artifactId>exec-maven-plugin</artifactId>
        <version>1.2.1</version>
        <executions>
            <execution>
                <id>default-cli</id>
                <goals>
                    <goal>java</goal>
                </goals>
                <configuration>
                    <mainClass>uk.co.pookey.hibernate.App</mainClass>
                    <systemProperties>
                        <systemProperty>
                            <key>derby.stream.error.file</key>
                            <value>${basedir}/target/derby.log</value>
                        </systemProperty>
                    </systemProperties>
                </configuration>
            </execution>
        </executions>
    </plugin>            

If you want to run the jar with mvn exec:exec :

    <plugin>
        <groupId>org.codehaus.mojo</groupId>
        <artifactId>exec-maven-plugin</artifactId>
        <version>1.2.1</version>
        <executions>
            <execution>
                <id>default-cli</id>
                <goals>
                    <goal>exec</goal>
                </goals>
                <configuration>
                    <executable>java</executable>
                    <!-- optional -->
                    <workingDirectory>/tmp</workingDirectory>
                    <arguments>
                        <argument>-jar</argument>
                        <argument>${basedir}/target/hibernate-derby-memory-${project.version}.jar</argument>
                    </arguments>
                </configuration>                        
            </execution>
        </executions>
    </plugin>         

If you have problems with jetty terminating, I'd recommend running an integration test (or just don't exit the jetty main thread without terminating jetty first, or just use jetty's stop port)! It is possible to run a JVM in the background and let maven stop it again after testing. There are three phases for this: pre-integration-test, integration-test or post-integration-test. You can read more about this at http://docs.codehaus.org/display/MAVENUSER/Maven+and+Integration+Testing . When using jetty maven plugin, the pom.xml config could like:

   <plugin>
        <groupId>org.mortbay.jetty</groupId>
        <artifactId>jetty-maven-plugin</artifactId>
        <!-- test start/stop: -->
        <executions>                
            <execution>
                <id>start-jetty</id>
                <phase>pre-integration-test</phase>
                <goals>
                    <goal>deploy-war</goal>
                </goals>
                <configuration>
                    <daemon>true</daemon>
                    <systemProperties>
                        <systemProperty>
                            <name>some.prop</name>
                            <value>false</value>
                        </systemProperty>
                    </systemProperties>
                </configuration>
            </execution>
            <execution>
                <id>stop-jetty</id>
                <phase>post-integration-test</phase>
                <goals>
                    <goal>stop</goal>
                </goals>
            </execution>
        </executions>    
    </plugin>

Instead of binding the jetty maven plugin to these phases, you could bind the maven antrun plugin to execute arbitrary commands.