Moshi Retrofit2 Kotlin Class Not Found Exception - kotlin

I'm trying to learn how to implement Retrofit2 and Moshi inside the Kotlin programming language. However, I seem to be having trouble trying to compile my code.
I define the following data classes/models which map to the json response I receive from the api I am hitting:
#JsonClass(generateAdapter = true)
data class Catalogs(
val languages: List<LanguageCatalog>
)
#JsonClass(generateAdapter = true)
data class LanguageCatalog(
val direction: String,
val identifier: String,
val title: String,
val resources: List<ResourceCatalog>
)
#JsonClass(generateAdapter = true)
data class Project(
val identifier: String,
val sort: Int,
val title: String,
val versification: String?
)
#JsonClass(generateAdapter = true)
data class ResourceCatalog(
val identifier: String,
val modified: String,
val projects: List<Project>,
val title: String,
val version: String
)
Then I have an interface which defines the behavior for the API:
interface Door43Service
{
#GET("v3/catalog.json")
fun getFormat() : Observable<Catalogs>
companion object
{
fun create(): Door43Service
{
val retrofit = Retrofit.Builder()
.addCallAdapterFactory(
RxJava2CallAdapterFactory.create()
)
.addConverterFactory(
MoshiConverterFactory.create()
)
.baseUrl("https://api.door32.org/")
.build()
return retrofit.create(Door43Service::class.java)
}
}
}
Lastly, I implemented everything inside a main function to get the json data from the api:
val door43Service by lazy {
Door43Service.create()
}
var disposable: Disposable? = null
fun main(args: Array<String>)
{
door43Service.getFormat()
.subscribe(
{ result -> println(result.languages)},
{ error -> println(error.message)}
)
}
The data that gets returned from the api is pretty long, but an example of it can be found at http://api-info.readthedocs.io/en/latest/door43.html
My issue is I am getting the following error in my stack:
Exception in thread "main" java.lang.IllegalArgumentException: Unable to create converter for class model.Catalogs
for method Door43Service.getFormat
at retrofit2.ServiceMethod$Builder.methodError(ServiceMethod.java:755)
at retrofit2.ServiceMethod$Builder.createResponseConverter(ServiceMethod.java:741)
at retrofit2.ServiceMethod$Builder.build(ServiceMethod.java:172)
at retrofit2.Retrofit.loadServiceMethod(Retrofit.java:170)
at retrofit2.Retrofit$1.invoke(Retrofit.java:147)
at com.sun.proxy.$Proxy0.getFormat(Unknown Source)
at MainKt.main(main.kt:11)
Caused by: java.lang.RuntimeException: Failed to find the generated JsonAdapter class for class model.Catalogs
at com.squareup.moshi.StandardJsonAdapters.generatedAdapter(StandardJsonAdapters.java:249)
at com.squareup.moshi.StandardJsonAdapters$1.create(StandardJsonAdapters.java:62)
at com.squareup.moshi.Moshi.adapter(Moshi.java:130)
at retrofit2.converter.moshi.MoshiConverterFactory.responseBodyConverter(MoshiConverterFactory.java:91)
at retrofit2.Retrofit.nextResponseBodyConverter(Retrofit.java:330)
at retrofit2.Retrofit.responseBodyConverter(Retrofit.java:313)
at retrofit2.ServiceMethod$Builder.createResponseConverter(ServiceMethod.java:739)
... 5 more
Caused by: java.lang.ClassNotFoundException: model.CatalogsJsonAdapter
at java.net.URLClassLoader.findClass(URLClassLoader.java:381)
at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:335)
at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
at java.lang.Class.forName0(Native Method)
at java.lang.Class.forName(Class.java:348)
at com.squareup.moshi.StandardJsonAdapters.generatedAdapter(StandardJsonAdapters.java:236)
At first glance, my understanding is that the compiler thinks I haven't defined an adapter for my Catalogs class, but I think that's supposed to be covered using the #JsonClass(generateradapter = true) annotation. Is there anything I'm missing? Why can't my program generate the adapter for my Catalogs class?

