Build fat jar in maven with dependencies only - java

I use maven-shade-plugin in my java project.
I have a module called core and i would like to pack all its dependencies into one single jar. I don't wan't to create fat big jar every time and deploy it along with my application code. My application jar has size < 1 MB but dependencies > 20 MB
On my server i would run my application like:
java -cp core.lib.jar:core.jar myApp
I need to deploy only core.jar to my server when i change application code and need to upload core.lib.jar only when i change dependencies (this is very rare).
There's a lot of docs how to build fat jar with java sources but i want to exclude them and deliver it to the server independently.
There is two maven plugins for building fat jar: assembly and shade. I want to stick with shade.
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.2.3</version>
<executions>
<execution>
<phase>none</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<artifactSet>
<excludes>
<exclude>core</exclude>
</excludes>
</artifactSet>
</configuration>
</execution>
</executions>
</plugin>
When i run mvn -pl core shade:shade i get error: core: Failed to create shaded artifact, project main artifact does not exist
I thought that that option:
core
would exclude my sources from the artifact.
I set phase to none because i don't want to create fat jar when i want to run package.
I would like to run mvn shade:shade and have updated fat jar.
I'm not an expert in maven or its plugins, so any help is very appreciated :)

I haven't used shade plugin by myself so I can't provide any practical guidance, however it seems like you need a multi-module maven project:
Refactor your project (move the folders) to look like this:
my-project
|__app
| |__src
| | |__main( (and all the production sources/resources inside)
| | |__test (all the tests/test resources inside)
| |__pom.xml
|__core-lib
| |__pom.xml
|__ pom.xml
my-project/pom.xml could be a "multi-module" aggregator: it should have <packaging>pom</packaging> and have the following:
<modules>
<module>app</module>
<module>core-lib</module>
</modules>
Now app module could be a place where you write the code of your application. It will change often, but it won't produce any fat jar - only a regular jar with a compiled code from this module only (in your example, it will be 1mb artifact).
The core-lib module will be a module that in it's pom.xml will contain the definitions of the shade / assembly plugin and this module will be responsible for the creation of the "fat jar". In its dependences section it will list all the 3rd party dependencies/code that you'll have to change 'rarely' (in your case, the one with 20 mb artifact).
If you need that, the "app" module could depend on "core-lib" module so that the application will have an access to the classes from the library.
So your usual workflow will be:
cd my-project/app
mvn package
If you want to build both application and core-libraries, then:
cd my-project
mvn package
You can read about multi modules here or here

I needed to move this block under plugin / configuration
<artifactSet>
<excludes>
<exclude>core</exclude>
</excludes>
</artifactSet>
Used to be in executions block.
Now i can run mvn -pl core shade:shade and have fat jar with dependencies only :)

Related

Maven : How to package external jars(folder) in Maven project

I am having a group of external jars(in hundreds) which I have added in the build path of my project to make it work.
But while packaging it is failing as these jar's are not available to maven.
I have gone though many articles and all the solutions(like adding the jar at system path) are for a single jar only.
<dependency>
<groupId>com.sample</groupId>
<artifactId>sample</artifactId>
<version>1.0</version>
<scope>system</scope>
<systemPath>${project.basedir}/src/main/resources/Name_Your_JAR.jar</systemPath>
</dependency>
Is there any way we can add the group of jars(folder) to the packaging on the project? or any other solution by which my project can build successfully?
can we create a single jar containing all my jars inside and then use the system scope of maven
I have tried creating jar by jar -cvf my_jar.jar * and placed this jar in the system scope. But it does not worked for me.
My solution : Maven pluggin addjar let us add all jar at a place(projectdirectory/lib in this case).
this enables you to add these jar's in the final package(jar in my case) when you maven build, but to run locally you have to add those jar files directly in the classpath.
<plugin>
<groupId>com.googlecode.addjars-maven-plugin</groupId>
<artifactId>addjars-maven-plugin</artifactId>
<version>1.0.5</version>
<executions>
<execution>
<goals>
<goal>add-jars</goal>
</goals>
<configuration>
<resources>
<resource>
<directory>${basedir}/lib</directory>
</resource>
</resources>
</configuration>
</execution>
</executions>
</plugin>
Now create a shade jar using mvn clean install shade:shade
The bad news: For a proper Maven project, you need to add each and every artifact you use as <dependency> in your POM.
The good news: I very much doubt that these 100 jars are all
- directly used in your source code
- not available in a public Maven repository like MavenCentral
So the better strategy would be to figure out what you really need and find that in MavenCentral. Then Maven finds all the transitive dependencies for you. So if you really need 10 of the jars and all other jars are just dependencies of your dependencies, just add these 10 ones (from MavenCentral) and you are done.

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

