Spring boot maven plugin with a main class in a dependency - java

I have a perhaps unusual case where I want to use the spring-boot maven plugin to perform 'mvn spring:start' (before integration tests) on a project with spring services, but the class with the main method is in a jar file. The reason for this is that there will be a number of these spring services that require some common structures in place when started up for testing purposes, so the idea is that there will be a common jar with the common stuff and the class with the main method and each individual service project will simply reuse that.
Unfortunately I am getting ClassNotFoundException for the class with the main method - spring boot clearly isn't looking in the jar files but only in the compiled classes of this project.
The pom in question (trimmed):
<?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.something</groupId>
<artifactId>something</artifactId>
<version>0.0.1-SNAPSHOT</version>
<dependencies>
...
<dependency>
<groupId>com.something</groupId>
<artifactId>artifact-with-main-method</artifactId>
<version>0.0.1-SNAPSHOT</version
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>1.4.2.RELEASE</version>
<dependencies>
<dependency>
<groupId>com.something</groupId>
<artifactId>artifact-with-main-method</artifactId>
<version>0.0.1-SNAPSHOT</version
</dependency>
</dependencies>
<configuration>
<mainClass>com.something.Application</mainClass>
<requiresUnpack>
<dependency>
<groupId>com.something</groupId>
<artifactId>artifact-with-main-method</artifactId>
</dependency>
</requiresUnpack>
</configuration>
<executions>
<execution>
<id>pre-integration-test</id>
<goals>
<goal>start</goal>
</goals>
</execution>
<execution>
<id>post-integration-test</id>
<goals>
<goal>stop</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
Output:
java.lang.ClassNotFoundException: com.something.Application
at java.net.URLClassLoader.findClass(URLClassLoader.java:381)
at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
at org.springframework.boot.maven.AbstractRunMojo$LaunchRunner.run(AbstractRunMojo.java:501)
at java.lang.Thread.run(Thread.java:745)
UPDATE:
I gave up trying to get this to work. I ended up using the mvn:exec plugin to execute a java command doing what I needed it instead. It works well enough.

Do you have:
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
in the dependency too? If yes, then it will make the dependency module impossible to import to the current module/project. So, remove it from the dependency, and add it to the current module that you are importing the main class into.

Related

Springboot application.properites not read

I've developed a springboot application with Maven in Eclipse. The class annotated with #SpringBootApplication reads the application.properties inside src/main/resources. Inside Eclipse everything works fine.
Using Maven I've generated a fat jar, this is the plugin I'm using:
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
<configuration>
<classifier>exec</classifier>
</configuration>
</execution>
</executions>
</plugin>
In the target folder 2 jars are generated, one named fatjar-exec.jar and the other fatjar.jar. When I run the command java -jar fatjar-exec.jar an exception is thrown since the application is not able to read the application.properties file.
I have also unzipped the jar and correctly the applciation.properties is located under BOOT-INF/classes folder. Any hints?
Please compare the contents of generated fatjars.
The regular one (without exec) has only one copy of springboot classes,
while the one generated with the clasifier has two.
one under /org/springframework/boot/loader (expected)
second under /BOOT-INF/classes/org/springframework/boot/loader
Probably the order of classpath search causes the file from the unexpected location to be picked up, and it cannot find the properties in /BOOT-INF/classes
IMHO the simplest version works best:
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
Check Custom repackage classifier for details how to configure maven if you want to keep the origial file (you were missing <id>repackage</id>).
This is working for me fine too.
My POM.xml is as follows :
<?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>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.3.RELEASE</version>
<relativePath /> <!-- lookup parent from repository -->
</parent>
<groupId>xxx</groupId>
<artifactId>yyy</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>demo</name>
<packaging>jar</packaging>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
<configuration>
<classifier>exec</classifier>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
mvn package -DskipTests
java -jar xxx-0.0.1-SNAPSHOT.jar

AWS EC2 Java Spring Boot - Exception in thread "main" java.lang.NoClassDefFoundError: org/springframework/boot/SpringApplication