So I got things working. All I had to do was rebuild the project by running gradle build in the terminal (I was originally running it in IntelliJ, but that didn't seem to actually run the build). The key issue was since the build wasn't running, the line in my gradle build script that says,
kapt "com.squareup.moshi:moshi-kotlin-codegen:$moshi_version"
wasn't running. This line basically gives the compiler to understand annotations in the code like #JsonClass. Without this line, the compiler won't understand the annotation. This was the root cause of my issue. I am keeping this post up in case anyone runs into the same issue.

Related

access property of a data class dynamically like Obj[propertyName]

I am learning Kotlin (coming from JS/TS background) and wondering if it is possible to access properties of a data class dynamically. For e.g.
data class Obj (private val name: String){}
val obj = Obj(name="sel")
would it be possible to do now:
val getValueOfField = "name"
println(obj[getValueOfField])
When I tried, it failed but also noticed type inference is an issue. When I did:
Obj::class.declaredMemberProperties.contains(getValueOfField)
to check if the field exists, it still failed to compile due to data inference issue.
No dynamic access is available without using reflection as the return type of such an operation would be unknown and so type safety would be lost.
You can do destructuring though which is almost as flexible:
data class Name (
val firstName: String,
val secondName: String
){}
fun main() {
val (name, _) = Name("Nie", "Selam")
println("$name is learning Kotlin") // prints Nie is learning Kotlin
}
See Kotlin playground.

Gradle lazy Exec-Task configuration

I'm running into a problem when configuring an Exec-Task using an Extension-Property.
Problem
The configuration of my Exec-Task relies on a String-Property that is defined in an extension. Unfortunately, the property is not set yet, when configuring the Exec-Task. This leads to an TaskCreationException:
Could not create task ':myTask'.
org.gradle.api.internal.tasks.DefaultTaskContainer$TaskCreationException: Could not create task ':myTask'.
...
Caused by: org.gradle.api.internal.provider.MissingValueException: Cannot query the value of extension 'myConfig' property 'command' because it has no value available.
at org.gradle.api.internal.provider.AbstractMinimalProvider.get(AbstractMinimalProvider.java:86)
Example
abstract class ConfigExtension() {
abstract val command: Property<String>
}
class MyGradlePlugin : Plugin<Project> {
override fun apply(project: Project) {
val myConfig = project.extensions.create(
"myConfig",
ConfigExtension::class.java
)
val myTask = project.tasks.register(
"myTask",
Exec::class.java
) {
it.commandLine(myConfig.command.get())
}
}
}
The problem seems to be the myConfig.command.get() which circumvents the lazy evaluation.
Test
#Test fun `plugin registers task`() {
// Create a test project and apply the plugin
val project = ProjectBuilder.builder().build()
project.plugins.apply("com.example.plugin")
// Verify the result
assertNotNull(project.tasks.findByName("myTask"))
}
Question
Is there a way to configure the commandLine-Value in a lazy manner like gradle tasks should be configured? [1] [2]

java.lang.ExceptionInInitializerError in self-referential objects?

I'm having problems with the jvm compiler.
I'm trying to write a factory method for classes. The factory method has an init() block that helps to define behaviour for the new object. While this method compiles for JVM, I encounter a problem when running it:
java.lang.ExceptionInInitializerError
Caused by: java.lang.IllegalArgumentException: Parameter specified as non-null is null: method type.ProblematicKt.nullable, parameter $this$nullable
Apparently, the object isn't yet defined when I attempt to run the problematicInit() block. How do I fix this?
Seems to be a JVM problem. It seems to work for Javascript compilations. My understanding was that getProblematic would be hoisted, but what's inside the scope would be deferred until it's run designed to be run later - after the factory method is completed.
interface ProblematicBuilderScope {
fun problematicInit(getX: () -> ProblematicInterface)
}
fun getProblematic() = X
class Problematic(...): ProblematicInterface
// Factory method with init() block
val X = Problematic.factory(...) {
problematicInit{ getProblematic() }
}
fun factory(init: ProblematicBuilderScope.() -> Unit): Problematic {
val newObject = Problematic(...)
val scope = ProblematicBuilderScope(newObject)
scope.init()
return newObject
}
here is a cleaner simpler way to achieve the same builder implementation
interface ProblematicInterface
class Problematic(): ProblematicInterface
fun buildProblematic(init: Problematic.() -> Unit): Problematic {
val newObject = Problematic()
init(newObject)
return newObject
}
val x = buildProblematic {
// this object type inside this clouse is Problematic
}

Kotlin Iterable not supported in Apache Beam?

