Byte Buddy: Creating class by implementing interface leads to NoClassFoundException - kotlin

I am trying to create a class in a unit test that implements an Interface by using byte buddy
interface SomeInterface {}
class ByteBuddyTest {
#Test
fun byteBuddyTest(){
val instrumentation = ByteBuddyAgent.install()
val bb = ByteBuddy()
val loadedRestController = bb
.subclass(SomeInterface::class.java)
.make()
.load(Object::class.java.classLoader, ClassLoadingStrategy.Default.WRAPPER)
.loaded
}
}
Unfortunately I am getting a NoClassFoundException when trying to implement the Interface
org/example/SomeInterface
java.lang.NoClassDefFoundError: org/example/SomeInterface
at java.lang.ClassLoader.defineClass1(Native Method)
at java.lang.ClassLoader.defineClass(ClassLoader.java:756)
at net.bytebuddy.dynamic.loading.ByteArrayClassLoader.access$300(ByteArrayClassLoader.java:56)
at net.bytebuddy.dynamic.loading.ByteArrayClassLoader$ClassDefinitionAction.run(ByteArrayClassLoader.java:686)
at net.bytebuddy.dynamic.loading.ByteArrayClassLoader$ClassDefinitionAction.run(ByteArrayClassLoader.java:638)
at java.security.AccessController.doPrivileged(Native Method)
at net.bytebuddy.dynamic.loading.ByteArrayClassLoader.doPrivileged(ByteArrayClassLoader.java)
at net.bytebuddy.dynamic.loading.ByteArrayClassLoader.findClass(ByteArrayClassLoader.java:405)
at java.lang.ClassLoader.loadClass(ClassLoader.java:418)
at java.lang.ClassLoader.loadClass(ClassLoader.java:351)
at java.lang.Class.forName0(Native Method)
at java.lang.Class.forName(Class.java:348)
Am I missing something during agent initialization?

You are loading the class into a child loader of the boot loader by using
Object::class.java.classLoader
This class loader is of course unaware of SomeInterface. Instead, load the class into a child of:
SomeInterface::class.java.classLoader

Related

Creating TypeDescryption of Array in ByteBuddy

