fix: preserve generic parameter types during deserialization to fix List<Byte> → List<Integer> [3.3]#16198
fix: preserve generic parameter types during deserialization to fix List<Byte> → List<Integer> [3.3]#16198uuuyuqi wants to merge 5 commits intoapache:3.3from
Conversation
When RPC methods have parameters like List<Byte>, Map<String, Byte>, or List<Short>, the provider-side deserialization incorrectly produces List<Integer> / Map<String, Integer> because the generic type info is discarded. This fix addresses the issue across three layers: 1. Model layer: add getGenericParameterTypes() to MethodDescriptor (default method for backward compat) and implement it in ReflectionMethodDescriptor to expose Type[] for parameters. 2. Protocol layer: update DecodeableRpcInvocation.drawArgs() (Dubbo protocol) and ReflectionPackableMethod.WrapRequestUnpack (Triple protocol) to pass generic Type to the serialization framework. 3. Serialization layer: fix Hessian2ObjectInput.readObject(Class, Type) to post-convert collection/map elements to the correct narrow number type (Byte, Short, Float). No wire protocol changes needed. Change-Id: Ifefdd0cd9719de380ef5b3b6076e51123781cbb8 Co-developed-by: Cursor <noreply@cursor.com>
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## 3.3 #16198 +/- ##
============================================
+ Coverage 60.79% 60.81% +0.02%
- Complexity 11751 11774 +23
============================================
Files 1953 1954 +1
Lines 89119 89148 +29
Branches 13444 13450 +6
============================================
+ Hits 54177 54217 +40
+ Misses 29368 29360 -8
+ Partials 5574 5571 -3
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
oxsean
left a comment
There was a problem hiding this comment.
I don't think this is a problem. Could you provide a complete demo to reproduce it?
When making an RPC call, the type of each value should be included during serialization, rather than being resolved by the provider. For generic calls, the type is handle by PojoUtils.
https://github.com/apache/dubbo/blob/3.3/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/filter/GenericFilter.java
|
@oxsean Thanks for the review! Here is the complete reproduction demo: Demo repo: https://github.com/uuuyuqi/dubbo3-list-byte-bug-demo Clone, start Nacos, run Root cause explainedThe issue is specific to hessian2 serialization with narrow number types ( Here is why:
This fix simply connects the existing dots:
No changes to the wire protocol, no impact on non-generic parameters (the |
|
@uuuyuqi you'd better add testcase that the interface has complex object which has narrow number bytes fields. e.g., |
| Thread.currentThread().getContextClassLoader())); | ||
| } | ||
| return readObject(cls); | ||
| T result = readObject(cls); |
There was a problem hiding this comment.
Consider using readObject(Class cl, Class<?>... expectedTypes) to avoid a second conversion.
…ype) Add NarrowNumberPojo with byte/short/float fields and List<Byte>/ Map<String, Byte> fields to verify that POJO deserialization via readObject(Class, Type) is not affected by the generic type fix. Change-Id: I07751b608827b03ad4ac7b02704b522751ecbea8 Co-developed-by: Cursor <noreply@cursor.com>
|
@zrlw Thanks for the suggestion! I've added a test case with The test (
This is expected because when the method parameter is a POJO (e.g., All 16 tests pass locally. |
…ype handling Use hessian2's built-in expectedTypes support instead of post-processing conversion, as suggested in review. Change-Id: I1cbb65a684f2f03aa7219826f1fa866b6d7d79cc Co-developed-by: Cursor <noreply@cursor.com>
|
Now it‘s using |
|
jobs/testjob_1-result-java21.txt:[11/35] [dubbo-samples-triple-rest-springmvc:1/1] TEST FAILURE: Run tests failed, version: -Ddubbo.version=3.3.7-SNAPSHOT -Dspring.version=6.1.5 -Djava.version=21, please check logs: /home/runner/work/dubbo/dubbo/2-advanced/dubbo-samples-triple-rest/dubbo-samples-triple-rest-springmvc/target/logs |
|
2026-04-11T13:28:00.3369064Z org.apache.dubbo.remoting.http12.exception.DecodeException: status=500, Internal Server Error |
Hessian2Input.readObject(Class, Class...) expectedTypes mechanism only works correctly for simple types. For complex POJO types like User, passing the class as expectedType causes CollectionDeserializer to use a wrong deserializer, leading to HessianProtocolException. Change-Id: If909026ae4f9633155ffd5bd5c7b318aa77f492b Co-developed-by: Cursor <noreply@cursor.com>
|
I'm sorry, but I don't think this problem needs to be fixed. Because the real reason why we cannot serialize it is that the Hessian types do not include byte. So the byte and Integer use the same tag value. If you fix this one, it will have the same problem like |
…pectedTypes filtering Align the type filtering logic with hessian-lite's FieldDeserializer2Factory, using isPrimitive() to cover all primitive wrapper types instead of only narrow number types (Byte/Short/Float). Change-Id: If4dd8f961ec3b3a8e4f2881a866f527777e7a47d Co-developed-by: Cursor <noreply@cursor.com>
how about |
|
@zrlw Fixed. The previous implementation passed all generic type arguments as Now only |
|
@RainYuY You're right that Hessian protocol doesn't have a native byte type — byte and int share the same tag. But hessian-lite already handles this through The issue was that Dubbo's invocation chain didn't pass generic type info down to hessian-lite, so this existing conversion capability was never triggered. This fix simply propagates the generic parameter types that are already available via reflection, letting hessian-lite's own type narrowing logic work as designed. No new conversion logic is introduced. For nested generics like |
Closes #16197
Summary
When RPC methods have parameters like
List<Byte>,Map<String, Byte>, orList<Short>, the provider-side deserialization incorrectly producesList<Integer>/Map<String, Integer>because the generic type information is discarded during deserialization.This PR fixes the issue across three layers:
getGenericParameterTypes()toMethodDescriptor(default method for backward compat) and implement it inReflectionMethodDescriptorto exposeType[]for method parameters.DecodeableRpcInvocation.drawArgs()(Dubbo protocol) andReflectionPackableMethod.WrapRequestUnpack(Triple protocol) to pass genericTypeto the serialization framework when available.Hessian2ObjectInput.readObject(Class, Type)to post-convert collection/map elements to the correct narrow number type (Byte,Short,Float).No wire protocol changes needed. The fix leverages existing local reflection information on the provider side.
Changes
MethodDescriptor.javadefault getGenericParameterTypes()ReflectionMethodDescriptor.javaDecodeableRpcInvocation.javaTypeindrawArgs()ReflectionPackableMethod.javaTypeinWrapRequestUnpackMultipleSerialization.javadeserialize(url, type, clz, genericType, is)overloadDefaultMultipleSerialization.javaHessian2ObjectInput.javaTest plan
ReflectionMethodDescriptor.getGenericParameterTypes()Hessian2ObjectInput.readObject(Class, Type)withList<Byte>,List<Short>,List<Float>,Map<String, Byte>Made with Cursor