192

Until now I created runnable JAR files via the Eclipse "Export..." functionallity but now I switched to IntelliJ IDEA and Gradle for build automation.

Some articles here suggest the "application" plugin, but this does not entirely lead to the result I expected (just a JAR, no start scripts or anything like this).

How can I achieve the same result Eclipse does with the "Export..." dialog?

Mahozad
  • 18,032
  • 13
  • 118
  • 133
Hannes
  • 5,002
  • 8
  • 31
  • 60

12 Answers12

200

An executable jar file is just a jar file containing a Main-Class entry in its manifest. So you just need to configure the jar task in order to add this entry in its manifest:

jar {
    manifest {
        attributes 'Main-Class': 'com.foo.bar.MainClass'
    }
}

You might also need to add classpath entries in the manifest, but that would be done the same way.

See http://docs.oracle.com/javase/tutorial/deployment/jar/manifestindex.html


If you already have defined an application context, you can re-use the definition rather than duplicate it:

application {
  // Define the main class for the application.
  mainClass = 'com.foo.bar.MainClass'
}

jar {
  manifest {
    attributes 'Main-Class': application.mainClass
  }
}
Dave Jarvis
  • 30,436
  • 41
  • 178
  • 315
JB Nizet
  • 678,734
  • 91
  • 1,224
  • 1,255
  • 6
    This seems to be what I was looking for; but: I declared already dependencies in the build.gradle, do I really have to add manually the class path or can I re-use my dependency declaration? – Hannes Feb 12 '14 at 08:03
  • You should be able to loop through the libraries inside the 'runtime' configuration and to concatenate them to create the Class-Path attribute value. – JB Nizet Feb 12 '14 at 08:05
  • 4
    What do you mean with 'inseid the "runtime" configuration'? Sorry for stupid questions, I am quite new to Gradle ... – Hannes Feb 12 '14 at 09:20
  • The gradle java plugin defines 4 "configurations" corresponding to 4 different classpaths: compile (used to compile the Java files), testCompile (which is used to compile the test Java source files), runtime (which is used to execute the application) and testRuntime (which is used to execute the tests). See http://www.gradle.org/docs/current/userguide/userguide_single.html#configurations – JB Nizet Feb 12 '14 at 12:04
  • Ah, thanks - than I understood it correct and manage to build the JAR, now I am frickeling with creating the classpath ;-) Thanks a lot for your help! – Hannes Feb 12 '14 at 12:17
  • 1
    Adding manifest attribute also as adding `chmod +x` attribute to a file is completely not enough to make JAR executable and absolutely not the same as Eclipse command `Export to` does. – Dims Jun 17 '16 at 15:55
  • gradle-shadow-plugin is better as stated below by Jared Burrows, see the getting started guide here: https://imperceptiblethoughts.com/shadow/getting-started/#default-java-groovy-tasks – Intel Jun 27 '19 at 01:02
  • How would I use this with the Kotlin DSL? When I tried it, I got an error; something like "Expression 'jar' cannot be invoked as a function. The function 'invoke()' is not found" – Julia Aug 18 '20 at 22:03
118

Both JB Nizet and Jorge_B's answers are correct.

In its simplest form, creating an executable JAR with Gradle is just a matter of adding the appropriate entries to the manifest. However, it's much more common to have dependencies that need to be included on the classpath, making this approach tricky in practice.

The application plugin provides an alternate approach; instead of creating an executable JAR, it provides:

  • a run task to facilitate easily running the application directly from the build
  • an installDist task that generates a directory structure including the built JAR, all of the JARs that it depends on, and a startup script that pulls it all together into a program you can run
  • distZip and distTar tasks that create archives containing a complete application distribution (startup scripts and JARs)

A third approach is to create a so-called "fat JAR" which is an executable JAR that includes not only your component's code, but also all of its dependencies. There are a few different plugins that use this approach. I've included links to a few that I'm aware of; I'm sure there are more.

Phil Haigh
  • 4,522
  • 1
  • 25
  • 29
