Maven NAR plugin + Spring boot in webapp, loading .nar artifact in Tomcat - java

I've managed to add platform dependencies to war and generated by maven-nar-plugin jni artifact into WEB-INF/lib/.
But the problem is: added artifact has .nar extension while WebappClassLoaderBase adds only .jars to its internal class repositories, so my jni bridge is not loaded and class is inaccessible leading to ClassNotFoundException.
What are my options here? I assume it's possible to
somehow change artifact extension on spring-boot:repackage?
add this custom artifact to classloader repositories?
Which would be better and how would I implement it?

Ok, so I've ended up with maven-dependency-plugin:
<build>
...
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>copy</goal>
</goals>
<configuration>
<artifactItems>
<artifactItem>
<groupId>${dep.groupId}</groupId>
<artifactId>${dep.artifactId}</artifactId>
<version>${dep.version}</version>
<type>nar</type>
<outputDirectory>${project.build.directory}/${project.build.finalName}/WEB-INF/lib</outputDirectory>
<destFileName>${dep.artifactId}-${dep.version}.jar</destFileName>
</artifactItem>
</artifactItems>
</configuration>
</execution>
</executions>
</plugin>
...
</plugins>
</build>
But still, there is a problem with redeployments to deal with, so if you realy want to do this, here is the way, but you probably don't need it that hard.

Related

Spring Boot Maven Plugin - No BOOT-INF directory

Between version 1.3.8.RELEASE of the spring-boot-maven-plugin and version 1.4.0.RELEASE - there has been a change in the generated package structure (if you extract the uber jar file)
1.3.8.RELEASE com, lib, META-INF and org directories
1.4.0.RELEASE has a BOOT-INF, META-INF and org directories
Basically from 1.4.0.RELEASE onwards - all the classes and libs are in the BOOT-INF directory.
Due to this - when you try to run a Spring Boot project on Amazon Lambda - it says that there is a jar not found as it cannot read the new Spring Boot Uber jar structure
My question is - is it possible in the newer versions of the Spring Boot Maven Plugin to get it to generate the uber jar to be the same structure as in version 1.3.9.RELEASE?
I tried the maven-shade-plugin - but that leads to other issues
Any help is greatly appreciated
Thanks
Damien
The solution was to add the MODULE layout for the plugin in the pom.xml file
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<layout>MODULE</layout>
</configuration>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
In my case I'm using spring boot 2.X and I declared the spring-boot-maven-plugin after the maven-dependency-plugin (which I used to unpack and create exploded app in Docker) and it must be before the unpack, makes sense, it was unpacking before the spring boot maven plugin executed. Next time I'll declare it first thing in the plugin chain, lost more than 1 hour on this. Hope it helps someone.
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<mainClass>${spring.boot.mainClass}</mainClass>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<executions>
<execution>
<id>unpack</id>
<phase>package</phase>
<goals>
<goal>unpack</goal>
</goals>
<configuration>
<artifactItems>
<artifactItem>
<groupId>${project.groupId}</groupId>
<artifactId>${project.artifactId}</artifactId>
<version>${project.version}</version>
</artifactItem>
</artifactItems>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
The answer above with
<layout>MODULE</layout>
does not work anymore, this is because layout element is deprecated in Spring Boot 2.x.
I am using Spring Boot 2.0.x, I found this helpful comment on github:
Support for the module layout was removed in Spring Boot 2.0 having been deprecated in 1.5. Unfortunately, the updates to the Maven Plugin's documentation were missed so we can use this issue to sort that out. You should use a custom LayoutFactory instead.
But as I did not want to implement LayoutFactory I tried this second solution below that actually repackage and creates an extra jar with a classifier given name:
This is due to the change in layout of executable jars in Spring Boot 1.4. Application classes are now packaging in BOOT-INF/classes.
Your client module depends on the repackaged, fat jar of your web module. Due to the new layout that means that the client module can no longer load the web module's classes. If you want to use your web module as a dependency, you should configure Boot's repackaging to apply a classifier to the fat jar. For example:
<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>
Doing so will allow other modules to depend on the original jar that does not embed the module's dependencies and has the classes at the root of the jar.
One original jar have the same structure as I wanted like
com.my-package.foo.bar
META-INF
and the second classifier have the newer structure with BOOT-INF/ etc.
For me, the solution was a bit more insidious....I had the spring-boot-maven-plugin nested under pluginManagement, (see below). Doh!
The nasty thing, is that when I'd run mvn spring-boot:run, spring boot comes up just fine, and runs app! It wasn't until we tried to deploy to PCF (as a spring-boot JAR), that we'd get an error that there was something wrong with format of the binary....
<build>
<!--
DON'T DO THIS!!
-->
<pluginManagement>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>${spring.boot.version}</version>
<executions>
<execution>
<goals>
<goal>repackage</goal>
<goal>build-info</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</pluginManagement>
<!--
DO THIS INSTEAD!!
-->
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>${spring.boot.version}</version>
<executions>
<execution>
<goals>
<goal>repackage</goal>
<goal>build-info</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
Once I removed the pluginManagement tags from the POM, I would now get the ./BOOT-INF structure. Please keep in mind that pluginManagement is typically for a parent-pom structure, where you want that plugin's config used across other modules.
I was using Gradle, instead of Maven, and this is what I had to do:
1- In my build.gradle, I added the following properties as defined in https://spring.io/guides/gs/spring-boot-Docker/.
buildscript {
...
dependencies {
...
classpath('gradle.plugin.com.palantir.gradle.docker:gradle-docker:0.13.0')
}
}
group = 'springio'
...
apply plugin: 'com.palantir.docker'
task unpack(type: Copy) {
dependsOn bootJar
from(zipTree(tasks.bootJar.outputs.files.singleFile))
into("build/dependency")
}
docker {
name "${project.group}/${bootJar.baseName}"
copySpec.from(tasks.unpack.outputs).into("dependency")
buildArgs(['DEPENDENCY': "dependency"])
}
2- My dependency folder was not being written to
ARG DEPENDENCY=target/dependency
instead, I located it in another folder, so I changed this property in the Dockerfile:
ARG DEPENDENCY=build/dependency
With this I got a successful build.

