Maven Uberjar as Maven Dependency - java

I'm currently working with a Legacy app, whose process for building plugins requires adding dependencies on about 150 non-maven (ant I believe?) jar files. Ideally, I'd like to package these 150-jars into a single JAR, place it on Artifactory, and use that as a maven-dependency so that my team can more easily setup their development environment.
I've been experimenting with one-jar, maven-assembly-plugin, and maven-shade-plugin, which appears to just create one jar that contains several other jars (i.e. unzip the contents). However when adding that jar as a maven dependency, maven appears unable to resolve the contents/dependencies within these "sub-jars."
The code below is only an example of something I've tried; so feel free to suggest other approaches.
<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>com.company</groupId>
<artifactId>some-jars</artifactId>
<packaging>jar</packaging>
<version>1.0.0</version>
<name>someName</name>
<properties>
<jdk.version>1.8</jdk.version>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<configuration>
<archive>
<manifest>
<mainClass>com.mycompany.mypackage.MyMainClass</mainClass>
</manifest>
</archive>
</configuration>
</plugin>
<plugin>
<groupId>org.dstovall</groupId>
<artifactId>onejar-maven-plugin</artifactId>
<version>1.4.4</version>
<executions>
<execution>
<configuration>
<binlibs>
<fileSet>
<directory>${basedir}/jars</directory>
<includes>
<include>*</include>
</includes>
</fileSet>
</binlibs>
<!-- Optional, default is false -->
<attachToBuild>true</attachToBuild>
<!-- Optional, default is "onejar" -->
<classifier>onejar</classifier>
</configuration>
<goals>
<goal>one-jar</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
<pluginRepositories>
<pluginRepository>
<id>onejar-maven-plugin.googlecode.com</id>
<url>http://onejar-maven-plugin.googlecode.com/svn/mavenrepo</url>
</pluginRepository>
</pluginRepositories>
</project>