I want to create classes like this in Byte-Buddy:
class A{
public B b;
}
class B{
public A[] a_array;
}
this is my code on how I imagine it could be done:
InstrumentedType arrayType = InstrumentedType.Default.of("[A",
TypeDescription.Generic.Builder.rawType(Object.class).build(),
Modifier.PUBLIC);
DynamicType.Unloaded B_made = new ByteBuddy()
.subclass(Object.class)
.name("B")
.defineField("a_array", arrayType, Modifier.PUBLIC)
.make();
DynamicType.Unloaded A_made = new ByteBuddy()
.subclass(Object.class)
.name("A")
.defineField("b", B_made.getTypeDescription(), Modifier.PUBLIC).make();
Class A = A_made.include(B_made).load(Test.class.getClassLoader()).getLoaded();
Class B = A.getField("b").getType();
System.out.println(B.getField("a_array"));
But this does not work even though Byte-Buddy will make the classes and load the them.
This code gives me this when evaluating B.getField("a_array")
Exception in thread "main" java.lang.NoClassDefFoundError: L[A;
at java.lang.Class.getDeclaredFields0(Native Method)
at java.lang.Class.privateGetDeclaredFields(Class.java:2583)
at java.lang.Class.getField0(Class.java:2975)
at java.lang.Class.getField(Class.java:1701)
at Test.main(Test.java:88)
Caused by: java.lang.ClassNotFoundException: [A
at net.bytebuddy.dynamic.loading.ByteArrayClassLoader.findClass(ByteArrayClassLoader.java:396)
at java.lang.ClassLoader.loadClass(ClassLoader.java:418)
at java.lang.ClassLoader.loadClass(ClassLoader.java:351)
... 5 more
I was wondering how can I fix this?
is it even possible for me to create TypeDescription of [A from TypeDescription of A??
To construct an array type, use TypeDescription.Generic.Builder which allows for creating array representations. Do not use InstrumentedType.Default for creating arrays as they are intended to represent definable types.

How to use CDI field injection in Quarkus in Kotlin?

I'd like to inject beans a Kotlin field in Quarkus. The sample file looks like
package org.example
import com.google.inject.Inject
import javax.enterprise.inject.spi.BeanManager
import javax.ws.rs.GET
import javax.ws.rs.Path
#Path("injectDemo")
open class InjectDemo #Inject constructor(val bm1: BeanManager) {
#field:Inject
protected open lateinit var bm2: BeanManager
#GET
fun demo() {
println("bm1 $bm1")
println("bm2 $bm2")
}
}
The constructor parameter injection works fine however field bm2 remains uninitialized.
Console output:
bm1 io.quarkus.arc.impl.BeanManagerImpl#6b7ac97f
2020-05-21 03:45:11,670 ERROR [io.qua.ver.htt.run.QuarkusErrorHandler] (executor-thread-1) HTTP Request to /injectDemo failed, error id: ac118d6b-a26e-47e7-8c10-12e6a96e50ba-3: org.jboss.resteasy.spi.UnhandledException: kotlin.UninitializedPropertyAccessException: lateinit property bm2 has not been initialized
at org.jboss.resteasy.core.ExceptionHandler.handleApplicationException(ExceptionHandler.java:106)
at org.jboss.resteasy.core.ExceptionHandler.handleException(ExceptionHandler.java:372)
at org.jboss.resteasy.core.SynchronousDispatcher.writeException(SynchronousDispatcher.java:216)
at org.jboss.resteasy.core.SynchronousDispatcher.invoke(SynchronousDispatcher.java:515)
at org.jboss.resteasy.core.SynchronousDispatcher.lambda$invoke$4(SynchronousDispatcher.java:259)
at org.jboss.resteasy.core.SynchronousDispatcher.lambda$preprocess$0(SynchronousDispatcher.java:160)
at org.jboss.resteasy.core.interception.jaxrs.PreMatchContainerRequestContext.filter(PreMatchContainerRequestContext.java:362)
at org.jboss.resteasy.core.SynchronousDispatcher.preprocess(SynchronousDispatcher.java:163)
at org.jboss.resteasy.core.SynchronousDispatcher.invoke(SynchronousDispatcher.java:245)
at io.quarkus.resteasy.runtime.standalone.RequestDispatcher.service(RequestDispatcher.java:73)
at io.quarkus.resteasy.runtime.standalone.VertxRequestHandler.dispatch(VertxRequestHandler.java:123)
at io.quarkus.resteasy.runtime.standalone.VertxRequestHandler.access$000(VertxRequestHandler.java:36)
at io.quarkus.resteasy.runtime.standalone.VertxRequestHandler$1.run(VertxRequestHandler.java:87)
at org.jboss.threads.ContextClassLoaderSavingRunnable.run(ContextClassLoaderSavingRunnable.java:35)
at org.jboss.threads.EnhancedQueueExecutor.safeRun(EnhancedQueueExecutor.java:2046)
at org.jboss.threads.EnhancedQueueExecutor$ThreadBody.doRunTask(EnhancedQueueExecutor.java:1578)
at org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1452)
at org.jboss.threads.DelegatingRunnable.run(DelegatingRunnable.java:29)
at org.jboss.threads.ThreadLocalResettingRunnable.run(ThreadLocalResettingRunnable.java:29)
at java.base/java.lang.Thread.run(Thread.java:832)
at org.jboss.threads.JBossThread.run(JBossThread.java:479)
Caused by: kotlin.UninitializedPropertyAccessException: lateinit property bm2 has not been initialized
at org.example.InjectDemo.getBm2(InjectDemo.kt:12)
at org.example.InjectDemo.demo(InjectDemo.kt:17)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:564)
at org.jboss.resteasy.core.MethodInjectorImpl.invoke(MethodInjectorImpl.java:167)
at org.jboss.resteasy.core.MethodInjectorImpl.invoke(MethodInjectorImpl.java:130)
at org.jboss.resteasy.core.ResourceMethodInvoker.internalInvokeOnTarget(ResourceMethodInvoker.java:621)
at org.jboss.resteasy.core.ResourceMethodInvoker.invokeOnTargetAfterFilter(ResourceMethodInvoker.java:487)
at org.jboss.resteasy.core.ResourceMethodInvoker.lambda$invokeOnTarget$2(ResourceMethodInvoker.java:437)
at org.jboss.resteasy.core.interception.jaxrs.PreMatchContainerRequestContext.filter(PreMatchContainerRequestContext.java:362)
at org.jboss.resteasy.core.ResourceMethodInvoker.invokeOnTarget(ResourceMethodInvoker.java:439)
at org.jboss.resteasy.core.ResourceMethodInvoker.invoke(ResourceMethodInvoker.java:400)
at org.jboss.resteasy.core.ResourceMethodInvoker.invoke(ResourceMethodInvoker.java:374)
at org.jboss.resteasy.core.ResourceMethodInvoker.invoke(ResourceMethodInvoker.java:67)
at org.jboss.resteasy.core.SynchronousDispatcher.invoke(SynchronousDispatcher.java:488)
... 17 more
Is it possible possible to use CDI field injection in Quarkus in Kotlin? If yes, what needs to be set up for injection to work?
I'm running the app from an uberjar, not a native image.
The jar contains generated class org.example.InjectDemo_Bean containing a method create() disassembled version of which doesn't show any attempt of inject into bm2 field:
public InjectDemo create(CreationalContext var1) {
Object var2 = this.injectProviderSupplier1.get();
CreationalContextImpl var3 = CreationalContextImpl.child((InjectableReferenceProvider)var2, var1);
Object var4 = ((InjectableReferenceProvider)var2).get((CreationalContext)var3);
return new InjectDemo((BeanManager)var4);
}
According to https://quarkus.io/guides/kotlin#cdi-inject-with-kotlin Kotlin annotation reflection miss Target annotation which caused injection to fail. The solution is to add field javax.enterprise.inject.Default annotation:
#field:Default
#field:Inject
protected open lateinit var bm2: BeanManager