How to make maven copy all classes from another project?

I have a number of Entity classes in a shared project.
In order to get JPA working properly, I need these class files in the classes folder and not inside the jar.
How would I go about doing this in maven?
Is there a specific maven plugin I can use?
Source:
project1/src/main/java/org/something/SomeClass.java
project2/src/main/java/org/somethingElse/SomeEntity.java
Target:
project1/target/classes/org/something/SomeClass.java
project1/target/classes/org/somethingElse/SomeEntity.java
Alternative approaches are also OK.
Update:
Use Case:
I have multiple projects.
One project is the core library for all things shared.
This includes a bunch of entity classes that have PersistanceUnit Entity Managers injected.
The issue arises when i try to use these entity classes because the entity manager is no longer injected. Additionally when I manually specify the entity manager, it doesn't recognize any of the entity classes in the shared jar.
Try
Unpacking specific artifacts
Sample config from docs:
<project>
[...]
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<version>2.10</version>
<executions>
<execution>
<id>unpack</id>
<phase>package</phase>
<goals>
<goal>unpack</goal>
</goals>
<configuration>
<artifactItems>
<artifactItem>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<type>jar</type>
<overWrite>false</overWrite>
<outputDirectory>${project.build.directory}/alternateLocation</outputDirectory>
<destFileName>optional-new-name.jar</destFileName>
<includes>**/*.class,**/*.xml</includes>
<excludes>**/*test.class</excludes>
</artifactItem>
</artifactItems>
<includes>**/*.java</includes>
<excludes>**/*.properties</excludes>
<outputDirectory>${project.build.directory}/wars</outputDirectory>
<overWriteReleases>false</overWriteReleases>
<overWriteSnapshots>true</overWriteSnapshots>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
[...]
</project>

How to choose which maven executions are run?

THE SETUP
I have a complex project that I'm trying to build using Maven. What follows is a simplified example of that project. The directory structure is as follows:
main - The project has more than one 'ear', each of which contains more than one 'war' plus other supporting jars. Yet they share a common set of WSDL/XSD, as well as Java code.
pom.xml - This is the main pom.xml file, I'll describe it in more detail below.
common-code-one - the two 'ear' package have a body of code in common, it's just java code, no wsdl, no xsd, so no problem.
wsdl - single package holding all the XSD and WSDL files, it has a pom.xml that simply packs it all up inside a jar file, for later use in sub-directories.
ear-package-one
common-code-two - Some of the XSD from 'wsdl' is unpacked here, and turned into code via jaxb2. As part of that, an episode file is generated.
war-package-one - More of the XSD and WSDL files are unpacked here, and turned into code via jaxws. To avoid having duplicate code, I need the episode file from 'common-code-two', and I need the XSD/WSDL files from 'wsdl'.
war-package-two - Different XSD/WSDL, but I still need the episode file from common-code-two, as well as the XSD/WSDL from 'wsdl'.
ejb-package-one - Don't need anything from 'wsdl', but may need some of the code from 'common-code-two', without needing the episode file.
...
ear-package-two
common-code-two - Same problem, different XSD.
ejb-package-two - Same problem, different code.
war-package-three - Same problem, different XSD/WSDL.
war-package-four - Same problem, different XSD/WSDL.
Now, the main 'pom.xml' file. Following Maven's DRY principle, I've put both executions of the maven-dependency-plugin into the main pom.xml. The problem is that in some of the sub-packages above, I only want the 'unpack-schema' execution to run, in others I want both the 'unpack-schema' and 'unpack-common-binding' to run. Of course, for the ones that don't need either, I just don't add the dependency plugin to the build list.
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<executions>
<execution>
<id>unpack-schema</id>
<phase>generate-sources</phase>
<goals>
<goal>unpack</goal>
</goals>
<configuration>
<artifactItems>
<artifactItem>
<groupId>com.foo.bar</groupId>
<artifactId>wsdl</artifactId>
<version>0.0.1-SNAPSHOT</version>
<type>jar</type>
<overWrite>true</overWrite>
<outputDirectory>${project.build.directory}</outputDirectory>
<includes>${schema.include.list}</includes>
</artifactItem>
</artifactItems>
<overWriteReleases>true</overWriteReleases>
<overWriteSnapshots>true</overWriteSnapshots>
</configuration>
</execution>
<execution>
<id>unpack-common-binding</id>
<phase>generate-sources</phase>
<goals>
<goal>unpack</goal>
</goals>
<configuration>
<artifactItems>
<artifactItem>
<groupId>com.foo.bar</groupId>
<artifactId>${common.code.jar}</artifactId>
<version>0.0.1-SNAPSHOT</version>
<type>jar</type>
<overWrite>true</overWrite>
<outputDirectory>${project.build.directory}</outputDirectory>
<includes>**/commonCode.xml</includes>
</artifactItem>
</artifactItems>
<overWriteReleases>true</overWriteReleases>
<overWriteSnapshots>true</overWriteSnapshots>
</configuration>
</execution>
</executions>
</plugin>
THE QUESTION
What do I put in the sub-projects' pom.xml files so that only the executions that are desired happen?
If I simply put in
<build>
<plugins>
...
<plugin>
<artifactId>maven-dependency-plugin</artifactId>
</plugin>
...
</plugins>
</build>
then both executions happen. In the 'war' packages, that's ok, that's what I want to have happen, but in the 'common-code' packages, I only want the 'unpack-schema' execution to happen.
I've seen this question but it was never really answered, and the commented solution (run multiple builds) just isn't suitable for my environment. This is going to be going through continuous integration/build, so it has to compile straight up. No scripts, no multiple build passes.
It's easy, most plugins have an <skip/> configuration item, dependency plugin is no exception!
so, you can overwrite the pom of common-code with <skip/>to disable a certain execution! for example:
<execution>
<id>unpack-schema</id>
<phase>generate-sources</phase>
<goals>
<goal>unpack</goal>
</goals>
<configuration>
<skip>true</skip>
</configuration>
</execution>
BTW: a tip for you: for multi-module projects, a plugin configured at parent pom.xml will run on all sub-modules, unless you disable it on sub-module's pom.xml with !
Good Luck!