Hello I am trying to run Spring Boot in a AWS EC2 instance and i am getting the following error:
Command in EC2: java -jar app-dal-1.0-SNAPSHOT.jar
Exception in thread "main" java.lang.NoClassDefFoundError: org/springframework/boot/SpringApplication
at com.smartcommunity.smartparking.appdal.BootApp.main(BootApp.java:12)
Caused by: java.lang.ClassNotFoundException: org.springframework.boot.SpringApplication
at java.net.URLClassLoader.findClass(URLClassLoader.java:382)
at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:349)
at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
... 1 more
Everything works fine in my local machine.
Local Java version "9.0.4" - EC2 Java Version 1.8.0
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.smartcommunity.smartparking</groupId>
<artifactId>app-dal</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.4.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>2.0.4.RELEASE</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpcore</artifactId>
<version>4.3</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.8</version>
</dependency>
<dependency>
<groupId>org.mongodb</groupId>
<artifactId>mongodb-driver</artifactId>
<version>3.5.0</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.3.1</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.3.1</version>
</dependency>
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-core</artifactId>
<version>8.5.32</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<executions>
<execution>
<id>copy-dependencies</id>
<phase>prepare-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>
<configuration>
<archive>
<manifest>
<addClasspath>true</addClasspath>
<classpathPrefix>lib/</classpathPrefix>
<mainClass>com.smartcommunity.smartparking.appdal.BootApp</mainClass>
</manifest>
</archive>
</configuration>
</plugin>
</plugins>
</build>
</project>
Steps I am following, using IntelliJ Maven Lifecycle:
Maven Clean
Maven Compile
Maven Package
Maven Install
When using Spring Boot it expects a special structure in your jar file. The spring-boot-maven-plugin makes sure that this structure is created in the jar.
The spring-boot-maven-plugin is specially designed and build to create executable jar files for Spring Boot based applications. So instead of your explicitly added >maven-dependency-plugin and maven-jar-plugin, replace those with a single spring-boot-maven-plugin.
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<plugin>
</plugins>
</build>
Basically this is al you need.
Copy your output jar file from target repo to different location and execute it in your local machine. Let me know if you are facing any issues. In that case, we should modify the pom file to generated resources.
Sorry, since I am new contributor I can't comment you this in your question.
Adding spring-boot-maven-plugin in the pom.xml file as one of the configs plugin did the trick. Thanks to everyone who commented, the Solution to this problem and who came with the solution is in the comments. Thanks #M. Deinum

Reporting and Merging multi-module jacoco reports with report-aggregate

Attempting to get one jacoco report that will show all the results from multiple modules.
I am able to see that each of the sub-modules have a jacoco.exec after building the project but unsure of how to get it to output one report that will have all the results from every module combined.
This is what I have included in my Root pom.xml:
<plugin>
<groupId>#project.groupId#</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>#project.version#</version>
</plugin>
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<executions>
<execution>
<id>prepare-agent</id>
<goals>
<goal>prepare-agent</goal>
</goals>
</execution>
</executions>
</plugin>
I created a new module explicitly for reporting purposes. (e.g. report-aggregate-module)
Deleted the group ids and used generic artifact ids for this example:
This is what I put in the pom.xml for this report-aggregate sub-module:
<?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>
<artifactId>report-aggregate</artifactId>
<groupId>com.name.group</groupId>
<version>1.0.0-SNAPSHOT</version>
</parent>
<artifactId>report-aggregate</artifactId>
<name>Report Aggregate</name>
<properties>
<wildfly.version>10.0.0.Final</wildfly.version>
<wildfly.artifactId>wildfly-dist</wildfly.artifactId>
</properties>
<dependencies>
<dependency>
<groupId></groupId>
<artifactId>sub-module1</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId></groupId>
<artifactId>sub-module2</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId></groupId>
<artifactId>sub-module3</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId></groupId>
<artifactId>sub-module4</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<executions>
<execution>
<id>report-aggregate</id>
<phase>verify</phase>
<goals>
<goal>report-aggregate</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
Everything seems to compile okay, the jacoco exec doesn't seem to get created for the report-aggregate-module. Anyone know a solution to this or if I'm doing this incorrectly?
Sorry if this comes a late answer.
I assume that your root pom.xml is an aggregater, with <modules> consisting of module1, module2, and report-aggregate. This is the cause of your trouble: as your root pom.xml is an aggregator, it runs JaCoCo BEFORE your submodules do, so your final report is empty. You should:
Move the configuration of goal report-aggregate of the jacoco-maven-plugin from the root POM to the report-aggregate POM. This should do the trick, because your report-aggregate POM uses <dependencies>, not <modules>.
Keep the configuration of goal prepare-agent of the jacoco-maven-plugin in the root POM.
I suggest you look at the JaCoCo forum https://groups.google.com/forum/#!topic/jacoco/FpdLbxsXSTY. It refers to a complete demo integration test https://github.com/jacoco/jacoco/tree/master/jacoco-maven-plugin.test/it/it-report-aggregate.

