Getting my dependencies into my JAR - java

I can package a JAVA project I've written. One which uses the Gson library for JSON features. I'm very new to JAVA so I could be making a dumb mistake but here's what I've assumed:
In the source code I have:
import com.google.gson.Gson;
and then use this import like so:
Gson gson = new Gson();
String json = gson.toJson(result);
In my Maven pom.xml I've included the following in the dependency section:
<dependencies>
<dependency>
<groupId>com.amazonaws</groupId>
<artifactId>aws-lambda-java-core</artifactId>
<version>1.0.0</version>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.8.2</version>
<scope>compile</scope>
</dependency>
</dependencies>
As I say, it does package to a JAR file with no errors (using Maven for packaging) but my JAR file is being used on AWS as a serverless function and so I believe what I need to do is include the Gson dependency as part of my JAR file (could be wrong). This seems to be backed up by the errors I get on AWS:
Having done some google searches it looked like maybe Maven's "shade" plugin might be the ticket. I added it into my pom.xml like so:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.1.0</version>
<configuration>
<artifactSet>
<includes>
<include>com/google/gson/**</include>
</includes>
</artifactSet>
</configuration>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
</execution>
</executions>
</plugin>
But looking in the generated JAR file I see no difference. What do I need to do to get this to work?
The full POM file can be found here: pom.xml

I'm unfamiliar with the shade plugin others have referenced. The way it sounds to me, you need an artifact that's an executable jar: a jar, with its dependencies.
Here's how I do that, using Maven:
<?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.company.groupId</groupId>
<artifactId>artifact-id</artifactId>
<version>1.0.0</version>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.7.0</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<version>3.1.0</version>
<configuration>
<archive>
<manifest>
<mainClass>com.company.App</mainClass>
</manifest>
</archive>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
</configuration>
<executions>
<execution>
<id>make-assembly</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>3.0.0</version>
<executions>
<execution>
<id>attach-javadoc</id>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<version>3.0.1</version>
<executions>
<execution>
<id>attach-source</id>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>com.amazonaws</groupId>
<artifactId>aws-lambda-java-core</artifactId>
<version>1.0.0</version>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.8.2</version>
<scope>compile</scope>
</dependency>
</dependencies>
</project>
What you want to pay attention to is the build plugin, maven-assembly-plugin. This tells Maven how to assemble/package the results of the build. In its configuration, you define the main class that contains the runnable application, which usually is going to be where your public static void main(String[] args) declaration is. You also define a descriptor-ref, which is a String that will be appended to the jar's name. So, you'd end up with artifactId-1.0.0-jar-with-dependencies.jar, using my POM as an example.
To further explain what's going on, without the changes I recommend, your POM is telling Maven to just build your code. As part of that, you declare dependencies, of which you have two right now: aws-lambda-java-core and gson. When you don't provide a scope, it defaults to compile scope. This tells Maven to grab that dependency when the program is compiled, so that the program can use that dependency's code. But, when packaging the build artifact of your program, Maven, by default, will not include those dependencies in the final jar; it expects that when you run the jar, you'll have those dependencies on your classpath.
By adding the assembly build plugin, you're changing those instructions to Maven. With that plugin, you're telling Maven that when it builds the program, it needs to assemble it in such a way that all declared dependencies are included (read: assembled) with the program, and to do that during the package phase of the build; you'll see these dependencies in the lib folder of the build artifact. And then, like I mentioned earlier, the descriptorRef is descriptive info that will be appended onto the build artifact's name.
As an aside, and not truly relevant to your question, I'd recommend looking into FasterXML for JSON handling and manipulation. So much more powerful, so much easier, and it's widely supported and used, which means it has a great community behind it.

If you have dependencies that will be provided from runtime container, you should set scope provided to these dependencies.
<dependency>
<groupId>com.amazonaws</groupId>
<artifactId>aws-lambda-java-core</artifactId>
<version>1.0.0</version>
<scope>provided</scope>
</dependency>
And remove <artifactSet> section from plugin configuration, execute mvn package then jar with required dependencies will be created.

Related

Spring Boot Layered jar: Extract layers.xml to separate dependency

I'm starting to catch up on the capabilities of the repackage goal in spring-boot-maven-plugin.It looks promising, but I need to fine-tune it a little.
I can easily do that by creating a layers.xml file somewhere in my project, but the problem is that I don't only have 1 project, but rather half a dozen. All of the projects need the same kind of layering, but I don't really want to copy the same configuration for every project I want to use it on.
A nice-looking solution would be to extract that configuration file into a separate jar for example and have the plugin take the config file from there, but I see no way of doing it. Is there any other solution that doesn't involve me copying the configuration file to every project I have?
Unfortunately, even though the projects I have use the same parent, but are not in the same multi-module project.
I managed to work out a solution.
Before asking the question my spring-boot-mave-plugin config looked something like this:
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<layers>
<enabled>true</enabled>
</layers>
</configuration>
<executions>
<execution>
<id>repackage</id>
<goals>
<goal>repackage</goal>
</goals>
<phase>package</phase>
<configuration>
<layers>
<enabled>true</enabled>
<configuration><!-- something like classpath:layers.xml --></configuration>
</layers>
<classifier>exec</classifier>
</configuration>
</execution>
</executions>
<dependencies>
<dependency>
<groupId>artifact</groupId>
<artifactId>with-layers.xml</artifactId>
<version>...</version>
</dependency>
</dependencies>
</plugin>
The solution becomes one step more convoluted, by bringing in the maven-dependency-plugin, which downloads the before mentioned dependency and unpacks it in the build folder with this configuration:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<version>3.1.1</version>
<executions>
<execution>
<id>copy-shc-build-tools</id>
<goals>
<goal>unpack</goal>
</goals>
<phase>package</phase>
<configuration>
<artifactItems>
<artifactItem>
<groupId>artifact</groupId>
<artifactId>with-layers.xml</artifactId>
<version>...</version>
<type>jar</type>
<outputDirectory>${project.build.directory}</outputDirectory>
<includes>**/layers.xml</includes>
</artifactItem>
</artifactItems>
</configuration>
</execution>
</executions>
<configuration>
<overWriteReleases>false</overWriteReleases>
<overWriteSnapshots>true</overWriteSnapshots>
</configuration>
</plugin>
In turn, the line <configuration><!-- something like classpath:layers.xml --></configuration> becomes <configuration>${project.build.directory}/layers/layers.xml</configuration>.
The documentation for Spring Boot Maven Plugin states you can set the path to the layers.xml manually, so why not have all pom.xml point to the same location?
<project>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>2.3.2.RELEASE</version>
<configuration>
<layers>
<enabled>true</enabled>
<configuration>${project.basedir}/../layers.xml</configuration>
</layers>
</configuration>
</plugin>
</plugins>
</build>
</project>
The /../ means one level higher from the project directory. So say you have a bunch of projects in one directory, put the layers.xml there and it'll work.
Another approach could be to reuse the Maven Plugin declaration by moving it to a so-called parent POM. This is a technique where the common/shared parts of the POM files of a series of projects is moved to a single POM file (the parent POM). Here's an example

