How to copy dependencies of a multi module project? - java

I have a project that depends on a different project-module in my workspace. I'm trying to copy all dependencies (including the module) to a lib folder for creating an executable jar that has no packaged all jars inside itself.
But maven-dependency-plugin keeps complaining that it could not copy the module classes to the target folder of my project. What might be wrong?
my.groupt
my-module
1.0
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<executions>
<execution>
<id>copy-dependencies</id>
<phase>package</phase>
<goals>
<goal>copy-dependencies</goal>
</goals>
<configuration>
<outputDirectory>${project.build.directory}/lib</outputDirectory>
</configuration>
</execution>
</executions>
</plugin>
Result:
Failed to execute goal
org.apache.maven.plugins:maven-dependency-plugin:2.1:copy-dependencies
(copy-dependencies) on project my-project: Error copying
artifact from C:\workspace\my-module\target\classes to
C:\workspace\my-project\target\lib\classes: (Access denied)

The reason for your failure is workspace resolution in eclipse. Eclipse m2e injects itself into the artifact resolution of maven.
So when your my-project tries to get all dependencies, the artifact resolver returns not the jar file (which does not yet exist), but the classes folder of your dependent project.
If you try to build your project using "run as -> maven install", it should work.
So this is a scenario that you cannot comfortably resolve in workspace. Either turn of workspace resolution for my-project, disable the dependency plugin inside your eclipse (lifecycle bindings) or use a different plugin like assembly to copy your depdencies (which is cleaner, btw.). Note however, that the latter will also only work if maven is called manually.

You are probably looking for the Maven Shade plugin.
It is much more flexible but be warned - any module built through Maven Shade makes automatic tracing of dependencies very difficult. Try not to make your shaded result the primary outcome of the build process.

Related

Maven : Distributing maven jar

I have a sample.jar created from a Maven project with all the dependencies (fat jar using maven assembly plugin) it requires. I use this jar in a client's application by using mvn install:install-file and including the dependency in the client application's pom.xml. This works.
But is there a way such that I do not have to build the sample.jar as a fat jar?
Instead let the client application's pom resolve the dependencies required by sample.jar as well by reading the sample.jar's pom.xml, if all of the dependencies of sample.jar are available from Maven central repo?
UPDATE:
My maven assembly plugin.
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<configuration>
<archive>
<manifest>
<mainClass>myMainClass</mainClass>
</manifest>
</archive>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
<appendAssemblyId>false</appendAssemblyId>
</configuration>
<executions>
<execution>
<id>make-assembly</id> <!-- this is used for inheritance merges -->
<phase>package</phase> <!-- bind to the packaging phase -->
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
</plugin>
In your repository of choice (Central, a proprietary Artifactory etc.), you need to upload both the pom and the (thin) jar.
When you declare a dependency in the client project, Maven will automatically look for both. It will use the pom to determine the transitive dependencies and will compile against the jar.
There are a few things to note here.
You didn't specify how exactly you generate the fat jar but normally maven-assembly-plugin will create an additional file next to the thin (regular) jar file it creates. You can check if that is the case by looking at your project's target folder.
Unless otherwise configured, maven-assembly-plugin would also make any artifact it creates a project artifact. When you run mvn install all project artifacts will be installed in the local Maven repo. That means you probably already have both the thin and the fat jar in your local Maven repo.
Assuming the above statements hold true in your particular case and your client project has a standard dependency record (with no special types, qualifiers, ...) it is most likely that it already uses the thin jar. You can check if that is the case by running mvn dependency:tree in your client project.
Please check this answer. Worth adding that either generatePom or pomFile options in the mvn install command are crucial for Maven to be able to resolve transitive dependencies if you use the thin version of your jar. Alternatively, you may upload your thin jar into an artifactory, if possible, that should also do it.
Useful references:
Installing an artifact with a custom POM
Generating a generic POM

'mvn clean' vs.'mvn clean install' (when a plugin execution is attached to 'clean')

