Building a maven project for deployment - java

Hello all I'm pretty new to using maven to build Java project so I'm sure the solution to my problem will be quite easy. I have installed the Eclipse Maven plugin and created a maven project. I now wish to package this project and distribute it as a runnable jar file. However when I run
mvn package
and then try to run the jar file I get the following error:
Exception in thread "main" java.lang.NoClassDefFoundError: main/Formatter
The pom for this particular file looks like so:
<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>GROUP</groupId>
<artifactId>pcap.csv.xml</artifactId>
<version>0.0.1-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>net.sf.opencsv</groupId>
<artifactId>opencsv</artifactId>
<version>2.0</version>
</dependency>
</dependencies>
</project>
Any help would be greatly appreciated.

You need to specify the main class in the jar plugin:
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<configuration>
<archive>
<manifest>
<addClasspath>true</addClasspath>
<mainClass>main.Formatter</mainClass>
</manifest>
</archive>
</configuration>
</plugin>
</plugins>
</build>
If you want to distribute a single jar you need to use a single-jarifier (fatjar, jarjar, onejar, etc.)

Related

Multi-module JavaFX maven project packaging issue

This is an attempt to create a multi-module JavaFX application with maven.
Given the following structure of the project:
project
| pom1.xml
|_____ Word Generator (Folder)
| pom2.xml
|_____logic (folder)
| WordGenerator
|_____UI (folder)
| pom3.xml
|_____marty
| App
| PrimaryController
| SecondaryController
We have the following structure of the pom files in order of the scheme above:
pom1.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>org.games.marty</groupId>
<artifactId>words</artifactId>
<packaging>pom</packaging>
<version>0.1</version>
<modules>
<module>UI</module>
<module>Word Generator</module>
</modules>
<properties>
<maven.compiler.source>16</maven.compiler.source>
<maven.compiler.target>16</maven.compiler.target>
</properties>
<build>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version>
<configuration>
<source>16</source>
<target>16</target>
</configuration>
</plugin>
</plugins>
</pluginManagement>
</build>
</project>
pom2.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">
<parent>
<artifactId>words</artifactId>
<groupId>org.games.marty</groupId>
<version>0.1</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>word.generator</artifactId>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>16</maven.compiler.source>
<maven.compiler.target>16</maven.compiler.target>
</properties>
<build>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version>
<configuration>
<source>16</source>
<target>16</target>
</configuration>
</plugin>
<plugin>
<!-- Build an executable JAR -->
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>3.1.0</version>
<configuration>
<archive>
<manifest>
<addClasspath>true</addClasspath>
<classpathPrefix>lib/</classpathPrefix>
<mainClass>org.games.marty.logic.WordGenerator</mainClass>
</manifest>
</archive>
</configuration>
</plugin>
</plugins>
</pluginManagement>
</build>
</project>
pom3.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>
<artifactId>UI</artifactId>
<version>0.1</version>
<parent>
<artifactId>words</artifactId>
<groupId>org.games.marty</groupId>
<version>0.1</version>
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>16</maven.compiler.source>
<maven.compiler.target>16</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-controls</artifactId>
<version>16</version>
</dependency>
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-fxml</artifactId>
<version>16</version>
</dependency>
<dependency>
<groupId>org.games.marty</groupId>
<artifactId>word.generator</artifactId>
<version>0.1</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version>
<configuration>
<release>16</release>
</configuration>
</plugin>
<plugin>
<groupId>org.openjfx</groupId>
<artifactId>javafx-maven-plugin</artifactId>
<version>0.0.6</version>
<executions>
<execution>
<!-- Default configuration for running -->
<!-- Usage: mvn clean javafx:run -->
<id>default-cli</id>
<configuration>
<mainClass>org.games.marty.App</mainClass>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<!-- Build an executable JAR -->
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>3.1.0</version>
<configuration>
<archive>
<manifest>
<addClasspath>true</addClasspath>
<classpathPrefix>lib/</classpathPrefix>
<mainClass>org.games.marty.App</mainClass>
</manifest>
</archive>
</configuration>
</plugin>
</plugins>
</build>
</project>
The way we have attempted to build the application in order for the UI to have access to the WordGenerator logic is to maven package the result from the pom1.xml directive
We get the above error as mentioned earlier:
Error: Could not find or load main class org.games.marty.App
Caused by: java.lang.NoClassDefFoundError: javafx/application/Application
As far as my understanding goes, the JavaFX dependencies are installed throught maven and should be available but they are missing?
Packaging via mvn package using the maven-jar-plugin is not enough
mvn package, by default, is just going to package the jar for your application code, it isn't going to include all of the dependant library code (which is why the dependent code cannot be found when you attempt to run your application).
You could package your application code and dependant libraries using an assembly, as detailed in How can I create an executable JAR with dependencies using Maven?, though that approach is not the only one to solve your problem.
You need to build some kind of runtime image
There are numerous options for building runtime images and I don't know your requirements, so I can't recommend what you should do instead. Example options are:
A zip/tar of your application plus libraries in a separate directory.
The creation of a single jar that includes all dependant code.
Either of solutions 1 or 2, plus the inclusion of a packaged JRE.
A runtime image with your code and libraries which also uses just the custom runtime portions of the JRE and JavaFX modules you need (using jlink).
A native installer for either 3 or 4 (using jpackage + a native installer creation tool, e.g. WIX, RPM, DEB installer creators).
The last method (native installer), is the packaging, distribution, and installation method I would recommend for most non-trivial applications.
You need to research how to do this
To get your solution, you will need to do your own research, and, once you have chosen an approach and toolset, you could create a new question regarding the implementation of that approach, if you continue to have difficulties.
Related resources
How can I create an executable JAR with dependencies using Maven?
openjfx Runtime images documentation
maven shade plugin
Maven Shade JavaFX runtime components are missing
openjfx JavaFX maven plugin
badass runtime plugin
badass jlink plugin
jlink guide
jpackage script
JEP 392: packaging tool
Warning for shaded jars
If you bundle all JavaFX code into a single jar using the maven shade plugin, you will get a warning like the following when you run your application from Java 16+:
WARNING: Unsupported JavaFX configuration: classes were loaded from 'unnamed module #28c71909'
This indicates that such a configuration is not supported, and may (and probably will) break in future and perhaps current JavaFX platform revisions. Thus, shaded jars that include JavaFX platform code are not recommended by me, even though such jars might currently work for your deployments.
JavaFX 11+ is built to be used as a set of modules. Configurations are not supported if they do not run the JavaFX platform off of the module path but instead run the platform code off of the classpath (as a shaded jar would).