Maven clean install not including sqlite dependency for executable jar file

After using
mvn clean install
and then
java -jar executable.jar
I get this error:
java.lang.ClassNotFoundException: org.sqlite.JDBC
This 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>****</groupId>
<artifactId>****</artifactId>
<version>0.7-SNAPSHOT</version>
<packaging>jar</packaging>
<name>****</name>
<description>****</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>
<dependency>
<groupId>org.xerial</groupId>
<artifactId>sqlite-jdbc</artifactId>
<version>3.23.1</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>
<!-- https://mvnrepository.com/artifact/org.tinyjee.jgraphx/jgraphx -->
<dependency>
<groupId>org.tinyjee.jgraphx</groupId>
<artifactId>jgraphx</artifactId>
<version>3.4.1.3</version>
</dependency>
</dependencies>
<build>
<sourceDirectory>******</sourceDirectory>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>1.2.1</version>
<configuration>
<mainClass>*****</mainClass>
</configuration>
</plugin>
<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>******</mainClass>
</manifest>
</archive>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.3</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>
Running the program inside intelliJ works without problems. I included it there from the project structure.
I replaced names and directories with ****. This is a school project and I don't want my prof accusing me of providing solutions to other groups in case they find this stackoverflow entry.
Probably you are getting this only when you are running your jar because the dependencies are not available/packaged inside of it.
Try generating a "fat jar" (also known as uber-jar), it will package all your dependencies inside the jar:
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
</execution>
</executions>
<configuration>
<finalName>YOUR_JAR_FINAL_NAME</finalName>
</configuration>
</plugin>
</plugins>
</build>
Documentation related to the maven-shade-plugin can be found in here
Since you are using a runnable jar file, you can follow this section of the documentation related to Executable Jars
Some background.
Maven install never installs dependencies;
it only installs the project that is build via the POM.
The installation of dependencies are a task that you must also perform
if you don't use either a "fat jar" (which I can't recommend) or use
the new spring boot executable jar implementation.
The classic "fat jar" is an amazingly terrible
(but often the only option)
solution for a problem like this.
Consider using Spring-Boot;
they have developed a new,
sane,
version of an executable jar file that includes the dependencies within the executable jar and adds them to the classpath at startup.
This functionality is similar to the functionality of a "war" file when it is added to
a JEE container.
Caveat: I don't work for Pivotal,
I just like much of their work (the Spring Framework).
Edit:
Here is a link to the
Executable Jar Section in the Spring Boot Reference.
It contains some details.

How to include libraries/dependencies when creating a jar file?

