I have the following module structure:
powercontrol
powercontrol-core
pom.xml
powercontrol-data
pom.xml
powercontrol-gui
pom.xml
powercontrol-ui
pom.xml
pom.xml
Now I want that the GUI (Graphical User Interface) and UI (Command Line User Interface) can be executed by the client.
I tried to use the maven shade plugin inside the GUI and UI, but this makes it really a mess.
I prefer:
A jar file with all the third party dependencies (log4j etc).
A jar file (or maybe lib folder?) with all the project modules.
A'n executor for the GUI and UI.
Example:
powercontrol/
bin/
gui
ui
lib/
third-party.jar
powercontrol-core.jar
powercontrol-data.jar
powercontrol-gui.jar
powercontrol-ui.jar
I'm a bit stuck with getting a good structure now, where should I start?
All feedback, suggestions etc are welcome. Thank you in advance!
UPDATE 8/28/2015
I made a new module named: powercontrol-dist that will be executed as last in the Maven lifecycle. This module will generate a lib folder and copy all the dependencies from the powercontrol-gui and powercontrol-cli to this folder.
Now I have 2 questions!
Question 1
Is this a good way to go? Or is there a better way?
powercontrol-dist/pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<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>
<parent>
<groupId>nl.nberlijn.powercontrol</groupId>
<artifactId>powercontrol</artifactId>
<version>1.0</version>
</parent>
<artifactId>powercontrol-dist</artifactId>
<packaging>pom</packaging>
<name>PowerControl Dist</name>
<description>Dist</description>
<dependencies>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>powercontrol-gui</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>powercontrol-cli</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<version>2.10</version>
<executions>
<execution>
<id>copy</id>
<phase>package</phase>
<goals>
<goal>copy-dependencies</goal>
</goals>
<configuration>
<outputDirectory>${project.build.directory}/lib</outputDirectory>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
Output:
powercontrol/
bin/
gui.exe
ui.exe
lib/
third-party-lib.....jar
third-party-lib.....jar
third-party-lib.....jar
powercontrol-core.jar
powercontrol-data.jar
powercontrol-gui.jar
powercontrol-cli.jar
Question 2
Also I want to make two .exe files "gui.exe and cli.exe" referencing to the powercontrol-gui.jar and the powercontrol-cli.jar.
Is adding a mainclass to the manifest inside the pom.xml in the powercontrol-gui and powercontrol-cli module enough?
<build>
<plugins>
<plugin>
<artifactId>maven-jar-plugin</artifactId>
<configuration>
<archive>
<manifest>
<mainClass>${main.class}</mainClass>
</manifest>
</archive>
</configuration>
</plugin>
</plugins>
</build>
If you want two executable JARs (gui.jar, ui.jar), you should add the shade plugin to those two modules, so that as part of each module, a standalone executable JAR is built. Both of the JARs will contain all the third-party stuff as well. You cannot create a standalone executable JAR where parts of the dependencies are in an external JAR (unless you do you own classloader magic or unless you also have to specify the external JAR on the command line).
If you are stuck with the maven shade plugin, you should tell us what problem exactly you have. Typically these can be resolved. A common problem is that certain files need to be "merged" when a shaded JAR is created, in particular files in META-INF e.g. used by Spring or by the Java Service Locator mechanism. The shade plugin offers support for such merging, but it needs to be configured for the case at hand.
Btw. I'd recommend calling the command line version "cli.jar" - "ui" sounds like "gui".
Ok, since you updated your question and now seem to be asking for a "native launcher" (exe file) instead of an executable JAR file - those are completely different things.
Launching an exe file from the command line:
C:\> gui
Launching an executable JAR file from the command line
C:\ java -jar gui.jar
To get the first, you need to create a native launcher that internally invokes Java. A project that might support you in that task could be launch4j - they also seem to provide a Maven plugin.
Related
We have a couple of legacy Java projects, which we converted to Maven projects / modules. Previously, all projects were NetBeans projects and had no real dependency management. External dependencies existed on the companies network drive and were directly included as JARs in the NetBeans projects of each module. For the internal dependencies, simple project references were used. It was a pain to build everything because the programmer had to build everything in the right order.
Now, we are in the position that we can open all the Maven modules in IntelliJ IDEA and NetBeans. However, I am having trouble figuring out the best way to combine the different modules and external dependencies in a specific way, which conforms to in-house plugin-like structure. Especially with NetBeans (developing with both IDEs must be possible).
Here is how the git repositories / project structure roughly looks like. The folder structure of the modules is the default Maven structure for each module. The list feature of this site was too clumsy, so I included it as screenshot...
We have an internal maven repository for the stuff and building with maven etc. is working. For Intellij IDEA i can run and debug the end product for customer1 via a custom run configuration, which copies the needed files in the needed structure:
With IntelliJ IDEA, I can debug the software, but I think that the approach (custom IntelliJ run config I created, pointing to all needed JARs and files directly) is rather ugly, and for NetBeans I could not find a similar "run configuration" mechanism.
So I tried to achieve this build process by creating a new "Customer1Runnable" Maven project as a sort of build description, which points to all needed Maven modules. Based on this, I believed I could achieve and automatism to create the needed software structure. Ergo copy all modules into a plugin folder and all dependencies of the modules into a lib folder inside the Customer1Runnable project, using the maven-assembly-plugin.
First off, is my assumption correct that this is a possible use case for the maven-assembly-plugin?
The project itself does not have any source files, it is only a pom.xml and the assembly-config.xml descriptor. I attached the assembly-plugin to the package phase. When running the mvn package command all connected modules are built, but for the execution of the assembly-plugin I get the following output:
For starters, I only tried to include one module in the assembly descriptor. This is the XML (opicom-assembly.xml) for it:
<assembly xmlns="http://maven.apache.org/ASSEMBLY/2.1.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/ASSEMBLY/2.1.0 http://maven.apache.org/xsd/assembly-2.1.0.xsd">
<id>opicom-assembly</id>
<formats>
<format>dir</format>
</formats>
<includeBaseDirectory>false</includeBaseDirectory>
<moduleSets>
<moduleSet>
<useAllReactorProjects>true</useAllReactorProjects>
<includes>
<include>my.company.reporting:module1</include>
</includes>
</moduleSet>
</moduleSets>
</assembly>
pom.xml of Customer1Runnable project
<?xml version="1.0" encoding="UTF-8"?>
<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>
<version>1.6</version>
<groupId>my.company.customer1</groupId>
<artifactId>OpicomRunnable</artifactId>
<packaging>pom</packaging>
<name>OpicomRunnable</name>
<repositories>
<repository>
<id>Company-Maven-Repo</id>
<url>file:\\\\MyCompany\TFSDrop\MavenRepo</url>
</repository>
</repositories>
<modules>
<module>../my.company.customer1.module1</module>
<module>../my.company.customer1.module2</module>
.
.
.
<module>../../MyCompany_Common/Report/my.company.reporting.module1</module>
</modules>
<build>
<plugins>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<version>3.3.0</version>
<inherited>true</inherited>
<executions>
<execution>
<id>copy-dependencies</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
<configuration>
<descriptors>
<descriptor>opicom-assembly.xml</descriptor>
</descriptors>
</configuration>
</plugin>
</plugins>
</build>
</project>
The pom of a module looks like this:
<?xml version="1.0" encoding="UTF-8"?>
<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>
<parent>
<groupId>my.company</groupId>
<artifactId>reporting</artifactId>
<version>1.3</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>module1</artifactId>
<version>1.3</version>
<packaging>jar</packaging>
<dependencies>
<!-- external dependencies -->
<dependency>
<groupId>commons-pool</groupId>
<artifactId>commons-pool</artifactId>
<version>1.6</version>
</dependency>
<dependency>
<groupId>com.oracle.database.jdbc</groupId>
<artifactId>ojdbc8</artifactId>
<version>21.1.0.0</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<version>3.3.0</version>
<configuration>
<finalName>my-company-${project.artifactId}</finalName>
<appendAssemblyId>false</appendAssemblyId>
<outputDirectory>../build</outputDirectory>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
</configuration>
</plugin>
</plugins>
</build>
</project>
Thanks for any input on what I am doing wrong here / how to achieve this with Maven.
EDIT:
As requested, here an example project as ZIP-File.
https://drive.google.com/drive/folders/1ilJeTrOPgYgUTdOP0J4BQcBnPT5fls0k?usp=sharing
The parent directories ModuleGroupCustomer and ModuleGroupCommon do represent git repositories in the real scenario. The relative module path is caused, because the maven project which should be my "run config" points to maven projects in both repositories.
Maybe I am misunderstanding Maven in general? I thought of it in terms of use cases for dependency management similar to .Net nuget packages, but also as "project configuration" like ordinary NetBeans/Intellij projects.
Is it better to simply stick to the existing NetBeans projects for day to day development?
After a long and tedious process of trial and error, I have found a solution which is working for me. So I decided to share the solution online, in case someone else runs into a similar problem. Here is a link to the final zip archive containing working example projects => File CustomerRunnable_RunningAssemblyPluginStackoverflowExample.zip https://drive.google.com/drive/u/0/folders/1ilJeTrOPgYgUTdOP0J4BQcBnPT5fls0k
My error was that I misunderstood how the assembly-plugin works. The approach that I executed the plugin inside my aggregator pom (CustommerRunnable) is wrong, as this maven project only exists as parent pom.
The CustommerRunnable pom.xml references all customer plugins as modules. Those modules have not the CustommerRunnable as parent, but a different pom. Then I created a separate maven project "distribution". The pom.xml of the distribution defines all the plugins (needed customer maven modules) as dependencies. It also has the CustommerRunnable pom.xml as parent. Hence when I run the project in NetBeans, all connected modules are also build(if necessary).
It also configures the assembly plugin. The assembly plugin is attached to the maven package-phase and thus executed with it. It also uses a custom assembly descriptor, which copies all the previously defined plugins into the right folders. This is done by using dependencySets with include and exclude patterns.
See https://maven.apache.org/plugins/maven-assembly-plugin/advanced-descriptor-topics.html for details on this.
So one dependencySet copies all jar files of all plugins to a /plugin folder by using an include pattern. Then this approach is inversed to copy the jar files of all external dependencies to a /lib folder.
The descriptor also defines some extra files to copy to a particular location. exec-maven-plugin, so I can comfortably start the customer software out of NetBeans. I didn't yet manage to configure the execute plugin correctly regarding the needed classpath arguments.
Endresult looks like this:
It is also worth noting that the configurations of the "Build project", "Run project" and "Debug project" inside NetBeans need a tiny bit of modification. (Right Click Module "distribution" -> "Properties" -> point "Actions"
My first use of Maven and I'm stuck with dependencies.
I created a Maven project with Eclipse and added dependencies, and it was working without problems.
But when I try to run it via command line:
$ mvn package # successfully completes
$ java -cp target/bil138_4-0.0.1-SNAPSHOT.jar tr.edu.hacettepe.cs.b21127113.bil138_4.App # NoClassDefFoundError for dependencies
It downloads dependencies, successfully builds, but when I try to run it, I get NoClassDefFoundError:
Exception in thread "main" java.lang.NoClassDefFoundError: org/codehaus/jackson/JsonParseException
at tr.edu.hacettepe.cs.b21127113.bil138_4.db.DatabaseManager.<init>(DatabaseManager.java:16)
at tr.edu.hacettepe.cs.b21127113.bil138_4.db.DatabaseManager.<init>(DatabaseManager.java:22)
at tr.edu.hacettepe.cs.b21127113.bil138_4.App.main(App.java:10)
Caused by: java.lang.ClassNotFoundException: org.codehaus.jackson.JsonParseException
at java.net.URLClassLoader$1.run(URLClassLoader.java:217)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(URLClassLoader.java:205)
at java.lang.ClassLoader.loadClass(ClassLoader.java:321)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:294)
at java.lang.ClassLoader.loadClass(ClassLoader.java:266)
... 3 more
My pom.xml is like this:
<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>tr.edu.hacettepe.cs.b21127113</groupId>
<artifactId>bil138_4</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>bil138_4</name>
<url>http://maven.apache.org</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-core-asl</artifactId>
</dependency>
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-mapper-asl</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-core-asl</artifactId>
<version>1.9.6</version>
</dependency>
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-mapper-asl</artifactId>
<version>1.9.6</version>
</dependency>
</dependencies>
</dependencyManagement>
</project>
Can anyone help me?
By default, Maven doesn't bundle dependencies in the JAR file it builds, and you're not providing them on the classpath when you're trying to execute your JAR file at the command-line. This is why the Java VM can't find the library class files when trying to execute your code.
You could manually specify the libraries on the classpath with the -cp parameter, but that quickly becomes tiresome.
A better solution is to "shade" the library code into your output JAR file. There is a Maven plugin called the maven-shade-plugin to do this. You need to register it in your POM, and it will automatically build an "uber-JAR" containing your classes and the classes for your library code too when you run mvn package.
To simply bundle all required libraries, add the following to your POM:
<project>
...
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.4.1</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
...
</project>
Once this is done, you can rerun the commands you used above:
$ mvn package
$ java -cp target/bil138_4-0.0.1-SNAPSHOT.jar tr.edu.hacettepe.cs.b21127113.bil138_4.App
If you want to do further configuration of the shade plugin in terms of what JARs should be included, specifying a Main-Class for an executable JAR file, and so on, see the "Examples" section on the maven-shade-plugin site.
when I try to run it, I get NoClassDefFoundError
Run it how? You're probably trying to run it with eclipse without having correctly imported your maven classpath. See the m2eclipse plugin for integrating maven with eclipse for that.
To verify that your maven config is correct, you could run your app with the exec plugin using:
mvn exec:java -D exec.mainClass=<your main class>
Update: First, regarding your error when running exec:java, your main class is tr.edu.hacettepe.cs.b21127113.bil138_4.App. When talking about class names, they're (almost) always dot-separated. The simple class name is just the last part: App in your case. The fully-qualified name is the full package plus the simple class name, and that's what you give to maven or java when you want to run something. What you were trying to use was a file system path to a source file. That's an entirely different beast. A class name generally translates directly to a class file that's found in the class path, as compared to a source file in the file system. In your specific case, the class file in question would probably be at target/classes/tr/edu/hacettepe/cs/b21127113/bil138_4/App.class because maven compiles to target/classes, and java traditionally creates a directory for each level of packaging.
Your original problem is simply that you haven't put the Jackson jars on your class path. When you run a java program from the command line, you have to set the class path to let it know where it can load classes from. You've added your own jar, but not the other required ones. Your comment makes me think you don't understand how to manually build a class path. In short, the class path can have two things: directories containing class files and jars containing class files. Directories containing jars won't work. For more details on building a class path, see "Setting the class path" and the java and javac tool documentation.
Your class path would need to be at least, and without the line feeds:
target/bil138_4-0.0.1-SNAPSHOT.jar:
/home/utdemir/.m2/repository/org/codehaus/jackson/jackson-core-asl/1.9.6/jackson-core-asl-1.9.6.jar:
/home/utdemir/.m2/repository/org/codehaus/jackson/jackson-mapper-asl/1.9.6/jackson-mapper-asl-1.9.6.jar
Note that the separator on Windows is a semicolon (;).
I apologize for not noticing it sooner. The problem was sitting there in your original post, but I missed it.
You have to make classpath in pom file for your dependency. Therefore you have to copy all the dependencies into one place.
Check my blog.
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<version>2.1</version>
<executions>
<execution>
<id>copy-dependencies</id>
<phase>package</phase>
<goals>
<goal>copy-dependencies</goal>
</goals>
<configuration>
<outputDirectory>${project.build.directory}/lib</outputDirectory>
<overWriteReleases>false</overWriteReleases>
<overWriteSnapshots>false</overWriteSnapshots>
<overWriteIfNewer>true</overWriteIfNewer>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>2.4</version>
<configuration>
<archive>
<manifest>
<addClasspath>true</addClasspath>
<classpathPrefix>lib/</classpathPrefix>
<mainClass>$fullqualified path to your main Class</mainClass>
</manifest>
</archive>
</configuration>
</plugin>
</plugins>
</build>
This is due to Morphia jar not being part of your output war/jar. Eclipse or local build includes them as part of classpath, but remote builds or auto/scheduled build don't consider them part of classpath.
You can include dependent jars using plugin.
Add below snippet into your pom's plugins section
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<version>3.0.0</version>
<configuration>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
</configuration>
</plugin>
For some reason, the lib is present while compiling, but missing while running.
My situation is, two versions of one lib conflict.
For example, A depends on B and C, while B depends on D:1.0, C depends on D:1.1, maven may
just import D:1.0. If A uses one class which is in D:1.1 but not in D:1.0, a NoClassDefFoundError will be throwed.
If you are in this situation too, you need to resolve the dependency conflict.
I was able to work around it by running mvn install:install-file with -Dpackaging=class. Then adding entry to POM as described here:
Choosing to Project -> Clean should resolve this
So i have a following problem:
I have a maven-project with several maven-dependencies. When i run mvn install it'll be packaged as .jar and the .jar together with the .pom-File will be placed inside my maven-repository. Now, this .jar does not contain other dependencies (and is also not supposed to!). Now, given that i have all the dependencies needed installed in my maven repository (which obviously maven will take care of), how can i run this jar on the command line without setting the classpath to point to every damn jar in the maven-repository? Is there any other way? mvn exec:java only seems to work within the maven-source directory, where it looks for the "pom.xml". But after installing, "pom.xml" becomes "name-version.pom" and i have a .jar instead of direct source/class-files. Is there any other way to point mvn exec:java to work with the .jar and .pom-File within the maven repository? Or maybe some other and better approach to do so?
Thanks in advance :)
EDIT1: I'll just put my comment from below in here to avoid further misunderstandings:
I do not want to put the dependency jars somewhere. I want to use the repository maven already takes care of.Theoretically given, that i have ALL libraries i will ever need already in my local maven repository. I want to be able to download any other maven project, that might be using some of the libraries i already have installed in my local repository, also install it using "maven install", then remove the source i downloaded and then execute the .jar created by maven and tell java or maven (depending on what the best approach is) to look for the dependencies of that project in my local maven repository.
I hope i made it clear enough :)
EDIT 2: So i decided to use mvn install to install the projects into my local .m2 repo and also keep the projects unpackaged in some defined folder.Then i can just call mvn exec:java inside those projects to run them and maven will resolve all the dependencies for me.
You may exclude some dependency that you don't want to be in your project like this -
<project>
...
<dependencies>
<dependency>
<groupId>sample.ProjectA</groupId>
<artifactId>Project-A</artifactId>
<version>1.0</version>
<scope>compile</scope>
<exclusions>
<exclusion> <!-- declare the exclusion here -->
<groupId>sample.ProjectB</groupId>
<artifactId>Project-B</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
</project>
For more information you may check the link
Make a Jar executable and define classpath dependencies with maven can be done using maven-jar-plugin to create a manifest file. The manifest file is usually used for that
example
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>2.6</version>
<configuration>
<archive>
<manifest>
<!-- It define the classpath dependencies -->
<addClasspath>true</addClasspath>
<classpathPrefix>dependency-jars/</classpathPrefix>
<!-- it makes the jar executable -->
<mainClass>com.mycompany.App</mainClass>
</manifest>
<!-- it define some entries about your artifact -->
<manifestEntries>
<Build-Maven>Maven ${maven.version}</Build-Maven>
<Build-Java>${java.version}</Build-Java>
<Build-OS>${os.name}</Build-OS>
<Build-Label>${project.version}</Build-Label>
</manifestEntries>
</archive>
</configuration>
</plugin>
When you run the command mvn package|install, the following meta-inf/manifest.mf file will be created and added into the Jar.
If you need the dependency jar be in somewhere, that can be done easily, you will use maven-dependency-plugin to copy project dependencies to somewhere you want. you can copy into your project build directory into the manifest prefix folder defined
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<version>2.5.1</version>
<executions>
<execution>
<id>copy-dependencies</id>
<phase>package</phase>
<goals>
<goal>copy-dependencies</goal>
</goals>
<configuration>
<outputDirectory>
${project.build.directory}/dependency-jars/
</outputDirectory>
</configuration>
</execution>
</executions>
</plugin>
all dependencies will be in {project}/target/dependency-jars/
That approach not include any dependency into the jar all will be out the jar in somewhere you define.
You can configurate your projects using this approach as you need
whit this approach you only have to do mvn clean install and execute your jar
I hope this aproach be the solution that you need.
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.
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>
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.
You are looking for the maven exec plugin.
mvn exec:java -Dexec.mainClass="com.example.Main" [-Dexec.args="argument1"]
would run your program
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.
I've started a new Maven project in NetBeans, accepting all the defaults. The POM, with all the JAR dependencies stripped out, is cut-n-pasted at the bottom of this question.
The application reads in various properties files (e.g. logging and config). It also reads in external resources such as fonts, images, and sounds. I do NOT want all these resources to be bundled up into the JAR file. Instead, I plan to deploy them in subdirectories beneath the directory where the JAR is deployed.
A simplified view of the project's directory structure looks like this:
-src
|---main
|---java
|---com.mypackage, etc
|---resources
|---conf
|---fonts
|---images
|---sounds
+target
What I would like to have after a clean build would look like this:
+src
-target
|---myproject-1.0.0.jar (compiled contents of "src/main/java" ONLY)
|---conf
|---fonts
|---images
|---sounds
However, when I do a "clean-and-build" or an "exec" through NetBeans (or the command-line for that matter)... what I'm actually getting looks like this:
+src
-target
|---classes
|---("src/main/java" and "src/main/resources" slammed together)
|---myproject-1.0.0.jar (the "classes" subdirectory JAR'ed up)
Can someone point me in the right direction for getting that first result rather than the second? I apologize if this is a silly question (I'm a Maven rookie), or if I overlooked a previously-asked duplicate. However, from the searching I've done on Stack Overflow... it looks like all the duplicate questions try to go the other way! (i.e. get resources into a JAR rather than keep them out)
pom.xml:
<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/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>steveperkins</groupId>
<artifactId>myproject</artifactId>
<packaging>jar</packaging>
<version>1.0.0</version>
<name>My Project</name>
<url>http://maven.apache.org</url>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>2.0.2</version>
<configuration>
<source>1.4</source>
<target>1.4</target>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
...
Although the proposed solutions would work they basically work around the maven conventions. A better alternative would be to filter out the resources so they are not included in the jar but still available as resources while working in the IDE. In the pom it should look like this:
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>jar</goal>
</goals>
<configuration>
<excludes>
<exclude>/conf/**</exclude>
<exclude>/fonts/**</exclude>
<exclude>/images/**</exclude>
<exclude>/sounds/**</exclude>
</excludes>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
This would effectively exclude them from the jar without any workarounds.
Here is the doc page for the jar plugin.
Though the above will answer your question may I suggest some additional possibility that could help you in your endeavour. As a second step, to still make these resources available you could package your project using the assembly plugin. this would allow you to create a zip file and place all the files, resources and jar, in an appropriate location so that when the zip is unpacked everything just falls into place.
If this project is part of a larger work you can still use the assembly plugin for each where you would have this situation and in the main project you could extract and reassemble them in a larger zip including all the necessary artifacts.
Lastly I suggest you leave the directory structure under target as-is. If you customize it it would be preferable to do it through the Maven variables so that the changes percolate to the other plugins. If you manually remove and rename stuff once Maven has gone through you may run into problems later. Normally the Maven jar plugin should be able to just get it right if you configure it the way you want so you have no needs to worry about what comes under target. Personally I use Eclipse and the pusign is pretty good at getting the IDE and Maven config in sync. For NetBeans I would suspect this would also be the case. If not the best approach would be to configure your project in NetBeans to use target/classes as a target folder for built artifacts and target/test-classes for stuff built from src/test/java.
Personally, I would not use the default location of resources but an "extra" location and configure the resources plugin to copy them where you want from there:
<project>
...
<build>
<plugins>
<plugin>
<artifactId>maven-resources-plugin</artifactId>
<version>2.4.3</version>
<executions>
<execution>
<id>copy-resources</id>
<!-- here the phase you need -->
<phase>validate</phase>
<goals>
<goal>copy-resources</goal>
</goals>
<configuration>
<outputDirectory>${basedir}/target</outputDirectory>
<resources>
<resource>
<directory>src/non-packaged-resources</directory>
<filtering>true</filtering>
</resource>
</resources>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
...
</build>
...
</project>
If you insist with using the default location (src/main/resources), I think you'll have to configure some exclusions (see below) to avoid resources getting copied by default and then use the same approach as above.
Another option would be to use the AntRun maven plugin and Ant to move files but this is not really the maven way so I won't detail it.
Resources
Copy Resources
Including and excluding files and directories
You can sonfigure a special execution of resources:copy-resources goal.
Eugene is on the right track but there's a better way to make this work.
It should look something like this:
<build>
<outputDirectory>target/${artifactId}-${version}</outputDirectory>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>2.3.1</version>
<configuration>
<classesDirectory>${project.build.outputDirectory}</classesDirectory>
<outputDirector>target</outputDirectory>
</configuration>
</plugin>
</plugins>
<resources>
<resource>
<directory>src/main/resources/conf</directory>
<targetPath>../conf</targetPath>
</resource>
<resource>
<directory>src/main/resources/ANOTHER_PATH</directory>
<targetPath>../ANOTHER_PATH</targetPath>
</resource>
</resources>
</build>
You won't be able to get rid of the 'classes' directory, but you'll be able to give it a different name that shouldn't interfere with NetBeans.
You can find more about the <outputDirectory> element here.
You can find more about the jar plugin here.
You can find more about the <resource> element here.
As a side note, you may want to consider running Maven under a 1.6 JDK and fork the maven-compiler-plugin to use your 1.4 JDK for compiling. You can find out more about this here. That should give you a boost to your compile time. You can also tell surefire when running test cases to use the 1.4 JDK for execution as well.