How do I call a static Java method from Gradle - java

I have a gradle build script which currently works by simply executing a Java class through it's main method. What I want to know is, how can I call a static method in the same class but not have to go through the main method. The current gradle code is as follows:
import org.apache.tools.ant.taskdefs.condition.Os
apply plugin: 'java'
defaultTasks 'runSimple'
project.ext.set("artifactId", "test-java")
File rootDir = project.getProjectDir()
File targetDir = file("${rootDir}/target")
FileCollection javaClasspath = files("${targetDir}/tools.jar")
task(runSimple, dependsOn: 'classes', type: JavaExec) {
main = 'com.test.model.JavaTest'
classpath = javaClasspath
args 'arg1'
args 'arg2'
}
And my Java class as follows:
package com.test.model;
public class JavaTest {
public static void main(String[] args) throws Exception {
System.out.println("In main");
anotherMethod(args[0], args[1]);
}
public static void anotherMethod(String arg1, String arg2) {
System.out.println("In anotherMethod");
System.out.println(arg1 + " " + arg2);
}
}
This gives me the output:
:compileJava UP-TO-DATE
:processResources UP-TO-DATE
:classes UP-TO-DATE
:runSimple
In main
In anotherMethod
arg1 arg2
BUILD SUCCESSFUL
Total time: 2.344 secs
My question is simply how can I skip the main method, and call the method "anotherMethod" directly from the gradle script? The output would then simply be:
In anotherMethod
arg1 arg2
Thanks

you have to add the jar or class to the classpath. here is an example with a jar file who contains the class.
Inside the file build.gradle add the dependencies.
My jar file is in the lib folder the path is lib/MQMonitor.jar.
import mypackage.MyClass
buildscript {
repositories {
flatDir name: 'localRepository', dirs: 'lib'
}
dependencies {
classpath name: 'MQMonitor'
}
}
task myTaskCallJava << {
MyClass.foo()
}