Maven not generating Main Class entry in MANIFEST.MF

I am trying to build a project in Java that should run if executed with double click. That's not been working so I debugged with cmd and there was no main class entry in the manifest.
So i added the contents of this answer (adopted to my application): Missing Main-Class attibute in MANIFEST.MF in Maven generated jar file
I am using jenkins as build platform and have tried executing with mvn clean install and mvn clean package.
pom.xml:
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>de.progii</groupId>
<artifactId>pong</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>pong</name>
<url>http://maven.apache.org</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<build>
<plugins>
<plugin>
<artifactId>maven-jar-plugin</artifactId>
<version>3.1.1</version>
<configuration>
<archive>
<manifest>
<addClasspath>true</addClasspath>
<mainClass>de.progii.pong.App</mainClass>
</manifest>
</archive>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>
I partly fixed the problem. The problem is not the maven jar plugin, its jenkins. It simply archives the wrong build. The artifact available for download seems to be archived before the maven jar plugin does the job. If I connect with sftp to the workingspace/target folder I can download the artifact with the same name as the artifact at the jenkins download page but execute it successfully!
edit
With the latest Update to Jenkins its working correctly

Create jar A without dependencies and make dependent project C download the dependency B that this dependency A depends on

I have successfully created jar A that does not contain dependencies. This jar A depends on Jar B. I don't want to create a fat jar. I just want dependent project (lets call it C) to add my jar A as dependency, and as soon as jar A is added, project should pull in jar B.
Is this possible?
Question ends here. Below is just what I have tried so far:
What I have done:
I created Fat Jars first with both maven and gradle. Now this has all the dependencies, but my own classes are buried somewhere inside. And dependent project cannot find my classes.
Then I created a jar that does not contain any dependency. I created them separately with maven and gradle. This resolved my classes not being found issue. But then I ran into another issue. As soon as I run the project, it complains that jar B is missing. Rightfully so, as I never included it.
I will just show you my 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/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>custom-spring-boot-starter</groupId>
<artifactId>custom-spring-boot-starter</artifactId>
<version>0.0.1-SNAPSHOT</version>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.3.RELEASE</version>
<relativePath /> <!-- lookup parent from repository -->
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
<scope>provided</scope>
</dependency>
<dependency> //THIS IS WHAT DEPENDENT PROJECT WILL NOT HAVE.
<groupId>com.microsoft.azure</groupId>
<artifactId>azure-servicebus</artifactId>
<version>0.9.7</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version>
<configuration>
<release>10</release>
</configuration>
</plugin>
</plugins>
</build>
This created a jar without dependencies. I added it to dependent project C. But it is complaining that it cannot find dependent B (azure-servicebus in my example). I was hoping that because pom.xml file is present in the jar file, dependent project will download another dependency automatically.
Then I rewrote my 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/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>custom-spring-boot-starter</groupId>
<artifactId>custom-spring-boot-starter</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.3.RELEASE</version>
<relativePath /> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>10</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.microsoft.azure</groupId>
<artifactId>azure-servicebus</artifactId>
<version>0.9.7</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version>
<configuration>
<release>10</release>
</configuration>
</plugin>
<plugin>
<groupId>pl.project13.maven</groupId>
<artifactId>git-commit-id-plugin</artifactId>
<executions>
<execution>
<id></id>
<goals>
<goal>revision</goal>
</goals>
<phase>validate</phase>
</execution>
</executions>
<configuration>
<dateFormat>yyyy-MM-dd-HH:mm:ss</dateFormat>
<dotGitDirectory>${project.basedir}/.git</dotGitDirectory>
<prefix>git</prefix>
<verbose>false</verbose>
<generateGitPropertiesFile>true</generateGitPropertiesFile>
<generateGitPropertiesFilename>${project.build.outputDirectory}/git.properties</generateGitPropertiesFilename>
<format>json</format>
<gitDescribe>
<skip>false</skip>
<always>false</always>
<dirty>-dirty</dirty>
</gitDescribe>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<configuration>
<archive>
<manifestEntries>
<Build-Jdk>${java.version} (${java.vendor} ${java.vm.version})</Build-Jdk>
<Digital-Voltage-Library-Version>${project.version}</Digital-Voltage-Library-Version>
<Build-Timestamp>${git.build.time}</Build-Timestamp>
<Build-Revision>${git.commit.id}</Build-Revision>
<Build-OS>${os.name} ${os.arch} ${os.version}</Build-OS>
</manifestEntries>
</archive>
</configuration>
</plugin>
</plugins>
</build>
This is the stacktrace of the error:
Caused by: java.lang.ClassNotFoundException: com.microsoft.windowsazure.services.servicebus.ServiceBusContract
at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:582)
at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:190)
at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:499)
at java.base/java.lang.Class.forName0(Native Method)
at java.base/java.lang.Class.forName(Class.java:374)
at org.springframework.util.ClassUtils.forName(ClassUtils.java:275)
at org.springframework.boot.autoconfigure.condition.OnBeanCondition$BeanSearchSpec.getReturnType(OnBeanCondition.java:505)
at org.springframework.boot.autoconfigure.condition.OnBeanCondition$BeanSearchSpec.addDeducedBeanTypeForBeanMethod(OnBeanCondition.java:491)
... 22 common frames omitted
This error easily goes away if I add azure-servicebus dependency to target project. But I don't want to do that. I want the dependency to pull in azure-servicebus.
This is how I am pulling in my jar A into project C.
compile fileTree(dir: '/lib', include: 'custom-spring-boot-starter-0.0.1-SNAPSHOT.jar')
Yes, if A has a Maven dependency on B, then B is automatically pulled when C depends on A. This is the Maven transitive dependency resolution.
Note that this has nothing to do with fat jars. B is not included in A, it is just mentioned as dependency in the POM of A.
I asked this question without knowing something very important.
When jars are put into artifactory, a corresponding .pom file also has to be placed alongside it (outside of the directory, just look at below link to understand directory structure). This pom file is what tells the dependent project that the jar you are dependent upon, requires so and so dependencies itself.
If you do mvn clean install, it automatically installs jar file and pom file at the correct location for you in your local maven repository.
This answer helped me understand:
https://stackoverflow.com/a/50002072/4828463
Thanks to everyone who tried.