Perhaps you may want to consider the BOM approach adopted by Spring IO Platform (http://platform.spring.io/platform/#quick-start), JBoss Java EE 6 spec dependency in Maven (http://www.andygibson.net/blog/quickbyte/jboss-java-ee-6-spec-dependency-in-maven/)
By using BOM approach, there is no need to bundle all the libraries to one single jar.

I had the same problem a while ago
For this reason i implemented maven-bulk-deploy that make easier to implement the BOM approach
Just you have put your jars in one folder choose a common-group-id/version (eg. the same of your main project) run the plugin and it will create a BOM file (ie pom ) and will deploy all jars in your asset repo
Feel free to checkout/fork the project to try it for solve your issue

Related

Create a simple JAR instead of an executable JAR with Spring-Boot

I have a problem with a Multi-Module Spring-Boot Application.
I have one Module that I use for Services, the core-Module. And one module for View-Related Classes, the web-Module.
The are both in a parent-Module, where I add the dependencies, like the "spring-boot-starter" and that can be used by both modules.
Now to the problem:
I want to run the web-Module with the embedded Tomcat and have the core-Module as a dependency in the web-module.
In other Spring projects I would just include the maven-jar-plugin and create a jar of the core-Module.
The problem in this Spring-Boot project is that the maven-jar-plugin is already configured, in the "spring-boot-starter". And it needs a mainClass, which only the web-module has.
Small excerpt from the "spring-boot-starter"-POM
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<configuration>
<archive>
<manifest>
<mainClass>${start-class}</mainClass>
<addDefaultImplementationEntries>true</addDefaultImplementationEntries>
</manifest>
</archive>
</configuration>
</plugin>
Is there a way to package the core-Module as a JAR without needing a "start-class" in that module?
It seem like you can disable the fat-JAR from replacing the original and getting installed into the repo by configuring the spring-boot-maven-plugin.
Taken from http://docs.spring.io/spring-boot/docs/1.5.1.RELEASE/maven-plugin/examples/repackage-disable-attach.html:
By default, the repackage goal will replace the original artifact with
the executable one. If you need to only deploy the original jar and
yet be able to run your app with the regular file name, configure the
plugin as follows:
<project>
...
<build>
...
<plugins>
...
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>1.5.1.RELEASE</version>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
<configuration>
<attach>false</attach>
</configuration>
</execution>
</executions>
...
</plugin>
...
</plugins>
...
</build>
...
</project>
This configuration will generate two artifacts: the original one and the executable counter part produced by the repackage goal. Only the original one will be installed/deployed.
You can just disable the spring-boot-maven plugin this way.
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<executions>
<execution>
<configuration>
<skip>true</skip>
</configuration>
</execution>
</executions>
</plugin>
I have found the answer.
I configured my application just like #judgingnotjudging explained. The difference was that I had put this, in the parent-POM:
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
It was preventing the default creation of JARs in the children-modules. I could resolve this problem by including this only in the web-Module.
That way Spring-Boot builds a fat-JAR from the web-Module and a simple JAR from the core-Module.
To create simple jar update
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
To
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<classifier>exec</classifier>
</configuration>
</plugin>
For more details please visit below Spring URL:-
https://docs.spring.io/spring-boot/docs/1.1.2.RELEASE/reference/html/howto-build.html
You can simply change
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
to
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version>
I don't know whether it's still relevant or not, however, a simple classifier needs to be configured for the spring-boot-maven-plugin -
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<classifier>exec</classifier>
</configuration>
</plugin>
</plugins>
</build>
This would generate two jars - one a normal one which can be included as a dependency, another as an exe jar with the "exec" word appended as a suffix - like test-0.0.1-SNAPSHOT-exec.jar and test-0.0.1-SNAPSHOT.jar
If you're using the embedded tomcat to run your app, don't you just want a standard Spring Boot fat jar for your web app? If so, just mark your web module as dependent on your core module in the pom, and build your project.
It's there a different use case that you need the jars separated?
Are you building your modules as completely separate modules or as modules as part of a single multi-module project? The difference is in the latter, you will have a pom at the root specifying the modules. I forget the Maven syntax specifically, so my example is Gradle (Maven docs for multi-module builds are here). Sorry about that.
baseProject
|----web-module
|----core-module
baseProject's build.gradle:
project(':web-module') {
dependencies {
compile project(':core-module')
}
evaluationDependsOn(':core-module')
}
Maven has a similar structure. You should review the docs, but I believe all you need to do is specify the module order correctly in your parent pom as below and include the dependency in your web-module pom.
Parent pom:
...
<modules>
<module>core-module</module>
<module>web-module</module>
</modules>
web-pom:
<project ...>
<parent>
<groupId>com.example</groupId>
<artifactId>simple-parent</artifactId>
<version>1.0</version>
</parent>
<artifactId>web-module</artifactId>
<packaging>war</packaging>
<name>My web-module</name>
<dependencies>
...
<dependency>
<groupId>com.example</groupId>
<artifactId>core-module</artifactId>
<version>1.0</version>
</dependency>
</dependencies>
...
core pom should only need to include the parent section as in the web-module above, and otherwise be the same.

Maven - Configuring POM to execute JAR

I have a simple Hello World application 'App' which has been made in a Maven project. I'm aware that after building the project, the Maven assembly plugin is required in order to execute the JAR which is created when the project is built. I have followed the instructions found at:
http://maven.apache.org/plugins/maven-assembly-plugin/usage.html
But after editing my pom.xml and re-building the project, I still cannot run my JAR file.
Here is my 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>
<groupId>com.mycompany</groupId>
<artifactId>mini-project-6-ex6</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.7</maven.compiler.source>
<maven.compiler.target>1.7</maven.compiler.target>
</properties>
<build>
<plugins>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<version>2.5.4</version>
<configuration>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
<archive>
<manifest>
<mainClass>com.mycompany.mini.project.ex6.App</mainClass>
</manifest>
</archive>
</configuration>
</plugin>
</plugins>
</build>
Is there a mistake in my POM or additional lines which I have left out which is causing me to not be able to run the resulting JAR file?
EDIT: It seems as though the JAR has no manifest attribute (see comments). I suppose that I could open the jar using 7zip or similar program and manually add a manifest file, but the real question is how to create the JAR using Maven to include a manifest file in the first place.
You're looking at the wrong JAR. The POM above will create at least two JARs:
mini-project-6-ex6-1.0-SNAPSHOT.jar
mini-project-6-ex6-1.0-SNAPSHOT-jar-with-dependencies.jar
The former is only the code of the module, the latter is the code plus all the dependencies.
If you run mvn install, you will only get the first one since the Assembly Plugin isn't configured to run automatically. You can run it manually with mvn assembly:assembly.
If you want it to run automatically when you mvn install, you need to change the POM:
<plugins>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<version>2.5.4</version>
<configuration>
...
</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>
See http://maven.apache.org/plugins/maven-assembly-plugin/usage.html#Execution:_Building_an_Assembly

How to correctly put precompiled jars inside a maven project?

There's a precompiled jar file. My sources depend on it. I need:
This file should be added to classpath
When I do mvn deploy without parameters, the file should be uploaded to remote repo
When others use my project as a dependency, the file should be automatically downloaded from the remote repo and added to classpath
I want this jar to have its own artifact id and pom file
If possible, I'd like to keep the original jar name
I tried to put this in my pom:
<repositories>
<repository>
<id>pom-local</id>
<url>file://${basedir}/repo</url>
</repository>
</repositories>
Then wrote another pom.xml and put it and the jar file to ${basedir}/repo/.
Strangely, according to google, nobody uses this. I didn't figure out how to keep the original jar name. And I can't make maven deploy it.
There's attach-artifact goal, but it requires a file name, not artifactId as the parameter and it puts the jar to the same folder.
Possible solution:
<dependency>
<groupId>org.foo</groupId>
<version>1.0</version>
<artifactId>eeerrr</artifactId>
</dependency>
</dependencies>
<repositories>
<repository>
<id>pom-local</id>
<url>file://${basedir}/repo</url>
</repository>
</repositories>
<properties>
<!-- Will use this instead of <distributionManagement>, because it's easier to extract url from one place -->
<altDeploymentRepository>todo::default::http://0.0.0.0</altDeploymentRepository>
</properties>
<build>
<plugins>
<!-- Extract url -->
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>build-helper-maven-plugin</artifactId>
<executions>
<execution>
<id>extract-url</id>
<phase>verify</phase>
<goals>
<goal>regex-property</goal>
</goals>
<configuration>
<name>deploymentRepository.url</name>
<value>${altDeploymentRepository}</value>
<regex>.*::(.*)</regex>
<replacement>$1</replacement>
<failIfNoMatch>true</failIfNoMatch>
</configuration>
</execution>
</executions>
</plugin>
<!-- Upload additional artifact -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-deploy-plugin</artifactId>
<executions>
<execution>
<id>deploy-3rd-party-jar</id>
<phase>deploy</phase>
<goals>
<goal>deploy-file</goal>
</goals>
<configuration>
<groupId>org.foo</groupId>
<artifactId>eeerrr</artifactId>
<version>1.0</version>
<packaging>jar</packaging>
<file>${basedir}/repo/org/foo/eeerrr/1.0/eeerrr-1.0.jar</file>
<url>${deploymentRepository.url}</url>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
Assuming that this precompiled jar doesnt change that often.
You can just do a one time deploy to your local cached repo and remote repo using mvn install:install-file and mvn deploy:deploy-file.
Have a look at How to deploy jars to nexus with cmd, this is a similar question.

Conventional workflow to build deployable maven code from repo

I am setting up a Maven repository with the goal of automating much of my build and deploy process. My current workflow begins with bringing up a server, doing a git clone, and running a bunch of mvn install commands to complete the top level application.
To my understanding, Maven is not a tool for deploying applications, and at this point I do not see a need for a continuous integration or continuous deployment server or formal process.
However, thus far I have not been able to find a canonical way to put together the complete, runnable program together with Maven.
This script goes so far as to get the top level jar and install it into the local maven repo:
mvn -DgroupId=me.company -DartifactId=Top-Level-Application -Dversion=1.0-SNAPSHOT -DrepoUrl='http://theserver:8081/nexus/' dependency:get
If I installed everything manually, this application would find all of its dependent jars in a lib/ directory, as Maven ought to do. However this jar as pulled is not deployable - while the dependent jars are installed to a local repo they are not compiled where the classpath expects it.
I imagine if I could rig the above script to pull the pom.xml instead of just the jar and run a mvn install on that, everything would go smoothly (although weirdly that seems to be duplicating the last step of the build process). Another option is for applications to contain all dependent jars rolled up into one giant jar and have no external dependencies.
Which of these (or other) options is the proper way to complete the Maven build process?
Here is our top-level pom narrowed down to as much as I can think would be relevant:
<project>
<modelVersion>4.0.0</modelVersion>
<groupId>me.company</groupId>
<artifactId>Top-Level-Application</artifactId>
<packaging>jar</packaging>
<version>1.0-SNAPSHOT</version>
<name>Top Level Application</name>
<url>http://company.me</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<distributionManagement>
<repository>
<id>releases</id>
<url>http://host:8081/nexus/content/repositories/releases</url>
</repository>
<snapshotRepository>
<id>snapshots</id>
<name>Internal Snapshots</name>
<url>http://host:8081/nexus/content/repositories/snapshots</url>
</snapshotRepository>
</distributionManagement>
<build>
<sourceDirectory>src</sourceDirectory>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>2.5.1</version>
<configuration>
<source>1.7</source>
<target>1.7</target>
</configuration>
</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>me.company.application.MainClass</mainClass>
</manifest>
</archive>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<version>2.4</version>
<executions>
<execution>
<id>copy</id>
<phase>compile</phase>
<goals>
<goal>copy-dependencies</goal>
</goals>
<configuration>
<outputDirectory>
${project.build.directory}/lib
</outputDirectory>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>me.company</groupId>
<artifactId>First-Library</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<!-- more in-house libraries -->
<!-- third party libraries - Apache Commons, javamail, etc. -->
</dependencies>
</project>
Correct me if I'm wrong -- you want to transform your source code into a compiled JAR, with appropriately-set classpath for its dependencies? That's not what dependency:get is for at all. You should look into the Maven JAR plugin and/or the Maven AppAssembler plugin.

Maven Copy in build phase

My situation is:
I have a project that scan a directory of libs.
This project dosen't has dependence with other projects (jars), but this dynamically load them from the directory to his classloader (is a kind of plugin architecture).
Both projects are under a pom project
Examples of poms:
Parent:
<groupId>mygroup</groupId>
<artifactId>myparent</artifactId>
<packaging>pom</packaging>
<version>1.0</version>
...
"Plugin":
<parent>
<artifactId>myparent</artifactId>
<groupId>mygroup</groupId>
<version>1.0</version>
</parent>
<groupId>mygroup</groupId>
<artifactId>myplugin</artifactId>
<packaging>jar</packaging>
<version>1.0</version>
...
"Loader":
<parent>
<artifactId>myparent</artifactId>
<groupId>mygroup</groupId>
<version>1.0</version>
</parent>
<groupId>mygroup</groupId>
<artifactId>myloader</artifactId>
<packaging>war</packaging>
<version>1.0</version>
...
Now, I need to generate the jar "plugin" and copy it to a defined directory in the project "loader" when I build it.
This have to work locally in eclipse and in jenkins automatically builds.
Someone has any idea how I can do this?
Basically it solved in four steps:
I configure the plugin "maven-assembly-plugin", in my "plugin" project, to copy the jar in a relative path within the project "loader".
In this configuration I specify the property "descriptorRef" in jar-with-dependencies, this helps me to pack the necessary libraries into the jar
I configure the plugin "maven-copy-plugin", in my "plugin" project, to copy the properties in the same relative path within the project "loader"
The pom.xml from my "plugin" project stay like this:
<plugins>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<configuration>
<outputDirectory>${my.output.directory}</outputDirectory>
<archive>
<manifest>
<addClasspath>true</addClasspath>
</manifest>
</archive>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
</configuration>
</plugin>
<plugin>
<groupId>com.goldin.plugins</groupId>
<artifactId>maven-copy-plugin</artifactId>
<version>0.2.3.6</version>
<executions>
<execution>
<id>create-archive</id>
<phase>package</phase>
<goals>
<goal>copy</goal>
</goals>
<configuration>
<resources>
<!-- Copy config properties -->
<resource>
<targetPath>${my.output.directory}</targetPath>
<directory>${project.basedir}/conf</directory>
<includes>
<include>*.properties</include>
</includes>
</resource>
</resources>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
On the other hand, I set the project to consume the plugins from the directory ${my.output.directory}
To automate the build in eclipse I create a new type of program builder for the project. This builder run the command "mvn assembly:assembly".
Step 1:
Step 2:
Step 3: In the tab "Build Options" you can configure details of this execution.
For jenkins, I create a dummy plugin which is used only from the tests.
Run maven install for plugin, which puts the resulting jar in your local maven repo. Then specify plugin in your dependencies section as a dependency. In this case, even though these projects are siblings, they are only siblings in the sense that the parent is specify the build order when mvn goals are run in parent. Their actual build artifacts must be included by the sibling if you intend to use it there.

Categories

Resources