I try to write a gradle task with kotlin, this is my code.:
GreetingTask.kt
class GreetingTask : DefaultTask() {
#TaskAction
fun greet() {
println("greet!")
}
}
build.gradle
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:0.12.613"
}
}
apply plugin: "kotlin"
dependencies {
compile "org.jetbrains.kotlin:kotlin-stdlib:0.12.613"
compile gradleApi()
}
GreetingTaskTest
class GreetingTaskTest {
#Test
public fun canAddTaskToProject() {
val project = ProjectBuilder.builder().build()
val task = project.task(hashMapOf("type" to javaClass<GreetingTask>()), "greeting")
assertTrue(task is GreetingTask)
}
}
When running the test now it results in a:
java.lang.VerifyError at GreetingTaskTest.kt:20
// reason -> Cannot inherit from final class
Which is this line:
val task = project.task(hashMapOf("type" to javaClass<GreetingTask>()), "greeting")
What i would like to know is:
Where does this issue come from and how do i fix it?
Classes in Kotlin are final by default, compared to open in java.
Declare the class GreetingTask as "open" and this error message is gone.
open class GreetingTask : DefaultTask() {
...
}
Related
I created a small mqtt application using eclipse paho mqtt library in kotlin with Gradle in Intellij IDE. it runs fine when running it through Intellij but when I build it and run the jar file that gets created I get a NoClassDefFoundError error.
From other questions I have seen about this it looks like it has something to do with the class path but I am not sure what needs to be done if that is indeed the issue because I am using gradle and not jar files for libraries.
I was following this tutorial
Here is my gradle file
plugins {
id 'org.jetbrains.kotlin.jvm' version '1.4.31'
id 'application'
}
group = 'me.package'
version = '1.0-SNAPSHOT'
repositories {
mavenCentral()
maven {
url "https://repo.eclipse.org/content/repositories/paho-snapshots/"
}
}
dependencies {
implementation 'org.eclipse.paho:org.eclipse.paho.client.mqttv3:1.2.5'
testImplementation 'org.jetbrains.kotlin:kotlin-test-junit'
}
test {
useJUnit()
}
compileKotlin {
kotlinOptions.jvmTarget = '1.8'
}
compileTestKotlin {
kotlinOptions.jvmTarget = '1.8'
}
application {
mainClassName = 'com.publisher.MainKt'
}
tasks.jar {
manifest {
attributes 'Main-Class': 'com.publisher.MainKt'
}
from {
configurations.compile.collect {
it.isDirectory() ? it : zipTree(it)
}
}
}
And my MainKt file
package com.publisher
import org.eclipse.paho.client.mqttv3.*
import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence
import java.io.File
fun main(args: Array<String>) {
val client = MqttClient("tcp://192.168.0.55:1883","publisher", MemoryPersistence())
val connOpts = MqttConnectOptions()
connOpts.isCleanSession = false
connOpts.isAutomaticReconnect = true
client.setCallback(object: MqttCallback {
override fun connectionLost(cause: Throwable?) {
println("Connection lost")
println(cause!!.message)
}
override fun messageArrived(topic: String?, message: MqttMessage?) {
println("Message Received for topic: $topic")
println("Message: ${message!!.payload}")
}
override fun deliveryComplete(token: IMqttDeliveryToken?) {
println("Message delivered")
}
})
try{
client.connect(connOpts)
println("Connected")
client.subscribe("config/+", 1) { topic, message ->
println("Getting configuration for $message")
val path = System.getProperty("user.dir")
val file = File("$path/${message}.json")
if(file.exists()){
client.publish("/devices/ + $message + /config", MqttMessage(file.readBytes()))
}
}
}catch (e: MqttException){
println("Error: ${e.localizedMessage}")
e.printStackTrace()
}
}
The way you start your application does not include the dependencies, meaning your MQTT driver and the Kotlin dependencies are not included.
Do the following:
gradle distZip
# alternatively
gradle distTar
This will create a zip/tar file containing all the dependencies and a start script. Use that to start your application.
You could consider the Shadow plugin, as it is straightforward to use. Your build.gradle would look something like this:
plugins {
id 'org.jetbrains.kotlin.jvm' version '1.4.31'
// Shadow plugin
id 'com.github.johnrengelman.shadow' version '6.1.0'
id 'java'
}
group = 'me.package'
version = '1.0-SNAPSHOT'
repositories {
mavenCentral()
maven {
url "https://repo.eclipse.org/content/repositories/paho-snapshots/"
}
}
dependencies {
implementation 'org.eclipse.paho:org.eclipse.paho.client.mqttv3:1.2.5'
testImplementation 'org.jetbrains.kotlin:kotlin-test-junit'
}
test {
useJUnit()
}
compileKotlin {
kotlinOptions.jvmTarget = '1.8'
}
compileTestKotlin {
kotlinOptions.jvmTarget = '1.8'
}
application {
mainClassName = 'com.publisher.MainKt'
}
tasks.jar {
manifest {
attributes 'Main-Class': 'com.publisher.MainKt'
}
}
So your fat JAR is generated in the /build/libs directory with all the dependencies included.
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.
I have a Spring Boot Java project that builds using Gradle (v6.2.2).
build.gradle.kts
plugins {
base
java
id("org.springframework.boot") apply false
}
val gradleVersionProperty: String by project
val javaVersion: String by project
val springBootVersion: String by project
val springCloudStarterParentBomVersion: String by project
if (JavaVersion.current() != JavaVersion.VERSION_11) {
throw GradleException("This build must be run with JDK 11")
} else {
println("Building with JDK " + JavaVersion.current())
}
tasks.withType<Wrapper> {
gradleVersion = gradleVersionProperty
distributionType = Wrapper.DistributionType.ALL
}
allprojects {
group = "com.meanwhile.in.hell"
version = "$version"
// Repos used in dependencyManagement section of settings.gradle
repositories {
mavenLocal()
mavenCentral()
maven("https://repo.spring.io/snapshot")
maven("https://repo.spring.io/milestone")
}
}
subprojects {
if (!project.name.startsWith("platform")) {
apply {
plugin("java-library")
}
java.sourceCompatibility = JavaVersion.VERSION_11
java.targetCompatibility = JavaVersion.VERSION_11
// Change the default test logging settings
tasks.withType<Test>() {
useJUnitPlatform()
testLogging {
events = setOf(
org.gradle.api.tasks.testing.logging.TestLogEvent.FAILED,
org.gradle.api.tasks.testing.logging.TestLogEvent.PASSED,
org.gradle.api.tasks.testing.logging.TestLogEvent.SKIPPED
)
exceptionFormat = org.gradle.api.tasks.testing.logging.TestExceptionFormat.FULL
}
enableAssertions = false
ignoreFailures = gradle.startParameter.isContinueOnFailure
maxParallelForks =
(Runtime.getRuntime().availableProcessors() / 2).takeIf { it > 0 } ?: 1
}
dependencies {
"api"(enforcedPlatform("org.springframework.boot:spring-boot-dependencies:$springBootVersion"))
"api"(enforcedPlatform("org.springframework.cloud:spring-cloud-dependencies:$springCloudStarterParentBomVersion"))
"implementation"(enforcedPlatform(project(":platform-dependencies")))
"testCompile"("org.springframework.boot:spring-boot-starter-test")
}
}
}
However, I would like to add support for Spring Boot Kotlin sub-projects within it. I have used a very simple sample project from a Kotlin-only project I have that builds fine within it. Without any changes to my root build.gradle.kts file, my current build error is:
* What went wrong:
Execution failed for task ':kotlin-sample-project:bootJar'.
> Main class name has not been configured and it could not be resolved
I have not configured the main class for any of the Java sub-projects and neither have I in my Kotlin-only other project.
My build.gradle.kts in kotlin-sample-project is very simple:
plugins {
id("org.springframework.boot")
}
dependencies {
implementation("org.springframework.cloud:spring-cloud-starter-gateway")
}
And my main class looks like:
src/main/kotlin/sample/KotlinSampleApplication.kts
package com.meanwhile.in.hell.kotlinsample
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.runApplication
#SpringBootApplication
open class KotlinSampleApplication
fun main(args: Array<String>) {
runApplication<KotlinSampleApplication>(*args)
}
I have tried to add the kotlin plugin, but the build fails instantly not knowing what it is.
plugins {
base
java
kotlin
}
Error:
Line 9: kotlin
^ Unresolved reference. None of the following candidates is applicable because of receiver type mismatch:
public val <T : Any> Class<TypeVariable(T)>.kotlin: KClass<TypeVariable(T)> defined in kotlin.jvm
I have tried to add the kotlin plugin, but the build fails instantly not knowing what it is.
It should be:
build.gradle.kts:
plugins {
kotlin("jvm").version("1.3.72")
}
dependencies {
implementation(kotlin("stdlib-jdk8"))
}
Bot Kotlin JVM plugin should be applied and Kotlin stdlib be present on compile classpath.
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
I converted simple Kitlon file into Library, the file is:
Display.kt:
package hello
fun main(args: Array<String>) {
println("Hello World!")
}
had been compiled into library using the command:
kotlinc Display.kt -d Display.jar
The output had been cross checked to be worked using the command:
kotlin -classpath Display.jar hello.DisplayKt
Then I moved it to folder src/main/resources, then tried calling it from another app, using the below code:
Hello.kt:
package hello
import hello.DisplayKt
fun main(args: Array<String>) {
println("Hi")
}
and defined the build.gradle file as below (tried to put all option I read about to solve my case):
// set up the kotlin-gradle plugin
buildscript {
ext.kotlin_version = '1.1.2-2'
repositories {
mavenCentral()
}
dependencies {
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}
// apply the kotlin-gradle plugin
apply plugin: "kotlin"
// add kotlin-stdlib dependencies.
repositories {
mavenCentral()
}
dependencies {
//dependencies from a remote repositor
compile "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
//local file
compile files('src/main/resources/Display.jar')
compile fileTree(dir: 'src/main/resources', include: '*.jar')
}
jar {
manifest {
//Define mainClassName as: '[your_namespace].[your_arctifact]Kt'
attributes ('Main-Class': 'hello.HelloKt', "Implementation-Title": "Gradle",
"Implementation-Version": 1)
}
// NEW LINE HERE !!!
from { configurations.compile.collect { it.isDirectory() ? it : zipTree(it) } }
}
sourceSets {
main {
java {
srcDirs = ['src/kotlin']
}
resources {
srcDirs = ['src/resources']
}
}
}
but, after running gradle build command, I got the below error:
Unresolved reference: DisplayKt
Note: I'm very very new to JAVA/KOTLIN and GRADLE
I found the answer, the full path of the function is hello.main