Maven - Executable file from a java project

I need to use maven (for a school project) to create an executable file from a single maven command. I've never used maven and tried many solutions here on stackoverlow. The solutions created a jar file, but the file never opened.
This is my project structure
src
com
project
code
swing
programm
interface
Main.class
I know this isn't maven convention, however changing it now would mean I would have to adjust the imports (as intelliJ doesn't refactor everything perfectly) for around 40 classes.
<?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>MyGroup</groupId>
<artifactId>myProgramm</artifactId>
<version>0.7-SNAPSHOT</version>
<packaging>jar</packaging>
<name>Hello World</name>
<description>Course Project</description>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<!-- https://mvnrepository.com/artifact/org.xerial/sqlite-jdbc -->
<dependency>
<groupId>org.xerial</groupId>
<artifactId>sqlite-jdbc</artifactId>
<version>3.25.2</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.junit/junit5-engine -->
<dependency>
<groupId>org.junit</groupId>
<artifactId>junit5-engine</artifactId>
<version>5.0.0-ALPHA</version>
</dependency>
</dependencies>
<build>
</build>
What do I have to put inside to make an executable file?
TimurJD's answer is correct however I would like to explain step by step what is actually happening and why.
To have a jar be executable the JVM needs to know where your main method is.
For that you need a file called META-INF/MANIFEST.MF inside the jar you create.
This file must contain a reference to the class containing your main method which is done like this:
Main-Class: com.example.ClassContainingMainMethod
There are many ways of creating said file but since you are using maven here is the plugin that will help you create this manifest file
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>3.1.1</version>
<configuration>
<archive>
<manifest>
<addClasspath>true</addClasspath>
<classpathPrefix>lib/</classpathPrefix>
<mainClass>com.my.packege.Main</mainClass>
</manifest>
</archive>
</configuration>
</plugin>
</plugins>
</build>
Once you have the plugin in your pom.xml simply run the maven install goal, either from your IDE or the command line. After you should find a folder called target in your project. That folder will contain the executable jar.
To run the jar you can call from the command line:
java -jar MyProject.jar
It should also be noted that unless you abide by the maven standard of keeping your source code in src/main/java you will have to specify your source folder explicitly.
You need to add plugin to your pom.xml file
<build>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>1.2.1</version>
<configuration>
<mainClass>com.example.Main</mainClass>
</configuration>
</plugin>
</plugins>
</build>
and to run the program: mvn clean install exec:java
... here is the link for doc http://www.mojohaus.org/exec-maven-plugin/usage.html
There are possible different solutions, depends on your requirements: https://www.baeldung.com/executable-jar-with-maven