Apache beam seems to be refusing to recognise Kotlin's Iterable. Here is a sample code:
#ProcessElement
fun processElement(
#Element input: KV<String, Iterable<String>>, receiver: OutputReceiver<String>
) {
val output = input.key + "|" + input.value.toString()
println("output: $output")
receiver.output(output)
}
I get the following weird error:
java.lang.IllegalArgumentException:
...PrintString, #ProcessElement processElement(KV, OutputReceiver), #ProcessElement processElement(KV, OutputReceiver):
#Element argument must have type org.apache.beam.sdk.values.KV<java.lang.String, java.lang.Iterable<? extends java.lang.String>>
Sure enough, if I replace Iterable with java.lang.Iterable, the same code works just fine. What am I doing wrong?
Version of depedencies:
kotlin-jvm: 1.3.21
org.apache.beam: 2.11.0
Here is a gist with full codes and stack trace:
https://gist.github.com/marcoslin/e1e19afdbacac9757f6974592cfd8d7f#file-apache-beam-iterable-notworking-kt
Update:
After a bit of trial and error, I found out that while List<String> throws similar exception but MutableList<String> actually works:
class PrintString: DoFn<KV<String, MutableList<String>>, String>() {
#ProcessElement
fun processElement(
#Element input: KV<String, MutableList<String>>, receiver: OutputReceiver<String>
) {
val output = input.key + "|" + input.value.toString()
println("output: $output")
receiver.output(output)
}
}
So, this reminded me that Kotlin's Immutable collection are actually only interface and that underlying collection is still mutable. However, attempt to replace Iterable with MutableIterable continue to raise the error.
Update 2:
I deployed my Kotlin Dataflow job using the MutableList per above and job failed with:
java.lang.RuntimeException: org.apache.beam.sdk.util.UserCodeException: java.lang.ClassCastException:
org.apache.beam.runners.dataflow.worker.util.BatchGroupAlsoByWindowViaIteratorsFn$WindowReiterable cannot be cast to java.util.List
at org.apache.beam.runners.dataflow.worker.GroupAlsoByWindowsParDoFn$1.output(GroupAlsoByWindowsParDoFn.java:184)
at org.apache.beam.runners.dataflow.worker.GroupAlsoByWindowFnRunner$1.outputWindowedValue(GroupAlsoByWindowFnRunner.java:102)
I had to switch back to use java.lang.Iterable.
I ran into this problem as well, when using a ParDo following a GroupByKey. It turns out that a #JvmWildcard annotation is needed in the Iterable generic type when writing a transformation that accepts the result of a GroupByKey.
See the contrived example below that reads a file and groups by the first character of each line.
class BeamPipe {
class ConcatLines : DoFn<KV<String, Iterable<#JvmWildcard String>>, KV<String, String>>() {
#ProcessElement
fun processElement(#Element input: KV<String, Iterable<#JvmWildcard String>>, receiver: OutputReceiver<KV<String, String>>) {
receiver.output(KV.of(input.key, input.value.joinToString("\n")))
}
}
fun pipe(options: PipelineOptions) {
val file =
"testFile.txt"
val p = Pipeline.create(options)
p.apply(TextIO.read().from(file))
.apply("Key lines by first character",
WithKeys.of { line: String -> line[0].toString() }
.withKeyType(TypeDescriptors.strings()))
.apply("Group lines by first character", GroupByKey.create<String, String>())
.apply("Concatenate lines", ParDo.of(ConcatLines()))
.apply("Write to files", FileIO.writeDynamic<String, KV<String, String>>()
.by { it.key }
.withDestinationCoder(StringUtf8Coder.of())
.via(Contextful.fn(ProcessFunction { it.value }), TextIO.sink())
.to("whatever")
.withNaming { key -> FileIO.Write.defaultNaming(key, ".txt") }
)
p.run()
}
}
This looks like a bug in the Beam Kotlin SDK. The Reflection analysis for your #ProcessElement method does not work correctly. You can probably work around this by using ProcessContext ctx instead of using the #Element parameter.
I am not very familiar with kotlin but it seems that you need to import import java.lang.Iterable before using it in your code.
May I know how to fix the issue when we get the iterable from groupbykey.create(). i could not groupbykey as you did javalang iterable
For those are experiencing this issue and found their way here, my current workaround to keep writing the pipeline in kotlin is to create a Java static class with function(s) that creates, contains, and processes your Iterable(s). The result (in non-iterable format) can then be passed back to the kotlin.

Error in TypeReference in Kotlin Jackson Mapper

I've got the following data class
data class MyResponse<T>(val header: String,
val body: T)
I want to write a generic Kotlin function that can deserialise json to various MyResponse<SimpleBody> or MyResponse<ComplexBody>
class JsonSerialiser {
val mapper = jacksonObjectMapper()
fun <T> fromJson(message: String, classz: Class<T>): T {
return mapper.readValue(message, classz)
}
}
val response: MyResponse<SimpleBody> = jsonSerialiser.fromJson("""{"header":"myheader", "body":{"simpleBody":{"name": "A"}}}""", MyResponse::class.java)
data class SimpleBody(val name: String)
It failed to compile with this error message
Kotlin: Type inference failed. Expected type mismatch: inferred type is MyResponse<*> but MyResponse<SimpleBody> was expected
Anyone knows how I can get this to work? I could use MyResponse<*> and then cast it, but I don't feel that's the right way.
Thanks & regards
Tin
You don't need this, just use com.fasterxml.jackson.module.kotlin.readValue:
val response: MyResponse<SimpleBody> = jacksonObjectMapper().readValue("""{"header":"myheader", "body":{"name": "A"}}""")
Please note that I changed the json a bit so that Jackson can parse it without further modifications