3

Imagine a normal java maven project with a Main class that produces the artifact project-a.jar. This project has a dependency on project-b.jar.

Is there a Maven plugin that allows to run that jar by a command like that?

mvn run-plugin:run org.mygroup:project-a:3.1 <args>

The plugin would resolve the runtime dependencies (using META-INF/maven/(...)/pom.xml), install the project and its dependencies to the local maven repository (if not already there), construct the classpath and invoke

java -cp (...)/project-a-3.1.jar;(...)/project-b-2.1.jar org.mygroup.Main <args>

I know that the usual way is to build an executable (fat) jar that contains the dependencies, but that's not what I am asking for.

Actually, it is not even necesary to read the pom from the jar, because maven can download it from the repositories given the coordinates.

Why this question is different to the Maven Run Project question:

I do not want to start from having the project's source already checked out. So the usual use of the exec plugin is not applicable. The OP of the Maven Run Project question obviously assumed the presence of a source code project folder. Her purpose was testing and she accepted an answer that clearly needs a project. The wording of both questions is correct, too. There is a difference between the words "project" and "jar" and their actual meaning in their respective contexts is quite different.

Community
  • 1
  • 1
Gustave
  • 3,359
  • 4
  • 31
  • 64
  • I think you are looking for maven exec plugin, see possible duplicate: [Maven Run Project](http://stackoverflow.com/questions/1089285/maven-run-project) –  Aug 13 '15 at 16:44
  • The `META-INF/maven/(...)/pom.xml` structure is not guaranteed to exist. This would be the case when you configure `maven-jar-plugin` with [`addMavenDescriptor`](http://maven.apache.org/shared/maven-archiver/index.html#class_archive) to `false`. – Tunaki Aug 13 '15 at 16:46
  • No, I think both that question as well as the exec plugin require that you are already in that project (having checked out the source and executing maven from the source code folder containing the pom). – Gustave Aug 13 '15 at 16:47
  • You should tell us what your big picture is, if you don't want to use the source tree, then I don't think maven is the tool you need. You probably should use a MANIFEST.MF specifying deps (see this [answer](http://stackoverflow.com/a/23986765/180100)) –  Aug 13 '15 at 16:49
  • There are already some plugins that don't need a source tree, one of the most obvious is the archetype plugin. – Gustave Aug 13 '15 at 16:53
  • Would be very nice to have such a plugin, think about it (I usually have a lot of time for that when I move around and assemble those megaton fat jars). And there are situations where disk space counts. – Gustave Aug 13 '15 at 16:56
  • @not guaranteed to exist: For my own project I can guarantee it, for the dependencies it is not needed. – Gustave Aug 13 '15 at 16:58
  • My advice, look at the linked answer, choose one of the listed solution and don't try to invent something "strange" (NB: it's just an advice) –  Aug 13 '15 at 16:59
  • As I already mentioned, the answers of the linked question are not applicable to this question. – Gustave Aug 13 '15 at 17:07
  • The invoke example is a bit odd: you use either -jar or -cp, you can't combine the two (copied from [this answer](http://stackoverflow.com/a/15930980/3080094)). – vanOekel Aug 13 '15 at 19:29
  • @vanOekel: true, thanks! – Gustave Aug 14 '15 at 07:10

4 Answers4

1

You can use the appassembler-maven-plugin plugin, it creates a shell script that has the dependencies in the classpath for you. Heres an example config

        <plugin>
            <groupId>org.codehaus.mojo</groupId>
            <artifactId>appassembler-maven-plugin</artifactId>
            <executions>
                <execution>
                    <phase>package</phase>
                    <goals>
                        <goal>assemble</goal>
                    </goals>
                </execution>
            </executions>
            <configuration>
                <extraJvmArguments>-Xms256m -Xmx1536m</extraJvmArguments>
                <programs>
                    <program>
                        <mainClass>com.package.MyMainClass</mainClass>
                        <name>TestFormattingUtils</name>
                    </program>
                </programs>
            </configuration>
        </plugin>

You can find the output script in .../target/appassembler/bin You can manually inspect the script and you'll see that its doing the type of command you wanted where it adds the jars to classpath via the command line. ie java -jar (...)/project-a-3.1.jar -cp (...)/project-b-2.1.jar <args>

Carlos Bribiescas
  • 4,197
  • 9
  • 35
  • 66
  • Using a shell is script is not an option, as it must should work on Windows, too. – Gustave Aug 13 '15 at 17:00
  • It also outputs a windows one, i just never look at it. Same name, but ends with `.bat`. – Carlos Bribiescas Aug 13 '15 at 17:01
  • Sounds interesting, I'll have a look at it, but using scripts (and different ones) is somwhat fragile and not very elegant when java and maven are already available. – Gustave Aug 13 '15 at 17:19
  • I know what you mean. You can run it though by first invoking the plugin, generating a fresh script then immediately running it. I wouldn't version control the script or anything like that. (Or else it would be quite brittle) – Carlos Bribiescas Aug 13 '15 at 17:30
0

I'm not a fan of jars-in-jar either, but I do maintain various tools with lots of dependencies. So, at one point, I decided to write an executable AppBoot jar which puts all the jars from a lib-subdirectory in a class-loader and then calls the main-method of the desired (executable) jar. This question prompted me to investigate if the exec-maven-plugin could do something similar, and it can.

The exec-maven-plugin does not require a "Java project" directory, but a pom.xml in a directory is required. The pom.xml I used is shown below, note that it can be placed in any (empty) directory and the application can be started by opening a shell/prompt in that directory and executing mvn exec:exec. Use mvn -X exec:exec to review the classpath used by the exec-maven-plugin.

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.descartes</groupId>
    <artifactId>exec-embed-demo</artifactId>
    <version>1.2.2-SNAPSHOT</version>
    <packaging>pom</packaging>

    <!-- Start the demo using Maven repository artifacts, execute with "mvn exec:exec" -->

    <properties>
        <demo.version>1.2.1.GH</demo.version>
        <mainclass>com.descartes.basicjsp.embed.demo.Launch</mainclass>
        <appname>${project.artifactId}</appname>
        <homedir>${project.basedir}/</homedir>
    </properties>

    <dependencies>
        <dependency>
            <!-- exec-maven-plugin will get all required (runtime) jar-files from this dependency. -->
            <groupId>com.descartes</groupId>
            <artifactId>basic-jsp-embed-demo</artifactId>
            <version>${demo.version}</version>
        </dependency>
    </dependencies>

    <build>
        <!-- The "outputDirectory" is added to the classpath by the exec-maven-plugin. -->
        <!-- Add this pom's directory to the classpath instead of "./target/classes". -->
        <!-- The directory should contain "logback.xml" to prevent a million lines of debug output from Tomcat. -->
        <outputDirectory>${homedir}</outputDirectory>
        <plugins>
            <plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>exec-maven-plugin</artifactId>
                <version>1.4.0</version>
                <!-- mvn exec:exec configuration -->
                <!-- Embedded Tomcat will not stop with "ctrl-c" -->
                <!-- Use http://localhost:8080/shutdown instead -->
                <configuration>
                    <executable>java</executable>
                    <arguments>
                        <argument>-Dapp.name=${appname}</argument>
                        <argument>-Dapp.home.dir=${homedir}</argument>
                        <argument>-Dapp.conf.dir=${homedir}</argument>
                        <argument>-cp</argument>
                        <classpath/>
                        <argument>${mainclass}</argument>
                    </arguments>
                </configuration>
                <!-- mvn exec:java configuration -->
                <!-- "ctrl-c" stops Tomcat but embedded Tomcat fails to start properly, probably a classloader issue. -->
                <!--
                <configuration>
                    <mainClass>${mainclass}</mainClass>
                    <systemProperties>
                        <systemProperty>
                            <key>app.name</key>
                            <value>${appname}</value>
                        </systemProperty>
                        <systemProperty>
                            <key>app.home.dir</key>
                            <value>${homedir}/</value>
                        </systemProperty>
                        <systemProperty>
                            <key>app.conf.dir</key>
                            <value>${homedir}/</value>
                        </systemProperty>
                    </systemProperties>
                </configuration>
                -->
            </plugin>
        </plugins>
    </build>

</project>

AppBoot is part of the basic-jsp-embed project that uses embedded Tomcat and that project can be found here (to install, download the latest release, unpack the zip-file and run "mvn install" in the root directory of the multi-module project).

On a side-note: managing a jar-set is tricky, use tools like jHades to verify you will not run into trouble with multiple versions of the same class in different jar-files.

vanOekel
  • 6,358
  • 1
  • 21
  • 56
-1

You are looking for the maven exec plugin.

mvn exec:java -Dexec.mainClass="com.example.Main" [-Dexec.args="argument1"]

would run your program

Max Fichtelmann
  • 3,366
  • 1
  • 22
  • 27
  • I don't think this answers the OP question. This command needs to be run on the project source, _not_ the final jar. – Tunaki Aug 13 '15 at 16:48
  • I don't see where you specify the coordinates of the project that you want to execute in your command. How do you specify it? By executing from the project folder... but that is not the question. – Gustave Aug 13 '15 at 16:51
  • `maven-exec-plugin` runs the compiled `/target/classes/**`, not the final artifact. And in current version 1.6.0 it cannot run a mix of provided and runtime dependencies. – leo May 26 '20 at 12:32
-1

Maven can not do what you want, simply because it has no way to resolve the dependencies of project A once it has been built into a final jar.

Maven does not magically download libraries from the Internet: what makes it work are the definition of repositories inside the pom.xml. Without pom.xml, like you seem to suggest, how would it know where to download libraries from? Maven is not a downloading tool, it is a project management tool and what you have is no longer a project but a final library.

Since you have control over project A, you should really rely on Maven conventions and either build a fat jar or an assembly (with maven-assembly-plugin).

By the way, the pom.xml file located under META-INF is not guaranteed to exist, and, in fact, it is not there if you look at Spring artifacts. Take a look at Maven Archiver documentation: the presence of this pom file is controlled by the addMavenDescriptor boolean attribute. Set this attribute to false and your main artifact will not have this pom file.

Tunaki
  • 132,869
  • 46
  • 340
  • 423