How to set up the main class header in the manifest

I've created a simple project in Java Maven. I can run this project by:
1:
java -cp target/MavenTestApp-1.0-SNAPSHOT.jar org.koushik.javabrains.App
But I would like to run it by:
2:
java -jar target/MavenTestApp-1.0-SNAPSHOT.jar
To run it by second method I need to change manifest file. I am using maven so I think that in this case I need to change the pom.xml file. My pom.xml file is:
<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>org.koushik.javabrains</groupId>
<artifactId>MavenTestApp</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<name>MavenTestApp</name>
<url>http://maven.apache.org</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>
I need to add something like this:
<mainClass>org.koushik.javabrains.App</mainClass>
But I not pretty sure where and how to do that? Could you also tell me if this line is good:
2:
java -jar target/MavenTestApp-1.0-SNAPSHOT.jar
Maybe at the end of this line there should be something or it is sufficient? Thanks.
Take a look at the Maven jar plugin documentation.
You will need to configure something similar in your .pom:
<project>
...
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
...
<configuration>
<archive>
<manifest>
<addClasspath>true</addClasspath>
<mainClass>org.koushik.javabrains.App</mainClass>
</manifest>
</archive>
</configuration>
...
</plugin>
</plugins>
</build>
...
</project>

Categories

Resources