Include source package and dependency only for a profile with Spring Boot maven plugin

In a Spring Boot based project of mine I want to create two different builds from the same project.
The decision on which build is generated should come from a maven profile.
I want to create one build (full) which includes a certain folder src/main/java/com/example/demo/full and a certain dependency, and a second build (default or light) build which does not include them.
Including the dependencies for build full works, but I don't know how to make sure the folder src/main/java/com/example/demo/full is only compiled for the full build.
Here 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>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.3.RELEASE</version>
</parent>
<groupId>com.example</groupId>
<artifactId>demo</artifactId>
<version>1.0</version>
<packaging>jar</packaging>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
<build>
<finalName>demo</finalName>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<useSystemClassLoader>false</useSystemClassLoader>
</configuration>
</plugin>
</plugins>
</build>
<profiles>
<profile>
<id>full</id>
<dependencies>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>3.16</version>
</dependency>
</dependencies>
</profile>
</profiles>
</project>
How can I manage to have the mentioned source-folder only compiled for profile full?
Add a second src folder like scr\foo and then add a profile in maven configure this src folder.
<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">
<build>
...
</build>
<profiles>
<profile>
<id>extraSource</id>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
<build>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>build-helper-maven-plugin</artifactId>
<version>3.0.0</version>
<executions>
<execution>
<id>add-source</id>
<phase>generate-sources</phase>
<goals>
<goal>add-source</goal>
</goals>
<configuration>
<sources>
<source>src/foo/</source>
</sources>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
</profiles>
Here the source folder is added using the Build Helper Plugin plugin for maven. As it is embedded in the build section of the specific profile, it is only active while executing maven with this profile (see the activation section)
there are problem with disabling one of your maven-source-plugin if this dependency is a part of parent which you cant not give ID to, ill recomend to use phase none with this code to one of your pom.xml files that will disable this.
I also recommend to use command: mvn -Prelease-profile help:effective-pom
to print if you have two of dependencies maven-source-plugin in your code, if yes, disable one of them with this code below:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<executions>
<execution>
<id>attach-sources</id>
<phase>none</phase>
</execution>
</executions>
</plugin>

How can I build a release with embedded jetty and maven?

I am trying to configure maven to build a runnable jar.
The project that this is about contains a couple of dependencies as well as embedded jetty.
I want to embed all dependencies (which works) and have one executable jar.
I can run the project fine from eclipse but once I try to run the jar I get ClassNotFoundExceptions on org.eclipse.jetty.servlet.ServletContextHandler.
However, when I use the maven-plugin and run exec:java the program starts without problems.
What am I doing wrong?
Here is a copy of 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>comms</groupId>
<artifactId>comms-app</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>Air</name>
<properties>
<jettyVersion>9.2.7.v20150116</jettyVersion>
<gsonVersion>2.3.1</gsonVersion>
<logjVersion>2.1</logjVersion>
<lombokVersion>1.16.2</lombokVersion>
</properties>
<dependencies>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-maven-plugin</artifactId>
<version>${jettyVersion}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>${gsonVersion}</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>${logjVersion}</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>${logjVersion}</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombokVersion}</version>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>1.1</version>
<executions>
<execution>
<goals><goal>java</goal></goals>
</execution>
</executions>
<configuration>
<mainClass>comms.Loader</mainClass>
</configuration>
</plugin>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
<configuration>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
<mainClass>comms.Loader</mainClass>
</configuration>
</plugin>
</plugins>
</build>
</project>
I noted that you declared jetty-maven-plugin as a dependency, not a plugin declaration. The jetty-maven-plugin depends on Jetty and that (i.e. the transitive dependency on Jetty) is how the Jetty classes get pulled into the Maven classpath.
However that dependency is declared as 'provided', which basically says: "my code is dependent on this, but normally it will be provided by the deployment environment". Maven will pull in the dependency because it needs it for building your source (which is probably why the exec plugin does work), but will not include it in your jar, because you specifically told it to do so.
Seems to me your pom simply does not reflect the situation:
If your code depends on Jetty classes (not the plugin), remove the
jetty-maven-plugin dependency and declare Jetty itself as an
explicit dependency.
Do not define the scope as 'provided' unless
the deployment environment really does provide the Jetty classes
(which it doesn't, given the error).
Hope this helps!

Categories