I created a Confluence plugin(A Java application) which has Maven on it and includes some dependencies in the pom.xml as follows: (It needs to use the Google Client Library)
<dependencies>
<dependency>
<groupId>com.google.apis</groupId>
<artifactId>google-api-services-calendar</artifactId>
<version>v3-rev254-1.22.0</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>com.google.api-client</groupId>
<artifactId>google-api-client</artifactId>
<version>1.22.0</version>
<scope>compile</scope>
</dependency>
..... Skip .....
</dependencies>
I also downloaded the Google Client Library and created a "libs" folder at the "src/main/resources/" path in this maven project to store them, and added them as jars in Eclipse as follows:
However, after executed "atlas-debug" to invoke a Confluence instance or "atlas-package" commands, the final exported jar file usually does not include the dependencies/libraries (I found this according to the failed jar file size, it is much smaller than the successful one).
How to make the library files really be included into the exported jar file every time I executed "atlas-debug" or "atlas-package" commands?
You can use the maven-assembly-plugin plugin that will package all your dependency in the jar. You can configure it in the plugins section under the build section in your pom.xml:
<build>
...
<plugins>
...
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<version>2.2.2</version>
<executions>
<execution>
<id>assembly</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
<configuration>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
<archive>
</archive>
</configuration>
</execution>
</executions>
</plugin>
...
</plugins>
...
</build>
Remember that the dependency configured with <scope>provided</scope> won't be included in the jar.

How to avoid the generation of default jar when specifying a final name?

We are using maven profile to build a jar specific to tomcat.
<profile>
<id>TOMCAT</id>
<build>
<plugins>
<plugin>
<artifactId>maven-jar-plugin</artifactId>
<executions>
<execution>
<id>default-jar</id>
<phase>package</phase>
<goals>
<goal>jar</goal>
</goals>
<configuration>
<finalName>${project.artifactId}-tomcat-${project.version}</finalName>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>org.apache.activemq</groupId>
<artifactId>activemq-core</artifactId>
<version>5.7.0</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.apache.geronimo.specs</groupId>
<artifactId>geronimo-j2ee-management_1.1_spec</artifactId>
<version>1.0.1</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.jboss.javaee</groupId>
<artifactId>jboss-jms-api</artifactId>
<version>1.1.0.GA</version>
</dependency>
</dependencies>
</profile>
My expectation is to create a single jar with name acme-tomcat-0.0.1-SNAPSHOT.jar but on building the project, maven is generating another one (a default one) acme-0.0.1-SNAPSHOT.jar. How can we avoid the generation of the second one (acme-0.0.1-SNAPSHOT.jar)?
Thanks
I've only used Maven a few times to build projects, and I've generated WARs for Tomcat instead of JARs, but the process should be similar.
My build looks like this:
<build>
<finalName>sb</finalName>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>
The header at the top goes like this:
<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.majisto.socialbusiness</groupId>
<artifactId>socialbusiness</artifactId>
<version>0.0.2</version>
<packaging>war</packaging>
<name>socialbusiness</name>
I imagine if you switch the packaging row to JAR you should get one JAR file out of it. The finalName in the build determines the filename and I think where you have it might be the confusing part to Maven. Let me know if you have any other questions. Maven made working with Spring so much easier. Are you using "maven package" in the CLI?
By changing the build section in the profile section from
<build>
<plugins>
<plugin>
<artifactId>maven-jar-plugin</artifactId>
<executions>
<execution>
<id>default-jar</id>
<phase>package</phase>
<goals>
<goal>jar</goal>
</goals>
<configuration>
<finalName>${project.artifactId}-tomcat-${project.version}</finalName>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
to
<build>
<finalName>${project.artifactId}-tomcat-${project.version}</finalName>
</build>
helped me to solve this issue

Maven: is it possible to include a test jar in another project's compile scope

Suppose I have two projects A and B, A project has only test cases. Project B depends A, and will use classes in A for source compile under src/main/java. Is maven able to achieve this? How?
I know this is weird, but this is the situation in my environment.
Thanks in advance.
Follow the Guide to using attached tests:
Make sure you create those test jars in project A
<project>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>2.2</version>
<executions>
<execution>
<goals>
<goal>test-jar</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
And then use them in project B
<project>
...
<dependencies>
<dependency>
<groupId>com.myco.app</groupId>
<artifactId>foo</artifactId>
<version>1.0-SNAPSHOT</version>
<type>test-jar</type>
<scope>compile</scope>
</dependency>
</dependencies>
...
</project>
This sounds like a strange use case. I'd spend some time examining if its really the right project structure.
If you decide if it is the correct project structure its just a dependency in maven. If the classes in Project A are only used for testing in Project B then you may want to set the dependency scope to test as well.
Create a test jar like this:
<project>
...
<build>
<plugins>
...
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>2.5</version>
<executions>
<execution>
<goals>
<goal>test-jar</goal>
</goals>
</execution>
</executions>
</plugin>
...
</plugins>
</build>
...
</project>
Using the test artifact by:
<project>
...
<dependencies>
<dependency>
<groupId>groupId</groupId>
<artifactId>artifactId</artifactId>
<type>test-jar</type>
<version>version</version>
</dependency>
</dependencies>
...
</project>

Categories