Let's say I want to install project-local dependencies (jar files) to my local maven repository (~/.m2) prior to compiling the project so I can reference them in my POM just like I would reference any dependency from Maven Central. Currently, I'm using Maven install plugin's install-file goal attached to the 'clean' phase (because my IDE uses it), like so:
<dependencies>
<dependency>
<groupId>group.id</groupId>
<artifactId>artifact.id</artifactId>
<version>artifact.version</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-install-plugin</artifactId>
<version>2.5.2</version>
<executions>
<execution>
<id>install-my-local-dependency</id>
<phase>clean</phase>
<goals>
<goal>install-file</goal>
</goals>
<configuration>
<groupId>group.id</groupId>
<artifactId>artifact.id</artifactId>
<version>artifact.version</version>
<file>${project.basedir}/lib/local-dep.jar</file>
<packaging>jar</packaging>
<generatePom>true</generatePom>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
When I execute mvn clean (or its equivalent in the IDE), everything runs as I'd expect:
--- maven-clean-plugin:2.5:clean (default-clean) # MyProject ---
--- maven-install-plugin:2.5.2:install-file (install-...) # MyProject ---
Installing ${project.basedir}/lib/local-dep.jar to ~/.m2/repository/group.id/artifact.id/local-dep.jar
But when I execute mvn clean install instead (or its equivalent in the IDE), I get the following error:
Failed to execute goal on project MyProject: Could not resolve dependencies for project com.example.MyProject:jar:1.0: Could not find artifact group.id:artifact.id:jar:artifact.version in central (https://repo.maven.apache.org/maven2) -> [Help 1]
For some reason, Maven install plugin's install-file goal either does not run in this case, or doesn't run soon enough. Why? The other StackOverflow answers I found explain differences between both commands but in my eyes, they do not explain this particular difference as my project has no modules.
Is there a better way of doing the same thing cross-platform, even on build servers (e.g. Jenkins) and with at least one other dependent project?
Should it be any help, I have the following Maven versions:
CLI: 3.6.0
IDE: 3.3.9
Maven first analyses the pom.xml and then calls the goals/phases. The analysis itself is complicated and has different depths, so I guess that calling clean alone will not make Maven analyse the dependencies, but calling clean install does so. Note that the analysis of the POM only happens once, not again for every goal/phase.
Generally, your approach cannot be recommended. Usually, you put project dependencies into remote Maven repositories, so that they can be resolved through them. If you work inside a company, you should set up a Nexus/Artifactory server that handles your artifacts.
If you want people outside your company to build the artifact, you need to find a provider for Maven repositories. I guess that github/gitlab can help you here. Then you need to add those repositories to the POM.

How to create runnable jar with dependencies in extra folder on mutimodule project?

I have a common library that is shared among different project. As the library is constantly extended (at least for now), it should just be picked up from eclipse workspace during build.
I'm trying to use the maven-dependency-plugin to copy all dependencies in a /lib folder next to the runnable jar. But it does not work:
<dependencies>
<!-- A jar that is opened as project in workspace, not installed into maven repo, and not a submodule. It should just be picked up and added as jar during package. -->
<dependency>
<groupId>my.domain</groupId>
<artifactId>project-commons</artifactId>
</dependency>
</dependencies>
<plugin>
<artifactId>maven-dependency-plugin</artifactId>
<version>2.8</version>
<executions>
<execution>
<id>copy-dependencies</id>
<phase>package</phase>
<goals>
<goal>copy-dependencies</goal>
</goals>
<configuration>
<outputDirectory>${project.build.directory}/lib/</outputDirectory>
<overWriteReleases>true</overWriteReleases>
</configuration>
</execution>
</executions>
</plugin>
[ERROR] Failed to execute goal
org.apache.maven.plugins:maven-dependency-plugin:2.8:copy-dependencies
(copy-dependencies) on project mydomain: Artifact has not been
packaged yet. When used on reactor artifact, copy should be executed
after packaging: see MDEP-187. -> [Help 1]
The error ocures when maven tries to copy the commons project that is open in the workspace. Did I miss anything?
I ran into this problem myself today, downloading the latest version of CTAKES (3.2) that integrates YTEX which would build on the command line but not in Eclipse. The error is a result of apparently a known issue with the plugin (https://jira.codehaus.org/browse/MDEP-187).
I eventually fixed it (and I think this will fix your problem) by changing the phase from package to prepare-package.
package
to
prepare-package
Try that and see if it works for you.

Maven: Export jar with 3rd-party jars added to classpath in manifest

I am developing a Java maven project with Eclipse and want to export a jar that includes all referenced libraries. These referenced libraries fall into one of these two categories:
They are explicit (or implicit) dependencies in the pom.xml
I have some libraries not available as maven artifacts and have put them in /lib (and added them to the build path in Eclipse)
There's the maven-assembly-plugin, which works fine for 1). However, I'm unable to find a maven plugin that also includes non-maven-dependencies, e.g. "all jars in /lib".
Then there's the Eclipse FatJar plugin, which sort of works, but hasn't been updated since 2009, so it seems unmaintained. Also I prefer an export method that I can directly specify in the pom.xml.
Can anyone point me to a maven plugin or the like to export all referenced libraries, including those from case 2) ? That only needs to involve putting them in the jar and referencing them in the manifest's classpath.
Thanks!
I think the best way to handle this is to include your custom libs into a local maven repository. Now you can inlcude your libraries as maven dependencies and you can export all your dependencies specified in your pom with the maven-assembly-plugin.
Here is a tutorial, how to put your libs into a local repository in maven to use it in your pom. http://www.mkyong.com/maven/how-to-include-library-manully-into-maven-local-repository/
And in your pom.xml:
<!-- setup jar manifest to executable with dependencies -->
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<configuration>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
<archive>
<manifest>
<mainClass>your.main.class</mainClass>
</manifest>
</archive>
</configuration>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
</plugin>
This looks like a task for Tycho. It is a set of maven plugins that allows to create eclipse plugins with maven. Tycho considers manifest entries as build dependencies.
However I'm not aware of how to package all those dependencies in a single jar. This may also be conflicting with the osgi spec. But if you wish to ignore osgi you could just try the jar-with-dependencies assembly.

Maven: My project has a dependency that is NOT in any maven repo (system scope). Does it get included in my final jar?

So my maven project depends on a jar that is NOT in any maven repository. Therefore I need to use the system scope to include this local file in my maven classpath. Question is: When I build my final jar to distribute my library, I do need to somehow include that dependency with it. The classes can be extracted and then bundled with my library classes OR the jar can be included somehow inside my library jar. I am not even sure the latter (jar inside jar) is possible in Java.
So how should I approach this problem? Will Maven take my system scope dependency and take care of that for me? What should I do?
Thanks!
No, it will not. These dependencies are only for compilation and transitive dependency resolution. Your library consumers should have the jar too.
However, you could use the Assembly Plugin to repackage the jar's classes into your artifact.
Or, the standard approach: then you will publish your artifact, you will create a public repository for deployment. You also could deploy the jar in it.
UDPATE: adding example for the shade plugin (instead of the Assembly)
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>1.4</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<artifactSet>
<includes>
<include>legacy-jar.groupId:legacyJar</include>
</includes>
</artifactSet>
</configuration>
</execution>
</executions>
</plugin>
There is a second option. Put the jar into your repository as described here:
http://maven.apache.org/guides/mini/guide-3rd-party-jars-local.html
and to your online repository
http://maven.apache.org/guides/mini/guide-3rd-party-jars-remote.html
give your client access to your repository and use your legacy.jar as normal dependency.
It depends a little bit what kind of library do you have. With this way you don't have problems with version conflicts of your legacy.jar in the environment of your customer.
Could this be what you are looking for, http://maven.40175.n5.nabble.com/How-to-specify-local-dependency-in-maven2-td103415.html.

Categories