'How to call custom Java method from Gradle

Please, what is the most convenient way how to call a Java method within Gradle build script? This method contain functionality I want to use during build; a piece of functionality too complex to be a in-script Groovy method but not enough to command full custom Gradle plugin.

My question is not about running a Java application from Gradle. That would involve forking JVM, and there is no simple way how to publish output back to Gradle and I need that output.

Initially I had my code in src/main/java but it has to be compiled early on to be callable from within Gradle build script.

Then I tried to move the build helper class to a submodule project, a one class submodule, hoping that the submodule would be builded before the main build script is called; in there I put:

 buildscript {
    dependencies {
       classpath 'this.project:build-helper:1.0-SNAPSHOT'
    }
 }

But the build-helper subproject is clearly not build first, despite having in in settings.gradle as first and build fails as the dependency is not recognised. It seems buildscript is evaluated before submodules are built, before anything else. D'oh!



Solution 1:[1]

There's two main use cases

Source is in src/main/java in the current project - Therefore the class does NOT exist when the buildscript classpath is defined

Options:

  1. Use a JavaExec task to run the class in another JVM

  2. Construct a URLClassLoader from sourceSets.main.runtimeClasspath and load / run the class via the classloader

eg:

task doStuff {
    doLast {
        URL[] urls = sourceSets.main.runtimeClasspath.files as URL[]
        def classloader = new URLClassLoader(urls, null)
        Class myClass = classloader.load("foo.bar.MyClass")
        def myInstance = myClass.newInstance()
        Method method = myClass.getMethod("doStuff")
        method.invoke(myInstance)
    }
}

Source is in another project. - Therefore the class will exist before the buildscript classpath is defined

In this case you can add the project to the buildscript classpath and invoke it

buildscript {
    dependencies {
        classpath project(':other-project')
    }
}
task doStuff {
    doLast {
        def myClass = new MyClass()
        myClass.doStuff()
    }
}

Solution 2:[2]

So the easiest way is to put my build helper Java source into buildSrc directory: <project root dir>/buildSrc/src/main/java/foo/bar/Extractor.java and that's it, you can call new Extractor(..).extratMetadata(...) directly from anywhere in Gradle build script, no need to import package, no need to use Java plugin, Gradle by default understands Maven like directory structure under buildScr.

Credits to: https://zeroturnaround.com/rebellabs/using-buildsrc-for-custom-logic-in-gradle-builds/

Solution 3:[3]

Groovy

  1. build your project files.
  2. run this groovy file and call object method
        // This code is similar to lance-java's
        def files = sourceSets.main.runtimeClasspath.files
        // files[0] is C:\...\YourProject\build\classes\java\main
        def classLoader = new URLClassLoader((new URL[]{files[0].toURI().toURL()}))
        def myClass = Class.forName("aa.bb.c.MyClass", true, classLoader);
        def myInstance = myClass.newInstance()
        Method method = myClass.getMethod("sayHello")
        method.invoke(myInstance)

Kotlin

  1. build your project files.
  2. run this groovy file and call class static method .
import java.net.URLClassLoader
...
task("helloworld") {
    doLast {
        val file: File = project.projectDir.toPath().resolve(tasks.jar.get().archiveFile.get().toString()).toFile()
        val classloader: ClassLoader = URLClassLoader(arrayOf(file.toURI().toURL()))
        Class.forName("mypackage.MyClass", true, classloader)
            .getMethod("sayHello").invoke(null)
    }
}
...

Solution 4:[4]

To run your custom Java class from Gradle build script (without JVM fork) you can include it in the buildscript classpath and then import it explicitly. Example:

buildscript {
    ext {
        ownRepo = "${System.properties['user.home']}/.gradle/local"
    }
    // Repositories for the build script.
    repositories {
        ivy {
            url ownRepo
        }
    }
    // Dependencies for the build script.
    dependencies {
        classpath "org:mytool:1.0"
    }
}

import org.mytool.Checksums
task calculateChecksums() {
    doLast {
        new Checksums().calculate(myAppDir)
    }
}

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2 Espinosa
Solution 3
Solution 4 Dimitar II