How can I make Kryo Serializer

I tried to solve the following problem by providing Kryo Serializer but it still doesn't work. It could not recognize the serializer of ModelCom. Also, any messages by print function don't show up.
I used Apache Flink 1.9.0 and Apache Jena 3.10.0
My code in Kotlin:
val serializer = object : Serializer<Model>(){
override fun write(kryo: Kryo, output: Output?, obj : Model?) {
print("write")
kryo.writeClassAndObject(output, obj)
}
override fun read(kryo: Kryo, input: Input?, type: Class<Model>?): Model {
print("read")
val m = kryo.readObject(input, Model::class.java)
return m
}
}
ExecutionContext.see.config.registerTypeWithKryoSerializer(ModelCom::class.java, serializer::class.java)
Error
Exception in thread "main" org.apache.flink.streaming.runtime.tasks.StreamTaskException: Cannot serialize operator object class org.apache.flink.streaming.api.operators.SimpleUdfStreamOperatorFactory.
at org.apache.flink.streaming.api.graph.StreamConfig.setStreamOperatorFactory(StreamConfig.java:222)
at org.apache.flink.streaming.api.graph.StreamingJobGraphGenerator.setVertexConfig(StreamingJobGraphGenerator.java:460)
at org.apache.flink.streaming.api.graph.StreamingJobGraphGenerator.createChain(StreamingJobGraphGenerator.java:272)
at org.apache.flink.streaming.api.graph.StreamingJobGraphGenerator.createChain(StreamingJobGraphGenerator.java:243)
at org.apache.flink.streaming.api.graph.StreamingJobGraphGenerator.createChain(StreamingJobGraphGenerator.java:243)
at org.apache.flink.streaming.api.graph.StreamingJobGraphGenerator.setChaining(StreamingJobGraphGenerator.java:207)
at org.apache.flink.streaming.api.graph.StreamingJobGraphGenerator.createJobGraph(StreamingJobGraphGenerator.java:159)
at org.apache.flink.streaming.api.graph.StreamingJobGraphGenerator.createJobGraph(StreamingJobGraphGenerator.java:94)
at org.apache.flink.streaming.api.graph.StreamGraph.getJobGraph(StreamGraph.java:737)
at org.apache.flink.optimizer.plan.StreamingPlan.getJobGraph(StreamingPlan.java:40)
at org.apache.flink.streaming.api.environment.LocalStreamEnvironment.execute(LocalStreamEnvironment.java:86)
at org.apache.flink.streaming.api.environment.StreamExecutionEnvironment.execute(StreamExecutionEnvironment.java:1507)
at org.apache.flink.streaming.api.environment.StreamExecutionEnvironment.execute(StreamExecutionEnvironment.java:1489)
at core.EgressEngine.start(EgressEngine.kt:187)
at core.EgressEngineKt.main(EgressEngine.kt:45)
Caused by: java.io.NotSerializableException: org.apache.jena.rdf.model.impl.ModelCom
at java.base/java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1185)
at java.base/java.io.ObjectOutputStream.defaultWriteFields(ObjectOutputStream.java:1553)
at java.base/java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1510)
at java.base/java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1433)
at java.base/java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1179)
at java.base/java.io.ObjectOutputStream.defaultWriteFields(ObjectOutputStream.java:1553)
at java.base/java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1510)
at java.base/java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1433)
at java.base/java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1179)
at java.base/java.io.ObjectOutputStream.defaultWriteFields(ObjectOutputStream.java:1553)
at java.base/java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1510)
at java.base/java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1433)
at java.base/java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1179)
at java.base/java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:349)
at org.apache.flink.util.InstantiationUtil.serializeObject(InstantiationUtil.java:586)
at org.apache.flink.util.InstantiationUtil.writeObjectToConfig(InstantiationUtil.java:515)
at org.apache.flink.streaming.api.graph.StreamConfig.setStreamOperatorFactory(StreamConfig.java:219)
... 14 more
Jena models are not serializable, so this approach isn't going to work. What you could do instead would be to send around just enough serialized data so that each instance that needs a model can instantiate one.
See this thread from the jena-users list about how to resolve this for Spark; the underlying issues are the same for any JVM-based framework that distributes computation.