Specifying destination for dependencies in a Maven project

In trying to see if this solution will work for my project, How to share Persistence.xml between ejb module and web module in EAR?
I cannot figure out how Maven determines where it packages files in the output.
In my web project it places dependencies in /WEB-INF/lib. This is the right place, but I don't see it specified.
In my ear project it places dependencies in /. This is also the right place, but I also don't see it specified or know how to override it.
I would like my web, ejb, and other dependencies in my ear project to continue to go to /, but I'd like my jpa/domain jar to be placed in /lib.
I tried:
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<version>2.8</version>
<executions>
<execution>
<id>copy</id>
<phase>package</phase>
<goals>
<goal>copy</goal>
</goals>
<configuration>
<artifactItems>
<artifactItem>
<groupId>my.group.org</groupId>
<artifactId>myJpaProject</artifactId>
<type>jar</type>
<overWrite>false</overWrite>
<outputDirectory>${project.build.directory}/lib</outputDirectory>
</artifactItem>
</artifactItems>
<!-- other configurations here -->
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
But this put the jar in /target/lib/myJpaProject.jar while leaving it at / inside the ear file.
I hope that what I'm missing is obvious, but I just can't seem to find it readily in tutorials or documentation.
The specific solution for putting shared libraries in the ear file under /lib is to set the defaultLibBundleDir tag for the maven-ear-plugin. It will place any dependencies of <type>jar</type> in the ear file's /lib directory.
<plugin>
<artifactId>maven-ear-plugin</artifactId>
<configuration>
<defaultLibBundleDir>lib/</defaultLibBundleDir>
…
</plugin>

