Kotlin all-open compiler plugin doesn't work - kotlin

I use Realm and it requires open keyword to it's model classes.
Following https://blog.jetbrains.com/kotlin/2016/12/kotlin-1-0-6-is-here/,
I tried to use all-open compiler plugin to remove the open keyword from Realm model classes.
First, I added all-open compiler plugin and set the package name of annotation
buildscript {
dependencies {
classpath "org.jetbrains.kotlin:kotlin-allopen:$kotlin_version"
}
}
apply plugin: "kotlin-allopen"
allOpen {
annotation("com.mycompany.myapp.annotation")
}
Second, I generated annotation
package com.mycompany.myapp.annotation
annotation class AllOpenAnnotation
Finally, I added the annotation to Realm model class
#AllOpenAnnotation
class Model {
var id: Int = -1,
var title: String = "",
var desc: String? = null
}: RealmObject()
But the error: cannot inherit from final Model error occurs.
Is there something that I did wrong?

You need to add the name of the annotation to the path in your config file:
allOpen {
annotation("com.mycompany.myapp.annotation.AllOpenAnnotation")
}

Related

Null property provided by Gradle when using custom plugin

I'm trying to follow the Gradle custom plugin documentation to create a plugin that can be configured.
My plugin code:
interface MyExtension {
var myValue: Property<String>
}
class MyPlugin : Plugin<Project> {
override fun apply(project: Project) {
val extension = project.extensions.create<MyExtension>("myExt")
}
}
in build.gradle.kts:
plugins {
`java-library`
}
apply<MyPlugin>()
the<MyExtension>().myValue.set("some-value")
Running this will give
Build file '<snip>/build.gradle.kts' line: 6
java.lang.NullPointerException (no error message)
Turns out the the<MyExtension>().myValue is null, so the set call fails. How do I do this correctly? Did I miss something in the documentation, or is it just wrong?
The documentation is not wrong. Properties can be managed by either you or by Gradle. For the latter, certain conditions have to be met.
Without managed properties
If you want to be completely in charge, you can instantiate any variables you declare yourself. For example, to declare a property on an extension that is an interface, it could look like this:
override fun apply(project: Project) {
val extension = project.extensions.create("myExt", MyExtension::class.java)
extension.myValue = project.objects.property(String::class.java)
}
Or you could instantiate it directly in the extension by making it a class instead:
open class MessageExtension(objects: ObjectFactory) {
val myValue: Property<String> = objects.property(String::class.java)
}
However, a property field is not really supposed to have a setter as the property itself has both a setter and a getter. So you should generally avoid the first approach and remove the setter on the second.
See here for more examples on managing the properties yourself.
With managed properties
To help you reduce boilerplate code, Gradle can instantiate the properties for you with what is called managed properties. To do use these, the property must not have a setter, and the getter should be abstract (which it implicitly is on an interface). So you could go back to your first example and fix it by changing var to val:
interface MyExtension {
val myValue: Property<String> // val (getter only)
}
Now Gradle will instantiate the field for you. The same thing works for abstract classes.
Read more about managed properties in the documentation here.

Ktor - create List from Json file

i am getting error - This class does not have constructor at object : TypeToken<List<Todo>>() + object is not abstract and does not implement object member
data class Todo(
val identifier: Long ,
val name: String ,
val description: String
)
class DefaultData {
private lateinit var myService: MyService
#PostConstruct
fun initializeDefault() {
val fileContent = this::class.java.classLoader.getResource("example.json").readText()
val todos: List<Todo> = Gson().fromJson(fileContent, object : TypeToken<List<Todo>>() {}.type)
myService.createTodoFromJsontodos
}
}
how can I fix this?
Objective is : To be able to create an endpoint that can get data from json file via service
Is there is a full fledged example
Also how to create interfaces in Ktor? As I want to use Dependency Inversion to enable retrieving data from different sources
Kotlin has built-in util similar to TypeToken, so I suggest using it instead:
Gson().fromJson(fileContent, typeOf<List<Todo>>().javaType)
You will need to add a dependency to kotlin-reflect. typeOf() function is marked as experimental, but I use it for some time already and never had any problems with it.
Also, you said in your comment that this is a starter project. If you don't have any existing code already then I suggest to use kotlinx-serialization instead of Gson. It is a de facto standard in Kotlin.
You can easily take advantage of kotlinx-serialization.
Steps:
Add the kotlin serialization plugin in your build.gradle file
kotlin("plugin.serialization") version "1.5.20"
plugins {
application
java
id("org.jetbrains.kotlin.jvm") version "1.5.21"
kotlin("plugin.serialization") version "1.5.20"
}
Add the dependecy for serialization library
dependencies {
...
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.2.2")
}
Decode your json string to corresponding object using Json decode method
val JSON = Json {isLenient = true}
val mytodos = JSON.decodeFromString(message) as List<Todo>

How to access `JavaToolchainSpec` from within custom Gradle task

