3

My application requires spring-context library. So in my build.gradle file, I add this dependency,

dependencies {
    compile 'org.springframework:spring-context:5.1.6.RELEASE'
}

I also want to create a runnable jar file, the jar task looks like,

jar {
    manifest {
        attributes 'Main-Class': 'my.package.my.main.class'
    }
}

Everything is ok when I launched the build file,

gradle build

But when I tried to execute this runnable jar application,

task runJar(dependsOn: jar, type: JavaExec) {
    classpath(configurations.compile)
    main = '-jar'; args 'build/libs/my-runnable.jar'
}

Console thrown NoClassDefFoundError,

Exception in thread "main" java.lang.NoClassDefFoundError: 
org/springframework/context/ConfigurableApplicationContext
...
...

I've tried sourceSets.main.runtimeClasspath, problem remain the same,

task runJar(dependsOn: jar, type: JavaExec) {
    classpath(sourceSets.main.runtimeClasspath)
    main = '-jar'; args 'build/libs/my-runnable.jar'
}

I checked the classpath files collection,

classpath.each {
    println it.name
}

spring-context package was right there,

spring-context-5.1.6.RELEASE.jar
spring-aop-5.1.6.RELEASE.jar
spring-beans-5.1.6.RELEASE.jar
spring-expression-5.1.6.RELEASE.jar
spring-core-5.1.6.RELEASE.jar
spring-jcl-5.1.6.RELEASE.jar

Then I tried another walk-around approach to solve this. I extracted all class files from those .jar libraries, and included them directly in my final runnable.jar,

jar {
    manifest {
        attributes 'Main-Class': 'my.package.my.main.class'
    }

    configurations.compile.each {
        from(project.zipTree(it))
    }
}

And this solution works. But such a "fat jar" is not what I want.

I am a newcomer of gradle. This really confused me. Any one can help? Thanks!

shen
  • 933
  • 10
  • 19

1 Answers1

1

After about 3 hours work, a simple truth is:

When you specify -jar then the -cp parameter will be ignored.

Some detailed discussion is in this question:

Run a JAR file from the command line and specify classpath

There are 2 simple walk-around solutions:

  1. Extract all .class files directly into your executable jar as I mentioned in my question.
jar {
    manifest {
        attributes 'Main-Class': 'my.package.my.main.class'
    }

    configurations.compile.each {
        from(project.zipTree(it))
    }
}
  1. Second solution is to add a Class-Path parameters in manifest file.
jar {
    manifest {
        attributes 'Main-Class': 'your.main.class.full.name'
        attributes 'Class-Path': configurations.compile.collect { it.name }.join(' ')
    }
}

Make sure you can find .jar libraries in your local system. Ex, simply copy them into a same directory,

task copyLibs(type: Copy) {
    from configurations.compile
    into 'build/libs/'
}

After that my build/libs/ directory looks like,

build
├── libs
│   ├── my-runnable-app.jar
│   ├── spring-aop-5.1.6.RELEASE.jar
│   ├── spring-beans-5.1.6.RELEASE.jar
│   ├── spring-context-5.1.6.RELEASE.jar
│   ├── spring-core-5.1.6.RELEASE.jar
│   ├── spring-expression-5.1.6.RELEASE.jar
│   └── spring-jcl-5.1.6.RELEASE.jar

Notice that all .jar libraries locate out side my-runnable-app.jar. If you want to include them in you my-runnable-app.jar, you need One-Jar toolkit. For more information about One-Jar, please read this question:

Reference jars inside a jar

shen
  • 933
  • 10
  • 19