uuuyuqi opened a new pull request, #110:
URL: https://github.com/apache/dubbo-hessian-lite/pull/110
## Problem
Reading an untyped map with an expected interface type fails when
`SerializerFactory` selects `ObjectDeserializer` for that interface. This
affects interfaces such as `Serializable` and `Cloneable`, not just generic
fields.
For example:
```java
class ResultDTO<T extends Serializable> implements Serializable {
private T module;
}
```
When `module` contains a `HashMap<Long, String>`, its erased field type is
`Serializable`. The default HashMap serializer writes an untyped map (`H` in
Hessian 2). Deserializing the field then fails with:
```text
ObjectDeserializer: unexpected object java.lang.Long
```
`Long` is the type of the first map key, not the underlying cause. Empty
maps also fail, with a different error. Generics are not required to reproduce
the issue: reading the same map directly as `Cloneable` fails as well.
The problem depends on the wire representation and the selected
deserializer; it does not mean every object or every Map implementation fails
when read through an interface.
## Cause
`ObjectDeserializer` does not override `readMap()`. When `Hessian2Input`
encounters an untyped map, it calls the selected deserializer's map reader,
which falls back to the error implementation in `AbstractDeserializer` instead
of reading the map.
## Changes
Implement both `readMap` overloads in `ObjectDeserializer`:
- Obtain the deserializer registered for `Map.class` from the current
input's serializer factory.
- Delegate map reading, preserving the expected key/value type hints and
existing map reference handling.
- Check that a non-null result is assignable to the expected interface,
otherwise throw a `HessianProtocolException` identifying both types.
- Allow a registered Map deserializer to return null without causing an NPE.
- Reject direct self-delegation to avoid infinite recursion for that
configuration.
This uses the existing Map deserialization implementation and respects
custom Map deserializers instead of unconditionally constructing a HashMap.
## Scope
This change adds **Map reading only**. It does not change serializer
selection, wire encoding, dependencies, or versions. Existing
Object/Map/HashMap, ordinary object, ArrayList, and null decoding paths are
unchanged.
`ObjectDeserializer.readList`, `readLengthList`, and `readObject(in,
fields)` retain their existing unsupported behavior. The fix does not make
arbitrary objects readable as arbitrary interfaces: non-null map results must
implement the expected interface.
## Tests
Add 16 regression tests covering:
- Non-empty and empty maps in a generic field bounded by `Serializable`.
- A `Cloneable` field.
- Expected key/value types, including `Short` values.
- Registered Map deserializers, a compatible custom interface, and null
results.
- Rejection of incompatible interfaces and direct self-delegation.
- Hessian 2 `H` maps and `M` maps with an empty type.
- Map self-references and shared references read through different
interfaces.
- Unchanged ordinary object, ArrayList, Object/Map/HashMap, and null
behavior.
Before the production fix, the new tests run against master produced 4
passes, 2 assertion failures, and 10 errors. After the fix, all 16 pass.
Full local test results (Maven 3.9.6):
| JDK | Tests (including skipped) | Failures | Errors | Skipped | Result |
| --- | ---: | ---: | ---: | ---: | --- |
| 8 | 213 | 0 | 0 | 2 | Pass |
| 11 | 218 | 0 | 0 | 2 | Pass |
| 17 | 241 | 0 | 0 | 38 | Pass |
| 21 | 241 | 0 | 0 | 38 | Pass |
Skips are due to existing JDK conditions; no tests were excluded to make the
build pass. All 16 new tests ran and passed on each listed JDK. They also pass
on JDK 8 with `-Dcom.caucho.hessian.unsafe=false`. JDK 22 was not available
locally.
Commands, with `JAVA_HOME` set for each JDK:
```bash
# Targeted regression tests on JDK 8
mvn -B -o -pl java-8-test -am \
-Dtest=ObjectDeserializerTest -Dsurefire.failIfNoSpecifiedTests=false test
# Full tests on JDK 8
mvn -B -o test
# Full tests on JDK 11, 17, and 21
mvn -B -o -Pbase test
# Targeted regression tests without Unsafe on JDK 8
mvn -B -o -pl java-8-test -am \
-Dtest=ObjectDeserializerTest -Dsurefire.failIfNoSpecifiedTests=false \
-Dcom.caucho.hessian.unsafe=false test
```
The explicit `base` profile on newer JDKs includes the production module in
the same reactor, ensuring the tests use the modified code rather than an
existing cached artifact. These are library-level tests, not Dubbo RPC
end-to-end tests. No artifacts were installed or deployed during local
verification.
--
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]