According to "Toolchains for plugin authors" it should be possible to access the configured JavaToolchainSpec from within a custom task. I try to use this approach within a custom plugin which creates a task based on the presence of the JavaPlugin and queries the configured languageVersion property. Here is a minimal example.
build.gradle
plugins {
id 'application'
id 'com.example.myplugin'
}
...
java {
toolchain {
languageVersion.set(JavaLanguageVersion.of(17))
}
}
MyPlugin.kt
class MyPlugin : Plugin<Project> {
override fun apply(target: Project) {
target.plugins.withType<JavaPlugin> {
target.tasks.create<MyTask>("mytask")
}
}
}
MyTask.kt
abstract class MyTask : DefaultTask() {
init {
val extension = project.extensions.getByType<JavaPluginExtension>();
val languageVersion = extension.toolchain.languageVersion.get();
...
}
}
Once Gradle creates MyTask and the languageVersion property is queried, the build fails with the following error.
Cannot query the value of property 'languageVersion' because it has no value available.
My guess is that I am accessing the extension too early and it has not set its values at this time. My question now is if there is a way to wire up the configured properties (ideally with lazy mechanisms) with the task.
apparently you have to configure the toolchain object in the java plugin extension to be able to use JavaToolchainService, using something like:
java {
toolchain {
languageVersion = JavaLanguageVersion.of(11)
}
}
if you don't do that Gradle will defaults to the current JVM and the whole toolchain API will be unavailable (leaving you with the error you reported)

ArrowKT - #optics annotation not generating code

How do you setup the Arrow dependencies for #optics annotation to actually work? No companion objects are generated for the data classes annotated with #optics.
If I'm not mistaken, this is an annotation processor, so it should be imported using kapt, however the documentation uses it as compile.
For arrow 0.10.0
apply plugin: 'kotlin-kapt'
def arrow_version = "0.10.1-SNAPSHOT"
dependencies {
implementation "io.arrow-kt:arrow-optics:$arrow_version"
implementation "io.arrow-kt:arrow-syntax:$arrow_version"
kapt "io.arrow-kt:arrow-meta:$arrow_version" // <-- this is the kapt plugin
}
then:
#optics data class Street(val number: Int, val name: String) {
companion object {} // <-- this is required
}
Everything is explained in the documentation, I don't know how I missed it
https://arrow-kt.io/docs/

how to configure build.gradle.kts to fix error "Duplicate JVM class name generated from: package-fragment"

I'm trying to follow this tutorial https://dev.to/tagmg/step-by-step-guide-to-building-web-api-with-kotlin-and-dropwizard and am instead writing my gradle.build file in Kotlin's DSL and am finding there is no direct mapping from Groovy to Kotlin and I'm now getting this error when running ./gradlew run:
(4, 1): Duplicate JVM class name 'dropwizard/tut/AppKt' generated from: package-fragment dropwizard.tut, package-fragment dropwizard.tut
plugins {
// Apply the Kotlin JVM plugin to add support for Kotlin on the JVM.
id("org.jetbrains.kotlin.jvm").version("1.3.31")
// Apply the application plugin to add support for building a CLI application.
application
}
repositories {
// Use jcenter for resolving dependencies.
// You can declare any Maven/Ivy/file repository here.
mavenCentral()
jcenter()
}
dependencies {
// Use the Kotlin JDK 8 standard library.
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
// Use the Kotlin test library.
testImplementation("org.jetbrains.kotlin:kotlin-test")
// Use the Kotlin JUnit integration.
testImplementation("org.jetbrains.kotlin:kotlin-test-junit")
compile("io.dropwizard:dropwizard-core:1.3.14")
}
application {
// Define the main class for the application
mainClassName = "dropwizard.tut.AppKt"
}
tasks.withType<Jar> {
manifest {
attributes["Main-Class"] = application.mainClassName
}
from({
configurations.runtimeClasspath.get().filter { it.name.endsWith("jar") }.map { zipTree(it) }
})
}
tasks.named<JavaExec>("run") {
args("server", "config/local.yaml")
}
I cannot tell (yet) why this happens but to work around it add #file:JvmName("SomethingUnique") to your JVM file. Note that renaming the file will not help and lead to the same error. Only changing the output name will resolve it.
The JVM only knows how to load classes, so the Kotlin-to-JVM compiler generates classes to hold top-level val or fun declarations.
When you have two similarly named files
// src/commonMain/kotlin/com/example/Foo.kt
package com.example
val a = 1
and
// src/jvmMain/kotlin/com/example/Foo.kt
package com.example
val b = 2
the kotlin-to-JVM compiler generates
package com.example;
public class FooKt {
public static final int a = 1;
}
and
public com.example;
public class FooKt {
public static final int b = 2;
}
Obviously, these two files can't coexist in the same JVM ClassLoader, hence the error message.
Solutions involve:
As #Fleshgrinder noted, adding a file-level JvmName annotation to at least one to override the derived name, FooKt.
Renaming files to be different where possible.
Moving top-level val and fun declarations from those files into other files so Kotlin does not need to create the FooKt class.
Moving top-level val and fun declarations into objects or companion objects.