davidmc24
  • 2,232
  • 1
  • 19
  • 17
  • Just tried shadow and one-jar. I'll stick to one-jar: it is simpler and easier to use. Cool! thanks – Jako Nov 28 '17 at 11:46
  • Unfortunately one-jar does not work with recent versions of Gradle. See, for example, https://github.com/rholder/gradle-one-jar/issues/34 – pharsicle May 11 '18 at 01:47
  • 1
    [Here](http://www.baeldung.com/gradle-fat-jar) is a one-liner solution to build one-jar by modifying the jar task. I've found this to be the most convenient. Note that you need to add `configurations.runtime` to bundle runtime dependencies in the single jar. – Quazi Irfan May 16 '18 at 23:40
  • I found "application plugin" to be suitable and flexible enough for my need. It allows also to pack everything to zip and include/exclude extra files in that zip. Also, it's possible to add another launcher script with additional entry point using custom task. – kinORnirvana Jan 05 '20 at 22:27
  • How would I execute the `.tar` / `.zip` files generated? – Tobiq Feb 13 '20 at 01:26
  • The .tar/.zip files mentioned are not directly executable. You extract them and run the script bundled within. – davidmc24 Feb 11 '21 at 21:33
40

Least effort solution for me was to make use of the gradle-shadow-plugin

Besides applying the plugin all that needs to be done is:

Configure the jar task to put your Main class into manifest

jar {
  manifest {
   attributes 'Main-Class': 'com.my.app.Main'
  }
}

Run the gradle task

./gradlew shadowJar

Take the app-version-all.jar from build/libs/

And finally execute it via:

java -jar app-version-all.jar
Jared Burrows
  • 54,294
  • 25
  • 151
  • 185
Ostkontentitan
  • 6,930
  • 5
  • 53
  • 71
  • 2
    Here's a [full `build.gradle` example](https://gist.github.com/TurekBot/4a3cd406cb52dfd8bfb8022b982f0f6c). – Brad Turek Jan 10 '18 at 17:46
  • When I do this build I get BUILD SUCCESSFUL but when I try to run jar file with java -jar build/libs/core-all-1.0.jar I get following error: Error: Could not find or load main class scanners.exchange.Main Caused by: java.lang.ClassNotFoundException: scanners.exchange.Main Do you know how I can solve this? – Luka Lopusina Jun 18 '18 at 12:08
  • @LukaLopusina The class you specified isn't in your JAR file. If you're using kotlin, you need to say `'com.my.app.MainKt'`. Without more info, I can't help you further. – byxor Oct 04 '19 at 14:29
  • Current plugin Shadow v5.+ is compatible with Gradle 5.0+ and Java 7+ only. – Zon Nov 06 '19 at 17:42
38

As others have noted, in order for a jar file to be executable, the application's entry point must be set in the Main-Class attribute of the manifest file. If the dependency class files are not collocated, then they need to be set in the Class-Path entry of the manifest file.

I have tried all kinds of plugin combinations and what not for the simple task of creating an executable jar and somehow someway, include the dependencies. All plugins seem to be lacking one way or another, but finally I got it like I wanted. No mysterious scripts, not a million different mini files polluting the build directory, a pretty clean build script file, and above all: not a million foreign third party class files merged into my jar archive.

The following is a copy-paste from here for your convenience..

[How-to] create a distribution zip file with dependency jars in subdirectory /lib and add all dependencies to Class-Path entry in the manifest file:

apply plugin: 'java'
apply plugin: 'java-library-distribution'

repositories {
    mavenCentral()
}

dependencies {
    compile 'org.apache.commons:commons-lang3:3.3.2'
}

// Task "distZip" added by plugin "java-library-distribution":
distZip.shouldRunAfter(build)

jar {
    // Keep jar clean:
    exclude 'META-INF/*.SF', 'META-INF/*.DSA', 'META-INF/*.RSA', 'META-INF/*.MF'

    manifest {
        attributes 'Main-Class': 'com.somepackage.MainClass',
                   'Class-Path': configurations.runtime.files.collect { "lib/$it.name" }.join(' ')
    }
    // How-to add class path:
    //     https://stackoverflow.com/questions/22659463/add-classpath-in-manifest-using-gradle
    //     https://gist.github.com/simon04/6865179
}

Hosted as a gist here.

The result can be found in build/distributions and the unzipped contents look like this:

lib/commons-lang3-3.3.2.jar
MyJarFile.jar

Contents of MyJarFile.jar#META-INF/MANIFEST.mf:

Manifest-Version: 1.0
Main-Class: com.somepackage.MainClass
Class-Path: lib/commons-lang3-3.3.2.jar

Community
  • 1
  • 1
Martin Andersson
  • 18,072
  • 9
  • 87
  • 115
  • The current application jar will be in the 'lib' directory of the distribution too. You have to either move it by had to the top directory or change this line: 'Class-Path': configurations.runtime.files.collect { "lib/$it.name" }.join(' ') } to this: 'Class-Path': configurations.runtime.files.collect { "$it.name" }.join(' ') } – Marc Nuri Sep 14 '15 at 10:29
  • 1
    @MarcNuri Are you sure? I tried using this approach for my application, and the current application jar was **not** in the `lib` directory of the produced zip/tar file, but rather was in `lib`'s parent directory, as this answer suggests. This solution seemed to work perfectly for me. – thejonwithnoh Dec 11 '15 at 18:42
  • 1
    @thejonwithnoh I'm sorry, you're right. I didn't see the solution proposed to use the "java-library-distribution" plugin. In my case I'm simply using the "application" plugin which does the same job with the main difference that all of the jar files (**including** the application jar) are located in the "lib" directory. Thus changing `"lib/$it.name"` to `"$it.name"` will do the job. – Marc Nuri Dec 14 '15 at 09:32
  • `configurations.runtime.files.collect` seems to be replaced with `configurations.runtimeClasspath.files.collect` as of Gradle 7.x – comfreak Sep 01 '22 at 14:35
