How to package dependencies into output jar? - java

I am trying to make my application work in a stand alone jar. The problem is, a jar is generated for my program, and a bunch of other jars are generated for the libraries. Is there any way to get these jars to get inside one? I am using Gradle if that helps.
The IntelliJ IDEA artifact config:
The output directory:
What I expected (and want) to happen:

You need a fat-jar (jar file with all it's dependencies inside). It's not a big problem for Gradle, you just need to make one additional task of type jar, which will collect all the dependencies and zip it alltogether
There are many examples, how you can do it, here is one of them. Take a closer look at task fatJar:
task fatJar(type: Jar) {
baseName = project.name + '-all'
from { configurations.compile.collect { it.isDirectory() ? it : zipTree(it) } }
with jar
}

Related

Compile a groovy script with all it's dependencies which are managed by gradle and then run it as a standalone application via the command line

I have a simple groovy script with a single java library dependency:
package com.mrhacki.myApp
import me.tongfei.progressbar.ProgressBar
class Loading {
static void main(String[] arguments) {
List list = ["file1", "file2", "file3"]
for (String x : ProgressBar.wrap(list, "TaskName")) {
println(x)
}
}
}
I'm using gradle to manage the dependencies of the project. The gradle configuration for the project is pretty straightforward too:
plugins {
id 'groovy'
}
group 'com.mrhacki'
version '1.0-SNAPSHOT'
repositories {
mavenCentral()
}
dependencies {
compile 'org.codehaus.groovy:groovy-all:2.3.11'
compile 'me.tongfei:progressbar:0.7.2'
}
If I run the script from the Intellij IDE, the script is executed as expected.
What I would like to do now is to compile the script with this dependency into one single .jar file, so I can distribute it as such, and run the application from any filesystem path, as the script logic will be dependent on the path from which the execution was called.
I've tried with a few gradle fat jars examples out there but none have worked for me since the .jar file is constantly throwing Could not find or load main class Loading when I try it to run.
If anyone would be so kind to give a hint or to show an example of a gradle task that would do a build that fits my described needs I would be very gratefull.
I'm aware of the groovy module Grape with the #Grab annotation too, but I would leave that as a last resort since I don't want the users to wait for the dependencies download, and would like to bundle them with the app.
I'm using groovy 2.5.6 and gradle 4.10 for the project
Thanks
You can simply create the fat-jar yourself, without any extra plugin, using the jar Task. For a simple/small project like yours, it should be straightforward :
jar {
manifest {
// required attribute "Main-Class"
attributes "Main-Class": "com.mrhacki.myApp.Loading"
}
// collect (and unzip) dependencies into the fat jar
from {
configurations.compile.collect {
it.isDirectory() ? it : zipTree(it)
}
}
}
EDIT : pleas take other comments into consideration: if you have more that one external lib you might have issues with this solution, so you should go for a solution with "shadow" plugins in this case.

Intellij Grade Build Jar with dependencies

I want to build a jar file in IntelliJ IDEA with Gradle.
When I run my code in Intellij everything works fine,
but when I run the jar file I get an error:
SQLExecption: No suitable driver found for jdbc:sqlite:/applications/elite-dangerous/database/ED_Database.db
I build the jar throw pressing the build button.
It's strange for me because it works perfectly fine when I run it in IntelliJ IDEA.
Dependencies included using implementation config are not being included in the Jar which makes them not available in runtime. So, I guess that could be the case. You can try changing implementation to compile dependencies ( which is deprecated, so not recommended ) or You can include your dependencies in the jar as below
jar {
manifest {
attributes 'Main-Class': 'eliteDangerousRestUpdater.Main'
}
from {
compileJava.classpath.collect {
it.isDirectory() ? it : zipTree(it)
}
}
}

Should Gradle make a per project local copy of JAR-s from mavenLocal()?

I have some library Gradle Java projects that I am currently publishing to my local Maven repositoty with the publishToMavenLocal task. I can look for a library called base file in my local .m2\repository\... area, and find it as (say):
... lib\base\08.00.003-SNAPSHOT\base-08.00.003-SNAPSHOT.jar
A client project, socket_listener uses base and declares the local repository as shown. This project build fine and runs successfully from the Gradle command line.
repositories {
mavenLocal()
jcenter()
}
// later in the file...
//
assemble.dependsOn distDirectory
Run ...
gradle socket_listener:run
:
lots of logger output
:
The socket_listener project creates a dist directory for the socket_listener.jar assembly with requried dependencies in a lib/* folder for dependencies with this:
task distDirectory( type: Copy ) {
into 'dist'
from jar
from 'src/dist'
into( 'lib' )
{
from configurations.runtime
}
}
Which I also thought was OK until I tried to run my listener project from the command line; it fails thus.
cd socket_listener\dist
java -jar SocketListener.jar
Gives error (manifest setting below):
Error: Could not find or load main class example.app.cmd.SocketListener
And when I inspected the lib/ folder -- NO base-08.00.003-SNAPSHOT.jar was to be found. None of the mavenLocal JAR files are in lib/. This is my first hurdle, the program won't work without those mavenLocal JAR-s.
As well manifest settings in gradle are just copies from other working projects.
mainClassName = 'example.app.cmd.SocketListener'
:
jar
{
manifest
{
attributes( "Main-Class": mainClassName )
attributes( "Application-Name": "socket_listener" )
attributes( "Class-Path": 'lib/' + ( configurations.runtime.collect { it.getName() }.join(' lib/') ) )
attributes( "Codebase": "*" )
attributes( "Permissions": "all-permissions" )
}
}
That should not be problem #2.
Upon discovering base-08.00.003-SNAPSHOT.jar missing-in-action from the lib/ directory, I set about to find that file. Guess what?
The ONLY place I found the JAR file is in the Maven repositor, viz.:
dir /s/b d:\*08.00.003-SNAPSHOT.jar
d:\.rep\.m2\repository\local\lib\driver_model\08.00.003-SNAPSHOT\driver_model-08.00.003-SNAPSHOT.jar
d:\.rep\.m2\repository\local\lib\base\08.00.003-SNAPSHOT\base-08.00.003-SNAPSHOT.jar
dir /s/b d:\*08.00.003-SNAPSHOT.jar
File Not Found
Which is kind of same as a linux find / -name "*08.00.003-SNAPSHOT.jar" command. That's every directory on the PC, directly after a successful
gradle socket_listener:build -x test --refresh-dependencies
gradle socket_listener:run
And ensuring the latest dependencies loaded. From my little experiemtal result it is quite clear that the project's Gradle build is using the library JAR-s directly from the repository -- For build, for run, for test.
This is completly UNEXPECTED ...
My collegues and I feel sure that other locally build artefacts were cached or at least saved locally to a dist/ folder. In ths example the base JAR is not copied anywhere.
Can I force the mavenLocal assets to be (at least) copied into the lib/ folder and be ready for use?
In addition, looking into the contents of the current SocketListener.jar's manifest I am not convinced that all requisite JAR files have been loaded. For a start, things that my base JAR depends are also will be missing. I feel that other up-stream dependencies are NOT there either.
What is the usual process to ensure all dependencies are gathered for a build and for running a Java command line app from both Gradle and the command line prompt??
Is it normal to use the maven repository like that?
How can it be overridden?
I have discovered that configurations.runtime is not what I need.
The from clause in the distDirectory task should
As follows
task distDirectory( type: Copy ) {
into 'dist'
from jar
from 'src/dist'
into( 'lib' )
{
from configurations.from configurations.runtimeClasspath
}
}
runtimeClasspath gives me all the direct dependencies for socket_listener.
I am unsure 'how' the build.gradle I took that model from performs for other projects. I expect those projects make an installer and don't need to use that dist/ folder explicitly.
Also, afer some testing and experimentation it is confirmed that this JAR won't run unless the Manifest also uses runtimeClasspath collection (or compileClasspath), viz.
jar {
manifest {
attributes( "Main-Class": mainClassName )
attributes( "Application-Name": "socket_listener" )
attributes( "Class-Path": 'lib/' + ( configurations.runtimeClasspath.collect { it.getName() }.join(' lib/') ) )
attributes( "Codebase": "*" )
attributes( "Permissions": "all-permissions" )
}
}
However, I suspect that is Not Always the case. The main class SocketListener probably needs an absent JAR in the runtimeClasspath to load. Other programs may load fine. They may fall over with a stacktrace that will identify were look. Or just work OK.
Would that the java launcher would be as nice.
Btw, I didn't find ClassPath mentioned in any of the many 'simple Gradle examples` for making a JAR that runs outh there the web. While you can build without a ClassPath, it is better to have one for real-world use imho.
On to practical matters. This problem arose because the build.gradle file in question uses: implementation( ... ) dependency specifications instead of:compile( ... ) dependencies.
I was able to confirm this by moving one dependency from an implementation to a compile line and use the runtime collection. The resulting JAR and lib/ have the module I moved.
I leave which one you use to the reader. Here's some references:
Recognizing API and implementation dependencies (The Java Library Plugin)
API and implementation separation (java plugin)
Declaring a concrete version of a dependency (Declaring Dependencies)

Gradle: multiple JARs without multiple projects

I have a library which is used to build a number of CLI tools using Gradle. Each CLI tool is a separate JAR. At the moment every tool requires a separate Gradle project, with an associated set of directories, like this:
Having all of this structure is resulting in the whole collection of tools becoming very unwieldy and difficult to work with. Is there any way to collect all of the different Mains into a single folder (suitably renamed) and configure Gradle to turn each one into a separate JAR?
FWIW, the JARs are currently created using https://github.com/johnrengelman/shadow . JAR size doesn't matter.
Thanks in advance.
Jars are just zip files with META-INF folder inside. Use Zip tasks to create them and dependsOn to run tasks as part of your build sequence.
I had the code like below for changing jar files:
task changeJar (type: Zip) {
baseName project.name
extension 'jar'
destinationDir new File('build')
entryCompression ZipEntryCompression.STORED
from { zipTree(new File(core.libsDir, core.name + '.jar')) }
from ( <somewhere else> ) {
exclude 'META-INF/'
}
}
I'm not sure if it's a good fit but you might be interested in my gradle-java-flavours plugin.
eg:
apply plugin: 'com.lazan.javaflavours'
javaFlavours {
flavour 'tool1'
flavour 'tool2'
}
dependencies {
compile 'a:a:1.0' // common to all tools
compileTool1 'b:b:2.0' // compile deps for tool1 only
runtimeTool2 'c:c:2.0' // runtime deps for tool2 only
}
Directories
src/main/java, src/test/java, src/main/resources, src/test/resources - common code & tests
src/tool1/java, src/testTool1/java, src/tool1/resources, src/testTool1/resources - tool1 only sources
src/tool2/java, src/testTool2/java, src/tool2/resources, src/testTool2/resources - tool2 only sources
Jars
projectName.jar
projectName-tool1.jar
projectName-tool2.jar

Configuration compile and runtime for gradle

I set up a classpath for my jar reference inside a jar.('classpath: 'wee.jar'), but apparently, I also need to type the following in my jar task
from {
configurations.compile.collect {
it.isDirectory() ? it : zipTree(it)
}
configurations.runtime.collect {
it.isDirectory() ? it : zipTree(it)
}
Can Someone explain to me what does from, configuration.compile.collect, runtime, and isDirectory and zipTree do? I look up google, but couldn't find any answer. I'm really new to gradle
For starters, you do not need both configurations.compile and configurations.runtime. In gradle, the compile time dependencies are already included in runtime config automatically - which makes compile a subset of runtime. Depending on what you're trying to achieve, you'll only need one or the other. So let's take this snippet:
configurations.compile.collect {
it.isDirectory() ? it : zipTree(it)
}
A configuration represents a collection of artifacts and their dependencies. compile and runtime are among configs that are added by the java plugin. collect is groovy for: do the following operation for every element of a collection and return the result as a set. So effectively the line of code translates to - for all dependencies declared in configurations.compile, do the following and return the results as a set.
it is groovy shorthand for iterator - so it represents each element of the aforesaid collection.
if `it` is a directory
include it as is,
else
unpack the file and then include it
(See zipTree reference here)
Putting the whole thing together, the code is taking all compile time dependency directories and all unpacked compile time jars and including that into the jar you're building.

Categories

Resources