diff --git a/dubbo-common/src/main/java/org/apache/dubbo/rpc/model/MethodDescriptor.java b/dubbo-common/src/main/java/org/apache/dubbo/rpc/model/MethodDescriptor.java index 2496d2dea311..881820729772 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/rpc/model/MethodDescriptor.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/rpc/model/MethodDescriptor.java @@ -32,6 +32,18 @@ public interface MethodDescriptor { Class[] getParameterClasses(); + /** + * Retrieves the generic parameter types of the method. + *

+ * For parameterized parameters like {@code List}, this returns the + * {@link java.lang.reflect.ParameterizedType} instead of just the raw {@code Class}. + * + * @return the generic parameter types + */ + default Type[] getGenericParameterTypes() { + return getParameterClasses(); + } + Class getReturnClass(); Type[] getReturnTypes(); diff --git a/dubbo-common/src/main/java/org/apache/dubbo/rpc/model/ReflectionMethodDescriptor.java b/dubbo-common/src/main/java/org/apache/dubbo/rpc/model/ReflectionMethodDescriptor.java index ea27aad53ca3..e90dd1132f4d 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/rpc/model/ReflectionMethodDescriptor.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/rpc/model/ReflectionMethodDescriptor.java @@ -41,6 +41,7 @@ public class ReflectionMethodDescriptor implements MethodDescriptor { public final String methodName; private final String[] compatibleParamSignatures; private final Class[] parameterClasses; + private final Type[] genericParameterTypes; private final Class returnClass; private final Type[] returnTypes; private final String paramDesc; @@ -52,6 +53,7 @@ public ReflectionMethodDescriptor(Method method) { this.method = method; this.methodName = method.getName(); this.parameterClasses = method.getParameterTypes(); + this.genericParameterTypes = method.getGenericParameterTypes(); this.returnClass = method.getReturnType(); Type[] returnTypesResult; try { @@ -122,6 +124,11 @@ public Class[] getParameterClasses() { return parameterClasses; } + @Override + public Type[] getGenericParameterTypes() { + return genericParameterTypes; + } + @Override public String getParamDesc() { return paramDesc; diff --git a/dubbo-common/src/test/java/org/apache/dubbo/rpc/model/ReflectionMethodDescriptorTest.java b/dubbo-common/src/test/java/org/apache/dubbo/rpc/model/ReflectionMethodDescriptorTest.java index 352b1ac645a3..b05ee89aeedf 100644 --- a/dubbo-common/src/test/java/org/apache/dubbo/rpc/model/ReflectionMethodDescriptorTest.java +++ b/dubbo-common/src/test/java/org/apache/dubbo/rpc/model/ReflectionMethodDescriptorTest.java @@ -20,7 +20,10 @@ import org.apache.dubbo.rpc.model.MethodDescriptor.RpcType; import org.apache.dubbo.rpc.support.DemoService; +import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; +import java.util.List; +import java.util.Map; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; @@ -89,6 +92,47 @@ void addAttribute() { Assertions.assertEquals(attr, method.getAttribute(attr)); } + @Test + void getGenericParameterTypes_nonGeneric() { + Type[] types = method.getGenericParameterTypes(); + Assertions.assertEquals(1, types.length); + Assertions.assertEquals(String.class, types[0]); + } + + @Test + void getGenericParameterTypes_withGenericParam() throws NoSuchMethodException { + ReflectionMethodDescriptor md = + new ReflectionMethodDescriptor(DemoService.class.getDeclaredMethod("processBytes", List.class)); + Type[] genericTypes = md.getGenericParameterTypes(); + Class[] rawTypes = md.getParameterClasses(); + + Assertions.assertEquals(1, genericTypes.length); + Assertions.assertEquals(List.class, rawTypes[0]); + Assertions.assertInstanceOf(ParameterizedType.class, genericTypes[0]); + + ParameterizedType pt = (ParameterizedType) genericTypes[0]; + Assertions.assertEquals(List.class, pt.getRawType()); + Assertions.assertEquals(Byte.class, pt.getActualTypeArguments()[0]); + } + + @Test + void getGenericParameterTypes_mixedParams() throws NoSuchMethodException { + ReflectionMethodDescriptor md = new ReflectionMethodDescriptor( + DemoService.class.getDeclaredMethod("processMultiple", String.class, List.class, Map.class)); + Type[] genericTypes = md.getGenericParameterTypes(); + Class[] rawTypes = md.getParameterClasses(); + + Assertions.assertEquals(3, genericTypes.length); + // String param: generic type == raw type + Assertions.assertSame(rawTypes[0], genericTypes[0]); + // List: generic type is ParameterizedType + Assertions.assertInstanceOf(ParameterizedType.class, genericTypes[1]); + Assertions.assertEquals(Short.class, ((ParameterizedType) genericTypes[1]).getActualTypeArguments()[0]); + // Map: generic type is ParameterizedType + Assertions.assertInstanceOf(ParameterizedType.class, genericTypes[2]); + Assertions.assertEquals(Byte.class, ((ParameterizedType) genericTypes[2]).getActualTypeArguments()[1]); + } + @Test void testEquals() { try { diff --git a/dubbo-common/src/test/java/org/apache/dubbo/rpc/support/DemoService.java b/dubbo-common/src/test/java/org/apache/dubbo/rpc/support/DemoService.java index 7a8e97b9c671..6e997357105f 100644 --- a/dubbo-common/src/test/java/org/apache/dubbo/rpc/support/DemoService.java +++ b/dubbo-common/src/test/java/org/apache/dubbo/rpc/support/DemoService.java @@ -16,6 +16,13 @@ */ package org.apache.dubbo.rpc.support; +import java.util.List; +import java.util.Map; + public interface DemoService { String sayHello(String name); + + void processBytes(List data); + + void processMultiple(String name, List values, Map mapping); } diff --git a/dubbo-common/src/test/java/org/apache/dubbo/rpc/support/DemoServiceImpl.java b/dubbo-common/src/test/java/org/apache/dubbo/rpc/support/DemoServiceImpl.java index cf81be8561a1..d3a222ab5949 100644 --- a/dubbo-common/src/test/java/org/apache/dubbo/rpc/support/DemoServiceImpl.java +++ b/dubbo-common/src/test/java/org/apache/dubbo/rpc/support/DemoServiceImpl.java @@ -16,9 +16,18 @@ */ package org.apache.dubbo.rpc.support; +import java.util.List; +import java.util.Map; + public class DemoServiceImpl implements DemoService { @Override public String sayHello(String name) { return "hello " + name; } + + @Override + public void processBytes(List data) {} + + @Override + public void processMultiple(String name, List values, Map mapping) {} } diff --git a/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/DecodeableRpcInvocation.java b/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/DecodeableRpcInvocation.java index 23ff9dee619f..4004147ecd1e 100644 --- a/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/DecodeableRpcInvocation.java +++ b/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/DecodeableRpcInvocation.java @@ -46,6 +46,7 @@ import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.lang.reflect.Type; import java.util.List; import java.util.Map; import java.util.function.Supplier; @@ -79,6 +80,8 @@ public class DecodeableRpcInvocation extends RpcInvocation implements Codec, Dec protected final FrameworkModel frameworkModel; + private Type[] genericParameterTypes; + protected final transient Supplier callbackServiceCodecFactory; private static final boolean CHECK_SERIALIZATION = @@ -236,6 +239,7 @@ protected Class[] drawPts(String path, String version, String desc, Class[ MethodDescriptor methodDescriptor = serviceDescriptor.getMethod(getMethodName(), desc); if (methodDescriptor != null) { pts = methodDescriptor.getParameterClasses(); + this.genericParameterTypes = methodDescriptor.getGenericParameterTypes(); this.setReturnTypes(methodDescriptor.getReturnTypes()); // switch TCCL @@ -270,10 +274,15 @@ protected Class[] drawPts(String path, String version, String desc, Class[ } protected Object[] drawArgs(ObjectInput in, Class[] pts) throws IOException, ClassNotFoundException { - Object[] args; - args = new Object[pts.length]; + Object[] args = new Object[pts.length]; for (int i = 0; i < args.length; i++) { - args[i] = in.readObject(pts[i]); + if (genericParameterTypes != null + && i < genericParameterTypes.length + && genericParameterTypes[i] != pts[i]) { + args[i] = in.readObject(pts[i], genericParameterTypes[i]); + } else { + args[i] = in.readObject(pts[i]); + } } return args; } diff --git a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/ReflectionPackableMethod.java b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/ReflectionPackableMethod.java index 046410a20dfb..0d17ebc30882 100644 --- a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/ReflectionPackableMethod.java +++ b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/ReflectionPackableMethod.java @@ -114,7 +114,8 @@ public ReflectionPackableMethod( // server this.responsePack = new WrapResponsePack(serialization, url, serializeName, actualResponseType); - this.requestUnpack = new WrapRequestUnpack(serialization, url, allSerialize, actualRequestTypes); + this.requestUnpack = new WrapRequestUnpack( + serialization, url, allSerialize, actualRequestTypes, method.getGenericParameterTypes()); } this.allSerialize = allSerialize; } @@ -442,16 +443,20 @@ private class WrapRequestUnpack implements WrapperUnPack { private final Class[] actualRequestTypes; + private final Type[] genericParameterTypes; + private final Collection allSerialize; private WrapRequestUnpack( MultipleSerialization serialization, URL url, Collection allSerialize, - Class[] actualRequestTypes) { + Class[] actualRequestTypes, + Type[] genericParameterTypes) { this.serialization = serialization; this.url = url; this.actualRequestTypes = actualRequestTypes; + this.genericParameterTypes = genericParameterTypes; this.allSerialize = allSerialize; } @@ -467,7 +472,14 @@ public Object unpack(byte[] data, boolean isReturnTriException) throws IOExcepti for (int i = 0; i < wrapper.getArgs().size(); i++) { ByteArrayInputStream bais = new ByteArrayInputStream(wrapper.getArgs().get(i)); - ret[i] = serialization.deserialize(url, wrapper.getSerializeType(), actualRequestTypes[i], bais); + if (genericParameterTypes != null + && i < genericParameterTypes.length + && genericParameterTypes[i] != actualRequestTypes[i]) { + ret[i] = serialization.deserialize( + url, wrapper.getSerializeType(), actualRequestTypes[i], genericParameterTypes[i], bais); + } else { + ret[i] = serialization.deserialize(url, wrapper.getSerializeType(), actualRequestTypes[i], bais); + } } return ret; } diff --git a/dubbo-serialization/dubbo-serialization-api/src/main/java/org/apache/dubbo/common/serialize/DefaultMultipleSerialization.java b/dubbo-serialization/dubbo-serialization-api/src/main/java/org/apache/dubbo/common/serialize/DefaultMultipleSerialization.java index b0ccb0fd04b5..00ae86b6145b 100644 --- a/dubbo-serialization/dubbo-serialization-api/src/main/java/org/apache/dubbo/common/serialize/DefaultMultipleSerialization.java +++ b/dubbo-serialization/dubbo-serialization-api/src/main/java/org/apache/dubbo/common/serialize/DefaultMultipleSerialization.java @@ -21,6 +21,7 @@ import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.lang.reflect.Type; public class DefaultMultipleSerialization implements MultipleSerialization { @@ -46,6 +47,17 @@ public Object deserialize(URL url, String serializeType, Class clz, InputStre return in.readObject(clz); } + @Override + public Object deserialize(URL url, String serializeType, Class clz, Type type, InputStream os) + throws IOException, ClassNotFoundException { + serializeType = convertHessian(serializeType); + final Serialization serialization = url.getOrDefaultFrameworkModel() + .getExtensionLoader(Serialization.class) + .getExtension(serializeType); + final ObjectInput in = serialization.deserialize(null, os); + return in.readObject(clz, type); + } + private String convertHessian(String ser) { if (ser.equals("hessian4")) { return "hessian2"; diff --git a/dubbo-serialization/dubbo-serialization-api/src/main/java/org/apache/dubbo/common/serialize/MultipleSerialization.java b/dubbo-serialization/dubbo-serialization-api/src/main/java/org/apache/dubbo/common/serialize/MultipleSerialization.java index 3bafedfbca89..d7f3a4c316ca 100644 --- a/dubbo-serialization/dubbo-serialization-api/src/main/java/org/apache/dubbo/common/serialize/MultipleSerialization.java +++ b/dubbo-serialization/dubbo-serialization-api/src/main/java/org/apache/dubbo/common/serialize/MultipleSerialization.java @@ -23,6 +23,7 @@ import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.lang.reflect.Type; @SPI(scope = ExtensionScope.FRAMEWORK) public interface MultipleSerialization { @@ -31,4 +32,9 @@ public interface MultipleSerialization { Object deserialize(URL url, String serializeType, Class clz, InputStream os) throws IOException, ClassNotFoundException; + + default Object deserialize(URL url, String serializeType, Class clz, Type type, InputStream os) + throws IOException, ClassNotFoundException { + return deserialize(url, serializeType, clz, os); + } } diff --git a/dubbo-serialization/dubbo-serialization-hessian2/src/main/java/org/apache/dubbo/common/serialize/hessian2/Hessian2ObjectInput.java b/dubbo-serialization/dubbo-serialization-hessian2/src/main/java/org/apache/dubbo/common/serialize/hessian2/Hessian2ObjectInput.java index 8bb224d5452e..50847132bb50 100644 --- a/dubbo-serialization/dubbo-serialization-hessian2/src/main/java/org/apache/dubbo/common/serialize/hessian2/Hessian2ObjectInput.java +++ b/dubbo-serialization/dubbo-serialization-hessian2/src/main/java/org/apache/dubbo/common/serialize/hessian2/Hessian2ObjectInput.java @@ -22,6 +22,7 @@ import java.io.IOException; import java.io.InputStream; +import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.util.Objects; @@ -119,6 +120,7 @@ public T readObject(Class cls) throws IOException, ClassNotFoundException } @Override + @SuppressWarnings("unchecked") public T readObject(Class cls, Type type) throws IOException, ClassNotFoundException { if (!Objects.equals( mH2i.getSerializerFactory().getClassLoader(), @@ -126,9 +128,35 @@ public T readObject(Class cls, Type type) throws IOException, ClassNotFou mH2i.setSerializerFactory(hessian2FactoryManager.getSerializerFactory( Thread.currentThread().getContextClassLoader())); } + if (type instanceof ParameterizedType) { + Type[] typeArgs = ((ParameterizedType) type).getActualTypeArguments(); + Class[] expectedTypes = new Class[typeArgs.length]; + boolean hasExpectedType = false; + for (int i = 0; i < typeArgs.length; i++) { + if (typeArgs[i] instanceof Class && isPrimitive((Class) typeArgs[i])) { + expectedTypes[i] = (Class) typeArgs[i]; + hasExpectedType = true; + } + } + if (hasExpectedType) { + return (T) mH2i.readObject(cls, expectedTypes); + } + } return readObject(cls); } + private static boolean isPrimitive(Class type) { + return type.isPrimitive() + || type == Boolean.class + || type == Character.class + || type == Byte.class + || type == Short.class + || type == Integer.class + || type == Long.class + || type == Float.class + || type == Double.class; + } + public InputStream readInputStream() throws IOException { return mH2i.readInputStream(); } diff --git a/dubbo-serialization/dubbo-serialization-hessian2/src/test/java/org/apache/dubbo/common/serialize/hessian2/Hessian2SerializationTest.java b/dubbo-serialization/dubbo-serialization-hessian2/src/test/java/org/apache/dubbo/common/serialize/hessian2/Hessian2SerializationTest.java index 93dc9d1185e1..9e4fe59c9237 100644 --- a/dubbo-serialization/dubbo-serialization-hessian2/src/test/java/org/apache/dubbo/common/serialize/hessian2/Hessian2SerializationTest.java +++ b/dubbo-serialization/dubbo-serialization-hessian2/src/test/java/org/apache/dubbo/common/serialize/hessian2/Hessian2SerializationTest.java @@ -27,6 +27,9 @@ import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.util.Arrays; import java.util.Date; import java.util.HashMap; import java.util.LinkedList; @@ -507,6 +510,225 @@ void testReadObjectNotMatched() throws IOException, ClassNotFoundException { frameworkModel.destroy(); } + @Test + void testReadObjectWithGenericType_ListByte() throws Exception { + FrameworkModel frameworkModel = new FrameworkModel(); + Serialization serialization = + frameworkModel.getExtensionLoader(Serialization.class).getExtension("hessian2"); + URL url = URL.valueOf("").setScopeModel(frameworkModel); + + List original = Arrays.asList((byte) 1, (byte) 2, (byte) 127, (byte) -1); + + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + ObjectOutput objectOutput = serialization.serialize(url, outputStream); + objectOutput.writeObject(original); + objectOutput.flushBuffer(); + + byte[] bytes = outputStream.toByteArray(); + ByteArrayInputStream inputStream = new ByteArrayInputStream(bytes); + ObjectInput objectInput = serialization.deserialize(url, inputStream); + + Type listByteType = makeParameterizedType(List.class, Byte.class); + List result = objectInput.readObject(List.class, listByteType); + + Assertions.assertEquals(original.size(), result.size()); + for (int i = 0; i < original.size(); i++) { + Assertions.assertInstanceOf( + Byte.class, + result.get(i), + "Element at index " + i + " should be Byte but was " + + result.get(i).getClass().getName()); + Assertions.assertEquals(original.get(i), result.get(i)); + } + + frameworkModel.destroy(); + } + + @Test + void testReadObjectWithGenericType_ListShort() throws Exception { + FrameworkModel frameworkModel = new FrameworkModel(); + Serialization serialization = + frameworkModel.getExtensionLoader(Serialization.class).getExtension("hessian2"); + URL url = URL.valueOf("").setScopeModel(frameworkModel); + + List original = Arrays.asList((short) 1, (short) 200, (short) -100); + + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + ObjectOutput objectOutput = serialization.serialize(url, outputStream); + objectOutput.writeObject(original); + objectOutput.flushBuffer(); + + byte[] bytes = outputStream.toByteArray(); + ByteArrayInputStream inputStream = new ByteArrayInputStream(bytes); + ObjectInput objectInput = serialization.deserialize(url, inputStream); + + Type listShortType = makeParameterizedType(List.class, Short.class); + List result = objectInput.readObject(List.class, listShortType); + + Assertions.assertEquals(original.size(), result.size()); + for (int i = 0; i < original.size(); i++) { + Assertions.assertInstanceOf(Short.class, result.get(i)); + Assertions.assertEquals(original.get(i), result.get(i)); + } + + frameworkModel.destroy(); + } + + @Test + void testReadObjectWithGenericType_ListFloat() throws Exception { + FrameworkModel frameworkModel = new FrameworkModel(); + Serialization serialization = + frameworkModel.getExtensionLoader(Serialization.class).getExtension("hessian2"); + URL url = URL.valueOf("").setScopeModel(frameworkModel); + + List original = Arrays.asList(1.5f, 2.5f, -3.14f); + + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + ObjectOutput objectOutput = serialization.serialize(url, outputStream); + objectOutput.writeObject(original); + objectOutput.flushBuffer(); + + byte[] bytes = outputStream.toByteArray(); + ByteArrayInputStream inputStream = new ByteArrayInputStream(bytes); + ObjectInput objectInput = serialization.deserialize(url, inputStream); + + Type listFloatType = makeParameterizedType(List.class, Float.class); + List result = objectInput.readObject(List.class, listFloatType); + + Assertions.assertEquals(original.size(), result.size()); + for (int i = 0; i < original.size(); i++) { + Assertions.assertInstanceOf(Float.class, result.get(i)); + Assertions.assertEquals(original.get(i), result.get(i)); + } + + frameworkModel.destroy(); + } + + @Test + void testReadObjectWithGenericType_MapStringByte() throws Exception { + FrameworkModel frameworkModel = new FrameworkModel(); + Serialization serialization = + frameworkModel.getExtensionLoader(Serialization.class).getExtension("hessian2"); + URL url = URL.valueOf("").setScopeModel(frameworkModel); + + Map original = new HashMap<>(); + original.put("a", (byte) 1); + original.put("b", (byte) 2); + + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + ObjectOutput objectOutput = serialization.serialize(url, outputStream); + objectOutput.writeObject(original); + objectOutput.flushBuffer(); + + byte[] bytes = outputStream.toByteArray(); + ByteArrayInputStream inputStream = new ByteArrayInputStream(bytes); + ObjectInput objectInput = serialization.deserialize(url, inputStream); + + Type mapType = makeParameterizedType(Map.class, String.class, Byte.class); + Map result = objectInput.readObject(Map.class, mapType); + + Assertions.assertEquals(original.size(), result.size()); + for (Map.Entry entry : result.entrySet()) { + Assertions.assertInstanceOf( + Byte.class, entry.getValue(), "Value for key '" + entry.getKey() + "' should be Byte"); + } + Assertions.assertEquals(original.get("a"), result.get("a")); + Assertions.assertEquals(original.get("b"), result.get("b")); + + frameworkModel.destroy(); + } + + @Test + void testReadObjectWithGenericType_nonGenericUnchanged() throws Exception { + FrameworkModel frameworkModel = new FrameworkModel(); + Serialization serialization = + frameworkModel.getExtensionLoader(Serialization.class).getExtension("hessian2"); + URL url = URL.valueOf("").setScopeModel(frameworkModel); + + String original = "hello"; + + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + ObjectOutput objectOutput = serialization.serialize(url, outputStream); + objectOutput.writeObject(original); + objectOutput.flushBuffer(); + + byte[] bytes = outputStream.toByteArray(); + ByteArrayInputStream inputStream = new ByteArrayInputStream(bytes); + ObjectInput objectInput = serialization.deserialize(url, inputStream); + Assertions.assertEquals(original, objectInput.readObject(String.class, String.class)); + + frameworkModel.destroy(); + } + + private static ParameterizedType makeParameterizedType(Class rawType, Type... typeArguments) { + return new ParameterizedType() { + @Override + public Type[] getActualTypeArguments() { + return typeArguments; + } + + @Override + public Type getRawType() { + return rawType; + } + + @Override + public Type getOwnerType() { + return null; + } + }; + } + + @Test + void testReadObjectWithGenericType_pojoWithNarrowNumberFields() throws Exception { + FrameworkModel frameworkModel = new FrameworkModel(); + Serialization serialization = + frameworkModel.getExtensionLoader(Serialization.class).getExtension("hessian2"); + URL url = URL.valueOf("").setScopeModel(frameworkModel); + + List scores = Arrays.asList((byte) 90, (byte) 85); + Map attrs = new HashMap<>(); + attrs.put("level", (byte) 5); + attrs.put("rank", (byte) 3); + NarrowNumberPojo original = new NarrowNumberPojo("Alice", (byte) 30, (short) 170, 12345.67f, scores, attrs); + + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + ObjectOutput objectOutput = serialization.serialize(url, outputStream); + objectOutput.writeObject(original); + objectOutput.flushBuffer(); + + byte[] bytes = outputStream.toByteArray(); + ByteArrayInputStream inputStream = new ByteArrayInputStream(bytes); + ObjectInput objectInput = serialization.deserialize(url, inputStream); + + NarrowNumberPojo result = objectInput.readObject(NarrowNumberPojo.class, NarrowNumberPojo.class); + + Assertions.assertEquals("Alice", result.getName()); + Assertions.assertEquals((byte) 30, result.getAge()); + Assertions.assertEquals((short) 170, result.getHeight()); + Assertions.assertEquals(12345.67f, result.getSalary(), 0.01f); + + Assertions.assertNotNull(result.getScores()); + Assertions.assertEquals(2, result.getScores().size()); + for (Object elem : result.getScores()) { + Assertions.assertInstanceOf(Byte.class, elem, "Score element should be Byte but was " + elem.getClass()); + } + Assertions.assertEquals((byte) 90, result.getScores().get(0)); + Assertions.assertEquals((byte) 85, result.getScores().get(1)); + + Assertions.assertNotNull(result.getAttributes()); + for (Map.Entry entry : result.getAttributes().entrySet()) { + Assertions.assertInstanceOf( + Byte.class, + entry.getValue(), + "Attribute value should be Byte but was " + entry.getValue().getClass()); + } + Assertions.assertEquals((byte) 5, result.getAttributes().get("level")); + Assertions.assertEquals((byte) 3, result.getAttributes().get("rank")); + + frameworkModel.destroy(); + } + @Test void testLimit1() throws IOException, ClassNotFoundException { FrameworkModel frameworkModel = new FrameworkModel(); diff --git a/dubbo-serialization/dubbo-serialization-hessian2/src/test/java/org/apache/dubbo/common/serialize/hessian2/NarrowNumberPojo.java b/dubbo-serialization/dubbo-serialization-hessian2/src/test/java/org/apache/dubbo/common/serialize/hessian2/NarrowNumberPojo.java new file mode 100644 index 000000000000..633269f8421d --- /dev/null +++ b/dubbo-serialization/dubbo-serialization-hessian2/src/test/java/org/apache/dubbo/common/serialize/hessian2/NarrowNumberPojo.java @@ -0,0 +1,110 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.dubbo.common.serialize.hessian2; + +import java.io.Serializable; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +public class NarrowNumberPojo implements Serializable { + + private String name; + private byte age; + private short height; + private float salary; + private List scores; + private Map attributes; + + public NarrowNumberPojo() {} + + public NarrowNumberPojo( + String name, byte age, short height, float salary, List scores, Map attributes) { + this.name = name; + this.age = age; + this.height = height; + this.salary = salary; + this.scores = scores; + this.attributes = attributes; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public byte getAge() { + return age; + } + + public void setAge(byte age) { + this.age = age; + } + + public short getHeight() { + return height; + } + + public void setHeight(short height) { + this.height = height; + } + + public float getSalary() { + return salary; + } + + public void setSalary(float salary) { + this.salary = salary; + } + + public List getScores() { + return scores; + } + + public void setScores(List scores) { + this.scores = scores; + } + + public Map getAttributes() { + return attributes; + } + + public void setAttributes(Map attributes) { + this.attributes = attributes; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + NarrowNumberPojo that = (NarrowNumberPojo) o; + return age == that.age + && height == that.height + && Float.compare(that.salary, salary) == 0 + && Objects.equals(name, that.name) + && Objects.equals(scores, that.scores) + && Objects.equals(attributes, that.attributes); + } + + @Override + public int hashCode() { + return Objects.hash(name, age, height, salary, scores, attributes); + } +}