13

This is for Kotlin DSL (build.gradle.kts).

Method 1 (no need for application or other plugins)

tasks.jar {
    manifest.attributes["Main-Class"] = "com.example.MyMainClass"
    // OR another notation
    // manifest {
    //     attributes["Main-Class"] = "com.example.MyMainClass"
    // }
}

If you use any external libraries, use below code. Copy library JARs in libs sub-directory of where you put your result JAR. Make sure your library JAR files do not contain space in their file name.

tasks.jar {
    manifest.attributes["Main-Class"] = "com.example.MyMainClass"
    manifest.attributes["Class-Path"] = configurations
        .runtimeClasspath
        .get()
        .joinToString(separator = " ") { file ->
            "libs/${file.name}"
        }
}

Note that Java requires us to use relative URLs for the Class-Path attribute. So, we cannot use the absolute path of Gradle dependencies (which is also prone to being changed and not available on other systems). If you want to use absolute paths, maybe this workaround will work.

Create the JAR with the following command:

./gradlew jar

The result JAR will be created in build/libs/ directory by default.

Method 2: Embedding libraries (if any) in the result JAR (fat or uber JAR)

tasks.jar {
    manifest.attributes["Main-Class"] = "com.example.MyMainClass"
    val dependencies = configurations
        .runtimeClasspath
        .get()
        .map(::zipTree) // OR .map { zipTree(it) }
    from(dependencies)
    duplicatesStrategy = DuplicatesStrategy.EXCLUDE
}

Creating the JAR is exactly the same as the previous method.

Method 3: Using the Shadow plugin (to create a fat or uber JAR)

plugins {
    id("com.github.johnrengelman.shadow") version "6.0.0"
}
// Shadow task depends on Jar task, so these will be reflected for Shadow as well
tasks.jar {
    manifest.attributes["Main-Class"] = "org.example.MainKt"
}

Create the JAR with this command:

./gradlew shadowJar

See Shadow documentations for more information about configuring the plugin.

Running the created JAR

java -jar my-artifact.jar

The above solutions were tested with:

  • Java 17
  • Gradle 7.1 (which uses Kotlin 1.4.31 for .kts build scripts)

See the official Gradle documentation for creating uber (fat) JARs.

For more information about manifests, see Oracle Java Documentation: Working with Manifest files.

Note that your resource files will be included in the JAR file automatically (assuming they were placed in /src/main/resources/ directory or any custom directory set as resources root in the build file). To access a resource file in your application, use this code (note the / at the start of names):

  • Kotlin
    val vegetables = MyClass::class.java.getResource("/vegetables.txt").readText()
    // Alternative ways:
    // val vegetables = object{}.javaClass.getResource("/vegetables.txt").readText()
    // val vegetables = MyClass::class.java.getResourceAsStream("/vegetables.txt").reader().readText()
    // val vegetables = object{}.javaClass.getResourceAsStream("/vegetables.txt").reader().readText()
    
  • Java
    var stream = MyClass.class.getResource("/vegetables.txt").openStream();
    // OR var stream = MyClass.class.getResourceAsStream("/vegetables.txt");
    var reader = new BufferedReader(new InputStreamReader(stream));
    var vegetables = reader.lines().collect(Collectors.joining("\n"));
    