Maven: How to run module before building other modules in multi-module project?

I have a Maven POM Project that includes a number of modules.
One of them is a REST Web Service that some of the other modules use.
This module is a standalone executable.
What I am trying to achieve is to have the web service build and run before all the other modules.
I understand that just by changing the order of the modules in the pom file I can get the module to build before the others, but how do I run it before building the rest of them?
I need to run it so that I can perform a series of tests included in the rest of the modules.
The reason I am trying to achieve this kind of functionality is because what I'm ultimately trying to do is having the project build and test correctly on Jenkins.
I understand that just by changing the order of the modules in the pom file I can get the module to build before the others, but how do I run it before building the rest of them?
Ordering of modules will not necessarily build them in that order. If one module depends on another then you must handle it through <dependency>in the pom of the dependent project.
To answer your question, build the REST service module first and deploy it to your server/container. In multimodule projects running command like mvn somegoal will run somegoal on all the modules. To run different goals on modules, try in two steps
mvn -pl module1 somegoal
mvn -pl module2 someothergoal
In your project they will take the form of
mvn -pl RESTModule deploy followed by usual
mvn command for running test for other modules. Remember the -pl option for these too.
Edit: After looking at the requirements from your comment I tried one more solution. Might work for you as well. Add maven-deploy-plugin to the pom of REST Module and tie it to a phase such that only the RESTModule project deploys the artifacts but not others (since they don't have such plugin in their pom). snippet to be added.
`
<project>
<build>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-deploy-plugin</artifactId>
<version>2.8.2</version>
<executions>
<execution>
<id>MyProjId</id>
<phase>package</phase>
<goals>
<goal>deploy</goal>
</goals>
</execution>
</executions>
</plugin>
</build>
<distributionManagement>
<repository>
<id>my-repository</id>
<url>http://path/to/your/repo</url>
</repository>
</distributionManagement>
</project>
and run the project using the parent pom in jenkins.

How to copy dependencies of a multi module project?

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.

Building Maven fat jar conditionally

I followed the example in Building a fat jar using maven and now I can run the following to build/test and install my jars.
mvn clean compile install
However, install now takes a lot longer because we are now building a fat jar. Is it possible to have two versions of install where one just builds jars without dependencies and the other does that and in addition builds the fat jar, like:
mvn clean compile install
mvn clean compile install-fatjar
I know install-fatjar is not a valid phase but just want to give an idea of what I'm trying to accomplish, i.e. a conditional install where the fat jar is built only when an option is provided.
Create a profile for the fat jar and configure the maven assembly plugin to create the fat jar in this profile.
For example use this profile:
<profiles>
<profile>
<id>fatjar</id>
<build>
<plugins>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<configuration>
<archive>
<manifest>
<mainClass>fully.qualified.MainClass</mainClass>
</manifest>
</archive>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
</configuration>
</plugin>
</plugins>
</build>
</profile>
</profiles>
Then you can build it by activating the profile
mvn -P fatjar clean install
where fatjar is the profile id.
Multiple profiles can also be activated
mvn -P fatjar,release install
Normally we don't use the fat-jar (or, uber-jar) as the main artifact.
You can make use of Assembly or Shade plugin to create a variation of the artifact (with a different classifier) which is a uber-jar.
It is strongly recommended that you still keep your "normal" artifact as is. If you want the uber-jar only occasionally, put the use of assembly/shade plugin for "uber-jar" creation in a profile, and activate the profile whenever you want. However, this should still generate you an "extra" uber-jar instead of making your main artifact a uber-jar.

Categories