Kotlin implement a method from an interface that's already present in super class

Let's say I have this interface:
interface Things {
fun size(): Int
}
And I want to subclass a List and implement this interface.
class Cars : ArrayList<String>, Things {}
I get a compilation error:
Inherited platform declarations clash: The following declarations have
the same JVM signature (size()I): fun (): Int defined in
Things fun size(): Int defined in Things
I can get around the compilation error by changing the size contract to a var implicit getter:
interface Things {
var size: Int
}
But then I get a long and complicated error that looks like a runtime error (IllegalStateException) but seems to happen when Kotlin is compiling :shrug:
I suspect I know why this is happening - ArrayList already has int size() and Kotlin now tries to add another of the same signature, but even if I'm right (10% chance?) it doesn't help much.
I'll also mention that it allows me to have get as part of the Things interface, which is satisfied by the list. I'm guessing the operator operator let's this be just different enough to slide?
So anyway, no IDE errors, but when I build I get this:
java.lang.IllegalStateException: Backend Internal error: Exception during code generation
Cause: Concrete fake override public open fun <set-size>(<set-?>: kotlin.Int): kotlin.Unit defined in org.blah.Stuff[PropertySetterDescriptorImpl#673f2280] should have exactly one concrete super-declaration: []
File being compiled at position: file:///blah/Stuff.kt
The root cause was thrown at: bridges.kt:122
at org.jetbrains.kotlin.codegen.CompilationErrorHandler.lambda$static$0(CompilationErrorHandler.java:24)
at org.jetbrains.kotlin.codegen.PackageCodegenImpl.generate(PackageCodegenImpl.java:76)
at org.jetbrains.kotlin.codegen.DefaultCodegenFactory.generatePackage(CodegenFactory.kt:96)
at org.jetbrains.kotlin.codegen.DefaultCodegenFactory.generateModule(CodegenFactory.kt:67)
at org.jetbrains.kotlin.codegen.KotlinCodegenFacade.doGenerateFiles(KotlinCodegenFacade.java:47)
at org.jetbrains.kotlin.codegen.KotlinCodegenFacade.compileCorrectFiles(KotlinCodegenFacade.java:39)
at org.jetbrains.kotlin.cli.jvm.compiler.KotlinToJVMBytecodeCompiler.generate(KotlinToJVMBytecodeCompiler.kt:476)
at org.jetbrains.kotlin.cli.jvm.compiler.KotlinToJVMBytecodeCompiler.compileModules$cli(KotlinToJVMBytecodeCompiler.kt:164)
at org.jetbrains.kotlin.cli.jvm.K2JVMCompiler.doExecute(K2JVMCompiler.kt:166)
at org.jetbrains.kotlin.cli.jvm.K2JVMCompiler.doExecute(K2JVMCompiler.kt:56)
at org.jetbrains.kotlin.cli.common.CLICompiler.execImpl(CLICompiler.kt:84)
at org.jetbrains.kotlin.cli.common.CLICompiler.execImpl(CLICompiler.kt:42)
at org.jetbrains.kotlin.cli.common.CLITool.exec(CLITool.kt:104)
at org.jetbrains.kotlin.incremental.IncrementalJvmCompilerRunner.runCompiler(IncrementalJvmCompilerRunner.kt:349)
at org.jetbrains.kotlin.incremental.IncrementalJvmCompilerRunner.runCompiler(IncrementalJvmCompilerRunner.kt:105)
at org.jetbrains.kotlin.incremental.IncrementalCompilerRunner.compileIncrementally(IncrementalCompilerRunner.kt:237)
at org.jetbrains.kotlin.incremental.IncrementalCompilerRunner.access$compileIncrementally(IncrementalCompilerRunner.kt:37)
at org.jetbrains.kotlin.incremental.IncrementalCompilerRunner$compile$2.invoke(IncrementalCompilerRunner.kt:79)
at org.jetbrains.kotlin.incremental.IncrementalCompilerRunner.compile(IncrementalCompilerRunner.kt:91)
at org.jetbrains.kotlin.daemon.CompileServiceImpl.execIncrementalCompiler(CompileServiceImpl.kt:579)
at org.jetbrains.kotlin.daemon.CompileServiceImpl.access$execIncrementalCompiler(CompileServiceImpl.kt:102)
at org.jetbrains.kotlin.daemon.CompileServiceImpl$compile$$inlined$ifAlive$lambda$2.invoke(CompileServiceImpl.kt:455)
at org.jetbrains.kotlin.daemon.CompileServiceImpl$compile$$inlined$ifAlive$lambda$2.invoke(CompileServiceImpl.kt:102)
at org.jetbrains.kotlin.daemon.CompileServiceImpl$doCompile$$inlined$ifAlive$lambda$2.invoke(CompileServiceImpl.kt:1005)
at org.jetbrains.kotlin.daemon.CompileServiceImpl$doCompile$$inlined$ifAlive$lambda$2.invoke(CompileServiceImpl.kt:102)
at org.jetbrains.kotlin.daemon.common.DummyProfiler.withMeasure(PerfUtils.kt:138)
at org.jetbrains.kotlin.daemon.CompileServiceImpl.checkedCompile(CompileServiceImpl.kt:1047)
at org.jetbrains.kotlin.daemon.CompileServiceImpl.doCompile(CompileServiceImpl.kt:1004)
at org.jetbrains.kotlin.daemon.CompileServiceImpl.compile(CompileServiceImpl.kt:454)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at sun.rmi.server.UnicastServerRef.dispatch(UnicastServerRef.java:346)
at sun.rmi.transport.Transport$1.run(Transport.java:200)
at sun.rmi.transport.Transport$1.run(Transport.java:197)
at java.security.AccessController.doPrivileged(Native Method)
at sun.rmi.transport.Transport.serviceCall(Transport.java:196)
at sun.rmi.transport.tcp.TCPTransport.handleMessages(TCPTransport.java:568)
at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run0(TCPTransport.java:826)
at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.lambda$run$0(TCPTransport.java:683)
at java.security.AccessController.doPrivileged(Native Method)
at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run(TCPTransport.java:682)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at java.lang.Thread.run(Thread.java:745)
Caused by: java.lang.IllegalStateException: Concrete fake override public open fun <set-size>(<set-?>: kotlin.Int): kotlin.Unit defined in org.blah.Stuff[PropertySetterDescriptorImpl#673f2280] should have exactly one concrete super-declaration: []
at org.jetbrains.kotlin.backend.common.bridges.BridgesKt.findConcreteSuperDeclaration(bridges.kt:122)
at org.jetbrains.kotlin.backend.common.bridges.BridgesKt.generateBridges(bridges.kt:59)
at org.jetbrains.kotlin.codegen.JvmBridgesImplKt.generateBridgesForFunctionDescriptorForJvm(JvmBridgesImpl.kt:92)
at org.jetbrains.kotlin.codegen.FunctionCodegen.generateBridges(FunctionCodegen.java:1041)
at org.jetbrains.kotlin.codegen.ClassBodyCodegen.generateBridges(ClassBodyCodegen.java:138)
at org.jetbrains.kotlin.codegen.ClassBodyCodegen.generateBody(ClassBodyCodegen.java:116)
at org.jetbrains.kotlin.codegen.MemberCodegen.generate(MemberCodegen.java:129)
at org.jetbrains.kotlin.codegen.MemberCodegen.genClassOrObject(MemberCodegen.java:302)
at org.jetbrains.kotlin.codegen.MemberCodegen.genClassOrObject(MemberCodegen.java:286)
at org.jetbrains.kotlin.codegen.PackageCodegenImpl.generateClassesAndObjectsInFile(PackageCodegenImpl.java:118)
at org.jetbrains.kotlin.codegen.PackageCodegenImpl.generateFile(PackageCodegenImpl.java:137)
at org.jetbrains.kotlin.codegen.PackageCodegenImpl.generate(PackageCodegenImpl.java:68)
... 44 more
> Task :app:buildInfoGeneratorDebug
Anything I'm missing. I've spent most of my life in Java and am late to the Kotlin party. Any workarounds, alternatives... or is this a known thing?
TYIA
The problem is because kotlin have extension val property for List: size.
The first error says that you have two different kotlin "things" (method and property) which in jvm are the same. And the second error is because you have val implementation in ArrayList and var field in interface so your class Cars needs to implement both setter and getter but implements only getter (from List val size).
The solution is simple: just change your interface property to val size: Int

Binding a domain model with nullable fields in ItemViewModel

When running this code:
class PersonApp : App(PersonView::class)
class Person {
var name: String? = null
}
class PersonModel: ItemViewModel<Person>() {
val name = bind(Person::name)
}
class PersonView : View() {
val model: PersonModel by inject()
override val root = form {
textfield(model.name)
}
}
The following exception is thrown:
Nov 26, 2017 12:18:10 PM tornadofx.DefaultErrorHandler uncaughtException
SEVERE: Uncaught error
kotlin.TypeCastException: null cannot be cast to non-null type javafx.beans.property.Property<N>
at favetelinguis.bfgx.PersonModel$$special$$inlined$bindMutableNullableField$1.invoke(ViewModel.kt:538)
at favetelinguis.bfgx.PersonModel$$special$$inlined$bindMutableNullableField$1.invoke(ViewModel.kt:512)
at favetelinguis.bfgx.PersonModel.<init>(Exeee.kt:27)
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
at java.lang.Class.newInstance(Class.java:442)
at tornadofx.FXKt.find(FX.kt:413)
at favetelinguis.bfgx.Bree$$special$$inlined$inject$1.getValue(Component.kt:954)
at favetelinguis.bfgx.Bree$$special$$inlined$inject$1.getValue(Component.kt:151)
at favetelinguis.bfgx.Bree.getModel(Exeee.kt)
at favetelinguis.bfgx.Bree$root$1.invoke(Exeee.kt:20)
at favetelinguis.bfgx.Bree$root$1.invoke(Exeee.kt:17)
at tornadofx.FXKt.opcr(FX.kt:454)
at tornadofx.FormsKt.form(Forms.kt:23)
at favetelinguis.bfgx.Bree.<init>(Exeee.kt:19)
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
at java.lang.Class.newInstance(Class.java:442)
at tornadofx.FXKt.find(FX.kt:413)
at tornadofx.FXKt.find$default(FX.kt:398)
at tornadofx.App.start(App.kt:79)
at com.sun.javafx.application.LauncherImpl.lambda$launchApplication1$162(LauncherImpl.java:863)
at com.sun.javafx.application.PlatformImpl.lambda$runAndWait$175(PlatformImpl.java:326)
at com.sun.javafx.application.PlatformImpl.lambda$null$173(PlatformImpl.java:295)
at java.security.AccessController.doPrivileged(Native Method)
at com.sun.javafx.application.PlatformImpl.lambda$runLater$174(PlatformImpl.java:294)
at com.sun.glass.ui.InvokeLaterDispatcher$Future.run(InvokeLaterDispatcher.java:95)
What is the idiomatic way of working with domain models where all the initial values are null and are to be set from the GUI? That is how can i change the code above to get it working as expected?
This should already be working in TornadoFX 1.7.13-SNAPSHOT, we did some improvements to nullable POJO's. An alternative approach (and better IMO) is to use JavaFX properties in your domain objects:
class Person {
val nameProperty = SimpleStringProperty()
var name by nameProperty
}
One last thing to consider is that you haven't set an initial item into your ViewModel. Remember that the ViewModel doesn't itself create instances of your item, you have to do that manually, or even give the ViewModel a default item when it's created:
class PersonModel : ItemViewModel<Person>(Person()) {
val name = bind(Person::name)
}