Mahozad
  • 18,032
  • 13
  • 118
  • 133
  • Also, see [this post](https://stackoverflow.com/a/71092054/8583692) to create a new task instead of modifying the existing *Jar* task. – Mahozad Feb 12 '22 at 13:00
12

You can use the SpringBoot plugin:

plugins {
  id "org.springframework.boot" version "2.2.2.RELEASE"
}

Create the jar

gradle assemble

And then run it

java -jar build/libs/*.jar

Note: your project does NOT need to be a SpringBoot project to use this plugin.

Topera
  • 12,223
  • 15
  • 67
  • 104
5

Have you tried the 'installApp' task? Does it not create a full directory with a set of start scripts?

http://www.gradle.org/docs/current/userguide/application_plugin.html

Jorge_B
  • 9,712
  • 2
  • 17
  • 22
  • From what I understand, `installApp` doesn't create a `META-INF/MANIFEST.MF` file. Am I doing something wrong? – advait Oct 14 '14 at 06:15
  • 1
    I do not see `installApp` task in the task list of [Application Plugin](https://docs.gradle.org/current/userguide/application_plugin.html#sec:application_tasks). Did you mean `installDist` instead? – Quazi Irfan May 16 '18 at 23:23
  • 2
    Yes, `installApp` was renamed to `installDist` in Gradle 3.0. Here is the [release note](https://docs.gradle.org/3.0/release-notes.html). – Quazi Irfan May 16 '18 at 23:25
5

Thank you Konstantin, it worked like a charm with few nuances. For some reason, specifying main class as part of jar manifest did not quite work and it wanted the mainClassName attribute instead. Here is a snippet from build.gradle that includes everything to make it work:

plugins {
  id 'java' 
  id 'com.github.johnrengelman.shadow' version '1.2.2'
}
...
...
apply plugin: 'application'
apply plugin: 'com.github.johnrengelman.shadow'
...
...
mainClassName = 'com.acme.myapp.MyClassMain'
...
...
...
shadowJar {
    baseName = 'myapp'
}

After running gradle shadowJar you get myapp-{version}-all.jar in your build folder which can be run as java -jar myapp-{version}-all.jar.

3

You can define a jar artifact in the module settings (or project structure).

  • Right click the module > Open module settings > Artifacts > + > JAR > from modules with dependencies.
  • Set the main class.

Making a jar is then as easy as clicking "Build artifact..." from the Build menu. As a bonus, you can package all the dependencies into a single jar.

Tested on IntelliJ IDEA 14 Ultimate.

Mondain
  • 31
  • 2
2

I checked quite some links for the solution, finally did the below mentioned steps to get it working. I am using Gradle 2.9.

Make the following changes in your build,gradle file :

  1. Mention plugin:

    apply plugin: 'eu.appsatori.fatjar'
    
  2. Provide the Buildscript:

    buildscript {
    repositories {
        jcenter()
    }
    
    dependencies {
        classpath "eu.appsatori:gradle-fatjar-plugin:0.3"
    }
    }
    
  3. Provide the Main Class:

    fatJar {
      classifier 'fat'
      manifest {
        attributes 'Main-Class': 'my.project.core.MyMainClass'
      }
      exclude 'META-INF/*.DSA', 'META-INF/*.RSA', 'META-INF/*.SF'
    }
    
  4. Create the fatjar:

    ./gradlew clean fatjar
    
  5. Run the fatjar from /build/libs/ :

    java -jar MyFatJar.jar
    
Raiv
  • 5,731
  • 1
  • 33
  • 51
Sandeep Sarkar
  • 399
  • 3
  • 5
  • 1
    From 2019: This suggestion does not work here. No dependencies are included in the fatjar – carl May 09 '19 at 15:26
1

Here is the solution I tried with Gradle 6.7

Runnable fat Jar (with all dependent libraries copied to the jar)

task fatJar(type: Jar) {
   
    manifest {
        attributes 'Main-Class': 'com.example.gradle.App'
    }
    from {
        configurations.compile.collect { it.isDirectory() ? it : zipTree(it) }
    } with jar
    
}

Runnable jar with all dependencies copied to a directory and adding the classpath to the manifest

    def dependsDir = "${buildDir}/libs/dependencies/"
    task copyDependencies(type: Copy) {
        from configurations.compile
        into "${dependsDir}"
    }
    task createJar(dependsOn: copyDependencies, type: Jar) {
      
        manifest {
            attributes('Main-Class': 'com.example.gradle.App',
                    'Class-Path': configurations.compile.collect { 'dependencies/' + it.getName() }.join(' ')
            )
        }
        with jar
    }

How to use ?

  • Add the above tasks to build.gradle
  • Execute gradle fatJar //create fatJar
  • Execute gradle createJar // create jar with dependencies copied.

More details : https://jafarmlp.medium.com/a-simple-java-project-with-gradle-2c323ae0e43d

jfk
  • 4,335
  • 34
  • 27
0
  1. Configure Main Class to your Manifest

If you are using gradle project, just add the following into your build.gradle

jar {
    manifest {
        attributes(
               'Main-Class': 'pokerhandscorer.PokerHandScorer'
        )
    }
}
  • Where 'pokerhandscorer' is the name of the package name, and PokerHandScorer is the main class name

This creates a jar file into your \build\libs{jarFilename}.jar

  1. Run jar file using java -jar /{path}/{jarFileName.jar}

java -jar /{path}/{jarFileName.jar}

Niraj
  • 517
  • 1
  • 5
  • 14