How to create a jar with dependencies as main build artifact, later used by assembly plugin?

I've been working on a Java Maven project that ultimately creates an executable jar file. At first I had no issues, but then I decided I wanted the dependencies to be copied into the jar as well.
I found the following (very helpful) stack overflow question and followed the instructions provided in the answer (substituting my own main class and target version): Problem building executable jar with maven
This worked wonderfully, but I end up with two jar files (ldap-daemon-0.0.1-SNAPSHOT.jar and ldap-daemon-0.0.1-SNAPSHOT-jar-with-dependencies.jar). I'd be ok with this, but as far as I can tell I can't actually get a copy of the jar with dependencies later using the maven-dependency-plugin's copy functionality.
So, what I want to know is how to accomplish one of the following:
Have my main build artifact, ldap-daemon-0.0.1-SNAPSHOT.jar, contain its dependencies
Use the maven-dependency-plugin to copy the second build artifact (ldap-daemon-0.0.1-SNAPSHOT-jar-with-dependencies.jar).
Here is my plugin configuration for the ldap-daemon (packaging configuration is "jar"):
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<version>2.4</version>
<configuration>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
<archive>
<manifest>
<mainClass>com.acuitus.ldapd.LDAPDaemonImp</mainClass>
</manifest>
</archive>
</configuration>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>6</source>
<target>6</target>
</configuration>
</plugin>
And here is my plugin configuration attempting to copy the resulting jar into a folder in a downstream project:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<version>2.7</version>
<executions>
<execution>
<id>copy</id>
<phase>package</phase>
<goals>
<goal>copy</goal>
</goals>
<configuration>
<artifactItems>
<artifactItem>
<groupId>com.acuitus</groupId>
<artifactId>ldap-daemon</artifactId>
<version>0.0.1-SNAPSHOT</version>
<type>jar</type>
<overWrite>true</overWrite>
<outputDirectory>${project.build.directory}/classes/www-export</outputDirectory>
<destFileName>ldap-daemon.jar</destFileName>
</artifactItem>
</artifactItems>
<outputDirectory>${project.build.directory}/wars</outputDirectory>
<overWriteReleases>false</overWriteReleases>
<overWriteSnapshots>true</overWriteSnapshots>
</configuration>
</execution>
</executions>
</plugin>
Any assistance is greatly appreciated. Thanks for your time!
Like you already know the assembly plugin generate two jar files the normal one and one with all dependencies. Maven uses the classifier construct for artefacts build from the same pom but differing in there content, for example one for jdk1.6 or jdk1.7. Or a more common example is the source code jar file from maven. This construct is also used by the assembly plugin. Your copy config looks like this:
<artifactItem>
<groupId>com.acuitus</groupId>
<artifactId>ldap-daemon</artifactId>
<version>0.0.1-SNAPSHOT</version>
<type>jar</type>
<overWrite>true</overWrite>
<outputDirectory>${project.build.directory}/classes/www-export</outputDirectory>
<destFileName>ldap-daemon.jar</destFileName>
</artifactItem>
So you tell maven to copy the normal jar file without the dependencies.
However the jar file you want is the ldap-daemon-0.0.1-SNAPSHOT-jar-with-dependencies.jar. So you need to specify the classifier so maven is able to fetch the correct jar file:
<artifactItem>
<groupId>com.acuitus</groupId>
<artifactId>ldap-daemon</artifactId>
<version>0.0.1-SNAPSHOT</version>
<type>jar</type>
<classifier>jar-with-dependencies</classifier>
<overWrite>true</overWrite>
<outputDirectory>${project.build.directory}/classes/www-export</outputDirectory>
<destFileName>ldap-daemon.jar</destFileName>
</artifactItem>
I still recommend to have a look also at maven-shade-plugin when you need more control over the generated jar files and classifier used.

Categories

Resources