Assuming the class is on the buildscript classpath (it should be, since you're calling main from the same class)
task runSimple {
doLast {
com.test.model.JavaTest.anotherMethod("foo", "bar")
}
}
Tested on Gradle 4.6

I have been working on this too. You know I like the function of eclipse and intellij with the run with Junit options, and I want to do this using command line and gradle.
If you could accept putting your test method in the directory of 'test' directory of gradle. I actually have a fair solution.
package com.udacity.gradle;
import org.junit.Test;
public class TestClass {
#Test
public void anotherMethod() {
System.out.println("This is it, I want this!!!");
}
#Test
public void notMyWantedMethod1() {
System.out.println("not my wanted");
}
public void notMyWantedMethod2() {
System.out.println("not my wanted");
}
}
This my test class which is in src/test/java/com/udacity/gradle/TestClass.java
Then the under below is the file of my build.gradle
apply plugin: "java"
repositories {
mavenCentral()
}
dependencies {
testCompile group: 'junit', name: 'junit', version: '4.12'
}
test {
testLogging.showStandardStreams = true
filter {
//include specific method in any of the tests
includeTestsMatching "*.TestClass.anotherMethod"
}
}
Simple idea you know this is a test class so I use the test task of gradle. And to specify which method to use, I added a Test filter which could specify down to method.
Then you could just run
gradle test
Then you could find in console that you have what you want in there. However, remember to add
testLogging.showStandardStreams = true
if you don't do this, gradle would swallow your console output. But even if you don't add this line. You could read a test log in the directory of
...../build/test-results/test/TEST-com.udacity.gradle.TestClass.xml
There are well organized test reports output in it.
<?xml version="1.0" encoding="UTF-8"?>
<testsuite name="com.udacity.gradle.TestClass" tests="1" skipped="0" failures="0" errors="0" timestamp="2018-03-31T19:26:44" hostname="hexin-PC" time="0.022">
<properties/>
<testcase name="anotherMethod" classname="com.udacity.gradle.TestClass" time="0.022"/>
<system-out><![CDATA[This is it, I want this!!!
]]></system-out>
<system-err><![CDATA[]]></system-err>
</testsuite>

If you want to execute a static method you will need to add the class to the Gradle build script's classpath.
To add the code to the build scripts classpath if your code is in a repository:
buildscript {
repositories {
maven { url "${yourRepositoryURL}" }
}
dependencies {
classpath 'com.yourgroup:yourpackagename:version'
}
}
To add the code to the build scripts classpath if you code is built locally (I didn't test this one):
buildscript {
dependencies {
classpath files("path/to/where/the/class/files/are")
}
}
Then you should be able to call that method just like any other:
task runSimple(dependsOn: 'classes') {
doFirst() {
com.test.model.JavaTest.anotherMethod('arg1', 'arg2')
}
}

Related

Unable to read the application.yml from cucumber test

I have a cucumber test :
#RunWith(CucumberWithSerenity.class)
#UsePersistantStepLibraries
#CucumberOptions(
features = "src/integrationTest/resources/features/",
glue = {"com/myorg/proj/integrationtest/stepdefinitions"},
plugin = {"pretty", "summary"}
)
public class integrationTestRunner extends ParentClassWithAllMethods {
}
This is test is not defined inside the test folder , but as a seprate folder (as source set, and some one else had created this structure).
The gradle configuration for the sourceset (where the test is ) is as below:
sourceSets {
integrationTest {
java {
compileClasspath += main.output + test.output
runtimeClasspath += main.output + test.output
srcDir file('src/integrationTest/java')
}
resources.srcDir file('src/integrationTest/resources')
}
}
// Dependencies for integrationTest
dependencies {
integrationTestCompileOnly 'org.projectlombok:lombok:1.18.12'
integrationTestAnnotationProcessor 'org.projectlombok:lombok:1.18.12'
integrationTestCompile group: 'org.junit.jupiter', name: 'junit-jupiter-api', version: '5.4.2'
integrationTestImplementation 'net.serenity-bdd:serenity-core:2.2.13'
integrationTestImplementation 'net.serenity-bdd:serenity-cucumber5:2.2.5'
integrationTestImplementation 'net.serenity-bdd:serenity-rest-assured:2.2.13'
integrationTestImplementation 'org.assertj:assertj-core:3.8.0'
integrationTestImplementation 'org.slf4j:slf4j-simple:1.7.7'
}
task integrationTest(type: Test) {
doFirst {
systemProperty 'param1',
System.getProperty('param1')
param1 = System.getProperty('param1')
if (param1 == null)
throw new GradleException("Parameter not passed ./gradlew -DParam<VALUE> ")
}
testClassesDirs = sourceSets.integrationTest.output.classesDirs
classpath = sourceSets.integrationTest.runtimeClasspath
outputs.upToDateWhen { false }
finalizedBy aggregate
}
Looking at the gradle file, there 3 things going on ,
There is a source set defined as integrationTest
The compileCLassPath , runTimeClassPath, srcDir has been defined for the same
Even the resources.srcDir is also being set (where I have application ymls)
I have create application.yml, application-integrationTest.yml inside the resources folder, but still unable to read any proporties using #Value.
Ex:
public class ParentClassWithAllMethods {
#Value("${custom.parameter}")
private String customParameter;
public ResponseOptions<Response> getMethod() {
String debugVal = customParameter;
}
.....
}
When , running the test, I was expecting to get the value defined in my yml file, but its null.
YML file looks like this,
custom:
parameter: https://something.com
Why am I not able to read the application configuration? It's always null !!
Your test class is not starting Spring, that is why you get null. Have a look here https://www.baeldung.com/cucumber-spring-integration, it shows how to connect Spring and Cucumber in JUnit tests.
The test class would then have annotations for Spring and Cucumber like this:
#CucumberContextConfiguration
#SpringBootTest
public class SpringIntegrationTest {
// executeGet implementation
}

How to include the client from openapi-generator in a gradle java application?

I want to create a gradle java application that generates a client from an openAPI specification file and uses that client.
So I created a java application with gradle init (type:application, language:Java, DSL:groovy, test-framework:Junit Jupiter, project-name:simple-java-app, package-structure:a.aa).
Small example of what works:
I can create a new source folder second/loc/src/main/java with a package b.bb and a class Foo.
And with the following build.gradle
plugins {
id 'java'
id 'application'
}
repositories {
jcenter()
}
sourceSets {
second {
java {
srcDir 'second/loc/src/main/java'
}
}
}
compileJava {
source += sourceSets.second.java
}
dependencies {
implementation 'com.google.guava:guava:29.0-jre'
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.6.2'
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.6.2'
}
application {
mainClassName = 'a.aa.App'
}
test {
useJUnitPlatform()
}
The main class can access Foo:
package a.aa;
import b.bb.Foo;
public class App {
public static void main(String[] args) {
System.out.println(new Foo().sayFoo());
}
}
What doesn't work
Now I try the same for generated code by openapi-generator:
Under plugins I add id "org.openapi.generator" version "4.3.1"
And I add a new task:
openApiGenerate {
generatorName = "java"
inputSpec = "$rootDir/specs/petstore.yaml".toString()
outputDir = "$buildDir/generated".toString()
apiPackage = "org.openapi.example.api"
invokerPackage = "org.openapi.example.invoker"
modelPackage = "org.openapi.example.model"
configOptions = [
dateLibrary: "java8"
]
}
Then I execute the task openApiGenerate and confirm in the file system that the sources have been generated(eclipse won't show the build folder).
Now I use the same method as above resulting in below build.gradle
plugins {
id 'java'
id 'application'
id "org.openapi.generator" version "4.3.1"
}
repositories {
jcenter()
}
openApiGenerate {
generatorName = "java"
inputSpec = "$rootDir/specs/petstore.yaml".toString()
outputDir = "$buildDir/generated".toString()
apiPackage = "org.openapi.example.api"
invokerPackage = "org.openapi.example.invoker"
modelPackage = "org.openapi.example.model"
configOptions = [
dateLibrary: "java8"
]
}
sourceSets {
client {
java {
srcDir '$buildDir/generated/src/main/java'
}
}
}
compileJava {
source += sourceSets.client.java
}
dependencies {
implementation 'com.google.guava:guava:29.0-jre'
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.6.2'
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.6.2'
}
application {
mainClassName = 'a.aa.App'
}
test {
useJUnitPlatform()
}
But when I try to use the classes now:
package a.aa;
import org.openapi.example.model.Pet;
public class App {
public static void main(String[] args) {
Pet p = new Pet(0L);
System.out.println(p.getId());
}
}
neither import nor Pet can be resolved.
> Task :compileJava FAILED
C:\...\simple-java-app\src\main\java\a\aa\App.java:6: error: package org.openapi.example.model does not exist
import org.openapi.example.model.Pet;
^
C:\...\simple-java-app\src\main\java\a\aa\App.java:14: error: cannot find symbol
Pet p = new Pet(0);
^
symbol: class Pet
location: class App
C:\...\simple-java-app\src\main\java\a\aa\App.java:14: error: cannot find symbol
Pet p = new Pet(0);
^
symbol: class Pet
location: class App
3 errors
FAILURE: Build failed with an exception.
I don't know how to debug this, frankly I'm unsure if source sets are even the right way. All openapi-generator tutorials seem to use them, I haven't tried subprojects yet, the openApiGenerate task seems to create a complete project with build.gradle and everything.
You need to add the sources from the generated code to your project. One example from one of my projects:
sourceSets.main.java.srcDir "${buildDir}/generated/src/main/java"
After generation make sure you refresh gradle and project.
With build.gradle.kts and gradle 7+ I used in Kotlin DSL:
configure<SourceSetContainer> {
named("main") {
java.srcDir("$buildDir/generated/src/main/java")
}
}
To add the java generated sources to the project's source.

Using Scala class in Java test with Gradle

I have a project with the following files:
src/main/scala/package/Class.scala
src/test/java/package/ClassTest.java
When I run ./gradlew clean build test I get an error in :compileTestJava - it says "error: cannot find symbol" because apparently ClassTest.java cannot find Class.scala
I have the following in build.gradle which I think should work but doesn't:
sourceSets {
test {
java { srcDirs += ['src/main/scala', 'src/test/java'] }
}
}
I also have the following plugins for what it is worth:
plugins {
id 'java'
id 'scala'
id 'java-library'
id 'maven-publish'
id 'jacoco'
}
The only other things in build.gradle are repositories and dependencies
I have simulated your scenario locally. The below works with Gradle 6.5 and 'java' and 'scala' plugins. My main srcDirs are slightly different from yours but I do not think that is material.
build.gradle:
sourceSets
{
main
{
scala {
srcDirs = ['app/glue', 'app/controllers', 'app/models']
}
}
test {
java {
srcDirs = ['test/java']
}
scala {
srcDirs = ['test/scala']
}
}
}
Sample JUnit test which refers to a scala case class Form which is defined in the 'main' source set:
public class TestScalaFromJava
{
#Test
public void testNothing() { }
#Test
public void testScalaClassInJava1() {
Firm firm = new Firm(1, "hello", "mello", "yellow");
}
}
I can then run either of the below and they work:
./gradlew compileTestJava
./gradlew clean build test

pitest not able to locate junit test

My gradle pitest is not able to give me the right results. It looks like it is not able to locate my test files.
I have the following build.gradle file:
apply plugin: "java" apply plugin: "maven" apply plugin: "info.solidsoft.pitest"
group = "myorg" version = 1.0
repositories {
mavenCentral() }
sourceSets.all { set ->
def jarTask = task("${set.name}Jar", type: Jar) {
baseName = baseName + "-$set.name"
from set.output
}
artifacts {
archives jarTask
} }
sourceSets {
api
impl main{ java { srcDir 'src/api/java' srcDir 'src/impl/java' } } test { java { srcDir 'src/test/java' } } }
buildscript {
repositories {
mavenCentral()
//Needed only for SNAPSHOT versions
//maven { url "http://oss.sonatype.org/content/repositories/snapshots/" }
}
dependencies {
classpath 'info.solidsoft.gradle.pitest:gradle-pitest-plugin:1.1.6'
} }
dependencies {
apiCompile 'commons-codec:commons-codec:1.5'
implCompile sourceSets.api.output
implCompile 'commons-lang:commons-lang:2.6'
testCompile 'junit:junit:4.9'
testCompile sourceSets.api.output
testCompile sourceSets.impl.output
runtime configurations.apiRuntime
runtime configurations.implRuntime }
jar {
from sourceSets.api.output
from sourceSets.impl.output }
pitest { println sourceSets.main
targetClasses = ['doubler.*'] targetTests = ['doubler.*'] verbose="on" }
THe output is stored in the correct folder. And when I run gradle test, it also runs fine.
Some additional information about this issue was supplied in the pitest user group.
https://groups.google.com/forum/#!topic/pitusers/8C7BHh-Vb6Y
The tests being run look like this.
#Test
public void testIt2() {
assert new DoublerImpl().testIt(1) == 2;
}
Pitest is correctly reporting that these tests provide 0% coverage of the class. There is no coverage because the assert keyword has been used.
Unless the -ea flag is set in the JVM running the tests assertions are disabled. There is basically hidden if block around this code generated by the compiler
#Test
public void testIt2() {
if (assertionsEnabled) {
assert new DoublerImpl().testIt(1) == 2;
}
}
As assertions are not enabled no code is executed.
To fix the issue use the built in JUnit assertions instead.
http://junit.sourceforge.net/javadoc/org/junit/Assert.html

Android Studio Gradle: Execute static Java Method (Migration from ANT to Gradle)

I am trying to run the static main method of a java class from my build.gradle script asp art of the build process. I am using Android Studio 1.0.2 with the Android/Gradle Plugin 'com.android.tools.build:gradle:1.0.0'
The java class whose main method I want to run during the build resides in ...\trunk-gradle\myproject\src\main\java\de\myapp\gradle
package de.myapp.gradle;
public class ConfigureCustomer {
public static void main(String[] args){
String server = args[0];
String customer = args[1];
System.out.println(String.format("Configuring customer %s with server %s", customer, server));
}
}
Before I used ANT to call that java method as follows:
<java failonerror="yes" classname="de.myapp.gradle.ConfigureCustomer ">
<classpath>
<path location="${base.dir}/bin/classes/"/>
</classpath>
<arg line="${customer}"/>
<arg line="${server }"/>
</java>
But now I am migrating to Groovy, so here is the relevant part of my project's build.gradle file that tries to execute the main method of above class (actual task definition is at the end just before the dependencies):
apply plugin: 'com.android.application'
android {
project.ext.set("customer", "")
project.ext.set("server", "")
dexOptions {
preDexLibraries = false
}
compileSdkVersion 19
buildToolsVersion "21.1.2"
defaultConfig {
//Default configuration
}
signingConfigs {
release {
//Configuration for release builds
}
}
buildTypes {
debug{
server = "test"
}
release {
server = "release"
}
}
productFlavors {
customerA{
customer = "a"
}
customerB{
customer = "b"
}
customerC{
customer = "c"
}
}
}
task (configureCustomer, type: JavaExec) {
println 'Running customer configuration...'
main = 'de.myapp.gradle.ConfigureCustomer'
args customer, server
}
dependencies {
//Dependency settings
}
So now when I run the following via the command line (windows):
graldew configureCustomer
I get the following error message:
Error: Could not find or load main class
de.myapp.gradle.ConfigureCustomer
My questions hence are as follows:
How do I manage to fix the error message above? Do I have to move my java class to another folder? Maybe configure sth in the build scipt?
How can I make sure the java task is executed after the classes have actually been compiled?
If i wanted to execute the task configureCustomer as part of another task, would I simply write the following line in my gradle's task definition?
configureCustomer
I also tried to add the classpath:
task (configureCustomer, type: JavaExec) {
println 'Running customer configuration...'
main = 'de.myapp.gradle.ConfigureCustomer'
classpath = sourceSets.main.runtimeClasspath
args customer, server
}
But all that got me was a gradle build error message saying:
Could not find property "main" on SourceSet container
So apparently "sourceSets.main.runtimeClasspath" does not exist in Android Studio's Gradle. Maybe it's named differently. Though I also tried setting the classpath like this:
classpath = '${projectDir.getAbsolutePath()}\\build\\intermediates\\classes\\' + customer + '\\release'
and I also tried this:
classpath = '${projectDir.getAbsolutePath()}\\build\\intermediates\\classes\\' + customer + '\\release\\de\\myapp\\gradle'
None of which worked, the error from above persists:
Error: Could not find or load main class
de.myapp.gradle.ConfigureCustomer
I finally found something that works for Android/Gradle but getting there seemed a lot more complicated, than it should have been.
So for recap - here is the Java class whose main method I'd like to execute:
package de.myapp.gradle;
public class ConfigureCustomer {
public static void main(String[] args){
String customer = args[0];
String versionName = args[1];
System.out.println(String.format("Configuring customer %s with versionName %s", customer, versionName ));
}
}
I want to execute the above for each flavor and only for release builds (not debug builds) so here is my task definition (you'd still have to make your task depend on one of the gradle build tasks so it's executed - I am depending on the preBuild task for this purpose):
android {
//Build type setup, signing configuration and other stuff
}
//After the android block my task definition follows:
task buildPrintout(type: JavaExec) {
android.applicationVariants.all { variant ->
//Runt he java task for every flavor
variant.productFlavors.each { flavor ->
println "Triggering customer configuration for flavor " + flavor.name
if (variant.buildType.name.equals('release')) {
//Run the java task only for release builds
//Cant find the runtime classpath in android/gradle so I'll directly link to my jar file here. The jarfile contains the class I want to run (with the main method)
classpath += files("libs/my-jarfile.jar")
//This is the fully qualified name of my class, including package name (de.myapp.gradle) and the classname (ConfigureCustomer)
main = "de.myapp.gradle.ConfigureCustomer"
//Pass in arguments - in this case the customer's name and the version name for the app (from AndroidManifest.xml)
args flavor.name, variant.versionName
}
}
}
}
You'll notice that I dumped the idea of having my Class integrated in the android project I am about to build. Instead I made that one class a separate project, built a jar file and dropped it in the libs folder of the android project i am building.
UPDATE 04.02.2015
I have slightly modified the above to use the javaexec method instead of the JavaExec Task type:
preBuild.doFirst {
android.applicationVariants.all { variant ->
variant.productFlavors.each { flavor ->
if (variant.buildType.name.equals('release')) {
javaexec {
println "Triggering customer build for flavor " + flavor.name
classpath += files("libs/my-jarfile.jar")
main = "de.myapp.gradle.ConfigureCustomer"
args flavor.name, variant.versionName
}
println "Done building customer for flavor " + flavor.name
}
}
}
}
And here is yet another variation, where we define the javaexec within a reusable (which is preferred) task, that we then add as a dependency to the preBuild task:
//Define our custom task and add the closures as an action
task buildCustomer << {
android.applicationVariants.all { variant ->
variant.productFlavors.each { flavor ->
if (variant.buildType.name.equals('release')) {
javaexec {
println "Triggering customer build for flavor " + flavor.name
classpath += files("libs/my-jarfile.jar")
main = "de.myapp.gradle.ConfigureCustomer"
args flavor.name, variant.versionName
}
println "Done building customer for flavor " + flavor.name
}
}
}
}
//Make preBuild depend on our task
preBuild.dependsOn buildCustomer
If you have any questions let me know and I'll try to answer them.
Change the way of configing classpath
classpath(files('build/intermediates/classes/release',"${android.getSdkDirectory().getAbsolutePath() + '/platforms/' + android.compileSdkVersion + '/android.jar'}"))
It works on android gradle 1.5

Categories

Resources