obabichevjb opened a new issue, #16489:
URL: https://github.com/apache/dubbo/issues/16489

   ### Pre-check
   
   - [x] I am sure that all the content I provide is in English.
   
   
   ### Search before asking
   
   - [x] I had searched in the 
[issues](https://github.com/apache/dubbo/issues?q=is%3Aissue) and found no 
similar issues.
   
   
   ### Apache Dubbo Component
   
   Java SDK (apache/dubbo)
   
   ### Dubbo Version
   
   Dubbo Java 3.3.6, OpenJDK 21 (Amazon Corretto 21.0.5), macOS 15.6 
(OS-independent)
   Kotlin 2.4.20, Gradle 9.5, default hessian2 serialization
   
   ### Steps to reproduce this issue
   
   
   A Kotlin project with one Dubbo dependency. Provider and consumer run in the 
same JVM over a real
   Netty `dubbo://` transport with a direct URL and `RegistryConfig("N/A")`, so 
no registry or other
   infrastructure is needed — and because the transport is real rather than 
`injvm://`, values are
   genuinely serialized.
   
   `build.gradle.kts`:
   
   ```kotlin
   plugins {
       kotlin("jvm") version "2.4.20"
       application
   }
   repositories { mavenCentral() }
   dependencies {
       implementation("org.apache.dubbo:dubbo:3.3.6")
       implementation("io.netty:netty-all:4.1.118.Final")
       implementation("org.javassist:javassist:3.30.2-GA")
       implementation("org.slf4j:slf4j-simple:2.0.13")
   }
   kotlin { jvmToolchain(21) }
   application { mainClass = "repro.MainKt" }
   ```
   
   `src/main/resources/security/serialize.allowlist`:
   
   ```
   repro
   ```
   
   `src/main/kotlin/Main.kt`:
   
   ```kotlin
   package repro
   
   import java.io.Serializable
   import org.apache.dubbo.config.*
   import org.apache.dubbo.config.bootstrap.DubboBootstrap
   
   sealed interface Reply : Serializable
   
   data class Found(val text: String) : Reply
   
   object NotFoundObject : Reply          // equals() is inherited identity 
comparison
   data object NotFoundDataObject : Reply // compiler generates equals()
   enum class NotFoundEnum : Reply { INSTANCE }
   
   interface ReplyService {
       fun roundTrip(reply: Reply): Reply // returns its argument unchanged
   }
   
   class ReplyServiceImpl : ReplyService {
       override fun roundTrip(reply: Reply): Reply = reply
   }
   
   /** The dispatch a consumer actually writes. */
   private fun describe(reply: Reply): String = when (reply) {
       NotFoundObject -> "matched NotFoundObject"
       NotFoundDataObject -> "matched NotFoundDataObject"
       NotFoundEnum.INSTANCE -> "matched NotFoundEnum.INSTANCE"
       is Found -> "matched Found(${reply.text})"
       else -> ">>> FELL THROUGH TO else <<<"
   }
   
   private fun report(label: String, sent: Reply, back: Reply) =
       println("%-12s | ===:%-5s | ==:%-5s | %s".format(label, back === sent, 
back == sent, describe(back)))
   
   fun main() {
       DubboBootstrap.getInstance()
           .application(ApplicationConfig("repro"))
           .registry(RegistryConfig("N/A"))
           .protocol(ProtocolConfig("dubbo", 20881))
           .service(ServiceConfig<ReplyService>().apply {
               setInterface(ReplyService::class.java)
               setRef(ReplyServiceImpl())
           })
           .start()
   
       val svc = ReferenceConfig<ReplyService>().apply {
           setInterface(ReplyService::class.java)
           url = "dubbo://127.0.0.1:20881"
       }.get()
   
       report("object", NotFoundObject, svc.roundTrip(NotFoundObject))
       report("data object", NotFoundDataObject, 
svc.roundTrip(NotFoundDataObject))
       report("enum", NotFoundEnum.INSTANCE, 
svc.roundTrip(NotFoundEnum.INSTANCE))
       report("data class", Found("hi"), svc.roundTrip(Found("hi")))
   
       DubboBootstrap.getInstance().destroy()
   }
   ```
   
   `./gradlew run` prints:
   
   ```
   object       | ===:false | ==:false | >>> FELL THROUGH TO else <<<
   data object  | ===:false | ==:true  | matched NotFoundDataObject
   enum         | ===:true  | ==:true  | matched NotFoundEnum.INSTANCE
   data class   | ===:false | ==:true  | matched Found(hi)
   ```
   
   
   ### What you expected to happen
   
   **Expected:** `NotFoundObject` should arrive at the consumer as the 
singleton, so the
   `NotFoundObject ->` branch runs — the same way an enum constant survives 
today.
   
   **Actual:** it arrives as a *different instance of the same class*, the 
`when` matches nothing, and
   control reaches `else`. No exception, no warning, no log line — only the 
wrong code path.
   
   Kotlin's `object` declares a singleton: the compiler emits a final class 
with a **private**
   constructor and a single `public static final INSTANCE`, and the language 
guarantees no other
   instance exists. The default bean deserializers do not honour that — they 
allocate a fresh instance,
   bypassing the private constructor, and populate its fields. So Dubbo 
produces an instance the
   language says cannot exist.
   
   Because a plain `object` inherits `equals` from `Any` (identity comparison), 
losing identity also
   loses equality, and Kotlin's `when` compares with `==`. That is why dispatch 
silently changes.
   
   Hessian2 already solves exactly this problem for the Java equivalent. 
`EnumDeserializer` writes the
   constant's name and resolves the real JVM constant on the way back, which is 
why the `enum` row
   above is intact including `===`. A Kotlin `object` is just a class with a 
static `INSTANCE` and gets
   no such treatment.
   
   ### Anything else
   
   _No response_
   
   ### Do you have a (mini) reproduction demo?
   
   - [ ] Yes, I have a minimal reproduction demo to help resolve this issue 
more effectively!
   
   ### Are you willing to submit a pull request to fix on your own?
   
   - [x] Yes I am willing to submit a pull request on my own!
   
   ### Code of Conduct
   
   - [x] I agree to follow this project's [Code of 
Conduct](https://www.apache.org/foundation/policies/conduct)
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to